BLE UART bridge

Hello!

I am programming BLE with the nRF52840-DK. [Toolchain Manager: v1.3.0, IDE: Visual Studio Code (VSCode), SDK: ncs v2.6.0, window11 pro]

I am integrating CDC ACM and NUS central. My current goal is to send data received via Bluetooth to a PC (PuTTY) through USB.

I want to continuously receive sensor data via Bluetooth (data rate: about 1.3Mbps).

However, I find it quite challenging. There's an issue where the buffer receiving Bluetooth data gets full.

< The issue of the buffer continuously filling up in the code is caused by the following factors: >

  1. Timing discrepancy between BLE data reception and UART transmission:

- Each time data is received via BLE, the ble_data_received callback is triggered, which adds the data to the buffer (write_buf).

- The UART interrupt is triggered only when the UART is ready, at which point it retrieves the data from the buffer (write_buf) and transmits it.

- The UART interrupt handler is called only after multiple nus_received callbacks have been invoked, leading to a buildup of data in the buffer.

  1. Buffer switching mechanism:

- In the current implementation, buffer switching occurs exclusively within the UART interrupt handler.

- As a result, while data is continuously being received via BLE, UART transmission does not happen quickly enough, leading to the buffer becoming full.

  1. UART FIFO size limitation:

- The FIFO size of the UART hardware may be limited, making it impossible to transmit all the data in a single interrupt. This limitation can prevent the buffer from being effectively cleared, causing it to continuously fill up.



< Key part of the code>

static struct bt_conn *default_conn;
static struct bt_nus_client nus_client;
static struct bt_gatt_exchange_params exchange_params;
static struct bt_le_conn_param *conn_param2 = BT_LE_CONN_PARAM(48, 48, 0, 400);

const struct device *usbd_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
#define RING_BUF_SIZE 8192
static uint8_t buffer1[RING_BUF_SIZE];
static uint8_t buffer2[RING_BUF_SIZE];
static uint8_t temp_buffer[8192];

static struct ring_buf ringbuf1, ringbuf2;
static struct ring_buf *write_buf, *read_buf;
static atomic_t buf_switch_flag = ATOMIC_INIT(0);

int rx_count = 0;


static void ble_data_sent(struct bt_nus_client *nus, uint8_t err, const uint8_t *const data, uint16_t len){
	LOG_INF("ble_data_sent");
}

static uint8_t ble_data_received(struct bt_nus_client *nus, const uint8_t *data, uint16_t len){
    ARG_UNUSED(nus);
    int rb_len;

	//LOG_INF("ble_data_received - len: %d", len);

    rb_len = ring_buf_put(write_buf, data, len);
    if (rb_len < len) {
        LOG_WRN("BLE data buffer full, dropped %d bytes", len - rb_len);
    }

    uart_irq_tx_enable(usbd_dev);

    rx_count++;
    return BT_GATT_ITER_CONTINUE;

}

static void uart_interrupt_handler(const struct device *dev, void *user_data){
    int rb_len, send_len;
	//LOG_INF("uart_interrupt_handler ");

	if (uart_irq_update(dev) && uart_irq_tx_ready(dev)) {
        if (atomic_cas(&buf_switch_flag, 0, 1)) {
            struct ring_buf *temp = write_buf;
            write_buf = read_buf;
            read_buf = temp;
            atomic_clear(&buf_switch_flag);
        }

        rb_len = ring_buf_get(read_buf, temp_buffer, sizeof(temp_buffer));
		//LOG_INF("uart_interrupt_handler - ring_buf_get: %d", rb_len);

        if (rb_len > 0) {
            send_len = uart_fifo_fill(dev, temp_buffer, rb_len);
			//LOG_INF("uart_interrupt_handler - uart_fifo_fill: %d", send_len);

            if (send_len < rb_len) {
                ring_buf_put(read_buf, temp_buffer + send_len, rb_len - send_len);
            }
        } else {
            uart_irq_tx_disable(dev);
        }
    }

}

static int nus_client_init(void){
	int err;
	struct bt_nus_client_init_param init = {
		.cb = {
			.received = ble_data_received,
			.sent = ble_data_sent,
		}
	};
	err = bt_nus_client_init(&nus_client, &init);
	LOG_INF("bt_nus_client_init >>> err:%d", err);
	return err;
}

static int cdc_acm_init(void){
	int err;
	uint32_t baudrate = 0U;
	 uint32_t dtr = 0U;
	if (!device_is_ready(usbd_dev)) {
        LOG_ERR("CDC ACM device not ready");
        return -ENODEV;
    }
	err = usb_enable(NULL);
	LOG_INF("main - usb_enable>> %d", err);
	while (true) {
        uart_line_ctrl_get(usbd_dev, UART_LINE_CTRL_DTR, &dtr);	
        if (dtr) {
			LOG_INF("DTR set");
            break;
        } else {
            k_sleep(K_MSEC(100));
        }
    }
	err = uart_line_ctrl_set(usbd_dev, UART_LINE_CTRL_DCD, 1);
    err = uart_line_ctrl_set(usbd_dev, UART_LINE_CTRL_DSR, 1);
	err = uart_line_ctrl_get(usbd_dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
	err = uart_irq_callback_set(usbd_dev, uart_interrupt_handler);
	return err;
}


int main(void) {
    int main_err;

    ring_buf_init(&ringbuf1, sizeof(buffer1), buffer1);
    ring_buf_init(&ringbuf2, sizeof(buffer2), buffer2);
    write_buf = &ringbuf1;
    read_buf = &ringbuf2;

	/*************************        USBD       *************************/ 
	main_err = cdc_acm_init();
	LOG_INF("main - cdc_acm_init >> %d", main_err);

	/************************* Bluetooth Central *************************/
	bt_conn_cb_register(&conn_callbacks);

    main_err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
	LOG_INF("main - bt_conn_auth_info_cb_register>> %d", main_err);

    main_err = bt_enable(NULL);
	LOG_INF("main - bt_enable>> %d", main_err);

    if (IS_ENABLED(CONFIG_SETTINGS)) {
        settings_load();
    }

    main_err = scan_init();
	LOG_INF("main - scan_init>> %d", main_err);

    main_err = nus_client_init();
	LOG_INF("main - nus_client_init>> %d", main_err);

    main_err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
	LOG_INF("main - bt_scan_start>> %d", main_err);

    return 0;
}


  • This code also has the same issue.

    - below is other version code

    static uint8_t buffer[8192];
    static uint8_t ble_data_received(struct bt_nus_client *nus, const uint8_t *data, uint16_t len){
        ARG_UNUSED(nus);
        uart_irq_tx_enable(usbd_dev);
        int rb_len;
        int send_len;
        rb_len = ring_buf_put(&ringbuf1, data, len);
        if (rb_len < len) {
            LOG_WRN("ble_data_received - %dth  Current RX buffer full, dropped %d bytes", rx_count, len - rb_len);
        }
        if(uart_irq_update(usbd_dev) && uart_irq_is_pending(usbd_dev)){
            if (uart_irq_tx_ready(usbd_dev)){
                rb_len = ring_buf_get(&ringbuf1, buffer, sizeof(buffer));
                if (rb_len != 0) {
                    send_len = uart_fifo_fill(usbd_dev, buffer, rb_len);
                    if (send_len < rb_len) {
                        ring_buf_put(current_rx_buf, buffer + send_len, rb_len - send_len);
                        LOG_WRN("ble_data_received - send_len < rb_len: %d", rb_len - send_len);
                    }
                }
            }
        }
        rx_count++;
        return BT_GATT_ITER_CONTINUE;
    }

  • When I switched from PuTTY to MATLAB, the issue was resolved. I’m not sure why, but I suspect it’s because MATLAB has a higher data rate for receiving data.

Related