tds_meter/
lib.rs

1#![no_std]
2
3use core::marker::PhantomData;
4
5use embedded_hal::{
6    adc::{Channel, OneShot},
7    blocking::delay::DelayMs,
8};
9// for f32::powi
10use num_traits::float::FloatCore;
11
12const NO_SAMPLES: usize = 30;
13const SAMPLE_INTERVAL_MS: u16 = 40;
14
15pub enum Error<AdcError> {
16    ReadError(AdcError),
17}
18
19fn median(mut data: [u16; NO_SAMPLES]) -> u16 {
20    data.sort_unstable();
21    return data[data.len() / 2];
22}
23
24pub struct TdsMeter<OneShotReader, Adc, Word, PinData, Delay>
25where
26    OneShotReader: OneShot<Adc, Word, PinData>,
27    PinData: Channel<Adc>,
28    Delay: DelayMs<u16>,
29{
30    adc: OneShotReader,
31    adc_range: u16,
32    adc_vref: f32,
33    pin_data: PinData,
34    _unused: PhantomData<Adc>,
35    _unused2: PhantomData<Word>,
36    _unused3: PhantomData<Delay>,
37}
38
39impl<OneShotReader, Adc, Word, PinData, Delay> TdsMeter<OneShotReader, Adc, Word, PinData, Delay>
40where
41    Word: Into<u16>,
42    OneShotReader: OneShot<Adc, Word, PinData>,
43    PinData: Channel<Adc>,
44    Delay: DelayMs<u16>,
45{
46    pub fn new(adc: OneShotReader, adc_range: u16, adc_vref: f32, pin_data: PinData) -> Self {
47        Self {
48            adc,
49            adc_range,
50            adc_vref,
51            pin_data,
52            _unused: PhantomData,
53            _unused2: PhantomData,
54            _unused3: PhantomData,
55        }
56    }
57
58    /// Output TDS value in parts per million
59    ///
60    /// Set temperature to the temperature of the water in °C or 25 if unsure.
61    pub fn measure(
62        &mut self,
63        temperature: f32,
64        delay: &mut Delay,
65    ) -> Result<f32, Error<OneShotReader::Error>> {
66        let mut data: [u16; NO_SAMPLES] = [0; NO_SAMPLES];
67        for i in 0..NO_SAMPLES {
68            loop {
69                let read_result = self.adc.read(&mut self.pin_data);
70
71                match read_result {
72                    Ok(word) => {
73                        data[i] = word.into();
74                        break;
75                    }
76                    Err(nb::Error::Other(failed)) => {
77                        return Err(Error::ReadError(failed));
78                    }
79                    Err(nb::Error::WouldBlock) => continue,
80                };
81            }
82
83            delay.delay_ms(SAMPLE_INTERVAL_MS);
84        }
85
86        let average_data = median(data);
87        let voltage = (average_data as f32 / self.adc_range as f32) * self.adc_vref;
88
89        // temperature compensation
90        let compensation_coefficient = 1.0 + 0.02 * (temperature - 25.0);
91        let compensated_voltage = voltage / compensation_coefficient;
92
93        Ok((66.71 * compensated_voltage.powi(3))
94            + (-112.93 * compensated_voltage.powi(2))
95            + (428.695 * compensated_voltage))
96    }
97}