Bluetooth cannot send data packets within the connection interval.

Our company has previously used the NRF52840, and we know that by modifying the GAP event length (NRF_SDH_BLE_GAP_EVENT_LENGTH), we can enable Bluetooth to transmit multiple data packets within one connection interval. Currently, our NRF5340 is encountering a similar issue: when its transmission speed is greater than the connection interval, it can send normally, but when its transmission speed is less than the connection interval, there will be cases where data packets fail to send (just like packet loss). We want to know what parameters can be configured in NRF5340 with nrf connect v3.1.0 to achieve the effect of GAP event length. We hope you can help us

Parents
  • Hello,

    So when you are saying that you are seeing packets are being failed to send, you mean that you are not able to queue them properly? If a packet is queued, it will be sent, however many retries it uses. This is according to the Bluetooth specification.

    So you should see it in the API that you are using to send packets. At some point, it should stop returning 0, and it will start returning an error value. 

    But the root problem here is that the bluetooth connection doesn't have enough throughput, I guess. You are queuing up packets faster than the device is able to send them, and eventually, you will have packets that are not able to be queued properly.

    I can recommend that you check the sample:

    v3.1.0\nrf\samples\bluetooth\throughput

    Relevant for the nRF5340 are the files:

    boards\nrf5340dk_nrf5340_cpuapp.conf
    prj.conf
    Kconfig.sysbuild
    sysbuild\ipc_radio\prj.conf

    Note that it is in the last file, sysbuild\ipc_radio\prj.conf that the parameters for connection event length and increased MTU is set. All the parameters that belongs to the radio core on the nRF5340.

    This is also described in our DevAcademy Bluetooth Low Energy Fundamentals course. Particularly Lesson 3 is describing how to change the connection parameters.

    Best regards,

    Edvin

  • Thanks bro

    According to your suggestions, our performance has been improved.

    We have set the connection interval to 7.5ms and the transmission timing to 3ms. We found that Bluetooth can send data packets normally within the 7.5ms interval. However, after sending the second data packet, Bluetooth will force the next transmission to wait until the end of the 7.5ms connection interval, which causes blockage on the transmitting end.
    Currently, we have configured the connection event extension with CONFIG_BT_USER_DATA_LEN_UPDATE=y, CONFIG_BT_CTLR_DATA_LENGTH_MAX=251, and the GAP event interval with CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=7500.
    Are we missing any key configurations such as the "Connection event length extension" in nRF52840, like setting opt.common_opt.conn_evt_evt.enable=true; err_code=sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EVT,&opt);? Or are there any existing configurations that need to be enabled in the main function?
  • Right. -12 means ENOMEM (see NCS\zephyr\lib\libc\minimal\include\errno.h)

    So -12 is not actually packet loss. It is packet that are never sent/queued. (packet loss is when a packet is sent, but not received and acked by the receiver. This will result in a retransmission from the transmitter, until it is acked).

    Every 3ms you are generating a new packet of 242 bytes. That means 333 packets of 242 * 8bits = 644kbps. If you are using 1MBPS PHY, the theoretical limit is roughly 700kbps, so you are pushing the limit. 

    To maximize the throughput, you could try a bit longer connection interval, perhaps 30ms, and also set your connection event length to the same. Then keep using long packets (242 is fine), and queue up as many as you can. 

    The issue may be that you need to increase your CONFIG_BT_BUF_ACL_TX_COUNT in prj.conf. I see that by default this is set to 3, meaning you can only have 3 packets in your queue. Try increasing it to 10, or perhaps 20. At least large enough that you have time to transmit the number of packets you intend to send every connection interval. And a little more, to catch up in case of packet loss -> retransmits.

    Also, if you are still not able to transmit the data fast enough, consider changing to 2MBPS PHY. For details on how to do this, please read through Lesson 3 Exercise 2 in DevAcademy's Bluetooth Low Energy Fundamentals course. Particularly steps 7-9.

    Best regards,

    Edvin

  • Thanks bro

    We have already been using the 2MBPS PHY, and the screenshot from the nRF Sniffer can also serve as evidence. We have adjusted CONFIG_BT_BUF_ACL_TX_COUNT to 30, but the experimental results remain consistent with those before (no changes occurred). Could it be that this configuration is not being implemented? Do we need to add some code in the main function to make it take effect?
    In particular, regarding the connection interval: when we increase the connection interval, the number of data packets with the -12 error increases—which corresponds to the increased number of queued data packets you mentioned. The more we extend the interval, the more queued data packets there are, showing a proportional relationship. This has been a point that has confused us.
    Attached are all our configurations and the code for the main function, for your reference. 0020.prj.conf
    /*
     * 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 <string.h>
    
    #include <zephyr/logging/log.h>
    
    #include <zephyr/drivers/gpio.h>
    #include<zephyr/sys/printk.h>
    #include<zephyr/drivers/clock_control.h>
    #include<zephyr/drivers/clock_control/nrf_clock_control.h>
    
    #define LOG_MODULE_NAME peripheral_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #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_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    // #define RX_THREAD_PRIORITY -1  // 高于默认
    // #define RX_THREAD_STACK_SIZE 1024
    // #define RX_QUEUE_MAX_ITEMS 20  // 根据数据率调整队列深度
    // #define RX_DATA_SIZE 251       // 匹配 DLE 最大 PDU 大小(242 字节 + 开销)
    // // 数据缓冲区结构
    // struct rx_data {
    //     uint8_t buffer[RX_DATA_SIZE]; // 存储接收的 BLE 数据
    //     size_t len;                   // 数据长度
    // };
    // K_MSGQ_DEFINE(rx_queue, RX_DATA_SIZE, RX_QUEUE_MAX_ITEMS, 4); // 消息队列,4字节对齐
    // void rx_thread_func(void *p1, void *p2, void *p3) {
    // 	// struct rx_data data;
    //     // while (1) {
    //     //     // 从 bt_recv() 或 UART/BLE 队列读取并处理数据
    //     //     if (k_msgq_get(&rx_queue, &data, K_NO_WAIT) == 0) {
    //     //         // 快速处理或转发数据,避免阻塞
    //     //     }
    //     //     k_yield();  // 让出 CPU
    //     // }
    // }
    // static K_THREAD_DEFINE(rx_thread_id, RX_THREAD_STACK_SIZE, rx_thread_func, NULL, NULL, NULL, RX_THREAD_PRIORITY, 0, 0);
    
    
    
    static uint8_t ble_ADC[250];
    static uint8_t ble_Tx[250];
    //static uint8_t ble_Tx1[250];
    static uint16_t bag=0;
    static uint16_t LASTbag=0;
    //static int8_t counter=0;
    static struct k_timer adc_timer;  // 定时器实例
    static void adc_timer_handler(struct k_timer *timer_id);  // 前向声明
    static struct k_work adc_work ;
    
    #define LED0_NODE DT_ALIAS(led0)
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
    
    static void exchange_func(struct bt_conn *conn, uint8_t att_err, struct bt_gatt_exchange_params *params);
    
    #define BLE_NUS_THROUGHPUT_MAX
    #define BLE_NUS_THROUGHPUT_TEST
    
    #if defined(BLE_NUS_THROUGHPUT_MAX)
    static K_SEM_DEFINE(nus_connection_sem,0,1);
    static const char*phy2str(uint8_t phy)
    {
    	switch (phy)
    	{
    	case 0:return"NO packets";
    		
    	case BT_GAP_LE_PHY_1M:return"LE 1M";
    	case BT_GAP_LE_PHY_2M:return"LE 2M";
    	case BT_GAP_LE_PHY_CODED:return "LE Coded";
    	default: return "Unknown";
    		
    	}
    }
    #endif
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    static struct k_work adv_work;
    
    static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
    static struct k_work_delayable uart_work;
    
    struct uart_data_t {
    	void *fifo_reserved;
    	uint8_t data[UART_BUF_SIZE];
    	uint16_t len;
    };
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    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),
    };
    
    #ifdef CONFIG_UART_ASYNC_ADAPTER
    UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
    #else
    #define async_adapter NULL
    #endif
    
    
    static void ADC_update(void)
    {
        //生成假数据(忽略ADS1299,模拟24字节/采样)
        for (uint8_t i = 0; i < 242; i++) {
            //ble_ADC[counter * 24+ i + 2] = i;  // 假数据:每个通道固定值i(0-23),偏移2字节包头
    		ble_ADC[i+2]=i;
        }
        //counter++;  // 递增计数器
    
        //if (counter == 10) {  // 累积10次(240字节 + 包头/序列号 = 242字节)
            // 复制到发送缓冲
            for (uint16_t i = 0; i < 242; i++) {
                ble_Tx[i] = ble_ADC[i];
            }
            ble_Tx[1] = (uint8_t)bag++;  // 设置序列号(低8位),递增
            if (bag > 0xFF) bag = 0;     // 循环0-255
    
            // 确保连续性(类似THI_update逻辑)
            if (LASTbag != (ble_Tx[1] - 1)) {
                ble_Tx[1] = (uint8_t)(LASTbag + 1);
            }
            LASTbag = ble_Tx[1];
           // 发送通过NUS(Zephyr API)
    		//bt_nus_send(NULL, ble_Tx, 242);
    
    		gpio_pin_toggle_dt(&led);
    	
    	    int err = bt_nus_send(NULL, ble_Tx, 242);
            if (err) {
                printk("Failed to send: %d\n", err);  // 调试:错误如 -ENOMEM (资源不足,重试机制可加)
            } else {
                printk("Sent packet %d (242 bytes)\n", LASTbag);  // 性能监控
             }
    
          // counter = 0;  // 重置计数器
        //}
    
        // 简单延时模拟采样间隔(可选,Zephyr中定时器已控制周期)
       //k_sleep(K_MSEC(1));  // 替换空循环,1ms稳定
    }
    static void adc_timer_handler(struct k_timer *timer_id)
    {
        ARG_UNUSED(timer_id);  // 避免未用警告
        //ADC_update();  // 调用更新函数
    	k_work_submit(&adc_work);
    }
    
    static void adc_work_handler(struct k_work *work)
    {
       ARG_UNUSED(work);
       ADC_update();
    }
    // static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    // {
    // 	ARG_UNUSED(dev);
    
    //    // static uint8_t*current_buf;
    // 	static bool buf_release;
    
    // 	static size_t aborted_len;
    // 	struct uart_data_t *buf;
    // 	static uint8_t *aborted_buf;
    // 	static bool disable_req;
    
    // 	switch (evt->type) {
    // 	case UART_TX_DONE:
    // 		LOG_DBG("UART_TX_DONE");
    // 		if ((evt->data.tx.len == 0) ||
    // 		    (!evt->data.tx.buf)) {
    // 			return;
    // 		}
    
    // 		if (aborted_buf) {
    // 			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    // 					   data[0]);
    // 			aborted_buf = NULL;
    // 			aborted_len = 0;
    // 		} else {
    // 			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    // 					   data[0]);
    // 		}
    
    // 		k_free(buf);
    
    // 		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    // 		if (!buf) {
    // 			return;
    // 		}
    
    // 		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
    // 			LOG_WRN("Failed to send data over UART");
    // 		}
    
    // 		break;
    
    // 	case UART_RX_RDY:
    // 		LOG_DBG("UART_RX_RDY");
    // 		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]);
    // 		buf->len += evt->data.rx.len;
    
    // 		buf_release=false;
    
    // 		if (disable_req) {
    // 			return;
    // 		}
    
    // 		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    // 		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
    // 			disable_req = true;
    // 			uart_rx_disable(uart);
    // 		}
    
    // 		break;
    
    // 	case UART_RX_DISABLED:
    // 		LOG_DBG("UART_RX_DISABLED");
    // 		disable_req = false;
    
    // 		buf = k_malloc(sizeof(*buf));
    // 		if (buf) {
    // 			buf->len = 0;
    // 		} else {
    // 			LOG_WRN("Not able to allocate UART receive buffer");
    // 			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    // 			return;
    // 		}
    
    // 		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    // 			       UART_WAIT_FOR_RX);
    
    // 		break;
    
    // 	case UART_RX_BUF_REQUEST:
    // 		LOG_DBG("UART_RX_BUF_REQUEST");
    // 		buf = k_malloc(sizeof(*buf));
    // 		if (buf) {
    // 			buf->len = 0;
    // 			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    // 		} else {
    // 			LOG_WRN("Not able to allocate UART receive buffer");
    // 		}
    
    // 		break;
    
    // 	case UART_RX_BUF_RELEASED:
    // 		LOG_DBG("UART_RX_BUF_RELEASED");
    // 		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    // 				   data[0]);
    
    // 		if (buf->len > 0) {
    // 			k_fifo_put(&fifo_uart_rx_data, buf);
    // 		} else {
    // 			k_free(buf);
    // 		}
    
    // 		break;
    
    // 	case UART_TX_ABORTED:
    // 		LOG_DBG("UART_TX_ABORTED");
    // 		if (!aborted_buf) {
    // 			aborted_buf = (uint8_t *)evt->data.tx.buf;
    // 		}
    
    // 		aborted_len += evt->data.tx.len;
    // 		buf = CONTAINER_OF((void *)aborted_buf, struct uart_data_t,
    // 				   data);
    
    // 		uart_tx(uart, &buf->data[aborted_len],
    // 			buf->len - aborted_len, SYS_FOREVER_MS);
    
    // 		break;
    
    // 	default:
    // 		break;
    // 	}
    // }
    
    
    
    
    // static void uart_work_handler(struct k_work *item)
    // {
    // 	struct uart_data_t *buf;
    
    // 	buf = k_malloc(sizeof(*buf));
    // 	if (buf) {
    // 		buf->len = 0;
    // 	} else {
    // 		LOG_WRN("Not able to allocate UART receive buffer");
    // 		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    // 		return;
    // 	}
    
    // 	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    // }
    
    // static bool uart_test_async_api(const struct device *dev)
    // {
    // 	const struct uart_driver_api *api =
    // 			(const struct uart_driver_api *)dev->api;
    
    // 	return (api->callback_set != NULL);
    // }
    
    // static int uart_init(void)
    // {
    // 	int err;
    // 	int pos;
    // 	struct uart_data_t *rx;
    // 	struct uart_data_t *tx;
    
    // 	if (!device_is_ready(uart)) {
    // 		return -ENODEV;
    // 	}
    
    // 	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    // 		err = usb_enable(NULL);
    // 		if (err && (err != -EALREADY)) {
    // 			LOG_ERR("Failed to enable USB");
    // 			return err;
    // 		}
    // 	}
    
    // 	rx = k_malloc(sizeof(*rx));
    // 	if (rx) {
    // 		rx->len = 0;
    // 	} else {
    // 		return -ENOMEM;
    // 	}
    
    // 	k_work_init_delayable(&uart_work, uart_work_handler);
    
    
    // 	if (IS_ENABLED(CONFIG_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
    // 		/* Implement API adapter */
    // 		uart_async_adapter_init(async_adapter, uart);
    // 		uart = async_adapter;
    // 	}
    
    // 	err = uart_callback_set(uart, uart_cb, NULL);
    // 	if (err) {
    // 		k_free(rx);
    // 		LOG_ERR("Cannot initialize UART callback");
    // 		return err;
    // 	}
    
    // 	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
    // 		LOG_INF("Wait for DTR");
    // 		while (true) {
    // 			uint32_t dtr = 0;
    
    // 			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
    // 			if (dtr) {
    // 				break;
    // 			}
    // 			/* Give CPU resources to low priority threads. */
    // 			k_sleep(K_MSEC(100));
    // 		}
    // 		LOG_INF("DTR set");
    // 		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
    // 		if (err) {
    // 			LOG_WRN("Failed to set DCD, ret code %d", err);
    // 		}
    // 		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
    // 		if (err) {
    // 			LOG_WRN("Failed to set DSR, ret code %d", err);
    // 		}
    // 	}
    
    // 	tx = k_malloc(sizeof(*tx));
    
    // 	if (tx) {
    // 		pos = snprintf(tx->data, sizeof(tx->data),
    // 			       "Starting Nordic UART service sample\r\n");
    
    // 		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    // 			k_free(rx);
    // 			k_free(tx);
    // 			LOG_ERR("snprintf returned %d", pos);
    // 			return -ENOMEM;
    // 		}
    
    // 		tx->len = pos;
    // 	} else {
    // 		k_free(rx);
    // 		return -ENOMEM;
    // 	}
    
    // 	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    // 	if (err) {
    // 		k_free(rx);
    // 		k_free(tx);
    // 		LOG_ERR("Cannot display welcome message (err: %d)", err);
    // 		return err;
    // 	}
    
    // 	err = uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_WAIT_FOR_RX);
    // 	if (err) {
    // 		LOG_ERR("Cannot enable uart reception (err: %d)", err);
    // 		/* Free the rx buffer only because the tx buffer will be handled in the callback */
    // 		k_free(rx);
    // 	}
    
    // 	return err;
    // }
    
    static void adv_work_handler(struct k_work *work)
    {
    	int err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_2, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)", err);
    		return;
    	}
    
    	LOG_INF("Advertising successfully started");
    }
    
    static void advertising_start(void)
    {
    	k_work_submit(&adv_work);
    }
    
    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(conn, &my_data_len);
        if (err) {
            LOG_ERR("data_len_update failed (err %d)", err);
        }
    }
    static struct bt_gatt_exchange_params exchange_params;
    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) {
            LOG_ERR("bt_gatt_exchange_mtu failed (err %d)", err);
        }
    }
    static void exchange_func(struct bt_conn *conn, uint8_t att_err,
    			  struct bt_gatt_exchange_params *params)
    {
    	LOG_INF("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.
            LOG_INF("New MTU: %d bytes", payload_mtu);
        }
    }
    // static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
    // {
    //     if (err) {
    //         printk("MTU exchange failed (err %u)\n", err);
    //     } else {
    //         size_t mtu_size = bt_gatt_get_mtu(conn);
    //         printk("MTU exchange successful, negotiated MTU: %zu\n", mtu_size);
    //         // 期望 mtu_size = 247(如果 central 支持 >= 247)
    //     }
    // }
    
    // // 定义 MTU 交换参数
    // static struct bt_gatt_exchange_params mtu_params = {
    //     .func = mtu_exchange_cb,
    // };
    // static struct bt_conn*current_conn=NULL;
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		LOG_ERR("Connection failed, err 0x%02x %s", err, bt_hci_err_to_str(err));
    		return;
    	}
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Connected %s", addr);
    
    	// 先发起 MTU 交换,并检查返回值
        // int ret = bt_gatt_exchange_mtu(conn, &mtu_params);
        // if (ret) {
        //     printk("MTU exchange request failed (err %d)\n", ret);
        //     if (ret == -EALREADY) {
        //         // 已交换过,直接获取当前 MTU
        //         size_t mtu_size = bt_gatt_get_mtu(conn);
        //         printk("MTU already exchanged, current MTU: %zu\n", mtu_size);
        //     } else {
        //         // 其他错误处理,例如重试或日志
        //         // 可根据需要添加重试逻辑:atomic_clear_bit(conn->flags, BT_CONN_ATT_MTU_EXCHANGED); 然后重试(调试用,非推荐)
        //     }
        // }
        
         struct bt_le_conn_param param={
            .interval_min=0x0006,
    		.interval_max=0x0006,
    		.latency=0x0000,
    		.timeout=0x00C8,
    	 };
    	 bt_conn_le_param_update(conn,&param);
    
    	 struct bt_conn_le_phy_param phy_param ={
            .pref_tx_phy=BT_GAP_LE_PHY_2M,
    		.pref_rx_phy=BT_GAP_LE_PHY_2M,
    		.options=0
    	 };
    	 bt_conn_le_phy_update(conn,&phy_param);
    
    
    	current_conn = bt_conn_ref(conn);
    	if (!current_conn) {
            printk("Failed to reference connection");
            return;
        }
        printk("Connection referenced successfully");
    //    k_work_delayable_init(&param_update_work,param_update_work_handler);
    //    k_work_schedule(&param_update_work,K_MSEC(300));
    	//k_timer_start(&adc_timer,K_MSEC(1000),K_MSEC(1000));
    	dk_set_led_on(CON_STATUS_LED);
    	#if defined(BLE_NUS_THROUGHPUT_MAX)
    	k_sem_give(&nus_connection_sem);
    	#endif
    	//uint16_t mtu;
    	//bt_gatt_exchange_mtu(conn,&mtu_params);
    	//size_t mtu_size=bt_gatt_get_mtu(conn);
    	k_sleep(K_MSEC(1000));  // Delay added to avoid link layer collisions.
        update_data_length(conn);
        update_mtu(conn);
    	//update_phy(conn);
    }
    
    
    // static void param_update_work_handler(struct k_work *work) {
    //     // 请求数据长度扩展
    //     bt_conn_le_data_len_update(current_conn, BT_LE_DATA_LEN_PARAM(BT_LE_DATA_LEN_PARAM_MAX));
    
    //     // 请求 PHY 更新(2Mbps)
    //     bt_conn_le_phy_update(current_conn, BT_CONN_LE_PHY_PARAM_2M);
    
    // }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s, reason 0x%02x %s", addr, reason, bt_hci_err_to_str(reason));
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    	}
    }
    
    // static struct bt_conn_cb conn_callbacks = {
    //        .connected=connected,
    // 	   .disconnected=disconnected,
    // };
    // bt_conn_cb_register(&conn_callbacks);
    
    static void recycled_cb(void)
    {
    	LOG_INF("Connection object available from previous conn. Disconnect is complete!");
    	advertising_start();
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    			     enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", addr, level);
    	} else {
    		LOG_WRN("Security failed: %s level %u err %d %s", addr, level, err,
    			bt_security_err_to_str(err));
    	}
    }
    #endif
    
    #if defined(BLE_NUS_THROUGHPUT_MAX)
    
    static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
    {		
    	LOG_INF("Connection parameters update request received.");
    	LOG_INF("Minimum interval: %d, Maximum interval: %d",
    	       param->interval_min, param->interval_max);
    	LOG_INF("Latency: %d, Timeout: %d", param->latency, param->timeout);
    
    	return true;
    }
    
    static void le_param_updated(struct bt_conn *conn, uint16_t interval,
    			     uint16_t latency, uint16_t timeout)
    {
    	LOG_INF("Connection parameters updated."
    	       " interval: %d, latency: %d, timeout: %d",
    	       interval, latency, timeout);
    }
    
    static void le_phy_updated(struct bt_conn *conn,
    			   struct bt_conn_le_phy_info *param)
    {
    	LOG_INF("LE PHY updated: TX PHY %s, RX PHY %s",
    	       phy2str(param->tx_phy), phy2str(param->rx_phy));
    }
    
    static void le_data_length_updated(struct bt_conn *conn,
    				   struct bt_conn_le_data_len_info *info)
    {
    	LOG_INF("LE data len updated: TX (len: %d time: %d)"
    	       " RX (len: %d time: %d)", info->tx_max_len,
    	       info->tx_max_time, info->rx_max_len, info->rx_max_time);
    
    }
    
    #endif
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected        = connected,
    	.disconnected     = disconnected,
    	.recycled         = recycled_cb,
    
    #if defined(BLE_NUS_THROUGHPUT_MAX)
    .le_param_req = le_param_req,
        .le_param_updated = le_param_updated,
        .le_phy_updated = le_phy_updated,
        .le_data_len_updated = le_data_length_updated,
    #endif
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	.security_changed = security_changed,
    #endif
    };
    
    #if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
    static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    }
    
    static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	auth_conn = bt_conn_ref(conn);
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    
    	if (IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX) || IS_ENABLED(CONFIG_SOC_SERIES_NRF54LX)) {
    		LOG_INF("Press Button 0 to confirm, Button 1 to reject.");
    	} else {
    		LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
    	}
    }
    
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing cancelled: %s", addr);
    }
    
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
    }
    
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing failed conn: %s, reason %d %s", addr, reason,
    		bt_security_err_to_str(reason));
    }
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.passkey_display = auth_passkey_display,
    	.passkey_confirm = auth_passkey_confirm,
    	.cancel = auth_cancel,
    };
    
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    #else
    static struct bt_conn_auth_cb conn_auth_callbacks;
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks;
    #endif
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
    			  uint16_t len)
    {
    	// int err;
    	// char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	// bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	// LOG_INF("Received data from: %s", addr);
    
    	// for (uint16_t pos = 0; pos != len;) {
    	// 	struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    	// 	if (!tx) {
    	// 		LOG_WRN("Not able to allocate UART send data buffer");
    	// 		return;
    	// 	}
    
    	// 	/* Keep the last byte of TX buffer for potential LF char. */
    	// 	size_t tx_data_size = sizeof(tx->data) - 1;
    
    	// 	if ((len - pos) > tx_data_size) {
    	// 		tx->len = tx_data_size;
    	// 	} else {
    	// 		tx->len = (len - pos);
    	// 	}
    
    	// 	memcpy(tx->data, &data[pos], tx->len);
    
    	// 	pos += tx->len;
    
    	// 	/* Append the LF character when the CR character triggered
    	// 	 * transmission from the peer.
    	// 	 */
    	// 	if ((pos == len) && (data[len - 1] == '\r')) {
    	// 		tx->data[tx->len] = '\n';
    	// 		tx->len++;
    	// 	}
    
    	// 	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    	// 	if (err) {
    	// 		k_fifo_put(&fifo_uart_tx_data, tx);
    	// 	}
    	// }
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    void error(void)
    {
    	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);
    
    	while (true) {
    		/* Spin for ever */
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept) {
    		bt_conn_auth_passkey_confirm(auth_conn);
    		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
    	} else {
    		bt_conn_auth_cancel(auth_conn);
    		LOG_INF("Numeric Reject, conn %p", (void *)auth_conn);
    	}
    
    	bt_conn_unref(auth_conn);
    	auth_conn = NULL;
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	uint32_t buttons = button_state & has_changed;
    
    	if (auth_conn) {
    		if (buttons & KEY_PASSKEY_ACCEPT) {
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT) {
    			num_comp_reply(false);
    		}
    	}
    }
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    static void configure_gpio(void)
    {
    	int err;
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		LOG_ERR("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    int main(void)
    {
    	int blink_status = 0;
    	int err = 0;
    
    	configure_gpio();
    
    	// err = uart_init();
    	// if (err) {
    	// 	error();
    	// }
    
    	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
    		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    		if (err) {
    			LOG_ERR("Failed to register authorization callbacks. (err: %d)", err);
    			return 0;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err) {
    			LOG_ERR("Failed to register authorization info callbacks. (err: %d)", err);
    			return 0;
    		}
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		error();
    	}
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	err = bt_nus_init(&nus_cb);
    	if (err) {
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return 0;
    	}
    
    	k_work_init(&adv_work, adv_work_handler);
    
        k_work_init(&adc_work,adc_work_handler);
        k_timer_init(&adc_timer,adc_timer_handler,NULL);
    	k_timer_start(&adc_timer,K_MSEC(3),K_MSEC(3));
    
    	advertising_start();
         
    	int ret;
    	  //bool led_state = true;
       if (!gpio_is_ready_dt(&led)) {
    		return 0;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 0;
    	}
    	
        
        ble_ADC[0]=0x64;
        
        //int ble_Tx1[250];
    	// for(uint16_t i=0;i<242;i++)
        //    ble_Tx1[i]=i;
        // ble_Tx1[1]=(uint16_t)bag++;
    	// if (bag>0xFF) bag=0;
    	//k_sleep(K_MSEC(1000));  // Delay added to avoid link layer collisions.
        
    
    
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		//bt_nus_send(NULL, ble_Tx, 8);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    
    
    
    void ble_write_thread(void)
    {
    	/* Don't go any further until BLE is initialized */
    	k_sem_take(&ble_init_ok, K_FOREVER);
    	struct uart_data_t nus_data = {
    		.len = 0,
    	};
    
    	for (;;) {
    		/* Wait indefinitely for data to be sent over bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    						     K_FOREVER);
    
    		int plen = MIN(sizeof(nus_data.data) - nus_data.len, buf->len);
    		int loc = 0;
    
    		while (plen > 0) {
    			memcpy(&nus_data.data[nus_data.len], &buf->data[loc], plen);
    			nus_data.len += plen;
    			loc += plen;
    
    			if (nus_data.len >= sizeof(nus_data.data) ||
    			   (nus_data.data[nus_data.len - 1] == '\n') ||
    			   (nus_data.data[nus_data.len - 1] == '\r')) {
    				if (bt_nus_send(NULL, nus_data.data, nus_data.len)) {
    					LOG_WRN("Failed to send data over BLE connection");
    				}
    				nus_data.len = 0;
    			}
    
    			plen = MIN(sizeof(nus_data.data), buf->len - loc);
    		}
    
    		k_free(buf);
    	}
    }
    
    // K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
    // 		NULL, PRIORITY, 0, 0);
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
  • Sorry, I forgot this was the nRF5340. You need to add this config to the sysbuild\ipc_radio\prj.conf. Is that the one that you sent me?

    Will I be able to reproduce the behavior that you are seeing if you zip the applications (peripheral and central)? Do I need any external HW, other than a couple of DKs?

    Best regards,

    Edvin

  • Thanks bro

    Sorry, I forgot to send you the prj.conf for ipc_radio. I might be missing key configurations in the prj.conf of ipc_radio, so could you please help me check it? I will send it to you right away. You don't need any other hardware; a central device and peripheral devices are sufficient.81441.prj.conf

  • I have checked the prj.conf of my ipc_radio, and all the key configurations from the file at v3.1.0\nrf\samples\bluetooth\throughput\sysbuild\ipc_radio\prj.conf have been included in mine.
    Interestingly, when my timer K_MSEC() is set to 4 and the connection interval is set to 7.5ms, data packets do not queue (-12), but the rate is 3.8ms, which does not meet our target of 3ms. When K_MSEC() is set to 4 and the connection interval is 15ms, data packets start to queue (approximately 2 out of every 5 packets are queued). When K_MSEC() is set to 3 and the connection interval is 7.5ms, data packets also queue (roughly 1 out of every 3 packets is queued). If K_MSEC() is set to 3 and the connection interval is 15ms, even more data packets will queue.
    This seems very much like a blockage at the TX (transmit) end. Regrettably, however, adjusting the value of CONFIG_BT_BUF_ACL_TX_COUNT does not result in any observable changes.
Reply
  • I have checked the prj.conf of my ipc_radio, and all the key configurations from the file at v3.1.0\nrf\samples\bluetooth\throughput\sysbuild\ipc_radio\prj.conf have been included in mine.
    Interestingly, when my timer K_MSEC() is set to 4 and the connection interval is set to 7.5ms, data packets do not queue (-12), but the rate is 3.8ms, which does not meet our target of 3ms. When K_MSEC() is set to 4 and the connection interval is 15ms, data packets start to queue (approximately 2 out of every 5 packets are queued). When K_MSEC() is set to 3 and the connection interval is 7.5ms, data packets also queue (roughly 1 out of every 3 packets is queued). If K_MSEC() is set to 3 and the connection interval is 15ms, even more data packets will queue.
    This seems very much like a blockage at the TX (transmit) end. Regrettably, however, adjusting the value of CONFIG_BT_BUF_ACL_TX_COUNT does not result in any observable changes.
Children
Related