diff --git a/Cargo.lock b/Cargo.lock index 2db1753..7b380ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -535,6 +535,9 @@ dependencies = [ [[package]] name = "lib-bms-protocol" version = "0.1.0" +dependencies = [ + "embedded-hal 1.0.0", +] [[package]] name = "litrs" diff --git a/lib-bms-protocol/Cargo.toml b/lib-bms-protocol/Cargo.toml index 62ddd04..2964329 100644 --- a/lib-bms-protocol/Cargo.toml +++ b/lib-bms-protocol/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +embedded-hal = "1.0.0" [profile.dev] diff --git a/lib-bms-protocol/src/lib.rs b/lib-bms-protocol/src/lib.rs index 842be77..0b61641 100644 --- a/lib-bms-protocol/src/lib.rs +++ b/lib-bms-protocol/src/lib.rs @@ -1,29 +1,190 @@ #![no_std] +use embedded_hal::i2c::{I2c, Operation, SevenBitAddress}; + +pub const BMS_I2C_ADDRESS: u8 = 0x55; + +pub trait BmsReadable { + fn read_from_i2c(i2c_dev: &mut I) -> Result + where + Self: Sized, + I: I2c; +} + +pub trait BmsWriteable { + fn write_to_device(&self, i2c_dev: &mut I) -> Result<(), BmsProtocolError> + where + I: I2c; +} + +pub enum BmsProtocolError { + I2cCommunicationError, +} + +impl From for BmsProtocolError +where + E: embedded_hal::i2c::Error, +{ + fn from(_value: E) -> Self { + Self::I2cCommunicationError + } +} + +#[derive(Debug)] +pub struct BmsSoftwareReset; + +impl BmsWriteable for BmsSoftwareReset { + fn write_to_device(&self, i2c_dev: &mut I) -> Result<(), BmsProtocolError> + where + I: I2c, + { + i2c_dev.write( + BMS_I2C_ADDRESS, + &(BmsRegisterMap::Reset as u32).to_be_bytes(), + )?; + Ok(()) + } +} #[derive(Debug)] #[repr(transparent)] -pub struct Version(u32); +pub struct ProtocolVersion(u32); +impl BmsReadable for ProtocolVersion { + fn read_from_i2c(i2c_dev: &mut I) -> Result + where + Self: Sized, + I: I2c, + { + let cmd = BmsRegisterMap::ProtocolVersion; + let mut version: [u8; 4] = [0x00; 4]; + i2c_dev.transaction( + BMS_I2C_ADDRESS, + &mut [ + Operation::Write(&(cmd as u32).to_be_bytes()), + Operation::Read(&mut version), + ], + )?; + Ok(ProtocolVersion(u32::from_be_bytes(version))) + } +} + +#[derive(Debug)] +#[repr(transparent)] +pub struct FirmwareVersion(u32); + +impl BmsReadable for FirmwareVersion { + fn read_from_i2c(i2c_dev: &mut I) -> Result + where + Self: Sized, + I: I2c, + { + let cmd = BmsRegisterMap::FirmwareVersion; + let mut version: [u8; 4] = [0x00; 4]; + i2c_dev.transaction( + BMS_I2C_ADDRESS, + &mut [ + Operation::Write(&(cmd as u32).to_be_bytes()), + Operation::Read(&mut version), + ], + )?; + Ok(FirmwareVersion(u32::from_be_bytes(version))) + } +} #[derive(Debug)] pub struct Config { pub capacity_mah: u32, - pub v_full_mv: u32, + pub v_full_mv: u32, pub v_empty_mv: u32, } +impl BmsReadable for Config { + fn read_from_i2c(i2c_dev: &mut I) -> Result + where + Self: Sized, + I: I2c, + { + let cmd = BmsRegisterMap::ConfigCapacityMilliAmpereHours; + let mut capacity_mah: [u8; 4] = [0x00; 4]; + let mut v_full_mv: [u8; 4] = [0x00; 4]; + let mut v_empty_mv: [u8; 4] = [0x00; 4]; + i2c_dev.transaction( + BMS_I2C_ADDRESS, + &mut [ + Operation::Write(&(cmd as u32).to_be_bytes()), + Operation::Read(&mut capacity_mah), + Operation::Read(&mut v_full_mv), + Operation::Read(&mut v_empty_mv), + ], + )?; + Ok(Config { + capacity_mah: u32::from_be_bytes(capacity_mah), + v_full_mv: u32::from_be_bytes(v_full_mv), + v_empty_mv: u32::from_be_bytes(v_empty_mv), + }) + } +} + #[derive(Debug, Clone, Copy)] pub struct BatteryState { pub lifetime_capacity_mah: u32, pub remaining_capacity_mah: u32, - pub current_mv: u16, + pub current_mv: u32, pub temperature_celcius: i32, - pub health_percent: u8 + pub health_percent: u32, } -pub enum BmsCommand { - GetVersion, - GetConfig, - GetBatteryState, +impl BmsReadable for BatteryState { + fn read_from_i2c(i2c_dev: &mut I) -> Result + where + Self: Sized, + I: I2c, + { + let cmd = BmsRegisterMap::BatteryLifetimeCapacityMilliAmpereHours; + let mut lifetime_capacity_mah: [u8; 4] = [0x00; 4]; + let mut remaining_capacity_mah: [u8; 4] = [0x00; 4]; + let mut current_mv: [u8; 4] = [0x00; 4]; + let mut temperature_celcius: [u8; 4] = [0x00; 4]; + let mut health_percent: [u8; 4] = [0x00; 4]; + i2c_dev.transaction( + BMS_I2C_ADDRESS, + &mut [ + Operation::Write(&(cmd as u32).to_be_bytes()), + Operation::Read(&mut lifetime_capacity_mah), + Operation::Read(&mut remaining_capacity_mah), + Operation::Read(&mut current_mv), + Operation::Read(&mut temperature_celcius), + Operation::Read(&mut health_percent), + ], + )?; + Ok(BatteryState { + lifetime_capacity_mah: u32::from_be_bytes(lifetime_capacity_mah), + remaining_capacity_mah: u32::from_be_bytes(remaining_capacity_mah), + current_mv: u32::from_be_bytes(current_mv), + temperature_celcius: i32::from_be_bytes(temperature_celcius), + health_percent: u32::from_be_bytes(health_percent), + }) + } +} + +#[repr(u32)] +#[non_exhaustive] +pub enum BmsRegisterMap { + // software reset register + Reset = 0x00000000, + // bms protocol revesion as u32 value + ProtocolVersion = 0x00000001, + // bms firmware revesion as u32 value + FirmwareVersion = 0x00000002, + // Configuration Values Start Here + ConfigCapacityMilliAmpereHours = 0x00010000, + ConfigFullMilliVolt = 0x00010004, + ConfigEmptyMilliVolt = 0x00010008, + // Battery State Values Start Here + BatteryLifetimeCapacityMilliAmpereHours = 0x00020000, + BatteryRemainingCapacityMilliAmpereHours = 0x00020004, + BatteryCurrentVoltageMilliVolt = 0x00020008, + BatteryTemperatureCelcius = 0x0002000c, + BatteryHealthPercent = 0x00020010, }