embedded_sht3x/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code, missing_docs)]
3#![no_std]
4
5use bitflags::bitflags;
6use core::fmt::Display;
7use crc::{Crc, CRC_8_NRSC_5};
8use embedded_hal::i2c::{Operation, SevenBitAddress};
9#[allow(unused_imports)]
10use micromath::F32Ext;
11
12/// The I2C address when the ADDR pin is connected to logic low
13pub const I2C_ADDRESS_LOGIC_LOW: SevenBitAddress = 0x44;
14/// The I2C address when the ADDR pin is connected to logic high
15pub const I2C_ADDRESS_LOGIC_HIGH: SevenBitAddress = 0x45;
16/// The default I2C address (ADDR pin connected to low)
17pub const DEFAULT_I2C_ADDRESS: SevenBitAddress = I2C_ADDRESS_LOGIC_LOW;
18
19const CLEAR_STATUS_COMMAND: &[u8] = &[0x30, 0x41];
20const DISABLE_HEATER_COMMAND: &[u8] = &[0x30, 0x66];
21const ENABLE_HEATER_COMMAND: &[u8] = &[0x30, 0x6d];
22const GET_STATUS_COMMAND: &[u8] = &[0xf3, 0x2d];
23const MEASUREMENT_HIGH_REPEATIBILITY_COMMAND: &[u8] = &[0x2c, 0x06];
24const MEASUREMENT_MEDIUM_REPEATIBILITY_COMMAND: &[u8] = &[0x2c, 0x0d];
25const MEASUREMENT_LOW_REPEATIBILITY_COMMAND: &[u8] = &[0x2c, 0x10];
26const RESET_COMMAND: &[u8] = &[0x30, 0xa2];
27
28/// All possible errors generated when using the Sht3x struct
29#[derive(Debug)]
30pub enum Error<I2cE>
31where
32    I2cE: embedded_hal::i2c::Error,
33{
34    /// I²C bus error
35    I2c(I2cE),
36    /// The computed CRC and the one sent by the device mismatch
37    BadCrc,
38}
39
40impl<I2cE> From<I2cE> for Error<I2cE>
41where
42    I2cE: embedded_hal::i2c::Error,
43{
44    fn from(value: I2cE) -> Self {
45        Error::I2c(value)
46    }
47}
48
49/// The repeatability influences the measument duration and the energy consumption of the sensor
50/// It also gives a more or less accurate measurement
51///
52/// Here are the repeatibility values for humidity and temperature:
53///  - Low repeatability: 0.21 %RH - 0.15 °C
54///  - Medium repeatability: 0.15 %RH - 0.08 °C
55///  - High repeatability: 0.08 %RH - 0.04 °C
56///
57/// The measurement durations are the following:
58///  - Low repeatability: 4 ms (with supply voltage of 2.4-5.5 V) or 4.5 ms (with supply voltage of 2.15-2.4 V)
59///  - Medium repeatability: 6 ms (with supply voltage of 2.4-5.5 V) or 6.5 ms (with supply voltage of 2.15-2.4 V)
60///  - High repeatability: 15 ms (with supply voltage of 2.4-5.5 V) or 15.5 ms (with supply voltage of 2.15-2.4 V)
61#[derive(Debug)]
62pub enum Repeatability {
63    /// High repeatability: 0.08 %RH - 0.04 °C
64    High,
65    /// Medium repeatability: 0.15 %RH - 0.08 °C
66    Medium,
67    /// Low repeatability: 0.21 %RH - 0.15 °C
68    Low,
69}
70
71bitflags! {
72    /// The status of the sensor.
73    ///
74    /// It gives information on the operational status of the heater, the alert
75    /// mode and on the execution status of the last command and the last write
76    /// sequence.
77    #[derive(Debug)]
78    pub struct Status: u16 {
79        /// Write data checksum status
80        ///
81        /// - '0': checksum of last write transfer was correct
82        /// - '1': checksum of last write transfer was incorrect
83        const WRITE_DATA_CHECKSUM = 1 << 0;
84        /// Command status
85        ///
86        /// - '0': last command executed successfully
87        /// - '1': last command not processed. It was either invalid or failed
88        /// the integrated command checksum
89        const COMMAND = 1 << 1;
90        /// System reset detected
91        ///
92        /// - '0': no reset detected since last [Sht3x<I2C, D>::clear_status()]
93        /// call
94        /// - '1': reset detected (hard reset, supply fail or soft reset
95        /// ([Sht3x<I2c, D>::reset()])
96        const RESET = 1 << 4;
97        /// Temperature tracking alert
98        ///
99        /// - '0': no alert
100        /// - '1': alert
101        const T_TRACKING_ALERT = 1 << 10;
102        /// Relative humidity tracking alert
103        ///
104        /// - '0': no alert
105        /// - '1': alert
106        const RH_TRACKING_ALERT = 1 << 11;
107        /// Heater status
108        ///
109        /// - '0': Heater OFF
110        /// - '1': Heater ON
111        const HEATER = 1 << 13;
112        /// Alert pending status
113        ///
114        /// - '0': no pending alerts
115        /// - '1': at least one pending alert
116        const ALERT_PENDING = 1 << 15;
117    }
118}
119
120impl Display for Status {
121    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122        bitflags::parser::to_writer(self, f)
123    }
124}
125
126/// The temperature unit to use in the measurements.
127#[derive(Clone, Copy, Debug, Default, PartialEq)]
128pub enum TemperatureUnit {
129    #[default]
130    /// Temperature in °C.
131    Celcius,
132    /// Temperature in °F.
133    Farenheit,
134}
135
136/// The result of a measurement.
137///
138/// Such a measurement can be obtained using [`Sht3x::single_measurement()`].
139#[derive(Clone, Copy, Debug, Default)]
140pub struct Measurement {
141    /// The measured relative humidity (in %).
142    pub humidity: f32,
143    /// The measured temperature (either in °C or °F according to the configuration of the device)
144    pub temperature: f32,
145    /// The temperature unit used for the measurement
146    pub unit: TemperatureUnit,
147}
148
149/// SHT3x device driver
150#[derive(Debug)]
151pub struct Sht3x<I2C, D> {
152    address: SevenBitAddress,
153    delay: D,
154    i2c: I2C,
155    /// The repeatability to use for measurements (defaults to medium).
156    pub repeatability: Repeatability,
157    /// The temperature unit to use for measurements (defaults to celcius).
158    pub unit: TemperatureUnit,
159}
160
161impl<I2C, D> Sht3x<I2C, D>
162where
163    I2C: embedded_hal::i2c::I2c,
164    D: embedded_hal::delay::DelayNs,
165{
166    /// Clear the status of the sensor.
167    ///
168    /// All the flags of the status register will be cleared (set to zero).
169    pub fn clear_status(&mut self) -> Result<(), Error<I2C::Error>> {
170        self.i2c.write(self.address, CLEAR_STATUS_COMMAND)?;
171        Ok(())
172    }
173
174    /// Deactivate the internal heater.
175    pub fn disable_heater(&mut self) -> Result<(), Error<I2C::Error>> {
176        self.i2c.write(self.address, DISABLE_HEATER_COMMAND)?;
177        Ok(())
178    }
179
180    /// Activate the internal heater.
181    pub fn enable_heater(&mut self) -> Result<(), Error<I2C::Error>> {
182        self.i2c.write(self.address, ENABLE_HEATER_COMMAND)?;
183        Ok(())
184    }
185
186    /// Get the current status of the sensor
187    pub fn get_status(&mut self) -> Result<Status, Error<I2C::Error>> {
188        let mut status = [0u8; 2];
189        let mut status_crc = [0u8; 1];
190        let mut operations = [
191            Operation::Write(GET_STATUS_COMMAND),
192            Operation::Read(&mut status),
193            Operation::Read(&mut status_crc),
194        ];
195        self.i2c.transaction(self.address, &mut operations)?;
196        Self::check_crc(&status, status_crc[0])?;
197        Ok(Status::from_bits_retain(Self::get_u16_value(&status)))
198    }
199
200    /// Perform a single-shot measurement
201    ///
202    /// This driver uses clock stretching so the result of the measurement is returned
203    /// as soon as the data is available after the measurement command has been sent to the sensor.
204    /// Therefore this call will block for a least 4 ms and at most 15.5 ms depending on the chosen
205    /// repeatability and the supply voltage of the sensor.
206    pub fn single_measurement(&mut self) -> Result<Measurement, Error<I2C::Error>> {
207        let command = match self.repeatability {
208            Repeatability::High => MEASUREMENT_HIGH_REPEATIBILITY_COMMAND,
209            Repeatability::Medium => MEASUREMENT_MEDIUM_REPEATIBILITY_COMMAND,
210            Repeatability::Low => MEASUREMENT_LOW_REPEATIBILITY_COMMAND,
211        };
212        let mut temperature = [0u8; 2];
213        let mut humidity = [0u8; 2];
214        let mut temperature_crc = [0u8; 1];
215        let mut humidity_crc = [0u8; 1];
216        let mut operations = [
217            Operation::Write(command),
218            Operation::Read(&mut temperature),
219            Operation::Read(&mut temperature_crc),
220            Operation::Read(&mut humidity),
221            Operation::Read(&mut humidity_crc),
222        ];
223        self.i2c.transaction(self.address, &mut operations)?;
224        Self::check_crc(&temperature, temperature_crc[0])?;
225        Self::check_crc(&humidity, humidity_crc[0])?;
226        let temperature = Self::get_u16_value(&temperature);
227        let humidity = Self::get_u16_value(&humidity);
228
229        Ok(Measurement {
230            temperature: match self.unit {
231                TemperatureUnit::Celcius => ((temperature as f32 * 175.0) / 65535.0) - 45.0,
232                TemperatureUnit::Farenheit => ((temperature as f32 * 315.0) / 65535.0) - 49.0,
233            },
234            humidity: (humidity as f32 * 100.0) / 65535.0,
235            unit: self.unit,
236        })
237    }
238
239    /// Create a new instance of the SHT3x device.
240    pub fn new(i2c: I2C, address: SevenBitAddress, delay: D) -> Self {
241        Self {
242            address,
243            delay,
244            i2c,
245            repeatability: Repeatability::Medium,
246            unit: TemperatureUnit::Celcius,
247        }
248    }
249
250    /// Perform a soft reset to force the system into a well-defined state without removing
251    /// the power supply.
252    pub fn reset(&mut self) -> Result<(), Error<I2C::Error>> {
253        self.i2c.write(self.address, RESET_COMMAND)?;
254        self.delay.delay_us(1500); // Wait for the sensor to enter idle state
255        Ok(())
256    }
257
258    fn calc_crc(data: &[u8; 2]) -> u8 {
259        let crc = Crc::<u8>::new(&CRC_8_NRSC_5);
260        let mut digest = crc.digest();
261        digest.update(data);
262        digest.finalize()
263    }
264
265    fn check_crc(data: &[u8; 2], expected_crc: u8) -> Result<(), Error<I2C::Error>> {
266        if Self::calc_crc(data) != expected_crc {
267            Err(Error::BadCrc)
268        } else {
269            Ok(())
270        }
271    }
272
273    #[inline]
274    fn get_u16_value(data: &[u8; 2]) -> u16 {
275        (data[0] as u16) << 8 | (data[1] as u16)
276    }
277}
278
279/// Converts a relative humidity value in % to an absolute humidity value in g/m³,
280/// temperature being in °C.
281pub fn calculate_absolute_humidity(measurement: Measurement) -> f32 {
282    let temperature = match measurement.unit {
283        TemperatureUnit::Celcius => measurement.temperature,
284        TemperatureUnit::Farenheit => convert_farenheit_to_celcius(measurement.temperature),
285    };
286    (6.112 * ((17.67 * temperature) / (temperature + 243.5)).exp() * measurement.humidity * 2.1674)
287        / (273.15 + temperature)
288}
289
290/// Converts a temperature in °C to °F.
291pub fn convert_celcius_to_farenheit(temperature: f32) -> f32 {
292    temperature * 1.8 + 32.0
293}
294
295/// Converts a temperature in °F to °C.
296pub fn convert_farenheit_to_celcius(temperature: f32) -> f32 {
297    (temperature - 32.0) * 0.55555
298}
299
300#[cfg(test)]
301mod tests {
302    use crate::*;
303    use embedded_hal_mock::eh1::delay::StdSleep as Delay;
304    use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
305
306    #[test]
307    fn calculate_absolute_humidity() {
308        assert!(
309            (crate::calculate_absolute_humidity(Measurement {
310                humidity: 45.59,
311                temperature: 21.18,
312                unit: TemperatureUnit::Celcius
313            }) - 8.43)
314                .abs()
315                < 0.01
316        );
317        assert!(
318            (crate::calculate_absolute_humidity(Measurement {
319                humidity: 45.59,
320                temperature: 70.12,
321                unit: TemperatureUnit::Farenheit
322            }) - 8.43)
323                .abs()
324                < 0.01
325        );
326        assert!(
327            (crate::calculate_absolute_humidity(Measurement {
328                humidity: 34.71,
329                temperature: 2.93,
330                unit: TemperatureUnit::Celcius
331            }) - 2.06)
332                .abs()
333                < 0.01
334        );
335        assert!(
336            (crate::calculate_absolute_humidity(Measurement {
337                humidity: 74.91,
338                temperature: 107.7,
339                unit: TemperatureUnit::Farenheit
340            }) - 42.49)
341                .abs()
342                < 0.01
343        );
344    }
345
346    #[test]
347    fn clear_status() {
348        let expectations = [
349            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CLEAR_STATUS_COMMAND.to_vec()),
350            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
351            I2cTransaction::write(DEFAULT_I2C_ADDRESS, GET_STATUS_COMMAND.to_vec()),
352            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x00, 0x00].to_vec()),
353            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x81].to_vec()),
354            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
355        ];
356        let mut i2c = I2cMock::new(&expectations);
357        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
358        device.clear_status().unwrap();
359        let status = device.get_status().unwrap();
360        assert!(!status.contains(Status::WRITE_DATA_CHECKSUM));
361        assert!(!status.contains(Status::COMMAND));
362        assert!(!status.contains(Status::RESET));
363        assert!(!status.contains(Status::T_TRACKING_ALERT));
364        assert!(!status.contains(Status::RH_TRACKING_ALERT));
365        assert!(!status.contains(Status::HEATER));
366        assert!(!status.contains(Status::ALERT_PENDING));
367        i2c.done();
368    }
369
370    #[test]
371    fn convert_celcius_to_farenheit() {
372        assert!((crate::convert_celcius_to_farenheit(0.0) - 32.0).abs() < 0.01);
373        assert!((crate::convert_celcius_to_farenheit(15.73) - 60.31).abs() < 0.01);
374        assert!((crate::convert_celcius_to_farenheit(-7.49) - 18.52).abs() < 0.01);
375        assert!((crate::convert_celcius_to_farenheit(37.5) - 99.5).abs() < 0.01);
376    }
377
378    #[test]
379    fn convert_farenheit_to_celcius() {
380        assert!((crate::convert_farenheit_to_celcius(32.0) - 0.0).abs() < 0.01);
381        assert!((crate::convert_farenheit_to_celcius(60.31) - 15.73).abs() < 0.01);
382        assert!((crate::convert_farenheit_to_celcius(18.52) - -7.49).abs() < 0.01);
383        assert!((crate::convert_farenheit_to_celcius(99.5) - 37.5).abs() < 0.01);
384    }
385
386    #[test]
387    fn get_status() {
388        let expectations = [
389            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
390            I2cTransaction::write(DEFAULT_I2C_ADDRESS, GET_STATUS_COMMAND.to_vec()),
391            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x00, 0x00].to_vec()),
392            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x81].to_vec()),
393            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
394        ];
395        let mut i2c = I2cMock::new(&expectations);
396        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
397        let status = device.get_status().unwrap();
398        assert!(!status.contains(Status::WRITE_DATA_CHECKSUM));
399        assert!(!status.contains(Status::COMMAND));
400        assert!(!status.contains(Status::RESET));
401        assert!(!status.contains(Status::T_TRACKING_ALERT));
402        assert!(!status.contains(Status::RH_TRACKING_ALERT));
403        assert!(!status.contains(Status::HEATER));
404        assert!(!status.contains(Status::ALERT_PENDING));
405        i2c.done();
406    }
407
408    #[test]
409    fn heater() {
410        let expectations = [
411            I2cTransaction::write(DEFAULT_I2C_ADDRESS, ENABLE_HEATER_COMMAND.to_vec()),
412            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
413            I2cTransaction::write(DEFAULT_I2C_ADDRESS, GET_STATUS_COMMAND.to_vec()),
414            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x20, 0x03].to_vec()),
415            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x0e].to_vec()),
416            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
417            I2cTransaction::write(DEFAULT_I2C_ADDRESS, DISABLE_HEATER_COMMAND.to_vec()),
418            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
419            I2cTransaction::write(DEFAULT_I2C_ADDRESS, GET_STATUS_COMMAND.to_vec()),
420            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x00, 0x03].to_vec()),
421            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0xd2].to_vec()),
422            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
423        ];
424        let mut i2c = I2cMock::new(&expectations);
425        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
426        device.enable_heater().unwrap();
427        let status = device.get_status().unwrap();
428        assert!(status.contains(Status::WRITE_DATA_CHECKSUM));
429        assert!(status.contains(Status::COMMAND));
430        assert!(!status.contains(Status::RESET));
431        assert!(!status.contains(Status::T_TRACKING_ALERT));
432        assert!(!status.contains(Status::RH_TRACKING_ALERT));
433        assert!(status.contains(Status::HEATER));
434        assert!(!status.contains(Status::ALERT_PENDING));
435        device.disable_heater().unwrap();
436        let status = device.get_status().unwrap();
437        assert!(status.contains(Status::WRITE_DATA_CHECKSUM));
438        assert!(status.contains(Status::COMMAND));
439        assert!(!status.contains(Status::RESET));
440        assert!(!status.contains(Status::T_TRACKING_ALERT));
441        assert!(!status.contains(Status::RH_TRACKING_ALERT));
442        assert!(!status.contains(Status::HEATER));
443        assert!(!status.contains(Status::ALERT_PENDING));
444        i2c.done();
445    }
446
447    #[test]
448    fn reset() {
449        let expectations = [I2cTransaction::write(
450            DEFAULT_I2C_ADDRESS,
451            RESET_COMMAND.to_vec(),
452        )];
453        let mut i2c = I2cMock::new(&expectations);
454        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
455        device.reset().unwrap();
456        i2c.done();
457    }
458
459    #[test]
460    fn single_measurement_farenheit() {
461        let expectations = [
462            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
463            I2cTransaction::write(
464                DEFAULT_I2C_ADDRESS,
465                MEASUREMENT_MEDIUM_REPEATIBILITY_COMMAND.to_vec(),
466            ),
467            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x71, 0x17].to_vec()),
468            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x9a].to_vec()),
469            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0xcb, 0x91].to_vec()),
470            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x39].to_vec()),
471            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
472        ];
473        let mut i2c = I2cMock::new(&expectations);
474        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
475        device.unit = TemperatureUnit::Farenheit;
476        let measurement = device.single_measurement().unwrap();
477        assert!((measurement.temperature - 90.16).abs() < 0.01);
478        assert!((measurement.humidity - 79.52).abs() < 0.01);
479        i2c.done();
480    }
481
482    #[test]
483    fn single_measurement_high_repeatability() {
484        let expectations = [
485            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
486            I2cTransaction::write(
487                DEFAULT_I2C_ADDRESS,
488                MEASUREMENT_HIGH_REPEATIBILITY_COMMAND.to_vec(),
489            ),
490            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x5f, 0x58].to_vec()),
491            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x38].to_vec()),
492            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x7b, 0xb2].to_vec()),
493            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x7d].to_vec()),
494            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
495        ];
496        let mut i2c = I2cMock::new(&expectations);
497        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
498        device.repeatability = Repeatability::High;
499        let measurement = device.single_measurement().unwrap();
500        assert!((measurement.temperature - 20.18).abs() < 0.01);
501        assert!((measurement.humidity - 48.32).abs() < 0.01);
502        i2c.done();
503    }
504
505    #[test]
506    fn single_measurement_low_repeatability() {
507        let expectations = [
508            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
509            I2cTransaction::write(
510                DEFAULT_I2C_ADDRESS,
511                MEASUREMENT_LOW_REPEATIBILITY_COMMAND.to_vec(),
512            ),
513            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x5f, 0x58].to_vec()),
514            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x38].to_vec()),
515            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x7b, 0xb2].to_vec()),
516            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x7d].to_vec()),
517            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
518        ];
519        let mut i2c = I2cMock::new(&expectations);
520        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
521        device.repeatability = Repeatability::Low;
522        let measurement = device.single_measurement().unwrap();
523        assert!((measurement.temperature - 20.18).abs() < 0.01);
524        assert!((measurement.humidity - 48.32).abs() < 0.01);
525        i2c.done();
526    }
527
528    #[test]
529    fn single_measurement_medium_repeatability() {
530        let expectations = [
531            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
532            I2cTransaction::write(
533                DEFAULT_I2C_ADDRESS,
534                MEASUREMENT_MEDIUM_REPEATIBILITY_COMMAND.to_vec(),
535            ),
536            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x71, 0x17].to_vec()),
537            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x9a].to_vec()),
538            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0xcb, 0x91].to_vec()),
539            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x39].to_vec()),
540            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
541        ];
542        let mut i2c = I2cMock::new(&expectations);
543        let mut device = Sht3x::new(&mut i2c, DEFAULT_I2C_ADDRESS, Delay {});
544        let measurement = device.single_measurement().unwrap();
545        assert!((measurement.temperature - 32.31).abs() < 0.01);
546        assert!((measurement.humidity - 79.52).abs() < 0.01);
547        i2c.done();
548    }
549}