use crate::fat_error::{FatError, FatResult}; use crate::hal::Box; use alloc::string::String; use async_trait::async_trait; use bq34z100::{Bq34z100g1, Bq34z100g1Driver, Flags}; use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use esp_hal::delay::Delay; use esp_hal::i2c::master::I2c; use esp_hal::Blocking; use measurements::Temperature; use serde::Serialize; #[async_trait] pub trait BatteryInteraction { async fn state_charge_percent(&mut self) -> FatResult; async fn remaining_milli_ampere_hour(&mut self) -> FatResult; async fn max_milli_ampere_hour(&mut self) -> FatResult; async fn design_milli_ampere_hour(&mut self) -> FatResult; async fn voltage_milli_volt(&mut self) -> FatResult; async fn average_current_milli_ampere(&mut self) -> FatResult; async fn cycle_count(&mut self) -> FatResult; async fn state_health_percent(&mut self) -> FatResult; async fn bat_temperature(&mut self) -> FatResult; async fn get_battery_state(&mut self) -> FatResult; } #[derive(Debug, Serialize)] pub struct BatteryInfo { pub voltage_milli_volt: u16, pub average_current_milli_ampere: i16, pub cycle_count: u16, pub design_milli_ampere_hour: u16, pub remaining_milli_ampere_hour: u16, pub state_of_charge: f32, pub state_of_health: u16, pub temperature: u16, } #[derive(Debug, Serialize)] pub enum BatteryError { NoBatteryMonitor, CommunicationError(String), } #[derive(Debug, Serialize)] pub enum BatteryState { Unknown, Info(BatteryInfo), } /// If no battery monitor is installed this implementation will be used pub struct NoBatteryMonitor {} #[async_trait] impl BatteryInteraction for NoBatteryMonitor { async fn state_charge_percent(&mut self) -> FatResult { // No monitor configured: assume full battery for lightstate logic Ok(100.0) } async fn remaining_milli_ampere_hour(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn max_milli_ampere_hour(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn design_milli_ampere_hour(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn voltage_milli_volt(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn average_current_milli_ampere(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn cycle_count(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn state_health_percent(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn bat_temperature(&mut self) -> FatResult { Err(FatError::NoBatteryMonitor) } async fn get_battery_state(&mut self) -> FatResult { Ok(BatteryState::Unknown) } } //TODO implement this battery monitor kind once controller is complete #[allow(dead_code)] pub struct WchI2cSlave {} pub type I2cDev = I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>; pub struct BQ34Z100G1 { pub battery_driver: Bq34z100g1Driver, } #[async_trait] impl BatteryInteraction for BQ34Z100G1 { async fn state_charge_percent(&mut self) -> FatResult { self.battery_driver .state_of_charge() .map(|v| v as f32) .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn remaining_milli_ampere_hour(&mut self) -> FatResult { self.battery_driver .remaining_capacity() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn max_milli_ampere_hour(&mut self) -> FatResult { self.battery_driver .full_charge_capacity() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn design_milli_ampere_hour(&mut self) -> FatResult { self.battery_driver .design_capacity() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn voltage_milli_volt(&mut self) -> FatResult { self.battery_driver.voltage().map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn average_current_milli_ampere(&mut self) -> FatResult { self.battery_driver .average_current() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn cycle_count(&mut self) -> FatResult { self.battery_driver .cycle_count() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn state_health_percent(&mut self) -> FatResult { self.battery_driver .state_of_health() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn bat_temperature(&mut self) -> FatResult { self.battery_driver .temperature() .map_err(|e| FatError::String { error: alloc::format!("{:?}", e), }) } async fn get_battery_state(&mut self) -> FatResult { Ok(BatteryState::Info(BatteryInfo { voltage_milli_volt: self.voltage_milli_volt().await?, average_current_milli_ampere: self.average_current_milli_ampere().await?, cycle_count: self.cycle_count().await?, design_milli_ampere_hour: self.design_milli_ampere_hour().await?, remaining_milli_ampere_hour: self.remaining_milli_ampere_hour().await?, state_of_charge: self.state_charge_percent().await?, state_of_health: self.state_health_percent().await?, temperature: self.bat_temperature().await?, })) } } pub fn print_battery_bq34z100( battery_driver: &mut Bq34z100g1Driver>, Delay>, ) -> FatResult<()> { log::info!("Try communicating with battery"); let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { log::info!("Firmware {:?}", e); 0 }); log::info!("fw version is {}", fwversion); let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| { log::info!("Design capacity {:?}", e); 0 }); log::info!("Design Capacity {}", design_capacity); if design_capacity == 1000 { log::info!("Still stock configuring battery, readouts are likely to be wrong!"); } let flags = battery_driver.get_flags_decoded().unwrap_or(Flags { fast_charge_allowed: false, full_chage: false, charging_not_allowed: false, charge_inhibit: false, bat_low: false, bat_high: false, over_temp_discharge: false, over_temp_charge: false, discharge: false, state_of_charge_f: false, state_of_charge_1: false, cf: false, ocv_taken: false, }); log::info!("Flags {:?}", flags); let chem_id = battery_driver.chem_id().unwrap_or_else(|e| { log::info!("Chemid {:?}", e); 0 }); let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| { log::info!("Bat Temp {:?}", e); 0 }); let temp_c = Temperature::from_kelvin(bat_temp as f64 / 10_f64).as_celsius(); let voltage = battery_driver.voltage().unwrap_or_else(|e| { log::info!("Bat volt {:?}", e); 0 }); let current = battery_driver.current().unwrap_or_else(|e| { log::info!("Bat current {:?}", e); 0 }); let state = battery_driver.state_of_charge().unwrap_or_else(|e| { log::info!("Bat Soc {:?}", e); 0 }); let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| { log::info!("Bat Charge Volt {:?}", e); 0 }); let charge_current = battery_driver.charge_current().unwrap_or_else(|e| { log::info!("Bat Charge Current {:?}", e); 0 }); log::info!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current); let _ = battery_driver.unsealed(); let _ = battery_driver.it_enable(); Ok(()) }