ESP32和nrf mesh实现子节点共用,即蓝牙mesh设备的跨网关控制

本文介绍了如何使用ESP32和NRFMesh实现跨网关控制,通过蓝牙辅助配网将子设备配置到mesh网络中,并解决在特定SDK中初始化顺序问题,确保网关能接收并更新来自手机的netKey和appKey。
摘要由CSDN通过智能技术生成

众所周知,同一个mesh网络即netkey,netkey_idx相同,appkey,appkey_idx相同。在此前提下,想要实现跨网关控制就比较容易了。

我选用的方案是nrf mesh是网关一,ESP32作为网关二,TLSR8258作为子设备。首先通过nrf mesh将子设备配置到mesh网络中,然后将nrf mesh中的net_key和appkey通过蓝牙辅助配网(蓝牙直连的方式)传给ESP32从而实现配网。

以下主要是ESP32的实现逻辑介绍:

首先我们选用的是蓝牙gatt(实现辅助配网)和蓝牙 mesh(实现控制)共存的方案(没有SDK可以私聊),以下为主函数部分

void provisioner_app_main(void)
{
    esp_err_t err;

    ESP_LOGI(TAG, "Initializing...");//初始化
     err = bluetooth_init();ble协议栈初始化
    if (err) {
        ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err);
        return;
    }
    read_mesh_nodes_info();//读取mesh底层节点信息(KV存储读取)
    ble_mesh_get_dev_uuid(dev_uuid);//获取设备的uuid(将mac地址加入到uuid中)
    /* Initialize the Bluetooth Mesh Subsystem */
    err = ble_mesh_init();//ble mesh协议栈初始化
    if (err) {
        ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err);
    }
    gatts_demo_main();//gatt服务开启(和mesh分时复用)
}

上述代码通过测试发现,在此SDK中,必须先初始化mesh网络再初始化gatt,很明显不符合我们采用gatt来发送配置信息的需求。因此我们需要设计出新的解决方案,即修改和更新。为了解决这个问题,我们需要先查找到appkey和netkey的设置位置和修改。

对于普通配网器SDK,我们可以看到初始化部分

appkey的绑定很容易找到,可以先追溯一下netkey的绑定位置

esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
//------------->>>函数内有一个回调参数
BTC_BLE_MESH_ACT_PROVISIONER_ENABLE
//------------->>>根据参数查找到回调函数
//------------->>>回调函数调用下面这个函数
bt_mesh_provisioner_enable(arg->provisioner_enable.bearers);
//------------->>>进入函数内部,找到关于netKey的相关函数
bt_mesh_provisioner_net_create();
//------------->>>在函数内我们可以看到netKey是随机生成的
if (bt_mesh_rand(p_key, 16)) { //生成随机的netkey
        BT_ERR("Failed to generate Primary NetKey");
        return -EIO;
    }
//------------->>>生成netkey之后进行绑定
bt_mesh_net_keys_create(&sub->keys[0], p_key)

由上述追溯可知,我们最简单的方式就是用自己接收到的netKey把上面那个rand变量替换即可。但是,建议是不要修改底层,往应用层修改,于是有了下面的修改方法

//首先定义一个结构体用于存储需要使用的netKey
struct add_local_net_key_args {
        uint8_t net_key[16];
        uint16_t net_idx;
} add_local_net_key;//定义了一个结构体变量
//然后开始对结构体赋值,因为我们采用蓝牙辅助(随便一个蓝牙助手工具都可以)的形式发送的,所以需要更新netKey
//即收到蓝牙数据时先将netKey存入flash,调用netKey更新函数更新netKey
//蓝牙mesh netkey更新函数
void ble_mesh_updata_net_key(void)//当蓝牙辅助配网完成后,获取到手机发送的Netkey,更新网关net key
{
    size_t length=16;
    char net_key[16]={0};//产品secret,用于读取
     nvs_get_blob(skynvs_handle,MESH_NET_KEY,net_key, &length);//读取appkey

    memcpy(add_local_net_key.net_key, net_key, sizeof(add_local_net_key.net_key));
    esp_ble_mesh_provisioner_update_local_net_key(add_local_net_key.net_key, prov_key.net_idx);
}
//
//由于我们采用了掉电记忆,所以初始化时也需要解决这个问题,所以做了如下初始化处理
add_local_net_key.net_idx = NET_KEY_IDX;//netkey index 0x0000
 char net_key[16]={2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};//APPkey,用于mesh网络通信加密
 nvs_get_blob(skynvs_handle, MESH_NET_KEY,net_key,&length);//KV存储APPKEY,读取失败,原值保持不变
 memcpy(add_local_net_key.net_key, net_key, sizeof(add_local_net_key.net_key));
//此函数为原有函数,为确定下面函数的添加位置而写在此处
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;
    }
    const uint8_t*nowNetKey= bt_mesh_provisioner_local_net_key_get(add_local_net_key.net_idx);//查找netkey
    if(nowNetKey==NULL){ //未绑定netkey
       esp_ble_mesh_provisioner_add_local_net_key(add_local_net_key.net_key,add_local_net_key.net_idx);//绑定netKey
       ESP_LOGI(TAG,"%s,line=%d,NET KEY need add",__func__,__LINE__);
    }else{      //已经绑定过netkey
     printf("now net_key=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",nowNetKey[0],nowNetKey[1],nowNetKey[2],nowNetKey[3],nowNetKey[4],nowNetKey[5],
     nowNetKey[6],nowNetKey[7],nowNetKey[8],nowNetKey[9],nowNetKey[10],nowNetKey[11],nowNetKey[12],nowNetKey[13],nowNetKey[14],nowNetKey[15]);
        if(memcmp(add_local_net_key.net_key,nowNetKey,sizeof(add_local_net_key.net_key))==0){//目标netkey和实际netKey已经相等
        ESP_LOGI(TAG,"%s,line=%d,net key is same",__func__,__LINE__);
                    //不需要做任何动作
        }else{ //netKEY需要更新
            esp_ble_mesh_provisioner_update_local_net_key(add_local_net_key.net_key,add_local_net_key.net_idx);//更新netkey
            ESP_LOGI(TAG,"%s,line=%d,net key need updata",__func__,__LINE__);
        }
    }

此时,netKey的更新同步和掉电重启都已经设置完毕,appKey于其类似,基本一模一样的流程(就不再赘述),关于更新谁先谁后都一样可以正常使用。(可能也是我没有测出问题)

此处测试出的问题和坑点就是有个函数和我们调用的函数类似,调用了好像没用,也没有细细研究,有哪个大佬懂的也可以解惑一下

//两个相似的绑定函数
esp_ble_mesh_provisioner_add_local_net_key()
bt_ble_mesh_provisioner_add_local_net_key()
//两个相似的更新函数
esp_ble_mesh_provisioner_update_local_net_key()
bt_ble_mesh_provisioner_update_local_net_key()

怎样验证是否更新正常

由于配网过程中存在netKey和appKey的下发过程,为了大家都便于验证,就直接采用esp32作为网关和节点吧,采用其他芯片作为节点方法类似

网关端:

prov_random()
//----》内部调用
send_prov_data()
//----》
 netkey = bt_mesh_provisioner_net_key_get(prov_ctx.curr_net_idx);
//此处调用一个打印函数打印出netkey即可验证网关端下发的是否正确

子设备端:

netKey

prov_data()
//---》函数内部调用
bt_mesh_provision() 
//---》在函数开始位置打印netkey即可验证netkey是否和网关一致
BT_ERR("cyy net_key %s", bt_hex(net_key, 16));
//---》下面这个函数是不是很熟悉,就不强调了
bt_mesh_net_create()

appkey,此时,appKey和netKey的验证方式不太一样,因为appKey的绑定是在mesh关系建立以后,我们只需要在mesh配置回调函数打印即可

example_ble_mesh_config_server_cb()
//------------》》》内部有个opcode回调
ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD
//------------》》》只需要在这个回调打印appKey即可
ESP_LOG_BUFFER_HEX("AppKey", param->value.state_change.appkey_add.app_key, 16);

由此,添加和验证完成,正常情况下,已经完成了,如果你还不放心(也可能是控制不了),怕哪里有遗漏,你可以采用下面方法再验证

可以用定时器的方式反复给子节点发送消息验证是否成功,但是这样太麻烦了,因此我们采用更高效的方法。

因为网关代码默认会在添加完成后去控制一下子设备

所以,我们只要控制完成之后再去控制一下就能达成不断控制的效果

 case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
            node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET onoff: 0x%02x", node->onoff);
             esp_ble_mesh_generic_client_set_state_t set_state = {0};
            node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET onoff: 0x%02x", node->onoff);
            /* After Generic OnOff Status for Generic OnOff Get is received, Generic OnOff Set will be sent */
            example_ble_mesh_set_msg_common(&common, node, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET);
            set_state.onoff_set.op_en = false;
            set_state.onoff_set.onoff = !node->onoff;
            set_state.onoff_set.tid = 1;
            
             vTaskDelay(5000/ portTICK_PERIOD_MS);
             err = esp_ble_mesh_generic_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
             vTaskDelay(5000/ portTICK_PERIOD_MS);
             err = esp_ble_mesh_generic_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
            break;

至此,互控设计完成,大佬们有什么不同见解希望指正交流。

小博写文章为了帮助更多的人更快解决问题,看完有用记得三连!!还有什么想看的基于ESP32蓝牙mesh或者基于TLSR8258的小问题也可以留言讨论

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值