Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sensors

Ariel OS introduces a sensor API and a matching ecosystem of sensor drivers. This sensor API has two main goals:

  • Providing a unified way of accessing the readings from all registered sensor driver instances in a homogeneous way.
  • Making it easy and as transparent as possible to substitute a specific sensor device by a similar one from the same category.

Definitions

The sensor API and this documentation rely on the following definitions:

  • A sensor device is a device measuring one or multiple physical quantities and reporting them as one or more digital values—we call these values samples.
  • Sensor devices measuring the same physical quantity are said to be part of the same sensor category. A sensor device may be part of multiple sensor categories.
  • A measurement is the physical operation of measuring one or several physical quantities.
  • A reading is the digital result returned by a sensor device after carrying out a measurement. Samples of different physical quantities can therefore be part of the same reading.
  • A sensor driver refers to a sensor device as exposed by the sensor abstraction layer.
  • A sensor driver instance is an instance of a sensor driver.

Note

Currently, existing sensor drivers are mainly focused on sensor devices with a digital serial interface (e.g., I2C, SPI), but this is not a limitation of the sensor API: it should also accommodate analog sensor devices that require readings of the internal ADC for instance.

Ariel OS Sensor API

The core of the sensor API is the dyn-compatible ariel_os_sensors::Sensor trait. It defines a single interface that sensor drivers implement, to allow homogeneous access to their readings. The two main methods are Sensor::trigger_measurement() and Sensor::wait_for_reading() which, together, allow triggering a measurement and awaiting its reading, in a polling fashion: the measurement is explicitly triggered, but Sensor::wait_for_reading() returns a Future, which completes when the reading is available (which may take some time depending on the sensor device).

Note

In the future, the API should be expanded to provide abstractions over sensor device interrupts, specific to each sensor category.

Additionally, a registry exposes sensor driver instances that have been registered in the application, providing a single access point to them, and allowing for instance to iterate at runtime over all the sensor driver instances registered in the application.

Using Sensor Drivers through the API

This section covers the usage of existing sensor drivers.

Initializing the Sensor Driver

To use a sensor driver implementing Ariel OS sensor API, the sensor driver’s package must first be added as a dependency. Then, the sensor driver must be instantiated, and the instance registered into the registry. Additionally, sensor drivers typically require an async runner that needs to run continuously to process measurement triggers.

There is not currently an explicitly defined interface for sensor initialization, even if existing sensor drivers try to follow a common one. For this reason, see the sensor-related examples to learn how to initialize the existing sensor drivers.

Note

Sensor initialization is planned to be later abstracted over and automatically generated from SBD files. The initialization of the existing sensor drivers may need to be significantly overhauled when doing so.

Accessing the Sensor Driver Instance

Sensor driver instances are accessed through the registry: ariel_os::sensors::REGISTRY. Its sensors() method returns an iterator over all registered sensor driver instances. If a specific sensor driver instance is desired, the iterator can be filtered based on the sensor categories, obtained with Sensor::categories(), and on the sensor driver instance’s label, obtained with Sensor::label(). The label is defined when instantiating the sensor driver and allows disambiguating between multiple identical sensor devices: the label can for instance indicate where the sensor device is located or what its function is.

Triggering Measurements and Obtaining Readings

After obtaining a reference to the sensor driver instance, a measurement can be triggered and then awaited. There is no bounds on how long a measurement can take, so Sensor::wait_for_reading() returns a Future that completes when the reading is ready. The split between Sensor::trigger_measurement() and Sensor::wait_for_reading() makes it possible to trigger parallel measurements while having to await the reading of only one sensor driver instance at a time, avoiding the need for allocating multiple Futures. Note that sensor drivers may internally implement time-based polling instead of relying on sensor device interrupts to determine when samples are available.

When Sensor::wait_for_reading() successfully completes, it returns Samples, which exposes Reading::samples(), allowing to obtain an iterator over pairs of ReadingChannels and Samples. Taking into account the ReadingChannels is essential to make sense of the Samples: most importantly, they contain the scaling value to use with the Sample. Additionally, ReadingChannel provides the unit of measurement with ReadingChannel::unit(), and its Labels allow associating each Sample to the exact physical quantity it relates to, and filtering them if needed. The ReadingChannels of a sensor driver are fixed and can be obtained without triggering a measurement using Sensor::reading_channels().

Separately from the ReadingChannels, each Sample comes with SampleMetadata, which provides important auxiliary information about the sample, which may change between individual measurements. For instance, the reading channel may be disabled, either temporarily or by configuration, in which case the sample’s value does not have meaning. Additionally, it also provides the sample’s accuracy, which the sensor driver may determine for each individual sample.

The API is designed not to use floats, so that it can easily be used on the smallest microcontrollers. Floats can still be constructed and used if needed, especially for easily displaying values.