aht20_driver/
lib.rs

1#![cfg_attr(not(test), no_std)]
2//! AHT20 driver.
3//!
4//! Example:
5//!
6//!     # use embedded_hal_mock::eh1::delay::NoopDelay as MockDelay;
7//!     # use embedded_hal_mock::eh1::i2c::Mock as I2cMock;
8//!     # use embedded_hal_mock::eh1::i2c::Transaction;
9//!     # use aht20_driver::{AHT20, AHT20Initialized, Command, SENSOR_ADDRESS};
10//!     # let expectations = vec![
11//!     #     // check_status immediately succeeds, we don't need to send Initialize.
12//!     #     Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
13//!     #     // send_trigger_measurement
14//!     #     Transaction::write(
15//!     #         SENSOR_ADDRESS,
16//!     #         vec![
17//!     #             Command::TriggerMeasurement as u8,
18//!     #             0b0011_0011, // 0x33
19//!     #             0b0000_0000, // 0x00
20//!     #         ],
21//!     #     ),
22//!     #     // check_status - with ready bit set to 'ready' (off)
23//!     #     Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
24//!     #     // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
25//!     #     // These are taken from a run of the sensor.
26//!     #     Transaction::read(
27//!     #         SENSOR_ADDRESS,
28//!     #         vec![
29//!     #             0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
30//!     #             //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
31//!     #             //             and 3 are described as 'reserved'.
32//!     #             0b0110_0101, // 101, 0x65 - first byte of humidity value
33//!     #             0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
34//!     #             0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
35//!     #             0b1100_1101, // 205, 0xcd - first full byte of temperature.
36//!     #             0b0010_0110, //  38, 0x26 - second full byte of temperature.
37//!     #             0b1100_0110, // 198, 0xc6 - CRC
38//!     #         ],
39//!     #     ),
40//!     # ];
41//!     # let mock_i2c = I2cMock::new(&expectations);
42//!     # let mut mock_delay = MockDelay::new();
43//!     let mut aht20_uninit = AHT20::new(mock_i2c, SENSOR_ADDRESS);
44//!     let mut aht20 = aht20_uninit.init(&mut mock_delay).unwrap();
45//!     let measurement = aht20.measure(&mut mock_delay).unwrap();
46//!
47//!     println!("temperature (aht20): {:.2}C", measurement.temperature);
48//!     println!("humidity (aht20): {:.2}%", measurement.humidity);
49//!
50//!     aht20_uninit.destroy().done();
51//!
52//! [AHT20 Datasheet](https://cdn-learn.adafruit.com/assets/assets/000/091/676/original/AHT20-datasheet-2020-4-16.pdf?1591047915)
53//!
54//! Note that the datasheet linked directly from the manufacturer's website
55//! [Aogong AHT20](http://www.aosong.com/en/products-32.html) is an older datasheet (version
56//! 1.0, rather than version 1.1 as linked above) and is significantly more
57//! difficult to understand. I recommend reading version 1.1. All section
58//! references in this file are to the 1.1 version.
59//!
60//! The below is a flowchart of how the sensor gets initialized and measurements taken.
61//! Note that the flowchart does not include the parameters that you need to give to
62//! some commands, and it also doesn't include the SoftReset command flow.
63//!
64//! ```text
65//!           Start (Power on)
66//!                  │
67//!                  ▼
68//!              Wait 40 ms
69//!                  │
70//!                  ▼
71//!           Read status byte    ◄───     Wait 10 ms
72//!                  │                           ▲
73//!                  ▼                           │
74//!          Status::Calibrated ──► No ──► Command::Initialize (0xBE)
75//!                  │
76//!                  ▼
77//!                 Yes
78//!                  │
79//!                  ▼
80//! Command::TriggerMeasurement  (0xAC)  ◄─┐
81//!                  │                     │
82//!                  ▼                     │
83//!             Wait 80 ms                 │
84//!                  │                     │
85//!                  ▼                     │
86//!           Read status byte    ◄──┐     │
87//!                  │               │     │
88//!                  ▼               │     │
89//!             Status::Busy  ───►  Yes    │
90//!                  │                     │
91//!                  ▼                     │
92//!                 No                     │
93//!                  │                     │
94//!                  ▼                     │
95//!             Read 7 bytes               │
96//!                  │                     │
97//!                  ▼                     │
98//!             Calculate CRC              │
99//!                  │                     │
100//!                  ▼                     │
101//!               CRC good ─► No  ─────────┘
102//!                  │                     ▲
103//!                  ▼                     │
104//!                 Yes                    │
105//!                  │                     │
106//!                  ▼                     │
107//!           CRC-checked Ready ─► No ─────┘
108//!                  │
109//!                  ▼
110//!                 Yes
111//!                  │
112//!                  ▼
113//!        Calc Humidity and Temp
114//! ```
115
116use crc_any::CRCu8;
117use embedded_hal::delay::DelayNs;
118use embedded_hal::i2c::I2c;
119
120/// AHT20 sensor's I2C address.
121pub const SENSOR_ADDRESS: u8 = 0b0011_1000; // This is I2C address 0x38;
122
123/// Commands that can be sent to the AHT20 sensor.
124///
125/// Note that a few of these take parameters but that there are no explanations provided about what
126/// those parameters actually are. You should consider the command and specified parameters to be
127/// just one three-byte command. These can be found in the datasheet, Section 5.3, page 8, Table 9.
128pub enum Command {
129    Initialize = 0b1011_1110, // 0xBE, Initialize and calibrate the sensor.
130    // This command takes two bytes of parameter: 0b0000_1000 (0x08), then 0b0000_0000 (0x00).
131    Calibrate = 0b1110_0001, // 0xE1, Calibrate - or return calibration status.
132    // Status will be Status::Calibrated, where bit4 indicates calibrated. If it's 0, it's not.
133    TriggerMeasurement = 0b1010_1100, // 0xAC
134    // This command takes two bytes of parameter: 0b00110011 (0x33), then 0b0000_0000 (0x00).
135    // Wait 80ms for the measurement. You'll get a status byte back. Check the status for
136    // Status::Busy to be 0. If it is, then read 7 bytes. A status byte, 5 data plus a byte of CRC.
137    SoftReset = 0b1011_1010, // 0xBA
138                             // Also see Section 5.5. This takes 20ms or less to complete.
139}
140
141/// Status byte meanings.
142///
143/// Table 10, page 8 of the datasheet.
144pub enum Status {
145    Busy = 0b1000_0000, // Status bit for busy - 8th bit enabled. 1<<7, 0x80
146    // 1 is Busy measuring. 0 is "Free in dormant state" or "ready".
147    Calibrated = 0b0000_1000, // Status bit for calibrated - 4th bit enabled. 1<<3, 0x08.
148                              // 1 is Calibrated, 0 is uncalibrated. If 0, send Command::Initialize.
149}
150
151/// SensorStatus is the response from the sensor indicating if it is ready to read from, and if it
152/// is calibrated.
153///
154/// This is returned from the `check_status` method. It is used both
155/// during initialization, which is when the sensor caibrates itself, and during
156/// measure. During measure the sensor will report itself as busy (not ready)
157/// for a period of 80ms.
158#[derive(Debug, Clone, Copy)]
159pub struct SensorStatus(pub u8);
160
161impl SensorStatus {
162    /// Create a new SensorStatus from an AHT20 status byte.
163    ///
164    /// That byte comes from the `check_status` method.
165    pub fn new(status: u8) -> Self {
166        SensorStatus(status)
167    }
168
169    /// Check if the sensor is ready to have data read from it. After issuing a sensor read, you
170    /// must check is_ready before reading the result. The measure function takes care of this wait
171    /// and check.
172    pub fn is_ready(self) -> bool {
173        // The busy bit should be 0 (not busy) for the sensor to report ready.
174        (self.0 & Status::Busy as u8) == 0
175    }
176
177    /// Check if the sensor is calibrated. If it is not, you must call `init` to initialize the
178    /// sensor.
179    pub fn is_calibrated(self) -> bool {
180        // The calibrated bit should be set.
181        (self.0 & Status::Calibrated as u8) != 0
182    }
183}
184
185/// SensorReading is a single reading from the AHT20 sensor.
186///
187/// This is returned from the `measure` method. You get:
188/// * humidity in % Relative Humidity
189/// * temperature in degrees Celsius.
190#[derive(Debug, Clone, Copy)]
191pub struct SensorReading {
192    pub humidity: f32,
193    pub temperature: f32,
194}
195
196impl SensorReading {
197    /// Create a SensorReading from the data returned by the sensor.
198    ///
199    /// This is done by the `measure` method.
200    fn from_bytes(sensor_data: [u8; 5]) -> Self {
201        let (humidity_val, temperature_val) = SensorReading::raw_from_bytes(sensor_data);
202
203        // From section 6.1 "Relative humidity transformation" here is how we turn this into
204        // a relative humidity percantage value.
205        let humidity_percent = (humidity_val as f32) / ((1 << 20) as f32) * 100.0;
206
207        // From section 6.2 "Temperature transformation" here is how we turn this into
208        // a temprature in °C.
209        let temperature_celcius = (temperature_val as f32) / ((1 << 20) as f32) * 200.0 - 50.0;
210
211        SensorReading {
212            humidity: humidity_percent,
213            temperature: temperature_celcius,
214        }
215    }
216
217    /// Identical to `from_bytes`, but doesn't use floating point math.
218    ///
219    /// This limits the precision to just integer values, but doesn't bring in floating point
220    /// libraries on microcontrollers with no FP support, saving space and being faster.
221    fn from_bytes_no_fp(sensor_data: [u8; 5]) -> Self {
222        let (humidity_val, temperature_val) = SensorReading::raw_from_bytes(sensor_data);
223
224        // From section 6.1 "Relative humidity transformation" here is how we turn this into
225        // a relative humidity percantage value.
226        let humidity_percent = (100 * humidity_val) >> 20;
227
228        // From section 6.2 "Temperature transformation" here is how we turn this into
229        // a temprature in °C.
230        let temperature_celcius = (((200 * temperature_val) >> 20) - 50) as f32;
231
232        SensorReading {
233            humidity: humidity_percent as f32,
234            temperature: temperature_celcius,
235        }
236    }
237
238    /// Turn the raw data from a sensor reding into two u32 raw values.
239    ///
240    /// These values still need to be converted into %age humidity and degrees C, this is done with
241    /// `from_bytes` and `from_bytes_no_fp`. This is just a helper method for the aforementioned.
242    fn raw_from_bytes(sensor_data: [u8; 5]) -> (u32, u32) {
243        // Our five bytes of sensor data is split into 20 bits (two and a half bytes) humidity and
244        // 20 bits temperature. We'll have to spend a bit of time splitting the middle byte up.
245        let humidity_bytes: &[u8] = &sensor_data[..2];
246        let split_byte: u8 = sensor_data[2];
247        let temperature_bytes: &[u8] = &sensor_data[3..5];
248
249        // We have a byte that might look like 0x0101_1010, we want only the first four bits, (the
250        // 0101) to be at the end of the byte. So we shift them four right and end up with
251        // 0x0000_0101. These 4 bits go at the very end of our 20-bit humidity value.
252        // In the final 32-bit value they're these ones: 0x0000_0000_0000_0000_0000_0000_0000_1111
253        let right_bits_humidity: u32 = (split_byte >> 4).into();
254        // In the final 32-bit value they're these ones: 0x0000_0000_0000_1111_1111_0000_0000_0000
255        let left_bits_humidity: u32 = (humidity_bytes[0] as u32) << 12;
256        // In the final 32-bit value they're these ones: 0x0000_0000_0000_0000_0000_1111_1111_0000
257        let middle_bits_humidity: u32 = (humidity_bytes[1] as u32) << 4;
258        // We combine them to form the complete 20 bits: 0x0000_0000_0000_1111_1111_1111_1111_1111
259        let humidity_val: u32 = left_bits_humidity | middle_bits_humidity | right_bits_humidity;
260
261        // With that same example byte - we want to keep only the last four bits this time, so we
262        // mask the first four and end up with 0x0000_1010. These bits end up at the very start of
263        // our 20-bit temperature value. In the final 32-bit value they're these ones:
264        // 0x0000_0000_0000_1111_0000_0000_0000_0000 To get them into their final position - we'll
265        // left-shift them by 16 positions.
266        let split_byte_temperature: u32 = (split_byte & 0b0000_1111).into();
267        // We need to fill the rightmost 20 bits, starting with our split byte
268        // In the final 32-bit value they're these ones: 0x0000_0000_0000_1111_0000_0000_0000_0000
269        let left_bits_temp: u32 = split_byte_temperature << 16;
270        // In the final 32-bit value they're these ones: 0x0000_0000_0000_0000_1111_1111_0000_0000
271        let middle_bits_temp: u32 = (temperature_bytes[0] as u32) << 8;
272        // And just for symmetry...
273        // In the final 32-bit value they're these ones: 0x0000_0000_0000_0000_0000_0000_1111_1111
274        let right_bits_temp: u32 = temperature_bytes[1] as u32;
275        // We combine them to form the complete 20 bits: 0x0000_0000_0000_1111_1111_1111_1111_1111
276        let temperature_val: u32 = left_bits_temp | middle_bits_temp | right_bits_temp;
277
278        (humidity_val, temperature_val)
279    }
280}
281
282/// Driver errors.
283#[derive(Debug, PartialEq)]
284pub enum Error<E> {
285    /// I2C bus error
286    I2c(E),
287    /// CRC validation failed
288    InvalidCrc,
289    /// Unexpectedly not ready - this can happen when the sensor sends back "busy" but the
290    /// I2C data gets corrupted and we receive "ready", then later the
291    /// CRC-checked status byte correctly reports "busy" and we have to abort the measurement.
292    UnexpectedBusy,
293    /// Errors such as overflowing the stack.
294    Internal,
295}
296
297impl<E> core::fmt::Display for Error<E> {
298    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
299        match self {
300            Error::I2c(_e) => write!(f, "I2C error"),
301            Error::InvalidCrc => write!(f, "invalid CRC error"),
302            Error::UnexpectedBusy => write!(f, "unexpected busy error"),
303            Error::Internal => write!(f, "internal ATH20 driver error"),
304        }
305    }
306}
307
308impl<E> core::error::Error for Error<E> where E: core::fmt::Debug {}
309
310/// An AHT20 sensor on the I2C bus `I`.
311///
312/// The address of the sensor will be `SENSOR_ADDRESS` from this package, unless there is some kind
313/// of special address translating hardware in use.
314pub struct AHT20<I>
315where
316    I: I2c,
317{
318    i2c: I,
319    address: u8,
320}
321
322impl<I> AHT20<I>
323where
324    I: I2c,
325{
326    /// Initializes the AHT20 driver.
327    ///
328    /// This consumes the I2C bus `I`. Before you can get temperature and humidity measurements,
329    /// you must call the `init` method which calibrates the sensor. The address will almost always
330    /// be `SENSOR_ADDRESS` from this crate.
331    pub fn new(i2c: I, address: u8) -> Self {
332        AHT20 { i2c, address }
333    }
334
335    /// Run the AHT20 init and calibration routines.
336    ///
337    /// This must be called before any other methods except `check_status`. This method will take
338    /// *at least* 40ms to return.
339    ///
340    /// ```text
341    ///          Start (Power on)
342    ///                 │
343    ///                 ▼
344    ///             Wait 40 ms
345    ///                 │
346    ///                 ▼
347    ///           Read  status byte    ◄───    Wait 10 ms
348    ///                 │                           ▲
349    ///                 ▼                           │
350    ///         Status::Calibrated ──► No ──► Command::Initialize (0xBE)
351    ///                 │
352    ///                 ▼
353    ///                Yes
354    /// ```
355    pub fn init(
356        &mut self,
357        delay: &mut impl DelayNs,
358    ) -> Result<AHT20Initialized<I>, Error<I::Error>> {
359        delay.delay_ms(40);
360
361        while !self.check_status()?.is_calibrated() {
362            self.send_initialize()?;
363            #[cfg(feature = "use-defmt")]
364            defmt::debug!("init: waiting for sensor to report being calibrated, 10ms.");
365            delay.delay_ms(10);
366        }
367
368        #[cfg(feature = "use-defmt")]
369        defmt::debug!("init: sensor reporting being calibrated, init done.");
370        Ok(AHT20Initialized { aht20: self })
371    }
372
373    /// check_Status reads a status byte from the AHT20 sensor to check its status.
374    ///
375    /// The sensor can be calibrated or not, also busy generating a sensor measurement or ready.
376    /// This method returns the SensorStatus struct, which you can use to determine what the state
377    /// of the sensor is.
378    ///
379    /// NOTE: The documentation suggests that we send a CheckStatus (0x71) command, followed by a
380    ///       read. Experience (https://github.com/anglerud/aht20-driver/pull/10) indicates that we
381    ///       can create a hang writing that command, and that just reading a status byte works.
382    ///
383    /// This is used by both measure_once and init.
384    fn check_status(&mut self) -> Result<SensorStatus, Error<I::Error>> {
385        #[cfg(feature = "use-defmt")]
386        defmt::debug!("check_status: requesting a status check from sensor.");
387        let mut read_buffer = [0u8; 1];
388
389        self.i2c
390            .read(self.address, &mut read_buffer)
391            .map_err(Error::I2c)?;
392
393        let status_byte = read_buffer[0];
394        Ok(SensorStatus::new(status_byte))
395    }
396
397    /// send_initialize sends the Initialize command to the sensor which make it calibrate.
398    ///
399    /// After sending initialize, there is a required 40ms wait period and verification
400    /// that the sensor reports itself calibrated. See the `init` method.
401    fn send_initialize(&mut self) -> Result<(), Error<I::Error>> {
402        #[cfg(feature = "use-defmt")]
403        defmt::debug!("send_initialize: requesting sensor to initialize itself.");
404        let command: [u8; 3] = [
405            // Initialize = 0b1011_1110. Equivalent to 0xBE, Section 5.3, page 8, Table 9
406            Command::Initialize as u8,
407            // Two parameters as described in the datasheet. There is no indication what these
408            // parameters mean, just that they should be provided. There is also no returned
409            // value.
410            0b0000_1000, // 0x08
411            0b0000_0000, // 0x00
412        ];
413
414        self.i2c.write(self.address, &command).map_err(Error::I2c)?;
415
416        Ok(())
417    }
418
419    /// Destroys this driver and releases the I2C bus `I`
420    pub fn destroy(self) -> I {
421        self.i2c
422    }
423}
424
425/// AHT20Initialized is returned by AHT20::init() and the sensor is ready to read from.
426///
427/// In this state you can trigger a measurement with `.measure(&mut delay)`.
428pub struct AHT20Initialized<'a, I>
429where
430    I: I2c,
431{
432    aht20: &'a mut AHT20<I>,
433}
434
435impl<'a, I> AHT20Initialized<'a, I>
436where
437    I: I2c,
438{
439    /// Measure temperature and humidity.
440    ///
441    /// This masurement takes at least 80ms to complete. Together with the `measure_once` method,
442    /// this is the work being carried out:
443    ///
444    /// ```text
445    /// Command::TriggerMeasurement (0xAC)   ◄─┐
446    ///                  │                     │
447    ///                  ▼                     │
448    ///             Wait 80 ms                 │
449    ///                  │                     │
450    ///                  ▼                     │
451    ///           Read status byte    ◄──┐     │
452    ///                  │               │     │
453    ///                  ▼               │     │
454    ///             Status::Busy  ───►  Yes    │
455    ///                  │                     │
456    ///                  ▼                     │
457    ///                 No                     │
458    ///                  │                     │
459    ///                  ▼                     │
460    ///             Read 7 bytes               │
461    ///                  │                     │
462    ///                  ▼                     │
463    ///             Calculate CRC              │
464    ///                  │                     │
465    ///                  ▼                     │
466    ///               CRC good ─► No  ─────────┘
467    ///                  │                     ▲
468    ///                  ▼                     │
469    ///                 Yes                    │
470    ///                  │                     │
471    ///                  ▼                     │
472    ///           CRC-checked Ready ─► No ─────┘
473    ///                  │
474    ///                  ▼
475    ///                 Yes
476    ///                  │
477    ///                  ▼
478    ///        Calc Humidity and Temp
479    /// ```
480    pub fn measure(&mut self, delay: &mut impl DelayNs) -> Result<SensorReading, Error<I::Error>> {
481        loop {
482            let measurement_result = self.measure_once(delay);
483            match measurement_result {
484                Ok(sb) => {
485                    return Ok(SensorReading::from_bytes(sb));
486                }
487                Err(Error::InvalidCrc) => {
488                    // CRC failed to validate, we'll go back and issue another read request.
489                    #[cfg(feature = "use-defmt")]
490                    defmt::error!("Invalid CRC, retrying.");
491                }
492                Err(Error::UnexpectedBusy) => {
493                    // Possibly indicates the previously seen 'ready' was due to uncorrected noise.
494                    #[cfg(feature = "use-defmt")]
495                    defmt::error!("Sensor contradicted a ready status with a crc-checked busy.");
496                }
497                Err(other) => return Err(other),
498            }
499        }
500    }
501
502    /// This is identical to `measure`, except it doesn't use floating point math.
503    ///
504    /// This means it can be used on microcontrollers with no FP support, without having
505    /// to bring in floating point math functions, which can take up a lot of space, and
506    /// might be slow.
507    ///
508    /// The drawback is that precision is limited to only integer values.
509    pub fn measure_no_fp(
510        &mut self,
511        delay: &mut impl DelayNs,
512    ) -> Result<SensorReading, Error<I::Error>> {
513        // TODO: See if we can refactor here, the only difference is from_bytes and
514        //       from_bytes_no_fp.
515        loop {
516            let measurement_result = self.measure_once(delay);
517            match measurement_result {
518                Ok(sb) => {
519                    return Ok(SensorReading::from_bytes_no_fp(sb));
520                }
521                Err(Error::InvalidCrc) => {
522                    // CRC failed to validate, we'll go back and issue another read request.
523                    #[cfg(feature = "use-defmt")]
524                    defmt::error!("Invalid CRC, retrying.");
525                }
526                Err(Error::UnexpectedBusy) => {
527                    // Possibly indicates the previously seen 'ready' was due to uncorrected noise.
528                    #[cfg(feature = "use-defmt")]
529                    defmt::error!("Sensor contradicted a ready status with a crc-checked busy.");
530                }
531                Err(other) => return Err(other),
532            }
533        }
534    }
535
536    /// Perform one measurement and return the sensor's 5 raw data bytes.
537    ///
538    /// This takes at least 80ms to complete, and only returns 2x20 bits in 5 bytes.
539    /// This data is interpreted by the `measure` function.
540    fn measure_once(&mut self, delay: &mut impl DelayNs) -> Result<[u8; 5], Error<I::Error>> {
541        self.send_trigger_measurement()?;
542        delay.delay_ms(80);
543
544        // Wait for measurement to be ready
545        while !self.aht20.check_status()?.is_ready() {
546            #[cfg(feature = "use-defmt")]
547            defmt::debug!("measure_once: waiting for ready, 1ms.");
548            delay.delay_ms(1);
549        }
550
551        // 1 byte status, 20 bits humidity + 20 bits temperature, 1 byte CRC
552        let mut read_buffer = [0u8; 7];
553        self.aht20
554            .i2c
555            .read(self.aht20.address, &mut read_buffer)
556            .map_err(Error::I2c)?;
557
558        let data: &[u8] = &read_buffer[..6];
559        let crc_byte: u8 = read_buffer[6];
560
561        let crc = compute_crc(data);
562        if crc_byte != crc {
563            return Err(Error::InvalidCrc);
564        }
565
566        // The first byte of the sensor's response is a repeat of the status byte.
567        // There is a minescule chance that the previous ready message was caused
568        // by noise on the i2c bus. This byte has been CRC-checked.
569        let status = SensorStatus::new(read_buffer[0]);
570        if !status.is_ready() {
571            return Err(Error::UnexpectedBusy);
572        }
573
574        // Arrays implement TryFrom for slices. In case the length of the slice does not match
575        // the requested array - it will return a TryFromSliceError, but we are selecting the
576        // right number of bytes so there is no risk. Mapping to a generic error.
577        data[1..6].try_into().map_err(|_| Error::Internal)
578    }
579
580    /// Send the "Trigger Measurement" command to the sensor.
581    ///
582    /// This does not return anything, it only instructs the sensor to get the data ready. After
583    /// sending this command, you need to wait 80ms before attempting to read data back. See the
584    /// `measure_once` function and the flowchart at the top of this file.
585    fn send_trigger_measurement(&mut self) -> Result<(), Error<I::Error>> {
586        // TriggerMeasurement is 0b1010_1100. Equivalent to 0xAC: Section 5.3, page 8, Table 9
587        // This command takes two bytes of parameter:  0b00110011 (0x33), then 0b0000_0000 (0x00).
588        let command: [u8; 3] = [
589            Command::TriggerMeasurement as u8,
590            // Two parameters as described in the datasheet. There is no indication what these
591            // parameters mean, just that they should be provided. There is no returned value.
592            // To get the measurement, see [measure](measure).
593            0b0011_0011, // 0x33
594            0b0000_0000, // 0x00
595        ];
596
597        self.aht20
598            .i2c
599            .write(self.aht20.address, &command)
600            .map_err(Error::I2c)?;
601
602        Ok(())
603    }
604
605    /// Send the Soft Reset command to the sensor.
606    ///
607    /// This performs a soft reset, it's unclear when this might be needed. It takes 20ms to
608    /// complete and returns nothing.
609    pub fn soft_reset(&mut self, delay: &mut impl DelayNs) -> Result<(), Error<I::Error>> {
610        // SoftReset is 0b1011_1010. Equivalent to 0xBA, Section 5.3, page 8, Table 9.
611        let command: [u8; 1] = [Command::SoftReset as u8];
612
613        self.aht20
614            .i2c
615            .write(self.aht20.address, &command)
616            .map_err(Error::I2c)?;
617        // The datasheet in section 5.5 says there is a guarantee that the reset time does
618        // not exceed 20ms. We wait the full 20ms to ensure you can trigger a measurement
619        // immediately after this function.
620        delay.delay_ms(20);
621
622        Ok(())
623    }
624
625    /// Destroys this initialized driver and lets you release the I2C bus `I`
626    pub fn destroy(self) -> &'a mut AHT20<I> {
627        self.aht20
628    }
629}
630
631/// compute_crc uses the CRCu8 algoritm from crc-any. The parameter choice makes this a
632/// "CRC-8-Dallas/Maxim".
633///
634/// The CRC invocation takes some parameters, which we get from the datasheet:
635/// https://cdn-learn.adafruit.com/assets/assets/000/091/676/original/AHT20-datasheet-2020-4-16.pdf?1591047915
636/// Section 5.4.4:
637///
638/// > CRC initial vaue is 0xFF, crc8 check polynomial CRC[7:0]=1+x**4 + x**5 + x**8
639///
640/// https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Polynomial_representations_of_cyclic_redundancy_checks
641/// You can find it in the table on wikipedia, under "CRC-8-Dallas/Maxim", 1-Wire bus.
642///
643/// This article explains how we get from `CRC[7:0]=1 + x**4 + x**5 + x**8` to `0x31` as the hex
644/// representation: http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html#ch72
645///
646/// The **N is the Nth bit (zero indexed).
647/// > The most significant bit [(x**8)] is left out in the hexadecimal representation
648///
649/// That the leaves bit 0 (the +1 we do), 4, 5. So that gives us:
650///
651/// ```python
652/// >>> hex(0x00110001)
653/// '0x31'
654/// ```
655///
656/// This is also what Knurling's test driver crate uses.
657/// https://github.com/knurling-rs/test-driver-crate-example/blob/main/src/lib.rs#L59
658/// which indicates this is either an I2C thing, or a common driver default as CRC parameters.
659fn compute_crc(bytes: &[u8]) -> u8 {
660    // Poly (0x31), bits (8), initial (0xff), final_xor (0x00), reflect (false).
661    let mut crc = CRCu8::create_crc(0x31, 8, 0xff, 0x00, false);
662    crc.digest(bytes);
663    crc.get_crc()
664}
665
666#[cfg(test)]
667mod tests {
668    use super::{AHT20Initialized, Error, AHT20, SENSOR_ADDRESS};
669    use embedded_hal_mock::eh1::delay::NoopDelay as MockDelay;
670    use embedded_hal_mock::eh1::i2c::Mock as I2cMock;
671    use embedded_hal_mock::eh1::i2c::Transaction;
672
673    /// Test SensorStatus reporting being ready.
674    #[test]
675    fn sensorstatus_is_ready() {
676        let status = super::SensorStatus::new(0x00);
677        assert!(status.is_ready());
678    }
679
680    /// Test SensorStatus reporting being busy.
681    #[test]
682    fn sensorstatus_is_not_ready() {
683        // 8th bit being 1 signifies "busy"
684        // Equiv to 0x01 << 7, or 128 (dec) or 0x80 (hex)
685        let status = super::SensorStatus::new(0b1000_0000);
686        assert!(!status.is_ready());
687    }
688
689    /// Test SensorStatus reporting being calibrated.
690    #[test]
691    fn sensorstatus_is_calibrated() {
692        // 4th bit being 1 signifies the sensor being calibrated.
693        // Equiv to 0x01 << 3, or 8 (dec) or 0x08
694        let status = super::SensorStatus::new(0b0000_1000);
695        assert!(status.is_calibrated());
696    }
697
698    /// Test SensorStatus reporting being uncalibrated.
699    #[test]
700    fn sensorstatus_is_not_calibrated() {
701        let status = super::SensorStatus::new(0b0000_0000);
702        assert!(!status.is_calibrated());
703    }
704
705    /// Test creating new AHT20 sensors.
706    ///
707    /// Test that we can create multiple AHT20 devices. We test this because it's one of the
708    /// measures of success for this driver.
709    #[test]
710    fn aht20_new() {
711        // In the real app we'd used shared-bus to share the i2c bus between the two drivers, but
712        // I think this is fine for a test.
713        let mock_i2c_1 = I2cMock::new(&[]);
714        let mock_i2c_2 = I2cMock::new(&[]);
715
716        let aht20_1 = AHT20::new(mock_i2c_1, SENSOR_ADDRESS);
717        let aht20_2 = AHT20::new(mock_i2c_2, SENSOR_ADDRESS);
718
719        aht20_1.destroy().done();
720        aht20_2.destroy().done();
721    }
722
723    /// Test reading a status byte.
724    #[test]
725    fn check_status() {
726        let expectations = vec![Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000])];
727        let mock_i2c = I2cMock::new(&expectations);
728
729        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
730        let status = aht20.check_status().unwrap();
731        assert!(status.is_calibrated());
732
733        let mut mock = aht20.destroy();
734        mock.done(); // verify expectations
735    }
736
737    /// Test sending the i2c Initialize command.
738    #[test]
739    fn send_initialize() {
740        let expectations = vec![Transaction::write(
741            SENSOR_ADDRESS,
742            vec![
743                super::Command::Initialize as u8,
744                0b0000_1000, // 0x08
745                0b0000_0000, // 0x00
746            ],
747        )];
748        let mock_i2c = I2cMock::new(&expectations);
749
750        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
751        aht20.send_initialize().unwrap();
752
753        let mut mock = aht20.destroy();
754        mock.done(); // verify expectations
755    }
756
757    /// Initialize sensor, with the sensor reporting calibrated immediately.
758    ///
759    /// No call to send_initialize will be required.
760    #[test]
761    fn init_with_calibrated_sensor() {
762        // This test has check_status return an already calibrated sensor. This means
763        // that send_initialize is not called.
764        let expectations = vec![
765            // 4th bit being 1 signifies the sensor being calibrated.
766            // Equiv to 0x01 << 3, or 8 (dec) or 0x08
767            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
768        ];
769        let mock_i2c = I2cMock::new(&expectations);
770        let mut mock_delay = MockDelay::new();
771
772        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
773        aht20.init(&mut mock_delay).unwrap();
774
775        let mut mock = aht20.destroy();
776        mock.done(); // verify expectations
777    }
778
779    /// Initialize sensor, with a report of an uncalibrated sensor.
780    ///
781    /// The sensor will report being uncalibrated once, then after initialization the sensor will
782    /// report being calibrated.
783    #[test]
784    fn init_with_uncalibrated_sensor() {
785        // This test has check_status return an uncalibrated sensor. With that, a call
786        // to send_initialize is done to initialize and calibrate the sensor. A second
787        // call to check_status verifies the new calibrated status.
788        let expectations = vec![
789            // 4th bit being 0 signifies the sensor not being calibrated.
790            Transaction::read(SENSOR_ADDRESS, vec![0b0000_0000]),
791            // This is send_initialize
792            Transaction::write(
793                SENSOR_ADDRESS,
794                vec![
795                    super::Command::Initialize as u8,
796                    0b0000_1000, // 0x08
797                    0b0000_0000, // 0x00
798                ],
799            ),
800            // One more check_status will be called, this time with the 4th bit set
801            // to 1 - signifying the sensor is now calibrated and we can finish the init.
802            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
803        ];
804        let mock_i2c = I2cMock::new(&expectations);
805        let mut mock_delay = MockDelay::new();
806
807        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
808        aht20.init(&mut mock_delay).unwrap();
809
810        let mut mock = aht20.destroy();
811        mock.done(); // verify expectations
812    }
813
814    /// Test sending the i2c SoftReset command.
815    #[test]
816    fn soft_reset() {
817        let expectations = vec![Transaction::write(
818            SENSOR_ADDRESS,
819            vec![super::Command::SoftReset as u8],
820        )];
821        let mock_i2c = I2cMock::new(&expectations);
822        let mut mock_delay = MockDelay::new();
823
824        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
825        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
826        aht20_init.soft_reset(&mut mock_delay).unwrap();
827
828        let mut mock = aht20.destroy();
829        mock.done(); // verify expectations
830    }
831
832    /// Test sending the i2c TriggerMeasurement command.
833    #[test]
834    fn send_trigger_measurement() {
835        let expectations = vec![Transaction::write(
836            SENSOR_ADDRESS,
837            vec![
838                super::Command::TriggerMeasurement as u8,
839                0b0011_0011, // 0x33
840                0b0000_0000, // 0x00
841            ],
842        )];
843        let mock_i2c = I2cMock::new(&expectations);
844
845        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
846        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
847        aht20_init.send_trigger_measurement().unwrap();
848
849        let mut mock = aht20.destroy();
850        mock.done(); // verify expectations
851    }
852
853    /// Measure once, sensor reports ready at once.
854    ///
855    /// No wait is needed in this scenario.
856    #[test]
857    fn measure_once_immediately_ready() {
858        let expectations = vec![
859            // send_trigger_measurement
860            Transaction::write(
861                SENSOR_ADDRESS,
862                vec![
863                    super::Command::TriggerMeasurement as u8,
864                    0b0011_0011, // 0x33
865                    0b0000_0000, // 0x00
866                ],
867            ),
868            // check_status called. 4th bit set to to 1 - signifying the sensor is calibrated 8th
869            // bit set to 0 (not busy), signalling that a measurement is ready for us to read.
870            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
871            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
872            // These are taken from a run of the sensor.
873            Transaction::read(
874                SENSOR_ADDRESS,
875                vec![
876                    0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
877                    //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
878                    //             and 3 are described as 'reserved'.
879                    0b0110_0101, // 101, 0x65 - first byte of humidity value
880                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
881                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
882                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
883                    0b0010_0110, //  38, 0x26 - second full byte of temperature.
884                    0b1100_0110, // 198, 0xc6 - CRC
885                ],
886            ),
887        ];
888        let mock_i2c = I2cMock::new(&expectations);
889        let mut mock_delay = MockDelay::new();
890
891        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
892        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
893        aht20_init.measure_once(&mut mock_delay).unwrap();
894
895        let mut mock = aht20.destroy();
896        mock.done(); // verify expectations
897    }
898
899    /// Measure once, sensor erroniously reports ready at once, then correctly reports
900    /// busy in the CRC-checked status byte causing an error.
901    ///
902    /// No wait is needed in this scenario.
903    #[test]
904    fn measure_once_ready_misreported() {
905        let expectations = vec![
906            // send_trigger_measurement
907            Transaction::write(
908                SENSOR_ADDRESS,
909                vec![
910                    super::Command::TriggerMeasurement as u8,
911                    0b0011_0011, // 0x33
912                    0b0000_0000, // 0x00
913                ],
914            ),
915            // check_status called. 4th bit set to to 1 - signifying the sensor is calibrated 8th
916            // bit set to 0 (not busy), signalling that a measurement is ready for us to read.
917            // NOTE: This read says we're not busy, that is "ready".
918            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
919            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
920            // These are taken from a run of the sensor.
921            Transaction::read(
922                SENSOR_ADDRESS,
923                vec![
924                    0b1001_1100, // 156, 0x9c - busy, calibrated, and some mystery flags.
925                    //             bit 8 set to 1 is busy. bit 4 set is calibrated. bit 5
926                    //             and 3 are described as 'reserved'.
927                    //             NOTE: this says busy, contradicting the ready above.
928                    0b0110_0101, // 101, 0x65 - first byte of humidity value
929                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
930                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
931                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
932                    0b0010_0110, //  38, 0x26 - second full byte of temperature.
933                    0b0010_1010, // 424, 0x2a - CRC
934                ],
935            ),
936        ];
937        let mock_i2c = I2cMock::new(&expectations);
938        let mut mock_delay = MockDelay::new();
939
940        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
941        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
942        // We received a ready from the check_status method, then a busy in the CRC-checked
943        // status byte - and therefore we got the UnexpectedBusy.
944        assert_eq!(
945            aht20_init.measure_once(&mut mock_delay),
946            Err(Error::UnexpectedBusy)
947        );
948
949        let mut mock = aht20.destroy();
950        mock.done(); // verify expectations
951    }
952
953    /// Measure once, with a wait inserted.
954    ///
955    /// We signal via check_status that a wait should be inserted before another attempt to read
956    /// data from the sensor is made.
957    #[test]
958    fn measure_once_wait_once() {
959        let expectations = vec![
960            // send_trigger_measurement
961            Transaction::write(
962                SENSOR_ADDRESS,
963                vec![
964                    super::Command::TriggerMeasurement as u8,
965                    0b0011_0011, // 0x33
966                    0b0000_0000, // 0x00
967                ],
968            ),
969            // check_status called. 4th bit set to to 1 - signifying the sensor is calibrated 8th
970            // bit set to 1 (busy), signalling that we should wait for the sensor.
971            Transaction::read(SENSOR_ADDRESS, vec![0b1000_1000]),
972            // Next time round, we say that the sensor is good to go.
973            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
974            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
975            // These are taken from a run of the sensor.
976            Transaction::read(
977                SENSOR_ADDRESS,
978                vec![
979                    0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
980                    //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
981                    //             and 3 are described as 'reserved'.
982                    0b0110_0101, // 101, 0x65 - first byte of humidity value
983                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
984                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
985                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
986                    0b0010_0110, //  38, 0x26 - second full byte of temperature.
987                    0b1100_0110, // 198, 0xc6 - CRC
988                ],
989            ),
990        ];
991        let mock_i2c = I2cMock::new(&expectations);
992        let mut mock_delay = MockDelay::new();
993
994        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
995        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
996        aht20_init.measure_once(&mut mock_delay).unwrap();
997
998        let mut mock = aht20.destroy();
999        mock.done(); // verify expectations
1000    }
1001
1002    /// Single measurement pass with bad CRC.
1003    ///
1004    /// Intentionally corrupt the read data to make sure we get a CRC error.
1005    #[test]
1006    fn measure_once_bad_crc() {
1007        let expectations = vec![
1008            // send_trigger_measurement
1009            Transaction::write(
1010                SENSOR_ADDRESS,
1011                vec![
1012                    super::Command::TriggerMeasurement as u8,
1013                    0b0011_0011, // 0x33
1014                    0b0000_0000, // 0x00
1015                ],
1016            ),
1017            // Check status, and  we say that the sensor is good to go.
1018            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
1019            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
1020            // These are taken from a run of the sensor.
1021            Transaction::read(
1022                SENSOR_ADDRESS,
1023                vec![
1024                    0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
1025                    //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
1026                    //             and 3 are described as 'reserved'.
1027                    0b0110_0101, // 101, 0x65 - first byte of humidity value
1028                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
1029                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
1030                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
1031                    0b0010_0111, //  39, 0x27 - second full byte of temperature.
1032                    //  NOTE: This should be 38, 0x26, but is intentionally corrupted
1033                    //        so that the CRC won't match. Last bit flipped from 0 to 1.
1034                    0b1100_0110, // 198, 0xc6 - CRC
1035                ],
1036            ),
1037        ];
1038        let mock_i2c = I2cMock::new(&expectations);
1039        let mut mock_delay = MockDelay::new();
1040
1041        // test and verify
1042        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
1043        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
1044        match aht20_init.measure_once(&mut mock_delay) {
1045            Ok(_) => panic!("CRC is wrong and measure_once should not pass."),
1046            Err(err_type) => assert_eq!(err_type, Error::InvalidCrc),
1047        }
1048
1049        let mut mock = aht20.destroy();
1050        mock.done(); // verify expectations
1051    }
1052
1053    /// Test a measurement.
1054    ///
1055    /// This uses data from an actual sensor run.
1056    #[test]
1057    fn measure() {
1058        // setup
1059        let expectations = vec![
1060            // send_trigger_measurement
1061            Transaction::write(
1062                SENSOR_ADDRESS,
1063                vec![
1064                    super::Command::TriggerMeasurement as u8,
1065                    0b0011_0011, // 0x33
1066                    0b0000_0000, // 0x00
1067                ],
1068            ),
1069            // check_status - with ready bit set to 'ready' (off)
1070            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
1071            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
1072            // These are taken from a run of the sensor.
1073            Transaction::read(
1074                SENSOR_ADDRESS,
1075                vec![
1076                    0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
1077                    //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
1078                    //             and 3 are described as 'reserved'.
1079                    0b0110_0101, // 101, 0x65 - first byte of humidity value
1080                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
1081                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
1082                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
1083                    0b0010_0110, //  38, 0x26 - second full byte of temperature.
1084                    0b1100_0110, // 198, 0xc6 - CRC
1085                ],
1086            ),
1087        ];
1088        let mock_i2c = I2cMock::new(&expectations);
1089        let mut mock_delay = MockDelay::new();
1090
1091        // test
1092        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
1093        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
1094        let measurement = aht20_init.measure(&mut mock_delay).unwrap();
1095
1096        // verification
1097        let mut mock = aht20.destroy();
1098        mock.done(); // verify expectations
1099
1100        // Temp was 22.52C and humidity 39.73% when above data taken.
1101        assert!(measurement.temperature > 22.5);
1102        assert!(measurement.temperature < 22.6);
1103        assert!(measurement.humidity > 39.7 && measurement.humidity < 39.8);
1104    }
1105
1106    /// Test a measurement, without using floating point math.
1107    ///
1108    /// This uses data from an actual sensor run.
1109    #[test]
1110    fn measure_no_fp() {
1111        // setup
1112        let expectations = vec![
1113            // send_trigger_measurement
1114            Transaction::write(
1115                SENSOR_ADDRESS,
1116                vec![
1117                    super::Command::TriggerMeasurement as u8,
1118                    0b0011_0011, // 0x33
1119                    0b0000_0000, // 0x00
1120                ],
1121            ),
1122            // check_status - with ready bit set to 'ready' (off)
1123            Transaction::read(SENSOR_ADDRESS, vec![0b0000_1000]),
1124            // We can now read 7 bytes. status byte, 5 data bytes, crc byte.
1125            // These are taken from a run of the sensor.
1126            Transaction::read(
1127                SENSOR_ADDRESS,
1128                vec![
1129                    0b0001_1100, //  28, 0x1c - ready, calibrated, and some mystery flags.
1130                    //             bit 8 set to 0 is ready. bit 4 set is calibrated. bit 5
1131                    //             and 3 are described as 'reserved'.
1132                    0b0110_0101, // 101, 0x65 - first byte of humidity value
1133                    0b1011_0100, // 180, 0xb4 - second byte of humidity vaue
1134                    0b0010_0101, //  37, 0x25 - split byte. 4 bits humidity, 4 bits temperature.
1135                    0b1100_1101, // 205, 0xcd - first full byte of temperature.
1136                    0b0010_0110, //  38, 0x26 - second full byte of temperature.
1137                    0b1100_0110, // 198, 0xc6 - CRC
1138                ],
1139            ),
1140        ];
1141        let mock_i2c = I2cMock::new(&expectations);
1142        let mut mock_delay = MockDelay::new();
1143
1144        // test
1145        let mut aht20 = AHT20::new(mock_i2c, SENSOR_ADDRESS);
1146        let mut aht20_init = AHT20Initialized { aht20: &mut aht20 };
1147        let measurement = aht20_init.measure_no_fp(&mut mock_delay).unwrap();
1148
1149        // verification
1150        let mut mock = aht20.destroy();
1151        mock.done(); // verify expectations
1152
1153        // Temp was 22.52C and humidity 39.73% when above data taken.
1154        // No fp mode will found that to 22.0C and 39.0%.
1155        println!("temp: {:.2}", measurement.temperature);
1156        println!("humidity: {:.2}", measurement.humidity);
1157        assert!(measurement.temperature == 22.0);
1158        assert!(measurement.humidity == 39.0);
1159    }
1160
1161    /// Test a valid CRC invocation.
1162    /// Test a valid CRC invocation.
1163    #[test]
1164    fn crc_correct() {
1165        // Example from the Interface Specification document.
1166        assert_eq!(super::compute_crc(&[0xBE, 0xEF]), 0x92);
1167    }
1168
1169    /// Test a CRC call that does not match.
1170    #[test]
1171    fn crc_wrong() {
1172        // Changed example from the Interface Specification document. This should not match - the
1173        // bytes going in are changed from the known good values, but the expected result is the
1174        // same.
1175        assert_ne!(super::compute_crc(&[0xFF, 0xFF]), 0x92);
1176    }
1177}
OSZAR »