hx711/
lib.rs

1//! A platform agnostic driver to interface with the HX711 (load cell amplifier and ADC)
2//!
3//! This driver was built using [`embedded-hal`] traits.
4//!
5//! [`embedded-hal`]: https://docs.rs/embedded-hal/0.2
6
7#![deny(missing_docs)]
8#![no_std]
9#![cfg_attr(feature = "never_type", feature(never_type))]
10#![cfg_attr(all(feature = "never_type", test), feature(unwrap_infallible))]
11
12extern crate embedded_hal as hal;
13
14extern crate nb;
15
16use hal::blocking::delay::DelayUs;
17use hal::digital::v2::InputPin;
18use hal::digital::v2::OutputPin;
19
20#[cfg(feature = "never_type")]
21use core::convert::Infallible;
22
23/// Maximum ADC value
24pub const MAX_VALUE: i32 = (1 << 23) - 1;
25
26/// Minimum ADC value
27pub const MIN_VALUE: i32 = 1 << 23;
28
29/// When PD_SCK pin changes from low to high and stays at high for logner than 60 us, HX711 enters power down mode. When PD_SCK returns low, the chip will reset and enter normal operation mode.
30#[allow(dead_code)]
31const TIME_TO_SLEEP: u32 = 70;
32
33/// DOUT falling edge to PD_SCK rising edge (T1)
34const TIME_BEFORE_READOUT: u32 = 1;
35
36/// PD_SCK high time (T3)
37const TIME_SCK_HIGH: u32 = 1;
38
39/// PD_SCK low time (T4)
40const TIME_SCK_LOW: u32 = 1;
41
42/// HX711 driver
43pub struct Hx711<D, IN, OUT> {
44    delay: D,
45    dout: IN,
46    pd_sck: OUT,
47    mode: Mode,
48}
49
50/// Error type for Input and Output errors on digital pins.
51/// For some HALs, the digital input and output pins can never fail.
52/// If you use the driver with such a crate, you can use `.into_ok()` on all results
53/// instead of `.unwrap()` or `.expect()`.
54#[derive(Debug)]
55pub enum Error<EIN, EOUT> {
56    /// Error while reading a digital pin
57    Input(EIN),
58    /// Error while writing a digital pin
59    Output(EOUT),
60}
61
62/// For some hardware crates, the digital input and output pins can never fail.
63/// This implementation enables the use of `.into_ok()`.
64#[cfg(feature = "never_type")]
65impl Into<!> for Error<!, !> {
66    fn into(self) -> ! {
67        panic!()
68    }
69}
70
71/// For some hardware crates, the digital input and output pins can never fail.
72/// This implementation enables the use of `.into_ok()`.
73#[cfg(feature = "never_type")]
74impl Into<!> for Error<Infallible, Infallible> {
75    fn into(self) -> ! {
76        panic!()
77    }
78}
79
80impl<D, IN, OUT, EIN, EOUT> Hx711<D, IN, OUT>
81where
82    D: DelayUs<u32>,
83    IN: InputPin<Error = EIN>,
84    OUT: OutputPin<Error = EOUT>,
85{
86    /// Creates a new driver from Input and Outut pins
87    pub fn new(delay: D, dout: IN, mut pd_sck: OUT) -> Result<Self, Error<EIN, EOUT>> {
88        pd_sck.set_low().map_err(Error::Output)?;
89        let mut hx711 = Hx711 {
90            delay,
91            dout,
92            pd_sck,
93            mode: Mode::ChAGain128,
94        };
95        hx711.reset()?;
96        Ok(hx711)
97    }
98
99    /// Get the mode (channel and gain).
100    pub fn get_mode(&self) -> Mode {
101        self.mode
102    }
103
104    /// Set the mode (channel and gain).
105    pub fn set_mode(&mut self, mode: Mode) -> nb::Result<(), Error<EIN, EOUT>> {
106        self.mode = mode;
107        self.retrieve().and(Ok(()))
108    }
109    /// Put the chip in power down state.
110    pub fn disable(&mut self) -> Result<(), Error<EIN, EOUT>> {
111        self.pd_sck.set_high().map_err(Error::Output)?;
112        self.delay.delay_us(TIME_TO_SLEEP);
113        Ok(())
114    }
115
116    /// Wake the chip up and set mode.
117    pub fn enable(&mut self) -> Result<(), Error<EIN, EOUT>> {
118        self.pd_sck.set_low().map_err(Error::Output)?;
119        self.delay.delay_us(TIME_SCK_LOW);
120        nb::block! {self.set_mode(self.mode)}
121    }
122
123    /// Reset the chip.
124    pub fn reset(&mut self) -> Result<(), Error<EIN, EOUT>> {
125        self.disable()?;
126        self.enable()
127    }
128
129    /// Retrieve the latest conversion value if available
130    pub fn retrieve(&mut self) -> nb::Result<i32, Error<EIN, EOUT>> {
131        self.pd_sck.set_low().map_err(Error::Output)?;
132        if self.dout.is_high().map_err(Error::Input)? {
133            // Conversion not ready yet
134            return Err(nb::Error::WouldBlock);
135        }
136        self.delay.delay_us(TIME_BEFORE_READOUT);
137
138        let mut count: i32 = 0;
139        for _ in 0..24 {
140            // Read 24 bits
141            count <<= 1;
142            self.pd_sck.set_high().map_err(Error::Output)?;
143            self.delay.delay_us(TIME_SCK_HIGH);
144            self.pd_sck.set_low().map_err(Error::Output)?;
145
146            if self.dout.is_high().map_err(Error::Input)? {
147                count += 1;
148            }
149            self.delay.delay_us(TIME_SCK_LOW);
150        }
151
152        // Continue to set mode for next conversion
153        let n_reads = self.mode as u16;
154        for _ in 0..n_reads {
155            self.pd_sck.set_high().map_err(Error::Output)?;
156            self.delay.delay_us(TIME_SCK_HIGH);
157            self.pd_sck.set_low().map_err(Error::Output)?;
158            self.delay.delay_us(TIME_SCK_LOW);
159        }
160
161        Ok(i24_to_i32(count))
162    }
163}
164
165/// The HX711 can run in three modes:
166#[derive(Copy, Clone)]
167pub enum Mode {
168    /// Chanel A with factor 128 gain
169    ChAGain128 = 1,
170    /// Chanel B with factor 64 gain
171    ChBGain32 = 2,
172    /// Chanel B with factor 32 gain
173    ChBGain64 = 3,
174}
175
176/// Convert 24 bit signed integer to i32
177fn i24_to_i32(x: i32) -> i32 {
178    if x >= 0x800000 {
179        x | !0xFFFFFF
180    } else {
181        x
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use crate::*;
188
189    #[cfg(feature = "never_type")]
190    use core::convert::Infallible;
191
192    #[test]
193    fn convert() {
194        assert_eq!(i24_to_i32(0x000001), 1);
195        assert_eq!(i24_to_i32(0x000002), 2);
196        assert_eq!(i24_to_i32(0xFFFFFF), -1);
197        assert_eq!(i24_to_i32(0xFFFFF3), -13);
198        assert_eq!(i24_to_i32(0xF00000), -1048576);
199        assert_eq!(i24_to_i32(0x800000), -8388608);
200        assert_eq!(i24_to_i32(0x7FFFFF), 8388607);
201    }
202
203    #[test]
204    #[cfg(feature = "never_type")]
205    fn infallible_into_ok() {
206        let this_is_ok: Result<usize, Error<Infallible, Infallible>> = Ok(77);
207        assert_eq!(this_is_ok.into_ok(), 77);
208    }
209
210    #[test]
211    #[cfg(feature = "never_type")]
212    fn never_fail_into_ok() {
213        let this_is_ok: Result<usize, Error<!, !>> = Ok(77);
214        assert_eq!(this_is_ok.into_ok(), 77);
215    }
216}