1#![cfg_attr(not(test), no_std)]
21
22pub mod commands;
23
24mod errors;
25
26use core::cell::RefCell;
27pub use errors::Error;
28
29mod connection;
30#[cfg(feature = "embedded-hal-async")]
31mod connection_async;
32#[cfg(feature = "embedded-hal")]
33mod connection_sync;
34mod io;
35pub mod types;
36
37use crate::connection::State;
38#[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
39use crate::types::Milliseconds;
40#[cfg(feature = "embassy")]
41use embassy_sync::mutex::Mutex;
42
43#[derive(Debug)]
56pub struct Sen6x<'a, I2C, D> {
57 i2c: I2C,
58 delay: RefCell<&'a mut D>,
59 state: State,
60}
61
62#[cfg(feature = "embedded-hal")]
63trait SensorConnectionSync {
64 type I2c: embedded_hal::i2c::I2c<Error = Self::Error>;
65 type Delay: embedded_hal::delay::DelayNs;
66 type Error;
67 fn transaction<R>(&self, f: impl FnOnce(&mut Self::I2c) -> R) -> R;
68 fn delay(&self, delay: Milliseconds);
69}
70
71#[cfg(feature = "embedded-hal-async")]
72trait SensorConnectionAsync {
73 type I2c: embedded_hal_async::i2c::I2c<Error = Self::Error>;
74 type Delay: embedded_hal_async::delay::DelayNs;
75 type Error;
76 async fn transaction<R>(&self, f: impl AsyncFnOnce(&mut Self::I2c) -> R) -> R;
77 async fn delay(&self, delay: Milliseconds);
78}
79
80trait SensorState<E> {
81 fn check_state(&self, valid_in: &[State]) -> Result<(), crate::Error<E>>;
82
83 fn state(&mut self) -> &mut State;
84}
85
86impl<'a, I2C, D, E> SensorState<E> for Sen6x<'a, I2C, D> {
87 fn check_state(&self, valid_in: &[State]) -> Result<(), crate::Error<E>> {
88 if !valid_in.contains(&self.state) {
89 return Err(crate::Error::NotAllowed);
90 }
91 Ok(())
92 }
93
94 fn state(&mut self) -> &mut State {
95 &mut self.state
96 }
97}
98
99#[cfg(feature = "embassy")]
100impl<'a, M, I2C, D, E> SensorConnectionAsync
101 for Sen6x<'a, &'a embassy_sync::mutex::Mutex<M, I2C>, D>
102where
103 I2C: embedded_hal_async::i2c::I2c<Error = E>,
104 M: embassy_sync::blocking_mutex::raw::RawMutex,
105 D: embedded_hal_async::delay::DelayNs,
106{
107 type I2c = I2C;
108 type Error = E;
109 type Delay = D;
110
111 async fn transaction<R>(&self, f: impl AsyncFnOnce(&mut I2C) -> R) -> R {
112 let mut i2c = self.i2c.lock().await;
113 f(&mut *i2c).await
114 }
115
116 #[allow(clippy::await_holding_refcell_ref)]
120 async fn delay(&self, delay: Milliseconds) {
121 let mut d = self.delay.borrow_mut();
122 d.delay_ms(delay as u32).await;
123 }
124}
125
126mod sealed {
127 pub trait Sealed {}
128 #[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
129 impl<I2C> Sealed for &mut I2C {}
130 #[cfg(feature = "embassy")]
131 impl<M, I2C> Sealed for &embassy_sync::mutex::Mutex<M, I2C> where
132 M: embassy_sync::blocking_mutex::raw::RawMutex
133 {
134 }
135}
136
137#[doc(hidden)]
140pub trait IntoI2cConnection<'a>: sealed::Sealed {
141 type Connection;
142 fn into_i2c_connection(self) -> Self::Connection;
143}
144
145#[cfg(any(feature = "embedded-hal", feature = "embedded-hal-async"))]
146impl<'a, I2C: 'a> IntoI2cConnection<'a> for &'a mut I2C {
147 type Connection = RefCell<&'a mut I2C>;
148 fn into_i2c_connection(self) -> Self::Connection {
149 RefCell::new(self)
150 }
151}
152
153#[cfg(feature = "embassy")]
154impl<'a, M, I2C> IntoI2cConnection<'a> for &'a Mutex<M, I2C>
155where
156 M: embassy_sync::blocking_mutex::raw::RawMutex,
157{
158 type Connection = &'a Mutex<M, I2C>;
159 fn into_i2c_connection(self) -> Self::Connection {
160 self
161 }
162}
163
164impl<'a, C, D> Sen6x<'a, C, D> {
165 #[cfg_attr(feature = "embedded-hal", doc = "```")]
176 #[cfg_attr(
177 feature = "embedded-hal",
178 doc = "use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction};"
179 )]
180 #[cfg_attr(
181 feature = "embedded-hal",
182 doc = "use embedded_hal_mock::eh1::delay::NoopDelay;"
183 )]
184 #[cfg_attr(feature = "embedded-hal", doc = "use sen6x::{Sen6x, Sen66Commands};")]
185 #[cfg_attr(feature = "embedded-hal", doc = "")]
186 #[cfg_attr(
187 feature = "embedded-hal",
188 doc = "// The SEN6x lives at I²C address 0x6B; starting a measurement writes command 0x0021."
189 )]
190 #[cfg_attr(
191 feature = "embedded-hal",
192 doc = "let mut i2c = I2cMock::new(&[Transaction::write(0x6B, vec![0x00, 0x21])]);"
193 )]
194 #[cfg_attr(feature = "embedded-hal", doc = "let mut delay = NoopDelay::new();")]
195 #[cfg_attr(feature = "embedded-hal", doc = "")]
196 #[cfg_attr(
197 feature = "embedded-hal",
198 doc = "let mut sensor = Sen6x::new(&mut i2c, &mut delay);"
199 )]
200 #[cfg_attr(
201 feature = "embedded-hal",
202 doc = "sensor.start_continuous_measurement()?;"
203 )]
204 #[cfg_attr(feature = "embedded-hal", doc = "")]
205 #[cfg_attr(
206 feature = "embedded-hal",
207 doc = "i2c.done(); // all expected I²C traffic happened"
208 )]
209 #[cfg_attr(
210 feature = "embedded-hal",
211 doc = "# Ok::<(), sen6x::Error<embedded_hal::i2c::ErrorKind>>(())"
212 )]
213 #[cfg_attr(feature = "embedded-hal", doc = "```")]
214 pub fn new<I2C>(i2c: I2C, delay: &'a mut D) -> Self
215 where
216 I2C: IntoI2cConnection<'a, Connection = C>,
217 {
218 Self {
219 i2c: i2c.into_i2c_connection(),
220 delay: RefCell::new(delay),
221 state: State::Idle,
222 }
223 }
224}
225
226#[cfg(feature = "embedded-hal")]
227pub use commands::{
228 Sen62Commands, Sen63cCommands, Sen65Commands, Sen66Commands, Sen68Commands, Sen69cCommands,
229};
230#[cfg(feature = "embedded-hal-async")]
231pub use commands::{
232 Sen62CommandsAsync, Sen63cCommandsAsync, Sen65CommandsAsync, Sen66CommandsAsync,
233 Sen68CommandsAsync, Sen69cCommandsAsync,
234};
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 #[test]
240 fn sen6x_is_send() {
241 fn assert_send<T: Send>() {}
242 assert_send::<Sen6x<'static, RefCell<&'static mut u8>, u8>>();
243 }
244}