Skip to main content

ds18b20/
lib.rs

1#![no_std]
2
3//! # Test Test
4
5use embedded_hal::blocking::delay::DelayUs;
6use embedded_hal::digital::v2::{InputPin, OutputPin};
7use one_wire_bus::{self, Address, OneWire, OneWireError, OneWireResult};
8
9pub const FAMILY_CODE: u8 = 0x28;
10
11pub mod commands;
12mod resolution;
13
14use one_wire_bus::crc::check_crc8;
15pub use resolution::Resolution;
16
17/// All of the data that can be read from the sensor.
18#[derive(Debug)]
19pub struct SensorData {
20    /// Temperature in degrees Celsius. Defaults to 85 on startup
21    pub temperature: f32,
22
23    /// The current resolution configuration
24    pub resolution: Resolution,
25
26    /// If the last recorded temperature is lower than this, the sensor is put in an alarm state
27    pub alarm_temp_low: i8,
28
29    /// If the last recorded temperature is higher than this, the sensor is put in an alarm state
30    pub alarm_temp_high: i8,
31}
32
33pub struct Ds18b20 {
34    address: Address,
35}
36
37impl Ds18b20 {
38    /// Checks that the given address contains the correct family code, reads
39    /// configuration data, then returns a device
40    pub fn new<E>(address: Address) -> OneWireResult<Ds18b20, E> {
41        if address.family_code() == FAMILY_CODE {
42            Ok(Ds18b20 { address })
43        } else {
44            Err(OneWireError::FamilyCodeMismatch)
45        }
46    }
47
48    /// Returns the device address
49    pub fn address(&self) -> &Address {
50        &self.address
51    }
52
53    /// Starts a temperature measurement for just this device
54    /// You should wait for the measurement to finish before reading the measurement.
55    /// The amount of time you need to wait depends on the current resolution configuration
56    pub fn start_temp_measurement<T, E>(
57        &self,
58        onewire: &mut OneWire<T>,
59        delay: &mut impl DelayUs<u16>,
60    ) -> OneWireResult<(), E>
61    where
62        T: InputPin<Error = E>,
63        T: OutputPin<Error = E>,
64    {
65        onewire.send_command(commands::CONVERT_TEMP, Some(&self.address), delay)?;
66        Ok(())
67    }
68
69    pub fn read_data<T, E>(
70        &self,
71        onewire: &mut OneWire<T>,
72        delay: &mut impl DelayUs<u16>,
73    ) -> OneWireResult<SensorData, E>
74    where
75        T: InputPin<Error = E>,
76        T: OutputPin<Error = E>,
77    {
78        let data = read_data(&self.address, onewire, delay)?;
79        Ok(data)
80    }
81
82    pub fn set_config<T, E>(
83        &self,
84        alarm_temp_low: i8,
85        alarm_temp_high: i8,
86        resolution: Resolution,
87        onewire: &mut OneWire<T>,
88        delay: &mut impl DelayUs<u16>,
89    ) -> OneWireResult<(), E>
90    where
91        T: InputPin<Error = E>,
92        T: OutputPin<Error = E>,
93    {
94        onewire.send_command(commands::WRITE_SCRATCHPAD, Some(&self.address), delay)?;
95        onewire.write_byte(alarm_temp_high.to_ne_bytes()[0], delay)?;
96        onewire.write_byte(alarm_temp_low.to_ne_bytes()[0], delay)?;
97        onewire.write_byte(resolution.to_config_register(), delay)?;
98        Ok(())
99    }
100
101    pub fn save_to_eeprom<T, E>(
102        &self,
103        onewire: &mut OneWire<T>,
104        delay: &mut impl DelayUs<u16>,
105    ) -> OneWireResult<(), E>
106    where
107        T: InputPin<Error = E>,
108        T: OutputPin<Error = E>,
109    {
110        save_to_eeprom(Some(&self.address), onewire, delay)
111    }
112
113    pub fn recall_from_eeprom<T, E>(
114        &self,
115        onewire: &mut OneWire<T>,
116        delay: &mut impl DelayUs<u16>,
117    ) -> OneWireResult<(), E>
118    where
119        T: InputPin<Error = E>,
120        T: OutputPin<Error = E>,
121    {
122        recall_from_eeprom(Some(&self.address), onewire, delay)
123    }
124}
125
126/// Starts a temperature measurement for all devices on this one-wire bus, simultaneously
127pub fn start_simultaneous_temp_measurement<T, E>(
128    onewire: &mut OneWire<T>,
129    delay: &mut impl DelayUs<u16>,
130) -> OneWireResult<(), E>
131where
132    T: InputPin<Error = E>,
133    T: OutputPin<Error = E>,
134{
135    onewire.reset(delay)?;
136    onewire.skip_address(delay)?;
137    onewire.write_byte(commands::CONVERT_TEMP, delay)?;
138    Ok(())
139}
140
141/// Read the contents of the EEPROM config to the scratchpad for all devices simultaneously.
142pub fn simultaneous_recall_from_eeprom<T, E>(
143    onewire: &mut OneWire<T>,
144    delay: &mut impl DelayUs<u16>,
145) -> OneWireResult<(), E>
146where
147    T: InputPin<Error = E>,
148    T: OutputPin<Error = E>,
149{
150    recall_from_eeprom(None, onewire, delay)
151}
152
153/// Read the config contents of the scratchpad memory to the EEPROMfor all devices simultaneously.
154pub fn simultaneous_save_to_eeprom<T, E>(
155    onewire: &mut OneWire<T>,
156    delay: &mut impl DelayUs<u16>,
157) -> OneWireResult<(), E>
158where
159    T: InputPin<Error = E>,
160    T: OutputPin<Error = E>,
161{
162    save_to_eeprom(None, onewire, delay)
163}
164
165pub fn read_scratchpad<T, E>(
166    address: &Address,
167    onewire: &mut OneWire<T>,
168    delay: &mut impl DelayUs<u16>,
169) -> OneWireResult<[u8; 9], E>
170where
171    T: InputPin<Error = E>,
172    T: OutputPin<Error = E>,
173{
174    onewire.reset(delay)?;
175    onewire.match_address(address, delay)?;
176    onewire.write_byte(commands::READ_SCRATCHPAD, delay)?;
177    let mut scratchpad = [0; 9];
178    onewire.read_bytes(&mut scratchpad, delay)?;
179    check_crc8(&scratchpad)?;
180    Ok(scratchpad)
181}
182
183fn read_data<T, E>(
184    address: &Address,
185    onewire: &mut OneWire<T>,
186    delay: &mut impl DelayUs<u16>,
187) -> OneWireResult<SensorData, E>
188where
189    T: InputPin<Error = E>,
190    T: OutputPin<Error = E>,
191{
192    let scratchpad = read_scratchpad(address, onewire, delay)?;
193
194    let resolution = if let Some(resolution) = Resolution::from_config_register(scratchpad[4]) {
195        resolution
196    } else {
197        return Err(OneWireError::CrcMismatch);
198    };
199    let raw_temp = u16::from_le_bytes([scratchpad[0], scratchpad[1]]);
200    let temperature = match resolution {
201        Resolution::Bits12 => (raw_temp as f32) / 16.0,
202        Resolution::Bits11 => (raw_temp as f32) / 8.0,
203        Resolution::Bits10 => (raw_temp as f32) / 4.0,
204        Resolution::Bits9 => (raw_temp as f32) / 2.0,
205    };
206    Ok(SensorData {
207        temperature,
208        resolution,
209        alarm_temp_high: i8::from_le_bytes([scratchpad[2]]),
210        alarm_temp_low: i8::from_le_bytes([scratchpad[3]]),
211    })
212}
213
214fn recall_from_eeprom<T, E>(
215    address: Option<&Address>,
216    onewire: &mut OneWire<T>,
217    delay: &mut impl DelayUs<u16>,
218) -> OneWireResult<(), E>
219where
220    T: InputPin<Error = E>,
221    T: OutputPin<Error = E>,
222{
223    onewire.send_command(commands::RECALL_EEPROM, address, delay)?;
224
225    // wait for the recall to finish (up to 10ms)
226    let max_retries = (10000 / one_wire_bus::READ_SLOT_DURATION_MICROS) + 1;
227    for _ in 0..max_retries {
228        if onewire.read_bit(delay)? == true {
229            return Ok(());
230        }
231    }
232    Err(OneWireError::Timeout)
233}
234
235fn save_to_eeprom<T, E>(
236    address: Option<&Address>,
237    onewire: &mut OneWire<T>,
238    delay: &mut impl DelayUs<u16>,
239) -> OneWireResult<(), E>
240where
241    T: InputPin<Error = E>,
242    T: OutputPin<Error = E>,
243{
244    onewire.send_command(commands::COPY_SCRATCHPAD, address, delay)?;
245    delay.delay_us(10000); // delay 10ms for the write to complete
246    Ok(())
247}