From e6f8e34f7d595856444f4b6c8ac0f4230467482a Mon Sep 17 00:00:00 2001 From: Empire Date: Sun, 1 Feb 2026 03:57:36 +0100 Subject: [PATCH] remove: delete `initial_hal` implementation, update moisture sensor logic to handle optional raw values, optimize TWAI management, and improve CAN data handling --- Software/CAN_Sensor/src/main.rs | 7 +- Software/MainBoard/rust/src/hal/esp.rs | 2 +- .../MainBoard/rust/src/hal/initial_hal.rs | 147 ------------------ Software/MainBoard/rust/src/hal/mod.rs | 8 +- Software/MainBoard/rust/src/hal/v4_hal.rs | 90 ++++------- Software/MainBoard/rust/src/plant_state.rs | 72 +++++---- .../MainBoard/rust/src/webserver/post_json.rs | 6 +- 7 files changed, 86 insertions(+), 246 deletions(-) delete mode 100644 Software/MainBoard/rust/src/hal/initial_hal.rs diff --git a/Software/CAN_Sensor/src/main.rs b/Software/CAN_Sensor/src/main.rs index 8acdeba..23438b6 100644 --- a/Software/CAN_Sensor/src/main.rs +++ b/Software/CAN_Sensor/src/main.rs @@ -315,7 +315,7 @@ async fn worker( probe_gnd.set_as_output(Speed::Low); probe_gnd.set_low(); - let probe_duration = Duration::from_millis(1000); + let probe_duration = Duration::from_millis(100); while Instant::now() .checked_duration_since(start) .unwrap_or(Duration::from_millis(0)) @@ -352,8 +352,7 @@ async fn worker( } probe_gnd.set_as_input(Pull::None); - // Compute frequency from 100 ms window - let freq_hz = pulses; // pulses per 0.1s => Hz + let freq_hz: u32 = pulses * (1000/probe_duration.as_millis()).into(); // pulses per 0.1s => Hz let mut msg: heapless::String<128> = heapless::String::new(); let _ = write!( @@ -365,7 +364,7 @@ async fn worker( ); log(msg); - let mut moisture = CanFrame::new(moisture_id, &(freq_hz as u16).to_be_bytes()).unwrap(); + let mut moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap(); match can.transmit(&mut moisture) { Ok(..) => { let mut msg: heapless::String<128> = heapless::String::new(); diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs index 89d0ef5..03e2fb6 100644 --- a/Software/MainBoard/rust/src/hal/esp.rs +++ b/Software/MainBoard/rust/src/hal/esp.rs @@ -162,7 +162,7 @@ impl Esp<'_> { loop { match self.uart0.read_buffered(&mut buf) { Ok(read) => { - if (read == 0) { + if read == 0 { return Ok(None); } let c = buf[0] as char; diff --git a/Software/MainBoard/rust/src/hal/initial_hal.rs b/Software/MainBoard/rust/src/hal/initial_hal.rs deleted file mode 100644 index bcfc3a4..0000000 --- a/Software/MainBoard/rust/src/hal/initial_hal.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::alloc::boxed::Box; -use crate::fat_error::{FatError, FatResult}; -use crate::hal::esp::Esp; -use crate::hal::rtc::{BackupHeader, RTCModuleInteraction}; -use crate::hal::water::TankSensor; -use crate::hal::{BoardInteraction, FreePeripherals, Moistures, TIME_ACCESS}; -use crate::{ - bail, - config::PlantControllerConfig, - hal::battery::{BatteryInteraction, NoBatteryMonitor}, -}; -use async_trait::async_trait; -use chrono::{DateTime, Utc}; -use esp_hal::gpio::{Level, Output, OutputConfig}; -use measurements::{Current, Voltage}; - -pub struct Initial<'a> { - pub(crate) general_fault: Output<'a>, - pub(crate) esp: Esp<'a>, - pub(crate) config: PlantControllerConfig, - pub(crate) battery: Box, - pub rtc: Box, -} - -pub(crate) struct NoRTC {} - -#[async_trait(?Send)] -impl RTCModuleInteraction for NoRTC { - async fn get_backup_info(&mut self) -> Result { - bail!("Please configure board revision") - } - - async fn get_backup_config(&mut self, _chunk: usize) -> FatResult<([u8; 32], usize, u16)> { - bail!("Please configure board revision") - } - - async fn backup_config(&mut self, _offset: usize, _bytes: &[u8]) -> FatResult<()> { - bail!("Please configure board revision") - } - - async fn backup_config_finalize(&mut self, _crc: u16, _length: usize) -> FatResult<()> { - bail!("Please configure board revision") - } - - async fn get_rtc_time(&mut self) -> Result, FatError> { - bail!("Please configure board revision") - } - - async fn set_rtc_time(&mut self, _time: &DateTime) -> Result<(), FatError> { - bail!("Please configure board revision") - } -} - -pub(crate) fn create_initial_board( - free_pins: FreePeripherals<'static>, - config: PlantControllerConfig, - esp: Esp<'static>, -) -> Result + Send>, FatError> { - log::info!("Start initial"); - let general_fault = Output::new(free_pins.gpio23, Level::Low, OutputConfig::default()); - let v = Initial { - general_fault, - config, - esp, - battery: Box::new(NoBatteryMonitor {}), - rtc: Box::new(NoRTC {}), - }; - Ok(Box::new(v)) -} - -#[async_trait(?Send)] -impl<'a> BoardInteraction<'a> for Initial<'a> { - fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> { - bail!("Please configure board revision") - } - - fn get_esp(&mut self) -> &mut Esp<'a> { - &mut self.esp - } - - fn get_config(&mut self) -> &PlantControllerConfig { - &self.config - } - - fn get_battery_monitor(&mut self) -> &mut Box { - &mut self.battery - } - - fn get_rtc_module(&mut self) -> &mut Box { - &mut self.rtc - } - - async fn set_charge_indicator(&mut self, _charging: bool) -> Result<(), FatError> { - bail!("Please configure board revision") - } - - async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { - let rtc = TIME_ACCESS.get().await.lock().await; - self.esp.deep_sleep(duration_in_ms, rtc); - } - fn is_day(&self) -> bool { - false - } - async fn light(&mut self, _enable: bool) -> Result<(), FatError> { - bail!("Please configure board revision") - } - - async fn pump(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> { - bail!("Please configure board revision") - } - - async fn pump_current(&mut self, _plant: usize) -> Result { - bail!("Please configure board revision") - } - - async fn fault(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> { - bail!("Please configure board revision") - } - - async fn measure_moisture_hz(&mut self) -> Result { - bail!("Please configure board revision") - } - - async fn general_fault(&mut self, enable: bool) { - self.general_fault.set_level(enable.into()); - } - - async fn test(&mut self) -> Result<(), FatError> { - bail!("Please configure board revision") - } - - fn set_config(&mut self, config: PlantControllerConfig) { - self.config = config; - } - - async fn get_mptt_voltage(&mut self) -> Result { - bail!("Please configure board revision") - } - - async fn get_mptt_current(&mut self) -> Result { - bail!("Please configure board revision") - } - - async fn can_power(&mut self, state: bool) -> FatResult<()> { - bail!("Please configure board revision") - } -} diff --git a/Software/MainBoard/rust/src/hal/mod.rs b/Software/MainBoard/rust/src/hal/mod.rs index d79cdda..eb6aa5e 100644 --- a/Software/MainBoard/rust/src/hal/mod.rs +++ b/Software/MainBoard/rust/src/hal/mod.rs @@ -3,7 +3,6 @@ use esp_hal::uart::{Config as UartConfig}; pub(crate) mod battery; // mod can_api; // replaced by external canapi crate pub mod esp; -mod initial_hal; mod little_fs2storage_adapter; pub(crate) mod rtc; mod shared_flash; @@ -40,7 +39,7 @@ use esp_hal::peripherals::TWAI0; use crate::{ bail, - config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, + config::{BatteryBoardVersion, PlantControllerConfig}, hal::{ battery::{BatteryInteraction, NoBatteryMonitor}, esp::Esp, @@ -50,7 +49,6 @@ use crate::{ }; use alloc::boxed::Box; use alloc::format; -use alloc::string::String; use alloc::sync::Arc; use async_trait::async_trait; use bincode::{Decode, Encode}; @@ -682,8 +680,8 @@ pub async fn esp_set_time(time: DateTime) -> FatResult<()> { #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize)] pub struct Moistures { - pub sensor_a_hz: [f32; PLANT_COUNT], - pub sensor_b_hz: [f32; PLANT_COUNT], + pub sensor_a_hz: [Option; PLANT_COUNT], + pub sensor_b_hz: [Option; PLANT_COUNT], } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index 37bfdbd..64cd5c5 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -11,8 +11,7 @@ use crate::hal::{ }; use crate::log::{LogMessage, LOG_ACCESS}; use alloc::boxed::Box; -use alloc::string::{String, ToString}; -use alloc::vec; +use alloc::string::{ToString}; use async_trait::async_trait; use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET}; use canapi::SensorSlot; @@ -22,7 +21,6 @@ use embassy_time::{Duration, Timer, WithTimeout}; use embedded_can::{Frame, Id}; use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull}; use esp_hal::i2c::master::I2c; -use esp_hal::peripherals; use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode}; use esp_hal::{twai, Async, Blocking}; use ina219::address::{Address, Pin}; @@ -134,7 +132,7 @@ pub struct V4<'a> { extra1: Output<'a>, extra2: Output<'a>, - can_mutex: embassy_sync::mutex::Mutex, + twai_config: Option> } @@ -152,11 +150,13 @@ pub(crate) async fn create_v4( let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default()); general_fault.set_low(); - let twai_peripheral = Some(peripherals.twai); - - - let twai_rx_pin = Some(peripherals.gpio2); - let twai_tx_pin = Some(peripherals.gpio0); + let twai_config = Some(TwaiConfiguration::new( + peripherals.twai, + peripherals.gpio0, + peripherals.gpio2, + TWAI_BAUDRATE, + TwaiMode::Normal, + )); let extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default()); let extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default()); @@ -260,38 +260,11 @@ pub(crate) async fn create_v4( extra1, extra2, can_power, - can_mutex: embassy_sync::mutex::Mutex::new(()), + twai_config }; Ok(Box::new(v)) } -fn teardown_twai(old: Twai) { - let config = old.stop(); - drop(config); - // Re-acquire the peripheral and pins - let rx_pin = unsafe { peripherals::GPIO2::steal() }; - let tx_pin = unsafe { peripherals::GPIO0::steal() }; - - // Set pins to low to avoid parasitic powering - let _ = Input::new(rx_pin, InputConfig::default().with_pull(Pull::None)); - let _ = Input::new(tx_pin, InputConfig::default().with_pull(Pull::None)); -} - -fn create_twai<'a>() -> Twai<'a, Async> { - // Release the pins from Output back to raw pins and store everything - let twai = unsafe { peripherals::TWAI0::steal() }; - let twai_rx_pin = unsafe { peripherals::GPIO2::steal() }; - let twai_tx_pin = unsafe { peripherals::GPIO0::steal() }; - - let twai_config = TwaiConfiguration::new( - twai, - twai_rx_pin, - twai_tx_pin, - TWAI_BAUDRATE, - TwaiMode::Normal, - ); - twai_config.into_async().start() -} #[async_trait(?Send)] impl<'a> BoardInteraction<'a> for V4<'a> { @@ -384,10 +357,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } async fn measure_moisture_hz(&mut self) -> FatResult { self.can_power.set_high(); - let mut twai = create_twai(); - - - + let config = self.twai_config.take().expect("twai config not set"); + let mut twai = config.into_async().start(); Timer::after_millis(10).await; @@ -395,17 +366,20 @@ impl<'a> BoardInteraction<'a> for V4<'a> { let _ = wait_for_can_measurements(&mut twai, &mut moistures) .with_timeout(Duration::from_millis(5000)) .await; - teardown_twai(twai); + + let config = twai.stop().into_blocking(); + self.twai_config.replace(config); + self.can_power.set_low(); Ok(moistures) } async fn detect_sensors(&mut self) -> FatResult { self.can_power.set_high(); - let mut twai = create_twai(); - // Give CAN some time to stabilize - Timer::after_millis(3000).await; + let config = self.twai_config.take().expect("twai config not set"); + let mut twai = config.into_async().start(); + Timer::after_millis(1000).await; info!("Sending info messages now"); // Send a few test messages per potential sensor node for plant in 0..PLANT_COUNT { @@ -418,7 +392,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(1000)) + .with_timeout(Duration::from_millis(3000)) .await; match resu { Ok(_) => { @@ -438,11 +412,13 @@ 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(1000)) + .with_timeout(Duration::from_millis(3000)) .await; - teardown_twai(twai); + let config = twai.stop().into_blocking(); + self.twai_config.replace(config); + self.can_power.set_low(); @@ -490,8 +466,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } let moisture = self.measure_moisture_hz().await?; for plant in 0..PLANT_COUNT { - let a = moisture.sensor_a_hz[plant] as u32; - let b = moisture.sensor_b_hz[plant] as u32; + 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_ACCESS .lock() .await @@ -515,11 +491,9 @@ impl<'a> BoardInteraction<'a> for V4<'a> { } async fn can_power(&mut self, state: bool) -> FatResult<()> { - if state && self.can_power.is_set_low(){ - self.can_power.set_high(); - create_twai(); + if state && self.can_power.is_set_low() { + self.can_power.set_high(); } else { - teardown_twai(create_twai()); self.can_power.set_low(); } Ok(()) @@ -552,10 +526,10 @@ async fn wait_for_can_measurements( let frequency = u16::from_be_bytes([data[0], data[1]]); match sensor { SensorSlot::A => { - moistures.sensor_a_hz[plant] = frequency as f32; + moistures.sensor_a_hz[plant] = Some(frequency as f32); } SensorSlot::B => { - moistures.sensor_b_hz[plant] = frequency as f32; + moistures.sensor_b_hz[plant] = Some(frequency as f32); } } } @@ -585,10 +559,10 @@ impl From for DetectionResult { fn from(value: Moistures) -> Self { let mut result = DetectionResult::default(); for (plant, sensor) in value.sensor_a_hz.iter().enumerate() { - result.plant[plant].sensor_a = *sensor > 1.0_f32; + result.plant[plant].sensor_a = sensor.is_some(); } for (plant, sensor) in value.sensor_b_hz.iter().enumerate() { - result.plant[plant].sensor_b = *sensor > 1.0_f32; + result.plant[plant].sensor_b = sensor.is_some(); } result } diff --git a/Software/MainBoard/rust/src/plant_state.rs b/Software/MainBoard/rust/src/plant_state.rs index 87649c6..7a7bb59 100644 --- a/Software/MainBoard/rust/src/plant_state.rs +++ b/Software/MainBoard/rust/src/plant_state.rs @@ -9,6 +9,7 @@ const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, thi #[derive(Debug, PartialEq, Serialize)] pub enum MoistureSensorError { + NoMessage, ShortCircuit { hz: f32, max: f32 }, OpenLoop { hz: f32, min: f32 }, } @@ -118,41 +119,56 @@ impl PlantState { ) -> Self { let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a { let raw = moistures.sensor_a_hz[plant_id]; - match map_range_moisture( - raw, - board.board_hal.get_config().plants[plant_id] - .moisture_sensor_min_frequency - .map(|a| a as f32), - board.board_hal.get_config().plants[plant_id] - .moisture_sensor_max_frequency - .map(|b| b as f32), - ) { - Ok(moisture_percent) => MoistureSensorState::MoistureValue { - raw_hz: raw, - moisture_percent, - }, - Err(err) => MoistureSensorState::SensorError(err), + match raw { + None => { + MoistureSensorState::SensorError(MoistureSensorError::NoMessage) + } + Some(raw) => { + match map_range_moisture( + raw, + board.board_hal.get_config().plants[plant_id] + .moisture_sensor_min_frequency + .map(|a| a as f32), + board.board_hal.get_config().plants[plant_id] + .moisture_sensor_max_frequency + .map(|b| b as f32), + ) { + Ok(moisture_percent) => MoistureSensorState::MoistureValue { + raw_hz: raw, + moisture_percent, + }, + Err(err) => MoistureSensorState::SensorError(err), + } + } } + } else { MoistureSensorState::Disabled }; let sensor_b = if board.board_hal.get_config().plants[plant_id].sensor_b { let raw = moistures.sensor_b_hz[plant_id]; - match map_range_moisture( - raw, - board.board_hal.get_config().plants[plant_id] - .moisture_sensor_min_frequency - .map(|a| a as f32), - board.board_hal.get_config().plants[plant_id] - .moisture_sensor_max_frequency - .map(|b| b as f32), - ) { - Ok(moisture_percent) => MoistureSensorState::MoistureValue { - raw_hz: raw, - moisture_percent, - }, - Err(err) => MoistureSensorState::SensorError(err), + match raw { + None => { + MoistureSensorState::SensorError(MoistureSensorError::NoMessage) + } + Some(raw) => { + match map_range_moisture( + raw, + board.board_hal.get_config().plants[plant_id] + .moisture_sensor_min_frequency + .map(|a| a as f32), + board.board_hal.get_config().plants[plant_id] + .moisture_sensor_max_frequency + .map(|b| b as f32), + ) { + Ok(moisture_percent) => MoistureSensorState::MoistureValue { + raw_hz: raw, + moisture_percent, + }, + Err(err) => MoistureSensorState::SensorError(err), + } + } } } else { MoistureSensorState::Disabled diff --git a/Software/MainBoard/rust/src/webserver/post_json.rs b/Software/MainBoard/rust/src/webserver/post_json.rs index 74c3aaf..df0d1da 100644 --- a/Software/MainBoard/rust/src/webserver/post_json.rs +++ b/Software/MainBoard/rust/src/webserver/post_json.rs @@ -130,11 +130,11 @@ where T: Read + Write, { let actual_data = read_up_to_bytes_from_request(request, None).await?; - let pump_test: CanPower = serde_json::from_slice(&actual_data)?; + let can_power_request: CanPower = serde_json::from_slice(&actual_data)?; let mut board = BOARD_ACCESS.get().await.lock().await; - let config = &board.board_hal.can_power(pump_test.state).await?; - let enable = pump_test.state; + board.board_hal.can_power(can_power_request.state).await?; + let enable = can_power_request.state; info!( "set can power to {enable}" );