1#[cfg(feature = "embedded-hal")]
17mod driver;
18#[cfg(feature = "embedded-hal")]
19pub mod sim;
20
21#[cfg(feature = "embedded-hal")]
22pub use driver::{Bme280, STATUS_POLLS};
23
24pub const I2C_ADDRESS_PRIMARY: u8 = 0x76;
26pub const I2C_ADDRESS_SECONDARY: u8 = 0x77;
28pub const CHIP_ID: u8 = 0x60;
30
31pub mod register {
33 pub const CHIP_ID: u8 = 0xD0;
35 pub const RESET: u8 = 0xE0;
37 pub const CALIB_TEMP_PRESS: u8 = 0x88;
39 pub const CALIB_HUMIDITY: u8 = 0xE1;
41 pub const DATA: u8 = 0xF7;
44 pub const CTRL_HUM: u8 = 0xF2;
46 pub const STATUS: u8 = 0xF3;
48 pub const CTRL_MEAS: u8 = 0xF4;
50 pub const CONFIG: u8 = 0xF5;
52}
53
54pub const RESET_WORD: u8 = 0xB6;
56pub const SKIPPED_OUTPUT: u32 = 0x80000;
58pub const SKIPPED_HUMIDITY: u16 = 0x8000;
60pub const CALIB_TEMP_PRESS_LEN: usize = 26;
62pub const CALIB_HUMIDITY_LEN: usize = 7;
64pub const DATA_LEN: usize = 8;
66pub const STARTUP_MICROS: u32 = 2_000;
69
70pub fn measuring(status: u8) -> bool {
81 status & 0x08 != 0
82}
83
84pub fn image_updating(status: u8) -> bool {
95 status & 0x01 != 0
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
103pub struct Calibration {
104 dig_t1: u16,
105 dig_t2: i16,
106 dig_t3: i16,
107 dig_p1: u16,
108 dig_p2: i16,
109 dig_p3: i16,
110 dig_p4: i16,
111 dig_p5: i16,
112 dig_p6: i16,
113 dig_p7: i16,
114 dig_p8: i16,
115 dig_p9: i16,
116 dig_h1: u8,
117 dig_h2: i16,
118 dig_h3: u8,
119 dig_h4: i16,
120 dig_h5: i16,
121 dig_h6: i8,
122}
123
124impl Calibration {
125 pub fn from_registers(temp_press: &[u8; 26], humidity: &[u8; 7]) -> Calibration {
139 let tp = temp_press;
140 let h = humidity;
141 Calibration {
142 dig_t1: u16::from_le_bytes([tp[0], tp[1]]),
143 dig_t2: i16::from_le_bytes([tp[2], tp[3]]),
144 dig_t3: i16::from_le_bytes([tp[4], tp[5]]),
145 dig_p1: u16::from_le_bytes([tp[6], tp[7]]),
146 dig_p2: i16::from_le_bytes([tp[8], tp[9]]),
147 dig_p3: i16::from_le_bytes([tp[10], tp[11]]),
148 dig_p4: i16::from_le_bytes([tp[12], tp[13]]),
149 dig_p5: i16::from_le_bytes([tp[14], tp[15]]),
150 dig_p6: i16::from_le_bytes([tp[16], tp[17]]),
151 dig_p7: i16::from_le_bytes([tp[18], tp[19]]),
152 dig_p8: i16::from_le_bytes([tp[20], tp[21]]),
153 dig_p9: i16::from_le_bytes([tp[22], tp[23]]),
154 dig_h1: tp[25],
155 dig_h2: i16::from_le_bytes([h[0], h[1]]),
156 dig_h3: h[2],
157 dig_h4: ((h[3] as i8 as i16) * 16) | (h[4] & 0x0F) as i16,
158 dig_h5: ((h[5] as i8 as i16) * 16) | (h[4] >> 4) as i16,
159 dig_h6: h[6] as i8,
160 }
161 }
162
163 pub fn compensate(&self, raw: &RawMeasurement) -> Measurement {
176 let t_fine = self.t_fine(raw.temperature);
177 Measurement {
178 temperature_centi_celsius: compensate_temperature(t_fine),
179 pressure_centi_pascals: self.compensate_pressure(raw.pressure, t_fine),
180 humidity_q22_10: self.compensate_humidity(raw.humidity, t_fine),
181 }
182 }
183
184 fn t_fine(&self, adc_t: i32) -> i32 {
186 let var1 = ((adc_t / 8) - (self.dig_t1 as i32 * 2)) * self.dig_t2 as i32 / 2048;
187 let near = (adc_t / 16) - self.dig_t1 as i32;
188 let var2 = (((near * near) / 4096) * self.dig_t3 as i32) / 16384;
189 var1 + var2
190 }
191
192 fn compensate_pressure(&self, adc_p: i32, t_fine: i32) -> u32 {
194 let mut var1 = t_fine as i64 - 128000;
195 let mut var2 = var1 * var1 * self.dig_p6 as i64;
196 var2 += (var1 * self.dig_p5 as i64) * 131072;
197 var2 += self.dig_p4 as i64 * 34359738368;
198 var1 = (var1 * var1 * self.dig_p3 as i64) / 256 + (var1 * self.dig_p2 as i64 * 4096);
199 var1 = (140737488355328 + var1) * self.dig_p1 as i64 / 8589934592;
200 if var1 == 0 {
201 return 3_000_000;
202 }
203 let mut var4 = 1048576 - adc_p as i64;
204 var4 = (((var4 * 2147483648) - var2) * 3125) / var1;
205 var1 = (self.dig_p9 as i64 * (var4 / 8192) * (var4 / 8192)) / 33554432;
206 var2 = (self.dig_p8 as i64 * var4) / 524288;
207 var4 = ((var4 + var1 + var2) / 256) + (self.dig_p7 as i64 * 16);
208 let pressure = ((var4 / 2) * 100) / 128;
209 pressure.clamp(3_000_000, 11_000_000) as u32
210 }
211
212 fn compensate_humidity(&self, adc_h: i32, t_fine: i32) -> u32 {
214 let var1 = t_fine - 76800;
215 let var2 = adc_h * 16384;
216 let var3 = self.dig_h4 as i32 * 1048576;
217 let var4 = self.dig_h5 as i32 * var1;
218 let var5 = (((var2 - var3) - var4) + 16384) / 32768;
219 let var2 = (var1 * self.dig_h6 as i32) / 1024;
220 let var3 = (var1 * self.dig_h3 as i32) / 2048;
221 let var4 = ((var2 * (var3 + 32768)) / 1024) + 2097152;
222 let var2 = ((var4 * self.dig_h2 as i32) + 8192) / 16384;
223 let var3 = var5 * var2;
224 let var4 = ((var3 / 32768) * (var3 / 32768)) / 128;
225 let var5 = (var3 - ((var4 * self.dig_h1 as i32) / 16)).clamp(0, 419430400);
226 ((var5 / 4096) as u32).min(102400)
227 }
228}
229
230fn compensate_temperature(t_fine: i32) -> i32 {
232 ((t_fine * 5 + 128) / 256).clamp(-4000, 8500)
233}
234
235#[derive(Clone, Copy, Debug, PartialEq, Eq)]
237pub struct RawMeasurement {
238 pub temperature: i32,
240 pub pressure: i32,
242 pub humidity: i32,
244}
245
246impl RawMeasurement {
247 pub fn from_registers(data: &[u8; 8]) -> RawMeasurement {
260 let pressure =
261 (i32::from(data[0]) << 12) | (i32::from(data[1]) << 4) | (i32::from(data[2]) >> 4);
262 let temperature =
263 (i32::from(data[3]) << 12) | (i32::from(data[4]) << 4) | (i32::from(data[5]) >> 4);
264 let humidity = (i32::from(data[6]) << 8) | i32::from(data[7]);
265 RawMeasurement {
266 temperature,
267 pressure,
268 humidity,
269 }
270 }
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
278pub struct Measurement {
279 pub temperature_centi_celsius: i32,
281 pub pressure_centi_pascals: u32,
283 pub humidity_q22_10: u32,
285}
286
287impl Measurement {
288 pub fn celsius(&self) -> f32 {
290 self.temperature_centi_celsius as f32 / 100.0
291 }
292
293 pub fn pascals(&self) -> u32 {
295 self.pressure_centi_pascals / 100
296 }
297
298 pub fn hectopascals(&self) -> f32 {
300 self.pressure_centi_pascals as f32 / 10_000.0
301 }
302
303 pub fn relative_humidity_percent(&self) -> f32 {
305 self.humidity_q22_10 as f32 / 1024.0
306 }
307}
308
309#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
314pub enum Oversampling {
315 #[default]
318 Skipped,
319 X1,
321 X2,
323 X4,
325 X8,
327 X16,
329}
330
331impl Oversampling {
332 pub fn code(self) -> u8 {
334 match self {
335 Oversampling::Skipped => 0b000,
336 Oversampling::X1 => 0b001,
337 Oversampling::X2 => 0b010,
338 Oversampling::X4 => 0b011,
339 Oversampling::X8 => 0b100,
340 Oversampling::X16 => 0b101,
341 }
342 }
343
344 pub fn from_code(code: u8) -> Oversampling {
356 match code & 0b111 {
357 0b000 => Oversampling::Skipped,
358 0b001 => Oversampling::X1,
359 0b010 => Oversampling::X2,
360 0b011 => Oversampling::X4,
361 0b100 => Oversampling::X8,
362 _ => Oversampling::X16,
363 }
364 }
365
366 pub fn factor(self) -> u8 {
368 match self {
369 Oversampling::Skipped => 0,
370 Oversampling::X1 => 1,
371 Oversampling::X2 => 2,
372 Oversampling::X4 => 4,
373 Oversampling::X8 => 8,
374 Oversampling::X16 => 16,
375 }
376 }
377}
378
379#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
381pub enum Mode {
382 #[default]
384 Sleep,
385 Forced,
387 Normal,
389}
390
391impl Mode {
392 pub fn code(self) -> u8 {
394 match self {
395 Mode::Sleep => 0b00,
396 Mode::Forced => 0b01,
397 Mode::Normal => 0b11,
398 }
399 }
400
401 pub fn from_code(code: u8) -> Mode {
411 match code & 0b11 {
412 0b00 => Mode::Sleep,
413 0b11 => Mode::Normal,
414 _ => Mode::Forced,
415 }
416 }
417}
418
419#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
424pub enum Standby {
425 #[default]
427 Ms0_5,
428 Ms62_5,
430 Ms125,
432 Ms250,
434 Ms500,
436 Ms1000,
438 Ms10,
440 Ms20,
442}
443
444impl Standby {
445 pub fn code(self) -> u8 {
447 match self {
448 Standby::Ms0_5 => 0b000,
449 Standby::Ms62_5 => 0b001,
450 Standby::Ms125 => 0b010,
451 Standby::Ms250 => 0b011,
452 Standby::Ms500 => 0b100,
453 Standby::Ms1000 => 0b101,
454 Standby::Ms10 => 0b110,
455 Standby::Ms20 => 0b111,
456 }
457 }
458
459 pub fn from_code(code: u8) -> Standby {
469 match code & 0b111 {
470 0b000 => Standby::Ms0_5,
471 0b001 => Standby::Ms62_5,
472 0b010 => Standby::Ms125,
473 0b011 => Standby::Ms250,
474 0b100 => Standby::Ms500,
475 0b101 => Standby::Ms1000,
476 0b110 => Standby::Ms10,
477 _ => Standby::Ms20,
478 }
479 }
480
481 pub fn microseconds(self) -> u32 {
483 match self {
484 Standby::Ms0_5 => 500,
485 Standby::Ms62_5 => 62_500,
486 Standby::Ms125 => 125_000,
487 Standby::Ms250 => 250_000,
488 Standby::Ms500 => 500_000,
489 Standby::Ms1000 => 1_000_000,
490 Standby::Ms10 => 10_000,
491 Standby::Ms20 => 20_000,
492 }
493 }
494}
495
496#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
501pub enum Filter {
502 #[default]
504 Off,
505 X2,
507 X4,
509 X8,
511 X16,
513}
514
515impl Filter {
516 pub fn code(self) -> u8 {
518 match self {
519 Filter::Off => 0b000,
520 Filter::X2 => 0b001,
521 Filter::X4 => 0b010,
522 Filter::X8 => 0b011,
523 Filter::X16 => 0b100,
524 }
525 }
526
527 pub fn from_code(code: u8) -> Filter {
537 match code & 0b111 {
538 0b000 => Filter::Off,
539 0b001 => Filter::X2,
540 0b010 => Filter::X4,
541 0b011 => Filter::X8,
542 _ => Filter::X16,
543 }
544 }
545
546 pub fn coefficient(self) -> u8 {
548 match self {
549 Filter::Off => 0,
550 Filter::X2 => 2,
551 Filter::X4 => 4,
552 Filter::X8 => 8,
553 Filter::X16 => 16,
554 }
555 }
556}
557
558#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
563pub struct CtrlHum {
564 pub humidity: Oversampling,
566}
567
568impl CtrlHum {
569 pub fn bits(self) -> u8 {
575 self.humidity.code()
576 }
577
578 pub fn from_bits(bits: u8) -> CtrlHum {
588 CtrlHum {
589 humidity: Oversampling::from_code(bits),
590 }
591 }
592}
593
594#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
599pub struct CtrlMeas {
600 pub temperature: Oversampling,
602 pub pressure: Oversampling,
604 pub mode: Mode,
606}
607
608impl CtrlMeas {
609 pub fn bits(self) -> u8 {
615 (self.temperature.code() << 5) | (self.pressure.code() << 2) | self.mode.code()
616 }
617
618 pub fn from_bits(bits: u8) -> CtrlMeas {
628 CtrlMeas {
629 temperature: Oversampling::from_code(bits >> 5),
630 pressure: Oversampling::from_code(bits >> 2),
631 mode: Mode::from_code(bits),
632 }
633 }
634}
635
636#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
641pub struct Config {
642 pub standby: Standby,
644 pub filter: Filter,
646 pub spi_3wire: bool,
648}
649
650impl Config {
651 pub fn bits(self) -> u8 {
657 (self.standby.code() << 5) | (self.filter.code() << 2) | u8::from(self.spi_3wire)
658 }
659
660 pub fn from_bits(bits: u8) -> Config {
670 Config {
671 standby: Standby::from_code(bits >> 5),
672 filter: Filter::from_code(bits >> 2),
673 spi_3wire: bits & 0x01 != 0,
674 }
675 }
676}
677
678pub fn max_measurement_micros(
705 temperature: Oversampling,
706 pressure: Oversampling,
707 humidity: Oversampling,
708) -> u32 {
709 let mut micros = 1_250;
710 if temperature != Oversampling::Skipped {
711 micros += 2_300 * u32::from(temperature.factor());
712 }
713 if pressure != Oversampling::Skipped {
714 micros += 2_300 * u32::from(pressure.factor()) + 575;
715 }
716 if humidity != Oversampling::Skipped {
717 micros += 2_300 * u32::from(humidity.factor()) + 575;
718 }
719 micros
720}
721
722pub fn typical_measurement_micros(
738 temperature: Oversampling,
739 pressure: Oversampling,
740 humidity: Oversampling,
741) -> u32 {
742 let mut micros = 1_000;
743 if temperature != Oversampling::Skipped {
744 micros += 2_000 * u32::from(temperature.factor());
745 }
746 if pressure != Oversampling::Skipped {
747 micros += 2_000 * u32::from(pressure.factor()) + 500;
748 }
749 if humidity != Oversampling::Skipped {
750 micros += 2_000 * u32::from(humidity.factor()) + 500;
751 }
752 micros
753}
754
755impl Calibration {
756 pub fn to_registers(&self) -> ([u8; CALIB_TEMP_PRESS_LEN], [u8; CALIB_HUMIDITY_LEN]) {
767 let mut tp = [0u8; CALIB_TEMP_PRESS_LEN];
768 let words = [
769 self.dig_t1,
770 self.dig_t2 as u16,
771 self.dig_t3 as u16,
772 self.dig_p1,
773 self.dig_p2 as u16,
774 self.dig_p3 as u16,
775 self.dig_p4 as u16,
776 self.dig_p5 as u16,
777 self.dig_p6 as u16,
778 self.dig_p7 as u16,
779 self.dig_p8 as u16,
780 self.dig_p9 as u16,
781 ];
782 for (index, word) in words.into_iter().enumerate() {
783 let [low, high] = word.to_le_bytes();
784 tp[2 * index] = low;
785 tp[2 * index + 1] = high;
786 }
787 tp[25] = self.dig_h1;
788
789 let h2 = (self.dig_h2 as u16).to_le_bytes();
790 let h = [
791 h2[0],
792 h2[1],
793 self.dig_h3,
794 (self.dig_h4 >> 4) as u8,
795 (((self.dig_h5 & 0x0F) as u8) << 4) | (self.dig_h4 & 0x0F) as u8,
796 (self.dig_h5 >> 4) as u8,
797 self.dig_h6 as u8,
798 ];
799 (tp, h)
800 }
801}
802
803impl RawMeasurement {
804 pub fn to_registers(&self) -> [u8; DATA_LEN] {
813 let pressure = self.pressure as u32;
814 let temperature = self.temperature as u32;
815 let humidity = self.humidity as u16;
816 [
817 (pressure >> 12) as u8,
818 (pressure >> 4) as u8,
819 ((pressure & 0x0F) << 4) as u8,
820 (temperature >> 12) as u8,
821 (temperature >> 4) as u8,
822 ((temperature & 0x0F) << 4) as u8,
823 (humidity >> 8) as u8,
824 humidity as u8,
825 ]
826 }
827}
828
829#[cfg(test)]
830mod control_tests {
831 use super::*;
832
833 #[test]
834 fn oversampling_codes_follow_the_datasheet_tables() {
835 assert_eq!(Oversampling::Skipped.code(), 0b000);
836 assert_eq!(Oversampling::X16.code(), 0b101);
837 assert_eq!(Oversampling::from_code(0b110), Oversampling::X16);
838 assert_eq!(Oversampling::from_code(0b100), Oversampling::X8);
839 assert_eq!(Oversampling::X8.factor(), 8);
840 }
841
842 #[test]
843 fn mode_codes_treat_both_forced_encodings_alike() {
844 assert_eq!(Mode::from_code(0b01), Mode::Forced);
845 assert_eq!(Mode::from_code(0b10), Mode::Forced);
846 assert_eq!(Mode::from_code(0b11), Mode::Normal);
847 assert_eq!(Mode::Normal.code(), 0b11);
848 }
849
850 #[test]
851 fn standby_codes_carry_the_bme280_reassignment_of_the_top_two() {
852 assert_eq!(Standby::Ms10.code(), 0b110);
853 assert_eq!(Standby::Ms20.code(), 0b111);
854 assert_eq!(Standby::from_code(0b110).microseconds(), 10_000);
855 assert_eq!(Standby::from_code(0b101).microseconds(), 1_000_000);
856 assert_eq!(Standby::from_code(0b000).microseconds(), 500);
857 }
858
859 #[test]
860 fn filter_codes_saturate_at_sixteen() {
861 assert_eq!(Filter::X16.code(), 0b100);
862 assert_eq!(Filter::from_code(0b111), Filter::X16);
863 assert_eq!(Filter::from_code(0b011).coefficient(), 8);
864 assert_eq!(Filter::Off.coefficient(), 0);
865 }
866
867 #[test]
868 fn control_registers_pack_their_fields_where_the_datasheet_puts_them() {
869 let ctrl_meas = CtrlMeas {
870 temperature: Oversampling::X2,
871 pressure: Oversampling::X16,
872 mode: Mode::Normal,
873 };
874 assert_eq!(ctrl_meas.bits(), 0b0101_0111);
875 assert_eq!(CtrlMeas::from_bits(0b0101_0111), ctrl_meas);
876 let config = Config {
877 standby: Standby::Ms1000,
878 filter: Filter::X4,
879 spi_3wire: true,
880 };
881 assert_eq!(config.bits(), 0b1010_1001);
882 assert_eq!(Config::from_bits(0b1010_1001), config);
883 assert_eq!(CtrlHum::from_bits(0x03).humidity, Oversampling::X4);
884 assert_eq!(CtrlMeas::default().bits(), 0x00);
885 assert_eq!(Config::default().bits(), 0x00);
886 }
887
888 #[test]
889 fn status_flags_sit_in_bits_three_and_zero() {
890 assert!(measuring(0x08));
891 assert!(!measuring(0x01));
892 assert!(image_updating(0x01));
893 assert!(!image_updating(0x08));
894 }
895
896 #[test]
897 fn measurement_times_match_the_datasheet_worked_example() {
898 let (t, p, h) = (Oversampling::X1, Oversampling::X4, Oversampling::Skipped);
899 assert_eq!(max_measurement_micros(t, p, h), 13_325);
900 assert_eq!(typical_measurement_micros(t, p, h), 11_500);
901 let all = Oversampling::X1;
902 assert_eq!(max_measurement_micros(all, all, all), 9_300);
903 assert_eq!(
904 max_measurement_micros(
905 Oversampling::Skipped,
906 Oversampling::Skipped,
907 Oversampling::Skipped
908 ),
909 1_250
910 );
911 }
912
913 #[test]
914 fn calibration_registers_round_trip_including_the_shared_h4_h5_byte() {
915 let tp = [
916 0x45, 0x6F, 0x6F, 0x68, 0x32, 0x00, 0x46, 0x91, 0x6A, 0xD6, 0xD0, 0x0B, 0x4E, 0x1E,
917 0x88, 0xFF, 0xF9, 0xFF, 0xAC, 0x26, 0x0A, 0xD8, 0xBD, 0x10, 0x00, 0x4B,
918 ];
919 let h = [0x62, 0x01, 0x00, 0x15, 0x23, 0x03, 0x1E];
920 let calibration = Calibration::from_registers(&tp, &h);
921 assert_eq!(calibration.dig_t1, 28485);
922 assert_eq!(calibration.dig_p2, -10646);
923 assert_eq!(calibration.dig_h4, 339);
924 assert_eq!(calibration.dig_h5, 50);
925 assert_eq!(calibration.to_registers(), (tp, h));
926
927 let negative = Calibration {
928 dig_h4: -100,
929 dig_h5: -7,
930 ..calibration
931 };
932 let (tp2, h2) = negative.to_registers();
933 assert_eq!(Calibration::from_registers(&tp2, &h2), negative);
934 }
935
936 #[test]
937 fn raw_measurement_registers_round_trip() {
938 let data = [0x53, 0xD0, 0xE0, 0x81, 0xD9, 0x00, 0x6E, 0x62];
939 let raw = RawMeasurement::from_registers(&data);
940 assert_eq!(raw.to_registers(), data);
941 let skipped = RawMeasurement {
942 temperature: SKIPPED_OUTPUT as i32,
943 pressure: SKIPPED_OUTPUT as i32,
944 humidity: i32::from(SKIPPED_HUMIDITY),
945 };
946 assert_eq!(
947 RawMeasurement::from_registers(&skipped.to_registers()),
948 skipped
949 );
950 }
951}
952
953#[cfg(test)]
954mod tests {
955 use super::*;
956
957 fn sample_calibration() -> Calibration {
960 Calibration {
961 dig_t1: 28485,
962 dig_t2: 26735,
963 dig_t3: 50,
964 dig_p1: 37190,
965 dig_p2: -10646,
966 dig_p3: 3024,
967 dig_p4: 7758,
968 dig_p5: -120,
969 dig_p6: -7,
970 dig_p7: 9900,
971 dig_p8: -10230,
972 dig_p9: 4285,
973 dig_h1: 75,
974 dig_h2: 354,
975 dig_h3: 0,
976 dig_h4: 339,
977 dig_h5: 50,
978 dig_h6: 30,
979 }
980 }
981
982 #[test]
983 fn temperature_matches_the_datasheet_worked_example() {
984 let calib = Calibration {
990 dig_t1: 27504,
991 dig_t2: 26435,
992 dig_t3: -1000,
993 ..sample_calibration()
994 };
995 let t_fine = calib.t_fine(519888);
996 assert!((t_fine - 128422).abs() <= 1, "t_fine {t_fine}");
997 assert_eq!(compensate_temperature(t_fine), 2508);
998 }
999
1000 #[test]
1001 fn integer_compensation_tracks_the_floating_point_reference() {
1002 let calib = sample_calibration();
1003 for &adc_t in &[400_000, 500_000, 519_888, 540_000] {
1006 let t_fine = calib.t_fine(adc_t);
1007 let t_int = compensate_temperature(t_fine);
1008 let t_ref = reference_temperature(&calib, adc_t);
1009 assert!(
1010 ((t_int as f64) / 100.0 - t_ref).abs() < 0.02,
1011 "temperature {t_int} vs {t_ref}"
1012 );
1013
1014 for &adc_p in &[300_000, 415_148, 512_000] {
1015 let p_int = calib.compensate_pressure(adc_p, t_fine);
1016 let p_ref = reference_pressure(&calib, adc_p, adc_t);
1017 assert!(
1018 ((p_int as f64) / 100.0 - p_ref).abs() < 2.0,
1019 "pressure {p_int} (centi-Pa) vs {p_ref} Pa"
1020 );
1021 }
1022
1023 for &adc_h in &[20_000, 30_000, 45_000] {
1024 let h_int = calib.compensate_humidity(adc_h, t_fine);
1025 let h_ref = reference_humidity(&calib, adc_h, adc_t);
1026 assert!(
1027 ((h_int as f64) / 1024.0 - h_ref).abs() < 0.05,
1028 "humidity {h_int} (Q22.10) vs {h_ref} %"
1029 );
1030 }
1031 }
1032 }
1033
1034 #[test]
1035 fn raw_measurement_unpacks_the_data_registers() {
1036 let data = [0x53, 0xD0, 0xE0, 0x81, 0xD9, 0x00, 0x6E, 0x62];
1038 let raw = RawMeasurement::from_registers(&data);
1039 assert_eq!(raw.pressure, 0x5_3D0E);
1040 assert_eq!(raw.temperature, 0x8_1D90);
1041 assert_eq!(raw.humidity, 0x6E62);
1042 }
1043
1044 #[test]
1045 fn measurement_unit_helpers_convert_correctly() {
1046 let measurement = Measurement {
1047 temperature_centi_celsius: 2508,
1048 pressure_centi_pascals: 10_065_300,
1049 humidity_q22_10: 47_104,
1050 };
1051 assert!((measurement.celsius() - 25.08).abs() < 0.001);
1052 assert_eq!(measurement.pascals(), 100_653);
1053 assert!((measurement.hectopascals() - 1006.53).abs() < 0.01);
1054 assert!((measurement.relative_humidity_percent() - 46.0).abs() < 0.001);
1055 }
1056
1057 fn reference_temperature(c: &Calibration, adc_t: i32) -> f64 {
1060 let var1 = (adc_t as f64 / 16384.0 - c.dig_t1 as f64 / 1024.0) * c.dig_t2 as f64;
1061 let var2 = {
1062 let v = adc_t as f64 / 131072.0 - c.dig_t1 as f64 / 8192.0;
1063 v * v * c.dig_t3 as f64
1064 };
1065 ((var1 + var2) / 5120.0).clamp(-40.0, 85.0)
1066 }
1067
1068 fn reference_t_fine(c: &Calibration, adc_t: i32) -> f64 {
1069 let var1 = (adc_t as f64 / 16384.0 - c.dig_t1 as f64 / 1024.0) * c.dig_t2 as f64;
1070 let var2 = {
1071 let v = adc_t as f64 / 131072.0 - c.dig_t1 as f64 / 8192.0;
1072 v * v * c.dig_t3 as f64
1073 };
1074 var1 + var2
1075 }
1076
1077 fn reference_pressure(c: &Calibration, adc_p: i32, adc_t: i32) -> f64 {
1078 let t_fine = reference_t_fine(c, adc_t);
1079 let mut var1 = (t_fine / 2.0) - 64000.0;
1080 let mut var2 = var1 * var1 * c.dig_p6 as f64 / 32768.0;
1081 var2 += var1 * c.dig_p5 as f64 * 2.0;
1082 var2 = (var2 / 4.0) + (c.dig_p4 as f64 * 65536.0);
1083 let var3 = c.dig_p3 as f64 * var1 * var1 / 524288.0;
1084 var1 = (var3 + c.dig_p2 as f64 * var1) / 524288.0;
1085 var1 = (1.0 + var1 / 32768.0) * c.dig_p1 as f64;
1086 if var1 <= 0.0 {
1087 return 30000.0;
1088 }
1089 let mut pressure = 1048576.0 - adc_p as f64;
1090 pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1;
1091 var1 = c.dig_p9 as f64 * pressure * pressure / 2147483648.0;
1092 var2 = pressure * c.dig_p8 as f64 / 32768.0;
1093 pressure += (var1 + var2 + c.dig_p7 as f64) / 16.0;
1094 pressure.clamp(30000.0, 110000.0)
1095 }
1096
1097 fn reference_humidity(c: &Calibration, adc_h: i32, adc_t: i32) -> f64 {
1098 let t_fine = reference_t_fine(c, adc_t);
1099 let var1 = t_fine - 76800.0;
1100 let var2 = c.dig_h4 as f64 * 64.0 + (c.dig_h5 as f64 / 16384.0) * var1;
1101 let var3 = adc_h as f64 - var2;
1102 let var4 = c.dig_h2 as f64 / 65536.0;
1103 let var5 = 1.0 + (c.dig_h3 as f64 / 67108864.0) * var1;
1104 let var6 = 1.0 + (c.dig_h6 as f64 / 67108864.0) * var1 * var5;
1105 let var6 = var3 * var4 * (var5 * var6);
1106 (var6 * (1.0 - c.dig_h1 as f64 * var6 / 524288.0)).clamp(0.0, 100.0)
1107 }
1108}