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