pamoja_sensors/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! Concrete sensor drivers for the pamoja SDK.
4//!
5//! [`pamoja-gpio`](https://docs.rs/pamoja-gpio) builds the address and mode bytes a
6//! controller puts on an I2C or SPI bus; this crate is the layer just above it, the
7//! per-part knowledge that turns the raw register bytes a specific sensor returns
8//! into a physical reading. Each module is one named part: it knows that part's
9//! register map, builds the bytes that configure it, and decodes what it sends back,
10//! applying the exact conversion the manufacturer's datasheet specifies.
11//!
12//! Each module is two layers. The decode layer is pure logic with no I/O: the register
13//! map, the bytes that configure the part, and the conversion of what it sends back,
14//! so it runs on a microcontroller, on a gateway, and in a test with nothing plugged
15//! in. The driver layer (the `embedded-hal` feature, on by default) is a type named
16//! after the part that owns a bus from [`pamoja-hal`](https://docs.rs/pamoja-hal),
17//! performs the datasheet's transfer sequence, and implements the core
18//! [`Sensor`](https://docs.rs/pamoja-core/latest/pamoja_core/trait.Sensor.html)
19//! trait, so a `Bme280` reads through any `embedded-hal` I2C or SPI implementation
20//! and a `Ds18b20` through any 1-Wire bus. A driver's reading is the part's full
21//! measurement; `Sensor::map` selects one channel for a controller that wants a
22//! single number. Every driver is tested against its datasheet's own transfer
23//! sequence over a scripted bus, and a driver's error converts into the core error so
24//! the part slots into a profile or a node unchanged. The shapes the drivers share,
25//! a register bus over I2C or SPI and the 16-bit register access of the Texas
26//! Instruments parts, are in [`driver`].
27//!
28//! The conversions are anchored to each datasheet's own reference values, since they
29//! are where memory-driven bugs hide. The BME280 compensation is ported from Bosch's
30//! published reference code and cross-checked against its floating-point form; the
31//! DS18B20 decode is pinned to the datasheet's temperature/data table and its CRC to
32//! the Maxim 1-Wire polynomial; the INA219 math is checked against the datasheet's
33//! worked design example; the ADS1115 full-scale conversion against its per-gain LSB
34//! sizes. The parts added after those follow the same rule: the BMP280 port of Bosch's
35//! integer compensation against its floating-point form; the SHT3x and SCD4x
36//! conversions against the endpoints of their linear formulas, their command words
37//! against the tables, and their CRC-8 against Sensirion's published check value; the
38//! TMP117 decode against its temperature data table; the HDC1080 equations against
39//! their endpoints and identification registers; the OPT3001 lux decode against its
40//! table of worked result words and its full-scale table; the INA226 math against the
41//! datasheet's calibration example.
42//!
43//! - [`bme280`] - Bosch temperature, pressure, and humidity over I2C or SPI.
44//! - [`bmp280`] - Bosch pressure and temperature, the BME280 without humidity.
45//! - [`ds18b20`] - Maxim 1-Wire digital thermometer, with CRC-checked scratchpads.
46//! - [`hdc1080`] - Texas Instruments low-power I2C humidity and temperature sensor.
47//! - [`ina219`] - Texas Instruments high-side current, voltage, and power monitor.
48//! - [`ina226`] - Texas Instruments high- or low-side current, voltage, and power
49//! monitor with an alert pin.
50//! - [`ads1115`] - Texas Instruments 16-bit I2C analog-to-digital converter.
51//! - [`opt3001`] - Texas Instruments ambient light sensor with a human-eye response.
52//! - [`scd4x`] - Sensirion SCD40 and SCD41 photoacoustic CO2 sensors with humidity and
53//! temperature, over I2C.
54//! - [`sht3x`] - Sensirion humidity and temperature sensor, with CRC-checked words.
55//! - [`tmp117`] - Texas Instruments ±0.1 °C digital temperature sensor with alert limits.
56//!
57//! # Examples
58//!
59//! A greenhouse soil bed, read through a TMP117 that is not plugged in yet, as the one
60//! number a controller wants: `Sensor::map` picks degrees Celsius out of the part's full
61//! reading. A real bus from `pamoja-hal` takes the simulated part's place unchanged.
62//!
63//! ```
64//! use pamoja_core::Sensor;
65//! use pamoja_hal::script::{block_on, DelayLog};
66//! use pamoja_sensors::tmp117::{address, sim, Tmp117};
67//!
68//! let part = sim::reporting(address::ADD0_GND, 18.5);
69//! let thermometer = Tmp117::new(part, address::ADD0_GND, DelayLog::new());
70//! let mut celsius = thermometer.map(|reading| reading.celsius());
71//! assert_eq!(block_on(celsius.read())?, 18.5);
72//! # Ok::<(), pamoja_core::Error>(())
73//! ```
74
75#[cfg(any(feature = "embedded-hal", test))]
76extern crate alloc;
77
78pub mod ads1115;
79pub mod bme280;
80pub mod bmp280;
81pub mod ds18b20;
82pub mod hdc1080;
83pub mod ina219;
84pub mod ina226;
85pub mod opt3001;
86pub mod scd4x;
87pub mod sht3x;
88pub mod tmp117;
89
90#[cfg(feature = "embedded-hal")]
91pub mod driver;
92mod error;
93
94#[cfg(feature = "embedded-hal")]
95pub use error::DriverError;
96pub use error::SensorError;