diff --git a/battery_board/rust/.cargo/config.toml b/battery_board/rust/.cargo/config.toml deleted file mode 100644 index e69de29..0000000 diff --git a/battery_board/rust/rust-toolchain.toml b/battery_board/rust/rust-toolchain.toml deleted file mode 100644 index e69de29..0000000 diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 61f0600..84367a1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -48,56 +48,56 @@ partition_table = "partitions.csv" #ESP stuff esp-bootloader-esp-idf = { version = "0.2.0", features = ["esp32c6"] } esp-hal = { version = "=1.0.0-rc.0", features = [ - "esp32c6", - "log-04", - "unstable", + "esp32c6", + "log-04", + "unstable", ] } log = "0.4.27" embassy-net = { version = "0.7.0", features = [ - "dhcpv4", - "log", - "medium-ethernet", - "tcp", - "udp", + "dhcpv4", + "log", + "medium-ethernet", + "tcp", + "udp", ] } embedded-io = "0.6.1" embedded-io-async = "0.6.1" esp-alloc = "0.8.0" esp-backtrace = { version = "0.17.0", features = [ - "esp32c6", - "exception-handler", - "panic-handler", - "println", + "esp32c6", + "exception-handler", + "panic-handler", + "println", ] } esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] } # for more networking protocol support see https://crates.io/crates/edge-net embassy-executor = { version = "0.7.0", features = [ - "log", - "task-arena-size-20480", + "log", + "task-arena-size-20480", ] } embassy-time = { version = "0.4.0", features = ["log"] } esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] } esp-wifi = { version = "0.15.0", features = [ - "builtin-scheduler", - "esp-alloc", - "esp32c6", - "log-04", - "smoltcp", - "wifi", + "builtin-scheduler", + "esp-alloc", + "esp32c6", + "log-04", + "smoltcp", + "wifi", ] } smoltcp = { version = "0.12.0", default-features = false, features = [ - "log", - "medium-ethernet", - "multicast", - "proto-dhcpv4", - "proto-dns", - "proto-ipv4", - "socket-dns", - "socket-icmp", - "socket-raw", - "socket-tcp", - "socket-udp", + "log", + "medium-ethernet", + "multicast", + "proto-dhcpv4", + "proto-dns", + "proto-ipv4", + "socket-dns", + "socket-icmp", + "socket-raw", + "socket-tcp", + "socket-udp", ] } static_cell = "2.1.1" embedded-hal = "1.0.0" @@ -127,7 +127,7 @@ chrono-tz = { version = "0.10.3", default-features = false, features = ["filter- eeprom24x = "0.7.2" #url = "2.5.3" crc = "3.2.1" -bincode = {version = "2.0.1", default-features = false, features = ["alloc", "serde"] } +bincode = { version = "2.0.1", default-features = false, features = ["alloc", "serde"] } ringbuffer = "0.15.0" #text-template = "0.1.0" strum_macros = "0.27.0" @@ -141,6 +141,7 @@ embedded-can = "0.4.1" critical-section = "1.2.0" portable-atomic = "1.11.1" embassy-sync = { version = "0.7.2", features = ["log"] } +async-trait = "0.1.89" [patch.crates-io] diff --git a/rust/src/hal/battery.rs b/rust/src/hal/battery.rs index 47e6541..04de704 100644 --- a/rust/src/hal/battery.rs +++ b/rust/src/hal/battery.rs @@ -1,22 +1,20 @@ use anyhow::anyhow; -use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; -use embedded_hal_bus::i2c::MutexDevice; -use esp_idf_hal::delay::Delay; -use esp_idf_hal::i2c::{I2cDriver, I2cError}; +use async_trait::async_trait; use measurements::Temperature; use serde::Serialize; +#[async_trait] pub trait BatteryInteraction { - fn state_charge_percent(&mut self) -> Result; - fn remaining_milli_ampere_hour(&mut self) -> Result; - fn max_milli_ampere_hour(&mut self) -> Result; - fn design_milli_ampere_hour(&mut self) -> Result; - fn voltage_milli_volt(&mut self) -> Result; - fn average_current_milli_ampere(&mut self) -> Result; - fn cycle_count(&mut self) -> Result; - fn state_health_percent(&mut self) -> Result; - fn bat_temperature(&mut self) -> Result; - fn get_battery_state(&mut self) -> Result; + async fn state_charge_percent(&mut self) -> Result; + async fn remaining_milli_ampere_hour(&mut self) -> Result; + async fn max_milli_ampere_hour(&mut self) -> Result; + async fn design_milli_ampere_hour(&mut self) -> Result; + async fn voltage_milli_volt(&mut self) -> Result; + async fn average_current_milli_ampere(&mut self) -> Result; + async fn cycle_count(&mut self) -> Result; + async fn state_health_percent(&mut self) -> Result; + async fn bat_temperature(&mut self) -> Result; + async fn get_battery_state(&mut self) -> Result; } #[derive(Debug, Serialize)] diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index dfb77f3..4b17942 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -6,7 +6,10 @@ use anyhow::{anyhow, bail, Context}; use chrono::{DateTime, Utc}; use serde::Serialize; -use alloc::{vec::Vec, string::{String, ToString}}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; #[link_section = ".rtc.data"] static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; @@ -39,35 +42,38 @@ pub struct FileSystemSizeInfo { } pub struct MqttClient<'a> { - mqtt_client: EspMqttClient<'a>, + //mqtt_client: EspMqttClient<'a>, base_topic: heapless::String<64>, } pub struct Esp<'a> { pub(crate) mqtt_client: Option>, - pub(crate) wifi_driver: EspWifi<'a>, - pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, - pub(crate) delay: Delay, + //pub(crate) wifi_driver: EspWifi<'a>, + //pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, } +struct AccessPointInfo {} + impl Esp<'_> { const SPIFFS_PARTITION_NAME: &'static str = "storage"; const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; const BASE_PATH: &'static str = "/spiffs"; pub(crate) fn mode_override_pressed(&mut self) -> bool { - self.boot_button.get_level() == Level::Low + todo!(); + //self.boot_button.get_level() == Level::Low } - pub(crate) fn sntp(&mut self, max_wait_ms: u32) -> anyhow::Result> { - let sntp = sntp::EspSntp::new_default()?; - let mut counter = 0; - while sntp.get_sync_status() != SyncStatus::Completed { - self.delay.delay_ms(100); - counter += 100; - if counter > max_wait_ms { - bail!("Reached sntp timeout, aborting") - } - } - self.time() + pub(crate) async fn sntp(&mut self, max_wait_ms: u32) -> anyhow::Result> { + //let sntp = sntp::EspSntp::new_default()?; + //let mut counter = 0; + //while sntp.get_sync_status() != SyncStatus::Completed { + // self.delay.delay_ms(100); + // counter += 100; + // if counter > max_wait_ms { + // bail!("Reached sntp timeout, aborting") + // } + //} + //self.time() + todo!(); } pub(crate) fn time(&mut self) -> anyhow::Result> { let time = EspSystemTime {}.now().as_millis(); @@ -76,7 +82,8 @@ impl Esp<'_> { .ok_or(anyhow!("could not convert timestamp"))?; anyhow::Ok(local_time) } - pub(crate) fn wifi_scan(&mut self) -> anyhow::Result> { + + pub(crate) async fn wifi_scan(&mut self) -> anyhow::Result> { self.wifi_driver.start_scan( &ScanConfig { scan_type: ScanType::Passive(Duration::from_secs(5)), @@ -128,7 +135,7 @@ impl Esp<'_> { } } - pub(crate) fn wifi_ap(&mut self) -> anyhow::Result<()> { + pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<()> { let ssid = match self.load_config() { Ok(config) => config.network.ap_ssid.clone(), Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), @@ -146,7 +153,7 @@ impl Esp<'_> { anyhow::Ok(()) } - pub(crate) fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result { + pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result { let ssid = network_config .ssid .clone() @@ -208,33 +215,36 @@ impl Esp<'_> { log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); anyhow::Ok(address) } - pub(crate) fn load_config(&mut self) -> anyhow::Result { + pub(crate) async fn load_config(&mut self) -> anyhow::Result { let cfg = File::open(Self::CONFIG_FILE)?; let config: PlantControllerConfig = serde_json::from_reader(cfg)?; anyhow::Ok(config) } - pub(crate) fn save_config(&mut self, config: &PlantControllerConfig) -> anyhow::Result<()> { + pub(crate) async fn save_config( + &mut self, + config: &PlantControllerConfig, + ) -> anyhow::Result<()> { let mut cfg = File::create(Self::CONFIG_FILE)?; serde_json::to_writer(&mut cfg, &config)?; log::info!("Wrote config config {:?}", config); anyhow::Ok(()) } - pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> { + pub(crate) async fn mount_file_system(&mut self) -> anyhow::Result<()> { log(LogMessage::MountingFilesystem, 0, 0, "", ""); let base_path = String::try_from("/spiffs")?; let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?; let conf = todo!(); //let conf = esp_idf_sys::esp_vfs_spiffs_conf_t { - //base_path: base_path.as_ptr(), - //partition_label: storage.as_ptr(), - //max_files: 5, - //format_if_mount_failed: true, + //base_path: base_path.as_ptr(), + //partition_label: storage.as_ptr(), + //max_files: 5, + //format_if_mount_failed: true, //}; //TODO //unsafe { - //esp_idf_sys::esp!(esp_idf_sys::esp_vfs_spiffs_register(&conf))?; + //esp_idf_sys::esp!(esp_idf_sys::esp_vfs_spiffs_register(&conf))?; //} let free_space = self.file_system_size()?; @@ -247,7 +257,7 @@ impl Esp<'_> { ); anyhow::Ok(()) } - fn file_system_size(&mut self) -> anyhow::Result { + async fn file_system_size(&mut self) -> anyhow::Result { let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?; let mut total_size = 0; let mut used_size = 0; @@ -265,7 +275,7 @@ impl Esp<'_> { }) } - pub(crate) fn list_files(&self) -> FileList { + pub(crate) async fn list_files(&self) -> FileList { let storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); let mut file_system_corrupt = None; @@ -312,7 +322,7 @@ impl Esp<'_> { iter_error, } } - pub(crate) fn delete_file(&self, filename: &str) -> anyhow::Result<()> { + pub(crate) async fn delete_file(&self, filename: &str) -> anyhow::Result<()> { let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); match fs::remove_file(filepath) { OkStd(_) => anyhow::Ok(()), @@ -321,7 +331,11 @@ impl Esp<'_> { } } } - pub(crate) fn get_file_handle(&self, filename: &str, write: bool) -> anyhow::Result { + pub(crate) async fn get_file_handle( + &self, + filename: &str, + write: bool, + ) -> anyhow::Result { let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); anyhow::Ok(if write { File::create(filepath)? @@ -361,20 +375,22 @@ impl Esp<'_> { for i in 0..PLANT_COUNT { log::info!( "LAST_WATERING_TIMESTAMP[{}] = UTC {}", - i, LAST_WATERING_TIMESTAMP[i] + i, + LAST_WATERING_TIMESTAMP[i] ); } for i in 0..PLANT_COUNT { log::info!( "CONSECUTIVE_WATERING_PLANT[{}] = {}", - i, CONSECUTIVE_WATERING_PLANT[i] + i, + CONSECUTIVE_WATERING_PLANT[i] ); } } } } - pub(crate) fn mqtt(&mut self, network_config: &NetworkConfig) -> anyhow::Result<()> { + pub(crate) async fn mqtt(&mut self, network_config: &NetworkConfig) -> anyhow::Result<()> { let base_topic = network_config .base_topic .as_ref() @@ -491,7 +507,9 @@ impl Esp<'_> { log::info!("Mqtt connection callback received, progressing"); match mqtt_connected_event_ok.load(std::sync::atomic::Ordering::Relaxed) { true => { - log::info!("Mqtt did callback as connected, testing with roundtrip now"); + log::info!( + "Mqtt did callback as connected, testing with roundtrip now" + ); //subscribe to roundtrip client.subscribe(round_trip_topic.as_str(), ExactlyOnce)?; client.subscribe(stay_alive_topic.as_str(), ExactlyOnce)?; @@ -534,7 +552,11 @@ impl Esp<'_> { } bail!("Mqtt did not fire connection callback in time"); } - pub(crate) fn mqtt_publish(&mut self, subtopic: &str, message: &[u8]) -> anyhow::Result<()> { + pub(crate) async fn mqtt_publish( + &mut self, + subtopic: &str, + message: &[u8], + ) -> anyhow::Result<()> { if self.mqtt_client.is_none() { return anyhow::Ok(()); } diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index 3dd4355..8663791 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -4,9 +4,10 @@ mod initial_hal; mod rtc; mod v3_hal; mod v4_hal; -mod water; mod v4_sensor; +mod water; +use crate::alloc::string::ToString; use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; use crate::hal::water::TankSensor; use crate::{ @@ -19,11 +20,16 @@ use crate::{ }; use alloc::boxed::Box; use anyhow::{Ok, Result}; +use async_trait::async_trait; use battery::BQ34Z100G1; use bq34z100::Bq34z100g1Driver; use ds323x::{DateTimeAccess, Ds323x}; use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::mutex::Mutex; +use embassy_sync::{LazyLock, Mutex}; use embedded_hal_bus::i2c::MutexDevice; +use esp_idf_hal::can::CAN; use esp_idf_hal::pcnt::PCNT1; use esp_idf_hal::{ adc::ADC1, @@ -46,9 +52,6 @@ use esp_idf_sys::{ }; use esp_ota::mark_app_valid; use measurements::{Current, Voltage}; -use embassy_sync::{Mutex, LazyLock}; -use esp_idf_hal::can::CAN; -use crate::alloc::string::ToString; //Only support for 8 right now! pub const PLANT_COUNT: usize = 8; @@ -87,8 +90,9 @@ pub struct HAL<'a> { pub board_hal: Box + Send>, } +#[async_trait] pub trait BoardInteraction<'a> { - fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>>; + fn get_tank_sensor(&mut self) -> Option<&mut TankSensor>; fn get_esp(&mut self) -> &mut Esp<'a>; fn get_config(&mut self) -> &PlantControllerConfig; fn get_battery_monitor(&mut self) -> &mut Box; @@ -99,20 +103,20 @@ pub trait BoardInteraction<'a> { fn is_day(&self) -> bool; //should be multsampled fn light(&mut self, enable: bool) -> Result<()>; - fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; - fn pump_current(&mut self, plant: usize) -> Result; - fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; - fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result; - fn general_fault(&mut self, enable: bool); - fn test(&mut self) -> Result<()>; - fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; - fn get_mptt_voltage(&mut self) -> anyhow::Result; - fn get_mptt_current(&mut self) -> anyhow::Result; + async fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; + async fn pump_current(&mut self, plant: usize) -> Result; + async fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; + async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result; + async fn general_fault(&mut self, enable: bool); + async fn test(&mut self) -> Result<()>; + async fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; + async fn get_mptt_voltage(&mut self) -> anyhow::Result; + async fn get_mptt_current(&mut self) -> anyhow::Result; } impl dyn BoardInteraction<'_> { //the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons - fn _progress(&mut self, counter: u32) { + async fn _progress(&mut self, counter: u32) { let even = counter % 2 == 0; let current = counter / (PLANT_COUNT as u32); for led in 0..PLANT_COUNT { @@ -177,7 +181,7 @@ impl PlantHal { Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) } - pub fn create() -> Result>> { + pub fn create() -> Result>> { let peripherals = Peripherals::take()?; let sys_loop = EspSystemEventLoop::take()?; let nvs = EspDefaultNvsPartition::take()?; diff --git a/rust/src/hal/rtc.rs b/rust/src/hal/rtc.rs index 5532b1c..9175e59 100644 --- a/rust/src/hal/rtc.rs +++ b/rust/src/hal/rtc.rs @@ -1,4 +1,7 @@ +use crate::hal::Box; +use alloc::vec::Vec; use anyhow::{anyhow, bail}; +use async_trait::async_trait; use bincode::config::Configuration; use bincode::{config, Decode, Encode}; use chrono::{DateTime, Utc}; @@ -7,23 +10,20 @@ use eeprom24x::addr_size::TwoBytes; use eeprom24x::page_size::B32; use eeprom24x::unique_serial::No; use eeprom24x::Storage; -use embedded_hal_bus::i2c::MutexDevice; use embedded_storage::ReadStorage as embedded_storage_ReadStorage; use embedded_storage::Storage as embedded_storage_Storage; -use esp_idf_hal::delay::Delay; -use esp_idf_hal::i2c::I2cDriver; use serde::{Deserialize, Serialize}; -use std::result::Result::Ok as OkStd; const X25: crc::Crc = crc::Crc::::new(&crc::CRC_16_IBM_SDLC); const CONFIG: Configuration = config::standard(); +#[async_trait] pub trait RTCModuleInteraction { - fn get_backup_info(&mut self) -> anyhow::Result; - fn get_backup_config(&mut self) -> anyhow::Result>; - fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; - fn get_rtc_time(&mut self) -> anyhow::Result>; - fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()>; + async fn get_backup_info(&mut self) -> anyhow::Result; + async fn get_backup_config(&mut self) -> anyhow::Result>; + async fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; + async fn get_rtc_time(&mut self) -> anyhow::Result>; + async fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()>; } const BACKUP_HEADER_MAX_SIZE: usize = 64; @@ -97,7 +97,8 @@ impl RTCModuleInteraction for DS3231Module<'_> { let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; log::info!( "Raw header is {:?} with size {}", - header_page_buffer, encoded + header_page_buffer, + encoded ); self.storage .write(0, &header_page_buffer) diff --git a/rust/src/hal/v4_sensor.rs b/rust/src/hal/v4_sensor.rs index 4b1c331..6c5ea0f 100644 --- a/rust/src/hal/v4_sensor.rs +++ b/rust/src/hal/v4_sensor.rs @@ -1,3 +1,5 @@ +use crate::hal::Sensor; +use crate::log::{log, LogMessage}; use alloc::string::ToString; use embedded_hal_bus::i2c::MutexDevice; use esp_idf_hal::can::CanDriver; @@ -5,13 +7,11 @@ use esp_idf_hal::delay::Delay; use esp_idf_hal::i2c::I2cDriver; use esp_idf_hal::pcnt::PcntDriver; use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; -use crate::hal::Sensor; -use crate::log::{log, LogMessage}; const REPEAT_MOIST_MEASURE: usize = 10; pub trait SensorInteraction { - fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result; + async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result; } const MS0: u8 = 1_u8; @@ -22,19 +22,23 @@ const MS4: u8 = 2_u8; const SENSOR_ON: u8 = 5_u8; pub enum SensorImpl<'a> { - PulseCounter{ + PulseCounter { signal_counter: PcntDriver<'a>, sensor_expander: Pca9535Immediate>>, }, - CanBus{ - can: CanDriver<'a> - } + CanBus { + can: CanDriver<'a>, + }, } impl SensorInteraction for SensorImpl<'_> { fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result { match self { - SensorImpl::PulseCounter { signal_counter, sensor_expander, .. } => { + SensorImpl::PulseCounter { + signal_counter, + sensor_expander, + .. + } => { let mut results = [0_f32; REPEAT_MOIST_MEASURE]; for repeat in 0..REPEAT_MOIST_MEASURE { signal_counter.counter_pause()?; @@ -71,8 +75,7 @@ impl SensorInteraction for SensorImpl<'_> { } sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?; - sensor_expander - .pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; + sensor_expander.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; let delay = Delay::new_default(); let measurement = 100; // TODO what is this scaling factor? what is its purpose? @@ -84,8 +87,7 @@ impl SensorInteraction for SensorImpl<'_> { delay.delay_ms(measurement); signal_counter.counter_pause()?; sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; - sensor_expander - .pin_set_low(GPIOBank::Bank0, SENSOR_ON)?; + sensor_expander.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?; sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?; sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?; sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?; diff --git a/rust/src/hal/water.rs b/rust/src/hal/water.rs index 52ba5f1..722d2e3 100644 --- a/rust/src/hal/water.rs +++ b/rust/src/hal/water.rs @@ -102,7 +102,7 @@ impl<'a> TankSensor<'a> { self.get_flow_meter_value() } - pub fn water_temperature_c(&mut self) -> anyhow::Result { + pub async fn water_temperature_c(&mut self) -> anyhow::Result { //multisample should be moved to water_temperature_c let mut attempt = 1; let water_temp: Result = loop { @@ -124,7 +124,7 @@ impl<'a> TankSensor<'a> { water_temp } - fn single_temperature_c(&mut self) -> anyhow::Result { + async fn single_temperature_c(&mut self) -> anyhow::Result { self.one_wire_bus .reset(&mut self.delay) .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; @@ -152,7 +152,7 @@ impl<'a> TankSensor<'a> { anyhow::Ok(sensor_data.temperature / 10_f32) } - pub fn tank_sensor_voltage(&mut self) -> anyhow::Result { + pub async fn tank_sensor_voltage(&mut self) -> anyhow::Result { self.tank_power.set_high()?; //let stabilize self.delay.delay_ms(100); diff --git a/rust/src/main.rs b/rust/src/main.rs index 340ff37..5f90013 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -11,23 +11,28 @@ use crate::{ hal::{PlantHal, HAL, PLANT_COUNT}, //webserver::httpd, }; -use core::sync::atomic::Ordering; +use ::log::{error, info, warn}; use alloc::borrow::ToOwned; -use alloc::sync::Arc; -use alloc::fmt::format; use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::{format, vec}; use anyhow::{bail, Context}; use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono_tz::Tz::{self, UTC}; +use core::sync::atomic::Ordering; use embassy_executor::Spawner; -use hal::battery::BatteryState; -use esp_hal::{timer::systimer::SystemTimer, clock::CpuClock, delay::Delay}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::lazy_lock::LazyLock; +use embassy_sync::mutex::Mutex; +use embassy_sync::mutex::MutexGuard; +use embassy_time::Timer; +use esp_hal::{clock::CpuClock, delay::Delay, timer::systimer::SystemTimer}; use esp_println::logger; +use hal::battery::BatteryState; use log::{log, LogMessage}; use plant_state::PlantState; -use serde::{Deserialize, Serialize}; -use embassy_sync::{Mutex, LazyLock, mutex::MutexGuard}; use portable_atomic::AtomicBool; +use serde::{Deserialize, Serialize}; use tank::*; mod config; mod hal; @@ -38,8 +43,9 @@ mod tank; extern crate alloc; //mod webserver; -pub static BOARD_ACCESS: LazyLock> = LazyLock::new(|| PlantHal::create().unwrap()); -pub static STAY_ALIVE: LazyLock = LazyLock::new(|| AtomicBool::new(false)); +pub static BOARD_ACCESS: LazyLock> = + LazyLock::new(|| PlantHal::create().unwrap()); +pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false); #[derive(Serialize, Deserialize, Debug, PartialEq)] enum WaitType { @@ -55,11 +61,11 @@ struct Solar { } impl WaitType { - fn blink_pattern(&self) -> u32 { + fn blink_pattern(&self) -> u64 { match self { - WaitType::MissingConfig => 500_u32, - WaitType::ConfigButton => 100_u32, - WaitType::MqttConfig => 200_u32, + WaitType::MissingConfig => 500_u64, + WaitType::ConfigButton => 100_u64, + WaitType::MqttConfig => 200_u64, } } } @@ -116,14 +122,13 @@ enum NetworkMode { OFFLINE, } -fn safe_main() -> anyhow::Result<()> { - - log::info!("Startup Rust"); +async fn safe_main() -> anyhow::Result<()> { + info!("Startup Rust"); let mut to_config = false; let version = get_version(); - log::info!( + info!( "Version using git has {} build on {}", version.git_hash, version.build_time ); @@ -133,41 +138,42 @@ fn safe_main() -> anyhow::Result<()> { //log::info!("Partition count is {}", count); //let mut ota_state: esp_ota_img_states_t = 0; //let running_partition = unsafe { esp_ota_get_running_partition() }; - //let address = unsafe { (*running_partition).address }; + //let partition_address = unsafe { (*running_partition).address }; //log::info!("Partition address is {}", address); + let partition_address = 0x1337; // TODO //let ota_state_string = unsafe { - //esp_ota_get_state_partition(running_partition, &mut ota_state); - //if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_NEW { - //"ESP_OTA_IMG_NEW" - //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY { - //"ESP_OTA_IMG_PENDING_VERIFY" - //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_VALID { - //"ESP_OTA_IMG_VALID" - //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_INVALID { - //"ESP_OTA_IMG_INVALID" - //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_ABORTED { - //"ESP_OTA_IMG_ABORTED" - //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED { - //"ESP_OTA_IMG_UNDEFINED" - //} else { - //&format!("unknown {ota_state}") - //} + //esp_ota_get_state_partition(running_partition, &mut ota_state); + //if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_NEW { + //"ESP_OTA_IMG_NEW" + //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY { + //"ESP_OTA_IMG_PENDING_VERIFY" + //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_VALID { + //"ESP_OTA_IMG_VALID" + //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_INVALID { + //"ESP_OTA_IMG_INVALID" + //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_ABORTED { + //"ESP_OTA_IMG_ABORTED" + //} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED { + //"ESP_OTA_IMG_UNDEFINED" + //} else { + //&format!("unknown {ota_state}") + //} //}; //log(LogMessage::PartitionState, 0, 0, "", ota_state_string); + let ota_state_string = "unknown"; - let mut board = BOARD_ACCESS - .lock() - .expect("Could not lock board no other lock should be able to exist during startup!"); - board.board_hal.general_fault(false); + let mut board = BOARD_ACCESS.get().lock().await; + board.board_hal.general_fault(false).await; let cur = board .board_hal .get_rtc_module() .get_rtc_time() + .await .or_else(|err| { - log::info!("rtc module error: {:?}", err); + info!("rtc module error: {:?}", err); board.board_hal.general_fault(true); board.board_hal.get_esp().time() }) @@ -182,35 +188,35 @@ fn safe_main() -> anyhow::Result<()> { log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); } - log::info!("cur is {}", cur); - update_charge_indicator(&mut board); + info!("cur is {}", cur); + update_charge_indicator().await; if board.board_hal.get_esp().get_restart_to_conf() { log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); for _i in 0..2 { - board.board_hal.general_fault(true); - Delay::new_default().delay_ms(100); - board.board_hal.general_fault(false); - Delay::new_default().delay_ms(100); + board.board_hal.general_fault(true).await; + Timer::after_millis(100).await; + board.board_hal.general_fault(false).await; + Timer::after_millis(100).await; } to_config = true; - board.board_hal.general_fault(true); + board.board_hal.general_fault(true).await; board.board_hal.get_esp().set_restart_to_conf(false); } else if board.board_hal.get_esp().mode_override_pressed() { - board.board_hal.general_fault(true); + board.board_hal.general_fault(true).await; log(LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); for _i in 0..5 { - board.board_hal.general_fault(true); - Delay::new_default().delay_ms(100); - board.board_hal.general_fault(false); - Delay::new_default().delay_ms(100); + board.board_hal.general_fault(true).await; + Timer::after_millis(100).await; + board.board_hal.general_fault(false).await; + Timer::after_millis(100).await; } if board.board_hal.get_esp().mode_override_pressed() { - board.board_hal.general_fault(true); + board.board_hal.general_fault(true).await; to_config = true; } else { - board.board_hal.general_fault(false); + board.board_hal.general_fault(false).await; } } @@ -222,39 +228,39 @@ fn safe_main() -> anyhow::Result<()> { let reboot_now = Arc::new(AtomicBool::new(false)); //TODO //let _webserver = httpd(reboot_now.clone()); - wait_infinity(WaitType::MissingConfig, reboot_now.clone()); + wait_infinity(WaitType::MissingConfig, reboot_now.clone()).await; } - log::info!("attempting to connect wifi"); + info!("attempting to connect wifi"); let network_mode = if board.board_hal.get_config().network.ssid.is_some() { - try_connect_wifi_sntp_mqtt(&mut board) + try_connect_wifi_sntp_mqtt().await } else { - log::info!("No wifi configured"); + info!("No wifi configured"); //the current sensors require this amount to stabilize, in case of wifi this is already handles for sure; - board.board_hal.get_esp().delay.delay_ms(100); + Timer::after_millis(100).await; NetworkMode::OFFLINE }; if matches!(network_mode, NetworkMode::OFFLINE) && to_config { - log::info!("Could not connect to station and config mode forced, switching to ap mode!"); - match board.board_hal.get_esp().wifi_ap() { + info!("Could not connect to station and config mode forced, switching to ap mode!"); + match board.board_hal.get_esp().wifi_ap().await { Ok(_) => { - log::info!("Started ap, continuing") + info!("Started ap, continuing") } - Err(err) => log::info!("Could not start config override ap mode due to {}", err), + Err(err) => info!("Could not start config override ap mode due to {}", err), } } let timezone = match &board.board_hal.get_config().timezone { Some(tz_str) => tz_str.parse::().unwrap_or_else(|_| { - log::info!("Invalid timezone '{}', falling back to UTC", tz_str); + info!("Invalid timezone '{}', falling back to UTC", tz_str); UTC }), None => UTC, // Fallback to UTC if no timezone is set }; let timezone_time = cur.with_timezone(&timezone); - log::info!( + info!( "Running logic at utc {} and {} {}", cur, timezone.name(), @@ -264,14 +270,14 @@ fn safe_main() -> anyhow::Result<()> { if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { publish_firmware_info( version, - address, + partition_address, ota_state_string, - &mut board, ip_address, timezone_time, - ); - publish_battery_state(&mut board); - let _ = publish_mppt_state(&mut board); + ) + .await; + publish_battery_state().await; + let _ = publish_mppt_state().await; } log( @@ -290,15 +296,16 @@ fn safe_main() -> anyhow::Result<()> { "", ); + drop(board); + if to_config { //check if client or ap mode and init Wi-Fi - log::info!("executing config mode override"); + info!("executing config mode override"); //config upload will trigger reboot! - drop(board); let reboot_now = Arc::new(AtomicBool::new(false)); //TODO //let _webserver = httpd(reboot_now.clone()); - wait_infinity(WaitType::ConfigButton, reboot_now.clone()); + wait_infinity(WaitType::ConfigButton, reboot_now.clone()).await; } else { log(LogMessage::NormalRun, 0, 0, "", ""); } @@ -345,7 +352,7 @@ fn safe_main() -> anyhow::Result<()> { .board_hal .get_tank_sensor() .context("no sensor") - .and_then(|f| f.water_temperature_c()); + .and_then(async |f| f.water_temperature_c().await); if let Ok(res) = water_temp { if res < WATER_FROZEN_THRESH { @@ -353,11 +360,11 @@ fn safe_main() -> anyhow::Result<()> { } } - publish_tank_state(&mut board, &tank_state, &water_temp); + publish_tank_state(&tank_state, &water_temp); let plantstate: [PlantState; PLANT_COUNT] = - core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board)); - publish_plant_states(&mut board, &timezone_time, &plantstate); + core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board).await); + publish_plant_states(&timezone_time, &plantstate).await; let pump_required = plantstate .iter() @@ -387,7 +394,7 @@ fn safe_main() -> anyhow::Result<()> { &(plant_id + 1).to_string(), "", ); - board.board_hal.fault(plant_id, true)?; + board.board_hal.fault(plant_id, true).await?; } log( LogMessage::PumpPlant, @@ -403,12 +410,11 @@ fn safe_main() -> anyhow::Result<()> { board.board_hal.get_esp().last_pump_time(plant_id); //state.active = true; - pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false); + pump_info(plant_id, true, pump_ineffective, 0, 0, 0, false).await; - let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run)?; - board.board_hal.pump(plant_id, false)?; + let result = do_secure_pump(plant_id, plant_config, dry_run).await?; + board.board_hal.pump(plant_id, false).await?; pump_info( - &mut board, plant_id, false, pump_ineffective, @@ -416,7 +422,8 @@ fn safe_main() -> anyhow::Result<()> { result.max_current_ma, result.min_current_ma, result.error, - ); + ) + .await; } else if !state.pump_in_timeout(plant_config, &timezone_time) { // plant does not need to be watered and is not in timeout // -> reset consecutive pump count @@ -433,6 +440,7 @@ fn safe_main() -> anyhow::Result<()> { .board_hal .get_battery_monitor() .state_charge_percent() + .await .unwrap_or(0.); // try to load full battery state if failed the battery state is unknown @@ -440,6 +448,7 @@ fn safe_main() -> anyhow::Result<()> { .board_hal .get_battery_monitor() .get_battery_state() + .await .unwrap_or(BatteryState::Unknown); let mut light_state = LightState { @@ -505,7 +514,7 @@ fn safe_main() -> anyhow::Result<()> { board.board_hal.light(false)?; } - log::info!("Lightstate is {:?}", light_state); + info!("Lightstate is {:?}", light_state); } match serde_json::to_string(&light_state) { @@ -513,10 +522,11 @@ fn safe_main() -> anyhow::Result<()> { let _ = board .board_hal .get_esp() - .mqtt_publish("/light", state.as_bytes()); + .mqtt_publish("/light", state.as_bytes()) + .await; } Err(err) => { - log::info!("Error publishing lightstate {}", err); + info!("Error publishing lightstate {}", err); } }; @@ -526,25 +536,26 @@ fn safe_main() -> anyhow::Result<()> { let _ = board .board_hal .get_esp() - .mqtt_publish("/deepsleep", "low Volt 12h".as_bytes()); + .mqtt_publish("/deepsleep", "low Volt 12h".as_bytes()).await; 12 * 60 } else if is_day { let _ = board .board_hal .get_esp() - .mqtt_publish("/deepsleep", "normal 20m".as_bytes()); + .mqtt_publish("/deepsleep", "normal 20m".as_bytes()).await; 20 } else { let _ = board .board_hal .get_esp() - .mqtt_publish("/deepsleep", "night 1h".as_bytes()); + .mqtt_publish("/deepsleep", "night 1h".as_bytes()).await; 60 }; let _ = board .board_hal .get_esp() - .mqtt_publish("/state", "sleep".as_bytes()); + .mqtt_publish("/state", "sleep".as_bytes()) + .await; //determine next event //is light out of work trigger soon? @@ -556,25 +567,24 @@ fn safe_main() -> anyhow::Result<()> { let stay_alive_mqtt = STAY_ALIVE.load(Ordering::Relaxed); let stay_alive = stay_alive_mqtt; - log::info!("Check stay alive, current state is {}", stay_alive); + info!("Check stay alive, current state is {}", stay_alive); if stay_alive { - log::info!("Go to stay alive move"); + info!("Go to stay alive move"); drop(board); let reboot_now = Arc::new(AtomicBool::new(false)); //TODO //let _webserver = httpd(reboot_now.clone()); - wait_infinity(WaitType::MqttConfig, reboot_now.clone()); + wait_infinity(WaitType::MqttConfig, reboot_now.clone()).await; + } else { + board.board_hal.get_esp().set_restart_to_conf(false); + board + .board_hal + .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); } - board.board_hal.get_esp().set_restart_to_conf(false); - board - .board_hal - .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); - anyhow::Ok(()) } -pub fn do_secure_pump( - board: &mut MutexGuard, +pub async fn do_secure_pump( plant_id: usize, plant_config: &PlantConfig, dry_run: bool, @@ -584,6 +594,7 @@ pub fn do_secure_pump( let mut error = false; let mut first_error = true; let mut pump_time_s = 0; + let board = &mut BOARD_ACCESS.get().lock().await; if !dry_run { board .board_hal @@ -595,8 +606,8 @@ pub fn do_secure_pump( .get_tank_sensor() .unwrap() .start_flow_meter(); - board.board_hal.pump(plant_id, true)?; - Delay::new_default().delay_ms(10); + board.board_hal.pump(plant_id, true).await?; + Timer::after_millis(10).await; for step in 0..plant_config.pump_time_s as usize { let flow_value = board .board_hal @@ -606,16 +617,16 @@ pub fn do_secure_pump( flow_collector[step] = flow_value; let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; - log::info!( + info!( "Flow value is {} ml, limit is {} ml raw sensor {}", flow_value_ml, plant_config.pump_limit_ml, flow_value ); if flow_value_ml > plant_config.pump_limit_ml as f32 { - log::info!("Flow value is reached, stopping"); + info!("Flow value is reached, stopping"); break; } - let current = board.board_hal.pump_current(plant_id); + let current = board.board_hal.pump_current(plant_id).await; match current { Ok(current) => { let current_ma = current.as_milliamperes() as u16; @@ -630,8 +641,8 @@ pub fn do_secure_pump( plant_config.max_pump_current_ma.to_string().as_str(), step.to_string().as_str(), ); - board.board_hal.general_fault(true); - board.board_hal.fault(plant_id, true)?; + board.board_hal.general_fault(true).await; + board.board_hal.fault(plant_id, true).await?; if !plant_config.ignore_current_error { error = true; break; @@ -649,8 +660,8 @@ pub fn do_secure_pump( plant_config.min_pump_current_ma.to_string().as_str(), step.to_string().as_str(), ); - board.board_hal.general_fault(true); - board.board_hal.fault(plant_id, true)?; + board.board_hal.general_fault(true).await; + board.board_hal.fault(plant_id, true).await?; if !plant_config.ignore_current_error { error = true; break; @@ -661,7 +672,7 @@ pub fn do_secure_pump( } Err(err) => { if !plant_config.ignore_current_error { - log::info!("Error getting pump current: {}", err); + info!("Error getting pump current: {}", err); log( LogMessage::PumpMissingSensorCurrent, plant_id as u32, @@ -676,7 +687,7 @@ pub fn do_secure_pump( } } } - Delay::new_default().delay_ms(1000); + Timer::after_millis(1000).await; pump_time_s += 1; } } @@ -687,7 +698,7 @@ pub fn do_secure_pump( .unwrap() .get_flow_meter_value(); let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; - log::info!( + info!( "Final flow value is {} with {} ml", final_flow_value, flow_value_ml ); @@ -703,9 +714,10 @@ pub fn do_secure_pump( }) } -fn update_charge_indicator(board: &mut MutexGuard) { +async fn update_charge_indicator() { + let board = BOARD_ACCESS.get().lock().await; //we have mppt controller, ask it for charging current - if let Ok(current) = board.board_hal.get_mptt_current() { + if let Ok(current) = board.board_hal.get_mptt_current().await { let _ = board .board_hal .set_charge_indicator(current.as_milliamperes() > 20_f64); @@ -723,11 +735,8 @@ fn update_charge_indicator(board: &mut MutexGuard) { } } -fn publish_tank_state( - board: &mut MutexGuard, - tank_state: &TankState, - water_temp: &anyhow::Result, -) { +async fn publish_tank_state(tank_state: &TankState, water_temp: &anyhow::Result) { + let board = &mut BOARD_ACCESS.get().lock().await; match serde_json::to_string( &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp), ) { @@ -738,16 +747,13 @@ fn publish_tank_state( .mqtt_publish("/water", state.as_bytes()); } Err(err) => { - log::info!("Error publishing tankstate {}", err); + info!("Error publishing tankstate {}", err); } }; } -fn publish_plant_states( - board: &mut MutexGuard, - timezone_time: &DateTime, - plantstate: &[PlantState; 8], -) { +async fn publish_plant_states(timezone_time: &DateTime, plantstate: &[PlantState; 8]) { + let board = &mut BOARD_ACCESS.get().lock().await; for (plant_id, (plant_state, plant_conf)) in plantstate .iter() .zip(&board.board_hal.get_config().plants.clone()) @@ -759,37 +765,41 @@ fn publish_plant_states( let _ = board .board_hal .get_esp() - .mqtt_publish(&plant_topic, state.as_bytes()); - //reduce speed as else messages will be dropped - board.board_hal.get_esp().delay.delay_ms(200); + .mqtt_publish(&plant_topic, state.as_bytes()) + .await; + //TODO? reduce speed as else messages will be dropped + Timer::after_millis(200).await } Err(err) => { - log::info!("Error publishing plant state {}", err); + error!("Error publishing plant state {}", err); } }; } } -fn publish_firmware_info( +async fn publish_firmware_info( version: VersionInfo, address: u32, ota_state_string: &str, - board: &mut MutexGuard, ip_address: &String, timezone_time: DateTime, ) { + let board = &mut BOARD_ACCESS.get().lock().await; let _ = board .board_hal .get_esp() - .mqtt_publish("/firmware/address", ip_address.as_bytes()); + .mqtt_publish("/firmware/address", ip_address.as_bytes()) + .await; let _ = board .board_hal .get_esp() - .mqtt_publish("/firmware/githash", version.git_hash.as_bytes()); + .mqtt_publish("/firmware/githash", version.git_hash.as_bytes()) + .await; let _ = board .board_hal .get_esp() - .mqtt_publish("/firmware/buildtime", version.build_time.as_bytes()); + .mqtt_publish("/firmware/buildtime", version.build_time.as_bytes()) + .await; let _ = board.board_hal.get_esp().mqtt_publish( "/firmware/last_online", timezone_time.to_rfc3339().as_bytes(), @@ -797,7 +807,8 @@ fn publish_firmware_info( let _ = board .board_hal .get_esp() - .mqtt_publish("/firmware/ota_state", ota_state_string.as_bytes()); + .mqtt_publish("/firmware/ota_state", ota_state_string.as_bytes()) + .await; let _ = board.board_hal.get_esp().mqtt_publish( "/firmware/partition_address", format!("{:#06x}", address).as_bytes(), @@ -805,34 +816,36 @@ fn publish_firmware_info( let _ = board .board_hal .get_esp() - .mqtt_publish("/state", "online".as_bytes()); + .mqtt_publish("/state", "online".as_bytes()) + .await; } -fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard) -> NetworkMode { +async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { + let board = BOARD_ACCESS.get().lock().await; let nw_conf = &board.board_hal.get_config().network.clone(); - match board.board_hal.get_esp().wifi(nw_conf) { + match board.board_hal.get_esp().wifi(nw_conf).await { Ok(ip_info) => { - let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10) { + let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10).await { Ok(new_time) => { - log::info!("Using time from sntp"); + info!("Using time from sntp"); let _ = board.board_hal.get_rtc_module().set_rtc_time(&new_time); SntpMode::SYNC { current: new_time } } Err(err) => { - log::info!("sntp error: {}", err); - board.board_hal.general_fault(true); + warn!("sntp error: {}", err); + board.board_hal.general_fault(true).await; SntpMode::OFFLINE } }; let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() { let nw_config = &board.board_hal.get_config().network.clone(); - match board.board_hal.get_esp().mqtt(nw_config) { + match board.board_hal.get_esp().mqtt(nw_config).await { Ok(_) => { - log::info!("Mqtt connection ready"); + info!("Mqtt connection ready"); true } Err(err) => { - log::info!("Could not connect mqtt due to {}", err); + warn!("Could not connect mqtt due to {}", err); false } } @@ -846,15 +859,14 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard) -> NetworkMode { } } Err(_) => { - log::info!("Offline mode"); + info!("Offline mode"); board.board_hal.general_fault(true); NetworkMode::OFFLINE } } } -fn pump_info( - board: &mut MutexGuard, +async fn pump_info( plant_id: usize, pump_active: bool, pump_ineffective: bool, @@ -871,24 +883,30 @@ fn pump_info( min_current_ma: min_current_ma, }; let pump_topic = format!("/pump{}", plant_id + 1); + match serde_json::to_string(&pump_info) { Ok(state) => { - let _ = board + let _ = BOARD_ACCESS + .get() + .lock() + .await .board_hal .get_esp() .mqtt_publish(&pump_topic, state.as_bytes()); //reduce speed as else messages will be dropped - Delay::new_default().delay_ms(200); + //TODO maybee not required for low level hal? + Timer::after_millis(200).await; } Err(err) => { - log::info!("Error publishing pump state {}", err); + warn!("Error publishing pump state {}", err); } }; } -fn publish_mppt_state(board: &mut MutexGuard<'_, HAL<'_>>) -> anyhow::Result<()> { - let current = board.board_hal.get_mptt_current()?; - let voltage = board.board_hal.get_mptt_voltage()?; +async fn publish_mppt_state() -> anyhow::Result<()> { + let board_hal = &mut BOARD_ACCESS.get().lock().await.board_hal; + let current = board_hal.get_mptt_current().await?; + let voltage = board_hal.get_mptt_voltage().await?; let solar_state = Solar { current_ma: current.as_milliamperes() as u32, voltage_ma: voltage.as_millivolts() as u32, @@ -896,35 +914,33 @@ fn publish_mppt_state(board: &mut MutexGuard<'_, HAL<'_>>) -> anyhow::Result<()> if let Ok(serialized_solar_state_bytes) = serde_json::to_string(&solar_state).map(|s| s.into_bytes()) { - let _ = board - .board_hal + let _ = board_hal .get_esp() .mqtt_publish("/mppt", &serialized_solar_state_bytes); } Ok(()) } -fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) { +async fn publish_battery_state() -> () { + let board = BOARD_ACCESS.get().lock().await; let state = board.board_hal.get_battery_monitor().get_battery_state(); if let Ok(serialized_battery_state_bytes) = serde_json::to_string(&state).map(|s| s.into_bytes()) { - let _ = board + board .board_hal .get_esp() .mqtt_publish("/battery", &serialized_battery_state_bytes); } } -fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { +async fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { let delay = wait_type.blink_pattern(); let mut led_count = 8; let mut pattern_step = 0; - let delay_handle = Delay::new_default(); loop { - let mut board = BOARD_ACCESS.lock().unwrap(); - update_charge_indicator(&mut board); - + update_charge_indicator().await; + let mut board = BOARD_ACCESS.get().lock().await; match wait_type { WaitType::MissingConfig => { // Keep existing behavior: circular filling pattern @@ -952,9 +968,9 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { board.board_hal.general_fault(true); drop(board); - //cannot use shared delay as that is inside the mutex here - delay_handle.delay_ms(delay); - let mut board = BOARD_ACCESS.lock().unwrap(); + + Timer::after_millis(delay).await; + let mut board = BOARD_ACCESS.get().lock().await; board.board_hal.general_fault(false); // Clear all LEDs @@ -962,17 +978,15 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { let _ = board.board_hal.fault(i, false); } drop(board); - delay_handle.delay_ms(delay); + Timer::after_millis(delay).await; - if wait_type == WaitType::MqttConfig - && !STAY_ALIVE.load(Ordering::Relaxed) - { + if wait_type == WaitType::MqttConfig && !STAY_ALIVE.load(Ordering::Relaxed) { reboot_now.store(true, Ordering::Relaxed); } if reboot_now.load(Ordering::Relaxed) { //ensure clean http answer - Delay::new_default().delay_ms(500); - BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); + Timer::after_millis(500).await; + BOARD_ACCESS.get().lock().await.board_hal.deep_sleep(1); } } } @@ -980,7 +994,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { #[esp_hal_embassy::main] async fn main(spawner: Spawner) { // intialize embassy - esp_log::info::logger::init_logger_from_env(); + logger::init_logger_from_env(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); @@ -991,26 +1005,23 @@ async fn main(spawner: Spawner) { info!("Embassy initialized!"); - let result = safe_main(); + let result = safe_main().await; match result { // this should not get triggered, safe_main should not return but go into deep sleep with sensible // timeout, this is just a fallback Ok(_) => { - log::info!("Main app finished, restarting"); - BOARD_ACCESS - .lock() - .unwrap() - .board_hal - .get_esp() - .set_restart_to_conf(false); - BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); + warn!("Main app finished, but should never do, restarting"); + let board = &mut BOARD_ACCESS.get().lock().await.board_hal; + + board.get_esp().set_restart_to_conf(false); + board.deep_sleep(1); } // if safe_main exists with an error, rollback to a known good ota version Err(err) => { - log::info!("Failed main {}", err); + error!("Failed main {}", err); //TODO //let _rollback_successful = rollback_and_reboot(); - panic!("Failed to rollback :("); + //panic!("Failed to rollback :("); } } } diff --git a/rust/src/plant_state.rs b/rust/src/plant_state.rs index b7993aa..e50d31d 100644 --- a/rust/src/plant_state.rs +++ b/rust/src/plant_state.rs @@ -116,7 +116,7 @@ fn map_range_moisture( } impl PlantState { - pub fn read_hardware_state(plant_id: usize, board: &mut HAL) -> Self { + pub async fn read_hardware_state(plant_id: usize, board: &mut HAL<'_>) -> Self { let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a { match board.board_hal.measure_moisture_hz(plant_id, Sensor::A) { Ok(raw) => match map_range_moisture(