v0.2:重写项目。 #6
| @@ -1,189 +1,100 @@ | ||||
| use std::{ | ||||
|     sync::{ | ||||
|         Arc, Mutex, MutexGuard, | ||||
|     }, | ||||
| }; | ||||
| use std::sync::{Arc, Mutex, MutexGuard}; | ||||
|  | ||||
| use embedded_hal::digital::v2::{OutputPin, PinState}; | ||||
| use embedded_svc::event_bus::{EventBus, Postbox}; | ||||
| use esp_idf_hal::gpio::{Gpio6, Output}; | ||||
| 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::{info, warn, debug}; | ||||
| use log::{warn}; | ||||
| use serde_json::json; | ||||
|  | ||||
| use crate::voltage_detection::{VoltageDetectionWorker, VOLTAGE_EVENTLOOP}; | ||||
|  | ||||
| const WAITING_OFF_SECONDS: u8 = 60; | ||||
|  | ||||
| pub static mut DC_OUT_STATE_EVENT_LOOP: Option< | ||||
| pub static mut CHARGE_STATE_EVENT_LOOP: Option< | ||||
|     EspEventLoop<esp_idf_svc::eventloop::User<Background>>, | ||||
| > = None; | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
| pub enum DcOutStatus { | ||||
|     WaitingOn(u8), | ||||
|     On, | ||||
|     Off, | ||||
|     WaitingOff, | ||||
|     TurningOff(u8), | ||||
| pub enum ChargeStatus { | ||||
|     Charging, | ||||
|     Charged, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| pub struct DcOutControllerState { | ||||
|     pub status: DcOutStatus, | ||||
| pub struct ChargeControllerState { | ||||
|     pub status: ChargeStatus, | ||||
|     pub pin_state: PinState, | ||||
| } | ||||
|  | ||||
| impl DcOutControllerState { | ||||
| impl ChargeControllerState { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             status: DcOutStatus::On, | ||||
|             status: ChargeStatus::Charging, | ||||
|             pin_state: PinState::Low, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn handle_adapter_down(&mut self) { | ||||
|         info!("status: {:?}", self); | ||||
|         match self.status { | ||||
|             DcOutStatus::On => { | ||||
|                 self.status = DcOutStatus::WaitingOff; | ||||
|             } | ||||
|             DcOutStatus::WaitingOn(_) => { | ||||
|                 self.status = DcOutStatus::Off; | ||||
|             } | ||||
|             DcOutStatus::TurningOff(seconds) => { | ||||
|                 let seconds = seconds - 1; | ||||
|                 if seconds > 0 { | ||||
|                     self.status = DcOutStatus::TurningOff(seconds); | ||||
|                 } else { | ||||
|                     self.status = DcOutStatus::Off; | ||||
|                 } | ||||
|             } | ||||
|             _ => {} | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn turn_off(&mut self) { | ||||
|         match self.status { | ||||
|             DcOutStatus::On => { | ||||
|                 self.status = DcOutStatus::TurningOff(WAITING_OFF_SECONDS); | ||||
|             } | ||||
|             DcOutStatus::WaitingOff => { | ||||
|                 self.status = DcOutStatus::TurningOff(WAITING_OFF_SECONDS); | ||||
|             } | ||||
|             DcOutStatus::WaitingOn(_) => { | ||||
|                 self.status = DcOutStatus::Off; | ||||
|             } | ||||
|             DcOutStatus::TurningOff(seconds) => { | ||||
|                 let seconds = seconds - 1; | ||||
|                 if seconds > 0 { | ||||
|                     self.status = DcOutStatus::TurningOff(seconds); | ||||
|                 } else { | ||||
|                     self.status = DcOutStatus::Off; | ||||
|                 } | ||||
|             } | ||||
|             _ => {} | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn turn_on(&mut self) { | ||||
|         match self.status { | ||||
|             DcOutStatus::WaitingOff => { | ||||
|                 self.status = DcOutStatus::On; | ||||
|             } | ||||
|             DcOutStatus::Off => { | ||||
|                 self.status = DcOutStatus::WaitingOn(WAITING_OFF_SECONDS); | ||||
|             } | ||||
|             DcOutStatus::WaitingOn(seconds) => { | ||||
|                 let seconds = seconds - 1; | ||||
|                 if seconds > 0 { | ||||
|                     self.status = DcOutStatus::WaitingOn(seconds); | ||||
|                 } else { | ||||
|                     self.status = DcOutStatus::On; | ||||
|                 } | ||||
|             } | ||||
|             DcOutStatus::TurningOff(seconds) => { | ||||
|                 let seconds = seconds - 1; | ||||
|                 if seconds > 0 { | ||||
|                     self.status = DcOutStatus::TurningOff(seconds); | ||||
|                 } else { | ||||
|                     self.status = DcOutStatus::On; | ||||
|                 } | ||||
|             } | ||||
|             _ => {} | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     pub fn to_json(&self) -> String { | ||||
|         let status = match self.status { | ||||
|             DcOutStatus::WaitingOn(_) => "WaitingOn", | ||||
|             DcOutStatus::On => "On", | ||||
|             DcOutStatus::Off => "Off", | ||||
|             DcOutStatus::WaitingOff => "WaitingOff", | ||||
|             DcOutStatus::TurningOff(_) => "TurningOff", | ||||
|             ChargeStatus::Charging => "Charging", | ||||
|             ChargeStatus::Charged => "Charged", | ||||
|         }; | ||||
|         let pin_state = match self.pin_state { | ||||
|             PinState::Low => "Low", | ||||
|             PinState::High => "High", | ||||
|         }; | ||||
|         let seconds: i16 = match self.status { | ||||
|             DcOutStatus::WaitingOn(seconds) => seconds.into(), | ||||
|             DcOutStatus::TurningOff(seconds) => seconds.into(), | ||||
|             _ => -1, | ||||
|         }; | ||||
|         json!({ "status": status, "pin_state": pin_state, "seconds": seconds }).to_string() | ||||
|         json!({ "status": status, "pin_state": pin_state}).to_string() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl EspTypedEventSource for DcOutControllerState { | ||||
| impl EspTypedEventSource for ChargeControllerState { | ||||
|     fn source() -> *const c_types::c_char { | ||||
|         b"Charge\0".as_ptr() as *const _ | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl EspTypedEventSerializer<DcOutControllerState> for DcOutControllerState { | ||||
| impl EspTypedEventSerializer<ChargeControllerState> for ChargeControllerState { | ||||
|     fn serialize<R>( | ||||
|         event: &DcOutControllerState, | ||||
|         event: &ChargeControllerState, | ||||
|         f: impl for<'a> FnOnce(&'a EspEventPostData) -> R, | ||||
|     ) -> R { | ||||
|         f(&unsafe { EspEventPostData::new(Self::source(), Self::event_id(), event) }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl EspTypedEventDeserializer<DcOutControllerState> for DcOutControllerState { | ||||
| impl EspTypedEventDeserializer<ChargeControllerState> for ChargeControllerState { | ||||
|     fn deserialize<R>( | ||||
|         data: &EspEventFetchData, | ||||
|         f: &mut impl for<'a> FnMut(&'a DcOutControllerState) -> R, | ||||
|         f: &mut impl for<'a> FnMut(&'a ChargeControllerState) -> R, | ||||
|     ) -> R { | ||||
|         f(unsafe { data.as_payload() }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| type DcOutPin = Gpio6<Output>; | ||||
| type ChargeCtlPin = Gpio7<Output>; | ||||
|  | ||||
| pub struct DcOutController { | ||||
|     pub state: DcOutControllerState, | ||||
| pub struct ChargeController { | ||||
|     pub state: ChargeControllerState, | ||||
|     voltage_subscription: Option<EspSubscription<User<Background>>>, | ||||
|     pin: Arc<Mutex<DcOutPin>>, | ||||
|     pin: Arc<Mutex<ChargeCtlPin>>, | ||||
| } | ||||
|  | ||||
| impl DcOutController { | ||||
| impl ChargeController { | ||||
|     pub fn new() -> anyhow::Result<Self> { | ||||
|         let pin = unsafe { Gpio6::<Output>::new() } | ||||
|         let pin = unsafe { Gpio7::<Output>::new() } | ||||
|             .into_output() | ||||
|             .map_err(|err| anyhow::anyhow!("Make Gpio6 Into output Failed. {}", err))?; | ||||
|  | ||||
|         match EspBackgroundEventLoop::new(&Default::default()) { | ||||
|             Ok(eventloop) => unsafe { DC_OUT_STATE_EVENT_LOOP = Some(eventloop) }, | ||||
|             Ok(eventloop) => unsafe { CHARGE_STATE_EVENT_LOOP = Some(eventloop) }, | ||||
|             Err(err) => anyhow::bail!("Init Event Loop failed. {:?}", err), | ||||
|         } | ||||
|  | ||||
|         anyhow::Ok(Self { | ||||
|             state: DcOutControllerState::new(), | ||||
|             state: ChargeControllerState::new(), | ||||
|             voltage_subscription: None, | ||||
|             pin: Arc::new(Mutex::new(pin)), | ||||
|         }) | ||||
| @@ -196,33 +107,39 @@ impl DcOutController { | ||||
|         if let Some(event_loop) = unsafe { VOLTAGE_EVENTLOOP.as_mut() } { | ||||
|             let voltage_subscription = event_loop | ||||
|                 .subscribe(move |obj: &VoltageDetectionWorker| { | ||||
|                     if obj.adapter_voltage < 1000 { | ||||
|                         if obj.battery_voltage < 1000 { | ||||
|                             state.turn_off(); | ||||
|                         } else { | ||||
|                             state.handle_adapter_down(); | ||||
|                     match state.status { | ||||
|                         ChargeStatus::Charging => { | ||||
|                             if obj.battery_voltage < 12600 { | ||||
|                                 state.status = ChargeStatus::Charging; | ||||
|                             } else { | ||||
|                                 state.status = ChargeStatus::Charged; | ||||
|                             } | ||||
|                         } | ||||
|                         ChargeStatus::Charged => { | ||||
|                             if obj.battery_voltage < 10500 { | ||||
|                                 state.status = ChargeStatus::Charging; | ||||
|                             } else { | ||||
|                                 state.status = ChargeStatus::Charged; | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         state.turn_on(); | ||||
|                     } | ||||
|  | ||||
|                     match pin.lock() { | ||||
|                         Ok(pin) => { | ||||
|                             if let Err(err) = Self::output_ctl(state, 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 { DC_OUT_STATE_EVENT_LOOP.as_mut() } { | ||||
|                     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!("DC_OUT_STATE_EVENT_LOOP is None"); | ||||
|                         warn!("CHARGE_STATE_EVENT_LOOP is None"); | ||||
|                     } | ||||
|                     debug!("status: {:?}", state); | ||||
|                 }) | ||||
|                 .map_err(|err| anyhow::anyhow!("Subscribe Voltage Failed. {}", err))?; | ||||
|             self.voltage_subscription = Some(voltage_subscription); | ||||
| @@ -232,14 +149,14 @@ impl DcOutController { | ||||
|         anyhow::Ok(()) | ||||
|     } | ||||
|     fn output_ctl( | ||||
|         mut state: DcOutControllerState, | ||||
|         mut pin: MutexGuard<DcOutPin>, | ||||
|         state: &mut ChargeControllerState, | ||||
|         mut pin: MutexGuard<ChargeCtlPin>, | ||||
|     ) -> anyhow::Result<()> { | ||||
|         if DcOutStatus::Off == state.status && state.pin_state == PinState::Low { | ||||
|         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 DcOutStatus::On == state.status && 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; | ||||
|   | ||||
							
								
								
									
										35
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -15,12 +15,13 @@ mod message_queue; | ||||
| mod time; | ||||
| mod voltage_detection; | ||||
| mod wifi; | ||||
| mod charge_controller; | ||||
|  | ||||
| use crate::{ | ||||
|     beep::{ringtone, Beep}, | ||||
|     dc_out_controller::{DcOutController, DcOutControllerState, DC_OUT_STATE_EVENT_LOOP}, | ||||
|     message_queue::MqDto, | ||||
|     voltage_detection::{VoltageDetectionWorker, VOLTAGE_EVENTLOOP}, | ||||
|     voltage_detection::{VoltageDetectionWorker, VOLTAGE_EVENTLOOP}, charge_controller::{CHARGE_STATE_EVENT_LOOP, ChargeControllerState, ChargeController}, | ||||
| }; | ||||
| use crate::{ | ||||
|     message_queue::MessageQueue, time::Time, voltage_detection::VoltageDetection, wifi::Internet, | ||||
| @@ -56,7 +57,7 @@ fn main() { | ||||
|         blink.play(); | ||||
|     }); | ||||
|  | ||||
|     let mut voltage_detection = VoltageDetection::new(); | ||||
|     let voltage_detection = VoltageDetection::new(); | ||||
|  | ||||
|     voltage_detection.unwrap() | ||||
|         .watching() | ||||
| @@ -77,6 +78,12 @@ fn main() { | ||||
|         .watch() | ||||
|         .expect("Can not watch for dc_out_controller"); | ||||
|  | ||||
|     let mut charge_controller = ChargeController::new().expect("Can not get ChargeController instance"); | ||||
|     charge_controller | ||||
|         .watch() | ||||
|         .expect("Can not watch for charge_controller"); | ||||
|  | ||||
|  | ||||
|     sleep(Duration::from_millis(100)); | ||||
|  | ||||
|     let mut _mq = MessageQueue::new(); | ||||
| @@ -91,6 +98,7 @@ fn main() { | ||||
|  | ||||
|     let _mq_tx_for_voltage = _mq.tx.clone(); | ||||
|     let _mq_tx_for_dc_out_state = _mq.tx.clone(); | ||||
|     let _mq_tx_for_charge_state = _mq.tx.clone(); | ||||
|  | ||||
|     let _voltage_subscription; | ||||
|     if let Some(voltage_event_loop) = unsafe { VOLTAGE_EVENTLOOP.as_mut() } { | ||||
| @@ -151,6 +159,29 @@ fn main() { | ||||
|         panic!("DC_OUT_STATE_EVENT_LOOP is undefined!"); | ||||
|     } | ||||
|  | ||||
|     let _charge_state_subscription; | ||||
|     if let Some(charge_state_event_loop) = unsafe { CHARGE_STATE_EVENT_LOOP.as_mut() } { | ||||
|         _charge_state_subscription = charge_state_event_loop | ||||
|             .subscribe(move |message: &ChargeControllerState| { | ||||
|                 match _mq_tx_for_charge_state.lock() { | ||||
|                     Ok(tx) => { | ||||
|                         let result = tx.send(MqDto { | ||||
|                             topic: "charge_state".to_string(), | ||||
|                             message: message.to_json(), | ||||
|                         }); | ||||
|  | ||||
|                         if let Err(err) = result { | ||||
|                             warn!("send charge_state message failed. {}", err) | ||||
|                         } | ||||
|                     } | ||||
|                     Err(err) => warn!("send charge_state to mq message failed. {}", err), | ||||
|                 } | ||||
|             }) | ||||
|             .expect(" Listening Event Loop Failed"); | ||||
|     } else { | ||||
|         panic!("CHARGE_STATE_EVENT_LOOP is undefined!"); | ||||
|     } | ||||
|  | ||||
|     loop { | ||||
|         sleep(Duration::from_millis(100)); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user