1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use embedded_hal::spi::SpiDevice;
6use pamoja_core::Sensor;
7
8use super::{
9 image_updating, max_measurement_micros, measuring, register, Calibration, Config, CtrlHum,
10 CtrlMeas, Filter, Measurement, Mode, Oversampling, RawMeasurement, CALIB_HUMIDITY_LEN,
11 CALIB_TEMP_PRESS_LEN, CHIP_ID, DATA_LEN, RESET_WORD, STARTUP_MICROS,
12};
13use crate::driver::{I2cRegisters, RegisterBus, SpiRegisters};
14use crate::error::{DriverError, SensorError};
15
16pub const STATUS_POLLS: u8 = 20;
19
20#[derive(Debug)]
51pub struct Bme280<B, D> {
52 bus: B,
53 delay: D,
54 temperature: Oversampling,
55 pressure: Oversampling,
56 humidity: Oversampling,
57 filter: Filter,
58 calibration: Option<Calibration>,
59}
60
61impl<I2C: I2c, D> Bme280<I2cRegisters<I2C>, D> {
62 pub fn i2c(bus: I2C, address: u8, delay: D) -> Self {
75 Bme280::new(I2cRegisters::new(bus, address), delay)
76 }
77}
78
79impl<SPI: SpiDevice, D> Bme280<SpiRegisters<SPI>, D> {
80 pub fn spi(device: SPI, delay: D) -> Self {
91 Bme280::new(SpiRegisters::new(device), delay)
92 }
93}
94
95impl<B, D> Bme280<B, D> {
96 pub fn new(bus: B, delay: D) -> Self {
107 Bme280 {
108 bus,
109 delay,
110 temperature: Oversampling::X1,
111 pressure: Oversampling::X1,
112 humidity: Oversampling::X1,
113 filter: Filter::Off,
114 calibration: None,
115 }
116 }
117
118 pub fn with_oversampling(
132 mut self,
133 temperature: Oversampling,
134 pressure: Oversampling,
135 humidity: Oversampling,
136 ) -> Self {
137 self.temperature = temperature;
138 self.pressure = pressure;
139 self.humidity = humidity;
140 self
141 }
142
143 pub fn with_filter(mut self, filter: Filter) -> Self {
155 self.filter = filter;
156 self
157 }
158
159 pub fn calibration(&self) -> Option<&Calibration> {
161 self.calibration.as_ref()
162 }
163
164 pub fn release(self) -> (B, D) {
170 (self.bus, self.delay)
171 }
172}
173
174impl<B: RegisterBus, D: DelayNs> Bme280<B, D> {
175 pub fn init(&mut self) -> Result<(), DriverError<B::Error>> {
190 self.write(register::RESET, RESET_WORD)?;
191 self.delay.delay_us(STARTUP_MICROS);
192 self.wait(image_updating)?;
193
194 if self.read(register::CHIP_ID)? != CHIP_ID {
195 return Err(SensorError::Identity.into());
196 }
197
198 let mut temp_press = [0u8; CALIB_TEMP_PRESS_LEN];
199 let mut humidity = [0u8; CALIB_HUMIDITY_LEN];
200 self.bus
201 .read_registers(register::CALIB_TEMP_PRESS, &mut temp_press)
202 .map_err(DriverError::Bus)?;
203 self.bus
204 .read_registers(register::CALIB_HUMIDITY, &mut humidity)
205 .map_err(DriverError::Bus)?;
206 self.calibration = Some(Calibration::from_registers(&temp_press, &humidity));
207
208 let config = Config {
209 filter: self.filter,
210 ..Config::default()
211 };
212 self.write(register::CONFIG, config.bits())?;
213 let ctrl_hum = CtrlHum {
214 humidity: self.humidity,
215 };
216 self.write(register::CTRL_HUM, ctrl_hum.bits())?;
217 self.write(register::CTRL_MEAS, self.ctrl_meas(Mode::Sleep).bits())?;
218 Ok(())
219 }
220
221 pub fn measure(&mut self) -> Result<Measurement, DriverError<B::Error>> {
237 if self.calibration.is_none() {
238 self.init()?;
239 }
240 self.write(register::CTRL_MEAS, self.ctrl_meas(Mode::Forced).bits())?;
241 self.delay.delay_us(max_measurement_micros(
242 self.temperature,
243 self.pressure,
244 self.humidity,
245 ));
246 self.wait(measuring)?;
247
248 let mut data = [0u8; DATA_LEN];
249 self.bus
250 .read_registers(register::DATA, &mut data)
251 .map_err(DriverError::Bus)?;
252 let raw = RawMeasurement::from_registers(&data);
253 let calibration = self.calibration.as_ref().expect("read at initialization");
254 Ok(calibration.compensate(&raw))
255 }
256
257 fn ctrl_meas(&self, mode: Mode) -> CtrlMeas {
258 CtrlMeas {
259 temperature: self.temperature,
260 pressure: self.pressure,
261 mode,
262 }
263 }
264
265 fn read(&mut self, register: u8) -> Result<u8, DriverError<B::Error>> {
266 self.bus.read_register(register).map_err(DriverError::Bus)
267 }
268
269 fn write(&mut self, register: u8, value: u8) -> Result<(), DriverError<B::Error>> {
270 self.bus
271 .write_register(register, value)
272 .map_err(DriverError::Bus)
273 }
274
275 fn wait(&mut self, busy: fn(u8) -> bool) -> Result<(), DriverError<B::Error>> {
276 for _ in 0..STATUS_POLLS {
277 if !busy(self.read(register::STATUS)?) {
278 return Ok(());
279 }
280 self.delay.delay_ms(1);
281 }
282 Err(DriverError::Timeout)
283 }
284}
285
286impl<B, D> Sensor for Bme280<B, D>
287where
288 B: RegisterBus + Send,
289 B::Error: core::fmt::Debug,
290 D: DelayNs + Send,
291{
292 type Reading = Measurement;
293
294 async fn read(&mut self) -> pamoja_core::Result<Measurement> {
295 self.measure().map_err(pamoja_core::Error::from)
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use crate::bme280::{Standby, I2C_ADDRESS_PRIMARY, I2C_ADDRESS_SECONDARY};
303 use alloc::vec;
304 use alloc::vec::Vec;
305 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
306 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep, SpiScript, SpiStep};
307
308 const CALIBRATION_A: [u8; CALIB_TEMP_PRESS_LEN] = [
309 0x45, 0x6F, 0x6F, 0x68, 0x32, 0x00, 0x46, 0x91, 0x6A, 0xD6, 0xD0, 0x0B, 0x4E, 0x1E, 0x88,
310 0xFF, 0xF9, 0xFF, 0xAC, 0x26, 0x0A, 0xD8, 0xBD, 0x10, 0x00, 0x4B,
311 ];
312 const CALIBRATION_B: [u8; CALIB_HUMIDITY_LEN] = [0x62, 0x01, 0x00, 0x15, 0x23, 0x03, 0x1E];
313 const DATA: [u8; DATA_LEN] = [0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00, 0x75, 0x30];
314
315 fn init_steps(address: u8, config: u8, ctrl_hum: u8, ctrl_meas: u8) -> Vec<I2cStep> {
316 vec![
317 I2cStep::write(address, [register::RESET, RESET_WORD]),
318 I2cStep::write_read(address, [register::STATUS], [0x00]),
319 I2cStep::write_read(address, [register::CHIP_ID], [CHIP_ID]),
320 I2cStep::write_read(address, [register::CALIB_TEMP_PRESS], CALIBRATION_A),
321 I2cStep::write_read(address, [register::CALIB_HUMIDITY], CALIBRATION_B),
322 I2cStep::write(address, [register::CONFIG, config]),
323 I2cStep::write(address, [register::CTRL_HUM, ctrl_hum]),
324 I2cStep::write(address, [register::CTRL_MEAS, ctrl_meas]),
325 ]
326 }
327
328 fn measure_steps(address: u8, ctrl_meas: u8) -> Vec<I2cStep> {
329 vec![
330 I2cStep::write(address, [register::CTRL_MEAS, ctrl_meas]),
331 I2cStep::write_read(address, [register::STATUS], [0x00]),
332 I2cStep::write_read(address, [register::DATA], DATA),
333 ]
334 }
335
336 #[test]
337 fn init_follows_the_datasheet_sequence_and_keeps_the_calibration() {
338 let mut steps = init_steps(I2C_ADDRESS_PRIMARY, 0x00, 0x01, 0x24);
339 steps.extend(measure_steps(I2C_ADDRESS_PRIMARY, 0x25));
340 let mut sensor = Bme280::i2c(I2cScript::new(steps), I2C_ADDRESS_PRIMARY, DelayLog::new());
341 sensor.init().unwrap();
342 let expected = Calibration::from_registers(&CALIBRATION_A, &CALIBRATION_B);
343 assert_eq!(sensor.calibration(), Some(&expected));
344
345 let measurement = sensor.measure().unwrap();
346 assert_eq!(
347 measurement,
348 expected.compensate(&RawMeasurement::from_registers(&DATA))
349 );
350 assert_eq!(measurement.temperature_centi_celsius, 2044);
351
352 let (bus, delay) = sensor.release();
353 assert!(bus.release().done());
354 assert_eq!(delay.waits_ns()[0], STARTUP_MICROS * 1_000);
355 assert_eq!(
356 delay.waits_ns()[1],
357 9_300 * 1_000,
358 "x1, x1, x1 is 9.3 ms at most"
359 );
360 }
361
362 #[test]
363 fn oversampling_and_filter_settings_reach_the_control_registers() {
364 let config = Config {
365 standby: Standby::Ms0_5,
366 filter: Filter::X16,
367 spi_3wire: false,
368 }
369 .bits();
370 let ctrl_meas_sleep = CtrlMeas {
371 temperature: Oversampling::X2,
372 pressure: Oversampling::X16,
373 mode: Mode::Sleep,
374 }
375 .bits();
376 let mut steps = init_steps(I2C_ADDRESS_SECONDARY, config, 0x00, ctrl_meas_sleep);
377 steps.extend(measure_steps(I2C_ADDRESS_SECONDARY, ctrl_meas_sleep | 0x01));
378 let mut sensor = Bme280::i2c(
379 I2cScript::new(steps),
380 I2C_ADDRESS_SECONDARY,
381 DelayLog::new(),
382 )
383 .with_oversampling(Oversampling::X2, Oversampling::X16, Oversampling::Skipped)
384 .with_filter(Filter::X16);
385 sensor.measure().unwrap();
386 let (bus, delay) = sensor.release();
387 assert!(bus.release().done());
388 let expected =
389 max_measurement_micros(Oversampling::X2, Oversampling::X16, Oversampling::Skipped);
390 assert_eq!(delay.waits_ns()[1], expected * 1_000);
391 }
392
393 #[test]
394 fn a_part_that_is_not_a_bme280_is_refused_before_anything_else() {
395 let steps = [
396 I2cStep::write(0x76, [register::RESET, RESET_WORD]),
397 I2cStep::write_read(0x76, [register::STATUS], [0x00]),
398 I2cStep::write_read(0x76, [register::CHIP_ID], [0x58]),
399 ];
400 let mut sensor = Bme280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
401 assert_eq!(
402 sensor.init(),
403 Err(DriverError::Sensor(SensorError::Identity))
404 );
405 assert!(sensor.calibration().is_none());
406 assert!(sensor.release().0.release().done());
407 }
408
409 #[test]
410 fn a_conversion_that_never_finishes_is_a_timeout() {
411 let mut steps = init_steps(0x76, 0x00, 0x01, 0x24);
412 steps.push(I2cStep::write(0x76, [register::CTRL_MEAS, 0x25]));
413 for _ in 0..STATUS_POLLS {
414 steps.push(I2cStep::write_read(0x76, [register::STATUS], [0x08]));
415 }
416 let mut sensor = Bme280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
417 assert_eq!(sensor.measure(), Err(DriverError::Timeout));
418 assert!(sensor.release().0.release().done());
419 }
420
421 #[test]
422 fn a_bus_fault_surfaces_as_a_bus_error_and_maps_to_the_core_io_error() {
423 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
424 let steps = [I2cStep::fault(0x76, kind)];
425 let mut sensor = Bme280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
426 let error = sensor.init().unwrap_err();
427 assert!(matches!(error, DriverError::Bus(_)));
428 let core: pamoja_core::Error = error.into();
429 assert!(matches!(core, pamoja_core::Error::Io(_)));
430 let identity: pamoja_core::Error =
431 DriverError::<ErrorKind>::Sensor(SensorError::Identity).into();
432 assert!(matches!(identity, pamoja_core::Error::Io(_)));
433 let crc: pamoja_core::Error = DriverError::<ErrorKind>::Sensor(SensorError::Crc).into();
434 assert!(matches!(crc, pamoja_core::Error::Codec(_)));
435 }
436
437 #[test]
438 fn as_a_sensor_it_reads_and_maps_to_one_channel() {
439 let mut steps = init_steps(0x76, 0x00, 0x01, 0x24);
440 steps.extend(measure_steps(0x76, 0x25));
441 let sensor = Bme280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
442 let mut celsius = sensor.map(|measurement| measurement.celsius());
443 let reading = block_on(celsius.read()).unwrap();
444 assert!((reading - 20.44).abs() < 0.001);
445 assert!(celsius.into_inner().release().0.release().done());
446 }
447
448 #[test]
449 fn over_spi_the_same_sequence_uses_the_bosch_control_bytes() {
450 let steps = [
451 SpiStep::write([register::RESET & 0x7F, RESET_WORD]),
452 SpiStep::write([register::STATUS | 0x80]),
453 SpiStep::read([0x00]),
454 SpiStep::write([register::CHIP_ID | 0x80]),
455 SpiStep::read([CHIP_ID]),
456 SpiStep::write([register::CALIB_TEMP_PRESS | 0x80]),
457 SpiStep::read(CALIBRATION_A),
458 SpiStep::write([register::CALIB_HUMIDITY | 0x80]),
459 SpiStep::read(CALIBRATION_B),
460 SpiStep::write([register::CONFIG & 0x7F, 0x00]),
461 SpiStep::write([register::CTRL_HUM & 0x7F, 0x01]),
462 SpiStep::write([register::CTRL_MEAS & 0x7F, 0x24]),
463 SpiStep::write([register::CTRL_MEAS & 0x7F, 0x25]),
464 SpiStep::write([register::STATUS | 0x80]),
465 SpiStep::read([0x00]),
466 SpiStep::write([register::DATA | 0x80]),
467 SpiStep::read(DATA),
468 ];
469 let mut sensor = Bme280::spi(SpiScript::new(steps), DelayLog::new());
470 let measurement = sensor.measure().unwrap();
471 assert_eq!(measurement.temperature_centi_celsius, 2044);
472 assert!(sensor.release().0.release().done());
473 }
474
475 #[test]
476 fn init_and_a_forced_measurement_make_the_transfers_the_datasheet_gives() {
477 const PART: u8 = crate::bme280::I2C_ADDRESS_PRIMARY;
478 let calibration_a = [
479 0x45, 0x6F, 0x6F, 0x68, 0x32, 0x00, 0x46, 0x91, 0x6A, 0xD6, 0xD0, 0x0B, 0x4E, 0x1E,
480 0x88, 0xFF, 0xF9, 0xFF, 0xAC, 0x26, 0x0A, 0xD8, 0xBD, 0x10, 0x00, 0x4B,
481 ];
482 let calibration_b = [0x62, 0x01, 0x00, 0x15, 0x23, 0x03, 0x1E];
483 let bus = I2cScript::new([
484 I2cStep::write(PART, [0xE0, 0xB6]),
485 I2cStep::write_read(PART, [0xF3], [0x00]),
486 I2cStep::write_read(PART, [0xD0], [0x60]),
487 I2cStep::write_read(PART, [0x88], calibration_a),
488 I2cStep::write_read(PART, [0xE1], calibration_b),
489 I2cStep::write(PART, [0xF5, 0x00]),
490 I2cStep::write(PART, [0xF2, 0x01]),
491 I2cStep::write(PART, [0xF4, 0x24]),
492 I2cStep::write(PART, [0xF4, 0x25]),
493 I2cStep::write_read(PART, [0xF3], [0x00]),
494 I2cStep::write_read(
495 PART,
496 [0xF7],
497 [0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00, 0x75, 0x30],
498 ),
499 ]);
500
501 let mut sensor = Bme280::i2c(bus, PART, DelayLog::new());
502 let measurement = block_on(Sensor::read(&mut sensor)).expect("the scripted part answers");
503 assert_eq!(measurement.temperature_centi_celsius, 2044);
504
505 let (registers, delay) = sensor.release();
506 assert!(registers.release().done());
507 assert!(delay.total_micros() >= 2_000 + 9_300);
508 }
509}