杰理 SDK 文档中心
首页
首页
  • 概述

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

涂鸦协议接入

本文档介绍 AC63_BT_SDK 中涂鸦(Tuya)蓝牙协议栈的接入方式,涵盖 SDK 目录结构、初始化流程、DP 数据收发、事件回调、JL 平台移植层、OTA、产测与 UART 通用处理等完整实现机制,帮助开发者将涂鸦智能生态(涂鸦智能 App、云平台)接入基于杰理 AC63 系列芯片的蓝牙产品。

Purpose and Scope

本页面向需要将产品接入涂鸦智能生态的开发者,完整说明仓库中 apps/common/third_party_profile/tuya_protocol/ 目录所承载的涂鸦 BLE 协议栈如何与杰理蓝牙协议栈(BLE GATT、UART、Flash、OS)协同工作,以及应用层(Demo、产测、OTA、UART 透传)如何基于 SDK 实现设备功能。

本页不包含以下内容,它们属于其他目录项:

  • 涂鸦 Mesh 灯控模型(apps/mesh/examples/TUYA_light.c、SIG_MESH_TUYA_LIGHT)请参见 Mesh 相关页面。
  • 腾讯、阿里(天猫精灵)等第三方协议接入请参见各自的 third_party_profile 页面。
  • 通用 BLE 协议栈、GATT 服务、广播配置等基础能力请参见蓝牙协议栈相关页面。

概述

涂鸦协议接入的本质是:在杰理 AC63 平台(作为 BLE 从机)上运行涂鸦官方 BLE SDK,通过 SDK 提供的设备参数(PID、auth key、device id、MAC)完成与涂鸦云的认证与绑定,通过 DP 点(Data Point) 机制与涂鸦智能 App 双向同步设备状态,并借助事件回调、OTA、产测等扩展能力形成完整的产品闭环。

仓库中的涂鸦代码按三层组织:

层级目录职责
应用层app/demo、app/product_test、app/uart_common设备业务逻辑:DP 点解析、LED 控制、OTA、产测指令、UART 透传
SDK 层sdk/include(及未列出的 sdk 源文件)涂鸦官方协议栈:DP 编解码、加密(AES/MD5)、绑定、事件队列、GATT 发送队列
移植层port/把涂鸦 SDK 抽象接口(广播、扫描应答、时间、Flash、日志、随机数、加密等)映射到 JL 平台

SDK 对外暴露的入口 API 集中在 sdk/include/tuya_ble_api.h,应用只需按序调用 tuya_ble_sdk_init() → tuya_ble_callback_queue_register(),并在 BLE 收包处调用 tuya_ble_gatt_receive_data(),即可完成协议栈的启动与数据通路建立。涂鸦 SDK 与 JL 平台之间通过 port/tuya_ble_port_JL.c 等移植文件适配,保证 SDK 无平台依赖。

架构

flowchart TD
    subgraph sg_App["应用层 (apps/common/third_party_profile/tuya_protocol/app)"]
        Demo["tuya_ble_app_demo<br/>DP 解析 / LED 控制 / 回调注册"]
        OTA["tuya_ota<br/>OTA 数据处理"]
        ProdTest["tuya_ble_app_production_test<br/>产测指令"]
        UartHandler["tuya_ble_app_uart_common_handler<br/>UART 通用处理"]
    end

    subgraph sg_Sdk["涂鸦 SDK 层 (sdk)"]
        Api["tuya_ble_api.h<br/>对外 API 入口"]
        DataHandler["tuya_ble_data_handler<br/>DP 编解码"]
        Event["tuya_ble_event<br/>事件与回调队列"]
        Bulk["tuya_ble_bulk_data<br/>批量数据传输"]
        GattQ["tuya_ble_gatt_send_queue<br/>GATT 发送队列"]
        Feature["tuya_ble_feature_weather<br/>天气特性"]
    end

    subgraph sg_Port["移植层 (port)"]
        PortJL["tuya_ble_port_JL.c<br/>广播/扫描应答/时间/加密"]
        PortPeriph["tuya_ble_port_peripheral<br/>外设接口"]
        Stdlib["tuya_ble_stdlib.h<br/>标准库映射"]
    end

    subgraph sg_Platform["JL 平台"]
        BLE["杰理 BLE 协议栈<br/>GATT / 广播"]
        UART["UART 外设"]
        Flash["Flash 存储"]
        OS["OS 任务/信号量"]
        Crypto["AES / MD5 硬件加速"]
    end

    Demo --> Api
    OTA --> Api
    ProdTest --> Api
    UartHandler --> Api
    Api --> DataHandler
    Api --> Event
    Api --> GattQ
    Api --> Bulk
    Api --> Feature
    DataHandler --> PortJL
    Event --> PortJL
    PortJL --> BLE
    PortJL --> UART
    PortJL --> Flash
    PortJL --> OS
    PortJL --> Crypto
    PortPeriph --> BLE
    Stdlib --> OS

架构说明:

  • 应用层是开发者主要改动的部分:tuya_ble_app_demo.c 演示了完整的接入范式——构造 tuya_ble_device_param_t、调用 tuya_ble_sdk_init()、注册回调、在回调里处理 DP 数据并控制 GPIO。tuya_ota.c、tuya_ble_app_production_test.c、tuya_ble_app_uart_common_handler.c 则是可选能力,分别处理固件升级、产线测试、WIFI+BLE 组合设备的 UART 转发。
  • SDK 层以 tuya_ble_api.h 为唯一对外入口,内部的数据处理、事件分发、GATT 发送队列、批量数据、天气特性等模块对应用透明。协议版本由宏 TUYA_BLE_PROTOCOL_VERSION_HIGN 区分(仓库 Demo 支持 0x03/0x04 两套 DP 上报 API)。
  • 移植层把 SDK 的弱符号(__TUYA_BLE_WEAK)实现覆盖为 JL 平台实现。例如广播数据更新时,根据 CONFIG_BT_GATT_COMMON_ENABLE 选择走 GATT 公共服务还是传统 ble_op_* 接口,并通过注册的回调钩子注入广播内容。
  • JL 平台提供 BLE 协议栈、UART、Flash、OS 与 AES/MD5 加密能力,是涂鸦 SDK 的运行时底座。apps/spp_and_le/app_main.c 在 TUYA_DEMO_EN 下创建 user_deal 线程承载涂鸦任务调度,CONFIG_APP_TUYA 将应用动作绑定为 ACTION_TUYA。

SDK 初始化流程

涂鸦 BLE 协议栈的启动遵循"先初始化 SDK,再注册回调"的固定顺序。Demo 中的 tuya_ble_app_init() 是标准范式:

void tuya_ble_app_init(void)
{
    device_param.device_id_len = 16;    //If use the license stored by the SDK,initialized to 0, Otherwise 16 or 20.

    int ret = 0;
    device_param.use_ext_license_key = 1;
    if (device_param.device_id_len == 16) {
        memcpy(device_param.auth_key, (void *)auth_key_test, AUTH_KEY_LEN);
        memcpy(device_param.device_id, (void *)device_id_test, DEVICE_ID_LEN);
        memcpy(device_param.mac_addr.addr, mac_test, 6);
        device_param.mac_addr.addr_type = TUYA_BLE_ADDRESS_TYPE_RANDOM;
    }
    device_param.p_type = TUYA_BLE_PRODUCT_ID_TYPE_PID;
    device_param.product_id_len = 8;
    memcpy(device_param.product_id, APP_PRODUCT_ID, 8);
    device_param.firmware_version = TY_APP_VER_NUM;
    device_param.hardware_version = TY_HARD_VER_NUM;
    printf("HJY:12345\n");
    tuya_ble_sdk_init(&device_param);
    ret = tuya_ble_callback_queue_register(tuya_cb_handler);
    y_printf("tuya_ble_callback_queue_register,ret=%d\n", ret);

    //tuya_ota_init();

    //TUYA_APP_LOG_INFO("demo project version : "TUYA_BLE_DEMO_VERSION_STR);
    TUYA_APP_LOG_INFO("app version : "TY_APP_VER_STR);

}

Source: tuya_ble_app_demo.c

关键点与设计意图:

  • device_id_len 与 use_ext_license_key 共同决定授权信息的来源。Demo 将 device_id_len 置为 16 并开启外部授权(use_ext_license_key=1),此时必须手动填入 16 字节 device id、32 字节 auth key 与 6 字节 MAC;若置 0,则由 SDK 内部存储的 license 提供,App 侧无需关心密钥细节。涂鸦云端绑定正是基于这套三元组完成设备身份认证。
  • p_type = TUYA_BLE_PRODUCT_ID_TYPE_PID 表示使用产品 PID(8 字节)而非二维码/其他类型标识产品;APP_PRODUCT_ID 宏来自 tuya_ble_app_demo.h(如 'y','j','f','s','5','0','6','f'),需替换为涂鸦 IoT 平台为产品分配的 PID。
  • tuya_ble_sdk_init() 必须在所有平台初始化完成后调用(API 注释明确要求 "after all platform init complete"),因为 SDK 内部会立即申请任务、初始化定时器与加密模块,这些依赖 JL 的 OS 与硬件驱动就绪。
  • 回调注册紧随 init:tuya_ble_callback_queue_register(tuya_cb_handler) 把应用处理函数挂入 SDK 的事件队列,此后所有上行业务事件(连接状态、DP 下发、OTA 数据、解绑等)都会以队列方式异步进入 tuya_cb_handler。

初始化时序

sequenceDiagram
    participant App as 应用层 (tuya_ble_app_init)
    participant Sdk as 涂鸦 SDK (tuya_ble_api)
    participant Port as 移植层 (tuya_ble_port_JL)
    participant Jl as JL 平台 (BLE/OS/Crypto)

    App->>App: 填充 device_param<br/>(PID/auth_key/device_id/MAC/版本)
    App->>Sdk: tuya_ble_sdk_init(&device_param)
    Sdk->>Port: 平台初始化回调 (task/定时器/随机数/加密)
    Port->>Jl: os_task / timer / aes / md5
    Jl-->>Port: 资源就绪
    Sdk->>Sdk: 解析授权信息、初始化内部状态机
    App->>Sdk: tuya_ble_callback_queue_register(tuya_cb_handler)
    Sdk-->>App: 注册成功 (ret)
    Note over Sdk,Jl: BLE 连接事件到达时
    Jl->>Port: GATT 连接回调
    Port->>Sdk: tuya_ble_connected_handler()
    Sdk-->>App: 事件入队 TUYA_BLE_CB_EVT_CONNECTE_STATUS

初始化完成后,SDK 进入等待广播/连接状态;每次 BLE 连接、断开、链路加密成功,JL 协议栈侧必须把事件透传给 SDK(tuya_ble_connected_handler() / tuya_ble_disconnected_handler() / tuya_ble_link_encrypted_handler()),否则 SDK 的绑定与加密状态机不会推进。

数据通路:GATT 收包入口

涂鸦 SDK 本身不直接操作 BLE 控制器,而是要求平台在收到对端数据时"喂"给 SDK。tuya_ble_api.h 定义了收包入口:

/**
 * @brief   Function for transmit ble data from peer devices to tuya sdk.
 *
 * @note    This function must be called from where the ble data is received.
 *.
 * */
tuya_ble_status_t tuya_ble_gatt_receive_data(uint8_t *p_data, uint16_t len);

/**
 * @brief   Function for transmit uart data to tuya sdk.
 *
 * @note    This function must be called from where the uart data is received.
 *.
 * */
tuya_ble_status_t tuya_ble_common_uart_receive_data(uint8_t *p_data, uint16_t len);

Source: tuya_ble_api.h

设计意图:SDK 采用"外部分发、内部解析"的架构——平台只负责把原始字节送入 SDK,协议帧校验、加密解密、DP 编解码全部在 SDK 内部完成。这既保证了 SDK 可移植性(不绑定任何厂商的 GATT 实现),也让平台侧接入成本降到最低:只需在 GATT 写回调/通知回调里调用一次 tuya_ble_gatt_receive_data()。同理,WIFI+BLE 组合设备通过 tuya_ble_common_uart_receive_data() 把 MCU 侧 UART 数据送入 SDK;若应用自己有完整的帧解析逻辑,也可直接调用 tuya_ble_common_uart_send_full_instruction_received() 提交完整指令(含 0x55/0x66 0xaa 帧头与校验和)。

事件回调机制与 DP 数据处理

事件回调总入口

涂鸦 SDK 通过统一回调 tuya_cb_handler(tuya_ble_cb_evt_param_t *event) 向上层分发所有业务事件。Demo 中的处理分支覆盖了典型产品需要的全部事件类型:

static void tuya_cb_handler(tuya_ble_cb_evt_param_t *event)
{
    int16_t result = 0;
    printf("tuya cb_handler event->evt=0x%x\n", event->evt);
    switch (event->evt) {
    case TUYA_BLE_CB_EVT_CONNECTE_STATUS:
        TUYA_APP_LOG_INFO("received tuya ble conncet status update event,current connect status = %d", event->connect_status);
        break;
    case TUYA_BLE_CB_EVT_DP_DATA_RECEIVED:
        tuya_data_parse(event);
        break;
    case TUYA_BLE_CB_EVT_DP_DATA_REPORT_RESPONSE:
        TUYA_APP_LOG_INFO("received dp data report response result code =%d", event->dp_response_data.status);
        break;
    ...
    case TUYA_BLE_CB_EVT_DP_QUERY:
        TUYA_APP_LOG_INFO("received TUYA_BLE_CB_EVT_DP_QUERY event");
        if (dp_data_len > 0) {
#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03)
            tuya_ble_dp_data_report(dp_data_array, dp_data_len);
#endif
#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04)
            tuya_ble_dp_data_send(sn, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITH_RESPONSE, dp_data_array, dp_data_len);
#endif
        }
        break;
    case TUYA_BLE_CB_EVT_OTA_DATA:
        tuya_ota_proc(event->ota_data.type, event->ota_data.p_data, event->ota_data.data_len);
        break;
    case TUYA_BLE_CB_EVT_NETWORK_INFO:
        TUYA_APP_LOG_INFO("received net info : %s", event->network_data.p_data);
        tuya_ble_net_config_response(result);
        break;
    case TUYA_BLE_CB_EVT_TIME_STAMP:
        TUYA_APP_LOG_INFO("received unix timestamp : %s ,time_zone : %d", event->timestamp_data.timestamp_string, event->timestamp_data.time_zone);
        tuya_data_init();
        break;
    case TUYA_BLE_CB_EVT_DATA_PASSTHROUGH:
        TUYA_APP_LOG_HEXDUMP_DEBUG("received ble passthrough data :", event->ble_passthrough_data.p_data, event->ble_passthrough_data.data_len);
        tuya_ble_data_passthrough(event->ble_passthrough_data.p_data, event->ble_passthrough_data.data_len);
        break;
    default:
        TUYA_APP_LOG_WARNING("app_tuya_cb_queue msg: unknown event type 0x%04x", event->evt);
        break;
    }
    tuya_ble_inter_event_response(event);
}

Source: tuya_ble_app_demo.c

设计要点:

  • 回调末尾必须调用 tuya_ble_inter_event_response(event) 释放事件资源,否则 SDK 内部事件缓冲会泄漏,最终导致收不到新事件——这是接入时最容易遗漏的一行。
  • TUYA_BLE_CB_EVT_DP_QUERY(App 主动查询 DP 状态)要求设备缓存最近一次上报的 DP 数据(Demo 用 dp_data_array / dp_data_len 静态缓存),并在收到查询时重发,保证 App 打开即见最新状态。
  • TUYA_BLE_CB_EVT_TIME_STAMP 到达时调用 tuya_data_init() 主动上报一次 DP,利用 App 校准后的时间戳同步设备状态,典型场景是设备上电后首次配对。
  • 协议版本宏 TUYA_BLE_PROTOCOL_VERSION_HIGN 决定使用哪套发送 API:v3 用 tuya_ble_dp_data_report(),v4 用 tuya_ble_dp_data_send()(携带 sn、发送类型、模式、ack 参数)。两套 API 在 tuya_ble_api.h 中以 #if 分别编译,仓库 Demo 对两版均做了兼容。

DP 上报(设备 → App)

tuya_data_init() 演示了构造并上报一个 DP 点的完整过程:

void tuya_data_init()
{
    struct {
        uint8_t id;
        uint8_t type;
        uint16_t len;
        uint8_t data;
    } p_dp_data;

    p_dp_data.id = 1;
    p_dp_data.type = 1;
    p_dp_data.len = 0x0100;
    p_dp_data.data = tuya_led_state;

#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03)
    tuya_ble_dp_data_report(&p_dp_data, 5); //1
#endif
#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04)
    tuya_ble_dp_data_send(sn, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITH_RESPONSE, &p_dp_data, 5);
#endif
}

Source: tuya_ble_app_demo.c

DP 帧布局(Demo 结构体)为:id(DP 点编号)+ type(数据类型,1 表示 bool)+ len(长度字段,小端,Demo 中 0x0100 表示长度 1)+ data(数据负载)。SDK 会将其封装为涂鸦协议帧并通过 GATT 发送队列发出,发送结果以 TUYA_BLE_CB_EVT_DP_DATA_REPORT_RESPONSE 事件回传 status。

DP 下发(App → 设备)

tuya_data_parse() 演示了解析 App 下发的 DP 数据并执行动作:

void tuya_data_parse(tuya_ble_cb_evt_param_t *event)
{
    uint8_t *buf = event->dp_received_data.p_data;
    uint32_t sn = event->dp_received_data.sn;
    put_buf(buf, event->dp_received_data.data_len);
    struct {
        uint8_t id;
        uint8_t type;
        uint16_t len;
        uint8_t data;
    } p_dp_data;

    p_dp_data.id = buf[0];
    p_dp_data.type = buf[1];
    p_dp_data.len = 0x0100;
    p_dp_data.data = buf[4];

    switch (buf[0]) {
    case 1:
        printf("tuya switch control, onoff set to: %d\n", p_dp_data.data);
        tuya_led_state = p_dp_data.data;
        gpio_direction_output(LED_PIN, tuya_led_state);
        break;
    default:
        printf("unknow control msg len = %d, data:", buf[2]);
        break;
    }
#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x03)
    tuya_ble_dp_data_report(&p_dp_data, 5); //1
#endif
#if (TUYA_BLE_PROTOCOL_VERSION_HIGN == 0x04)
    tuya_ble_dp_data_send(sn, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITH_RESPONSE, &p_dp_data, 5);
#endif
}

Source: tuya_ble_app_demo.c

设计意图:收到 App 控制指令后先执行实际动作(Demo 中通过 gpio_direction_output(LED_PIN, ...) 控制 IO_PORTA_01 上的 LED),再把同一 DP 数据原样回传,形成"执行 + 回执"闭环。这样 App 端能立即拿到设备确认后的状态,即使设备本地状态与 App 期望不一致(例如开关联动、异常保护导致拒绝执行),回执也能如实反映真实状态。实际产品中应把 switch(buf[0]) 扩展为产品定义的每个 DP 点 id,并在 default 分支做异常处理。

端到端 DP 数据流

sequenceDiagram
    participant App as 涂鸦智能 App
    participant JlBle as JL BLE 协议栈
    participant Port as 移植层/收包入口
    participant Sdk as 涂鸦 SDK
    participant Cb as 应用回调 tuya_cb_handler
    participant Dev as 设备 GPIO/业务

    App->>JlBle: GATT Write (DP 控制帧)
    JlBle->>Port: 平台收包
    Port->>Sdk: tuya_ble_gatt_receive_data(p_data, len)
    Sdk->>Sdk: 帧校验/解密/DP 解析
    Sdk->>Cb: TUYA_BLE_CB_EVT_DP_DATA_RECEIVED 入队
    Cb->>Dev: tuya_data_parse() → GPIO 控制
    Dev-->>Cb: 执行结果
    Cb->>Sdk: tuya_ble_dp_data_send / report (回执)
    Sdk->>JlBle: GATT 发送队列 → 通知
    JlBle-->>App: Notification (DP 回执)

自定义事件

SDK 还提供应用自定义事件通道,用于在 SDK 任务上下文之外传递业务数据:

void custom_evt_1_send_test(uint8_t data)
{
    tuya_ble_custom_evt_t event;

    for (uint8_t i = 0; i < 50; i++) {
        custom_data.data[i] = data;
    }
    event.evt_id = APP_CUSTOM_EVENT_1;
    event.custom_event_handler = (void *)custom_data_process;
    event.data = &custom_data;
    tuya_ble_custom_event_send(&event);
}

Source: tuya_ble_app_demo.c

tuya_ble_custom_event_send() 把 {evt_id, handler, data} 打包投递到 SDK 事件队列,由注册的 custom_data_process() 在 SDK 上下文异步消费(Demo 中演示了 5 个自定义事件 id 的开关分支)。这为"非 BLE 中断上下文触发的业务上报"提供了线程安全入口,避免应用直接跨任务操作 SDK 内部数据结构。

JL 平台移植层(port)

涂鸦 SDK 通过弱符号(__TUYA_BLE_WEAK)定义平台抽象接口,JL 移植层在 port/tuya_ble_port_JL.c 中强制覆盖(#undef __TUYA_BLE_WEAK)为真实实现。以广播数据更新为例:

__TUYA_BLE_WEAK tuya_ble_status_t tuya_ble_gap_advertising_adv_data_update(uint8_t const *p_ad_data, uint8_t ad_len)
{
#if CONFIG_BT_GATT_COMMON_ENABLE
    ble_gatt_server_adv_enable(0);
    if (app_set_adv_data) {
        app_set_adv_data((void *)p_ad_data, (int)ad_len);
    }
    ble_gatt_server_adv_enable(1);
#else
    ble_op_adv_enable(0);
    ble_op_set_adv_data((int)ad_len, (void *)p_ad_data);
    tuya_set_adv_enable();
#endif
    return TUYA_BLE_SUCCESS;
}

Source: tuya_ble_port_JL.c

移植要点与设计意图:

  • 广播更新采用"先停后启"策略:先 ble_gatt_server_adv_enable(0) 关闭广播,通过 tuya_app_set_adv_data_register() 注册的钩子注入新广播内容,再恢复广播。这是因为 JL BLE 控制器要求广播参数变更在广播关闭状态下进行,避免协议栈内部状态冲突。
  • 双路径适配:CONFIG_BT_GATT_COMMON_ENABLE 为 1 时使用 GATT 公共服务(ble_gatt_server_adv_enable + 应用回调),否则使用传统 ble_op_adv_enable / ble_op_set_adv_data 接口。这让同一套移植代码兼容不同 SDK 配置的工程。
  • 注册钩子:tuya_app_set_adv_data_register() / tuya_app_set_rsp_data_register() 由应用在启动早期调用,把广播/扫描应答内容设置函数注入移植层;扫描应答更新 tuya_ble_gap_advertising_scan_rsp_data_update() 采用完全相同的模式。
  • 移植层还覆盖了任务创建(struct ble_task_param 保存涂鸦创建的任务信息,供调试与任务间同步)、信号量、时间戳、随机数、AES/MD5 加密、Flash 存储等接口。SDK 的加密安全通信(绑定密钥协商、数据加密)全部依赖这些底层能力。

OTA 与产测扩展

OTA(tuya_ota.c)

OTA 由回调事件驱动:TUYA_BLE_CB_EVT_OTA_DATA 触发 tuya_ota_proc(type, p_data, data_len) 处理分片数据,最终将完整固件交给 JL 的升级模块(移植层包含 update.h / update_loader_download.h 依赖)。OTA 数据流与 DP 数据流共用 GATT 通道,SDK 负责分片与校验,应用只需在回调中转发给升级模块。Demo 中 tuya_ble_app_init() 里 //tuya_ota_init(); 被注释,说明 OTA 模块按需使能。

产测(tuya_ble_app_production_test.c)

产测模块响应涂鸦产测指令,典型的产测交互包括:设备授权信息写入(通过 tuya_ble_device_update_product_id() / tuya_ble_device_update_login_key() / tuya_ble_device_update_bound_state() 等 API 动态更新),以及产测指令的异步应答(tuya_ble_api.h 在 240 行之后提供 tuya_ble_production_test_asynchronous_response() 类接口)。产测场景下 device_id_len 通常置 0,由产测工具通过指令写入授权信息,而非编译期硬编码。

UART 通用处理(tuya_ble_app_uart_common_handler.c)

针对 WIFI+BLE 组合设备(如涂鸦 WIFI 模组 + JL BLE 模组),该模块把 UART 上收到的 MCU 指令转发给 SDK(tuya_ble_common_uart_receive_data()),并处理 SDK 回给 MCU 的数据。这样 BLE 端与 WIFI 端通过涂鸦协议在 UART 上桥接,实现"App 连 BLE 控 WIFI 设备"的组合产品形态。

配置选项

宏/配置项类型默认值说明
TUYA_DEMO_ENbool工程相关使能涂鸦 Demo:在 spp_and_le/app_main.c 中创建 user_deal 线程承载涂鸦任务调度
CONFIG_APP_TUYAbool关闭将应用动作绑定为 ACTION_TUYA(任务名 "tuya")
TUYA_BLE_PROTOCOL_VERSION_HIGNint0x03/0x04涂鸦协议主版本,决定 DP 发送 API 族(v3 report 系 / v4 send 系)
TUYA_BLE_USE_OSbool1是否使用 RTOS;为 0 时应用主循环需调用 tuya_ble_main_tasks_exec() 驱动 SDK
TUYA_BLE_LINK_LAYER_ENCRYPTION_SUPPORT_ENABLEbool依赖协议版本使能链路层加密状态上报(tuya_ble_link_encrypted_handler())
TUYA_BLE_BEACON_KEY_ENABLEboolv4 可选使能 beacon key 更新(tuya_ble_device_update_beacon_key())
CONFIG_BT_GATT_COMMON_ENABLEbool工程相关移植层广播更新走 GATT 公共服务或传统 ble_op_* 路径
device_param.device_id_lenuint80/16/200 表示授权信息由 SDK license 提供;16/20 表示外部传入 device id
device_param.use_ext_license_keyuint81(Demo)是否使用外部授权密钥(auth key)
device_param.p_typeenumTUYA_BLE_PRODUCT_ID_TYPE_PID产品标识类型(PID 等)
APP_PRODUCT_IDchar[8]涂鸦平台分配的 PID产品 ID,需替换为实际值
TUYA_BLE_ADDRESS_TYPE_RANDOMenum—设备广播地址类型(Demo 用随机地址)

API 参考(tuya_ble_api.h 核心接口)

tuya_ble_status_t tuya_ble_sdk_init(tuya_ble_device_param_t *param_data)

初始化涂鸦 BLE SDK,必须在所有平台初始化完成后调用一次。

参数:

  • param_data(tuya_ble_device_param_t *):设备参数(device id、auth key、MAC、PID、版本等)

返回: tuya_ble_status_t,TUYA_BLE_SUCCESS 表示成功。

tuya_ble_status_t tuya_ble_callback_queue_register(tuya_ble_cb_handler_t handler)

注册业务事件回调(Demo 中为 tuya_cb_handler),事件以队列方式异步派发。

tuya_ble_status_t tuya_ble_gatt_receive_data(uint8_t *p_data, uint16_t len)

在 BLE 收包处调用,把对端原始数据送入 SDK。返回: TUYA_BLE_SUCCESS 表示已接收。

tuya_ble_status_t tuya_ble_dp_data_report(uint8_t *p_data, uint32_t len)(协议 v3)

上报 DP 数据(主动上报或应答 DP 查询)。

tuya_ble_status_t tuya_ble_dp_data_send(uint32_t sn, tuya_ble_dp_data_send_type_t type, tuya_ble_dp_data_send_mode_t mode, tuya_ble_dp_data_send_ack_t ack, uint8_t *p_dp_data, uint32_t dp_data_len)(协议 v4)

带发送属性的 DP 上报。type:DP_SEND_TYPE_ACTIVE(主动)/ DP_SEND_TYPE_PASSIVE(应答查询);mode:目标(云面板/App 等);ack:是否需要响应。v4 要求每个 DP 的长度字段 L 必须为 2 字节。

tuya_ble_status_t tuya_ble_dp_data_with_time_send(...)(协议 v4)

带时间戳的 DP 上报。time_type 支持 DP_TIME_TYPE_MS_STRING(13 字节毫秒字符串)与 DP_TIME_TYPE_UNIX_TIMESTAMP(4 字节 unix 时间戳)。

void tuya_ble_connected_handler(void) / void tuya_ble_disconnected_handler(void)

连接/断开事件透传,平台 BLE 回调中必须调用,驱动 SDK 内部状态机。

void tuya_ble_link_encrypted_handler(void)(需使能链路层加密)

链路层加密成功后调用,通知 SDK 进入加密通信状态。

tuya_ble_status_t tuya_ble_data_passthrough(uint8_t *p_data, uint32_t len)

数据透传:把 SDK 收到的透传数据转发给 App(对应 TUYA_BLE_CB_EVT_DATA_PASSTHROUGH 的回传路径)。

tuya_ble_status_t tuya_ble_device_update_product_id(...) / tuya_ble_device_update_login_key(...) / tuya_ble_device_update_bound_state(uint8_t state)

动态更新设备授权信息(产测/授权流程使用),信息变更后必须立即调用。

失败模式、边界情况与并发

失败模式

失败场景表现处理方式(依据源码)
授权信息错误(device id / auth key / PID 不匹配)App 无法发现/绑定设备核对 device_param 三要素与涂鸦平台一致;产测流程用 tuya_ble_device_update_* 系列 API 重新写入
事件资源未释放SDK 事件队列耗尽,收不到后续事件回调末尾必须调用 tuya_ble_inter_event_response(event)
DP 上报未带响应App 侧状态不同步使用 DP_SEND_WITH_RESPONSE,并在 TUYA_BLE_CB_EVT_DP_DATA_REPORT_RESPONSE 中检查 status
OTA 数据分片丢失升级失败依赖 SDK 分片校验与 JL 升级模块;失败后需重新发起升级
广播更新未关闭广播广播内容不生效或协议栈冲突移植层采用"先 adv_enable(0) 再更新再 adv_enable(1)"的原子序列
回调事件为未知类型日志打印 unknown event type 0x%04xSDK 升级后需同步扩展应用回调分支

边界情况

  • 首次上电/未绑定:设备应正常广播并可被 App 发现;Demo 在收到 TUYA_BLE_CB_EVT_TIME_STAMP(App 连接后下发时间)时调用 tuya_data_init() 主动上报一次 DP,确保 App 端状态初始化。
  • App 查询 DP(TUYA_BLE_CB_EVT_DP_QUERY):设备必须缓存最近一次上报数据(Demo 的 dp_data_array/dp_data_len),否则查询时无数据可回。
  • 解绑/复位:TUYA_BLE_CB_EVT_UNBOUND、TUYA_BLE_CB_EVT_ANOMALY_UNBOUND、TUYA_BLE_CB_EVT_DEVICE_RESET 三个事件分别对应正常解绑、异常解绑与设备复位,产品应在此清除本地绑定状态。
  • 协议版本差异:Demo 对 v3/v4 两套 DP API 做了 #if 兼容,切换协议版本时只需改宏,业务逻辑(tuya_data_parse 的 DP 布局)不变。

并发与任务模型

涂鸦 SDK 的事件回调运行在 SDK 内部任务上下文(JL 侧由 tuya_ble_port_JL.c 创建,apps/spp_and_le/app_main.c 在 TUYA_DEMO_EN 下额外创建 user_deal 线程承载涂鸦任务调度),而 GPIO 控制、UART 收包发生在其他上下文。因此:

  • 回调内不要执行耗时操作(如长 Flash 写入、大块 memcpy),应把工作投递到应用自己的任务;tuya_ble_custom_event_send() 提供了跨任务投递的安全通道。
  • tuya_ble_gatt_receive_data() / tuya_ble_common_uart_receive_data() 从 BLE/UART 中断或回调上下文调用,SDK 内部负责入队与加锁,应用侧无需重复加锁。
  • 广播更新接口内部使用 ble_gatt_server_adv_enable(0/1) 成对调用,天然互斥;多个任务同时触发广播更新时仍建议统一入口串行化,避免广播反复抖动。

性能与运维考虑

  • GATT 发送队列(sdk/include/tuya_ble_gatt_send_queue.h)是 DP 上报的缓冲层:高频 DP 上报会被排队,避免单次通知超长导致丢包;发送结果通过报告响应事件回传。产品应控制 DP 上报频率,长数据使用批量数据模块(tuya_ble_bulk_data.h)。
  • 日志:Demo 大量使用 TUYA_APP_LOG_* / printf,量产固件建议关闭或降级日志等级,避免 UART 日志与业务 UART 抢占带宽。
  • 升级可靠性:OTA 走 BLE 通道,建议在连接稳定、信号好的环境下升级,必要时结合链路层加密(TUYA_BLE_LINK_LAYER_ENCRYPTION_SUPPORT_ENABLE)防止固件被窃取。
  • 调试建议:tuya_ble_port_JL.c 中 struct ble_task_param 保留了涂鸦创建的任务名与信号量,可在异常时打印任务状态辅助定位死锁/阻塞问题。

扩展点

  1. DP 点扩展:在 tuya_data_parse() 的 switch (buf[0]) 中按产品定义扩展 DP id;上报侧用 tuya_data_init() 同款结构体构造多 DP 帧(数组 + 总长度)。
  2. 新回调事件:SDK 事件号在 tuya_ble_event.h 中定义,应用在 tuya_cb_handler 的 switch 中添加分支即可,无需改动 SDK。
  3. 新平台移植:参考 port/tuya_ble_port_JL.c 覆盖 __TUYA_BLE_WEAK 接口(广播、扫描应答、任务、信号量、时间、随机数、Flash、AES/MD5),并保持 tuya_ble_gatt_receive_data() 收包入口不变。
  4. 功能特性开关:tuya_ble_feature_weather.h(天气特性)、tuya_ble_bulk_data.h(批量数据)、tuya_ble_data_handler.h(DP 编解码)均为可选的 SDK 能力,按产品形态使能。
  5. Mesh 形态:如需 Mesh 组网灯控,见 apps/mesh/examples/TUYA_light.c(SIG_MESH_TUYA_LIGHT=8,TUYA_CATEGORY=0x1011,产品 PID "yjfs506f"),与本文的 BLE 单点接入是两套独立协议栈。

测试覆盖

仓库未提供针对涂鸦协议栈的独立单元测试工程;接入验证依赖以下路径:

  • Demo 编译验证:spp_and_le 工程 CONFIG_APP_TUYA / TUYA_DEMO_EN 使能后编译,验证任务创建与回调注册(tuya_ble_callback_queue_register 返回值检查)。
  • 真机联调:涂鸦智能 App 配网 → 绑定 → DP 下发(App 控制 LED)→ DP 上报(设备状态回显)→ 解绑 → 重新绑定,覆盖 tuya_cb_handler 全部事件分支。
  • 产测流程:tuya_ble_app_production_test.c 配合涂鸦产测工具验证授权写入与指令应答。
  • Mesh 例程:apps/mesh/examples/TUYA_light.c 在 CONFIG_MESH_MODEL == SIG_MESH_TUYA_LIGHT 下编译,验证涂鸦 Mesh 灯模型。

相关链接

  • tuya_ble_api.h(SDK 对外 API)
  • tuya_ble_app_demo.c(接入示例)
  • tuya_ble_app_demo.h(PID/版本宏)
  • tuya_ble_port_JL.c(JL 平台移植层)
  • tuya_ota.c(OTA 处理)
  • tuya_ble_app_production_test.c(产测)
  • tuya_ble_app_uart_common_handler.c(UART 通用处理)
  • app_main.c(应用集成/任务创建)
  • TUYA_light.c(涂鸦 Mesh 灯控例程)
  • 涂鸦 Mesh 模型:见 Mesh 开发相关页面;其他第三方协议(腾讯、阿里 HiLink)见 third_party_profile 对应页面。
Prev
Sig Mesh 方案
Next
腾讯连连接入