From 281ea38d04eb8483ee8c29c199c8a9ce382f398e Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sun, 11 Sep 2022 16:49:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=20ADC=20=E7=AE=A1=E8=84=9A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- src/charge_controller.rs | 249 +++++++++++++++++++++++++++++++++++++++ src/dc_out_controller.rs | 2 +- src/main.rs | 2 +- src/message_queue.rs | 3 +- src/voltage_detection.rs | 113 +++++++++++------- 6 files changed, 324 insertions(+), 51 deletions(-) create mode 100644 src/charge_controller.rs diff --git a/README.md b/README.md index fec0593..71603e9 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ ## GPIO 定义 -- `GPIO 1`:UPS 输入电压检测,使用 `ADC 0`; -- `GPIO 2`:电池电芯电压检测,使用 `ADC 0`; -- `GPIO 3`:UPS 输出电压检测,使用 `ADC 0`; +- `GPIO 1`:UPS 输入电压检测,使用 `ADC 1`; +- `GPIO 2`:电池电芯电压检测,使用 `ADC 1`; +- `GPIO 3`:UPS 输出电压检测,使用 `ADC 1`; - `GPIO 4`:蜂鸣器模拟信号输出,使用 `CHANNEL 0`, `TIMER 0`; - `GPIO 5`:工作状态指示灯信号输出; - `GPIO 6`:UPS 输出控制信号,适用于 P-MOS 开关; \ No newline at end of file diff --git a/src/charge_controller.rs b/src/charge_controller.rs new file mode 100644 index 0000000..3c4c222 --- /dev/null +++ b/src/charge_controller.rs @@ -0,0 +1,249 @@ +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_svc::eventloop::{ + Background, EspBackgroundEventLoop, EspEventFetchData, EspEventLoop, EspEventPostData, + EspSubscription, EspTypedEventDeserializer, EspTypedEventSerializer, EspTypedEventSource, User, +}; +use esp_idf_sys::c_types; +use log::{info, warn, debug}; +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< + EspEventLoop>, +> = None; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum DcOutStatus { + WaitingOn(u8), + On, + Off, + WaitingOff, + TurningOff(u8), +} + +#[derive(Debug, Clone, Copy)] +pub struct DcOutControllerState { + pub status: DcOutStatus, + pub pin_state: PinState, +} + +impl DcOutControllerState { + pub fn new() -> Self { + Self { + status: DcOutStatus::On, + 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", + }; + 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() + } +} + +impl EspTypedEventSource for DcOutControllerState { + fn source() -> *const c_types::c_char { + b"Charge\0".as_ptr() as *const _ + } +} + +impl EspTypedEventSerializer for DcOutControllerState { + fn serialize( + event: &DcOutControllerState, + f: impl for<'a> FnOnce(&'a EspEventPostData) -> R, + ) -> R { + f(&unsafe { EspEventPostData::new(Self::source(), Self::event_id(), event) }) + } +} + +impl EspTypedEventDeserializer for DcOutControllerState { + fn deserialize( + data: &EspEventFetchData, + f: &mut impl for<'a> FnMut(&'a DcOutControllerState) -> R, + ) -> R { + f(unsafe { data.as_payload() }) + } +} + +type DcOutPin = Gpio6; + +pub struct DcOutController { + pub state: DcOutControllerState, + voltage_subscription: Option>>, + pin: Arc>, +} + +impl DcOutController { + pub fn new() -> anyhow::Result { + let pin = unsafe { Gpio6::::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) }, + Err(err) => anyhow::bail!("Init Event Loop failed. {:?}", err), + } + + anyhow::Ok(Self { + state: DcOutControllerState::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| { + if obj.adapter_voltage < 1000 { + if obj.battery_voltage < 1000 { + state.turn_off(); + } else { + state.handle_adapter_down(); + } + } else { + state.turn_on(); + } + + match pin.lock() { + Ok(pin) => { + if let Err(err) = Self::output_ctl(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 Err(err) = event_loop.post(&state, None) { + warn!("Post DC Out Status Failed. {}", err); + } + } else { + warn!("DC_OUT_STATE_EVENT_LOOP is None"); + } + debug!("status: {:?}", state); + }) + .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( + mut state: DcOutControllerState, + mut pin: MutexGuard, + ) -> anyhow::Result<()> { + if DcOutStatus::Off == state.status && state.pin_state == PinState::Low { + 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 { + pin.set_low() + .map_err(|err| anyhow::anyhow!("Set DC Output Control Pin Low Failed. {}", err))?; + state.pin_state = PinState::Low; + } + return anyhow::Ok(()); + } +} diff --git a/src/dc_out_controller.rs b/src/dc_out_controller.rs index 8487490..98e9020 100644 --- a/src/dc_out_controller.rs +++ b/src/dc_out_controller.rs @@ -141,7 +141,7 @@ impl DcOutControllerState { impl EspTypedEventSource for DcOutControllerState { fn source() -> *const c_types::c_char { - b"VOLTAGES\0".as_ptr() as *const _ + b"DcOut\0".as_ptr() as *const _ } } diff --git a/src/main.rs b/src/main.rs index 74c27b4..02c7b81 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,7 @@ fn main() { let mut voltage_detection = VoltageDetection::new(); - voltage_detection + voltage_detection.unwrap() .watching() .expect("Can not watch voltages."); diff --git a/src/message_queue.rs b/src/message_queue.rs index 71f175b..6155db4 100644 --- a/src/message_queue.rs +++ b/src/message_queue.rs @@ -95,11 +95,12 @@ impl MessageQueue { let rx = self.rx.to_owned(); // let (tx, rx) = mpsc::channel::(); spawn(move || loop { - if let Ok(dto) = rx.lock().unwrap().recv_timeout(Duration::from_secs(1)) { + if let Ok(dto) = rx.lock().unwrap().recv_timeout(Duration::from_millis(400)) { if let Err(err) = watcher.publish(dto.topic.as_str(), dto.message.as_bytes()) { warn!("Can not publish message to MQTT. {}", err); } } + thread::sleep(Duration::from_millis(100)) }); anyhow::Ok(()) } diff --git a/src/voltage_detection.rs b/src/voltage_detection.rs index 44501c5..fdcd0ea 100644 --- a/src/voltage_detection.rs +++ b/src/voltage_detection.rs @@ -1,32 +1,32 @@ use std::{ sync::{Arc, Mutex}, + thread::{sleep, spawn}, time::Duration, }; use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot}; -use embedded_svc::{ - event_bus::Postbox, - timer::{PeriodicTimer, TimerService}, -}; +use embedded_svc::{event_bus::Postbox}; use esp_idf_hal::{ adc::{ config::{self}, - Analog, PoweredAdc, ADC1, + Analog, Atten6dB, PoweredAdc, ADC1, }, gpio::{Gpio1, Gpio2, Gpio3, Input}, }; use esp_idf_svc::{ eventloop::{ - EspEventFetchData, EspEventPostData, EspTypedEventDeserializer, EspTypedEventSerializer, - EspTypedEventSource, EspEventLoop, Background, EspBackgroundEventLoop, + Background, EspBackgroundEventLoop, EspEventFetchData, EspEventLoop, EspEventPostData, + EspTypedEventDeserializer, EspTypedEventSerializer, EspTypedEventSource, }, - timer::{EspTimer, EspTimerService}, }; use esp_idf_sys::c_types; -use log::{warn, debug}; +use log::{debug, warn}; use serde::{Deserialize, Serialize}; -pub static mut VOLTAGE_EVENTLOOP: Option>> = None; +use crate::time::Time; + +pub static mut VOLTAGE_EVENTLOOP: Option>> = + None; const ADAPTER_OFFSET: f32 = 12002f32 / 900f32; const BATTERY_OFFSET: f32 = 12002f32 / 900f32; @@ -34,7 +34,6 @@ const OUTPUT_OFFSET: f32 = 12002f32 / 900f32; pub struct VoltageDetection { pub worker: VoltageDetectionWorker, - watch_timer: Option, } #[derive(Clone, Copy, Serialize, Deserialize)] @@ -45,11 +44,10 @@ pub struct VoltageDetectionWorker { } impl VoltageDetection { - pub fn new() -> Self { - return Self { + pub fn new() -> anyhow::Result { + return anyhow::Ok(Self { worker: VoltageDetectionWorker::new(), - watch_timer: None, - }; + }); } pub fn watching(&mut self) -> anyhow::Result<()> { @@ -58,16 +56,41 @@ impl VoltageDetection { Err(err) => anyhow::bail!("Init Event Loop failed. {:?}", err), } let worker = Arc::new(Mutex::new(self.worker)); - let mut timer = EspTimerService::new()?.timer(move || { - match worker.lock().as_mut() { - Ok(worker) => { - if let Err(err) = worker.read_once() { - warn!("Read Failed. {}", err); + + spawn(move || { + let handler = || -> anyhow::Result<()> { + let mut adapter_pin = unsafe { Gpio1::::new() } + .into_analog_atten_6db() + .map_err(|err| { + anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err) + })?; + let mut battery_pin = unsafe { Gpio2::::new() } + .into_analog_atten_6db() + .map_err(|err| { + anyhow::anyhow!("Failed to set GPIO 2 as analog input. {}", err) + })?; + let mut output_pin = unsafe { Gpio3::::new() } + .into_analog_atten_6db() + .map_err(|err| { + anyhow::anyhow!("Failed to set GPIO 3 as analog input. {}", err) + })?; + + let mut worker = worker.lock().map_err(|err| { + anyhow::anyhow!("Lock VoltageDetection Worker Failed. {}", err) + })?; + + let mut last_runing_at; + + loop { + last_runing_at = Time::new().get_time(); + match worker.read_once(&mut adapter_pin, &mut battery_pin, &mut output_pin) { + Ok(_) => debug!( + "Adapter: {},\tBattery: {},\t Output: {}", + worker.adapter_voltage, worker.battery_voltage, worker.output_voltage + ), + Err(err) => warn!("Read Failed. {}", err), } - debug!( - "Adapter: {},\tBattery: {},\t Output: {}", - worker.adapter_voltage, worker.battery_voltage, worker.output_voltage - ); + if let Some(eventloop) = unsafe { VOLTAGE_EVENTLOOP.as_mut() } { if let Err(err) = eventloop.post( &mut VoltageDetectionWorker { @@ -82,15 +105,20 @@ impl VoltageDetection { } else { warn!("EVENTLOOP IS NONE"); } - } - Err(err) => { - warn!("Lock VoltageDetection Worker Failed. {}", err) - } - } - })?; - timer.every(Duration::from_secs(1))?; + let mut delta = Time::new().get_time() - last_runing_at; - self.watch_timer = Some(timer); + if delta >= Duration::from_millis(1000) { + delta = Duration::ZERO + } + + sleep(Duration::from_millis(1000) - delta); + } + }; + + if let Err(err) = handler() { + warn!("init failed. {}", err) + } + }); return anyhow::Ok(()); } } @@ -104,10 +132,12 @@ impl VoltageDetectionWorker { }; } - pub fn read_once(&mut self) -> anyhow::Result<()> { - let adapter_pin = unsafe { Gpio1::::new() } - .into_analog_atten_6db() - .map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?; + pub fn read_once( + &mut self, + adapter_pin: &mut Gpio1>, + battery_pin: &mut Gpio2>, + output_pin: &mut Gpio3>, + ) -> anyhow::Result<()> { match self.read_pin_once(adapter_pin) { Ok(voltage) => { self.adapter_voltage = ((voltage as f32) * ADAPTER_OFFSET) as u16; @@ -117,9 +147,6 @@ impl VoltageDetectionWorker { } } - let battery_pin = unsafe { Gpio2::::new() } - .into_analog_atten_6db() - .map_err(|err| anyhow::anyhow!("Failed to set GPIO 2 as analog input. {}", err))?; match self.read_pin_once(battery_pin) { Ok(voltage) => { self.battery_voltage = ((voltage as f32) * BATTERY_OFFSET) as u16; @@ -128,10 +155,6 @@ impl VoltageDetectionWorker { warn!("Adapter Voltage read failed: {:?}", err); } } - - let output_pin = unsafe { Gpio3::::new() } - .into_analog_atten_6db() - .map_err(|err| anyhow::anyhow!("Failed to set GPIO 3 as analog input. {}", err))?; match self.read_pin_once(output_pin) { Ok(voltage) => { self.output_voltage = ((voltage as f32) * OUTPUT_OFFSET) as u16; @@ -145,10 +168,10 @@ impl VoltageDetectionWorker { } pub fn read_pin_once, PIN: Channel>( &mut self, - mut pin: PIN, + pin: &mut PIN, ) -> anyhow::Result { let mut adc = PoweredAdc::new(unsafe { ADC1::new() }, config::Config::new())?; - let voltage = adc.read(&mut pin); + let voltage = adc.read(pin); match voltage { Ok(voltage) => anyhow::Ok(voltage), Err(err) => {