Sensors¶
The sensor subsystem exposes an API to uniformly access sensor devices. Common operations are: reading data and executing code when specific conditions are met.
Basic Operation¶
Channels¶
Fundamentally, a channel is a quantity that a sensor device can measure.
Sensors can have multiple channels, either to represent different axes of the same physical property (e.g. acceleration); or because they can measure different properties altogether (ambient temperature, pressure and humidity). Complex sensors cover both cases, so a single device can expose three acceleration channels and a temperature one.
It is imperative that all sensors that support a given channel express results in the same unit of measurement. Consult the API Reference for all supported channels, along with their description and units of measurement:
Values¶
Sensor devices return results as struct sensor_value
. This
representation avoids use of floating point values as they may not be
supported on certain setups.
Fetching Values¶
Getting a reading from a sensor requires two operations. First, an
application instructs the driver to fetch a sample of all its channels.
Then, individual channels may be read. In the case of channels with
multiple axes, they can be read in a single operation by supplying
the corresponding _XYZ
channel type and a buffer of 3
struct sensor_value
objects. This approach ensures consistency
of channels between reads and efficiency of communication by issuing a
single transaction on the underlying bus.
Below is an example illustrating the usage of the BME280 sensor, which
measures ambient temperature and atmospheric pressure. Note that
sensor_sample_fetch()
is only called once, as it reads and
compensates data for both channels.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | {
struct device *dev = device_get_binding("BME280");
if (dev == NULL) {
printk("Could not get BME280 device\n");
return;
}
printk("dev %p name %s\n", dev, dev->config->name);
while (1) {
struct sensor_value temp, press, humidity;
sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);
sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity);
printk("temp: %d.%06d; press: %d.%06d; humidity: %d.%06d\n",
temp.val1, temp.val2, press.val1, press.val2,
humidity.val1, humidity.val2);
k_sleep(K_MSEC(1000));
}
}
|
The example assumes that the returned values have type struct
sensor_value
, which is the case for BME280. A real application
supporting multiple sensors should inspect the type
field of
the temp
and press
values and use the other fields
of the structure accordingly.
Configuration and Attributes¶
Setting the communication bus and address is considered the most basic configuration for sensor devices. This setting is done at compile time, via the configuration menu. If the sensor supports interrupts, the interrupt lines and triggering parameters described below are also configured at compile time.
Alongside these communication parameters, sensor chips typically expose multiple parameters that control the accuracy and frequency of measurement. In compliance with Zephyr’s design goals, most of these values are statically configured at compile time.
However, certain parameters could require runtime configuration, for example, threshold values for interrupts. These values are configured via attributes. The example in the following section showcases a sensor with an interrupt line that is triggered when the temperature crosses a threshold. The threshold is configured at runtime using an attribute.
Triggers¶
Triggers in Zephyr refer to the interrupt lines of the sensor chips. Many sensor chips support one or more triggers. Some examples of triggers include: new data is ready for reading, a channel value has crossed a threshold, or the device has sensed motion.
To configure a trigger, an application needs to supply a struct
sensor_trigger
and a handler function. The structure contains the trigger
type and the channel on which the trigger must be configured.
Because most sensors are connected via SPI or I2C busses, it is not possible to communicate with them from the interrupt execution context. The execution of the trigger handler is deferred to a thread, so that data fetching operations are possible. A driver can spawn its own thread to fetch data, thus ensuring minimum latency. Alternatively, multiple sensor drivers can share a system-wide thread. The shared thread approach increases the latency of handling interrupts but uses less memory. You can configure which approach to follow for each driver. Most drivers can entirely disable triggers resulting in a smaller footprint.
The following example contains a trigger fired whenever temperature crosses the 26 degree Celsius threshold. It also samples the temperature every second. A real application would ideally disable periodic sampling in the interest of saving power. Since the application has direct access to the kernel config symbols, no trigger is registered when triggering was disabled by the driver’s configuration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
#define UCEL_PER_CEL 1000000
#define UCEL_PER_MCEL 1000
#define TEMP_INITIAL_CEL 25
#define TEMP_WINDOW_HALF_UCEL 500000
static const char *now_str(void)
{
static char buf[16]; /* ...HH:MM:SS.MMM */
u32_t now = k_uptime_get_32();
unsigned int ms = now % MSEC_PER_SEC;
unsigned int s;
unsigned int min;
unsigned int h;
now /= MSEC_PER_SEC;
s = now % 60U;
now /= 60U;
min = now % 60U;
now /= 60U;
h = now;
snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u",
h, min, s, ms);
return buf;
}
#ifdef CONFIG_MCP9808_TRIGGER
static struct sensor_trigger trig;
static int set_window(struct device *dev,
const struct sensor_value *temp)
{
const int temp_ucel = temp->val1 * UCEL_PER_CEL + temp->val2;
const int low_ucel = temp_ucel - TEMP_WINDOW_HALF_UCEL;
const int high_ucel = temp_ucel + TEMP_WINDOW_HALF_UCEL;
struct sensor_value val = {
.val1 = low_ucel / UCEL_PER_CEL,
.val2 = low_ucel % UCEL_PER_CEL,
};
int rc = sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
SENSOR_ATTR_LOWER_THRESH, &val);
if (rc == 0) {
val.val1 = high_ucel / UCEL_PER_CEL,
val.val2 = high_ucel % UCEL_PER_CEL,
rc = sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
SENSOR_ATTR_UPPER_THRESH, &val);
}
if (rc == 0) {
printf("Alert on temp outside [%d, %d] milli-Celsius\n",
low_ucel / UCEL_PER_MCEL,
high_ucel / UCEL_PER_MCEL);
}
return rc;
}
static inline int set_window_ucel(struct device *dev,
int temp_ucel)
{
struct sensor_value val = {
.val1 = temp_ucel / UCEL_PER_CEL,
.val2 = temp_ucel % UCEL_PER_CEL,
};
return set_window(dev, &val);
}
static void trigger_handler(struct device *dev, struct sensor_trigger *trig)
{
struct sensor_value temp;
static size_t cnt;
++cnt;
sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
printf("trigger fired %u, temp %g deg C\n", cnt,
sensor_value_to_double(&temp));
set_window(dev, &temp);
}
#endif
void main(void)
{
const char *const devname = DT_INST_0_MICROCHIP_MCP9808_LABEL;
struct device *dev = device_get_binding(devname);
int rc;
if (dev == NULL) {
printf("Device %s not found.\n", devname);
return;
}
#ifdef CONFIG_MCP9808_TRIGGER
rc = set_window_ucel(dev, TEMP_INITIAL_CEL * UCEL_PER_CEL);
if (rc == 0) {
trig.type = SENSOR_TRIG_THRESHOLD;
trig.chan = SENSOR_CHAN_AMBIENT_TEMP;
rc = sensor_trigger_set(dev, &trig, trigger_handler);
}
if (rc != 0) {
printf("Trigger set failed: %d\n", rc);
return;
}
printk("Trigger set got %d\n", rc);
#endif
while (1) {
struct sensor_value temp;
rc = sensor_sample_fetch(dev);
if (rc != 0) {
printf("sensor_sample_fetch error: %d\n", rc);
break;
}
rc = sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
if (rc != 0) {
printf("sensor_channel_get error: %d\n", rc);
break;
}
printf("%s: %g C\n", now_str(),
sensor_value_to_double(&temp));
k_sleep(K_SECONDS(2));
}
}
|
API Reference¶
-
group
sensor_interface
Sensor Interface.
Defines
-
SENSOR_G
¶ The value of gravitational constant in micro m/s^2.
-
SENSOR_PI
¶ The value of constant PI in micros.
Typedefs
-
typedef
sensor_trigger_handler_t
¶ Callback API upon firing of a trigger.
- Parameters
struct device *dev
: Pointer to the sensor devicestruct sensor_trigger *trigger
: The trigger
-
typedef
sensor_attr_set_t
¶ Callback API upon setting a sensor’s attributes.
See sensor_attr_set() for argument description
-
typedef
sensor_trigger_set_t
¶ Callback API for setting a sensor’s trigger and handler.
See sensor_trigger_set() for argument description
-
typedef
sensor_sample_fetch_t
¶ Callback API for fetching data from a sensor.
See sensor_sample_fetch() for argument description
-
typedef
sensor_channel_get_t
¶ Callback API for getting a reading from a sensor.
See sensor_channel_get() for argument description
Enums
-
enum
sensor_channel
¶ Sensor channels.
Values:
-
SENSOR_CHAN_ACCEL_X
¶ Acceleration on the X axis, in m/s^2.
-
SENSOR_CHAN_ACCEL_Y
¶ Acceleration on the Y axis, in m/s^2.
-
SENSOR_CHAN_ACCEL_Z
¶ Acceleration on the Z axis, in m/s^2.
-
SENSOR_CHAN_ACCEL_XYZ
¶ Acceleration on the X, Y and Z axes.
-
SENSOR_CHAN_GYRO_X
¶ Angular velocity around the X axis, in radians/s.
-
SENSOR_CHAN_GYRO_Y
¶ Angular velocity around the Y axis, in radians/s.
-
SENSOR_CHAN_GYRO_Z
¶ Angular velocity around the Z axis, in radians/s.
-
SENSOR_CHAN_GYRO_XYZ
¶ Angular velocity around the X, Y and Z axes.
-
SENSOR_CHAN_MAGN_X
¶ Magnetic field on the X axis, in Gauss.
-
SENSOR_CHAN_MAGN_Y
¶ Magnetic field on the Y axis, in Gauss.
-
SENSOR_CHAN_MAGN_Z
¶ Magnetic field on the Z axis, in Gauss.
-
SENSOR_CHAN_MAGN_XYZ
¶ Magnetic field on the X, Y and Z axes.
-
SENSOR_CHAN_DIE_TEMP
¶ Device die temperature in degrees Celsius.
-
SENSOR_CHAN_AMBIENT_TEMP
¶ Ambient temperature in degrees Celsius.
-
SENSOR_CHAN_PRESS
¶ Pressure in kilopascal.
-
SENSOR_CHAN_PROX
¶ Proximity. Adimensional. A value of 1 indicates that an object is close.
-
SENSOR_CHAN_HUMIDITY
¶ Humidity, in percent.
-
SENSOR_CHAN_LIGHT
¶ Illuminance in visible spectrum, in lux.
-
SENSOR_CHAN_IR
¶ Illuminance in infra-red spectrum, in lux.
-
SENSOR_CHAN_RED
¶ Illuminance in red spectrum, in lux.
-
SENSOR_CHAN_GREEN
¶ Illuminance in green spectrum, in lux.
-
SENSOR_CHAN_BLUE
¶ Illuminance in blue spectrum, in lux.
-
SENSOR_CHAN_ALTITUDE
¶ Altitude, in meters
-
SENSOR_CHAN_PM_1_0
¶ 1.0 micro-meters Particulate Matter, in ug/m^3
-
SENSOR_CHAN_PM_2_5
¶ 2.5 micro-meters Particulate Matter, in ug/m^3
-
SENSOR_CHAN_PM_10
¶ 10 micro-meters Particulate Matter, in ug/m^3
-
SENSOR_CHAN_DISTANCE
¶ Distance. From sensor to target, in meters
-
SENSOR_CHAN_CO2
¶ CO2 level, in parts per million (ppm)
-
SENSOR_CHAN_VOC
¶ VOC level, in parts per billion (ppb)
-
SENSOR_CHAN_GAS_RES
¶ Gas sensor resistance in ohms.
-
SENSOR_CHAN_VOLTAGE
¶ Voltage, in volts
-
SENSOR_CHAN_CURRENT
¶ Current, in amps
-
SENSOR_CHAN_RESISTANCE
¶ Resistance , in Ohm
-
SENSOR_CHAN_ROTATION
¶ Angular rotation, in degrees
-
SENSOR_CHAN_POS_DX
¶ Position change on the X axis, in points.
-
SENSOR_CHAN_POS_DY
¶ Position change on the Y axis, in points.
-
SENSOR_CHAN_POS_DZ
¶ Position change on the Z axis, in points.
-
SENSOR_CHAN_ALL
¶ All channels.
-
SENSOR_CHAN_COMMON_COUNT
¶ Number of all common sensor channels.
-
SENSOR_CHAN_PRIV_START
= SENSOR_CHAN_COMMON_COUNT¶ This and higher values are sensor specific. Refer to the sensor header file.
-
SENSOR_CHAN_MAX
= INT16_MAX¶ Maximum value describing a sensor channel type.
-
-
enum
sensor_trigger_type
¶ Sensor trigger types.
Values:
-
SENSOR_TRIG_TIMER
¶ Timer-based trigger, useful when the sensor does not have an interrupt line.
-
SENSOR_TRIG_DATA_READY
¶ Trigger fires whenever new data is ready.
-
SENSOR_TRIG_DELTA
¶ Trigger fires when the selected channel varies significantly. This includes any-motion detection when the channel is acceleration or gyro. If detection is based on slope between successive channel readings, the slope threshold is configured via the SENSOR_ATTR_SLOPE_TH and SENSOR_ATTR_SLOPE_DUR attributes.
-
SENSOR_TRIG_NEAR_FAR
¶ Trigger fires when a near/far event is detected.
-
SENSOR_TRIG_THRESHOLD
¶ Trigger fires when channel reading transitions configured thresholds. The thresholds are configured via the SENSOR_ATTR_LOWER_THRESH and SENSOR_ATTR_UPPER_THRESH attributes.
-
SENSOR_TRIG_TAP
¶ Trigger fires when a single tap is detected.
-
SENSOR_TRIG_DOUBLE_TAP
¶ Trigger fires when a double tap is detected.
-
SENSOR_TRIG_COMMON_COUNT
¶ Number of all common sensor triggers.
-
SENSOR_TRIG_PRIV_START
= SENSOR_TRIG_COMMON_COUNT¶ This and higher values are sensor specific. Refer to the sensor header file.
-
SENSOR_TRIG_MAX
= INT16_MAX¶ Maximum value describing a sensor trigger type.
-
-
enum
sensor_attribute
¶ Sensor attribute types.
Values:
-
SENSOR_ATTR_SAMPLING_FREQUENCY
¶ Sensor sampling frequency, i.e. how many times a second the sensor takes a measurement.
-
SENSOR_ATTR_LOWER_THRESH
¶ Lower threshold for trigger.
-
SENSOR_ATTR_UPPER_THRESH
¶ Upper threshold for trigger.
-
SENSOR_ATTR_SLOPE_TH
¶ Threshold for any-motion (slope) trigger.
-
SENSOR_ATTR_SLOPE_DUR
¶ Duration for which the slope values needs to be outside the threshold for the trigger to fire.
-
SENSOR_ATTR_OVERSAMPLING
¶ Oversampling factor
-
SENSOR_ATTR_FULL_SCALE
¶ Sensor range, in SI units.
-
SENSOR_ATTR_OFFSET
¶ The sensor value returned will be altered by the amount indicated by offset: final_value = sensor_value + offset.
-
SENSOR_ATTR_CALIB_TARGET
¶ Calibration target. This will be used by the internal chip’s algorithms to calibrate itself on a certain axis, or all of them.
-
SENSOR_ATTR_COMMON_COUNT
¶ Number of all common sensor attributes.
-
SENSOR_ATTR_PRIV_START
= SENSOR_ATTR_COMMON_COUNT¶ This and higher values are sensor specific. Refer to the sensor header file.
-
SENSOR_ATTR_MAX
= INT16_MAX¶ Maximum value describing a sensor attribute type.
-
Functions
-
int
sensor_attr_set
(struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val)¶ Set an attribute for a sensor.
- Return
0 if successful, negative errno code if failure.
- Parameters
dev
: Pointer to the sensor devicechan
: The channel the attribute belongs to, if any. Some attributes may only be set for all channels of a device, depending on device capabilities.attr
: The attribute to setval
: The value to set the attribute to
-
static int
sensor_trigger_set
(struct device *dev, struct sensor_trigger *trig, sensor_trigger_handler_t handler)¶ Activate a sensor’s trigger and set the trigger handler.
The handler will be called from a thread, so I2C or SPI operations are safe. However, the thread’s stack is limited and defined by the driver. It is currently up to the caller to ensure that the handler does not overflow the stack.
This API is not permitted for user threads.
- Return
0 if successful, negative errno code if failure.
- Parameters
dev
: Pointer to the sensor devicetrig
: The trigger to activatehandler
: The function that should be called when the trigger fires
-
int
sensor_sample_fetch
(struct device *dev)¶ Fetch a sample from the sensor and store it in an internal driver buffer.
Read all of a sensor’s active channels and, if necessary, perform any additional operations necessary to make the values useful. The user may then get individual channel values by calling sensor_channel_get.
Since the function communicates with the sensor device, it is unsafe to call it in an ISR if the device is connected via I2C or SPI.
- Return
0 if successful, negative errno code if failure.
- Parameters
dev
: Pointer to the sensor device
-
int
sensor_sample_fetch_chan
(struct device *dev, enum sensor_channel type)¶ Fetch a sample from the sensor and store it in an internal driver buffer.
Read and compute compensation for one type of sensor data (magnetometer, accelerometer, etc). The user may then get individual channel values by calling sensor_channel_get.
This is mostly implemented by multi function devices enabling reading at different sampling rates.
Since the function communicates with the sensor device, it is unsafe to call it in an ISR if the device is connected via I2C or SPI.
- Return
0 if successful, negative errno code if failure.
- Parameters
dev
: Pointer to the sensor devicetype
: The channel that needs updated
-
int
sensor_channel_get
(struct device *dev, enum sensor_channel chan, struct sensor_value *val)¶ Get a reading from a sensor device.
Return a useful value for a particular channel, from the driver’s internal data. Before calling this function, a sample must be obtained by calling sensor_sample_fetch or sensor_sample_fetch_chan. It is guaranteed that two subsequent calls of this function for the same channels will yield the same value, if sensor_sample_fetch or sensor_sample_fetch_chan has not been called in the meantime.
For vectorial data samples you can request all axes in just one call by passing the specific channel with _XYZ suffix. The sample will be returned at val[0], val[1] and val[2] (X, Y and Z in that order).
- Return
0 if successful, negative errno code if failure.
- Parameters
dev
: Pointer to the sensor devicechan
: The channel to readval
: Where to store the value
-
static s32_t
sensor_ms2_to_g
(const struct sensor_value *ms2)¶ Helper function to convert acceleration from m/s^2 to Gs.
- Return
The converted value, in Gs.
- Parameters
ms2
: A pointer to a sensor_value struct holding the acceleration, in m/s^2.
-
static void
sensor_g_to_ms2
(s32_t g, struct sensor_value *ms2)¶ Helper function to convert acceleration from Gs to m/s^2.
- Parameters
g
: The G value to be converted.ms2
: A pointer to a sensor_value struct, where the result is stored.
-
static s32_t
sensor_rad_to_degrees
(const struct sensor_value *rad)¶ Helper function for converting radians to degrees.
- Return
The converted value, in degrees.
- Parameters
rad
: A pointer to a sensor_value struct, holding the value in radians.
-
static void
sensor_degrees_to_rad
(s32_t d, struct sensor_value *rad)¶ Helper function for converting degrees to radians.
- Parameters
d
: The value (in degrees) to be converted.rad
: A pointer to a sensor_value struct, where the result is stored.
-
static double
sensor_value_to_double
(struct sensor_value *val)¶ Helper function for converting struct sensor_value to double.
- Return
The converted value.
- Parameters
val
: A pointer to a sensor_value struct.
-
struct
sensor_value
¶ - #include <sensor.h>
Representation of a sensor readout value.
The value is represented as having an integer and a fractional part, and can be obtained using the formula val1 + val2 * 10^(-6). Negative values also adhere to the above formula, but may need special attention. Here are some examples of the value representation:
0.5: val1 = 0, val2 = 500000 -0.5: val1 = 0, val2 = -500000 -1.0: val1 = -1, val2 = 0 -1.5: val1 = -1, val2 = -500000
-
struct
sensor_trigger
¶ - #include <sensor.h>
Sensor trigger spec.
-