feat: 支持电源的延迟断开和立即闭合。
This commit is contained in:
parent
65028bd7f6
commit
10fb4601e9
@ -19,9 +19,12 @@ pio = ["esp-idf-sys/pio"]
|
||||
anyhow = {version = "1.0.57", features = ["backtrace"]}
|
||||
embedded-graphics = "0.7.1"
|
||||
embedded-hal = "1.0.0-alpha.8"
|
||||
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
|
||||
embedded-hal-0-2 = {package = "embedded-hal", version = "0.2.7", features = ["unproven"]}
|
||||
env_logger = "0.9.0"
|
||||
esp-idf-hal = "0.37.3"
|
||||
esp-idf-sys = {version = "0.31.5", features = ["binstart"]}
|
||||
log = "0.4.17"
|
||||
retry = "1.3.1"
|
||||
ssd1306 = "0.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -59,7 +59,7 @@ pub mod ringtone {
|
||||
|
||||
pub type Type = [bool; 16];
|
||||
|
||||
pub const POWER_DOWN: Type = [
|
||||
pub const ADAPTER_DOWN: Type = [
|
||||
true, true, true, true, false, false, false, false, false, false, false, false, false,
|
||||
false, false, false,
|
||||
];
|
||||
@ -68,4 +68,8 @@ pub mod ringtone {
|
||||
false, false,
|
||||
];
|
||||
pub const SILENCE: Type = [false; 16];
|
||||
pub const SHUTDOWN: Type = [
|
||||
true, false, true, true, true, true, false, true, true, true, true, true, false, false,
|
||||
false, false,
|
||||
];
|
||||
}
|
||||
|
@ -1,24 +1,109 @@
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use retry;
|
||||
use std::{
|
||||
sync::{
|
||||
mpsc::{self},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread,
|
||||
time::{self, SystemTime},
|
||||
};
|
||||
|
||||
use embedded_hal::digital::blocking::OutputPin;
|
||||
use log::{info, trace, warn};
|
||||
|
||||
pub struct DcOutController<T: OutputPin> {
|
||||
pub state: bool,
|
||||
pin: T,
|
||||
}
|
||||
|
||||
impl<T> DcOutController<T>
|
||||
pub struct DcOutController<P>
|
||||
where
|
||||
T: OutputPin,
|
||||
P: OutputPin + Send,
|
||||
{
|
||||
pub fn new(pin: T) -> DcOutController<T> {
|
||||
return DcOutController { state: false, pin };
|
||||
pub state: bool,
|
||||
pin: Arc<Mutex<P>>,
|
||||
shutdown_tx: Option<mpsc::Sender<bool>>,
|
||||
}
|
||||
|
||||
impl<P> DcOutController<P>
|
||||
where
|
||||
P: OutputPin + Send,
|
||||
P: 'static,
|
||||
P: std::marker::Sync,
|
||||
{
|
||||
pub fn new(pin: P) -> Self {
|
||||
return Self {
|
||||
state: false,
|
||||
pin: Arc::new(Mutex::new(pin)),
|
||||
shutdown_tx: None,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn on(&mut self) -> Result<(), T::Error> {
|
||||
self.state = true;
|
||||
return self.pin.set_low();
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
let mut pin = retry::retry(retry::delay::Fixed::from_millis(100).take(10), || {
|
||||
self.pin.lock()
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
|
||||
|
||||
retry::retry(retry::delay::Fixed::from_millis(100).take(10), || {
|
||||
pin.set_low()
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn off(&mut self) -> Result<(), T::Error> {
|
||||
self.state = false;
|
||||
return self.pin.set_high();
|
||||
|
||||
pub fn shutdown(&mut self) {
|
||||
if self.shutdown_tx.is_some() {
|
||||
trace!("Shutdown task already running, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Shutdown task started...");
|
||||
let (tx, rx) = mpsc::channel();
|
||||
self.shutdown_tx = Some(tx);
|
||||
|
||||
let pin = Arc::clone(&self.pin);
|
||||
|
||||
thread::spawn(move || {
|
||||
// Wait for computer shutdown finished
|
||||
let target_at = SystemTime::now() + time::Duration::from_secs(10);
|
||||
loop {
|
||||
if rx.try_recv().is_ok_and(|state| !*state) {
|
||||
break;
|
||||
}
|
||||
|
||||
if target_at > SystemTime::now() {
|
||||
thread::sleep(time::Duration::from_millis(1000));
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Shutdown timeout, force shutdown...");
|
||||
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");
|
||||
info!("Shutdown finished");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop_shutdown(&mut self) -> Result<()> {
|
||||
if let Some(tx) = self.shutdown_tx.as_mut() {
|
||||
info!("Shutdown task stopped...");
|
||||
if retry::retry(retry::delay::Fixed::from_millis(100).take(5), || {
|
||||
tx.send(false)
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("Failed to stop shutdown"))
|
||||
.is_err() {
|
||||
warn!("Message to shutdown task was not sent");
|
||||
} else {
|
||||
info!("Shutdown task stopped");
|
||||
}
|
||||
self.shutdown_tx = None;
|
||||
};
|
||||
|
||||
self.start()?;
|
||||
info!("Power restored");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![feature(is_some_with)]
|
||||
use esp_idf_sys as _;
|
||||
use std::{thread, time::Duration, sync::mpsc};
|
||||
use std::{thread, time::Duration, sync::mpsc, env};
|
||||
|
||||
mod beep;
|
||||
mod blink;
|
||||
@ -7,10 +8,13 @@ mod dc_out_controller;
|
||||
mod manager;
|
||||
mod screen;
|
||||
fn main() {
|
||||
env::set_var("RUST_LOG", env!("RUST_LOG"));
|
||||
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.
|
||||
esp_idf_sys::link_patches();
|
||||
|
||||
|
||||
let peripherals = esp_idf_hal::peripherals::Peripherals::take().unwrap();
|
||||
|
||||
let gpio5 = peripherals.pins.gpio5;
|
||||
@ -66,5 +70,6 @@ fn main() {
|
||||
).expect("Failed to create manager");
|
||||
loop {
|
||||
manager.handling_once().expect("Failed to handle once");
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
|
||||
use embedded_hal::digital::blocking::OutputPin;
|
||||
use embedded_hal_0_2::adc::OneShot;
|
||||
use esp_idf_hal::{
|
||||
@ -7,6 +5,12 @@ use esp_idf_hal::{
|
||||
gpio::{Gpio1, Gpio2},
|
||||
};
|
||||
use esp_idf_sys::EspError;
|
||||
use log::info;
|
||||
use std::{
|
||||
sync::mpsc,
|
||||
thread,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{beep::ringtone, dc_out_controller::DcOutController, screen::Screen};
|
||||
|
||||
@ -15,7 +19,7 @@ type BatteryGpio = Gpio2<Atten11dB<ADC1>>;
|
||||
|
||||
pub struct Manager<C>
|
||||
where
|
||||
C: OutputPin,
|
||||
C: OutputPin + Send,
|
||||
{
|
||||
dc_out_controller: DcOutController<C>,
|
||||
screen: Screen,
|
||||
@ -23,11 +27,14 @@ where
|
||||
adapter_pin: AdapterGpio,
|
||||
battery_pin: BatteryGpio,
|
||||
tx: mpsc::Sender<ringtone::Type>,
|
||||
adapter_downed_at: Option<SystemTime>,
|
||||
}
|
||||
|
||||
impl<C> Manager<C>
|
||||
where
|
||||
C: OutputPin,
|
||||
C: OutputPin + Send,
|
||||
C: 'static,
|
||||
C: std::marker::Sync,
|
||||
{
|
||||
pub fn new(
|
||||
dc_out_controller: DcOutController<C>,
|
||||
@ -48,6 +55,7 @@ where
|
||||
adapter_pin,
|
||||
battery_pin,
|
||||
tx,
|
||||
adapter_downed_at: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -71,21 +79,34 @@ where
|
||||
battery /= 10.0_f32;
|
||||
|
||||
if adapter < 1000.0 {
|
||||
self.dc_out_controller.off().expect("Can not turn off Out");
|
||||
if battery < 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 {
|
||||
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)))
|
||||
{
|
||||
self.dc_out_controller.shutdown();
|
||||
self.tx
|
||||
.send(ringtone::SHUTDOWN)
|
||||
.expect("Can not send message");
|
||||
} else {
|
||||
self.tx
|
||||
.send(ringtone::POWER_DOWN)
|
||||
.send(ringtone::ADAPTER_DOWN)
|
||||
.expect("Can not send message");
|
||||
}
|
||||
} else {
|
||||
self.dc_out_controller.stop_shutdown().expect("Can not stop shutdown");
|
||||
self.adapter_downed_at = None;
|
||||
self.tx
|
||||
.send(ringtone::SILENCE)
|
||||
.expect("Can not send message");
|
||||
self.dc_out_controller.on().expect("Can not turn on Out");
|
||||
}
|
||||
self.screen
|
||||
.draw_voltage(adapter, battery)
|
||||
|
@ -58,9 +58,6 @@ impl Screen {
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
|
||||
println!("LED rendering done");
|
||||
|
||||
let mut instance = Screen { display };
|
||||
|
||||
instance.draw_boot()?;
|
||||
@ -95,8 +92,6 @@ impl Screen {
|
||||
)
|
||||
.draw(&mut self.display).expect("Failed to draw text");
|
||||
|
||||
println!("LED rendering done");
|
||||
|
||||
self.display
|
||||
.flush()
|
||||
.map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?;
|
||||
@ -126,8 +121,6 @@ impl Screen {
|
||||
)
|
||||
.draw(&mut self.display).expect("Failed to draw text");
|
||||
|
||||
println!("LED rendering done");
|
||||
|
||||
self.display
|
||||
.flush()
|
||||
.map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?;
|
||||
|
Loading…
Reference in New Issue
Block a user