use anyhow::anyhow; use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; use embedded_hal_bus::i2c::MutexDevice; use esp_idf_hal::delay::Delay; use esp_idf_hal::i2c::{I2cDriver, I2cError}; use measurements::Temperature; use serde::Serialize; pub trait BatteryInteraction { fn state_charge_percent(&mut self) -> Result; fn remaining_milli_ampere_hour(&mut self) -> Result; fn max_milli_ampere_hour(&mut self) -> Result; fn design_milli_ampere_hour(&mut self) -> Result; fn voltage_milli_volt(&mut self) -> Result; fn average_current_milli_ampere(&mut self) -> Result; fn cycle_count(&mut self) -> Result; fn state_health_percent(&mut self) -> Result; fn bat_temperature(&mut self) -> Result; fn get_battery_state(&mut self) -> Result; } #[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), } impl From> for BatteryError { fn from(err: Bq34Z100Error) -> Self { BatteryError::CommunicationError( anyhow!("failed to communicate with battery monitor: {:?}", err).to_string(), ) } } #[derive(Debug, Serialize)] pub enum BatteryState { Unknown, Info(BatteryInfo), } /// If no battery monitor is installed this implementation will be used pub struct NoBatteryMonitor {} impl BatteryInteraction for NoBatteryMonitor { fn state_charge_percent(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn remaining_milli_ampere_hour(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn max_milli_ampere_hour(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn design_milli_ampere_hour(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn voltage_milli_volt(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn average_current_milli_ampere(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn cycle_count(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn state_health_percent(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn bat_temperature(&mut self) -> Result { Err(BatteryError::NoBatteryMonitor) } fn get_battery_state(&mut self) -> Result { Ok(BatteryState::Unknown) } } //TODO implement this battery monitor kind once controller is complete #[allow(dead_code)] pub struct WchI2cSlave {} pub struct BQ34Z100G1<'a> { pub battery_driver: Bq34z100g1Driver>, Delay>, } impl BatteryInteraction for BQ34Z100G1<'_> { fn state_charge_percent(&mut self) -> Result { Ok(self.battery_driver.state_of_charge().map(f32::from)?) } fn remaining_milli_ampere_hour(&mut self) -> Result { Ok(self.battery_driver.remaining_capacity()?) } fn max_milli_ampere_hour(&mut self) -> Result { Ok(self.battery_driver.full_charge_capacity()?) } fn design_milli_ampere_hour(&mut self) -> Result { Ok(self.battery_driver.design_capacity()?) } fn voltage_milli_volt(&mut self) -> Result { Ok(self.battery_driver.voltage()?) } fn average_current_milli_ampere(&mut self) -> Result { Ok(self.battery_driver.average_current()?) } fn cycle_count(&mut self) -> Result { Ok(self.battery_driver.cycle_count()?) } fn state_health_percent(&mut self) -> Result { Ok(self.battery_driver.state_of_health()?) } fn bat_temperature(&mut self) -> Result { Ok(self.battery_driver.temperature()?) } fn get_battery_state(&mut self) -> Result { Ok(BatteryState::Info(BatteryInfo { voltage_milli_volt: self.voltage_milli_volt()?, average_current_milli_ampere: self.average_current_milli_ampere()?, cycle_count: self.cycle_count()?, design_milli_ampere_hour: self.design_milli_ampere_hour()?, remaining_milli_ampere_hour: self.remaining_milli_ampere_hour()?, state_of_charge: self.state_charge_percent()?, state_of_health: self.state_health_percent()?, temperature: self.bat_temperature()?, })) } } pub fn print_battery_bq34z100( battery_driver: &mut Bq34z100g1Driver>, Delay>, ) -> anyhow::Result<(), Bq34Z100Error> { println!("Try communicating with battery"); let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { println!("Firmware {:?}", e); 0 }); println!("fw version is {}", fwversion); let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| { println!("Design capacity {:?}", e); 0 }); println!("Design Capacity {}", design_capacity); if design_capacity == 1000 { println!("Still stock configuring battery, readouts are likely to be wrong!"); } let flags = battery_driver.get_flags_decoded()?; println!("Flags {:?}", flags); let chem_id = battery_driver.chem_id().unwrap_or_else(|e| { println!("Chemid {:?}", e); 0 }); let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| { println!("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| { println!("Bat volt {:?}", e); 0 }); let current = battery_driver.current().unwrap_or_else(|e| { println!("Bat current {:?}", e); 0 }); let state = battery_driver.state_of_charge().unwrap_or_else(|e| { println!("Bat Soc {:?}", e); 0 }); let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| { println!("Bat Charge Volt {:?}", e); 0 }); let charge_current = battery_driver.charge_current().unwrap_or_else(|e| { println!("Bat Charge Current {:?}", e); 0 }); println!("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(); anyhow::Result::Ok(()) }