feat: reporting voltage via MQTT.
This commit is contained in:
parent
d20344fe5e
commit
3c8fdd124b
@ -24,6 +24,8 @@ esp-idf-hal = "0.38.0"
|
|||||||
esp-idf-svc = "0.42.1"
|
esp-idf-svc = "0.42.1"
|
||||||
esp-idf-sys = { version = "0.31.6", features = ["binstart"] }
|
esp-idf-sys = { version = "0.31.6", features = ["binstart"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
|
serde = {version="1.0.144", features = ["derive"]}
|
||||||
|
serde_json = "1.0.83"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -38,10 +38,10 @@ where
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.toggle().unwrap();
|
self.toggle().unwrap();
|
||||||
thread::sleep(Duration::from_millis(30));
|
thread::sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
self.toggle().unwrap();
|
self.toggle().unwrap();
|
||||||
thread::sleep(Duration::from_millis(1930));
|
thread::sleep(Duration::from_millis(990));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
src/eventloop.rs
Normal file
0
src/eventloop.rs
Normal file
62
src/main.rs
62
src/main.rs
@ -1,5 +1,6 @@
|
|||||||
use embedded_svc::mqtt::client::{Publish, QoS};
|
use embedded_svc::event_bus::EventBus;
|
||||||
use esp_idf_sys as _;
|
use esp_idf_svc::eventloop::{Background, EspBackgroundEventLoop, EspEventLoop};
|
||||||
|
use esp_idf_sys::{self as _, EspError};
|
||||||
use log::*;
|
use log::*;
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
@ -10,13 +11,16 @@ use std::{
|
|||||||
mod blink;
|
mod blink;
|
||||||
mod message_queue;
|
mod message_queue;
|
||||||
mod time;
|
mod time;
|
||||||
mod wifi;
|
|
||||||
mod voltage_detection;
|
mod voltage_detection;
|
||||||
|
mod wifi;
|
||||||
|
|
||||||
|
use crate::voltage_detection::VoltageDetectionWorker;
|
||||||
use crate::{
|
use crate::{
|
||||||
message_queue::MessageQueue, time::Time, wifi::Internet, voltage_detection::VoltageDetection,
|
message_queue::MessageQueue, time::Time, voltage_detection::VoltageDetection, wifi::Internet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static mut EVENTLOOP: Option<EspEventLoop<esp_idf_svc::eventloop::User<Background>>> = None;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env::set_var("DEFMT_LOG", "trace");
|
env::set_var("DEFMT_LOG", "trace");
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
env::set_var("RUST_BACKTRACE", "1");
|
||||||
@ -40,6 +44,12 @@ fn main() {
|
|||||||
let sda_pin = peripherals.pins.gpio4;
|
let sda_pin = peripherals.pins.gpio4;
|
||||||
let scl_pin = peripherals.pins.gpio10;
|
let scl_pin = peripherals.pins.gpio10;
|
||||||
|
|
||||||
|
info!("About to start a background event loop");
|
||||||
|
match EspBackgroundEventLoop::new(&Default::default()) {
|
||||||
|
Ok(eventloop) => unsafe { EVENTLOOP = Some(eventloop) },
|
||||||
|
Err(err) => error!("Init Event Loop failed. {:?}", err),
|
||||||
|
};
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut blink = blink::Blink::new(
|
let mut blink = blink::Blink::new(
|
||||||
blink_pin
|
blink_pin
|
||||||
@ -51,7 +61,9 @@ fn main() {
|
|||||||
|
|
||||||
let mut voltage_detection = VoltageDetection::new();
|
let mut voltage_detection = VoltageDetection::new();
|
||||||
|
|
||||||
voltage_detection.watching().expect("Can not watch voltages.");
|
voltage_detection
|
||||||
|
.watching()
|
||||||
|
.expect("Can not watch voltages.");
|
||||||
|
|
||||||
let _wifi = Internet::new().unwrap();
|
let _wifi = Internet::new().unwrap();
|
||||||
|
|
||||||
@ -61,21 +73,35 @@ fn main() {
|
|||||||
let mut time = Time::new();
|
let mut time = Time::new();
|
||||||
time.sync().unwrap();
|
time.sync().unwrap();
|
||||||
|
|
||||||
|
let _subscription;
|
||||||
|
if let Some(eventloop) = unsafe { EVENTLOOP.as_mut() } {
|
||||||
|
_subscription = eventloop
|
||||||
|
.subscribe(move |message: &VoltageDetectionWorker| {
|
||||||
|
info!("Event Loop Value");
|
||||||
|
if let Ok(json_str) = serde_json::to_string(&message) {
|
||||||
|
if let Err(err) = mq.publish("voltage", json_str.as_bytes()) {
|
||||||
|
warn!("Can not publish message to MQTT. {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect(" Listening Event Loop Failed");
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
sleep(Duration::from_millis(1000));
|
sleep(Duration::from_millis(1000));
|
||||||
if let Some(ref mut mq_client) = mq.client {
|
// if let Some(ref mut mq_client) = mq.client {
|
||||||
let timestamps = time.get_time().as_millis();
|
// let timestamps = time.get_time().as_millis();
|
||||||
|
|
||||||
info!("timestamps {}", timestamps);
|
// info!("timestamps {}", timestamps);
|
||||||
mq_client
|
// mq_client
|
||||||
.publish(
|
// .publish(
|
||||||
"ups-0.2/heartbeat",
|
// "ups-0.2/heartbeat",
|
||||||
QoS::AtMostOnce,
|
// QoS::AtMostOnce,
|
||||||
false,
|
// false,
|
||||||
timestamps.to_string().as_bytes(),
|
// timestamps.to_string().as_bytes(),
|
||||||
)
|
// )
|
||||||
.map_err(|err| warn!("publish heartbeat failed {}", err))
|
// .map_err(|err| warn!("publish heartbeat failed {}", err))
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,13 +61,16 @@ impl MessageQueue {
|
|||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn publish(&mut self, bytes: &[u8]) -> Result<u32> {
|
pub fn publish(&mut self, topic: &str, bytes: &[u8]) -> Result<u32> {
|
||||||
self.client
|
self.client
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.publish("", QoS::AtMostOnce, false, bytes)
|
.publish(
|
||||||
.map_err(|err| {
|
format!("{}/{}", "ups_0_2", topic).as_str(),
|
||||||
anyhow::anyhow!("publish message to queue was failed!. {}", err)
|
QoS::AtMostOnce,
|
||||||
})
|
false,
|
||||||
|
bytes,
|
||||||
|
)
|
||||||
|
.map_err(|err| anyhow::anyhow!("publish message to queue was failed!. {}", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,24 +3,41 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot, digital::v2::OutputPin};
|
use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot};
|
||||||
use embedded_svc::timer::{PeriodicTimer, TimerService};
|
use embedded_svc::{
|
||||||
|
event_bus::Postbox,
|
||||||
|
timer::{PeriodicTimer, TimerService},
|
||||||
|
};
|
||||||
use esp_idf_hal::{
|
use esp_idf_hal::{
|
||||||
adc::{
|
adc::{
|
||||||
config::{self},
|
config::{self},
|
||||||
Analog, PoweredAdc, ADC1,
|
Analog, PoweredAdc, ADC1,
|
||||||
},
|
},
|
||||||
gpio::{Gpio1, Gpio2, Gpio3, Input, Pull},
|
gpio::{Gpio1, Gpio2, Gpio3, Input},
|
||||||
};
|
};
|
||||||
use esp_idf_svc::timer::{EspTimer, EspTimerService};
|
use esp_idf_svc::{
|
||||||
|
eventloop::{
|
||||||
|
EspEventFetchData, EspEventPostData, EspTypedEventDeserializer, EspTypedEventSerializer,
|
||||||
|
EspTypedEventSource,
|
||||||
|
},
|
||||||
|
timer::{EspTimer, EspTimerService},
|
||||||
|
};
|
||||||
|
use esp_idf_sys::c_types;
|
||||||
use log::{info, warn};
|
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 struct VoltageDetection {
|
||||||
pub worker: VoltageDetectionWorker,
|
pub worker: VoltageDetectionWorker,
|
||||||
watch_timer: Option<EspTimer>,
|
watch_timer: Option<EspTimer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct VoltageDetectionWorker {
|
pub struct VoltageDetectionWorker {
|
||||||
pub adapter_voltage: u16,
|
pub adapter_voltage: u16,
|
||||||
pub battery_voltage: u16,
|
pub battery_voltage: u16,
|
||||||
@ -39,7 +56,7 @@ impl VoltageDetection {
|
|||||||
let worker = Arc::new(Mutex::new(self.worker));
|
let worker = Arc::new(Mutex::new(self.worker));
|
||||||
let mut timer = EspTimerService::new()?.timer(move || {
|
let mut timer = EspTimerService::new()?.timer(move || {
|
||||||
info!("One-shot timer triggered");
|
info!("One-shot timer triggered");
|
||||||
match worker.as_ref().lock().as_mut() {
|
match worker.lock().as_mut() {
|
||||||
Ok(worker) => {
|
Ok(worker) => {
|
||||||
if let Err(err) = worker.read_once() {
|
if let Err(err) = worker.read_once() {
|
||||||
warn!("Read Failed. {}", err);
|
warn!("Read Failed. {}", err);
|
||||||
@ -48,6 +65,22 @@ impl VoltageDetection {
|
|||||||
"Adapter: {},\tBattery: {},\t Output: {}",
|
"Adapter: {},\tBattery: {},\t Output: {}",
|
||||||
worker.adapter_voltage, worker.battery_voltage, worker.output_voltage
|
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) => {
|
Err(err) => {
|
||||||
warn!("Lock VoltageDetection Worker Failed. {}", err)
|
warn!("Lock VoltageDetection Worker Failed. {}", err)
|
||||||
@ -60,6 +93,7 @@ impl VoltageDetection {
|
|||||||
return anyhow::Ok(());
|
return anyhow::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VoltageDetectionWorker {
|
impl VoltageDetectionWorker {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
@ -71,11 +105,11 @@ impl VoltageDetectionWorker {
|
|||||||
|
|
||||||
pub fn read_once(&mut self) -> anyhow::Result<()> {
|
pub fn read_once(&mut self) -> anyhow::Result<()> {
|
||||||
let adapter_pin = unsafe { Gpio1::<Input>::new() }
|
let adapter_pin = unsafe { Gpio1::<Input>::new() }
|
||||||
.into_analog_atten_11db()
|
.into_analog_atten_6db()
|
||||||
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
|
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
|
||||||
match self.read_pin_once(adapter_pin) {
|
match self.read_pin_once(adapter_pin) {
|
||||||
Ok(voltage) => {
|
Ok(voltage) => {
|
||||||
self.adapter_voltage = voltage;
|
self.adapter_voltage = ((voltage as f32) * ADAPTER_OFFSET) as u16;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("Adapter Voltage read failed: {:?}", err);
|
warn!("Adapter Voltage read failed: {:?}", err);
|
||||||
@ -83,11 +117,11 @@ impl VoltageDetectionWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let battery_pin = unsafe { Gpio2::<Input>::new() }
|
let battery_pin = unsafe { Gpio2::<Input>::new() }
|
||||||
.into_analog_atten_11db()
|
.into_analog_atten_6db()
|
||||||
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
|
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 2 as analog input. {}", err))?;
|
||||||
match self.read_pin_once(battery_pin) {
|
match self.read_pin_once(battery_pin) {
|
||||||
Ok(voltage) => {
|
Ok(voltage) => {
|
||||||
self.battery_voltage = voltage;
|
self.battery_voltage = ((voltage as f32) * BATTERY_OFFSET) as u16;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("Adapter Voltage read failed: {:?}", err);
|
warn!("Adapter Voltage read failed: {:?}", err);
|
||||||
@ -95,11 +129,11 @@ impl VoltageDetectionWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output_pin = unsafe { Gpio3::<Input>::new() }
|
let output_pin = unsafe { Gpio3::<Input>::new() }
|
||||||
.into_analog_atten_11db()
|
.into_analog_atten_6db()
|
||||||
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
|
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 3 as analog input. {}", err))?;
|
||||||
match self.read_pin_once(output_pin) {
|
match self.read_pin_once(output_pin) {
|
||||||
Ok(voltage) => {
|
Ok(voltage) => {
|
||||||
self.output_voltage = voltage;
|
self.output_voltage = ((voltage as f32) * OUTPUT_OFFSET) as u16;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("Adapter Voltage read failed: {:?}", err);
|
warn!("Adapter Voltage read failed: {:?}", err);
|
||||||
@ -115,10 +149,34 @@ impl VoltageDetectionWorker {
|
|||||||
let mut adc = PoweredAdc::new(unsafe { ADC1::new() }, config::Config::new())?;
|
let mut adc = PoweredAdc::new(unsafe { ADC1::new() }, config::Config::new())?;
|
||||||
let voltage = adc.read(&mut pin);
|
let voltage = adc.read(&mut pin);
|
||||||
match voltage {
|
match voltage {
|
||||||
Ok(voltage) => anyhow::Ok((self.adapter_voltage + voltage * 10) / 11),
|
Ok(voltage) => anyhow::Ok(voltage),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
anyhow::bail!("Adapter Voltage read failed: {:?}", 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() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user