1.. _seed-0119: 2 3============= 40119: Sensors 5============= 6.. seed:: 7 :number: 0119 8 :name: Sensors 9 :status: Accepted 10 :proposal_date: 2023-10-18 11 :cl: 175479 12 :authors: Yuval Peress 13 :facilitator: Taylor Cramer 14 15------- 16Summary 17------- 18This SEED proposes that Pigweed provide a full end-to-end sensor solution, 19including a shared set of abstractions and communication primitives. This 20will allow Pigweed users to build reusable drivers and portable applications. 21 22---------- 23Motivation 24---------- 25Sensors are part of nearly every embedded device. However, sensor drivers and 26application logic are often bespoke. Current clients of Pigweed must write 27sensor drivers using various bus APIs. This results in drivers and applications 28that are deeply integrated and not reusable. When drivers are custom-built for 29particular applications, they frequently omit significant functionality needed 30by other potential users. 31 32This solution does not scale. 33 34Introducing a standard sensor driver interface will allow Pigweed users to 35build drivers that can be reused across applications, and applications that 36are compatible with multiple different drivers. 37 38A second layer of a sensor framework will then be introduced. This secondary 39layer will provide functionality required for more advanced applications and 40will include support for virtual sensors and multi-client connections per 41sensor. It will also be compatible with Android's `CHRE`_. 42 43--------- 44Prior Art 45--------- 46Sensor APIs are available today on many platforms: 47 48- `Android`_ 49- `Apple iOS and macOS`_ 50- `Linux`_ 51- `Windows`_ 52- `Zephyr`_ 53 54Where possible, we strive to make it possible for Pigweed's Sensor API to 55delegate to these platform-level APIs without significant loss of 56functionality. 57 58However, where performance, code size, API comprehensibility, or reusability 59are of particular concern, priority is given to ensuring that Pigweed-native 60sensor drivers and APIs meet the needs of Pigweed applications. 61 62------------- 63System Design 64------------- 65At the high level, the ``pw_sensor`` module will introduce the following 66concepts: 67 68- ``Sensor``: a class which will enable a single user to **configure**, 69 **read**, or **stream** data. The implementation can vary. Some 70 implementations may call down to an already implemented sensor driver of the 71 underlying RTOS, others may be software sensors which themselves will open 72 ``Connection``\s to other sensors, or in other cases, the ``Sensor`` 73 implementation will actually perform remote calls to a sensor on another SoC 74 or board. 75- ``Connection``: a class which will enable us to abstract away the single 76 client concept of the ``Sensor``. A ``Connection`` is owned by some logic 77 and uses the same configuration as the ``Sensor``. The only difference is 78 that the Pigweed provided sensor framework will then mux all the 79 configurations targeting the same sensor into a single configuration. This 80 single configuration will be used for the actual ``Sensor`` object (see 81 `pw sensor configuration mux`_ as an example). When data becomes 82 available, the sensor framework will demux the data and provide it to the 83 various clients based on the live ``Connection``\s. 84 85.. image:: 0119/high-level-view.svg 86 87Actually reading the data is a 2 step process. The first is getting the data 88from the ``Sensor`` object. The second step is decoding it into something 89that's usable. Data from the first step is generally still in register format 90and includes some headers from the driver which will allow the ``Decoder`` to 91convert the values to the appropriate representation (most likely ``float``). 92 93.. image:: 0119/data-pipeline.svg 94 95----------------- 96The Sensor Driver 97----------------- 98The sensor driver provides the 3 functionalities listed above (configuring, 99reading, and streaming). 100 101Each sensor may assume it has exactly 1 caller. It is up to the application to 102leverage the right locking and arbitration mechanisms if a sensor is to be 103shared between parts of the code. Doing this keeps the driver implementor's job 104much simpler and allows them to focus on performance, testing, and simplicity. 105In order to provide consumers of sensor data with more advanced features, a 106sensor framework will also be provided and discussed in the following section 107:ref:`the sensor framework`. 108 109Asynchronous APIs 110----------------- 111Bus transactions are asynchronous by nature and often can be set up to use an 112interrupt to signal completion. By making the assumption that all reading, 113writing, and configuring of the sensor are asynchronous, it's possible to 114provide an API which is callable even from an interrupt context. The final 115result of any operation of the API will use the ``pw_async`` logic. Doing so 116will allow the clients to avoid concerns about the context in which callbacks 117are called and having to schedule work on the right thread. Instead, they're 118able to rely on the async dispatcher. 119 120Configuring 121----------- 122Sensors generally provide some variable configurations. In some cases, these 123configurations are global (i.e. they apply to the device). An example of such 124global configuration might be a FIFO watermark (via a batching duration). In 125other cases, the configuration might apply to specific sub-sensors / 126measurements. An example of a specific configuration attribute can be the sample 127rate which on an Inertial Measurement Unit (IMU) might be an acceleration and 128rotational velocity. We can describe each configuration with the following: 129 130- Measurement type: such as acceleration or rotational velocity 131- Measurement index: the index of the measurement. This is almost always 0, but 132 some sensors do provide multiple samples of the same measurement type (range 133 finders). In which case it's possible that we would need to configure 134 separate instances of the sensor. 135- Attribute: such as the sample rate, scale, offset, or batch duration 136- Value: the value associated with the configuration (might be a ``bool``, 137 ``float``, ``uint64_t``, or something else entirely). 138 139Here's an example: 140 141+---------------+----------------+--------+ 142| Measurement | Attribute | Value | 143+-------+-------+----------------+--------+ 144| Type | Index | | | 145+=======+=======+================+========+ 146| Accel | 0 | Sample Rate | 1000Hz | 147+-------+-------+----------------+--------+ 148| All | 0 | Batch duration | 200ms | 149+-------+-------+----------------+--------+ 150 151Reading 152------- 153Reading a sensor involves initiating some I/O which will fetch an unknown amount 154of data. As such, the operation will require some ``Allocator`` to be used along 155with a possible *Measurement Type* filter to limit the amount of data being 156retrieved and stored. When complete, the result will be provided in a 157``pw::ConstByteSpan`` which was allocated from the ``Allocator``. This byte span 158can be cached or possibly sent over a wire for decoding. 159 160Streaming 161--------- 162Streaming data from a sensor is effectively the same as reading the sensor with 163minor considerations. Instead of filtering "what" data we want, we're able to 164specify "when" we want the data. This happens in the form of one or more 165interrupts. There will be some additional control over the data returned from 166the stream; it will come in the form of an operation. 3 operations will be 167supported for streams: 168 169- ``Include``: which tells the driver to include any/all associated data with 170 the trigger. As an example, a batching trigger will include all the data from 171 the FIFO so it can be decoded later. 172- ``Drop``: which tells the driver to get rid of the associated data and just 173 report that the event happened. This might be done on a FIFO full event to 174 reset the state and start over. 175- ``Noop``: which tells the driver to just report the event and do nothing with 176 the associated data (maybe the developer wants to read it separately). 177 178.. note:: 179 We do not allow specifying a measurement filter like we do in the reading API 180 because it would drastically increase the cost of the driver developer. 181 Imagine a trigger for the stream on an IMU using the batch duration where we 182 want to only get the acceleration values from the FIFO. This scenario doesn't 183 make much sense to support since the caller should simply turn off the 184 gyroscope in the FIFO via the configuration. Having the gyroscope 185 measurements in the FIFO usually means they will simply be discarded when 186 read. This puts a very heavy burden on the driver author to place a filter in 187 the reader logic as well as in the decoder. 188 189Decoder 190------- 191The decoder provides functionality to peek into the raw data returned from the 192``Sensor``. It should implement functionality such as: 193 194- Checking if a measurement type is present in the buffer. If so, how many 195 :ref:`pw sensor define frame` and indices? 196- Checking how much memory will be required to decode the frame header (which 197 includes information like the base timestamp, frame count, etc) and each frame 198 of data. 199- Decoding frames of data. There will be a hard mapping of a measurement type to 200 data representation. Example: a measurement type of *Acceleration* will always 201 decode to a ``struct acceleration_data``. 202 203.. _the sensor framework: 204 205-------------------- 206The Sensor Framework 207-------------------- 208The sensor framework is an abstraction above the ``Sensor`` class which provides 209a superset of features but on a ``Connection`` object. The framework will be a 210singleton object and will provide consumers the following: 211 212- List all sensors represented as read-only ``SensorInfo`` objects. 213- Ability to open/close connections. When a connection is open, a ``Connection`` 214 object is returned. The connection can be closed by either calling 215 ``Connection::Close()`` or simply calling the ``Connection``\s deconstructor. 216 217Once the sensor framework is linked into the application, ``Sensor`` objects 218should not be manipulated directly. Instead, the only direct client of the 219``Sensor``\s is the framework. Users can request a list of all the sensors 220(``SensorInfo`` objects). Once the client finds the sensor they want to listen 221to, they can request a ``Connection`` to be opened to that sensor. A 222``Connection`` provides very similar functionality to that of the ``Sensor`` but 223is owned by the framework. As an example, a configuration change made on the 224``Connection`` will trigger the framework to mux together all the configurations 225of all the connections that point to the same ``Sensor``. Once complete, a 226single configuration will be selected and set on the ``Sensor``. Similarly, when 227the ``Sensor`` produces data, the data will be demuxed and sent to all the open 228``Connection``\s. 229 230Virtual Sensors 231--------------- 232This framework provides an interesting way to build portable virtual (soft) 233sensors. If the library containing the virtual sensors depends on the framework, 234it's possible for the virtual sensors to own connections, configure the sources, 235and perform all the necessary signal processing without compromising other 236unknown clients of the same sensor (since the framework handles all the 237configuration arbitration). 238 239As an example, a hinge angle sensor could accept 2 ``Connection`` objects to 240accelerometers in its constructor. When the hinge angle sensor is configured 241(such as sample rate) it would pass the configuration down to the connections 242and request the same sample rate from the 2 accelerometers. 243 244-------- 245Glossary 246-------- 247 248.. _pw sensor define frame: 249 250Frame 251 A single time slice. Usually this is used to reference a single sample of 252 multiple sensor measurement types such as an IMU measuring both acceleration 253 and rotational velocity at the same time. 254 255-------- 256Examples 257-------- 258 259.. _pw sensor configuration mux: 260 261Pigweed will provide some default mechanism for muxing together 262``Configuration`` objects. Like many other modules and backends in Pigweed, this 263mechanism will be overridable by the application. Below is an example of what it 264might look like: 265 266- Assume a client requests samples at 1kHz 267- Assume a second client requests samples at 1.1kHz 268- The resulting sample rate is 1.1kHz, but it's much more likely that the sensor 269 doesn't support 1.1kHz and will instead be giving both clients 2kHz of 270 samples. It will then be up to both clients to decimate the data correctly. 271 272.. note:: 273 Decimating 2kHz down to 1.1kHz isn't as simple as just throwing away 9 274 samples for every 20. What the client is likely to do is use a weighted 275 average in order to simulate the 1.1kHz. It's likely that Pigweed should at 276 some point provide a decimation library with a few common strategies which 277 would help developers with the task. 278 279.. _`Android`: https://developer.android.com/develop/sensors-and-location/sensors/sensors_overview 280.. _`Apple iOS and macOS`: https://developer.apple.com/documentation/sensorkit 281.. _CHRE: https://source.android.com/docs/core/interaction/contexthub 282.. _Linux: https://www.kernel.org/doc/html/v4.14/driver-api/iio/intro.html 283.. _Windows: https://learn.microsoft.com/en-us/windows/win32/sensorsapi/the-sensor-object 284.. _Zephyr: https://docs.zephyrproject.org/apidoc/latest/group__sensor__interface.html 285