refactor: BatteryInfo structure (consistent layout)
- use tagged enum serialization for BatteryError - flatten BatteryInfo telemetry with consistent field names and typed error
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use crate::fat_error::{FatError, FatResult};
|
use crate::fat_error::{FatError, FatResult};
|
||||||
use crate::hal::Box;
|
use crate::hal::Box;
|
||||||
|
use alloc::string::String;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
@@ -18,15 +19,23 @@ pub trait BatteryInteraction {
|
|||||||
async fn reset(&mut self) -> FatResult<()>;
|
async fn reset(&mut self) -> FatResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Copy, Clone)]
|
#[derive(Debug, Serialize, Clone)]
|
||||||
pub struct BatteryInfo {
|
pub struct BatteryInfo {
|
||||||
pub voltage_milli_volt: u32,
|
pub voltage_mv: Option<u32>,
|
||||||
pub average_current_milli_ampere: i32,
|
pub avg_current_ma: Option<i32>,
|
||||||
pub design_milli_ampere_hour: u32,
|
pub design_mah: Option<u32>,
|
||||||
pub remaining_milli_ampere_hour: u32,
|
pub remaining_mah: Option<u32>,
|
||||||
pub state_of_charge: u8,
|
pub soc_pct: Option<f32>,
|
||||||
pub state_of_health: u32,
|
pub soh_pct: Option<f32>,
|
||||||
pub temperature: i32,
|
pub temperature_c: Option<i32>,
|
||||||
|
pub error: Option<BatteryError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum BatteryError {
|
||||||
|
NoBatteryMonitor,
|
||||||
|
CommunicationError { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@@ -71,17 +80,18 @@ impl BatteryInteraction for WCHI2CSlave<'_> {
|
|||||||
let config = Config::read_from_i2c(&mut self.i2c)?;
|
let config = Config::read_from_i2c(&mut self.i2c)?;
|
||||||
|
|
||||||
let state_of_charge =
|
let state_of_charge =
|
||||||
(state.remaining_capacity_mah * 100 / state.lifetime_capacity_mah) as u8;
|
state.remaining_capacity_mah as f32 * 100. / state.lifetime_capacity_mah as f32;
|
||||||
let state_of_health = state.lifetime_capacity_mah / config.capacity_mah * 100;
|
let state_of_health = state.lifetime_capacity_mah as f32 / config.capacity_mah as f32 * 100.;
|
||||||
|
|
||||||
Ok(BatteryState::Info(BatteryInfo {
|
Ok(BatteryState::Info(BatteryInfo {
|
||||||
voltage_milli_volt: state.current_mv,
|
voltage_mv: Some(state.current_mv),
|
||||||
average_current_milli_ampere: 1337,
|
avg_current_ma: Some(1337),
|
||||||
design_milli_ampere_hour: config.capacity_mah,
|
design_mah: Some(config.capacity_mah),
|
||||||
remaining_milli_ampere_hour: state.remaining_capacity_mah,
|
remaining_mah: Some(state.remaining_capacity_mah),
|
||||||
state_of_charge,
|
soc_pct: Some(state_of_charge),
|
||||||
state_of_health,
|
soh_pct: Some(state_of_health),
|
||||||
temperature: state.temperature_celcius,
|
temperature_c: Some(state.temperature_celcius),
|
||||||
|
error: None
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ use embassy_sync::once_lock::OnceLock;
|
|||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use esp_hal::rom::ets_delay_us;
|
use esp_hal::rom::ets_delay_us;
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
use esp_println::println;
|
use esp_println::{logger, println};
|
||||||
use hal::battery::BatteryState;
|
use hal::battery::{BatteryError, BatteryInfo, BatteryState};
|
||||||
use log::LogMessage;
|
use log::LogMessage;
|
||||||
use option_lock::OptionLock;
|
use option_lock::OptionLock;
|
||||||
use plant_state::PlantState;
|
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,
|
board.board_hal.get_config().night_lamp.night_lamp_hour_end,
|
||||||
);
|
);
|
||||||
|
|
||||||
match battery_state {
|
match &battery_state {
|
||||||
BatteryState::Unknown => {
|
BatteryState::Unknown => {
|
||||||
light_state.battery_low = false;
|
light_state.battery_low = false;
|
||||||
}
|
}
|
||||||
BatteryState::Info(data) => {
|
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();
|
board.board_hal.get_esp().set_low_voltage_in_cycle();
|
||||||
info!("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();
|
board.board_hal.get_esp().clear_low_voltage_in_cycle();
|
||||||
info!("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 =
|
let deep_sleep_duration_minutes: u32 =
|
||||||
// if battery soc is unknown assume battery has enough change
|
// 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;
|
let _ = mqtt::publish("/deepsleep", "low Volt 12h").await;
|
||||||
12 * 60
|
12 * 60
|
||||||
} else if is_day {
|
} else if is_day {
|
||||||
@@ -984,16 +984,38 @@ async fn publish_mppt_state(
|
|||||||
async fn publish_battery_state(
|
async fn publish_battery_state(
|
||||||
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||||
) -> FatResult<()> {
|
) -> FatResult<()> {
|
||||||
let state = board.board_hal.get_battery_monitor().get_state().await;
|
let telemetry = match board
|
||||||
let value = match state {
|
.board_hal
|
||||||
Ok(state) => {
|
.get_battery_monitor()
|
||||||
let json = serde_json::to_string(&state)?.to_owned();
|
.get_state()
|
||||||
json.to_owned()
|
.await
|
||||||
}
|
|
||||||
Err(_) => "error".to_owned(),
|
|
||||||
};
|
|
||||||
{
|
{
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user