diff --git a/board/modules/Sensors_simplified/Sensors/Sensors.kicad_pro b/board/modules/Sensors_simplified/Sensors/Sensors.kicad_pro index b818b9c..4bbaac6 100644 --- a/board/modules/Sensors_simplified/Sensors/Sensors.kicad_pro +++ b/board/modules/Sensors_simplified/Sensors/Sensors.kicad_pro @@ -460,6 +460,7 @@ "single_global_label": "ignore", "unannotated": "error", "unconnected_wire_endpoint": "warning", + "undefined_netclass": "error", "unit_value_mismatch": "error", "unresolved_variable": "error", "wire_dangling": "error" diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml index 2fe2676..79c15a1 100644 --- a/rust/.cargo/config.toml +++ b/rust/.cargo/config.toml @@ -4,6 +4,7 @@ rustflags = [ # NOTE: May negatively impact performance of produced code "-C", "force-frame-pointers", "-Z", "stack-protector=all", + "-C", "link-arg=-Tlinkall.x", ] target = "riscv32imac-unknown-none-elf" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 84367a1..99b8b27 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,31 +1,8 @@ [package] -name = "plant-ctrl2" -version = "0.1.0" -authors = ["Empire Phoenix"] edition = "2021" -resolver = "2" -#rust-version = "1.71" - -[profile.dev] -# Explicitly disable LTO which the Xtensa codegen backend has issues -lto = false -strip = false -debug = true -overflow-checks = true -panic = "abort" -incremental = true -opt-level = 2 - - -[profile.release] -# Explicitly disable LTO which the Xtensa codegen backend has issues -lto = false -strip = true -debug = false -overflow-checks = false -panic = "abort" -incremental = true -opt-level = "z" +name = "plant-ctrl2" +rust-version = "1.86" +version = "0.1.0" [package.metadata.cargo_runner] # The string `$TARGET_FILE` will be replaced with the path from cargo. @@ -40,6 +17,26 @@ command = [ "partitions.csv" ] +[profile.dev] +lto = true +strip = false +debug = false +overflow-checks = true +panic = "abort" +incremental = true +opt-level = "z" + + +[profile.release] +# Explicitly disable LTO which the Xtensa codegen backend has issues +lto = true +strip = true +debug = false +overflow-checks = true +panic = "abort" +incremental = true +opt-level = "z" + [package.metadata.espflash] partition_table = "partitions.csv" @@ -78,28 +75,28 @@ embassy-executor = { version = "0.7.0", features = [ ] } embassy-time = { version = "0.4.0", features = ["log"] } esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] } -esp-wifi = { version = "0.15.0", features = [ - "builtin-scheduler", - "esp-alloc", - "esp32c6", - "log-04", - "smoltcp", - "wifi", -] } -smoltcp = { version = "0.12.0", default-features = false, features = [ - "log", - "medium-ethernet", - "multicast", - "proto-dhcpv4", - "proto-dns", - "proto-ipv4", - "socket-dns", - "socket-icmp", - "socket-raw", - "socket-tcp", - "socket-udp", -] } -static_cell = "2.1.1" +#esp-wifi = { version = "0.15.0", features = [ +# "builtin-scheduler", +# "esp-alloc", +# "esp32c6", +# "log-04", +# "smoltcp", +# "wifi", +#] } +#smoltcp = { version = "0.12.0", default-features = false, features = [ +# "log", +# "medium-ethernet", +# "multicast", +# "proto-dhcpv4", +# "proto-dns", +# "proto-ipv4", +# "socket-dns", +# "socket-icmp", +# "socket-raw", +# "socket-tcp", +# "socket-udp", +#] } +#static_cell = "2.1.1" embedded-hal = "1.0.0" heapless = { version = "0.8", features = ["serde"] } embedded-hal-bus = { version = "0.3.0" } @@ -122,8 +119,8 @@ serde_json = { version = "1.0.143", default-features = false, features = ["alloc #timezone -chrono = { version = "0.4.23", default-features = false, features = ["iana-time-zone", "alloc", "serde"] } -chrono-tz = { version = "0.10.3", default-features = false, features = ["filter-by-regex"] } +chrono = { version = "0.4.42", default-features = false, features = ["iana-time-zone", "alloc", "serde"] } +chrono-tz = { version = "0.10.4", default-features = false, features = ["filter-by-regex"] } eeprom24x = "0.7.2" #url = "2.5.3" crc = "3.2.1" @@ -138,18 +135,13 @@ ina219 = { version = "0.2.0" } embedded-storage = "=0.3.1" ekv = "1.0.0" embedded-can = "0.4.1" -critical-section = "1.2.0" portable-atomic = "1.11.1" embassy-sync = { version = "0.7.2", features = ["log"] } async-trait = "0.1.89" +bq34z100 = { version = "0.4.0", default-features = false } [patch.crates-io] -#esp-idf-hal = { git = "https://github.com/esp-rs/esp-idf-hal.git" } -#esp-idf-hal = { git = "https://github.com/empirephoenix/esp-idf-hal.git" } -#esp-idf-sys = { git = "https://github.com/empirephoenix/esp-idf-sys.git" } -#esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys.git" } -#esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc.git" } #bq34z100 = { path = "../../bq34z100_rust" } [build-dependencies] diff --git a/rust/build.rs b/rust/build.rs index 6d32dbc..33f236a 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -50,6 +50,12 @@ fn linker_be_nice() { } fn main() { + //webpack(); + //linker_be_nice(); + let _ = EmitBuilder::builder().all_git().all_build().emit(); +} + +fn webpack() { println!("cargo:rerun-if-changed=./src/src_webpack"); Command::new("rm") .arg("./src/webserver/bundle.js") @@ -112,6 +118,4 @@ fn main() { .unwrap(); } } - linker_be_nice(); - let _ = EmitBuilder::builder().all_git().all_build().emit(); -} +} \ No newline at end of file diff --git a/rust/src/hal/v3_hal.rs b/rust/scratch/v3_hal.rs similarity index 100% rename from rust/src/hal/v3_hal.rs rename to rust/scratch/v3_hal.rs diff --git a/rust/src/hal/v4_hal.rs b/rust/scratch/v4_hal.rs similarity index 100% rename from rust/src/hal/v4_hal.rs rename to rust/scratch/v4_hal.rs diff --git a/rust/src/hal/v4_sensor.rs b/rust/scratch/v4_sensor.rs similarity index 100% rename from rust/src/hal/v4_sensor.rs rename to rust/scratch/v4_sensor.rs diff --git a/rust/scratch/water.rs b/rust/scratch/water.rs new file mode 100644 index 0000000..30f215d --- /dev/null +++ b/rust/scratch/water.rs @@ -0,0 +1,176 @@ +use crate::hal::TANK_MULTI_SAMPLE; +use anyhow::{anyhow, bail}; +use ds18b20::Ds18b20; +use esp_idf_hal::adc::oneshot::config::AdcChannelConfig; +use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver}; +use esp_idf_hal::adc::{attenuation, Resolution, ADC1}; +use esp_idf_hal::delay::Delay; +use esp_idf_hal::gpio::{AnyIOPin, AnyInputPin, Gpio5, InputOutput, PinDriver, Pull}; +use esp_idf_hal::pcnt::{ + PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PCNT1, +}; +use esp_idf_sys::EspError; +use one_wire_bus::OneWire; + +pub struct TankSensor<'a> { + // one_wire_bus: OneWire>, + // tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, ADC1>>, + // tank_power: PinDriver<'a, AnyIOPin, InputOutput>, + // flow_counter: PcntDriver<'a>, + // delay: Delay, +} + +impl<'a> TankSensor<'a> { + pub(crate) fn create( + // one_wire_pin: AnyIOPin, + // adc1: ADC1, + // gpio5: Gpio5, + // tank_power_pin: AnyIOPin, + // flow_sensor_pin: AnyIOPin, + // pcnt1: PCNT1, + ) -> anyhow::Result> { + // let mut one_wire_pin = + // PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin"); + // one_wire_pin + // .set_pull(Pull::Floating) + // .expect("Failed to set pull"); + // + // let adc_config = AdcChannelConfig { + // attenuation: attenuation::DB_11, + // resolution: Resolution::Resolution12Bit, + // calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve, + // }; + // let tank_driver = AdcDriver::new(adc1).expect("Failed to configure ADC"); + // let tank_channel = AdcChannelDriver::new(tank_driver, gpio5, &adc_config) + // .expect("Failed to configure ADC channel"); + // + // let mut tank_power = + // PinDriver::input_output(tank_power_pin).expect("Failed to configure pin"); + // tank_power + // .set_pull(Pull::Floating) + // .expect("Failed to set pull"); + // + // let one_wire_bus = + // OneWire::new(one_wire_pin).expect("OneWire bus did not pull up after release"); + // + // let mut flow_counter = PcntDriver::new( + // pcnt1, + // Some(flow_sensor_pin), + // Option::::None, + // Option::::None, + // Option::::None, + // )?; + // + // flow_counter.channel_config( + // PcntChannel::Channel1, + // PinIndex::Pin0, + // PinIndex::Pin1, + // &PcntChannelConfig { + // lctrl_mode: PcntControlMode::Keep, + // hctrl_mode: PcntControlMode::Keep, + // pos_mode: PcntCountMode::Increment, + // neg_mode: PcntCountMode::Hold, + // counter_h_lim: i16::MAX, + // counter_l_lim: 0, + // }, + // )?; + // + // Ok(TankSensor { + // one_wire_bus, + // tank_channel, + // tank_power, + // flow_counter, + // delay: Default::default(), + // }) + bail!("Tank sensor not implemented"); + } + + pub fn reset_flow_meter(&mut self) { + // self.flow_counter.counter_pause().unwrap(); + // self.flow_counter.counter_clear().unwrap(); + } + + pub fn start_flow_meter(&mut self) { + //self.flow_counter.counter_resume().unwrap(); + } + + pub fn get_flow_meter_value(&mut self) -> i16 { + //self.flow_counter.get_counter_value().unwrap() + 5_i16 + } + + pub fn stop_flow_meter(&mut self) -> i16 { + //self.flow_counter.counter_pause().unwrap(); + self.get_flow_meter_value() + } + + pub async fn water_temperature_c(&mut self) -> anyhow::Result { + //multisample should be moved to water_temperature_c + let mut attempt = 1; + let water_temp: Result = loop { + let temp = self.single_temperature_c().await; + match &temp { + Ok(res) => { + log::info!("Water temp is {}", res); + break temp; + } + Err(err) => { + log::info!("Could not get water temp {} attempt {}", err, attempt) + } + } + if attempt == 5 { + break temp; + } + attempt += 1; + }; + water_temp + } + + async fn single_temperature_c(&mut self) -> anyhow::Result { + bail!("err"); + // self.one_wire_bus + // .reset(&mut self.delay) + // .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; + // let first = self.one_wire_bus.devices(false, &mut self.delay).next(); + // if first.is_none() { + // bail!("Not found any one wire Ds18b20"); + // } + // let device_address = first + // .unwrap() + // .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; + + // let water_temp_sensor = Ds18b20::new::(device_address) + // .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; + // + // water_temp_sensor + // .start_temp_measurement(&mut self.one_wire_bus, &mut self.delay) + // .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; + // ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut self.delay); + // let sensor_data = water_temp_sensor + // .read_data(&mut self.one_wire_bus, &mut self.delay) + // .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; + // if sensor_data.temperature == 85_f32 { + // bail!("Ds18b20 dummy temperature returned"); + // } + //anyhow::Ok(sensor_data.temperature / 10_f32) + Ok(13_f32) + } + + pub async fn tank_sensor_voltage(&mut self) -> anyhow::Result { + // self.tank_power.set_high()?; + // //let stabilize + // self.delay.delay_ms(100); + // + // let mut store = [0_u16; TANK_MULTI_SAMPLE]; + // for multisample in 0..TANK_MULTI_SAMPLE { + // let value = self.tank_channel.read()?; + // store[multisample] = value; + // } + // self.tank_power.set_low()?; + // + // store.sort(); + // let median_mv = store[6] as f32 / 1000_f32; + let median_mv = 10_f32; + anyhow::Ok(median_mv) + } +} diff --git a/rust/src/config.rs b/rust/src/config.rs index 5ac1426..b5e3707 100644 --- a/rust/src/config.rs +++ b/rust/src/config.rs @@ -1,7 +1,8 @@ +use alloc::string::String; +use core::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)] diff --git a/rust/src/hal/battery.rs b/rust/src/hal/battery.rs index 04de704..5ada3c3 100644 --- a/rust/src/hal/battery.rs +++ b/rust/src/hal/battery.rs @@ -1,20 +1,23 @@ +use alloc::string::String; +use crate::hal::Box; use anyhow::anyhow; use async_trait::async_trait; +use bq34z100::{Bq34Z100Error, Bq34z100g1Driver}; use measurements::Temperature; use serde::Serialize; #[async_trait] pub trait BatteryInteraction { - async fn state_charge_percent(&mut self) -> Result; - async fn remaining_milli_ampere_hour(&mut self) -> Result; - async fn max_milli_ampere_hour(&mut self) -> Result; - async fn design_milli_ampere_hour(&mut self) -> Result; - async fn voltage_milli_volt(&mut self) -> Result; - async fn average_current_milli_ampere(&mut self) -> Result; - async fn cycle_count(&mut self) -> Result; - async fn state_health_percent(&mut self) -> Result; - async fn bat_temperature(&mut self) -> Result; - async fn get_battery_state(&mut self) -> Result; + async fn state_charge_percent(& mut self) -> Result; + async fn remaining_milli_ampere_hour(& mut self) -> Result; + async fn max_milli_ampere_hour(& mut self) -> Result; + async fn design_milli_ampere_hour(& mut self) -> Result; + async fn voltage_milli_volt(& mut self) -> Result; + async fn average_current_milli_ampere(& mut self) -> Result; + async fn cycle_count(& mut self) -> Result; + async fn state_health_percent(& mut self) -> Result; + async fn bat_temperature(& mut self) -> Result; + async fn get_battery_state(& mut self) -> Result; } #[derive(Debug, Serialize)] @@ -35,13 +38,13 @@ pub enum BatteryError { CommunicationError(String), } -impl From> for BatteryError { - fn from(err: Bq34Z100Error) -> Self { - BatteryError::CommunicationError( - anyhow!("failed to communicate with battery monitor: {:?}", err).to_string(), - ) - } -} +// impl From> for BatteryError { +// fn from(err: Bq34Z100Error) -> Self { +// BatteryError::CommunicationError( +// anyhow!("failed to communicate with battery monitor: {:?}", err).to_string(), +// ) +// } +// } #[derive(Debug, Serialize)] pub enum BatteryState { @@ -51,45 +54,45 @@ pub enum BatteryState { /// If no battery monitor is installed this implementation will be used pub struct NoBatteryMonitor {} - +#[async_trait] impl BatteryInteraction for NoBatteryMonitor { - fn state_charge_percent(&mut self) -> Result { + async fn state_charge_percent(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn remaining_milli_ampere_hour(&mut self) -> Result { + async fn remaining_milli_ampere_hour(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn max_milli_ampere_hour(&mut self) -> Result { + async fn max_milli_ampere_hour(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn design_milli_ampere_hour(&mut self) -> Result { + async fn design_milli_ampere_hour(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn voltage_milli_volt(&mut self) -> Result { + async fn voltage_milli_volt(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn average_current_milli_ampere(&mut self) -> Result { + async fn average_current_milli_ampere(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn cycle_count(&mut self) -> Result { + async fn cycle_count(& mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn state_health_percent(&mut self) -> Result { + async fn state_health_percent(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn bat_temperature(&mut self) -> Result { + async fn bat_temperature(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } - fn get_battery_state(&mut self) -> Result { + async fn get_battery_state(& mut self) -> Result { Ok(BatteryState::Unknown) } } @@ -98,115 +101,115 @@ impl BatteryInteraction for NoBatteryMonitor { #[allow(dead_code)] pub struct WchI2cSlave {} -pub struct BQ34Z100G1<'a> { - pub battery_driver: Bq34z100g1Driver>, Delay>, -} - -impl BatteryInteraction for BQ34Z100G1<'_> { - fn state_charge_percent(&mut self) -> Result { - Ok(self.battery_driver.state_of_charge().map(f32::from)?) - } - - fn remaining_milli_ampere_hour(&mut self) -> Result { - Ok(self.battery_driver.remaining_capacity()?) - } - - fn max_milli_ampere_hour(&mut self) -> Result { - Ok(self.battery_driver.full_charge_capacity()?) - } - - fn design_milli_ampere_hour(&mut self) -> Result { - Ok(self.battery_driver.design_capacity()?) - } - - fn voltage_milli_volt(&mut self) -> Result { - Ok(self.battery_driver.voltage()?) - } - - fn average_current_milli_ampere(&mut self) -> Result { - Ok(self.battery_driver.average_current()?) - } - - fn cycle_count(&mut self) -> Result { - Ok(self.battery_driver.cycle_count()?) - } - - fn state_health_percent(&mut self) -> Result { - Ok(self.battery_driver.state_of_health()?) - } - - fn bat_temperature(&mut self) -> Result { - Ok(self.battery_driver.temperature()?) - } - - fn get_battery_state(&mut self) -> Result { - Ok(BatteryState::Info(BatteryInfo { - voltage_milli_volt: self.voltage_milli_volt()?, - average_current_milli_ampere: self.average_current_milli_ampere()?, - cycle_count: self.cycle_count()?, - design_milli_ampere_hour: self.design_milli_ampere_hour()?, - remaining_milli_ampere_hour: self.remaining_milli_ampere_hour()?, - state_of_charge: self.state_charge_percent()?, - state_of_health: self.state_health_percent()?, - temperature: self.bat_temperature()?, - })) - } -} - -pub fn print_battery_bq34z100( - battery_driver: &mut Bq34z100g1Driver>, Delay>, -) -> anyhow::Result<(), Bq34Z100Error> { - log::info!("Try communicating with battery"); - let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { - log::info!("Firmware {:?}", e); - 0 - }); - log::info!("fw version is {}", fwversion); - - let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| { - log::info!("Design capacity {:?}", e); - 0 - }); - log::info!("Design Capacity {}", design_capacity); - if design_capacity == 1000 { - log::info!("Still stock configuring battery, readouts are likely to be wrong!"); - } - - let flags = battery_driver.get_flags_decoded()?; - log::info!("Flags {:?}", flags); - - let chem_id = battery_driver.chem_id().unwrap_or_else(|e| { - log::info!("Chemid {:?}", e); - 0 - }); - - let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| { - log::info!("Bat Temp {:?}", e); - 0 - }); - let temp_c = Temperature::from_kelvin(bat_temp as f64 / 10_f64).as_celsius(); - let voltage = battery_driver.voltage().unwrap_or_else(|e| { - log::info!("Bat volt {:?}", e); - 0 - }); - let current = battery_driver.current().unwrap_or_else(|e| { - log::info!("Bat current {:?}", e); - 0 - }); - let state = battery_driver.state_of_charge().unwrap_or_else(|e| { - log::info!("Bat Soc {:?}", e); - 0 - }); - let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| { - log::info!("Bat Charge Volt {:?}", e); - 0 - }); - let charge_current = battery_driver.charge_current().unwrap_or_else(|e| { - log::info!("Bat Charge Current {:?}", e); - 0 - }); - log::info!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current); - let _ = battery_driver.unsealed(); - let _ = battery_driver.it_enable(); - anyhow::Result::Ok(()) -} +// pub struct BQ34Z100G1<'a> { +// pub battery_driver: Bq34z100g1Driver>, Delay>, +// } +// +// impl BatteryInteraction for BQ34Z100G1<'_> { +// fn state_charge_percent(&mut self) -> Result { +// Ok(self.battery_driver.state_of_charge().map(f32::from)?) +// } +// +// fn remaining_milli_ampere_hour(&mut self) -> Result { +// Ok(self.battery_driver.remaining_capacity()?) +// } +// +// fn max_milli_ampere_hour(&mut self) -> Result { +// Ok(self.battery_driver.full_charge_capacity()?) +// } +// +// fn design_milli_ampere_hour(&mut self) -> Result { +// Ok(self.battery_driver.design_capacity()?) +// } +// +// fn voltage_milli_volt(&mut self) -> Result { +// Ok(self.battery_driver.voltage()?) +// } +// +// fn average_current_milli_ampere(&mut self) -> Result { +// Ok(self.battery_driver.average_current()?) +// } +// +// fn cycle_count(&mut self) -> Result { +// Ok(self.battery_driver.cycle_count()?) +// } +// +// fn state_health_percent(&mut self) -> Result { +// Ok(self.battery_driver.state_of_health()?) +// } +// +// fn bat_temperature(&mut self) -> Result { +// Ok(self.battery_driver.temperature()?) +// } +// +// fn get_battery_state(&mut self) -> Result { +// Ok(BatteryState::Info(BatteryInfo { +// voltage_milli_volt: self.voltage_milli_volt()?, +// average_current_milli_ampere: self.average_current_milli_ampere()?, +// cycle_count: self.cycle_count()?, +// design_milli_ampere_hour: self.design_milli_ampere_hour()?, +// remaining_milli_ampere_hour: self.remaining_milli_ampere_hour()?, +// state_of_charge: self.state_charge_percent()?, +// state_of_health: self.state_health_percent()?, +// temperature: self.bat_temperature()?, +// })) +// } +// } +// +// pub fn print_battery_bq34z100( +// battery_driver: &mut Bq34z100g1Driver>, Delay>, +// ) -> anyhow::Result<(), Bq34Z100Error> { +// log::info!("Try communicating with battery"); +// let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { +// log::info!("Firmware {:?}", e); +// 0 +// }); +// log::info!("fw version is {}", fwversion); +// +// let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| { +// log::info!("Design capacity {:?}", e); +// 0 +// }); +// log::info!("Design Capacity {}", design_capacity); +// if design_capacity == 1000 { +// log::info!("Still stock configuring battery, readouts are likely to be wrong!"); +// } +// +// let flags = battery_driver.get_flags_decoded()?; +// log::info!("Flags {:?}", flags); +// +// let chem_id = battery_driver.chem_id().unwrap_or_else(|e| { +// log::info!("Chemid {:?}", e); +// 0 +// }); +// +// let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| { +// log::info!("Bat Temp {:?}", e); +// 0 +// }); +// let temp_c = Temperature::from_kelvin(bat_temp as f64 / 10_f64).as_celsius(); +// let voltage = battery_driver.voltage().unwrap_or_else(|e| { +// log::info!("Bat volt {:?}", e); +// 0 +// }); +// let current = battery_driver.current().unwrap_or_else(|e| { +// log::info!("Bat current {:?}", e); +// 0 +// }); +// let state = battery_driver.state_of_charge().unwrap_or_else(|e| { +// log::info!("Bat Soc {:?}", e); +// 0 +// }); +// let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| { +// log::info!("Bat Charge Volt {:?}", e); +// 0 +// }); +// let charge_current = battery_driver.charge_current().unwrap_or_else(|e| { +// log::info!("Bat Charge Current {:?}", e); +// 0 +// }); +// log::info!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current); +// let _ = battery_driver.unsealed(); +// let _ = battery_driver.it_enable(); +// anyhow::Result::Ok(()) +// } diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 4b17942..3643755 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -10,6 +10,10 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +use core::marker::PhantomData; +use core::net::IpAddr; +use core::str::FromStr; +use embassy_time::Timer; #[link_section = ".rtc.data"] static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; @@ -42,15 +46,23 @@ pub struct FileSystemSizeInfo { } pub struct MqttClient<'a> { + dummy: PhantomData<&'a ()>, //mqtt_client: EspMqttClient<'a>, base_topic: heapless::String<64>, } pub struct Esp<'a> { pub(crate) mqtt_client: Option>, + pub(crate) dummy: PhantomData<&'a ()>, //pub(crate) wifi_driver: EspWifi<'a>, //pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, } +pub struct IpInfo { + pub(crate) ip: IpAddr, + netmask: IpAddr, + gateway: IpAddr, +} + struct AccessPointInfo {} impl Esp<'_> { @@ -76,23 +88,25 @@ impl Esp<'_> { todo!(); } pub(crate) fn time(&mut self) -> anyhow::Result> { - let time = EspSystemTime {}.now().as_millis(); - let smaller_time = time as i64; - let local_time = DateTime::from_timestamp_millis(smaller_time) - .ok_or(anyhow!("could not convert timestamp"))?; - anyhow::Ok(local_time) + bail!("todo"); + // let time = EspSystemTime {}.now().as_millis(); + // let smaller_time = time as i64; + // let local_time = DateTime::from_timestamp_millis(smaller_time) + // .ok_or(anyhow!("could not convert timestamp"))?; + // anyhow::Ok(local_time) } pub(crate) async fn wifi_scan(&mut self) -> anyhow::Result> { - self.wifi_driver.start_scan( - &ScanConfig { - scan_type: ScanType::Passive(Duration::from_secs(5)), - show_hidden: false, - ..Default::default() - }, - true, - )?; - anyhow::Ok(self.wifi_driver.get_scan_result()?) + bail!("todo"); + // self.wifi_driver.start_scan( + // &ScanConfig { + // scan_type: ScanType::Passive(Duration::from_secs(5)), + // show_hidden: false, + // ..Default::default() + // }, + // true, + // )?; + // anyhow::Ok(self.wifi_driver.get_scan_result()?) } pub(crate) fn last_pump_time(&self, plant: usize) -> Option> { @@ -140,19 +154,22 @@ impl Esp<'_> { Ok(config) => config.network.ap_ssid.clone(), Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), }; - - let apconfig = AccessPointConfiguration { - ssid, - auth_method: AuthMethod::None, - ssid_hidden: false, - ..Default::default() - }; - self.wifi_driver - .set_configuration(&Configuration::AccessPoint(apconfig))?; - self.wifi_driver.start()?; - anyhow::Ok(()) + todo!("todo"); + // + // let apconfig = AccessPointConfiguration { + // ssid, + // auth_method: AuthMethod::None, + // ssid_hidden: false, + // ..Default::default() + // }; + // self.wifi_driver + // .set_configuration(&Configuration::AccessPoint(apconfig))?; + // self.wifi_driver.start()?; + // anyhow::Ok(()) } + + pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result { let ssid = network_config .ssid @@ -160,80 +177,83 @@ impl Esp<'_> { .ok_or(anyhow!("No ssid configured"))?; let password = network_config.password.clone(); let max_wait = network_config.max_wait; - - match password { - Some(pw) => { - //TODO expect error due to invalid pw or similar! //call this during configuration and check if works, revert to config mode if not - self.wifi_driver.set_configuration(&Configuration::Client( - ClientConfiguration { - ssid, - password: pw, - ..Default::default() - }, - ))?; - } - None => { - self.wifi_driver.set_configuration(&Configuration::Client( - ClientConfiguration { - ssid, - auth_method: AuthMethod::None, - ..Default::default() - }, - ))?; - } - } - - self.wifi_driver.start()?; - self.wifi_driver.connect()?; - - let delay = Delay::new_default(); - let mut counter = 0_u32; - while !self.wifi_driver.is_connected()? { - delay.delay_ms(250); - counter += 250; - if counter > max_wait { - //ignore these errors, Wi-Fi will not be used this - self.wifi_driver.disconnect().unwrap_or(()); - self.wifi_driver.stop().unwrap_or(()); - bail!("Did not manage wifi connection within timeout"); - } - } - log::info!("Should be connected now, waiting for link to be up"); - - while !self.wifi_driver.is_up()? { - delay.delay_ms(250); - counter += 250; - if counter > max_wait { - //ignore these errors, Wi-Fi will not be used this - self.wifi_driver.disconnect().unwrap_or(()); - self.wifi_driver.stop().unwrap_or(()); - bail!("Did not manage wifi connection within timeout"); - } - } - //update freertos registers ;) - let address = self.wifi_driver.sta_netif().get_ip_info()?; - log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); - anyhow::Ok(address) + bail!("todo") + // match password { + // Some(pw) => { + // //TODO expect error due to invalid pw or similar! //call this during configuration and check if works, revert to config mode if not + // self.wifi_driver.set_configuration(&Configuration::Client( + // ClientConfiguration { + // ssid, + // password: pw, + // ..Default::default() + // }, + // ))?; + // } + // None => { + // self.wifi_driver.set_configuration(&Configuration::Client( + // ClientConfiguration { + // ssid, + // auth_method: AuthMethod::None, + // ..Default::default() + // }, + // ))?; + // } + // } + // + // self.wifi_driver.start()?; + // self.wifi_driver.connect()?; + // + // let delay = Delay::new_default(); + // let mut counter = 0_u32; + // while !self.wifi_driver.is_connected()? { + // delay.delay_ms(250); + // counter += 250; + // if counter > max_wait { + // //ignore these errors, Wi-Fi will not be used this + // self.wifi_driver.disconnect().unwrap_or(()); + // self.wifi_driver.stop().unwrap_or(()); + // bail!("Did not manage wifi connection within timeout"); + // } + // } + // log::info!("Should be connected now, waiting for link to be up"); + // + // while !self.wifi_driver.is_up()? { + // delay.delay_ms(250); + // counter += 250; + // if counter > max_wait { + // //ignore these errors, Wi-Fi will not be used this + // self.wifi_driver.disconnect().unwrap_or(()); + // self.wifi_driver.stop().unwrap_or(()); + // bail!("Did not manage wifi connection within timeout"); + // } + // } + // //update freertos registers ;) + // let address = self.wifi_driver.sta_netif().get_ip_info()?; + // log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}")); + // anyhow::Ok(address) } - pub(crate) async fn load_config(&mut self) -> anyhow::Result { - let cfg = File::open(Self::CONFIG_FILE)?; - let config: PlantControllerConfig = serde_json::from_reader(cfg)?; - anyhow::Ok(config) + pub(crate) fn load_config(&mut self) -> anyhow::Result { + bail!("todo"); + // let cfg = File::open(Self::CONFIG_FILE)?; + // let config: PlantControllerConfig = serde_json::from_reader(cfg)?; + // anyhow::Ok(config) } pub(crate) async fn save_config( &mut self, config: &PlantControllerConfig, ) -> anyhow::Result<()> { - let mut cfg = File::create(Self::CONFIG_FILE)?; - serde_json::to_writer(&mut cfg, &config)?; - log::info!("Wrote config config {:?}", config); - anyhow::Ok(()) + bail!("todo"); + // let mut cfg = File::create(Self::CONFIG_FILE)?; + // serde_json::to_writer(&mut cfg, &config)?; + // log::info!("Wrote config config {:?}", config); + // anyhow::Ok(()) } - pub(crate) async fn mount_file_system(&mut self) -> anyhow::Result<()> { + pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> { + bail!("fail"); log(LogMessage::MountingFilesystem, 0, 0, "", ""); let base_path = String::try_from("/spiffs")?; let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?; - let conf = todo!(); + //let conf = todo!(); //let conf = esp_idf_sys::esp_vfs_spiffs_conf_t { //base_path: base_path.as_ptr(), @@ -247,102 +267,112 @@ impl Esp<'_> { //esp_idf_sys::esp!(esp_idf_sys::esp_vfs_spiffs_register(&conf))?; //} - let free_space = self.file_system_size()?; - log( - LogMessage::FilesystemMount, - free_space.free_size as u32, - free_space.total_size as u32, - &free_space.used_size.to_string(), - "", - ); + // let free_space = self.file_system_size()?; + // log( + // LogMessage::FilesystemMount, + // free_space.free_size as u32, + // free_space.total_size as u32, + // &free_space.used_size.to_string(), + // "", + // ); anyhow::Ok(()) } async fn file_system_size(&mut self) -> anyhow::Result { - let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?; - let mut total_size = 0; - let mut used_size = 0; - unsafe { - esp_idf_sys::esp!(esp_spiffs_info( - storage.as_ptr(), - &mut total_size, - &mut used_size - ))?; - } - anyhow::Ok(FileSystemSizeInfo { - total_size, - used_size, - free_size: total_size - used_size, - }) + bail!("fail"); + // let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?; + // let mut total_size = 0; + // let mut used_size = 0; + // unsafe { + // esp_idf_sys::esp!(esp_spiffs_info( + // storage.as_ptr(), + // &mut total_size, + // &mut used_size + // ))?; + // } + // anyhow::Ok(FileSystemSizeInfo { + // total_size, + // used_size, + // free_size: total_size - used_size, + // }) } pub(crate) async fn list_files(&self) -> FileList { - let storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); - - let mut file_system_corrupt = None; - - let mut iter_error = None; - let mut result = Vec::new(); - - let filepath = Path::new(Self::BASE_PATH); - let read_dir = fs::read_dir(filepath); - match read_dir { - OkStd(read_dir) => { - for item in read_dir { - match item { - OkStd(file) => { - let f = FileInfo { - filename: file.file_name().into_string().unwrap(), - size: file.metadata().map(|it| it.len()).unwrap_or_default() - as usize, - }; - result.push(f); - } - Err(err) => { - iter_error = Some(format!("{err:?}")); - break; - } - } - } - } - Err(err) => { - file_system_corrupt = Some(format!("{err:?}")); - } - } - let mut total: usize = 0; - let mut used: usize = 0; - unsafe { - esp_spiffs_info(storage.as_ptr(), &mut total, &mut used); - } - - FileList { - total, - used, - file_system_corrupt, - files: result, - iter_error, - } + return FileList { + total: 0, + used: 0, + file_system_corrupt: None, + files: Vec::new(), + iter_error: None, + }; + // + // let storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); + // + // let mut file_system_corrupt = None; + // + // let mut iter_error = None; + // let mut result = Vec::new(); + // + // let filepath = Path::new(Self::BASE_PATH); + // let read_dir = fs::read_dir(filepath); + // match read_dir { + // OkStd(read_dir) => { + // for item in read_dir { + // match item { + // OkStd(file) => { + // let f = FileInfo { + // filename: file.file_name().into_string().unwrap(), + // size: file.metadata().map(|it| it.len()).unwrap_or_default() + // as usize, + // }; + // result.push(f); + // } + // Err(err) => { + // iter_error = Some(format!("{err:?}")); + // break; + // } + // } + // } + // } + // Err(err) => { + // file_system_corrupt = Some(format!("{err:?}")); + // } + // } + // let mut total: usize = 0; + // let mut used: usize = 0; + // unsafe { + // esp_spiffs_info(storage.as_ptr(), &mut total, &mut used); + // } + // + // FileList { + // total, + // used, + // file_system_corrupt, + // files: result, + // iter_error, + // } } pub(crate) async fn delete_file(&self, filename: &str) -> anyhow::Result<()> { - let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); - match fs::remove_file(filepath) { - OkStd(_) => anyhow::Ok(()), - Err(err) => { - bail!(format!("{err:?}")) - } - } - } - pub(crate) async fn get_file_handle( - &self, - filename: &str, - write: bool, - ) -> anyhow::Result { - let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); - anyhow::Ok(if write { - File::create(filepath)? - } else { - File::open(filepath)? - }) + bail!("todo"); + // let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); + // match fs::remove_file(filepath) { + // OkStd(_) => anyhow::Ok(()), + // Err(err) => { + // bail!(format!("{err:?}")) + // } + // } } + // pub(crate) async fn get_file_handle( + // &self, + // filename: &str, + // write: bool, + // ) -> anyhow::Result { + // let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename)); + // anyhow::Ok(if write { + // File::create(filepath)? + // } else { + // File::open(filepath)? + // }) + // } pub(crate) fn init_rtc_deepsleep_memory(&self, init_rtc_store: bool, to_config_mode: bool) { if init_rtc_store { @@ -407,200 +437,204 @@ impl Esp<'_> { bail!("Mqtt url was empty") } - let last_will_topic = format!("{}/state", base_topic); - let mqtt_client_config = MqttClientConfiguration { - lwt: Some(LwtConfiguration { - topic: &last_will_topic, - payload: "lost".as_bytes(), - qos: AtLeastOnce, - retain: true, - }), - client_id: Some("plantctrl"), - keep_alive_interval: Some(Duration::from_secs(60 * 60 * 2)), - username: network_config.mqtt_user.as_ref().map(|v| &**v), - password: network_config.mqtt_password.as_ref().map(|v| &**v), - //room for improvement - ..Default::default() - }; - - let mqtt_connected_event_received = Arc::new(AtomicBool::new(false)); - let mqtt_connected_event_ok = Arc::new(AtomicBool::new(false)); - - let round_trip_ok = Arc::new(AtomicBool::new(false)); - let round_trip_topic = format!("{}/internal/roundtrip", base_topic); - let stay_alive_topic = format!("{}/stay_alive", base_topic); - log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic); - - let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone(); - let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone(); - let stay_alive_topic_copy = stay_alive_topic.clone(); - let round_trip_topic_copy = round_trip_topic.clone(); - let round_trip_ok_copy = round_trip_ok.clone(); - let client_id = mqtt_client_config.client_id.unwrap_or("not set"); - log(LogMessage::MqttInfo, 0, 0, client_id, mqtt_url); - let mut client = EspMqttClient::new_cb(mqtt_url, &mqtt_client_config, move |event| { - let payload = event.payload(); - match payload { - embedded_svc::mqtt::client::EventPayload::Received { - id: _, - topic, - data, - details: _, - } => { - let data = String::from_utf8_lossy(data); - if let Some(topic) = topic { - //todo use enums - if topic.eq(round_trip_topic_copy.as_str()) { - round_trip_ok_copy.store(true, std::sync::atomic::Ordering::Relaxed); - } else if topic.eq(stay_alive_topic_copy.as_str()) { - let value = - data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1"); - log(LogMessage::MqttStayAliveRec, 0, 0, &data, ""); - STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed); - } else { - log(LogMessage::UnknownTopic, 0, 0, "", topic); - } - } - } - esp_idf_svc::mqtt::client::EventPayload::Connected(_) => { - mqtt_connected_event_received_copy - .store(true, std::sync::atomic::Ordering::Relaxed); - mqtt_connected_event_ok_copy.store(true, std::sync::atomic::Ordering::Relaxed); - log::info!("Mqtt connected"); - } - esp_idf_svc::mqtt::client::EventPayload::Disconnected => { - mqtt_connected_event_received_copy - .store(true, std::sync::atomic::Ordering::Relaxed); - mqtt_connected_event_ok_copy.store(false, std::sync::atomic::Ordering::Relaxed); - log::info!("Mqtt disconnected"); - } - esp_idf_svc::mqtt::client::EventPayload::Error(esp_error) => { - log::info!("EspMqttError reported {:?}", esp_error); - mqtt_connected_event_received_copy - .store(true, std::sync::atomic::Ordering::Relaxed); - mqtt_connected_event_ok_copy.store(false, std::sync::atomic::Ordering::Relaxed); - log::info!("Mqtt error"); - } - esp_idf_svc::mqtt::client::EventPayload::BeforeConnect => { - log::info!("Mqtt before connect") - } - esp_idf_svc::mqtt::client::EventPayload::Subscribed(_) => { - log::info!("Mqtt subscribed") - } - esp_idf_svc::mqtt::client::EventPayload::Unsubscribed(_) => { - log::info!("Mqtt unsubscribed") - } - esp_idf_svc::mqtt::client::EventPayload::Published(_) => { - log::info!("Mqtt published") - } - esp_idf_svc::mqtt::client::EventPayload::Deleted(_) => { - log::info!("Mqtt deleted") - } - } - })?; - - let mut wait_for_connections_event = 0; - while wait_for_connections_event < 100 { - wait_for_connections_event += 1; - match mqtt_connected_event_received.load(std::sync::atomic::Ordering::Relaxed) { - true => { - log::info!("Mqtt connection callback received, progressing"); - match mqtt_connected_event_ok.load(std::sync::atomic::Ordering::Relaxed) { - true => { - log::info!( - "Mqtt did callback as connected, testing with roundtrip now" - ); - //subscribe to roundtrip - client.subscribe(round_trip_topic.as_str(), ExactlyOnce)?; - client.subscribe(stay_alive_topic.as_str(), ExactlyOnce)?; - //publish to roundtrip - client.publish( - round_trip_topic.as_str(), - ExactlyOnce, - false, - "online_test".as_bytes(), - )?; - - let mut wait_for_roundtrip = 0; - while wait_for_roundtrip < 100 { - wait_for_roundtrip += 1; - match round_trip_ok.load(std::sync::atomic::Ordering::Relaxed) { - true => { - log::info!("Round trip registered, proceeding"); - self.mqtt_client = Some(MqttClient { - mqtt_client: client, - base_topic: base_topic_copy, - }); - return anyhow::Ok(()); - } - false => { - unsafe { vTaskDelay(10) }; - } - } - } - bail!("Mqtt did not complete roundtrip in time"); - } - false => { - bail!("Mqtt did respond but with failure") - } - } - } - false => { - unsafe { vTaskDelay(10) }; - } - } - } - bail!("Mqtt did not fire connection callback in time"); + bail!("todo"); + // + // let last_will_topic = format!("{}/state", base_topic); + // let mqtt_client_config = MqttClientConfiguration { + // lwt: Some(LwtConfiguration { + // topic: &last_will_topic, + // payload: "lost".as_bytes(), + // qos: AtLeastOnce, + // retain: true, + // }), + // client_id: Some("plantctrl"), + // keep_alive_interval: Some(Duration::from_secs(60 * 60 * 2)), + // username: network_config.mqtt_user.as_ref().map(|v| &**v), + // password: network_config.mqtt_password.as_ref().map(|v| &**v), + // //room for improvement + // ..Default::default() + // }; + // + // let mqtt_connected_event_received = Arc::new(AtomicBool::new(false)); + // let mqtt_connected_event_ok = Arc::new(AtomicBool::new(false)); + // + // let round_trip_ok = Arc::new(AtomicBool::new(false)); + // let round_trip_topic = format!("{}/internal/roundtrip", base_topic); + // let stay_alive_topic = format!("{}/stay_alive", base_topic); + // log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic); + // + // let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone(); + // let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone(); + // let stay_alive_topic_copy = stay_alive_topic.clone(); + // let round_trip_topic_copy = round_trip_topic.clone(); + // let round_trip_ok_copy = round_trip_ok.clone(); + // let client_id = mqtt_client_config.client_id.unwrap_or("not set"); + // log(LogMessage::MqttInfo, 0, 0, client_id, mqtt_url); + // let mut client = EspMqttClient::new_cb(mqtt_url, &mqtt_client_config, move |event| { + // let payload = event.payload(); + // match payload { + // embedded_svc::mqtt::client::EventPayload::Received { + // id: _, + // topic, + // data, + // details: _, + // } => { + // let data = String::from_utf8_lossy(data); + // if let Some(topic) = topic { + // //todo use enums + // if topic.eq(round_trip_topic_copy.as_str()) { + // round_trip_ok_copy.store(true, std::sync::atomic::Ordering::Relaxed); + // } else if topic.eq(stay_alive_topic_copy.as_str()) { + // let value = + // data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1"); + // log(LogMessage::MqttStayAliveRec, 0, 0, &data, ""); + // STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed); + // } else { + // log(LogMessage::UnknownTopic, 0, 0, "", topic); + // } + // } + // } + // esp_idf_svc::mqtt::client::EventPayload::Connected(_) => { + // mqtt_connected_event_received_copy + // .store(true, std::sync::atomic::Ordering::Relaxed); + // mqtt_connected_event_ok_copy.store(true, std::sync::atomic::Ordering::Relaxed); + // log::info!("Mqtt connected"); + // } + // esp_idf_svc::mqtt::client::EventPayload::Disconnected => { + // mqtt_connected_event_received_copy + // .store(true, std::sync::atomic::Ordering::Relaxed); + // mqtt_connected_event_ok_copy.store(false, std::sync::atomic::Ordering::Relaxed); + // log::info!("Mqtt disconnected"); + // } + // esp_idf_svc::mqtt::client::EventPayload::Error(esp_error) => { + // log::info!("EspMqttError reported {:?}", esp_error); + // mqtt_connected_event_received_copy + // .store(true, std::sync::atomic::Ordering::Relaxed); + // mqtt_connected_event_ok_copy.store(false, std::sync::atomic::Ordering::Relaxed); + // log::info!("Mqtt error"); + // } + // esp_idf_svc::mqtt::client::EventPayload::BeforeConnect => { + // log::info!("Mqtt before connect") + // } + // esp_idf_svc::mqtt::client::EventPayload::Subscribed(_) => { + // log::info!("Mqtt subscribed") + // } + // esp_idf_svc::mqtt::client::EventPayload::Unsubscribed(_) => { + // log::info!("Mqtt unsubscribed") + // } + // esp_idf_svc::mqtt::client::EventPayload::Published(_) => { + // log::info!("Mqtt published") + // } + // esp_idf_svc::mqtt::client::EventPayload::Deleted(_) => { + // log::info!("Mqtt deleted") + // } + // } + // })?; + // + // let mut wait_for_connections_event = 0; + // while wait_for_connections_event < 100 { + // wait_for_connections_event += 1; + // match mqtt_connected_event_received.load(std::sync::atomic::Ordering::Relaxed) { + // true => { + // log::info!("Mqtt connection callback received, progressing"); + // match mqtt_connected_event_ok.load(std::sync::atomic::Ordering::Relaxed) { + // true => { + // log::info!( + // "Mqtt did callback as connected, testing with roundtrip now" + // ); + // //subscribe to roundtrip + // client.subscribe(round_trip_topic.as_str(), ExactlyOnce)?; + // client.subscribe(stay_alive_topic.as_str(), ExactlyOnce)?; + // //publish to roundtrip + // client.publish( + // round_trip_topic.as_str(), + // ExactlyOnce, + // false, + // "online_test".as_bytes(), + // )?; + // + // let mut wait_for_roundtrip = 0; + // while wait_for_roundtrip < 100 { + // wait_for_roundtrip += 1; + // match round_trip_ok.load(std::sync::atomic::Ordering::Relaxed) { + // true => { + // log::info!("Round trip registered, proceeding"); + // self.mqtt_client = Some(MqttClient { + // mqtt_client: client, + // base_topic: base_topic_copy, + // }); + // return anyhow::Ok(()); + // } + // false => { + // unsafe { vTaskDelay(10) }; + // } + // } + // } + // bail!("Mqtt did not complete roundtrip in time"); + // } + // false => { + // bail!("Mqtt did respond but with failure") + // } + // } + // } + // false => { + // unsafe { vTaskDelay(10) }; + // } + // } + // } + // bail!("Mqtt did not fire connection callback in time"); } pub(crate) async fn mqtt_publish( &mut self, subtopic: &str, message: &[u8], ) -> anyhow::Result<()> { - if self.mqtt_client.is_none() { - return anyhow::Ok(()); - } - if !subtopic.starts_with("/") { - log::info!("Subtopic without / at start {}", subtopic); - bail!("Subtopic without / at start {}", subtopic); - } - if subtopic.len() > 192 { - log::info!("Subtopic exceeds 192 chars {}", subtopic); - bail!("Subtopic exceeds 192 chars {}", subtopic); - } - 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() { - log::info!("Some error assembling full_topic 1"); - bail!("Some error assembling full_topic 1") - }; - if full_topic.push_str(subtopic).is_err() { - log::info!("Some error assembling full_topic 2"); - bail!("Some error assembling full_topic 2") - }; - let publish = client - .mqtt_client - .publish(&full_topic, ExactlyOnce, true, message); - Delay::new(10).delay_ms(50); - match publish { - OkStd(message_id) => { - log::info!( - "Published mqtt topic {} with message {:#?} msgid is {:?}", - full_topic, - String::from_utf8_lossy(message), - message_id - ); - anyhow::Ok(()) - } - Err(err) => { - log::info!( - "Error during mqtt send on topic {} with message {:#?} error is {:?}", - full_topic, - String::from_utf8_lossy(message), - err - ); - Err(err)? - } - } + bail!("todo"); + // + // if self.mqtt_client.is_none() { + // return anyhow::Ok(()); + // } + // if !subtopic.starts_with("/") { + // log::info!("Subtopic without / at start {}", subtopic); + // bail!("Subtopic without / at start {}", subtopic); + // } + // if subtopic.len() > 192 { + // log::info!("Subtopic exceeds 192 chars {}", subtopic); + // bail!("Subtopic exceeds 192 chars {}", subtopic); + // } + // 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() { + // log::info!("Some error assembling full_topic 1"); + // bail!("Some error assembling full_topic 1") + // }; + // if full_topic.push_str(subtopic).is_err() { + // log::info!("Some error assembling full_topic 2"); + // bail!("Some error assembling full_topic 2") + // }; + // let publish = client + // .mqtt_client + // .publish(&full_topic, ExactlyOnce, true, message); + // Timer::after_millis(10).await; + // match publish { + // OkStd(message_id) => { + // log::info!( + // "Published mqtt topic {} with message {:#?} msgid is {:?}", + // full_topic, + // String::from_utf8_lossy(message), + // message_id + // ); + // anyhow::Ok(()) + // } + // Err(err) => { + // log::info!( + // "Error during mqtt send on topic {} with message {:#?} error is {:?}", + // full_topic, + // String::from_utf8_lossy(message), + // err + // ); + // Err(err)? + // } + // } } } diff --git a/rust/src/hal/initial_hal.rs b/rust/src/hal/initial_hal.rs index ab77832..a378e78 100644 --- a/rust/src/hal/initial_hal.rs +++ b/rust/src/hal/initial_hal.rs @@ -1,19 +1,21 @@ +use alloc::vec::Vec; use crate::hal::esp::Esp; use crate::hal::rtc::{BackupHeader, RTCModuleInteraction}; -use crate::hal::water::TankSensor; +//use crate::hal::water::TankSensor; use crate::hal::{deep_sleep, BoardInteraction, FreePeripherals, Sensor}; use crate::{ config::PlantControllerConfig, hal::battery::{BatteryInteraction, NoBatteryMonitor}, }; use anyhow::{bail, Result}; +use async_trait::async_trait; use chrono::{DateTime, Utc}; use embedded_hal::digital::OutputPin; use measurements::{Current, Voltage}; use crate::alloc::boxed::Box; pub struct Initial<'a> { - pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, + //pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, pub(crate) esp: Esp<'a>, pub(crate) config: PlantControllerConfig, pub(crate) battery: Box, @@ -22,44 +24,45 @@ pub struct Initial<'a> { struct NoRTC {} +#[async_trait] impl RTCModuleInteraction for NoRTC { - fn get_backup_info(&mut self) -> Result { + async fn get_backup_info(&mut self) -> Result { bail!("Please configure board revision") } - fn get_backup_config(&mut self) -> Result> { + async fn get_backup_config(&mut self) -> Result> { bail!("Please configure board revision") } - fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> { + async fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> { bail!("Please configure board revision") } - fn get_rtc_time(&mut self) -> Result> { + async fn get_rtc_time(&mut self) -> Result> { bail!("Please configure board revision") } - fn set_rtc_time(&mut self, _time: &DateTime) -> Result<()> { + async fn set_rtc_time(&mut self, _time: &DateTime) -> Result<()> { bail!("Please configure board revision") } } pub(crate) fn create_initial_board( - free_pins: FreePeripherals, + //free_pins: FreePeripherals, fs_mount_error: bool, config: PlantControllerConfig, esp: Esp<'static>, ) -> Result + Send>> { log::info!("Start initial"); - let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?; - general_fault.set_pull(Pull::Floating)?; - general_fault.set_low()?; - - if fs_mount_error { - general_fault.set_high()? - } + // let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?; + // general_fault.set_pull(Pull::Floating)?; + // general_fault.set_low()?; + // + // if fs_mount_error { + // general_fault.set_high()? + // } let v = Initial { - general_fault, + //general_fault, config, esp, battery: Box::new(NoBatteryMonitor {}), @@ -68,10 +71,11 @@ pub(crate) fn create_initial_board( Ok(Box::new(v)) } +#[async_trait] impl<'a> BoardInteraction<'a> for Initial<'a> { - fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> { - None - } + // fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> { + // None + // } fn get_esp(&mut self) -> &mut Esp<'a> { &mut self.esp @@ -103,41 +107,42 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { bail!("Please configure board revision") } - fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> { + async fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> { bail!("Please configure board revision") } - fn pump_current(&mut self, _plant: usize) -> Result { + async fn pump_current(&mut self, _plant: usize) -> Result { bail!("Please configure board revision") } - fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> { + async fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> { bail!("Please configure board revision") } - fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result { + async fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result { bail!("Please configure board revision") } - fn general_fault(&mut self, enable: bool) { - let _ = self.general_fault.set_state(enable.into()); + async fn general_fault(&mut self, enable: bool) { + //let _ = self.general_fault.set_state(enable.into()); } - fn test(&mut self) -> Result<()> { + async fn test(&mut self) -> Result<()> { bail!("Please configure board revision") } - fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> { + async fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> { self.config = config; - self.esp.save_config(&self.config)?; + //TODO +// self.esp.save_config(&self.config)?; anyhow::Ok(()) } - fn get_mptt_voltage(&mut self) -> Result { + async fn get_mptt_voltage(&mut self) -> Result { bail!("Please configure board revision") } - fn get_mptt_current(&mut self) -> Result { + async fn get_mptt_current(&mut self) -> Result { bail!("Please configure board revision") } } diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index 8663791..22370c8 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -2,55 +2,33 @@ pub(crate) mod battery; mod esp; mod initial_hal; mod rtc; -mod v3_hal; -mod v4_hal; -mod v4_sensor; -mod water; +//mod water; use crate::alloc::string::ToString; -use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; -use crate::hal::water::TankSensor; +use crate::hal::rtc::{RTCModuleInteraction}; +//use crate::hal::water::TankSensor; use crate::{ config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, hal::{ - battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor}, + battery::{BatteryInteraction, NoBatteryMonitor}, esp::Esp, }, log::{log, LogMessage}, }; use alloc::boxed::Box; +use alloc::format; +use core::marker::PhantomData; use anyhow::{Ok, Result}; use async_trait::async_trait; -use battery::BQ34Z100G1; +//use battery::BQ34Z100G1; use bq34z100::Bq34z100g1Driver; use ds323x::{DateTimeAccess, Ds323x}; use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex}; use embassy_sync::mutex::Mutex; -use embassy_sync::{LazyLock, Mutex}; -use embedded_hal_bus::i2c::MutexDevice; -use esp_idf_hal::can::CAN; -use esp_idf_hal::pcnt::PCNT1; -use esp_idf_hal::{ - adc::ADC1, - delay::Delay, - gpio::{ - Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, - Gpio2, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3, - Gpio30, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, IOPin, PinDriver, Pull, - }, - i2c::{APBTickType, I2cConfig, I2cDriver}, - pcnt::PCNT0, - prelude::Peripherals, - reset::ResetReason, - units::FromValueType, -}; -use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition, wifi::EspWifi}; -use esp_idf_sys::{ - esp_deep_sleep, esp_restart, esp_sleep_enable_ext1_wakeup, - esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, -}; -use esp_ota::mark_app_valid; +use embassy_sync::lazy_lock::LazyLock; +use esp_hal::clock::CpuClock; +use esp_hal::timer::systimer::SystemTimer; use measurements::{Current, Voltage}; //Only support for 8 right now! @@ -58,24 +36,27 @@ pub const PLANT_COUNT: usize = 8; const TANK_MULTI_SAMPLE: usize = 11; -pub static I2C_DRIVER: LazyLock>> = LazyLock::new(PlantHal::create_i2c); +//pub static I2C_DRIVER: LazyLock>> = LazyLock::new(PlantHal::create_i2c); fn deep_sleep(duration_in_ms: u64) -> ! { - unsafe { - //if we don't do this here, we might just revert newly flashed firmware - mark_app_valid(); - //allow early wakeup by pressing the boot button - if duration_in_ms == 0 { - esp_restart(); - } else { - //configure gpio 1 to wakeup on low, reused boot button for this - esp_sleep_enable_ext1_wakeup( - 0b10u64, - esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, - ); - esp_deep_sleep(duration_in_ms); + //unsafe { + // //if we don't do this here, we might just revert newly flashed firmware + // mark_app_valid(); + // //allow early wakeup by pressing the boot button + // if duration_in_ms == 0 { + // esp_restart(); + // } else { + // //configure gpio 1 to wakeup on low, reused boot button for this + // esp_sleep_enable_ext1_wakeup( + // 0b10u64, + // esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, + // ); + // esp_deep_sleep(duration_in_ms); + // } + loop { + todo!() } - }; + //}; } #[derive(Debug, PartialEq)] @@ -92,7 +73,7 @@ pub struct HAL<'a> { #[async_trait] pub trait BoardInteraction<'a> { - fn get_tank_sensor(&mut self) -> Option<&mut TankSensor>; + //fn get_tank_sensor(&mut self) -> Option<&mut TankSensor>; fn get_esp(&mut self) -> &mut Esp<'a>; fn get_config(&mut self) -> &PlantControllerConfig; fn get_battery_monitor(&mut self) -> &mut Box; @@ -114,13 +95,14 @@ pub trait BoardInteraction<'a> { async fn get_mptt_current(&mut self) -> anyhow::Result; } + impl dyn BoardInteraction<'_> { //the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons async fn _progress(&mut self, counter: u32) { let even = counter % 2 == 0; let current = counter / (PLANT_COUNT as u32); for led in 0..PLANT_COUNT { - self.fault(led, current == led as u32).unwrap(); + self.fault(led, current == led as u32).await.unwrap(); } let _ = self.general_fault(even.into()); } @@ -128,134 +110,138 @@ impl dyn BoardInteraction<'_> { #[allow(dead_code)] pub struct FreePeripherals { - pub gpio0: Gpio0, - pub gpio1: Gpio1, - pub gpio2: Gpio2, - pub gpio3: Gpio3, - pub gpio4: Gpio4, - pub gpio5: Gpio5, - pub gpio6: Gpio6, - pub gpio7: Gpio7, - pub gpio8: Gpio8, - //config button here - pub gpio10: Gpio10, - pub gpio11: Gpio11, - pub gpio12: Gpio12, - pub gpio13: Gpio13, - pub gpio14: Gpio14, - pub gpio15: Gpio15, - pub gpio16: Gpio16, - pub gpio17: Gpio17, - pub gpio18: Gpio18, - //i2c here - pub gpio21: Gpio21, - pub gpio22: Gpio22, - pub gpio23: Gpio23, - pub gpio24: Gpio24, - pub gpio25: Gpio25, - pub gpio26: Gpio26, - pub gpio27: Gpio27, - pub gpio28: Gpio28, - pub gpio29: Gpio29, - pub gpio30: Gpio30, - pub pcnt0: PCNT0, - pub pcnt1: PCNT1, - pub adc1: ADC1, - pub can: CAN, + // pub gpio0: Gpio0, + // pub gpio1: Gpio1, + // pub gpio2: Gpio2, + // pub gpio3: Gpio3, + // pub gpio4: Gpio4, + // pub gpio5: Gpio5, + // pub gpio6: Gpio6, + // pub gpio7: Gpio7, + // pub gpio8: Gpio8, + // //config button here + // pub gpio10: Gpio10, + // pub gpio11: Gpio11, + // pub gpio12: Gpio12, + // pub gpio13: Gpio13, + // pub gpio14: Gpio14, + // pub gpio15: Gpio15, + // pub gpio16: Gpio16, + // pub gpio17: Gpio17, + // pub gpio18: Gpio18, + // //i2c here + // pub gpio21: Gpio21, + // pub gpio22: Gpio22, + // pub gpio23: Gpio23, + // pub gpio24: Gpio24, + // pub gpio25: Gpio25, + // pub gpio26: Gpio26, + // pub gpio27: Gpio27, + // pub gpio28: Gpio28, + // pub gpio29: Gpio29, + // pub gpio30: Gpio30, + // pub pcnt0: PCNT0, + // pub pcnt1: PCNT1, + // pub adc1: ADC1, + // pub can: CAN, } impl PlantHal { - fn create_i2c() -> Mutex> { - let peripherals = unsafe { Peripherals::new() }; - - let config = I2cConfig::new() - .scl_enable_pullup(true) - .sda_enable_pullup(true) - .baudrate(100_u32.kHz().into()) - .timeout(APBTickType::from(Duration::from_millis(100))); - - let i2c = peripherals.i2c0; - let scl = peripherals.pins.gpio19.downgrade(); - let sda = peripherals.pins.gpio20.downgrade(); - - Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) - } + // fn create_i2c() -> Mutex> { + // let peripherals = unsafe { Peripherals::new() }; + // + // let config = I2cConfig::new() + // .scl_enable_pullup(true) + // .sda_enable_pullup(true) + // .baudrate(100_u32.kHz().into()) + // .timeout(APBTickType::from(Duration::from_millis(100))); + // + // let i2c = peripherals.i2c0; + // let scl = peripherals.pins.gpio19.downgrade(); + // let sda = peripherals.pins.gpio20.downgrade(); + // + // Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) + // } pub fn create() -> Result>> { - let peripherals = Peripherals::take()?; - let sys_loop = EspSystemEventLoop::take()?; - let nvs = EspDefaultNvsPartition::take()?; - let wifi_driver = EspWifi::new(peripherals.modem, sys_loop, Some(nvs))?; + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); - let mut boot_button = PinDriver::input(peripherals.pins.gpio9.downgrade())?; - boot_button.set_pull(Pull::Floating)?; + esp_alloc::heap_allocator!(size: 64 * 1024); + let timer0 = SystemTimer::new(peripherals.SYSTIMER); + esp_hal_embassy::init(timer0.alarm0); - let free_pins = FreePeripherals { - can: peripherals.can, - adc1: peripherals.adc1, - pcnt0: peripherals.pcnt0, - pcnt1: peripherals.pcnt1, - gpio0: peripherals.pins.gpio0, - gpio1: peripherals.pins.gpio1, - gpio2: peripherals.pins.gpio2, - gpio3: peripherals.pins.gpio3, - gpio4: peripherals.pins.gpio4, - gpio5: peripherals.pins.gpio5, - gpio6: peripherals.pins.gpio6, - gpio7: peripherals.pins.gpio7, - gpio8: peripherals.pins.gpio8, - gpio10: peripherals.pins.gpio10, - gpio11: peripherals.pins.gpio11, - gpio12: peripherals.pins.gpio12, - gpio13: peripherals.pins.gpio13, - gpio14: peripherals.pins.gpio14, - gpio15: peripherals.pins.gpio15, - gpio16: peripherals.pins.gpio16, - gpio17: peripherals.pins.gpio17, - gpio18: peripherals.pins.gpio18, - gpio21: peripherals.pins.gpio21, - gpio22: peripherals.pins.gpio22, - gpio23: peripherals.pins.gpio23, - gpio24: peripherals.pins.gpio24, - gpio25: peripherals.pins.gpio25, - gpio26: peripherals.pins.gpio26, - gpio27: peripherals.pins.gpio27, - gpio28: peripherals.pins.gpio28, - gpio29: peripherals.pins.gpio29, - gpio30: peripherals.pins.gpio30, - }; + // let mut boot_button = PinDriver::input(peripherals.pins.gpio9.downgrade())?; + // boot_button.set_pull(Pull::Floating)?; + // + // let free_pins = FreePeripherals { + // can: peripherals.can, + // adc1: peripherals.adc1, + // pcnt0: peripherals.pcnt0, + // pcnt1: peripherals.pcnt1, + // gpio0: peripherals.pins.gpio0, + // gpio1: peripherals.pins.gpio1, + // gpio2: peripherals.pins.gpio2, + // gpio3: peripherals.pins.gpio3, + // gpio4: peripherals.pins.gpio4, + // gpio5: peripherals.pins.gpio5, + // gpio6: peripherals.pins.gpio6, + // gpio7: peripherals.pins.gpio7, + // gpio8: peripherals.pins.gpio8, + // gpio10: peripherals.pins.gpio10, + // gpio11: peripherals.pins.gpio11, + // gpio12: peripherals.pins.gpio12, + // gpio13: peripherals.pins.gpio13, + // gpio14: peripherals.pins.gpio14, + // gpio15: peripherals.pins.gpio15, + // gpio16: peripherals.pins.gpio16, + // gpio17: peripherals.pins.gpio17, + // gpio18: peripherals.pins.gpio18, + // gpio21: peripherals.pins.gpio21, + // gpio22: peripherals.pins.gpio22, + // gpio23: peripherals.pins.gpio23, + // gpio24: peripherals.pins.gpio24, + // gpio25: peripherals.pins.gpio25, + // gpio26: peripherals.pins.gpio26, + // gpio27: peripherals.pins.gpio27, + // gpio28: peripherals.pins.gpio28, + // gpio29: peripherals.pins.gpio29, + // gpio30: peripherals.pins.gpio30, + // }; + // let mut esp = Esp { - mqtt_client: None, - wifi_driver, - boot_button, - delay: Delay::new(1000), - }; + mqtt_client: None, + dummy: PhantomData::default() + // wifi_driver, + // boot_button + }; //init,reset rtc memory depending on cause let mut init_rtc_store: bool = false; let mut to_config_mode: bool = false; - let reasons = ResetReason::get(); - match reasons { - ResetReason::Software => {} - ResetReason::ExternalPin => {} - ResetReason::Watchdog => { - init_rtc_store = true; - } - ResetReason::Sdio => init_rtc_store = true, - ResetReason::Panic => init_rtc_store = true, - ResetReason::InterruptWatchdog => init_rtc_store = true, - ResetReason::PowerOn => init_rtc_store = true, - ResetReason::Unknown => init_rtc_store = true, - ResetReason::Brownout => init_rtc_store = true, - ResetReason::TaskWatchdog => init_rtc_store = true, - ResetReason::DeepSleep => {} - ResetReason::USBPeripheral => { - init_rtc_store = true; - to_config_mode = true; - } - ResetReason::JTAG => init_rtc_store = true, - }; + let reasons = ""; + // let reasons = ResetReason::get(); + // match reasons { + // ResetReason::Software => {} + // ResetReason::ExternalPin => {} + // ResetReason::Watchdog => { + // init_rtc_store = true; + // } + // ResetReason::Sdio => init_rtc_store = true, + // ResetReason::Panic => init_rtc_store = true, + // ResetReason::InterruptWatchdog => init_rtc_store = true, + // ResetReason::PowerOn => init_rtc_store = true, + // ResetReason::Unknown => init_rtc_store = true, + // ResetReason::Brownout => init_rtc_store = true, + // ResetReason::TaskWatchdog => init_rtc_store = true, + // ResetReason::DeepSleep => {} + // ResetReason::USBPeripheral => { + // init_rtc_store = true; + // to_config_mode = true; + // } + // ResetReason::JTAG => init_rtc_store = true, + // }; log( LogMessage::ResetReason, init_rtc_store as u32, @@ -270,70 +256,76 @@ impl PlantHal { let config = esp.load_config(); log::info!("Init rtc driver"); - let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER)); + // let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER)); + // + // log::info!("Init rtc eeprom driver"); + // let eeprom = { + // Eeprom24x::new_24x32( + // MutexDevice::new(&I2C_DRIVER), + // SlaveAddr::Alternative(true, true, true), + // ) + // }; + // let rtc_time = rtc.datetime(); + // match rtc_time { + // OkStd(tt) => { + // log::info!("Rtc Module reports time at UTC {}", tt); + // } + // Err(err) => { + // log::info!("Rtc Module could not be read {:?}", err); + // } + // } - log::info!("Init rtc eeprom driver"); - let eeprom = { - Eeprom24x::new_24x32( - MutexDevice::new(&I2C_DRIVER), - SlaveAddr::Alternative(true, true, true), - ) - }; - let rtc_time = rtc.datetime(); - match rtc_time { - OkStd(tt) => { - log::info!("Rtc Module reports time at UTC {}", tt); - } - Err(err) => { - log::info!("Rtc Module could not be read {:?}", err); - } - } - - let storage = Storage::new(eeprom, Delay::new(1000)); - let rtc_module: Box = - Box::new(DS3231Module { rtc, storage }) as Box; + // let storage = Storage::new(eeprom, Delay::new(1000)); + //let rtc_module: Box = + // Box::new(DS3231Module { rtc, storage }) as Box; let hal = match config { Result::Ok(config) => { let battery_interaction: Box = match config.hardware.battery { BatteryBoardVersion::Disabled => Box::new(NoBatteryMonitor {}), - BatteryBoardVersion::BQ34Z100G1 => { - let mut battery_driver = Bq34z100g1Driver { - i2c: MutexDevice::new(&I2C_DRIVER), - delay: Delay::new(0), - flash_block_data: [0; 32], - }; - let status = print_battery_bq34z100(&mut battery_driver); - match status { - OkStd(_) => {} - Err(err) => { - log( - LogMessage::BatteryCommunicationError, - 0u32, - 0, - "", - &format!("{err:?})"), - ); - } - } - Box::new(BQ34Z100G1 { battery_driver }) - } + // BatteryBoardVersion::BQ34Z100G1 => { + // let mut battery_driver = Bq34z100g1Driver { + // i2c: MutexDevice::new(&I2C_DRIVER), + // delay: Delay::new(0), + // flash_block_data: [0; 32], + // }; + // let status = print_battery_bq34z100(&mut battery_driver); + // match status { + // Ok(_) => {} + // Err(err) => { + // log( + // LogMessage::BatteryCommunicationError, + // 0u32, + // 0, + // "", + // &format!("{err:?})"), + // ); + // } + // } + // Box::new(BQ34Z100G1 { battery_driver }) + // } BatteryBoardVersion::WchI2cSlave => { // TODO use correct implementation once availible Box::new(NoBatteryMonitor {}) } + _ => { + todo!() + } }; let board_hal: Box = match config.hardware.board { BoardVersion::INITIAL => { - initial_hal::create_initial_board(free_pins, fs_mount_error, config, esp)? + initial_hal::create_initial_board(fs_mount_error, config, esp)? } - BoardVersion::V3 => { - v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)? - } - BoardVersion::V4 => { - v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)? + // BoardVersion::V3 => { + // v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)? + // } + // BoardVersion::V4 => { + // v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)? + // } + _ => { + todo!() } }; @@ -349,7 +341,6 @@ impl PlantHal { ); HAL { board_hal: initial_hal::create_initial_board( - free_pins, fs_mount_error, PlantControllerConfig::default(), esp, diff --git a/rust/src/hal/rtc.rs b/rust/src/hal/rtc.rs index 9175e59..154dc61 100644 --- a/rust/src/hal/rtc.rs +++ b/rust/src/hal/rtc.rs @@ -1,133 +1,138 @@ use crate::hal::Box; use alloc::vec::Vec; -use anyhow::{anyhow, bail}; use async_trait::async_trait; -use bincode::config::Configuration; -use bincode::{config, Decode, Encode}; use chrono::{DateTime, Utc}; -use ds323x::{DateTimeAccess, Ds323x}; -use eeprom24x::addr_size::TwoBytes; -use eeprom24x::page_size::B32; -use eeprom24x::unique_serial::No; -use eeprom24x::Storage; -use embedded_storage::ReadStorage as embedded_storage_ReadStorage; -use embedded_storage::Storage as embedded_storage_Storage; -use serde::{Deserialize, Serialize}; - -const X25: crc::Crc = crc::Crc::::new(&crc::CRC_16_IBM_SDLC); -const CONFIG: Configuration = config::standard(); +// use crate::hal::Box; +// use alloc::vec::Vec; +// use anyhow::{anyhow, bail}; +// use async_trait::async_trait; +// use bincode::config::Configuration; +// use bincode::{config, Decode, Encode}; +// use chrono::{DateTime, Utc}; +// use ds323x::{DateTimeAccess, Ds323x}; +// use eeprom24x::addr_size::TwoBytes; +// use eeprom24x::page_size::B32; +// use eeprom24x::unique_serial::No; +// use eeprom24x::Storage; +// use embedded_storage::ReadStorage as embedded_storage_ReadStorage; +// use embedded_storage::Storage as embedded_storage_Storage; +// use serde::{Deserialize, Serialize}; +// +// const X25: crc::Crc = crc::Crc::::new(&crc::CRC_16_IBM_SDLC); +// const CONFIG: Configuration = config::standard(); +// #[async_trait] pub trait RTCModuleInteraction { - async fn get_backup_info(&mut self) -> anyhow::Result; - async fn get_backup_config(&mut self) -> anyhow::Result>; - async fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; - async fn get_rtc_time(&mut self) -> anyhow::Result>; - async fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()>; + async fn get_backup_info(&mut self) -> anyhow::Result; + async fn get_backup_config(&mut self) -> anyhow::Result>; + async fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>; + async fn get_rtc_time(&mut self) -> anyhow::Result>; + async fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()>; } - -const BACKUP_HEADER_MAX_SIZE: usize = 64; -#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)] +// +// const BACKUP_HEADER_MAX_SIZE: usize = 64; +// #[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)] pub struct BackupHeader { pub timestamp: i64, crc16: u16, pub size: u16, } - -pub struct DS3231Module<'a> { - pub(crate) rtc: - Ds323x>>, ds323x::ic::DS3231>, - - pub(crate) storage: Storage>, B32, TwoBytes, No, Delay>, -} - -impl RTCModuleInteraction for DS3231Module<'_> { - fn get_backup_info(&mut self) -> anyhow::Result { - let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; - - self.storage - .read(0, &mut header_page_buffer) - .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; - - let (header, len): (BackupHeader, usize) = - bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; - - log::info!("Raw header is {:?} with size {}", header_page_buffer, len); - anyhow::Ok(header) - } - - fn get_backup_config(&mut self) -> anyhow::Result> { - let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; - - self.storage - .read(0, &mut header_page_buffer) - .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; - let (header, _header_size): (BackupHeader, usize) = - bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; - - let mut data_buffer = vec![0_u8; header.size as usize]; - //read the specified number of bytes after the header - self.storage - .read(BACKUP_HEADER_MAX_SIZE as u32, &mut data_buffer) - .map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?; - - let checksum = X25.checksum(&data_buffer); - if checksum != header.crc16 { - bail!( - "Invalid checksum, got {} but expected {}", - checksum, - header.crc16 - ); - } - - anyhow::Ok(data_buffer) - } - fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()> { - let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; - - let time = self.get_rtc_time()?.timestamp_millis(); - let checksum = X25.checksum(bytes); - - let header = BackupHeader { - crc16: checksum, - timestamp: time, - size: bytes.len() as u16, - }; - let config = config::standard(); - let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; - log::info!( - "Raw header is {:?} with size {}", - header_page_buffer, - encoded - ); - self.storage - .write(0, &header_page_buffer) - .map_err(|err| anyhow!("Error writing header {:?}", err))?; - - //write rest after the header - self.storage - .write(BACKUP_HEADER_MAX_SIZE as u32, &bytes) - .map_err(|err| anyhow!("Error writing body {:?}", err))?; - - anyhow::Ok(()) - } - - fn get_rtc_time(&mut self) -> anyhow::Result> { - match self.rtc.datetime() { - OkStd(rtc_time) => anyhow::Ok(rtc_time.and_utc()), - Err(err) => { - bail!("Error getting rtc time {:?}", err) - } - } - } - - fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()> { - let naive_time = time.naive_utc(); - match self.rtc.set_datetime(&naive_time) { - OkStd(_) => anyhow::Ok(()), - Err(err) => { - bail!("Error getting rtc time {:?}", err) - } - } - } -} +// +// pub struct DS3231Module<'a> { +// pub(crate) rtc: +// Ds323x>>, ds323x::ic::DS3231>, +// +// pub(crate) storage: Storage>, B32, TwoBytes, No, Delay>, +// } +// +// impl RTCModuleInteraction for DS3231Module<'_> { +// fn get_backup_info(&mut self) -> anyhow::Result { +// let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; +// +// self.storage +// .read(0, &mut header_page_buffer) +// .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; +// +// let (header, len): (BackupHeader, usize) = +// bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; +// +// log::info!("Raw header is {:?} with size {}", header_page_buffer, len); +// anyhow::Ok(header) +// } +// +// fn get_backup_config(&mut self) -> anyhow::Result> { +// let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; +// +// self.storage +// .read(0, &mut header_page_buffer) +// .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; +// let (header, _header_size): (BackupHeader, usize) = +// bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; +// +// let mut data_buffer = vec![0_u8; header.size as usize]; +// //read the specified number of bytes after the header +// self.storage +// .read(BACKUP_HEADER_MAX_SIZE as u32, &mut data_buffer) +// .map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?; +// +// let checksum = X25.checksum(&data_buffer); +// if checksum != header.crc16 { +// bail!( +// "Invalid checksum, got {} but expected {}", +// checksum, +// header.crc16 +// ); +// } +// +// anyhow::Ok(data_buffer) +// } +// fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()> { +// let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; +// +// let time = self.get_rtc_time()?.timestamp_millis(); +// let checksum = X25.checksum(bytes); +// +// let header = BackupHeader { +// crc16: checksum, +// timestamp: time, +// size: bytes.len() as u16, +// }; +// let config = config::standard(); +// let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; +// log::info!( +// "Raw header is {:?} with size {}", +// header_page_buffer, +// encoded +// ); +// self.storage +// .write(0, &header_page_buffer) +// .map_err(|err| anyhow!("Error writing header {:?}", err))?; +// +// //write rest after the header +// self.storage +// .write(BACKUP_HEADER_MAX_SIZE as u32, &bytes) +// .map_err(|err| anyhow!("Error writing body {:?}", err))?; +// +// anyhow::Ok(()) +// } +// +// fn get_rtc_time(&mut self) -> anyhow::Result> { +// match self.rtc.datetime() { +// OkStd(rtc_time) => anyhow::Ok(rtc_time.and_utc()), +// Err(err) => { +// bail!("Error getting rtc time {:?}", err) +// } +// } +// } +// +// fn set_rtc_time(&mut self, time: &DateTime) -> anyhow::Result<()> { +// let naive_time = time.naive_utc(); +// match self.rtc.set_datetime(&naive_time) { +// OkStd(_) => anyhow::Ok(()), +// Err(err) => { +// bail!("Error getting rtc time {:?}", err) +// } +// } +// } +// } diff --git a/rust/src/hal/water.rs b/rust/src/hal/water.rs deleted file mode 100644 index 722d2e3..0000000 --- a/rust/src/hal/water.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::hal::TANK_MULTI_SAMPLE; -use anyhow::{anyhow, bail}; -use ds18b20::Ds18b20; -use esp_idf_hal::adc::oneshot::config::AdcChannelConfig; -use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver}; -use esp_idf_hal::adc::{attenuation, Resolution, ADC1}; -use esp_idf_hal::delay::Delay; -use esp_idf_hal::gpio::{AnyIOPin, AnyInputPin, Gpio5, InputOutput, PinDriver, Pull}; -use esp_idf_hal::pcnt::{ - PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PCNT1, -}; -use esp_idf_sys::EspError; -use one_wire_bus::OneWire; - -pub struct TankSensor<'a> { - one_wire_bus: OneWire>, - tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, ADC1>>, - tank_power: PinDriver<'a, AnyIOPin, InputOutput>, - flow_counter: PcntDriver<'a>, - delay: Delay, -} - -impl<'a> TankSensor<'a> { - pub(crate) fn create( - one_wire_pin: AnyIOPin, - adc1: ADC1, - gpio5: Gpio5, - tank_power_pin: AnyIOPin, - flow_sensor_pin: AnyIOPin, - pcnt1: PCNT1, - ) -> anyhow::Result> { - let mut one_wire_pin = - PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin"); - one_wire_pin - .set_pull(Pull::Floating) - .expect("Failed to set pull"); - - let adc_config = AdcChannelConfig { - attenuation: attenuation::DB_11, - resolution: Resolution::Resolution12Bit, - calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve, - }; - let tank_driver = AdcDriver::new(adc1).expect("Failed to configure ADC"); - let tank_channel = AdcChannelDriver::new(tank_driver, gpio5, &adc_config) - .expect("Failed to configure ADC channel"); - - let mut tank_power = - PinDriver::input_output(tank_power_pin).expect("Failed to configure pin"); - tank_power - .set_pull(Pull::Floating) - .expect("Failed to set pull"); - - let one_wire_bus = - OneWire::new(one_wire_pin).expect("OneWire bus did not pull up after release"); - - let mut flow_counter = PcntDriver::new( - pcnt1, - Some(flow_sensor_pin), - Option::::None, - Option::::None, - Option::::None, - )?; - - flow_counter.channel_config( - PcntChannel::Channel1, - PinIndex::Pin0, - PinIndex::Pin1, - &PcntChannelConfig { - lctrl_mode: PcntControlMode::Keep, - hctrl_mode: PcntControlMode::Keep, - pos_mode: PcntCountMode::Increment, - neg_mode: PcntCountMode::Hold, - counter_h_lim: i16::MAX, - counter_l_lim: 0, - }, - )?; - - Ok(TankSensor { - one_wire_bus, - tank_channel, - tank_power, - flow_counter, - delay: Default::default(), - }) - } - - pub fn reset_flow_meter(&mut self) { - self.flow_counter.counter_pause().unwrap(); - self.flow_counter.counter_clear().unwrap(); - } - - pub fn start_flow_meter(&mut self) { - self.flow_counter.counter_resume().unwrap(); - } - - pub fn get_flow_meter_value(&mut self) -> i16 { - self.flow_counter.get_counter_value().unwrap() - } - - pub fn stop_flow_meter(&mut self) -> i16 { - self.flow_counter.counter_pause().unwrap(); - self.get_flow_meter_value() - } - - pub async fn water_temperature_c(&mut self) -> anyhow::Result { - //multisample should be moved to water_temperature_c - let mut attempt = 1; - let water_temp: Result = loop { - let temp = self.single_temperature_c(); - match &temp { - Ok(res) => { - log::info!("Water temp is {}", res); - break temp; - } - Err(err) => { - log::info!("Could not get water temp {} attempt {}", err, attempt) - } - } - if attempt == 5 { - break temp; - } - attempt += 1; - }; - water_temp - } - - async fn single_temperature_c(&mut self) -> anyhow::Result { - self.one_wire_bus - .reset(&mut self.delay) - .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; - let first = self.one_wire_bus.devices(false, &mut self.delay).next(); - if first.is_none() { - bail!("Not found any one wire Ds18b20"); - } - let device_address = first - .unwrap() - .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; - - let water_temp_sensor = Ds18b20::new::(device_address) - .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; - - water_temp_sensor - .start_temp_measurement(&mut self.one_wire_bus, &mut self.delay) - .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; - ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut self.delay); - let sensor_data = water_temp_sensor - .read_data(&mut self.one_wire_bus, &mut self.delay) - .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; - if sensor_data.temperature == 85_f32 { - bail!("Ds18b20 dummy temperature returned"); - } - anyhow::Ok(sensor_data.temperature / 10_f32) - } - - pub async fn tank_sensor_voltage(&mut self) -> anyhow::Result { - self.tank_power.set_high()?; - //let stabilize - self.delay.delay_ms(100); - - let mut store = [0_u16; TANK_MULTI_SAMPLE]; - for multisample in 0..TANK_MULTI_SAMPLE { - let value = self.tank_channel.read()?; - store[multisample] = value; - } - self.tank_power.set_low()?; - - store.sort(); - let median_mv = store[6] as f32 / 1000_f32; - anyhow::Ok(median_mv) - } -} diff --git a/rust/src/log/mod.rs b/rust/src/log/mod.rs index 3658548..d20027d 100644 --- a/rust/src/log/mod.rs +++ b/rust/src/log/mod.rs @@ -1,14 +1,13 @@ +use crate::vec; use alloc::string::ToString; use alloc::vec::Vec; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::lazy_lock::LazyLock; +use embassy_sync::mutex::Mutex; use serde::Serialize; -use std::{collections::HashMap, sync::Mutex}; -use strum::EnumIter; -use strum_macros::IntoStaticStr; +use strum_macros::{EnumIter, IntoStaticStr}; -use esp_idf_svc::systime::EspSystemTime; -use once_cell::sync::Lazy; use ringbuffer::{ConstGenericRingBuffer, RingBuffer}; -use text_template::Template; use unit_enum::UnitEnum; const TXT_SHORT_LENGTH: usize = 8; @@ -20,8 +19,8 @@ const BUFFER_SIZE: usize = 220; static mut BUFFER: ConstGenericRingBuffer = ConstGenericRingBuffer::::new(); #[allow(static_mut_refs)] -static BUFFER_ACCESS: Lazy>> = - Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) }); +static BUFFER_ACCESS: LazyLock>> = + LazyLock::new(|| unsafe { Mutex::new(&mut BUFFER) }); #[derive(Serialize, Debug, Clone)] pub struct LogEntry { @@ -33,11 +32,11 @@ pub struct LogEntry { pub txt_long: heapless::String, } -pub fn init() { +pub async fn init() { unsafe { BUFFER = ConstGenericRingBuffer::::new(); }; - let mut access = BUFFER_ACCESS.lock().unwrap(); + let mut access = BUFFER_ACCESS.get().lock().await; access.drain().for_each(|_| {}); } @@ -59,8 +58,8 @@ fn limit_length(input: &str, target: &mut heapless::String Vec { - let buffer = BUFFER_ACCESS.lock().unwrap(); +pub async fn get_log() -> Vec { + let buffer = BUFFER_ACCESS.get().lock().await; let mut read_copy = Vec::new(); for entry in buffer.iter() { let copy = entry.clone(); @@ -70,32 +69,35 @@ pub fn get_log() -> Vec { read_copy } -pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) { +pub async fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) { let mut txt_short_stack: heapless::String = heapless::String::new(); let mut txt_long_stack: heapless::String = heapless::String::new(); limit_length(txt_short, &mut txt_short_stack); limit_length(txt_long, &mut txt_long_stack); - let time = EspSystemTime {}.now().as_millis() as u64; + //TODO + let time = 0; + // let time = EspSystemTime {}.now().as_millis() as u64; + // let ordinal = message_key.ordinal() as u16; - let template_string: &str = message_key.into(); - - let mut values: HashMap<&str, &str> = HashMap::new(); - let number_a_str = number_a.to_string(); - let number_b_str = number_b.to_string(); - - values.insert("number_a", &number_a_str); - values.insert("number_b", &number_b_str); - values.insert("txt_short", txt_short); - values.insert("txt_long", txt_long); - - let template = Template::from(template_string); - let serial_entry = template.fill_in(&values); - - log::info!("{serial_entry}"); - //TODO push to mqtt? + // let template_string: &str = message_key.into(); + // + // let mut values: HashMap<&str, &str> = HashMap::new(); + // let number_a_str = number_a.to_string(); + // let number_b_str = number_b.to_string(); + // + // values.insert("number_a", &number_a_str); + // values.insert("number_b", &number_b_str); + // values.insert("txt_short", txt_short); + // values.insert("txt_long", txt_long); + // + // let template = Template::from(template_string); + // let serial_entry = template.fill_in(&values); + // + // log::info!("{serial_entry}"); + // //TODO push to mqtt? let entry = LogEntry { timestamp: time, @@ -106,29 +108,10 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &st txt_long: txt_long_stack, }; - let mut buffer = BUFFER_ACCESS.lock().unwrap(); + let mut buffer = BUFFER_ACCESS.get().lock().await; buffer.push(entry); } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn within_limit() { - let test = "12345678"; - - let mut txt_short_stack: heapless::String = heapless::String::new(); - let mut txt_long_stack: heapless::String = heapless::String::new(); - limit_length(test, &mut txt_short_stack); - limit_length(test, &mut txt_long_stack); - - assert_eq!(txt_short_stack.as_str(), test); - assert_eq!(txt_long_stack.as_str(), test); - } -} - -#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)] +#[derive(IntoStaticStr, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)] pub enum LogMessage { #[strum( serialize = "Reset due to ${txt_long} requires rtc clear ${number_a} and force config mode ${number_b}" diff --git a/rust/src/main.rs b/rust/src/main.rs index 5f90013..c14e4b5 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -5,6 +5,10 @@ reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] + +esp_bootloader_esp_idf::esp_app_desc!(); +use esp_backtrace as _; + use crate::config::PlantConfig; use crate::{ config::BoardVersion::INITIAL, @@ -16,24 +20,25 @@ use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::{format, vec}; +use core::any::Any; use anyhow::{bail, Context}; use chrono::{DateTime, Datelike, Timelike, Utc}; -use chrono_tz::Tz::{self, UTC}; +use chrono_tz::Tz::{self}; use core::sync::atomic::Ordering; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::lazy_lock::LazyLock; use embassy_sync::mutex::Mutex; -use embassy_sync::mutex::MutexGuard; use embassy_time::Timer; -use esp_hal::{clock::CpuClock, delay::Delay, timer::systimer::SystemTimer}; -use esp_println::logger; +use esp_println::{logger, println}; use hal::battery::BatteryState; use log::{log, LogMessage}; use plant_state::PlantState; use portable_atomic::AtomicBool; use serde::{Deserialize, Serialize}; -use tank::*; +use crate::tank::{TankError, WATER_FROZEN_THRESH}; + +//use tank::*; mod config; mod hal; mod log; @@ -44,7 +49,9 @@ extern crate alloc; //mod webserver; pub static BOARD_ACCESS: LazyLock> = - LazyLock::new(|| PlantHal::create().unwrap()); + LazyLock::new(|| { + PlantHal::create().unwrap() + }); pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false); #[derive(Serialize, Deserialize, Debug, PartialEq)] @@ -163,10 +170,10 @@ async fn safe_main() -> anyhow::Result<()> { //}; //log(LogMessage::PartitionState, 0, 0, "", ota_state_string); let ota_state_string = "unknown"; - + println!("faul led"); let mut board = BOARD_ACCESS.get().lock().await; board.board_hal.general_fault(false).await; - + println!("faul led2"); let cur = board .board_hal .get_rtc_module() @@ -204,7 +211,7 @@ async fn safe_main() -> anyhow::Result<()> { board.board_hal.get_esp().set_restart_to_conf(false); } else if board.board_hal.get_esp().mode_override_pressed() { board.board_hal.general_fault(true).await; - log(LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); + log(LogMessage::ConfigModeButtonOverride, 0, 0, "", "").await; for _i in 0..5 { board.board_hal.general_fault(true).await; Timer::after_millis(100).await; @@ -251,19 +258,21 @@ async fn safe_main() -> anyhow::Result<()> { } } - let timezone = match &board.board_hal.get_config().timezone { - Some(tz_str) => tz_str.parse::().unwrap_or_else(|_| { - info!("Invalid timezone '{}', falling back to UTC", tz_str); - UTC - }), - None => UTC, // Fallback to UTC if no timezone is set - }; - let timezone_time = cur.with_timezone(&timezone); + // let timezone = match &board.board_hal.get_config().timezone { + // Some(tz_str) => tz_str.parse::().unwrap_or_else(|_| { + // info!("Invalid timezone '{}', falling back to UTC", tz_str); + // UTC + // }), + // None => UTC, // Fallback to UTC if no timezone is set + // }; + let timezone = Tz::UTC; + + let timezone_time = cur;//TODO.with_timezone(&timezone); info!( "Running logic at utc {} and {} {}", cur, - timezone.name(), + "todo timezone.name()", timezone_time ); @@ -273,7 +282,7 @@ async fn safe_main() -> anyhow::Result<()> { partition_address, ota_state_string, ip_address, - timezone_time, + &timezone_time.to_rfc3339(), ) .await; publish_battery_state().await; @@ -294,9 +303,10 @@ async fn safe_main() -> anyhow::Result<()> { .to_string() .as_str(), "", - ); + ).await; - drop(board); + //TODO must drop board here? + //drop(board); if to_config { //check if client or ap mode and init Wi-Fi @@ -307,52 +317,54 @@ async fn safe_main() -> anyhow::Result<()> { //let _webserver = httpd(reboot_now.clone()); wait_infinity(WaitType::ConfigButton, reboot_now.clone()).await; } else { - log(LogMessage::NormalRun, 0, 0, "", ""); + log(LogMessage::NormalRun, 0, 0, "", "").await; } let dry_run = false; - - let tank_state = determine_tank_state(&mut board); - - if tank_state.is_enabled() { - if let Some(err) = tank_state.got_error(&board.board_hal.get_config().tank) { - match err { - TankError::SensorDisabled => { /* unreachable */ } - TankError::SensorMissing(raw_value_mv) => log( - LogMessage::TankSensorMissing, - raw_value_mv as u32, - 0, - "", - "", - ), - TankError::SensorValueError { value, min, max } => log( - LogMessage::TankSensorValueRangeError, - min as u32, - max as u32, - &format!("{}", value), - "", - ), - TankError::BoardError(err) => { - log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string()) - } - } - // 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) - { - log(LogMessage::TankWaterLevelLow, 0, 0, "", ""); - board.board_hal.general_fault(true); - } - } + // + // let tank_state = determine_tank_state(&mut board); + // + // if tank_state.is_enabled() { + // if let Some(err) = tank_state.got_error(&board.board_hal.get_config().tank) { + // match err { + // TankError::SensorDisabled => { /* unreachable */ } + // TankError::SensorMissing(raw_value_mv) => log( + // LogMessage::TankSensorMissing, + // raw_value_mv as u32, + // 0, + // "", + // "", + // ).await, + // TankError::SensorValueError { value, min, max } => log( + // LogMessage::TankSensorValueRangeError, + // min as u32, + // max as u32, + // &format!("{}", value), + // "", + // ).await, + // TankError::BoardError(err) => { + // log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string()).await + // } + // } + // // disabled cannot trigger this because of wrapping if is_enabled + // board.board_hal.general_fault(true).await; + // } else if tank_state + // .warn_level(&board.board_hal.get_config().tank) + // .is_ok_and(|warn| warn) + // { + // log(LogMessage::TankWaterLevelLow, 0, 0, "", "").await; + // board.board_hal.general_fault(true).await; + // } + // } let mut water_frozen = false; - let water_temp = board - .board_hal - .get_tank_sensor() - .context("no sensor") - .and_then(async |f| f.water_temperature_c().await); + //TODO + let water_temp = anyhow::Ok(12_f32); + // board + // .board_hal + // .get_tank_sensor() + // .context("no sensor") + // .and_then(async |f| f.water_temperature_c().await); if let Ok(res) = water_temp { if res < WATER_FROZEN_THRESH { @@ -360,80 +372,89 @@ async fn safe_main() -> anyhow::Result<()> { } } - publish_tank_state(&tank_state, &water_temp); + //publish_tank_state(&tank_state, &water_temp).await; - let plantstate: [PlantState; PLANT_COUNT] = - core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board).await); - publish_plant_states(&timezone_time, &plantstate).await; - let pump_required = plantstate - .iter() - .zip(&board.board_hal.get_config().plants) - .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) - && !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() - { - 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); + let plantstate: [PlantState; PLANT_COUNT] = [ + PlantState::read_hardware_state(0, &mut board).await, + PlantState::read_hardware_state(1, &mut board).await, + PlantState::read_hardware_state(2, &mut board).await, + PlantState::read_hardware_state(3, &mut board).await, + PlantState::read_hardware_state(4, &mut board).await, + PlantState::read_hardware_state(5, &mut board).await, + PlantState::read_hardware_state(6, &mut board).await, + PlantState::read_hardware_state(7, &mut board).await, + ]; + //publish_plant_states(&timezone_time.clone(), &plantstate).await; - let pump_ineffective = pump_count > plant_config.max_consecutive_pump_count as u32; - if pump_ineffective { - log( - LogMessage::ConsecutivePumpCountLimit, - pump_count, - plant_config.max_consecutive_pump_count as u32, - &(plant_id + 1).to_string(), - "", - ); - board.board_hal.fault(plant_id, true).await?; - } - log( - LogMessage::PumpPlant, - (plant_id + 1) as u32, - plant_config.pump_time_s as u32, - &dry_run.to_string(), - "", - ); - 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(plant_id, true, pump_ineffective, 0, 0, 0, false).await; - - let result = do_secure_pump(plant_id, plant_config, dry_run).await?; - board.board_hal.pump(plant_id, false).await?; - pump_info( - plant_id, - false, - pump_ineffective, - result.median_current_ma, - result.max_current_ma, - result.min_current_ma, - result.error, - ) - .await; - } else if !state.pump_in_timeout(plant_config, &timezone_time) { - // plant does not need to be watered and is not in timeout - // -> reset consecutive pump count - board - .board_hal - .get_esp() - .store_consecutive_pump_count(plant_id, 0); - } - } - } + // let pump_required = plantstate + // .iter() + // .zip(&board.board_hal.get_config().plants) + // .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) + // && !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() + // { + // 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); + // + // let pump_ineffective = pump_count > plant_config.max_consecutive_pump_count as u32; + // if pump_ineffective { + // log( + // LogMessage::ConsecutivePumpCountLimit, + // pump_count, + // plant_config.max_consecutive_pump_count as u32, + // &(plant_id + 1).to_string(), + // "", + // ); + // board.board_hal.fault(plant_id, true).await?; + // } + // log( + // LogMessage::PumpPlant, + // (plant_id + 1) as u32, + // plant_config.pump_time_s as u32, + // &dry_run.to_string(), + // "", + // ); + // 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(plant_id, true, pump_ineffective, 0, 0, 0, false).await; + // + // let result = do_secure_pump(plant_id, plant_config, dry_run).await?; + // board.board_hal.pump(plant_id, false).await?; + // pump_info( + // plant_id, + // false, + // pump_ineffective, + // result.median_current_ma, + // result.max_current_ma, + // result.min_current_ma, + // result.error, + // ) + // .await; + // } else if !state.pump_in_timeout(plant_config, &timezone_time) { + // // plant does not need to be watered and is not in timeout + // // -> reset consecutive pump count + // 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 @@ -455,67 +476,67 @@ async fn safe_main() -> anyhow::Result<()> { enabled: board.board_hal.get_config().night_lamp.enabled, ..Default::default() }; - if light_state.enabled { - 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_end, - ); - - if state_of_charge - < board - .board_hal - .get_config() - .night_lamp - .low_soc_cutoff - .into() - { - board.board_hal.get_esp().set_low_voltage_in_cycle(); - } else if state_of_charge - > board - .board_hal - .get_config() - .night_lamp - .low_soc_restore - .into() - { - board.board_hal.get_esp().clear_low_voltage_in_cycle(); - } - 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 !light_state.is_day { - if light_state.battery_low { - board.board_hal.light(false)?; - } else { - light_state.active = true; - board.board_hal.light(true)?; - } - } - } else if light_state.battery_low { - board.board_hal.light(false)?; - } else { - light_state.active = true; - board.board_hal.light(true)?; - } - } else { - light_state.active = false; - board.board_hal.light(false)?; - } - - info!("Lightstate is {:?}", light_state); - } + // if light_state.enabled { + // 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_end, + // ); + // + // if state_of_charge + // < board + // .board_hal + // .get_config() + // .night_lamp + // .low_soc_cutoff + // .into() + // { + // board.board_hal.get_esp().set_low_voltage_in_cycle(); + // } else if state_of_charge + // > board + // .board_hal + // .get_config() + // .night_lamp + // .low_soc_restore + // .into() + // { + // board.board_hal.get_esp().clear_low_voltage_in_cycle(); + // } + // 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 !light_state.is_day { + // if light_state.battery_low { + // board.board_hal.light(false)?; + // } else { + // light_state.active = true; + // board.board_hal.light(true)?; + // } + // } + // } else if light_state.battery_low { + // board.board_hal.light(false)?; + // } else { + // light_state.active = true; + // board.board_hal.light(true)?; + // } + // } else { + // light_state.active = false; + // board.board_hal.light(false)?; + // } + // + // info!("Lightstate is {:?}", light_state); + // } match serde_json::to_string(&light_state) { Ok(state) => { @@ -596,24 +617,25 @@ pub async fn do_secure_pump( let mut pump_time_s = 0; let board = &mut BOARD_ACCESS.get().lock().await; if !dry_run { - board - .board_hal - .get_tank_sensor() - .unwrap() - .reset_flow_meter(); - board - .board_hal - .get_tank_sensor() - .unwrap() - .start_flow_meter(); + // board + // .board_hal + // .get_tank_sensor() + // .unwrap() + // .reset_flow_meter(); + // board + // .board_hal + // .get_tank_sensor() + // .unwrap() + // .start_flow_meter(); board.board_hal.pump(plant_id, true).await?; Timer::after_millis(10).await; for step in 0..plant_config.pump_time_s as usize { - let flow_value = board - .board_hal - .get_tank_sensor() - .unwrap() - .get_flow_meter_value(); + // let flow_value = board + // .board_hal + // .get_tank_sensor() + // .unwrap() + // .get_flow_meter_value(); + let flow_value = 1; flow_collector[step] = flow_value; let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; @@ -691,12 +713,13 @@ pub async fn do_secure_pump( pump_time_s += 1; } } - board.board_hal.get_tank_sensor().unwrap().stop_flow_meter(); - let final_flow_value = board - .board_hal - .get_tank_sensor() - .unwrap() - .get_flow_meter_value(); + // board.board_hal.get_tank_sensor().unwrap().stop_flow_meter(); + // let final_flow_value = board + // .board_hal + // .get_tank_sensor() + // .unwrap() + // .get_flow_meter_value(); + let final_flow_value = 12; let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; info!( "Final flow value is {} with {} ml", @@ -715,8 +738,42 @@ pub async fn do_secure_pump( } async fn update_charge_indicator() { - let board = BOARD_ACCESS.get().lock().await; + let board = &mut BOARD_ACCESS.get().lock().await; //we have mppt controller, ask it for charging current + // let tank_state = determine_tank_state(&mut board); + // + // if tank_state.is_enabled() { + // if let Some(err) = tank_state.got_error(&board.board_hal.get_config().tank) { + // match err { + // TankError::SensorDisabled => { /* unreachable */ } + // TankError::SensorMissing(raw_value_mv) => log( + // LogMessage::TankSensorMissing, + // raw_value_mv as u32, + // 0, + // "", + // "", + // ).await, + // TankError::SensorValueError { value, min, max } => log( + // LogMessage::TankSensorValueRangeError, + // min as u32, + // max as u32, + // &format!("{}", value), + // "", + // ).await, + // TankError::BoardError(err) => { + // log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string()).await + // } + // } + // // disabled cannot trigger this because of wrapping if is_enabled + // board.board_hal.general_fault(true).await; + // } else if tank_state + // .warn_level(&board.board_hal.get_config().tank) + // .is_ok_and(|warn| warn) + // { + // log(LogMessage::TankWaterLevelLow, 0, 0, "", "").await; + // board.board_hal.general_fault(true).await; + // } + // } if let Ok(current) = board.board_hal.get_mptt_current().await { let _ = board .board_hal @@ -726,7 +783,7 @@ async fn update_charge_indicator() { else if let Ok(charging) = board .board_hal .get_battery_monitor() - .average_current_milli_ampere() + .average_current_milli_ampere().await { let _ = board.board_hal.set_charge_indicator(charging > 20); } else { @@ -734,55 +791,55 @@ async fn update_charge_indicator() { let _ = board.board_hal.set_charge_indicator(false); } } +// +// async fn publish_tank_state(tank_state: &TankState, water_temp: &anyhow::Result) { +// let board = &mut BOARD_ACCESS.get().lock().await; +// match serde_json::to_string( +// &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp), +// ) { +// Ok(state) => { +// let _ = board +// .board_hal +// .get_esp() +// .mqtt_publish("/water", state.as_bytes()); +// } +// Err(err) => { +// info!("Error publishing tankstate {}", err); +// } +// }; +// } -async fn publish_tank_state(tank_state: &TankState, water_temp: &anyhow::Result) { - let board = &mut BOARD_ACCESS.get().lock().await; - match serde_json::to_string( - &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, water_temp), - ) { - Ok(state) => { - let _ = board - .board_hal - .get_esp() - .mqtt_publish("/water", state.as_bytes()); - } - Err(err) => { - info!("Error publishing tankstate {}", err); - } - }; -} - -async fn publish_plant_states(timezone_time: &DateTime, plantstate: &[PlantState; 8]) { - let board = &mut BOARD_ACCESS.get().lock().await; - for (plant_id, (plant_state, plant_conf)) in plantstate - .iter() - .zip(&board.board_hal.get_config().plants.clone()) - .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()) - .await; - //TODO? reduce speed as else messages will be dropped - Timer::after_millis(200).await - } - Err(err) => { - error!("Error publishing plant state {}", err); - } - }; - } -} +// async fn publish_plant_states(timezone_time: &DateTime, plantstate: &[PlantState; 8]) { +// let board = &mut BOARD_ACCESS.get().lock().await; +// for (plant_id, (plant_state, plant_conf)) in plantstate +// .iter() +// .zip(&board.board_hal.get_config().plants.clone()) +// .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()) +// .await; +// //TODO? reduce speed as else messages will be dropped +// Timer::after_millis(200).await +// } +// Err(err) => { +// error!("Error publishing plant state {}", err); +// } +// }; +// } +// } async fn publish_firmware_info( version: VersionInfo, address: u32, ota_state_string: &str, ip_address: &String, - timezone_time: DateTime, + timezone_time: &String, ) { let board = &mut BOARD_ACCESS.get().lock().await; let _ = board @@ -802,7 +859,7 @@ async fn publish_firmware_info( .await; let _ = board.board_hal.get_esp().mqtt_publish( "/firmware/last_online", - timezone_time.to_rfc3339().as_bytes(), + timezone_time.as_bytes(), ); let _ = board .board_hal @@ -821,7 +878,7 @@ async fn publish_firmware_info( } async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { - let board = BOARD_ACCESS.get().lock().await; + let board = &mut BOARD_ACCESS.get().lock().await; let nw_conf = &board.board_hal.get_config().network.clone(); match board.board_hal.get_esp().wifi(nw_conf).await { Ok(ip_info) => { @@ -860,7 +917,7 @@ async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { } Err(_) => { info!("Offline mode"); - board.board_hal.general_fault(true); + board.board_hal.general_fault(true).await; NetworkMode::OFFLINE } } @@ -922,8 +979,8 @@ async fn publish_mppt_state() -> anyhow::Result<()> { } async fn publish_battery_state() -> () { - let board = BOARD_ACCESS.get().lock().await; - let state = board.board_hal.get_battery_monitor().get_battery_state(); + let board = &mut BOARD_ACCESS.get().lock().await; + let state = board.board_hal.get_battery_monitor().get_battery_state().await; if let Ok(serialized_battery_state_bytes) = serde_json::to_string(&state).map(|s| s.into_bytes()) { @@ -995,13 +1052,10 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { async fn main(spawner: Spawner) { // intialize embassy logger::init_logger_from_env(); - let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); - let peripherals = esp_hal::init(config); - - esp_alloc::heap_allocator!(size: 64 * 1024); - - let timer0 = SystemTimer::new(peripherals.SYSTIMER); - esp_hal_embassy::init(timer0.alarm0); + //force init here! + let board = BOARD_ACCESS.get().lock().await; + drop(board); + println!("test"); info!("Embassy initialized!"); diff --git a/rust/src/plant_state.rs b/rust/src/plant_state.rs index e50d31d..0676007 100644 --- a/rust/src/plant_state.rs +++ b/rust/src/plant_state.rs @@ -3,7 +3,7 @@ use crate::{ hal::{Sensor, HAL}, in_time_range, }; -use alloc::string::String; +use alloc::string::{String, ToString}; use chrono::{DateTime, TimeDelta, Utc}; use chrono_tz::Tz; use serde::{Deserialize, Serialize}; @@ -118,7 +118,7 @@ fn map_range_moisture( impl PlantState { pub async fn read_hardware_state(plant_id: usize, board: &mut HAL<'_>) -> Self { let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a { - match board.board_hal.measure_moisture_hz(plant_id, Sensor::A) { + match board.board_hal.measure_moisture_hz(plant_id, Sensor::A).await { Ok(raw) => match map_range_moisture( raw, board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, @@ -139,7 +139,7 @@ impl PlantState { }; let sensor_b = if board.board_hal.get_config().plants[plant_id].sensor_b { - match board.board_hal.measure_moisture_hz(plant_id, Sensor::B) { + match board.board_hal.measure_moisture_hz(plant_id, Sensor::B).await { Ok(raw) => match map_range_moisture( raw, board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, @@ -264,50 +264,50 @@ impl PlantState { PlantWateringMode::TimerOnly => !self.pump_in_timeout(plant_conf, current_time), } } - - pub fn to_mqtt_info( - &self, - plant_conf: &PlantConfig, - current_time: &DateTime, - ) -> PlantInfo<'_> { - PlantInfo { - sensor_a: &self.sensor_a, - sensor_b: &self.sensor_b, - mode: plant_conf.mode, - do_water: self.needs_to_be_watered(plant_conf, current_time), - dry: if let Some(moisture_percent) = self.plant_moisture().0 { - moisture_percent < plant_conf.target_moisture - } else { - false - }, - cooldown: self.pump_in_timeout(plant_conf, current_time), - out_of_work_hour: in_time_range( - current_time, - plant_conf.pump_hour_start, - plant_conf.pump_hour_end, - ), - consecutive_pump_count: self.pump.consecutive_pump_count, - pump_error: self.pump.is_err(plant_conf), - last_pump: self - .pump - .previous_pump - .map(|t| t.with_timezone(¤t_time.timezone())), - next_pump: if matches!( - plant_conf.mode, - PlantWateringMode::TimerOnly - | PlantWateringMode::TargetMoisture - | PlantWateringMode::MinMoisture - ) { - self.pump.previous_pump.and_then(|last_pump| { - last_pump - .checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into())) - .map(|t| t.with_timezone(¤t_time.timezone())) - }) - } else { - None - }, - } - } + // + // pub fn to_mqtt_info( + // &self, + // plant_conf: &PlantConfig, + // current_time: &DateTime, + // ) -> PlantInfo<'_> { + // PlantInfo { + // sensor_a: &self.sensor_a, + // sensor_b: &self.sensor_b, + // mode: plant_conf.mode, + // do_water: self.needs_to_be_watered(plant_conf, current_time), + // dry: if let Some(moisture_percent) = self.plant_moisture().0 { + // moisture_percent < plant_conf.target_moisture + // } else { + // false + // }, + // cooldown: self.pump_in_timeout(plant_conf, current_time), + // out_of_work_hour: in_time_range( + // current_time, + // plant_conf.pump_hour_start, + // plant_conf.pump_hour_end, + // ), + // consecutive_pump_count: self.pump.consecutive_pump_count, + // pump_error: self.pump.is_err(plant_conf), + // last_pump: self + // .pump + // .previous_pump + // .map(|t| t.with_timezone(¤t_time.timezone())), + // next_pump: if matches!( + // plant_conf.mode, + // PlantWateringMode::TimerOnly + // | PlantWateringMode::TargetMoisture + // | PlantWateringMode::MinMoisture + // ) { + // self.pump.previous_pump.and_then(|last_pump| { + // last_pump + // .checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into())) + // .map(|t| t.with_timezone(¤t_time.timezone())) + // }) + // } else { + // None + // }, + // } + // } } #[derive(Debug, PartialEq, Serialize)] @@ -330,8 +330,8 @@ pub struct PlantInfo<'a> { /// how often has the pump been watered without reaching target moisture consecutive_pump_count: u32, pump_error: Option, - /// last time when the pump was active - last_pump: Option>, - /// next time when pump should activate - next_pump: Option>, + // /// last time when the pump was active + // last_pump: Option>, + // /// next time when pump should activate + // next_pump: Option>, } diff --git a/rust/src/tank.rs b/rust/src/tank.rs index 1f69937..7f53694 100644 --- a/rust/src/tank.rs +++ b/rust/src/tank.rs @@ -151,21 +151,21 @@ impl 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 - .get_tank_sensor() - .context("no sensor") - .and_then(|f| f.tank_sensor_voltage()) - { - Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), - Err(err) => TankState::Error(TankError::BoardError(err.to_string())), - } - } else { - TankState::Disabled - } -} +// 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 +// .get_tank_sensor() +// .context("no sensor") +// .and_then(|f| f.tank_sensor_voltage()) +// { +// Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), +// Err(err) => TankState::Error(TankError::BoardError(err.to_string())), +// } +// } else { +// TankState::Disabled +// } +// } #[derive(Debug, Serialize)] /// Information structure send to mqtt for monitoring purposes