feat: reporting voltage via MQTT.

This commit is contained in:
Ivan Li 2022-08-21 21:54:09 +08:00
parent d20344fe5e
commit 3c8fdd124b
6 changed files with 130 additions and 41 deletions

View File

@ -24,6 +24,8 @@ esp-idf-hal = "0.38.0"
esp-idf-svc = "0.42.1"
esp-idf-sys = { version = "0.31.6", features = ["binstart"] }
log = "0.4.17"
serde = {version="1.0.144", features = ["derive"]}
serde_json = "1.0.83"
[build-dependencies]

View File

@ -38,10 +38,10 @@ where
break;
}
self.toggle().unwrap();
thread::sleep(Duration::from_millis(30));
thread::sleep(Duration::from_millis(10));
self.toggle().unwrap();
thread::sleep(Duration::from_millis(1930));
thread::sleep(Duration::from_millis(990));
}
}
}

0
src/eventloop.rs Normal file
View File

View File

@ -1,5 +1,6 @@
use embedded_svc::mqtt::client::{Publish, QoS};
use esp_idf_sys as _;
use embedded_svc::event_bus::EventBus;
use esp_idf_svc::eventloop::{Background, EspBackgroundEventLoop, EspEventLoop};
use esp_idf_sys::{self as _, EspError};
use log::*;
use std::{
env,
@ -10,13 +11,16 @@ use std::{
mod blink;
mod message_queue;
mod time;
mod wifi;
mod voltage_detection;
mod wifi;
use crate::voltage_detection::VoltageDetectionWorker;
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() {
env::set_var("DEFMT_LOG", "trace");
env::set_var("RUST_BACKTRACE", "1");
@ -40,6 +44,12 @@ fn main() {
let sda_pin = peripherals.pins.gpio4;
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 || {
let mut blink = blink::Blink::new(
blink_pin
@ -51,7 +61,9 @@ fn main() {
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();
@ -61,21 +73,35 @@ fn main() {
let mut time = Time::new();
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 {
sleep(Duration::from_millis(1000));
if let Some(ref mut mq_client) = mq.client {
let timestamps = time.get_time().as_millis();
// if let Some(ref mut mq_client) = mq.client {
// let timestamps = time.get_time().as_millis();
info!("timestamps {}", timestamps);
mq_client
.publish(
"ups-0.2/heartbeat",
QoS::AtMostOnce,
false,
timestamps.to_string().as_bytes(),
)
.map_err(|err| warn!("publish heartbeat failed {}", err))
.unwrap();
}
// info!("timestamps {}", timestamps);
// mq_client
// .publish(
// "ups-0.2/heartbeat",
// QoS::AtMostOnce,
// false,
// timestamps.to_string().as_bytes(),
// )
// .map_err(|err| warn!("publish heartbeat failed {}", err))
// .unwrap();
// }
}
}

View File

@ -61,13 +61,16 @@ impl MessageQueue {
anyhow::Ok(())
}
pub fn publish(&mut self, bytes: &[u8]) -> Result<u32> {
pub fn publish(&mut self, topic: &str, bytes: &[u8]) -> Result<u32> {
self.client
.as_mut()
.as_mut()
.unwrap()
.publish("", QoS::AtMostOnce, false, bytes)
.map_err(|err| {
anyhow::anyhow!("publish message to queue was failed!. {}", err)
})
.publish(
format!("{}/{}", "ups_0_2", topic).as_str(),
QoS::AtMostOnce,
false,
bytes,
)
.map_err(|err| anyhow::anyhow!("publish message to queue was failed!. {}", err))
}
}

View File

@ -3,24 +3,41 @@ use std::{
time::Duration,
};
use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot, digital::v2::OutputPin};
use embedded_svc::timer::{PeriodicTimer, TimerService};
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, 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 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)]
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct VoltageDetectionWorker {
pub adapter_voltage: u16,
pub battery_voltage: u16,
@ -39,7 +56,7 @@ impl VoltageDetection {
let worker = Arc::new(Mutex::new(self.worker));
let mut timer = EspTimerService::new()?.timer(move || {
info!("One-shot timer triggered");
match worker.as_ref().lock().as_mut() {
match worker.lock().as_mut() {
Ok(worker) => {
if let Err(err) = worker.read_once() {
warn!("Read Failed. {}", err);
@ -48,6 +65,22 @@ impl VoltageDetection {
"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)
@ -60,6 +93,7 @@ impl VoltageDetection {
return anyhow::Ok(());
}
}
impl VoltageDetectionWorker {
pub fn new() -> Self {
return Self {
@ -71,11 +105,11 @@ impl VoltageDetectionWorker {
pub fn read_once(&mut self) -> anyhow::Result<()> {
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))?;
match self.read_pin_once(adapter_pin) {
Ok(voltage) => {
self.adapter_voltage = voltage;
self.adapter_voltage = ((voltage as f32) * ADAPTER_OFFSET) as u16;
}
Err(err) => {
warn!("Adapter Voltage read failed: {:?}", err);
@ -83,11 +117,11 @@ impl VoltageDetectionWorker {
}
let battery_pin = unsafe { Gpio2::<Input>::new() }
.into_analog_atten_11db()
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
.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;
self.battery_voltage = ((voltage as f32) * BATTERY_OFFSET) as u16;
}
Err(err) => {
warn!("Adapter Voltage read failed: {:?}", err);
@ -95,11 +129,11 @@ impl VoltageDetectionWorker {
}
let output_pin = unsafe { Gpio3::<Input>::new() }
.into_analog_atten_11db()
.map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?;
.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;
self.output_voltage = ((voltage as f32) * OUTPUT_OFFSET) as u16;
}
Err(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 voltage = adc.read(&mut pin);
match voltage {
Ok(voltage) => anyhow::Ok((self.adapter_voltage + voltage * 10) / 11),
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() })
}
}