Health Server and Health Client models.

Hello All,

I am currently developing a mesh application with several nodes. I wish to add the health server faults for monitoring the nodes in the network. As of now I am able to receive the health messages with no faults detected. Health Client was referred from the 'Provisioner Sample'. example where the callbacks printed out the detected errors.

Now I am already running a sensor server on the node and I required the faults occurring in the nodes to be sent to the client. As of now I am not able to find any samples regarding this issue.

I have referred to this documentation for the health server and this documentation for the health client, but do not understand how to actually send the faults to the client.

Could someone send any samples for the same?

Currently developing using nRF Connect SDK v2.1.2 and nRF52840 dongle for evaluation.

Parents
  • Hi Sumedh,

    I will need more time to look into this. Unless someone from the community already helped you by then, I will respond to you latest by the end of Wednesday Jan 18.

    Hieu

  • Thank you for your response Hieu.

    We'll be waiting for any help on this topic.

    Regards,

    Sumedh L.

  • Hi, 

    Hieu is out of office this week so I will help you progress this case in the meanwhile.

    As of now I am able to receive the health messages with no faults detected. Health Client was referred from the 'Provisioner Sample'. example where the callbacks printed out the detected errors.

    Could you either send us a minimal sample that reproduces how far you've come in setting up the health server/client models in your application or send us steps for how to recreate what you've done?

    Kind regards,
    Andreas

  • Hello Andreas,

    Thank you for responding.

    I have copied the following code from the sensor server sample provided. My main issue with this code is understanding how I am supposed to send the faults to the client. I have seen the faults and warnings mentioned in nrf/include/bluetooth/mesh/properties.h. I wish to use few of these codes send the health details of my node to the client.

    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(100));		//Changed the Blinking speed for the attention callback. SL
    	} else {
    		dk_set_leds(DK_NO_LEDS_MSK);
    	}
    }
    
    /**
     * @brief: This function will enable the attention of the node.
     * This is the function that is executed during identification of the node in the nRF Mesh application.
    */
    static void attention_on(struct bt_mesh_model *mod)
    {
    	attention = true;
    	k_work_reschedule(&attention_blink_work, K_NO_WAIT);
    }
    
    /**
     * @brief: This function will disable the attention of the node.
     * This is the function that is executed during identification of the node in the nRF Mesh application.
    */
    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);

    On the Client end I implemented the following code for health client. I based this code from the Mesh_Provisioner sample provided. Now I wish to see the sent codes from the node here. 

    static void current_health_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("\nHealth Current Status from Node ID: %d\n", addr);
    	
    	if (!fault_count) {
    		printk("\nHealth 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_client = {
    	.current_status = current_health_status,
    };

    It would be useful to get some sample or guide on how to add these faults and send them as well as clear them if necessary.

    Regards,

    Sumedh.

  • Hi, 

    Unfortunately I did not have time to handle this case before I'm going out of office, but I will get back to it next week. Apologies for the inconvinience

    Kind regards,
    Andreas

  • Hello Andreas,

    Is there any update regarding the topic? We are still waiting for the response.

    Regards,

    Sumedh L.

  • Hi Sumedh,

    Our apologies for the long wait. My sickness came at a bad time and coincided with Andreas' out of office period.
    I have now recovered and returned to office and will take over once more.

    A colleague helped me find some clue in the Bluetooth Mesh Shell application and we believe that the codes below serve as sample implementation.
    Could you please try to follow it and see if it works?
    Sorry I haven't been able to verify it before sharing it with you, but I don't want to keep you waiting any longer. 

    // From zephyr/subsys/bluetooth/mesh/shell.c
    
    static uint8_t cur_faults[BT_MESH_SHELL_CUR_FAULTS_MAX];
    static uint8_t reg_faults[BT_MESH_SHELL_CUR_FAULTS_MAX * 2];
    
    static void get_faults(uint8_t *faults, uint8_t faults_size, uint8_t *dst, uint8_t *count)
    {
    	uint8_t i, limit = *count;
    
    	for (i = 0U, *count = 0U; i < faults_size && *count < limit; i++) {
    		if (faults[i]) {
    			*dst++ = faults[i];
    			(*count)++;
    		}
    	}
    }
    
    static int fault_get_cur(struct bt_mesh_model *model, uint8_t *test_id,
    			 uint16_t *company_id, uint8_t *faults, uint8_t *fault_count)
    {
    	shell_print_ctx("Sending current faults");
    
    	*test_id = 0x00;
    	*company_id = BT_COMP_ID_LF;
    
    	get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
    
    	return 0;
    }
    
    static int fault_get_reg(struct bt_mesh_model *model, uint16_t cid,
    			 uint8_t *test_id, uint8_t *faults, uint8_t *fault_count)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		shell_print_ctx("Faults requested for unknown Company ID"
    				" 0x%04x", cid);
    		return -EINVAL;
    	}
    
    	shell_print_ctx("Sending registered faults");
    
    	*test_id = 0x00;
    
    	get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
    
    	return 0;
    }
    
    static int fault_clear(struct bt_mesh_model *model, uint16_t cid)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		return -EINVAL;
    	}
    
    	(void)memset(reg_faults, 0, sizeof(reg_faults));
    
    	return 0;
    }
    
    static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
    		      uint16_t cid)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		return -EINVAL;
    	}
    
    	if (test_id != 0x00) {
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static const struct bt_mesh_health_srv_cb health_srv_cb = {
    	.fault_get_cur = fault_get_cur,
    	.fault_get_reg = fault_get_reg,
    	.fault_clear = fault_clear,
    	.fault_test = fault_test,
    };
    
    static int cmd_add_fault(const struct shell *shell, size_t argc, char *argv[])
    {
    	uint8_t fault_id;
    	uint8_t i;
    	struct bt_mesh_elem *elem;
    	int err = 0;
    
    	elem = primary_element();
    	if (elem == NULL) {
    		shell_print(shell, "Element not found!");
    		return -EINVAL;
    	}
    
    	fault_id = shell_strtoul(argv[1], 0, &err);
    	if (err) {
    		shell_warn(shell, "Unable to parse input string argument");
    		return err;
    	}
    
    	if (!fault_id) {
    		shell_print(shell, "The Fault ID must be non-zero!");
    		return -EINVAL;
    	}
    
    	for (i = 0U; i < sizeof(cur_faults); i++) {
    		if (!cur_faults[i]) {
    			cur_faults[i] = fault_id;
    			break;
    		}
    	}
    
    	if (i == sizeof(cur_faults)) {
    		shell_print(shell, "Fault array is full. Use \"del-fault\" to "
    			    "clear it");
    		return 0;
    	}
    
    	for (i = 0U; i < sizeof(reg_faults); i++) {
    		if (!reg_faults[i]) {
    			reg_faults[i] = fault_id;
    			break;
    		}
    	}
    
    	if (i == sizeof(reg_faults)) {
    		shell_print(shell, "No space to store more registered faults");
    	}
    
    	bt_mesh_fault_update(elem);
    
    	return 0;
    }

    Please let me know if there are any problems.

    Best regards,

    Hieu

Reply
  • Hi Sumedh,

    Our apologies for the long wait. My sickness came at a bad time and coincided with Andreas' out of office period.
    I have now recovered and returned to office and will take over once more.

    A colleague helped me find some clue in the Bluetooth Mesh Shell application and we believe that the codes below serve as sample implementation.
    Could you please try to follow it and see if it works?
    Sorry I haven't been able to verify it before sharing it with you, but I don't want to keep you waiting any longer. 

    // From zephyr/subsys/bluetooth/mesh/shell.c
    
    static uint8_t cur_faults[BT_MESH_SHELL_CUR_FAULTS_MAX];
    static uint8_t reg_faults[BT_MESH_SHELL_CUR_FAULTS_MAX * 2];
    
    static void get_faults(uint8_t *faults, uint8_t faults_size, uint8_t *dst, uint8_t *count)
    {
    	uint8_t i, limit = *count;
    
    	for (i = 0U, *count = 0U; i < faults_size && *count < limit; i++) {
    		if (faults[i]) {
    			*dst++ = faults[i];
    			(*count)++;
    		}
    	}
    }
    
    static int fault_get_cur(struct bt_mesh_model *model, uint8_t *test_id,
    			 uint16_t *company_id, uint8_t *faults, uint8_t *fault_count)
    {
    	shell_print_ctx("Sending current faults");
    
    	*test_id = 0x00;
    	*company_id = BT_COMP_ID_LF;
    
    	get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
    
    	return 0;
    }
    
    static int fault_get_reg(struct bt_mesh_model *model, uint16_t cid,
    			 uint8_t *test_id, uint8_t *faults, uint8_t *fault_count)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		shell_print_ctx("Faults requested for unknown Company ID"
    				" 0x%04x", cid);
    		return -EINVAL;
    	}
    
    	shell_print_ctx("Sending registered faults");
    
    	*test_id = 0x00;
    
    	get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
    
    	return 0;
    }
    
    static int fault_clear(struct bt_mesh_model *model, uint16_t cid)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		return -EINVAL;
    	}
    
    	(void)memset(reg_faults, 0, sizeof(reg_faults));
    
    	return 0;
    }
    
    static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
    		      uint16_t cid)
    {
    	if (cid != CONFIG_BT_COMPANY_ID) {
    		return -EINVAL;
    	}
    
    	if (test_id != 0x00) {
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static const struct bt_mesh_health_srv_cb health_srv_cb = {
    	.fault_get_cur = fault_get_cur,
    	.fault_get_reg = fault_get_reg,
    	.fault_clear = fault_clear,
    	.fault_test = fault_test,
    };
    
    static int cmd_add_fault(const struct shell *shell, size_t argc, char *argv[])
    {
    	uint8_t fault_id;
    	uint8_t i;
    	struct bt_mesh_elem *elem;
    	int err = 0;
    
    	elem = primary_element();
    	if (elem == NULL) {
    		shell_print(shell, "Element not found!");
    		return -EINVAL;
    	}
    
    	fault_id = shell_strtoul(argv[1], 0, &err);
    	if (err) {
    		shell_warn(shell, "Unable to parse input string argument");
    		return err;
    	}
    
    	if (!fault_id) {
    		shell_print(shell, "The Fault ID must be non-zero!");
    		return -EINVAL;
    	}
    
    	for (i = 0U; i < sizeof(cur_faults); i++) {
    		if (!cur_faults[i]) {
    			cur_faults[i] = fault_id;
    			break;
    		}
    	}
    
    	if (i == sizeof(cur_faults)) {
    		shell_print(shell, "Fault array is full. Use \"del-fault\" to "
    			    "clear it");
    		return 0;
    	}
    
    	for (i = 0U; i < sizeof(reg_faults); i++) {
    		if (!reg_faults[i]) {
    			reg_faults[i] = fault_id;
    			break;
    		}
    	}
    
    	if (i == sizeof(reg_faults)) {
    		shell_print(shell, "No space to store more registered faults");
    	}
    
    	bt_mesh_fault_update(elem);
    
    	return 0;
    }

    Please let me know if there are any problems.

    Best regards,

    Hieu

Children
Related