#include <zephyr/logging/log.h> #include <zephyr/drivers/uart.h> #include <include/BLEAdvScanner.h> #include <bluetooth/scan.h> #include <zephyr/sys/ring_buffer.h> const struct device *uart= DEVICE_DT_GET(DT_NODELABEL(uart0)); K_THREAD_STACK_DEFINE(scan_thread_stack, 2048); struct k_thread scan_thread_data; K_THREAD_STACK_DEFINE(transceiver_thread_stack, 2048); struct k_thread transceiver_thread_data; LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG); static uint32_t g_pktcount=0; // UART RX primary buffers uint8_t uart_double_buffer[2][UART_BUF_SIZE]; uint8_t *uart_buf_next = uart_double_buffer[1]; K_SEM_DEFINE(tx_done, 1, 1); K_SEM_DEFINE(rx_disabled, 0, 1); static uint8_t rx_buf[128]; // Adjust buffer size if needed static uint8_t rx_index = 0; static uint8_t size_expected = 0; // UART TX fifo RING_BUF_DECLARE(app_tx_fifo, UART_TX_BUF_SIZE); volatile int bytes_claimed; // SCAN message queue K_MSGQ_DEFINE(scan_msgq, sizeof(scan_msg_queue_item), SCAN_MSG_QUEUE_SIZE, 8); // UART RX message queue K_MSGQ_DEFINE(uart_rx_msgq, sizeof(uart_msg_queue_item), UART_RX_MSG_QUEUE_SIZE, 4); // Callback function called by BLE driver upom receiving a packet during Scanning // Keep this function short, like an ISR static void ble_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,struct net_buf_simple *ad) { static scan_msg_queue_item new_message; new_message.rxdpktindex = g_pktcount++; new_message.rssi = rssi; new_message.rxtime = k_uptime_get_32(); new_message.advdata_len = ad->len; bt_addr_le_to_str(addr,new_message.advr_addr, sizeof(new_message.advr_addr)); memcpy(&new_message.advdata, ad->data, ad->len); if (k_msgq_put(&scan_msgq, &new_message, K_NO_WAIT) != 0) { LOG_ERR("Error: SCAN message queue full!\n"); } } // Callback function called by BLE driver upom receiving a packet during Scanning // Keep this function short, like an ISR static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable) { static scan_msg_queue_item new_message; new_message.rxdpktindex = g_pktcount++; new_message.rssi = device_info->recv_info->rssi; new_message.rxtime = k_uptime_get_32(); new_message.advdata_len = device_info->adv_data->len; bt_addr_le_to_str(device_info->recv_info->addr,new_message.advr_addr, sizeof(new_message.advr_addr)); memcpy(&new_message.advdata, device_info->adv_data->data, device_info->adv_data->len); if (k_msgq_put(&scan_msgq, &new_message, K_NO_WAIT) != 0) { LOG_ERR("Error: SCAN message queue full!\n"); } } BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, NULL, NULL); static int scan_init(void) { int err; struct bt_le_scan_param scan_param = { .type = BT_LE_SCAN_TYPE_PASSIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = SCAN_INTERVAL_TICKS, .window = SCAN_WINDOW_TICKS, }; struct bt_scan_init_param scan_init = { .scan_param = &scan_param, }; bt_addr_t addr ={ { 0x11, 0x93, 0xD3, 0x34, 0x22, 0x77 }}; bt_addr_le_t filter_addr = { .a = addr, .type = BT_ADDR_LE_RANDOM, }; struct bt_uuid_16 uuid_to_filter = BT_UUID_INIT_16(0x1234); bt_scan_init(&scan_init); bt_scan_cb_register(&scan_cb); err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_ADDR, &filter_addr); err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, &uuid_to_filter); if (err) { LOG_ERR("Scanning filters cannot be set (err %d)", err); return err; } err = bt_scan_filter_enable(BT_SCAN_ADDR_FILTER | BT_SCAN_UUID_FILTER, false); if (err) { LOG_ERR("Filters cannot be turned on (err %d)", err); return err; } LOG_INF("Scan module initialized"); return err; } static int uart_tx_get_from_queue(void) { uint8_t *data_ptr; // Try to claim any available bytes in the FIFO bytes_claimed = ring_buf_get_claim(&app_tx_fifo, &data_ptr, UART_TX_BUF_SIZE); if(bytes_claimed > 0) { // Start a UART transmission based on the number of available bytes uart_tx(uart, data_ptr, bytes_claimed, SYS_FOREVER_MS); } return bytes_claimed; } void receive_complete_message(uart_msg_queue_item new_message) { if (new_message.length > 0) { if (size_expected > 0) { for (int i=0; i<new_message.length; i++) { if (new_message.bytes[i] != '\n' && new_message.bytes[i] != '\r') { rx_buf[rx_index] = new_message.bytes[i]; rx_index++; } } LOG_INF("size expected = %u",size_expected); LOG_INF("rx_index = %u",rx_index); if (size_expected == rx_index ){ rx_buf[rx_index] = 0; rx_index = 0; size_expected =0; LOG_INF("received complete message , %s", rx_buf); } } else { rx_index =0; for (int i=4;i<new_message.length; i++) { if (new_message.bytes[i] != '\n' && new_message.bytes[i] != '\r') { rx_buf[rx_index] = new_message.bytes[i]; rx_index++; } } for (int i = 0; i < 4; i++) { int value; if (new_message.bytes[i] >= '0' && new_message.bytes[i] <= '9') { value = new_message.bytes[i] - '0'; } else if (new_message.bytes[i] >= 'A' && new_message.bytes[i] <= 'F') { value = 10 + new_message.bytes[i] - 'A'; } else if (new_message.bytes[i] >= 'a' && new_message.bytes[i] <= 'f') { value = 10 + new_message.bytes[i] - 'a'; } else { printf("Error: Invalid hex digit at position %d.\n", i); return -1; // Indicate error } size_expected += value * (1 << (4 * i)); // Multiply by 16^i } //size_expected += new_message.bytes[0] - '0'; LOG_INF("size expected = %u", size_expected); if (size_expected == rx_index ) { rx_buf[rx_index] = 0; LOG_INF("received complete message , %s", rx_buf); size_expected =0; rx_index =0; } } } } void app_uart_async_callback(const struct device *uart_dev, struct uart_event *evt, void *user_data) { static uart_msg_queue_item new_message; switch (evt->type) { case UART_TX_DONE: // Free up the written bytes in the TX FIFO ring_buf_get_finish(&app_tx_fifo, bytes_claimed); // If there is more data in the TX fifo, start the transmission if(uart_tx_get_from_queue() == 0) { // Or release the semaphore if the TX fifo is empty k_sem_give(&tx_done); } break; case UART_RX_RDY: memcpy(new_message.bytes, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len); new_message.length = evt->data.rx.len; receive_complete_message(new_message); break; case UART_RX_BUF_REQUEST: uart_rx_buf_rsp(uart, uart_buf_next, UART_BUF_SIZE); break; case UART_RX_BUF_RELEASED: uart_buf_next = evt->data.rx_buf.buf; break; case UART_RX_DISABLED: k_sem_give(&rx_disabled); break; default: break; } } static int tx_message_over_uart(const uint8_t * data_ptr, uint32_t data_len) { while(1) { // Try to move the data into the TX ring buffer uint32_t written_to_buf = ring_buf_put(&app_tx_fifo, data_ptr, data_len); data_len -= written_to_buf; // In case the UART TX is idle, start transmission if(k_sem_take(&tx_done, K_NO_WAIT) == 0) { uart_tx_get_from_queue(); } // In case all the data was written, exit the loop if(data_len == 0) break; // In case some data is still to be written, sleep for some time and run the loop one more time k_msleep(10); data_ptr += written_to_buf; } return 0; } int main(void) { int err; err = bt_enable(NULL); if (err) { LOG_ERR("Bluetooth init failed (err %d)", err); return 0; } LOG_DBG("Bluetooth Initiated!!"); if (!device_is_ready(uart)){ LOG_ERR("UART device not ready!!"); } uart_callback_set(uart, app_uart_async_callback, NULL); uart_rx_enable(uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS); LOG_DBG("UART Initiated!!"); #ifdef CONFIG_SCANFILTER err = scan_init(); if (err != 0) { LOG_ERR("scan_init failed (err %d)", err); return 0; } err = bt_scan_start(BT_SCAN_TYPE_SCAN_PASSIVE); if (err) { LOG_ERR("Start scanning failed (err %d)", err); return; } #else struct bt_le_scan_param scan_param = { .type = BT_LE_SCAN_TYPE_PASSIVE, .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval = SCAN_INTERVAL_TICKS, .window = SCAN_WINDOW_TICKS, }; err = bt_le_scan_start(&scan_param, ble_scan_cb); if (err) { LOG_ERR("Start scanning failed (err %d)", err); return; } #endif scan_msg_queue_item incoming_message; while (1) { k_msgq_get(&scan_msgq, &incoming_message, K_FOREVER); // Process the message here. static uint8_t addr_buffer[BT_ADDR_LE_STR_LEN + 1], data_buffer[MAX_ADVDATA_LEN+1]; static uint8_t uart_tx_data[150]; memcpy(addr_buffer, incoming_message.advr_addr, strlen(incoming_message.advr_addr)); memcpy(data_buffer, incoming_message.advdata, incoming_message.advdata_len); memset(uart_tx_data, 0, 150); snprintf(uart_tx_data, sizeof(uart_tx_data), "RX %i: %i: %i: %s: %s\r\n", incoming_message.rxdpktindex, incoming_message.rssi, incoming_message.rxtime, addr_buffer, data_buffer); tx_message_over_uart(uart_tx_data, strlen(uart_tx_data)); } return 0; }
I have this code developed for nRF5340.
Basically it scans for BLE advertisements based on the filters added and transmits the packets through UART.
Now for certain reasons, I wanted to go with nRF52840 and i am doing a study to check if all these features can be supported in lower version SoC?
Questions:
1) Does nRF52840 supports all filter types(whitelist) that has been supported by 5340 ?
2) How efficient is nRF52840 wrt BLE Scanning ? Do we have performance metrics for BLE on this SoC?
3) I have used nRF Connect SDK for 5340 development ? Is that SDK support available for 52840 ? I think it does have zephyr RTOS support ! will there be any limitation ?
4) Is there any performance benchmark captured to show comparison between nRF5340 and 52840 ?