Send all sensor data simultaneously?

I am using BLE mesh in nrf52840. I have modified the sensor server sample and sensor client sample to suit my requirements. I am using toolchain and sdk v2.6.1. It is working properly, but i would like to do a few improvements and I don't know how and would like some help. 

1. The first thing is that it is being used in an environment with lots of interference, so there is data loss. I am using a temperature and humidity sensor. The problem is that I am currently sending temperature and humidity separately. If i loose both temperature and humidity those can be ignored. But sometimes only temperature or only humidity is published. I reduced the get data interval quick (in the sample) to 0 so that i can get both temp and humidity at the same time. But there are times when i get only temperature or only humidity, I want a solution to send both temperature and humidity at the same time. After all it is the same sensor that is reading both these values.

 2. If there are multiple sensor servers connected to one sensor client then even if one sensor server responds 

static void get_data(struct k_work *work)
{
	if (!bt_mesh_is_provisioned()) {
		k_work_schedule(&get_data_work, K_MSEC(GET_DATA_INTERVAL));
		return;
	}

	static uint32_t sensor_idx;
	int err;
	
	/* Only one message can be published at a time. Swap sensor after each timeout. */
	switch (sensor_idx++) {
		case (0): {
			err = bt_mesh_sensor_cli_get(
				&sensor_cli, NULL, &bt_mesh_sensor_precise_present_amb_temp,
				NULL);
			if (err) {
				printk("Error getting Ambient temperature (%d)\n", err);
			}
			break;
		}
		case (1): {
			err = bt_mesh_sensor_cli_get(
				&sensor_cli, NULL, &bt_mesh_sensor_present_amb_rel_humidity,
				NULL);
			if (err) {
				printk("Error getting humidity data (%d)\n", err);
			}
			break;
		}
	}

	if (sensor_idx % 2) {
		k_work_schedule(&get_data_work, K_NO_WAIT);
	} else {
		k_work_schedule(&get_data_work, K_MSEC(GET_DATA_INTERVAL));
		sensor_idx = 0;
	}
}
 

the error message is not printed. How do I make it so that error message is printed if even one node doesn't send data

Parents
  • Hello,

    Sorry for the late reply.

    I don't know what sort of mesh models you are using, but perhaps you can combine the temperature and humidity values in the same model (e.g. first two bytes for temperature and two more for moisture)?

    Or do you need to follow some standard where they are separate?

    Best regards,

    Edvin

  • (e.g. first two bytes for temperature and two more for moisture) I wanted to do this, so i have to choose a type that supports 32bits like &bt_mesh_sensor_pressure or &bt_mesh_sensor_precise_tot_dev_energy_use? and change the get function that it? Im attchiing the model_handler.c.

    Also can you pls tell me if there is any change i can make to reduce data loss.

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/drivers/sensor.h>
    #include <bluetooth/mesh/models.h>
    #include <bluetooth/mesh/sensor_types.h>
    #include <dk_buttons_and_leds.h>
    #include <float.h>
    #include "model_handler.h"
    #include "temp_read.h"
    
    #if DT_NODE_HAS_STATUS(DT_NODELABEL(bme680), okay)
    /** Thingy53 */
    #define SENSOR_NODE DT_NODELABEL(bme680)
    #define SENSOR_DATA_TYPE SENSOR_CHAN_AMBIENT_TEMP
    #elif DT_NODE_HAS_STATUS(DT_NODELABEL(temp), okay)
    /** nRF52 DK */
    #define SENSOR_NODE DT_NODELABEL(temp)
    #else
    #error "Unsupported board!"
    #endif
    
    #define LED0_NODE DT_ALIAS(led0)
    #define BLINK_TIME_MS 500
    
    static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    #define TEMP_INIT(_val) { .format = &bt_mesh_sensor_format_temp, .raw = {      \
    	FIELD_GET(GENMASK(7, 0), (int16_t)(_val) * 100),                                \
    	FIELD_GET(GENMASK(15, 8), (int16_t)(_val) * 100)                                \
    }}
    
    #define HUMIDITY_INIT(_val) { .format = &bt_mesh_sensor_format_percentage_16, .raw = {      \
    	FIELD_GET(GENMASK(7, 0), (_val) * 100),                                \
    	FIELD_GET(GENMASK(15, 8), (_val) * 100)                                \
    }}
    
    #define COL_INIT(_start, _width) { TEMP_INIT(_start), TEMP_INIT(_width) }
    
    struct sensor_value_range {
    	struct bt_mesh_sensor_value start;
    	struct bt_mesh_sensor_value end;
    };
    
    #define DEFAULT_TEMP_RANGE_LOW -50
    #define DEFAULT_TEMP_RANGE_HIGH 100
    /* Range limiting the reported values of the chip temperature */
    static struct sensor_value_range temp_range = {
    	TEMP_INIT(DEFAULT_TEMP_RANGE_LOW),
    	TEMP_INIT(DEFAULT_TEMP_RANGE_HIGH),
    };
    
    #define DEFAULT_HUMIDITY_RANGE_LOW 0
    #define DEFAULT_HUMIDITY_RANGE_HIGH 100
    /* Range limiting the reported values of the chip temperature */
    static struct sensor_value_range humid_range = {
    	HUMIDITY_INIT(DEFAULT_HUMIDITY_RANGE_LOW),
    	HUMIDITY_INIT(DEFAULT_HUMIDITY_RANGE_HIGH),
    };
    
    //temp sensor
    static const struct device *I2C_reader = I2C_DEV;
    
    
    #if IS_ENABLED(CONFIG_BT_MESH_NLC_PERF_CONF)
    static const uint8_t cmp2_elem_offset1[1] = { 0 };
    static const uint8_t cmp2_elem_offset2[1] = { 1 };
    
    static const struct bt_mesh_comp2_record comp_rec[2] = {
    	{
    	.id = BT_MESH_NLC_PROFILE_ID_AMBIENT_LIGHT_SENSOR,
    	.version.x = 1,
    	.version.y = 0,
    	.version.z = 0,
    	.elem_offset_cnt = 1,
    	.elem_offset = cmp2_elem_offset1,
    	.data_len = 0
    	},
    	{
    	.id = BT_MESH_NLC_PROFILE_ID_OCCUPANCY_SENSOR,
    	.version.x = 1,
    	.version.y = 0,
    	.version.z = 0,
    	.elem_offset_cnt = 1,
    	.elem_offset = cmp2_elem_offset2,
    	.data_len = 0
    	}
    };
    
    static const struct bt_mesh_comp2 comp_p2 = {
    	.record_cnt = 2,
    	.record = comp_rec
    };
    #endif
    
    /*
    Add function to read temp sensor data from board
    */
    static int amb_temp_sensor_get(struct bt_mesh_sensor_srv *srv,
    			 struct bt_mesh_sensor *sensor,
    			 struct bt_mesh_msg_ctx *ctx,
    			 struct bt_mesh_sensor_value *rsp)
    {
    	struct sensor_value temperature;
    
    	int err = read_sensor_data(&temperature, AONE_TEMP);
    
    	if (err) {
    		printk("Error getting temperature sensor data (%d)\n", err);
    	}
    
    	err = bt_mesh_sensor_value_from_sensor_value(
    		sensor->type->channels[0].format, &temperature, rsp);
    	if (err) {
    		printk("Error encoding temperature sensor data (%d)\n", err);
    	}
    
    	if (!BT_MESH_SENSOR_VALUE_IN_RANGE(rsp, &temp_range.start, &temp_range.end)) {
    		printk("Outside temp range");
    	}
    
    	return err;
    }
    
    /*
    Add function to read Humididty sensor data from board
    */
    static int amb_humidity_sensor_get(struct bt_mesh_sensor_srv *srv,
    			 struct bt_mesh_sensor *sensor,
    			 struct bt_mesh_msg_ctx *ctx,
    			 struct bt_mesh_sensor_value *rsp)
    {
    	struct sensor_value humidity;
    
    	int err = read_sensor_data(&humidity, AONE_HUMIDITY); 
    
    	if (err) {
    		printk("Error getting Humidity sensor data (%d)\n", err);
    	}
    
    	err = bt_mesh_sensor_value_from_sensor_value(
    		sensor->type->channels[0].format, &humidity, rsp);
    	if (err) {
    		printk("Error encoding humidity sensor data (%d)\n", err);
    	}
    
    	if (!BT_MESH_SENSOR_VALUE_IN_RANGE(rsp, &humid_range.start, &humid_range.end)) {
    		printk("Outside humidity range");
    	}
    
    	return err;
    }
    
    static int amb_temp_range_settings_restore(const char *name, size_t len, settings_read_cb read_cb,
    					    void *cb_arg)
    {
    	const char *next;
    	int rc;
    
    	if (!(settings_name_steq(name, "range", &next) && !next)) {
    		return -ENOENT;
    	}
    
    	if (len != sizeof(temp_range)) {
    		return -EINVAL;
    	}
    
    	rc = read_cb(cb_arg, &temp_range, sizeof(temp_range));
    	if (rc < 0) {
    		return rc;
    	}
    
    	printk("Restored temperature range setting\n");
    	return 0;
    }
    
    static int amb_humidity_range_settings_restore(const char *name, size_t len, settings_read_cb read_cb,
    					    void *cb_arg)
    {
    	const char *next;
    	int rc;
    
    	if (!(settings_name_steq(name, "range", &next) && !next)) {
    		return -ENOENT;
    	}
    
    	if (len != sizeof(humid_range)) {
    		return -EINVAL;
    	}
    
    	rc = read_cb(cb_arg, &humid_range, sizeof(humid_range));
    	if (rc < 0) {
    		return rc;
    	}
    
    	printk("Restored Humidity range setting\n");
    	return 0;
    }
    
    struct settings_handler temp_range_conf = { .name = "temp",
    					    .h_set = amb_temp_range_settings_restore };
    
    struct settings_handler amb_humidity_range_conf = { .name = "humidity",
    					    .h_set = amb_humidity_range_settings_restore };
    
    /* Tolerance is based on the temperature sensor's accuracy and range (deviation/range=5/125 = 4%). */
    static const struct bt_mesh_sensor_descriptor amb_temp_sensor_descriptor = {
    	.tolerance = {
    		.negative = BT_MESH_SENSOR_TOLERANCE_ENCODE(4),
    		.positive = BT_MESH_SENSOR_TOLERANCE_ENCODE(4),
    	},
    	.sampling_type = BT_MESH_SENSOR_SAMPLING_INSTANTANEOUS,
    };
    
    static const struct bt_mesh_sensor_descriptor amb_humidity_sensor_descriptor = {
    	.tolerance = {
    		.negative = BT_MESH_SENSOR_TOLERANCE_ENCODE(4),
    		.positive = BT_MESH_SENSOR_TOLERANCE_ENCODE(4),
    	},
    	.sampling_type = BT_MESH_SENSOR_SAMPLING_INSTANTANEOUS,
    };
    
    static void temp_sensor_range_get(struct bt_mesh_sensor_srv *srv, struct bt_mesh_sensor *sensor,
    				const struct bt_mesh_sensor_setting *setting,
    				struct bt_mesh_msg_ctx *ctx, struct bt_mesh_sensor_value *rsp)
    {
    	rsp[0] = temp_range.start;
    	rsp[1] = temp_range.end;
    	printk("Temperature sensor lower limit: %s\n", bt_mesh_sensor_ch_str(&rsp[0]));
    	printk("Temperature sensor upper limit: %s\n", bt_mesh_sensor_ch_str(&rsp[1]));
    }
    
    static int temp_sensor_range_set(struct bt_mesh_sensor_srv *srv, struct bt_mesh_sensor *sensor,
    			       const struct bt_mesh_sensor_setting *setting,
    			       struct bt_mesh_msg_ctx *ctx,
    			       const struct bt_mesh_sensor_value *value)
    {
    	temp_range.start = value[0];
    	temp_range.end = value[1];
    	printk("Temperature sensor lower limit: %s\n", bt_mesh_sensor_ch_str(&value[0]));
    	printk("Temperature sensor upper limit: %s\n", bt_mesh_sensor_ch_str(&value[1]));
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		int err;
    
    		err = settings_save_one("temp/range", &temp_range, sizeof(temp_range));
    		if (err) {
    			printk("Error storing setting (%d)\n", err);
    		} else {
    			printk("Stored setting\n");
    		}
    	}
    	return 0;
    }
    
    static void humid_sensor_range_get(struct bt_mesh_sensor_srv *srv, struct bt_mesh_sensor *sensor,
    				const struct bt_mesh_sensor_setting *setting,
    				struct bt_mesh_msg_ctx *ctx, struct bt_mesh_sensor_value *rsp)
    {
    	rsp[0] = humid_range.start;
    	rsp[1] = humid_range.end;
    	printk("Humidity sensor lower limit: %s\n", bt_mesh_sensor_ch_str(&rsp[0]));
    	printk("Humidity sensor upper limit: %s\n", bt_mesh_sensor_ch_str(&rsp[1]));
    }
    
    static int humid_sensor_range_set(struct bt_mesh_sensor_srv *srv, struct bt_mesh_sensor *sensor,
    			       const struct bt_mesh_sensor_setting *setting,
    			       struct bt_mesh_msg_ctx *ctx,
    			       const struct bt_mesh_sensor_value *value)
    {
    	humid_range.start = value[0];
    	humid_range.end = value[1];
    	printk("Humidity sensor lower limit: %s\n", bt_mesh_sensor_ch_str(&value[0]));
    	printk("Humidity sensor upper limit: %s\n", bt_mesh_sensor_ch_str(&value[1]));
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		int err;
    
    		err = settings_save_one("humidity/range", &humid_range, sizeof(humid_range));
    		if (err) {
    			printk("Error storing setting (%d)\n", err);
    		} else {
    			printk("Stored setting\n");
    		}
    	}
    	return 0;
    }
    
    static struct bt_mesh_sensor_setting amb_temp_sensor_setting[] = { {
    	.type = &bt_mesh_sensor_dev_op_temp_range_spec,
    	.get = temp_sensor_range_get,
    	.set = temp_sensor_range_set,
    } };
    
    static struct bt_mesh_sensor_setting amb_humidity_sensor_setting[] = { {
    	.type = &bt_mesh_sensor_present_amb_rel_humidity,
    	.get = humid_sensor_range_get,
    	.set = humid_sensor_range_set,
    } };
    
    static struct bt_mesh_sensor amb_temp_sensor_struct = {
    	.type = &bt_mesh_sensor_precise_present_amb_temp,
    	.get = amb_temp_sensor_get,
    	.descriptor = &amb_temp_sensor_descriptor,
    	.settings = {
    		.list = (const struct bt_mesh_sensor_setting *)&amb_temp_sensor_setting,
    		.count = ARRAY_SIZE(amb_temp_sensor_setting),
    	},
    };
    
    static struct bt_mesh_sensor amb_humidity_sensor_struct = {
    	.type = &bt_mesh_sensor_present_amb_rel_humidity,
    	.get = amb_humidity_sensor_get,
    	.descriptor = &amb_humidity_sensor_descriptor,
    	.settings = {
    		.list = (const struct bt_mesh_sensor_setting *)&amb_humidity_sensor_setting,
    		.count = ARRAY_SIZE(amb_humidity_sensor_setting),
    	},
    };						
    
    static struct bt_mesh_sensor *const amb_temp_sensor[] = {
    	&amb_temp_sensor_struct,
    	&amb_humidity_sensor_struct,
    };
    
    //Add temp_sensor service to server
    static struct bt_mesh_sensor_srv ambient_temp_sensor_srv =
    	BT_MESH_SENSOR_SRV_INIT(amb_temp_sensor, ARRAY_SIZE(amb_temp_sensor));
    
    #pragma region attendion and health
    static void button_handler_cb(uint32_t pressed, uint32_t changed)
    {
    	if (!bt_mesh_is_provisioned()) {
    		return;
    	}
    }
    
    static struct button_handler button_handler = {
    	.cb = button_handler_cb,
    };
    
    /* 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(const struct bt_mesh_model *mod)
    {
    	attention = true;
    	k_work_reschedule(&attention_blink_work, K_NO_WAIT);
    }
    
    static void attention_off(const 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);
    #pragma endregion
    
    static struct bt_mesh_elem elements[] = {
    	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_SENSOR_SRV(&ambient_temp_sensor_srv)),
    		     BT_MESH_MODEL_NONE),
    };
    
    static const struct bt_mesh_comp comp = {
    	.cid = CONFIG_BT_COMPANY_ID,
    	.elem = elements,
    	.elem_count = ARRAY_SIZE(elements),
    };
    
    // Initialize the mesh models and services
    const struct bt_mesh_comp *model_handler_init(void)
    {
    #if IS_ENABLED(CONFIG_BT_MESH_NLC_PERF_CONF)
    	if (bt_mesh_comp2_register(&comp_p2)) {
    		printf("Failed to register comp2\n");
    	}
    #endif
    
    	if (!gpio_is_ready_dt(&led0)) {
    		printk("LED not ready\n");
    	}
    
    	int err = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE);
    	if (err < 0) {
    		printk("LED not working\n");
    	}
    
    	// k_work_init_delayable(&attention_blink_work, attention_blink);
    
    	if (!device_is_ready(I2C_reader)) {
    		printk("sensor not ready\n");
    	} else {
    		printk("sensor (%s) initiated\n", I2C_reader->name);
    	}
    
    	// dk_button_handler_add(&button_handler);
    
    	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
    		settings_subsys_init();
    		settings_register(&temp_range_conf);
    		settings_register(&amb_humidity_range_conf);
    	}
    
    	return &comp;
    }

    . Also i wanted to add DFU to my project. But i cannot figure out how to work the DFU distributer and DFU target examples. I have generated the dfu_application.zip in dfu target but im not able to upload this zip file to dfu distributer it says magic error 33 or 20 (i forgot cant test in rn cause im working on something else for sometime), if i upload app_update.bin to DFU distributer then press reset nothing happens. Do i have to add publish and subscribe grps in DFU target and distributer? but this was not mentioned in the sample description it only told to bind the application key 1. What im I doing wrong?

    P.S pls note that i dont really know much about these stuff. I have merely changed the get function (to get sensor value) in the sensor server example to suit my needs and deleted the extra elements

    edit: i just saw that there is a funtion to extend models, can i extend the temperature sensor model with humidity sensor model to get both data together then?

  • I would say it is probably cleaner to use a custom model/type. You can refer to the sensor_server sample, which uses 4 bytes to store a sensor value. Alternatively, you can look into the chat sample, that can store several characters in a string.

    Regarding DFU, I suggest you read through the DFU overview in our documentation, just to get an idea of the flow.

    Then, read the testing description of the DFU distributor sample, as it has a guide like approach to how to do this. If it doesn't work, please let me know where the behavior differs from the distributor description. 

    Best regards,

    Edvin

Reply Children
No Data
Related