OVMS3-idf/examples/bluetooth/esp_ble_mesh/components/fast_prov_vendor_model/esp_fast_prov_client_model.c
lly ecf7ea897e ble_mesh: Rename common_vendor_models to components
Since the scripts of CI will bypass components when trying
to get EXAMPLE_PATHS, and these BLE Mesh components will
only be used by other mesh examples, i.e. no need to be
compiled as a single example, so we rename the folder
to componnets.
2020-05-22 10:56:26 +08:00

407 lines
15 KiB
C

// 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 <stdio.h>
#include <string.h>
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_local_data_operation_api.h"
#include "esp_fast_prov_operation.h"
#include "esp_fast_prov_client_model.h"
#include "esp_fast_prov_server_model.h"
#define TAG "FAST_PROV_CLIENT"
/* Note: these variables are used by ble_mesh_fast_prov_client demo */
#define GET_ALL_NODE_ADDR_TIMEOUT K_SECONDS(60)
/* Timer used to send Fast Prov All Node Addr Get message */
#if !defined(CONFIG_BLE_MESH_FAST_PROV)
static struct k_delayed_work get_all_node_addr_timer;
#endif
/* Unicast address of the Primary Provisioner */
static uint16_t prim_prov_addr;
/* Note: these variables are used by ble_mesh_fast_prov_server demo */
/* Send 4 node addresses (8 octets) most each time to prevent segmentation */
#define NODE_ADDR_SEND_MAX_LEN 8
/* Timer used to send self-provisioned node addresses to Primary Provisioner */
struct k_delayed_work send_self_prov_node_addr_timer;
bt_mesh_atomic_t fast_prov_cli_flags;
/* Self-provisioned node addresses that are being sent */
typedef struct {
struct net_buf_simple *addr; /* Unicast addresses of self-provisioned nodes being sent */
bool send_succeed; /* Indicate if sent operation is successful */
bool ack_received; /* Indicate if sent address has been acked */
} example_send_prov_node_addr_t;
NET_BUF_SIMPLE_DEFINE_STATIC(send_addr, NODE_ADDR_SEND_MAX_LEN);
static example_send_prov_node_addr_t node_addr_send = {
.addr = &send_addr,
.send_succeed = true,
.ack_received = true,
};
/* Self-provisioned node addresses that have been sent successfully */
uint16_t addr_already_sent[CONFIG_BLE_MESH_MAX_PROV_NODES];
static example_fast_prov_server_t *get_fast_prov_srv_user_data(void)
{
esp_ble_mesh_model_t *model = NULL;
model = example_find_model(esp_ble_mesh_get_primary_element_address(),
ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV, CID_ESP);
if (!model) {
ESP_LOGE(TAG, "%s: Failed to get config server model", __func__);
return NULL;
}
return (example_fast_prov_server_t *)(model->user_data);
}
/* Timeout handler for send_self_prov_node_addr_timer */
void example_send_self_prov_node_addr(struct k_work *work)
{
example_fast_prov_server_t *fast_prov_srv = NULL;
esp_ble_mesh_model_t *model = NULL;
int i, j, err;
bt_mesh_atomic_test_and_clear_bit(&fast_prov_cli_flags, SEND_SELF_PROV_NODE_ADDR_START);
fast_prov_srv = get_fast_prov_srv_user_data();
if (!fast_prov_srv) {
ESP_LOGE(TAG, "%s: Failed to get fast prov server model user_data", __func__);
return;
}
model = example_find_model(esp_ble_mesh_get_primary_element_address(),
ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, CID_ESP);
if (!model) {
ESP_LOGE(TAG, "%s: Failed to get fast prov client model", __func__);
return;
}
if (node_addr_send.send_succeed == true && node_addr_send.ack_received == false) {
ESP_LOGW(TAG, "%s: Previous node address message is being sent", __func__);
return;
}
if (node_addr_send.send_succeed == true) {
/* If the previous node address message has been sent successfully, and when
* timeout event comes, we will update the send buffer (node_addr_send).
*/
net_buf_simple_reset(node_addr_send.addr);
for (i = 0; i < CONFIG_BLE_MESH_MAX_PROV_NODES; i++) {
uint16_t addr = example_get_node_address(i);
if (!ESP_BLE_MESH_ADDR_IS_UNICAST(addr)) {
continue;
}
for (j = 0; j < ARRAY_SIZE(addr_already_sent); j++) {
if (addr == addr_already_sent[j]) {
ESP_LOGW(TAG, "%s: node addr 0x%04x has already been sent", __func__, addr);
break;
}
}
if (j != ARRAY_SIZE(addr_already_sent)) {
continue;
}
net_buf_simple_add_le16(node_addr_send.addr, addr);
if (node_addr_send.addr->len == NODE_ADDR_SEND_MAX_LEN) {
break;
}
}
}
if (node_addr_send.addr->len) {
example_msg_common_info_t info = {
.net_idx = fast_prov_srv->net_idx,
.app_idx = fast_prov_srv->app_idx,
.dst = fast_prov_srv->prim_prov_addr,
.timeout = 0,
.role = ROLE_FAST_PROV,
};
err = example_send_fast_prov_self_prov_node_addr(model, &info, node_addr_send.addr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send node address", __func__);
node_addr_send.send_succeed = false;
} else {
node_addr_send.send_succeed = true;
}
node_addr_send.ack_received = false;
}
/* If sending node addresses failed, the Provisioner will start the timer in case
* no other devices will be provisioned and the timer will never start.
*/
if (node_addr_send.send_succeed == false && node_addr_send.ack_received == false) {
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);
}
}
return;
}
#if !defined(CONFIG_BLE_MESH_FAST_PROV)
/* Timeout handler for get_all_node_addr_timer */
static void example_get_all_node_addr(struct k_work *work)
{
esp_ble_mesh_model_t *model = NULL;
example_node_info_t *node = NULL;
esp_err_t err;
node = example_get_node_info(prim_prov_addr);
if (!node) {
ESP_LOGE(TAG, "%s: Failed to get node info", __func__);
return;
}
model = example_find_model(esp_ble_mesh_get_primary_element_address(),
ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, CID_ESP);
if (!model) {
ESP_LOGE(TAG, "%s: Failed to get model info", __func__);
return;
}
example_msg_common_info_t info = {
.net_idx = node->net_idx,
.app_idx = node->app_idx,
.dst = node->unicast_addr,
.timeout = 10000,
.role = ROLE_PROVISIONER,
};
err = example_send_fast_prov_all_node_addr_get(model, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send Fast Prov Node Address Get message", __func__);
return;
}
}
#endif
esp_err_t example_fast_prov_client_recv_timeout(uint32_t opcode, esp_ble_mesh_model_t *model,
esp_ble_mesh_msg_ctx_t *ctx)
{
#if defined(CONFIG_BLE_MESH_FAST_PROV)
example_fast_prov_server_t *fast_prov_srv = NULL;
#endif
example_node_info_t *node = NULL;
esp_err_t err;
ESP_LOGW(TAG, "%s: Receive fast prov server status timeout", __func__);
if (!model || !ctx) {
return ESP_ERR_INVALID_ARG;
}
#if defined(CONFIG_BLE_MESH_FAST_PROV)
fast_prov_srv = get_fast_prov_srv_user_data();
if (!fast_prov_srv) {
ESP_LOGE(TAG, "%s: Failed to get fast prov server model user_data", __func__);
return ESP_FAIL;
}
#endif
switch (opcode) {
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET: {
example_fast_prov_info_set_t set = {0};
node = example_get_node_info(ctx->addr);
if (!node) {
return ESP_FAIL;
}
example_msg_common_info_t info = {
.net_idx = node->net_idx,
.app_idx = node->app_idx,
.dst = node->unicast_addr,
.timeout = 0,
};
#if defined(CONFIG_BLE_MESH_FAST_PROV)
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_srv->group_addr;
}
info.role = ROLE_FAST_PROV;
#else
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));
info.role = ROLE_PROVISIONER;
#endif
err = example_send_fast_prov_info_set(model, &info, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send Fast Prov Info Set message", __func__);
return ESP_FAIL;
}
break;
}
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD:
break;
#if defined(CONFIG_BLE_MESH_FAST_PROV)
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR:
if (node_addr_send.addr->len) {
example_msg_common_info_t info = {
.net_idx = fast_prov_srv->net_idx,
.app_idx = fast_prov_srv->app_idx,
.dst = fast_prov_srv->prim_prov_addr,
.timeout = 0,
.role = ROLE_FAST_PROV,
};
err = example_send_fast_prov_self_prov_node_addr(model, &info, node_addr_send.addr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send Fast Prov Node Addr message", __func__);
node_addr_send.send_succeed = false;
} else {
node_addr_send.send_succeed = true;
}
node_addr_send.ack_received = false;
}
if (node_addr_send.send_succeed == false && node_addr_send.ack_received == false) {
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);
}
}
break;
#endif
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET: {
node = example_get_node_info(ctx->addr);
if (!node) {
return ESP_FAIL;
}
example_msg_common_info_t info = {
.net_idx = node->net_idx,
.app_idx = node->app_idx,
.dst = node->unicast_addr,
.timeout = 10000,
.role = ROLE_PROVISIONER,
};
err = example_send_fast_prov_all_node_addr_get(model, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send Fast Prov Node Address message", __func__);
return ESP_FAIL;
}
break;
}
default:
break;
}
return ESP_OK;
}
esp_err_t example_fast_prov_client_recv_status(esp_ble_mesh_model_t *model,
esp_ble_mesh_msg_ctx_t *ctx,
uint16_t len, const uint8_t *data)
{
if (!model || !ctx) {
return ESP_ERR_INVALID_ARG;
}
ESP_LOG_BUFFER_HEX("fast prov client receives", data, len);
switch (ctx->recv_op) {
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS:
ESP_LOG_BUFFER_HEX("fast prov info status", data, len);
#if !defined(CONFIG_BLE_MESH_FAST_PROV)
prim_prov_addr = ctx->addr;
k_delayed_work_init(&get_all_node_addr_timer, example_get_all_node_addr);
k_delayed_work_submit(&get_all_node_addr_timer, GET_ALL_NODE_ADDR_TIMEOUT);
#endif
break;
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS:
ESP_LOGI(TAG, "status_key: 0x%02x, status_act: 0x%02x", data[0], data[1]);
break;
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK: {
/* node address message has been acked */
int i, j;
uint8_t length = node_addr_send.addr->len;
node_addr_send.send_succeed = true;
node_addr_send.ack_received = true;
for (i = 0; i < (length >> 1); i++) {
uint16_t addr = net_buf_simple_pull_le16(node_addr_send.addr);
if (ESP_BLE_MESH_ADDR_IS_UNICAST(addr)) {
for (j = 0; j < ARRAY_SIZE(addr_already_sent); j++) {
if (addr_already_sent[j] == addr) {
break;
}
}
if (j != ARRAY_SIZE(addr_already_sent)) {
continue;
}
for (j = 0; j < ARRAY_SIZE(addr_already_sent); j++) {
if (addr_already_sent[j] == ESP_BLE_MESH_ADDR_UNASSIGNED) {
addr_already_sent[j] = addr;
break;
}
}
if (j == ARRAY_SIZE(addr_already_sent)) {
ESP_LOGE(TAG, "%s: No place to store the sent node address", __func__);
return ESP_FAIL;
}
}
}
/* In case spending too much time on the first node address message(i.e. failed
* to receive ack), and the timer for the second or further messages is timeout,
* thus the Provisioner will never be able to send other addresses.
*/
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);
}
break;
}
case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: {
ESP_LOG_BUFFER_HEX("Node address", data, len);
esp_ble_mesh_model_t *cli_model = NULL;
example_node_info_t *node = NULL;
esp_err_t err;
node = example_get_node_info(prim_prov_addr);
if (!node) {
ESP_LOGE(TAG, "%s: Failed to get node info", __func__);
return ESP_FAIL;
}
cli_model = example_find_model(esp_ble_mesh_get_primary_element_address(),
ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, CID_NVAL);
if (!cli_model) {
ESP_LOGE(TAG, "%s: Failed to get Generic OnOff Client Model info", __func__);
return ESP_FAIL;
}
example_msg_common_info_t info = {
.net_idx = node->net_idx,
.app_idx = node->app_idx,
.dst = node->group_addr,
.timeout = 0,
.role = ROLE_PROVISIONER,
};
err = example_send_generic_onoff_set(cli_model, &info, LED_ON, 0x00, false);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: Failed to send Generic OnOff Set Unack message", __func__);
return ESP_FAIL;
}
break;
}
default:
ESP_LOGE(TAG, "%s: Invalid fast prov status opcode", __func__);
return ESP_FAIL;
}
return ESP_OK;
}