onewire/
ds18b20.rs

1use byteorder::ByteOrder;
2use byteorder::LittleEndian;
3use core::fmt::Debug;
4use hal::blocking::delay::DelayUs;
5
6use crate::Device;
7use crate::Error;
8use crate::OneWire;
9use crate::Sensor;
10
11pub const FAMILY_CODE: u8 = 0x28;
12
13#[repr(u8)]
14pub enum Command {
15    Convert = 0x44,
16    WriteScratchpad = 0x4e,
17    ReadScratchpad = 0xBE,
18    CopyScratchpad = 0x48,
19    RecallE2 = 0xB8,
20    ReadPowerSupply = 0xB4,
21}
22
23#[repr(u8)]
24#[derive(Debug, Copy, Clone)]
25pub enum MeasureResolution {
26    TC8 = 0b0001_1111,
27    TC4 = 0b0011_1111,
28    TC2 = 0b0101_1111,
29    TC = 0b0111_1111,
30}
31
32impl MeasureResolution {
33    pub fn time_ms(&self) -> u16 {
34        match self {
35            &MeasureResolution::TC8 => 94,
36            &MeasureResolution::TC4 => 188,
37            &MeasureResolution::TC2 => 375,
38            &MeasureResolution::TC => 750,
39        }
40    }
41}
42
43pub struct DS18B20 {
44    device: Device,
45    resolution: MeasureResolution,
46}
47
48impl DS18B20 {
49    pub fn new<E: Debug>(device: Device) -> Result<DS18B20, Error<E>> {
50        if device.address[0] != FAMILY_CODE {
51            Err(Error::FamilyCodeMismatch(FAMILY_CODE, device.address[0]))
52        } else {
53            Ok(DS18B20 {
54                device,
55                resolution: MeasureResolution::TC,
56            })
57        }
58    }
59
60    pub unsafe fn new_forced(device: Device) -> DS18B20 {
61        DS18B20 {
62            device,
63            resolution: MeasureResolution::TC,
64        }
65    }
66
67    pub fn measure_temperature<E: Debug>(
68        &self,
69        wire: &mut OneWire<E>,
70        delay: &mut dyn DelayUs<u16>,
71    ) -> Result<MeasureResolution, Error<E>> {
72        wire.reset_select_write_only(delay, &self.device, &[Command::Convert as u8])?;
73        Ok(self.resolution)
74    }
75
76    pub fn read_temperature<E: Debug>(
77        &self,
78        wire: &mut OneWire<E>,
79        delay: &mut dyn DelayUs<u16>,
80    ) -> Result<u16, Error<E>> {
81        let mut scratchpad = [0u8; 9];
82        wire.reset_select_write_read(
83            delay,
84            &self.device,
85            &[Command::ReadScratchpad as u8],
86            &mut scratchpad[..],
87        )?;
88        super::ensure_correct_rcr8(&self.device, &scratchpad[..8], scratchpad[8])?;
89        Ok(DS18B20::read_temperature_from_scratchpad(&scratchpad))
90    }
91
92    fn read_temperature_from_scratchpad(scratchpad: &[u8]) -> u16 {
93        LittleEndian::read_u16(&scratchpad[0..2])
94    }
95}
96
97impl Sensor for DS18B20 {
98    fn family_code() -> u8 {
99        FAMILY_CODE
100    }
101
102    fn start_measurement<E: Debug>(
103        &self,
104        wire: &mut OneWire<E>,
105        delay: &mut dyn DelayUs<u16>,
106    ) -> Result<u16, Error<E>> {
107        Ok(self.measure_temperature(wire, delay)?.time_ms())
108    }
109
110    fn read_measurement<E: Debug>(
111        &self,
112        wire: &mut OneWire<E>,
113        delay: &mut dyn DelayUs<u16>,
114    ) -> Result<f32, Error<E>> {
115        self.read_temperature(wire, delay)
116            .map(|t| t as i16 as f32 / 16_f32)
117    }
118
119    fn read_measurement_raw<E: Debug>(
120        &self,
121        wire: &mut OneWire<E>,
122        delay: &mut dyn DelayUs<u16>,
123    ) -> Result<u16, Error<E>> {
124        self.read_temperature(wire, delay)
125    }
126}
127
128/// Split raw u16 value to two parts: integer and fraction N
129/// Original value may be calculated as: integer + fraction/10000
130pub fn split_temp(temperature: u16) -> (i16, i16) {
131    if temperature < 0x8000 {
132        (temperature as i16 >> 4, (temperature as i16 & 0xF) * 625)
133    } else {
134        let abs = -(temperature as i16);
135        (-(abs >> 4), -625 * (abs & 0xF))
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::split_temp;
142    #[test]
143    fn test_temp_conv() {
144        assert_eq!(split_temp(0x07d0), (125, 0));
145        assert_eq!(split_temp(0x0550), (85, 0));
146        assert_eq!(split_temp(0x0191), (25, 625)); // 25.0625
147        assert_eq!(split_temp(0x00A2), (10, 1250)); // 10.125
148        assert_eq!(split_temp(0x0008), (0, 5000)); // 0.5
149        assert_eq!(split_temp(0x0000), (0, 0)); // 0
150        assert_eq!(split_temp(0xfff8), (0, -5000)); // -0.5
151        assert_eq!(split_temp(0xFF5E), (-10, -1250)); // -10.125
152        assert_eq!(split_temp(0xFE6F), (-25, -625)); // -25.0625
153        assert_eq!(split_temp(0xFC90), (-55, 0)); // -55
154    }
155}