more async migration
This commit is contained in:
		| @@ -141,6 +141,7 @@ embedded-can = "0.4.1" | |||||||
| critical-section = "1.2.0" | critical-section = "1.2.0" | ||||||
| portable-atomic = "1.11.1" | portable-atomic = "1.11.1" | ||||||
| embassy-sync = { version = "0.7.2", features = ["log"] } | embassy-sync = { version = "0.7.2", features = ["log"] } | ||||||
|  | async-trait = "0.1.89" | ||||||
|  |  | ||||||
|  |  | ||||||
| [patch.crates-io] | [patch.crates-io] | ||||||
|   | |||||||
| @@ -1,22 +1,20 @@ | |||||||
| use anyhow::anyhow; | use anyhow::anyhow; | ||||||
| use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; | use async_trait::async_trait; | ||||||
| use embedded_hal_bus::i2c::MutexDevice; |  | ||||||
| use esp_idf_hal::delay::Delay; |  | ||||||
| use esp_idf_hal::i2c::{I2cDriver, I2cError}; |  | ||||||
| use measurements::Temperature; | use measurements::Temperature; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
| pub trait BatteryInteraction { | pub trait BatteryInteraction { | ||||||
|     fn state_charge_percent(&mut self) -> Result<f32, BatteryError>; |     async fn state_charge_percent(&mut self) -> Result<f32, BatteryError>; | ||||||
|     fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; |     async fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; |     async fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; |     async fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError>; |     async fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError>; |     async fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError>; | ||||||
|     fn cycle_count(&mut self) -> Result<u16, BatteryError>; |     async fn cycle_count(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn state_health_percent(&mut self) -> Result<u16, BatteryError>; |     async fn state_health_percent(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn bat_temperature(&mut self) -> Result<u16, BatteryError>; |     async fn bat_temperature(&mut self) -> Result<u16, BatteryError>; | ||||||
|     fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>; |     async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>; | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Serialize)] | #[derive(Debug, Serialize)] | ||||||
|   | |||||||
| @@ -6,7 +6,10 @@ use anyhow::{anyhow, bail, Context}; | |||||||
| use chrono::{DateTime, Utc}; | use chrono::{DateTime, Utc}; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
|  |  | ||||||
| use alloc::{vec::Vec, string::{String, ToString}}; | use alloc::{ | ||||||
|  |     string::{String, ToString}, | ||||||
|  |     vec::Vec, | ||||||
|  | }; | ||||||
|  |  | ||||||
| #[link_section = ".rtc.data"] | #[link_section = ".rtc.data"] | ||||||
| static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; | static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; | ||||||
| @@ -39,35 +42,38 @@ pub struct FileSystemSizeInfo { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub struct MqttClient<'a> { | pub struct MqttClient<'a> { | ||||||
|     mqtt_client: EspMqttClient<'a>, |     //mqtt_client: EspMqttClient<'a>, | ||||||
|     base_topic: heapless::String<64>, |     base_topic: heapless::String<64>, | ||||||
| } | } | ||||||
| pub struct Esp<'a> { | pub struct Esp<'a> { | ||||||
|     pub(crate) mqtt_client: Option<MqttClient<'a>>, |     pub(crate) mqtt_client: Option<MqttClient<'a>>, | ||||||
|     pub(crate) wifi_driver: EspWifi<'a>, |     //pub(crate) wifi_driver: EspWifi<'a>, | ||||||
|     pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, |     //pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, | ||||||
|     pub(crate) delay: Delay, |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct AccessPointInfo {} | ||||||
|  |  | ||||||
| impl Esp<'_> { | impl Esp<'_> { | ||||||
|     const SPIFFS_PARTITION_NAME: &'static str = "storage"; |     const SPIFFS_PARTITION_NAME: &'static str = "storage"; | ||||||
|     const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; |     const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; | ||||||
|     const BASE_PATH: &'static str = "/spiffs"; |     const BASE_PATH: &'static str = "/spiffs"; | ||||||
|  |  | ||||||
|     pub(crate) fn mode_override_pressed(&mut self) -> bool { |     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<DateTime<Utc>> { |     pub(crate) async fn sntp(&mut self, max_wait_ms: u32) -> anyhow::Result<DateTime<Utc>> { | ||||||
|         let sntp = sntp::EspSntp::new_default()?; |         //let sntp = sntp::EspSntp::new_default()?; | ||||||
|         let mut counter = 0; |         //let mut counter = 0; | ||||||
|         while sntp.get_sync_status() != SyncStatus::Completed { |         //while sntp.get_sync_status() != SyncStatus::Completed { | ||||||
|             self.delay.delay_ms(100); |         //  self.delay.delay_ms(100); | ||||||
|             counter += 100; |         //    counter += 100; | ||||||
|             if counter > max_wait_ms { |         //    if counter > max_wait_ms { | ||||||
|                 bail!("Reached sntp timeout, aborting") |         //        bail!("Reached sntp timeout, aborting") | ||||||
|             } |         //    } | ||||||
|         } |         //} | ||||||
|         self.time() |         //self.time() | ||||||
|  |         todo!(); | ||||||
|     } |     } | ||||||
|     pub(crate) fn time(&mut self) -> anyhow::Result<DateTime<Utc>> { |     pub(crate) fn time(&mut self) -> anyhow::Result<DateTime<Utc>> { | ||||||
|         let time = EspSystemTime {}.now().as_millis(); |         let time = EspSystemTime {}.now().as_millis(); | ||||||
| @@ -76,7 +82,8 @@ impl Esp<'_> { | |||||||
|             .ok_or(anyhow!("could not convert timestamp"))?; |             .ok_or(anyhow!("could not convert timestamp"))?; | ||||||
|         anyhow::Ok(local_time) |         anyhow::Ok(local_time) | ||||||
|     } |     } | ||||||
|     pub(crate) fn wifi_scan(&mut self) -> anyhow::Result<Vec<AccessPointInfo>> { |  | ||||||
|  |     pub(crate) async fn wifi_scan(&mut self) -> anyhow::Result<Vec<AccessPointInfo>> { | ||||||
|         self.wifi_driver.start_scan( |         self.wifi_driver.start_scan( | ||||||
|             &ScanConfig { |             &ScanConfig { | ||||||
|                 scan_type: ScanType::Passive(Duration::from_secs(5)), |                 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() { |         let ssid = match self.load_config() { | ||||||
|             Ok(config) => config.network.ap_ssid.clone(), |             Ok(config) => config.network.ap_ssid.clone(), | ||||||
|             Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), |             Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), | ||||||
| @@ -146,7 +153,7 @@ impl Esp<'_> { | |||||||
|         anyhow::Ok(()) |         anyhow::Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> { |     pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> { | ||||||
|         let ssid = network_config |         let ssid = network_config | ||||||
|             .ssid |             .ssid | ||||||
|             .clone() |             .clone() | ||||||
| @@ -208,18 +215,21 @@ impl Esp<'_> { | |||||||
|         log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); |         log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); | ||||||
|         anyhow::Ok(address) |         anyhow::Ok(address) | ||||||
|     } |     } | ||||||
|     pub(crate) fn load_config(&mut self) -> anyhow::Result<PlantControllerConfig> { |     pub(crate) async fn load_config(&mut self) -> anyhow::Result<PlantControllerConfig> { | ||||||
|         let cfg = File::open(Self::CONFIG_FILE)?; |         let cfg = File::open(Self::CONFIG_FILE)?; | ||||||
|         let config: PlantControllerConfig = serde_json::from_reader(cfg)?; |         let config: PlantControllerConfig = serde_json::from_reader(cfg)?; | ||||||
|         anyhow::Ok(config) |         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)?; |         let mut cfg = File::create(Self::CONFIG_FILE)?; | ||||||
|         serde_json::to_writer(&mut cfg, &config)?; |         serde_json::to_writer(&mut cfg, &config)?; | ||||||
|         log::info!("Wrote config config {:?}", config); |         log::info!("Wrote config config {:?}", config); | ||||||
|         anyhow::Ok(()) |         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, "", ""); |         log(LogMessage::MountingFilesystem, 0, 0, "", ""); | ||||||
|         let base_path = String::try_from("/spiffs")?; |         let base_path = String::try_from("/spiffs")?; | ||||||
|         let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?; |         let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?; | ||||||
| @@ -247,7 +257,7 @@ impl Esp<'_> { | |||||||
|         ); |         ); | ||||||
|         anyhow::Ok(()) |         anyhow::Ok(()) | ||||||
|     } |     } | ||||||
|     fn file_system_size(&mut self) -> anyhow::Result<FileSystemSizeInfo> { |     async fn file_system_size(&mut self) -> anyhow::Result<FileSystemSizeInfo> { | ||||||
|         let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?; |         let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?; | ||||||
|         let mut total_size = 0; |         let mut total_size = 0; | ||||||
|         let mut used_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 storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); | ||||||
|  |  | ||||||
|         let mut file_system_corrupt = None; |         let mut file_system_corrupt = None; | ||||||
| @@ -312,7 +322,7 @@ impl Esp<'_> { | |||||||
|             iter_error, |             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)); |         let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); | ||||||
|         match fs::remove_file(filepath) { |         match fs::remove_file(filepath) { | ||||||
|             OkStd(_) => anyhow::Ok(()), |             OkStd(_) => anyhow::Ok(()), | ||||||
| @@ -321,7 +331,11 @@ impl Esp<'_> { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub(crate) fn get_file_handle(&self, filename: &str, write: bool) -> anyhow::Result<File> { |     pub(crate) async fn get_file_handle( | ||||||
|  |         &self, | ||||||
|  |         filename: &str, | ||||||
|  |         write: bool, | ||||||
|  |     ) -> anyhow::Result<File> { | ||||||
|         let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); |         let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); | ||||||
|         anyhow::Ok(if write { |         anyhow::Ok(if write { | ||||||
|             File::create(filepath)? |             File::create(filepath)? | ||||||
| @@ -361,20 +375,22 @@ impl Esp<'_> { | |||||||
|                 for i in 0..PLANT_COUNT { |                 for i in 0..PLANT_COUNT { | ||||||
|                     log::info!( |                     log::info!( | ||||||
|                         "LAST_WATERING_TIMESTAMP[{}] = UTC {}", |                         "LAST_WATERING_TIMESTAMP[{}] = UTC {}", | ||||||
|                         i, LAST_WATERING_TIMESTAMP[i] |                         i, | ||||||
|  |                         LAST_WATERING_TIMESTAMP[i] | ||||||
|                     ); |                     ); | ||||||
|                 } |                 } | ||||||
|                 for i in 0..PLANT_COUNT { |                 for i in 0..PLANT_COUNT { | ||||||
|                     log::info!( |                     log::info!( | ||||||
|                         "CONSECUTIVE_WATERING_PLANT[{}] = {}", |                         "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 |         let base_topic = network_config | ||||||
|             .base_topic |             .base_topic | ||||||
|             .as_ref() |             .as_ref() | ||||||
| @@ -491,7 +507,9 @@ impl Esp<'_> { | |||||||
|                     log::info!("Mqtt connection callback received, progressing"); |                     log::info!("Mqtt connection callback received, progressing"); | ||||||
|                     match mqtt_connected_event_ok.load(std::sync::atomic::Ordering::Relaxed) { |                     match mqtt_connected_event_ok.load(std::sync::atomic::Ordering::Relaxed) { | ||||||
|                         true => { |                         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 |                             //subscribe to roundtrip | ||||||
|                             client.subscribe(round_trip_topic.as_str(), ExactlyOnce)?; |                             client.subscribe(round_trip_topic.as_str(), ExactlyOnce)?; | ||||||
|                             client.subscribe(stay_alive_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"); |         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() { |         if self.mqtt_client.is_none() { | ||||||
|             return anyhow::Ok(()); |             return anyhow::Ok(()); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -4,9 +4,10 @@ mod initial_hal; | |||||||
| mod rtc; | mod rtc; | ||||||
| mod v3_hal; | mod v3_hal; | ||||||
| mod v4_hal; | mod v4_hal; | ||||||
| mod water; |  | ||||||
| mod v4_sensor; | mod v4_sensor; | ||||||
|  | mod water; | ||||||
|  |  | ||||||
|  | use crate::alloc::string::ToString; | ||||||
| use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; | use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; | ||||||
| use crate::hal::water::TankSensor; | use crate::hal::water::TankSensor; | ||||||
| use crate::{ | use crate::{ | ||||||
| @@ -19,11 +20,16 @@ use crate::{ | |||||||
| }; | }; | ||||||
| use alloc::boxed::Box; | use alloc::boxed::Box; | ||||||
| use anyhow::{Ok, Result}; | use anyhow::{Ok, Result}; | ||||||
|  | use async_trait::async_trait; | ||||||
| use battery::BQ34Z100G1; | use battery::BQ34Z100G1; | ||||||
| use bq34z100::Bq34z100g1Driver; | use bq34z100::Bq34z100g1Driver; | ||||||
| use ds323x::{DateTimeAccess, Ds323x}; | use ds323x::{DateTimeAccess, Ds323x}; | ||||||
| use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; | 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 embedded_hal_bus::i2c::MutexDevice; | ||||||
|  | use esp_idf_hal::can::CAN; | ||||||
| use esp_idf_hal::pcnt::PCNT1; | use esp_idf_hal::pcnt::PCNT1; | ||||||
| use esp_idf_hal::{ | use esp_idf_hal::{ | ||||||
|     adc::ADC1, |     adc::ADC1, | ||||||
| @@ -46,9 +52,6 @@ use esp_idf_sys::{ | |||||||
| }; | }; | ||||||
| use esp_ota::mark_app_valid; | use esp_ota::mark_app_valid; | ||||||
| use measurements::{Current, Voltage}; | 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! | //Only support for 8 right now! | ||||||
| pub const PLANT_COUNT: usize = 8; | pub const PLANT_COUNT: usize = 8; | ||||||
| @@ -87,8 +90,9 @@ pub struct HAL<'a> { | |||||||
|     pub board_hal: Box<dyn BoardInteraction<'a> + Send>, |     pub board_hal: Box<dyn BoardInteraction<'a> + Send>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
| pub trait BoardInteraction<'a> { | 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_esp(&mut self) -> &mut Esp<'a>; | ||||||
|     fn get_config(&mut self) -> &PlantControllerConfig; |     fn get_config(&mut self) -> &PlantControllerConfig; | ||||||
|     fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>; |     fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>; | ||||||
| @@ -99,20 +103,20 @@ pub trait BoardInteraction<'a> { | |||||||
|     fn is_day(&self) -> bool; |     fn is_day(&self) -> bool; | ||||||
|     //should be multsampled |     //should be multsampled | ||||||
|     fn light(&mut self, enable: bool) -> Result<()>; |     fn light(&mut self, enable: bool) -> Result<()>; | ||||||
|     fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; |     async fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||||
|     fn pump_current(&mut self, plant: usize) -> Result<Current>; |     async fn pump_current(&mut self, plant: usize) -> Result<Current>; | ||||||
|     fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; |     async fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||||
|     fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>; |     async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>; | ||||||
|     fn general_fault(&mut self, enable: bool); |     async fn general_fault(&mut self, enable: bool); | ||||||
|     fn test(&mut self) -> Result<()>; |     async fn test(&mut self) -> Result<()>; | ||||||
|     fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; |     async fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; | ||||||
|     fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>; |     async fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>; | ||||||
|     fn get_mptt_current(&mut self) -> anyhow::Result<Current>; |     async fn get_mptt_current(&mut self) -> anyhow::Result<Current>; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl dyn BoardInteraction<'_> { | 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 |     //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 even = counter % 2 == 0; | ||||||
|         let current = counter / (PLANT_COUNT as u32); |         let current = counter / (PLANT_COUNT as u32); | ||||||
|         for led in 0..PLANT_COUNT { |         for led in 0..PLANT_COUNT { | ||||||
| @@ -177,7 +181,7 @@ impl PlantHal { | |||||||
|         Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) |         Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn create() -> Result<Mutex<HAL<'static>>> { |     pub fn create() -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>> { | ||||||
|         let peripherals = Peripherals::take()?; |         let peripherals = Peripherals::take()?; | ||||||
|         let sys_loop = EspSystemEventLoop::take()?; |         let sys_loop = EspSystemEventLoop::take()?; | ||||||
|         let nvs = EspDefaultNvsPartition::take()?; |         let nvs = EspDefaultNvsPartition::take()?; | ||||||
|   | |||||||
| @@ -1,4 +1,7 @@ | |||||||
|  | use crate::hal::Box; | ||||||
|  | use alloc::vec::Vec; | ||||||
| use anyhow::{anyhow, bail}; | use anyhow::{anyhow, bail}; | ||||||
|  | use async_trait::async_trait; | ||||||
| use bincode::config::Configuration; | use bincode::config::Configuration; | ||||||
| use bincode::{config, Decode, Encode}; | use bincode::{config, Decode, Encode}; | ||||||
| use chrono::{DateTime, Utc}; | use chrono::{DateTime, Utc}; | ||||||
| @@ -7,23 +10,20 @@ use eeprom24x::addr_size::TwoBytes; | |||||||
| use eeprom24x::page_size::B32; | use eeprom24x::page_size::B32; | ||||||
| use eeprom24x::unique_serial::No; | use eeprom24x::unique_serial::No; | ||||||
| use eeprom24x::Storage; | use eeprom24x::Storage; | ||||||
| use embedded_hal_bus::i2c::MutexDevice; |  | ||||||
| use embedded_storage::ReadStorage as embedded_storage_ReadStorage; | use embedded_storage::ReadStorage as embedded_storage_ReadStorage; | ||||||
| use embedded_storage::Storage as embedded_storage_Storage; | 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 serde::{Deserialize, Serialize}; | ||||||
| use std::result::Result::Ok as OkStd; |  | ||||||
|  |  | ||||||
| const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | ||||||
| const CONFIG: Configuration = config::standard(); | const CONFIG: Configuration = config::standard(); | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
| pub trait RTCModuleInteraction { | pub trait RTCModuleInteraction { | ||||||
|     fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader>; |     async fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader>; | ||||||
|     fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>>; |     async fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>>; | ||||||
|     fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; |     async fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; | ||||||
|     fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>>; |     async fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>>; | ||||||
|     fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()>; |     async fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()>; | ||||||
| } | } | ||||||
|  |  | ||||||
| const BACKUP_HEADER_MAX_SIZE: usize = 64; | 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)?; |         let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; | ||||||
|         log::info!( |         log::info!( | ||||||
|             "Raw header is {:?} with size {}", |             "Raw header is {:?} with size {}", | ||||||
|             header_page_buffer, encoded |             header_page_buffer, | ||||||
|  |             encoded | ||||||
|         ); |         ); | ||||||
|         self.storage |         self.storage | ||||||
|             .write(0, &header_page_buffer) |             .write(0, &header_page_buffer) | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | use crate::hal::Sensor; | ||||||
|  | use crate::log::{log, LogMessage}; | ||||||
| use alloc::string::ToString; | use alloc::string::ToString; | ||||||
| use embedded_hal_bus::i2c::MutexDevice; | use embedded_hal_bus::i2c::MutexDevice; | ||||||
| use esp_idf_hal::can::CanDriver; | 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::i2c::I2cDriver; | ||||||
| use esp_idf_hal::pcnt::PcntDriver; | use esp_idf_hal::pcnt::PcntDriver; | ||||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||||
| use crate::hal::Sensor; |  | ||||||
| use crate::log::{log, LogMessage}; |  | ||||||
|  |  | ||||||
| const REPEAT_MOIST_MEASURE: usize = 10; | const REPEAT_MOIST_MEASURE: usize = 10; | ||||||
|  |  | ||||||
| pub trait SensorInteraction { | pub trait SensorInteraction { | ||||||
|     fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32>; |     async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32>; | ||||||
| } | } | ||||||
|  |  | ||||||
| const MS0: u8 = 1_u8; | const MS0: u8 = 1_u8; | ||||||
| @@ -27,14 +27,18 @@ pub enum SensorImpl<'a> { | |||||||
|         sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, |         sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, | ||||||
|     }, |     }, | ||||||
|     CanBus { |     CanBus { | ||||||
|         can: CanDriver<'a> |         can: CanDriver<'a>, | ||||||
|     } |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl SensorInteraction for SensorImpl<'_> { | impl SensorInteraction for SensorImpl<'_> { | ||||||
|     fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> { |     fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> { | ||||||
|         match self { |         match self { | ||||||
|             SensorImpl::PulseCounter { signal_counter, sensor_expander, .. } => { |             SensorImpl::PulseCounter { | ||||||
|  |                 signal_counter, | ||||||
|  |                 sensor_expander, | ||||||
|  |                 .. | ||||||
|  |             } => { | ||||||
|                 let mut results = [0_f32; REPEAT_MOIST_MEASURE]; |                 let mut results = [0_f32; REPEAT_MOIST_MEASURE]; | ||||||
|                 for repeat in 0..REPEAT_MOIST_MEASURE { |                 for repeat in 0..REPEAT_MOIST_MEASURE { | ||||||
|                     signal_counter.counter_pause()?; |                     signal_counter.counter_pause()?; | ||||||
| @@ -71,8 +75,7 @@ impl SensorInteraction for SensorImpl<'_> { | |||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?; |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?; | ||||||
|                     sensor_expander |                     sensor_expander.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; | ||||||
|                         .pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; |  | ||||||
|  |  | ||||||
|                     let delay = Delay::new_default(); |                     let delay = Delay::new_default(); | ||||||
|                     let measurement = 100; // TODO what is this scaling factor? what is its purpose? |                     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); |                     delay.delay_ms(measurement); | ||||||
|                     signal_counter.counter_pause()?; |                     signal_counter.counter_pause()?; | ||||||
|                     sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; |                     sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; | ||||||
|                     sensor_expander |                     sensor_expander.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?; | ||||||
|                         .pin_set_low(GPIOBank::Bank0, SENSOR_ON)?; |  | ||||||
|                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?; |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?; | ||||||
|                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?; |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?; | ||||||
|                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?; |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?; | ||||||
|   | |||||||
| @@ -102,7 +102,7 @@ impl<'a> TankSensor<'a> { | |||||||
|         self.get_flow_meter_value() |         self.get_flow_meter_value() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn water_temperature_c(&mut self) -> anyhow::Result<f32> { |     pub async fn water_temperature_c(&mut self) -> anyhow::Result<f32> { | ||||||
|         //multisample should be moved to water_temperature_c |         //multisample should be moved to water_temperature_c | ||||||
|         let mut attempt = 1; |         let mut attempt = 1; | ||||||
|         let water_temp: Result<f32, anyhow::Error> = loop { |         let water_temp: Result<f32, anyhow::Error> = loop { | ||||||
| @@ -124,7 +124,7 @@ impl<'a> TankSensor<'a> { | |||||||
|         water_temp |         water_temp | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn single_temperature_c(&mut self) -> anyhow::Result<f32> { |     async fn single_temperature_c(&mut self) -> anyhow::Result<f32> { | ||||||
|         self.one_wire_bus |         self.one_wire_bus | ||||||
|             .reset(&mut self.delay) |             .reset(&mut self.delay) | ||||||
|             .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; |             .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; | ||||||
| @@ -152,7 +152,7 @@ impl<'a> TankSensor<'a> { | |||||||
|         anyhow::Ok(sensor_data.temperature / 10_f32) |         anyhow::Ok(sensor_data.temperature / 10_f32) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn tank_sensor_voltage(&mut self) -> anyhow::Result<f32> { |     pub async fn tank_sensor_voltage(&mut self) -> anyhow::Result<f32> { | ||||||
|         self.tank_power.set_high()?; |         self.tank_power.set_high()?; | ||||||
|         //let stabilize |         //let stabilize | ||||||
|         self.delay.delay_ms(100); |         self.delay.delay_ms(100); | ||||||
|   | |||||||
							
								
								
									
										339
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										339
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -11,23 +11,28 @@ use crate::{ | |||||||
|     hal::{PlantHal, HAL, PLANT_COUNT}, |     hal::{PlantHal, HAL, PLANT_COUNT}, | ||||||
|     //webserver::httpd, |     //webserver::httpd, | ||||||
| }; | }; | ||||||
| use core::sync::atomic::Ordering; | use ::log::{error, info, warn}; | ||||||
| use alloc::borrow::ToOwned; | use alloc::borrow::ToOwned; | ||||||
| use alloc::sync::Arc; |  | ||||||
| use alloc::fmt::format; |  | ||||||
| use alloc::string::{String, ToString}; | use alloc::string::{String, ToString}; | ||||||
|  | use alloc::sync::Arc; | ||||||
|  | use alloc::{format, vec}; | ||||||
| use anyhow::{bail, Context}; | use anyhow::{bail, Context}; | ||||||
| use chrono::{DateTime, Datelike, Timelike, Utc}; | use chrono::{DateTime, Datelike, Timelike, Utc}; | ||||||
| use chrono_tz::Tz::{self, UTC}; | use chrono_tz::Tz::{self, UTC}; | ||||||
|  | use core::sync::atomic::Ordering; | ||||||
| use embassy_executor::Spawner; | use embassy_executor::Spawner; | ||||||
| use hal::battery::BatteryState; | use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||||||
| use esp_hal::{timer::systimer::SystemTimer, clock::CpuClock, delay::Delay}; | 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 esp_println::logger; | ||||||
|  | use hal::battery::BatteryState; | ||||||
| use log::{log, LogMessage}; | use log::{log, LogMessage}; | ||||||
| use plant_state::PlantState; | use plant_state::PlantState; | ||||||
| use serde::{Deserialize, Serialize}; |  | ||||||
| use embassy_sync::{Mutex, LazyLock, mutex::MutexGuard}; |  | ||||||
| use portable_atomic::AtomicBool; | use portable_atomic::AtomicBool; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| use tank::*; | use tank::*; | ||||||
| mod config; | mod config; | ||||||
| mod hal; | mod hal; | ||||||
| @@ -38,8 +43,9 @@ mod tank; | |||||||
| extern crate alloc; | extern crate alloc; | ||||||
| //mod webserver; | //mod webserver; | ||||||
|  |  | ||||||
| pub static BOARD_ACCESS: LazyLock<Mutex<HAL>> = LazyLock::new(|| PlantHal::create().unwrap()); | pub static BOARD_ACCESS: LazyLock<Mutex<CriticalSectionRawMutex, HAL>> = | ||||||
| pub static STAY_ALIVE: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBool::new(false)); |     LazyLock::new(|| PlantHal::create().unwrap()); | ||||||
|  | pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false); | ||||||
|  |  | ||||||
| #[derive(Serialize, Deserialize, Debug, PartialEq)] | #[derive(Serialize, Deserialize, Debug, PartialEq)] | ||||||
| enum WaitType { | enum WaitType { | ||||||
| @@ -55,11 +61,11 @@ struct Solar { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl WaitType { | impl WaitType { | ||||||
|     fn blink_pattern(&self) -> u32 { |     fn blink_pattern(&self) -> u64 { | ||||||
|         match self { |         match self { | ||||||
|             WaitType::MissingConfig => 500_u32, |             WaitType::MissingConfig => 500_u64, | ||||||
|             WaitType::ConfigButton => 100_u32, |             WaitType::ConfigButton => 100_u64, | ||||||
|             WaitType::MqttConfig => 200_u32, |             WaitType::MqttConfig => 200_u64, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -116,14 +122,13 @@ enum NetworkMode { | |||||||
|     OFFLINE, |     OFFLINE, | ||||||
| } | } | ||||||
|  |  | ||||||
| fn safe_main() -> anyhow::Result<()> { | async fn safe_main() -> anyhow::Result<()> { | ||||||
|  |     info!("Startup Rust"); | ||||||
|     log::info!("Startup Rust"); |  | ||||||
|  |  | ||||||
|     let mut to_config = false; |     let mut to_config = false; | ||||||
|  |  | ||||||
|     let version = get_version(); |     let version = get_version(); | ||||||
|     log::info!( |     info!( | ||||||
|         "Version using git has {} build on {}", |         "Version using git has {} build on {}", | ||||||
|         version.git_hash, version.build_time |         version.git_hash, version.build_time | ||||||
|     ); |     ); | ||||||
| @@ -133,8 +138,9 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|     //log::info!("Partition count is {}", count); |     //log::info!("Partition count is {}", count); | ||||||
|     //let mut ota_state: esp_ota_img_states_t = 0; |     //let mut ota_state: esp_ota_img_states_t = 0; | ||||||
|     //let running_partition = unsafe { esp_ota_get_running_partition() }; |     //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); |     //log::info!("Partition address is {}", address); | ||||||
|  |     let partition_address = 0x1337; | ||||||
|  |  | ||||||
|     // TODO |     // TODO | ||||||
|     //let ota_state_string = unsafe { |     //let ota_state_string = unsafe { | ||||||
| @@ -156,18 +162,18 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|     //} |     //} | ||||||
|     //}; |     //}; | ||||||
|     //log(LogMessage::PartitionState, 0, 0, "", ota_state_string); |     //log(LogMessage::PartitionState, 0, 0, "", ota_state_string); | ||||||
|  |     let ota_state_string = "unknown"; | ||||||
|  |  | ||||||
|     let mut board = BOARD_ACCESS |     let mut board = BOARD_ACCESS.get().lock().await; | ||||||
|         .lock() |     board.board_hal.general_fault(false).await; | ||||||
|         .expect("Could not lock board no other lock should be able to exist during startup!"); |  | ||||||
|     board.board_hal.general_fault(false); |  | ||||||
|  |  | ||||||
|     let cur = board |     let cur = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_rtc_module() |         .get_rtc_module() | ||||||
|         .get_rtc_time() |         .get_rtc_time() | ||||||
|  |         .await | ||||||
|         .or_else(|err| { |         .or_else(|err| { | ||||||
|             log::info!("rtc module error: {:?}", err); |             info!("rtc module error: {:?}", err); | ||||||
|             board.board_hal.general_fault(true); |             board.board_hal.general_fault(true); | ||||||
|             board.board_hal.get_esp().time() |             board.board_hal.get_esp().time() | ||||||
|         }) |         }) | ||||||
| @@ -182,35 +188,35 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|         log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); |         log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     log::info!("cur is {}", cur); |     info!("cur is {}", cur); | ||||||
|     update_charge_indicator(&mut board); |     update_charge_indicator().await; | ||||||
|  |  | ||||||
|     if board.board_hal.get_esp().get_restart_to_conf() { |     if board.board_hal.get_esp().get_restart_to_conf() { | ||||||
|         log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); |         log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); | ||||||
|         for _i in 0..2 { |         for _i in 0..2 { | ||||||
|             board.board_hal.general_fault(true); |             board.board_hal.general_fault(true).await; | ||||||
|             Delay::new_default().delay_ms(100); |             Timer::after_millis(100).await; | ||||||
|             board.board_hal.general_fault(false); |             board.board_hal.general_fault(false).await; | ||||||
|             Delay::new_default().delay_ms(100); |             Timer::after_millis(100).await; | ||||||
|         } |         } | ||||||
|         to_config = true; |         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); |         board.board_hal.get_esp().set_restart_to_conf(false); | ||||||
|     } else if board.board_hal.get_esp().mode_override_pressed() { |     } 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, "", ""); |         log(LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); | ||||||
|         for _i in 0..5 { |         for _i in 0..5 { | ||||||
|             board.board_hal.general_fault(true); |             board.board_hal.general_fault(true).await; | ||||||
|             Delay::new_default().delay_ms(100); |             Timer::after_millis(100).await; | ||||||
|             board.board_hal.general_fault(false); |             board.board_hal.general_fault(false).await; | ||||||
|             Delay::new_default().delay_ms(100); |             Timer::after_millis(100).await; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if board.board_hal.get_esp().mode_override_pressed() { |         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; |             to_config = true; | ||||||
|         } else { |         } 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)); |         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||||
|         //TODO |         //TODO | ||||||
|         //let _webserver = httpd(reboot_now.clone()); |         //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() { |     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 { |     } 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; |         //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 |         NetworkMode::OFFLINE | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if matches!(network_mode, NetworkMode::OFFLINE) && to_config { |     if matches!(network_mode, NetworkMode::OFFLINE) && to_config { | ||||||
|         log::info!("Could not connect to station and config mode forced, switching to ap mode!"); |         info!("Could not connect to station and config mode forced, switching to ap mode!"); | ||||||
|         match board.board_hal.get_esp().wifi_ap() { |         match board.board_hal.get_esp().wifi_ap().await { | ||||||
|             Ok(_) => { |             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 { |     let timezone = match &board.board_hal.get_config().timezone { | ||||||
|         Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { |         Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { | ||||||
|             log::info!("Invalid timezone '{}', falling back to UTC", tz_str); |             info!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||||
|             UTC |             UTC | ||||||
|         }), |         }), | ||||||
|         None => UTC, // Fallback to UTC if no timezone is set |         None => UTC, // Fallback to UTC if no timezone is set | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let timezone_time = cur.with_timezone(&timezone); |     let timezone_time = cur.with_timezone(&timezone); | ||||||
|     log::info!( |     info!( | ||||||
|         "Running logic at utc {} and {} {}", |         "Running logic at utc {} and {} {}", | ||||||
|         cur, |         cur, | ||||||
|         timezone.name(), |         timezone.name(), | ||||||
| @@ -264,14 +270,14 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { |     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { | ||||||
|         publish_firmware_info( |         publish_firmware_info( | ||||||
|             version, |             version, | ||||||
|             address, |             partition_address, | ||||||
|             ota_state_string, |             ota_state_string, | ||||||
|             &mut board, |  | ||||||
|             ip_address, |             ip_address, | ||||||
|             timezone_time, |             timezone_time, | ||||||
|         ); |         ) | ||||||
|         publish_battery_state(&mut board); |         .await; | ||||||
|         let _ = publish_mppt_state(&mut board); |         publish_battery_state().await; | ||||||
|  |         let _ = publish_mppt_state().await; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     log( |     log( | ||||||
| @@ -290,15 +296,16 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|         "", |         "", | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     drop(board); | ||||||
|  |  | ||||||
|     if to_config { |     if to_config { | ||||||
|         //check if client or ap mode and init Wi-Fi |         //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! |         //config upload will trigger reboot! | ||||||
|         drop(board); |  | ||||||
|         let reboot_now = Arc::new(AtomicBool::new(false)); |         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||||
|         //TODO |         //TODO | ||||||
|         //let _webserver = httpd(reboot_now.clone()); |         //let _webserver = httpd(reboot_now.clone()); | ||||||
|         wait_infinity(WaitType::ConfigButton, reboot_now.clone()); |         wait_infinity(WaitType::ConfigButton, reboot_now.clone()).await; | ||||||
|     } else { |     } else { | ||||||
|         log(LogMessage::NormalRun, 0, 0, "", ""); |         log(LogMessage::NormalRun, 0, 0, "", ""); | ||||||
|     } |     } | ||||||
| @@ -345,7 +352,7 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|         .board_hal |         .board_hal | ||||||
|         .get_tank_sensor() |         .get_tank_sensor() | ||||||
|         .context("no 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 let Ok(res) = water_temp { | ||||||
|         if res < WATER_FROZEN_THRESH { |         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] = |     let plantstate: [PlantState; PLANT_COUNT] = | ||||||
|         core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board)); |         core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board).await); | ||||||
|     publish_plant_states(&mut board, &timezone_time, &plantstate); |     publish_plant_states(&timezone_time, &plantstate).await; | ||||||
|  |  | ||||||
|     let pump_required = plantstate |     let pump_required = plantstate | ||||||
|         .iter() |         .iter() | ||||||
| @@ -387,7 +394,7 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|                         &(plant_id + 1).to_string(), |                         &(plant_id + 1).to_string(), | ||||||
|                         "", |                         "", | ||||||
|                     ); |                     ); | ||||||
|                     board.board_hal.fault(plant_id, true)?; |                     board.board_hal.fault(plant_id, true).await?; | ||||||
|                 } |                 } | ||||||
|                 log( |                 log( | ||||||
|                     LogMessage::PumpPlant, |                     LogMessage::PumpPlant, | ||||||
| @@ -403,12 +410,11 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|                 board.board_hal.get_esp().last_pump_time(plant_id); |                 board.board_hal.get_esp().last_pump_time(plant_id); | ||||||
|                 //state.active = true; |                 //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)?; |                 let result = do_secure_pump(plant_id, plant_config, dry_run).await?; | ||||||
|                 board.board_hal.pump(plant_id, false)?; |                 board.board_hal.pump(plant_id, false).await?; | ||||||
|                 pump_info( |                 pump_info( | ||||||
|                     &mut board, |  | ||||||
|                     plant_id, |                     plant_id, | ||||||
|                     false, |                     false, | ||||||
|                     pump_ineffective, |                     pump_ineffective, | ||||||
| @@ -416,7 +422,8 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|                     result.max_current_ma, |                     result.max_current_ma, | ||||||
|                     result.min_current_ma, |                     result.min_current_ma, | ||||||
|                     result.error, |                     result.error, | ||||||
|                 ); |                 ) | ||||||
|  |                 .await; | ||||||
|             } else if !state.pump_in_timeout(plant_config, &timezone_time) { |             } else if !state.pump_in_timeout(plant_config, &timezone_time) { | ||||||
|                 // plant does not need to be watered and is not in timeout |                 // plant does not need to be watered and is not in timeout | ||||||
|                 // -> reset consecutive pump count |                 // -> reset consecutive pump count | ||||||
| @@ -433,6 +440,7 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|         .board_hal |         .board_hal | ||||||
|         .get_battery_monitor() |         .get_battery_monitor() | ||||||
|         .state_charge_percent() |         .state_charge_percent() | ||||||
|  |         .await | ||||||
|         .unwrap_or(0.); |         .unwrap_or(0.); | ||||||
|  |  | ||||||
|     // try to load full battery state if failed the battery state is unknown |     // try to load full battery state if failed the battery state is unknown | ||||||
| @@ -440,6 +448,7 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|         .board_hal |         .board_hal | ||||||
|         .get_battery_monitor() |         .get_battery_monitor() | ||||||
|         .get_battery_state() |         .get_battery_state() | ||||||
|  |         .await | ||||||
|         .unwrap_or(BatteryState::Unknown); |         .unwrap_or(BatteryState::Unknown); | ||||||
|  |  | ||||||
|     let mut light_state = LightState { |     let mut light_state = LightState { | ||||||
| @@ -505,7 +514,7 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|             board.board_hal.light(false)?; |             board.board_hal.light(false)?; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         log::info!("Lightstate is {:?}", light_state); |         info!("Lightstate is {:?}", light_state); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     match serde_json::to_string(&light_state) { |     match serde_json::to_string(&light_state) { | ||||||
| @@ -513,10 +522,11 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|             let _ = board |             let _ = board | ||||||
|                 .board_hal |                 .board_hal | ||||||
|                 .get_esp() |                 .get_esp() | ||||||
|                 .mqtt_publish("/light", state.as_bytes()); |                 .mqtt_publish("/light", state.as_bytes()) | ||||||
|  |                 .await; | ||||||
|         } |         } | ||||||
|         Err(err) => { |         Err(err) => { | ||||||
|             log::info!("Error publishing lightstate {}", err); |             info!("Error publishing lightstate {}", err); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -526,25 +536,26 @@ fn safe_main() -> anyhow::Result<()> { | |||||||
|             let _ = board |             let _ = board | ||||||
|                 .board_hal |                 .board_hal | ||||||
|                 .get_esp() |                 .get_esp() | ||||||
|                 .mqtt_publish("/deepsleep", "low Volt 12h".as_bytes()); |                 .mqtt_publish("/deepsleep", "low Volt 12h".as_bytes()).await; | ||||||
|             12 * 60 |             12 * 60 | ||||||
|         } else if is_day { |         } else if is_day { | ||||||
|             let _ = board |             let _ = board | ||||||
|                 .board_hal |                 .board_hal | ||||||
|                 .get_esp() |                 .get_esp() | ||||||
|                 .mqtt_publish("/deepsleep", "normal 20m".as_bytes()); |                 .mqtt_publish("/deepsleep", "normal 20m".as_bytes()).await; | ||||||
|             20 |             20 | ||||||
|         } else { |         } else { | ||||||
|             let _ = board |             let _ = board | ||||||
|                 .board_hal |                 .board_hal | ||||||
|                 .get_esp() |                 .get_esp() | ||||||
|                 .mqtt_publish("/deepsleep", "night 1h".as_bytes()); |                 .mqtt_publish("/deepsleep", "night 1h".as_bytes()).await; | ||||||
|             60 |             60 | ||||||
|         }; |         }; | ||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .get_esp() | ||||||
|         .mqtt_publish("/state", "sleep".as_bytes()); |         .mqtt_publish("/state", "sleep".as_bytes()) | ||||||
|  |         .await; | ||||||
|  |  | ||||||
|     //determine next event |     //determine next event | ||||||
|     //is light out of work trigger soon? |     //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_mqtt = STAY_ALIVE.load(Ordering::Relaxed); | ||||||
|  |  | ||||||
|     let stay_alive = stay_alive_mqtt; |     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 { |     if stay_alive { | ||||||
|         log::info!("Go to stay alive move"); |         info!("Go to stay alive move"); | ||||||
|         drop(board); |         drop(board); | ||||||
|         let reboot_now = Arc::new(AtomicBool::new(false)); |         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||||
|         //TODO |         //TODO | ||||||
|         //let _webserver = httpd(reboot_now.clone()); |         //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.get_esp().set_restart_to_conf(false); | ||||||
|         board |         board | ||||||
|             .board_hal |             .board_hal | ||||||
|             .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); |             .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); | ||||||
|     anyhow::Ok(()) |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn do_secure_pump( | pub async fn do_secure_pump( | ||||||
|     board: &mut MutexGuard<HAL>, |  | ||||||
|     plant_id: usize, |     plant_id: usize, | ||||||
|     plant_config: &PlantConfig, |     plant_config: &PlantConfig, | ||||||
|     dry_run: bool, |     dry_run: bool, | ||||||
| @@ -584,6 +594,7 @@ pub fn do_secure_pump( | |||||||
|     let mut error = false; |     let mut error = false; | ||||||
|     let mut first_error = true; |     let mut first_error = true; | ||||||
|     let mut pump_time_s = 0; |     let mut pump_time_s = 0; | ||||||
|  |     let board = &mut BOARD_ACCESS.get().lock().await; | ||||||
|     if !dry_run { |     if !dry_run { | ||||||
|         board |         board | ||||||
|             .board_hal |             .board_hal | ||||||
| @@ -595,8 +606,8 @@ pub fn do_secure_pump( | |||||||
|             .get_tank_sensor() |             .get_tank_sensor() | ||||||
|             .unwrap() |             .unwrap() | ||||||
|             .start_flow_meter(); |             .start_flow_meter(); | ||||||
|         board.board_hal.pump(plant_id, true)?; |         board.board_hal.pump(plant_id, true).await?; | ||||||
|         Delay::new_default().delay_ms(10); |         Timer::after_millis(10).await; | ||||||
|         for step in 0..plant_config.pump_time_s as usize { |         for step in 0..plant_config.pump_time_s as usize { | ||||||
|             let flow_value = board |             let flow_value = board | ||||||
|                 .board_hal |                 .board_hal | ||||||
| @@ -606,16 +617,16 @@ pub fn do_secure_pump( | |||||||
|             flow_collector[step] = flow_value; |             flow_collector[step] = flow_value; | ||||||
|             let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; |             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 is {} ml, limit is {} ml raw sensor {}", | ||||||
|                 flow_value_ml, plant_config.pump_limit_ml, flow_value |                 flow_value_ml, plant_config.pump_limit_ml, flow_value | ||||||
|             ); |             ); | ||||||
|             if flow_value_ml > plant_config.pump_limit_ml as f32 { |             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; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             let current = board.board_hal.pump_current(plant_id); |             let current = board.board_hal.pump_current(plant_id).await; | ||||||
|             match current { |             match current { | ||||||
|                 Ok(current) => { |                 Ok(current) => { | ||||||
|                     let current_ma = current.as_milliamperes() as u16; |                     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(), |                                 plant_config.max_pump_current_ma.to_string().as_str(), | ||||||
|                                 step.to_string().as_str(), |                                 step.to_string().as_str(), | ||||||
|                             ); |                             ); | ||||||
|                             board.board_hal.general_fault(true); |                             board.board_hal.general_fault(true).await; | ||||||
|                             board.board_hal.fault(plant_id, true)?; |                             board.board_hal.fault(plant_id, true).await?; | ||||||
|                             if !plant_config.ignore_current_error { |                             if !plant_config.ignore_current_error { | ||||||
|                                 error = true; |                                 error = true; | ||||||
|                                 break; |                                 break; | ||||||
| @@ -649,8 +660,8 @@ pub fn do_secure_pump( | |||||||
|                                 plant_config.min_pump_current_ma.to_string().as_str(), |                                 plant_config.min_pump_current_ma.to_string().as_str(), | ||||||
|                                 step.to_string().as_str(), |                                 step.to_string().as_str(), | ||||||
|                             ); |                             ); | ||||||
|                             board.board_hal.general_fault(true); |                             board.board_hal.general_fault(true).await; | ||||||
|                             board.board_hal.fault(plant_id, true)?; |                             board.board_hal.fault(plant_id, true).await?; | ||||||
|                             if !plant_config.ignore_current_error { |                             if !plant_config.ignore_current_error { | ||||||
|                                 error = true; |                                 error = true; | ||||||
|                                 break; |                                 break; | ||||||
| @@ -661,7 +672,7 @@ pub fn do_secure_pump( | |||||||
|                 } |                 } | ||||||
|                 Err(err) => { |                 Err(err) => { | ||||||
|                     if !plant_config.ignore_current_error { |                     if !plant_config.ignore_current_error { | ||||||
|                         log::info!("Error getting pump current: {}", err); |                         info!("Error getting pump current: {}", err); | ||||||
|                         log( |                         log( | ||||||
|                             LogMessage::PumpMissingSensorCurrent, |                             LogMessage::PumpMissingSensorCurrent, | ||||||
|                             plant_id as u32, |                             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; |             pump_time_s += 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -687,7 +698,7 @@ pub fn do_secure_pump( | |||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get_flow_meter_value(); |         .get_flow_meter_value(); | ||||||
|     let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; |     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 is {} with {} ml", | ||||||
|         final_flow_value, flow_value_ml |         final_flow_value, flow_value_ml | ||||||
|     ); |     ); | ||||||
| @@ -703,9 +714,10 @@ pub fn do_secure_pump( | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn update_charge_indicator(board: &mut MutexGuard<HAL>) { | async fn update_charge_indicator() { | ||||||
|  |     let board = BOARD_ACCESS.get().lock().await; | ||||||
|     //we have mppt controller, ask it for charging current |     //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 |         let _ = board | ||||||
|             .board_hal |             .board_hal | ||||||
|             .set_charge_indicator(current.as_milliamperes() > 20_f64); |             .set_charge_indicator(current.as_milliamperes() > 20_f64); | ||||||
| @@ -723,11 +735,8 @@ fn update_charge_indicator(board: &mut MutexGuard<HAL>) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn publish_tank_state( | async fn publish_tank_state(tank_state: &TankState, water_temp: &anyhow::Result<f32>) { | ||||||
|     board: &mut MutexGuard<HAL>, |     let board = &mut BOARD_ACCESS.get().lock().await; | ||||||
|     tank_state: &TankState, |  | ||||||
|     water_temp: &anyhow::Result<f32>, |  | ||||||
| ) { |  | ||||||
|     match serde_json::to_string( |     match serde_json::to_string( | ||||||
|         &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp), |         &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()); |                 .mqtt_publish("/water", state.as_bytes()); | ||||||
|         } |         } | ||||||
|         Err(err) => { |         Err(err) => { | ||||||
|             log::info!("Error publishing tankstate {}", err); |             info!("Error publishing tankstate {}", err); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| fn publish_plant_states( | async fn publish_plant_states(timezone_time: &DateTime<Tz>, plantstate: &[PlantState; 8]) { | ||||||
|     board: &mut MutexGuard<HAL>, |     let board = &mut BOARD_ACCESS.get().lock().await; | ||||||
|     timezone_time: &DateTime<Tz>, |  | ||||||
|     plantstate: &[PlantState; 8], |  | ||||||
| ) { |  | ||||||
|     for (plant_id, (plant_state, plant_conf)) in plantstate |     for (plant_id, (plant_state, plant_conf)) in plantstate | ||||||
|         .iter() |         .iter() | ||||||
|         .zip(&board.board_hal.get_config().plants.clone()) |         .zip(&board.board_hal.get_config().plants.clone()) | ||||||
| @@ -759,37 +765,41 @@ fn publish_plant_states( | |||||||
|                 let _ = board |                 let _ = board | ||||||
|                     .board_hal |                     .board_hal | ||||||
|                     .get_esp() |                     .get_esp() | ||||||
|                     .mqtt_publish(&plant_topic, state.as_bytes()); |                     .mqtt_publish(&plant_topic, state.as_bytes()) | ||||||
|                 //reduce speed as else messages will be dropped |                     .await; | ||||||
|                 board.board_hal.get_esp().delay.delay_ms(200); |                 //TODO? reduce speed as else messages will be dropped | ||||||
|  |                 Timer::after_millis(200).await | ||||||
|             } |             } | ||||||
|             Err(err) => { |             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, |     version: VersionInfo, | ||||||
|     address: u32, |     address: u32, | ||||||
|     ota_state_string: &str, |     ota_state_string: &str, | ||||||
|     board: &mut MutexGuard<HAL>, |  | ||||||
|     ip_address: &String, |     ip_address: &String, | ||||||
|     timezone_time: DateTime<Tz>, |     timezone_time: DateTime<Tz>, | ||||||
| ) { | ) { | ||||||
|  |     let board = &mut BOARD_ACCESS.get().lock().await; | ||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .get_esp() | ||||||
|         .mqtt_publish("/firmware/address", ip_address.as_bytes()); |         .mqtt_publish("/firmware/address", ip_address.as_bytes()) | ||||||
|  |         .await; | ||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .get_esp() | ||||||
|         .mqtt_publish("/firmware/githash", version.git_hash.as_bytes()); |         .mqtt_publish("/firmware/githash", version.git_hash.as_bytes()) | ||||||
|  |         .await; | ||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .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( |     let _ = board.board_hal.get_esp().mqtt_publish( | ||||||
|         "/firmware/last_online", |         "/firmware/last_online", | ||||||
|         timezone_time.to_rfc3339().as_bytes(), |         timezone_time.to_rfc3339().as_bytes(), | ||||||
| @@ -797,7 +807,8 @@ fn publish_firmware_info( | |||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .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( |     let _ = board.board_hal.get_esp().mqtt_publish( | ||||||
|         "/firmware/partition_address", |         "/firmware/partition_address", | ||||||
|         format!("{:#06x}", address).as_bytes(), |         format!("{:#06x}", address).as_bytes(), | ||||||
| @@ -805,34 +816,36 @@ fn publish_firmware_info( | |||||||
|     let _ = board |     let _ = board | ||||||
|         .board_hal |         .board_hal | ||||||
|         .get_esp() |         .get_esp() | ||||||
|         .mqtt_publish("/state", "online".as_bytes()); |         .mqtt_publish("/state", "online".as_bytes()) | ||||||
|  |         .await; | ||||||
| } | } | ||||||
|  |  | ||||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> 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(); |     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) => { |         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) => { |                 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); |                     let _ = board.board_hal.get_rtc_module().set_rtc_time(&new_time); | ||||||
|                     SntpMode::SYNC { current: new_time } |                     SntpMode::SYNC { current: new_time } | ||||||
|                 } |                 } | ||||||
|                 Err(err) => { |                 Err(err) => { | ||||||
|                     log::info!("sntp error: {}", err); |                     warn!("sntp error: {}", err); | ||||||
|                     board.board_hal.general_fault(true); |                     board.board_hal.general_fault(true).await; | ||||||
|                     SntpMode::OFFLINE |                     SntpMode::OFFLINE | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|             let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() { |             let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() { | ||||||
|                 let nw_config = &board.board_hal.get_config().network.clone(); |                 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(_) => { |                     Ok(_) => { | ||||||
|                         log::info!("Mqtt connection ready"); |                         info!("Mqtt connection ready"); | ||||||
|                         true |                         true | ||||||
|                     } |                     } | ||||||
|                     Err(err) => { |                     Err(err) => { | ||||||
|                         log::info!("Could not connect mqtt due to {}", err); |                         warn!("Could not connect mqtt due to {}", err); | ||||||
|                         false |                         false | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -846,15 +859,14 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Err(_) => { |         Err(_) => { | ||||||
|             log::info!("Offline mode"); |             info!("Offline mode"); | ||||||
|             board.board_hal.general_fault(true); |             board.board_hal.general_fault(true); | ||||||
|             NetworkMode::OFFLINE |             NetworkMode::OFFLINE | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn pump_info( | async fn pump_info( | ||||||
|     board: &mut MutexGuard<HAL>, |  | ||||||
|     plant_id: usize, |     plant_id: usize, | ||||||
|     pump_active: bool, |     pump_active: bool, | ||||||
|     pump_ineffective: bool, |     pump_ineffective: bool, | ||||||
| @@ -871,24 +883,30 @@ fn pump_info( | |||||||
|         min_current_ma: min_current_ma, |         min_current_ma: min_current_ma, | ||||||
|     }; |     }; | ||||||
|     let pump_topic = format!("/pump{}", plant_id + 1); |     let pump_topic = format!("/pump{}", plant_id + 1); | ||||||
|  |  | ||||||
|     match serde_json::to_string(&pump_info) { |     match serde_json::to_string(&pump_info) { | ||||||
|         Ok(state) => { |         Ok(state) => { | ||||||
|             let _ = board |             let _ = BOARD_ACCESS | ||||||
|  |                 .get() | ||||||
|  |                 .lock() | ||||||
|  |                 .await | ||||||
|                 .board_hal |                 .board_hal | ||||||
|                 .get_esp() |                 .get_esp() | ||||||
|                 .mqtt_publish(&pump_topic, state.as_bytes()); |                 .mqtt_publish(&pump_topic, state.as_bytes()); | ||||||
|             //reduce speed as else messages will be dropped |             //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) => { |         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<()> { | async fn publish_mppt_state() -> anyhow::Result<()> { | ||||||
|     let current = board.board_hal.get_mptt_current()?; |     let board_hal = &mut BOARD_ACCESS.get().lock().await.board_hal; | ||||||
|     let voltage = board.board_hal.get_mptt_voltage()?; |     let current = board_hal.get_mptt_current().await?; | ||||||
|  |     let voltage = board_hal.get_mptt_voltage().await?; | ||||||
|     let solar_state = Solar { |     let solar_state = Solar { | ||||||
|         current_ma: current.as_milliamperes() as u32, |         current_ma: current.as_milliamperes() as u32, | ||||||
|         voltage_ma: voltage.as_millivolts() 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) = |     if let Ok(serialized_solar_state_bytes) = | ||||||
|         serde_json::to_string(&solar_state).map(|s| s.into_bytes()) |         serde_json::to_string(&solar_state).map(|s| s.into_bytes()) | ||||||
|     { |     { | ||||||
|         let _ = board |         let _ = board_hal | ||||||
|             .board_hal |  | ||||||
|             .get_esp() |             .get_esp() | ||||||
|             .mqtt_publish("/mppt", &serialized_solar_state_bytes); |             .mqtt_publish("/mppt", &serialized_solar_state_bytes); | ||||||
|     } |     } | ||||||
|     Ok(()) |     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(); |     let state = board.board_hal.get_battery_monitor().get_battery_state(); | ||||||
|     if let Ok(serialized_battery_state_bytes) = |     if let Ok(serialized_battery_state_bytes) = | ||||||
|         serde_json::to_string(&state).map(|s| s.into_bytes()) |         serde_json::to_string(&state).map(|s| s.into_bytes()) | ||||||
|     { |     { | ||||||
|         let _ = board |         board | ||||||
|             .board_hal |             .board_hal | ||||||
|             .get_esp() |             .get_esp() | ||||||
|             .mqtt_publish("/battery", &serialized_battery_state_bytes); |             .mqtt_publish("/battery", &serialized_battery_state_bytes); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | ||||||
|     let delay = wait_type.blink_pattern(); |     let delay = wait_type.blink_pattern(); | ||||||
|     let mut led_count = 8; |     let mut led_count = 8; | ||||||
|     let mut pattern_step = 0; |     let mut pattern_step = 0; | ||||||
|     let delay_handle = Delay::new_default(); |  | ||||||
|     loop { |     loop { | ||||||
|         let mut board = BOARD_ACCESS.lock().unwrap(); |         update_charge_indicator().await; | ||||||
|         update_charge_indicator(&mut board); |         let mut board = BOARD_ACCESS.get().lock().await; | ||||||
|  |  | ||||||
|         match wait_type { |         match wait_type { | ||||||
|             WaitType::MissingConfig => { |             WaitType::MissingConfig => { | ||||||
|                 // Keep existing behavior: circular filling pattern |                 // Keep existing behavior: circular filling pattern | ||||||
| @@ -952,9 +968,9 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | |||||||
|  |  | ||||||
|         board.board_hal.general_fault(true); |         board.board_hal.general_fault(true); | ||||||
|         drop(board); |         drop(board); | ||||||
|         //cannot use shared delay as that is inside the mutex here |  | ||||||
|         delay_handle.delay_ms(delay); |         Timer::after_millis(delay).await; | ||||||
|         let mut board = BOARD_ACCESS.lock().unwrap(); |         let mut board = BOARD_ACCESS.get().lock().await; | ||||||
|         board.board_hal.general_fault(false); |         board.board_hal.general_fault(false); | ||||||
|  |  | ||||||
|         // Clear all LEDs |         // Clear all LEDs | ||||||
| @@ -962,17 +978,15 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | |||||||
|             let _ = board.board_hal.fault(i, false); |             let _ = board.board_hal.fault(i, false); | ||||||
|         } |         } | ||||||
|         drop(board); |         drop(board); | ||||||
|         delay_handle.delay_ms(delay); |         Timer::after_millis(delay).await; | ||||||
|  |  | ||||||
|         if wait_type == WaitType::MqttConfig |         if wait_type == WaitType::MqttConfig && !STAY_ALIVE.load(Ordering::Relaxed) { | ||||||
|             && !STAY_ALIVE.load(Ordering::Relaxed) |  | ||||||
|         { |  | ||||||
|             reboot_now.store(true, Ordering::Relaxed); |             reboot_now.store(true, Ordering::Relaxed); | ||||||
|         } |         } | ||||||
|         if reboot_now.load(Ordering::Relaxed) { |         if reboot_now.load(Ordering::Relaxed) { | ||||||
|             //ensure clean http answer |             //ensure clean http answer | ||||||
|             Delay::new_default().delay_ms(500); |             Timer::after_millis(500).await; | ||||||
|             BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); |             BOARD_ACCESS.get().lock().await.board_hal.deep_sleep(1); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -980,7 +994,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | |||||||
| #[esp_hal_embassy::main] | #[esp_hal_embassy::main] | ||||||
| async fn main(spawner: Spawner) { | async fn main(spawner: Spawner) { | ||||||
|     // intialize embassy |     // 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 config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); | ||||||
|     let peripherals = esp_hal::init(config); |     let peripherals = esp_hal::init(config); | ||||||
|  |  | ||||||
| @@ -991,26 +1005,23 @@ async fn main(spawner: Spawner) { | |||||||
|  |  | ||||||
|     info!("Embassy initialized!"); |     info!("Embassy initialized!"); | ||||||
|  |  | ||||||
|     let result = safe_main(); |     let result = safe_main().await; | ||||||
|     match result { |     match result { | ||||||
|         // this should not get triggered, safe_main should not return but go into deep sleep with sensible |         // this should not get triggered, safe_main should not return but go into deep sleep with sensible | ||||||
|         // timeout, this is just a fallback |         // timeout, this is just a fallback | ||||||
|         Ok(_) => { |         Ok(_) => { | ||||||
|             log::info!("Main app finished, restarting"); |             warn!("Main app finished, but should never do, restarting"); | ||||||
|             BOARD_ACCESS |             let board = &mut BOARD_ACCESS.get().lock().await.board_hal; | ||||||
|                 .lock() |  | ||||||
|                 .unwrap() |             board.get_esp().set_restart_to_conf(false); | ||||||
|                 .board_hal |             board.deep_sleep(1); | ||||||
|                 .get_esp() |  | ||||||
|                 .set_restart_to_conf(false); |  | ||||||
|             BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); |  | ||||||
|         } |         } | ||||||
|         // if safe_main exists with an error, rollback to a known good ota version |         // if safe_main exists with an error, rollback to a known good ota version | ||||||
|         Err(err) => { |         Err(err) => { | ||||||
|             log::info!("Failed main {}", err); |             error!("Failed main {}", err); | ||||||
|             //TODO |             //TODO | ||||||
|             //let _rollback_successful = rollback_and_reboot(); |             //let _rollback_successful = rollback_and_reboot(); | ||||||
|             panic!("Failed to rollback :("); |             //panic!("Failed to rollback :("); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -116,7 +116,7 @@ fn map_range_moisture( | |||||||
| } | } | ||||||
|  |  | ||||||
| impl PlantState { | 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 { |         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) { |             match board.board_hal.measure_moisture_hz(plant_id, Sensor::A) { | ||||||
|                 Ok(raw) => match map_range_moisture( |                 Ok(raw) => match map_range_moisture( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user