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, 

    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. 


Reply Children
  • Dear Hung Bui,

    I prepared an example for you using the very same code we have exchanged above, I've modified the bt mesh demo sample to show what I was talking about:

    • I enabled GATT provisioning to be performed via nrf Mesh app.
    • I removed the configure and continue configure functions (and their semaphores) (now the act of adding an application key is done via the app, the act of binding said keys is performed in a callback, specifically in app_keys.c callbacks).
    • I put back the status parameter in configuration client functions so to prove that they still work somehow, despite being called in the very context you have show (app_keys callbacks).
    • I commented the vast majority of the code as it was useless to this purpose.

    Remember to add the following configurations to your prj.conf file:

    CONFIG_HWINFO=y
    CONFIG_DK_LIBRARY=y
    CONFIG_BT_MESH_DK_PROV=y
    
    CONFIG_BT_COMPANY_ID=0x0059
    CONFIG_BT_DEVICE_NAME="Mesh Demo"
    CONFIG_BT_L2CAP_TX_BUF_COUNT=8
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_ECC=y
    CONFIG_BT_TINYCRYPT_ECC=y
    
    CONFIG_BT_MESH_PB_GATT=y
    CONFIG_BT_MESH_PB_ADV=y
    CONFIG_BT_MESH_GATT_PROXY=y
    CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME=y

    The main.c code now becomes:

    #include <zephyr/sys/printk.h>
    #include <zephyr/settings/settings.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/mesh.h>
    #include <bluetooth/mesh/dk_prov.h>
    #include <dk_buttons_and_leds.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, CONFIG_BT_COMPANY_ID)
    
    // 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 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("vnd_button_pressed\n");
    
    	// 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(CONFIG_BT_COMPANY_ID, 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 = CONFIG_BT_COMPANY_ID,
    	.elem = elements,
    	.elem_count = ARRAY_SIZE(elements),
    };
    
    // static void configure(void)
    // {
    // 	printk("Configuring...\n");
    // 	uint8_t status = 0;
    	
    //  	printk("%d\n", bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, &status));
    // 	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");
    	
    	dk_leds_init();
    	dk_buttons_init(NULL);
    
    	err = bt_mesh_init(bt_mesh_dk_prov_init(), &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);
    	// }
    	bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
    
    
    // #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");
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    	// configure();
    	// while (1) {
    	// //	k_sem_take(&tune_sem, K_FOREVER);
    	// //	board_play_tune(tune_str);
    	// }
    
    	k_msleep(1000);	
    	printk("Device is %s.\n", bt_mesh_is_provisioned() ? "provisioned" : "not provisioned");
    
    	return 0;
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void on_app_key_added(uint16_t app_idx, uint16_t net_idx) {
    	printk("app key added\n");
    	switch (app_idx) {
    		case (0): {
    			uint8_t status = 0;
    			struct bt_mesh_elem* elem = bt_mesh_model_elem(health_srv.model);
                uint16_t addr = elem->addr;
                uint16_t elem_addr = addr + elem->loc;
    			int err;
    
    			printk("app key 0 - addr = %d - elem_addr = %d - loc = %d\n", addr, elem_addr, elem->loc);
    			
    			/* Bind to vendor model */
    			err = bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, elem_addr, app_idx, MOD_LF, CONFIG_BT_COMPANY_ID, &status);
    			printk("bind_vnd: err = %d, status = %d\n", err, status);
    			/* Bind to Health model */
    			err = bt_mesh_cfg_cli_mod_app_bind(net_idx, addr, elem_addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, &status);
    			printk("bind:     err = %d, status = %d\n", err, status);
    
    			/* Add model subscription */
    			err = bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, elem_addr, GROUP_ADDR, MOD_LF, CONFIG_BT_COMPANY_ID, &status);
    			printk("add_vnd:  err = %d, status = %d\n", err, status);
    			break;
    		}
    
    		default: {
    			printk("Unforseen AppKey idx %d\n", 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);

    To reproduce the error:

    1. Grab your handy nrf 52840 dk
    2. Build and flash
    3. Add a terminal to the device
    4. Provision the device in the nrf mesh app

    The output should be:

    • app key 0 - addr = 2 - elem_addr = 2 - loc = 0
    • bind_vnd: err = 0, status = 0
    • bind: err = 0, status = 0
    • add_vnd: err = 0, status = 0

    This should prove that the synchronous and blocking behavior inside a callback that is faulty when using bt_mesh_provision is not faulty using the traditional provisioning protocol.

    I honestly have no idea why though, as said before, my best guess it that the bind, bind_vnd, add_sub functions are called before the act of adding an application key when using bt_mesh_provision while they are called after the addition of the application key when using standard provisioning.

    Sorry for the long message, I hope I gave you enough information about this issue Smile

    If you find out anything about this, please let me know! And keep up the good work.

    Best regards,

    Ale

  • Hi Ale, 


    Why don't you send me the code that has the issue (the one with work queue/thread queue that you said doesn't work)? 

    The code you sent I assume works just fine with a phone, correct ?

    As I has explained to you earlier, you don't have an issue with a phone because the configuration client (the one add appkey) doesn't run on the chip when you configure with a phone. bt_mesh_cfg_cli_app_key_add() is the blocking part. 

    The client is on the phone and that's why you don't have any issue with blocking problem. 

    Please let me know if something is still unclear. 

  • Hi Hung Bui,

    A quick update: unfortunately, there's still something that is unclear to me about how the underlying code works. Even setting the add_app_key to be asynchronous and non-blocking doesn't solve the issue.

    Unfortunately, the project I'm working on is protected by an NDA so I can't really disclose more than I already did with you. While I managed to make the thread approach work on the sample, I still had some troubles implementing it in the project.

    BUT. But I was able to make the whole thing work anyway thanks to your suggestions:

    • Set every single call to the cfg client to be asynchronous non-blocking by seting status parameter to NULL.
    • Implemented the semaphore logic so to configure the node only after the bt_ready function has finished.

    At this point, I would get two errors:

    • <err> bt_mesh_transport: No multi-segment message context available
    • <wrn> bt_mesh_net: Unable to allocate loopback.
      <err> bt_mesh_cfg_srv_ Unable to send App Key Status response.

    I managed to solve these errors by setting in the prj.conf file respectively:

    • CONFIG_BT_MESH_TX_SEG_MSG_COUNT=8
      CONFIG_BT_MESH_RX_SEG_MSG_COUNT=8
    • CONFIG_BT_MESH_LOOPBACK_BUFS=8

    Now everything does work as intended. Thank you for your great effort and help, it's always a pleasure to talk to you. Have a great day,

    Ale

  • Hi Ale, 
    I'm glad that you now have a working prototype. Best of luck with the further development of the project. 

Related