前言
ble mesh配网器使用ESP32的方案,乐鑫开源的SDK和丰富的组件,可以快速上手ble mesh的网关应用,本章讲解基于ble mesh配网器的开发
概念
配网器Provisioner用于发现与配置未配网设备,使其加入mesh网络,成为mesh网络的节点设备
两个阶段
- 配网阶段:为设备分配单播地址、添加网络密钥 (NetKey) 等。通过配网,设备加入 ESP-BLE-MESH 网络,身份从未配网设备变为节点
- 配置阶段:为节点添加应用密钥 (AppKey), 并将应用密钥绑定到相应模型。配置期间,有些选项是可选的,比如为节点添加订阅地址、设置发布地址等。通过配置,该节点实际上可以向 Provisioner 发送消息,也可以接收来自 Provisioner 的消息
密钥
- 网络密钥:用于加密网络层消息,具有相同网络密钥的节点视为同一网络
- 应用密钥:用于加密上层传输层中的消息,如服务器模型与客户端模型绑定的应用密钥不一致,则无法进行通信
节点删除
- provisioner将节点添加至黑名单
- provisioner启动密钥更新程序
- 节点执行重置程序,切换自身为未配网设备
有效载荷
不分包有效载荷长度为11字节,对于verdor消息,由于opcode占用3字节,剩余有效负载长度为8字节
工程 vendor client
- 这里我们采用的是vendor client的工程,实现对节点进行配网与配置模型, 路径
examples\bluetooth\esp_ble_mesh\ble_mesh_vendor_model\vendor_client
流程
模型定义
例程中使用了乐鑫自己的vendor模型,id为0x02E5;为了和节点正常通信,需要对齐下vendor id与opcode;
#define CID_ESP 0x0211//0x02E5
#define ESP_BLE_MESH_VND_MODEL_ID_SERVER 0x0000
#define ESP_BLE_MESH_VND_MODEL_ID_CLIENT 0x0001
//需要与节点vendor对应(泰凌微opcode从0xE0~0xFF)
#define ESP_BLE_MESH_VND_MODEL_OP_SEND ESP_BLE_MESH_MODEL_OP_3(0xF0, CID_ESP)
#define ESP_BLE_MESH_VND_MODEL_OP_STATUS ESP_BLE_MESH_MODEL_OP_3(0xF1, CID_ESP)
地址
ble mesh初始化
- 在函数接口 ble_mesh_init初始化ble mesh,配置元素、key密钥、注册回调;通过match来匹配对应DEV UUID
static esp_err_t ble_mesh_init(void)
{
uint8_t match[2] = {0x66, 0x66};//后两位dev uuid
esp_err_t err;
prov_key.net_idx = ESP_BLE_MESH_KEY_PRIMARY;
prov_key.app_idx = APP_KEY_IDX;
memset(prov_key.app_key, APP_KEY_OCTET, sizeof(prov_key.app_key));//app key
esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb);//注册配网回调
esp_ble_mesh_register_config_client_callback(example_ble_mesh_config_client_cb);//客户端配置回调
esp_ble_mesh_register_custom_model_callback(example_ble_mesh_custom_model_cb);//vendor模型回调
err = esp_ble_mesh_init(&provision, &composition);//初始化ble mesh
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize mesh stack");
return err;
}
err = esp_ble_mesh_client_model_init(&vnd_models[0]);//初始化模型
if (err) {
ESP_LOGE(TAG, "Failed to initialize vendor client");
return err;
}
err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, sizeof(match), 0, false);//匹配device uuid前两位
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set matching device uuid");
return err;
}
err = esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);//使能PB-ADV与PB-GATT
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to enable mesh provisioner");
return err;
}
err = esp_ble_mesh_provisioner_add_local_app_key(prov_key.app_key, prov_key.net_idx, prov_key.app_idx);//添加key
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to add local AppKey");
return err;
}
ESP_LOGI(TAG, "ESP BLE Mesh Provisioner initialized");
return ESP_OK;
}
地址分配
- 配网器使用固定地址0x01,节点元素地址从0x0005开始
static esp_ble_mesh_prov_t provision = {
.prov_uuid = dev_uuid,
.prov_unicast_addr = PROV_OWN_ADDR,//0x01
.prov_start_address = 0x0005,
};
配网验证
Provisioner匹配uuid成功后,在组网的时通过获取composition data命令获取节点支持的所有model id,只有节点支持了对应的模型后,才能发送该模型定义的opcode
配网数据
字段
- CID:2byte CompanyID
- PID:2byte 产品ID
- VID:2byte 产品版本ID
- CRPL:2byte 重放攻击列表项最小数目
- Features:2byte 支持mesh的能力
通信验证
封装通信接口
- 节点元素的地址分配从0x05开始累加,绑定了多个节点的情况下,逻辑上采用idNum来决定发送给哪个地址
void ble_mesh_send_single_node_vendor_msg(uint8_t idNum, uint8_t sw)
{
//自定义的协议
struct{
uint8_t start;
uint8_t id;
uint8_t ctrl; //
uint8_t crc;
}send_node_msg;
esp_ble_mesh_msg_ctx_t ctx = {0};
uint32_t opcode;
esp_err_t err;
//ctx.model
ctx.net_idx = prov_key.net_idx;
ctx.app_idx = prov_key.app_idx;
ctx.send_ttl = MSG_SEND_TTL;
ctx.send_rel = MSG_SEND_REL;
opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND;//
ctx.addr = idNum + 0x05; //地址
//自定义的简易应用协议,先不管
send_node_msg.start = 0xA5;//包头
send_node_msg.id = idNum;//分配的id
send_node_msg.ctrl = sw;//控制量
send_node_msg.crc = 0xFF; //先不处理
err = esp_ble_mesh_client_model_send_msg(vendor_client.model, &ctx, opcode,
sizeof(send_node_msg), (uint8_t *)&send_node_msg, MSG_TIMEOUT, false, MSG_ROLE);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to send vendor message 0x%06x", opcode);
return;
}
ESP_LOGI(TAG, "send vendor message 0x%06x", opcode);
}
接收信息
- 在vendor模型回调example_ble_mesh_custom_model_cb中,单接收到节点数据后生成ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT事件
static void example_ble_mesh_custom_model_cb(esp_ble_mesh_model_cb_event_t event,
esp_ble_mesh_model_cb_param_t *param)
{
static int64_t start_time;
........
switch (event) {
case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT://接收消息
{
ESP_LOGI(TAG, "Receive publish message 0x%06x addr = %d", param->client_recv_publish_msg.opcode, param->client_recv_publish_msg.ctx->addr);
ESP_LOGI(TAG, "%d %x %x", param->client_recv_publish_msg.length, param->client_recv_publish_msg.msg[0],param->client_recv_publish_msg.msg[1]);
//发送消息给app task进行处理
ble_mqtt_msg_data_t ble_msg_data;
ble_msg_data.type = BLE_MESH_NODE_MSG_TYPE;
ble_msg_data.datas.ble_relay_msg.handle_id = (uint8_t)param->client_recv_publish_msg.ctx->addr;
ble_msg_data.datas.ble_relay_msg.len = param->client_recv_publish_msg.length;
memcpy(ble_msg_data.datas.ble_relay_msg.value, param->client_recv_publish_msg.msg, ble_msg_data.datas.ble_relay_msg.len);
if(xQueueSendToBack( gw_ble_mqtt_queue, &ble_msg_data, 0 ) == pdPASS)
{
ESP_LOGI(TAG, "ble mesh node msg send ok");
}
else
ESP_LOGE(TAG, "ble mesh node msg send err");
break;
}
default:
break;
}
}
总结
到这里完成了单个节点的通信配置,但是在系统中会存在多个不同功能节点,如何有效快速的进行配网,区分不同功能的节点设备,可靠的通信,还需要更多的去理解ble mesh的应用