use std::{ sync::{Arc, Mutex, MutexGuard}, time::Duration, }; use embedded_hal::digital::v2::{OutputPin, PinState}; use embedded_svc::event_bus::{EventBus, Postbox}; use esp_idf_hal::gpio::{Gpio7, Output}; use esp_idf_svc::eventloop::{ Background, EspBackgroundEventLoop, EspEventFetchData, EspEventLoop, EspEventPostData, EspSubscription, EspTypedEventDeserializer, EspTypedEventSerializer, EspTypedEventSource, User, }; use esp_idf_sys::c_types; use log::warn; use serde_json::json; use crate::{ time::Time, voltage_detection::{VoltageDetectionWorker, VOLTAGE_EVENTLOOP}, }; pub static mut CHARGE_STATE_EVENT_LOOP: Option< EspEventLoop>, > = None; #[derive(Debug, Clone, Copy, PartialEq)] pub enum ChargeStatus { Charging, Charged, } #[derive(Debug, Clone, Copy)] pub struct ChargeControllerState { pub status: ChargeStatus, pub pin_state: PinState, pub charge_deadline_at: Duration, } impl ChargeControllerState { pub fn new() -> Self { Self { status: ChargeStatus::Charging, pin_state: PinState::Low, charge_deadline_at: Duration::ZERO, } } pub fn to_json(&self) -> String { let status = match self.status { ChargeStatus::Charging => "Charging", ChargeStatus::Charged => "Charged", }; let pin_state = match self.pin_state { PinState::Low => "Low", PinState::High => "High", }; let now = Time::new().get_time(); let charging_count_down = if now > self.charge_deadline_at { -1i64 } else { (self.charge_deadline_at - now).as_secs() as i64 }; json!({ "status": status, "pin_state": pin_state, "charging_count_down": charging_count_down}).to_string() } } impl EspTypedEventSource for ChargeControllerState { fn source() -> *const c_types::c_char { b"Charge\0".as_ptr() as *const _ } } impl EspTypedEventSerializer for ChargeControllerState { fn serialize( event: &ChargeControllerState, f: impl for<'a> FnOnce(&'a EspEventPostData) -> R, ) -> R { f(&unsafe { EspEventPostData::new(Self::source(), Self::event_id(), event) }) } } impl EspTypedEventDeserializer for ChargeControllerState { fn deserialize( data: &EspEventFetchData, f: &mut impl for<'a> FnMut(&'a ChargeControllerState) -> R, ) -> R { f(unsafe { data.as_payload() }) } } type ChargeCtlPin = Gpio7; pub struct ChargeController { pub state: ChargeControllerState, voltage_subscription: Option>>, pin: Arc>, } impl ChargeController { pub fn new() -> anyhow::Result { let pin = unsafe { Gpio7::::new() } .into_output() .map_err(|err| anyhow::anyhow!("Make Gpio6 Into output Failed. {}", err))?; match EspBackgroundEventLoop::new(&Default::default()) { Ok(eventloop) => unsafe { CHARGE_STATE_EVENT_LOOP = Some(eventloop) }, Err(err) => anyhow::bail!("Init Event Loop failed. {:?}", err), } anyhow::Ok(Self { state: ChargeControllerState::new(), voltage_subscription: None, pin: Arc::new(Mutex::new(pin)), }) } pub fn watch(&mut self) -> anyhow::Result<()> { let mut state = self.state.to_owned(); let pin = self.pin.to_owned(); if let Some(event_loop) = unsafe { VOLTAGE_EVENTLOOP.as_mut() } { let voltage_subscription = event_loop .subscribe(move |obj: &VoltageDetectionWorker| { match state.status { ChargeStatus::Charging => { if obj.battery_voltage < 12600 { state.status = ChargeStatus::Charging; } else { let now = Time::new().get_time(); if state.charge_deadline_at == Duration::ZERO { state.charge_deadline_at = now + Duration::from_secs(600); } else if now > state.charge_deadline_at { state.status = ChargeStatus::Charged; } else { state.status = ChargeStatus::Charging; } } } ChargeStatus::Charged => { if obj.battery_voltage < 10500 { state.status = ChargeStatus::Charging; } else { state.status = ChargeStatus::Charged; } } } match pin.lock() { Ok(pin) => { if let Err(err) = Self::output_ctl(&mut state, pin) { warn!("Put Control Pin State Failed. {}", err); } } Err(_) => todo!(), } if let Some(event_loop) = unsafe { CHARGE_STATE_EVENT_LOOP.as_mut() } { if let Err(err) = event_loop.post(&state, None) { warn!("Post DC Out Status Failed. {}", err); } } else { warn!("CHARGE_STATE_EVENT_LOOP is None"); } }) .map_err(|err| anyhow::anyhow!("Subscribe Voltage Failed. {}", err))?; self.voltage_subscription = Some(voltage_subscription); } else { anyhow::bail!("Voltage Event Loop is None"); } anyhow::Ok(()) } fn output_ctl( state: &mut ChargeControllerState, mut pin: MutexGuard, ) -> anyhow::Result<()> { if ChargeStatus::Charging == state.status { pin.set_high() .map_err(|err| anyhow::anyhow!("Set DC Output Control Pin High Failed. {}", err))?; state.pin_state = PinState::High; } else if ChargeStatus::Charged == state.status { pin.set_low() .map_err(|err| anyhow::anyhow!("Set DC Output Control Pin Low Failed. {}", err))?; state.pin_state = PinState::Low; } return anyhow::Ok(()); } }