embedded_onewire/search.rs
1use crate::{
2 OneWire, OneWireStatus, consts::ONEWIRE_CONDITIONAL_SEARCH_CMD, consts::ONEWIRE_SEARCH_CMD,
3 error::OneWireError, utils::OneWireCrc,
4};
5
6/// A structure for searching devices on a 1-Wire bus.
7/// This structure implements the search algorithm for discovering devices on the 1-Wire bus.
8/// It maintains the state of the search.
9pub struct OneWireSearch<'a, T> {
10 onewire: &'a mut T,
11 cmd: u8,
12 last_device: bool,
13 last_discrepancy: u8,
14 last_family_discrepancy: u8,
15 family: u8,
16 rom: [u8; 8],
17}
18
19impl<T> core::fmt::Debug for OneWireSearch<'_, T> {
20 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21 f.debug_struct("OneWireSearch")
22 .field("cmd", &self.cmd)
23 .field("last_device", &self.last_device)
24 .field("last_discrepancy", &self.last_discrepancy)
25 .field("last_family_discrepancy", &self.last_family_discrepancy)
26 .field("family", &self.family)
27 .field("rom", &self.rom)
28 .finish()
29 }
30}
31
32#[repr(u8)]
33/// Type of search performed using [`OneWireSearch`] or [`OneWireSearchAsync`](crate::OneWireSearchAsync).
34pub enum OneWireSearchKind {
35 /// Normal search
36 Normal = ONEWIRE_SEARCH_CMD,
37 /// Search only for devicess with alarm
38 Alarmed = ONEWIRE_CONDITIONAL_SEARCH_CMD,
39}
40
41impl<'a, T> OneWireSearch<'a, T> {
42 /// Creates a new [`OneWireSearch`] instance.
43 ///
44 /// # Arguments
45 /// * `onewire` - A mutable reference to a type that implements the `OneWire` trait.
46 /// * `cmd` - The command to use for the search operation (e.g., `0xf0` for normal search, `0xec` for search in alarm state).
47 pub fn new(onewire: &'a mut T, cmd: OneWireSearchKind) -> Self {
48 Self {
49 onewire,
50 cmd: cmd as _,
51 last_device: false,
52 last_discrepancy: 0,
53 last_family_discrepancy: 0,
54 family: 0, // Initialize family code to 0
55 rom: [0; 8],
56 }
57 }
58
59 /// Creates a new [`OneWireSearch`] instance with a specific family code.
60 /// # Arguments
61 /// * `onewire` - A mutable reference to a type that implements the `OneWire` trait.
62 /// * `cmd` - The command to use for the search operation (e.g., `0xf0` for normal search, `0xec` for search in alarm state).
63 /// * `family` - The family code of the devices to search for.
64 pub fn with_family(onewire: &'a mut T, cmd: OneWireSearchKind, family: u8) -> Self {
65 let rom = [family, 0, 0, 0, 0, 0, 0, 0]; // Initialize the ROM with the family code
66 Self {
67 onewire,
68 cmd: cmd as _,
69 last_device: false,
70 last_discrepancy: 0,
71 last_family_discrepancy: 0,
72 family,
73 rom,
74 }
75 }
76
77 /// Resets the search state.
78 fn reset(&mut self) {
79 self.last_device = false; // Reset the last device flag
80 self.last_discrepancy = 0; // Reset the last discrepancy
81 self.last_family_discrepancy = 0; // Reset the last family discrepancy
82 self.rom = [self.family, 0, 0, 0, 0, 0, 0, 0]; // Reset the ROM array
83 }
84}
85
86impl<T: OneWire> OneWireSearch<'_, T> {
87 /// Searches for devices on the 1-Wire bus.
88 /// This method implements the [1-Wire search algorithm](https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html) to discover devices connected to the bus.
89 /// The [next](OneWireSearch::next) method can be called repeatedly to find all devices on the bus.
90 /// At the end of the search, calling this method will return `None` to indicate that no more devices are present.
91 /// At that point, the search state becomes unusable and should be dropped.
92 /// The search state is reset if the [verify](OneWireSearch::verify) method is called.
93 ///
94 /// # Returns
95 /// A result containing the ROM code of the found device as a `u64` value.
96 ///
97 /// | Bit | Description |
98 /// |-----|-------------|
99 /// | 0-7 | Family code (e.g., 0x28 for DS18B20) |
100 /// | 8-15 | Serial number (first byte) |
101 /// | 16-23 | Serial number (second byte) |
102 /// | 24-31 | Serial number (third byte) |
103 /// | 32-39 | Serial number (fourth byte) |
104 /// | 40-47 | Serial number (fifth byte) |
105 /// | 48-55 | Serial number (sixth byte) |
106 /// | 56-63 | CRC-8 (`0b1_0001_1001` poly) |
107 #[allow(clippy::should_implement_trait)]
108 pub fn next(&mut self) -> Result<Option<u64>, OneWireError<T::BusError>> {
109 if self.onewire.get_overdrive_mode() {
110 return Err(OneWireError::BusInvalidSpeed);
111 }
112 if self.last_device {
113 return Ok(None); // If the last device was found, return None
114 }
115 let status = self.onewire.reset()?;
116 if !status.presence() {
117 return Err(OneWireError::NoDevicePresent);
118 }
119 if status.shortcircuit() {
120 return Err(OneWireError::ShortCircuit);
121 }
122 let mut id_bit_num: u8 = 1;
123 let mut last_zero: u8 = 0;
124 let mut idx: usize = 0; // Index in the ROM array
125 let mut rom_mask: u8 = 1; // Mask for the current bit in the ROM byte
126 // Search ROM command
127 self.onewire.write_byte(self.cmd)?;
128 let res = loop {
129 // Read the id_bit and the complement_bit using triplet if available
130 // and if this is not the first spin of the loop.
131 // If triplet is not implemented, fallback to reading bits, and let
132 // the write flag indicate if we need to write the direction bit later.
133 #[cfg(feature = "triplet-read")]
134 let (id_bit, complement_bit, dir) = { self.onewire.read_triplet()? };
135 #[cfg(not(feature = "triplet-read"))]
136 let (id_bit, complement_bit) = {
137 let id_bit = self.onewire.read_bit()?;
138 let complement_bit = self.onewire.read_bit()?;
139 (id_bit, complement_bit)
140 };
141 if id_bit && complement_bit {
142 // Both bits are 1, which is an error condition, reset the search
143 break false;
144 }
145 let set = if id_bit != complement_bit {
146 // The bits are different, use the id_bit
147 id_bit
148 } else {
149 #[cfg(not(feature = "triplet-read"))]
150 {
151 // Both bits are 0, use the direction from the ROM
152 let idir = if id_bit_num < self.last_discrepancy {
153 self.rom[idx] & rom_mask > 0
154 } else {
155 id_bit_num == self.last_discrepancy
156 };
157 if !idir {
158 last_zero = id_bit_num;
159 if last_zero < 9 {
160 self.last_family_discrepancy = last_zero;
161 }
162 }
163 idir
164 }
165 #[cfg(feature = "triplet-read")]
166 {
167 if !dir {
168 last_zero = id_bit_num;
169 if last_zero < 9 {
170 self.last_family_discrepancy = last_zero;
171 }
172 }
173 dir
174 }
175 };
176 if set {
177 self.rom[idx] |= rom_mask; // Set the bit in the ROM
178 } else {
179 self.rom[idx] &= !rom_mask; // Clear the bit in the ROM
180 }
181 #[cfg(not(feature = "triplet-read"))]
182 self.onewire.write_bit(set)?; // Write the direction bit if triplet is not implemented
183
184 id_bit_num += 1;
185 rom_mask <<= 1; // Move to the next bit in the ROM byte
186
187 if rom_mask == 0 {
188 idx += 1; // Move to the next byte in the ROM
189 rom_mask = 1; // Reset the mask for the next byte
190 }
191 if id_bit_num > 64 {
192 self.last_discrepancy = last_zero;
193 self.last_device = self.last_discrepancy == 0;
194 break true;
195 }
196 };
197
198 if !res || self.rom[0] == 0 {
199 // If no device was found or the first byte is zero, reset the search state
200 return Ok(None);
201 }
202 if !OneWireCrc::validate(&self.rom) {
203 // If the CRC is not valid, reset the search state
204 return Err(OneWireError::InvalidCrc);
205 }
206 if self.family != 0 && self.rom[0] != self.family {
207 // If a specific family code was set and it does not match the found device
208 return Ok(None);
209 }
210 Ok(Some(u64::from_le_bytes(self.rom)))
211 }
212
213 /// Verifies if the device with the given ROM code is present on the 1-Wire bus.
214 ///
215 /// This function should be called with a search state that has been exhausted (i.e., after calling [next](OneWireSearch::next) until it returns `None`).
216 /// This functions resets the search state, and calling [next](OneWireSearch::next) after this call will start a new search.
217 pub fn verify(&mut self, rom: u64) -> Result<bool, OneWireError<T::BusError>> {
218 self.reset(); // Reset the search state
219 self.rom = rom.to_le_bytes(); // Set the ROM to verify
220 self.last_discrepancy = 64; // Set the last discrepancy to 64
221 let res = self.next()?;
222 self.reset(); // Reset the search state after verification
223 Ok(res == Some(rom))
224 }
225}