pamoja_hal/onewire.rs
1//! The 1-Wire bus: one data line, a pull-up resistor, and strict timing.
2//!
3//! A 1-Wire device such as the DS18B20 shares a single open-drain line with the
4//! controller and any number of other devices. The controller drives the line low for
5//! fixed intervals and releases it for the pull-up to raise; the device answers by
6//! holding the line low inside windows the controller samples. Everything on the bus
7//! is built from three primitives, and [`OneWireBus`] names them: a reset pulse that
8//! every device answers with a presence pulse, a write slot that carries one bit, and
9//! a read slot that samples one bit. [`BitBang`] implements them over any pin that can
10//! be driven low and read back, plus a delay, so a microcontroller runs the bus from
11//! one GPIO with no peripheral at all.
12//!
13//! Above the primitives sit the ROM commands every 1-Wire device understands, the
14//! 64-bit [`RomCode`] that identifies each one (an 8-bit family code, a 48-bit serial,
15//! and a CRC-8), and the [`Search`] that enumerates every device on a bus one ROM
16//! code at a time. The slot and reset timings are the ones the DS18B20 datasheet
17//! specifies, and they match the timings the Linux kernel's `w1` bus driver uses for a
18//! bit-banged controller.
19//!
20//! Bit-banging needs microsecond-accurate delays, which a microcontroller has and a
21//! Linux process does not. On a Raspberry Pi use the kernel's own `w1-gpio` driver
22//! and read a thermometer through the sysfs file it exposes; the DS18B20 driver in
23//! `pamoja-sensors` covers both paths.
24
25use core::fmt;
26
27use embedded_hal::delay::DelayNs;
28use embedded_hal::digital::{InputPin, OutputPin};
29
30/// The ROM commands every 1-Wire device answers, issued right after a reset.
31pub mod command {
32 /// Enumerate the devices on the bus one ROM code at a time; see [`super::Search`].
33 pub const SEARCH_ROM: u8 = 0xF0;
34 /// Like `SEARCH_ROM`, but only devices with an alarm condition take part.
35 pub const ALARM_SEARCH: u8 = 0xEC;
36 /// Read the ROM code of the only device on the bus.
37 pub const READ_ROM: u8 = 0x33;
38 /// Address one device by its ROM code; the next function command goes to it alone.
39 pub const MATCH_ROM: u8 = 0x55;
40 /// Address every device at once, for a bus with one device or a broadcast command.
41 pub const SKIP_ROM: u8 = 0xCC;
42 /// Resume addressing the device most recently selected with `MATCH_ROM`.
43 pub const RESUME: u8 = 0xA5;
44}
45
46/// The bus timings in microseconds, from the DS18B20 datasheet's slot definitions.
47pub mod timing {
48 /// The reset pulse: the controller holds the line low at least 480 us.
49 pub const RESET_LOW_US: u32 = 500;
50 /// How long after releasing the reset pulse the presence pulse is sampled. A device
51 /// pulls the line low 15 to 60 us after the release and holds it 60 to 240 us, so
52 /// 70 us falls inside the presence pulse of every device.
53 pub const PRESENCE_SAMPLE_US: u32 = 70;
54 /// The rest of the 480 us receive window after the presence sample.
55 pub const PRESENCE_TAIL_US: u32 = 410;
56 /// Write 1: the line is pulled low briefly, within the 1 to 15 us the slot allows.
57 pub const WRITE_ONE_LOW_US: u32 = 6;
58 /// Write 1: the line stays released for the rest of the slot plus recovery.
59 pub const WRITE_ONE_HIGH_US: u32 = 64;
60 /// Write 0: the line is held low for the whole 60 to 120 us slot.
61 pub const WRITE_ZERO_LOW_US: u32 = 60;
62 /// Write 0: the recovery time before the next slot.
63 pub const WRITE_ZERO_HIGH_US: u32 = 10;
64 /// Read: the controller starts the slot with a short low pulse of at least 1 us.
65 pub const READ_LOW_US: u32 = 2;
66 /// Read: the line is sampled this long after release, inside the 15 us window
67 /// from the start of the slot in which the device's bit is valid.
68 pub const READ_SAMPLE_US: u32 = 10;
69 /// Read: the rest of the 60 us slot plus recovery.
70 pub const READ_TAIL_US: u32 = 58;
71}
72
73/// Computes the Maxim 1-Wire CRC-8 over `data`.
74///
75/// This is the CRC every 1-Wire device appends to its ROM code, and the DS18B20 to its
76/// scratchpad. The polynomial is X^8 + X^5 + X^4 + 1, processed least-significant-bit
77/// first from a zero shift register, which is the reflected form `0x8C`.
78///
79/// # Arguments
80///
81/// * `data` - the bytes the CRC covers, in transmission order.
82///
83/// # Returns
84///
85/// The 8-bit CRC. Over a message followed by its own CRC byte the result is zero.
86///
87/// # Examples
88///
89/// ```
90/// use pamoja_hal::onewire::crc8;
91///
92/// // The published check value of CRC-8/MAXIM over the ASCII digits 1 to 9.
93/// assert_eq!(crc8(b"123456789"), 0xA1);
94/// ```
95pub fn crc8(data: &[u8]) -> u8 {
96 let mut crc = 0u8;
97 for &byte in data {
98 let mut bits = byte;
99 for _ in 0..8 {
100 let mix = (crc ^ bits) & 0x01;
101 crc >>= 1;
102 if mix != 0 {
103 crc ^= 0x8C;
104 }
105 bits >>= 1;
106 }
107 }
108 crc
109}
110
111/// What can go wrong above the pin: the bus protocol itself.
112#[derive(Clone, Copy, Debug, PartialEq, Eq)]
113pub enum OneWireError<E> {
114 /// The pin or delay underneath the bus failed.
115 Pin(E),
116 /// No device answered the reset pulse with a presence pulse.
117 NoDevice,
118 /// A ROM code or a scratchpad arrived with a CRC that does not match its bytes.
119 Crc,
120}
121
122impl<E> From<E> for OneWireError<E> {
123 fn from(error: E) -> Self {
124 OneWireError::Pin(error)
125 }
126}
127
128impl<E: fmt::Debug> fmt::Display for OneWireError<E> {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 OneWireError::Pin(error) => write!(f, "1-wire pin error: {error:?}"),
132 OneWireError::NoDevice => f.write_str("no 1-wire device answered the reset"),
133 OneWireError::Crc => f.write_str("1-wire CRC mismatch"),
134 }
135 }
136}
137
138impl<E: fmt::Debug> core::error::Error for OneWireError<E> {}
139
140/// The 64-bit identity every 1-Wire device carries: family, serial, and CRC.
141///
142/// The bytes are in bus order: the family code first, six serial bytes, then the CRC-8
143/// over the first seven.
144///
145/// # Examples
146///
147/// ```
148/// use pamoja_hal::onewire::RomCode;
149///
150/// // Every DS18B20 carries family code 0x28, and each its own 48-bit serial, the one
151/// // printed by a bus search.
152/// const DS18B20: u8 = 0x28;
153/// let probe = RomCode::new(DS18B20, 0x0000_05E2_FDC3).expect("a 48-bit serial");
154/// assert_eq!(probe.family(), DS18B20);
155/// assert_eq!(probe.serial(), 0x0000_05E2_FDC3);
156///
157/// // The eight bytes it answers a search with, CRC and all, read back to the same probe.
158/// assert_eq!(RomCode::from_bytes(probe.bytes())?, probe);
159/// # Ok::<(), pamoja_hal::onewire::OneWireError<core::convert::Infallible>>(())
160/// ```
161#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
162pub struct RomCode([u8; 8]);
163
164impl RomCode {
165 /// Builds a ROM code from a family code and a 48-bit serial, computing the CRC.
166 ///
167 /// # Arguments
168 ///
169 /// * `family` - the device family code, `0x28` for a DS18B20.
170 /// * `serial` - the 48-bit serial number.
171 ///
172 /// # Returns
173 ///
174 /// The ROM code, or `None` if `serial` does not fit in 48 bits.
175 pub fn new(family: u8, serial: u64) -> Option<RomCode> {
176 if serial >> 48 != 0 {
177 return None;
178 }
179 let mut bytes = [0u8; 8];
180 bytes[0] = family;
181 bytes[1..7].copy_from_slice(&serial.to_le_bytes()[..6]);
182 bytes[7] = crc8(&bytes[..7]);
183 Some(RomCode(bytes))
184 }
185
186 /// Checks the CRC of eight bytes read off the bus and keeps them as a ROM code.
187 ///
188 /// # Arguments
189 ///
190 /// * `bytes` - the eight bytes in bus order.
191 ///
192 /// # Returns
193 ///
194 /// The ROM code.
195 ///
196 /// # Errors
197 ///
198 /// Returns [`OneWireError::Crc`] if the last byte is not the CRC-8 of the first
199 /// seven.
200 pub fn from_bytes<E>(bytes: [u8; 8]) -> Result<RomCode, OneWireError<E>> {
201 if crc8(&bytes[..7]) != bytes[7] {
202 return Err(OneWireError::Crc);
203 }
204 Ok(RomCode(bytes))
205 }
206
207 /// Returns the eight bytes in bus order.
208 pub fn bytes(&self) -> [u8; 8] {
209 self.0
210 }
211
212 /// Returns the family code, the first byte.
213 pub fn family(&self) -> u8 {
214 self.0[0]
215 }
216
217 /// Returns the 48-bit serial number.
218 pub fn serial(&self) -> u64 {
219 let mut serial = [0u8; 8];
220 serial[..6].copy_from_slice(&self.0[1..7]);
221 u64::from_le_bytes(serial)
222 }
223
224 /// Returns the CRC byte.
225 pub fn crc(&self) -> u8 {
226 self.0[7]
227 }
228
229 fn bit(&self, index: u8) -> bool {
230 (self.0[usize::from(index / 8)] >> (index % 8)) & 1 == 1
231 }
232
233 fn set_bit(&mut self, index: u8, value: bool) {
234 let mask = 1 << (index % 8);
235 if value {
236 self.0[usize::from(index / 8)] |= mask;
237 } else {
238 self.0[usize::from(index / 8)] &= !mask;
239 }
240 }
241}
242
243/// A 1-Wire bus: the three signaling primitives, and the byte and ROM operations
244/// built on them.
245///
246/// Implement `reset`, `write_bit`, and `read_bit` for a controller; everything else has
247/// a default built on those. [`BitBang`] is the implementation over a bare pin.
248pub trait OneWireBus {
249 /// The error the controller underneath reports.
250 type Error;
251
252 /// Sends a reset pulse and reports whether any device answered with presence.
253 ///
254 /// # Returns
255 ///
256 /// `true` if at least one device is on the bus.
257 ///
258 /// # Errors
259 ///
260 /// Returns the controller's error if the line cannot be driven or read.
261 fn reset(&mut self) -> Result<bool, Self::Error>;
262
263 /// Sends one bit in a write slot.
264 ///
265 /// # Arguments
266 ///
267 /// * `bit` - the bit to send.
268 ///
269 /// # Errors
270 ///
271 /// Returns the controller's error if the line cannot be driven.
272 fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error>;
273
274 /// Samples one bit in a read slot.
275 ///
276 /// # Returns
277 ///
278 /// The bit the addressed device drove.
279 ///
280 /// # Errors
281 ///
282 /// Returns the controller's error if the line cannot be driven or read.
283 fn read_bit(&mut self) -> Result<bool, Self::Error>;
284
285 /// Sends one byte, least significant bit first, as the bus requires.
286 ///
287 /// # Arguments
288 ///
289 /// * `byte` - the byte to send.
290 ///
291 /// # Errors
292 ///
293 /// Returns the controller's error if the line cannot be driven.
294 fn write_byte(&mut self, byte: u8) -> Result<(), Self::Error> {
295 for shift in 0..8 {
296 self.write_bit((byte >> shift) & 1 == 1)?;
297 }
298 Ok(())
299 }
300
301 /// Reads one byte, least significant bit first.
302 ///
303 /// # Returns
304 ///
305 /// The byte the device sent.
306 ///
307 /// # Errors
308 ///
309 /// Returns the controller's error if the line cannot be driven or read.
310 fn read_byte(&mut self) -> Result<u8, Self::Error> {
311 let mut byte = 0u8;
312 for shift in 0..8 {
313 if self.read_bit()? {
314 byte |= 1 << shift;
315 }
316 }
317 Ok(byte)
318 }
319
320 /// Sends every byte of `bytes` in order.
321 ///
322 /// # Arguments
323 ///
324 /// * `bytes` - the bytes to send.
325 ///
326 /// # Errors
327 ///
328 /// Returns the controller's error if the line cannot be driven.
329 fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
330 for &byte in bytes {
331 self.write_byte(byte)?;
332 }
333 Ok(())
334 }
335
336 /// Fills `buffer` with bytes read in order.
337 ///
338 /// # Arguments
339 ///
340 /// * `buffer` - where the bytes go; its length is how many are read.
341 ///
342 /// # Errors
343 ///
344 /// Returns the controller's error if the line cannot be driven or read.
345 fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> {
346 for slot in buffer.iter_mut() {
347 *slot = self.read_byte()?;
348 }
349 Ok(())
350 }
351
352 /// Resets the bus and addresses every device at once with `SKIP_ROM`.
353 ///
354 /// The next function command reaches every device, which is what a bus with a
355 /// single device, or a broadcast such as a temperature conversion, wants.
356 ///
357 /// # Errors
358 ///
359 /// Returns [`OneWireError::NoDevice`] if nothing answered the reset, or the
360 /// controller's error.
361 fn skip_rom(&mut self) -> Result<(), OneWireError<Self::Error>> {
362 if !self.reset()? {
363 return Err(OneWireError::NoDevice);
364 }
365 self.write_byte(command::SKIP_ROM)?;
366 Ok(())
367 }
368
369 /// Resets the bus and addresses one device by its ROM code with `MATCH_ROM`.
370 ///
371 /// # Arguments
372 ///
373 /// * `rom` - the device to address; the next function command goes to it alone.
374 ///
375 /// # Errors
376 ///
377 /// Returns [`OneWireError::NoDevice`] if nothing answered the reset, or the
378 /// controller's error.
379 fn match_rom(&mut self, rom: &RomCode) -> Result<(), OneWireError<Self::Error>> {
380 if !self.reset()? {
381 return Err(OneWireError::NoDevice);
382 }
383 self.write_byte(command::MATCH_ROM)?;
384 self.write_bytes(&rom.bytes())?;
385 Ok(())
386 }
387
388 /// Resets the bus and reads the ROM code of the only device on it.
389 ///
390 /// With more than one device the answers collide and the CRC fails; use
391 /// [`Search`] instead.
392 ///
393 /// # Returns
394 ///
395 /// The device's ROM code.
396 ///
397 /// # Errors
398 ///
399 /// Returns [`OneWireError::NoDevice`] if nothing answered the reset,
400 /// [`OneWireError::Crc`] if the code read does not check, or the controller's
401 /// error.
402 fn read_rom(&mut self) -> Result<RomCode, OneWireError<Self::Error>> {
403 if !self.reset()? {
404 return Err(OneWireError::NoDevice);
405 }
406 self.write_byte(command::READ_ROM)?;
407 let mut bytes = [0u8; 8];
408 self.read_bytes(&mut bytes)?;
409 RomCode::from_bytes(bytes)
410 }
411}
412
413impl<T: OneWireBus + ?Sized> OneWireBus for &mut T {
414 type Error = T::Error;
415
416 fn reset(&mut self) -> Result<bool, Self::Error> {
417 (**self).reset()
418 }
419
420 fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> {
421 (**self).write_bit(bit)
422 }
423
424 fn read_bit(&mut self) -> Result<bool, Self::Error> {
425 (**self).read_bit()
426 }
427}
428
429/// A 1-Wire controller bit-banged over one open-drain pin and a delay.
430///
431/// The pin must drive the line low when set low and release it when set high, with a
432/// pull-up resistor (4.7 kΩ is the datasheet value) raising the released line, and
433/// it must read back the line's level. On a microcontroller that is an open-drain
434/// output that can be read, or a pin switched between output and input.
435///
436/// # Examples
437///
438/// ```
439/// use pamoja_hal::digital::PinState::{High, Low};
440/// use pamoja_hal::onewire::{BitBang, OneWireBus};
441/// use pamoja_hal::script::{DelayLog, PinScript};
442///
443/// // The scripted pin answers the presence sample low: a device is there.
444/// let pin = PinScript::new([Low]);
445/// let mut bus = BitBang::new(pin, DelayLog::new());
446/// assert!(bus.reset()?);
447///
448/// let (pin, delay) = bus.into_parts();
449/// assert_eq!(pin.driven(), [Low, High]);
450/// assert_eq!(delay.total_micros(), 980);
451/// # Ok::<(), core::convert::Infallible>(())
452/// ```
453#[derive(Debug)]
454pub struct BitBang<P, D> {
455 pin: P,
456 delay: D,
457}
458
459impl<P, D> BitBang<P, D> {
460 /// Creates a controller over `pin`, timed by `delay`.
461 ///
462 /// # Arguments
463 ///
464 /// * `pin` - the open-drain line, driven low to signal and released to listen.
465 /// * `delay` - the microsecond timer that paces the slots.
466 ///
467 /// # Returns
468 ///
469 /// The controller, with the line released.
470 pub fn new(pin: P, delay: D) -> BitBang<P, D> {
471 BitBang { pin, delay }
472 }
473
474 /// Gives back the pin and the delay.
475 ///
476 /// # Returns
477 ///
478 /// The pin and delay the controller was built from.
479 pub fn into_parts(self) -> (P, D) {
480 (self.pin, self.delay)
481 }
482}
483
484impl<P: OutputPin + InputPin, D: DelayNs> OneWireBus for BitBang<P, D> {
485 type Error = P::Error;
486
487 fn reset(&mut self) -> Result<bool, Self::Error> {
488 self.pin.set_low()?;
489 self.delay.delay_us(timing::RESET_LOW_US);
490 self.pin.set_high()?;
491 self.delay.delay_us(timing::PRESENCE_SAMPLE_US);
492 let present = self.pin.is_low()?;
493 self.delay.delay_us(timing::PRESENCE_TAIL_US);
494 Ok(present)
495 }
496
497 fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> {
498 let (low, high) = if bit {
499 (timing::WRITE_ONE_LOW_US, timing::WRITE_ONE_HIGH_US)
500 } else {
501 (timing::WRITE_ZERO_LOW_US, timing::WRITE_ZERO_HIGH_US)
502 };
503 self.pin.set_low()?;
504 self.delay.delay_us(low);
505 self.pin.set_high()?;
506 self.delay.delay_us(high);
507 Ok(())
508 }
509
510 fn read_bit(&mut self) -> Result<bool, Self::Error> {
511 self.pin.set_low()?;
512 self.delay.delay_us(timing::READ_LOW_US);
513 self.pin.set_high()?;
514 self.delay.delay_us(timing::READ_SAMPLE_US);
515 let bit = self.pin.is_high()?;
516 self.delay.delay_us(timing::READ_TAIL_US);
517 Ok(bit)
518 }
519}
520
521/// Enumerates the devices on a bus, one ROM code per call.
522///
523/// This is the binary tree walk the 1-Wire specification defines: after a
524/// `SEARCH_ROM` every device sends each bit of its ROM code and then the complement,
525/// the controller writes back the branch it takes, and the devices whose bit differs
526/// drop out until one remains. Each call to [`next`](Search::next) returns the next
527/// device; the walk remembers where it branched and returns `None` once every device
528/// has been named.
529///
530/// # Examples
531///
532/// ```
533/// use pamoja_hal::onewire::{OneWireBus, RomCode, Search};
534///
535/// fn thermometers<B: OneWireBus>(bus: &mut B) -> Result<Vec<RomCode>, B::Error> {
536/// let mut found = Vec::new();
537/// let mut search = Search::new();
538/// while let Some(rom) = search.next(bus).map_err(|error| match error {
539/// pamoja_hal::onewire::OneWireError::Pin(error) => error,
540/// _ => unreachable!("a bus with no devices or a bad CRC ends the search"),
541/// })? {
542/// if rom.family() == 0x28 {
543/// found.push(rom);
544/// }
545/// }
546/// Ok(found)
547/// }
548/// # let _ = thermometers::<pamoja_hal::onewire::BitBang<pamoja_hal::script::PinScript, pamoja_hal::script::DelayLog>>;
549/// ```
550#[derive(Clone, Copy, Debug)]
551pub struct Search {
552 command: u8,
553 rom: RomCode,
554 last_discrepancy: u8,
555 last_device: bool,
556}
557
558impl Default for Search {
559 fn default() -> Self {
560 Search::new()
561 }
562}
563
564impl Search {
565 /// Starts a search that visits every device.
566 ///
567 /// # Returns
568 ///
569 /// The search, positioned before the first device.
570 pub fn new() -> Search {
571 Search::with_command(command::SEARCH_ROM)
572 }
573
574 /// Starts a search that visits only the devices with an alarm condition.
575 ///
576 /// # Returns
577 ///
578 /// The search, positioned before the first alarming device.
579 pub fn alarms() -> Search {
580 Search::with_command(command::ALARM_SEARCH)
581 }
582
583 fn with_command(command: u8) -> Search {
584 Search {
585 command,
586 rom: RomCode([0; 8]),
587 last_discrepancy: 0,
588 last_device: false,
589 }
590 }
591
592 /// Finds the next device on `bus`.
593 ///
594 /// # Arguments
595 ///
596 /// * `bus` - the bus to search.
597 ///
598 /// # Returns
599 ///
600 /// The next device's ROM code, or `None` when every device has been returned or no
601 /// device answered the reset.
602 ///
603 /// # Errors
604 ///
605 /// Returns [`OneWireError::Crc`] if a ROM code arrived corrupted, which happens
606 /// when a device joins or drops mid-search, or the controller's error.
607 pub fn next<B: OneWireBus>(
608 &mut self,
609 bus: &mut B,
610 ) -> Result<Option<RomCode>, OneWireError<B::Error>> {
611 if self.last_device {
612 return Ok(None);
613 }
614 if !bus.reset()? {
615 self.last_discrepancy = 0;
616 return Ok(None);
617 }
618 bus.write_byte(self.command)?;
619
620 let mut last_zero = 0u8;
621 for index in 0..64u8 {
622 let bit = bus.read_bit()?;
623 let complement = bus.read_bit()?;
624 if bit && complement {
625 self.last_discrepancy = 0;
626 self.last_device = false;
627 return Ok(None);
628 }
629 let position = index + 1;
630 let direction = if bit != complement {
631 bit
632 } else {
633 let taken = if position < self.last_discrepancy {
634 self.rom.bit(index)
635 } else {
636 position == self.last_discrepancy
637 };
638 if !taken {
639 last_zero = position;
640 }
641 taken
642 };
643 self.rom.set_bit(index, direction);
644 bus.write_bit(direction)?;
645 }
646
647 self.last_discrepancy = last_zero;
648 if last_zero == 0 {
649 self.last_device = true;
650 }
651 RomCode::from_bytes(self.rom.bytes()).map(Some)
652 }
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658 use crate::script::{DelayLog, PinScript};
659 use core::convert::Infallible;
660 use embedded_hal::digital::PinState::{High, Low};
661
662 #[test]
663 fn crc8_matches_the_published_check_value_and_zeros_over_its_own_crc() {
664 assert_eq!(crc8(b"123456789"), 0xA1);
665 let rom = RomCode::new(0x28, 0x0000_05E2_FDC3).unwrap();
666 assert_eq!(crc8(&rom.bytes()), 0);
667 }
668
669 #[test]
670 fn a_rom_code_keeps_its_family_serial_and_crc_in_bus_order() {
671 let rom = RomCode::new(0x28, 0x1234_5678_9ABC).unwrap();
672 let bytes = rom.bytes();
673 assert_eq!(bytes[0], 0x28);
674 assert_eq!(&bytes[1..7], &[0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]);
675 assert_eq!(bytes[7], crc8(&bytes[..7]));
676 assert_eq!(rom.serial(), 0x1234_5678_9ABC);
677 assert_eq!(rom.crc(), bytes[7]);
678 let mut corrupted = bytes;
679 corrupted[3] ^= 0x01;
680 assert_eq!(
681 RomCode::from_bytes::<Infallible>(corrupted),
682 Err(OneWireError::Crc)
683 );
684 assert!(RomCode::new(0x28, 1 << 48).is_none());
685 }
686
687 #[test]
688 fn reset_drives_the_datasheet_pulse_and_samples_presence() {
689 let mut bus = BitBang::new(PinScript::new([Low]), DelayLog::new());
690 assert!(bus.reset().unwrap());
691 let (pin, delay) = bus.into_parts();
692 assert_eq!(pin.driven(), [Low, High]);
693 assert_eq!(delay.waits_ns(), [500_000, 70_000, 410_000]);
694
695 let mut empty = BitBang::new(PinScript::new([High]), DelayLog::new());
696 assert!(!empty.reset().unwrap());
697 }
698
699 #[test]
700 fn write_slots_hold_the_line_low_for_the_bit_they_carry() {
701 let mut bus = BitBang::new(PinScript::new([]), DelayLog::new());
702 bus.write_bit(true).unwrap();
703 bus.write_bit(false).unwrap();
704 let (pin, delay) = bus.into_parts();
705 assert_eq!(pin.driven(), [Low, High, Low, High]);
706 assert_eq!(delay.waits_ns(), [6_000, 64_000, 60_000, 10_000]);
707 }
708
709 #[test]
710 fn a_byte_goes_out_least_significant_bit_first() {
711 let mut bus = BitBang::new(PinScript::new([]), DelayLog::new());
712 bus.write_byte(0xCC).unwrap();
713 let (_, delay) = bus.into_parts();
714 let bits: alloc::vec::Vec<bool> = delay
715 .waits_ns()
716 .chunks(2)
717 .map(|slot| slot[0] == 6_000)
718 .collect();
719 assert_eq!(
720 bits,
721 [false, false, true, true, false, false, true, true],
722 "0xCC is 1100 1100, sent from bit 0"
723 );
724 }
725
726 #[test]
727 fn read_slots_sample_inside_the_window_and_assemble_a_byte() {
728 let levels = [High, Low, High, High, Low, Low, High, Low];
729 let mut bus = BitBang::new(PinScript::new(levels), DelayLog::new());
730 assert_eq!(bus.read_byte().unwrap(), 0b0100_1101);
731 let (pin, delay) = bus.into_parts();
732 assert_eq!(pin.driven().len(), 16);
733 assert_eq!(&delay.waits_ns()[..3], &[2_000, 10_000, 58_000]);
734 }
735
736 #[test]
737 fn skip_and_match_rom_fail_when_nothing_answers() {
738 let mut bus = BitBang::new(PinScript::new([High]), DelayLog::new());
739 assert_eq!(bus.skip_rom(), Err(OneWireError::NoDevice));
740 let rom = RomCode::new(0x28, 1).unwrap();
741 let mut bus = BitBang::new(PinScript::new([High]), DelayLog::new());
742 assert_eq!(bus.match_rom(&rom), Err(OneWireError::NoDevice));
743 }
744
745 #[test]
746 fn read_rom_returns_the_single_device_and_checks_its_crc() {
747 let rom = RomCode::new(0x28, 0x0000_05E2_FDC3).unwrap();
748 let mut levels = alloc::vec![Low];
749 for byte in rom.bytes() {
750 for shift in 0..8 {
751 levels.push(if (byte >> shift) & 1 == 1 { High } else { Low });
752 }
753 }
754 let mut bus = BitBang::new(PinScript::new(levels.clone()), DelayLog::new());
755 assert_eq!(bus.read_rom().unwrap(), rom);
756
757 levels[9] = if levels[9] == High { Low } else { High };
758 let mut bus = BitBang::new(PinScript::new(levels), DelayLog::new());
759 assert_eq!(bus.read_rom(), Err(OneWireError::Crc));
760 }
761
762 /// A bus of simulated devices that answer a search the way real ones do: every
763 /// device still in the running drives its bit and its complement, and drops out
764 /// when the controller takes the other branch.
765 struct SimulatedBus {
766 devices: alloc::vec::Vec<RomCode>,
767 active: alloc::vec::Vec<bool>,
768 bit_index: u8,
769 phase: u8,
770 }
771
772 impl SimulatedBus {
773 fn new(devices: &[RomCode]) -> Self {
774 SimulatedBus {
775 devices: devices.to_vec(),
776 active: alloc::vec![true; devices.len()],
777 bit_index: 0,
778 phase: 0,
779 }
780 }
781
782 fn wired_and(&self, complement: bool) -> bool {
783 self.devices
784 .iter()
785 .zip(&self.active)
786 .filter(|(_, active)| **active)
787 .all(|(device, _)| device.bit(self.bit_index) != complement)
788 }
789 }
790
791 impl OneWireBus for SimulatedBus {
792 type Error = Infallible;
793
794 fn reset(&mut self) -> Result<bool, Infallible> {
795 self.active.iter_mut().for_each(|active| *active = true);
796 self.bit_index = 0;
797 self.phase = 0;
798 Ok(!self.devices.is_empty())
799 }
800
801 fn write_bit(&mut self, bit: bool) -> Result<(), Infallible> {
802 if self.phase == 2 {
803 let index = self.bit_index;
804 for (device, active) in self.devices.iter().zip(self.active.iter_mut()) {
805 if device.bit(index) != bit {
806 *active = false;
807 }
808 }
809 self.bit_index += 1;
810 self.phase = 0;
811 }
812 Ok(())
813 }
814
815 fn read_bit(&mut self) -> Result<bool, Infallible> {
816 let bit = match self.phase {
817 0 => self.wired_and(false),
818 _ => self.wired_and(true),
819 };
820 self.phase += 1;
821 Ok(bit)
822 }
823 }
824
825 #[test]
826 fn a_search_names_every_device_once_and_then_stops() {
827 let devices = [
828 RomCode::new(0x28, 0x0000_05E2_FDC3).unwrap(),
829 RomCode::new(0x28, 0x0000_0A11_0042).unwrap(),
830 RomCode::new(0x10, 0x0000_0000_0001).unwrap(),
831 RomCode::new(0x28, 0x0000_05E2_FDC2).unwrap(),
832 ];
833 let mut bus = SimulatedBus::new(&devices);
834 let mut search = Search::new();
835 let mut found = alloc::vec::Vec::new();
836 while let Some(rom) = search.next(&mut bus).unwrap() {
837 found.push(rom);
838 }
839 assert_eq!(found.len(), devices.len());
840 for device in &devices {
841 assert!(found.contains(device), "{device:?} was not found");
842 }
843 assert_eq!(search.next(&mut bus).unwrap(), None);
844 }
845
846 #[test]
847 fn a_search_of_an_empty_bus_finds_nothing() {
848 let mut bus = SimulatedBus::new(&[]);
849 assert_eq!(Search::new().next(&mut bus).unwrap(), None);
850 }
851
852 #[test]
853 fn a_search_of_one_device_returns_it_and_finishes() {
854 let only = RomCode::new(0x28, 7).unwrap();
855 let mut bus = SimulatedBus::new(&[only]);
856 let mut search = Search::new();
857 assert_eq!(search.next(&mut bus).unwrap(), Some(only));
858 assert_eq!(search.next(&mut bus).unwrap(), None);
859 }
860}