阿里云IoT针对蓝牙设备,提供了一套基于蓝牙BLE链接轻量级安全Breeze方案,可以实现APP-设备-云的完整链路。本文主要介绍在设备端上,如何让硬件具备蓝牙BLE能力,并与手机蓝牙连接后通过一套SDK建立安全通道,将数据推送给云端。除此之外,通道也具备作为蓝牙辅助配网,和OTA升级的能力。

Breeze组件源代码位于componet/wireless/ble/breeze下,头文件位于include/wireless/bluetooth/breeze下。

API列表

breeze_start 启动breeze SDK服务。用户使用此接口初始化和启动breeze服务
breeze_end 停止breeze服务,用户调用此接口停止breeze服务
breeze_post 推送设备端状态数据至移动端,使用BLE indicate方式
breeze_post_fast 推送设备端状态数据至移动端,区别在于使用BLE notification方式
breeze_post_ext 设备端上报带有cmd字段的数据至移动端
breeze_append_adv_data 广播内容增加用户自定义数据
breeze_restart_advertising SDK重启蓝牙广播
breeze_awss_init 该接口对蓝牙配网SDK进行初始化。在用户业务逻辑初始化阶段调用
breeze_awss_end 停止蓝牙配网服务

使用

使能Breeze SDK

在组件aos.mk添加breeze组件:
$(NAME)_COMPONENTS := breeze

包含头文件

应用需要在应用源文件添加breeze部分对外头文件:

#include <breeze.h>

使用示例

使用SDK API

可参考事例breezeapp, 路径在application/example/example_legacy/bluetooth/breezeapp

  1. 准备设备信息,包含设备Product ID, device Secret, device name, product key, product secret。

/*
* 1.定义五元组
*/
#define PRODUCT_ID     577245
#define DEVICE_SECRET  "************************"
#define DEVICE_NAME    "12345678901234567890"
#define PRODUCT_KEY    "**********"
#define PRODUCT_SECRET "***********"

2.SDK初始化,包括对结构体的初始化,回调函数注册等。

static void alink_work(void *arg)
{
    bool                 ret;
    uint32_t             err_code;
    struct device_config init_bzlink;
    uint8_t              bd_addr[BD_ADDR_LEN] = { 0 };
//如果使能OTA,注册相关回调函数
#ifdef CONFIG_AIS_OTA
    ota_breeze_service_manage_t ota_module;
#endif

    (void)arg;

    //初始化结构体
    memset(&init_bzlink, 0, sizeof(struct device_config));
    init_bzlink.product_id        = PRODUCT_ID;
    init_bzlink.status_changed_cb = dev_status_changed_handler;
    init_bzlink.set_cb            = set_dev_status_handler;
    init_bzlink.get_cb            = get_dev_status_handler;
    init_bzlink.apinfo_cb         = apinfo_handler;


    init_bzlink.product_secret_len = strlen(PRODUCT_SECRET);
    memcpy(init_bzlink.product_secret, PRODUCT_SECRET,
    init_bzlink.product_secret_len);

    init_bzlink.product_key_len = strlen(PRODUCT_KEY);
    memcpy(init_bzlink.product_key, PRODUCT_KEY, init_bzlink.product_key_len);

    init_bzlink.device_key_len = strlen(DEVICE_NAME);
    memcpy(init_bzlink.device_key, DEVICE_NAME, init_bzlink.device_key_len);

#ifndef CONFIG_MODEL_SECURITY
    init_bzlink.secret_len = strlen(DEVICE_SECRET);
    memcpy(init_bzlink.secret, DEVICE_SECRET, init_bzlink.secret_len);
#else
    init_bzlink.secret_len = 0;
#endif

#ifdef CONFIG_AIS_OTA
    ota_module.is_ota_enable = true;
    ota_module.verison.fw_ver_len = strlen(SOFTWARE_VERSION);
    if(ota_module.verison.fw_ver_len > sizeof(ota_module.verison.fw_ver)) {
        printf("breeze version too long");
        return;
    }
    memcpy(ota_module.verison.fw_ver, SOFTWARE_VERSION, ota_module.verison.fw_ver_len);
    ota_module.get_dat_cb = NULL;
    ota_breeze_service_init(&ota_module);
    init_bzlink.ota_cb = ota_module.get_dat_cb;
#else
    init_bzlink.ota_cb = ota_handler;
#endif
    //开启breeze SDK
    ret = breeze_start(&init_bzlink);
    if (ret != 0) {
        printf("breeze_start failed.\r\n");
    } else {
        printf("breeze_start succeed.\r\n");
    }
}

int application_start(int argc, char **argv)
{
    alink_work(NULL);
    return 0;
}

3.整个通道建立以及对应事件后会通过回调来驱动

  • 连接状态回调,会打印连接状态的状态:蓝牙连接,断开,认证,发送数据完成等。

static bool ble_connected = false;
static void dev_status_changed_handler(breeze_event_t event)
{
    switch (event) {
        case CONNECTED:
            ble_connected = true;
            printf("dev_status_changed(): Connected.\n");
            break;

        case DISCONNECTED:
            ble_connected = false;
            printf("dev_status_changed(): Disconnected.\n");
            break;

        case AUTHENTICATED:
            printf("dev_status_changed(): Authenticated.\n");
            break;

        case TX_DONE:
            printf("dev_status_changed(): Tx-done.\n");
            break;

        default:
            break;
    }
}

  • 手机下发数据回调函数,与get_dev_status_handler区别在于不需要单独使用0x03格式命令来回复给手机端。

static void set_dev_status_handler(uint8_t *buffer, uint32_t length)
{
    printf("%s command (len: %u) received.\r\n", __func__, length);
}

  • 手机下发数据回调函数,需要回复0x03格式命令并填充数据。

static void get_dev_status_handler(uint8_t *buffer, uint32_t length)
{
    /* echo the receiving data */
    uint8_t cmd = 0x03;
    breeze_post_ext(cmd, buffer, length);
}

  • 辅助配网信息回调,如果有对应配网信息会通过此回调通知

static void apinfo_handler(breeze_apinfo_t *ap)
{
    //解析配网信息并处理辅助配网逻辑
}

  • OTA事件数据回调,在OTA中需要处理此事件

static void ota_handler(breeze_otainfo_t *ota)
{
    if(ota != NULL){
        if(ota->type == OTA_CMD){
            printf("RECV OTA CMD\n");
        } else if(ota->type == OTA_EVT){
            printf("RECV OTA EVT (%d)\n", ota->cmd_evt.m_evt.evt);
        } else{
            printf("unknown ota info\r\n");
        }

    }
}

API详情

breeze_start

启动breeze SDK服务。用户使用此接口初始化和启动breeze服务。

函数原型

int breeze_start(struct device_config *dev_conf)

输入参数

dev_conf device_config 初始化Breeze SDK的信息,包含设备信息,回调函数等

返回参数

0-成功;-1-失败

调用示例

参考使用SDK API部分示例。

breeze_end

函数原型

int breeze_end(void)

输入参数

返回参数

0-成功;-1-失败

调用示例

int ret = breeze_end();
if (ret != 0){
    printf("Breeze SDK deinit failed\n")
}

breeze_post

推送设备端状态数据至移动端,使用BLE indication方式。

函数原型

uint32_t breeze_post(uint8_t *buffer, uint32_t length)

输入参数

buffer uint8_t* 待发送数据指针
length uint32_t 待发送数据长度

返回参数

0-成功;其他错误值-失败

调用示例

//发送数据
uint8_t data = {0x01, 0x02, 0x03, 0x04, 0x05};
breeze_post(date, sizeof(data));

//然后在异步事件中判断是否有TX_DONE事件回调,有则证明发送成功
static void dev_status_changed_handler(breeze_event_t event)
{
    switch (event) {
      	//其他事件...
        case TX_DONE:
            printf("dev_status_changed(): Tx-done.\n");
            break;

        default:
            break;
    }
}

breeze_post_fast

和breeze_post类似,推送设备端状态数据至移动端,区别在于使用BLE notification方式,因此无法和breeze_post一样在dev_status_changed_handler中检测TX_DONE事件。

函数原型

uint32_t breeze_post_fast(uint8_t *buffer, uint32_t length)

输入参数

buffer uint8_t* 待发送数据指针
length uint32_t 待发送数据长度

返回参数

0-成功;其他错误值-失败。

调用示例

//发送数据
uint8_t data = {0x01, 0x02, 0x03, 0x04, 0x05};
breeze_post_fast(date, sizeof(data));

breeze_post_ext

设备端上报带有cmd字段的数据至移动端,常用的cmd命令字段值有0x03,表示对get_cb的回应消息,另外OTA模块有自定义的cmd字段,同样可以和breeze_post一样在dev_status_changed_handler中检测TX_DONE事件

函数原型

uint32_t breeze_post_ext(uint8_t cmd, uint8_t *buffer, uint32_t length)

输入参数

buffer uint8_t* 待发送数据指针
length uint32_t 待发送数据长度
cmd uint8_t* 推送给移动端的cmd类型

返回参数

0-成功;其他错误值-失败

调用示例:

uint8_t data = {0x01, 0x02, 0x03, 0x04, 0x05};
uint8_t cmd = 0x03;
breeze_post_ext(cmd ,date, sizeof(data));

//然后在异步事件中判断是否有TX_DONE事件回调,有则证明发送成功
static void dev_status_changed_handler(breeze_event_t event)
{
    switch (event) {
      	//其他事件...
        case TX_DONE:
            printf("dev_status_changed(): Tx-done.\n");
            break;

        default:
            break;
    }
}

breeze_append_adv_data

广播内容增加用户自定义数据,调用后需要调用breeze_restart_advertsing。

函数原型

void breeze_append_adv_data(uint8_t *data, uint32_t len)

输入参数

buffer uint8_t* 待增加数据指针
length uint32_t 待增加数据长度

返回参数

调用示例:

static bool ble_connected = False;
static void continue_adv_work(void *arg)
{
    static uint8_t user_adv[] = {0x55, 0xaa};

    user_adv[0]++;
    user_adv[1]++;
    breeze_append_adv_data(user_adv, sizeof(user_adv) / sizeof(user_adv[0]));

    if (!ble_connected) breeze_restart_advertising();
    aos_post_delayed_action(2000, continue_adv_work, NULL);
}
#endif

breeze_restart_advertising

SDK重启蓝牙广播,其广播内容是默认状态。

函数原型

void breeze_restart_advertising()

输入参数

返回参数

调用示例:

static void event_handler(ali_event_t *p_event)
		//.....
       case BZ_EVENT_DISCONNECTED:
            core_reset();
            notify_status(DISCONNECTED);
#if BZ_ENABLE_OTA
            m_disc_evt.type = OTA_EVT;
            m_disc_evt.cmd_evt.m_evt.evt = ALI_OTA_ON_DISCONNECTED;
            m_disc_evt.cmd_evt.m_evt.d = 0;
            b_notify_upper = true;
            if(g_disconnect_by_ota == true){
            } else
#endif
            {
#ifndef BLE_ADV_ASYN
				//在应用层或者接口层直接调用
                 breeze_restart_advertising();
#endif
            }
            break;

breeze_awss_init

该接口对蓝牙配网SDK进行初始化。在用户业务逻辑初始化阶段调用。

函数原型

void breeze_awss_init(apinfo_ready_cb cb, breeze_dev_info_t *info)

输入参数

cb apinfo_ready_cb 设备完成WiFi信息(SSID、密码)后的回调函数,由用户定义/提供,并由SDK完成调用
info breeze_dev_info_t 为设备信息,包括ProductID,Product Key,Product Secret,Device Name,Device Secret等字段,由用户提供

返回参数

调用示例:

//配网回调函数
static void apinfo_ready_handler(breeze_apinfo_t *ap)
{
    if (!ap)
        return;

    memcpy(&apinfo, ap, sizeof(apinfo));
    //这里拿到配网信息(password,ssid等)之后的流程
}

//需要蓝牙配网的五元组信息
breeze_dev_info_t dinfo = {
        .product_id = PRODUCT_ID,
        .product_key = PRODUCT_KEY,
        .product_secret = PRODUCT_SECRET,
        .device_name = DEVICE_NAME,
        .device_secret = DEVICE_SECRET
    };
breeze_awss_init(apinfo_ready_handler, &dinfo);

breeze_awss_end

停止蓝牙配网服务。

函数原型

void breeze_awss_end()

输入参数

返回参数

调用示例:

配置说明

在选择后可以继续根据配置使能相对应的功能和配置。

配置项说明

  • Enable secure and sequencial adv: 连续安全广播,默认关闭。
  • Enable auth per product: 使能设备一型一密安全,默认一机一密。
  • Enable AWSS:使能蓝牙辅助配网功能,SDK默认关闭。
  • Enable authentication:使能SDK认证功能,默认打开
  • Authentication offline, 离线认证使能,在第一次连接后会存储密钥下次连接后不需要再次通过云端鉴权认证。 默认在线认证,每次连接手机端需要和云端再次交互认证设备。
  • Extend MTU:扩展SDK的MTU,根据蓝牙ATT层的MTU动态决定MTU。
  • Enable default built-in components:默认选择适配自带蓝牙协议栈和安全部分,客户在对接到第三方ble stack, OS, 安全组件需要关闭此选项。

标准宏和结构体说明

struct device_config

breeze SDK初始化结构体,SDK初始化必须填充此结构体。

struct device_config
{
    uint8_t         bd_addr[BD_ADDR_LEN];
    char            model[STR_MODEL_LEN];
    uint32_t        product_id;
    char            product_key[STR_PROD_KEY_LEN];
    uint8_t         product_key_len;//product key的长度
    char            device_key[STR_DEV_KEY_LEN]; //设备的device name,字符串
    uint8_t         device_key_len;//设备的device name长度
    char            secret[STR_SEC_LEN];//设备的密钥,设备唯一,字符串
    uint8_t         secret_len;//设备的密钥长度
    char            product_secret[STR_PROD_SEC_LEN];
    uint8_t         product_secret_len;
    dev_status_changed_cb status_changed_cb;
    set_dev_status_cb     set_cb;
    get_dev_status_cb     get_cb;
    apinfo_ready_cb       apinfo_cb;
    ota_dev_cb            ota_cb;
};

其各个参数说明如下:

  • bd_addr 蓝牙地址
  • model 根据PID的得到的model信息,小端格式,对应广播字段的设备ID
  • product_id product ID信息,由云端生成
  • product_key 同一类型设备的product key
  • product_key_len product key的长度
  • device_key 设备的device name
  • device_key_len 设备device name的长度
  • secret 设备的密钥,设备唯一
  • secret_len 设备密钥的长度
  • product_secret 同一类型的设备的product secret
  • product_secret_len product secret的长度,同一类型设备唯一

列出结构体中有关回调函数:

dev_status_changed_cb

SDK状态回调函数,包含连接,认证,断开,数据发送完成等。 回调原型

typedef void (*dev_status_changed_cb)(breeze_event_t event)

set_dev_status_cb

回调函数原型

typedef void (*set_dev_status_cb)(uint8_t *buffer, uint32_t length)

get_dev_status_cb

回调函数原型

typedef void (*get_dev_status_cb)(uint8_t *buffer, uint32_t length)

apinfo_ready_cb

蓝牙辅助配网事件回调接口。 回调函数原型

typedef void (*apinfo_ready_cb)(breeze_apinfo_t *ap);

ota_dev_cb

OTA事件和数据回调接口。 回调函数原型

typedef void (*ota_dev_cb)(breeze_otainfo_t *otainfo);