Not able to receive status messages published by onoff Server using message context instead of default publish parameters set by provisioner

Hello,

I am trying to send status messages to a Client model with a specific unicast address on one node. This node's parent element has both generic onoff server and client models implemented. All onoff server and client models across all nodes are configured to publish and subscribe to specific group addresses (0xc000 and 0xc001 respectively). When I try to publish a status message to this client model using the message context (with the destination address as the elements unicast address) instead of using the default publish parameters, it does get received by the access layer but, not by the client model (breakpoint at status handler does not hit).

When I publish a get/set message to the server model in the same element, using the same context instead of the default publish parameters, it gets received by the model (breakpoint at get/set handlers get hit). Am I missing something here? Please suggest.

I am working with NCS 1.9.1 on nRF52480 DK's

  • Hi,

    This seems a bit odd. 

    What example are you basing your project on? Can you give more details on what you have done so far? What modifications have you done? 

    Are you getting any error codes? It is received by the access layer so I assume it is being sent successfully from the server.

    message context (with the destination address as the elements unicast address) instead of using the default publish parameters

    Can you elaborate on this?

  • I have added generic onoff server and client models to provisioner example to be able to communicate with light and light switch examples. I will have many light (publish and subscribed to 0xc000 and 0xc001 respectively) and light switches (publish and subscribed to 0xc001 and 0xc000 respectively) in the network. 

    I want to send a message from either light or light switch specifically to the provisioner node if I want to remotely un-provision them. To do this from one of the light nodes(onoff server), I am sending a status message using 

    bt_mesh_onoff_srv_pub(&srv, &ctx, &status);
    ctx.addr= PROVISIONER_ADDRESS;
    ctx.app_idx= 0;
    ctx.send_rel=true;
    ctx.send_ttl= BT_MESH_TTL_DEFAULT;
    status.present_on_off = false;// use false to send remote reset request message
    context details mentioned above. I am able to see this message reach the access layer in the provisioner example by enabling the access layer debug option in the proj conf. No error codes.
    When I send the message using NULL instead of the ctx, I reaches the model and gets the status to the status_handler in the provisioner's client model.
    When I use bt_mesh_onoff_cli_set_unack(&client, &ctx, &set); using the same context settings from one of the light switches, the message reaches the model layer in the provisioner and the corresponding set_handler is called.
    I am not sure whats going on here..
  • I have attached my modified main.c of Provisioner, Model_handler c files for both switch and light examples. This is just to test if the message can be address to the provisioner node only. The unicast address for the provisioner element is 0x0001

    below is the provisioner node main.c

    /*
     * Copyright (c) 2019 Tobias Svehagen
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <sys/printk.h>
    
    #include <settings/settings.h>
    
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/mesh.h>
    #include <bluetooth/mesh/gen_onoff_srv.h>
    #include <bluetooth/mesh/gen_onoff_cli.h>
    
    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);
    
    static struct bt_mesh_cfg_cli cfg_cli = {
    };
    
    static void status_handler(struct bt_mesh_onoff_cli *cli,
    			   struct bt_mesh_msg_ctx *ctx,
    			   const struct bt_mesh_onoff_status *status);
    
    static void stat_set(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    const struct bt_mesh_onoff_set *set,
    		    struct bt_mesh_onoff_status *rsp);
    
    static void stat_get(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    struct bt_mesh_onoff_status *rsp);
    
    static const struct bt_mesh_onoff_srv_handlers onoff_handlers = {
    	.set = stat_set,
    	.get = stat_get,
    };
    
    struct bt_mesh_onoff_cli client = BT_MESH_ONOFF_CLI_INIT(&status_handler);
    struct bt_mesh_onoff_srv srv = BT_MESH_ONOFF_SRV_INIT(&onoff_handlers);
    
    static void status_handler(struct bt_mesh_onoff_cli *cli,
    			   struct bt_mesh_msg_ctx *ctx,
    			   const struct bt_mesh_onoff_status *status)
    {
    	printk("status ack received.. with onoff status %d from node address %d\n", status->present_on_off, ctx->addr);
    }
    
    static void stat_set(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    const struct bt_mesh_onoff_set *set,
    		    struct bt_mesh_onoff_status *rsp)
    {
    	printk("SET: request received.. with onoff status %d from node address %d\n", set->on_off, ctx->addr);
    }
    
    static void stat_get(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    struct bt_mesh_onoff_status *rsp)
    {
    	printk("GET: request received.. from node address %d\n", ctx->addr);
    }
    
    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 struct bt_mesh_model root_models[] = {
    	BT_MESH_MODEL_CFG_SRV,
    	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
    	BT_MESH_MODEL_ONOFF_CLI(&client),
    	BT_MESH_MODEL_ONOFF_SRV(&srv),
    	BT_MESH_MODEL_HEALTH_CLI(&health_cli),
    };
    
    static struct bt_mesh_elem elements[] = {
    	BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
    };
    
    static const struct bt_mesh_comp 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;
    
    	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(key->keys[0].app_key, 16);
    
    	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 status = 0;
    	int err;
    	struct bt_mesh_cfg_mod_pub clnt_pub = { 0 };
    	struct bt_mesh_cfg_mod_pub srv_pub = { 0 };
    
    	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;
    	}
    
    	/* Add Application Key */
    	err = bt_mesh_cfg_app_key_add(self->net_idx, self->addr, self->net_idx,
    				      app_idx, key->keys[0].app_key, &status);
    	if (err || status) {
    		printk("Failed to add app-key (err %d, status %d)\n", err,
    		       status);
    		return;
    	}
    
    	err = bt_mesh_cfg_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;
    	}
    
    	err = bt_mesh_cfg_mod_app_bind(self->net_idx, self->addr, self->addr,
    				       app_idx, BT_MESH_MODEL_ID_GEN_ONOFF_SRV,
    				       &status);
    	if (err || status) {
    		printk("Failed to bind app-key (err %d, status %d)\n", err,
    		       status);
    		return;
    	}
    	// else{
    	// 	printk("Configuring Srv model on local node\n");
    
    	// 	srv_pub.addr = 0xc001;
    	// 	srv_pub.app_idx = app_idx;
    	// 	srv_pub.cred_flag = false;
    	// 	srv_pub.uuid=NULL;
    	// 	srv_pub.ttl = 5;
    	// 	srv_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    	// 	srv_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    	// 	err = bt_mesh_cfg_mod_pub_set(net_idx, self->addr, self->addr,
    	// 			BT_MESH_MODEL_ID_GEN_ONOFF_SRV, &srv_pub,
    	// 			&status);
    
    	// 	if (err || status) {
    	// 		printk("Failed to add pub address (err: %d, status: %d)\n", err,
    	// 			status);
    	// 			return;
    	// 	}
    
    	// 	err = bt_mesh_cfg_mod_sub_add(net_idx, self->addr, self->addr, 0xc000,
    	// 				BT_MESH_MODEL_ID_GEN_ONOFF_SRV, &status);
    
    	// 	if (err || status) {
    	// 		printk("Failed to add sub address (err: %d, status: %d)\n", err,
    	// 			status);
    	// 			return;
    	// 	}
    	// }
    
    	err = bt_mesh_cfg_mod_app_bind(self->net_idx, self->addr, self->addr,
    				       app_idx, BT_MESH_MODEL_ID_GEN_ONOFF_CLI,
    				       &status);
    	if (err || status) {
    		printk("Failed to bind app-key (err %d, status %d)\n", err,
    		       status);
    		return;
    	}
    	// else{
    	// 	printk("Configuring Client model on local node\n");
    	// 	clnt_pub.addr = 0xc000;
    		
    	// 	clnt_pub.app_idx = app_idx;
    	// 	clnt_pub.cred_flag = false;
    	// 	clnt_pub.uuid=NULL;
    	// 	clnt_pub.ttl = 5;
    	// 	clnt_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    	// 	clnt_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    	// 	err = bt_mesh_cfg_mod_pub_set(net_idx, self->addr, self->addr,
    	// 			BT_MESH_MODEL_ID_GEN_ONOFF_CLI, &clnt_pub,
    	// 			&status);
    
    	// 	if (err || status) {
    	// 		printk("Failed to add pub address (err: %d, status: %d)\n", err,
    	// 			status);
    	// 			return;
    	// 	}
    
    	// 	err = bt_mesh_cfg_mod_sub_add(net_idx, self->addr, self->addr, 0xc001,
    	// 				BT_MESH_MODEL_ID_GEN_ONOFF_CLI, &status);
    
    	// 	if (err || status) {
    	// 		printk("Failed to add sub address (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;
    	struct bt_mesh_comp_p0 comp;
    	uint8_t status;
    	int err, elem_addr;
    	//size_t subs_count = 1; // ----------------------------added code------------------//
    	//uint16_t sub;
    	struct bt_mesh_cfg_mod_pub clnt_pub = { 0 };
    	struct bt_mesh_cfg_mod_pub srv_pub = { 0 };//-------------till here-------------------//
    	static int flag = 0;
    
    	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;
    	}
    
    	/* Add Application Key */
    	err = bt_mesh_cfg_app_key_add(net_idx, node->addr, net_idx, app_idx,
    				      key->keys[0].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_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);
    
    		// ---------------------test code ----------------------------//
    			if ((id == BT_MESH_MODEL_ID_GEN_ONOFF_SRV) || (id == BT_MESH_MODEL_ID_GEN_ONOFF_CLI))
    			{
    				flag++;
    				printk("value to flag -> %d\n", flag);
    			}
    		// ---------------------test code ----------------------------//
    
    			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_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);
    			}
    
    		// ----------------------------added code--------------------//
    			if(flag == 4)
    			{
    				flag = 0;
    
    				if (id == BT_MESH_MODEL_ID_GEN_ONOFF_SRV) 
    				{
    					printk("Configuring Srv model on remote node\n");
    
    					srv_pub.addr = 0xc001;
    					srv_pub.app_idx = app_idx;
    					srv_pub.cred_flag = false;
    					srv_pub.uuid=NULL;
    					srv_pub.ttl = 5;
    					srv_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    					srv_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    					err = bt_mesh_cfg_mod_pub_set(net_idx, node->addr, elem_addr,
    							BT_MESH_MODEL_ID_GEN_ONOFF_SRV, &srv_pub,
    							&status);
    
    					if (err || status) {
    						printk("Failed to add pub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    					err = bt_mesh_cfg_mod_sub_add(net_idx, node->addr, elem_addr, 0xc000,
    							BT_MESH_MODEL_ID_GEN_ONOFF_SRV, &status);
    
    					if (err || status) {
    						printk("Failed to add sub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    
    				}
    				else if (id == BT_MESH_MODEL_ID_SENSOR_SRV)
    				{
    					printk("Configuring Srv model on remote node\n");
    
    					srv_pub.addr = 0xc001;
    					srv_pub.app_idx = app_idx;
    					srv_pub.cred_flag = false;
    					srv_pub.uuid=NULL;
    					srv_pub.ttl = 5;
    					srv_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    					srv_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    					err = bt_mesh_cfg_mod_pub_set(net_idx, node->addr, elem_addr,
    							BT_MESH_MODEL_ID_SENSOR_SRV, &srv_pub,
    							&status);
    
    					if (err || status) {
    						printk("Failed to add pub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    					err = bt_mesh_cfg_mod_sub_add(net_idx, node->addr, elem_addr, 0xc000,
    							BT_MESH_MODEL_ID_SENSOR_SRV, &status);
    
    					if (err || status) {
    						printk("Failed to add sub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    
    				}
    				else if(id == BT_MESH_MODEL_ID_SENSOR_CLI)
    				{
    					printk("Configuring Client model on remote node\n");
    					clnt_pub.addr = 0xc000;
    					
    					clnt_pub.app_idx = app_idx;
    					clnt_pub.cred_flag = false;
    					clnt_pub.uuid=NULL;
    					clnt_pub.ttl = 5;
    					clnt_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    					clnt_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    					err = bt_mesh_cfg_mod_pub_set(net_idx, node->addr, elem_addr,
    							BT_MESH_MODEL_ID_SENSOR_CLI, &clnt_pub,
    							&status);
    
    					if (err || status) {
    						printk("Failed to add pub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    					err = bt_mesh_cfg_mod_sub_add(net_idx, node->addr, elem_addr, 0xc001,
    							BT_MESH_MODEL_ID_SENSOR_CLI, &status);
    
    					if (err || status) {
    						printk("Failed to add sub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    				
    				}
    				else if(id == BT_MESH_MODEL_ID_GEN_ONOFF_CLI)
    				{
    					printk("Configuring Client model on remote node\n");
    					clnt_pub.addr = 0xc000;
    					
    					clnt_pub.app_idx = app_idx;
    					clnt_pub.cred_flag = false;
    					clnt_pub.uuid=NULL;
    					clnt_pub.ttl = 5;
    					clnt_pub.period = 0U;//BT_MESH_PUB_PERIOD_10SEC(0);
    					clnt_pub.transmit = 0U;// BT_MESH_TRANSMIT(1, 100);
    
    					err = bt_mesh_cfg_mod_pub_set(net_idx, node->addr, elem_addr,
    							BT_MESH_MODEL_ID_GEN_ONOFF_CLI, &clnt_pub,
    							&status);
    
    					if (err || status) {
    						printk("Failed to add pub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    					err = bt_mesh_cfg_mod_sub_add(net_idx, node->addr, elem_addr, 0xc001,
    							BT_MESH_MODEL_ID_GEN_ONOFF_CLI, &status);
    
    					if (err || status) {
    						printk("Failed to add sub address (err: %d, status: %d)\n", err,
    							status);
    					}
    
    				
    				}//-------------till here-------------------//
    				else
    				{}//-------------till here-------------------//
    			}
    		}
    
    		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_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 net_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, &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;
    }
    
    void 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;
    	}
    
    	printk("Bluetooth initialized\n");
    	bt_ready();
    
    	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));
    
    		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);
    	}
    }
    

    below is the model_handler.c for light node

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/mesh/models.h>
    #include <dk_buttons_and_leds.h>
    #include "model_handler.h"
    
    static void led_set(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    const struct bt_mesh_onoff_set *set,
    		    struct bt_mesh_onoff_status *rsp);
    
    static void led_get(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    struct bt_mesh_onoff_status *rsp);
    
    static const struct bt_mesh_onoff_srv_handlers onoff_handlers = {
    	.set = led_set,
    	.get = led_get,
    };
    
    struct led_ctx {
    	struct bt_mesh_onoff_srv srv;
    	struct k_work_delayable work;
    	uint32_t remaining;
    	bool value;
    };
    
    static struct led_ctx led_ctx[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(led0))
    	{ .srv = BT_MESH_ONOFF_SRV_INIT(&onoff_handlers) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led1))
    	{ .srv = BT_MESH_ONOFF_SRV_INIT(&onoff_handlers) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led2))
    	{ .srv = BT_MESH_ONOFF_SRV_INIT(&onoff_handlers) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led3))
    	{ .srv = BT_MESH_ONOFF_SRV_INIT(&onoff_handlers) },
    #endif
    };
    
    static void led_transition_start(struct led_ctx *led)
    {
    	int led_idx = led - &led_ctx[0];
    
    	/* As long as the transition is in progress, the onoff
    	 * state is "on":
    	 */
    	dk_set_led(led_idx, true);
    	k_work_reschedule(&led->work, K_MSEC(led->remaining));
    	led->remaining = 0;
    }
    
    static void led_status(struct led_ctx *led, struct bt_mesh_onoff_status *status)
    {
    	int err = 0;
    	/* Do not include delay in the remaining time. */
    	status->remaining_time = led->remaining ? led->remaining :
    		k_ticks_to_ms_ceil32(k_work_delayable_remaining_get(&led->work));
    	status->target_on_off = led->value;
    	/* As long as the transition is in progress, the onoff state is "on": */
    	status->present_on_off = led->value || status->remaining_time;
    
    	//----------------------Added test code--------------------------------//
    	struct bt_mesh_onoff_status stat;
    	struct bt_mesh_msg_ctx ctx;
    	ctx.addr= 0x001;
    	ctx.app_idx= 0;
    	ctx.send_rel=false;
    	ctx.send_ttl= BT_MESH_TTL_DEFAULT;
    	stat.present_on_off = false;// use false to send remote reset request message
    	err = bt_mesh_onoff_srv_pub(&led->srv, &ctx, &stat);
    	if(err)
    	{
    		printk("Error publishing status to provisioner on..%d\n", err);
    	}
    	//---------------------Till here---------------------------------------//
    }
    
    static void led_set(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    const struct bt_mesh_onoff_set *set,
    		    struct bt_mesh_onoff_status *rsp)
    {
    	struct led_ctx *led = CONTAINER_OF(srv, struct led_ctx, srv);
    	int led_idx = led - &led_ctx[0];
    
    	if (set->on_off == led->value) {
    		goto respond;
    	}
    
    	led->value = set->on_off;
    	if (!bt_mesh_model_transition_time(set->transition)) {
    		led->remaining = 0;
    		dk_set_led(led_idx, set->on_off);
    		goto respond;
    	}
    
    	led->remaining = set->transition->time;
    
    	if (set->transition->delay) {
    		k_work_reschedule(&led->work, K_MSEC(set->transition->delay));
    	} else {
    		led_transition_start(led);
    	}
    
    respond:
    	if (rsp) {
    		led_status(led, rsp);
    	}
    }
    
    static void led_get(struct bt_mesh_onoff_srv *srv, struct bt_mesh_msg_ctx *ctx,
    		    struct bt_mesh_onoff_status *rsp)
    {
    	struct led_ctx *led = CONTAINER_OF(srv, struct led_ctx, srv);
    
    	led_status(led, rsp);
    }
    
    static void led_work(struct k_work *work)
    {
    	struct led_ctx *led = CONTAINER_OF(work, struct led_ctx, work.work);
    	int led_idx = led - &led_ctx[0];
    
    	if (led->remaining) {
    		led_transition_start(led);
    	} else {
    		dk_set_led(led_idx, led->value);
    
    		/* Publish the new value at the end of the transition */
    		struct bt_mesh_onoff_status status;
    
    		led_status(led, &status);
    		bt_mesh_onoff_srv_pub(&led->srv, NULL, &status);
    	}
    }
    
    /* Set up a repeating delayed work to blink the DK's LEDs when attention is
     * requested.
     */
    static struct k_work_delayable attention_blink_work;
    static bool attention;
    
    static void attention_blink(struct k_work *work)
    {
    	static int idx;
    	const uint8_t pattern[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(led0))
    		BIT(0),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led1))
    		BIT(1),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led2))
    		BIT(2),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led3))
    		BIT(3),
    #endif
    	};
    
    	if (attention) {
    		dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
    		k_work_reschedule(&attention_blink_work, K_MSEC(30));
    	} else {
    		dk_set_leds(DK_NO_LEDS_MSK);
    	}
    }
    
    static void attention_on(struct bt_mesh_model *mod)
    {
    	attention = true;
    	k_work_reschedule(&attention_blink_work, K_NO_WAIT);
    }
    
    static void attention_off(struct bt_mesh_model *mod)
    {
    	/* Will stop rescheduling blink timer */
    	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_elem elements[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(led0))
    	BT_MESH_ELEM(
    		1, BT_MESH_MODEL_LIST(
    			BT_MESH_MODEL_CFG_SRV,
    			BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    			BT_MESH_MODEL_ONOFF_SRV(&led_ctx[0].srv)),
    		BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led1))
    	BT_MESH_ELEM(
    		2, BT_MESH_MODEL_LIST(BT_MESH_MODEL_ONOFF_SRV(&led_ctx[1].srv)),
    		BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led2))
    	BT_MESH_ELEM(
    		3, BT_MESH_MODEL_LIST(BT_MESH_MODEL_ONOFF_SRV(&led_ctx[2].srv)),
    		BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(led3))
    	BT_MESH_ELEM(
    		4, BT_MESH_MODEL_LIST(BT_MESH_MODEL_ONOFF_SRV(&led_ctx[3].srv)),
    		BT_MESH_MODEL_NONE),
    #endif
    };
    
    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)
    {
    	k_work_init_delayable(&attention_blink_work, attention_blink);
    
    	for (int i = 0; i < ARRAY_SIZE(led_ctx); ++i) {
    		k_work_init_delayable(&led_ctx[i].work, led_work);
    	}
    
    	return &comp;
    }
    

    below is the model_handler.c for switch node

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    /**
     * @file
     * @brief Model handler for the light switch.
     *
     * Instantiates a Generic OnOff Client model for each button on the devkit, as
     * well as the standard Config and Health Server models. Handles all application
     * behavior related to the models.
     */
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/mesh/models.h>
    #include <dk_buttons_and_leds.h>
    #include "model_handler.h"
    
    /* Light switch behavior */
    
    /** Context for a single light switch. */
    struct button {
    	/** Current light status of the corresponding server. */
    	bool status;
    	/** Generic OnOff client instance for this switch. */
    	struct bt_mesh_onoff_cli client;
    };
    
    static void status_handler(struct bt_mesh_onoff_cli *cli,
    			   struct bt_mesh_msg_ctx *ctx,
    			   const struct bt_mesh_onoff_status *status);
    
    static struct button buttons[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(sw0))
    	{ .client = BT_MESH_ONOFF_CLI_INIT(&status_handler) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw1))
    	{ .client = BT_MESH_ONOFF_CLI_INIT(&status_handler) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw2))
    	{ .client = BT_MESH_ONOFF_CLI_INIT(&status_handler) },
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw3))
    	{ .client = BT_MESH_ONOFF_CLI_INIT(&status_handler) },
    #endif
    };
    
    static void status_handler(struct bt_mesh_onoff_cli *cli,
    			   struct bt_mesh_msg_ctx *ctx,
    			   const struct bt_mesh_onoff_status *status)
    {
    	struct button *button =
    		CONTAINER_OF(cli, struct button, client);
    	int index = button - &buttons[0];
    
    	button->status = status->present_on_off;
    	dk_set_led(index, status->present_on_off);
    
    	printk("Button %d: Received response: %s\n", index + 1,
    	       status->present_on_off ? "on" : "off");
    }
    
    static void button_handler_cb(uint32_t pressed, uint32_t changed)
    {
    	int err;
    	if (!bt_mesh_is_provisioned()) {
    		return;
    	}
    
    	for (int i = 0; i < ARRAY_SIZE(buttons); ++i) {
    
    		struct bt_mesh_onoff_set set = {
    			.on_off = !buttons[i].status,
    		};
    
    		if(!(pressed & changed & BIT(i)))
    		{
    			continue;
    		}
    
    	//------------------Added test code-----------------------------//
    		struct bt_mesh_msg_ctx m;
    		m.addr= 0x0001;
    		m.app_idx= 0;
    		m.send_rel=false;
    		m.send_ttl= BT_MESH_TTL_DEFAULT;
    		err = bt_mesh_onoff_cli_set_unack(&buttons[i].client, NULL, &set);
    		if (!err) {
    			/* There'll be no response status for the
    				* unacked message. Set the state immediately.
    				*/
    			buttons[i].status = set.on_off;
    			dk_set_led(i, set.on_off);
    		}
    	//------------------Added test code-----------------------------//
    		/* As we can't know how many nodes are in a group, it doesn't
    		 * make sense to send acknowledged messages to group addresses -
    		 * we won't be able to make use of the responses anyway.
    		 */
    
    		if (bt_mesh_model_pub_is_unicast(buttons[i].client.model)) {
    		err = bt_mesh_onoff_cli_set(&buttons[i].client, NULL,
    						&set, NULL);
    		} else {
    			err = bt_mesh_onoff_cli_set_unack(&buttons[i].client,
    							NULL, &set);
    			if (!err) {
    				/* There'll be no response status for the
    				* unacked message. Set the state immediately.
    				*/
    				buttons[i].status = set.on_off;
    				dk_set_led(i, set.on_off);
    			}
    		}
    
    		if (err) {
    			printk("OnOff %d set failed: %d\n", i + 1, err);
    		}	
    	}
    }
    
    /* Set up a repeating delayed work to blink the DK's LEDs when attention is
     * requested.
     */
    static struct k_work_delayable attention_blink_work;
    static bool attention;
    
    static void attention_blink(struct k_work *work)
    {
    	static int idx;
    	const uint8_t pattern[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(sw0))
    		BIT(0),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw1))
    		BIT(1),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw2))
    		BIT(2),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw3))
    		BIT(3),
    #endif
    	};
    
    	if (attention) {
    		dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
    		k_work_reschedule(&attention_blink_work, K_MSEC(30));
    	} else {
    		dk_set_leds(DK_NO_LEDS_MSK);
    	}
    }
    
    static void attention_on(struct bt_mesh_model *mod)
    {
    	attention = true;
    	k_work_reschedule(&attention_blink_work, K_NO_WAIT);
    }
    
    static void attention_off(struct bt_mesh_model *mod)
    {
    	/* Will stop rescheduling blink timer */
    	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_elem elements[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(sw0))
    	BT_MESH_ELEM(1,
    		     BT_MESH_MODEL_LIST(
    			     BT_MESH_MODEL_CFG_SRV,
    			     BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    			     BT_MESH_MODEL_ONOFF_CLI(&buttons[0].client)),
    		     BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw1))
    	BT_MESH_ELEM(2,
    		     BT_MESH_MODEL_LIST(
    			     BT_MESH_MODEL_ONOFF_CLI(&buttons[1].client)),
    		     BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw2))
    	BT_MESH_ELEM(3,
    		     BT_MESH_MODEL_LIST(
    			     BT_MESH_MODEL_ONOFF_CLI(&buttons[2].client)),
    		     BT_MESH_MODEL_NONE),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw3))
    	BT_MESH_ELEM(4,
    		     BT_MESH_MODEL_LIST(
    			     BT_MESH_MODEL_ONOFF_CLI(&buttons[3].client)),
    		     BT_MESH_MODEL_NONE),
    #endif
    };
    
    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)
    {
    	static struct button_handler button_handler = {
    		.cb = button_handler_cb,
    	};
    
    	dk_button_handler_add(&button_handler);
    	k_work_init_delayable(&attention_blink_work, attention_blink);
    
    	return &comp;
    }
    

    Can status messages be sent randomly? or do they get accepted only after a get/set message gets sent first? Please advice

  • Hi,

    I've taken over this case from my colleague and I will continue the support.

    Firstly apologies for the long response time. Secondly has any new information occurred in this case since our previous reply?

    Kind regards,
    Andreas

  • Hello AHaug,

    thank you for your reply. I have had no success with this issue. Sending a context message from onoff server to cli(after being configured to publish and subscribe to group addresses by the provisioner)  just does not seem to work for some reason. Please advice.

Related