ups-esp32c3-rust/src/voltage_detection.rs

183 lines
5.8 KiB
Rust

use std::{
sync::{Arc, Mutex},
time::Duration,
};
use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot};
use embedded_svc::{
event_bus::Postbox,
timer::{PeriodicTimer, TimerService},
};
use esp_idf_hal::{
adc::{
config::{self},
Analog, PoweredAdc, ADC1,
},
gpio::{Gpio1, Gpio2, Gpio3, Input},
};
use esp_idf_svc::{
eventloop::{
EspEventFetchData, EspEventPostData, EspTypedEventDeserializer, EspTypedEventSerializer,
EspTypedEventSource,
},
timer::{EspTimer, EspTimerService},
};
use esp_idf_sys::c_types;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use crate::EVENTLOOP;
const ADAPTER_OFFSET: f32 = 12002f32 / 900f32;
const BATTERY_OFFSET: f32 = 12002f32 / 900f32;
const OUTPUT_OFFSET: f32 = 12002f32 / 900f32;
pub struct VoltageDetection {
pub worker: VoltageDetectionWorker,
watch_timer: Option<EspTimer>,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct VoltageDetectionWorker {
pub adapter_voltage: u16,
pub battery_voltage: u16,
pub output_voltage: u16,
}
impl VoltageDetection {
pub fn new() -> Self {
return Self {
worker: VoltageDetectionWorker::new(),
watch_timer: None,
};
}
pub fn watching(&mut self) -> anyhow::Result<()> {
let worker = Arc::new(Mutex::new(self.worker));
let mut timer = EspTimerService::new()?.timer(move || {
info!("One-shot timer triggered");
match worker.lock().as_mut() {
Ok(worker) => {
if let Err(err) = worker.read_once() {
warn!("Read Failed. {}", err);
}
info!(
"Adapter: {},\tBattery: {},\t Output: {}",
worker.adapter_voltage, worker.battery_voltage, worker.output_voltage
);
if let Some(eventloop) = unsafe { EVENTLOOP.as_mut() } {
if let Err(err) = eventloop.post(
&mut VoltageDetectionWorker {
adapter_voltage: worker.adapter_voltage,
battery_voltage: worker.battery_voltage,
output_voltage: worker.output_voltage,
},
None,
) {
warn!("Post Result to Event Loop failed. {}", err);
} else {
info!("Event Loop Post");
}
} else {
warn!("EVENTLOOP IS NONE");
}
}
Err(err) => {
warn!("Lock VoltageDetection Worker Failed. {}", err)
}
}
})?;
timer.every(Duration::from_secs(1))?;
self.watch_timer = Some(timer);
return anyhow::Ok(());
}
}
impl VoltageDetectionWorker {
pub fn new() -> Self {
return Self {
adapter_voltage: 0,
battery_voltage: 0,
output_voltage: 0,
};
}
pub fn read_once(&mut self) -> anyhow::Result<()> {
let adapter_pin = unsafe { Gpio1::<Input>::new() }
.into_analog_atten_6db()
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
match self.read_pin_once(adapter_pin) {
Ok(voltage) => {
self.adapter_voltage = ((voltage as f32) * ADAPTER_OFFSET) as u16;
}
Err(err) => {
warn!("Adapter Voltage read failed: {:?}", err);
}
}
let battery_pin = unsafe { Gpio2::<Input>::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;
}
Err(err) => {
warn!("Adapter Voltage read failed: {:?}", err);
}
}
let output_pin = unsafe { Gpio3::<Input>::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;
}
Err(err) => {
warn!("Adapter Voltage read failed: {:?}", err);
}
}
return anyhow::Ok(());
}
pub fn read_pin_once<AN: Analog<ADC1>, PIN: Channel<AN, ID = u8>>(
&mut self,
mut pin: PIN,
) -> anyhow::Result<u16> {
let mut adc = PoweredAdc::new(unsafe { ADC1::new() }, config::Config::new())?;
let voltage = adc.read(&mut pin);
match voltage {
Ok(voltage) => anyhow::Ok(voltage),
Err(err) => {
anyhow::bail!("Adapter Voltage read failed: {:?}", err)
}
}
}
}
impl EspTypedEventSource for VoltageDetectionWorker {
fn source() -> *const c_types::c_char {
b"VOLTAGES\0".as_ptr() as *const _
}
}
impl EspTypedEventSerializer<VoltageDetectionWorker> for VoltageDetectionWorker {
fn serialize<R>(
event: &VoltageDetectionWorker,
f: impl for<'a> FnOnce(&'a EspEventPostData) -> R,
) -> R {
f(&unsafe { EspEventPostData::new(Self::source(), Self::event_id(), event) })
}
}
impl EspTypedEventDeserializer<VoltageDetectionWorker> for VoltageDetectionWorker {
fn deserialize<R>(
data: &EspEventFetchData,
f: &mut impl for<'a> FnMut(&'a VoltageDetectionWorker) -> R,
) -> R {
f(unsafe { data.as_payload() })
}
}