1use embedded_devices_derive::{forward_register_fns, sensor};
110use embedded_interfaces::TransportError;
111use uom::si::f64::{Pressure, Ratio, ThermodynamicTemperature};
112use uom::si::pressure::pascal;
113use uom::si::ratio::percent;
114use uom::si::thermodynamic_temperature::degree_celsius;
115
116pub mod address;
117pub mod registers;
118
119use self::address::Address;
120use self::registers::{
121 BurstMeasurementsPTH, Config, ControlHumidity, ControlMeasurement, IIRFilter, Id, Oversampling, SensorMode,
122 TrimmingParameters1, TrimmingParameters2,
123};
124
125#[cfg_attr(feature = "defmt", derive(defmt::Format))]
126#[derive(Debug, thiserror::Error)]
127pub enum InitError<BusError> {
128 #[error("transport error")]
130 Transport(#[from] TransportError<(), BusError>),
131 #[error("invalid chip id {0:#02x}")]
133 InvalidChip(u8),
134}
135
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137#[derive(Debug, thiserror::Error)]
138pub enum MeasurementError<BusError> {
139 #[error("transport error")]
141 Transport(#[from] TransportError<(), BusError>),
142 #[error("not yet calibrated")]
144 NotCalibrated,
145}
146
147#[derive(Debug, embedded_devices_derive::Measurement)]
149pub struct Measurement {
150 #[measurement(Temperature)]
152 pub temperature: ThermodynamicTemperature,
153 #[measurement(Pressure)]
155 pub pressure: Option<Pressure>,
156 #[measurement(RelativeHumidity)]
158 pub humidity: Option<Ratio>,
159}
160
161#[derive(Debug, Clone, Copy)]
164pub(super) struct TFine(i32);
165
166#[maybe_async_cfg::maybe(
169 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
170 sync(feature = "sync"),
171 async(feature = "async")
172)]
173pub struct BME280Common<
174 D: hal::delay::DelayNs,
175 I: embedded_interfaces::registers::RegisterInterface,
176 const IS_BME: bool,
177> {
178 pub(super) delay: D,
180 interface: I,
182 pub(super) calibration_data: Option<CalibrationData>,
184}
185
186pub trait BME280CommonRegister {}
187
188#[cfg(feature = "sync")]
194pub type BME280Sync<D, I> = BME280CommonSync<D, I, true>;
195
196#[cfg(feature = "async")]
202pub type BME280Async<D, I> = BME280CommonAsync<D, I, true>;
203
204#[derive(Debug, Clone)]
206pub struct Configuration {
207 pub temperature_oversampling: Oversampling,
209 pub pressure_oversampling: Oversampling,
211 pub humidity_oversampling: Oversampling,
213 pub iir_filter: IIRFilter,
215}
216
217impl Default for Configuration {
218 fn default() -> Self {
219 Self {
220 temperature_oversampling: Oversampling::X_1,
221 pressure_oversampling: Oversampling::X_1,
222 humidity_oversampling: Oversampling::X_1,
223 iir_filter: IIRFilter::Disabled,
224 }
225 }
226}
227
228#[derive(Debug)]
229pub(super) struct CalibrationData {
230 dig_t1: u16,
231 dig_t2: i16,
232 dig_t3: i16,
233 dig_p1: u16,
234 dig_p2: i16,
235 dig_p3: i16,
236 dig_p4: i16,
237 dig_p5: i16,
238 dig_p6: i16,
239 dig_p7: i16,
240 dig_p8: i16,
241 dig_p9: i16,
242 dig_h1: u8,
243 dig_h2: i16,
244 dig_h3: u8,
245 dig_h4: i16,
246 dig_h5: i16,
247 dig_h6: i8,
248}
249
250impl CalibrationData {
251 pub fn new(params1: self::registers::TrimmingParameters1, params2: self::registers::TrimmingParameters2) -> Self {
252 let params1 = params1.unpack();
253 let params2 = params2.unpack();
254 Self {
255 dig_t1: params1.dig_t1,
256 dig_t2: params1.dig_t2,
257 dig_t3: params1.dig_t3,
258 dig_p1: params1.dig_p1,
259 dig_p2: params1.dig_p2,
260 dig_p3: params1.dig_p3,
261 dig_p4: params1.dig_p4,
262 dig_p5: params1.dig_p5,
263 dig_p6: params1.dig_p6,
264 dig_p7: params1.dig_p7,
265 dig_p8: params1.dig_p8,
266 dig_p9: params1.dig_p9,
267 dig_h1: params1.dig_h1,
268 dig_h2: params2.dig_h2,
269 dig_h3: params2.dig_h3,
270 dig_h4: (params2.dig_h4_msb as i16 * 16) | ((params2.dig_h5_lsn_h4_lsn as i16) & 0x0F),
271 dig_h5: (params2.dig_h5_msb as i16 * 16) | (((params2.dig_h5_lsn_h4_lsn as i16) & 0xF0) >> 4),
272 dig_h6: params2.dig_h6,
273 }
274 }
275
276 pub(super) fn compensate_temperature(&self, uncompensated: u32) -> (ThermodynamicTemperature, TFine) {
277 let dig_t1 = self.dig_t1 as i32;
278 let dig_t2 = self.dig_t2 as i32;
279 let dig_t3 = self.dig_t3 as i32;
280
281 let var1 = (uncompensated >> 3) as i32 - (dig_t1 << 1);
282 let var1 = (var1 * dig_t2) >> 11;
283 let var2 = (uncompensated >> 4) as i32 - dig_t1;
284 let var2 = (((var2 * var2) >> 12) * dig_t3) >> 14;
285 let t_fine = var1 + var2;
286 let temperature = (t_fine * 5 + 128) as f64 / (100.0 * 256.0);
287
288 (
289 ThermodynamicTemperature::new::<degree_celsius>(temperature),
290 TFine(t_fine),
291 )
292 }
293
294 pub(super) fn compensate_pressure(&self, uncompensated: u32, t_fine: TFine) -> Option<Pressure> {
295 let t_fine = t_fine.0;
296
297 let dig_p1 = self.dig_p1 as i64;
298 let dig_p2 = self.dig_p2 as i64;
299 let dig_p3 = self.dig_p3 as i64;
300 let dig_p4 = self.dig_p4 as i64;
301 let dig_p5 = self.dig_p5 as i64;
302 let dig_p6 = self.dig_p6 as i64;
303 let dig_p7 = self.dig_p7 as i64;
304 let dig_p8 = self.dig_p8 as i64;
305 let dig_p9 = self.dig_p9 as i64;
306
307 let var1 = t_fine as i64 - 128000;
308 let var2 = var1 * var1 * dig_p6;
309 let var2 = var2 + ((var1 * dig_p5) << 17);
310 let var2 = var2 + (dig_p4 << 35);
311 let var1 = ((var1 * var1 * dig_p3) >> 8) + ((var1 * dig_p2) << 12);
312 let var1 = (((1i64 << 47) + var1) * dig_p1) >> 33;
313 if var1 == 0 {
314 return None;
315 }
316
317 let var4: i64 = 0x100000i64 - uncompensated as i64;
318 let var4 = (((var4 << 31) - var2) * 3125) / var1;
319 let var1 = (dig_p9 * (var4 >> 13) * (var4 >> 13)) >> 25;
320 let var2 = (dig_p8 * var4) >> 19;
321 let var4 = ((var4 + var1 + var2) >> 8) + (dig_p7 << 4);
322 let pressure = (var4 as i32 as f64) / 256.0;
323 Some(Pressure::new::<pascal>(pressure))
324 }
325
326 fn compensate_humidity(&self, uncompensated: u16, t_fine: TFine) -> Ratio {
327 let t_fine = t_fine.0;
328
329 let dig_h1 = self.dig_h1 as i32;
330 let dig_h2 = self.dig_h2 as i32;
331 let dig_h3 = self.dig_h3 as i32;
332 let dig_h4 = self.dig_h4 as i32;
333 let dig_h5 = self.dig_h5 as i32;
334 let dig_h6 = self.dig_h6 as i32;
335
336 let var1 = t_fine - 76800i32;
337 let uncompensated = (uncompensated as i32) << 14;
338 let var5 = (((uncompensated - (dig_h4 << 20)) - (dig_h5 * var1)) + 0x4000) >> 15;
339 let var2 = (var1 * dig_h6) >> 10;
340 let var3 = (var1 * dig_h3) >> 11;
341 let var4 = ((var2 * (var3 + 0x8000)) >> 10) + 0x200000;
342 let var2 = ((var4 * dig_h2) + 0x2000) >> 14;
343 let var3 = var5 * var2;
344 let var4 = ((var3 >> 15) * (var3 >> 15)) >> 7;
345 let var5 = var3 - ((var4 * dig_h1) >> 4);
346 let var5 = var5.clamp(0, 0x19000000);
347 let var5 = var5 >> 12;
348
349 let humidity = var5 as f64 / (1i32 << 10) as f64;
350 Ratio::new::<percent>(humidity)
351 }
352}
353
354#[maybe_async_cfg::maybe(
355 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
356 sync(feature = "sync"),
357 async(feature = "async")
358)]
359impl<D, I, const IS_BME: bool>
360 BME280Common<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>, IS_BME>
361where
362 I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
363 D: hal::delay::DelayNs,
364{
365 #[inline]
371 pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
372 Self {
373 delay,
374 interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
375 calibration_data: None,
376 }
377 }
378}
379
380#[maybe_async_cfg::maybe(
381 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), SpiDevice),
382 sync(feature = "sync"),
383 async(feature = "async")
384)]
385impl<D, I, const IS_BME: bool> BME280Common<D, embedded_interfaces::spi::SpiDevice<I>, IS_BME>
386where
387 I: hal::spi::r#SpiDevice,
388 D: hal::delay::DelayNs,
389{
390 #[inline]
396 pub fn new_spi(delay: D, interface: I) -> Self {
397 Self {
398 delay,
399 interface: embedded_interfaces::spi::SpiDevice::new(interface),
400 calibration_data: None,
401 }
402 }
403}
404
405#[forward_register_fns]
406#[maybe_async_cfg::maybe(
407 idents(
408 hal(sync = "embedded_hal", async = "embedded_hal_async"),
409 RegisterInterface,
410 ResettableDevice
411 ),
412 sync(feature = "sync"),
413 async(feature = "async")
414)]
415impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface, const IS_BME: bool>
416 BME280Common<D, I, IS_BME>
417{
418 pub async fn init(&mut self) -> Result<(), InitError<I::BusError>> {
425 use crate::device::ResettableDevice;
426
427 self.reset().await?;
429
430 let chip = self.read_register::<Id>().await?.read_chip();
432 if let self::registers::Chip::Invalid(x) = chip {
433 return Err(InitError::InvalidChip(x));
434 }
435
436 self.calibrate().await?;
438 Ok(())
439 }
440
441 pub async fn calibrate(&mut self) -> Result<(), TransportError<(), I::BusError>> {
446 let params1 = self.read_register::<TrimmingParameters1>().await?;
447 let params2 = self.read_register::<TrimmingParameters2>().await?;
448 self.calibration_data = Some(CalibrationData::new(params1, params2));
449
450 Ok(())
451 }
452}
453
454#[maybe_async_cfg::maybe(
455 idents(
456 hal(sync = "embedded_hal", async = "embedded_hal_async"),
457 RegisterInterface,
458 ResettableDevice
459 ),
460 sync(feature = "sync"),
461 async(feature = "async")
462)]
463impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface, const IS_BME: bool>
464 crate::device::ResettableDevice for BME280Common<D, I, IS_BME>
465{
466 type Error = TransportError<(), I::BusError>;
467
468 async fn reset(&mut self) -> Result<(), Self::Error> {
471 self.write_register(self::registers::Reset::default()).await?;
472 self.delay.delay_ms(2).await;
473 Ok(())
474 }
475}
476
477#[sensor(Temperature, Pressure, RelativeHumidity)]
480#[maybe_async_cfg::maybe(
481 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
482 sync(feature = "sync"),
483 async(feature = "async")
484)]
485impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> BME280Common<D, I, true> {
486 pub async fn configure(&mut self, config: Configuration) -> Result<(), TransportError<(), I::BusError>> {
489 self.write_register(ControlHumidity::default().with_oversampling(config.humidity_oversampling))
490 .await?;
491
492 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
494 self.write_register(
495 reg_ctrl_m
496 .with_temperature_oversampling(config.temperature_oversampling)
497 .with_pressure_oversampling(config.pressure_oversampling),
498 )
499 .await?;
500
501 let mut reg_config = self.read_register::<Config>().await?;
502 reg_config.write_filter(config.iir_filter);
503 self.write_register(reg_config).await?;
504
505 Ok(())
506 }
507}
508
509#[maybe_async_cfg::maybe(
510 idents(
511 hal(sync = "embedded_hal", async = "embedded_hal_async"),
512 RegisterInterface,
513 OneshotSensor
514 ),
515 sync(feature = "sync"),
516 async(feature = "async")
517)]
518impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
519 for BME280Common<D, I, true>
520{
521 type Error = MeasurementError<I::BusError>;
522 type Measurement = Measurement;
523
524 async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
534 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
535 self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Forced))
536 .await?;
537
538 let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
540 let o_h = reg_ctrl_h.read_oversampling();
541 let o_t = reg_ctrl_m.read_temperature_oversampling();
542 let o_p = reg_ctrl_m.read_pressure_oversampling();
543
544 let mut max_measurement_delay_us = 1250 + 2300 * o_t.factor();
547 if o_p.factor() > 0 {
548 max_measurement_delay_us += 575 + 2300 * o_p.factor();
549 }
550 if o_h.factor() > 0 {
551 max_measurement_delay_us += 575 + 2300 * o_h.factor();
552 }
553
554 self.delay.delay_us(max_measurement_delay_us).await;
555
556 let raw_data = self.read_register::<BurstMeasurementsPTH>().await?;
557 let Some(ref cal) = self.calibration_data else {
558 return Err(MeasurementError::NotCalibrated);
559 };
560
561 let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
562 let pressure = (o_p != Oversampling::Disabled)
563 .then(|| cal.compensate_pressure(raw_data.read_pressure_value(), t_fine))
564 .flatten();
565 let humidity =
566 (o_h != Oversampling::Disabled).then(|| cal.compensate_humidity(raw_data.read_humidity_value(), t_fine));
567
568 Ok(Measurement {
569 temperature,
570 pressure,
571 humidity,
572 })
573 }
574}
575
576#[maybe_async_cfg::maybe(
577 idents(
578 hal(sync = "embedded_hal", async = "embedded_hal_async"),
579 RegisterInterface,
580 ContinuousSensor
581 ),
582 sync(feature = "sync"),
583 async(feature = "async")
584)]
585impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
586 for BME280Common<D, I, true>
587{
588 type Error = MeasurementError<I::BusError>;
589 type Measurement = Measurement;
590
591 async fn start_measuring(&mut self) -> Result<(), Self::Error> {
593 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
594 self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Normal))
595 .await?;
596 Ok(())
597 }
598
599 async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
601 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
602 self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Sleep))
603 .await?;
604 Ok(())
605 }
606
607 async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
609 let reg_config = self.read_register::<Config>().await?;
610 let t_standby_us = reg_config.read_standby_time().time_us();
611
612 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
613 let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
614
615 let o_h = reg_ctrl_h.read_oversampling();
616 let o_t = reg_ctrl_m.read_temperature_oversampling();
617 let o_p = reg_ctrl_m.read_pressure_oversampling();
618
619 let mut t_measure_us = 1250 + 2300 * o_t.factor();
622 if o_p.factor() > 0 {
623 t_measure_us += 575 + 2300 * o_p.factor();
624 }
625 if o_h.factor() > 0 {
626 t_measure_us += 575 + 2300 * o_h.factor();
627 }
628 Ok(t_measure_us + t_standby_us)
629 }
630
631 async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
633 let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
634 let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
635
636 let o_h = reg_ctrl_h.read_oversampling();
637 let o_p = reg_ctrl_m.read_pressure_oversampling();
638
639 let raw_data = self.read_register::<BurstMeasurementsPTH>().await?;
640 let Some(ref cal) = self.calibration_data else {
641 return Err(MeasurementError::NotCalibrated);
642 };
643
644 let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
645 let pressure = (o_p != Oversampling::Disabled)
646 .then(|| cal.compensate_pressure(raw_data.read_pressure_value(), t_fine))
647 .flatten();
648 let humidity =
649 (o_h != Oversampling::Disabled).then(|| cal.compensate_humidity(raw_data.read_humidity_value(), t_fine));
650
651 Ok(Some(Measurement {
652 temperature,
653 pressure,
654 humidity,
655 }))
656 }
657
658 async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
660 Ok(true)
661 }
662
663 async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
665 let interval = self.measurement_interval_us().await?;
666 self.delay.delay_us(interval).await;
667 self.current_measurement().await?.ok_or_else(|| {
668 MeasurementError::Transport(TransportError::Unexpected(
669 "measurement was not ready even though we expected it to be",
670 ))
671 })
672 }
673}