embedded_devices/devices/texas_instruments/tmp102/
mod.rs

1//! The TMP102 device is a digital temperature sensor designed for NTC/PTC
2//! thermistor replacement where high accuracy is required. The device offers an
3//! accuracy of ±0.5°C without requiring calibration or external component
4//! signal conditioning. Device temperature sensors are highly linear and do not
5//! require complex calculations or lookup tables to derive the temperature. The
6//! on-chip 12-bit ADC offers resolutions down to 0.0625°C.
7//!
8//! ## Usage (sync)
9//!
10//! ```rust, no_run
11//! # #[cfg(feature = "sync")] mod test {
12//! # fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_interfaces::TransportError<(), I::Error>>
13//! # where
14//! #   I: embedded_hal::i2c::I2c + embedded_hal::i2c::ErrorType,
15//! #   D: embedded_hal::delay::DelayNs
16//! # {
17//! use embedded_devices::devices::texas_instruments::tmp102::{TMP102Sync, address::Address, registers::Temperature};
18//! use embedded_devices::sensor::OneshotSensorSync;
19//! use uom::si::thermodynamic_temperature::degree_celsius;
20//!
21//! // Create and initialize the device. Default conversion mode is continuous.
22//! let mut tmp102 = TMP102Sync::new_i2c(delay, i2c, Address::Gnd);
23//!
24//! // Read the latest temperature conversion in °C
25//! let temp = tmp102
26//!     .read_temperature()?
27//!     .get::<degree_celsius>();
28//! println!("Current temperature: {:?}°C", temp);
29//!
30//! // Perform a one-shot measurement now and return to sleep afterwards.
31//! let temp = tmp102.measure()?
32//!     .temperature.get::<degree_celsius>();
33//! println!("Oneshot temperature: {:?}°C", temp);
34//! # Ok(())
35//! # }
36//! # }
37//! ```
38//!
39//! ## Usage (async)
40//!
41//! ```rust, no_run
42//! # #[cfg(feature = "async")] mod test {
43//! # async fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_interfaces::TransportError<(), I::Error>>
44//! # where
45//! #   I: embedded_hal_async::i2c::I2c + embedded_hal_async::i2c::ErrorType,
46//! #   D: embedded_hal_async::delay::DelayNs
47//! # {
48//! use embedded_devices::devices::texas_instruments::tmp102::{TMP102Async, address::Address, registers::Temperature};
49//! use embedded_devices::sensor::OneshotSensorAsync;
50//! use uom::si::thermodynamic_temperature::degree_celsius;
51//!
52//! // Create and initialize the device. Default conversion mode is continuous.
53//! let mut tmp102 = TMP102Async::new_i2c(delay, i2c, Address::Gnd);
54//!
55//! // Read the latest temperature conversion in °C
56//! let temp = tmp102
57//!     .read_temperature().await?
58//!     .get::<degree_celsius>();
59//! println!("Current temperature: {:?}°C", temp);
60//!
61//! // Perform a one-shot measurement now and return to sleep afterwards.
62//! let temp = tmp102.measure().await?
63//!     .temperature.get::<degree_celsius>();
64//! println!("Oneshot temperature: {:?}°C", temp);
65//! # Ok(())
66//! # }
67//! # }
68//! ```
69
70use embedded_devices_derive::{forward_register_fns, sensor};
71use embedded_interfaces::TransportError;
72use uom::si::f64::ThermodynamicTemperature;
73use uom::si::thermodynamic_temperature::degree_celsius;
74
75pub mod address;
76pub mod registers;
77
78use self::registers::{Configuration, Temperature};
79
80/// Measurement data
81#[derive(Debug, embedded_devices_derive::Measurement)]
82pub struct Measurement {
83    /// Measured temperature
84    #[measurement(Temperature)]
85    pub temperature: ThermodynamicTemperature,
86}
87
88/// The TMP102 is a general purpose digital temperature sensor. It provides a 13-bit
89/// temperature result with a resolution of 0.0625 °C and an accuracy of up to ±3 °C across the
90/// temperature range of –40 °C to 125 °C with no calibration.
91///
92/// For a full description and usage examples, refer to the [module documentation](self).
93#[maybe_async_cfg::maybe(
94    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
95    sync(feature = "sync"),
96    async(feature = "async")
97)]
98pub struct TMP102<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
99    /// The delay provider
100    delay: D,
101    /// The interface to communicate with the device
102    interface: I,
103}
104
105pub trait TMP102Register {}
106
107#[maybe_async_cfg::maybe(
108    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
109    sync(feature = "sync"),
110    async(feature = "async")
111)]
112impl<D, I> TMP102<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>>
113where
114    I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
115    D: hal::delay::DelayNs,
116{
117    /// Initializes a new device with the given address on the specified bus.
118    /// This consumes the I2C bus `I`.
119    #[inline]
120    pub fn new_i2c(delay: D, interface: I, address: self::address::Address) -> Self {
121        Self {
122            delay,
123            interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
124        }
125    }
126}
127
128#[forward_register_fns]
129#[sensor(Temperature)]
130#[maybe_async_cfg::maybe(
131    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
132    sync(feature = "sync"),
133    async(feature = "async")
134)]
135impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> TMP102<D, I> {
136    /// Read the last temperature measured
137    pub async fn read_temperature(&mut self) -> Result<ThermodynamicTemperature, TransportError<(), I::BusError>> {
138        let reg_conf = self.read_register::<Configuration>().await?;
139
140        // Read and return the temperature
141        let raw_temp = self.read_register::<Temperature>().await?.read_raw_temperature() as i32;
142        if reg_conf.read_extended() {
143            Ok(ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 128.0))
144        } else {
145            Ok(ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 256.0))
146        }
147    }
148}
149
150#[maybe_async_cfg::maybe(
151    idents(
152        hal(sync = "embedded_hal", async = "embedded_hal_async"),
153        RegisterInterface,
154        OneshotSensor
155    ),
156    sync(feature = "sync"),
157    async(feature = "async")
158)]
159impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
160    for TMP102<D, I>
161{
162    type Error = TransportError<(), I::BusError>;
163    type Measurement = Measurement;
164
165    /// Performs a one-shot measurement. This will set `shutdown` in [`self::registers::Configuration´].
166    /// which will cause the device to perform a single conversion a return to sleep mode afterwards.
167    ///
168    /// This function will initialize the measurement, wait until the data is acquired and return
169    /// the temperature.
170    async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
171        let reg_conf = self.read_register::<Configuration>().await?;
172        self.write_register(reg_conf.with_oneshot(true).with_shutdown(false))
173            .await?;
174
175        // Wait for the duration of the conversion
176        self.delay.delay_us(12_500).await;
177
178        let raw_temp = self.read_register::<Temperature>().await?.read_raw_temperature() as i32;
179        if reg_conf.read_extended() {
180            Ok(Measurement {
181                temperature: ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 128.0),
182            })
183        } else {
184            Ok(Measurement {
185                temperature: ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 256.0),
186            })
187        }
188    }
189}
190
191#[maybe_async_cfg::maybe(
192    idents(
193        hal(sync = "embedded_hal", async = "embedded_hal_async"),
194        RegisterInterface,
195        ContinuousSensor
196    ),
197    sync(feature = "sync"),
198    async(feature = "async")
199)]
200impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
201    for TMP102<D, I>
202{
203    type Error = TransportError<(), I::BusError>;
204    type Measurement = Measurement;
205
206    /// Starts continuous measurement.
207    async fn start_measuring(&mut self) -> Result<(), Self::Error> {
208        let reg_conf = self.read_register::<Configuration>().await?;
209        self.write_register(reg_conf.with_oneshot(false).with_shutdown(false))
210            .await?;
211        Ok(())
212    }
213
214    /// Stops continuous measurement.
215    async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
216        let reg_conf = self.read_register::<Configuration>().await?;
217        self.write_register(reg_conf.with_oneshot(false).with_shutdown(true))
218            .await?;
219        Ok(())
220    }
221
222    /// Expected amount of time between measurements in microseconds.
223    async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
224        let reg_conf = self.read_register::<Configuration>().await?;
225        let conversion_time_us = reg_conf.read_conversion_cycle_time().conversion_time_ms() * 1000;
226        Ok(conversion_time_us)
227    }
228
229    /// Returns the most recent measurement. Will never return None.
230    async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
231        let raw_temp = self.read_register::<Temperature>().await?.read_raw_temperature() as i32;
232        let reg_conf = self.read_register::<Configuration>().await?;
233        if reg_conf.read_extended() {
234            Ok(Some(Measurement {
235                temperature: ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 128.0),
236            }))
237        } else {
238            Ok(Some(Measurement {
239                temperature: ThermodynamicTemperature::new::<degree_celsius>(raw_temp as f64 / 256.0),
240            }))
241        }
242    }
243
244    /// Not supported, always returns true.
245    async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
246        Ok(true)
247    }
248
249    /// Opportunistically waits one conversion interval and returns the measurement.
250    async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
251        let interval = self.measurement_interval_us().await?;
252        self.delay.delay_us(interval).await;
253        self.current_measurement()
254            .await?
255            .ok_or_else(|| TransportError::Unexpected("measurement was not ready even though we expected it to be"))
256    }
257}