xref: /aosp_15_r20/external/pigweed/seed/0119.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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