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