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}