chore: cargo fmt
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::str::FromStr; | ||||
| use crate::hal::PLANT_COUNT; | ||||
| use crate::plant_state::PlantWateringMode; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] | ||||
| #[serde(default)] | ||||
| @@ -11,7 +11,7 @@ pub struct NetworkConfig { | ||||
|     pub password: Option<heapless::String<64>>, | ||||
|     pub mqtt_url: Option<heapless::String<128>>, | ||||
|     pub base_topic: Option<heapless::String<64>>, | ||||
|     pub max_wait: u32 | ||||
|     pub max_wait: u32, | ||||
| } | ||||
| impl Default for NetworkConfig { | ||||
|     fn default() -> Self { | ||||
| @@ -21,7 +21,7 @@ impl Default for NetworkConfig { | ||||
|             password: None, | ||||
|             mqtt_url: None, | ||||
|             base_topic: None, | ||||
|             max_wait: 10000 | ||||
|             max_wait: 10000, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -73,18 +73,18 @@ impl Default for TankConfig { | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| pub enum BatteryBoardVersion{ | ||||
| pub enum BatteryBoardVersion { | ||||
|     #[default] | ||||
|     Disabled, | ||||
|     BQ34Z100G1, | ||||
|     WchI2cSlave | ||||
|     WchI2cSlave, | ||||
| } | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| pub enum BoardVersion{ | ||||
| pub enum BoardVersion { | ||||
|     #[default] | ||||
|     INITIAL, | ||||
|     V3, | ||||
|     V4 | ||||
|     V4, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| use crate::to_string; | ||||
| use anyhow::bail; | ||||
| use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; | ||||
| use embedded_hal_bus::i2c::MutexDevice; | ||||
| @@ -6,7 +7,6 @@ use esp_idf_hal::i2c::{I2cDriver, I2cError}; | ||||
| use measurements::Temperature; | ||||
| use serde::Serialize; | ||||
| use std::result::Result::Ok as OkStd; | ||||
| use crate::to_string; | ||||
|  | ||||
| pub trait BatteryInteraction { | ||||
|     fn state_charge_percent(&mut self) -> anyhow::Result<u8>; | ||||
| @@ -31,29 +31,24 @@ pub struct BatteryState { | ||||
|     state_of_health: String, | ||||
|     temperature: String, | ||||
| } | ||||
|  | ||||
| pub enum BatteryMonitor<'a> { | ||||
|     Disabled { | ||||
|  | ||||
|     }, | ||||
|     Disabled {}, | ||||
|     BQ34Z100G1 { | ||||
|         battery_driver: Bq34z100g1Driver<MutexDevice<'a, I2cDriver<'a>>, Delay> | ||||
|         battery_driver: Bq34z100g1Driver<MutexDevice<'a, I2cDriver<'a>>, Delay>, | ||||
|     }, | ||||
|     WchI2cSlave { | ||||
|  | ||||
|     } | ||||
|     WchI2cSlave {}, | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| impl BatteryInteraction for BatteryMonitor<'_> { | ||||
|     fn state_charge_percent(&mut self) -> anyhow::Result<u8> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.state_of_charge() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading SoC {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             } | ||||
| @@ -65,128 +60,122 @@ impl BatteryInteraction for BatteryMonitor<'_> { | ||||
|  | ||||
|     fn remaining_milli_ampere_hour(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|                 match battery_driver.remaining_capacity(){ | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.remaining_capacity() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading remaining_milli_ampere_hour {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn max_milli_ampere_hour(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.full_charge_capacity() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading max_milli_ampere_hour {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn design_milli_ampere_hour(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.design_capacity() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading design_milli_ampere_hour {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn voltage_milli_volt(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|                 match battery_driver.voltage() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading voltage_milli_volt {:?}", err), | ||||
|                 } | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => match battery_driver.voltage() { | ||||
|                 OkStd(r) => anyhow::Ok(r), | ||||
|                 Err(err) => bail!("Error reading voltage_milli_volt {:?}", err), | ||||
|             }, | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn average_current_milli_ampere(&mut self) -> anyhow::Result<i16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.average_current() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading average_current_milli_ampere {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn cycle_count(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|                 match battery_driver.cycle_count() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading cycle_count {:?}", err), | ||||
|                 } | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => match battery_driver.cycle_count() { | ||||
|                 OkStd(r) => anyhow::Ok(r), | ||||
|                 Err(err) => bail!("Error reading cycle_count {:?}", err), | ||||
|             }, | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn state_health_percent(&mut self) -> anyhow::Result<u8> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => { | ||||
|                 match battery_driver.state_of_health() { | ||||
|                     OkStd(r) => anyhow::Ok(r as u8), | ||||
|                     Err(err) => bail!("Error reading state_health_percent {:?}", err), | ||||
|                 } | ||||
|             }, | ||||
|             } | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn bat_temperature(&mut self) -> anyhow::Result<u16> { | ||||
|         match self { | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver} => { | ||||
|                 match battery_driver.temperature() { | ||||
|                     OkStd(r) => anyhow::Ok(r), | ||||
|                     Err(err) => bail!("Error reading bat_temperature {:?}", err), | ||||
|                 } | ||||
|             BatteryMonitor::BQ34Z100G1 { battery_driver } => match battery_driver.temperature() { | ||||
|                 OkStd(r) => anyhow::Ok(r), | ||||
|                 Err(err) => bail!("Error reading bat_temperature {:?}", err), | ||||
|             }, | ||||
|             BatteryMonitor::WchI2cSlave { .. } => { | ||||
|                 bail!("Not implemented") | ||||
|             }, | ||||
|             &mut BatteryMonitor::Disabled {  } => { | ||||
|             } | ||||
|             &mut BatteryMonitor::Disabled {} => { | ||||
|                 bail!("Battery monitor is disabled") | ||||
|             } | ||||
|         } | ||||
| @@ -205,12 +194,8 @@ impl BatteryInteraction for BatteryMonitor<'_> { | ||||
|         }; | ||||
|  | ||||
|         match serde_json::to_string(&bat) { | ||||
|             Ok(state) => { | ||||
|                 state | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 format!("{:?}", err).to_owned() | ||||
|             } | ||||
|             Ok(state) => state, | ||||
|             Err(err) => format!("{:?}", err).to_owned(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,10 +6,15 @@ use anyhow::{anyhow, bail, Context}; | ||||
| use chrono::{DateTime, Utc}; | ||||
| use embedded_svc::ipv4::IpInfo; | ||||
| use embedded_svc::mqtt::client::QoS::{AtLeastOnce, ExactlyOnce}; | ||||
| use embedded_svc::wifi::{AccessPointConfiguration, AccessPointInfo, AuthMethod, ClientConfiguration, Configuration}; | ||||
| use embedded_svc::wifi::{ | ||||
|     AccessPointConfiguration, AccessPointInfo, AuthMethod, ClientConfiguration, Configuration, | ||||
| }; | ||||
| use esp_idf_hal::delay::Delay; | ||||
| use esp_idf_hal::gpio::{Level, PinDriver}; | ||||
| use esp_idf_svc::mqtt::client::{EspMqttClient, LwtConfiguration, MqttClientConfiguration}; | ||||
| use esp_idf_svc::sntp; | ||||
| use esp_idf_svc::sntp::SyncStatus; | ||||
| use esp_idf_svc::systime::EspSystemTime; | ||||
| use esp_idf_svc::wifi::config::{ScanConfig, ScanType}; | ||||
| use esp_idf_svc::wifi::EspWifi; | ||||
| use esp_idf_sys::{esp_spiffs_info, vTaskDelay}; | ||||
| @@ -23,10 +28,6 @@ use std::str::FromStr; | ||||
| use std::sync::atomic::AtomicBool; | ||||
| use std::sync::Arc; | ||||
| use std::time::Duration; | ||||
| use esp_idf_svc::sntp; | ||||
| use esp_idf_svc::sntp::SyncStatus; | ||||
| use esp_idf_svc::systime::EspSystemTime; | ||||
|  | ||||
|  | ||||
| #[link_section = ".rtc.data"] | ||||
| static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; | ||||
| @@ -37,7 +38,6 @@ static mut LOW_VOLTAGE_DETECTED: bool = false; | ||||
| #[link_section = ".rtc.data"] | ||||
| static mut RESTART_TO_CONF: bool = false; | ||||
|  | ||||
|  | ||||
| #[derive(Serialize, Debug)] | ||||
| pub struct FileInfo { | ||||
|     filename: String, | ||||
| @@ -61,16 +61,15 @@ pub struct FileSystemSizeInfo { | ||||
|  | ||||
| pub struct MqttClient<'a> { | ||||
|     mqtt_client: EspMqttClient<'a>, | ||||
|     base_topic: heapless::String<64> | ||||
|     base_topic: heapless::String<64>, | ||||
| } | ||||
| pub struct ESP<'a> { | ||||
|     pub(crate) mqtt_client: Option<MqttClient<'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) delay: Delay | ||||
|     pub(crate) delay: Delay, | ||||
| } | ||||
|  | ||||
|  | ||||
| impl ESP<'_> { | ||||
|     const SPIFFS_PARTITION_NAME: &'static str = "storage"; | ||||
|     const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; | ||||
| @@ -131,9 +130,7 @@ impl ESP<'_> { | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn low_voltage_in_cycle(&mut self) -> bool { | ||||
|         unsafe { | ||||
|             LOW_VOLTAGE_DETECTED | ||||
|         } | ||||
|         unsafe { LOW_VOLTAGE_DETECTED } | ||||
|     } | ||||
|     pub(crate) fn store_consecutive_pump_count(&mut self, plant: usize, count: u32) { | ||||
|         unsafe { | ||||
| @@ -153,13 +150,9 @@ impl ESP<'_> { | ||||
|     } | ||||
|  | ||||
|     pub(crate) 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() | ||||
|             } | ||||
|         let ssid = match self.load_config() { | ||||
|             Ok(config) => config.network.ap_ssid.clone(), | ||||
|             Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), | ||||
|         }; | ||||
|  | ||||
|         let apconfig = AccessPointConfiguration { | ||||
| @@ -168,17 +161,17 @@ impl ESP<'_> { | ||||
|             ssid_hidden: false, | ||||
|             ..Default::default() | ||||
|         }; | ||||
|         self.wifi_driver.set_configuration(&Configuration::AccessPoint(apconfig))?; | ||||
|         self.wifi_driver | ||||
|             .set_configuration(&Configuration::AccessPoint(apconfig))?; | ||||
|         self.wifi_driver.start()?; | ||||
|         anyhow::Ok(()) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     pub(crate) fn wifi( | ||||
|         &mut self, | ||||
|         network_config: &NetworkConfig | ||||
|     ) -> anyhow::Result<IpInfo> { | ||||
|         let ssid = network_config.ssid.clone().ok_or(anyhow!("No ssid configured"))?; | ||||
|     pub(crate) fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> { | ||||
|         let ssid = network_config | ||||
|             .ssid | ||||
|             .clone() | ||||
|             .ok_or(anyhow!("No ssid configured"))?; | ||||
|         let password = network_config.password.clone(); | ||||
|         let max_wait = network_config.max_wait; | ||||
|  | ||||
| @@ -298,7 +291,6 @@ impl ESP<'_> { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     pub(crate) fn list_files(&self) -> FileList { | ||||
|         let storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); | ||||
|  | ||||
| @@ -367,7 +359,7 @@ impl ESP<'_> { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn init_rtc_deepsleep_memory(&self, init_rtc_store: bool, to_config_mode: bool){ | ||||
|     pub(crate) fn init_rtc_deepsleep_memory(&self, init_rtc_store: bool, to_config_mode: bool) { | ||||
|         if init_rtc_store { | ||||
|             unsafe { | ||||
|                 LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT]; | ||||
| @@ -544,9 +536,9 @@ impl ESP<'_> { | ||||
|                                 match round_trip_ok.load(std::sync::atomic::Ordering::Relaxed) { | ||||
|                                     true => { | ||||
|                                         println!("Round trip registered, proceeding"); | ||||
|                                         self.mqtt_client = Some(MqttClient{ | ||||
|                                         self.mqtt_client = Some(MqttClient { | ||||
|                                             mqtt_client: client, | ||||
|                                             base_topic: base_topic_copy | ||||
|                                             base_topic: base_topic_copy, | ||||
|                                         }); | ||||
|                                         return anyhow::Ok(()); | ||||
|                                     } | ||||
| @@ -569,11 +561,7 @@ 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) fn mqtt_publish(&mut self, subtopic: &str, message: &[u8]) -> anyhow::Result<()> { | ||||
|         if self.mqtt_client.is_none() { | ||||
|             return anyhow::Ok(()); | ||||
|         } | ||||
| @@ -587,10 +575,7 @@ impl ESP<'_> { | ||||
|         } | ||||
|         let client = self.mqtt_client.as_mut().unwrap(); | ||||
|         let mut full_topic: heapless::String<256> = heapless::String::new(); | ||||
|         if full_topic | ||||
|             .push_str(client.base_topic.as_str()) | ||||
|             .is_err() | ||||
|         { | ||||
|         if full_topic.push_str(client.base_topic.as_str()).is_err() { | ||||
|             println!("Some error assembling full_topic 1"); | ||||
|             bail!("Some error assembling full_topic 1") | ||||
|         }; | ||||
| @@ -598,7 +583,9 @@ impl ESP<'_> { | ||||
|             println!("Some error assembling full_topic 2"); | ||||
|             bail!("Some error assembling full_topic 2") | ||||
|         }; | ||||
|         let publish = client.mqtt_client.publish(&full_topic, ExactlyOnce, true, message); | ||||
|         let publish = client | ||||
|             .mqtt_client | ||||
|             .publish(&full_topic, ExactlyOnce, true, message); | ||||
|         Delay::new(10).delay_ms(50); | ||||
|         match publish { | ||||
|             OkStd(message_id) => { | ||||
| @@ -621,4 +608,4 @@ impl ESP<'_> { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| use crate::config::{BoardHardware, PlantControllerConfig}; | ||||
| use crate::hal::battery::{BatteryInteraction, BatteryMonitor}; | ||||
| use crate::hal::esp::ESP; | ||||
| use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, Sensor, FreePeripherals}; | ||||
| use esp_idf_hal::gpio::{IOPin, Pull}; | ||||
| use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor}; | ||||
| use anyhow::{bail, Result}; | ||||
| use chrono::{DateTime, Utc}; | ||||
| use embedded_hal::digital::OutputPin; | ||||
| use esp_idf_hal::gpio::{IOPin, Pull}; | ||||
| use esp_idf_hal::gpio::{InputOutput, PinDriver}; | ||||
|  | ||||
| pub struct Initial<'a> { | ||||
|   | ||||
| @@ -305,7 +305,8 @@ impl PlantHal { | ||||
|                     } | ||||
|                     BatteryBoardVersion::WchI2cSlave => BatteryMonitor::WchI2cSlave {}, | ||||
|                 }; | ||||
|                 let battery_interaction = Box::new(battery_monitor) as Box<dyn BatteryInteraction + Send>; | ||||
|                 let battery_interaction = | ||||
|                     Box::new(battery_monitor) as Box<dyn BatteryInteraction + Send>; | ||||
|  | ||||
|                 let board_hal: Box<dyn BoardInteraction + Send> = match config.hardware.board { | ||||
|                     BoardVersion::INITIAL => { | ||||
|   | ||||
| @@ -23,12 +23,12 @@ use esp_idf_hal::pcnt::{ | ||||
|     PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, | ||||
| }; | ||||
| use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; | ||||
| use one_wire_bus::OneWire; | ||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
| use std::result::Result::Ok as OkStd; | ||||
| use ina219::address::Address; | ||||
| use ina219::calibration::{Calibration, UnCalibrated}; | ||||
| use ina219::SyncIna219; | ||||
| use one_wire_bus::OneWire; | ||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
| use std::result::Result::Ok as OkStd; | ||||
|  | ||||
| const MS0: u8 = 1_u8; | ||||
| const MS1: u8 = 0_u8; | ||||
| @@ -174,14 +174,19 @@ pub(crate) fn create_v4( | ||||
|     } | ||||
|  | ||||
|     let mut mppt_ina = SyncIna219::new(MutexDevice::new(&I2C_DRIVER), Address::from_byte(68)?)?; | ||||
|     esp.delay.delay_ms(mppt_ina.configuration()?.conversion_time().unwrap().as_millis() as u32); | ||||
|     esp.delay.delay_ms( | ||||
|         mppt_ina | ||||
|             .configuration()? | ||||
|             .conversion_time() | ||||
|             .unwrap() | ||||
|             .as_millis() as u32, | ||||
|     ); | ||||
|     println!("Bus Voltage: {}", mppt_ina.bus_voltage()?); | ||||
|     println!("Shunt Voltage: {}", mppt_ina.shunt_voltage()?); | ||||
|     let volt = (mppt_ina.shunt_voltage()?.shunt_voltage_mv()) as f32 / 1000_f32; | ||||
|     let current = volt /0.05; | ||||
|     let current = volt / 0.05; | ||||
|     println!("Shunt Current: {}", current); | ||||
|      | ||||
|      | ||||
|  | ||||
|     let v = V4 { | ||||
|         mppt_ina, | ||||
|         esp, | ||||
|   | ||||
							
								
								
									
										255
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -21,16 +21,16 @@ use std::{ | ||||
|     sync::{atomic::AtomicBool, Arc, Mutex}, | ||||
| }; | ||||
| mod config; | ||||
| mod log; | ||||
| mod hal; | ||||
| mod log; | ||||
| mod plant_state; | ||||
| mod tank; | ||||
|  | ||||
| use crate::config::BoardVersion::INITIAL; | ||||
| use crate::hal::battery::BatteryInteraction; | ||||
| use crate::hal::{BoardInteraction, PlantHal, HAL, PLANT_COUNT}; | ||||
| use plant_state::PlantState; | ||||
| use tank::*; | ||||
| use crate::config::BoardVersion::INITIAL; | ||||
| use crate::hal::{BoardInteraction, PlantHal, HAL, PLANT_COUNT}; | ||||
| use crate::hal::battery::BatteryInteraction; | ||||
| pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap()); | ||||
| pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false)); | ||||
|  | ||||
| @@ -72,7 +72,7 @@ struct LightState { | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Debug, PartialEq, Default)] | ||||
| ///mqtt stuct to track pump activities | ||||
| struct PumpInfo{ | ||||
| struct PumpInfo { | ||||
|     enabled: bool, | ||||
|     pump_ineffective: bool, | ||||
| } | ||||
| @@ -88,14 +88,16 @@ enum SensorError { | ||||
| #[derive(Serialize, Debug, PartialEq)] | ||||
| enum SntpMode { | ||||
|     OFFLINE, | ||||
|     SYNC{ | ||||
|         current: DateTime<Utc> | ||||
|     } | ||||
|     SYNC { current: DateTime<Utc> }, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Debug, PartialEq)] | ||||
| enum NetworkMode{ | ||||
|     WIFI {sntp: SntpMode, mqtt: bool, ip_address: String}, | ||||
| enum NetworkMode { | ||||
|     WIFI { | ||||
|         sntp: SntpMode, | ||||
|         mqtt: bool, | ||||
|         ip_address: String, | ||||
|     }, | ||||
|     OFFLINE, | ||||
| } | ||||
|  | ||||
| @@ -150,10 +152,13 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     }; | ||||
|     log(LogMessage::PartitionState, 0, 0, "", ota_state_string); | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.lock().expect("Could not lock board no other lock should be able to exist during startup!"); | ||||
|     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 cur = board.board_hal | ||||
|     let cur = board | ||||
|         .board_hal | ||||
|         .get_rtc_time() | ||||
|         .or_else(|err| { | ||||
|             println!("rtc module error: {:?}", err); | ||||
| @@ -172,7 +177,11 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     } | ||||
|  | ||||
|     println!("cur is {}", cur); | ||||
|     match board.board_hal.get_battery_monitor().average_current_milli_ampere() { | ||||
|     match board | ||||
|         .board_hal | ||||
|         .get_battery_monitor() | ||||
|         .average_current_milli_ampere() | ||||
|     { | ||||
|         Ok(charging) => { | ||||
|             let _ = board.board_hal.set_charge_indicator(charging > 20); | ||||
|         } | ||||
| @@ -208,7 +217,9 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if board.board_hal.get_config().hardware.board == INITIAL && board.board_hal.get_config().network.ssid.is_none(){ | ||||
|     if board.board_hal.get_config().hardware.board == INITIAL | ||||
|         && board.board_hal.get_config().network.ssid.is_none() | ||||
|     { | ||||
|         let _ = board.board_hal.get_esp().wifi_ap(); | ||||
|         drop(board); | ||||
|         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||
| @@ -234,7 +245,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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(|_| { | ||||
|             println!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||
|             UTC | ||||
| @@ -251,16 +262,30 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     ); | ||||
|  | ||||
|     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { | ||||
|         publish_firmware_info(version, address, ota_state_string, &mut board, &ip_address, timezone_time); | ||||
|         publish_firmware_info( | ||||
|             version, | ||||
|             address, | ||||
|             ota_state_string, | ||||
|             &mut board, | ||||
|             &ip_address, | ||||
|             timezone_time, | ||||
|         ); | ||||
|         publish_battery_state(&mut board); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     log( | ||||
|         LogMessage::StartupInfo, | ||||
|         matches!(network_mode, NetworkMode::WIFI { .. }) as u32, | ||||
|         matches!(network_mode, NetworkMode::WIFI { sntp: SntpMode::SYNC { .. }, .. }) as u32, | ||||
|         matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. }).to_string().as_str(), | ||||
|         matches!( | ||||
|             network_mode, | ||||
|             NetworkMode::WIFI { | ||||
|                 sntp: SntpMode::SYNC { .. }, | ||||
|                 .. | ||||
|             } | ||||
|         ) as u32, | ||||
|         matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. }) | ||||
|             .to_string() | ||||
|             .as_str(), | ||||
|         "", | ||||
|     ); | ||||
|  | ||||
| @@ -304,7 +329,10 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|             } | ||||
|             // disabled cannot trigger this because of wrapping if is_enabled | ||||
|             board.board_hal.general_fault(true); | ||||
|         } else if tank_state.warn_level(&board.board_hal.get_config().tank).is_ok_and(|warn| warn) { | ||||
|         } else if tank_state | ||||
|             .warn_level(&board.board_hal.get_config().tank) | ||||
|             .is_ok_and(|warn| warn) | ||||
|         { | ||||
|             log(LogMessage::TankWaterLevelLow, 0, 0, "", ""); | ||||
|             board.board_hal.general_fault(true); | ||||
|         } | ||||
| @@ -332,10 +360,17 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         && !water_frozen; | ||||
|     if pump_required { | ||||
|         log(LogMessage::EnableMain, dry_run as u32, 0, "", ""); | ||||
|         for (plant_id, (state, plant_config)) in plantstate.iter().zip(&board.board_hal.get_config().plants.clone()).enumerate() { | ||||
|         for (plant_id, (state, plant_config)) in plantstate | ||||
|             .iter() | ||||
|             .zip(&board.board_hal.get_config().plants.clone()) | ||||
|             .enumerate() | ||||
|         { | ||||
|             if state.needs_to_be_watered(plant_config, &timezone_time) { | ||||
|                 let pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id) + 1; | ||||
|                 board.board_hal.get_esp().store_consecutive_pump_count(plant_id, pump_count); | ||||
|                 board | ||||
|                     .board_hal | ||||
|                     .get_esp() | ||||
|                     .store_consecutive_pump_count(plant_id, pump_count); | ||||
|  | ||||
|                 let pump_ineffective = pump_count > plant_config.max_consecutive_pump_count as u32; | ||||
|                 if pump_ineffective { | ||||
| @@ -343,7 +378,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|                         LogMessage::ConsecutivePumpCountLimit, | ||||
|                         pump_count, | ||||
|                         plant_config.max_consecutive_pump_count as u32, | ||||
|                         &(plant_id+1).to_string(), | ||||
|                         &(plant_id + 1).to_string(), | ||||
|                         "", | ||||
|                     ); | ||||
|                     board.board_hal.fault(plant_id, true)?; | ||||
| @@ -355,29 +390,38 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|                     &dry_run.to_string(), | ||||
|                     "", | ||||
|                 ); | ||||
|                 board.board_hal.get_esp().store_last_pump_time(plant_id, cur); | ||||
|                 board | ||||
|                     .board_hal | ||||
|                     .get_esp() | ||||
|                     .store_last_pump_time(plant_id, cur); | ||||
|                 board.board_hal.get_esp().last_pump_time(plant_id); | ||||
|                 //state.active = true; | ||||
|  | ||||
|                 pump_info(&mut board,  plant_id, true, pump_ineffective); | ||||
|                 pump_info(&mut board, plant_id, true, pump_ineffective); | ||||
|  | ||||
|                 if !dry_run { | ||||
|                     board.board_hal.pump(plant_id, true)?; | ||||
|                     Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32); | ||||
|                     board.board_hal.pump(plant_id, false)?; | ||||
|                 } | ||||
|                 pump_info(&mut board,  plant_id, false, pump_ineffective); | ||||
|  | ||||
|                 pump_info(&mut board, plant_id, false, pump_ineffective); | ||||
|             } 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 | ||||
|                 board.board_hal.get_esp().store_consecutive_pump_count(plant_id, 0); | ||||
|                 board | ||||
|                     .board_hal | ||||
|                     .get_esp() | ||||
|                     .store_consecutive_pump_count(plant_id, 0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let is_day = board.board_hal.is_day(); | ||||
|     let state_of_charge = board.board_hal.get_battery_monitor().state_charge_percent().unwrap_or(0); | ||||
|     let state_of_charge = board | ||||
|         .board_hal | ||||
|         .get_battery_monitor() | ||||
|         .state_charge_percent() | ||||
|         .unwrap_or(0); | ||||
|  | ||||
|     let mut light_state = LightState { | ||||
|         enabled: board.board_hal.get_config().night_lamp.enabled, | ||||
| @@ -387,7 +431,11 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         light_state.is_day = is_day; | ||||
|         light_state.out_of_work_hour = !in_time_range( | ||||
|             &timezone_time, | ||||
|             board.board_hal.get_config().night_lamp.night_lamp_hour_start, | ||||
|             board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .night_lamp_hour_start, | ||||
|             board.board_hal.get_config().night_lamp.night_lamp_hour_end, | ||||
|         ); | ||||
|  | ||||
| @@ -399,7 +447,12 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         light_state.battery_low = board.board_hal.get_esp().low_voltage_in_cycle(); | ||||
|  | ||||
|         if !light_state.out_of_work_hour { | ||||
|             if board.board_hal.get_config().night_lamp.night_lamp_only_when_dark { | ||||
|             if board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .night_lamp_only_when_dark | ||||
|             { | ||||
|                 if !light_state.is_day { | ||||
|                     if light_state.battery_low { | ||||
|                         board.board_hal.light(false)?; | ||||
| @@ -424,7 +477,10 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|  | ||||
|     match serde_json::to_string(&light_state) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.board_hal.get_esp().mqtt_publish( "/light", state.as_bytes()); | ||||
|             let _ = board | ||||
|                 .board_hal | ||||
|                 .get_esp() | ||||
|                 .mqtt_publish("/light", state.as_bytes()); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             println!("Error publishing lightstate {}", err); | ||||
| @@ -432,16 +488,28 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     }; | ||||
|  | ||||
|     let deep_sleep_duration_minutes: u32 = if state_of_charge < 10 { | ||||
|         let _ = board.board_hal.get_esp().mqtt_publish( "/deepsleep", "low Volt 12h".as_bytes()); | ||||
|         let _ = board | ||||
|             .board_hal | ||||
|             .get_esp() | ||||
|             .mqtt_publish("/deepsleep", "low Volt 12h".as_bytes()); | ||||
|         12 * 60 | ||||
|     } else if is_day { | ||||
|         let _ = board.board_hal.get_esp().mqtt_publish( "/deepsleep", "normal 20m".as_bytes()); | ||||
|         let _ = board | ||||
|             .board_hal | ||||
|             .get_esp() | ||||
|             .mqtt_publish("/deepsleep", "normal 20m".as_bytes()); | ||||
|         20 | ||||
|     } else { | ||||
|         let _ = board.board_hal.get_esp().mqtt_publish( "/deepsleep", "night 1h".as_bytes()); | ||||
|         let _ = board | ||||
|             .board_hal | ||||
|             .get_esp() | ||||
|             .mqtt_publish("/deepsleep", "night 1h".as_bytes()); | ||||
|         60 | ||||
|     }; | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( "/state", "sleep".as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/state", "sleep".as_bytes()); | ||||
|  | ||||
|     //determine next event | ||||
|     //is light out of work trigger soon? | ||||
| @@ -462,7 +530,9 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         wait_infinity(WaitType::MqttConfig, reboot_now.clone()); | ||||
|     } | ||||
|     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 | ||||
|         .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); | ||||
| } | ||||
|  | ||||
| fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> { | ||||
| @@ -487,10 +557,19 @@ fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> { | ||||
|     water_temp | ||||
| } | ||||
|  | ||||
| fn publish_tank_state(board: &mut MutexGuard<HAL>, tank_state: &TankState, water_temp: &anyhow::Result<f32>) { | ||||
|     match serde_json::to_string(&tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp)) { | ||||
| fn publish_tank_state( | ||||
|     board: &mut MutexGuard<HAL>, | ||||
|     tank_state: &TankState, | ||||
|     water_temp: &anyhow::Result<f32>, | ||||
| ) { | ||||
|     match serde_json::to_string( | ||||
|         &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp), | ||||
|     ) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.board_hal.get_esp().mqtt_publish("/water", state.as_bytes()); | ||||
|             let _ = board | ||||
|                 .board_hal | ||||
|                 .get_esp() | ||||
|                 .mqtt_publish("/water", state.as_bytes()); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             println!("Error publishing tankstate {}", err); | ||||
| @@ -498,12 +577,23 @@ fn publish_tank_state(board: &mut MutexGuard<HAL>, tank_state: &TankState, water | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn publish_plant_states(board: &mut MutexGuard<HAL>, timezone_time: &DateTime<Tz>, plantstate: &[PlantState; 8]) { | ||||
|     for (plant_id, (plant_state, plant_conf)) in plantstate.iter().zip(& board.board_hal.get_config().plants.clone()).enumerate() { | ||||
| fn publish_plant_states( | ||||
|     board: &mut MutexGuard<HAL>, | ||||
|     timezone_time: &DateTime<Tz>, | ||||
|     plantstate: &[PlantState; 8], | ||||
| ) { | ||||
|     for (plant_id, (plant_state, plant_conf)) in plantstate | ||||
|         .iter() | ||||
|         .zip(&board.board_hal.get_config().plants.clone()) | ||||
|         .enumerate() | ||||
|     { | ||||
|         match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) { | ||||
|             Ok(state) => { | ||||
|                 let plant_topic = format!("/plant{}", plant_id + 1); | ||||
|                 let _ = board.board_hal.get_esp().mqtt_publish(&plant_topic, state.as_bytes()); | ||||
|                 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); | ||||
|             } | ||||
| @@ -514,26 +604,45 @@ fn publish_plant_states(board: &mut MutexGuard<HAL>, timezone_time: &DateTime<Tz | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn publish_firmware_info(version: VersionInfo, address: u32, ota_state_string: &str, board: &mut MutexGuard<HAL>, ip_address: &String, timezone_time: DateTime<Tz>) { | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish("/firmware/address", ip_address.as_bytes()); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( "/firmware/githash", version.git_hash.as_bytes()); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( | ||||
|         "/firmware/buildtime", | ||||
|         version.build_time.as_bytes(), | ||||
|     ); | ||||
| fn publish_firmware_info( | ||||
|     version: VersionInfo, | ||||
|     address: u32, | ||||
|     ota_state_string: &str, | ||||
|     board: &mut MutexGuard<HAL>, | ||||
|     ip_address: &String, | ||||
|     timezone_time: DateTime<Tz>, | ||||
| ) { | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/firmware/address", ip_address.as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/firmware/githash", version.git_hash.as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/firmware/buildtime", version.build_time.as_bytes()); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( | ||||
|         "/firmware/last_online", | ||||
|         timezone_time.to_rfc3339().as_bytes(), | ||||
|     ); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( "/firmware/ota_state", ota_state_string.as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/firmware/ota_state", ota_state_string.as_bytes()); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( | ||||
|         "/firmware/partition_address", | ||||
|         format!("{:#06x}", address).as_bytes(), | ||||
|     ); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( "/state", "online".as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/state", "online".as_bytes()); | ||||
| } | ||||
|  | ||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode{ | ||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode { | ||||
|     let nw_conf = &board.board_hal.get_config().network.clone(); | ||||
|     match board.board_hal.get_esp().wifi(nw_conf) { | ||||
|         Ok(ip_info) => { | ||||
| @@ -541,7 +650,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode{ | ||||
|                 Ok(new_time) => { | ||||
|                     println!("Using time from sntp"); | ||||
|                     let _ = board.board_hal.set_rtc_time(&new_time); | ||||
|                     SntpMode::SYNC {current: new_time} | ||||
|                     SntpMode::SYNC { current: new_time } | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     println!("sntp error: {}", err); | ||||
| @@ -567,7 +676,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode{ | ||||
|             NetworkMode::WIFI { | ||||
|                 sntp: sntp_mode, | ||||
|                 mqtt: mqtt_connected, | ||||
|                 ip_address: ip_info.ip.to_string() | ||||
|                 ip_address: ip_info.ip.to_string(), | ||||
|             } | ||||
|         } | ||||
|         Err(_) => { | ||||
| @@ -578,15 +687,23 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode{ | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn pump_info(board: &mut MutexGuard<HAL>, plant_id: usize, pump_active: bool, pump_ineffective: bool)  { | ||||
| fn pump_info( | ||||
|     board: &mut MutexGuard<HAL>, | ||||
|     plant_id: usize, | ||||
|     pump_active: bool, | ||||
|     pump_ineffective: bool, | ||||
| ) { | ||||
|     let pump_info = PumpInfo { | ||||
|         enabled: pump_active, | ||||
|         pump_ineffective | ||||
|         pump_ineffective, | ||||
|     }; | ||||
|     let pump_topic = format!("/pump{}", plant_id + 1); | ||||
|     match serde_json::to_string(&pump_info) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.board_hal.get_esp().mqtt_publish(&pump_topic, state.as_bytes()); | ||||
|             let _ = board | ||||
|                 .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); | ||||
|         } | ||||
| @@ -596,11 +713,12 @@ fn pump_info(board: &mut MutexGuard<HAL>, plant_id: usize, pump_active: bool, pu | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn publish_battery_state( | ||||
|     board: &mut MutexGuard<'_, HAL<'_>> | ||||
| ) { | ||||
| fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) { | ||||
|     let state = board.board_hal.get_battery_monitor().get_battery_state(); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish( "/battery", state.as_bytes()); | ||||
|     let _ = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/battery", state.as_bytes()); | ||||
| } | ||||
|  | ||||
| fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | ||||
| @@ -611,7 +729,11 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | ||||
|     loop { | ||||
|         unsafe { | ||||
|             let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|             if let Ok(charging) = board.board_hal.get_battery_monitor().average_current_milli_ampere() { | ||||
|             if let Ok(charging) = board | ||||
|                 .board_hal | ||||
|                 .get_battery_monitor() | ||||
|                 .average_current_milli_ampere() | ||||
|             { | ||||
|                 let _ = board.board_hal.set_charge_indicator(charging > 20); | ||||
|             } | ||||
|             match wait_type { | ||||
| @@ -673,7 +795,12 @@ fn main() { | ||||
|         // timeout, this is just a fallback | ||||
|         Ok(_) => { | ||||
|             println!("Main app finished, restarting"); | ||||
|             BOARD_ACCESS.lock().unwrap().board_hal.get_esp().set_restart_to_conf(false); | ||||
|             BOARD_ACCESS | ||||
|                 .lock() | ||||
|                 .unwrap() | ||||
|                 .board_hal | ||||
|                 .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 | ||||
|   | ||||
| @@ -2,8 +2,8 @@ use chrono::{DateTime, TimeDelta, Utc}; | ||||
| use chrono_tz::Tz; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::{config::PlantConfig, in_time_range}; | ||||
| use crate::hal::{BoardInteraction, Sensor, HAL}; | ||||
| use crate::{config::PlantConfig, in_time_range}; | ||||
|  | ||||
| const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin) | ||||
| const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels | ||||
| @@ -112,10 +112,7 @@ fn map_range_moisture( | ||||
| } | ||||
|  | ||||
| impl PlantState { | ||||
|     pub fn read_hardware_state( | ||||
|         plant_id: usize, | ||||
|         board: &mut HAL | ||||
|     ) -> Self { | ||||
|     pub 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( | ||||
|   | ||||
| @@ -114,11 +114,7 @@ impl TankState { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn as_mqtt_info( | ||||
|         &self, | ||||
|         config: &TankConfig, | ||||
|         water_temp: &anyhow::Result<f32>, | ||||
|     ) -> TankInfo { | ||||
|     pub fn as_mqtt_info(&self, config: &TankConfig, water_temp: &anyhow::Result<f32>) -> TankInfo { | ||||
|         let mut tank_err: Option<TankError> = None; | ||||
|         let left_ml = match self.left_ml(config) { | ||||
|             Err(err) => { | ||||
| @@ -155,9 +151,7 @@ impl TankState { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn determine_tank_state( | ||||
|     board: &mut std::sync::MutexGuard<'_, HAL<'_>> | ||||
| ) -> TankState { | ||||
| pub fn determine_tank_state(board: &mut std::sync::MutexGuard<'_, HAL<'_>>) -> TankState { | ||||
|     if board.board_hal.get_config().tank.tank_sensor_enabled { | ||||
|         match board.board_hal.tank_sensor_voltage() { | ||||
|             Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), | ||||
|   | ||||
| @@ -2,8 +2,7 @@ | ||||
|  | ||||
| use crate::hal::battery::BatteryInteraction; | ||||
| use crate::{ | ||||
|     determine_tank_state, get_version, log::LogMessage, | ||||
|     plant_state::PlantState, BOARD_ACCESS, | ||||
|     determine_tank_state, get_version, log::LogMessage, plant_state::PlantState, BOARD_ACCESS, | ||||
| }; | ||||
| use anyhow::bail; | ||||
| use chrono::DateTime; | ||||
| @@ -83,11 +82,14 @@ fn get_time( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().expect("board access"); | ||||
|     let native = board.board_hal.get_esp() | ||||
|     let native = board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .time() | ||||
|         .map(|t| t.to_rfc3339()) | ||||
|         .unwrap_or("error".to_string()); | ||||
|     let rtc = board.board_hal | ||||
|     let rtc = board | ||||
|         .board_hal | ||||
|         .get_rtc_time() | ||||
|         .map(|t| t.to_rfc3339()) | ||||
|         .unwrap_or("error".to_string()); | ||||
| @@ -116,35 +118,28 @@ fn get_live_moisture( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().expect("Should never fail"); | ||||
|     let plant_state = Vec::from_iter( | ||||
|         (0..PLANT_COUNT).map(|i| PlantState::read_hardware_state(i, &mut board)), | ||||
|     ); | ||||
|     let a = Vec::from_iter( | ||||
|         plant_state | ||||
|             .iter() | ||||
|             .map(|s| { | ||||
|                 match &s.sensor_a  { | ||||
|                     MoistureSensorState::Disabled => "disabled".to_string(), | ||||
|                     MoistureSensorState::MoistureValue {raw_hz, moisture_percent } => { | ||||
|                         format!("{moisture_percent:.2}% {raw_hz}hz",) | ||||
|                     } | ||||
|                     MoistureSensorState::SensorError(err) => format!("{err:?}"), | ||||
|                 } | ||||
|             }) | ||||
|     ); | ||||
|     let b = Vec::from_iter( | ||||
|         plant_state | ||||
|             .iter() | ||||
|             .map(|s| { | ||||
|                 match &s.sensor_b  { | ||||
|                     MoistureSensorState::Disabled => "disabled".to_string(), | ||||
|                     MoistureSensorState::MoistureValue {raw_hz, moisture_percent } => { | ||||
|                         format!("{moisture_percent:.2}% {raw_hz}hz",) | ||||
|                     } | ||||
|                     MoistureSensorState::SensorError(err) => format!("{err:?}"), | ||||
|                 } | ||||
|             }) | ||||
|     ); | ||||
|     let plant_state = | ||||
|         Vec::from_iter((0..PLANT_COUNT).map(|i| PlantState::read_hardware_state(i, &mut board))); | ||||
|     let a = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_a { | ||||
|         MoistureSensorState::Disabled => "disabled".to_string(), | ||||
|         MoistureSensorState::MoistureValue { | ||||
|             raw_hz, | ||||
|             moisture_percent, | ||||
|         } => { | ||||
|             format!("{moisture_percent:.2}% {raw_hz}hz",) | ||||
|         } | ||||
|         MoistureSensorState::SensorError(err) => format!("{err:?}"), | ||||
|     })); | ||||
|     let b = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_b { | ||||
|         MoistureSensorState::Disabled => "disabled".to_string(), | ||||
|         MoistureSensorState::MoistureValue { | ||||
|             raw_hz, | ||||
|             moisture_percent, | ||||
|         } => { | ||||
|             format!("{moisture_percent:.2}% {raw_hz}hz",) | ||||
|         } | ||||
|         MoistureSensorState::SensorError(err) => format!("{err:?}"), | ||||
|     })); | ||||
|  | ||||
|     let data = Moistures { | ||||
|         moisture_a: a, | ||||
| @@ -267,9 +262,10 @@ fn tank_info( | ||||
|     let tank_info = determine_tank_state(&mut board); | ||||
|     //should be multsampled | ||||
|     let water_temp = board.board_hal.water_temperature_c(); | ||||
|     Ok(Some(serde_json::to_string( | ||||
|         &tank_info.as_mqtt_info(&board.board_hal.get_config().tank, &water_temp), | ||||
|     )?)) | ||||
|     Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info( | ||||
|         &board.board_hal.get_config().tank, | ||||
|         &water_temp, | ||||
|     ))?)) | ||||
| } | ||||
|  | ||||
| fn night_lamp_test( | ||||
| @@ -297,7 +293,9 @@ fn wifi_scan( | ||||
| fn list_files( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().expect("It should be possible to lock the board for exclusive fs access"); | ||||
|     let mut board = BOARD_ACCESS | ||||
|         .lock() | ||||
|         .expect("It should be possible to lock the board for exclusive fs access"); | ||||
|     let result = board.board_hal.get_esp().list_files(); | ||||
|     let file_list_json = serde_json::to_string(&result)?; | ||||
|     anyhow::Ok(Some(file_list_json)) | ||||
| @@ -469,7 +467,12 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|     let reboot_now_for_reboot = reboot_now.clone(); | ||||
|     server | ||||
|         .fn_handler("/reboot", Method::Post, move |_| { | ||||
|             BOARD_ACCESS.lock().unwrap().board_hal.get_esp().set_restart_to_conf(true); | ||||
|             BOARD_ACCESS | ||||
|                 .lock() | ||||
|                 .unwrap() | ||||
|                 .board_hal | ||||
|                 .get_esp() | ||||
|                 .set_restart_to_conf(true); | ||||
|             reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed); | ||||
|             anyhow::Ok(()) | ||||
|         }) | ||||
| @@ -490,7 +493,8 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|             let file_handle = BOARD_ACCESS | ||||
|                 .lock() | ||||
|                 .unwrap() | ||||
|                 .board_hal.get_esp() | ||||
|                 .board_hal | ||||
|                 .get_esp() | ||||
|                 .get_file_handle(&filename, false); | ||||
|             match file_handle { | ||||
|                 Ok(mut file_handle) => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user