From af27f3b82072530fb8422f356021495480f6c550 Mon Sep 17 00:00:00 2001 From: Empire Date: Sun, 15 Mar 2026 13:43:36 +0100 Subject: [PATCH 1/3] canbus debugging --- Software/MainBoard/rust/src/hal/battery.rs | 2 +- Software/MainBoard/rust/src/hal/v4_hal.rs | 4 +- Software/MainBoard/rust/src/main.rs | 81 ++++++++++++++++++++-- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/Software/MainBoard/rust/src/hal/battery.rs b/Software/MainBoard/rust/src/hal/battery.rs index 8de40c4..d723fb9 100644 --- a/Software/MainBoard/rust/src/hal/battery.rs +++ b/Software/MainBoard/rust/src/hal/battery.rs @@ -18,7 +18,7 @@ pub trait BatteryInteraction { async fn reset(&mut self) -> FatResult<()>; } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Copy, Clone)] pub struct BatteryInfo { pub voltage_milli_volt: u32, pub average_current_milli_ampere: i32, diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index 6d82f4f..ff8d87c 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -358,6 +358,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } async fn measure_moisture_hz(&mut self) -> FatResult { self.can_power.set_high(); + Timer::after_millis(1000).await; let config = self.twai_config.take().expect("twai config not set"); let mut twai = config.into_async().start(); @@ -377,6 +378,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { async fn detect_sensors(&mut self, request: Detection) -> FatResult { self.can_power.set_high(); + Timer::after_millis(1000).await; let config = self.twai_config.take().expect("twai config not set"); let mut twai = config.into_async().start(); @@ -409,7 +411,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { // Try a few times; we intentionally ignore rx here and rely on stub logic let resu = twai .transmit_async(&frame) - .with_timeout(Duration::from_millis(3000)) + .with_timeout(Duration::from_millis(500)) .await; match resu { Ok(_) => {} diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index a7d02f6..af2942f 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -530,13 +530,22 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { 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 { - board.board_hal.get_esp().set_low_voltage_in_cycle(); - info!("Set low voltage in cycle"); - } else if state_of_charge > board.board_hal.get_config().night_lamp.low_soc_restore { - board.board_hal.get_esp().clear_low_voltage_in_cycle(); - info!("Clear low voltage in cycle"); + match battery_state { + BatteryState::Unknown => { + light_state.battery_low = false; + }, + BatteryState::Info(data) => { + if data.state_of_charge < board.board_hal.get_config().night_lamp.low_soc_cutoff { + board.board_hal.get_esp().set_low_voltage_in_cycle(); + info!("Set low voltage in cycle"); + } + if data.state_of_charge > board.board_hal.get_config().night_lamp.low_soc_restore { + board.board_hal.get_esp().clear_low_voltage_in_cycle(); + info!("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 { @@ -583,7 +592,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { let deep_sleep_duration_minutes: u32 = // if battery soc is unknown assume battery has enough change - if state_of_charge < 10 && !matches!(battery_state, BatteryState::Unknown) { + if matches!(battery_state, BatteryState::Info(data) if data.state_of_charge < 10) { let _ = board .board_hal .get_esp() @@ -1002,7 +1011,65 @@ async fn wait_infinity( let mut pattern_step = 0; let serial_config_receive = AtomicBool::new(false); let mut suppress_further_mppt_error = false; + + // Long-press exit (for webserver config modes): hold boot button for 5 seconds. + let mut exit_hold_started: Option = None; + let exit_hold_duration = Duration::from_secs(5); + let mut exit_hold_blink = false; loop { + // While in config webserver mode, allow exiting via long-press. + if matches!(wait_type, WaitType::MissingConfig | WaitType::ConfigButton) { + let mut board = BOARD_ACCESS.get().await.lock().await; + let pressed = board.board_hal.get_esp().mode_override_pressed(); + + match (pressed, exit_hold_started) { + (true, None) => { + exit_hold_started = Some(Instant::now()); + PROGRESS_ACTIVE.store(true, Ordering::Relaxed); + } + (false, Some(_)) => { + exit_hold_started = None; + // Clear any interim hold display. + board.board_hal.clear_progress().await; + } + _ => {} + } + + if let Some(started) = exit_hold_started { + let elapsed = Instant::now() - started; + // Visible countdown: fill LEDs progressively during the hold. + // Also toggle general fault LED to match the "enter config" visibility. + exit_hold_blink = !exit_hold_blink; + + let progress = core::cmp::min(elapsed, exit_hold_duration); + let lit = ((progress.as_millis() as u64 * 8) / exit_hold_duration.as_millis() as u64) + .saturating_add(1) + .min(8) as usize; + + for i in 0..8 { + let _ = board.board_hal.fault(i, i < lit).await; + } + board.board_hal.general_fault(exit_hold_blink).await; + + if elapsed >= exit_hold_duration { + info!("Exiting config mode due to 5s button hold"); + board.board_hal.get_esp().set_restart_to_conf(false); + // ensure clean http answer / visible confirmation + Timer::after_millis(500).await; + board.board_hal + .deep_sleep(0) + .await; + } + + // Short tick while holding so the pattern updates smoothly. + drop(board); + Timer::after_millis(100).await; + continue; + } + // Release lock and continue with normal wait blinking. + drop(board); + } + { let mut board = BOARD_ACCESS.get().await.lock().await; match update_charge_indicator(&mut board).await { From 02c9486e983fa32a7c45b5607e8423ac41ee0cba Mon Sep 17 00:00:00 2001 From: Empire Date: Sun, 15 Mar 2026 20:28:53 +0100 Subject: [PATCH 2/3] canbus fix and ota adjustments --- Software/MainBoard/rust/src/hal/esp.rs | 10 +++++----- Software/MainBoard/rust/src/hal/v4_hal.rs | 4 ++-- Software/MainBoard/rust/src/main.rs | 5 ----- Software/MainBoard/rust/src_webpack/src/main.ts | 2 ++ Software/MainBoard/rust/src_webpack/src/ota.html | 8 ++------ 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs index 6f576dd..755f294 100644 --- a/Software/MainBoard/rust/src/hal/esp.rs +++ b/Software/MainBoard/rust/src/hal/esp.rs @@ -269,16 +269,16 @@ impl Esp<'_> { let ntp_addrs = stack .dns_query(NTP_SERVER, DnsQueryType::A) - .await - .expect("Failed to resolve DNS"); - if ntp_addrs.is_empty() { + .await; + if ntp_addrs.is_err() { bail!("Failed to resolve DNS"); } - info!("NTP server: {ntp_addrs:?}"); + let ntp = ntp_addrs.unwrap()[0]; + info!("NTP server: {ntp:?}"); let mut counter = 0; loop { - let addr: IpAddr = ntp_addrs[0].into(); + let addr: IpAddr = ntp.into(); let timeout = get_time(SocketAddr::from((addr, 123)), &socket, context) .with_timeout(Duration::from_millis((_max_wait_ms / 10) as u64)) .await; diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index 0d20d4f..a871273 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -357,7 +357,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } async fn measure_moisture_hz(&mut self) -> FatResult { self.can_power.set_high(); - Timer::after_millis(1000).await; + Timer::after_millis(500).await; let config = self.twai_config.take().expect("twai config not set"); let mut twai = config.into_async().start(); @@ -365,7 +365,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { let mut moistures = Moistures::default(); let _ = wait_for_can_measurements(&mut twai, &mut moistures) - .with_timeout(Duration::from_millis(5000)) + .with_timeout(Duration::from_millis(1000)) .await; let config = twai.stop().into_blocking(); diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index 96ff5e3..a9d26dd 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -510,11 +510,6 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { .unwrap_or(BatteryState::Unknown); info!("Battery state is {battery_state:?}"); - let state_of_charge = match &battery_state { - BatteryState::Unknown => 0, - BatteryState::Info(data) => data.state_of_charge, - }; - let mut light_state = LightState { enabled: board.board_hal.get_config().night_lamp.enabled, ..Default::default() diff --git a/Software/MainBoard/rust/src_webpack/src/main.ts b/Software/MainBoard/rust/src_webpack/src/main.ts index a9dc729..5e4ed1f 100644 --- a/Software/MainBoard/rust/src_webpack/src/main.ts +++ b/Software/MainBoard/rust/src_webpack/src/main.ts @@ -198,6 +198,8 @@ export class Controller { const crc = this.crc32(data); const size = data.length; + console.log("Uploading new firmware with size " + size + " and crc " + crc + "") + const payload = new Uint8Array(size + 8); const view = new DataView(payload.buffer); view.setUint32(0, size, true); diff --git a/Software/MainBoard/rust/src_webpack/src/ota.html b/Software/MainBoard/rust/src_webpack/src/ota.html index 0d18370..318e1cf 100644 --- a/Software/MainBoard/rust/src_webpack/src/ota.html +++ b/Software/MainBoard/rust/src_webpack/src/ota.html @@ -35,12 +35,8 @@
- State0: - -
-
- State1: - + State: +
From 924a9ba2284f1ee7a3d3c79b4fb306fd297fb992 Mon Sep 17 00:00:00 2001 From: Empire Date: Tue, 17 Mar 2026 20:29:51 +0100 Subject: [PATCH 3/3] add: script to erase OTA data using cargo espflash --- Software/MainBoard/rust/erase_ota.sh | 1 + 1 file changed, 1 insertion(+) create mode 100755 Software/MainBoard/rust/erase_ota.sh diff --git a/Software/MainBoard/rust/erase_ota.sh b/Software/MainBoard/rust/erase_ota.sh new file mode 100755 index 0000000..7e19270 --- /dev/null +++ b/Software/MainBoard/rust/erase_ota.sh @@ -0,0 +1 @@ +cargo espflash erase-parts otadata --partition-table partitions.csv