nrf52840 dongle connection

Subject: Request for Assistance: Coding BLE and SPI for Gesture Control using nRF52840

Dear nRF Support Team,

I hope this email finds you well. My name is sharon and I am currently working on a project that involves sending gesture data from a mobile app to an LED matrix. I am using the nRF52840 dongle as a BLE interface between my Android application and the LED matrix.

Below is an overview of my setup:

  • Mobile App: Custom-built (using Jetpack Compose)
  • BLE Interface: nRF52840 dongle
  • MCU: S32K118 (communicates with LED driver via SPI)
  • Communication Flow:
    1. Phone sends gestures via Bluetooth to nRF52840
    2. nRF52840 communicates with the S32K118 MCU via SPI
    3. S32K118 controls the LED matrix driver (SPI)

I am reaching out to request your assistance with the following:

  1. BLE Communication:

    • I need guidance on coding the nRF52840 to receive BLE data from the mobile app.
    • How can I efficiently send gesture data (e.g., touchpad coordinates or patterns) over BLE?
  2. SPI Communication:

    • What would be the recommended approach for transferring BLE data from the nRF52840 to the S32K118 via SPI?
    • Are there any reference examples for SPI communication on the nRF52840 that I can follow?

Any code snippets, libraries, or documentation you can provide would be greatly appreciated. Additionally, if there are specific SDK examples or configurations you recommend, I’d be glad to explore those.

Thank you for your time and support. Please let me know if you need any further information from my end.

Best regards,
 iam using vs studio for coding with nrf extension

Parents
  • Hi Sharon,

    BLE Communication:

    • I need guidance on coding the nRF52840 to receive BLE data from the mobile app.

    The nRF Connect SDK suppors most BLE use cases. To get started I suggest you first go through nRF Connect SDK Fundamentals, and then Bluetooth Low Energy Fundamentals.

    How can I efficiently send gesture data (e.g., touchpad coordinates or patterns) over BLE?

    There are several ways to do this. You can use existing service(s) or a custom service. A simple way to get started is to use the Nordic UART Service (NUS) which gives you a simple data channel. But this is up to you.

    What would be the recommended approach for transferring BLE data from the nRF52840 to the S32K118 via SPI?

    It is difficult to be specifc here as I don't have experience with S32K118. I am wondering why you need the additional MCU though, why not use the nRF52840 and connect the LED matrix driver directly from that? Do you need an additional MCU?

    Are there any reference examples for SPI communication on the nRF52840 that I can follow?

    The SDK has some SPI examples, and this is covered in nRF Connect SDK Intermediate.

    Br,

    Einar

  • can u guide me how send data recieved in dongle via ble to other peripherals via spi i only want to send data from dongle please help how to do and with stepss please

  • Hi,

    Some questions:

    • How are you building this (for which board and which SDK version, and what is the configuration)?
    • Which errors do you get? Please share the full log

    PS: To share code in a readable way you can use Insert -> Code.

  • /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample
     */
    #include "uart_async_adapter.h"
    
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/usb/usb_device.h>
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <soc.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    
    #include <bluetooth/services/nus.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/settings/settings.h>
    
    #include <stdio.h>
    
    #include <zephyr/logging/log.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    
    
    #define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
    #define PRIORITY 7
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define RUN_LED_BLINK_INTERVAL 1000
    
    #define CON_STATUS_LED DK_LED2
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    
    //static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
    static struct k_work_delayable uart_work;
    
    
    
    LOG_MODULE_REGISTER(main);
    
    // SPI Configuration
    #define SPI_OP SPI_WORD_SET(8) | SPI_TRANSFER_MSB
    
    static const struct spi_dt_spec spi_dev = SPI_DT_SPEC_GET(DT_NODELABEL(bme280), SPI_OP, 0);
    
    // Example Data for SPI Write
    uint8_t led_control_data[3] = { 0x05, 0x0A, 0x01 }; // Row=5, Column=10, Status=ON
    
    // Function to send data over SPI
    void bme_write_reg(const uint8_t *data, size_t len) {
        struct spi_buf tx_buf = { .buf = (void *)data, .len = len };
        struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
    
        if (spi_write(spi_dev.bus, &spi_dev.config, &tx) != 0) {
            LOG_ERR("SPI write failed");
        } else {
            LOG_INF("SPI data sent: %d %d %d", data[0], data[1], data[2]);
        }
    }
    
    // BLE Receive Callback
    static void ble_receive_cb(struct bt_conn *conn, const uint8_t *data, uint16_t len) {
        if (len == 3) {
            memcpy(led_control_data, data, len);  // Store the received data
            LOG_INF("Received BLE Data: Row=%d, Column=%d, Status=%d", 
                    data[0], data[1], data[2]);
        } else {
            LOG_WRN("Invalid data length received over BLE");
        }
    }
    
    // BLE NUS Callbacks
    static struct bt_nus_cb nus_cb = {
        .received = ble_receive_cb,
    };
    
    // Bluetooth Initialization
    void bt_ready(int err) {
        if (err) {
            LOG_ERR("Bluetooth init failed (err %d)", err);
            return;
        }
    
        LOG_INF("Bluetooth initialized");
    
        // Start advertising
        err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, NULL, 0, NULL, 0);
        if (err) {
            LOG_ERR("Advertising failed to start (err %d)", err);
            return;
        }
    
        LOG_INF("Advertising successfully started");
    }
    
    // Main function
    void main(void) {
        int err;
    
        LOG_INF("Initializing Bluetooth and SPI...");
    
        // Initialize Bluetooth
        err = bt_enable(bt_ready);
        if (err) {
            LOG_ERR("Bluetooth enable failed (err %d)", err);
            return;
        }
    
        // Initialize SPI device
        if (!device_is_ready(spi_dev.bus)) {
            LOG_ERR("SPI device not ready");
            return;
        }
    
        LOG_INF("SPI device initialized");
    
        // Register NUS service
        bt_nus_init(&nus_cb);
    
        // Main loop: Continuously send SPI data
        while (1) {
            bme_write_reg(led_control_data, sizeof(led_control_data));
    
            // Sleep to avoid overwhelming the SPI bus
            k_msleep(1000);  // Send data every 1 second
        }
    }
    iam using nrf52840 dongle  and sdk version iam using is nrf connect extension in vs studio 2.7.0 data is not sending via spi please help

  • I see.

    sharon123321 said:
    data is not sending via spi

    Do you get any errors in the log, or something else? In what way is it not working? If that does not provide anythign specific, what do you see with a logic analyzer on the SPI lines, and how does it differ from what you would expect? Please elaborate.

  • i have replaced the 

    Sending data between a UART and a Bluetooth LE connection example code instead of uart i included spi connection but its not sending nothing when i used picoscope

  • can u verify wheather my code is correct or not?

Reply Children
  • Hi,

    I am not sure in what way your applicaiton is supposed to work and how it is failing. Can you explain in detail what you intend it should, do and in what way it does not work when you test? And after that, what have you learned from debugging?

  • /*
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <soc.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    #include <bluetooth/services/nus.h>
    #include <zephyr/settings/settings.h>
    #include <stdio.h>
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/bluetooth/addr.h>
    #include <zephyr/bluetooth/conn.h>
    #include <bluetooth/services/lbs.h>
    static struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
    	(BT_LE_ADV_OPT_CONNECTABLE |
    	 BT_LE_ADV_OPT_USE_IDENTITY), /* Connectable advertising and use identity address */
    	800, /* Min Advertising Interval 500ms (800*0.625ms) */
    	801, /* Max Advertising Interval 500.625ms (801*0.625ms) */
    	NULL); /* Set to NULL for undirected advertising */
    
    
    #define MATRIX_ROWS 16
    #define MATRIX_COLS 32
    #define BYTES_PER_MATRIX (MATRIX_ROWS * MATRIX_COLS / 8)  // 64 bytes for 16x32
    
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    #define USER_BUTTON DK_BTN1_MSK
    #define RUN_STATUS_LED DK_LED1
    #define CONNECTION_STATUS_LED DK_LED2
    #define RUN_LED_BLINK_INTERVAL 1000
    #define SPI_OP (SPI_WORD_SET(8) | SPI_TRANSFER_MSB)
    static const struct spi_dt_spec spi_dev = SPI_DT_SPEC_GET(DT_NODELABEL(bme280), SPI_OP, 0);
    
    
    struct bt_conn *my_conn = NULL;
    static struct bt_gatt_exchange_params exchange_params;
    
    
    static void exchange_func(struct bt_conn *conn, uint8_t att_err,
    			  struct bt_gatt_exchange_params *params);
    
    
    /* STEP 7.1 - Define the function to update the connection's PHY */
    static void update_phy(struct bt_conn *conn)
    {
    	int err;
    	const struct bt_conn_le_phy_param preferred_phy = {
    		.options = BT_CONN_LE_PHY_OPT_NONE,
    		.pref_rx_phy = BT_GAP_LE_PHY_2M,
    		.pref_tx_phy = BT_GAP_LE_PHY_2M,
    	};
    	err = bt_conn_le_phy_update(conn, &preferred_phy);
    	if (err) {
    		printk("bt_conn_le_phy_update() returned %d", err);
    	}
    }
    
    
    /* STEP 10 - Define the function to update the connection's data length */
    static void update_data_length(struct bt_conn *conn)
    {
    	int err;
    	struct bt_conn_le_data_len_param my_data_len = {
    		.tx_max_len = BT_GAP_DATA_LEN_MAX,
    		.tx_max_time = BT_GAP_DATA_TIME_MAX,
    	};
    	err = bt_conn_le_data_len_update(my_conn, &my_data_len);
    	if (err) {
    		printk("data_len_update failed (err %d)", err);
    	}
    }
    
    static void update_mtu(struct bt_conn *conn)
    {
    	int err;
    	exchange_params.func = exchange_func;
    
    	err = bt_gatt_exchange_mtu(conn, &exchange_params);
    	if (err) {
    		printk("bt_gatt_exchange_mtu failed (err %d)", err);
    	}
    }
    
    
    // Convert ASCII to decimal and subtract 0x30 if needed
    /*void convert_ascii_to_decimal(uint8_t *data, size_t len) {
        for (size_t i = 0; i < len; i++) {
            if (data[i] >= '0' && data[i] <= '9') {
                data[i] -= 0x30;
            }
        }
    }*/
    
    
    #define MATRIX_ROWS 8
    #define MATRIX_COLS 32
    #define BYTES_PER_MATRIX (MATRIX_ROWS * MATRIX_COLS / 8)  // 32 bytes per 8x32 matrix
    #define TOTAL_BYTES (2 * BYTES_PER_MATRIX) // 64 bytes total for two matrices (16x32 grid)
    
    uint8_t spi_data[TOTAL_BYTES + 4];  // 64 bytes + 2 dummy bytes at start and end
    
    // Function to mirror an 8-bit byte
    uint8_t mirror_byte(uint8_t byte) {
        uint8_t mirrored = 0;
        for (int i = 0; i < 8; i++) {
            mirrored |= ((byte >> i) & 1) << (7 - i);
        }
        return mirrored;
    }
    
    // Function to process and convert the entire 16x32 matrix (2 cascaded 8x32 matrices)
    // Helper function to process a single 8x32 matrix
    void process_8x32_matrix(const uint8_t *matrix_data, uint8_t *output_data, int *output_index, int matrix_offset) {
        for (int col = 4; col > 0; col--) {  // Process columns from 4 to 1
            for (int row = MATRIX_ROWS - 1; row >= 0; row--) {  // Bottom to top in each column
                
                // Calculate bit index for the current cell
                int bit_index = matrix_offset + row * MATRIX_COLS + (col * 8 - 1);
                uint8_t byte = matrix_data[bit_index / 8];
                
                // Mirror the byte and store in output
                uint8_t mirrored_byte = mirror_byte(byte);
                output_data[(*output_index)++] = mirrored_byte;
            }
        }
    }
    
    // Main function to process the entire 16x32 matrix
    void process_16x32_matrix(const uint8_t *matrix_data, uint8_t *output_data) {
        int output_index = 0;
    
        // Process the first 8x32 matrix (matrix_offset = 0)
        process_8x32_matrix(matrix_data, output_data, &output_index, 0);
    
        // Process the second 8x32 matrix (matrix_offset = MATRIX_ROWS * MATRIX_COLS)
        process_8x32_matrix(matrix_data, output_data, &output_index, MATRIX_ROWS * MATRIX_COLS);
    }
    
    // Main processing function
    void process_received_data(const uint8_t *received_data, uint16_t len) {
        if (len != TOTAL_BYTES) {
            printk("Error: Incorrect data length. Expected 64 bytes.\n");
            return;
        }
    
        // Set dummy start bytes
        spi_data[0] = 0x00;
        spi_data[1] = 0x00;
    
        // Process the entire 16x32 matrix into spi_data
        process_16x32_matrix(received_data, &spi_data[2]);
    
        // Set dummy end bytes
        spi_data[TOTAL_BYTES + 2] = 0x00;
        spi_data[TOTAL_BYTES + 3] = 0x00;
    
        // Print the processed SPI data for verification
        printk("Processed SPI Data:\n");
        for (int i = 0; i < TOTAL_BYTES + 4; i++) {
            printk("%02X ", spi_data[i]);
        }
        printk("\n");
    
        // Transmit over SPI (replace with actual SPI transmission function)
         spi_send_data(spi_data, TOTAL_BYTES + 4);
    }
    
    // SPI send function: Sends data to the MCU via SPI and prints it in hex and decimal
    int spi_send_data(const uint8_t *data, size_t len) {
        int err;
        struct spi_buf tx_buf = { .buf = (void *)data, .len = len };
        struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 };
    
        
    
        
      
         
        err = spi_write_dt(&spi_dev, &tx);
        if (err < 0) {
            printk("spi_write_dt() failed, err %d\n", err);
            return err;
        }
        
         
        printk("SPI data sent successfully\n");
        return 0;
    }
    
    // BLE receive callback: Processes data received from BLE and sends it over SPI
    static void ble_receive_cb(struct bt_conn *conn, const uint8_t *data, uint16_t len) {
        printk("Received BLE data (length=%d): ", len);
        for (int i = 0; i < len; i++) {
            printk("%02X ", data[i]);
        }
        printk("\n");
         
    
        uint8_t converted_data[len];
        memcpy(converted_data, data, len);
       // interpret_ble_data_as_matrix(converted_data,len);
        //convert_ascii_to_decimal(converted_data, len);
        // process_received_data(converted_data, len);
       spi_send_data(converted_data, len);
    }
    
    static const struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
        BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data sd[] = {
        BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    
    static void on_connected(struct bt_conn *conn, uint8_t err) {
        if (err) {
            printk("Connection failed (err %u)\n", err);
            return;
        }
        printk("Connected\n");
        my_conn = bt_conn_ref(conn);
    	dk_set_led(CONNECTION_STATUS_LED, 1);
    
        update_phy(my_conn);
    	/* STEP 13.5 - Update the data length and MTU */
    	update_data_length(my_conn);
    	update_mtu(my_conn);
    }
    
    static void on_disconnected(struct bt_conn *conn, uint8_t reason) {
        printk("Disconnected (reason %u)\n", reason);
        dk_set_led(CONNECTION_STATUS_LED, 0);
    	bt_conn_unref(my_conn);
    }
    
    /* STEP 8.1 - Write a callback function to inform about updates in the PHY */
    void on_le_phy_updated(struct bt_conn *conn, struct bt_conn_le_phy_info *param)
    {
    	// PHY Updated
    	if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_1M) {
    		printk("PHY updated. New PHY: 1M");
    	} else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_2M) {
    		printk("PHY updated. New PHY: 2M");
    	} else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_CODED_S8) {
    		printk("PHY updated. New PHY: Long Range");
    	}
    }
    /* STEP 13.1 - Write a callback function to inform about updates in data length */
    void on_le_data_len_updated(struct bt_conn *conn, struct bt_conn_le_data_len_info *info)
    {
    	uint16_t tx_len = info->tx_max_len;
    	uint16_t tx_time = info->tx_max_time;
    	uint16_t rx_len = info->rx_max_len;
    	uint16_t rx_time = info->rx_max_time;
    	printk("Data length updated. Length %d/%d bytes, time %d/%d us", tx_len, rx_len, tx_time,
    		rx_time);
    }
    struct bt_conn_cb connection_callbacks = {
        .connected = on_connected,
        .disconnected = on_disconnected,
        /* STEP 8.3 - Add the callback for PHY mode updates */
    	.le_phy_updated = on_le_phy_updated,
    	/* STEP 13.2 - Add the callback for data length updates */
    	.le_data_len_updated = on_le_data_len_updated,
    };
    /* STEP 13.3 - Implement callback function for MTU exchange */
    static void exchange_func(struct bt_conn *conn, uint8_t att_err,
    			  struct bt_gatt_exchange_params *params)
    {
    	printk("MTU exchange %s", att_err == 0 ? "successful" : "failed");
    	if (!att_err) {
    		uint16_t payload_mtu =
    			bt_gatt_get_mtu(conn) - 3; // 3 bytes used for Attribute headers.
    		printk("New MTU: %d bytes", payload_mtu);
    	}
    }
    
    static struct bt_nus_cb nus_cb = {
        .received = ble_receive_cb,
    };
    
    int main(void) {
        int err;
    	int blink_status = 0;
        printk("Starting BLE and SPI communication\n");
    
        err = spi_is_ready_dt(&spi_dev);
        if (!err) {
            printk("Error: SPI device is not ready, err: %d", err);
            return 0;
        }
    
        err = bt_enable(NULL);
        if (err) {
            printk("Bluetooth init failed (err %d)\n", err);
            return -1;
        }
        bt_conn_cb_register(&connection_callbacks);
       
    
        printk("Bluetooth initialized\n");
        err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err) {
            printk("Advertising failed to start (err %d)\n", err);
            return -1;
        }
    
        printk("Advertising successfully started\n");
        
        
        
         err = bt_nus_init(&nus_cb);
        if (err) {
            printk("Failed to initialize UART service (err: %d)", err);
            return -1;
        }
        
        
        // while (1) {
        //     k_sleep(K_SECONDS(1));
        // }
       while (1) {
            // Example: send data continuously at regular intervals (e.g., every second)
            // Modify this interval as needed for your application
           // spi_send_data(spi_data, TOTAL_BYTES + 4);
    
            dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
            k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
        }
    }
    *******************nov5***************************************************************************
    
    &i2c0 { status = "disabled";};
    &spi0 { status = "disabled";};
    &i2c1 { status = "disabled";};
     
    &spi1 {
        compatible = "nordic,nrf-spim";
        status = "okay";
        pinctrl-0 = <&spi1_default>;
        pinctrl-1 = <&spi1_sleep>;
        pinctrl-names = "default", "sleep";
        cs-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
        bme280: bme280@0 {
            compatible = "bosch,bme280";
            reg = <0>;
            spi-max-frequency = <125000>;
        };
    };
    &pinctrl {
        spi1_default: spi1_default {
            group1 {
                psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,					 
                        <NRF_PSEL(SPIM_MOSI, 0, 2)>,
                        <NRF_PSEL(SPIM_MISO, 1, 15)>;
            };
        };
        spi1_sleep: spi1_sleep {
            group1 {
                psels = <NRF_PSEL(SPIM_SCK, 0, 28)>,
                        <NRF_PSEL(SPIM_MOSI, 0, 29)>,
                        <NRF_PSEL(SPIM_MISO, 0, 31)>;
                low-power-enable;
            };
        };
    };  this is the working code i want to convert this dongle as slave current iam using dongle as master for sending data to mcu s32k144 now i want to change nrf as slave
    i want to change the nrf config to slave for sending data to mcu s32k144 via SPI  currently iam using as nrf as master KINDLY HELP ME OUT

  • Hi,

    You can refer to this spi-master-slave sample that demonstrate SPI operations. It does both operations in the same application, so I have also modified it to make a simpler sample where master and slave are separate (so you can look at the slave part only): spi_master_slave_separate-main.zip-

Related