-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbattery.rs
More file actions
110 lines (94 loc) · 3.64 KB
/
battery.rs
File metadata and controls
110 lines (94 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! A driver for reading the battery supply voltage using the node's ADC.
use super::OsResult;
use crate::re_esp;
use esp_idf_svc::{
hal::{
adc::{
oneshot::{
config::{AdcChannelConfig, Calibration},
AdcChannelDriver, AdcDriver,
},
Resolution, ADC1, ADCCH2, ADCU1,
},
gpio::Gpio3,
},
sys::adc_atten_t,
};
use std::rc::Rc;
/// GPIO pin where the output of the voltage divider is connected
type BatteryGpio = Gpio3<'static>;
/// The ADC hardware
type BatteryAdc = ADC1<'static>;
/// The ADC unit
type BatteryAdcUnit = ADCU1;
/// ADC1 channel type for GPIO3
type BatteryChannel = ADCCH2<BatteryAdcUnit>;
/// Alias for the ADC driver
type BatteryAdcDriver = AdcDriver<'static, BatteryAdcUnit>;
/// Alias for the ADC channel driver
type BatteryAdcChannelDriver = AdcChannelDriver<'static, BatteryChannel, Rc<BatteryAdcDriver>>;
/// Input signal attenuation level
/// See the attenuation table [here](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32s3/api-reference/peripherals/adc.html#adc-attenuation).
// With the resistor configuration below, the maximum ADC input
// at 4.2V should be 970mV, so 0 attenuation is almost the correct
// choice. Due to the high resistor values, even if this higher voltage enters
// the ADC, the current should be very limited, i.e. no damage should be done.
const ATTEN: adc_atten_t = 0;
/// Value of the first resistor of the voltage divider
const R1: f32 = 1_000_000.; // 1MOhm
/// Value of the second resistor of the voltage divider
const R2: f32 = 300_000.; // 300kOhm
/// ADC channel configuration
const CONFIG: AdcChannelConfig = AdcChannelConfig {
attenuation: ATTEN, /* refer to the attenuation value above */
calibration: Calibration::Curve, /* ADC auto-calibration type */
resolution: Resolution::Resolution12Bit, /* ADC resolution */
};
/// Critical voltage value that's still higher than the minimum supply voltage for the ESP32
pub const CRITICAL_VOLTAGE: f32 = 3.22;
/// Amount of samples to read when reading ADC value.
pub const SAMPLES: u16 = 16;
/// Battery voltage measurement driver.
pub struct Battery {
/// ADC driver handle
adc: Rc<BatteryAdcDriver>,
/// ADC channel driver handle
ch: BatteryAdcChannelDriver,
}
impl Battery {
/// Initiliaze a new instance of this driver using the given peripheral handles.
pub fn new(adc: BatteryAdc, gpio: BatteryGpio) -> OsResult<Self> {
let adc = Rc::new(re_esp!(BatteryAdcDriver::new(adc), AdcInit)?);
let ch = re_esp!(
BatteryAdcChannelDriver::new(Rc::clone(&adc), gpio, &CONFIG),
AdcInit
)?;
Ok(Self { adc, ch })
}
/// Read the ADC value and calculate the voltage.
pub fn read(&mut self) -> OsResult<f32> {
let raw = self.read_raw_avg()?;
let volts = f32::from(self.raw_to_mv(raw)?) / 1000.;
let result = (volts * (R1 + R2)) / R2;
Ok(result)
}
/// Read the raw ADC value.
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn read_raw_avg(&mut self) -> OsResult<u16> {
let mut avg = 0;
// NOTE: If `SAMPLES` is changed in the future, `u32` or higher
// must be used.
// 16 * 4095 = 65520 => within u16 range, so no need to convert to u32
for _ in 0..SAMPLES {
avg += self.read_raw()?;
}
avg /= SAMPLES;
Ok(avg)
}
fn raw_to_mv(&self, raw: u16) -> OsResult<u16> {
re_esp!(self.adc.raw_to_mv(&self.ch, raw), AdcRead)
}
fn read_raw(&mut self) -> OsResult<u16> {
re_esp!(self.adc.read_raw(&mut self.ch), AdcRead)
}
}