From d059b7b1db4c8ff15303449bf22c84ae9836dcf6 Mon Sep 17 00:00:00 2001 From: Empire Date: Fri, 20 Jun 2025 23:59:14 +0200 Subject: [PATCH] Merge branch 'develop' into housekeeping and clippy and spellcheck --- rust/.idea/dictionaries/project.xml | 7 + rust/src/hal/esp.rs | 6 +- rust/src/hal/initial_hal.rs | 8 +- rust/src/hal/mod.rs | 20 +-- rust/src/hal/v3_hal.rs | 8 +- rust/src/hal/v4_hal.rs | 56 ++++--- rust/src/log/mod.rs | 2 +- rust/src/main.rs | 169 ++++++++++---------- rust/src/webserver/{webserver.rs => mod.rs} | 0 9 files changed, 135 insertions(+), 141 deletions(-) rename rust/src/webserver/{webserver.rs => mod.rs} (100%) diff --git a/rust/.idea/dictionaries/project.xml b/rust/.idea/dictionaries/project.xml index e169be7..929ed9a 100644 --- a/rust/.idea/dictionaries/project.xml +++ b/rust/.idea/dictionaries/project.xml @@ -1,7 +1,14 @@ + buildtime + deepsleep + githash + lightstate + mppt + plantstate sntp + vergen \ No newline at end of file diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 70ff6b8..d1fc437 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -63,14 +63,14 @@ pub struct MqttClient<'a> { mqtt_client: EspMqttClient<'a>, base_topic: heapless::String<64>, } -pub struct ESP<'a> { +pub struct Esp<'a> { pub(crate) mqtt_client: Option>, pub(crate) wifi_driver: EspWifi<'a>, pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, pub(crate) delay: Delay, } -impl ESP<'_> { +impl Esp<'_> { const SPIFFS_PARTITION_NAME: &'static str = "storage"; const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; const BASE_PATH: &'static str = "/spiffs"; @@ -310,7 +310,7 @@ impl ESP<'_> { filename: file.file_name().into_string().unwrap(), size: file .metadata() - .and_then(|it| Ok(it.len())) + .map(|it| it.len()) .unwrap_or_default() as usize, }; diff --git a/rust/src/hal/initial_hal.rs b/rust/src/hal/initial_hal.rs index cda3d10..14238d2 100644 --- a/rust/src/hal/initial_hal.rs +++ b/rust/src/hal/initial_hal.rs @@ -1,4 +1,4 @@ -use crate::hal::esp::ESP; +use crate::hal::esp::Esp; use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor}; use crate::{ config::PlantControllerConfig, @@ -13,7 +13,7 @@ use measurements::{Current, Voltage}; pub struct Initial<'a> { pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, - pub(crate) esp: ESP<'a>, + pub(crate) esp: Esp<'a>, pub(crate) config: PlantControllerConfig, pub(crate) battery: Box, } @@ -22,7 +22,7 @@ pub(crate) fn create_initial_board( free_pins: FreePeripherals, fs_mount_error: bool, config: PlantControllerConfig, - esp: ESP<'static>, + esp: Esp<'static>, ) -> Result + Send>> { let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?; general_fault.set_pull(Pull::Floating)?; @@ -41,7 +41,7 @@ pub(crate) fn create_initial_board( } impl<'a> BoardInteraction<'a> for Initial<'a> { - fn get_esp(&mut self) -> &mut ESP<'a> { + fn get_esp(&mut self) -> &mut Esp<'a> { &mut self.esp } diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index 6d2ddbb..1155ebc 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -8,7 +8,7 @@ use crate::{ config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, hal::{ battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor}, - esp::ESP, + esp::Esp, }, log::{log, LogMessage}, }; @@ -37,12 +37,12 @@ use esp_idf_sys::{ esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, }; use esp_ota::mark_app_valid; +use measurements::{Current, Voltage}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::result::Result::Ok as OkStd; use std::sync::Mutex; use std::time::Duration; -use measurements::{Current, Voltage}; //Only support for 8 right now! pub const PLANT_COUNT: usize = 8; @@ -84,25 +84,15 @@ pub struct HAL<'a> { pub board_hal: Box + Send>, } -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Serialize, Deserialize, PartialEq, Debug, Default)] pub struct BackupHeader { pub timestamp: i64, crc16: u16, pub size: usize, } -impl Default for BackupHeader { - fn default() -> Self { - Self { - timestamp: Default::default(), - crc16: Default::default(), - size: Default::default(), - } - } -} - pub trait BoardInteraction<'a> { - fn get_esp(&mut self) -> &mut ESP<'a>; + fn get_esp(&mut self) -> &mut Esp<'a>; fn get_config(&mut self) -> &PlantControllerConfig; fn get_battery_monitor(&mut self) -> &mut Box; fn set_charge_indicator(&mut self, charging: bool) -> Result<()>; @@ -225,7 +215,7 @@ impl PlantHal { gpio30: peripherals.pins.gpio30, }; - let mut esp = ESP { + let mut esp = Esp { mqtt_client: None, wifi_driver, boot_button, diff --git a/rust/src/hal/v3_hal.rs b/rust/src/hal/v3_hal.rs index 3645905..cbca6f1 100644 --- a/rust/src/hal/v3_hal.rs +++ b/rust/src/hal/v3_hal.rs @@ -5,7 +5,7 @@ use crate::hal::{ use crate::log::{log, LogMessage}; use crate::{ config::PlantControllerConfig, - hal::{battery::BatteryInteraction, esp::ESP}, + hal::{battery::BatteryInteraction, esp::Esp}, }; use anyhow::{anyhow, bail, Ok, Result}; use chrono::{DateTime, Utc}; @@ -79,7 +79,7 @@ const FAULT_2: usize = 23; pub struct V3<'a> { config: PlantControllerConfig, battery_monitor: Box, - esp: ESP<'a>, + esp: Esp<'a>, shift_register: ShiftRegister40< PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, @@ -107,7 +107,7 @@ pub struct V3<'a> { pub(crate) fn create_v3( peripherals: FreePeripherals, - esp: ESP<'static>, + esp: Esp<'static>, config: PlantControllerConfig, battery_monitor: Box, ) -> Result + Send>> { @@ -270,7 +270,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> { fn get_mptt_current(&mut self) -> Result { bail!("Board does not have current sensor") } - fn get_esp(&mut self) -> &mut ESP<'a> { + fn get_esp(&mut self) -> &mut Esp<'a> { &mut self.esp } diff --git a/rust/src/hal/v4_hal.rs b/rust/src/hal/v4_hal.rs index e3b121b..ddaae4a 100644 --- a/rust/src/hal/v4_hal.rs +++ b/rust/src/hal/v4_hal.rs @@ -1,6 +1,6 @@ use crate::config::PlantControllerConfig; use crate::hal::battery::BatteryInteraction; -use crate::hal::esp::ESP; +use crate::hal::esp::Esp; use crate::hal::{ deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25, @@ -22,15 +22,15 @@ use esp_idf_hal::i2c::I2cDriver; use esp_idf_hal::pcnt::{ PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, }; -use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; +use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError}; use ina219::address::{Address, Pin}; -use ina219::calibration::{Calibration, UnCalibrated}; +use ina219::calibration::UnCalibrated; +use ina219::configuration::{Configuration, OperatingMode}; use ina219::SyncIna219; use measurements::{Current, Resistance, Voltage}; use one_wire_bus::OneWire; use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; use std::result::Result::Ok as OkStd; -use ina219::configuration::{Configuration, OperatingMode}; const MS0: u8 = 1_u8; const MS1: u8 = 0_u8; @@ -47,23 +47,27 @@ pub enum Charger<'a> { }, } -impl<'a> Charger<'a> { - pub(crate) fn powersave(&mut self) { - match self { Charger::SolarMpptV1 { mppt_ina, .. } => { - let _ = mppt_ina.set_configuration(Configuration { - reset: Default::default(), - bus_voltage_range: Default::default(), - shunt_voltage_range: Default::default(), - bus_resolution: Default::default(), - shunt_resolution: Default::default(), - operating_mode: OperatingMode::PowerDown, - }).map_err(|e| { - println!( - "Error setting ina mppt configuration during deepsleep preparation{:?}", +impl Charger<'_> { + pub(crate) fn power_save(&mut self) { + match self { + Charger::SolarMpptV1 { mppt_ina, .. } => { + let _ = mppt_ina + .set_configuration(Configuration { + reset: Default::default(), + bus_voltage_range: Default::default(), + shunt_voltage_range: Default::default(), + bus_resolution: Default::default(), + shunt_resolution: Default::default(), + operating_mode: OperatingMode::PowerDown, + }) + .map_err(|e| { + println!( + "Error setting ina mppt configuration during deep sleep preparation{:?}", e ); - }); - } } + }); + } + } } fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> { match self { @@ -105,7 +109,7 @@ impl<'a> Charger<'a> { } pub struct V4<'a> { - esp: ESP<'a>, + esp: Esp<'a>, charger: Charger<'a>, battery_monitor: Box, config: PlantControllerConfig, @@ -130,10 +134,10 @@ pub struct V4<'a> { pub(crate) fn create_v4( peripherals: FreePeripherals, - esp: ESP<'static>, + esp: Esp<'static>, config: PlantControllerConfig, battery_monitor: Box, -) -> anyhow::Result> { +) -> anyhow::Result + Send + 'static>> { let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?; awake.set_high()?; @@ -244,7 +248,7 @@ pub(crate) fn create_v4( Address::from_pins(Pin::Vcc, Pin::Gnd), )?; - mppt_ina.set_configuration(Configuration{ + mppt_ina.set_configuration(Configuration { reset: Default::default(), bus_voltage_range: Default::default(), shunt_voltage_range: Default::default(), @@ -252,7 +256,7 @@ pub(crate) fn create_v4( shunt_resolution: ina219::configuration::Resolution::Avg128, operating_mode: Default::default(), })?; - //TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access? + //TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access? esp.delay.delay_ms( mppt_ina .configuration()? @@ -288,7 +292,7 @@ pub(crate) fn create_v4( } impl<'a> BoardInteraction<'a> for V4<'a> { - fn get_esp(&mut self) -> &mut ESP<'a> { + fn get_esp(&mut self) -> &mut Esp<'a> { &mut self.esp } @@ -306,7 +310,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { self.awake.set_low().unwrap(); - self.charger.powersave(); + self.charger.power_save(); deep_sleep(duration_in_ms); } diff --git a/rust/src/log/mod.rs b/rust/src/log/mod.rs index 0b3384b..3315333 100644 --- a/rust/src/log/mod.rs +++ b/rust/src/log/mod.rs @@ -65,7 +65,7 @@ pub fn get_log() -> Vec { read_copy.push(copy); } drop(buffer); - return read_copy; + read_copy } pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) { diff --git a/rust/src/main.rs b/rust/src/main.rs index 5a9d60f..9f86aca 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,7 +1,7 @@ use crate::{ config::BoardVersion::INITIAL, hal::{PlantHal, HAL, PLANT_COUNT}, - webserver::webserver::httpd, + webserver::httpd, }; use anyhow::bail; use chrono::{DateTime, Datelike, Timelike, Utc}; @@ -12,7 +12,7 @@ use esp_idf_sys::{ esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW, esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED, - esp_ota_img_states_t_ESP_OTA_IMG_VALID, vTaskDelay, + esp_ota_img_states_t_ESP_OTA_IMG_VALID, }; use esp_ota::{mark_app_valid, rollback_and_reboot}; use hal::battery::BatteryState; @@ -28,13 +28,12 @@ mod hal; mod log; mod plant_state; mod tank; +mod webserver; pub static BOARD_ACCESS: Lazy> = Lazy::new(|| PlantHal::create().unwrap()); pub static STAY_ALIVE: Lazy = Lazy::new(|| AtomicBool::new(false)); -mod webserver { - pub mod webserver; -} + #[derive(Serialize, Deserialize, Debug, PartialEq)] enum WaitType { @@ -69,7 +68,7 @@ struct LightState { } #[derive(Serialize, Deserialize, Debug, PartialEq, Default)] -///mqtt stuct to track pump activities +///mqtt struct to track pump activities struct PumpInfo { enabled: bool, pump_ineffective: bool, @@ -175,16 +174,7 @@ fn safe_main() -> anyhow::Result<()> { } println!("cur is {}", cur); - match board - .board_hal - .get_battery_monitor() - .average_current_milli_ampere() - { - Ok(charging) => { - let _ = board.board_hal.set_charge_indicator(charging > 20); - } - Err(_) => {} - } + update_charge_indicator(&mut board); if board.board_hal.get_esp().get_restart_to_conf() { log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); @@ -265,7 +255,7 @@ fn safe_main() -> anyhow::Result<()> { address, ota_state_string, &mut board, - &ip_address, + ip_address, timezone_time, ); publish_battery_state(&mut board); @@ -426,7 +416,7 @@ fn safe_main() -> anyhow::Result<()> { .board_hal .get_battery_monitor() .get_battery_state() - .unwrap_or(hal::battery::BatteryState::Unknown); + .unwrap_or(BatteryState::Unknown); let mut light_state = LightState { enabled: board.board_hal.get_config().night_lamp.enabled, @@ -556,6 +546,26 @@ fn safe_main() -> anyhow::Result<()> { .deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); } +fn update_charge_indicator(board: &mut MutexGuard) { + //we have mppt controller, ask it for charging current + if let Ok(current) = board.board_hal.get_mptt_current() { + let _ = board + .board_hal + .set_charge_indicator(current.as_milliamperes() > 20_f64); + } + //fallback to battery controller and ask it instead + else if let Ok(charging) = board + .board_hal + .get_battery_monitor() + .average_current_milli_ampere() + { + let _ = board.board_hal.set_charge_indicator(charging > 20); + } else { + //who knows + let _ = board.board_hal.set_charge_indicator(false); + } +} + fn obtain_tank_temperature(board: &mut MutexGuard) -> anyhow::Result { //multisample should be moved to water_temperature_c let mut attempt = 1; @@ -608,7 +618,7 @@ fn publish_plant_states( .zip(&board.board_hal.get_config().plants.clone()) .enumerate() { - match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) { + 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 @@ -679,7 +689,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard) -> NetworkMode { SntpMode::OFFLINE } }; - let mqtt_connected = if let Some(_) = board.board_hal.get_config().network.mqtt_url { + let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() { let nw_config = &board.board_hal.get_config().network.clone(); match board.board_hal.get_esp().mqtt(nw_config) { Ok(_) => { @@ -750,76 +760,59 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { let delay = wait_type.blink_pattern(); let mut led_count = 8; let mut pattern_step = 0; - + let delay_handle = Delay::new_default(); loop { - unsafe { - let mut board = BOARD_ACCESS.lock().unwrap(); + let mut board = BOARD_ACCESS.lock().unwrap(); + update_charge_indicator(&mut board); - //we have mppt controller, ask it for charging current - if let Ok(current) = board.board_hal.get_mptt_current() { - let _ = board.board_hal.set_charge_indicator(current.as_milliamperes() > 20_f64); - } - //fallback to battery controller and ask it instead - else if let Ok(charging) = board - .board_hal - .get_battery_monitor() - .average_current_milli_ampere() - { - let _ = board.board_hal.set_charge_indicator(charging > 20); - } - else { - //who knows - let _ = board.board_hal.set_charge_indicator(false); - } - - match wait_type { - WaitType::MissingConfig => { - // Keep existing behavior: circular filling pattern - led_count %= 8; - led_count += 1; - for i in 0..8 { - let _ = board.board_hal.fault(i, i < led_count); - } - } - WaitType::ConfigButton => { - // Alternating pattern: 1010 1010 -> 0101 0101 - pattern_step = (pattern_step + 1) % 2; - for i in 0..8 { - let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0); - } - } - WaitType::MqttConfig => { - // Moving dot pattern - pattern_step = (pattern_step + 1) % 8; - for i in 0..8 { - let _ = board.board_hal.fault(i, i == pattern_step); - } + match wait_type { + WaitType::MissingConfig => { + // Keep existing behavior: circular filling pattern + led_count %= 8; + led_count += 1; + for i in 0..8 { + let _ = board.board_hal.fault(i, i < led_count); } } - - board.board_hal.general_fault(true); - drop(board); - vTaskDelay(delay); - let mut board = BOARD_ACCESS.lock().unwrap(); - board.board_hal.general_fault(false); - - // Clear all LEDs - for i in 0..8 { - let _ = board.board_hal.fault(i, false); + WaitType::ConfigButton => { + // Alternating pattern: 1010 1010 -> 0101 0101 + pattern_step = (pattern_step + 1) % 2; + for i in 0..8 { + let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0); + } } - drop(board); - vTaskDelay(delay); + WaitType::MqttConfig => { + // Moving dot pattern + pattern_step = (pattern_step + 1) % 8; + for i in 0..8 { + let _ = board.board_hal.fault(i, i == pattern_step); + } + } + } - if wait_type == WaitType::MqttConfig - && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) - { - reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); - } - if reboot_now.load(std::sync::atomic::Ordering::Relaxed) { - //ensure clean http answer - Delay::new_default().delay_ms(500); - BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); - } + board.board_hal.general_fault(true); + drop(board); + //cannot use shared delay as that is inside the mutex here + delay_handle.delay_ms(delay); + let mut board = BOARD_ACCESS.lock().unwrap(); + board.board_hal.general_fault(false); + + // Clear all LEDs + for i in 0..8 { + let _ = board.board_hal.fault(i, false); + } + drop(board); + delay_handle.delay_ms(delay); + + if wait_type == WaitType::MqttConfig + && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) + { + reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); + } + if reboot_now.load(std::sync::atomic::Ordering::Relaxed) { + //ensure clean http answer + Delay::new_default().delay_ms(500); + BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); } } } @@ -827,7 +820,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { fn main() { let result = safe_main(); match result { - // this should not get triggered, safe_main should not return but go into deep sleep with sensbile + // this should not get triggered, safe_main should not return but go into deep sleep with sensible // timeout, this is just a fallback Ok(_) => { println!("Main app finished, restarting"); @@ -849,13 +842,13 @@ fn main() { } pub fn in_time_range(cur: &DateTime, start: u8, end: u8) -> bool { - let curhour = cur.hour() as u8; + let current_hour = cur.hour() as u8; //eg 10-14 if start < end { - curhour > start && curhour < end + current_hour > start && current_hour < end } else { //eg 20-05 - curhour > start || curhour < end + current_hour > start || current_hour < end } } diff --git a/rust/src/webserver/webserver.rs b/rust/src/webserver/mod.rs similarity index 100% rename from rust/src/webserver/webserver.rs rename to rust/src/webserver/mod.rs