Bluetooth Mesh 应用框架
本文档介绍杰理(Jieli)AC63 蓝牙 SoC SDK 中 apps/mesh 应用工程所构成的 Bluetooth Mesh 应用框架:它如何基于 REGISTER_APPLICATION 应用注册机制接入 SDK 的应用核心(app_core),如何与内置的 SIG Mesh 协议栈(apps/common/third_party_profile/sig_mesh)交互,以及如何通过 mesh_config_common.c 的全局配置变量调节节点(Node)、低功耗节点(LPN)、Friend、代理(Proxy)和配网(PB-ADV)等行为。
Purpose and Scope
本页覆盖以下内容:
- Mesh 应用工程的入口与生命周期管理(
apps/mesh/app_mesh.c、apps/mesh/app_main.c); - 事件驱动架构:按键事件、蓝牙连接状态事件、HCI 事件如何路由到 Mesh 应用;
- SIG Mesh 协议栈的统一 API 头文件(
apps/common/third_party_profile/sig_mesh/api/sig_mesh_api.h)所暴露的模型、地址与错误码体系; - Mesh 硬件/协议参数配置(
apps/mesh/api/mesh_config_common.c); - 工程目录结构、板级工程(board)与可扩展的示例(
mesh_dfu、tencent_mesh、audio_codec_demo)。
以下主题属于兄弟页面,不在本页展开:
- Mesh OTA/DFU 固件升级的详细协议(见
apps/mesh/mesh_dfu); - 腾讯 LL Sync Mesh(低功耗广域物联网)接入(见
apps/common/third_party_profile/llsync_mesh与apps/mesh/examples/tencent_mesh.c); - 具体芯片(AC631N/AC632N/AC635N/AC636N/AC637N/AC638N)的板级工程配置(见
apps/mesh/board)。
概述
apps/mesh 是 SDK 内置的 BLE Mesh 演示应用。与 SDK 中其它应用(如经典蓝牙音箱、通话应用)一样,它通过 REGISTER_APPLICATION(app_mesh) 宏注册为一个名为 "mesh" 的应用实例,并挂载 state_machine 与 event_handler 两个回调,从而融入 SDK 统一的应用框架(system/app_core.h)与事件系统(system/includes.h)。
框架的核心设计意图是:把"蓝牙协议栈初始化"、"应用状态迁移"和"系统事件分发"三者解耦。应用只负责声明自己关心的事件类型(按键、蓝牙状态、HCI 状态、设备事件),框架负责在合适的时机调用对应回调;Mesh 协议栈的具体实现则被封装在 sig_mesh_api 之下,应用层通过 mesh_config_common.c 的全局常量即可调整协议行为,无需修改协议栈源码。
Mesh 协议栈本身是一个 Zephyr 风格(net/buf.h、atomic_h.h)的 SIG Mesh 实现,覆盖配网(Provisioning)、模型(Models,如 cfg、health、scene、blob、dfu、rpr 等)、代理(Proxy)、LPN/Friend、私有信标(Private Beacon)与 Remote Provisioning 等能力。
架构
flowchart TD
subgraph sg_App["应用层 apps/mesh"]
AppMain["app_main.c<br/>注册 main 动作"]
AppMesh["app_mesh.c<br/>REGISTER_APPLICATION(app_mesh)"]
AudioDemo["audio_codec_demo.c"]
MeshDfu["mesh_dfu/<br/>Distributor Loader"]
TencentMesh["examples/tencent_mesh.c"]
end
subgraph sg_Core["SDK 应用框架"]
AppCore["app_core / application<br/>state_machine + event_handler"]
SysEvent["系统事件系统<br/>SYS_KEY_EVENT / SYS_BT_EVENT / SYS_DEVICE_EVENT"]
end
subgraph sg_Stack["SIG Mesh 协议栈"]
SigApi["sig_mesh_api.h<br/>access / main / proxy / cfg / health<br/>blob / dfu / rpr / scene ..."]
MeshCfg["api/mesh_config.h"]
MeshLog["adaptation/mesh_log.h"]
end
subgraph sg_Hw["蓝牙控制器与硬件"]
BtStack["btstack / btctrler_task"]
BleModule["ble_module / adv & scan 硬件"]
BtPll["bt_pll_para 时钟配置"]
end
subgraph sg_Cfg["参数配置层"]
MeshCfgCommon["api/mesh_config_common.c<br/>adv / lpn / friend / pb-adv / beacon"]
end
AppMesh -->|"注册 application"| AppCore
AppCore -->|"分发事件"| SysEvent
SysEvent -->|"SYS_BT_EVENT"| AppMesh
AppMesh -->|"调用"| SigApi
SigApi -->|"基于"| MeshCfg
MeshCfgCommon -->|"全局常量供栈读取"| SigApi
SigApi -->|"经 btstack 收发"| BtStack
BtStack --> BleModule
AppMesh -->|"clk_set / bt_pll_para"| BtPll
AppMesh --> AudioDemo
AppMesh --> MeshDfu
AppMesh --> TencentMesh
AppMain -->|"ACTION_AT_MAIN 启动"| AppMesh
架构说明
- 应用层:
app_mesh.c是框架的"心脏",它定义应用生命周期回调,并在BT_STATUS_INIT_OK事件后调用bt_ble_init()初始化 BLE,从而启动 Mesh 协议栈。app_main.c则以it.name = "mesh"; it.action = ACTION_AT_MAIN;把 Mesh 应用声明为主入口动作。 - SDK 应用框架:
REGISTER_APPLICATION将应用注册进app_core,应用的状态迁移(CREATE/START/PAUSE/RESUME/STOP/DESTROY)由框架统一驱动;事件则通过event_handler分发。 - SIG Mesh 协议栈:
sig_mesh_api.h是协议栈的"伞形头文件",聚合了配网、配置客户端/服务端、健康、代理、场景、BLOB/DFU 传输、远程配网(RPR)、私有信标等全部子模块。 - 参数配置层:
mesh_config_common.c通过_WEAK_弱符号定义协议栈可覆盖的默认参数(广播间隔、LPN/Friend 参数、PB-ADV 超时等),应用可在自己的工程里重定义同名符号实现覆盖。 - 硬件层:协议栈的收发最终落到
btstack/btctrler的 BLE 广播与扫描硬件上;app_start()中通过clk_set("sys", BT_NORMAL_HZ)与bt_pll_para(...)完成射频时钟配置。
应用入口与生命周期
应用注册
Mesh 应用通过 REGISTER_APPLICATION(app_mesh) 注册,名称为 "mesh",绑定的动作为 ACTION_AT_MAIN,初始状态为 APP_STA_DESTROY:
REGISTER_APPLICATION(app_mesh) = {
.name = "mesh",
.action = ACTION_AT_MAIN,
.ops = &app_at_ops,
.state = APP_STA_DESTROY,
};
Source: app_mesh.c
其中 app_at_ops 挂载了两个回调:state_machine(状态机)与 event_handler(事件分发):
static const struct application_operation app_at_ops = {
.state_machine = state_machine,
.event_handler = event_handler,
};
Source: app_mesh.c
app_main.c 则在主入口中把该应用声明为 "mesh" 并指定 ACTION_AT_MAIN,使系统启动后直接进入 Mesh 模式。
状态机
state_machine 处理六个应用状态。实际启动逻辑集中在 APP_STA_START 分支:只有收到 ACTION_AT_MAIN 动作时才调用 app_start(),其它状态(CREATE/PAUSE/RESUME/STOP/DESTROY)目前为空实现,为后续扩展预留了位置:
static int state_machine(struct application *app, enum app_state state, struct intent *it)
{
switch (state) {
case APP_STA_CREATE:
break;
case APP_STA_START:
if (!it) {
break;
}
switch (it->action) {
case ACTION_AT_MAIN:
app_start();
break;
}
break;
...
}
return 0;
}
Source: app_mesh.c
启动流程
app_start() 完成三件事:打印 Mesh Demo 标志、配置系统/射频时钟、初始化蓝牙协议栈并打开按键消息使能:
static void app_start()
{
log_info("-------------BLE MESH DEMO-------------");
is_app_active = 1;
clk_set("sys", BT_NORMAL_HZ);
u32 sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_init();
sys_key_event_enable();
}
Source: app_mesh.c
设计意图:bt_pll_para 必须使用与 clk_set 相同的系统时钟参数,否则 BLE 射频频率会偏移;btstack_init() 在此时只是初始化协议栈任务,真正的 BLE/Mesh 初始化要等控制器上报 BT_STATUS_INIT_OK 之后才执行(见下文事件处理)。
事件处理
event_handler 是框架与外部世界之间的唯一入口,按事件类型分发:
SYS_KEY_EVENT→app_key_event_handler();SYS_BT_EVENT且arg == SYS_BT_EVENT_TYPE_CON_STATUS→bt_connction_status_event_handler();SYS_BT_EVENT且arg == SYS_BT_EVENT_TYPE_HCI_STATUS→bt_hci_event_handler();SYS_DEVICE_EVENT→ 直接返回 0(暂不处理)。
static int event_handler(struct application *app, struct sys_event *event)
{
switch (event->type) {
case SYS_KEY_EVENT:
app_key_event_handler(event);
return 0;
case SYS_BT_EVENT:
if ((u32)event->arg == SYS_BT_EVENT_TYPE_CON_STATUS) {
bt_connction_status_event_handler(&event->u.bt);
} else if ((u32)event->arg == SYS_BT_EVENT_TYPE_HCI_STATUS) {
bt_hci_event_handler(&event->u.bt);
}
return 0;
case SYS_DEVICE_EVENT:
return 0;
default:
return FALSE;
}
return FALSE;
}
Source: app_mesh.c
蓝牙就绪事件:Mesh 协议栈的真正起点
BT_STATUS_INIT_OK 事件是框架中最关键的分支点。收到该事件后,若处于 BQB 测试模式则启动 ble_bqb_test_thread_init();若开启 TCFG_NORMAL_SET_DUT_MODE 则进入 DUT 测试;否则调用 bt_ble_init() 正式初始化 BLE——Mesh 协议栈的配网广播(Unprovisioned Device Beacon)即由此开启:
case BT_STATUS_INIT_OK:
log_info("BT_STATUS_INIT_OK\n");
if (BT_MODE_IS(BT_BQB)) {
void ble_bqb_test_thread_init(void);
ble_bqb_test_thread_init();
} else {
#if TCFG_NORMAL_SET_DUT_MODE
log_info("set dut mode\n");
extern void ble_standard_dut_test_init(void);
ble_standard_dut_test_init();
#else
extern void bt_ble_init(void);
bt_ble_init();
#endif
}
is_app_active = 0;
break;
Source: app_mesh.c
HCI 事件与软关机
bt_hci_event_handler 处理厂商自定义 HCI 事件 HCI_EVENT_VENDOR_REMOTE_TEST (0xFE):value == 0 表示清除测试盒标志;value == 1 表示 EDR 已连接、需要关闭 BLE 广播。按键长按 TCFG_ADKEY_VALUE6 触发 app_set_soft_poweroff():先调用 ble_module_enable(0) 主动断开蓝牙链路,再延时 300ms 等待链路断开完成后执行 power_set_soft_poweroff:
static void app_set_soft_poweroff(void)
{
log_info("set_soft_poweroff\n");
is_app_active = 1;
//必须先主动断开蓝牙链路,否则要等链路超时断开
ble_module_enable(0);
//延时300ms,确保BT退出链路断开
sys_timeout_add(NULL, power_set_soft_poweroff, WAIT_DISCONN_TIME_MS);
}
Source: app_mesh.c
注意 is_app_active 的双重作用:启动与关机期间置 1,蓝牙就绪后清 0,并被 app_state_idle_query() 用于系统休眠查询——只有 !is_app_active 时系统才允许进入休眠。
SIG Mesh 协议栈集成
apps/common/third_party_profile/sig_mesh/api/sig_mesh_api.h 是协议栈的伞形头文件,聚合了配网、模型、传输等全部子模块:
#include "api/basic_depend.h"
#include "api/mesh_config.h"
#include "kernel/atomic_h.h"
#include "net/buf.h"
#include "api/access.h"
#include "api/main.h"
#include "api/proxy.h"
#include "api/cfg.h"
#include "api/cfg_cli.h"
#include "api/cfg_srv.h"
#include "api/health_cli.h"
#include "api/health_srv.h"
#include "api/keys.h"
#include "api/cdb.h"
#include "api/sar_cfg.h"
#include "api/rpr.h"
#include "api/heartbeat.h"
#include "api/blob.h"
#include "api/blob_srv.h"
#include "api/blob_cli.h"
#include "api/dfd.h"
#include "api/dfd_srv.h"
#include "api/dfu.h"
#include "api/dfu_cli.h"
#include "api/dfu_metadata.h"
#include "api/dfu_srv.h"
#include "api/blob_io_flash.h"
...
#include "api/scene.h"
#include "api/scene_cli.h"
#include "api/statistic.h"
Source: sig_mesh_api.h
地址类型宏
头文件定义了 Mesh 地址判定宏,用于区分单播(Unicast)、组播(Group)、虚拟(Virtual)与保留地址(RFU),这是所有模型收发逻辑的基础:
#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb)
Source: sig_mesh_api.h
模型列表宏
BT_MESH_MODEL_LIST 允许在元素(Element)定义中直接以内联数组声明模型列表,是注册模型的标准写法:
#define BT_MESH_MODEL_LIST(...) ((struct bt_mesh_model[]){ __VA_ARGS__ })
Source: sig_mesh_api.h
协议栈还定义了完整的 errno 风格错误码(ENONE 0 起,EPERM/ENOENT/ENOMEM/EINVAL/ETIMEDOUT 等),模型回调通过返回值向调用方表达失败原因,应用层可据此做重试或告警。
配置体系
apps/mesh/api/mesh_config_common.c 以全局常量形式给出协议栈硬件参数默认值,全部参数可在应用工程中通过重定义同名符号覆盖(部分声明带 _WEAK_):
| 分组 | 参数 | 默认值 | 含义 |
|---|---|---|---|
| 节点消息广播 | config_bt_mesh_node_msg_adv_interval | ADV_SCAN_UNIT(10)(10ms) | 节点发送消息的广播间隔 |
| 节点消息广播 | config_bt_mesh_node_msg_adv_duration | 50ms | 单次广播持续时间 |
| 代理广播 | config_bt_mesh_proxy_unprovision_adv_interval | ADV_SCAN_UNIT(30) | 未配网代理广播间隔 |
| 代理广播 | config_bt_mesh_proxy_pre_node_adv_interval | ADV_SCAN_UNIT(10) | 入网前代理广播间隔 |
| 代理广播 | config_bt_mesh_proxy_node_adv_interval | ADV_SCAN_UNIT(300)(_WEAK_) | 入网后代理广播间隔 |
| LPN | config_bt_mesh_lpn_auto_timeout | 0s | LPN 自动进入低功耗的超时 |
| LPN | config_bt_mesh_lpn_retry_timeout | 10s | 轮询失败重试超时 |
| LPN | config_bt_mesh_lpn_scan_latency | 10ms | 扫描延迟 |
| LPN | config_bt_mesh_lpn_init_poll_timeout | 300(×100ms) | 初始轮询超时 |
| LPN | config_bt_mesh_lpn_poll_timeout | 200(×100ms) | 常规轮询超时 |
| LPN | config_bt_mesh_lpn_powerup_add_sub_list | 1 | 上电自动加入订阅列表 |
| LPN | config_bt_mesh_lpn_recv_delay | 100ms | 接收延迟(Friend Request 3.6.5.3) |
| Friend | config_bt_mesh_friend_lpn_count | 1 | 可服务的 LPN 数量 |
| Friend | config_bt_mesh_friend_recv_win | 250 | 接收窗口(Friend Offer 3.6.5.4) |
| Friend | config_bt_mesh_friend_sub_list_size | 2 | 订阅列表大小 |
| Friend | config_bt_mesh_friend_queue_size | 2 | 消息队列大小 |
| 广播缓存 | config_bt_mesh_adv_buf_count | 10(_WEAK_) | 广播缓冲区数量,必须 ≥ 3 |
| PB-ADV 配网 | config_bt_mesh_pb_adv_interval | ADV_SCAN_UNIT(15) | PB-ADV 广播间隔 |
| PB-ADV 配网 | config_bt_mesh_pb_adv_duration | 40ms | PB-ADV 广播时长 |
| PB-ADV 配网 | config_bt_mesh_prov_retransmit_timeout | 300ms | 配网重传超时 |
| PB-ADV 配网 | config_bt_mesh_prov_transaction_timeout | 30s | 配网事务超时 |
| PB-ADV 配网 | config_bt_mesh_prov_link_close_timeout | 3s | 配网链路关闭超时 |
| PB-ADV 配网 | config_bt_mesh_prov_protocol_timeout | 60s | 配网协议总超时 |
| 信标 | config_bt_mesh_unprov_beacon_interval | 200ms | 未配网信标间隔 |
| 信标 | config_bt_mesh_secure_beacon_interval | 10s | 安全网络信标间隔 |
Source: mesh_config_common.c
其中 ADV_SCAN_UNIT(_ms) 定义为 ((_ms) * 8 / 5),把毫秒转换为 0.625ms 的广播时隙单位。编译期约束通过 #if (MESH_ADV_BUFFER_COUNT < 3) #error 保证广播缓冲下限,防止缓冲不足导致消息丢失。
核心流程
sequenceDiagram
participant AppCore as app_core 框架
participant Mesh as app_mesh 应用
participant Bt as btstack/控制器
participant Sig as SIG Mesh 协议栈
participant Hw as BLE 射频硬件
AppCore->>Mesh: APP_STA_START + ACTION_AT_MAIN
Mesh->>Mesh: app_start(): clk_set + bt_pll_para
Mesh->>Bt: btstack_init()
Bt-->>AppCore: SYS_BT_EVENT BT_STATUS_INIT_OK
AppCore->>Mesh: bt_connction_status_event_handler()
Mesh->>Mesh: bt_ble_init()
Mesh->>Sig: 启动 Mesh 协议栈(读取 mesh_config_common 参数)
Sig->>Hw: 开启未配网信标 / 代理广播
Hw-->>Sig: 收到配网/消息广播
Sig-->>Mesh: 模型回调 / 状态通知
Mesh-->>AppCore: 按键/蓝牙/HCI 事件继续由 event_handler 分发
配网(Provisioning)成功前,节点持续以 config_bt_mesh_unprov_beacon_interval(200ms)发送未配网信标;配网后切换到安全网络信标(10s)与节点消息广播,LPN 则按 poll_timeout 周期向 Friend 轮询缓存消息。整套流程的时序完全由上述配置参数驱动,应用层无需干预协议细节。
使用示例
示例 1:按键事件驱动 Mesh 行为
应用层把按键事件统一转发给 input_key_handler,并监听长按 TCFG_ADKEY_VALUE6 触发软关机。这展示了"系统按键 → 应用框架 → Mesh 业务"的典型链路:
static void app_key_event_handler(struct sys_event *event)
{
u8 event_type = 0;
u8 key_value = 0;
if (event->arg == (void *)DEVICE_EVENT_FROM_KEY) {
event_type = event->u.key.event;
key_value = event->u.key.value;
printf("app_key_evnet: %d,%d\n", event_type, key_value);
input_key_handler(event_type, key_value);
if (event_type == KEY_EVENT_LONG && key_value == TCFG_ADKEY_VALUE6) {
app_set_soft_poweroff();
}
}
}
Source: app_mesh.c
示例 2:HCI 厂商事件处理
通过 HCI 事件控制 BLE 广播启停,是产测/外部主控联动的常用手段——EDR 连接建立后关闭 BLE 广播以避免射频冲突:
static int bt_hci_event_handler(struct bt_event *bt)
{
//对应原来的蓝牙连接上断开处理函数 ,bt->value=reason
log_info("------------------------bt_hci_event_handler reason %x %x", bt->event, bt->value);
if (bt->event == HCI_EVENT_VENDOR_REMOTE_TEST) {
if (0 == bt->value) {
log_info("clear_test_box_flag");
return 0;
} else {
//1:edr con;2:ble con;
if (1 == bt->value) {
bt_ble_adv_enable(0);
}
}
}
return 0;
}
Source: app_mesh.c
示例 3:覆盖协议栈默认参数
协议栈参数以 _WEAK_ 弱符号导出,应用工程可以重定义同名全局变量实现定制。例如把入网后的代理广播间隔改为 100ms 以获得更快的手机连接响应:
_WEAK_
const u16 config_bt_mesh_proxy_node_adv_interval = ADV_SCAN_UNIT(300); // unit: ms
Source: mesh_config_common.c
示例 4:地址判定与模型列表
在自定义模型中,用地址宏区分单播/组播目标,用 BT_MESH_MODEL_LIST 声明元素模型:
#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
#define BT_MESH_MODEL_LIST(...) ((struct bt_mesh_model[]){ __VA_ARGS__ })
Source: sig_mesh_api.h
工程结构与扩展点
flowchart LR
subgraph sg_Apps["apps/mesh 工程"]
A["app_mesh.c / app_main.c<br/>框架入口"]
B["api/mesh_config_common.c<br/>协议参数"]
C["mesh_dfu/<br/>DFU 升级"]
D["examples/tencent_mesh.c<br/>腾讯 LL Sync"]
E["audio_codec_demo.c<br/>音频编解码"]
F["board/<br/>AC631N/AC632N/AC635N/AC636N/AC637N/AC638N"]
end
subgraph sg_Common["apps/common/third_party_profile"]
G["sig_mesh/<br/>SIG Mesh 协议栈"]
H["llsync_mesh/<br/>腾讯 LL Sync Mesh SDK"]
end
A --> B
A --> C
A --> D
A --> E
A --> F
D --> H
A --> G
扩展应用的方式:
- 新增应用动作:在
state_machine的APP_STA_START分支中增加it->action判断,实现不同启动模式(如 AT 模式、产测模式); - 新增模型:在 SIG Mesh 栈的
access.h/cfg.h之上注册struct bt_mesh_model,通过BT_MESH_MODEL_LIST挂载到元素; - 新增板级工程:复制
board/下现有.cbp(CodeBlocks 工程)并修改芯片型号与引脚配置; - 覆盖弱符号:重定义
config_bt_mesh_*系列参数调整协议行为,无需改动协议栈源码; - 集成第三方协议:参考
tencent_mesh.c接入llsync_meshSDK,与 SIG Mesh 共存或互斥运行。
配置选项
配置变量集中定义于 apps/mesh/api/mesh_config_common.c(详见上文"配置体系"表格),并通过 mesh_config.h 暴露给协议栈。所有间隔类参数均以 ADV_SCAN_UNIT(1 单位 = 0.625ms)换算;时间类参数按注释标注的单位解释(如 poll_timeout 单位为 100ms)。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_adv_buf_count | u8 | 10(WEAK) | 广播缓冲数,编译期强制 ≥ 3 |
config_bt_mesh_lpn_auto_timeout | u8 | 0 | LPN 自动休眠超时(秒) |
config_bt_mesh_lpn_poll_timeout | u32 | 200 | LPN 轮询周期(×100ms) |
config_bt_mesh_friend_lpn_count | u8 | 1 | Friend 可服务 LPN 数 |
config_bt_mesh_prov_protocol_timeout | u8 | 60 | 配网协议总超时(秒) |
config_bt_mesh_unprov_beacon_interval | u32 | 200 | 未配网信标间隔(ms) |
config_bt_mesh_secure_beacon_interval | u16 | 10 | 安全信标间隔(秒) |
API 参考
应用框架回调
| 函数 | 签名 | 作用 |
|---|---|---|
state_machine | static int state_machine(struct application *app, enum app_state state, struct intent *it) | 应用状态机;APP_STA_START + ACTION_AT_MAIN 时执行 app_start() |
event_handler | static int event_handler(struct application *app, struct sys_event *event) | 系统事件分发:按键/蓝牙连接/HCI 状态 |
app_start | static void app_start(void) | 时钟配置 + btstack_init() + 按键使能 |
app_set_soft_poweroff | static void app_set_soft_poweroff(void) | 关闭 BLE 并延时 300ms 后软关机 |
bt_connction_status_event_handler | static int bt_connction_status_event_handler(struct bt_event *bt) | 蓝牙状态回调;BT_STATUS_INIT_OK 后调用 bt_ble_init() |
bt_hci_event_handler | static int bt_hci_event_handler(struct bt_event *bt) | HCI 厂商事件(0xFE)处理:EDR 连接后关 BLE 广播 |
app_key_event_handler | static void app_key_event_handler(struct sys_event *event) | 按键事件转发至 input_key_handler |
Source: app_mesh.c
协议栈宏与常量(sig_mesh_api.h)
| 宏/常量 | 定义 | 说明 |
|---|---|---|
ADV_SCAN_UNIT(_ms) | ((_ms) * 8 / 5) | 毫秒 → 0.625ms 广播时隙单位 |
BT_MESH_ADDR_IS_UNICAST(addr) | (addr) && (addr) < 0x8000 | 单播地址判定 |
BT_MESH_ADDR_IS_GROUP(addr) | 0xc000 <= addr <= 0xff00 | 组播地址判定 |
BT_MESH_ADDR_IS_VIRTUAL(addr) | 0x8000 <= addr < 0xc000 | 虚拟地址判定 |
BT_MESH_ADDR_IS_RFU(addr) | 0xff00 < addr <= 0xfffb | 保留地址判定 |
BT_MESH_MODEL_LIST(...) | 内联模型数组 | 在元素中声明模型列表 |
ENONE/EPERM/ENOENT/ENOMEM/EINVAL/ETIMEDOUT 等 | errno 风格 | 模型与协议回调的错误码约定 |
Source: sig_mesh_api.h
失败模式、边界情况与并发
软关机竞态
app_set_soft_poweroff() 先调用 ble_module_enable(0) 再延时 300ms 执行 power_set_soft_poweroff,注释明确说明"必须先主动断开蓝牙链路,否则要等链路超时断开"。若在链路活跃时直接关机,会导致关机流程阻塞在链路超时上,因此该延时是保证优雅下电的关键边界。
广播缓冲下限
mesh_config_common.c 通过编译期断言强制 MESH_ADV_BUFFER_COUNT >= 3:
#define MESH_ADV_BUFFER_COUNT 10
_WEAK_
const u8 config_bt_mesh_adv_buf_count = MESH_ADV_BUFFER_COUNT; // must >= 3
#if (MESH_ADV_BUFFER_COUNT < 3)
#error " MESH_ADV_BUFFER_COUNT must >= 3 "
#endif
Source: mesh_config_common.c
缓冲不足会在广播洪峰(多模型同时发消息)时丢弃消息,这是 Mesh 可靠性最典型的失败模式,因此用编译错误而非运行告警来拦截。
休眠准入控制
app_state_idle_query() 返回 !is_app_active。is_app_active 在 app_start() 与软关机时置 1、BT_STATUS_INIT_OK 后清 0,确保"协议栈尚未就绪"或"正在关机"时系统不会误入休眠——这是单核 SoC 上防止广播/配网流程被睡眠打断的关键保护。
并发模型
SDK 采用"蓝牙协议栈任务 + 应用任务"的线程模型:btstack_init() 创建协议栈任务,事件通过 SYS_BT_EVENT 异步投递给应用任务。因此 event_handler 内不应执行耗时操作(如长时间的配网重传),重传与超时均由协议栈任务内部状态机驱动(config_bt_mesh_prov_*_timeout 系列参数控制)。net/buf.h 与 atomic_h.h 的引入表明协议栈内部使用原子操作保护缓冲区引用计数。
测试盒联动
HCI_EVENT_VENDOR_REMOTE_TEST(0xFE)是厂商扩展的产测接口:value=0 清除测试标志,value=1(EDR 连接)时主动关闭 BLE 广播。边界情况是双模同时连接时的射频仲裁——BLE 广播关闭后需要由外部事件重新开启,框架未自动恢复,这是有意为之(避免与 EDR 语音链路争抢射频资源)。
日志分级
mesh_config_common.c 通过 CONFIG_DEBUG_LIB(0) 控制 MESH_HCI / MESH_GATT 标签的日志使能,且 log_config.c 中 log_tag_const_*_MESH 系列常量决定 [app_mesh] 标签各级别输出。调试 Mesh 问题时,可通过这些标签开关在保留协议栈性能的同时定位广播/连接问题。
性能与运行注意事项
- 广播间隔与功耗权衡:节点消息广播间隔(10ms)与代理入网后广播间隔(300ms)相差 30 倍。入网后拉长间隔可显著降低平均电流,但会增加手机 App 发现/连接代理的时延;对功耗敏感的产品应优先调大
config_bt_mesh_proxy_node_adv_interval。 - LPN 轮询周期:
config_bt_mesh_lpn_poll_timeout = 200(×100ms = 20s)决定低功耗节点与 Friend 的同步频率。轮询越频繁消息时延越低、功耗越高;auto_timeout = 0表示不自动进入低功耗,需由应用主动切换。 - Friend 资源上限:
friend_lpn_count = 1、sub_list_size = 2、queue_size = 2是低成本 Friend 的典型配置,队列溢出时 Friend 会丢弃最旧消息,LPN 通过retry_timeout = 10s兜底重取。 - 配网超时链:
prov_retransmit_timeout(300ms) →prov_transaction_timeout(30s) →prov_protocol_timeout(60s) 构成三级超时。配网失败排查时按此层级定位:广播层丢失(看重传)、事务层超时(看距离/干扰)、协议层超时(看 Provisioner 行为)。 - 射频时钟一致性:
app_start()中clk_set("sys", BT_NORMAL_HZ)与bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0)必须保持参数一致,否则 BLE 频偏超标会导致配网与组网失败。 - 休眠保护:
is_app_active标志位参与系统休眠准入判断,任何"正在配网/正在关机"状态都被显式排除在休眠之外,避免广播流程被睡眠打断。
测试与验证
仓库中的可验证载体:
- 板级工程:
apps/mesh/board/下为 AC631N、AC632N、AC635N、AC636N、AC637N、AC638N 提供独立.cbp工程,覆盖从低端到高端的全系列芯片; - 产测模式:
BT_MODE_IS(BT_BQB)走ble_bqb_test_thread_init(),TCFG_NORMAL_SET_DUT_MODE走ble_standard_dut_test_init(),与HCI_EVENT_VENDOR_REMOTE_TEST测试盒联动共同构成产测闭环; - 示例:
audio_codec_demo.c(Mesh 音频编解码测试)、tencent_mesh.c(腾讯 LL Sync 接入)、mesh_dfu/(配网器/分发器固件升级)验证了框架在不同业务场景下的可扩展性; - 链接脚本:
cpu/bd19/sdk_ld.c中.bt_mesh_scene_entry_sig段表明协议栈通过链接脚本收集场景(Scene)入口,新增场景模型时需确认目标芯片链接脚本包含对应段。