Bluetooth Mesh Sensors

The Bluetooth Mesh specification provides a common scheme for representing all sensors. A single Bluetooth Mesh Sensor instance represents a single physical sensor, and a mesh device may present any number of sensors to the network through a Sensor Server model. Sensors represent their measurements as a list of sensor channels, as described by the sensor’s assigned type.

Sensors are accessed through the Sensor models, which are documented separately:

Basic example

A sensor reporting the device operating temperature could combine the Bluetooth Mesh Present Device Operating Temperature sensor type with the on-chip TEMP_NRF5 temperature sensor driver:

static struct device *dev;

static int temp_get(struct bt_mesh_sensor *sensor,
                    struct bt_mesh_msg_ctx *ctx,
                    struct sensor_value *rsp)
{
    sensor_sample_fetch(dev);
    return sensor_channel_get(dev, SENSOR_CHAN_DIE_TEMP, rsp);
}

struct bt_mesh_sensor temp_sensor = {
    .type = &bt_mesh_sensor_present_dev_op_temp,
    .get = temp_get,
};

void init(void)
{
    dev = device_get_binding(DT_INST_0_NORDIC_NRF_TEMP_LABEL);
}

Additionally, a pointer to the temp_sensor structure should be passed to a Sensor Server to be exposed to the mesh. See Sensor Server for details.

Sensor types

Sensor types are the specification defined data types for the various Bluetooth Mesh sensor parameters. Each sensor type is assigned its own Device Property ID, as specified in the Bluetooth Mesh Device Properties Specification. Like the Device Properties, the Sensor types are connected to a Bluetooth GATT Characteristic, which describes the unit, range, resolution and encoding scheme of the sensor type.

Note

The Bluetooth Mesh specification only allows sensor types that have a Device Property ID in the Bluetooth Mesh Device Properties Specification. It’s not possible to represent vendor specific sensor values.

The sensor types may either be used as the data types of the sensor output values, or as configuration parameters for the sensors.

The Bluetooth Mesh Sensor type API is built to mirror and integrate well with the Zephyr Sensors API. Some concepts in the Bluetooth Mesh Specification are changed slightly to fit better with the Zephyr Sensor API, with focus on making integration as simple as possible.

Sensor Channels

Each sensor type may consist of one or more channels. The list of sensor channels in each sensor type is immutable, and all channels must always have a valid value when the sensor data is passed around. This is slightly different from the sensor type representation in the Bluetooth Mesh Specification, which represents multi-channel sensors as structures, rather than flat lists.

Each channel in a sensor type is represented by a single sensor_value. For sensor values that are represented as whole numbers, the fractional part of the value (sensor_value.val2) is ignored. Boolean types are inferred only from the integer part of the value (sensor_value.val1).

Every sensor channel has a name and a unit, as listed in the sensor type documentation. The name and unit are only available if CONFIG_BT_MESH_SENSOR_LABELS option is set, and can aid in debugging and presentation of the sensor output. Both the channel name and unit is also listed in the documentation for each sensor type.

Most sensor values are reported as scalars with some scaling factor applied to them during encoding. This scaling factor and the encoded data type determines the resolution and range of the sensor data in a specific channel. For instance, if a sensor channel measuring electric current has a resolution of 0.5 Ampere, this is the highest resolution value other mesh devices will be able to read out from the sensor. Before encoding, the sensor values are rounded to their nearest available representation, so the following sensor value would be read as 7.5 Ampere:

/* Sensor value: 7.3123 A */
struct sensor_value electrical_current = {
    .val1 = 7,
    .val2 = 312300, /* 6 digit fraction */
};

Various other encoding schemes are used to represent non-scalars. See the documentation or specification for the individual sensor channels for more details.

Sensor series types

Some sensor types are made specially for being used in a sensor series. These sensor types have one primary channel containing the sensor data and two secondary channels that denote some interval in which the primary channel’s data is captured. Together, the three channels are able to represent historical sensor data as a histogram, and Sensor Client models may request access to specific measurement spans from a Sensor Server model.

The unit of the measurement span is defined by the sensor type, and will typically be a time interval or a range of operational parameters, like temperature or voltage level. For instance, the bt_mesh_sensor_rel_dev_energy_use_in_a_period_of_day sensor type represents the energy used by the device in specific periods of the day. The primary channel of this sensor type measures energy usage in kWh, and the secondary channels denote the timespan in which the specific energy usage was measured. A sensor of this type may be queried for specific measurement periods measured in hours, and should provide the registered energy usage only for the requested time span.

Sensor setting types

Some sensor types are made specifically to act as sensor settings. These values are encoded the same way as other sensor types, but typically represent a configurable sensor setting or some specification value assigned to the sensor from the manufacturer. For instance, the bt_mesh_sensor_motion_threshold sensor type can be used to configure the sensitivity of a sensor reporting motion sensor data (bt_mesh_sensor_motion_sensed).

Typically, settings should only be meta data related to the sensor data type, but the API contains no restrictions for which sensor types can be used for sensor settings.

Available sensor types

All available sensor types are collected in the Bluetooth Mesh sensor types module.

Sample data reporting

Sensors may report their values to the mesh in three ways:

  • Unprompted publications

  • Periodic publication

  • Polling

Unprompted publications may be done at any time, and only includes the sensor data of a single sensor at a time. The application may generate an unprompted publication by calling bt_mesh_sensor_srv_sample(). This triggers the sensor’s bt_mesh_sensor.get callback, and only publishes if the sensor’s Delta threshold is satisfied.

Unprompted publications can also be forced by calling bt_mesh_sensor_srv_pub() directly.

Periodic publication is controlled by the Sensor Server model’s publication parameters, and configured by the Config models. The sensor Server model reports data for all its sensor instances periodically, at a rate determined by the sensors’ cadence. Every publication interval, the Server consolidates a list of sensors to include in the publication, and requests the most recent data from each. The combined data of all these sensors is published as a single message for other nodes in the mesh network.

If no publication parameters are configured for the Sensor Server model, Sensor Client models may poll the most recent sensor samples directly.

All three methods of reporting may be combined.

Cadence

Each sensor may use the cadence state to control the rate at which their data is published. The sensor’s publication interval is defined as a divisor of the holding sensor Server’s publication interval, that is always a power of two. Under normal circumstances, the sensor’s period divisor is always 1, and the sensor only publishes on the Server’s actual publication interval.

All single-channel sensors have a configurable fast cadence range that automatically controls the sensor cadence. If the sensor’s value is within its configured fast cadence range, the sensor engages the period divisor, and starts publishing with fast cadence.

The fast cadence range always starts at the cadence range low value, and spans to the cadence range high value. If the high value is lower than the low value, the effect is inverted, and the sensor operates at high cadence if its value is outside the range.

To prevent sensors from saturating the mesh network, each sensor also defines a minimum publication interval, which is always taken into account when performing the period division.

The period divisor, fast cadence range and minimum interval is configured by a Sensor Client model (through a Sensor Setup Server). The sensor’s cadence is automatically recalculated for every sample, based on its configuration.

Delta threshold

All single channel sensors have a delta threshold state to aid the publication rate. The delta threshold state determines the smallest change in sensor value that should trigger a publication. Whenever a sensor value is published to the mesh network (through periodic publishing or otherwise), the sensor saves the value, and compares it to subsequent samples. Once a sample is sufficiently far away from the previously published value, it gets published.

The delta threshold works on both periodic publication and unprompted publications. If periodic publication is enabled and the minimum interval has expired, the sensor will periodically check whether the delta threshold has been breached, so that it can publish the value on the next periodic interval.

The delta threshold may either be specified as a percent wise change, or as an absolute delta. The percent wise change is always measured relatively to the previously published value, and allows the sensor to automatically scale its threshold to account for relative inaccuracy or noise.

The sensor has separate delta thresholds for positive and negative changes.

Descriptors

Descriptors are optional meta information structures for every sensor. A sensor’s Descriptor contains parameters that may aid other mesh nodes in interpreting the data:

  • Tolerance

  • Sampling function

  • Measurement period

  • Update interval

The sensor descriptor is constant throughout the sensor’s lifetime. If the sensor has a descriptor, a pointer to it should be passed to bt_mesh_sensor.descriptor on init.

See bt_mesh_sensor_descriptor for details.

Usage

Sensors instances are generally static structures that are initialized at startup. Only the bt_mesh_sensor.type member is mandatory, the rest are optional. Apart from the Cadence and Descriptor states, all states are accessed through getter functions. The absence of a getter for a state marks it as not supported by the sensor.

Sensor data

Sensor data is accessed through the bt_mesh_sensor.get callback, which is expected to fill the rsp parameter with the most recent sensor data and return a status code. Each sensor channel will be encoded internally according to the sensor type.

The sensor data in the callback typically comes from a sensor using the Zephyr sensor API. The Zephyr sensor API records samples in two steps:

1. Tell the sensor to take a sample by calling sensor_sample_fetch(). 2. Read the recorded sample data with sensor_channel_get().

The first step may be done at any time. Typically, the sensor fetching is triggered by a timer, an external event or a sensor trigger, but it may be called in the get callback itself. Note that the get callback requires an immediate response, so if the sample fetching takes a significant amount of time, it should generally be done asynchronously. The method of sampling may be communicated to other mesh nodes through the sensor’s descriptor.

The read step would typically be done in the callback, to pass the sensor data to the mesh.

If the Sensor Server is configured to do periodic publishing, the get callback will be called for every publication interval. Publication may also be forced by calling bt_mesh_sensor_srv_sample(), which will trigger the get callback and publish only if the sensor value has changed.

Sensor series

Sensor series data is organized into a static set of columns, specified at init. The sensor series bt_mesh_sensor_series.get callback must be implemented to enable the sensor’s series data feature. Only some sensor types support series access, see the sensor type’s documentation. The format of the column may be queried with bt_mesh_sensor_column_format_get().

The get callback gets called with a direct pointer to one of the columns in the column list, and is expected to fill the value parameter with sensor data for the specified column. If a Sensor Client requests a series of columns, the callback may be called repeatedly, requesting data from each column.

Example: Average ambient temperature in a period of day as a sensor series:

/* 4 columns representing different hours in a day */
static const struct bt_mesh_sensor_column columns[] = {
    {{0}, {6}},
    {{6}, {12}},
    {{12}, {18}},
    {{18}, {24}},
};

static struct bt_mesh_sensor temp_sensor = {
    .type = &bt_mesh_sensor_avg_amb_temp_in_day,
    .series = {
        columns,
        ARRAY_SIZE(columns),
        getter,
    },
};

/** Sensor data is divided into columns and filled elsewhere */
static struct sensor_value avg_temp[ARRAY_SIZE(columns)];

static int getter(struct bt_mesh_sensor *sensor, struct bt_mesh_msg_ctx *ctx,
                              const struct bt_mesh_sensor_column *column,
                              struct sensor_value *value)
{
    /* The column pointer is always a direct pointer to one of our columns,
     *  so determining the column index is easy:
     */
    uint32_t index = column - &columns[0];

    value[0] = avg_temp[index];
    value[1] = column->start;
    value[2] = column->end;

    return 0;
}

Sensor settings

The list of settings a sensor supports should be set on init. The list should be constant throughout the sensor’s lifetime, and may be declared const. Each entry in the list has a type and two access callbacks, and the list should only contain unique entry types.

The bt_mesh_sensor_setting.get callback is mandatory, while the bt_mesh_sensor_setting.set is optional, allowing for read-only entries. The value of the settings may change at runtime, even outside the set callback. New values may be rejected by returning a negative error code from the set callback.

API documentation

Header file: include/bluetooth/mesh/sensor.h
Source file: subsys/bluetooth/mesh/sensor.c
group bt_mesh_sensor

API for Bluetooth Mesh Sensors.

Defines

BT_MESH_SENSOR_PERIOD_DIV_MAX

Largest period divisor value allowed.

BT_MESH_SENSOR_INTERVAL_MAX

Largest sensor interval allowed (in seconds).

BT_MESH_SENSOR_CH_STR_LEN

String length for representing a single sensor channel.

BT_MESH_SENSOR_TYPE_FLAG_SERIES

Flag indicating this sensor type has a series representation.

bt_mesh_sensor_ch_str(ch)

String duplication variant of bt_mesh_sensor_ch_str_real for use in logging.

Parameters
  • [in] ch: Sensor channel to represent.

Enums

enum bt_mesh_sensor_sampling

Sensor sampling type.

Represents the sampling function used to produce the presented sensor value.

Values:

enumerator BT_MESH_SENSOR_SAMPLING_UNSPECIFIED

The sampling function is unspecified

enumerator BT_MESH_SENSOR_SAMPLING_INSTANTANEOUS

The presented value is an instantaneous sample.

enumerator BT_MESH_SENSOR_SAMPLING_ARITHMETIC_MEAN

The presented value is the arithmetic mean of multiple samples.

enumerator BT_MESH_SENSOR_SAMPLING_RMS

The presented value is the root mean square of multiple samples.

enumerator BT_MESH_SENSOR_SAMPLING_MAXIMUM

The presented value is the maximum of multiple samples.

enumerator BT_MESH_SENSOR_SAMPLING_MINIMUM

The presented value is the minimum of multiple samples.

enumerator BT_MESH_SENSOR_SAMPLING_ACCUMULATED

The presented value is the accumulated moving average value of the samples. The updating frequency of the moving average should be indicated in bt_mesh_descriptor::update_interval. The total measurement period should be indicated in bt_mesh_descriptor::period.

enumerator BT_MESH_SENSOR_SAMPLING_COUNT

The presented value is a count of events in a specific measurement period. bt_mesh_descriptor::period should denote the measurement period, or left to 0 to indicate that the sample is a lifetime value.

enum bt_mesh_sensor_delta

Delta threshold type.

Values:

enumerator BT_MESH_SENSOR_DELTA_VALUE

Value based delta threshold. The delta threshold values are represented as absolute value changes.

enumerator BT_MESH_SENSOR_DELTA_PERCENT

Percent based delta threshold. The delta threshold values are represented as percentages of their old value (resolution: 0.01 %).

enumerator BT_MESH_SENSOR_DELTA_DISABLED

Delta threshold is disabled.

enum bt_mesh_sensor_cadence

Sensor sampling cadence

Values:

enumerator BT_MESH_SENSOR_CADENCE_NORMAL

Normal sensor publish interval.

enumerator BT_MESH_SENSOR_CADENCE_FAST

Fast sensor publish interval.

Functions

bool bt_mesh_sensor_delta_threshold(const struct bt_mesh_sensor *sensor, const struct sensor_value *value)

Check if a value change breaks the delta threshold.

Sensors should publish their value if the measured sample is outside the delta threshold compared to the previously published value. This function checks the threshold and the previously published value for this sensor, and returns whether the sensor should publish its value.

Note

Only single-channel sensors support cadence. Multi-channel sensors are always considered out of their threshold range, and will always return true from this function. Single-channel sensors that haven’t been assigned a threshold will return true if the value is different.

Return

true if the difference between the measurements exceeds the delta threshold, false otherwise.

Parameters
  • [in] sensor: The sensor instance.

  • [in] value: Sensor value.

const struct bt_mesh_sensor_type *bt_mesh_sensor_type_get(uint16_t id)

Get the sensor type associated with the given Device Property ID.

Only known sensor types from Sensor types will be available. Sensor types can be made known to the sensor module by enabling CONFIG_BT_MESH_SENSOR_ALL_TYPES or by referencing them in the application.

Return

The associated sensor type, or NULL if the ID is unknown.

Parameters
  • [in] id: A Device Property ID.

bool bt_mesh_sensor_value_in_column(const struct sensor_value *value, const struct bt_mesh_sensor_column *col)

Check whether a single channel sensor value lies within a column.

Return

true if the value belongs in the column, false otherwise.

Parameters
  • [in] value: Value to check. Only the first channel is considered.

  • [in] col: Sensor column.

const struct bt_mesh_sensor_format *bt_mesh_sensor_column_format_get(const struct bt_mesh_sensor_type *type)

Get the format of the sensor column data.

Return

The sensor type’s sensor column format if series access is supported. Otherwise NULL.

Parameters
  • [in] type: Sensor type.

int bt_mesh_sensor_ch_to_str(const struct sensor_value *ch, char *str, size_t len)

Get a human readable representation of a single sensor channel.

Return

Number of bytes that should have been written if str is sufficiently large.

Parameters
  • [in] ch: Sensor channel to represent.

  • [out] str: String buffer to fill. Should be BT_MESH_SENSOR_CH_STR_LEN bytes long.

  • [in] len: Length of str buffer.

const char *bt_mesh_sensor_ch_str_real(const struct sensor_value *ch)

Get a human readable representation of a single sensor channel.

Note

This function is not thread safe.

Return

A string representing the sensor channel.

Parameters
  • [in] ch: Sensor channel to represent.

struct bt_mesh_sensor_descriptor
#include <sensor.h>

Sensor descriptor representing various static metadata for the sensor.

Public Members

struct sensor_value positive

Maximum positive measurement error (in percent). A tolerance of 0 should be interpreted as “unspecified”.

struct sensor_value negative

Maximum negative measurement error (in percent). A tolerance of 0 should be interpreted as “unspecified”.

struct bt_mesh_sensor_descriptor.[anonymous] tolerance

Sensor measurement tolerance specification.

enum bt_mesh_sensor_sampling sampling_type

Sampling type for the sensor data.

uint64_t period

Measurement period for the samples, if applicable.

uint64_t update_interval

Update interval for the samples, if applicable.

struct bt_mesh_sensor_threshold
#include <sensor.h>

Sensor thresholds for publishing.

Public Members

enum bt_mesh_sensor_delta type

Type of delta threshold

struct sensor_value up

Minimal delta for a positive change.

struct sensor_value down

Minimal delta for a negative change.

struct bt_mesh_sensor_threshold.[anonymous] delta

Delta threshold values.

Denotes the minimal sensor value change that should cause the sensor to publish its value.

enum bt_mesh_sensor_cadence cadence

Cadence when the sensor value is inside the range.

If the cadence is fast when the value is inside the range, it’s normal when it’s outside the range. If the cadence is normal when the value is inside the range, it’s fast outside the range.

struct sensor_value low

Lower boundary for the range based sensor cadence threshold.

struct sensor_value high

Upper boundary for the range based sensor cadence threshold.

struct bt_mesh_sensor_threshold.[anonymous] range

Range based threshold values.

Denotes the value range in which the sensor should be in fast cadence mode.

struct bt_mesh_sensor_unit
#include <sensor.h>

Unit for single sensor channel values.

Public Members

const char *name

English name of the unit, e.g. “Decibel”.

const char *symbol

Symbol of the unit, e.g. “dB”.

struct bt_mesh_sensor_format
#include <sensor.h>

Sensor channel value format.

Public Members

int (*const encode)(const struct bt_mesh_sensor_format *format, const struct sensor_value *val, struct net_buf_simple *buf)

Sensor channel value encode function.

Return

0 on success, or (negative) error code otherwise.

Parameters
  • [in] format: Pointer to the format structure.

  • [in] val: Sensor channel value to encode.

  • [out] buf: Buffer to encode the value into.

int (*const decode)(const struct bt_mesh_sensor_format *format, struct net_buf_simple *buf, struct sensor_value *val)

Sensor channel value decode function.

Return

0 on success, or (negative) error code otherwise.

Parameters
  • [in] format: Pointer to the format structure.

  • [in] buf: Buffer to decode the value from.

  • [out] val: Resulting sensor channel value.

void *user_data

User data pointer.

size_t size

Size of the encoded data in bytes.

struct bt_mesh_sensor_channel
#include <sensor.h>

Signle sensor channel

Public Members

const struct bt_mesh_sensor_format *format

Format for this sensor channel.

struct bt_mesh_sensor_type
#include <sensor.h>

Sensor type. Should only be instantiated in sensor_types.c. See sensor_types.h for a list of all defined sensor types.

Public Members

uint16_t id

Device Property ID.

uint8_t flags

Flags,

See

BT_MESH_SENSOR_TYPE_FLAG_SERIES

uint8_t channel_count

The number of channels supported by this type.

const struct bt_mesh_sensor_channel *channels

Array of channel descriptors.

All channels are mandatory and immutable.

struct bt_mesh_sensor_setting
#include <sensor.h>

Single sensor setting.

Public Members

const struct bt_mesh_sensor_type *type

Sensor type of this setting.

void (*get)(struct bt_mesh_sensor *sensor, const struct bt_mesh_sensor_setting *setting, struct bt_mesh_msg_ctx *ctx, struct sensor_value *rsp)

Getter for this sensor setting.

Note

This handler is mandatory.

Parameters
  • [in] sensor: Sensor this setting belongs to.

  • [in] setting: Pointer to this setting structure.

  • [in] ctx: Context parameters for the packet this call originated from, or NULL if this call wasn’t triggered by a packet.

  • [out] rsp: Response buffer for the setting value. Points to an array with the number of channels specified by the setting sensor type. All channels must be filled.

int (*set)(struct bt_mesh_sensor *sensor, const struct bt_mesh_sensor_setting *setting, struct bt_mesh_msg_ctx *ctx, const struct sensor_value *value)

Setter for this sensor setting.

Should only be specified for writable sensor settings.

Return

0 on success, or (negative) error code otherwise.

Parameters
  • [in] sensor: Sensor this setting belongs to.

  • [in] setting: Pointer to this setting structure.

  • [in] ctx: Context parameters for the packet this call originated from, or NULL if this call wasn’t triggered by a packet.

  • [in] value: New setting value. Contains the number of channels specified by the setting sensor type.

struct bt_mesh_sensor_column
#include <sensor.h>

Single sensor series data column.

The series data columns represent a range for specific measurement values, inside which a set of sensor measurements were made. The range is interpreted as a half-open interval (i.e. start <= value < end).

Note

Contrary to the Bluetooth Mesh specification, the column has an end value instead of a width, to match the conventional property format. This reduces implementation complexity for sensor series values that include the start and end (or min and max) of the measurement range, as the value of the column can be copied directly into the corresponding channels.

Public Members

struct sensor_value start

Start of the column (inclusive).

struct sensor_value end

End of the column (exclusive).

struct bt_mesh_sensor_series
#include <sensor.h>

Sensor series specification.

Public Members

const struct bt_mesh_sensor_column *columns

Pointer to the list of columns.

The columns may overlap, but the start value of each column must be unique. The list of columns do not have to cover the entire valid range, and values that don’t fit in any of the columns should be ignored. If columns overlap, samples must be present in all columns they fall into. The columns may come in any order.

uint32_t column_count

Number of columns.

int (*get)(struct bt_mesh_sensor *sensor, struct bt_mesh_msg_ctx *ctx, const struct bt_mesh_sensor_column *column, struct sensor_value *value)

Getter for the series values.

Should return the historical data for the latest sensor readings in the given column.

Return

0 on success, or (negative) error code otherwise.

Parameters
  • [in] sensor: Sensor pointer.

  • [in] ctx: Message context pointer, or NULL if this call didn’t originate from a mesh message.

  • [in] column: The requested sensor column. Points to a column in the columns array.

  • [out] value: Sensor value response buffer. Holds the number of channels indicated by the sensor type. All channels must be filled.

struct bt_mesh_sensor
#include <sensor.h>

Sensor instance.

Public Members

const struct bt_mesh_sensor_type *type

Sensor type.

Must be one of the specification defined types listed in Sensor types.

const struct bt_mesh_sensor_descriptor *descriptor

Optional sensor descriptor.

const struct bt_mesh_sensor_setting *list

Static array of sensor settings

size_t count

Number of sensor settings.

const struct bt_mesh_sensor.[anonymous] settings

Sensor settings access specification.

const struct bt_mesh_sensor_series series

Sensor series specification.

Only sensors whose type have the BT_MESH_SENSOR_TYPE_FLAG_SERIES flag set, a non-empty list of columns and a defined series getter will accept series messages.

int (*const get)(struct bt_mesh_sensor *sensor, struct bt_mesh_msg_ctx *ctx, struct sensor_value *rsp)

Getter function for the sensor value.

Return

0 on success, or (negative) error code otherwise.

Parameters
  • [in] sensor: Sensor instance.

  • [in] ctx: Message context, or NULL if the call wasn’t triggered by a mesh message.

  • [out] rsp: Value response buffer. Fits the number of channels specified by the sensor type. All channels must be filled.

struct bt_mesh_sensor_threshold threshold

Sensor threshold specification.

sys_snode_t node

Linked list node.

struct sensor_value prev

The previously published sensor value.

uint16_t seq

Sequence number of the previous publication.

uint8_t min_int

Minimum possible interval for fast cadence value publishing in seconds.

See

BT_MESH_SENSOR_INTERVAL_MAX

uint8_t pub_div

Fast period divisor used when publishing with fast cadence.

uint8_t fast_pub

Flag indicating whether the sensor is in fast cadence mode.