1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code, missing_docs)]
3#![no_std]
4
5use bitflags::bitflags;
6use crc::{Crc, CRC_8_NRSC_5};
7
8#[cfg(not(feature = "async"))]
9use embedded_hal as hal;
10#[cfg(feature = "async")]
11use embedded_hal_async as hal;
12
13use hal::delay::DelayNs;
14use hal::i2c::{I2c, SevenBitAddress};
15
16use weather_utils::{unit::Celsius, TemperatureAndRelativeHumidity};
17
18pub const DEFAULT_I2C_ADDRESS: SevenBitAddress = 0x38;
20
21const CHECK_STATUS_COMMAND: &[u8] = &[0b0111_0001];
22const INITIALIZATION_COMMAND: &[u8] = &[0b1011_1110, 0x08, 0x00];
23const TRIGGER_MEASUREMENT_COMMAND: &[u8] = &[0b1010_1100, 0x33, 0x00];
24const SOFT_RESET_COMMAND: &[u8] = &[0b1011_1010];
25
26#[derive(Debug)]
28pub enum Error<I2cError>
29where
30 I2cError: hal::i2c::Error,
31{
32 I2c(I2cError),
34 InvalidCrc,
36 UnexpectedBusy,
38}
39
40impl<I2cError> From<I2cError> for Error<I2cError>
41where
42 I2cError: hal::i2c::Error,
43{
44 fn from(value: I2cError) -> Self {
45 Error::I2c(value)
46 }
47}
48
49#[derive(Debug)]
50struct SensorMeasurement {
51 raw_humidity: u32,
52 raw_temperature: u32,
53}
54
55impl From<&[u8]> for SensorMeasurement {
56 fn from(data: &[u8]) -> Self {
57 let raw_humidity: u32 =
58 ((data[0] as u32) << 12) | ((data[1] as u32) << 4) | ((data[2] >> 4) as u32);
59 let raw_temperature: u32 =
60 (((data[2] & 0b0000_1111) as u32) << 16) | ((data[3] as u32) << 8) | (data[4] as u32);
61 SensorMeasurement {
62 raw_humidity,
63 raw_temperature,
64 }
65 }
66}
67
68impl SensorMeasurement {
69 pub fn humidity(&self) -> f32 {
71 ((self.raw_humidity as f32) / ((1 << 20) as f32)) * 100.0
72 }
73
74 pub fn temperature(&self) -> f32 {
76 ((self.raw_temperature as f32) / ((1 << 20) as f32)) * 200.0 - 50.0
77 }
78}
79
80impl From<SensorMeasurement> for TemperatureAndRelativeHumidity<Celsius> {
81 fn from(value: SensorMeasurement) -> Self {
82 TemperatureAndRelativeHumidity::<Celsius>::new(value.temperature(), value.humidity())
83 }
84}
85
86bitflags! {
87 struct SensorStatus: u8 {
88 const BUSY = 0b1000_0000;
89 const CALIBRATED = 0b0000_1000;
90 }
91}
92
93impl SensorStatus {
94 fn is_calibrated(&self) -> bool {
95 self.contains(SensorStatus::CALIBRATED)
96 }
97
98 fn is_ready(&self) -> bool {
99 !self.contains(SensorStatus::BUSY)
100 }
101}
102
103#[derive(Debug)]
105pub struct Aht20<I2C, D> {
106 i2c: I2C,
107 address: SevenBitAddress,
108 delay: D,
109}
110
111impl<I2C, D> Aht20<I2C, D>
112where
113 I2C: I2c,
114 D: DelayNs,
115{
116 #[maybe_async_cfg::maybe(
118 sync(not(feature = "async"), keep_self),
119 async(feature = "async", keep_self)
120 )]
121 pub async fn new(
122 i2c: I2C,
123 address: SevenBitAddress,
124 delay: D,
125 ) -> Result<Self, Error<I2C::Error>> {
126 let mut dev = Self {
127 i2c,
128 address,
129 delay,
130 };
131
132 while !dev.check_status().await?.is_calibrated() {
133 dev.send_initialize().await?;
134 dev.delay_ms(10).await;
135 }
136
137 Ok(dev)
138 }
139
140 #[maybe_async_cfg::maybe(
144 sync(not(feature = "async"), keep_self),
145 async(feature = "async", keep_self)
146 )]
147 pub async fn measure(
148 &mut self,
149 ) -> Result<TemperatureAndRelativeHumidity<Celsius>, Error<I2C::Error>> {
150 self.send_trigger_measurement().await?;
151
152 self.delay_ms(80).await;
154 while !self.check_status().await?.is_ready() {
155 self.delay_ms(1).await;
156 }
157
158 let mut buffer = [0u8; 7];
159 self.i2c
160 .read(self.address, &mut buffer)
161 .await
162 .map_err(Error::I2c)?;
163
164 let data = &buffer[..6];
165 let crc = buffer[6];
166 self.check_crc(data, crc)?;
167
168 let status = SensorStatus::from_bits_retain(buffer[0]);
169 if !status.is_ready() {
170 return Err(Error::UnexpectedBusy);
171 }
172
173 let measurement = SensorMeasurement::from(&data[1..6]);
174 Ok(measurement.into())
175 }
176
177 #[maybe_async_cfg::maybe(
180 sync(not(feature = "async"), keep_self),
181 async(feature = "async", keep_self)
182 )]
183 pub async fn soft_reset(&mut self) -> Result<(), Error<I2C::Error>> {
184 self.i2c
185 .write(self.address, SOFT_RESET_COMMAND)
186 .await
187 .map_err(Error::I2c)?;
188 self.delay_ms(20).await;
189 Ok(())
190 }
191
192 fn check_crc(&self, data: &[u8], crc_value: u8) -> Result<(), Error<I2C::Error>> {
193 let crc = Crc::<u8>::new(&CRC_8_NRSC_5);
194 let mut digest = crc.digest();
195 digest.update(data);
196 if digest.finalize() != crc_value {
197 return Err(Error::InvalidCrc);
198 }
199 Ok(())
200 }
201
202 #[maybe_async_cfg::maybe(
203 sync(not(feature = "async"), keep_self),
204 async(feature = "async", keep_self)
205 )]
206 async fn check_status(&mut self) -> Result<SensorStatus, Error<I2C::Error>> {
207 let mut buffer = [0];
208 self.i2c
209 .write_read(self.address, CHECK_STATUS_COMMAND, &mut buffer)
210 .await
211 .map_err(Error::I2c)?;
212 Ok(SensorStatus::from_bits_retain(buffer[0]))
213 }
214
215 #[maybe_async_cfg::maybe(
216 sync(not(feature = "async"), keep_self),
217 async(feature = "async", keep_self)
218 )]
219 async fn delay_ms(&mut self, duration: u32) {
220 self.delay.delay_ms(duration).await;
221 }
222
223 #[maybe_async_cfg::maybe(
224 sync(not(feature = "async"), keep_self),
225 async(feature = "async", keep_self)
226 )]
227 async fn send_initialize(&mut self) -> Result<(), Error<I2C::Error>> {
228 self.i2c
229 .write(self.address, INITIALIZATION_COMMAND)
230 .await
231 .map_err(Error::I2c)?;
232 Ok(())
233 }
234
235 #[maybe_async_cfg::maybe(
236 sync(not(feature = "async"), keep_self),
237 async(feature = "async", keep_self)
238 )]
239 async fn send_trigger_measurement(&mut self) -> Result<(), Error<I2C::Error>> {
240 self.i2c
241 .write(self.address, TRIGGER_MEASUREMENT_COMMAND)
242 .await
243 .map_err(Error::I2c)?;
244 Ok(())
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use crate::*;
251 use approx::assert_relative_eq;
252 use embedded_hal::i2c::ErrorKind;
253 use embedded_hal_mock::eh1::delay::StdSleep as Delay;
254 use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
255
256 #[test]
257 fn test_i2c_error() {
258 let error: Error<hal::i2c::ErrorKind> = hal::i2c::ErrorKind::Other.into();
259 assert!(matches!(error, Error::I2c(_)));
260 }
261
262 #[test]
263 fn test_sensor_measurement() {
264 let measurement: SensorMeasurement = [0x7b, 0xb3, 0x05, 0x9d, 0x49].as_slice().into();
265 assert_eq!(measurement.raw_humidity, 0x0007bb30);
266 assert_eq!(measurement.raw_temperature, 0x00059d49);
267 assert_relative_eq!(measurement.humidity(), 48.32, epsilon = 0.01);
268 assert_relative_eq!(measurement.temperature(), 20.18, epsilon = 0.01);
269 }
270
271 #[test]
272 fn test_aht20_creation_with_busy() {
273 let expectations = [
274 I2cTransaction::write_read(
275 DEFAULT_I2C_ADDRESS,
276 CHECK_STATUS_COMMAND.to_vec(),
277 [SensorStatus::BUSY.bits()].to_vec(),
278 ),
279 I2cTransaction::write(DEFAULT_I2C_ADDRESS, INITIALIZATION_COMMAND.to_vec()),
280 I2cTransaction::write_read(
281 DEFAULT_I2C_ADDRESS,
282 CHECK_STATUS_COMMAND.to_vec(),
283 [SensorStatus::CALIBRATED.bits()].to_vec(),
284 ),
285 ];
286 let mut i2c = I2cMock::new(&expectations);
287 let _device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
288 i2c.done();
289 }
290
291 #[test]
292 fn test_aht20_creation_with_check_status_error() {
293 let expectations = [I2cTransaction::write_read(
294 DEFAULT_I2C_ADDRESS,
295 CHECK_STATUS_COMMAND.to_vec(),
296 [SensorStatus::BUSY.bits()].to_vec(),
297 )
298 .with_error(ErrorKind::Bus)];
299 let mut i2c = I2cMock::new(&expectations);
300 let err = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
301 assert!(matches!(err, Err(Error::I2c(ErrorKind::Bus))));
302 i2c.done();
303 }
304
305 #[test]
306 fn test_aht20_creation_with_initialization_error() {
307 let expectations = [
308 I2cTransaction::write_read(
309 DEFAULT_I2C_ADDRESS,
310 CHECK_STATUS_COMMAND.to_vec(),
311 [SensorStatus::BUSY.bits()].to_vec(),
312 ),
313 I2cTransaction::write(DEFAULT_I2C_ADDRESS, INITIALIZATION_COMMAND.to_vec())
314 .with_error(ErrorKind::Other),
315 ];
316 let mut i2c = I2cMock::new(&expectations);
317 let err = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
318 assert!(matches!(err, Err(Error::I2c(ErrorKind::Other))));
319 i2c.done();
320 }
321
322 #[test]
323 fn test_measure() {
324 let expectations = [
325 I2cTransaction::write_read(
326 DEFAULT_I2C_ADDRESS,
327 CHECK_STATUS_COMMAND.to_vec(),
328 [SensorStatus::CALIBRATED.bits()].to_vec(),
329 ),
330 I2cTransaction::write(DEFAULT_I2C_ADDRESS, TRIGGER_MEASUREMENT_COMMAND.to_vec()),
331 I2cTransaction::write_read(
332 DEFAULT_I2C_ADDRESS,
333 CHECK_STATUS_COMMAND.to_vec(),
334 [SensorStatus::CALIBRATED.bits()].to_vec(),
335 ),
336 I2cTransaction::read(
337 DEFAULT_I2C_ADDRESS,
338 [
339 SensorStatus::CALIBRATED.bits(),
340 0x7b,
341 0xb3,
342 0x05,
343 0x9d,
344 0x49,
345 0x7d,
346 ]
347 .to_vec(),
348 ),
349 ];
350 let mut i2c = I2cMock::new(&expectations);
351 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
352 let measurement = device.measure().unwrap();
353 assert_relative_eq!(measurement.temperature.celsius(), 20.18, epsilon = 0.01);
354 assert_relative_eq!(measurement.relative_humidity, 48.32, epsilon = 0.01);
355 i2c.done();
356 }
357
358 #[test]
359 fn test_measure_with_trigger_measurement_error() {
360 let expectations = [
361 I2cTransaction::write_read(
362 DEFAULT_I2C_ADDRESS,
363 CHECK_STATUS_COMMAND.to_vec(),
364 [SensorStatus::CALIBRATED.bits()].to_vec(),
365 ),
366 I2cTransaction::write(DEFAULT_I2C_ADDRESS, TRIGGER_MEASUREMENT_COMMAND.to_vec())
367 .with_error(ErrorKind::ArbitrationLoss),
368 ];
369 let mut i2c = I2cMock::new(&expectations);
370 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
371 let err = device.measure().expect_err("Arbitration loss");
372 assert!(matches!(err, Error::I2c(ErrorKind::ArbitrationLoss)));
373 i2c.done();
374 }
375
376 #[test]
377 fn test_measure_with_measure_read_error() {
378 let expectations = [
379 I2cTransaction::write_read(
380 DEFAULT_I2C_ADDRESS,
381 CHECK_STATUS_COMMAND.to_vec(),
382 [SensorStatus::CALIBRATED.bits()].to_vec(),
383 ),
384 I2cTransaction::write(DEFAULT_I2C_ADDRESS, TRIGGER_MEASUREMENT_COMMAND.to_vec()),
385 I2cTransaction::write_read(
386 DEFAULT_I2C_ADDRESS,
387 CHECK_STATUS_COMMAND.to_vec(),
388 [SensorStatus::CALIBRATED.bits()].to_vec(),
389 ),
390 I2cTransaction::read(
391 DEFAULT_I2C_ADDRESS,
392 [
393 SensorStatus::CALIBRATED.bits(),
394 0x7b,
395 0xb3,
396 0x05,
397 0x9d,
398 0x49,
399 0x7d,
400 ]
401 .to_vec(),
402 )
403 .with_error(ErrorKind::ArbitrationLoss),
404 ];
405 let mut i2c = I2cMock::new(&expectations);
406 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
407 let err = device.measure().expect_err("Arbitration loss");
408 assert!(matches!(err, Error::I2c(ErrorKind::ArbitrationLoss)));
409 i2c.done();
410 }
411
412 #[test]
413 fn test_measure_with_busy_and_unexpected_busy_error() {
414 let expectations = [
415 I2cTransaction::write_read(
416 DEFAULT_I2C_ADDRESS,
417 CHECK_STATUS_COMMAND.to_vec(),
418 [SensorStatus::CALIBRATED.bits()].to_vec(),
419 ),
420 I2cTransaction::write(DEFAULT_I2C_ADDRESS, TRIGGER_MEASUREMENT_COMMAND.to_vec()),
421 I2cTransaction::write_read(
422 DEFAULT_I2C_ADDRESS,
423 CHECK_STATUS_COMMAND.to_vec(),
424 [(SensorStatus::CALIBRATED | SensorStatus::BUSY).bits()].to_vec(),
425 ),
426 I2cTransaction::write_read(
427 DEFAULT_I2C_ADDRESS,
428 CHECK_STATUS_COMMAND.to_vec(),
429 [SensorStatus::CALIBRATED.bits()].to_vec(),
430 ),
431 I2cTransaction::read(
432 DEFAULT_I2C_ADDRESS,
433 [
434 (SensorStatus::CALIBRATED | SensorStatus::BUSY).bits(),
435 0x7b,
436 0xb3,
437 0x05,
438 0x9d,
439 0x49,
440 0x91,
441 ]
442 .to_vec(),
443 ),
444 ];
445 let mut i2c = I2cMock::new(&expectations);
446 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
447 let err = device.measure().expect_err("Unexpected Busy");
448 assert!(matches!(err, Error::UnexpectedBusy));
449 i2c.done();
450 }
451
452 #[test]
453 fn test_soft_reset() {
454 let expectations = [
455 I2cTransaction::write_read(
456 DEFAULT_I2C_ADDRESS,
457 CHECK_STATUS_COMMAND.to_vec(),
458 [SensorStatus::CALIBRATED.bits()].to_vec(),
459 ),
460 I2cTransaction::write(DEFAULT_I2C_ADDRESS, SOFT_RESET_COMMAND.to_vec()),
461 ];
462 let mut i2c = I2cMock::new(&expectations);
463 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
464 device.soft_reset().unwrap();
465 i2c.done();
466 }
467
468 #[test]
469 fn test_soft_reset_with_error() {
470 let expectations = [
471 I2cTransaction::write_read(
472 DEFAULT_I2C_ADDRESS,
473 CHECK_STATUS_COMMAND.to_vec(),
474 [SensorStatus::CALIBRATED.bits()].to_vec(),
475 ),
476 I2cTransaction::write(DEFAULT_I2C_ADDRESS, SOFT_RESET_COMMAND.to_vec())
477 .with_error(ErrorKind::Overrun),
478 ];
479 let mut i2c = I2cMock::new(&expectations);
480 let mut device = Aht20::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
481 let err = device.soft_reset().expect_err("Overrun");
482 assert!(matches!(err, Error::I2c(ErrorKind::Overrun)));
483 i2c.done();
484 }
485}