diff --git a/Cargo.toml b/Cargo.toml index ac29427..c5179f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,10 @@ pio = ["esp-idf-sys/pio"] [dependencies] anyhow = "1" +embedded-hal = "0.2.7" embedded-svc = "0.22.0" env_logger = "0.9.0" +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" diff --git a/src/blink.rs b/src/blink.rs new file mode 100644 index 0000000..e54e65e --- /dev/null +++ b/src/blink.rs @@ -0,0 +1,47 @@ +use embedded_hal::digital::v2::OutputPin; +use std::thread; +use std::time::Duration; + +pub struct Blink +where + T: OutputPin, +{ + state: bool, + pin: T, + stopped: bool, +} + +impl Blink +where + T: OutputPin, +{ + pub fn new(pin: T) -> Blink { + return Blink { + state: false, + pin, + stopped: false, + }; + } + + pub fn toggle(&mut self) -> Result<(), T::Error> { + self.state = !self.state; + if self.state { + self.pin.set_high() + } else { + self.pin.set_low() + } + } + + pub fn play(&mut self) where ::Error: std::fmt::Debug { + loop { + if self.stopped { + break; + } + self.toggle().unwrap(); + thread::sleep(Duration::from_millis(30)); + + self.toggle().unwrap(); + thread::sleep(Duration::from_millis(1930)); + } + } +} diff --git a/src/main.rs b/src/main.rs index 2cc21d0..126e62a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,22 @@ -use std::{env, thread::sleep, time::{Duration}}; use embedded_svc::mqtt::client::{Publish, QoS}; use esp_idf_sys as _; use log::*; +use std::{ + env, + thread::{self, sleep}, + time::Duration, +}; +mod blink; mod message_queue; -mod wifi; mod time; +mod wifi; +mod voltage_detection; + +use crate::{ + message_queue::MessageQueue, time::Time, wifi::Internet, voltage_detection::VoltageDetection, +}; -use crate::{message_queue::MessageQueue, wifi::Internet, time::Time}; fn main() { env::set_var("DEFMT_LOG", "trace"); env::set_var("RUST_BACKTRACE", "1"); @@ -18,7 +27,31 @@ fn main() { // or else some patches to the runtime implemented by esp-idf-sys might not link properly. esp_idf_sys::link_patches(); - println!("Hello, world!"); + info!("Hello, world!"); + + let peripherals = esp_idf_hal::peripherals::Peripherals::take().unwrap(); + + let blink_pin = peripherals.pins.gpio5; + let beep_pin = peripherals.pins.gpio6; + let ledc_timer0 = peripherals.ledc.timer0; + let ledc_channel0 = peripherals.ledc.channel0; + let dc_out_ctl_pin = peripherals.pins.gpio3; + let i2c0 = peripherals.i2c0; + let sda_pin = peripherals.pins.gpio4; + let scl_pin = peripherals.pins.gpio10; + + thread::spawn(move || { + let mut blink = blink::Blink::new( + blink_pin + .into_output() + .expect("Failed to set GPIO5 as output"), + ); + blink.play(); + }); + + let mut voltage_detection = VoltageDetection::new(); + + voltage_detection.watching().expect("Can not watch voltages."); let _wifi = Internet::new().unwrap(); @@ -34,14 +67,15 @@ fn main() { 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(); + mq_client + .publish( + "ups-0.2/heartbeat", + QoS::AtMostOnce, + false, + timestamps.to_string().as_bytes(), + ) + .map_err(|err| warn!("publish heartbeat failed {}", err)) + .unwrap(); } } } diff --git a/src/message_queue.rs b/src/message_queue.rs index a899827..59b4056 100644 --- a/src/message_queue.rs +++ b/src/message_queue.rs @@ -24,7 +24,7 @@ impl MessageQueue { }; let (mut client, mut connection) = - EspMqttClient::new_with_conn("mqtt://192.168.31.8:1883", &conf)?; + EspMqttClient::new_with_conn("mqtt://192.168.31.11:1883", &conf)?; info!("MQTT client started"); thread::spawn(move || { diff --git a/src/voltage_detection.rs b/src/voltage_detection.rs new file mode 100644 index 0000000..77213d1 --- /dev/null +++ b/src/voltage_detection.rs @@ -0,0 +1,124 @@ +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; + +use embedded_hal::{adc::Channel, prelude::_embedded_hal_adc_OneShot, digital::v2::OutputPin}; +use embedded_svc::timer::{PeriodicTimer, TimerService}; +use esp_idf_hal::{ + adc::{ + config::{self}, + Analog, PoweredAdc, ADC1, + }, + gpio::{Gpio1, Gpio2, Gpio3, Input, Pull}, +}; +use esp_idf_svc::timer::{EspTimer, EspTimerService}; +use log::{info, warn}; + +pub struct VoltageDetection { + pub worker: VoltageDetectionWorker, + watch_timer: Option, +} + +#[derive(Clone, Copy)] +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.as_ref().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 + ); + } + 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::::new() } + .into_analog_atten_11db() + .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; + } + Err(err) => { + warn!("Adapter Voltage read failed: {:?}", err); + } + } + + let battery_pin = unsafe { Gpio2::::new() } + .into_analog_atten_11db() + .map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?; + match self.read_pin_once(battery_pin) { + Ok(voltage) => { + self.battery_voltage = voltage; + } + Err(err) => { + warn!("Adapter Voltage read failed: {:?}", err); + } + } + + let output_pin = unsafe { Gpio3::::new() } + .into_analog_atten_11db() + .map_err(|err| anyhow::anyhow!("Failed to set GPIO 1 as analog input. {}", err))?; + match self.read_pin_once(output_pin) { + Ok(voltage) => { + self.output_voltage = voltage; + } + Err(err) => { + warn!("Adapter Voltage read failed: {:?}", err); + } + } + + return anyhow::Ok(()); + } + pub fn read_pin_once, PIN: Channel>( + &mut self, + mut pin: PIN, + ) -> anyhow::Result { + 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), + Err(err) => { + anyhow::bail!("Adapter Voltage read failed: {:?}", err) + } + } + } +}