xref: /aosp_15_r20/external/pigweed/pw_sensor/py/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_sensor-py:
2
3========================
4pw_sensor Python package
5========================
6.. pigweed-module::
7   :name: pw_sensor
8
9   - **Configure Sensors**: Update the sensor configurations to match your
10     requirements. Sensor configurations can be checked at compile time using
11     a YAML sensor description.
12   - **2 Phase Reading**: Design your own pipeline and priorities. Sensor can be
13     read on one thread (or no thread if DMA is available) and the data can be
14     decoded on another thread/core/device.
15
16The ``pw_sensor`` Python package provides utilities for generating data and code
17for Pigweed sensor drivers.
18
19.. warning::
20   This package is under development and the APIs are *VERY* likely to change.
21
22-----------------
23Using the package
24-----------------
25Typical users of ``pw_sensor`` begin by writing a YAML description of their
26sensor using the `metadata_schema.json`_ format, e.g.:
27
28.. code-block:: yaml
29
30   deps:
31      - "pw_sensor/channels.yaml"
32      - "pw_sensor/attributes.yaml"
33   compatible:
34      org: "Bosch"
35      part: "BMA4xx"
36   supported-buses:
37      - i2c
38      - spi
39   channels:
40      acceleration: []
41      die_temperature: []
42
43
44``pw_sensor`` provides a validator which will resolve any 'default' properties
45and make the final YAML easier for code generators to consume. The returned
46dictionary uses the `resolved_schema.json`_ format.
47
48Every platform/language may implement their own generator.
49Generators consume the validated (schema-compliant) YAML and may produce
50many types of outputs, such as a PDF datasheet, a C++ abstract class definition,
51or a Zephyr header of definitions describing the sensor.
52
53-------------------
54Describing a sensor
55-------------------
56When describing a sensor from the user's perspective, there are 3 primary points
57of interaction:
58
59#. compatible descriptor
60#. supported buses
61#. channels
62#. attributes
63#. triggers
64
65.. note::
66   Compatible string in Linux's devicetree are used to detail what a hardware
67   device is. They include a manufacturer and a model name in the format:
68   ``<manufacturer>,<model>``. In order to make this a bit more generic and
69   still functional with devicetree, Pigweed's compatible node consists of 2
70   separate properties instead of a single string: ``org`` and ``part``. This
71   abstracts away the devicetree model such that generators may produce other
72   targeted code. To read more about the compatible property, see
73   `Understanding the compatible Property`_
74
75Both *channels* and *attributes* covered in :ref:`seed-0120`, while the
76*compatible* descriptor allows us to have a unique identifier for each sensor.
77Next, we need a way to describe a sensor in a platform and language agnostic
78way.
79
80What are supported buses?
81=========================
82Currently, pw_sensor supports 2 types of sensor buses: i2c and spi. Each sensor
83must list at least 1 supported bus. Additional buses may be added as well as
84support for custom bus descriptors in downstream projects.
85
86What are channels?
87==================
88A channel is something that we can measure from a sensor. It's reasonable to ask
89"why not call it a measurement"? The answer is that a measurement isn't specific
90enough. A single illuminance sensor might provide a lux reading for:
91- Total lux (amount of light per square meter)
92- Red lux (amount of red light per square meter)
93- Green lux (amount of green light per square meter)
94- Blue lux (amount of blue light per square meter)
95- UV lux (amount of UV light per square meter)
96- IR lux (amount of infra-red light per square meter)
97
98All these are a "measurement" of light intensity, but they're different
99channels. When defining a channel we need to provide units. In the example
100above, the units are lux. Represented by the symbol "lx". It's likely that when
101verbose logging is needed or when generating documentation we might want to also
102associate a name and a longer description for the channel. This leaves us with
103the following structure for a channel:
104
105.. code-block:: yaml
106
107   <channel_id>:
108      "name": "string"
109      "description": "string"
110      "units": <string_units_id>
111
112When we construct the final sensor metadata, we can list the channels supported
113by that sensor. In some cases, the same channel may be available more than once.
114This happens at times with temperature sensors. In these cases, we can list
115multiple instances of a channel. Generally, if no instances are provided, it
116will be assumed that there's 1 instance of the channel. Otherwise, we might have
117something like:
118
119.. code-block:: yaml
120
121   channels:
122      ambient_temperature:
123         -  name: "-X"
124            description: "temperature measured in the -X direction"
125            units: "temperature"
126         -  name: "X"
127            description: "temperature measured in the +X direction"
128            units: "temperature"
129
130What are attributes?
131====================
132Attributes are used to change the behavior of a sensor. They're defined using
133the ``attributes`` key and are structured by associating the defined attribute
134type with a channel along with units and a representation (``float``,
135``signed``, or ``unsigned``). Here's an example:
136
137.. code-block:: yaml
138
139   attributes:
140      -  attribute: "sample_rate"
141         channel: "acceleration"
142         units: "frequency"
143         representation: "float"
144
145When associated with a ``sensor``, ``attributes`` define specific instances of
146configurable states for that sensor:
147
148.. code-block:: yaml
149
150   compatible: ...
151   channels: ...
152   attributes:
153      -  {}
154
155What are triggers?
156==================
157Triggers are events that have an interrupt associated with them. We can define
158common triggers which sensors can individually subscribe to. The definition
159looks like:
160
161.. code-block:: yaml
162
163   triggers:
164      fifo_watermark:
165         name: "FIFO watermark"
166         description: "Interrupt when the FIFO watermark has been reached (set as an attribute)"
167
168When associated with a ``sensor``, we simply need to match the right key in a
169list:
170
171.. code-block:: yaml
172
173   compatible: ...
174   channels: ...
175   attributes: ...
176   triggers:
177      -  fifo_watermark
178
179Additional metadata
180===================
181It's common for applications to require additional metadata that's not
182supported or used by Pigweed. These additional values can be added to the
183``extras`` key of the sensor:
184
185.. code-block:: yaml
186
187   compatible: ...
188   channels: ...
189   extras:
190     doc-ref: "my-driver-rst-ref"
191     memory-req: 512
192
193Values added here can be read by generator scripts.
194
195-----------------------
196The ``Validator`` class
197-----------------------
198The ``Validator`` class is used to take a sensor spec YAML file and expand it
199while verifying that all the information is available. It consists of 2 layers:
2001. Declarations
2012. Definitions
202
203The declaration YAML
204====================
205The declaration YAML files allow projects to define new sensor channels and
206attributes for their drivers. This allows proprietary functionality of sensors
207which cannot be made public. Pigweed will provide some baseline set of channels
208and attributes.
209
210The following YAML file is used to create a sensor which counts cakes. The
211sensor provides the ability to get the total cake count or a separate
212large/small cake count (for a total of 3 channels):
213
214.. code-block:: yaml
215
216   # File: my/org/sensors/cakes.yaml
217   units:
218      cake:
219         symbol: "cakes"
220   channels:
221     cakes:
222         description: "The number of cakes seen by the sensor"
223         units: "cake"
224      cakes_small:
225         description: "The number of cakes measuring 6 inches or less"
226         units: "cake"
227      cakes_large:
228         description: "The number of cakes measuring more than 6 inches"
229         units: "cake"
230
231The above YAML file will enable a 3 new channels: ``cakes``, ``cakes_small``,
232and ``cakes_large``. All 3 channels will use a unit ``cake``. A sensor
233implementing this channel would provide a definition file:
234
235.. code-block:: yaml
236
237   # File: my/org/sensors/cake/sensor.yaml
238   deps:
239      - "my/org/sensors/cakes.yaml"
240   compatible:
241      org: "myorg"
242      part: "cakevision"
243   supported-buses:
244      - i2c
245      - spi
246   channels:
247      cakes: []
248      cakes_small: []
249      cakes_large: []
250
251When validated, the above YAML will be converted to fill in the defined values.
252This means that ``channels/cakes`` will be automatically filled with:
253
254- ``name: "cakes"``: automatically derived from the name sinde the definition
255  did not provide a name.
256- ``description: "The number of cakes seen by the sensor"``: attained from the
257  definition file.
258- ``units``
259   - ``name: "cake"``: derived from the definition's ``symbol`` since ``name``
260     is not explicitly specified
261   - ``symbol: "cake"``: attained from definition file
262
263Output
264======
265The resulting output uses references. At times described above, things such as
266``units`` will be referenced from inside a sensor's channel. When validated, the
267corresponding ``units`` entry is guaranteed to be found at the top level
268``units`` map. Currently, there will be 5 keys in the returned dictionary:
269``sensors``, ``channels``, ``attributes``, ``units``, and ``triggers``.
270
271The ``sensors`` key is a dictionary mapping unique identifiers generated from
272the sensor's compatible string to the resolved values. There will always be
273exactly 1 of these since each sensor spec is required to only describe a single
274sensor (we'll see an example soon for how these are merged to create a project
275level sensor description). Each ``sensor`` will contain: ``name`` string,
276``description`` description struct, ``compatible`` struct, ``channels``
277dictionary, ``attributes`` list, and ``triggers`` list.
278
279The difference between the ``/sensors/channels`` and ``/channels`` dictionaries
280is that the former can be thought of as instantiating the latter.
281
282------------------------
283Sensor descriptor script
284------------------------
285A descriptor script is added to Pigweed via the ``pw sensor-desc`` subcommand.
286This command allows validating multiple sensor descriptors and passing the
287unified descriptor to a generator.
288
289.. list-table:: CLI Flags
290   :header-rows: 1
291
292   * - Flag(s)
293     - Description
294   * - ``--include-path``, ``-I``
295     - Directories in which to search for dependency files.
296   * - ``--verbose``, ``-v``
297     - Increase the verbosity level (can be used multiple times). Default
298       verbosity is WARNING, so additional flags increase it to INFO then DEBUG.
299   * - ``--generator``, ``-g``
300     - Generator ommand to run along with any flags. Data will be passed into
301       the generator as YAML through stdin.
302   * - ``-o``
303     - Write output to file instead of stdout.
304
305What are the include paths used for?
306====================================
307The sensor descriptor includes a ``deps`` list with file names which define
308various attributes used by the sensor. We wouldn't want to check in absolute
309paths in these lists, so instead, it's possible to list a relative path to the
310root of the project, then add include paths to the tool which will help resolve
311the dependencies. This should look familiar to header file resolution in C/C++.
312
313What is a generator?
314====================
315The sensor descriptor script validates each sensor descriptor file then creates
316a superset of all sensors and channels (making sure there aren't conflicts).
317Once complete, it will call the generator (if available) and pass the string
318YAML representation of the superset into the generator via stdin. Some ideas for
319generators:
320
321- Create a header with a list of all channels, assigning each channel a unique
322  ID.
323- Generate RST file with documentation on each supported sensor.
324- Generate stub driver implementation by knowing which channels and attributes
325  are supported.
326
327Example run (prints to stdout):
328
329.. code-block:: bash
330
331   $ pw --no-banner sensor-desc -I pw_sensor/ \
332     -g "python3 pw_sensor/py/pw_sensor/constants_generator.py --package pw.sensor" \
333     pw_sensor/sensor.yaml
334
335.. _Understanding the compatible Property: https://elinux.org/Device_Tree_Usage#Understanding_the_compatible_Property
336.. _metadata_schema.json: https://cs.opensource.google/pigweed/pigweed/+/main:pw_sensor/py/pw_sensor/metadata_schema.json
337.. _resolved_schema.json: https://cs.opensource.google/pigweed/pigweed/+/main:pw_sensor/py/pw_sensor/resolved_schema.json
338