上一篇把工具链跑通了,这一篇回到蓝牙本身。 信道探测是建立在这套 BLE 连接之上的,所以先把广播、连接、GATT 这几件事弄明白。
1. BLE 协议栈在 NCS 里长什么样
NCS 的 BLE 协议栈分三层:
┌─────────────────────────────────────┐ │ 应用层(你的 main.c) │ │ GATT 服务、业务逻辑 │ ├─────────────────────────────────────┤ │ Host(Zephyr Bluetooth Host) │ │ GAP / GATT / SMP / L2CAP │ ├─────────────────────────────────────┤ │ Controller(Nordic SoftDevice Ctrl)│ │ Link Layer / Radio / CS 引擎 │ ├─────────────────────────────────────┤ │ 2.4 GHz 射频 + 天线 │ └─────────────────────────────────────┘
信道探测的特殊之处在于:CS 的测量动作发生在 Controller 层,应用层通过 HCI/RAS 拿到结果。所以后面写 CS 应用时,代码量其实不大,难点在于理解 Controller 的配置参数。
【图片1:BLE 协议栈与 CS 的位置】
2. 广播:让别人找到你
BLE 外设通过广播包告诉周围"我在这里、我能连"。NCS 里最常用的是可连接广播:
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, sizeof(DEVICE_NAME) - 1),
};
bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));几个实际会关心的参数:
| 广播间隔 | 两次广播包之间的时间 | 越小越容易被发现,但更费电 |
| 广播类型 | 可连接 / 不可连接 / 定向 | 决定对方能不能发起连接 |
| 广播数据 | 名称、服务 UUID、厂商数据 | 空间只有 31 字节(Legacy) |
信道探测的 Reflector 就是靠广播里的 Ranging Service UUID 被 Initiator 发现的。
3. 连接:建立一条私有通道
连接建立后,双方按"连接间隔"轮流收发。这个参数对功耗和吞吐影响很大:
| 7.5 ms | 高实时性、CS 测量 | 较高 |
| 30–100 ms | 普通传感器上报 | 中等 |
| 500 ms–4 s | 低频采集 | 很低 |
信道探测对时间敏感,默认会使用较短的连接间隔,这也是后面测功耗时要注意的点。
4. GATT 与 NUS:把串口搬到空中
Nordic 的 NUS(Nordic UART Service) 把串口语义映射成两个 GATT 特征值:
RX Characteristic:手机 → 板子
TX Characteristic:板子 → 手机
对调试特别方便:手机装 nRF Connect,连上就能像串口助手一样收发。
4.1 工程结构
code/article02_ble_uart/
CMakeLists.txt prj.conf src/main.c
prj.conf 关键项:
CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_NUS=y CONFIG_BT_DEVICE_NAME="EEPV-CS-UART" CONFIG_BT_L2CAP_TX_MTU=247 CONFIG_BT_BUF_ACL_RX_SIZE=251 CONFIG_BT_BUF_ACL_TX_SIZE=251 CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_CTLR_PHY_2M=y CONFIG_DK_LIBRARY=y
4.2 核心逻辑
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
return;
}
dk_set_led_on(DK_LED1);
/* 连接后停止广播,节省功耗 */
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
dk_set_led_off(DK_LED1);
/* 断连后重新广播 */
bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
}
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
/* 原样回显,验证链路 */
bt_nus_send(NULL, data, len);
}完整代码见 code/article02_ble_uart/src/main.c。
5. 实测步骤
按上一篇的方法编译:
west build -b nrf54lm20dk/nrf54lm20b/cpuapp code/article02_ble_uart -d build/ble_uart
烧录 zephyr.hex。
手机打开 nRF Connect → 扫描,应看到 EEPV-CS-UART。
连接后,在 NUS 服务里向 RX 写入任意字符串,TX 会回显同样的内容。
板上 LED1 在连接期间常亮,断开后熄灭并恢复广播。
5.1 实测日志
先打开 VCOM1(/dev/tty.usbmodem0010518965833,115200),再复位开发板,可以看到:
*** Booting nRF Connect SDK v3.4.1-b20f8619ba9a *** *** Using Zephyr OS v4.4.2-33fa6a7aac6a *** <inf> bt_sdc_hci_driver: SoftDevice Controller build revision: <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002) <inf> bt_hci_core: HW Variant: nRF54Lx (0x0005) <inf> bt_hci_core: HCI: version 6.3 (0x11) revision 0x3080, manufacturer 0x0059 <inf> bt_hci_core: LMP: version 6.3 (0x11) subver 0x3080 <inf> ble_uart: Advertising as EEPV-CS-UART
用 BLE 扫描确认广播:
$ .venv/bin/python tools/scan_ble.py --filter EEPV 6526E504-F6B2-A4B2-20ED-933CE5300800 RSSI=-42 EEPV-CS-UART
5.2 两个编译坑
BT_LE_ADV_CONN 在 Zephyr 4.4 已被移除,要用 BT_LE_ADV_CONN_FAST_1。
CONFIG_BT_NUS_SECURITY_ENABLED 已不存在,写了会触发 Kconfig 报错并中止编译。
6. 低功耗初探
DK 上有专门的功耗测量排针。用万用表或示波器串一个采样电阻,可以观察:
广播间隔 100 ms 时的平均电流
连接间隔 7.5 ms / 100 ms 的差异
断开连接后的休眠电流
这组数据会成为信道探测功耗对比基线。
7. 小结
BLE 应用 = 广播 + 连接 + GATT 服务
NUS 是最省事的空中串口,适合调试
连接间隔同时决定实时性和功耗,CS 场景会偏向短间隔
下一篇进入正题:信道探测。我们会用两块板,把官方 Initiator / Reflector 示例跑起来,并量化测距精度。
我要赚赏金
