BLE Mesh Custom Model Appkey Index not updating during self-provisioning process, always equals 0xFFFF

Hello,

I am new to BLE Mesh and working with Zephyr. However, I've found the supported BLE Mesh samples to be very helpful in understanding the protocol and the environment.

I am working on evaluating BLE Mesh by modifying the mesh_demo sample. My goal is to create a two device types, each of which should self provision. The first device is a publisher and the second a subscriber.

For both device types I used the vendor model definition and related initialization.

Right now the data being sent is arbitrary and the publisher is working just fine with no issues. However, the subscription device has a strange issue. From my testing I have found that the custom vendor model appkey is being set successfully but the appkey index value for the custom model structure is not being updated during the self provisioning process. 

Doing some further debug and extra print statements, I see that the subscription device is receiving packets from the publisher but at the access layer the issue is revealed:

bt_mesh_access: element_model_recv: Model at 0x0002 is not bound to app idx
mod->keys_cnt = 1
mod->keys[0] = FFFF
key = 0000

To me, this looks like the appkey index for the subscription device model is 0xFFFF which would indicate that it has not been updated. The relevant code for this setup is here:

static struct bt_mesh_cfg_cli cfg_cli = {
};

static void attention_on(const struct bt_mesh_model *mod)
{
	board_led_0_blink();
}

static void attention_off(const struct bt_mesh_model *mod)
{
	board_led_0_blink();
}

static const struct bt_mesh_health_srv_cb health_cb = {
	.attn_on = attention_on,
	.attn_off = attention_off,
};

static struct bt_mesh_health_srv health_srv = {
	.cb = &health_cb,
};

BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);

struct bt_mesh_model models[] = {
    BT_MESH_MODEL_CFG_SRV,
	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
};

static const struct bt_mesh_model_op custom_hb_ops[] = {
	{ CUSTOM_OP, 0, custom_hb_recv },
	BT_MESH_MODEL_OP_END,
};

static struct bt_mesh_model vnd_models[] = {
    BT_MESH_MODEL_VND(
        CONFIG_BT_COMPANY_ID, // Vendor-specific company ID
        CUSTOM_MODEL_ID,      // Vendor-specific model ID
        custom_hb_ops,        // Operation table
        NULL,                 // No user data neededy
		NULL
    ),
};

static const struct bt_mesh_elem elements[] = {
	BT_MESH_ELEM(0, 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");

    /* Add Application Key */
    int err = bt_mesh_cfg_cli_app_key_add(net_idx, addr, net_idx, app_idx, app_key, NULL);
	if (err) {
		printk("Failed to add app key (err %d)\n", err);
		return;
	} else {
		printk("App key successfully added\n");
	}

    /* Bind to Custom model */
    err = bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, addr, addr, app_idx, CUSTOM_MODEL_ID, CONFIG_BT_COMPANY_ID, NULL);
	if (err) {
        printk("Failed to bind app key to model (err %d)\n", err);
        return;
    } else {
        printk("App key successfully bound to model\n");
    }

    /* Subscribe to Group Address */
    err = bt_mesh_cfg_cli_mod_sub_add_vnd(net_idx, addr, addr, group_addr, CUSTOM_MODEL_ID, CONFIG_BT_COMPANY_ID, NULL);
	if (err) {
        printk("Subscribe failed (err %d)\n", err);
        return;
    } else {
        printk("Successfully subscribed to group address\n");
    }

    printk("Configuration complete\n");
}

None of the configure functions returns any type of error, and, if I manually update the model structure the appkey index does work: 

vnd_models[0].keys[0] = app_idx;
However this is only a temporary workaround and would not be suitable for production. 
I have tried many many many different configurations for the custom model and the project configuration file settings but nothing seems to work. Any suggestions or ideas to try would be very much appreciated.
Also, my opcode values are defined as follows:
/** Custom Pub Model ID */
#define CUSTOM_MODEL_ID	0x0010
/** Custom Pub Model Op Code Size */
#define CUSTOM_OP_CODE_SIZE	3
/** Custom Pub Model Payload Op Code */
#define CUSTOM_OP	BT_MESH_MODEL_OP_3(0x01, CONFIG_BT_COMPANY_ID)
  • Hello Cam,

    I found some issues about the order the operations in a similar setup in my last reply in this DevZone case:

     Bluetooth: Mesh Demo example automatic provisioning 

    Could you enable more logging and see if that is what is happening here?

    In that same DevZone case, I also wrote about why the self-provisioning setup is recommended against for production.

    Hieu

  • Hi Hieu,

    Thank you for your reply. Are you referring to these steps you outlined:

    • On initialization when the device is not yet provision, add the app key to the node. The model features won't work yet in this boot.  
    • On initialization where the device has been provisioned, run the key binding. From this point on, the features should work normally.
    • Thus, to get things run, after flashing the device and let it run for the first time, simply reset it and things should then work.

    If so, I am a bit unsure what this would look like in the source code. Are you suggesting that I try adding the app key first on boot, then provisioning the device second, followed by binding the app key? Or is there some other order of operations. I don't understand how the multiple boot ups would work in this case. 

    Would I need to add some conditional to check if the app key has already been added? If it hasn't, add the app key and reboot, if it has, provision and do the key binding? Or is this different than your crude workaround?

  • Hi Cam,

    The status of whether an app key is added, and which models it is bound to is stored in NVM. During initialization, you can simply check the status and then proceed depends on what the current state is.

    It has been a long time, and I don't know if I had tried this then, but instead of a reset to split apart the sequence, you can also try adding a delay.

Related