文章目录
我们开发的BLE 设备多数都有两点要求:一是低功耗,电池供电需要持续工作数周甚至数个月;二是将BLE peripheral产生的数据快速传送给central,传输数据功耗较高,提高传输速率缩短传输时间也有利于降低平均功耗。我们该如何设置广播参数与连接参数以达到我们要求的功耗呢?该如何设置连接参数与报文长度(PDU / MTU)以尽可能达到最大传输速率呢?
一、如何提高BLE 数据传输速率?
BLE 数据传输相关的服务中有一个比较基础的串口透传服务,本文以nRF5_SDK_17.0.2 中的ble_app_uart 工程为例,展示如何提高BLE 的数据传输速率。
在尝试提高BLE 数据传输速率前,需要先获得两个信息:
- 当前使用的BLE 协议栈支持的理论最大数据吞吐率是多少?
- 如何获知当前的BLE 数据传输速率是多少?
1.1 Nordic BLE 最大数据吞吐率是多少?
对于第一个问题,我们可以从Nordic 协议栈规格说明书中获知,比如使用s132 softdevice 可以参考文档:S132 SoftDevice SoftDevice Specification v7.1,查阅Bluetooth Low Energy data throughput 章节,数据传输速率使用下面的公式:
#define OPCODE_LENGTH 1
#define HANDLE_LENGTH 2
Throughput_bps = num_packets * (ATT_MTU - OPCODE_LENGTH - HANDLE_LENGTH) * 8 / seconds
这里统计的传输数据指的是应用数据,ATT_MTU 减去Attribute protocol PDU Opcode 和Attribute Handle 字段长度,剩下的就是Attribute Value 字段(也即有效的应用数据)。每个字节占8 比特,下表中的传输速率单位是kbps(如果要换算成 KB/s 需要除以8),下表Connection interval 与Connection Event Length 相等:

从上表可知,跟传输速率相关的因素主要有ATT MTU size、Connection interval、Connection Event Length、Communication Mode、LE PHY speed 等。比如ATT MTU size 为23、Connection interval 与Connection Event Length 取7.5 ms、Communication Mode 为Send Notification、LE 1M PHY 的最大速率为24 KB/s;ATT MTU size 为247、Connection interval 与Connection Event Length 取50 ms、Communication Mode 为Send Notification、LE 2M PHY 的最大速率为165.94 KB/s。
1.2 如何获知BLE 当前数据吞吐率?
一般BLE peripheral 作为GATT Server 向BLE central 也即GATT Client 传输数据,想获得BLE 当前的数据吞吐率,一般有三种方式:
- BLE peripheral 端统计单位时间内发送出去的数据量;
- BLE central 端统计单位时间内接收到的数据量;
- BLE sniffer 抓取单位时间内传输的报文中有效数据量。
Nordic 手机端的应用并没有提供显示当前数据吞吐率的功能,我们开发GATT Server 应用再去修改BLE central 代码比较麻烦。BLE sniffer 抓包分析倒是比较方便,wireshark + nRF sniffer 抓包方案容易丢包也没有直接统计数据吞吐率指标,专业的蓝牙分析仪Ellisys Bluetooth Explorer 倒是可以直接统计数据吞吐率,蓝牙分析仪成本太高。因此,本文选择第一种方案,在BLE peripheral 代码中添加统计数据发送量的功能,并通过RTT Log 打印出来。
我们在.\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart 示例工程的基础上添加统计单位时间内数据发送量的代码,Nordic UART Service 我们在博文:如何实现扫码连接BLE 设备的功能? 中已经介绍过了,二者主要的代码逻辑差不多,主要有两点不同:
- ble_app_uart 工程在GAP 阶段作为Advertiser,在函数advertising_init 中初始化广播包内容、广播间隔、广播超时时间等,然后在函数advertising_start 中开始广播;ble_app_uart_c 工程在GAP 阶段作为Scanner 和Initiator,在函数scan_init 中设置扫描过滤条件、注册scan_evt_handler 等,然后在函数scan_start 中开始扫描周围的广播设备;
- ble_app_uart 工程在GATT 阶段作为GATT Server,在函数services_init --> ble_nus_init 中添加NUS service(包括RX Characteristic、TX Characteristic)、注册nus_data_handler 等,其中NUS 为Primary Service 对外提供串口透传服务;ble_app_uart_c 工程在GATT 阶段作为GATT Client,在函数db_discovery_init 和nus_c_init 中发现对端设备提供了哪些services(特别是NUS 服务)、注册db_disc_handler 和ble_nus_c_evt_handler 等,发现NUS 服务后就可以访问该服务了。
本文就不展开介绍ble_app_uart 工程代码逻辑了,我们重点关心的是GATT Server 如何使用NUS 服务向GATT Client 发送数据。从函数uart_event_handle 代码可以看出,使用函数ble_nus_data_send 可以通过NUS 服务向对端设备发送数据,该函数的声明如下:
// .\nRF5_SDK_17.0.2_d674dde\components\ble\ble_services\ble_nus\ble_nus.h
/**@brief Function for sending a data to the peer.
*
* @details This function sends the input string as an RX characteristic notification to the
* peer.
*
* @param[in] p_nus Pointer to the Nordic UART Service structure.
* @param[in] p_data String to be sent.
* @param[in,out] p_length Pointer Length of the string. Amount of sent bytes.
* @param[in] conn_handle Connection Handle of the destination client.
*
* @retval NRF_SUCCESS If the string was sent successfully. Otherwise, an error code is returned.
*/
uint32_t ble_nus_data_send(ble_nus_t * p_nus,
uint8_t * p_data,
uint16_t * p_length,
uint16_t conn_handle);
既然是统计单位时间内GATT Server 发送出去的数据量,自然需要一个定时器资源,我们选用低功耗的app_timer。为了提高数据发送速率,我们选择Send Notification 模式。为了少做无用功,我们在NUS Notification enable 的情况下再开始发送数据,当连接断开后便停止发送数据。新增用于统计BLE data throughput 的代码如下:
// .\nRF5_SDK_17.0.2_d674dde\examples\ble_peripheral\ble_app_uart\main.c
/**@brief Resources related to throughput testing.
*/
#define DATA_THROUGHPUT_INTERVAL APP_TIMER_TICKS(5) /**< data throughput interval (ticks). */
APP_TIMER_DEF(m_timer_throughput_id);
uint32_t m_data_sent_length = 0;
uint8_t m_data_array[BLE_NUS_MAX_DATA_LEN] = {
0};
/**@brief Data generation timer timeout handler function.
*/
static void data_throughput_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
static uint32_t timeout_count = 0;
ret_code_t err_code;
timeout_count++;
do
{
uint16_t length = BLE_NUS_MAX_DATA_LEN;
err_code = ble_nus_data_send(&m_nus, m_data_array, &length, m_conn_handle);
if ((err_code != NRF_ERROR_INVALID_STATE) &&
(err_code != NRF_ERROR_RESOURCES) &&
(err_code != NRF_ERROR_NOT_FOUND))
{
APP_ERROR_CHECK(err_code);
}
if(err_code == NRF_SUCCESS)
{
m_data_sent_length += length;
m_data_array[0]++;
m_data_array[length-1]++;
}
} while (err_code == NRF_SUCCESS);
// Timer interval 5 ms, when the timer reaches 1 second
if(timeout_count == 200)
{
// Send m_data_sent_length bytes of data within 1 second, which is equal to m_data_sent_length * 8 / 1024 kilobits of data
NRF_LOG_INFO("****** BLE data throughput: %d kbps ******", m_data_sent_length >> 7);
m_data_sent_length = 0;
timeout_count = 0;
}
}
/**@brief Function for initializing the timer module.
*/
static void timers_init(void)
{
......
// Create a data generation timer for testing throughput.
err_code = app_timer_create(&m_timer_throughput_id,
APP_TIMER_MODE_REPEATED,
data_throughput_timeout_handler);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for handling the data from the Nordic UART Service.
*/
最低0.47元/天 解锁文章
549

被折叠的 条评论
为什么被折叠?



