use esp_hal::Async;
use esp_hal::analog::adc::{Adc, AdcCalCurve, AdcConfig, AdcPin, Attenuation};
use esp_hal::peripherals::{ADC1, GPIO10};
const DIVIDER_RATIO: u32 = 2;
const SAMPLES: u32 = 16;
type Cal<'d> = AdcCalCurve<ADC1<'d>>;
#[allow(missing_docs)]
pub struct BatteryResources<'d> {
pub adc: ADC1<'d>,
pub pin: GPIO10<'d>,
}
reborrow_group!(BatteryResources { adc, pin });
impl<'d> BatteryResources<'d> {
pub fn init(&mut self) -> Battery<'_> {
let mut config = AdcConfig::new();
let pin = config.enable_pin_with_cal(self.pin.reborrow(), Attenuation::_11dB);
let adc = Adc::new(self.adc.reborrow(), config).into_async();
Battery { adc, pin }
}
}
pub struct Battery<'d> {
adc: Adc<'d, ADC1<'d>, Async>,
pin: AdcPin<GPIO10<'d>, ADC1<'d>, Cal<'d>>,
}
pub const fn estimate_percent(millivolts: u16) -> u8 {
const CURVE: [(u16, u8); 10] = [
(3300, 0),
(3400, 5),
(3500, 10),
(3600, 20),
(3700, 35),
(3800, 50),
(3900, 65),
(4000, 80),
(4100, 90),
(4200, 100),
];
if millivolts <= CURVE[0].0 {
return 0;
}
let mut i = 1;
while i < CURVE.len() {
let (v1, p1) = CURVE[i];
if millivolts <= v1 {
let (v0, p0) = CURVE[i - 1];
let span = (millivolts - v0) as u32 * (p1 - p0) as u32 / (v1 - v0) as u32;
return p0 + span as u8;
}
i += 1;
}
100
}
const _: () = {
assert!(estimate_percent(3000) == 0);
assert!(estimate_percent(3300) == 0);
assert!(estimate_percent(3750) == 42); assert!(estimate_percent(4200) == 100);
assert!(estimate_percent(4400) == 100);
};
impl Battery<'_> {
pub async fn millivolts(&mut self) -> u16 {
let mut sum = 0u32;
for _ in 0..SAMPLES {
sum += u32::from(self.adc.read_oneshot(&mut self.pin).await);
}
(sum / SAMPLES * DIVIDER_RATIO) as u16
}
pub async fn percent(&mut self) -> u8 {
estimate_percent(self.millivolts().await)
}
}