// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" #include "esp_ble_mesh_defs.h" #include "esp_ble_mesh_common_api.h" #include "esp_ble_mesh_provisioning_api.h" #include "esp_ble_mesh_networking_api.h" #include "esp_ble_mesh_config_model_api.h" #include "esp_ble_mesh_generic_model_api.h" #include "esp_fast_prov_common.h" #include "esp_fast_prov_operation.h" #include "esp_fast_prov_client_model.h" #include "ble_mesh_example_init.h" #define PROV_OWN_ADDR 0x0001 #define APP_KEY_OCTET 0x12 #define GROUP_ADDRESS 0xC000 static uint8_t dev_uuid[16] = { 0xdd, 0xdd }; static uint8_t match[] = { 0xdd, 0xdd }; static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS }, { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS }, { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS }, }; static esp_ble_mesh_cfg_srv_t config_server = { .relay = ESP_BLE_MESH_RELAY_DISABLED, .beacon = ESP_BLE_MESH_BEACON_ENABLED, #if defined(CONFIG_BLE_MESH_FRIEND) .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, #else .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, #endif #if defined(CONFIG_BLE_MESH_GATT_PROXY_SERVER) .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, #else .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, #endif .default_ttl = 7, /* 3 transmissions with a 20ms interval */ .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20), }; esp_ble_mesh_client_t config_client; esp_ble_mesh_client_t gen_onoff_client; esp_ble_mesh_client_t fast_prov_client = { .op_pair_size = ARRAY_SIZE(fast_prov_cli_op_pair), .op_pair = fast_prov_cli_op_pair, }; static esp_ble_mesh_model_op_t fast_prov_cli_op[] = { ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, 1), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS, 2), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS, 2), ESP_BLE_MESH_MODEL_OP_END, }; static esp_ble_mesh_model_t root_models[] = { ESP_BLE_MESH_MODEL_CFG_SRV(&config_server), ESP_BLE_MESH_MODEL_CFG_CLI(&config_client), ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(NULL, &gen_onoff_client), }; static esp_ble_mesh_model_t vnd_models[] = { ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, fast_prov_cli_op, NULL, &fast_prov_client), }; static esp_ble_mesh_elem_t elements[] = { ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models), }; static esp_ble_mesh_comp_t comp = { .cid = CID_ESP, .elements = elements, .element_count = ARRAY_SIZE(elements), }; static esp_ble_mesh_prov_t prov = { .prov_uuid = dev_uuid, .prov_unicast_addr = PROV_OWN_ADDR, .prov_start_address = 0x0005, .prov_attention = 0x00, .prov_algorithm = 0x00, .prov_pub_key_oob = 0x00, .prov_static_oob_val = NULL, .prov_static_oob_len = 0x00, .flags = 0x00, .iv_index = 0x00, }; example_prov_info_t prov_info = { .net_idx = ESP_BLE_MESH_KEY_PRIMARY, .app_idx = ESP_BLE_MESH_KEY_PRIMARY, .node_addr_cnt = 100, .unicast_max = 0x7FFF, .group_addr = GROUP_ADDRESS, .max_node_num = 0x01, }; static void provisioner_prov_link_open(esp_ble_mesh_prov_bearer_t bearer) { ESP_LOGI(TAG, "%s link open", bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); } static void provisioner_prov_link_close(esp_ble_mesh_prov_bearer_t bearer, uint8_t reason) { ESP_LOGI(TAG, "%s link close, reason 0x%02x", bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", reason); if (bearer == ESP_BLE_MESH_PROV_ADV && reason != 0x00) { prov_info.max_node_num++; } } static void provisioner_prov_complete(int node_index, const uint8_t uuid[16], uint16_t unicast_addr, uint8_t elem_num, uint16_t net_idx) { example_node_info_t *node = NULL; char name[11] = {0}; esp_err_t err; ESP_LOGI(TAG, "Node index: 0x%x, unicast address: 0x%02x, element num: %d, netkey index: 0x%02x", node_index, unicast_addr, elem_num, net_idx); ESP_LOGI(TAG, "Node uuid: %s", bt_hex(uuid, 16)); sprintf(name, "%s%d", "NODE-", node_index); if (esp_ble_mesh_provisioner_set_node_name(node_index, name)) { ESP_LOGE(TAG, "%s: Failed to set node name", __func__); return; } /* Sets node info */ err = example_store_node_info(uuid, unicast_addr, elem_num, prov_info.net_idx, prov_info.app_idx, LED_OFF); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to set node info", __func__); return; } /* Gets node info */ node = example_get_node_info(unicast_addr); if (!node) { ESP_LOGE(TAG, "%s: Failed to get node info", __func__); return; } /* The Provisioner will send Config AppKey Add to the node. */ example_msg_common_info_t info = { .net_idx = node->net_idx, .app_idx = node->app_idx, .dst = node->unicast_addr, .timeout = 0, .role = ROLE_PROVISIONER, }; esp_ble_mesh_cfg_app_key_add_t add_key = { .net_idx = prov_info.net_idx, .app_idx = prov_info.app_idx, }; memcpy(add_key.app_key, prov_info.app_key, 16); err = example_send_config_appkey_add(config_client.model, &info, &add_key); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); return; } } static void example_recv_unprov_adv_pkt(uint8_t dev_uuid[16], uint8_t addr[BLE_MESH_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_ble_mesh_dev_add_flag_t flag; esp_err_t err; bool reprov; if (bearer & ESP_BLE_MESH_PROV_ADV) { /* Checks if the device has been provisioned previously. If the device * is a re-provisioned one, we will ignore the 'max_node_num' count and * start to provision it directly. */ reprov = example_is_node_exist(dev_uuid); if (reprov) { goto add; } if (prov_info.max_node_num == 0) { return; } ESP_LOGI(TAG, "address: %s, address type: %d, adv type: %d", bt_hex(addr, 6), addr_type, adv_type); ESP_LOGI(TAG, "dev 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"); add: memcpy(add_dev.addr, addr, 6); 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; flag = ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG; err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev, flag); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to start provisioning a device", __func__); return; } if (!reprov) { if (prov_info.max_node_num) { prov_info.max_node_num--; } } } } static void example_provisioning_callback(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); break; case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT"); break; case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT: example_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"); provisioner_prov_link_open(param->provisioner_prov_link_open.bearer); 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); provisioner_prov_link_close(param->provisioner_prov_link_close.bearer, param->provisioner_prov_link_close.reason); break; case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT"); provisioner_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); 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 == ESP_OK) { esp_err_t err; prov_info.app_idx = param->provisioner_add_app_key_comp.app_idx; err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, CID_NVAL); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to bind AppKey with OnOff Client Model", __func__); return; } err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, CID_ESP); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to bind AppKey with Fast Prov Client Model", __func__); return; } } 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; default: break; } return; } static void example_custom_model_callback(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param) { uint32_t opcode; esp_err_t err; switch (event) { case ESP_BLE_MESH_MODEL_OPERATION_EVT: { if (!param->model_operation.model || !param->model_operation.model->op || !param->model_operation.ctx) { ESP_LOGE(TAG, "%s: model_operation parameter is NULL", __func__); return; } opcode = param->model_operation.opcode; switch (opcode) { case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: { ESP_LOGI(TAG, "%s: Fast Prov Client Model receives status, opcode 0x%04x", __func__, opcode); err = example_fast_prov_client_recv_status(param->model_operation.model, param->model_operation.ctx, param->model_operation.length, param->model_operation.msg); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to handle fast prov status message", __func__); return; } break; } default: ESP_LOGI(TAG, "%s: opcode 0x%04x", __func__, param->model_operation.opcode); break; } break; } case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_SEND_COMP_EVT, err_code %d", param->model_send_comp.err_code); break; case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT, err_code %d", param->model_publish_comp.err_code); break; case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_CLIENT_RECV_PUBLISH_MSG_EVT, opcode 0x%04x", param->client_recv_publish_msg.opcode); break; case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT, opcode 0x%04x, dst 0x%04x", param->client_send_timeout.opcode, param->client_send_timeout.ctx->addr); err = example_fast_prov_client_recv_timeout(param->client_send_timeout.opcode, param->client_send_timeout.model, param->client_send_timeout.ctx); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to resend fast prov client message", __func__); return; } break; default: break; } } static void example_config_client_callback(esp_ble_mesh_cfg_client_cb_event_t event, esp_ble_mesh_cfg_client_cb_param_t *param) { example_node_info_t *node = NULL; uint32_t opcode; uint16_t address; esp_err_t err; ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x", __func__, param->error_code, event, param->params->ctx.addr); opcode = param->params->opcode; address = param->params->ctx.addr; node = example_get_node_info(address); if (!node) { ESP_LOGE(TAG, "%s: Failed to get node info", __func__); return; } if (param->error_code) { ESP_LOGE(TAG, "Failed to send config client message, opcode: 0x%04x", opcode); return; } switch (event) { case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: break; case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: switch (opcode) { case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { example_fast_prov_info_set_t set = {0}; if (!node->reprov || !ESP_BLE_MESH_ADDR_IS_UNICAST(node->unicast_min)) { /* If the node is a new one or the node is re-provisioned but the information of the node * has not been set before, here we will set the Fast Prov Info Set info to the node. */ node->node_addr_cnt = prov_info.node_addr_cnt; node->unicast_min = prov_info.unicast_min; node->unicast_max = prov_info.unicast_max; node->flags = prov.flags; node->iv_index = prov.iv_index; node->fp_net_idx = prov_info.net_idx; node->group_addr = prov_info.group_addr; node->match_len = prov_info.match_len; memcpy(node->match_val, prov_info.match_val, prov_info.match_len); node->action = 0x81; } set.ctx_flags = 0x037F; memcpy(&set.node_addr_cnt, &node->node_addr_cnt, sizeof(example_node_info_t) - offsetof(example_node_info_t, node_addr_cnt)); example_msg_common_info_t info = { .net_idx = node->net_idx, .app_idx = node->app_idx, .dst = node->unicast_addr, .timeout = 0, .role = ROLE_PROVISIONER, }; err = example_send_fast_prov_info_set(fast_prov_client.model, &info, &set); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to set Fast Prov Info Set message", __func__); return; } break; } default: break; } break; case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: break; case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: switch (opcode) { case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { example_msg_common_info_t info = { .net_idx = node->net_idx, .app_idx = node->app_idx, .dst = node->unicast_addr, .timeout = 0, .role = ROLE_PROVISIONER, }; esp_ble_mesh_cfg_app_key_add_t add_key = { .net_idx = prov_info.net_idx, .app_idx = prov_info.app_idx, }; memcpy(add_key.app_key, prov_info.app_key, 16); err = example_send_config_appkey_add(config_client.model, &info, &add_key); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); return; } break; } default: break; } break; default: ESP_LOGE(TAG, "Not a config client status message event"); break; } } static void example_generic_client_callback(esp_ble_mesh_generic_client_cb_event_t event, esp_ble_mesh_generic_client_cb_param_t *param) { example_node_info_t *node = NULL; uint32_t opcode; uint16_t address; ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x", __func__, param->error_code, event, param->params->ctx.addr); opcode = param->params->opcode; address = param->params->ctx.addr; node = example_get_node_info(address); if (!node) { ESP_LOGE(TAG, "%s: Failed to get node info", __func__); return; } if (param->error_code) { ESP_LOGE(TAG, "Failed to send generic client message, opcode: 0x%04x", opcode); return; } switch (event) { case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: break; 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, "node->onoff: 0x%02x", node->onoff); break; default: break; } break; case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: break; case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: break; default: ESP_LOGE(TAG, "Not a generic client status message event"); break; } } static esp_err_t ble_mesh_init(void) { esp_err_t err; prov_info.unicast_min = prov.prov_start_address + prov_info.max_node_num; prov_info.match_len = sizeof(match); memcpy(prov_info.match_val, match, sizeof(match)); memset(prov_info.app_key, APP_KEY_OCTET, sizeof(prov_info.app_key)); esp_ble_mesh_register_prov_callback(example_provisioning_callback); esp_ble_mesh_register_custom_model_callback(example_custom_model_callback); esp_ble_mesh_register_config_client_callback(example_config_client_callback); esp_ble_mesh_register_generic_client_callback(example_generic_client_callback); err = esp_ble_mesh_init(&prov, &comp); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to initialize BLE Mesh", __func__); return ESP_FAIL; } err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, 0x02, 0x00, false); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to set matching device UUID", __func__); return ESP_FAIL; } err = esp_ble_mesh_client_model_init(&vnd_models[0]); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to initialize fast prov client model", __func__); return ESP_FAIL; } err = esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to enable provisioning", __func__); return ESP_FAIL; } err = esp_ble_mesh_provisioner_add_local_app_key(prov_info.app_key, prov_info.net_idx, prov_info.app_idx); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to add local application key", __func__); return ESP_FAIL; } ESP_LOGI(TAG, "BLE Mesh Provisioner initialized"); return err; } void app_main(void) { esp_err_t err; ESP_LOGI(TAG, "Initializing..."); err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); err = bluetooth_init(); if (err) { ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err); return; } ble_mesh_get_dev_uuid(dev_uuid); /* Initialize the Bluetooth Mesh Subsystem */ err = ble_mesh_init(); if (err) { ESP_LOGE(TAG, "Failed to initialize BLE Mesh (err %d)", err); } }