Automatic provisioning for fast test fails.

Dear fellow developers,

We're developing a test network for our bluetooth mesh devices where the final step is automatic provisioning for fast configuration (of course, limited to testing environments). In order to do so, we're studying the bt mesh demo sample. Our project is perfectly similar to it as it contains a configuration client that is supposed to configure the node once provisioned so we can seamlessly take the sample as example for this ticket.

Problem is, using bt_mesh_provision to provision the node using hardcoded keys raises some errors.

  1. MPU Fault when using CONFIG_SETTINGS=y. As shown in the image below, building and running the sample for DK 52840 produces a MPU fault that can be bypassed by setting CONFIG_SETTINGS=n in prj.cfg. (We're not experiencing this error in the project so I'll skip it)


  2. After setting CONFIG_SETTINGS to n, the MPU fault disappears. Apparently the node manages to provision itself and to call the configure() function as the provisioning phase is over. This configure function uses the configuration client's functions to add a NetKey to the node, add a AppKey to the node, bind the AppKey to a health model and to a vendor model. Unfortunately, this does not work correctly as each of the functions return a -116 (ETIMEDOUT) error (as shown below).

I've been working with the 2.5.1 toolchain up until yesterday but the pictures were taken using the latest one, 2.5.2.

The only changes I've made in the project are:

  1. Addition of printk functions as shown in the pic below to showcase the values of ret and status on different operations (as I'm having troubles debugging).
  2. Modifications of prj.conf file so enable logging and disable the settings subsystem.

A trace of the error (in one of the 4 functions) is:

bt_mesh_cfg_cli_app_key_add
'-> bt_mesh_msg_ackd_send
'-> bt_mesh_msg_ack_ctx_wait

How would you advise me to solve this problem?

Thanks for your kind attention, I hope to hear back from you guys soon.

Ale

Parents
  • Hi Alessandro, 

    I was checking with the mesh team and what they replied was that we shouldn't call a blocking API (that you wait for the response from the server) in a system work queue context ( bt_ready() callback). 
    So instead of calling configure() inside bt_ready() you can call it inside main() for example. You will need to use a semaphore to call that function when bt_ready() is called. Or you can use a work thread and start the thread inside bt_ready() instead. 

  • Dear Hung Bui,

    Sorry for the late response: i took my time to test your proposed solutions. I must say, it does indeed make sense to use a semaphore and I did.

    Semaphore initialized to 0, max value = 1. In the main function, before configuration, I put a k_sem_take while the corresponding k_sem_give is at the very end of the bt_ready function.

    Indeed, it does help as I don't experience the ETIMEDOUT error but I'm now facing new ones. If you don't mind, I'll ask you about these as well, otherwise I'll open another ticket:

    The add_net_key and add_app_key functions are now completed successfully as they return 0 with a status set to 0 but the bind, sub, pub operations do not.

    The architecture in the main project uses a callback in app_keys.h to statically bind application keys to different models of the mesh node as soon as the app_key is added to it.

    Setting the status parameter to NULL produces a warning and an error (bind, sub, pub result = 0 or = -12, ENOMEM, randomly):

    • <wrn> bt_mesh_net: Unable to allocate loopback
    • <err> bt_mesh_cfg_srv: Unable to send App Key Status response

    On the other hand, passing a non-NULL status parameter produces a warning (bind, sub, pub result = -16, EBUSY):

    •  <wrn> bt_mesh_msg: Another synchronous operation pending.

    It's worth noting that this solution does work like a charm using the nrf Mesh android App for provisioning. Somehow, it all breaks down when the provisioning is done via bt_mesh_provision.

    Best regards,

    Ale

  • Hi Ale, 

    Could you clarify if you have the issue with the mesh_demo sample ? 
    I don't see any error for all four of them when I tested with the code you provided. 

  • Nope, I don't. That's probably because of a weird interaction between the app_keys callbacks that are not present in the sample. You could say "well then just don't use them and code the necessary functions in the configure function". This could be a solution to be fair, especially using the pre-processor. But I'm striving to find a solution that is compatible with the original design that uses app_keys callbacks.

    The following code is a modification of the main.c file in mesh demo sample to reproduce the error. You should be able to make it work just by copying and pasting it in your own project as it is. I've changed a couple things.

    • Introduced semaphore logic as you suggested.
    • The configure function now only adds the application key.
    • Added a app_key callback function that handles just the BT_MESH_KEY_ADDED event. This allows me to set up the node in a lazy way just upon application_key grant. Specifically, the bind, bind_vnd, and sub functions are called in such a callback.

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2017 Intel Corporation
     *
     * 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 "board.h"
    
    #define MOD_LF 0x0000
    
    #define GROUP_ADDR 0xc000
    #define PUBLISHER_ADDR  0x000f
    
    #define OP_VENDOR_BUTTON BT_MESH_MODEL_OP_3(0x00, BT_COMP_ID_LF)
    
    K_SEM_DEFINE(bt_ready_sem, 0, 1);
    
    static const uint8_t net_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t dev_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t app_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint16_t net_idx;
    static const uint16_t app_idx;
    static const uint32_t iv_index;
    static uint8_t flags;
    static uint16_t addr = NODE_ADDR;
    
    static void heartbeat(const struct bt_mesh_hb_sub *sub, uint8_t hops,
    		      uint16_t feat)
    {
    	board_heartbeat(hops, feat);
    	board_play("100H");
    }
    
    static struct bt_mesh_cfg_cli cfg_cli = {
    };
    
    static void attention_on(struct bt_mesh_model *model)
    {
    	printk("attention_on()\n");
    	board_attention(true);
    	board_play("100H100C100H100C100H100C");
    }
    
    static void attention_off(struct bt_mesh_model *model)
    {
    	printk("attention_off()\n");
    	board_attention(false);
    }
    
    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,
    };
    
    BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
    
    static struct bt_mesh_model root_models[] = {
    	BT_MESH_MODEL_CFG_SRV,
    	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    	BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    };
    
    static int vnd_button_pressed(struct bt_mesh_model *model,
    			       struct bt_mesh_msg_ctx *ctx,
    			       struct net_buf_simple *buf)
    {
    	printk("src 0x%04x\n", ctx->addr);
    
    	if (ctx->addr == bt_mesh_model_elem(model)->addr) {
    		return 0;
    	}
    
    	board_other_dev_pressed(ctx->addr);
    	board_play("100G200 100G");
    
    	return 0;
    }
    
    static const struct bt_mesh_model_op vnd_ops[] = {
    	{ OP_VENDOR_BUTTON, BT_MESH_LEN_EXACT(0), vnd_button_pressed },
    	BT_MESH_MODEL_OP_END,
    };
    
    static struct bt_mesh_model vnd_models[] = {
    	BT_MESH_MODEL_VND(BT_COMP_ID_LF, MOD_LF, vnd_ops, NULL, NULL),
    };
    
    static struct bt_mesh_elem elements[] = {
    	BT_MESH_ELEM(0, root_models, vnd_models),
    };
    
    static const struct bt_mesh_comp comp = {
    	.cid = BT_COMP_ID_LF,
    	.elem = elements,
    	.elem_count = ARRAY_SIZE(elements),
    };
    
    static void configure(void)
    {
    	printk("Configuring...\n");
    	uint8_t status = 0;
    
    	/* Add Application Key */
    	printk("add      = %d\n", bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, &status));
    
    #if NODE_ADDR == PUBLISHER_ADDR
    	{
    		struct bt_mesh_cfg_cli_hb_pub pub = {
    			.dst = GROUP_ADDR,
    			.count = 0xff,
    			.period = 0x05,
    			.ttl = 0x07,
    			.feat = 0,
    			.net_idx = net_idx,
    		};
    
    		bt_mesh_cfg_cli_hb_pub_set(net_idx, addr, &pub, NULL);
    		printk("Publishing heartbeat messages\n");
    	}
    #endif
    	printk("Configuration complete\n");
    
    	board_play("100C100D100E100F100G100A100H");
    }
    
    static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };
    
    static const struct bt_mesh_prov prov = {
    	.uuid = dev_uuid,
    };
    
    BT_MESH_HB_CB_DEFINE(hb_cb) = {
    	.recv = heartbeat,
    };
    
    static void bt_ready(int err)
    {
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	err = bt_mesh_init(&prov, &comp);
    	if (err) {
    		printk("Initializing mesh failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Mesh initialized\n");
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		printk("Loading stored settings\n");
    		settings_load();
    	}
    
    	err = bt_mesh_provision(net_key, net_idx, flags, iv_index, addr,
    				dev_key);
    	if (err == -EALREADY) {
    		printk("Using stored settings\n");
    	} else if (err) {
    		printk("Provisioning failed (err %d)\n", err);
    		return;
    	} else {
    		printk("Provisioning completed\n");
    		// configure();
    	}
    
    #if NODE_ADDR != PUBLISHER_ADDR
    	/* Heartbeat subscription is a temporary state (due to there
    	 * not being an "indefinite" value for the period, so it never
    	 * gets stored persistently. Therefore, we always have to configure
    	 * it explicitly.
    	 */
    	{
    		struct bt_mesh_cfg_cli_hb_sub sub = {
    			.src = PUBLISHER_ADDR,
    			.dst = GROUP_ADDR,
    			.period = 0x10,
    		};
    
    		bt_mesh_cfg_cli_hb_sub_set(net_idx, addr, &sub, NULL);
    		printk("Subscribing to heartbeat messages\n");
    	}
    #endif
    	k_sem_give(&bt_ready_sem);
    }
    
    static uint16_t target = GROUP_ADDR;
    
    void board_button_1_pressed(void)
    {
    	NET_BUF_SIMPLE_DEFINE(msg, 3 + 4);
    	struct bt_mesh_msg_ctx ctx = {
    		.app_idx = app_idx,
    		.addr = target,
    		.send_ttl = BT_MESH_TTL_DEFAULT,
    	};
    
    	/* Bind to Health model */
    	bt_mesh_model_msg_init(&msg, OP_VENDOR_BUTTON);
    
    	if (bt_mesh_model_send(&vnd_models[0], &ctx, &msg, NULL, NULL)) {
    		printk("Unable to send Vendor Button message\n");
    	}
    
    	printk("Button message sent with OpCode 0x%08x\n", OP_VENDOR_BUTTON);
    }
    
    uint16_t board_set_target(void)
    {
    	switch (target) {
    	case GROUP_ADDR:
    		target = 1U;
    		break;
    	case 9:
    		target = GROUP_ADDR;
    		break;
    	default:
    		target++;
    		break;
    	}
    
    	return target;
    }
    
    static K_SEM_DEFINE(tune_sem, 0, 1);
    static const char *tune_str;
    
    void board_play(const char *str)
    {
    	tune_str = str;
    	k_sem_give(&tune_sem);
    }
    
    
    int main(void)
    {
    	int err;
    
    	printk("Initializing...\n");
    
    	err = board_init(&addr);
    	if (err) {
    		printk("Board initialization failed\n");
    		return 0;
    	}
    
    	printk("Unicast address: 0x%04x\n", addr);
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}		
    	k_sem_take(&bt_ready_sem, K_FOREVER);
    	configure();
    
    	while (1) {
    		k_sem_take(&tune_sem, K_FOREVER);
    		board_play_tune(tune_str);
    	}
    
    	return 0;
    }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void on_app_key_added(uint16_t app_idx, uint16_t net_idx) {
    	uint8_t status = 0;
    
    	switch (app_idx) {
    		case (0): {
    			/* Bind to vendor model */
    			printk("bind vnd = %d\n", bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, MOD_LF, BT_COMP_ID_LF, &status));
    
    			/* Bind to Health model */
    			printk("bind     = %d\n", bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, &status));
    
    			/* Add model subscription */
    			printk("sub      = %d\n", bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, GROUP_ADDR, MOD_LF, BT_COMP_ID_LF, &status));
    
    			break;
    		}
    
    		default: {
    			printk("Unforseen AppKey idx %d", app_idx);
    		}
    
    	}
    }
    
    #include <../subsys/bluetooth/mesh/mesh.h>
    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;
    		}
    	}
    }
    
    BT_MESH_APP_KEY_CB_DEFINE(on_app_key_event);
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    The output of the code is

    bind vnd = -16
    bind       = -16
    sub        = -16
    add        = 0

    Suggesting that the app_key callback functions are called before the operation of adding the application key. I would like to stress out that this does not happen using the nrf Mesh app for android, on the contrary, everything configures itself correctly.

    Before writing his post I also tried to use another semaphore exactly as done for the bt_ready function so to force the execution of the bind_vnd, bind, and sub functions only after the add_app_key function had terminated. Unfortunately this resulted in four -ETIMEDOUT errors as result of the four operations.

    Thanks for your inputs Hung Bui, you're always very helpful.

    Best regards,

    Ale

Reply
  • Nope, I don't. That's probably because of a weird interaction between the app_keys callbacks that are not present in the sample. You could say "well then just don't use them and code the necessary functions in the configure function". This could be a solution to be fair, especially using the pre-processor. But I'm striving to find a solution that is compatible with the original design that uses app_keys callbacks.

    The following code is a modification of the main.c file in mesh demo sample to reproduce the error. You should be able to make it work just by copying and pasting it in your own project as it is. I've changed a couple things.

    • Introduced semaphore logic as you suggested.
    • The configure function now only adds the application key.
    • Added a app_key callback function that handles just the BT_MESH_KEY_ADDED event. This allows me to set up the node in a lazy way just upon application_key grant. Specifically, the bind, bind_vnd, and sub functions are called in such a callback.

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2017 Intel Corporation
     *
     * 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 "board.h"
    
    #define MOD_LF 0x0000
    
    #define GROUP_ADDR 0xc000
    #define PUBLISHER_ADDR  0x000f
    
    #define OP_VENDOR_BUTTON BT_MESH_MODEL_OP_3(0x00, BT_COMP_ID_LF)
    
    K_SEM_DEFINE(bt_ready_sem, 0, 1);
    
    static const uint8_t net_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t dev_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t app_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint16_t net_idx;
    static const uint16_t app_idx;
    static const uint32_t iv_index;
    static uint8_t flags;
    static uint16_t addr = NODE_ADDR;
    
    static void heartbeat(const struct bt_mesh_hb_sub *sub, uint8_t hops,
    		      uint16_t feat)
    {
    	board_heartbeat(hops, feat);
    	board_play("100H");
    }
    
    static struct bt_mesh_cfg_cli cfg_cli = {
    };
    
    static void attention_on(struct bt_mesh_model *model)
    {
    	printk("attention_on()\n");
    	board_attention(true);
    	board_play("100H100C100H100C100H100C");
    }
    
    static void attention_off(struct bt_mesh_model *model)
    {
    	printk("attention_off()\n");
    	board_attention(false);
    }
    
    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,
    };
    
    BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
    
    static struct bt_mesh_model root_models[] = {
    	BT_MESH_MODEL_CFG_SRV,
    	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    	BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    };
    
    static int vnd_button_pressed(struct bt_mesh_model *model,
    			       struct bt_mesh_msg_ctx *ctx,
    			       struct net_buf_simple *buf)
    {
    	printk("src 0x%04x\n", ctx->addr);
    
    	if (ctx->addr == bt_mesh_model_elem(model)->addr) {
    		return 0;
    	}
    
    	board_other_dev_pressed(ctx->addr);
    	board_play("100G200 100G");
    
    	return 0;
    }
    
    static const struct bt_mesh_model_op vnd_ops[] = {
    	{ OP_VENDOR_BUTTON, BT_MESH_LEN_EXACT(0), vnd_button_pressed },
    	BT_MESH_MODEL_OP_END,
    };
    
    static struct bt_mesh_model vnd_models[] = {
    	BT_MESH_MODEL_VND(BT_COMP_ID_LF, MOD_LF, vnd_ops, NULL, NULL),
    };
    
    static struct bt_mesh_elem elements[] = {
    	BT_MESH_ELEM(0, root_models, vnd_models),
    };
    
    static const struct bt_mesh_comp comp = {
    	.cid = BT_COMP_ID_LF,
    	.elem = elements,
    	.elem_count = ARRAY_SIZE(elements),
    };
    
    static void configure(void)
    {
    	printk("Configuring...\n");
    	uint8_t status = 0;
    
    	/* Add Application Key */
    	printk("add      = %d\n", bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, &status));
    
    #if NODE_ADDR == PUBLISHER_ADDR
    	{
    		struct bt_mesh_cfg_cli_hb_pub pub = {
    			.dst = GROUP_ADDR,
    			.count = 0xff,
    			.period = 0x05,
    			.ttl = 0x07,
    			.feat = 0,
    			.net_idx = net_idx,
    		};
    
    		bt_mesh_cfg_cli_hb_pub_set(net_idx, addr, &pub, NULL);
    		printk("Publishing heartbeat messages\n");
    	}
    #endif
    	printk("Configuration complete\n");
    
    	board_play("100C100D100E100F100G100A100H");
    }
    
    static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };
    
    static const struct bt_mesh_prov prov = {
    	.uuid = dev_uuid,
    };
    
    BT_MESH_HB_CB_DEFINE(hb_cb) = {
    	.recv = heartbeat,
    };
    
    static void bt_ready(int err)
    {
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	err = bt_mesh_init(&prov, &comp);
    	if (err) {
    		printk("Initializing mesh failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Mesh initialized\n");
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		printk("Loading stored settings\n");
    		settings_load();
    	}
    
    	err = bt_mesh_provision(net_key, net_idx, flags, iv_index, addr,
    				dev_key);
    	if (err == -EALREADY) {
    		printk("Using stored settings\n");
    	} else if (err) {
    		printk("Provisioning failed (err %d)\n", err);
    		return;
    	} else {
    		printk("Provisioning completed\n");
    		// configure();
    	}
    
    #if NODE_ADDR != PUBLISHER_ADDR
    	/* Heartbeat subscription is a temporary state (due to there
    	 * not being an "indefinite" value for the period, so it never
    	 * gets stored persistently. Therefore, we always have to configure
    	 * it explicitly.
    	 */
    	{
    		struct bt_mesh_cfg_cli_hb_sub sub = {
    			.src = PUBLISHER_ADDR,
    			.dst = GROUP_ADDR,
    			.period = 0x10,
    		};
    
    		bt_mesh_cfg_cli_hb_sub_set(net_idx, addr, &sub, NULL);
    		printk("Subscribing to heartbeat messages\n");
    	}
    #endif
    	k_sem_give(&bt_ready_sem);
    }
    
    static uint16_t target = GROUP_ADDR;
    
    void board_button_1_pressed(void)
    {
    	NET_BUF_SIMPLE_DEFINE(msg, 3 + 4);
    	struct bt_mesh_msg_ctx ctx = {
    		.app_idx = app_idx,
    		.addr = target,
    		.send_ttl = BT_MESH_TTL_DEFAULT,
    	};
    
    	/* Bind to Health model */
    	bt_mesh_model_msg_init(&msg, OP_VENDOR_BUTTON);
    
    	if (bt_mesh_model_send(&vnd_models[0], &ctx, &msg, NULL, NULL)) {
    		printk("Unable to send Vendor Button message\n");
    	}
    
    	printk("Button message sent with OpCode 0x%08x\n", OP_VENDOR_BUTTON);
    }
    
    uint16_t board_set_target(void)
    {
    	switch (target) {
    	case GROUP_ADDR:
    		target = 1U;
    		break;
    	case 9:
    		target = GROUP_ADDR;
    		break;
    	default:
    		target++;
    		break;
    	}
    
    	return target;
    }
    
    static K_SEM_DEFINE(tune_sem, 0, 1);
    static const char *tune_str;
    
    void board_play(const char *str)
    {
    	tune_str = str;
    	k_sem_give(&tune_sem);
    }
    
    
    int main(void)
    {
    	int err;
    
    	printk("Initializing...\n");
    
    	err = board_init(&addr);
    	if (err) {
    		printk("Board initialization failed\n");
    		return 0;
    	}
    
    	printk("Unicast address: 0x%04x\n", addr);
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}		
    	k_sem_take(&bt_ready_sem, K_FOREVER);
    	configure();
    
    	while (1) {
    		k_sem_take(&tune_sem, K_FOREVER);
    		board_play_tune(tune_str);
    	}
    
    	return 0;
    }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void on_app_key_added(uint16_t app_idx, uint16_t net_idx) {
    	uint8_t status = 0;
    
    	switch (app_idx) {
    		case (0): {
    			/* Bind to vendor model */
    			printk("bind vnd = %d\n", bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, MOD_LF, BT_COMP_ID_LF, &status));
    
    			/* Bind to Health model */
    			printk("bind     = %d\n", bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, &status));
    
    			/* Add model subscription */
    			printk("sub      = %d\n", bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, GROUP_ADDR, MOD_LF, BT_COMP_ID_LF, &status));
    
    			break;
    		}
    
    		default: {
    			printk("Unforseen AppKey idx %d", app_idx);
    		}
    
    	}
    }
    
    #include <../subsys/bluetooth/mesh/mesh.h>
    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;
    		}
    	}
    }
    
    BT_MESH_APP_KEY_CB_DEFINE(on_app_key_event);
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    The output of the code is

    bind vnd = -16
    bind       = -16
    sub        = -16
    add        = 0

    Suggesting that the app_key callback functions are called before the operation of adding the application key. I would like to stress out that this does not happen using the nrf Mesh app for android, on the contrary, everything configures itself correctly.

    Before writing his post I also tried to use another semaphore exactly as done for the bt_ready function so to force the execution of the bind_vnd, bind, and sub functions only after the add_app_key function had terminated. Unfortunately this resulted in four -ETIMEDOUT errors as result of the four operations.

    Thanks for your inputs Hung Bui, you're always very helpful.

    Best regards,

    Ale

Children
  • Hi Ale, 

    Have you made sure you used the semaphore on the last 3 calls ? If you call them in the app_key_event, then it's the same issue like before.
    This code worked for me: 

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2017 Intel Corporation
     *
     * 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 "board.h"
    
    #define MOD_LF 0x0000
    
    #define GROUP_ADDR 0xc000
    #define PUBLISHER_ADDR  0x000f
    K_SEM_DEFINE(bt_ready_sem, 0, 1);
    #define OP_VENDOR_BUTTON BT_MESH_MODEL_OP_3(0x00, BT_COMP_ID_LF)
    
    static const uint8_t net_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t dev_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint8_t app_key[16] = {
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
    };
    static const uint16_t net_idx;
    static const uint16_t app_idx;
    static const uint32_t iv_index;
    static uint8_t flags;
    static uint16_t addr = NODE_ADDR;
    
    static void heartbeat(const struct bt_mesh_hb_sub *sub, uint8_t hops,
    		      uint16_t feat)
    {
    	board_heartbeat(hops, feat);
    	board_play("100H");
    }
    
    static struct bt_mesh_cfg_cli cfg_cli = {
    };
    
    static void attention_on(struct bt_mesh_model *model)
    {
    	printk("attention_on()\n");
    	board_attention(true);
    	board_play("100H100C100H100C100H100C");
    }
    
    static void attention_off(struct bt_mesh_model *model)
    {
    	printk("attention_off()\n");
    	board_attention(false);
    }
    
    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,
    };
    
    BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
    
    static struct bt_mesh_model root_models[] = {
    	BT_MESH_MODEL_CFG_SRV,
    	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    	BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    };
    
    static int vnd_button_pressed(struct bt_mesh_model *model,
    			       struct bt_mesh_msg_ctx *ctx,
    			       struct net_buf_simple *buf)
    {
    	printk("src 0x%04x\n", ctx->addr);
    
    	if (ctx->addr == bt_mesh_model_elem(model)->addr) {
    		return 0;
    	}
    
    	board_other_dev_pressed(ctx->addr);
    	board_play("100G200 100G");
    
    	return 0;
    }
    
    static const struct bt_mesh_model_op vnd_ops[] = {
    	{ OP_VENDOR_BUTTON, BT_MESH_LEN_EXACT(0), vnd_button_pressed },
    	BT_MESH_MODEL_OP_END,
    };
    
    static struct bt_mesh_model vnd_models[] = {
    	BT_MESH_MODEL_VND(BT_COMP_ID_LF, MOD_LF, vnd_ops, NULL, NULL),
    };
    
    static struct bt_mesh_elem elements[] = {
    	BT_MESH_ELEM(0, root_models, vnd_models),
    };
    
    static const struct bt_mesh_comp comp = {
    	.cid = BT_COMP_ID_LF,
    	.elem = elements,
    	.elem_count = ARRAY_SIZE(elements),
    };
    static void configure_continue(void)
    {
    int status = 0;
     /* Bind to vendor model */
    
     printk("%d\n", bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, MOD_LF, BT_COMP_ID_LF, &status));
    
    
    
     /* Bind to Health model */
    
     printk("%d\n", bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, &status));
    
    
    
     /* Add model subscription */
    
     printk("%d\n", bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, GROUP_ADDR, MOD_LF, BT_COMP_ID_LF, &status));
    
    }
    static void configure(void)
    {
    	printk("Configuring...\n");
    
    	/* Add Application Key */
    	// bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, NULL);
    
    	// /* Bind to vendor model */
    	// bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, MOD_LF, BT_COMP_ID_LF, NULL);
    
    	// /* Bind to Health model */
    	// bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV,
    	// 			     NULL);
    
    	// /* Add model subscription */
    	// bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, GROUP_ADDR, MOD_LF, BT_COMP_ID_LF,
    	// 				NULL);
    int status = 0;
    
    
    
     /* Add Application Key */
    
     printk("%d\n", bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, &status));
    
    
    
    
    // #if NODE_ADDR == PUBLISHER_ADDR
    // 	{
    // 		struct bt_mesh_cfg_cli_hb_pub pub = {
    // 			.dst = GROUP_ADDR,
    // 			.count = 0xff,
    // 			.period = 0x05,
    // 			.ttl = 0x07,
    // 			.feat = 0,
    // 			.net_idx = net_idx,
    // 		};
    
    // 		bt_mesh_cfg_cli_hb_pub_set(net_idx, addr, &pub, NULL);
    // 		printk("Publishing heartbeat messages\n");
    // 	}
    // #endif
    	printk("Configuration complete\n");
    
    	//board_play("100C100D100E100F100G100A100H");
    }
    
    static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };
    
    static const struct bt_mesh_prov prov = {
    	.uuid = dev_uuid,
    };
    
    BT_MESH_HB_CB_DEFINE(hb_cb) = {
    	.recv = heartbeat,
    };
    
    static void bt_ready(int err)
    {
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	err = bt_mesh_init(&prov, &comp);
    	if (err) {
    		printk("Initializing mesh failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Mesh initialized\n");
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		printk("Loading stored settings\n");
    		settings_load();
    	}
    
    	err = bt_mesh_provision(net_key, net_idx, flags, iv_index, addr,
    				dev_key);
    	if (err == -EALREADY) {
    		printk("Using stored settings\n");
    	} else if (err) {
    		printk("Provisioning failed (err %d)\n", err);
    		return;
    	} else {
    		printk("Provisioning completed\n");
    		k_sem_give(&bt_ready_sem);
    	}
    
    #if NODE_ADDR != PUBLISHER_ADDR
    	/* Heartbeat subscription is a temporary state (due to there
    	 * not being an "indefinite" value for the period, so it never
    	 * gets stored persistently. Therefore, we always have to configure
    	 * it explicitly.
    	 */
    	{
    		struct bt_mesh_cfg_cli_hb_sub sub = {
    			.src = PUBLISHER_ADDR,
    			.dst = GROUP_ADDR,
    			.period = 0x10,
    		};
    
    		bt_mesh_cfg_cli_hb_sub_set(net_idx, addr, &sub, NULL);
    		printk("Subscribing to heartbeat messages\n");
    	}
    #endif
    }
    
    static uint16_t target = GROUP_ADDR;
    
    void board_button_1_pressed(void)
    {
    	NET_BUF_SIMPLE_DEFINE(msg, 3 + 4);
    	struct bt_mesh_msg_ctx ctx = {
    		.app_idx = app_idx,
    		.addr = target,
    		.send_ttl = BT_MESH_TTL_DEFAULT,
    	};
    
    	/* Bind to Health model */
    	bt_mesh_model_msg_init(&msg, OP_VENDOR_BUTTON);
    
    	if (bt_mesh_model_send(&vnd_models[0], &ctx, &msg, NULL, NULL)) {
    		printk("Unable to send Vendor Button message\n");
    	}
    
    	printk("Button message sent with OpCode 0x%08x\n", OP_VENDOR_BUTTON);
    }
    
    uint16_t board_set_target(void)
    {
    	switch (target) {
    	case GROUP_ADDR:
    		target = 1U;
    		break;
    	case 9:
    		target = GROUP_ADDR;
    		break;
    	default:
    		target++;
    		break;
    	}
    
    	return target;
    }
    
    static K_SEM_DEFINE(tune_sem, 0, 1);
    static const char *tune_str;
    
    void board_play(const char *str)
    {
    	tune_str = str;
    	k_sem_give(&tune_sem);
    }
    
    int main(void)
    {
    	int err;
    
    	printk("Initializing...\n");
    
    	err = board_init(&addr);
    	if (err) {
    		printk("Board initialization failed\n");
    		return 0;
    	}
    
    	printk("Unicast address: 0x%04x\n", addr);
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    		k_sem_take(&bt_ready_sem, K_FOREVER);
    	configure();
    		k_sem_take(&bt_ready_sem, K_FOREVER);
    			printk("continue \n");
    	configure_continue();
    	while (1) {
    	//	k_sem_take(&tune_sem, K_FOREVER);
    	//	board_play_tune(tune_str);
    	}
    
    	return 0;
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void on_app_key_added(uint16_t app_idx, uint16_t net_idx) {
    	uint8_t status = 0;
    	printk("app key added\n");
    	switch (app_idx) {
    		case (0): {
    			printk("app key 0 \n");
    			k_sem_give(&bt_ready_sem);
    			/* Bind to vendor model */
    			// printk("bind vnd = %d\n", bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, MOD_LF, BT_COMP_ID_LF, &status));
    
    			// /* Bind to Health model */
    			// printk("bind     = %d\n", bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, &status));
    
    			// /* Add model subscription */
    			// printk("sub      = %d\n", bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, GROUP_ADDR, MOD_LF, BT_COMP_ID_LF, &status));
    
    			break;
    		}
    
    		default: {
    			printk("Unforseen AppKey idx %d", app_idx);
    		}
    
    	}
    }
    
    #include <../subsys/bluetooth/mesh/mesh.h>
    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;
    		}
    	}
    }
    
    BT_MESH_APP_KEY_CB_DEFINE(on_app_key_event);

  • Uhm, yeah I can confirm your version does work. I'm going to be honest though, since the logic shown in the sample is spread across different files in the main project, I'm not a big fan of this semaphore solution especially considering that all these modifications are just to ease the testing of the network and won't make it to production :/

    I'm trying to implement a solution using threads or k_work (so far, with no success).

    I'd be curious to understand why does this app_key callback approach work with traditional provisioning using nrf Mesh App but completely breaks when using bt_mesh_provision. I'm definitely missing something...

    Anyway, as always you're very very helpful. I hope they give you a raise ;)

    Best regards,

    Ale

  • Hi Ale, 

    alebldn said:
    I'd be curious to understand why does this app_key callback approach work with traditional provisioning using nrf Mesh App but completely breaks when using bt_mesh_provision.

    I think it's most likely related to that when you do provisioning from external device, there will be no blocking function (the configuration client sending out the command). It's because the blocking part is now on the phone. The device when configured by the phone is only the server, and it will have no problem with the thread context blocking each other.  

    alebldn said:
    Anyway, as always you're very very helpful. I hope they give you a raise ;)

    Thanks for the nice words, I will forward this to my manager :D 

  • Actually, in our project there is a configuration client sending out several blocking commands to the respective configuration server on the same node.

    So far our design works like this:

    1. Node is provisioned via the nrf Mesh app.
    2. Adding up to three app keys through the nrf Mesh app.
    3. According to the app key received, the configuration client on the node binds the key to different models and creates publication and subscription callbacks to ensure the model's correct behavior. This is done by leveraging the app_keys.h callbacks as shown in the example above.

    The only difference of the sample wrt our project is that the addition of the key is performed by the same device rather than the android application.

    The weird thing is that in the sample we shared above the operation of adding the application key was run just after the app_keys callbacks, resulting in an error while in our project, during provisioning through the android application, happens the other way around (the expected way) with the app_keys.h callbacks running just after the addition of the key.

    Anyway, hopefully you won't read this message until monday. I wish you a nice weekend!

    Best regards,

    Ale

  • Hi Ale, 


    I don't really know why it doesn't work when you use thread or work. But if you can provide the code that can reproduce the issue then I can take a look and maybe find the root cause. 


Related