onecable/
lib.rs

1#![no_std]
2//! # OneCable
3//! `onecable` is an implementation of the 1-Wire protocol,
4//! and is currently focussed of supporting the `DS18B20` temperature sensor.
5//!
6//! ## How to use?
7//!
8//! First, create a OneWire instance with [`OneWire::new`],
9//! then you can discover the ROM codes of the connected devices with e.g. [`OneWire::search_rom_iter`].
10//! If you have only one device connected to the OneWire bus, you can also use
11//! [`OneWire::read_rom`] to read the ROM code of that device.
12//!
13//! Once you have a valid ROM code, you can convert it to a DS18B20 sensor instance with
14//! [`ds18b20::DS18B20::try_from`].
15
16pub mod crc;
17pub mod ds18b20;
18pub mod rom_code;
19pub mod search_rom;
20pub mod wirelock;
21
22use crc::crc8_maxim;
23use embedded_hal::{
24    self,
25    delay::DelayNs,
26    digital::{InputPin, OutputPin},
27};
28use rom_code::RomCode;
29use search_rom::OneWireRomIter;
30
31/// Represents the OneWire bus.
32///
33/// Further information:
34/// <https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf>
35pub struct OneWire<'a, P: InputPin + OutputPin> {
36    pin: &'a mut P,
37}
38
39impl<'a, P: InputPin + OutputPin> OneWire<'a, P> {
40    /// pin needs to be an open drain pin
41    pub fn new(pin: &'a mut P) -> Self {
42        Self { pin }
43    }
44
45    /// Sends a RESET pulse and listens for a PRESENCE pulse.
46    ///
47    /// Retrurns `Ok(true)`, if a PRESENCE pulse is received, `Ok(false)` if not.
48    ///
49    /// `delay` must be able to accurately handle delays in the range of 1-480 us.
50    pub fn initialize_bus(&mut self, delay: &mut impl DelayNs) -> Result<bool, P::Error> {
51        self.pin.set_high()?;
52        delay.delay_us(2);
53        while self.pin.is_low()? {
54            delay.delay_us(2);
55        }
56
57        self.pin.set_low()?;
58        delay.delay_us(480);
59        self.pin.set_high()?;
60
61        // Devices wait 15-60 us and transmit 60-240 us.
62        // So, in a window from 60 - 75 us after bus release the device signal should be stable.
63        delay.delay_us(67);
64        let is_present = self.pin.is_low()?;
65
66        // overall device transmit window is 480 us wide
67        delay.delay_us(480 - 67);
68        Ok(is_present)
69    }
70
71    /// Writes a single bit to the OneWire bus.
72    ///
73    /// `delay` must be able to accurately handle delays in the range of 5-65 us.
74    pub fn write_bit(&mut self, bit: bool, delay: &mut impl DelayNs) -> Result<(), P::Error> {
75        match bit {
76            true => {
77                self.pin.set_low()?;
78                delay.delay_us(10);
79                self.pin.set_high()?;
80                delay.delay_us(50);
81            }
82            false => {
83                self.pin.set_low()?;
84                delay.delay_us(65);
85                self.pin.set_high()?;
86            }
87        }
88
89        // recovery time
90        delay.delay_us(5);
91
92        Ok(())
93    }
94
95    /// Reads a single bit from the OneWire bus.
96    ///
97    /// `delay` must be able to accurately handle delays in the range of 3-53 us.
98    pub fn read_bit(&mut self, delay: &mut impl DelayNs) -> Result<bool, P::Error> {
99        // request bit
100        self.pin.set_low()?;
101        delay.delay_us(3);
102        self.pin.set_high()?;
103        delay.delay_us(10);
104
105        // 0: device pulls bus low
106        // 1: device leaves bus high
107        let bit = self.pin.is_high()?;
108
109        // 52 us wait for frame end + 1 us recovery time
110        delay.delay_us(53);
111
112        Ok(bit)
113    }
114
115    /// Writes a byte to the OneWire bus,
116    /// least significant bit first.
117    ///
118    /// For requirements on `delay`, see [`OneWire::write_bit`].
119    pub fn write_byte(&mut self, byte: u8, delay: &mut impl DelayNs) -> Result<(), P::Error> {
120        let mut byte = byte;
121        // least significant bit first
122        for _ in 0..8 {
123            let bit = byte & 1 == 1; // extract lowest bit of byte
124            byte >>= 1;
125            self.write_bit(bit, delay)?;
126        }
127        Ok(())
128    }
129
130    /// Reads a byte from the OneWire bus,
131    /// least significant bit first.
132    ///
133    /// For requirements on `delay`, see [`OneWire::read_bit`].
134    pub fn read_byte(&mut self, delay: &mut impl DelayNs) -> Result<u8, P::Error> {
135        let mut byte = 0u8;
136        // least significant bit first,
137        // so push the bits at the front, and shift right
138        for _ in 0..8 {
139            byte >>= 1;
140
141            let bit = self.read_bit(delay)?;
142
143            // set highest bit of byte
144            if bit {
145                byte |= 0b10000000;
146            };
147        }
148        Ok(byte)
149    }
150
151    /// Issues a `Read Rom` command to the OneWire bus.
152    ///
153    /// Only yields valid data, if exactly one other device is connected to the OneWire bus.
154    /// Since all devices connected to the OneWire bus will respond, the data will be corrupt if multiple do so.
155    pub fn read_rom(
156        &mut self,
157        delay: &mut impl DelayNs,
158    ) -> Result<RomCode, OneWireError<P::Error>> {
159        if !self.initialize_bus(delay)? {
160            return Err(OneWireError::NoDevicePresent());
161        }
162
163        let read_rom_command = 0x33u8;
164        self.write_byte(read_rom_command, delay)?;
165
166        // most significant byte first
167        let mut bytes = [0; 8];
168        for byte in &mut bytes {
169            *byte = self.read_byte(delay)?;
170        }
171        let crc = crc8_maxim(&bytes[0..7]);
172        if crc != bytes[7] {
173            Err(OneWireError::CrCMismatch {
174                crc_received: bytes[7],
175                crc_calculated: crc,
176            })
177        } else {
178            Ok(RomCode { bytes })
179        }
180    }
181
182    /// Issues a `Match Rom` command to the OneWire bus.
183    pub fn match_rom(
184        &mut self,
185        rom_code: RomCode,
186        delay: &mut impl DelayNs,
187    ) -> Result<(), OneWireError<P::Error>> {
188        if !self.initialize_bus(delay)? {
189            return Err(OneWireError::NoDevicePresent());
190        }
191
192        let match_rom_command = 0x55u8;
193        self.write_byte(match_rom_command, delay)?;
194
195        // least significant byte first
196        for byte in rom_code.bytes {
197            self.write_byte(byte, delay)?;
198        }
199        Ok(())
200    }
201
202    /// Issues a `Skip Rom` command to the OneWire bus.
203    pub fn skip_rom(&mut self, delay: &mut impl DelayNs) -> Result<(), OneWireError<P::Error>> {
204        if !self.initialize_bus(delay)? {
205            return Err(OneWireError::NoDevicePresent());
206        }
207
208        let match_rom_command = 0xCCu8;
209        self.write_byte(match_rom_command, delay)?;
210        Ok(())
211    }
212
213    /// The iterator performs a `Search Rom` procedure.
214    pub fn search_rom_iter<'b, D: DelayNs>(
215        &'b mut self,
216        delay: &'b mut D,
217    ) -> OneWireRomIter<'a, 'b, P, D, false> {
218        OneWireRomIter::from_wire(self, delay)
219    }
220
221    /// The iterator performs an `Alarm Search` procedure.
222    pub fn alarm_search_iter<'b, D: DelayNs>(
223        &'b mut self,
224        delay: &'b mut D,
225    ) -> OneWireRomIter<'a, 'b, P, D, true> {
226        OneWireRomIter::alarm_from_wire(self, delay)
227    }
228}
229
230#[derive(Debug, thiserror::Error)]
231pub enum FamilyCodeError {
232    #[error("Expected to find family code {expected:X}, but found family code {received:X}.")]
233    MismatchedFamilyCode { expected: u8, received: u8 },
234}
235
236#[derive(Debug, thiserror::Error)]
237pub enum OneWireError<PinErr> {
238    #[error("An error occourred on the OneWire pin.")]
239    PinError(#[from] PinErr),
240    #[error(
241        "CRC={crc_received:X} of the data does not match the calculated CRC={crc_calculated:X}"
242    )]
243    CrCMismatch {
244        crc_received: u8,
245        crc_calculated: u8,
246    },
247    #[error("No device responded to the Reset pulse with a Presence pulse.")]
248    NoDevicePresent(),
249}