1.. _sensor_api: 2 3Sensors 4####### 5 6The sensor subsystem exposes an API to uniformly access sensor devices. 7Common operations are: reading data and executing code when specific 8conditions are met. 9 10Basic Operation 11*************** 12 13Channels 14======== 15 16Fundamentally, a channel is a quantity that a sensor device can measure. 17 18Sensors can have multiple channels, either to represent different axes of 19the same physical property (e.g. acceleration); or because they can measure 20different properties altogether (ambient temperature, pressure and 21humidity). Complex sensors cover both cases, so a single device can expose 22three acceleration channels and a temperature one. 23 24It is imperative that all sensors that support a given channel express 25results in the same unit of measurement. Consult the 26:ref:`sensor_api_reference` for all supported channels, along with their 27description and units of measurement: 28 29Values 30====== 31 32Sensor stable APIs return results as :c:struct:`sensor_value`. This 33representation avoids use of floating point values as they may not be 34supported on certain setups. 35 36A newer experimental (may change) API that can interpret raw sensor data is 37available in parallel. This new API exposes raw encoded sensor data to the 38application and provides a separate decoder to convert the data to a Q31 format 39which is compatible with the Zephyr :ref:`zdsp_api`. The values represented are 40in the range of (-1.0, 1.0) and require a shift operation in order to scale 41them to their SI unit values. See :ref:`Async Read` for more information. 42 43Fetching Values 44=============== 45 46Getting a reading from a sensor requires two operations. First, an 47application instructs the driver to fetch a sample of all its channels. 48Then, individual channels may be read. In the case of channels with 49multiple axes, they can be read in a single operation by supplying 50the corresponding :literal:`_XYZ` channel type and a buffer of 3 51:c:struct:`sensor_value` objects. This approach ensures consistency 52of channels between reads and efficiency of communication by issuing a 53single transaction on the underlying bus. 54 55Below is an example illustrating the usage of the BME280 sensor, which 56measures ambient temperature and atmospheric pressure. Note that 57:c:func:`sensor_sample_fetch` is only called once, as it reads and 58compensates data for both channels. 59 60.. literalinclude:: ../../../samples/sensor/bme280/src/main.c 61 :language: c 62 :lines: 12- 63 :linenos: 64 65.. _Async Read: 66 67Async Read 68========== 69 70To enable the async APIs, use :kconfig:option:`CONFIG_SENSOR_ASYNC_API`. 71 72Reading the sensors leverages the :ref:`rtio_api` subsystem. Applications 73gain control of the data processing thread and even memory management. In order 74to get started with reading the sensors, an IODev must be created via the 75:c:macro:`SENSOR_DT_READ_IODEV`. Next, an RTIO context must be created. It is 76strongly suggested that this context is created with a memory pool via 77:c:macro:`RTIO_DEFINE_WITH_MEMPOOL`. 78 79.. code-block:: C 80 81 #include <zephyr/device.h> 82 #include <zephyr/drivers/sensor.h> 83 #include <zephyr/rtio/rtio.h> 84 85 static const struct device *lid_accel = DEVICE_DT_GET(DT_ALIAS(lid_accel)); 86 SENSOR_DT_READ_IODEV(lid_accel_iodev, DT_ALIAS(lid_accel), SENSOR_CHAN_ACCEL_XYZ); 87 88 RTIO_DEFINE_WITH_MEMPOOL(sensors_rtio, 89 4, /* submission queue size */ 90 4, /* completion queue size */ 91 16, /* number of memory blocks */ 92 32, /* size of each memory block */ 93 4 /* memory alignment */ 94 ); 95 96To trigger a read, the application simply needs to call :c:func:`sensor_read` 97and pass the relevant IODev and RTIO context. Getting the result is done like 98any other RTIO operation, by waiting on a completion queue event (CQE). In 99order to help reduce some boilerplate code, the helper function 100:c:func:`sensor_processing_with_callback` is provided. When called, the 101function will block until a CQE becomes available from the provided RTIO 102context. The appropriate buffers are extracted and the callback is called. 103Once the callback is done, the memory is reclaimed by the memorypool. This 104looks like: 105 106.. code-block:: C 107 108 static void sensor_processing_callback(int result, uint8_t *buf, 109 uint32_t buf_len, void *userdata) { 110 // Process the data... 111 } 112 113 static void sensor_processing_thread(void *, void *, void *) { 114 while (true) { 115 sensor_processing_with_callback(&sensors_rtio, sensor_processing_callback); 116 } 117 } 118 K_THREAD_DEFINE(sensor_processing_tid, 1024, sensor_processing_thread, 119 NULL, NULL, NULL, 0, 0, 0); 120 121.. note:: 122 Helper functions to create custom length IODev nodes and ones that don't 123 have static bindings will be added soon. 124 125Processing the Data 126=================== 127 128Once data collection completes and the processing callback was called, 129processing the data is done via the :c:struct:`sensor_decoder_api`. The API 130provides a means for applications to control *when* to process the data and how 131many resources to dedicate to the processing. The API is entirely self 132contained and requires no system calls (even when 133:kconfig:option:`CONFIG_USERSPACE` is enabled). 134 135.. code-block:: C 136 137 static struct sensor_decoder_api *lid_accel_decoder = SENSOR_DECODER_DT_GET(DT_ALIAS(lid_accel)); 138 139 static void sensor_processing_callback(int result, uint8_t *buf, 140 uint32_t buf_len, void *userdata) { 141 uint64_t timestamp; 142 sensor_frame_iterator_t fit = {0}; 143 sensor_channel_iterator_t cit = {0}; 144 enum sensor_channel channels[3]; 145 q31_t values[3]; 146 int8_t shift[3]; 147 148 lid_accel_decoder->get_timestamp(buf, ×tamp); 149 lid_accel_decoder->decode(buf, &fit, &cit, channels, values, 3); 150 151 /* Values are now in q31_t format, we're going to convert them to micro-units */ 152 153 /* First, we need to know by how much to shift the values */ 154 lid_accel_decoder->get_shift(buf, channels[0], &shift[0]); 155 lid_accel_decoder->get_shift(buf, channels[1], &shift[1]); 156 lid_accel_decoder->get_shift(buf, channels[2], &shift[2]); 157 158 /* Shift the values to get the SI units */ 159 int64_t scaled_values[] = { 160 (int64_t)values[0] << shift[0], 161 (int64_t)values[1] << shift[1], 162 (int64_t)values[2] << shift[2], 163 }; 164 165 /* 166 * FIELD_GET(GENMASK64(63, 31), scaled_values[]) - will give the integer value 167 * FIELD_GET(GENMASK64(30, 0), scaled_values[]) / INT32_MAX - is the decimal value 168 */ 169 } 170 171Configuration and Attributes 172**************************** 173 174Setting the communication bus and address is considered the most basic 175configuration for sensor devices. This setting is done at compile time, via 176the configuration menu. If the sensor supports interrupts, the interrupt 177lines and triggering parameters described below are also configured at 178compile time. 179 180Alongside these communication parameters, sensor chips typically expose 181multiple parameters that control the accuracy and frequency of measurement. 182In compliance with Zephyr's design goals, most of these values are 183statically configured at compile time. 184 185However, certain parameters could require runtime configuration, for 186example, threshold values for interrupts. These values are configured via 187attributes. The example in the following section showcases a sensor with an 188interrupt line that is triggered when the temperature crosses a threshold. 189The threshold is configured at runtime using an attribute. 190 191Triggers 192******** 193 194:dfn:`Triggers` in Zephyr refer to the interrupt lines of the sensor chips. 195Many sensor chips support one or more triggers. Some examples of triggers 196include: new data is ready for reading, a channel value has crossed a 197threshold, or the device has sensed motion. 198 199To configure a trigger, an application needs to supply a 200:c:struct:`sensor_trigger` and a handler function. The structure contains the 201trigger type and the channel on which the trigger must be configured. 202 203Because most sensors are connected via SPI or I2C buses, it is not possible 204to communicate with them from the interrupt execution context. The 205execution of the trigger handler is deferred to a thread, so that data 206fetching operations are possible. A driver can spawn its own thread to fetch 207data, thus ensuring minimum latency. Alternatively, multiple sensor drivers 208can share a system-wide thread. The shared thread approach increases the 209latency of handling interrupts but uses less memory. You can configure which 210approach to follow for each driver. Most drivers can entirely disable 211triggers resulting in a smaller footprint. 212 213The following example contains a trigger fired whenever temperature crosses 214the 26 degree Celsius threshold. It also samples the temperature every 215second. A real application would ideally disable periodic sampling in the 216interest of saving power. Since the application has direct access to the 217kernel config symbols, no trigger is registered when triggering was disabled 218by the driver's configuration. 219 220.. literalinclude:: ../../../samples/sensor/mcp9808/src/main.c 221 :language: c 222 :lines: 12- 223 :linenos: 224 225.. _sensor_api_reference: 226 227API Reference 228************** 229 230.. doxygengroup:: sensor_interface 231.. doxygengroup:: sensor_emulator_backend 232