use self::address::Address;
use embedded_devices_derive::{forward_register_fns, sensor};
use embedded_interfaces::TransportError;
use registers::AdcRange;
use uom::si::electric_current::ampere;
use uom::si::electric_potential::volt;
use uom::si::electrical_resistance::ohm;
use uom::si::f64::ThermodynamicTemperature;
use uom::si::f64::{ElectricCharge, ElectricCurrent, ElectricPotential, ElectricalResistance, Energy, Power};
pub mod address;
pub mod registers;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, thiserror::Error)]
pub enum InitError<BusError> {
#[error("transport error")]
Transport(#[from] TransportError<(), BusError>),
#[error("invalid device id {0:#04x}")]
InvalidDeviceId(u16),
#[error("invalid manufacturer id {0:#04x}")]
InvalidManufacturerId(u16),
}
#[derive(Debug, thiserror::Error)]
pub enum MeasurementError<BusError> {
#[error("transport error")]
Transport(#[from] TransportError<(), BusError>),
#[error("conversion timeout")]
Timeout,
#[error("overflow in measurement")]
Overflow(Measurement),
}
#[derive(Debug, thiserror::Error)]
pub enum ContinuousMeasurementError<BusError> {
#[error("transport error")]
Transport(#[from] TransportError<(), BusError>),
#[error("overflow in measurement")]
Overflow(Measurement),
}
#[derive(Debug, embedded_devices_derive::Measurement)]
pub struct Measurement {
pub shunt_voltage: ElectricPotential,
#[measurement(Voltage)]
pub bus_voltage: ElectricPotential,
#[measurement(Temperature)]
pub temperature: ThermodynamicTemperature,
#[measurement(Current)]
pub current: ElectricCurrent,
#[measurement(Power)]
pub power: Power,
#[measurement(Energy)]
pub energy: Energy,
#[measurement(Charge)]
pub charge: ElectricCharge,
}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
sync(feature = "sync"),
async(feature = "async")
)]
pub struct INA228<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
delay: D,
interface: I,
shunt_resistance: ElectricalResistance,
max_expected_current: ElectricCurrent,
pub current_lsb_na: i64,
pub adc_range: self::registers::AdcRange,
}
pub trait INA228Register {}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D, I> INA228<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>>
where
I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
D: hal::delay::DelayNs,
{
#[inline]
pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
Self {
delay,
interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
shunt_resistance: Default::default(),
max_expected_current: Default::default(),
current_lsb_na: 1,
adc_range: AdcRange::Div4,
}
}
}
#[forward_register_fns]
#[sensor(Voltage, Temperature, Current, Power, Energy, Charge)]
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
ResettableDevice
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> INA228<D, I> {
pub async fn init(
&mut self,
shunt_resistance: ElectricalResistance,
max_expected_current: ElectricCurrent,
) -> Result<(), InitError<I::BusError>> {
use crate::device::ResettableDevice;
use registers::{DeviceId, ManufacturerId};
self.reset().await?;
self.delay.delay_us(500).await;
let manufacturer_id = self.read_register::<ManufacturerId>().await?.read_id();
if manufacturer_id != ManufacturerId::default().read_id() {
return Err(InitError::InvalidManufacturerId(manufacturer_id));
}
let device_id = self.read_register::<DeviceId>().await?.read_id();
if device_id != DeviceId::default().read_id() {
return Err(InitError::InvalidDeviceId(device_id));
}
self.calibrate(shunt_resistance, max_expected_current).await?;
Ok(())
}
pub async fn calibrate(
&mut self,
shunt_resistance: ElectricalResistance,
max_expected_current: ElectricCurrent,
) -> Result<(), TransportError<(), I::BusError>> {
self.shunt_resistance = shunt_resistance;
self.max_expected_current = max_expected_current;
let max_expected_shunt_voltage = shunt_resistance * max_expected_current;
let shunt_resistance = shunt_resistance.get::<ohm>();
let max_expected_current = max_expected_current.get::<ampere>();
let max_expected_shunt_voltage = max_expected_shunt_voltage.get::<volt>();
self.current_lsb_na = ((1_000_000_000f64 / (1 << 19) as f64) * max_expected_current) as i64;
let shunt_resistance_mohm = (1_000.0 * shunt_resistance) as i64;
let div4_threshold = 0.036; self.adc_range = if max_expected_shunt_voltage > div4_threshold {
self::registers::AdcRange::Div4
} else {
self::registers::AdcRange::Div1
};
let reg_conf = self.read_register::<self::registers::Configuration>().await?;
self.write_register(reg_conf.with_adc_range(self.adc_range)).await?;
let cal = 524_288 * self.current_lsb_na * shunt_resistance_mohm / (10_000_000 * self.adc_range.factor() as i64);
self.write_register(self::registers::ShuntCalibration::default().with_raw_value(cal as u16))
.await?;
Ok(())
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
ResettableDevice
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::device::ResettableDevice
for INA228<D, I>
{
type Error = TransportError<(), I::BusError>;
async fn reset(&mut self) -> Result<(), Self::Error> {
self.write_register(self::registers::Configuration::default().with_reset(true))
.await?;
Ok(())
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
OneshotSensor
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
for INA228<D, I>
{
type Error = MeasurementError<I::BusError>;
type Measurement = Measurement;
async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
self.write_register(
reg_adc_conf
.with_operating_mode(self::registers::OperatingMode::Triggered)
.with_enable_temperature(true)
.with_enable_shunt(true)
.with_enable_bus(true),
)
.await?;
let measurement_time_us = reg_adc_conf.read_bus_conversion_time().us()
+ reg_adc_conf.read_shunt_conversion_time().us()
+ reg_adc_conf.read_temperature_conversion_time().us();
self.delay
.delay_us(100 + measurement_time_us * reg_adc_conf.read_average_count().factor() as u32)
.await;
const TRIES: u8 = 5;
for _ in 0..TRIES {
let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
if diag.read_conversion_ready() {
let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
let temperature = self.read_register::<self::registers::Temperature>().await?;
let current = self.read_register::<self::registers::Current>().await?;
let energy = self.read_register::<self::registers::Energy>().await?;
let charge = self.read_register::<self::registers::Charge>().await?;
let power = self.read_register::<self::registers::Power>().await?;
let measurement = Measurement {
shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
bus_voltage: bus_voltage.read_value(),
temperature: temperature.read_value(),
current: current.read_current(self.current_lsb_na),
power: power.read_power(self.current_lsb_na),
energy: energy.read_energy(self.current_lsb_na),
charge: charge.read_charge(self.current_lsb_na),
};
if diag.read_math_overflow() {
return Err(MeasurementError::Overflow(measurement));
} else {
return Ok(measurement);
}
}
self.delay.delay_us(100).await;
}
Err(MeasurementError::Timeout)
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
ContinuousSensor
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
for INA228<D, I>
{
type Error = ContinuousMeasurementError<I::BusError>;
type Measurement = Measurement;
async fn start_measuring(&mut self) -> Result<(), Self::Error> {
let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
self.write_register(
reg_adc_conf
.with_operating_mode(self::registers::OperatingMode::Continuous)
.with_enable_temperature(true)
.with_enable_shunt(true)
.with_enable_bus(true),
)
.await?;
Ok(())
}
async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
self.write_register(
reg_adc_conf
.with_operating_mode(self::registers::OperatingMode::Continuous)
.with_enable_temperature(false)
.with_enable_shunt(false)
.with_enable_bus(false),
)
.await?;
Ok(())
}
async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
let measurement_time_us = reg_adc_conf.read_bus_conversion_time().us()
+ reg_adc_conf.read_shunt_conversion_time().us()
+ reg_adc_conf.read_temperature_conversion_time().us();
Ok(measurement_time_us)
}
async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
let temperature = self.read_register::<self::registers::Temperature>().await?;
let current = self.read_register::<self::registers::Current>().await?;
let energy = self.read_register::<self::registers::Energy>().await?;
let charge = self.read_register::<self::registers::Charge>().await?;
let power = self.read_register::<self::registers::Power>().await?;
let measurement = Measurement {
shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
bus_voltage: bus_voltage.read_value(),
temperature: temperature.read_value(),
current: current.read_current(self.current_lsb_na),
power: power.read_power(self.current_lsb_na),
energy: energy.read_energy(self.current_lsb_na),
charge: charge.read_charge(self.current_lsb_na),
};
if diag.read_math_overflow() {
Err(ContinuousMeasurementError::Overflow(measurement))
} else {
Ok(Some(measurement))
}
}
async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
Ok(diag.read_conversion_ready())
}
async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
loop {
let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
if diag.read_conversion_ready() {
let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
let temperature = self.read_register::<self::registers::Temperature>().await?;
let current = self.read_register::<self::registers::Current>().await?;
let energy = self.read_register::<self::registers::Energy>().await?;
let charge = self.read_register::<self::registers::Charge>().await?;
let power = self.read_register::<self::registers::Power>().await?;
let measurement = Measurement {
shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
bus_voltage: bus_voltage.read_value(),
temperature: temperature.read_value(),
current: current.read_current(self.current_lsb_na),
power: power.read_power(self.current_lsb_na),
energy: energy.read_energy(self.current_lsb_na),
charge: charge.read_charge(self.current_lsb_na),
};
if diag.read_math_overflow() {
return Err(ContinuousMeasurementError::Overflow(measurement));
} else {
return Ok(measurement);
}
}
self.delay.delay_us(100).await;
}
}
}