BLE MESH 无痛入门03 Provisioner 流程 概述 基于ESP32

前言

最近项目需要,需要学一些关于BLE MESH 的内容,学的比较痛苦,中文互联网里关于BLE MESH的内容很少,而且很多都是从协议栈的角度出发去讲,未免有些枯燥,我又是个英文小白,所以学起来就一脸蒙,现在稍微有些入门了,做个笔记总结一下,也方便后来者学习吧。这个笔记会以一个使用者的角度结合代码来描述。

开发环境

ESP-IDF 版本: v5.1
设备:ESP32S3 开发板 * n
nRF Mesh V3.3.0 APP

Provisioner流程概述

在上一篇文章中我们使用的是Nrf MeshAPP作为Provisioner,配网过程中的截图如下(有兴趣的同学可以比对下图文字和本文流程):Nrf MEsh APP 的配网截图
使用ESP32作为Provisioner和使用APP作为配网器其实原理上是一样的。先概括总体流程:
在这里插入图片描述

这次我们需要两块开发板完成实现,一块作为Provisioner,一块作为Node,分别下载esp-idf/examples/bluetooth/esp_ble_mesh/provisioner和esp-idf/examples/bluetooth/esp_ble_mesh/onoff_models/onoff_server例程,运行程序后我们就可以得到如下LOG:
在这里插入图片描述
我们主要分析配网流程:
从Log中我们可以看到流程调用顺序

  1. 初始化:
    1.设置匹配的UUID
    用于筛选期望的配网节点esp_ble_mesh_provisioner_set_dev_uuid_match
    2.使能配网
    选择配网的承载层 esp_ble_mesh_provisioner_set_dev_uuid_match
    3.添加本地的APP KEY esp_ble_mesh_provisioner_add_local_app_key
    同时在回调里为本地的Onoff Client Model也绑定了AppKey。

  2. 配网:
    1.接收到节点发出的请求,触发事件:ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT
    2.配网完成,触发事件:ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT
    3.设置节点名称完成,触发事件:ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT

  3. 配置节点:
    1.获取节点参数 example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
    2.添加节点的APP Key example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
    3.绑定模型的APP Key example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);

  4. 发送控制命令:
    1.在发送之前先读取了一遍状态example_ble_mesh_set_msg_common(&common, node, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET);
    2.发送控制命令:example_ble_mesh_set_msg_common(&common, node, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET);

我们再仔细分析一下每个操作的对应代码,关键的步骤标注了中文注释,其余部分如有疑惑欢迎留言。
1.1设置匹配的UUID

    uint8_t match[2] = {0xdd, 0xdd}; //节点的uuid前两位必须与该UUID相匹配,
    err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, sizeof(match), 0x0, false); //第三个参数表示即使收到了匹配的UUID的节点,也不会立即开始配网。
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to set matching device uuid (err %d)", err);
        return err;
    }

1.2使能配网

    err = esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to enable mesh provisioner (err %d)", err);
        return err;
    }

1.3添加本地的APP KEY

    err = esp_ble_mesh_provisioner_add_local_app_key(prov_key.app_key, prov_key.net_idx, prov_key.app_idx);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to add local AppKey (err %d)", err);
        return err;
    }

2.1接收到节点发出的请求

static void recv_unprov_adv_pkt(uint8_t dev_uuid[16], uint8_t addr[BD_ADDR_LEN],
                                esp_ble_mesh_addr_type_t addr_type, uint16_t oob_info,
                                uint8_t adv_type, esp_ble_mesh_prov_bearer_t bearer)
{
    esp_ble_mesh_unprov_dev_add_t add_dev = {0};
    int err;

    /* Due to the API esp_ble_mesh_provisioner_set_dev_uuid_match, Provisioner will only
     * use this callback to report the devices, whose device UUID starts with 0xdd & 0xdd,
     * to the application layer.
     */

    ESP_LOGI(TAG, "address: %s, address type: %d, adv type: %d", bt_hex(addr, BD_ADDR_LEN), addr_type, adv_type);
    ESP_LOGI(TAG, "device uuid: %s", bt_hex(dev_uuid, 16));
    ESP_LOGI(TAG, "oob info: %d, bearer: %s", oob_info, (bearer & ESP_BLE_MESH_PROV_ADV) ? "PB-ADV" : "PB-GATT");

    memcpy(add_dev.addr, addr, BD_ADDR_LEN);
    add_dev.addr_type = (uint8_t)addr_type;
    memcpy(add_dev.uuid, dev_uuid, 16);
    add_dev.oob_info = oob_info;
    add_dev.bearer = (uint8_t)bearer;
    /* Note: If unprovisioned device adv packets have not been received, we should not add
             device with ADD_DEV_START_PROV_NOW_FLAG set. */
    err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev,
            ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG); //开始配网
    if (err) {
        ESP_LOGE(TAG, "%s: Add unprovisioned device into queue failed", __func__);
    }

    return;
}

2.2配网完成

static esp_err_t prov_complete(int node_idx, const esp_ble_mesh_octet16_t uuid,
                               uint16_t unicast, uint8_t elem_num, uint16_t net_idx)
{
    esp_ble_mesh_client_common_param_t common = {0};
    esp_ble_mesh_cfg_client_get_state_t get_state = {0};
    esp_ble_mesh_node_info_t *node = NULL;
    char name[11] = {0};
    int err;

    ESP_LOGI(TAG, "node index: 0x%x, unicast address: 0x%02x, element num: %d, netkey index: 0x%02x",
             node_idx, unicast, elem_num, net_idx);
    ESP_LOGI(TAG, "device uuid: %s", bt_hex(uuid, 16));

    sprintf(name, "%s%d", "NODE-", node_idx);
    err = esp_ble_mesh_provisioner_set_node_name(node_idx, name); //设置节点名称
    if (err) {
        ESP_LOGE(TAG, "%s: Set node name failed", __func__);
        return ESP_FAIL;
    }

    err = example_ble_mesh_store_node_info(uuid, unicast, elem_num, LED_OFF);
    if (err) {
        ESP_LOGE(TAG, "%s: Store node info failed", __func__);
        return ESP_FAIL;
    }

    node = example_ble_mesh_get_node_info(unicast);
    if (!node) {
        ESP_LOGE(TAG, "%s: Get node info failed", __func__);
        return ESP_FAIL;
    }

    example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET); //获取节点配置数据
    get_state.comp_data_get.page = COMP_DATA_PAGE_0;
    err = esp_ble_mesh_config_client_get_state(&common, &get_state);
    if (err) {
        ESP_LOGE(TAG, "%s: Send config comp data get failed", __func__);
        return ESP_FAIL;
    }

    return ESP_OK;
}

2.3设置节点名称完成

 case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT: {
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code %d", param->provisioner_set_node_name_comp.err_code);
        if (param->provisioner_set_node_name_comp.err_code == ESP_OK) {
            const char *name = NULL;
            name = esp_ble_mesh_provisioner_get_node_name(param->provisioner_set_node_name_comp.node_index);
            if (!name) {
                ESP_LOGE(TAG, "Get node name failed");
                return;
            }
            ESP_LOGI(TAG, "Node %d name is: %s", param->provisioner_set_node_name_comp.node_index, name);
        }
        break;
    }

3.1获取节点参数

这一部分代码在2.2中实现

3.2添加节点的APP Key

接下来的代码懒得抄了,按照上述流程按图索骥即可。

注意事项

1.通过之前的文章我们知道,在OnoffServer中存在三个元素,那么第二个和第三个元素可以被控制吗?
答案是不行的,因为绑定APP Key的操作对应的是某个特定元素的特定模型,例程的APP KEY只绑定了主element(和Node地址一致)的ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV

    case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: {
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
            set_state.model_app_bind.element_addr = node->unicast;//这里写死了是给主element的ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV绑定APP Key
            set_state.model_app_bind.model_app_idx = prov_key.app_idx;
            set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV; 
            set_state.model_app_bind.company_id = ESP_BLE_MESH_CID_NVAL;
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
            break;
        }

2.本文使用的是provisioner和onoff_server两个例程完成的实验,那么换成onoff_client和provisioner两个例程,能不能完成配网呢?
答案是可以,但是仅能完成配网和添加APP KEY,绑定APP Key是不行的,为什么呢?我们看代码:

    case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: {
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
            set_state.model_app_bind.element_addr = node->unicast;
            set_state.model_app_bind.model_app_idx = prov_key.app_idx;
            set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV; //这里写死了是给ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV模型绑定APP KEY
            set_state.model_app_bind.company_id = ESP_BLE_MESH_CID_NVAL;
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
            break;
        }

3.可否用这个例程配网对vendor_models配网呢?
答案是不行,因为那两个例程的UUID不一致。

总结

这篇文章介绍了esp-idf/examples/bluetooth/esp_ble_mesh/provisioner例程的工作流程,阅读本文章后,希望读者已能够了解以下几项内容:

  1. 总体配网流程
  2. 修改provisioner匹配的UUID,配网承载层,节点名称。
  3. 使用config_client获取节点信息,配置节点。
  4. provisioner本地模型设置,绑定APPkey。
  • 27
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值