// 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 #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_err.h" #include "esp_log.h" #include "nvs_flash.h" #include "esp_console.h" #include "esp_vfs_dev.h" #include "driver/uart.h" #include "linenoise/linenoise.h" #include "argtable3/argtable3.h" #include "cmd_decl.h" #include "esp_bt.h" #include "esp_bt_main.h" #include "esp_bt_device.h" #include "esp_ble_mesh_defs.h" #include "esp_ble_mesh_common_api.h" #include "esp_ble_mesh_networking_api.h" #include "esp_ble_mesh_provisioning_api.h" #include "esp_ble_mesh_config_model_api.h" #include "esp_ble_mesh_generic_model_api.h" #include "board.h" #include "esp_fast_prov_operation.h" #include "esp_fast_prov_client_model.h" #include "esp_fast_prov_server_model.h" #define TAG "BLE_MESH_WIFI_COEXIST_DEMO" extern struct _led_state led_state[3]; extern struct k_delayed_work send_self_prov_node_addr_timer; extern bt_mesh_atomic_t fast_prov_cli_flags; static uint8_t dev_uuid[16] = { 0xdd, 0xdd }; static uint8_t prov_start_num = 0; static bool prov_start = false; 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, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK }, { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS }, }; /* Configuration Client Model user_data */ esp_ble_mesh_client_t config_client; /* Configuration Server Model user_data */ esp_ble_mesh_cfg_srv_t config_server = { .relay = ESP_BLE_MESH_RELAY_ENABLED, .beacon = ESP_BLE_MESH_BEACON_DISABLED, #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) .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 20ms interval */ .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20), }; /* Fast Prov Client Model user_data */ 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, }; /* Fast Prov Server Model user_data */ example_fast_prov_server_t fast_prov_server = { .primary_role = false, .max_node_num = 6, .prov_node_cnt = 0x0, .unicast_min = ESP_BLE_MESH_ADDR_UNASSIGNED, .unicast_max = ESP_BLE_MESH_ADDR_UNASSIGNED, .unicast_cur = ESP_BLE_MESH_ADDR_UNASSIGNED, .unicast_step = 0x0, .flags = 0x0, .iv_index = 0x0, .net_idx = ESP_BLE_MESH_KEY_UNUSED, .app_idx = ESP_BLE_MESH_KEY_UNUSED, .group_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, .prim_prov_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, .match_len = 0x0, .pend_act = FAST_PROV_ACT_NONE, .state = STATE_IDLE, }; static esp_ble_mesh_model_op_t onoff_op[] = { ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2), ESP_BLE_MESH_MODEL_OP_END, }; ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_pub, 2 + 1, ROLE_FAST_PROV); static esp_ble_mesh_model_op_t fast_prov_srv_op[] = { ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, 3), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, 16), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR, 2), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, 0), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_ADD, 2), ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_DELETE, 2), ESP_BLE_MESH_MODEL_OP_END, }; 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_ACK, 0), 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_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, onoff_op, &onoff_pub, &led_state[1]), }; static esp_ble_mesh_model_t vnd_models[] = { ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV, fast_prov_srv_op, NULL, &fast_prov_server), 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 = { .uuid = dev_uuid, .output_size = 0, .output_actions = 0, .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, }; static void gen_onoff_get_handler(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint16_t len, uint8_t *data) { struct _led_state *led = NULL; uint8_t send_data; esp_err_t err; led = (struct _led_state *)model->user_data; if (!led) { ESP_LOGE(TAG, "%s: Failed to get generic onoff server model user_data", __func__); return; } send_data = led->current; /* Sends Generic OnOff Status as a reponse to Generic OnOff Get */ err = esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, sizeof(send_data), &send_data); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Generic OnOff Status message", __func__); return; } /* When the node receives the first Generic OnOff Get/Set/Set Unack message, it will * start the timer used to disable fast provisioning functionality. */ if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); } } static void gen_onoff_set_unack_handler(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint16_t len, uint8_t *data) { struct _led_state *led = NULL; led = (struct _led_state *)model->user_data; if (!led) { ESP_LOGE(TAG, "%s: Failed to get generic onoff server model user_data", __func__); return; } led->current = data[0]; gpio_set_level(led->pin, led->current); /* When the node receives the first Generic OnOff Get/Set/Set Unack message, it will * start the timer used to disable fast provisioning functionality. */ if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); } } static void gen_onoff_set_handler(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint16_t len, uint8_t *data) { gen_onoff_set_unack_handler(model, ctx, len, data); gen_onoff_get_handler(model, ctx, len, data); } static void node_prov_complete(uint16_t net_idx, uint16_t addr, uint8_t flags, uint32_t iv_index) { ESP_LOGI(TAG, "net_idx: 0x%04x, unicast_addr: 0x%04x", net_idx, addr); ESP_LOGI(TAG, "flags: 0x%02x, iv_index: 0x%08x", flags, iv_index); board_prov_complete(); /* Updates the net_idx used by Fast Prov Server model, and it can also * be updated if the Fast Prov Info Set message contains a valid one. */ fast_prov_server.net_idx = net_idx; } static void provisioner_prov_link_open(esp_ble_mesh_prov_bearer_t bearer) { ESP_LOGI(TAG, "%s: bearer %s", __func__, 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: bearer %s, reason 0x%02x", __func__, bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", reason); if (prov_start_num) { prov_start_num--; } } static void provisioner_prov_complete(int node_idx, const uint8_t uuid[16], uint16_t unicast_addr, uint8_t element_num, uint16_t net_idx) { example_node_info_t *node = NULL; esp_err_t err; if (example_is_node_exist(uuid) == false) { fast_prov_server.prov_node_cnt++; } ESP_LOG_BUFFER_HEX("Device uuid", uuid + 2, 6); ESP_LOGI(TAG, "Unicast address 0x%04x", unicast_addr); /* Sets node info */ err = example_store_node_info(uuid, unicast_addr, element_num, net_idx, fast_prov_server.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; } if (fast_prov_server.primary_role == true) { /* If the Provisioner is the primary one (i.e. provisioned by the phone), it shall * store self-provisioned node addresses; * If the node_addr_cnt configured by the phone is small than or equal to the * maximum number of nodes it can provision, it shall reset the timer which is used * to send all node addresses to the phone. */ err = example_store_remote_node_address(unicast_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to store node address 0x%04x", __func__, unicast_addr); return; } if (fast_prov_server.node_addr_cnt != FAST_PROV_NODE_COUNT_MIN && fast_prov_server.node_addr_cnt <= fast_prov_server.max_node_num) { if (bt_mesh_atomic_test_and_clear_bit(fast_prov_server.srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_cancel(&fast_prov_server.gatt_proxy_enable_timer); } if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_submit(&fast_prov_server.gatt_proxy_enable_timer, GATT_PROXY_ENABLE_TIMEOUT); } } } else { /* When a device is provisioned, the non-primary Provisioner shall reset the timer * which is used to send node addresses to the primary Provisioner. */ if (bt_mesh_atomic_test_and_clear_bit(&fast_prov_cli_flags, SEND_SELF_PROV_NODE_ADDR_START)) { k_delayed_work_cancel(&send_self_prov_node_addr_timer); } if (!bt_mesh_atomic_test_and_set_bit(&fast_prov_cli_flags, SEND_SELF_PROV_NODE_ADDR_START)) { k_delayed_work_submit(&send_self_prov_node_addr_timer, SEND_SELF_PROV_NODE_ADDR_TIMEOUT); } } if (bt_mesh_atomic_test_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { /* When a device is provisioned, and the stop_prov flag of the Provisioner has been * set, the Provisioner shall reset the timer which is used to stop the provisioner * functionality. */ k_delayed_work_cancel(&fast_prov_server.disable_fast_prov_timer); k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); } /* 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_FAST_PROV, }; err = example_send_config_appkey_add(config_client.model, &info, NULL); 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[ESP_BD_ADDR_LEN], esp_ble_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; /* In Fast Provisioning, the Provisioner should only use PB-ADV to provision devices. */ if (prov_start && (bearer & ESP_BLE_MESH_PROV_ADV)) { /* Checks if the device is a reprovisioned one. */ if (example_is_node_exist(dev_uuid) == false) { if ((prov_start_num >= fast_prov_server.max_node_num) || (fast_prov_server.prov_node_cnt >= fast_prov_server.max_node_num)) { return; } } add_dev.addr_type = (uint8_t)addr_type; add_dev.oob_info = oob_info; add_dev.bearer = (uint8_t)bearer; memcpy(add_dev.uuid, dev_uuid, 16); memcpy(add_dev.addr, addr, ESP_BD_ADDR_LEN); 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 device", __func__); return; } /* If adding unprovisioned device successfully, increase prov_start_num */ prov_start_num++; } return; } static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param) { esp_err_t err; 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_NODE_PROV_ENABLE_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, err_code: %d", param->node_prov_enable_comp.err_code); break; case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, bearer: %s", param->node_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); break; case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, bearer: %s", param->node_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); break; case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT"); node_prov_complete(param->node_prov_complete.net_idx, param->node_prov_complete.addr, param->node_prov_complete.flags, param->node_prov_complete.iv_index); break; case ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT"); if (fast_prov_server.primary_role == true) { config_server.relay = ESP_BLE_MESH_RELAY_DISABLED; } prov_start = true; 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"); 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"); 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_SET_FAST_PROV_INFO_COMP_EVT: { ESP_LOGI(TAG, "ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT"); ESP_LOGI(TAG, "status_unicast: 0x%02x, status_net_idx: 0x%02x, status_match 0x%02x", param->set_fast_prov_info_comp.status_unicast, param->set_fast_prov_info_comp.status_net_idx, param->set_fast_prov_info_comp.status_match); err = example_handle_fast_prov_info_set_comp_evt(fast_prov_server.model, param->set_fast_prov_info_comp.status_unicast, param->set_fast_prov_info_comp.status_net_idx, param->set_fast_prov_info_comp.status_match); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to handle Fast Prov Info Set complete event", __func__); return; } break; } case ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT: ESP_LOGI(TAG, "ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT, status_action 0x%02x", param->set_fast_prov_action_comp.status_action); err = example_handle_fast_prov_action_set_comp_evt(fast_prov_server.model, param->set_fast_prov_action_comp.status_action); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to handle Fast Prov Action Set complete event", __func__); return; } break; default: break; } return; } static void example_ble_mesh_custom_model_cb(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_MODEL_OP_GEN_ONOFF_SET: gen_onoff_set_handler(param->model_operation.model, param->model_operation.ctx, param->model_operation.length, param->model_operation.msg); break; case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: gen_onoff_set_unack_handler(param->model_operation.model, param->model_operation.ctx, param->model_operation.length, param->model_operation.msg); break; case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_ADD: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_DELETE: { ESP_LOGI(TAG, "%s: Fast prov server receives msg, opcode 0x%04x", __func__, opcode); struct net_buf_simple buf = { .len = param->model_operation.length, .data = param->model_operation.msg, }; err = example_fast_prov_server_recv_msg(param->model_operation.model, param->model_operation.ctx, &buf); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to handle fast prov client message", __func__); return; } break; } 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_ACK: { ESP_LOGI(TAG, "%s: Fast prov client receives msg, 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 server 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); switch (param->model_send_comp.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_ACK: case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: err = example_handle_fast_prov_status_send_comp_evt(param->model_send_comp.err_code, param->model_send_comp.opcode, param->model_send_comp.model, param->model_send_comp.ctx); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to handle fast prov status send complete event", __func__); return; } break; default: break; } 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: Faield to resend fast prov client message", __func__); return; } break; default: break; } } 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) { 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 == false) { /* After sending Config AppKey Add successfully, start to send Fast Prov Info Set */ if (fast_prov_server.unicast_cur >= fast_prov_server.unicast_max) { /* TODO: * 1. If unicast_cur is >= unicast_max, we can also send the message to enable * the Provisioner functionality on the node, and need to add another vendor * message used by the node to require a new unicast address range from primary * Provisioner, and before get the correct response, the node should pend * the fast provisioning functionality. * 2. Currently if address is not enough, the Provisioner will only add the group * address to the node. */ ESP_LOGW(TAG, "%s: Not enough address to be assigned", __func__); node->lack_of_addr = true; } else { /* Send fast_prov_info_set message to node */ node->lack_of_addr = false; node->unicast_min = fast_prov_server.unicast_cur; if (fast_prov_server.unicast_cur + fast_prov_server.unicast_step >= fast_prov_server.unicast_max) { node->unicast_max = fast_prov_server.unicast_max; } else { node->unicast_max = fast_prov_server.unicast_cur + fast_prov_server.unicast_step; } node->flags = fast_prov_server.flags; node->iv_index = fast_prov_server.iv_index; node->fp_net_idx = fast_prov_server.net_idx; node->group_addr = fast_prov_server.group_addr; node->prov_addr = fast_prov_server.prim_prov_addr; node->match_len = fast_prov_server.match_len; memcpy(node->match_val, fast_prov_server.match_val, fast_prov_server.match_len); node->action = FAST_PROV_ACT_ENTER; fast_prov_server.unicast_cur = node->unicast_max + 1; } } if (node->lack_of_addr == false) { set.ctx_flags = 0x03FE; memcpy(&set.unicast_min, &node->unicast_min, sizeof(example_node_info_t) - offsetof(example_node_info_t, unicast_min)); } else { set.ctx_flags = BIT(6); set.group_addr = fast_prov_server.group_addr; } example_msg_common_info_t info = { .net_idx = node->net_idx, .app_idx = node->app_idx, .dst = node->unicast_addr, .timeout = 0, .role = ROLE_FAST_PROV, }; err = example_send_fast_prov_info_set(fast_prov_client.model, &info, &set); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send 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_FAST_PROV, }; err = example_send_config_appkey_add(config_client.model, &info, NULL); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); return; } break; } default: break; } break; default: return; } } static void example_ble_mesh_config_server_cb(esp_ble_mesh_cfg_server_cb_event_t event, esp_ble_mesh_cfg_server_cb_param_t *param) { esp_err_t err; ESP_LOGI(TAG, "%s, event = 0x%02x, opcode = 0x%04x, addr: 0x%04x", __func__, event, param->ctx.recv_op, param->ctx.addr); switch (event) { case ESP_BLE_MESH_CFG_SERVER_RECV_MSG_EVT: switch (param->ctx.recv_op) { case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: ESP_LOGI(TAG, "Config Server get Config AppKey Add"); err = example_handle_config_app_key_add_evt(param->status_cb.app_key_add.app_idx); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to bind app_idx 0x%04x with non-config models", __func__, param->status_cb.app_key_add.app_idx); return; } break; default: break; } break; default: return; } } static esp_err_t ble_mesh_init(void) { esp_err_t err; /* First two bytes of device uuid is compared with match value by Provisioner */ memcpy(dev_uuid + 2, esp_bt_dev_get_address(), 6); esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb); esp_ble_mesh_register_custom_model_callback(example_ble_mesh_custom_model_cb); esp_ble_mesh_register_config_client_callback(example_ble_mesh_config_client_cb); esp_ble_mesh_register_config_server_callback(example_ble_mesh_config_server_cb); err = esp_ble_mesh_init(&prov, &comp); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to initialize BLE Mesh", __func__); return err; } err = example_fast_prov_server_init(&vnd_models[0]); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to initialize fast prov server model", __func__); return err; } err = esp_ble_mesh_client_model_init(&vnd_models[1]); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to initialize fast prov client model", __func__); return err; } k_delayed_work_init(&send_self_prov_node_addr_timer, example_send_self_prov_node_addr); err = esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to enable node provisioning", __func__); return err; } ESP_LOGI(TAG, "BLE Mesh Wi-Fi Coexist Node initialized"); board_led_operation(LED_B, LED_ON); return ESP_OK; } static esp_err_t bluetooth_init(void) { esp_err_t ret; ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ret = esp_bt_controller_init(&bt_cfg); if (ret) { ESP_LOGE(TAG, "%s initialize controller failed", __func__); return ret; } ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); if (ret) { ESP_LOGE(TAG, "%s enable controller failed", __func__); return ret; } ret = esp_bluedroid_init(); if (ret) { ESP_LOGE(TAG, "%s init bluetooth failed", __func__); return ret; } ret = esp_bluedroid_enable(); if (ret) { ESP_LOGE(TAG, "%s enable bluetooth failed", __func__); return ret; } return ret; } #define WIFI_CONNECTED_BIT BIT0 static void initialize_console(void) { /* Disable buffering on stdin and stdout */ setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); /* Move the caret to the beginning of the next line on '\n' */ esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); /* Install UART driver for interrupt-driven reads and writes */ ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0) ); /* Tell VFS to use UART driver */ esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); /* Initialize the console */ esp_console_config_t console_config = { .max_cmdline_args = 32, .max_cmdline_length = 256, #if CONFIG_LOG_COLORS .hint_color = atoi(LOG_COLOR_CYAN) #endif }; ESP_ERROR_CHECK( esp_console_init(&console_config) ); /* Configure linenoise line completion library */ /* Enable multiline editing. If not set, long commands will scroll within * single line. */ linenoiseSetMultiLine(1); /* Tell linenoise where to get command completions and hints */ linenoiseSetCompletionCallback(&esp_console_get_completion); linenoiseSetHintsCallback((linenoiseHintsCallback *) &esp_console_get_hint); /* Set command history size */ linenoiseHistorySetMaxLen(100); } static void wifi_console_init(void) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); initialise_wifi(); initialize_console(); /* Register commands */ esp_console_register_help_command(); register_wifi(); /* Prompt to be printed before each line. * This can be customized, made dynamic, etc. */ const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR; printf("\n ==================================================\n"); printf(" | Steps to test WiFi throughput |\n"); printf(" | |\n"); printf(" | 1. Print 'help' to gain overview of commands |\n"); printf(" | 2. Configure device to station or soft-AP |\n"); printf(" | 3. Setup WiFi connection |\n"); printf(" | 4. Run iperf to test UDP/TCP RX/TX throughput |\n"); printf(" | |\n"); printf(" =================================================\n\n"); /* Figure out if the terminal supports escape sequences */ int probe_status = linenoiseProbe(); if (probe_status) { /* zero indicates success */ printf("\n" "Your terminal application does not support escape sequences.\n" "Line editing and history features are disabled.\n" "On Windows, try using Putty instead.\n"); linenoiseSetDumbMode(1); #if CONFIG_LOG_COLORS /* Since the terminal doesn't support escape sequences, * don't use color codes in the prompt. */ prompt = "esp32> "; #endif //CONFIG_LOG_COLORS } /* Main loop */ while (true) { /* Get a line using linenoise. * The line is returned when ENTER is pressed. */ char *line = linenoise(prompt); if (line == NULL) { /* Ignore empty lines */ continue; } /* Add the command to the history */ linenoiseHistoryAdd(line); /* Try to run the command */ int ret; esp_err_t err = esp_console_run(line, &ret); if (err == ESP_ERR_NOT_FOUND) { printf("Unrecognized command\n"); } else if (err == ESP_OK && ret != ESP_OK) { printf("Command returned non-zero error code: 0x%x\n", ret); } else if (err != ESP_OK) { printf("Internal error: %s\n", esp_err_to_name(err)); } /* linenoise allocates line buffer on the heap, so need to free it */ linenoiseFree(line); } return; } void app_main(void) { esp_err_t err; ESP_LOGI(TAG, "Initializing..."); err = board_init(); if (err) { ESP_LOGE(TAG, "board_init failed (err %d)", err); return; } err = bluetooth_init(); if (err) { ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err); return; } /* Initialize the Bluetooth Mesh Subsystem */ err = ble_mesh_init(); if (err) { ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err); return; } wifi_console_init(); }