1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Sensor;
6
7use super::{
8 data_ready, device_id, eeprom_unlock_busy, raw_from_celsius, register, revision, Alerts,
9 Averaging, Configuration, ConversionMode, Reading, DEVICE_ID,
10};
11use crate::driver::{read_word, write_word};
12use crate::error::{DriverError, SensorError};
13
14pub const STATUS_POLLS: u8 = 150;
21
22#[derive(Debug)]
55pub struct Tmp117<I2C, D> {
56 bus: I2C,
57 address: u8,
58 delay: D,
59 averaging: Averaging,
60 revision: Option<u8>,
61 pending: Alerts,
62}
63
64impl<I2C, D> Tmp117<I2C, D> {
65 pub fn new(bus: I2C, address: u8, delay: D) -> Self {
78 Tmp117 {
79 bus,
80 address,
81 delay,
82 averaging: Averaging::X8,
83 revision: None,
84 pending: Alerts::default(),
85 }
86 }
87
88 pub fn with_averaging(mut self, averaging: Averaging) -> Self {
102 self.averaging = averaging;
103 self
104 }
105
106 pub fn revision(&self) -> Option<u8> {
108 self.revision
109 }
110
111 pub fn release(self) -> (I2C, D) {
117 (self.bus, self.delay)
118 }
119}
120
121impl<I2C: I2c, D: DelayNs> Tmp117<I2C, D> {
122 pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
140 let id = self.read(register::DEVICE_ID)?;
141 if device_id(id) != DEVICE_ID {
142 return Err(SensorError::Identity.into());
143 }
144 self.wait(register::EEPROM_UL, eeprom_unlock_busy)?;
145 self.write(
146 register::CONFIGURATION,
147 self.configuration(ConversionMode::Shutdown).bits(),
148 )?;
149 self.revision = Some(revision(id));
150 Ok(())
151 }
152
153 pub fn measure(&mut self) -> Result<Reading, DriverError<I2C::Error>> {
173 if self.revision.is_none() {
174 self.init()?;
175 }
176 self.write(
177 register::CONFIGURATION,
178 self.configuration(ConversionMode::OneShot).bits(),
179 )?;
180 let stale = self.read(register::CONFIGURATION)?;
181 self.pending = self.pending | Alerts::from_bits(stale);
182 self.delay.delay_us(self.averaging.conversion_micros());
183 let status = self.wait(register::CONFIGURATION, |config| !data_ready(config))?;
184 self.pending = self.pending | Alerts::from_bits(status);
185 let word = self.read(register::TEMP_RESULT)?;
186 Ok(Reading::new(word as i16))
187 }
188
189 pub fn set_alert_limits(
208 &mut self,
209 high_celsius: f32,
210 low_celsius: f32,
211 ) -> Result<(), DriverError<I2C::Error>> {
212 self.write(register::THIGH_LIMIT, raw_from_celsius(high_celsius) as u16)?;
213 self.write(register::TLOW_LIMIT, raw_from_celsius(low_celsius) as u16)
214 }
215
216 pub fn alerts(&mut self) -> Result<Alerts, DriverError<I2C::Error>> {
234 let live = Alerts::from_bits(self.read(register::CONFIGURATION)?);
235 Ok(live | core::mem::take(&mut self.pending))
236 }
237
238 fn configuration(&self, mode: ConversionMode) -> Configuration {
239 Configuration {
240 mode,
241 averaging: self.averaging,
242 ..Configuration::default()
243 }
244 }
245
246 fn read(&mut self, register: u8) -> Result<u16, DriverError<I2C::Error>> {
247 read_word(&mut self.bus, self.address, register).map_err(DriverError::Bus)
248 }
249
250 fn write(&mut self, register: u8, value: u16) -> Result<(), DriverError<I2C::Error>> {
251 write_word(&mut self.bus, self.address, register, value).map_err(DriverError::Bus)
252 }
253
254 fn wait(
255 &mut self,
256 register: u8,
257 busy: fn(u16) -> bool,
258 ) -> Result<u16, DriverError<I2C::Error>> {
259 for _ in 0..STATUS_POLLS {
260 let word = self.read(register)?;
261 if !busy(word) {
262 return Ok(word);
263 }
264 self.delay.delay_ms(1);
265 }
266 Err(DriverError::Timeout)
267 }
268}
269
270impl<I2C, D> Sensor for Tmp117<I2C, D>
271where
272 I2C: I2c + Send,
273 I2C::Error: core::fmt::Debug,
274 D: DelayNs + Send,
275{
276 type Reading = Reading;
277
278 async fn read(&mut self) -> pamoja_core::Result<Reading> {
279 self.measure().map_err(pamoja_core::Error::from)
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use crate::tmp117::address::{ADD0_GND, ADD0_SCL};
287 use alloc::vec;
288 use alloc::vec::Vec;
289 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
290 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
291
292 const ID: [u8; 2] = [0x01, 0x17];
294 const EEPROM_READY: [u8; 2] = [0x00, 0x00];
296 const AT_25_C: [u8; 2] = [0x0C, 0x80];
298 const DATA_READY: u16 = 1 << 13;
300 const HIGH_ALERT: u16 = 1 << 15;
301 const LOW_ALERT: u16 = 1 << 14;
302
303 fn config(mode: ConversionMode, averaging: Averaging) -> u16 {
304 Configuration {
305 mode,
306 averaging,
307 ..Configuration::default()
308 }
309 .bits()
310 }
311
312 fn write(address: u8, register: u8, value: u16) -> I2cStep {
313 let [high, low] = value.to_be_bytes();
314 I2cStep::write(address, [register, high, low])
315 }
316
317 fn read(address: u8, register: u8, value: u16) -> I2cStep {
318 I2cStep::write_read(address, [register], value.to_be_bytes())
319 }
320
321 fn init_steps(address: u8, averaging: Averaging) -> Vec<I2cStep> {
322 vec![
323 I2cStep::write_read(address, [register::DEVICE_ID], ID),
324 I2cStep::write_read(address, [register::EEPROM_UL], EEPROM_READY),
325 write(
326 address,
327 register::CONFIGURATION,
328 config(ConversionMode::Shutdown, averaging),
329 ),
330 ]
331 }
332
333 fn measure_steps(
336 address: u8,
337 averaging: Averaging,
338 status: u16,
339 result: [u8; 2],
340 ) -> Vec<I2cStep> {
341 let one_shot = config(ConversionMode::OneShot, averaging);
342 vec![
343 write(address, register::CONFIGURATION, one_shot),
344 read(address, register::CONFIGURATION, one_shot),
345 read(address, register::CONFIGURATION, status),
346 I2cStep::write_read(address, [register::TEMP_RESULT], result),
347 ]
348 }
349
350 fn ready(averaging: Averaging) -> u16 {
351 DATA_READY | config(ConversionMode::Shutdown, averaging)
352 }
353
354 #[test]
355 fn init_follows_the_datasheet_sequence_and_measure_reads_the_result() {
356 let mut steps = init_steps(ADD0_GND, Averaging::X8);
357 steps.extend(measure_steps(
358 ADD0_GND,
359 Averaging::X8,
360 ready(Averaging::X8),
361 AT_25_C,
362 ));
363 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
364 assert_eq!(sensor.revision(), None);
365 sensor.init().unwrap();
366 assert_eq!(sensor.revision(), Some(0));
367
368 let reading = sensor.measure().unwrap();
369 assert_eq!(reading, Reading::new(0x0C80));
370 assert_eq!(reading.celsius(), 25.0);
371
372 let (bus, delay) = sensor.release();
373 assert!(bus.done());
374 assert_eq!(
375 delay.waits_ns(),
376 [125_000 * 1_000],
377 "8 averages convert in 125 ms"
378 );
379 }
380
381 #[test]
382 fn the_averaging_reaches_the_configuration_and_sets_the_wait() {
383 for averaging in [
384 Averaging::None,
385 Averaging::X8,
386 Averaging::X32,
387 Averaging::X64,
388 ] {
389 let mut steps = init_steps(ADD0_SCL, averaging);
390 steps.extend(measure_steps(
391 ADD0_SCL,
392 averaging,
393 ready(averaging),
394 AT_25_C,
395 ));
396 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_SCL, DelayLog::new())
397 .with_averaging(averaging);
398 assert_eq!(sensor.measure().unwrap().celsius(), 25.0);
399 let (bus, delay) = sensor.release();
400 assert!(bus.done());
401 assert_eq!(delay.waits_ns(), [averaging.conversion_micros() * 1_000]);
402 }
403 assert_eq!(config(ConversionMode::OneShot, Averaging::X64), 0x0E60);
405 assert_eq!(Averaging::X64.conversion_micros(), 1_000_000);
406 }
407
408 #[test]
409 fn a_stale_data_ready_flag_is_cleared_before_the_wait_and_the_poll_repeats() {
410 let one_shot = config(ConversionMode::OneShot, Averaging::X8);
411 let mut steps = init_steps(ADD0_GND, Averaging::X8);
412 steps.extend([
413 write(ADD0_GND, register::CONFIGURATION, one_shot),
414 read(ADD0_GND, register::CONFIGURATION, DATA_READY | one_shot),
415 read(ADD0_GND, register::CONFIGURATION, one_shot),
416 read(ADD0_GND, register::CONFIGURATION, one_shot),
417 read(ADD0_GND, register::CONFIGURATION, ready(Averaging::X8)),
418 I2cStep::write_read(ADD0_GND, [register::TEMP_RESULT], AT_25_C),
419 ]);
420 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
421 assert_eq!(sensor.measure().unwrap().celsius(), 25.0);
422 let (bus, delay) = sensor.release();
423 assert!(bus.done());
424 assert_eq!(delay.waits_ns(), [125_000_000, 1_000_000, 1_000_000]);
425 }
426
427 #[test]
428 fn a_part_that_is_not_a_tmp117_is_refused_and_the_revision_is_kept() {
429 let steps = [I2cStep::write_read(
430 ADD0_GND,
431 [register::DEVICE_ID],
432 [0x01, 0x16],
433 )];
434 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
435 assert_eq!(
436 sensor.init(),
437 Err(DriverError::Sensor(SensorError::Identity))
438 );
439 assert_eq!(sensor.revision(), None);
440 assert!(sensor.release().0.done());
441
442 let mut steps = init_steps(ADD0_GND, Averaging::X8);
444 steps[0] = I2cStep::write_read(ADD0_GND, [register::DEVICE_ID], [0x21, 0x17]);
445 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
446 sensor.init().unwrap();
447 assert_eq!(sensor.revision(), Some(2));
448 assert!(sensor.release().0.done());
449 }
450
451 #[test]
452 fn a_conversion_that_never_finishes_is_a_timeout() {
453 let one_shot = config(ConversionMode::OneShot, Averaging::X8);
454 let mut steps = init_steps(ADD0_GND, Averaging::X8);
455 steps.push(write(ADD0_GND, register::CONFIGURATION, one_shot));
456 steps.push(read(ADD0_GND, register::CONFIGURATION, one_shot));
457 for _ in 0..STATUS_POLLS {
458 steps.push(read(ADD0_GND, register::CONFIGURATION, one_shot));
459 }
460 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
461 assert_eq!(sensor.measure(), Err(DriverError::Timeout));
462 let (bus, delay) = sensor.release();
463 assert!(bus.done());
464 assert_eq!(delay.waits_ns().len(), 1 + usize::from(STATUS_POLLS));
465 assert_eq!(delay.waits_ns()[0], 125_000_000);
466 assert_eq!(
467 delay.total_micros(),
468 125_000 + 1_000 * u64::from(STATUS_POLLS)
469 );
470 }
471
472 #[test]
473 fn an_eeprom_that_never_finishes_loading_is_a_timeout() {
474 let mut steps = vec![I2cStep::write_read(ADD0_GND, [register::DEVICE_ID], ID)];
476 for _ in 0..STATUS_POLLS {
477 steps.push(read(ADD0_GND, register::EEPROM_UL, 1 << 14));
478 }
479 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
480 assert_eq!(sensor.init(), Err(DriverError::Timeout));
481 assert_eq!(sensor.revision(), None);
482 let (bus, delay) = sensor.release();
483 assert!(bus.done());
484 assert_eq!(delay.total_millis(), u64::from(STATUS_POLLS));
485 }
486
487 #[test]
488 fn a_bus_fault_surfaces_as_a_bus_error_and_maps_to_the_core_io_error() {
489 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
490 let steps = [I2cStep::fault(ADD0_GND, kind)];
491 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
492 let error = sensor.init().unwrap_err();
493 assert!(matches!(error, DriverError::Bus(_)));
494 let core: pamoja_core::Error = error.into();
495 assert!(matches!(core, pamoja_core::Error::Io(_)));
496 assert!(sensor.release().0.done());
497 }
498
499 #[test]
500 fn as_a_sensor_it_reads_and_maps_to_one_channel() {
501 let mut steps = init_steps(ADD0_GND, Averaging::X8);
502 steps.extend(measure_steps(
504 ADD0_GND,
505 Averaging::X8,
506 ready(Averaging::X8),
507 [0xF3, 0x80],
508 ));
509 let sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
510 let mut celsius = sensor.map(|reading| reading.celsius());
511 assert_eq!(block_on(celsius.read()).unwrap(), -25.0);
512 assert!(celsius.into_inner().release().0.done());
513 }
514
515 #[test]
516 fn alert_limits_are_written_in_register_format_and_flags_survive_the_polls() {
517 let shutdown = config(ConversionMode::Shutdown, Averaging::X8);
518 let mut steps = init_steps(ADD0_GND, Averaging::X8);
519 steps.push(write(ADD0_GND, register::THIGH_LIMIT, 0x0F00));
521 steps.push(write(ADD0_GND, register::TLOW_LIMIT, 0xFB00));
522 steps.extend(measure_steps(
525 ADD0_GND,
526 Averaging::X8,
527 HIGH_ALERT | ready(Averaging::X8),
528 [0x10, 0x00],
529 ));
530 steps.push(read(ADD0_GND, register::CONFIGURATION, shutdown));
531 steps.push(read(ADD0_GND, register::CONFIGURATION, shutdown));
532 steps.push(read(
533 ADD0_GND,
534 register::CONFIGURATION,
535 LOW_ALERT | shutdown,
536 ));
537 let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
538 sensor.init().unwrap();
539 sensor.set_alert_limits(30.0, -10.0).unwrap();
540 assert_eq!(sensor.measure().unwrap().celsius(), 32.0);
541 assert_eq!(
542 sensor.alerts().unwrap(),
543 Alerts {
544 high: true,
545 low: false
546 }
547 );
548 assert_eq!(sensor.alerts().unwrap(), Alerts::default());
549 assert_eq!(
550 sensor.alerts().unwrap(),
551 Alerts {
552 high: false,
553 low: true
554 }
555 );
556 assert!(sensor.release().0.done());
557 }
558
559 #[test]
560 fn init_and_one_conversion_make_the_transfers_the_datasheet_gives() {
561 const PART: u8 = ADD0_GND;
562 let bus = I2cScript::new([
563 I2cStep::write_read(PART, [0x0F], [0x01, 0x17]),
564 I2cStep::write_read(PART, [0x04], [0x00, 0x00]),
565 I2cStep::write(PART, [0x01, 0x06, 0x20]),
566 I2cStep::write(PART, [0x01, 0x0E, 0x20]),
567 I2cStep::write_read(PART, [0x01], [0x0E, 0x20]),
568 I2cStep::write_read(PART, [0x01], [0x26, 0x20]),
569 I2cStep::write_read(PART, [0x00], [0x0C, 0x80]),
570 ]);
571
572 let mut sensor = Tmp117::new(bus, PART, DelayLog::new());
573 let reading = block_on(Sensor::read(&mut sensor)).expect("the scripted part answers");
574 assert_eq!(reading.celsius(), 25.0);
575
576 let (bus, delay) = sensor.release();
577 assert!(bus.done());
578 assert_eq!(delay.total_micros(), 125_000);
579 }
580}