Autoprovisioning and autoconfiguring of nRF52840

Hello,

After a lot of efforts I managed to autoprovision and autoconfigure my nrf52840 board (provisioning and configuring inside the software and not through external app).

In particular, I have configured a switch and a led like a client and his server in an element of my node.

All is OK: when I toggle the switch the led goes on/off

But when I try to configure another couple of led/switch I fail.

The file that I use for provisioning is this:

#include "provision.h"
#include <zephyr/logging/log.h>

LOG_MODULE_DECLARE(main);

static const uint8_t net_key[16] = {
	0x6f, 0xea, 0x5b, 0x86, 0x38, 0x45, 0x26, 0xb9,
	0x0e, 0x7b, 0x73, 0x0c, 0x69, 0x94, 0x75, 0x0a,
};

static const uint8_t dev_key[16] = {
	0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01,
	0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01,
};

static const uint8_t first_app_key[16] = {
    // First AppKey
	0x8c, 0x32, 0x9f, 0x41, 0xd1, 0xd2, 0xc2, 0x9f,
	0x5a, 0xb7, 0x09, 0x0c, 0x0b, 0x31, 0x4d, 0x1d,
};

static const uint8_t second_app_key[16] = {
    0x88, 0xed, 0x35, 0xc4, 0x95, 0x01, 0xaf, 0x8a,
	0xce, 0x47, 0xd3, 0x11, 0xbf, 0x77, 0xb4, 0xc1,
};

static const uint8_t third_app_key[16] = {
	0x6a, 0x1a, 0xf1, 0xdb, 0xe8, 0x38, 0xdc, 0xf5,
	0x5c, 0x83, 0xa5, 0x33, 0x35, 0xd2, 0xe9, 0xde,
};

static uint16_t net_idx = 0;
static uint32_t iv_index = 0;
static uint8_t flags;
static bool needs_configuration = false;

uint16_t addr = 0x8000;

void configure()
{
	int res;
	uint8_t status = 0;

	if (needs_configuration)
	{
		if (addr == 0x8000)
		{
			LOG_ERR("Device hasn't been provisioned yet.");
			return;
		}

		LOG_INF("Configuring addr %d...", addr);

		res = bt_mesh_cfg_cli_net_key_add(net_idx, addr, net_idx, net_key, NULL);
		LOG_INF("NetKey %d: res %d, status = %d", net_idx, res, status);

		res = bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, 0, first_app_key, NULL);
		LOG_INF("AppKey 0: res %d, status = %d", res, status);

		res = bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, 1, second_app_key, NULL);
		LOG_INF("AppKey 1: res %d, status = %d", res, status);

		res = bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, 2, third_app_key, NULL);
		LOG_INF("AppKey 2: res %d, status = %d", res, status);

		LOG_INF("Configuration completed");
	}
}

void provision()
{

	int err;
	volatile uint32_t *node_addr  = (volatile uint32_t *) 0x10001080; // address node in UICR
	addr = *node_addr;
	/*err = sys_csrand_get(&addr, 2);
	if (err != 0)
	{
		LOG_ERR("Could not get a random address");
		return;
	}
	else
	{
		addr %= 0x7fff;
	}*/
	
	err = bt_mesh_provision(net_key, net_idx, flags, iv_index, addr, dev_key);
	if (err == -EALREADY)
	{
		LOG_INF("Using stored settings");
	}
	else if (err)
	{
		LOG_ERR("Provisioning failed (err %d)", err);
		return;
	}
	else
	{
		LOG_INF("Provisioning completed");
		// configure(addr);
	}
	needs_configuration = true;
}

Instead the file for configuring switch/led (and also chat in my example) is this:

#include "model_handler.h"
#include <zephyr/logging/log.h>
#include "reset_node.h"

#define BUTTON_IDX 0
#define LED_IDX 0
LOG_MODULE_REGISTER(model_handler, CONFIG_BT_MESH_MODEL_HANDLER_LOG_LEVEL);

/* HEALTH SERVER INIT */
/* Model Pub must be initialized here because the macro is defined as static, therefore it can't be externally defined and included here. */
BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
/* Model Health srv and srv callbacks must be initialized in the caller file due to a conflict with health_srv.c */
static const struct bt_mesh_health_srv_cb health_srv_cb = {
	.attn_on = attention_on,
	.attn_off = attention_off,
};
static struct bt_mesh_health_srv health_srv = {
	.cb = &health_srv_cb,
};

/* CONFIGURATION CLIENT INIT */
const struct bt_mesh_cfg_cli_cb cfg_cli_cb = {};
struct bt_mesh_cfg_cli cli_data = {
	.cb = &cfg_cli_cb};

/* ELEMENTS */
static struct bt_mesh_elem elements[] = {
	BT_MESH_ELEM(1,
				 BT_MESH_MODEL_LIST(
					 BT_MESH_MODEL_CFG_SRV,
					 BT_MESH_MODEL_CFG_CLI(&cli_data),
					 BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
					 BT_MESH_MODEL_DFD_SRV(&dfd_srv),
					 BT_MESH_MODEL_ONOFF_CLI(&buttons[BUTTON_IDX].client),
					 BT_MESH_MODEL_ONOFF_SRV(&led_ctx[LED_IDX].srv)),
				 BT_MESH_MODEL_LIST(
					 BT_MESH_MODEL_CHAT_CLI(&chat))),
	BT_MESH_ELEM(2,
				 BT_MESH_MODEL_LIST(
					 BT_MESH_MODEL_DFU_SRV(&dfu_srv)),
				 BT_MESH_MODEL_NONE),
};

/* COMPOSITION */
static const struct bt_mesh_comp comp = {
	.cid = CONFIG_BT_COMPANY_ID,
	.elem = elements,
	.elem_count = ARRAY_SIZE(elements),
};

const struct bt_mesh_comp *model_handler_init(void)
{
	dk_button_handler_add(&button_handler);
	dk_button_handler_add(&reset_handler);

	// health_k_work_init_delayable();
#ifdef CONFIG_MODEL_CHAT_SHELL
	chat_k_work_init_delayable();
#endif
	return &comp;
}

/**********************************************************************************/
/****************************** App Key Callbacks *********************************/
/**********************************************************************************/
#include <../subsys/bluetooth/mesh/mesh.h>

void on_app_key_added(uint16_t app_idx, uint16_t net_idx)
{
	int res;
	uint8_t status = 0;
	const struct bt_mesh_elem *elem;
	uint16_t addr;
	uint16_t elem_addr;

	switch (app_idx)
	{
	// Chat Vendor Model
	case (0):
	{
		elem = bt_mesh_model_elem(chat.model);
		addr = elem->rt->addr - elem->loc + 1;
		elem_addr = addr + elem->loc - 1;

		uint16_t sub_addr = 0xc000;
		struct bt_mesh_cfg_cli_mod_pub pub = {
			.addr = sub_addr,
			.app_idx = app_idx,
			.ttl = CONFIG_BT_MESH_DEFAULT_TTL,
			.transmit = BT_MESH_TRANSMIT(0, 10),
		};

		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, elem_addr, app_idx, BT_MESH_CHAT_CLI_VENDOR_MODEL_ID, BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);
		status = 0;
		res = bt_mesh_cfg_cli_mod_pub_set_vnd(net_idx, addr, elem_addr, BT_MESH_CHAT_CLI_VENDOR_MODEL_ID, BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID, &pub, NULL);
		LOG_INF("pub res = %d, status = %d", res, status);
		status = 0;
		res = bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, elem_addr, sub_addr, BT_MESH_CHAT_CLI_VENDOR_MODEL_ID, BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID, NULL);
		LOG_INF("sub res = %d, status = %d", res, status);

		break;
	}
	// Light switch and Light server models
	case (1):
	{
		elem = bt_mesh_model_elem(led_ctx[LED_IDX].srv.model);
		addr = elem->rt->addr - elem->loc + 1;
		elem_addr = addr + elem->loc - 1;
		uint16_t sub_addr = 0xc001;

		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL);
		LOG_INF("bind res = %d.", res);
		res = bt_mesh_cfg_cli_mod_sub_add(net_idx, addr, elem_addr, sub_addr, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL);
		LOG_INF("sub res = %d.", res);

		elem = bt_mesh_model_elem(buttons[BUTTON_IDX].client.model);
		addr = elem->rt->addr - elem->loc + 1;
		elem_addr = addr + elem->loc - 1;
		struct bt_mesh_cfg_cli_mod_pub pub = {
			.addr = sub_addr,
			.app_idx = app_idx,
			.ttl = CONFIG_BT_MESH_DEFAULT_TTL,
			.transmit = BT_MESH_TRANSMIT(1, 10),
		};

		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_GEN_ONOFF_CLI, NULL);
		LOG_INF("bind res = %d.", res);
		res = bt_mesh_cfg_cli_mod_pub_set(net_idx, addr, elem_addr, BT_MESH_MODEL_ID_GEN_ONOFF_CLI, &pub, NULL);
		LOG_INF("pub res = %d.", res);
		break;
	}
	// DFU and DFD
	case (2):
	{

		elem = bt_mesh_model_elem(dfd_srv.mod);
		addr = elem->rt->addr - elem->loc + 1;
		elem_addr = addr + elem->loc - 1;
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_BLOB_CLI, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_DFU_CLI, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_BLOB_SRV, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_DFD_SRV, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);

		elem = bt_mesh_model_elem(dfu_srv.mod);
		addr = elem->rt->addr - elem->loc + 1;
		elem_addr = addr + elem->loc - 1;
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_BLOB_SRV, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);
		LOG_INF("addr = %d - elem_addr = %d - net_idx = %d - app_idx = %d", addr, elem_addr, net_idx, app_idx);
		res = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_DFU_SRV, NULL);
		LOG_INF("bind res = %d, status = %d", res, status);

		break;
	}

	default:
	{
		LOG_ERR("Unforseen AppKey idx %d", app_idx);
	}
	}
}

void on_app_key_event(uint16_t app_idx, uint16_t net_idx, enum bt_mesh_key_evt evt)
{
	switch (evt)
	{
	case BT_MESH_KEY_ADDED:
	{

		on_app_key_added(app_idx, net_idx);

		break;
	}
	case BT_MESH_KEY_DELETED:
	{

		break;
	}
	case BT_MESH_KEY_UPDATED:
	{

		break;
	}
	case BT_MESH_KEY_SWAPPED:
	{

		break;
	}
	case BT_MESH_KEY_REVOKED:
	{

		break;
	}
	}

	// LOG_INF("AppKey event: AppIDX=%d - NetIDX=%d - evt=%d", app_idx, net_idx, evt);
}

BT_MESH_APP_KEY_CB_DEFINE(on_app_key_event);

What have I to do for adding another couple of switch/led (client/server), so that when I toggle the second switch the second led goes on/off?

I'm ready to add other information if these can permit you to resolve.

Thank you in advance for your replay

  • Hello,

    I don't know how to force these bindings without a provisioner, as it is not the intended usecase. 

    Perhaps you can help me get up to speed. Can you zip and send two applications that are working, binding the switch to the led. And then zip and upload two more applications where you attempt adding the other pair?

    Don't just upload .c files. I would need the entire project, so that I can unzip it and build it. Also, let me know what NCS version you are using.

    Best regards,

    Edvin

  • Hello,

    I luckily solved all the issues.

    First of all, I had memory problem that I solved reducing the dimension of my firmware.

    Then, as "Unable to allocate loopback" error was concerned, I simply found the right config parameter to change: I increased the value of CONFIG_BT_MESH_LOOPBACK_BUFS in prj.conf and the error vanished.

    Ticket closed, I suppose.

    Thank you and best regards

Related