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}