use self::commands::{
DeviceReset, GetDataReady, ReadMeasuredValuesMassConcentrationOnly, StartContinuousMeasurement, StopMeasurement,
};
use commands::DataReadyStatus;
use embedded_devices_derive::{forward_command_fns, sensor};
use uom::si::f64::MassConcentration;
use super::commands::Crc8Error;
pub mod address;
pub mod commands;
pub type TransportError<E> = embedded_interfaces::TransportError<Crc8Error, E>;
#[derive(Debug, embedded_devices_derive::Measurement)]
pub struct Measurement {
#[measurement(Pm1Concentration)]
pub pm1_concentration: Option<MassConcentration>,
#[measurement(Pm2_5Concentration)]
pub pm2_5_concentration: Option<MassConcentration>,
#[measurement(Pm4Concentration)]
pub pm4_concentration: Option<MassConcentration>,
#[measurement(Pm10Concentration)]
pub pm10_concentration: Option<MassConcentration>,
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
CommandInterface,
I2cDevice
),
sync(feature = "sync"),
async(feature = "async")
)]
pub struct SEN60<D: hal::delay::DelayNs, I: embedded_interfaces::commands::CommandInterface> {
delay: D,
interface: I,
}
pub trait SEN60Command {}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D, I> SEN60<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: self::address::Address) -> Self {
Self {
delay,
interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
}
}
}
#[forward_command_fns]
#[sensor(Pm1Concentration, Pm2_5Concentration, Pm4Concentration, Pm10Concentration)]
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
CommandInterface,
ResettableDevice
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::commands::CommandInterface> SEN60<D, I> {
pub async fn init(&mut self) -> Result<(), TransportError<I::BusError>> {
use crate::device::ResettableDevice;
self.delay.delay_ms(100).await;
self.reset().await?;
Ok(())
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
CommandInterface,
ResettableDevice
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::commands::CommandInterface> crate::device::ResettableDevice
for SEN60<D, I>
{
type Error = TransportError<I::BusError>;
async fn reset(&mut self) -> Result<(), Self::Error> {
let _ = self.execute::<StopMeasurement>(()).await;
self.execute::<DeviceReset>(()).await?;
Ok(())
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
CommandInterface,
ContinuousSensor
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::commands::CommandInterface> crate::sensor::ContinuousSensor
for SEN60<D, I>
{
type Error = TransportError<I::BusError>;
type Measurement = Measurement;
async fn start_measuring(&mut self) -> Result<(), Self::Error> {
self.execute::<StartContinuousMeasurement>(()).await?;
Ok(())
}
async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
self.execute::<StopMeasurement>(()).await?;
Ok(())
}
async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
Ok(1_000_000)
}
async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
let measurement = self.execute::<ReadMeasuredValuesMassConcentrationOnly>(()).await?;
Ok(Some(Measurement {
pm1_concentration: (measurement.read_raw_mass_concentration_pm1() != u16::MAX)
.then(|| measurement.read_mass_concentration_pm1()),
pm2_5_concentration: (measurement.read_raw_mass_concentration_pm2_5() != u16::MAX)
.then(|| measurement.read_mass_concentration_pm2_5()),
pm4_concentration: (measurement.read_raw_mass_concentration_pm4() != u16::MAX)
.then(|| measurement.read_mass_concentration_pm4()),
pm10_concentration: (measurement.read_raw_mass_concentration_pm10() != u16::MAX)
.then(|| measurement.read_mass_concentration_pm10()),
}))
}
async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
Ok(self.execute::<GetDataReady>(()).await?.read_data_ready() == DataReadyStatus::Ready)
}
async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
loop {
if self.is_measurement_ready().await? {
return self.current_measurement().await?.ok_or_else(|| {
TransportError::Unexpected("measurement was not ready even though we expected it to be")
});
}
self.delay.delay_ms(100).await;
}
}
}