PlantCtrl/rust/src/hal/battery.rs
2025-06-19 13:05:47 +02:00

275 lines
9.7 KiB
Rust

use anyhow::bail;
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;
use std::result::Result::Ok as OkStd;
use crate::to_string;
pub trait BatteryInteraction {
fn state_charge_percent(&mut self) -> anyhow::Result<u8>;
fn remaining_milli_ampere_hour(&mut self) -> anyhow::Result<u16>;
fn max_milli_ampere_hour(&mut self) -> anyhow::Result<u16>;
fn design_milli_ampere_hour(&mut self) -> anyhow::Result<u16>;
fn voltage_milli_volt(&mut self) -> anyhow::Result<u16>;
fn average_current_milli_ampere(&mut self) -> anyhow::Result<i16>;
fn cycle_count(&mut self) -> anyhow::Result<u16>;
fn state_health_percent(&mut self) -> anyhow::Result<u8>;
fn bat_temperature(&mut self) -> anyhow::Result<u16>;
fn get_battery_state(&mut self) -> String;
}
#[derive(Serialize)]
pub struct BatteryState {
voltage_milli_volt: String,
current_milli_ampere: String,
cycle_count: String,
design_milli_ampere: String,
remaining_milli_ampere: String,
state_of_charge: String,
state_of_health: String,
temperature: String,
}
pub enum BatteryMonitor<'a> {
Disabled {
},
BQ34Z100G1 {
battery_driver: Bq34z100g1Driver<MutexDevice<'a, I2cDriver<'a>>, Delay>
},
WchI2cSlave {
}
}
impl BatteryInteraction for BatteryMonitor<'_> {
fn state_charge_percent(&mut self) -> anyhow::Result<u8> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.state_of_charge() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading SoC {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
}
BatteryMonitor::Disabled {} => {
bail!("Battery monitor is disabled")
}
}
}
fn remaining_milli_ampere_hour(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.remaining_capacity(){
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading remaining_milli_ampere_hour {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn max_milli_ampere_hour(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.full_charge_capacity() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading max_milli_ampere_hour {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn design_milli_ampere_hour(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.design_capacity() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading design_milli_ampere_hour {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn voltage_milli_volt(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.voltage() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading voltage_milli_volt {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn average_current_milli_ampere(&mut self) -> anyhow::Result<i16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.average_current() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading average_current_milli_ampere {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn cycle_count(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.cycle_count() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading cycle_count {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn state_health_percent(&mut self) -> anyhow::Result<u8> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.state_of_health() {
OkStd(r) => anyhow::Ok(r as u8),
Err(err) => bail!("Error reading state_health_percent {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn bat_temperature(&mut self) -> anyhow::Result<u16> {
match self {
BatteryMonitor::BQ34Z100G1 { battery_driver} => {
match battery_driver.temperature() {
OkStd(r) => anyhow::Ok(r),
Err(err) => bail!("Error reading bat_temperature {:?}", err),
}
},
BatteryMonitor::WchI2cSlave { .. } => {
bail!("Not implemented")
},
&mut BatteryMonitor::Disabled { } => {
bail!("Battery monitor is disabled")
}
}
}
fn get_battery_state(&mut self) -> String {
let bat = BatteryState {
voltage_milli_volt: to_string(self.voltage_milli_volt()),
current_milli_ampere: to_string(self.average_current_milli_ampere()),
cycle_count: to_string(self.cycle_count()),
design_milli_ampere: to_string(self.design_milli_ampere_hour()),
remaining_milli_ampere: to_string(self.remaining_milli_ampere_hour()),
state_of_charge: to_string(self.state_charge_percent()),
state_of_health: to_string(self.state_health_percent()),
temperature: to_string(self.bat_temperature()),
};
match serde_json::to_string(&bat) {
Ok(state) => {
state
}
Err(err) => {
format!("{:?}", err).to_owned()
}
}
}
}
pub fn print_battery_bq34z100(
battery_driver: &mut Bq34z100g1Driver<MutexDevice<I2cDriver<'_>>, Delay>,
) -> anyhow::Result<(), Bq34Z100Error<I2cError>> {
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(())
}