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
//! The battery voltage, and the charge it suggests.
use core::fmt;
use esp_hal::{
Blocking,
analog::adc::{Adc, AdcCalCurve, AdcConfig, AdcPin, Attenuation},
peripherals::{ADC1, GPIO10},
};
use crate::{Millivolts, Percent, convert::battery_percent};
/// The peripherals the battery measurement uses.
#[derive(Debug)]
pub struct BatteryPins {
/// The ADC the battery is read with.
pub adc: ADC1<'static>,
/// The battery, through the board's 2:1 divider.
pub pin: GPIO10<'static>,
}
/// How many readings [`Battery::voltage`] averages.
const BATTERY_SAMPLES: u32 = 16;
/// The battery voltage, measured as M5Stack's M5Unified does for the
/// Cardputer and Cardputer ADV: GPIO10 on ADC1, through a 2:1 divider.
pub struct Battery {
adc: Adc<'static, ADC1<'static>, Blocking>,
pin: AdcPin<GPIO10<'static>, ADC1<'static>, AdcCalCurve<ADC1<'static>>>,
}
impl Battery {
/// The battery voltage, averaged over a few readings.
///
/// The board can't tell whether it's charging: on USB with the power
/// switch on, this is the charging voltage, close to full.
pub fn voltage(&mut self) -> Millivolts {
let sum: u32 = (0..BATTERY_SAMPLES)
.map(|_| u32::from(self.adc.read_blocking(&mut self.pin)))
.sum();
Millivolts(u16::try_from(sum / BATTERY_SAMPLES * 2).unwrap_or(u16::MAX))
}
/// The charge left, estimated from the [`voltage`](Self::voltage)
/// ([`battery_percent`]). Reads high on USB, where it can't be told from
/// charging.
///
/// [`battery_percent`]: crate::battery_percent
pub fn percent(&mut self) -> Percent {
battery_percent(self.voltage())
}
}
/// Sets up the battery measurement: ADC1 with the chip's calibration curve,
/// at 11 dB attenuation (up to about 3.1 V at the pin, 6.2 V at the battery).
#[must_use]
pub fn battery(pins: BatteryPins) -> Battery {
let mut config = AdcConfig::new();
let pin =
config.enable_pin_with_cal::<_, AdcCalCurve<ADC1<'static>>>(pins.pin, Attenuation::_11dB);
Battery {
adc: Adc::new(pins.adc, config),
pin,
}
}
impl fmt::Debug for Battery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Battery").finish_non_exhaustive()
}
}