From 8ce00c9d95f0080216217e42c68ebfb374ac1789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 13 Apr 2026 17:03:47 +0200 Subject: [PATCH] Refactor async logging to synchronous; improve error handling consistency across modules. --- Software/MainBoard/rust/espflash.toml | 6 +- Software/MainBoard/rust/src/hal/esp.rs | 14 ++-- Software/MainBoard/rust/src/hal/mod.rs | 12 ++- Software/MainBoard/rust/src/hal/rtc.rs | 6 +- .../rust/src/hal/savegame_manager.rs | 4 +- Software/MainBoard/rust/src/hal/v4_hal.rs | 6 +- .../MainBoard/rust/src/log/interceptor.rs | 12 ++- Software/MainBoard/rust/src/log/mod.rs | 33 +++++---- Software/MainBoard/rust/src/main.rs | 73 ++++++++----------- Software/MainBoard/rust/src/plant_state.rs | 2 +- .../MainBoard/rust/src/webserver/get_json.rs | 11 ++- .../MainBoard/rust/src_webpack/src/main.ts | 46 ++++++------ 12 files changed, 104 insertions(+), 121 deletions(-) diff --git a/Software/MainBoard/rust/espflash.toml b/Software/MainBoard/rust/espflash.toml index 207afc6..f2a3eb8 100644 --- a/Software/MainBoard/rust/espflash.toml +++ b/Software/MainBoard/rust/espflash.toml @@ -1,8 +1,6 @@ -[connection] +format = "EspIdf" -[[usb_device]] -vid = "303a" -pid = "1001" +[idf_format_args] [flash] size = "16MB" diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs index 90bb136..72ac67f 100644 --- a/Software/MainBoard/rust/src/hal/esp.rs +++ b/Software/MainBoard/rust/src/hal/esp.rs @@ -607,16 +607,14 @@ impl Esp<'_> { 0, "", "", - ) - .await; + ); log( LogMessage::LowVoltage, LOW_VOLTAGE_DETECTED as u32, 0, "", "", - ) - .await; + ); // is executed before main, no other code will alter these values during printing #[allow(static_mut_refs)] for (i, time) in LAST_WATERING_TIMESTAMP.iter().enumerate() { @@ -695,9 +693,9 @@ impl Esp<'_> { ))?; spawner.spawn(mqtt_runner(task))?; - log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic).await; + log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic); - log(LogMessage::MqttInfo, 0, 0, "", mqtt_url).await; + log(LogMessage::MqttInfo, 0, 0, "", mqtt_url); let mqtt_timeout = 15000; let res = async { @@ -839,10 +837,10 @@ async fn mqtt_incoming_task( true => 1, false => 0, }; - log(LogMessage::MqttStayAliveRec, a, 0, "", "").await; + log(LogMessage::MqttStayAliveRec, a, 0, "", ""); MQTT_STAY_ALIVE.store(value, Ordering::Relaxed); } else { - log(LogMessage::UnknownTopic, 0, 0, "", &topic).await; + log(LogMessage::UnknownTopic, 0, 0, "", &topic); } } }, diff --git a/Software/MainBoard/rust/src/hal/mod.rs b/Software/MainBoard/rust/src/hal/mod.rs index fec5d07..68b087d 100644 --- a/Software/MainBoard/rust/src/hal/mod.rs +++ b/Software/MainBoard/rust/src/hal/mod.rs @@ -85,14 +85,13 @@ use esp_alloc as _; use esp_backtrace as _; use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; use esp_hal::delay::Delay; -use esp_hal::i2c::master::{BusTimeout, Config, FsmTimeout, I2c, SoftwareTimeout}; +use esp_hal::i2c::master::{BusTimeout, Config, FsmTimeout, I2c}; use esp_hal::interrupt::software::SoftwareInterruptControl; use esp_hal::pcnt::unit::Unit; use esp_hal::pcnt::Pcnt; use esp_hal::rng::Rng; use esp_hal::rtc_cntl::{Rtc, SocResetReason}; use esp_hal::system::reset_reason; -use esp_hal::time::Rate; use esp_hal::timer::timg::{MwdtStage, TimerGroup, Wdt}; use esp_hal::uart::Uart; use esp_hal::Blocking; @@ -255,7 +254,8 @@ impl PlantHal { esp_alloc::heap_allocator!(size: 64 * 1024); esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000); - let rtc_peripheral: Rtc = Rtc::new(peripherals.LPWR); + let mut rtc_peripheral: Rtc = Rtc::new(peripherals.LPWR); + rtc_peripheral.rwdt.disable(); let timg0 = TimerGroup::new(peripherals.TIMG0); let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); @@ -469,8 +469,7 @@ impl PlantHal { to_config_mode as u32, "", &format!("{reasons:?}"), - ) - .await; + ); esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode) .await; @@ -575,8 +574,7 @@ impl PlantHal { 0, "", &err.to_string(), - ) - .await; + ); HAL { board_hal: v4_hal::create_v4( free_pins, diff --git a/Software/MainBoard/rust/src/hal/rtc.rs b/Software/MainBoard/rust/src/hal/rtc.rs index 11d7546..ef7eea9 100644 --- a/Software/MainBoard/rust/src/hal/rtc.rs +++ b/Software/MainBoard/rust/src/hal/rtc.rs @@ -1,5 +1,5 @@ -use crate::hal::Box; use crate::fat_error::FatResult; +use crate::hal::Box; use async_trait::async_trait; use bincode::{Decode, Encode}; use chrono::{DateTime, Utc}; @@ -26,7 +26,7 @@ pub trait RTCModuleInteraction { async fn set_rtc_time(&mut self, time: &DateTime) -> FatResult<()>; fn write(&mut self, offset: u32, data: &[u8]) -> FatResult<()>; - fn read(&mut self, offset:u32, data: &mut [u8]) -> FatResult<()>; + fn read(&mut self, offset: u32, data: &mut [u8]) -> FatResult<()>; } #[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)] @@ -67,7 +67,7 @@ impl RTCModuleInteraction for DS3231Module { Ok(()) } - fn read(&mut self, offset:u32, data: &mut [u8]) -> FatResult<()> { + fn read(&mut self, offset: u32, data: &mut [u8]) -> FatResult<()> { self.storage.read(offset, data)?; Ok(()) } diff --git a/Software/MainBoard/rust/src/hal/savegame_manager.rs b/Software/MainBoard/rust/src/hal/savegame_manager.rs index a60b99d..5ffd8d1 100644 --- a/Software/MainBoard/rust/src/hal/savegame_manager.rs +++ b/Software/MainBoard/rust/src/hal/savegame_manager.rs @@ -136,9 +136,7 @@ impl SavegameManager { let slot = self.storage.scan()?; match slot { None => Ok(None), - Some(slot) => { - self.load_slot(slot.idx) - } + Some(slot) => self.load_slot(slot.idx), } } diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index fe7fcce..228fe78 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -350,8 +350,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } Some(pump_ina) => { let raw = pump_ina.shunt_voltage()?; - let shunt_voltage = - Voltage::from_microvolts(raw.shunt_voltage_uv().abs() as f64); + let shunt_voltage = Voltage::from_microvolts(raw.shunt_voltage_uv().abs() as f64); let shut_value = Resistance::from_ohms(0.05_f64); let current = shunt_voltage.as_volts() / shut_value.as_ohms(); Ok(Current::from_amperes(current)) @@ -516,8 +515,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { for plant in 0..PLANT_COUNT { let a = moisture.sensor_a_hz[plant].unwrap_or(0.0) as u32; let b = moisture.sensor_b_hz[plant].unwrap_or(0.0) as u32; - log(LogMessage::TestSensor, a, b, &(plant + 1).to_string(), "") - .await; + log(LogMessage::TestSensor, a, b, &(plant + 1).to_string(), ""); } Timer::after_millis(10).await; Ok(()) diff --git a/Software/MainBoard/rust/src/log/interceptor.rs b/Software/MainBoard/rust/src/log/interceptor.rs index 549138b..196ebbd 100644 --- a/Software/MainBoard/rust/src/log/interceptor.rs +++ b/Software/MainBoard/rust/src/log/interceptor.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; use embassy_sync::mutex::Mutex; -use log::{LevelFilter, Log, Metadata, Record}; +use log::{error, LevelFilter, Log, Metadata, Record}; pub struct InterceptorLogger { // Async mutex for start/stop capture from async context @@ -34,9 +34,13 @@ impl InterceptorLogger { } pub fn init(&'static self) { - log::set_logger(self) - .map(|()| log::set_max_level(LevelFilter::Info)) - .unwrap(); + match log::set_logger(self) + .map(|()| log::set_max_level(LevelFilter::Info)) { + Ok(()) => {} + Err(e) => { + error!("Logger already set: {}", e); + } + } } } diff --git a/Software/MainBoard/rust/src/log/mod.rs b/Software/MainBoard/rust/src/log/mod.rs index ddc7328..7a25d8e 100644 --- a/Software/MainBoard/rust/src/log/mod.rs +++ b/Software/MainBoard/rust/src/log/mod.rs @@ -115,28 +115,25 @@ impl From for LogEntry { } } -pub async fn log( - message_key: LogMessage, - number_a: u32, - number_b: u32, - txt_short: &str, - txt_long: &str, -) { +pub 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); - LOG_CHANNEL - .send(LogRequest { - message_key, - number_a, - number_b, - txt_short: txt_short_stack, - txt_long: txt_long_stack, - }) - .await; + match LOG_CHANNEL.try_send(LogRequest { + message_key, + number_a, + number_b, + txt_short: txt_short_stack, + txt_long: txt_long_stack, + }) { + Ok(_) => {} + Err(_) => { + warn!("Log channel full, dropping log entry"); + } + } } impl LogArray { @@ -316,6 +313,10 @@ pub enum LogMessage { PumpMissingSensorCurrent, #[strum(serialize = "MPPT Current sensor could not be reached")] MPPTError, + #[strum( + serialize = "Trace: a: ${number_a} b: ${number_b} txt_s ${txt_short} long ${txt_long}" + )] + Trace, #[strum(serialize = "Parsing error reading message")] UnknownMessage, } diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index bf2acb4..c028d51 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -42,7 +42,7 @@ use embassy_sync::once_lock::OnceLock; use embassy_time::{Duration, Instant, Timer}; use esp_hal::rom::ets_delay_us; use esp_hal::system::software_reset; -use esp_println::{logger, println}; +use esp_println::println; use hal::battery::BatteryState; use log::LogMessage; use option_lock::OptionLock; @@ -185,7 +185,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { //check if we know the time current > 2020 (plausibility checks, this code is newer than 2020) if cur.year() < 2020 { to_config = true; - log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "").await; + log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); } info!("cur is {cur}"); match update_charge_indicator(&mut board).await { @@ -193,12 +193,12 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { Err(error) => { board.board_hal.general_fault(true).await; error!("Error updating charge indicator: {error}"); - log(LogMessage::MPPTError, 0, 0, "", "").await; + log(LogMessage::MPPTError, 0, 0, "", ""); let _ = board.board_hal.set_charge_indicator(false).await; } } if board.board_hal.get_esp().get_restart_to_conf() { - log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "").await; + log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); for _i in 0..2 { board.board_hal.general_fault(true).await; Timer::after_millis(100).await; @@ -210,7 +210,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { 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, "", "").await; + log(LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); for _i in 0..5 { board.board_hal.general_fault(true).await; Timer::after_millis(100).await; @@ -308,8 +308,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { .to_string() .as_str(), "", - ) - .await; + ); if to_config { //check if client or ap mode and init Wi-Fi @@ -324,7 +323,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { } wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await; } else { - log(LogMessage::NormalRun, 0, 0, "", "").await; + log(LogMessage::NormalRun, 0, 0, "", ""); } let dry_run = MQTT_STAY_ALIVE.load(Ordering::Relaxed); @@ -335,28 +334,22 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { 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::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()).await + log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string()) } } // disabled cannot trigger this because of wrapping if is_enabled @@ -365,7 +358,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { .warn_level(&board.board_hal.get_config().tank) .is_ok_and(|warn| warn) { - log(LogMessage::TankWaterLevelLow, 0, 0, "", "").await; + log(LogMessage::TankWaterLevelLow, 0, 0, "", ""); board.board_hal.general_fault(true).await; } } @@ -414,7 +407,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) && !water_frozen; if pump_required { - log(LogMessage::EnableMain, dry_run as u32, 0, "", "").await; + 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()) @@ -435,8 +428,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { plant_config.max_consecutive_pump_count as u32, &(plant_id + 1).to_string(), "", - ) - .await; + ); board.board_hal.fault(plant_id, true).await?; } log( @@ -445,8 +437,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { plant_config.pump_time_s as u32, &dry_run.to_string(), "", - ) - .await; + ); board .board_hal .get_esp() @@ -677,8 +668,7 @@ pub async fn do_secure_pump( current_ma as u32, plant_config.max_pump_current_ma.to_string().as_str(), step.to_string().as_str(), - ) - .await; + ); error = true; } else if high_current && first_error { log( @@ -687,8 +677,7 @@ pub async fn do_secure_pump( current_ma as u32, plant_config.max_pump_current_ma.to_string().as_str(), step.to_string().as_str(), - ) - .await; + ); board.board_hal.general_fault(true).await; board.board_hal.fault(plant_id, true).await?; if !plant_config.ignore_current_error { @@ -705,8 +694,7 @@ pub async fn do_secure_pump( current_ma as u32, plant_config.min_pump_current_ma.to_string().as_str(), step.to_string().as_str(), - ) - .await; + ); board.board_hal.general_fault(true).await; board.board_hal.fault(plant_id, true).await?; if !plant_config.ignore_current_error { @@ -725,8 +713,7 @@ pub async fn do_secure_pump( 0, "", "", - ) - .await; + ); error = true; break; } else { diff --git a/Software/MainBoard/rust/src/plant_state.rs b/Software/MainBoard/rust/src/plant_state.rs index 560c920..eed0c9d 100644 --- a/Software/MainBoard/rust/src/plant_state.rs +++ b/Software/MainBoard/rust/src/plant_state.rs @@ -1,6 +1,6 @@ -use bincode::{Decode, Encode}; use crate::hal::Moistures; use crate::{config::PlantConfig, hal::HAL, in_time_range}; +use bincode::{Decode, Encode}; use chrono::{DateTime, TimeDelta, Utc}; use chrono_tz::Tz; use serde::{Deserialize, Serialize}; diff --git a/Software/MainBoard/rust/src/webserver/get_json.rs b/Software/MainBoard/rust/src/webserver/get_json.rs index 4f2ee77..7251ae2 100644 --- a/Software/MainBoard/rust/src/webserver/get_json.rs +++ b/Software/MainBoard/rust/src/webserver/get_json.rs @@ -123,9 +123,7 @@ pub(crate) async fn get_config( let mut board = BOARD_ACCESS.get().await.lock().await; let json = match saveidx { None => serde_json::to_string(board.board_hal.get_config())?, - Some(idx) => { - board.board_hal.get_esp().load_config_slot(idx).await? - } + Some(idx) => board.board_hal.get_esp().load_config_slot(idx).await?, }; Ok(Some(json)) } @@ -174,7 +172,12 @@ pub(crate) async fn get_time( }, }; - let native = board.board_hal.get_time().await.with_timezone(&tz).to_rfc3339(); + let native = board + .board_hal + .get_time() + .await + .with_timezone(&tz) + .to_rfc3339(); let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await { Ok(time) => time.with_timezone(&tz).to_rfc3339(), diff --git a/Software/MainBoard/rust/src_webpack/src/main.ts b/Software/MainBoard/rust/src_webpack/src/main.ts index bb8ad59..5208998 100644 --- a/Software/MainBoard/rust/src_webpack/src/main.ts +++ b/Software/MainBoard/rust/src_webpack/src/main.ts @@ -47,28 +47,26 @@ export class Controller { }); } - loadLogLocaleConfig() { - return fetch(PUBLIC_URL + "/log_localization") - .then(response => response.json()) - .then(json => json as LogLocalisation) - .then(loglocale => { - controller.logView.setLogLocalisation(loglocale) - }) - .catch(error => { - console.log(error); - }); + async loadLogLocaleConfig() { + try { + const response = await fetch(PUBLIC_URL + "/log_localization"); + const json = await response.json(); + const loglocale = json as LogLocalisation; + controller.logView.setLogLocalisation(loglocale); + } catch (error) { + console.log(error); + } } - loadLog() { - return fetch(PUBLIC_URL + "/log") - .then(response => response.json()) - .then(json => json as LogArray) - .then(logs => { - controller.logView.setLog(logs) - }) - .catch(error => { - console.log(error); - }); + async loadLog() { + try { + const response = await fetch(PUBLIC_URL + "/log"); + const json = await response.json(); + const logs = json as LogArray; + controller.logView.setLog(logs); + } catch (error) { + console.log(error); + } } async getBackupInfo(): Promise { @@ -241,11 +239,11 @@ export class Controller { return response.status }) .then(status => { + controller.progressview.removeProgress("set_config"); if (status == 200) { - controller.progressview.removeProgress("set_config"); setTimeout(() => { - controller.downloadConfig().then(r => { - controller.updateSaveList().then(r => { + controller.downloadConfig().then(() => { + controller.updateSaveList().then(() => { }); }); }, 250) @@ -669,7 +667,7 @@ async function executeTasksSequentially() { } } -executeTasksSequentially().then(r => { +executeTasksSequentially().then(() => { controller.progressview.removeProgress("initial") });