Skip to main content

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}