蓝牙Mesh网络是一种多对多通信网络,节点设备之间可以相互通信,具有覆盖范围大、功耗低、安全性高、入网灵活、支持大规模节点设备等特点。 蓝牙SIG组织于2017年7月发布了蓝牙Mesh V1.0标准,AliOS Things BLE Mesh协议栈是基于该标准的实现。下面的架构图,展示了AliOS Things中提供的蓝牙相关软件产品,以及BLE Mesh协议栈在产品架构中的位置。

Ble mesh组件源代码位于component/wireless/bluetooth/blemesh下,头文件位于include/wireless/bluetooth/blemesh下。

API列表

bt_mesh_init 蓝牙Mesh协议栈初始化
bt_mesh_reset 蓝牙Mesh节点状态重置
bt_mesh_prov_enable 节点Provision使能设置(ADV或者GATT)
bt_mesh_prov_disable 节点Provision禁止设置(ADV或者GATT)
bt_mesh_model_msg_init BLE model消息初始化
bt_mesh_model_send 发送一条应用层消息
bt_mesh_model_publish 发送model的发布消息
bt_mesh_get_shell_cmd_list 获取BLE mesh相关的命令

使用

组件使能需要在需要引用blemesh组件的aos.mk中添加:

$(NAME)_COMPONENTS := bt_mesh

包含头文件

应用需要在源文件中添加ble mesh的头文件

#include <blemesh.h>

使用示例

1.蓝牙协议初始化

int ret;
ret = bt_enable(bt_ready);
if (ret) {
    printk("Bluetooth init failed (err %d)\n", ret);
  }

//然后在协议栈初始化成功回调函数中执行ble mesh初始化的流程

2.Mesh composition data准备

该步骤准备provision过程中所需的composition data,包括company ID、节点包含的elements信息。本示例中,element包括Configuration Client、Configuration Server、Sensor Client三个Model。

static struct bt_mesh_model root_models[] = {
        /* BLE mesh强制配置server model */
        BT_MESH_MODEL_CFG_SRV(&cfg_srv),
        BT_MESH_MODEL_CFG_CLI(&cfg_cli),
        BT_MESH_MODEL(BT_MESH_MODEL_ID_SENSOR_CLI, temp_cli_op,
                      &temp_cli_pub, NULL),
};

static struct bt_mesh_elem elements[] = {
        BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
};

/* 配置一个Provisioning Node的 composition data */
static const struct bt_mesh_comp comp = {
        .cid = CID_INTEL,
        .elem = elements,
        .elem_count = ARRAY_SIZE(elements),
};

3.Mesh provision数据准备

Mesh provision时所需的参数,包括设备uuid、OOB输出/入方式、provision完成的回调处理函数等。该示例代码中,指定设备uuid为0xdddd,OOB以数字形式输出,并指定了provision完成后的回调。

static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };

static const struct bt_mesh_prov prov = {
        .uuid = dev_uuid,
        .output_size = 4,
        .output_actions = BT_MESH_DISPLAY_NUMBER,
        .output_number = output_number,
        .complete = prov_complete,
};

4. 初始化mesh协议栈

int ret;
ret = bt_mesh_init(&prov, &comp);
if (ret) {
	printk("Initializing mesh failed (err %d)\n", ret);
	return;
 }

5. 使能provision

该步骤使能节点的provision功能,使设备可以被provisioner设备发现和provision。provision类型支持PB-ADV(BT_MESH_PROV_GATT)和PB-GATT(BT_MESH_PROV_ADV)。 通过以上几个步骤后,设备还需要主动调用bt_mesh_prov_enable来具备被provisioner发现和配置的功能,传入参数指定参数PB-GATT或者PB-ADV方式,或者都指定。用户可以通过Bluez meshctl、nRF Mesh手机APP等工具对该设备进行provision操作。

bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);

API详情

bt_mesh_init

初始化BLE mesh协议栈,成功之后Node需要调用bt_mesh_prov_enable()来使能unprovisioned adv。

函数原型

int bt_mesh_init(const struct bt_mesh_prov *prov, const struct bt_mesh_comp *comp)

输入参数

prov const struct bt_mesh_prov * provision配置信息,包含uuid、OOB认证方式和认证信息长度、provision相关回调等
comp const struct bt_mesh_comp * 节点支持的element、model等配置

返回参数

0 :成功。

负值:错误, 见错误编码定义部分。

调用示例:

/*定义Node上的各个element*/
static struct bt_mesh_model root_models[] = {
    BT_MESH_MODEL_CFG_SRV(&cfg_srv),
    BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
};
static struct bt_mesh_elem elements[] = {
    BT_MESH_ELEM(0, root_models, vnd_models),
};
static const struct bt_mesh_comp comp = {
    .cid = CID_INTEL,
    .elem = elements,
    .elem_count = ARRAY_SIZE(elements),
};
/*定义provisioning的能力和属性*/
static const struct bt_mesh_prov prov = {
    .uuid = dev_uuid,
    .output_size = 4,
    .output_actions = BT_MESH_DISPLAY_NUMBER,
    .output_number = output_number,
    .complete = prov_complete,
    .reset = prov_reset,
};

/*初始化调用BLE mesh协议栈*/

int ret;
ret = bt_mesh_init(&prov, &comp);
if (ret) {
    printk("Initializing mesh failed (err %d)\n", ret);
    return;
}

bt_mesh_reset

蓝牙Mesh节点状态重置。调用该接口后,设备需要重新provision才能加入mesh网络。此外,调用该接口后,设备不会自动广播unprovision beacon,要调用bt_mesh_prov_enable接口重新使能相应bearer的广播。

函数原型

void bt_mesh_reset(void)

输入参数

返回参数

调用示例

static int cmd_reset(int argc, char *argv[])
{
    bt_mesh_reset();
    printk("Local node reset complete\n");
    return 0;
}

bt_mesh_prov_enable

Provision使能设置,调用此接口设备才能进入unprovisioned广播态,参数可以指定provision方式(PB-ADV或者PB-GATT)

函数原型

int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers)

输入参数

bearer bt_mesh_prov_bearer_t Bearer设置,ADV或者GATT。

返回参数

0 :成功。负值:错误, 具体错误见错误编码定义部分。

bt_mesh_prov_disable

关闭Provision能力(PB-ADV或者PB-GATT)。

函数原型

int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers)

输入参数

bearer bt_mesh_prov_bearer_t Bearer provision方式,ADV或者GATT,或者全部。

返回参数

0 :成功。

负值:错误编码, 见错误编码定义部分。

调用示例:

err = bt_mesh_prov_disable(bearer);
if (err) {
    printk("Failed to disable %s (err %d)\n", bearer2str(bearer), err);
} else {
    printk("%s disabled\n", bearer2str(bearer));
}

bt_mesh_model_msg_init

model消息初始化。该接口初始化消息buf,并填充opcode头信息。

函数原型

void bt_mesh_model_msg_init(struct net_buf_simple *msg, u32_t opcode)

输入参数

msg struct net_buf_simple * 用于发送消息的net_buf
opcode u32_t 消息对应的opcode码

返回参数

调用示例

static void gen_onoff_get(struct bt_mesh_model *model,
                          struct bt_mesh_msg_ctx *ctx,
                          struct net_buf_simple *buf)
{
    struct net_buf_simple *msg = NET_BUF_SIMPLE(2 + 1 + 4);
    printk("onoff get: addr 0x%04x onoff 0x%02x\n",
           model->elem->addr, g_onoff_state.current);
    bt_mesh_model_msg_init(&msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
    net_buf_simple_add_u8(&msg, g_onoff_state.current);

    if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
        printk("Unable to send On Off Status response\n");
    }
}

bt_mesh_model_send

发送一条应用层消息。

函数原型

int bt_mesh_model_send(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct net_buf_simple *msg,
               const struct bt_mesh_send_cb *cb, void *cb_data)

输入参数

model struct bt_mesh_model * 消息对应的model
ctx struct bt_mesh_msg_ctx * 消息的上下文信息,包括key索引、对端地址、TTL等
msg struct net_buf_simple * 需要发送的消息
cb const struct bt_mesh_send_cb * 消息发送完毕的回调。该参数可选
cb_data void* 回调函数的用户参数

返回参数

0 :成功。

负值:错误编码,见错误编码定义部分。

调用示例

可参考bt_mesh_model_msg_init部分示例。

bt_mesh_model_publish

发送model的发布消息。调用该接口前,用户确保需要model的bt_mesh_model_pub.msg中包含有效的消息。该接口仅用于非周期性的消息发布。

函数原型

int bt_mesh_model_publish(struct bt_mesh_model *model)

输入参数

model struct bt_mesh_model * 消息对应的model

返回参数

0:成功 。

负值:错误, 编码见错误编码定义部分。

调用示例:

bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
net_buf_simple_add_u8(msg, g_onoff_state.current);
err = bt_mesh_model_publish(model);
if (err) {
    printk("bt_mesh_model_publish err %d\n", err);
}

bt_mesh_get_shell_cmd_list

获取ble mesh相关的命令,此部分命令列表可以参考通过shell 命令部分。

函数原型

struct mesh_shell_cmd *bt_mesh_get_shell_cmd_list()

输入参数

返回参数

struct mesh_shell_cmd的命令列表。

调用示例:

 mesh_cmds = bt_mesh_get_shell_cmd_list();
 if (mesh_cmds) {
     p = mesh_cmds;
     while (p->cmd_name != NULL) {
         if (strcmp(p->cmd_name, cmd_str) != 0) {
             p++;
             continue;
          }
          if (p->cb) {
             no_match = 0;
             p->cb(argc - 1, &(argv[1]));//这里会调用注册的回调函数
           }
           break;
        }
    }

配置说明

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

配置项说明

  • PB-GATT: 通过GATT链路进行provisioning。
  • GATT Proxy: 支持GATT代理服务,一般用在GATT client和mesh网络之间。
  • LOW-Power:Low power功能。
  • FRIEND:friend功能。
  • CFG_CLI:配置客户端,基本model。
  • HEALTH_CLI:基本model health。
  • standalone deploy:单独编译ble mesh组件,不包含依赖的ble host。
  • enable shell configuration component:使能shell命令的功能。

标准宏和结构体说明

struct bt_mesh_prov

bt mesh属性和能力的结构体。

static struct bt_mesh_prov prov = {
    .uuid = dev_uuid,
    .link_open = link_open,
    .link_close = link_close,
    .complete = prov_complete,
    .reset = prov_reset,
    //.static_val = NULL,
    //.static_val_len = 0,
    .output_size = 6,
    .output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
    .output_number = output_number,
    .output_string = output_string,
    .input_size = 6,
    .input_actions = (BT_MESH_ENTER_NUMBER | BT_MESH_ENTER_STRING),
    .input = input,
};

struct bt_mesh_provisioner

BLE mesh provisioner实例,初始化调用。如:

static struct bt_mesh_provisioner provisioner = {
    .prov_uuid              = 0,
    .prov_unicast_addr      = PROVISIONER_UNICAST_ADDR,
    .prov_start_address     = NODE_START_UNICAST_ADDR,
    .prov_attention         = 0,
    .prov_algorithm         = 0,
    .prov_pub_key_oob       = 1,
    .prov_pub_key_oob_cb    = provisioner_pub_key_oob,
    .prov_static_oob_val    = default_static_oob,
    .prov_static_oob_len    = 4,
    .prov_input_num         = provisioner_input_num,
    .prov_output_num        = provisioner_output_num,
    .flags                  = 0,
    .iv_index               = 0,
    .prov_link_open         = provisioner_link_open,
    .prov_link_close        = provisioner_link_close,
    .prov_complete          = provisioner_complete,
};

ret = bt_mesh_init(&prov, &comp, &provisioner);
if (ret) {
    printk("Mesh initialization failed (err %d)\n", ret);
}

struct bt_mesh_model_pub

model publication的定义。

struct bt_mesh_model_pub {
    /** 上下文的model,协议栈初始化需要设置. */
    struct bt_mesh_model *mod;

    u16_t addr;         /**< Publish 地址. */
    u16_t key;          /**< Publish AppKey 索引. */

    u8_t  ttl;          /**< Publish 的TTL. */
    u8_t  retransmit;   /**< 重试次数. */
    u8_t  period;       /**< Publish 时间长度. */
    u8_t  period_div:4, /**< Public 时间的除数因子. */
          cred:1,       /**< Friendship Credentials Flag. */
          count:3;      /**< 剩余重传次数. */

    u32_t period_start; /**< 当前时间段的开始时间. */

    struct net_buf_simple *msg;

    
    int (*update)(struct bt_mesh_model *mod);

    /** Publish 消息的定时器. */
    struct k_delayed_work timer;
};

struct bt_mesh_elem

bt mesh的结构体抽象。

struct bt_mesh_elem {
    /* 单播地址,在provisioning的时候设置 */
    u16_t addr;

    /* 地址的描述符  */
    const u16_t loc;

    const u8_t model_count;
    const u8_t vnd_model_count;

    struct bt_mesh_model * const models;
    struct bt_mesh_model * const vnd_models;
};

struct bt_mesh_msg_ctx

mesh发送消息的结构体。

struct bt_mesh_msg_ctx {
    /** 发送消息目的NetKey的索引*/
    u16_t net_idx;

    /** 要加密的AppKey索引. */
    u16_t app_idx;

    /** 目的地址*/
    u16_t addr;

    /** 收到的TTL */
    u8_t  recv_ttl:7;

    /** 当使用ACK的时候强制发送 */
    u8_t  send_rel:1;

    /** 发送TTL. */
    u8_t  send_ttl;
};

struct bt_mesh_model_op

mesh model的opcode结构体,包含opcode,handler。

struct bt_mesh_model_op {
    /* ble mesh的消息的OpCode  */
    const u32_t  opcode;

    /* 最小的消息长度 */
    const size_t min_len;

    /* 对应opcode的回调函数 */
    void (*const func)(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct net_buf_simple *buf);
};

struct bt_mesh_model

bt mesh model的实例。

struct bt_mesh_model {
    union {
        const u16_t id;
        struct {
            u16_t company;
            u16_t id;
        } vnd;
    };

    /* Model属于的Element */
    struct bt_mesh_elem *elem;

    /* Model的Publication 上下文*/
    struct bt_mesh_model_pub * const pub;

    /* AppKey列表 */
    u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];

    /* Subscription 列表 (组播或者虚拟地址) */
    u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT];

    const struct bt_mesh_model_op * const op;

    /* Model自定义用户地址 */
    void *user_data;
};

struct mesh_shell_cmd

BLE mesh 命令函结构体定义。

struct mesh_shell_cmd {
        const char *cmd_name;
        shell_cmd_function_t cb;
        const char *help;
        const char *desc;
};

BT_MESH_ELEM ( loc, mods, _vnd_mods)

BLE mesh element的抽象。可以使用此宏定义一个element, 配合bt_mesh_model, bt_mesh_elem使用如下。

static struct bt_mesh_model root_models[] = {
    BT_MESH_MODEL_CFG_SRV(&cfg_srv),
    BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    BT_MESH_MODEL_HEALTH_CLI(&health_cli),
};

static struct bt_mesh_elem elements[] = {
    BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
};

值定义

_mods model的数组
_vnd_mods vendor model的数组

BT_MESH_MODEL(id, op, pub, user_data)

BT SIG bt mesh model的定义。

op model opcode的回调函数
pub model publish的参数
user model的用户参数

BT_MESH_MODEL_CFG_CLI

定义一个config client的model。

#define BT_MESH_MODEL_CFG_CLI(cli_data)                                      \
        BT_MESH_MODEL(BT_MESH_MODEL_ID_CFG_CLI,                      \
                  bt_mesh_cfg_cli_op, NULL, cli_data)

BT_MESH_MODEL_CFG_SRV

定义一个config server的model。

#define BT_MESH_MODEL_CFG_SRV(srv_data)                                      \
        BT_MESH_MODEL(BT_MESH_MODEL_ID_CFG_SRV,                      \
                  bt_mesh_cfg_srv_op, NULL, srv_data

BT_MESH_MODEL_HEALTH_CLI

定义一个health client 的model。

#define BT_MESH_MODEL_HEALTH_CLI(cli_data)                                   \
        BT_MESH_MODEL(BT_MESH_MODEL_ID_HEALTH_CLI,                   \
                  bt_mesh_health_cli_op, NULL, cli_data)

BT_MESH_MODEL_HEALTH_SRV

定义一个health server的model。

#define BT_MESH_MODEL_HEALTH_SRV(srv, pub)                                   \
        BT_MESH_MODEL(BT_MESH_MODEL_ID_HEALTH_SRV,                   \
                  bt_mesh_health_srv_op, pub, srv)

enum bt_mesh_output_action_t

BLE mesh output能力,现阶段只处理BT_MESH_DISPLAY_STRING和BT_MESH_DISPLAY_NUMBER。

BT_MESH_NO_OUTPUT      
BT_MESH_BLINK          
BT_MESH_BEEP           
BT_MESH_VIBRATE        
BT_MESH_DISPLAY_NUMBER 
BT_MESH_DISPLAY_STRING

enum bt_mesh_input_action_t

BLE mesh 输入能力,现阶段只处理BT_MESH_ENTER_STRING和BT_MESH_ENTER_NUMBER。

BT_MESH_NO_INPUT    
BT_MESH_PUSH        
BT_MESH_TWIST       
BT_MESH_ENTER_NUMBER
BT_MESH_ENTER_STRING

错误码定义

使用了标准的POSIX错误码,仅列出常用的错误码,更多错误码请参考:components/wireless/bluetooth/blemesh/util/include/errno.h
#define EPERM 1         /*操作不允许*/
#define ENOENT 2        /*文件/路径不存在*/
#define ESRCH 3         /*进程不存在*/
#define EINTR 4         /*中断的系统调用*/
#define EIO 5           /*I/O错误*/
#define ENXIO 6         /*设备/地址不存在*/
#define E2BIG 7         /*参数列表过长*/
#define ENOEXEC 8       /*执行格式错误*/
#define EBADF 9         /*错误文件编号*/
#define ECHILD 10       /*子进程不存在*/
#define EAGAIN 11       /*重试*/
#define ENOMEM 12       /*内存不足*/
#define EACCES 13       /*无权限*/
#define EFAULT 14       /*地址错误*/
#define ENOTBLK 15      /*需要块设备*/
#define EBUSY 16        /*设备或资源忙*/
#define EEXIST 17       /*文件已存在*/
#define EXDEV 18        /*跨设备链路*/
#define ENODEV 19       /*设备不存在*/
#define ENOTDIR 20      /*路径不存在
#define EISDIR 21       /*是路径*/
#define EINVAL 22       /*无效参数*/
#define ENFILE 23       /*文件表溢出*/
#define EMFILE 24       /*打开的文件过多*/
#define ENOTTY 25       /*非打字机*/
#define ETXTBSY 26      /*文本文件忙*/
#define EFBIG 27        /*文件太大*/
#define ENOSPC 28       /*设备无空间*/
#define ESPIPE 29       /*非法查询*/
#define EROFS 30        /*只读文件系统*/
//....POSIX err code

enum bt_mesh_prov_bearer_t

provision bearer的两种类型。

BT_MESH_PROV_ADV 
BT_MESH_PROV_GATT

enum bt_mesh_prov_oob_info_t

OOB消息,对应蓝牙mesh协议的OOB消息,16比特,BLE mesh spec定义(Mesh Profile, V1.0, Page 119):

0 Other
1 Electronic / URI
2 2D machine-readable code
3 Bar code
4 Near Field Communication (NFC)
5 Number
6 String
7-10 Reserved for Future Use
11 on box
12 in box
13 On piece of paper
14 Inside manual
15 On device

BT_MESH_PROV_OOB_OTHER    
BT_MESH_PROV_OOB_URI      
BT_MESH_PROV_OOB_2D_CODE  
BT_MESH_PROV_OOB_BAR_CODE
BT_MESH_PROV_OOB_NFC      
BT_MESH_PROV_OOB_NUMBER  
BT_MESH_PROV_OOB_STRING  
BT_MESH_PROV_OOB_ON_BOX  
BT_MESH_PROV_OOB_IN_BOX  
BT_MESH_PROV_OOB_ON_PAPER
BT_MESH_PROV_OOB_IN_MANUAL
BT_MESH_PROV_OOB_ON_DEV