embedded_devices/devices/texas_instruments/ina219/
mod.rs

1//! The INA219 is a current shunt and power monitor with an I2C- or SMBUS-compatible interface.
2//! The device monitors both shunt voltage drop and bus supply voltage, with programmable
3//! conversion times and filtering. A programmable calibration value, combined with
4//! an internal multiplier, enables direct readouts of current in amperes. An additional
5//! multiplying register calculates power in watts. The I2C- or SMBUS-compatible
6//! interface features 16 programmable addresses.
7//!
8//! The INA219 is available in two grades: A and B. The B grade version has higher accuracy
9//! and higher precision specifications.
10//!
11//! The INA219 senses across shunts on buses that can vary from 0 to 26 V. The device uses
12//! a single 3- to 5.5-V supply, drawing a maximum of 1 mA of supply current. The INA219
13//! operates from –40°C to 125°C.
14//!
15//! ## Usage (sync)
16//!
17//! ```rust
18//! # #[cfg(feature = "sync")] mod test {
19//! # fn test<I, D>(mut i2c: I, delay: D) -> Result<(), I::Error>
20//! # where
21//! #   I: embedded_hal::i2c::I2c + embedded_hal::i2c::ErrorType,
22//! #   D: embedded_hal::delay::DelayNs
23//! # {
24//! use embedded_devices::devices::texas_instruments::ina219::{INA219Sync, address::Address, address::Pin};
25//! use embedded_devices::sensor::OneshotSensorSync;
26//! use uom::si::electric_current::{ampere, milliampere};
27//! use uom::si::electric_potential::millivolt;
28//! use uom::si::electrical_resistance::ohm;
29//! use uom::si::power::milliwatt;
30//! use uom::si::f64::{ElectricCurrent, ElectricalResistance};
31//!
32//! // Create and initialize the device
33//! let mut ina219 = INA219Sync::new_i2c(delay, i2c, Address::A0A1(Pin::Gnd, Pin::Gnd));
34//! ina219.init(
35//!   // Most units use a 100mΩ shunt resistor
36//!   ElectricalResistance::new::<ohm>(0.1),
37//!   // Maximum expected current 3A
38//!   ElectricCurrent::new::<ampere>(3.0),
39//! ).unwrap();
40//!
41//! // One-shot read all values
42//! let measurement = ina219.measure().unwrap();
43//! let bus_voltage = measurement.bus_voltage.get::<millivolt>();
44//! let current = measurement.current.get::<milliampere>();
45//! let power = measurement.power.get::<milliwatt>();
46//! println!("Current measurement: {:?}mV, {:?}mA, {:?}mW", bus_voltage, current, power);
47//! # Ok(())
48//! # }
49//! # }
50//! ```
51//!
52//! ## Usage (async)
53//!
54//! ```rust
55//! # #[cfg(feature = "async")] mod test {
56//! # async fn test<I, D>(mut i2c: I, delay: D) -> Result<(), I::Error>
57//! # where
58//! #   I: embedded_hal_async::i2c::I2c + embedded_hal_async::i2c::ErrorType,
59//! #   D: embedded_hal_async::delay::DelayNs
60//! # {
61//! use embedded_devices::devices::texas_instruments::ina219::{INA219Async, address::Address, address::Pin};
62//! use embedded_devices::sensor::OneshotSensorAsync;
63//! use uom::si::electric_current::{ampere, milliampere};
64//! use uom::si::electric_potential::millivolt;
65//! use uom::si::electrical_resistance::ohm;
66//! use uom::si::power::milliwatt;
67//! use uom::si::f64::{ElectricCurrent, ElectricalResistance};
68//!
69//! // Create and initialize the device
70//! let mut ina219 = INA219Async::new_i2c(delay, i2c, Address::A0A1(Pin::Gnd, Pin::Gnd));
71//! ina219.init(
72//!   // Most units use a 100mΩ shunt resistor
73//!   ElectricalResistance::new::<ohm>(0.1),
74//!   // Maximum expected current 3A
75//!   ElectricCurrent::new::<ampere>(3.0),
76//! ).await.unwrap();
77//!
78//! // One-shot read all values
79//! let measurement = ina219.measure().await.unwrap();
80//! let bus_voltage = measurement.bus_voltage.get::<millivolt>();
81//! let current = measurement.current.get::<milliampere>();
82//! let power = measurement.power.get::<milliwatt>();
83//! println!("Current measurement: {:?}mV, {:?}mA, {:?}mW", bus_voltage, current, power);
84//! # Ok(())
85//! # }
86//! # }
87//! ```
88
89use self::address::Address;
90
91use embedded_devices_derive::{forward_register_fns, sensor};
92use embedded_interfaces::TransportError;
93use uom::si::electric_current::ampere;
94use uom::si::electrical_resistance::ohm;
95use uom::si::f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Power};
96
97pub mod address;
98pub mod registers;
99
100#[derive(Debug, thiserror::Error)]
101pub enum MeasurementError<BusError> {
102    /// Transport error
103    #[error("transport error")]
104    Transport(#[from] TransportError<(), BusError>),
105    /// The conversion ready flag was not set within the expected time frame.
106    #[error("conversion timeout")]
107    Timeout,
108    /// Measurement was ready, but an overflow occurred. The power and
109    /// current measurement may be incorrect.
110    #[error("overflow in measurement")]
111    Overflow(Measurement),
112}
113
114#[derive(Debug, thiserror::Error)]
115pub enum ContinuousMeasurementError<BusError> {
116    /// Transport error
117    #[error("transport error")]
118    Transport(#[from] TransportError<(), BusError>),
119    /// Measurement was ready, but an overflow occurred. The power and
120    /// current measurement may be incorrect.
121    #[error("overflow in measurement")]
122    Overflow(Measurement),
123}
124
125/// Measurement data
126#[derive(Debug, embedded_devices_derive::Measurement)]
127pub struct Measurement {
128    /// Measured voltage across the shunt
129    pub shunt_voltage: ElectricPotential,
130    /// Measured voltage on the bus
131    #[measurement(Voltage)]
132    pub bus_voltage: ElectricPotential,
133    /// Measured current
134    #[measurement(Current)]
135    pub current: ElectricCurrent,
136    /// Measured power
137    #[measurement(Power)]
138    pub power: Power,
139}
140
141/// The INA219 is a 12-bit current shunt and power monitor that can sense
142/// on buses with 0-26V. It has a programmable gain amplifier to measure
143/// full-scale shunt voltage ranges from 40mV to 320mV.
144///
145/// For a full description and usage examples, refer to the [module documentation](self).
146#[maybe_async_cfg::maybe(
147    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
148    sync(feature = "sync"),
149    async(feature = "async")
150)]
151pub struct INA219<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
152    /// The delay provider
153    delay: D,
154    /// The interface to communicate with the device
155    interface: I,
156    /// Shunt resistance
157    shunt_resistance: ElectricalResistance,
158    /// Maximum expected current
159    max_expected_current: ElectricCurrent,
160    /// Configured nA/LSB for current readings
161    current_lsb_na: u32,
162}
163
164pub trait INA219Register {}
165
166#[maybe_async_cfg::maybe(
167    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
168    sync(feature = "sync"),
169    async(feature = "async")
170)]
171impl<D, I> INA219<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>>
172where
173    I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
174    D: hal::delay::DelayNs,
175{
176    /// Initializes a new device with the given address on the specified bus.
177    /// This consumes the I2C bus `I`.
178    ///
179    /// Before using this device, you should call the [`Self::init`] method which
180    /// saves the calibration values to enable current and power output.
181    #[inline]
182    pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
183        Self {
184            delay,
185            interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
186            shunt_resistance: Default::default(),
187            max_expected_current: Default::default(),
188            current_lsb_na: 1,
189        }
190    }
191}
192
193#[forward_register_fns]
194#[sensor(Voltage, Current, Power)]
195#[maybe_async_cfg::maybe(
196    idents(
197        hal(sync = "embedded_hal", async = "embedded_hal_async"),
198        RegisterInterface,
199        ResettableDevice
200    ),
201    sync(feature = "sync"),
202    async(feature = "async")
203)]
204impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> INA219<D, I> {
205    /// Soft-resets the device, calibrates it with the given shunt resistor
206    /// value and maximum expected current.
207    ///
208    /// You can change the values later using [`Self::calibrate`].
209    pub async fn init(
210        &mut self,
211        shunt_resistance: ElectricalResistance,
212        max_expected_current: ElectricCurrent,
213    ) -> Result<(), TransportError<(), I::BusError>> {
214        use crate::device::ResettableDevice;
215
216        self.reset().await?;
217        self.calibrate(shunt_resistance, max_expected_current).await?;
218        Ok(())
219    }
220
221    /// Writes the given shunt resistance and maximum expected current into
222    /// the device configuration register. This enables power and current output.
223    pub async fn calibrate(
224        &mut self,
225        shunt_resistance: ElectricalResistance,
226        max_expected_current: ElectricCurrent,
227    ) -> Result<(), TransportError<(), I::BusError>> {
228        self.shunt_resistance = shunt_resistance;
229        self.max_expected_current = max_expected_current;
230
231        let shunt_resistance = shunt_resistance.get::<ohm>();
232        let max_expected_current = max_expected_current.get::<ampere>();
233
234        self.current_lsb_na = ((1_000_000_000f64 / (1 << 15) as f64) * max_expected_current) as u32;
235        let shunt_resistance_mohm = (1_000.0 * shunt_resistance) as u32;
236
237        // Calibration Register = 0.04096 / (current_lsb (A) * shunt_resistance (Ω)),
238        // Juggling some numbers around to get a high precision calculation with u32.
239        let cal = 4_096_000_000 / (self.current_lsb_na * shunt_resistance_mohm / 10);
240        self.write_register(self::registers::Calibration::default().with_raw_value(cal as u16))
241            .await?;
242
243        Ok(())
244    }
245
246    /// Returns the currently stored measurement values without triggering a new measurement.
247    /// A timeout error cannot occur here.
248    pub async fn read_measurements(&mut self) -> Result<Measurement, MeasurementError<I::BusError>> {
249        let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
250        let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
251        let current = self.read_register::<self::registers::Current>().await?;
252        let power = self.read_register::<self::registers::Power>().await?;
253
254        let measurement = Measurement {
255            shunt_voltage: shunt_voltage.read_value(),
256            bus_voltage: bus_voltage.read_value(),
257            current: current.read_current(self.current_lsb_na),
258            power: power.read_power(self.current_lsb_na),
259        };
260
261        if bus_voltage.read_overflow() {
262            Err(MeasurementError::Overflow(measurement))
263        } else {
264            Ok(measurement)
265        }
266    }
267}
268
269#[maybe_async_cfg::maybe(
270    idents(
271        hal(sync = "embedded_hal", async = "embedded_hal_async"),
272        RegisterInterface,
273        ResettableDevice
274    ),
275    sync(feature = "sync"),
276    async(feature = "async")
277)]
278impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::device::ResettableDevice
279    for INA219<D, I>
280{
281    type Error = TransportError<(), I::BusError>;
282
283    /// Performs a soft-reset of the device, restoring internal registers to power-on reset values.
284    async fn reset(&mut self) -> Result<(), Self::Error> {
285        self.write_register(self::registers::Configuration::default().with_reset(true))
286            .await?;
287        Ok(())
288    }
289}
290
291#[maybe_async_cfg::maybe(
292    idents(
293        hal(sync = "embedded_hal", async = "embedded_hal_async"),
294        RegisterInterface,
295        OneshotSensor
296    ),
297    sync(feature = "sync"),
298    async(feature = "async")
299)]
300impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
301    for INA219<D, I>
302{
303    type Error = MeasurementError<I::BusError>;
304    type Measurement = Measurement;
305
306    /// Performs a one-shot measurement. This will set the operating mode to
307    /// [`self::registers::OperatingMode::ShuntAndBusTriggered´] and enable all conversion outputs causing the
308    /// device to perform a single conversion a return to sleep afterwards.
309    async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
310        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
311
312        // Initiate measurement
313        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::ShuntAndBusTriggered))
314            .await?;
315
316        // Wait until measurement is ready, plus 1ms extra
317        let measurement_time_us = reg_conf.read_bus_adc_resolution().conversion_time_us()
318            + reg_conf.read_shunt_adc_resolution().conversion_time_us();
319        self.delay.delay_us(1000 + measurement_time_us).await;
320
321        // Wait for the conversion ready flag to be set
322        const TRIES: u8 = 5;
323        for _ in 0..TRIES {
324            let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
325
326            if bus_voltage.read_conversion_ready() {
327                let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
328                let current = self.read_register::<self::registers::Current>().await?;
329                // Reading this register clears the conversion_ready flag
330                let power = self.read_register::<self::registers::Power>().await?;
331
332                let measurement = Measurement {
333                    shunt_voltage: shunt_voltage.read_value(),
334                    bus_voltage: bus_voltage.read_value(),
335                    current: current.read_current(self.current_lsb_na),
336                    power: power.read_power(self.current_lsb_na),
337                };
338
339                if bus_voltage.read_overflow() {
340                    return Err(MeasurementError::Overflow(measurement));
341                } else {
342                    return Ok(measurement);
343                }
344            }
345
346            self.delay.delay_us(1000).await;
347        }
348
349        Err(MeasurementError::Timeout)
350    }
351}
352
353#[maybe_async_cfg::maybe(
354    idents(
355        hal(sync = "embedded_hal", async = "embedded_hal_async"),
356        RegisterInterface,
357        ContinuousSensor
358    ),
359    sync(feature = "sync"),
360    async(feature = "async")
361)]
362impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
363    for INA219<D, I>
364{
365    type Error = ContinuousMeasurementError<I::BusError>;
366    type Measurement = Measurement;
367
368    /// Starts continuous measurement.
369    async fn start_measuring(&mut self) -> Result<(), Self::Error> {
370        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
371        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::ShuntAndBusContinuous))
372            .await?;
373        Ok(())
374    }
375
376    /// Stops continuous measurement.
377    async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
378        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
379        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::PowerDown))
380            .await?;
381        Ok(())
382    }
383
384    /// Expected amount of time between measurements in microseconds.
385    async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
386        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
387        let measurement_time_us = reg_conf.read_bus_adc_resolution().conversion_time_us()
388            + reg_conf.read_shunt_adc_resolution().conversion_time_us();
389        Ok(measurement_time_us)
390    }
391
392    /// Returns the most recent measurement. Will never return None.
393    async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
394        let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
395        let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
396        let current = self.read_register::<self::registers::Current>().await?;
397        // Reading this register clears the conversion_ready flag
398        let power = self.read_register::<self::registers::Power>().await?;
399
400        let measurement = Measurement {
401            shunt_voltage: shunt_voltage.read_value(),
402            bus_voltage: bus_voltage.read_value(),
403            current: current.read_current(self.current_lsb_na),
404            power: power.read_power(self.current_lsb_na),
405        };
406
407        if bus_voltage.read_overflow() {
408            Err(ContinuousMeasurementError::Overflow(measurement))
409        } else {
410            Ok(Some(measurement))
411        }
412    }
413
414    /// Check if new measurements are available.
415    async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
416        let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
417        Ok(bus_voltage.read_conversion_ready())
418    }
419
420    /// Wait indefinitely until new measurements are available and return them. Checks whether data
421    /// is ready in intervals of 100us.
422    async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
423        loop {
424            let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
425            if bus_voltage.read_conversion_ready() {
426                let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
427                let current = self.read_register::<self::registers::Current>().await?;
428                // Reading this register clears the conversion_ready flag
429                let power = self.read_register::<self::registers::Power>().await?;
430
431                let measurement = Measurement {
432                    shunt_voltage: shunt_voltage.read_value(),
433                    bus_voltage: bus_voltage.read_value(),
434                    current: current.read_current(self.current_lsb_na),
435                    power: power.read_power(self.current_lsb_na),
436                };
437
438                if bus_voltage.read_overflow() {
439                    return Err(ContinuousMeasurementError::Overflow(measurement));
440                } else {
441                    return Ok(measurement);
442                }
443            }
444
445            self.delay.delay_us(100).await;
446        }
447    }
448}