最近在MESH的开发中,遇到了很多问题,于是开始把目光放到了Vendor模型(自定义模型)上
ESP32官方对于Vendor Model有两个例程,一个vendor server,一个vendor client
于是做此文记录关于vendor模型的学习(本文会默认已经看过前面provisioner和onoff的文章,一些ble mesh的细节会简略描述)
首先先看vendor client,首先是大段的宏定义
#define CID_ESP 0x02E5
#define PROV_OWN_ADDR 0x0001
#define MSG_SEND_TTL 3
#define MSG_TIMEOUT 0
#define MSG_ROLE ROLE_PROVISIONER
#define COMP_DATA_PAGE_0 0x00
#define APP_KEY_IDX 0x0000
#define APP_KEY_OCTET 0x12
#define COMP_DATA_1_OCTET(msg, offset) (msg[offset])
#define COMP_DATA_2_OCTET(msg, offset) (msg[offset + 1] << 8 | msg[offset])
#define ESP_BLE_MESH_VND_MODEL_ID_CLIENT 0x0000
#define ESP_BLE_MESH_VND_MODEL_ID_SERVER 0x0001
#define ESP_BLE_MESH_VND_MODEL_OP_SEND ESP_BLE_MESH_MODEL_OP_3(0x00, CID_ESP)
#define ESP_BLE_MESH_VND_MODEL_OP_STATUS ESP_BLE_MESH_MODEL_OP_3(0x01, CID_ESP)
这里面主要是以下内容:
- CID_ESP 0x02E5:Espressif 的公司 ID,用于 Vendor 模型。
- PROV_OWN_ADDR 0x0001:Provisioner 的单播地址。
- MSG_SEND_TTL 3:消息的生存时间(TTL),表示消息最多转发 3 次。
- MSG_TIMEOUT 0:消息超时时间,0 表示无超时限制。
- MSG_ROLE :ROLE_PROVISIONER:设备角色为 Provisioner(配置者)。
- COMP_DATA_PAGE_0 0x00:Composition Data 的页面编号。
- APP_KEY_IDX 0x0000:应用密钥索引。
- APP_KEY_OCTET 0x12:应用密钥的一个字节值。
- COMP_DATA_1_OCTET 和 COMP_DATA_2_OCTET:宏用于解析 Composition Data 的 1 字节和 2 字节数据。
- ESP_BLE_MESH_VND_MODEL_ID_CLIENT 0x0000 和 ESP_BLE_MESH_VND_MODEL_ID_SERVER 0x0001:定义 Vendor 模型的客户端和服务端 ID。
- ESP_BLE_MESH_VND_MODEL_OP_SEND 和 ESP_BLE_MESH_VND_MODEL_OP_STATUS:定义 Vendor 模型的操作码(opcode),用于发送和接收状态消息。
接下来,按照main函数运行的数据,开始分析
首先是ble_mesh_init
static esp_err_t ble_mesh_init(void)
{
uint8_t match[2] = { 0x32, 0x10 };
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));
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);
err = esp_ble_mesh_init(&provision, &composition);
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), 0x0, false);
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_bearer_t)(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_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);
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;
}
首先是UUID的匹配,这次修改了万年不变的0xdd 0xdd,不多赘述
然后是配置net key和app key
接下来注册三个回调,整个mesh的大头也是在这里
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);
首先第一个是配网回调函数,这块和之前onoff的provisioning类似
static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event,
esp_ble_mesh_prov_cb_param_t *param)
{
switch (event) {
case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code %d", param->prov_register_comp.err_code);
mesh_example_info_restore(); /* Restore proper mesh example info */
break;
case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT, err_code %d", param->provisioner_prov_enable_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT, err_code %d", param->provisioner_prov_disable_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT");
recv_unprov_adv_pkt(param->provisioner_recv_unprov_adv_pkt.dev_uuid, param->provisioner_recv_unprov_adv_pkt.addr,
param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info,
param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT, bearer %s",
param->provisioner_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
break;
case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT, bearer %s, reason 0x%02x",
param->provisioner_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", param->provisioner_prov_link_close.reason);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT:
prov_complete(param->provisioner_prov_complete.node_idx, param->provisioner_prov_complete.device_uuid,
param->provisioner_prov_complete.unicast_addr, param->provisioner_prov_complete.element_num,
param->provisioner_prov_complete.netkey_idx);
break;
case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, err_code %d", param->provisioner_add_unprov_dev_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, err_code %d", param->provisioner_set_dev_uuid_match_comp.err_code);
break;
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 == 0) {
const char *name = esp_ble_mesh_provisioner_get_node_name(param->provisioner_set_node_name_comp.node_index);
if (name) {
ESP_LOGI(TAG, "Node %d name %s", param->provisioner_set_node_name_comp.node_index, name);
}
}
break;
case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT, err_code %d", param->provisioner_add_app_key_comp.err_code);
if (param->provisioner_add_app_key_comp.err_code == 0) {
prov_key.app_idx = param->provisioner_add_app_key_comp.app_idx;
esp_err_t err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_key.app_idx,
ESP_BLE_MESH_VND_MODEL_ID_CLIENT, CID_ESP);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to bind AppKey to vendor client");
}
}
break;
case ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT, err_code %d", param->provisioner_bind_app_key_to_model_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_STORE_NODE_COMP_DATA_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_STORE_NODE_COMP_DATA_COMP_EVT, err_code %d", param->provisioner_store_node_comp_data_comp.err_code);
break;
default:
break;
}
}
第一个事件是配网完成事件
ESP_BLE_MESH_PROV_REGISTER_COMP_EVT
在这里面调用了一个配网完成的存储函数
mesh_example_info_restore();
函数内容如下:
static void mesh_example_info_restore(void)
{
esp_err_t err = ESP_OK;
bool exist = false;
err = ble_mesh_nvs_restore(NVS_HANDLE, NVS_KEY, &store, sizeof(store), &exist);
if (err != ESP_OK) {
return;
}
if (exist) {
ESP_LOGI(TAG, "Restore, server_addr 0x%04x, vnd_tid 0x%04x", store.server_addr, store.vnd_tid);
}
}
第二个事件,是配网功能启动成功的事件
ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT
这里只添加了一个打印,在实际场景中,可以根据自己的需求添加
第三个事件,是禁用配网功能的事件
ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT
这里还是只有打印,实际场景中,可以根据需求增加内容,比如在配网完成后,禁用配网,启动其他的功能,比如服务器启动,wifi通信等
第四个事件,是发现未配网广播的事件
ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT
这里和onoff的很像,也是调用了一个函数recv_unprov_adv_pkt
bu'guo不过recv_unprov_adv_pkt的内容有所变化
static void recv_unprov_adv_pkt(uint8_t dev_uuid[ESP_BLE_MESH_OCTET16_LEN], 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};
esp_err_t err;
ESP_LOG_BUFFER_HEX("Device address", addr, BD_ADDR_LEN);
ESP_LOGI(TAG, "Address type 0x%02x, adv type 0x%02x", addr_type, adv_type);
ESP_LOG_BUFFER_HEX("Device UUID", dev_uuid, ESP_BLE_MESH_OCTET16_LEN);
ESP_LOGI(TAG, "oob info 0x%04x, 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 = (esp_ble_mesh_addr_type_t)addr_type;
memcpy(add_dev.uuid, dev_uuid, ESP_BLE_MESH_OCTET16_LEN);
add_dev.oob_info = oob_info;
add_dev.bearer = (esp_ble_mesh_prov_bearer_t)bearer;
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_OK) {
ESP_LOGE(TAG, "Failed to start provisioning device");
}
}
不过主要还是调用了
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);:
进行子节点配网
接下来是通信链路打开和关闭事件,不多做赘述
ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT
ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT
然后是重头戏,配网完成事件
ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT
这里还是调用了prov_complete函数,同样也是应用层自己实现的
static esp_err_t prov_complete(uint16_t node_index, const esp_ble_mesh_octet16_t uuid,
uint16_t primary_addr, uint8_t element_num, uint16_t net_idx)
{
esp_ble_mesh_client_common_param_t common = {0};
esp_ble_mesh_cfg_client_get_state_t get = {0};
esp_ble_mesh_node_t *node = NULL;
char name[10] = {'\0'};
esp_err_t err;
ESP_LOGI(TAG, "node_index %u, primary_addr 0x%04x, element_num %u, net_idx 0x%03x",
node_index, primary_addr, element_num, net_idx);
ESP_LOG_BUFFER_HEX("uuid", uuid, ESP_BLE_MESH_OCTET16_LEN);
store.server_addr = primary_addr;
mesh_example_info_store(); /* Store proper mesh example info */
sprintf(name, "%s%02x", "NODE-", node_index);
err = esp_ble_mesh_provisioner_set_node_name(node_index, name);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set node name");
return ESP_FAIL;
}
node = esp_ble_mesh_provisioner_get_node_with_addr(primary_addr);
if (node == NULL) {
ESP_LOGE(TAG, "Failed to get node 0x%04x info", primary_addr);
return ESP_FAIL;
}
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
get.comp_data_get.page = COMP_DATA_PAGE_0;
err = esp_ble_mesh_config_client_get_state(&common, &get);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Composition Data Get");
return ESP_FAIL;
}
return ESP_OK;
}
在里面,首先打印了配网节点的信息,我这块同时运行了server和client,配网完成打印信息如下:
I (544166) EXAMPLE: node_index 0, primary_addr 0x0005, element_num 1, net_idx 0x000
I (544166) uuid: 32 10 74 4d bd 61 ef 26 00 00 00 00 00 00 00 00
然后调用mesh_example_info_store存储节点信息,这个也是调用了系统提供的一个示例储存函数
esp_err_t ble_mesh_nvs_store(nvs_handle_t handle, const char *key, const void *data, size_t length)
{
esp_err_t err = ESP_OK;
if (key == NULL || data == NULL || length == 0) {
ESP_LOGE(TAG, "Store, invalid parameter");
return ESP_ERR_INVALID_ARG;
}
err = nvs_set_blob(handle, key, data, length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Store, nvs_set_blob failed, err %d", err);
return err;
}
err = nvs_commit(handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Store, nvs_commit failed, err %d", err);
return err;
}
ESP_LOGI(TAG, "Store, key \"%s\", length %u", key, length);
ESP_LOG_BUFFER_HEX("EXAMPLE_NVS: Store, data", data, length);
return err;
}
运行时会打印如下内容
I (544166) EXAMPLE_NVS: Store, key "vendor_client", length 4
I (544176) EXAMPLE_NVS: Store, data: 05 00 00 00
I (544166) EXAMPLE_NVS: Store, key "vendor_client", length 4
I (544176) EXAMPLE_NVS: Store, data: 05 00 00 00
I (544176) EXAMPLE_NVS: Store, data: 05 00 00 00
接下来调用esp_ble_mesh_provisioner_set_node_name设置节点名称
I (544176) EXAMPLE: ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code 0
I (544186) EXAMPLE: Node 0 name NODE-00
然后调用esp_ble_mesh_provisioner_get_node_with_addr获取节点信息
之后将获取的信息调用example_ble_mesh_set_msg_common设置消息参数
static void example_ble_mesh_set_msg_common(esp_ble_mesh_client_common_param_t *common,
esp_ble_mesh_node_t *node,
esp_ble_mesh_model_t *model, uint32_t opcode)
{
common->opcode = opcode;
common->model = model;
common->ctx.net_idx = prov_key.net_idx;
common->ctx.app_idx = prov_key.app_idx;
common->ctx.addr = node->unicast_addr;
common->ctx.send_ttl = MSG_SEND_TTL;
common->msg_timeout = MSG_TIMEOUT;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
common->msg_role = MSG_ROLE;
#endif
}
然后通过err = esp_ble_mesh_config_client_get_state(&common, &get);接口,来发送 Composition Data Get 消息,获取节点模型信息。
接下来的几个事件,和provisioner例程的差不多,设置节点名称完成,绑定app key,储存Composition Data,这里不多介绍了,都是打印了一个日志,这些在实际场景应用中的用法,我也没太搞懂
接下来是第二个回调函数
static void example_ble_mesh_config_client_cb(esp_ble_mesh_cfg_client_cb_event_t event,
esp_ble_mesh_cfg_client_cb_param_t *param)
{
esp_ble_mesh_client_common_param_t common = {0};
esp_ble_mesh_cfg_client_set_state_t set = {0};
esp_ble_mesh_node_t *node = NULL;
esp_err_t err;
ESP_LOGI(TAG, "Config client, err_code %d, event %u, addr 0x%04x, opcode 0x%04" PRIx32,
param->error_code, event, param->params->ctx.addr, param->params->opcode);
if (param->error_code) {
ESP_LOGE(TAG, "Send config client message failed, opcode 0x%04" PRIx32, param->params->opcode);
return;
}
node = esp_ble_mesh_provisioner_get_node_with_addr(param->params->ctx.addr);
if (!node) {
ESP_LOGE(TAG, "Failed to get node 0x%04x info", param->params->ctx.addr);
return;
}
switch (event) {
case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET) {
ESP_LOG_BUFFER_HEX("Composition data", param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
example_ble_mesh_parse_node_comp_data(param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
err = esp_ble_mesh_provisioner_store_node_comp_data(param->params->ctx.addr,
param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to store node composition data");
break;
}
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
set.app_key_add.net_idx = prov_key.net_idx;
set.app_key_add.app_idx = prov_key.app_idx;
memcpy(set.app_key_add.app_key, prov_key.app_key, ESP_BLE_MESH_OCTET16_LEN);
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config AppKey Add");
}
}
break;
case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD) {
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
set.model_app_bind.element_addr = node->unicast_addr;
set.model_app_bind.model_app_idx = prov_key.app_idx;
set.model_app_bind.model_id = ESP_BLE_MESH_VND_MODEL_ID_SERVER;
set.model_app_bind.company_id = CID_ESP;
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Model App Bind");
}
} else if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND) {
ESP_LOGW(TAG, "%s, Provision and config successfully", __func__);
}
break;
case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_STATUS) {
ESP_LOG_BUFFER_HEX("Composition data", param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
}
break;
case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT:
switch (param->params->opcode) {
case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: {
esp_ble_mesh_cfg_client_get_state_t get = {0};
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
get.comp_data_get.page = COMP_DATA_PAGE_0;
err = esp_ble_mesh_config_client_get_state(&common, &get);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Composition Data Get");
}
break;
}
case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD:
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
set.app_key_add.net_idx = prov_key.net_idx;
set.app_key_add.app_idx = prov_key.app_idx;
memcpy(set.app_key_add.app_key, prov_key.app_key, ESP_BLE_MESH_OCTET16_LEN);
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config AppKey Add");
}
break;
case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND:
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
set.model_app_bind.element_addr = node->unicast_addr;
set.model_app_bind.model_app_idx = prov_key.app_idx;
set.model_app_bind.model_id = ESP_BLE_MESH_VND_MODEL_ID_SERVER;
set.model_app_bind.company_id = CID_ESP;
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Model App Bind");
}
break;
default:
break;
}
break;
default:
ESP_LOGE(TAG, "Invalid config client event %u", event);
break;
}
}
这个用于处理client相关事件
首先是ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT
这个用于获取客户端响应状态,首先洁厕opcode是否为Composition Data并打印,之后调用example_ble_mesh_parse_node_comp_data解析Composition Data
再上一个回调函数中配网完成会调用一次esp_ble_mesh_config_client_get_state,之后进入该事件,打印信息
I (544646) Composition data: e5 02 00 00 00 00 0a 00 03 00 00 00 01 01 00 00
I (544646) Composition data: e5 02 00 00 00 00 0a 00 03 00 00 00 01 01 00 00
I (544646) Composition data: e5 02 01 00
I (544646) EXAMPLE: ********************** Composition Data Start **********************
I (544656) EXAMPLE: * CID 0x02e5, PID 0x0000, VID 0x0000, CRPL 0x000a, Features 0x0003 *
I (544646) EXAMPLE: ********************** Composition Data Start **********************
I (544656) EXAMPLE: * CID 0x02e5, PID 0x0000, VID 0x0000, CRPL 0x000a, Features 0x0003 *
I (544656) EXAMPLE: * CID 0x02e5, PID 0x0000, VID 0x0000, CRPL 0x000a, Features 0x0003 *
I (544666) EXAMPLE: * Loc 0x0000, NumS 0x01, NumV 0x01 *
I (544666) EXAMPLE: * SIG Model ID 0x0000 *
I (544676) EXAMPLE: * Vendor Model ID 0x0001, Company ID 0x02e5 *
example_ble_mesh_parse_node_comp_data的具体内容如下:
static void example_ble_mesh_parse_node_comp_data(const uint8_t *data, uint16_t length)
{
uint16_t cid, pid, vid, crpl, feat;
uint16_t loc, model_id, company_id;
uint8_t nums, numv;
uint16_t offset;
int i;
cid = COMP_DATA_2_OCTET(data, 0);
pid = COMP_DATA_2_OCTET(data, 2);
vid = COMP_DATA_2_OCTET(data, 4);
crpl = COMP_DATA_2_OCTET(data, 6);
feat = COMP_DATA_2_OCTET(data, 8);
offset = 10;
ESP_LOGI(TAG, "********************** Composition Data Start **********************");
ESP_LOGI(TAG, "* CID 0x%04x, PID 0x%04x, VID 0x%04x, CRPL 0x%04x, Features 0x%04x *", cid, pid, vid, crpl, feat);
for (; offset < length; ) {
loc = COMP_DATA_2_OCTET(data, offset);
nums = COMP_DATA_1_OCTET(data, offset + 2);
numv = COMP_DATA_1_OCTET(data, offset + 3);
offset += 4;
ESP_LOGI(TAG, "* Loc 0x%04x, NumS 0x%02x, NumV 0x%02x *", loc, nums, numv);
for (i = 0; i < nums; i++) {
model_id = COMP_DATA_2_OCTET(data, offset);
ESP_LOGI(TAG, "* SIG Model ID 0x%04x *", model_id);
offset += 2;
}
for (i = 0; i < numv; i++) {
company_id = COMP_DATA_2_OCTET(data, offset);
model_id = COMP_DATA_2_OCTET(data, offset + 2);
ESP_LOGI(TAG, "* Vendor Model ID 0x%04x, Company ID 0x%04x *", model_id, company_id);
offset += 4;
}
}
ESP_LOGI(TAG, "*********************** Composition Data End ***********************");
}