diff --git a/Software/MainBoard/rust/src/hal/battery.rs b/Software/MainBoard/rust/src/hal/battery.rs index d723fb9..fd05964 100644 --- a/Software/MainBoard/rust/src/hal/battery.rs +++ b/Software/MainBoard/rust/src/hal/battery.rs @@ -1,5 +1,6 @@ use crate::fat_error::{FatError, FatResult}; use crate::hal::Box; +use alloc::string::String; use async_trait::async_trait; use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -18,15 +19,23 @@ pub trait BatteryInteraction { async fn reset(&mut self) -> FatResult<()>; } -#[derive(Debug, Serialize, Copy, Clone)] +#[derive(Debug, Serialize, Clone)] pub struct BatteryInfo { - pub voltage_milli_volt: u32, - pub average_current_milli_ampere: i32, - pub design_milli_ampere_hour: u32, - pub remaining_milli_ampere_hour: u32, - pub state_of_charge: u8, - pub state_of_health: u32, - pub temperature: i32, + pub voltage_mv: Option, + pub avg_current_ma: Option, + pub design_mah: Option, + pub remaining_mah: Option, + pub soc_pct: Option, + pub soh_pct: Option, + pub temperature_c: Option, + pub error: Option, +} + +#[derive(Debug, Serialize, Clone)] +#[serde(tag = "kind")] +pub enum BatteryError { + NoBatteryMonitor, + CommunicationError { message: String }, } #[derive(Debug, Serialize)] @@ -71,17 +80,18 @@ impl BatteryInteraction for WCHI2CSlave<'_> { let config = Config::read_from_i2c(&mut self.i2c)?; let state_of_charge = - (state.remaining_capacity_mah * 100 / state.lifetime_capacity_mah) as u8; - let state_of_health = state.lifetime_capacity_mah / config.capacity_mah * 100; + state.remaining_capacity_mah as f32 * 100. / state.lifetime_capacity_mah as f32; + let state_of_health = state.lifetime_capacity_mah as f32 / config.capacity_mah as f32 * 100.; Ok(BatteryState::Info(BatteryInfo { - voltage_milli_volt: state.current_mv, - average_current_milli_ampere: 1337, - design_milli_ampere_hour: config.capacity_mah, - remaining_milli_ampere_hour: state.remaining_capacity_mah, - state_of_charge, - state_of_health, - temperature: state.temperature_celcius, + voltage_mv: Some(state.current_mv), + avg_current_ma: Some(1337), + design_mah: Some(config.capacity_mah), + remaining_mah: Some(state.remaining_capacity_mah), + soc_pct: Some(state_of_charge), + soh_pct: Some(state_of_health), + temperature_c: Some(state.temperature_celcius), + error: None })) } diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index 61bb631..a9248cc 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -42,8 +42,8 @@ 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::println; -use hal::battery::BatteryState; +use esp_println::{logger, println}; +use hal::battery::{BatteryError, BatteryInfo, BatteryState}; use log::LogMessage; use option_lock::OptionLock; use plant_state::PlantState; @@ -581,16 +581,16 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { board.board_hal.get_config().night_lamp.night_lamp_hour_end, ); - match battery_state { + 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 { + if data.soc_pct.is_some_and(|soc| soc < board.board_hal.get_config().night_lamp.low_soc_cutoff as f32) { 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 { + if data.soc_pct.is_some_and(|soc| soc > board.board_hal.get_config().night_lamp.low_soc_restore as f32) { board.board_hal.get_esp().clear_low_voltage_in_cycle(); info!("Clear low voltage in cycle"); } @@ -639,7 +639,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 matches!(battery_state, BatteryState::Info(data) if data.state_of_charge < 10) { + if matches!(battery_state, BatteryState::Info(data) if data.soc_pct.is_some_and(|soc| soc < 10.)) { let _ = mqtt::publish("/deepsleep", "low Volt 12h").await; 12 * 60 } else if is_day { @@ -984,16 +984,38 @@ async fn publish_mppt_state( async fn publish_battery_state( board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, ) -> FatResult<()> { - let state = board.board_hal.get_battery_monitor().get_state().await; - let value = match state { - Ok(state) => { - let json = serde_json::to_string(&state)?.to_owned(); - json.to_owned() - } - Err(_) => "error".to_owned(), - }; + let telemetry = match board + .board_hal + .get_battery_monitor() + .get_state() + .await { - let _ = mqtt::publish("/battery", &*value).await; + Ok(BatteryState::Info(info)) => info, + Ok(BatteryState::Unknown) => BatteryInfo { + voltage_mv: None, + avg_current_ma: None, + soc_pct: None, + soh_pct: None, + temperature_c: None, + remaining_mah: None, + design_mah: None, + error: Some(BatteryError::NoBatteryMonitor), + }, + Err(e) => BatteryInfo { + voltage_mv: None, + avg_current_ma: None, + soc_pct: None, + soh_pct: None, + temperature_c: None, + remaining_mah: None, + design_mah: None, + error: Some(BatteryError::CommunicationError { + message: alloc::format!("{:?}", e), + }), + }, + }; + if let Ok(json) = serde_json::to_string(&telemetry) { + let _ = mqtt::publish("/battery", &json).await; } Ok(()) }