腾讯连连接入
本页介绍杰理 AC63 系列蓝牙 SDK 中接入腾讯云 IoT 小程序"腾讯连连"(Tencent LLSync / 腾讯连连低功耗蓝牙 LL 同步协议)的完整实现,包括应用入口、BLE GATT 服务、协议数据分发任务、设备信息/数据/OTA 三个消息通道以及绑定状态机。
Purpose and Scope
本页覆盖腾讯连连(LLSync)在 fw-AC63_BT_SDK 中的端到端接入能力:
apps/spp_and_le/examples/ll_sync/下的演示应用(app_ll_sync.c、ll_sync_demo.c、ll_sync_profile.h),即CONFIG_APP_LL_SYNC编译选项对应的工程;apps/common/third_party_profile/Tecent_LL/下的腾讯官方 BLE IoT SDK 移植层(ll_task.c数据分发任务、tecent_protocol/协议实现、include/头文件);- 设备信息(绑定/解绑/配网)、业务数据、OTA 三个消息通道的分发机制与绑定状态机。
以下内容不在本页范围,请参见对应页面:
- BLE 协议栈基础(GATT/ATT/SM 配置)见蓝牙协议栈相关页面;
- 通用 OTA 升级流程见 OTA 升级页面;
- 其他第三方接入(如 Hilink)见各自集成页面。
Overview
腾讯连连(Tencent LianLian,简称 LL)是腾讯云 IoT 面向消费者的低功耗蓝牙配网与设备控制方案。设备端以 BLE GATT Server 形式对外提供服务,手机端"腾讯连连"小程序通过蓝牙扫描、连接设备,完成绑定(bind)、配网(Wi-Fi 信息下发)、业务数据收发与 OTA 升级。
本 SDK 的接入采用"透传 + 独立任务分发"架构:
- 设备作为 GATT Server,广播/响应携带腾讯自定义的 EIR Tag(
0xd6 0x05 0x08 0x00 'J' 'L' 'A' 'I' 'S' 'D' 'K'),手机小程序据此识别设备; - 手机向 GATT 写特征值时,写回调把原始数据写入环形缓冲
cbuf并唤醒专用任务tecent_ll_task; - 任务按包头中的
packet_channel字段将数据分发到设备信息通道(LL_DEVICE_INFO_MSG_CH)、业务数据通道(LL_DATA_MSG_CH)或 OTA 通道(LL_OTA_MSG_CH); - 各通道处理函数(
ble_device_info_msg_handle/ble_lldata_msg_handle/ble_ota_msg_handle)完成 LLSync 协议解析(绑定、时间同步、MTU 协商、数据加解密、OTA 分片),处理结果再经 GATT 通知(Notify/Indicate)回传手机。
该设计把"BLE 中断上下文"与"协议处理上下文"解耦:GATT 写回调只做入队,耗时协议解析全部放到独立任务中执行,避免阻塞蓝牙协议栈。
Architecture
flowchart TD
subgraph sg_App["应用层 (apps/spp_and_le/examples/ll_sync)"]
APP["app_ll_sync.c<br/>应用状态机 ACTION_LL_SYNC"]
DEMO["ll_sync_demo.c<br/>GATT Server / 广播 / 连接参数"]
PROFILE["ll_sync_profile.h<br/>特征值定义"]
end
subgraph sg_SDK["腾讯 LLSync SDK 移植层 (Tecent_LL)"]
TASK["ll_task.c<br/>tecent_ll_task 分发任务 + cbuf"]
DEV["tecent_protocol/ble_qiot_llsync_device.c<br/>设备信息通道"]
DATA["tecent_protocol/ble_qiot_llsync_data.c<br/>业务数据通道"]
OTA["tecent_protocol/ble_qiot_llsync_ota.c<br/>OTA 通道"]
EVT["tecent_protocol/ble_qiot_llsync_event.c<br/>事件上报"]
HDR["include/ble_qiot_llsync_*.h<br/>协议定义与导出接口"]
end
subgraph sg_BT["蓝牙协议栈"]
STACK["btstack (GATT/ATT/SM)"]
end
APP -->|"llsync_app_start"| DEMO
DEMO -->|"att_write_callback"| TASK
TASK -->|"packet_channel 分发"| DEV
TASK --> DATA
TASK --> OTA
DEV --> EVT
DATA --> EVT
OTA --> EVT
DEMO -->|"GATT 服务注册"| STACK
STACK -->|"BLE 事件/HCI 事件"| APP
HDR -.->|"类型与常量"| TASK
HDR -.->|"类型与常量"| DEV
HDR -.->|"类型与常量"| DATA
HDR -.->|"类型与常量"| OTA
架构说明:
- 应用层负责生命周期:
app_ll_sync.c中的状态机在收到ACTION_LL_SYNC时启动llsync_app_start(),初始化时钟与蓝牙协议栈;ll_sync_demo.c注册 GATT Server(读/写/事件回调)、配置广播与连接参数。 - SDK 移植层是腾讯 BLE IoT(ble_qiot)SDK 在本平台的落地:
ll_task.c提供任务化收包骨架;tecent_protocol/四个.c文件分别实现设备信息、业务数据、OTA、事件四类协议处理。 - 数据流方向:手机小程序 → BLE 写请求 → GATT 写回调 →
cbuf环形缓冲 →tecent_ll_task→ 按通道分发 → 协议处理 → 响应经 GATT 通知回手机。
以下源码链接基于 master 分支:
https://gitee.com/Jieli-Tech/fw-AC63_BT_SDK/blob/master
工程结构与文件职责
腾讯连连接入涉及两个目录,职责边界清晰:
| 文件 | 职责 |
|---|---|
apps/spp_and_le/examples/ll_sync/app_ll_sync.c | 应用入口:状态机、启动流程、软关机、电源事件、HCI 事件转发 |
apps/spp_and_le/examples/ll_sync/ll_sync_demo.c | GATT Server 演示:MTU/缓存/广播/连接参数/SM 配置、读写与事件回调 |
apps/spp_and_le/examples/ll_sync/ll_sync_profile.h | 腾讯 LLSync 自定义 GATT Profile(特征值 UUID)定义 |
apps/common/third_party_profile/Tecent_LL/tecent_ll_demo/ll_task.c | 协议数据接收任务:信号量 + 环形缓冲 + 通道分发 |
apps/common/third_party_profile/Tecent_LL/tecent_ll_demo/ll_task.h | LL_PACKET_CONTROL 控制块与包头定义 |
apps/common/third_party_profile/Tecent_LL/tecent_protocol/ble_qiot_llsync_device.c | 设备信息通道:绑定/解绑/配网/时间同步/MTU 协商 |
apps/common/third_party_profile/Tecent_LL/tecent_protocol/ble_qiot_llsync_data.c | 业务数据通道:LLSync 数据加解密收发 |
apps/common/third_party_profile/Tecent_LL/tecent_protocol/ble_qiot_llsync_ota.c | OTA 通道:固件升级分片与校验 |
apps/common/third_party_profile/Tecent_LL/tecent_protocol/ble_qiot_llsync_event.c | 事件上报(绑定结果、连接结果、OTA 进度等) |
apps/common/third_party_profile/Tecent_LL/include/ble_qiot_llsync_*.h | 协议常量、枚举、结构体与对外接口声明 |
消息通道与分发机制
ll_task.c 是整个接入的核心骨架。它定义全局控制块 LL_PACKET_CONTROL LL_packet_c,包含信号量 ll_sem 与环形缓冲 cbuf,并创建独立任务 tecent_ll_task:
static void tecent_ll_task(void *arg)
{
LL_PACKET_HEAD_T ll_packet_head;
u8 *buffer = NULL;
u8 result = 0;
while (1) {
os_sem_pend(&(__this->ll_sem), 0);
if (cbuf_get_data_len(&(__this->cbuf)) > LL_PACKET_HEAD_LEN) {
cbuf_read(&(__this->cbuf), &ll_packet_head, LL_PACKET_HEAD_LEN);
buffer = malloc(ll_packet_head.len);
if (buffer == NULL) {
printf("buf malloc err\n");
break;
}
cbuf_read(&(__this->cbuf), buffer, ll_packet_head.len);
switch (ll_packet_head.packet_channel) {
case LL_DEVICE_INFO_MSG_CH:
result = ble_device_info_msg_handle(buffer, ll_packet_head.len);
break;
case LL_DATA_MSG_CH:
result = ble_lldata_msg_handle(buffer, ll_packet_head.len);
break;
case LL_OTA_MSG_CH:
result = ble_ota_msg_handle(buffer, ll_packet_head.len);
break;
}
free(buffer);
}
}
}
Source: ll_task.c
关键设计点:
- 包头先行:每个数据包以
LL_PACKET_HEAD_T开头,任务先读取LL_PACKET_HEAD_LEN字节的包头拿到packet_channel(通道号)与len(载荷长度),再按len动态分配缓冲区读取剩余载荷。这样三种通道共用一个 FIFO,无需为每种消息预分配固定大小缓冲。 - 通道解耦:
switch按packet_channel分发到三个独立处理器,各自维护自己的协议状态机(绑定状态、数据加密会话、OTA 进度),互不干扰。 - 内存安全:载荷缓冲使用后立即
free;malloc失败则打印错误并跳出任务(这种异常路径会终止接收循环,实际工程中可改为continue以增强健壮性)。
任务初始化与收包入口:
int tecent_ll_task_init(void)
{
os_sem_create(&(__this->ll_sem), 0);
u32 malloc_size = (ble_get_user_data_mtu_size() + LL_PACKET_HEAD_LEN) * BLE_QIOT_TOTAL_PACKAGES;
__this->tecent_ll_buf = malloc(malloc_size);
memset(__this->tecent_ll_buf, 0x0, malloc_size);
cbuf_init(&(__this->cbuf), __this->tecent_ll_buf, malloc_size);
os_task_create(tecent_ll_task, NULL, 1, 512, 0, "tecent_ll_task");
return 0;
}
void tecent_ll_packet_recieve(void *buf, u16 len)
{
if (cbuf_is_write_able(&(__this->cbuf), len) >= len) {
cbuf_write(&(__this->cbuf), buf, len);
} else {
printf("[L]\n");
}
os_sem_post(&(__this->ll_sem));
}
Source: ll_task.c
- 缓冲总大小按
(用户数据 MTU + 包头长度)× BLE_QIOT_TOTAL_PACKAGES计算,保证至少能缓存"单个 MTU 数据包 × 包数上限"的流量,避免高频写入时丢包; tecent_ll_packet_recieve是 GATT 写回调与任务之间的桥梁:写回调只负责入队 + 发信号量,绝不在中断/协议栈上下文做协议解析;缓冲不可写时打印[L]提示丢包;- 任务栈 512 字节、优先级 1,属于轻量级接收任务。
应用入口与生命周期
app_ll_sync.c 通过 CONFIG_APP_LL_SYNC 编译开关启用(app_main.c 中对应注册 it.name = "ll_sync"、it.action = ACTION_LL_SYNC),并强约束板级配置:必须开启 BLE 且关闭 EDR,否则编译报错:
#if CONFIG_APP_LL_SYNC
#if !(TCFG_USER_BLE_ENABLE && (!TCFG_USER_EDR_ENABLE))
#error "board config error, confirm!!!!!!"
#endif
Source: app_ll_sync.c
应用状态机在 APP_STA_START 收到 ACTION_LL_SYNC 时启动:
static void llsync_app_start()
{
log_info("=======================================");
log_info("-----------ll_sync demo----------------");
log_info("=======================================");
log_info("app_file: %s", __FILE__);
clk_set("sys", BT_NORMAL_HZ);
//有蓝牙
#if TCFG_USER_BLE_ENABLE
u32 sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_ble_start_before_init(NULL, 0);
btstack_init();
#endif
/* 按键消息使能 */
sys_key_event_enable();
}
Source: app_ll_sync.c
启动要点:
clk_set("sys", BT_NORMAL_HZ)将系统时钟切到蓝牙正常模式,随后bt_pll_para按晶振频率与系统时钟配置 PLL——腾讯连连需要稳定的 BLE 射频时序,时钟切换是协议栈初始化的前置条件;btstack_ble_start_before_init+btstack_init完成 BLE 协议栈初始化;sys_key_event_enable使能按键事件,供演示工程绑定/解绑等交互使用。
llsync_state_machine 处理应用生命周期,llsync_bt_hci_event_handler 把 BLE 连接/断开等 HCI 事件转交给 bt_comm_ble_hci_event_handler 统一处理;llsync_set_soft_poweroff 实现软关机前先 btstack_ble_exit(0) 主动断开链路、再延时 WAIT_DISCONN_TIME_MS 执行 power_set_soft_poweroff,电源事件则通过 sys_event_notify 以 SYS_DEVICE_EVENT / DEVICE_EVENT_FROM_POWER 广播给系统。
GATT 服务、广播与安全配置
ll_sync_demo.c 定义了演示服务的全部 BLE 参数:
//ATT发送的包长, note: 23 <=need >= MTU
#define ATT_LOCAL_MTU_SIZE (200)
//ATT缓存的buffer大小, note: need >= 23,可修改
#define ATT_SEND_CBUF_SIZE (512)
// 广播周期 (unit:0.625ms)
#define ADV_INTERVAL_MIN (160 * 5)//
#define LL_TECENT_SUPPORT_TASK 1
Source: ll_sync_demo.c
ATT_LOCAL_MTU_SIZE决定本端 ATT 发送包长上限(200 字节,大于默认 23 字节 MTU),是 LLSync 大包数据的基础;ATT_SEND_CBUF_SIZE是 ATT 发送缓存,需 ≥ 23;ADV_INTERVAL_MIN = 160 × 5 × 0.625ms = 500ms为广播最小间隔;LL_TECENT_SUPPORT_TASK使能任务化收包(对应ll_task.c)。
连接参数更新表提供三组可选参数,使能开关 llsync_connection_update_enable 默认为 1:
//是否使能参数请求更新,0--disable, 1--enable
static uint8_t llsync_connection_update_enable = 1; ///0--disable, 1--enable
//当前请求的参数表index
//参数表
static const struct conn_update_param_t llsync_connection_param_table[] = {
{16, 24, 10, 600},//11
{12, 28, 10, 600},//3.7
{8, 20, 10, 600},
};
Source: ll_sync_demo.c
广播数据携带腾讯识别 Tag 0xd6 0x05 0x08 0x00 'J' 'L' 'A' 'I' 'S' 'D' 'K'(user_tag_string),"腾讯连连"小程序据此在扫描结果中识别本设备。
安全与 GATT 注册配置:
static const sm_cfg_t llsync_sm_init_config = {
.slave_security_auto_req = 0,
.slave_set_wait_security = 0,
#if PASSKEY_ENABLE
.io_capabilities = IO_CAPABILITY_DISPLAY_ONLY,
#else
.io_capabilities = IO_CAPABILITY_NO_INPUT_NO_OUTPUT,
#endif
.authentication_req_flags = SM_AUTHREQ_BONDING | SM_AUTHREQ_MITM_PROTECTION,
.min_key_size = 7,
.max_key_size = 16,
.sm_cb_packet_handler = NULL,
};
const gatt_server_cfg_t llsync_server_init_cfg = {
.att_read_cb = &llsync_att_read_callback,
.att_write_cb = &llsync_att_write_callback,
.event_packet_handler = &llsync_event_packet_handler,
};
static gatt_ctrl_t llsync_gatt_control_block = {
//public
.mtu_size = 517,
.cbuffer_size = 1024,
.multi_dev_flag = 0,
Source: ll_sync_demo.c
- SM 采用
NO_INPUT_NO_OUTPUT(PASSKEY_ENABLE默认 0),认证标志为BONDING | MITM,密钥长度 7–16 字节——无配对码的 Just Works 绑定,符合小程序扫码绑定的交互模型; - GATT 控制块
mtu_size = 517为协商后的最大 MTU 预留,cbuffer_size = 1024为 ATT 收发缓冲,multi_dev_flag = 0表示单连接模式; llsync_att_write_callback收到手机写数据后即调用tecent_ll_packet_recieve入队(对应LL_TECENT_SUPPORT_TASK使能时)。
LLSync 协议定义:设备信息通道
ble_qiot_llsync_device.h 定义了协议版本、绑定状态与设备信息消息类型。协议版本常量与掩码:
#define LLSYNC_BIND_STATE_MASK 0x03
#define LLSYNC_PROTO_VER_BIT 0x04
#define LLSYNC_PROTOCOL_VERSION_MASK 0xF0
#define LLSYNC_MTU_SET_MASK 0x8000
#define LLSYNC_MTU_SET_RESULT_ERR 0xFFFF // some error when setting mtu
#define BLE_QIOT_LLSYNC_PROTOCOL_VERSION (2) // llsync protocol version, equal or less than 15
#define ATT_DEFAULT_MTU 23 // default att mtu
#define ATT_MTU_TO_LLSYNC_MTU(_att_mtu) ((_att_mtu)-3)
#define BLE_LOCAL_PSK_LEN 4
#define BLE_BIND_IDENTIFY_STR_LEN 8
#define BLE_EXPIRATION_TIME 60 // timestamp expiration value
#define BLE_UNBIND_REQUEST_STR "UnbindRequest"
#define BLE_UNBIND_REQUEST_STR_LEN (sizeof("UnbindRequest") - 1)
#define BLE_UNBIND_RESPONSE "UnbindResponse"
#define BLE_UNBIND_RESPONSE_STR_LEN (sizeof("UnbindResponse") - 1)
Source: ble_qiot_llsync_device.h
设计意图:
- 一个 16 位状态字里用位域同时携带绑定状态(低 2 位)、协议版本位、协议版本号(高 4 位)与 MTU 设置结果(最高位),压缩到单次读特征值即可完成状态上报,减少空中交互次数;
BLE_QIOT_LLSYNC_PROTOCOL_VERSION = 2,要求 ≤ 15(与 4 位版本掩码对应);BLE_EXPIRATION_TIME = 60秒是时间戳签名(timestamp + SHA1)的过期窗口,用于防重放;- 绑定前写入
FEE1特征值的是ble_bind_data(nonce + timestamp),解绑使用签名串UnbindRequest/UnbindResponse。
设备信息消息类型枚举覆盖绑定全生命周期与配网:
typedef enum {
E_DEV_MSG_SYNC_TIME = 0, // sync info before bind
E_DEV_MSG_CONN_VALID, // connect request
E_DEV_MSG_BIND_SUCC, // inform bind success
E_DEV_MSG_BIND_FAIL, // inform bind failed
E_DEV_MSG_UNBIND, // unbind request
E_DEV_MSG_CONN_SUCC, // inform connect result
E_DEV_MSG_CONN_FAIL,
E_DEV_MSG_UNBIND_SUCC, // inform unbind result
E_DEV_MSG_UNBIND_FAIL,
E_DEV_MSG_SET_MTU_RESULT, // inform set mtu result
E_DEV_MSG_BIND_TIMEOUT, // inform bind timeout
E_DEV_MSG_GET_DEV_INFO = 0xE0, // configure network start
E_DEV_MSG_SET_WIFI_MODE,
E_DEV_MSG_SET_WIFI_INFO,
E_DEV_MSG_SET_WIFI_CONNECT,
E_DEV_MSG_SET_WIFI_TOKEN,
E_DEV_MSG_GET_DEV_LOG,
E_DEV_MSG_MSG_BUTT,
} e_dev_info_msg_type;
Source: ble_qiot_llsync_device.h
消息类型分为两段:0x00–0x0B 为绑定/连接/MTU 管理消息(时间同步、连接校验、绑定成功/失败/超时、解绑、MTU 结果);0xE0 起为配网消息(获取设备信息、设置 Wi-Fi 模式/信息/连接/Token、获取设备日志),即设备未入网时通过 BLE 通道完成 Wi-Fi 配置。
绑定与连接状态枚举:
typedef enum {
E_LLSYNC_BIND_IDLE = 0, // no bind
E_LLSYNC_BIND_WAIT, // wait bind, return idle state if no bind in the period
E_LLSYNC_BIND_SUCC, // bound
} e_llsync_bind_state;
typedef enum {
E_LLSYNC_DISCONNECTED = 0,
E_LLSYNC_CONNECTED,
} e_llsync_connection_state;
typedef enum {
E_BLE_DISCONNECTED = 0,
E_BLE_CONNECTED,
} e_ble_connection_state;
Source: ble_qiot_llsync_device.h
核心数据结构:设备信息(product_id / device_name / psk / mac)、绑定核心数据(local_psk + identify_str)、绑定数据(nonce + timestamp)、连接校验数据(timestamp + SHA1 签名)、解绑数据(SHA1 签名):
typedef struct ble_device_info_t_ {
char product_id[BLE_QIOT_PRODUCT_ID_LEN];
char device_name[BLE_QIOT_DEVICE_NAME_LEN + 1];
char psk[BLE_QIOT_PSK_LEN];
char mac[BLE_QIOT_MAC_LEN];
} ble_device_info;
typedef struct ble_core_data_ {
uint8_t bind_state;
char local_psk[BLE_LOCAL_PSK_LEN];
char identify_str[BLE_BIND_IDENTIFY_STR_LEN];
} ble_core_data;
// write to uuid FEE1 before bind
typedef struct ble_bind_data_t_ {
int nonce;
int timestamp;
} ble_bind_data;
// connect data struct
typedef struct ble_conn_data_t_ {
int timestamp;
char sign_info[SHA1_DIGEST_SIZE];
} ble_conn_data;
// unbind data struct
typedef struct ble_unbind_data_t_ {
char sign_info[SHA1_DIGEST_SIZE];
} ble_unbind_data;
Source: ble_qiot_llsync_device.h
安全模型要点:绑定前手机与设备通过 nonce + timestamp 交换建立共享密钥(local_psk 4 字节 + identify_str 8 字节);绑定后的每次连接使用 timestamp + SHA1(sign_info) 签名校验,BLE_EXPIRATION_TIME=60 秒防止重放攻击。ble_event_slice_t 则用于事件分片上报(BLE_QIOT_EVENT_MAX_SIZE 上限),把大事件拆成多次 GATT 通知。ble_init_flash_data() 负责从 Flash 读取已保存的 SDK 数据(绑定状态、PSK 等),使设备重启后保持绑定关系。
核心流程:小程序绑定到设备
sequenceDiagram
participant P as 腾讯连连小程序
participant S as 蓝牙协议栈 (btstack)
participant W as att_write_callback
participant T as tecent_ll_task
participant D as ble_qiot_llsync_device.c
P->>S: 扫描广播 (EIR Tag 0xd6 0x05 0x08 0x00 'JLAISDK')
S-->>P: 广播/扫描应答
P->>S: 发起连接 + 绑定 (Just Works)
S->>W: GATT Write (FEE1 绑定数据)
W->>T: tecent_ll_packet_recieve(buf, len) 入 cbuf
T->>T: os_sem_pend 唤醒, 读包头 packet_channel
T->>D: ble_device_info_msg_handle (LL_DEVICE_INFO_MSG_CH)
D->>D: 校验 nonce/timestamp, 生成 local_psk
D-->>W: 响应包 (绑定结果)
W-->>P: GATT Notify/Indicate 上报结果
P->>S: GATT Write (连接校验 timestamp+SHA1)
S->>T: tecent_ll_packet_recieve 再次入队
T->>D: 校验签名, 置 E_LLSYNC_BIND_SUCC / 连接态
D-->>P: 连接成功通知, 进入数据/OTA 通道收发
流程说明:
- 设备上电后
llsync_app_start完成时钟与协议栈初始化,ll_sync_demo.c启动广播(含腾讯 EIR Tag),小程序扫描识别; - 小程序连接设备并完成 Just Works 绑定(
SM_AUTHREQ_BONDING | MITM,NO_INPUT_NO_OUTPUT); - 小程序向绑定特征值写入
ble_bind_data(nonce + timestamp),GATT 写回调仅调用tecent_ll_packet_recieve把原始包写入cbuf并os_sem_post; tecent_ll_task被唤醒,读取包头得到LL_DEVICE_INFO_MSG_CH,调用ble_device_info_msg_handle完成绑定逻辑(时间同步、PSK 生成);- 处理结果写回 GATT 通知通道,小程序收到绑定成功/失败;
- 绑定后每次连接通过
timestamp + SHA1签名校验(E_DEV_MSG_CONN_VALID),通过后进入数据(LL_DATA_MSG_CH)与 OTA(LL_OTA_MSG_CH)通道正常业务。
绑定状态机
stateDiagram-v2
[*] --> E_LLSYNC_BIND_IDLE
E_LLSYNC_BIND_IDLE --> E_LLSYNC_BIND_WAIT: 收到绑定请求 (FEE1 写入)
E_LLSYNC_BIND_WAIT --> E_LLSYNC_BIND_SUCC: 绑定成功 (E_DEV_MSG_BIND_SUCC)
E_LLSYNC_BIND_WAIT --> E_LLSYNC_BIND_IDLE: 绑定失败/超时 (BIND_FAIL/BIND_TIMEOUT)
E_LLSYNC_BIND_SUCC --> E_LLSYNC_BIND_IDLE: 收到解绑 (UnbindRequest)
E_LLSYNC_BIND_IDLE --> E_LLSYNC_BIND_IDLE: 时间同步/配网消息 (SYNC_TIME/GET_DEV_INFO...)
状态定义见 e_llsync_bind_state(E_LLSYNC_BIND_IDLE → E_LLSYNC_BIND_WAIT → E_LLSYNC_BIND_SUCC),对应消息 E_DEV_MSG_BIND_SUCC/BIND_FAIL/BIND_TIMEOUT;绑定成功态收到 E_DEV_MSG_UNBIND(UnbindRequest 签名)后回到空闲态。ble_core_data.bind_state 持久化于 Flash,设备重启后通过 ble_init_flash_data() 恢复。
使用示例
1. 收包入队(GATT 写回调侧)
任何收到手机数据的通道(绑定、数据、OTA)最终都汇聚到同一入口:
void tecent_ll_packet_recieve(void *buf, u16 len)
{
if (cbuf_is_write_able(&(__this->cbuf), len) >= len) {
cbuf_write(&(__this->cbuf), buf, len);
} else {
printf("[L]\n");
}
os_sem_post(&(__this->ll_sem));
}
Source: ll_task.c
调用方式:ll_sync_demo.c 的 llsync_att_write_callback 在 LL_TECENT_SUPPORT_TASK 使能时把写数据交给本函数,由任务侧统一解析。
2. 任务初始化(应用启动时调用一次)
int tecent_ll_task_init(void)
{
os_sem_create(&(__this->ll_sem), 0);
u32 malloc_size = (ble_get_user_data_mtu_size() + LL_PACKET_HEAD_LEN) * BLE_QIOT_TOTAL_PACKAGES;
__this->tecent_ll_buf = malloc(malloc_size);
memset(__this->tecent_ll_buf, 0x0, malloc_size);
cbuf_init(&(__this->cbuf), __this->tecent_ll_buf, malloc_size);
os_task_create(tecent_ll_task, NULL, 1, 512, 0, "tecent_ll_task");
return 0;
}
Source: ll_task.c
3. 应用启动(状态机入口)
case APP_STA_START:
if (!it) {
break;
}
switch (it->action) {
case ACTION_LL_SYNC:
llsync_app_start();
break;
}
break;
Source: app_ll_sync.c
4. 软关机(先断链路再断电)
static void llsync_set_soft_poweroff(void)
{
log_info("set_soft_poweroff\n");
is_app_llsync_active = 1;
//必须先主动断开蓝牙链路,否则要等链路超时断开
#if TCFG_USER_BLE_ENABLE
btstack_ble_exit(0);
//延时300ms,确保BT退出链路断开
sys_timeout_add(NULL, power_set_soft_poweroff, WAIT_DISCONN_TIME_MS);
#else
power_set_soft_poweroff();
#endif
}
Source: app_ll_sync.c
配置选项
以下为 ll_sync_demo.c / app_ll_sync.c 中可直接调整的配置:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
CONFIG_APP_LL_SYNC | 宏 | 由工程定义 | 使能腾讯连连应用(app_main.c 中映射到 ACTION_LL_SYNC) |
TCFG_USER_BLE_ENABLE | 宏 | 板级配置 | 必须为 1 且 TCFG_USER_EDR_ENABLE 为 0,否则编译报错 |
ATT_LOCAL_MTU_SIZE | 宏 | 200 | 本端 ATT 发送包长上限(23 ≤ 值 ≥ MTU) |
ATT_SEND_CBUF_SIZE | 宏 | 512 | ATT 发送缓存大小(需 ≥ 23) |
ADV_INTERVAL_MIN | 宏 | 160×5 (0.625ms) | 广播最小间隔,约 500ms |
LL_TECENT_SUPPORT_TASK | 宏 | 1 | 使能任务化收包(走 tecent_ll_task 分发) |
PASSKEY_ENABLE | 宏 | 0 | 1 为 Display Only(输入 passkey),0 为 NoInputNoOutput |
llsync_connection_update_enable | u8 | 1 | 是否使能连接参数请求更新 |
llsync_connection_param_table[] | 数组 | {16,24,10,600} 等 3 组 | 连接参数候选表(min/max interval, latency, timeout) |
llsync_gatt_control_block.mtu_size | u16 | 517 | GATT 协商 MTU 预留值 |
llsync_gatt_control_block.cbuffer_size | u16 | 1024 | ATT 收发缓冲 |
llsync_gatt_control_block.multi_dev_flag | u8 | 0 | 单连接模式 |
BLE_QIOT_LLSYNC_PROTOCOL_VERSION | 宏 | 2 | LLSync 协议版本(≤ 15) |
BLE_EXPIRATION_TIME | 宏 | 60 (s) | 时间戳签名过期窗口 |
API 参考
int tecent_ll_task_init(void)
初始化腾讯连连接收任务:创建信号量、按 (用户数据 MTU + 包头) × BLE_QIOT_TOTAL_PACKAGES 分配环形缓冲、创建 tecent_ll_task 任务(优先级 1,栈 512 字节)。
返回: 恒为 0(成功)。
void tecent_ll_packet_recieve(void *buf, u16 len)
将收到的原始 LLSync 数据包写入环形缓冲并唤醒任务。须在 GATT 写回调中调用,不得在中断上下文做协议解析。
buf:收到的数据包指针;len:数据长度;- 缓冲不可写时打印
[L]并丢弃该包,随后仍会os_sem_post(任务侧因长度不足不会处理)。
u8 ble_device_info_msg_handle(u8 *buffer, u16 len)
设备信息通道处理器(绑定/解绑/配网/MTU 协商),由 tecent_ll_task 在 LL_DEVICE_INFO_MSG_CH 分支调用。实现见 tecent_protocol/ble_qiot_llsync_device.c。
u8 ble_lldata_msg_handle(u8 *buffer, u16 len)
业务数据通道处理器(LLSync 数据加解密收发),LL_DATA_MSG_CH 分支调用。实现见 tecent_protocol/ble_qiot_llsync_data.c。
u8 ble_ota_msg_handle(u8 *buffer, u16 len)
OTA 通道处理器(固件升级分片与校验),LL_OTA_MSG_CH 分支调用。实现见 tecent_protocol/ble_qiot_llsync_ota.c。
ble_qiot_ret_status_t ble_init_flash_data(void)
从 Flash 读取 SDK 持久化数据(绑定状态、local_psk、identify_str 等),设备重启后恢复绑定关系。声明见 ble_qiot_llsync_device.h。
失败模式与边界情况
- 环形缓冲溢出(丢包):
tecent_ll_packet_recieve在cbuf不可写时打印[L]并丢弃数据。高吞吐数据(如 OTA 分片连续下发)或 MTU 较大时易触发;缓解手段是保证tecent_ll_task_init分配的缓冲容量(MTU + 包头) × BLE_QIOT_TOTAL_PACKAGES大于单次突发流量,并确认任务调度及时。 malloc失败导致任务退出:tecent_ll_task中malloc(ll_packet_head.len)失败会break退出接收循环,此后该任务不再处理任何包。实际产品建议改为continue并做错误计数,避免协议栈静默失效。- 包头长度异常:任务只校验
cbuf_get_data_len > LL_PACKET_HEAD_LEN,未校验ll_packet_head.len是否超出缓冲/合理范围;若手机端发送畸形包(如超大len),malloc可能失败或产生大块内存申请。 - 未知通道号:
switch无default分支,未知packet_channel的包被读取后直接丢弃(buffer仍被free,不会泄漏)。 - 时间戳过期:
BLE_EXPIRATION_TIME = 60秒,绑定/连接签名中的timestamp超出窗口即拒绝,防止重放;设备与手机时钟偏差过大时会导致绑定失败,需先完成E_DEV_MSG_SYNC_TIME时间同步。 - 并发与上下文:GATT 写回调与
tecent_ll_task之间通过"cbuf 写 + 信号量"同步,是经典的单生产者单消费者模型,无需锁;但ble_*_msg_handle内部若操作共享状态(如绑定状态、OTA 进度),需保证只在任务上下文访问。 - 软关机竞态:
llsync_set_soft_poweroff先btstack_ble_exit(0)断开链路,再延时WAIT_DISCONN_TIME_MS执行power_set_soft_poweroff;若断电提前发生,链路可能未完全释放,影响下次开机。
性能与运维要点
- 任务化隔离:所有协议解析(HMAC/SHA1 签名校验、加解密、OTA 分片重组)都在
tecent_ll_task(优先级 1,栈 512B)中执行,BLE 协议栈不会被长任务阻塞;若后续 OTA 校验逻辑加重,可适当提高任务优先级或栈大小。 - MTU 与吞吐:
ATT_LOCAL_MTU_SIZE = 200、GATT 控制块mtu_size = 517、cbuffer_size = 1024决定了单包数据量与收发吞吐;调大 MTU 可减少 OTA 分片数,但会增大内存占用。 - 连接参数:三组候选参数(如
{16,24,10,600}对应约 10ms 间隔)由llsync_connection_update_enable控制请求更新,低间隔参数降低交互时延,适合控制类指令;功耗敏感场景可选择更大间隔组。 - 日志:
app_ll_sync.c使用LOG_TAG "[LLSYNC]"的debug.h日志体系(log_info/log_error);ll_sync_demo.c提供[BLE_llsync]前缀打印与put_buf十六进制 dump,便于联调抓包对照。
扩展点
- 新增消息通道:在
ll_task.c的switch中增加case分支,并在ll_task.h定义新的packet_channel常量与包头结构,即可扩展自定义通道(如设备日志、量产测试指令)。 - 配网/业务回调:设备信息通道中的
E_DEV_MSG_SET_WIFI_INFO/CONNECT/TOKEN等消息在ble_qiot_llsync_device.c处理,接入方可在其中挂接自己的 Wi-Fi 模组驱动;ble_qiot_llsync_event.c的事件上报接口用于把设备端结果回传小程序。 - 协议版本升级:
BLE_QIOT_LLSYNC_PROTOCOL_VERSION与LLSYNC_PROTOCOL_VERSION_MASK/LLSYNC_PROTO_VER_BIT支持版本协商,升级腾讯 SDK 时保持版本字段一致即可兼容新旧小程序。 - 演示替换:
apps/spp_and_le/examples/ll_sync/是纯演示工程,量产接入时可在llsync_att_write_callback/llsync_att_read_callback与tecent_ll_packet_recieve之间插入自己的鉴权或日志逻辑,不必改动 SDK 协议层。
相关链接
- ll_task.c(任务分发骨架)
- ll_sync_demo.c(GATT 服务演示)
- app_ll_sync.c(应用入口)
- ble_qiot_llsync_device.h(协议定义)
- Tecent_LL SDK 目录
- 腾讯云 IoT 官方 LLSync 文档(外部):https://cloud.tencent.com/document/product/1081