feat: support read dc out status.
This commit is contained in:
parent
10fb4601e9
commit
991e9c9867
@ -1,8 +1,8 @@
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use anyhow::{Ok, Result, anyhow};
|
||||
use retry;
|
||||
use std::{
|
||||
sync::{
|
||||
mpsc::{self},
|
||||
mpsc,
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread,
|
||||
@ -10,15 +10,37 @@ use std::{
|
||||
};
|
||||
|
||||
use embedded_hal::digital::blocking::OutputPin;
|
||||
use log::{info, trace, warn};
|
||||
use log::*;
|
||||
|
||||
pub enum DcOutStatus {
|
||||
On,
|
||||
Off,
|
||||
TurningOn(mpsc::Receiver<bool>),
|
||||
TurningOff(mpsc::Receiver<bool>),
|
||||
}
|
||||
|
||||
impl DcOutStatus {
|
||||
pub fn is_on(&self) -> bool {
|
||||
match self {
|
||||
DcOutStatus::On => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_off(&self) -> bool {
|
||||
match self {
|
||||
DcOutStatus::Off => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DcOutController<P>
|
||||
where
|
||||
P: OutputPin + Send,
|
||||
{
|
||||
pub state: bool,
|
||||
pin: Arc<Mutex<P>>,
|
||||
shutdown_tx: Option<mpsc::Sender<bool>>,
|
||||
status: DcOutStatus,
|
||||
}
|
||||
|
||||
impl<P> DcOutController<P>
|
||||
@ -29,15 +51,21 @@ where
|
||||
{
|
||||
pub fn new(pin: P) -> Self {
|
||||
return Self {
|
||||
state: false,
|
||||
status: DcOutStatus::On,
|
||||
pin: Arc::new(Mutex::new(pin)),
|
||||
shutdown_tx: None,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
pub fn open(&mut self) -> Result<()> {
|
||||
if self.status.is_on() {
|
||||
trace!("DC OUT already on, skipping");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pin = self.pin.clone();
|
||||
let mut pin = retry::retry(retry::delay::Fixed::from_millis(100).take(10), || {
|
||||
self.pin.lock()
|
||||
pin.lock()
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
|
||||
|
||||
@ -45,6 +73,9 @@ where
|
||||
pin.set_low()
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
|
||||
|
||||
self.status = DcOutStatus::On;
|
||||
info!("DC OUT ON");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -60,6 +91,9 @@ where
|
||||
|
||||
let pin = Arc::clone(&self.pin);
|
||||
|
||||
let (status_tx, status_rx) = mpsc::channel();
|
||||
self.status = DcOutStatus::TurningOff(status_rx);
|
||||
|
||||
thread::spawn(move || {
|
||||
// Wait for computer shutdown finished
|
||||
let target_at = SystemTime::now() + time::Duration::from_secs(10);
|
||||
@ -74,14 +108,24 @@ where
|
||||
}
|
||||
|
||||
info!("Shutdown timeout, force shutdown...");
|
||||
|
||||
let result = || {
|
||||
retry::retry(retry::delay::Fixed::from_millis(1000).take(10), || {
|
||||
return pin
|
||||
.lock()
|
||||
.map_err(|_| anyhow::anyhow!("Can not lock pin"))?
|
||||
.set_high()
|
||||
.map_err(|_| anyhow::anyhow!("Can not shutdown"));
|
||||
}).expect("Failed to shutdown");
|
||||
}).map_err(|_| anyhow!("Failed to send shutdown status"))?;
|
||||
status_tx.send(true).map_err(|_| anyhow!("Failed to send shutdown status"))?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if let Err(e) = result() {
|
||||
warn!("Failed to shutdown: {}", e);
|
||||
} else {
|
||||
info!("Shutdown finished");
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -101,9 +145,37 @@ where
|
||||
}
|
||||
self.shutdown_tx = None;
|
||||
};
|
||||
|
||||
self.start()?;
|
||||
info!("Power restored");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_status(&mut self) -> DcOutStatus {
|
||||
match &self.status {
|
||||
DcOutStatus::TurningOn(rx) => {
|
||||
rx.try_recv().map_or((), |state| {
|
||||
trace!("DcOutStatus::TurningOn({})", state);
|
||||
if state {
|
||||
self.status = DcOutStatus::On;
|
||||
} else {
|
||||
self.status = DcOutStatus::Off;
|
||||
}
|
||||
})
|
||||
},
|
||||
DcOutStatus::TurningOff(rx) => {
|
||||
rx.try_recv().map_or((), |state| {
|
||||
trace!("DcOutStatus::TurningOff({})", state);
|
||||
if state {
|
||||
self.status = DcOutStatus::Off;
|
||||
} else {
|
||||
self.status = DcOutStatus::On;
|
||||
}
|
||||
})
|
||||
},
|
||||
_default => {}
|
||||
}
|
||||
match &self.status {
|
||||
DcOutStatus::On => DcOutStatus::On,
|
||||
DcOutStatus::Off => DcOutStatus::Off,
|
||||
_ => DcOutStatus::On,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
src/main.rs
11
src/main.rs
@ -1,5 +1,6 @@
|
||||
#![feature(is_some_with)]
|
||||
use esp_idf_sys as _;
|
||||
use log::error;
|
||||
use std::{thread, time::Duration, sync::mpsc, env};
|
||||
|
||||
mod beep;
|
||||
@ -8,7 +9,8 @@ mod dc_out_controller;
|
||||
mod manager;
|
||||
mod screen;
|
||||
fn main() {
|
||||
env::set_var("RUST_LOG", env!("RUST_LOG"));
|
||||
env::set_var("DEFMT_LOG", "trace");
|
||||
env::set_var("RUST_LOG", "trace");
|
||||
env_logger::init();
|
||||
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
|
||||
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
|
||||
@ -69,7 +71,12 @@ fn main() {
|
||||
tx,
|
||||
).expect("Failed to create manager");
|
||||
loop {
|
||||
manager.handling_once().expect("Failed to handle once");
|
||||
match manager.handling_once() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
error!("Exec manager tick task failed: {}", err);
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use embedded_hal::digital::blocking::OutputPin;
|
||||
use embedded_hal_0_2::adc::OneShot;
|
||||
use esp_idf_hal::{
|
||||
adc::{Atten11dB, PoweredAdc, ADC1},
|
||||
gpio::{Gpio1, Gpio2},
|
||||
};
|
||||
use esp_idf_sys::EspError;
|
||||
use log::info;
|
||||
use log::*;
|
||||
use std::{
|
||||
sync::mpsc,
|
||||
thread,
|
||||
@ -43,11 +43,12 @@ where
|
||||
adapter_pin: AdapterGpio,
|
||||
battery_pin: BatteryGpio,
|
||||
tx: mpsc::Sender<ringtone::Type>,
|
||||
) -> Result<Self, EspError> {
|
||||
) -> Result<Self> {
|
||||
let adc = PoweredAdc::new(
|
||||
adc1,
|
||||
esp_idf_hal::adc::config::Config::new().calibration(true),
|
||||
)?;
|
||||
)
|
||||
.map_err(|err| anyhow!("Can not init Adc: {}", err))?;
|
||||
return Ok(Manager {
|
||||
dc_out_controller,
|
||||
screen,
|
||||
@ -59,14 +60,22 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_adapter_voltage(&mut self) -> Result<f32, EspError> {
|
||||
return Ok(self.adc.read(&mut self.adapter_pin).unwrap() as f32);
|
||||
pub fn get_adapter_voltage(&mut self) -> Result<f32> {
|
||||
return Ok(self
|
||||
.adc
|
||||
.read(&mut self.adapter_pin)
|
||||
.map_err(|err| anyhow!("Can not read adapter voltage. {:?}", err))?
|
||||
as f32);
|
||||
}
|
||||
pub fn get_battery_voltage(&mut self) -> Result<f32, EspError> {
|
||||
return Ok(self.adc.read(&mut self.battery_pin).unwrap() as f32);
|
||||
pub fn get_battery_voltage(&mut self) -> Result<f32> {
|
||||
return Ok(self
|
||||
.adc
|
||||
.read(&mut self.battery_pin)
|
||||
.map_err(|err| anyhow!("Can not read battery voltage. {:?}", err))?
|
||||
as f32);
|
||||
}
|
||||
|
||||
pub fn handling_once(&mut self) -> Result<(), EspError> {
|
||||
pub fn handling_once(&mut self) -> Result<()> {
|
||||
let mut adapter = 0.0_f32;
|
||||
let mut battery = 0.0_f32;
|
||||
for _ in 0..10 {
|
||||
@ -78,40 +87,61 @@ where
|
||||
adapter /= 10.0_f32;
|
||||
battery /= 10.0_f32;
|
||||
|
||||
if adapter < 1000.0 {
|
||||
if self.adapter_downed_at.is_none() {
|
||||
info!("Recording adapter downed at: {:?}", SystemTime::now());
|
||||
self.adapter_downed_at = Some(SystemTime::now());
|
||||
}
|
||||
if battery < 500.0 {
|
||||
if is_adapter_down(adapter) {
|
||||
if self.dc_out_controller.get_status().is_off() {
|
||||
self.tx
|
||||
.send(ringtone::SILENCE)
|
||||
.map_err(|err| anyhow!("Can not send silence to Beep. {:?}", err))?;
|
||||
} else if is_battery_down(battery) {
|
||||
self.tx
|
||||
.send(ringtone::BATTERY_LOW)
|
||||
.expect("Can not send message");
|
||||
} else if battery < 1500.0
|
||||
&& self
|
||||
.adapter_downed_at
|
||||
.is_some_and(|at| at.elapsed().is_ok_and(|dur| dur > &Duration::from_secs(10)))
|
||||
{
|
||||
} else if is_battery_low(battery) {
|
||||
self.dc_out_controller.shutdown();
|
||||
if self.adapter_downed_at.is_none() {
|
||||
info!("Recording adapter downed at: {:?}", SystemTime::now());
|
||||
self.adapter_downed_at = Some(SystemTime::now());
|
||||
} else if self
|
||||
.adapter_downed_at
|
||||
.is_some_and(|at| at.elapsed().is_ok_and(|dur| *dur > Duration::from_secs(5)))
|
||||
{
|
||||
self.tx
|
||||
.send(ringtone::SHUTDOWN)
|
||||
.expect("Can not send message");
|
||||
.map_err(|err| anyhow!("Can not send shutdown to Beep. {:?}", err))?;
|
||||
}
|
||||
} else {
|
||||
self.tx
|
||||
.send(ringtone::ADAPTER_DOWN)
|
||||
.expect("Can not send message");
|
||||
.map_err(|err| anyhow!("Can not send adapter down to Beep. {:?}", err))?;
|
||||
self.dc_out_controller
|
||||
.stop_shutdown()
|
||||
.map_err(|err| anyhow!("Can not stop shutdown. {:?}", err))?;
|
||||
self.adapter_downed_at = None;
|
||||
}
|
||||
} else {
|
||||
self.dc_out_controller.stop_shutdown().expect("Can not stop shutdown");
|
||||
self.dc_out_controller
|
||||
.stop_shutdown()
|
||||
.expect("Can not stop shutdown");
|
||||
self.tx.send(ringtone::SILENCE).map_err(|err| anyhow!("Can not send silence to Beep. {:?}", err))?;
|
||||
self.adapter_downed_at = None;
|
||||
self.tx
|
||||
.send(ringtone::SILENCE)
|
||||
.expect("Can not send message");
|
||||
self.dc_out_controller.open()?;
|
||||
}
|
||||
self.screen
|
||||
.draw_voltage(adapter, battery)
|
||||
.expect("Failed to draw voltage");
|
||||
.map_err(|err| anyhow!("Can not draw voltage. {:?}", err))?;
|
||||
|
||||
Ok({})
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_battery_low(battery: f32) -> bool {
|
||||
battery < 1500.0
|
||||
}
|
||||
|
||||
fn is_battery_down(battery: f32) -> bool {
|
||||
battery < 500.0
|
||||
}
|
||||
|
||||
fn is_adapter_down(adapter: f32) -> bool {
|
||||
adapter < 1000.0
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user