oxstack_cardputer/
battery.rs1use esp_hal::Async;
9use esp_hal::analog::adc::{Adc, AdcCalCurve, AdcConfig, AdcPin, Attenuation};
10use esp_hal::peripherals::{ADC1, GPIO10};
11
12const DIVIDER_RATIO: u32 = 2;
14const SAMPLES: u32 = 16;
16
17type Cal<'d> = AdcCalCurve<ADC1<'d>>;
18
19#[allow(missing_docs)]
21pub struct BatteryResources<'d> {
22 pub adc: ADC1<'d>,
23 pub pin: GPIO10<'d>,
24}
25reborrow_group!(BatteryResources { adc, pin });
26
27impl<'d> BatteryResources<'d> {
28 pub fn init(&mut self) -> Battery<'_> {
30 let mut config = AdcConfig::new();
31 let pin = config.enable_pin_with_cal(self.pin.reborrow(), Attenuation::_11dB);
33 let adc = Adc::new(self.adc.reborrow(), config).into_async();
34 Battery { adc, pin }
35 }
36}
37
38pub struct Battery<'d> {
40 adc: Adc<'d, ADC1<'d>, Async>,
41 pin: AdcPin<GPIO10<'d>, ADC1<'d>, Cal<'d>>,
42}
43
44pub const fn estimate_percent(millivolts: u16) -> u8 {
50 const CURVE: [(u16, u8); 10] = [
51 (3300, 0),
52 (3400, 5),
53 (3500, 10),
54 (3600, 20),
55 (3700, 35),
56 (3800, 50),
57 (3900, 65),
58 (4000, 80),
59 (4100, 90),
60 (4200, 100),
61 ];
62 if millivolts <= CURVE[0].0 {
63 return 0;
64 }
65 let mut i = 1;
66 while i < CURVE.len() {
67 let (v1, p1) = CURVE[i];
68 if millivolts <= v1 {
69 let (v0, p0) = CURVE[i - 1];
70 let span = (millivolts - v0) as u32 * (p1 - p0) as u32 / (v1 - v0) as u32;
71 return p0 + span as u8;
72 }
73 i += 1;
74 }
75 100
76}
77
78const _: () = {
79 assert!(estimate_percent(3000) == 0);
80 assert!(estimate_percent(3300) == 0);
81 assert!(estimate_percent(3750) == 42); assert!(estimate_percent(4200) == 100);
83 assert!(estimate_percent(4400) == 100);
84};
85
86impl Battery<'_> {
87 pub async fn millivolts(&mut self) -> u16 {
89 let mut sum = 0u32;
90 for _ in 0..SAMPLES {
91 sum += u32::from(self.adc.read_oneshot(&mut self.pin).await);
93 }
94 (sum / SAMPLES * DIVIDER_RATIO) as u16
95 }
96
97 pub async fn percent(&mut self) -> u8 {
99 estimate_percent(self.millivolts().await)
100 }
101}