#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(test), no_std)]
pub mod commands;
mod errors;
use core::cell::RefCell;
pub use errors::Error;
mod connection;
#[cfg(feature = "embedded-hal-async")]
mod connection_async;
#[cfg(feature = "embedded-hal")]
mod connection_sync;
mod io;
pub mod types;
use crate::connection::State;
#[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
use crate::types::Milliseconds;
#[cfg(feature = "embassy")]
use embassy_sync::mutex::Mutex;
#[derive(Debug)]
pub struct Sen6x<'a, I2C, D> {
i2c: I2C,
delay: RefCell<&'a mut D>,
state: State,
}
#[cfg(feature = "embedded-hal")]
trait SensorConnectionSync {
type I2c: embedded_hal::i2c::I2c<Error = Self::Error>;
type Delay: embedded_hal::delay::DelayNs;
type Error;
fn transaction<R>(&self, f: impl FnOnce(&mut Self::I2c) -> R) -> R;
fn delay(&self, delay: Milliseconds);
}
#[cfg(feature = "embedded-hal-async")]
trait SensorConnectionAsync {
type I2c: embedded_hal_async::i2c::I2c<Error = Self::Error>;
type Delay: embedded_hal_async::delay::DelayNs;
type Error;
async fn transaction<R>(&self, f: impl AsyncFnOnce(&mut Self::I2c) -> R) -> R;
async fn delay(&self, delay: Milliseconds);
}
trait SensorState<E> {
fn check_state(&self, valid_in: &[State]) -> Result<(), crate::Error<E>>;
fn state(&mut self) -> &mut State;
}
impl<'a, I2C, D, E> SensorState<E> for Sen6x<'a, I2C, D> {
fn check_state(&self, valid_in: &[State]) -> Result<(), crate::Error<E>> {
if !valid_in.contains(&self.state) {
return Err(crate::Error::NotAllowed);
}
Ok(())
}
fn state(&mut self) -> &mut State {
&mut self.state
}
}
#[cfg(feature = "embassy")]
impl<'a, M, I2C, D, E> SensorConnectionAsync
for Sen6x<'a, &'a embassy_sync::mutex::Mutex<M, I2C>, D>
where
I2C: embedded_hal_async::i2c::I2c<Error = E>,
M: embassy_sync::blocking_mutex::raw::RawMutex,
D: embedded_hal_async::delay::DelayNs,
{
type I2c = I2C;
type Error = E;
type Delay = D;
async fn transaction<R>(&self, f: impl AsyncFnOnce(&mut I2C) -> R) -> R {
let mut i2c = self.i2c.lock().await;
f(&mut *i2c).await
}
#[allow(clippy::await_holding_refcell_ref)]
async fn delay(&self, delay: Milliseconds) {
let mut d = self.delay.borrow_mut();
d.delay_ms(delay as u32).await;
}
}
mod sealed {
pub trait Sealed {}
#[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
impl<I2C> Sealed for &mut I2C {}
#[cfg(feature = "embassy")]
impl<M, I2C> Sealed for &embassy_sync::mutex::Mutex<M, I2C> where
M: embassy_sync::blocking_mutex::raw::RawMutex
{
}
}
#[doc(hidden)]
pub trait IntoI2cConnection<'a>: sealed::Sealed {
type Connection;
fn into_i2c_connection(self) -> Self::Connection;
}
#[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
impl<'a, I2C: 'a> IntoI2cConnection<'a> for &'a mut I2C {
type Connection = RefCell<&'a mut I2C>;
fn into_i2c_connection(self) -> Self::Connection {
RefCell::new(self)
}
}
#[cfg(feature = "embassy")]
impl<'a, M, I2C> IntoI2cConnection<'a> for &'a Mutex<M, I2C>
where
M: embassy_sync::blocking_mutex::raw::RawMutex,
{
type Connection = &'a Mutex<M, I2C>;
fn into_i2c_connection(self) -> Self::Connection {
self
}
}
impl<'a, C, D> Sen6x<'a, C, D> {
#[cfg_attr(feature = "embedded-hal", doc = "```")]
#[cfg_attr(
feature = "embedded-hal",
doc = "use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction};"
)]
#[cfg_attr(
feature = "embedded-hal",
doc = "use embedded_hal_mock::eh1::delay::NoopDelay;"
)]
#[cfg_attr(feature = "embedded-hal", doc = "use sen6x::{Sen6x, Sen66Commands};")]
#[cfg_attr(feature = "embedded-hal", doc = "")]
#[cfg_attr(
feature = "embedded-hal",
doc = "// The SEN6x lives at I²C address 0x6B; starting a measurement writes command 0x0021."
)]
#[cfg_attr(
feature = "embedded-hal",
doc = "let mut i2c = I2cMock::new(&[Transaction::write(0x6B, vec![0x00, 0x21])]);"
)]
#[cfg_attr(feature = "embedded-hal", doc = "let mut delay = NoopDelay::new();")]
#[cfg_attr(feature = "embedded-hal", doc = "")]
#[cfg_attr(
feature = "embedded-hal",
doc = "let mut sensor = Sen6x::new(&mut i2c, &mut delay);"
)]
#[cfg_attr(
feature = "embedded-hal",
doc = "sensor.start_continuous_measurement()?;"
)]
#[cfg_attr(feature = "embedded-hal", doc = "")]
#[cfg_attr(
feature = "embedded-hal",
doc = "i2c.done(); // all expected I²C traffic happened"
)]
#[cfg_attr(
feature = "embedded-hal",
doc = "# Ok::<(), sen6x::Error<embedded_hal::i2c::ErrorKind>>(())"
)]
#[cfg_attr(feature = "embedded-hal", doc = "```")]
pub fn new<I2C>(i2c: I2C, delay: &'a mut D) -> Self
where
I2C: IntoI2cConnection<'a, Connection = C>,
{
Self {
i2c: i2c.into_i2c_connection(),
delay: RefCell::new(delay),
state: State::Idle,
}
}
}
#[cfg(feature = "embedded-hal")]
pub use commands::{
Sen62Commands, Sen63cCommands, Sen65Commands, Sen66Commands, Sen68Commands, Sen69cCommands,
};
#[cfg(feature = "embedded-hal-async")]
pub use commands::{
Sen62CommandsAsync, Sen63cCommandsAsync, Sen65CommandsAsync, Sen66CommandsAsync,
Sen68CommandsAsync, Sen69cCommandsAsync,
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sen6x_is_send() {
fn assert_send<T: Send>() {}
assert_send::<Sen6x<'static, RefCell<&'static mut u8>, u8>>();
}
}