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: >
- 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.
- 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.
- 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;
}