#[cfg(feature = "_ble")]
use core::cell::Cell;
#[cfg(feature = "_ble")]
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal::digital::InputPin;
use rmk_macro::{input_device, processor};
#[cfg(feature = "_ble")]
use rmk_types::battery::{BatteryStatus, ChargeState};
#[cfg(feature = "_ble")]
use crate::RawMutex;
#[cfg(feature = "_ble")]
use crate::event::BatteryStatusEvent;
use crate::event::{BatteryAdcEvent, ChargingStateEvent, publish_event};
#[cfg(feature = "_ble")]
pub(crate) static BATTERY_STATUS: Mutex<RawMutex, Cell<BatteryStatus>> =
Mutex::new(Cell::new(BatteryStatus::Unavailable));
#[cfg(feature = "_ble")]
pub(crate) fn current_battery_status() -> BatteryStatus {
BATTERY_STATUS.lock(|c| c.get())
}
#[input_device(publish = ChargingStateEvent)]
pub struct ChargingStateReader<I: InputPin> {
state_input: I,
low_active: bool,
current_charging_state: bool,
first_read: bool,
}
impl<I: InputPin> ChargingStateReader<I> {
pub fn new(state_input: I, low_active: bool) -> Self {
Self {
state_input,
low_active,
current_charging_state: false,
first_read: false,
}
}
async fn read_charging_state_event(&mut self) -> ChargingStateEvent {
if !self.first_read {
embassy_time::Timer::after_secs(2).await;
let charging_state = if self.low_active {
self.state_input.is_low().unwrap_or(false)
} else {
self.state_input.is_high().unwrap_or(false)
};
self.current_charging_state = charging_state;
self.first_read = true;
return ChargingStateEvent {
charging: charging_state,
};
}
loop {
embassy_time::Timer::after_secs(5).await;
let charging_state = if self.low_active {
self.state_input.is_low().unwrap_or(false)
} else {
self.state_input.is_high().unwrap_or(false)
};
if charging_state != self.current_charging_state {
self.current_charging_state = charging_state;
return ChargingStateEvent {
charging: charging_state,
};
}
}
}
}
#[processor(subscribe = [BatteryAdcEvent, ChargingStateEvent])]
pub struct BatteryProcessor {
adc_divider_measured: u32,
adc_divider_total: u32,
battery_status: BatteryStatus,
}
impl BatteryProcessor {
pub fn new(adc_divider_measured: u32, adc_divider_total: u32) -> Self {
BatteryProcessor {
adc_divider_measured,
adc_divider_total,
battery_status: BatteryStatus::Unavailable,
}
}
#[cfg(feature = "_ble")]
fn commit(&mut self, status: BatteryStatus) {
self.battery_status = status;
BATTERY_STATUS.lock(|c| c.set(status));
publish_event(BatteryStatusEvent::from(status));
}
#[cfg(feature = "_ble")]
fn get_battery_percent(&self, val: u16) -> u8 {
let val = val as i32;
let mut measured = self.adc_divider_measured as i32;
let mut total = self.adc_divider_total as i32;
if 500 < val && val < 1000 {
measured = 1;
total = 5;
}
if val > 4755_i32 * measured / total {
100_u8
} else if val < 4055_i32 * measured / total {
0_u8
} else {
((val * total / measured - 4055) / 7) as u8
}
}
}
impl BatteryProcessor {
async fn on_battery_adc_event(&mut self, event: BatteryAdcEvent) {
let val = event.0;
trace!("Detected battery ADC value: {:?}", val);
#[cfg(feature = "_ble")]
match self.battery_status {
BatteryStatus::Available {
charge_state: ChargeState::Charging,
..
} => {}
BatteryStatus::Available { charge_state, level } => {
let battery_percent = self.get_battery_percent(val);
if level != Some(battery_percent) {
self.commit(BatteryStatus::Available {
charge_state,
level: Some(battery_percent),
});
}
}
BatteryStatus::Unavailable => {
let battery_percent = self.get_battery_percent(val);
self.commit(BatteryStatus::Available {
charge_state: ChargeState::Unknown,
level: Some(battery_percent),
});
}
}
}
async fn on_charging_state_event(&mut self, event: ChargingStateEvent) {
let charging = event.charging;
info!("Charging state changed: {:?}", charging);
#[cfg(feature = "_ble")]
{
let status = if charging {
let level = match self.battery_status {
BatteryStatus::Available { level, .. } => level,
BatteryStatus::Unavailable => None,
};
BatteryStatus::Available {
charge_state: ChargeState::Charging,
level,
}
} else {
BatteryStatus::Available {
charge_state: ChargeState::Discharging,
level: None,
}
};
self.commit(status);
}
}
}