Add a new Kconfig option to control the behavior of sw0. If enabled, sw0 button will be used for provisioning. This option also allows to select GPIO. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
414 lines
9.7 KiB
C
414 lines
9.7 KiB
C
/*
|
|
* Copyright (c) 2019 Tobias Svehagen
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/sys/printk.h>
|
|
#include <zephyr/settings/settings.h>
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#define SW0_NODE DT_ALIAS(sw0)
|
|
|
|
static const uint16_t net_idx;
|
|
static const uint16_t app_idx;
|
|
static uint16_t self_addr = 1, node_addr;
|
|
static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };
|
|
static uint8_t node_uuid[16];
|
|
|
|
K_SEM_DEFINE(sem_unprov_beacon, 0, 1);
|
|
K_SEM_DEFINE(sem_node_added, 0, 1);
|
|
#ifdef CONFIG_MESH_PROVISIONER_USE_SW0
|
|
K_SEM_DEFINE(sem_button_pressed, 0, 1);
|
|
#endif
|
|
|
|
static struct bt_mesh_cfg_cli cfg_cli = {
|
|
};
|
|
|
|
static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr,
|
|
uint8_t test_id, uint16_t cid, uint8_t *faults,
|
|
size_t fault_count)
|
|
{
|
|
size_t i;
|
|
|
|
printk("Health Current Status from 0x%04x\n", addr);
|
|
|
|
if (!fault_count) {
|
|
printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n",
|
|
test_id, cid);
|
|
return;
|
|
}
|
|
|
|
printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n",
|
|
test_id, cid, fault_count);
|
|
|
|
for (i = 0; i < fault_count; i++) {
|
|
printk("\t0x%02x\n", faults[i]);
|
|
}
|
|
}
|
|
|
|
static struct bt_mesh_health_cli health_cli = {
|
|
.current_status = health_current_status,
|
|
};
|
|
|
|
static const struct bt_mesh_model root_models[] = {
|
|
BT_MESH_MODEL_CFG_SRV,
|
|
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
|
|
BT_MESH_MODEL_HEALTH_CLI(&health_cli),
|
|
};
|
|
|
|
static const struct bt_mesh_elem elements[] = {
|
|
BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
|
|
};
|
|
|
|
static const struct bt_mesh_comp mesh_comp = {
|
|
.cid = BT_COMP_ID_LF,
|
|
.elem = elements,
|
|
.elem_count = ARRAY_SIZE(elements),
|
|
};
|
|
|
|
static void setup_cdb(void)
|
|
{
|
|
struct bt_mesh_cdb_app_key *key;
|
|
uint8_t app_key[16];
|
|
int err;
|
|
|
|
key = bt_mesh_cdb_app_key_alloc(net_idx, app_idx);
|
|
if (key == NULL) {
|
|
printk("Failed to allocate app-key 0x%04x\n", app_idx);
|
|
return;
|
|
}
|
|
|
|
bt_rand(app_key, 16);
|
|
|
|
err = bt_mesh_cdb_app_key_import(key, 0, app_key);
|
|
if (err) {
|
|
printk("Failed to import appkey into cdb. Err:%d\n", err);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
bt_mesh_cdb_app_key_store(key);
|
|
}
|
|
}
|
|
|
|
static void configure_self(struct bt_mesh_cdb_node *self)
|
|
{
|
|
struct bt_mesh_cdb_app_key *key;
|
|
uint8_t app_key[16];
|
|
uint8_t status = 0;
|
|
int err;
|
|
|
|
printk("Configuring self...\n");
|
|
|
|
key = bt_mesh_cdb_app_key_get(app_idx);
|
|
if (key == NULL) {
|
|
printk("No app-key 0x%04x\n", app_idx);
|
|
return;
|
|
}
|
|
|
|
err = bt_mesh_cdb_app_key_export(key, 0, app_key);
|
|
if (err) {
|
|
printk("Failed to export appkey from cdb. Err:%d\n", err);
|
|
return;
|
|
}
|
|
|
|
/* Add Application Key */
|
|
err = bt_mesh_cfg_cli_app_key_add(self->net_idx, self->addr, self->net_idx, app_idx,
|
|
app_key, &status);
|
|
if (err || status) {
|
|
printk("Failed to add app-key (err %d, status %d)\n", err,
|
|
status);
|
|
return;
|
|
}
|
|
|
|
err = bt_mesh_cfg_cli_mod_app_bind(self->net_idx, self->addr, self->addr, app_idx,
|
|
BT_MESH_MODEL_ID_HEALTH_CLI, &status);
|
|
if (err || status) {
|
|
printk("Failed to bind app-key (err %d, status %d)\n", err,
|
|
status);
|
|
return;
|
|
}
|
|
|
|
atomic_set_bit(self->flags, BT_MESH_CDB_NODE_CONFIGURED);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
bt_mesh_cdb_node_store(self);
|
|
}
|
|
|
|
printk("Configuration complete\n");
|
|
}
|
|
|
|
static void configure_node(struct bt_mesh_cdb_node *node)
|
|
{
|
|
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_RX_SDU_MAX);
|
|
struct bt_mesh_comp_p0_elem elem;
|
|
struct bt_mesh_cdb_app_key *key;
|
|
uint8_t app_key[16];
|
|
struct bt_mesh_comp_p0 comp;
|
|
uint8_t status;
|
|
int err, elem_addr;
|
|
|
|
printk("Configuring node 0x%04x...\n", node->addr);
|
|
|
|
key = bt_mesh_cdb_app_key_get(app_idx);
|
|
if (key == NULL) {
|
|
printk("No app-key 0x%04x\n", app_idx);
|
|
return;
|
|
}
|
|
|
|
err = bt_mesh_cdb_app_key_export(key, 0, app_key);
|
|
if (err) {
|
|
printk("Failed to export appkey from cdb. Err:%d\n", err);
|
|
return;
|
|
}
|
|
|
|
/* Add Application Key */
|
|
err = bt_mesh_cfg_cli_app_key_add(net_idx, node->addr, net_idx, app_idx, app_key, &status);
|
|
if (err || status) {
|
|
printk("Failed to add app-key (err %d status %d)\n", err, status);
|
|
return;
|
|
}
|
|
|
|
/* Get the node's composition data and bind all models to the appkey */
|
|
err = bt_mesh_cfg_cli_comp_data_get(net_idx, node->addr, 0, &status, &buf);
|
|
if (err || status) {
|
|
printk("Failed to get Composition data (err %d, status: %d)\n",
|
|
err, status);
|
|
return;
|
|
}
|
|
|
|
err = bt_mesh_comp_p0_get(&comp, &buf);
|
|
if (err) {
|
|
printk("Unable to parse composition data (err: %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
elem_addr = node->addr;
|
|
while (bt_mesh_comp_p0_elem_pull(&comp, &elem)) {
|
|
printk("Element @ 0x%04x: %u + %u models\n", elem_addr,
|
|
elem.nsig, elem.nvnd);
|
|
for (int i = 0; i < elem.nsig; i++) {
|
|
uint16_t id = bt_mesh_comp_p0_elem_mod(&elem, i);
|
|
|
|
if (id == BT_MESH_MODEL_ID_CFG_CLI ||
|
|
id == BT_MESH_MODEL_ID_CFG_SRV) {
|
|
continue;
|
|
}
|
|
printk("Binding AppKey to model 0x%03x:%04x\n",
|
|
elem_addr, id);
|
|
|
|
err = bt_mesh_cfg_cli_mod_app_bind(net_idx, node->addr, elem_addr, app_idx,
|
|
id, &status);
|
|
if (err || status) {
|
|
printk("Failed (err: %d, status: %d)\n", err,
|
|
status);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < elem.nvnd; i++) {
|
|
struct bt_mesh_mod_id_vnd id =
|
|
bt_mesh_comp_p0_elem_mod_vnd(&elem, i);
|
|
|
|
printk("Binding AppKey to model 0x%03x:%04x:%04x\n",
|
|
elem_addr, id.company, id.id);
|
|
|
|
err = bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, node->addr, elem_addr,
|
|
app_idx, id.id, id.company, &status);
|
|
if (err || status) {
|
|
printk("Failed (err: %d, status: %d)\n", err,
|
|
status);
|
|
}
|
|
}
|
|
|
|
elem_addr++;
|
|
}
|
|
|
|
atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
bt_mesh_cdb_node_store(node);
|
|
}
|
|
|
|
printk("Configuration complete\n");
|
|
}
|
|
|
|
static void unprovisioned_beacon(uint8_t uuid[16],
|
|
bt_mesh_prov_oob_info_t oob_info,
|
|
uint32_t *uri_hash)
|
|
{
|
|
memcpy(node_uuid, uuid, 16);
|
|
k_sem_give(&sem_unprov_beacon);
|
|
}
|
|
|
|
static void node_added(uint16_t idx, uint8_t uuid[16], uint16_t addr, uint8_t num_elem)
|
|
{
|
|
node_addr = addr;
|
|
k_sem_give(&sem_node_added);
|
|
}
|
|
|
|
static const struct bt_mesh_prov prov = {
|
|
.uuid = dev_uuid,
|
|
.unprovisioned_beacon = unprovisioned_beacon,
|
|
.node_added = node_added,
|
|
};
|
|
|
|
static int bt_ready(void)
|
|
{
|
|
uint8_t net_key[16], dev_key[16];
|
|
int err;
|
|
|
|
err = bt_mesh_init(&prov, &mesh_comp);
|
|
if (err) {
|
|
printk("Initializing mesh failed (err %d)\n", err);
|
|
return err;
|
|
}
|
|
|
|
printk("Mesh initialized\n");
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
printk("Loading stored settings\n");
|
|
settings_load();
|
|
}
|
|
|
|
bt_rand(net_key, 16);
|
|
|
|
err = bt_mesh_cdb_create(net_key);
|
|
if (err == -EALREADY) {
|
|
printk("Using stored CDB\n");
|
|
} else if (err) {
|
|
printk("Failed to create CDB (err %d)\n", err);
|
|
return err;
|
|
} else {
|
|
printk("Created CDB\n");
|
|
setup_cdb();
|
|
}
|
|
|
|
bt_rand(dev_key, 16);
|
|
|
|
err = bt_mesh_provision(net_key, BT_MESH_NET_PRIMARY, 0, 0, self_addr,
|
|
dev_key);
|
|
if (err == -EALREADY) {
|
|
printk("Using stored settings\n");
|
|
} else if (err) {
|
|
printk("Provisioning failed (err %d)\n", err);
|
|
return err;
|
|
} else {
|
|
printk("Provisioning completed\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t check_unconfigured(struct bt_mesh_cdb_node *node, void *data)
|
|
{
|
|
if (!atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) {
|
|
if (node->addr == self_addr) {
|
|
configure_self(node);
|
|
} else {
|
|
configure_node(node);
|
|
}
|
|
}
|
|
|
|
return BT_MESH_CDB_ITER_CONTINUE;
|
|
}
|
|
|
|
#ifdef CONFIG_MESH_PROVISIONER_USE_SW0
|
|
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
|
|
static struct gpio_callback button_cb_data;
|
|
|
|
static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
|
{
|
|
k_sem_give(&sem_button_pressed);
|
|
}
|
|
|
|
static void button_init(void)
|
|
{
|
|
int ret;
|
|
|
|
if (!gpio_is_ready_dt(&button)) {
|
|
printk("Error: button device %s is not ready\n", button.port->name);
|
|
return;
|
|
}
|
|
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
|
|
if (ret != 0) {
|
|
printk("Error %d: failed to configure %s pin %d\n", ret, button.port->name,
|
|
button.pin);
|
|
return;
|
|
}
|
|
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
|
|
if (ret != 0) {
|
|
printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
|
|
button.port->name, button.pin);
|
|
return;
|
|
}
|
|
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
|
|
gpio_add_callback(button.port, &button_cb_data);
|
|
}
|
|
#endif
|
|
|
|
int main(void)
|
|
{
|
|
char uuid_hex_str[32 + 1];
|
|
int err;
|
|
|
|
printk("Initializing...\n");
|
|
|
|
/* Initialize the Bluetooth Subsystem */
|
|
err = bt_enable(NULL);
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
return 0;
|
|
}
|
|
|
|
printk("Bluetooth initialized\n");
|
|
bt_ready();
|
|
|
|
#ifdef CONFIG_MESH_PROVISIONER_USE_SW0
|
|
button_init();
|
|
#endif
|
|
|
|
while (1) {
|
|
k_sem_reset(&sem_unprov_beacon);
|
|
k_sem_reset(&sem_node_added);
|
|
bt_mesh_cdb_node_foreach(check_unconfigured, NULL);
|
|
|
|
printk("Waiting for unprovisioned beacon...\n");
|
|
err = k_sem_take(&sem_unprov_beacon, K_SECONDS(10));
|
|
if (err == -EAGAIN) {
|
|
continue;
|
|
}
|
|
|
|
bin2hex(node_uuid, 16, uuid_hex_str, sizeof(uuid_hex_str));
|
|
|
|
#ifdef CONFIG_MESH_PROVISIONER_USE_SW0
|
|
k_sem_reset(&sem_button_pressed);
|
|
printk("Device %s detected, press button 1 to provision.\n", uuid_hex_str);
|
|
err = k_sem_take(&sem_button_pressed, K_SECONDS(30));
|
|
if (err == -EAGAIN) {
|
|
printk("Timed out, button 1 wasn't pressed in time.\n");
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
printk("Provisioning %s\n", uuid_hex_str);
|
|
err = bt_mesh_provision_adv(node_uuid, net_idx, 0, 0);
|
|
if (err < 0) {
|
|
printk("Provisioning failed (err %d)\n", err);
|
|
continue;
|
|
}
|
|
|
|
printk("Waiting for node to be added...\n");
|
|
err = k_sem_take(&sem_node_added, K_SECONDS(10));
|
|
if (err == -EAGAIN) {
|
|
printk("Timeout waiting for node to be added\n");
|
|
continue;
|
|
}
|
|
|
|
printk("Added node 0x%04x\n", node_addr);
|
|
}
|
|
return 0;
|
|
}
|