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