use embedded_hal::i2c::I2c;
pub const DEFAULT_I2C_ADDRESS: u8 = 0x0b;
pub mod registers {
#![allow(dead_code)]
pub const TIME_TO_EMPTY: u8 = 0x03;
pub const BEFORE_RSOC: u8 = 0x04;
pub const TIME_TO_FULL: u8 = 0x05;
pub const TSENSE1_THERMISTOR_B: u8 = 0x06;
pub const INIT_RSOC: u8 = 0x07;
pub const CELL_TEMPERATURE: u8 = 0x08;
pub const CELL_VOLTAGE: u8 = 0x09;
pub const CURRENT_DIRECTION: u8 = 0x0a;
pub const APA: u8 = 0x0b;
pub const APT: u8 = 0x0c;
pub const RSOC: u8 = 0x0d;
pub const TSENSE2_THERMISTOR_B: u8 = 0x0e;
pub const ITE: u8 = 0x0f;
pub const IC_VERSION: u8 = 0x11;
pub const BATTERY_PROFILE: u8 = 0x12;
pub const ALARM_LOW_RSOC: u8 = 0x13;
pub const ALARM_LOW_CELL_VOLTAGE: u8 = 0x14;
pub const POWER_MODE: u8 = 0x15;
pub const STATUS_BIT: u8 = 0x16;
pub const CYCLE_COUNT: u8 = 0x17;
pub const BATTERY_STATUS: u8 = 0x19;
pub const BATTERY_PROFILE_CODE: u8 = 0x1a;
pub const TERMINATION_CURRENT_RATE: u8 = 0x1c;
pub const EMPTY_CELL_VOLTAGE: u8 = 0x1d;
pub const ITE_OFFSET: u8 = 0x1e;
pub const ALARM_HIGH_CELL_VOLTAGE: u8 = 0x1f;
pub const ALARM_LOW_TEMPERATURE: u8 = 0x20;
pub const ALARM_HIGH_TEMPERATURE: u8 = 0x21;
pub const TOTAL_RUNTIME_LOW: u8 = 0x24;
pub const TOTAL_RUNTIME_HIGH: u8 = 0x25;
pub const ACCUMULATED_TEMPERATURE_LOW: u8 = 0x26;
pub const ACCUMULATED_TEMPERATURE_HIGH: u8 = 0x27;
pub const ACCUMULATED_RSOC_LOW: u8 = 0x28;
pub const ACCUMULATED_RSOC_HIGH: u8 = 0x29;
pub const MAXIMUM_CELL_VOLTAGE: u8 = 0x2a;
pub const MINIMUM_CELL_VOLTAGE: u8 = 0x2b;
pub const MAXIMUM_CELL_TEMPERATURE: u8 = 0x2c;
pub const MINIMUM_CELL_TEMPERATURE: u8 = 0x2d;
pub const AMBIENT_TEMPERATURE: u8 = 0x30;
pub const BATTERY_HEALTH: u8 = 0x32;
pub const USER_ID_LOW: u8 = 0x36;
pub const USER_ID_HIGH: u8 = 0x37;
}
pub mod constants {
#![allow(dead_code)]
pub const BEFORE_RSOC_1ST_SAMPLE: u16 = 0xaa55;
pub const BEFORE_RSOC_2ND_SAMPLE: u16 = 0xaa56;
pub const BEFORE_RSOC_3RD_SAMPLE: u16 = 0xaa57;
pub const BEFORE_RSOC_4TH_SAMPLE: u16 = 0xaa58;
pub const INIT_RSOC: u16 = 0xaa55;
pub const BATTERY_TYPE01: u16 = 0x00;
pub const BATTERY_TYPE04: u16 = 0x01;
pub const BATTERY_TYPE05: u16 = 0x02;
pub const BATTERY_TYPE06: u16 = 0x03;
pub const BATTERY_TYPE07: u16 = 0x04;
pub const IC_POWER_MODE_OPERATION: u16 = 0x01;
pub const IC_POWER_MODE_SLEEP: u16 = 0x02;
pub const BATTERY_STATUS_HIGH_CELL_VOLTAGE: u16 = 1 << 15;
pub const BATTERY_STATUS_HIGH_TEMPERATURE: u16 = 1 << 12;
pub const BATTERY_STATUS_LOW_CELL_VOLTAGE: u16 = 1 << 11;
pub const BATTERY_STATUS_LOW_RSOC: u16 = 1 << 9;
pub const BATTERY_STATUS_LOW_TEMPERATURE: u16 = 1 << 8;
pub const BATTERY_STATUS_INITIALIZED: u16 = 1 << 7;
pub const BATTERY_STATUS_DISCHARGING: u16 = 1 << 6;
pub const STATUS_BIT_TSENSE1: u16 = 1 << 0;
pub const STATUS_BIT_TSENSE2: u16 = 1 << 1;
}
pub struct Device<'a, I2C> {
address: u8,
i2c: &'a mut I2C,
}
#[derive(Debug)]
pub enum ChipError<E> {
I2CError(E),
CrcMismatch(u16),
}
impl<E> From<E> for ChipError<E> {
fn from(e: E) -> Self {
Self::I2CError(e)
}
}
impl<'a, I2C, E> Device<'a, I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(address: u8, i2c: &'a mut I2C) -> Self {
Self { address, i2c }
}
fn compute_crc_8(bytes: &[u8]) -> u8 {
let mut crc = 0;
for byte in bytes {
let byte = *byte;
crc ^= byte;
for _ in 0..8 {
if (crc & 0x80) == 0 {
crc = crc << 1;
} else {
crc = (crc << 1) ^ 0x07;
}
}
}
crc
}
pub fn read_register(&mut self, register: u8) -> Result<u16, ChipError<E>> {
let mut data: [u8; 6] = [
self.address << 1,
register,
(self.address << 1) | 0x01,
0,
0,
0,
];
self.i2c
.write_read(self.address, &[register], &mut data[3..])?;
#[cfg(feature = "log")]
log::trace!("Read bytes: {:2x?}", &data[..]);
let value = (data[4] as u16) << 8 | (data[3] as u16);
let crc = Self::compute_crc_8(&data[0..5]);
if crc != data[5] {
#[cfg(feature = "log")]
log::warn!(
"CRC didn't match! Got {:2x} but expected {:2x}",
data[5],
crc
);
return Err(ChipError::CrcMismatch(value));
}
Ok(value)
}
pub fn write_register(&mut self, register: u8, value: u16) -> Result<(), ChipError<E>> {
let mut data: [u8; 5] = [
self.address << 1,
register,
(value & 0xff) as u8,
((value & 0xff00) >> 8) as u8,
0,
];
data[4] = Self::compute_crc_8(&data[0..4]);
#[cfg(feature = "log")]
log::trace!("Writing data: {:2x?}", data);
self.i2c.write(self.address, &data[1..])?;
Ok(())
}
pub fn time_to_empty(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::TIME_TO_EMPTY)
}
pub fn time_to_full(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::TIME_TO_FULL)
}
pub fn write_before_rsoc(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::BEFORE_RSOC, value)
}
pub fn write_init_rsoc(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::INIT_RSOC, value)
}
pub fn write_battery_type(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::BATTERY_PROFILE, value)
}
pub fn write_apa(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::APA, value)
}
pub fn write_power_mode(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::POWER_MODE, value)
}
pub fn battery_status(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::BATTERY_STATUS)
}
pub fn write_battery_status(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::BATTERY_STATUS, value)
}
pub fn rsoc(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::RSOC)
}
pub fn battery_health(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::BATTERY_HEALTH)
}
pub fn cell_voltage(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::CELL_VOLTAGE)
}
pub fn ic_version(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::IC_VERSION)
}
pub fn ite(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::ITE)
}
pub fn cycle_count(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::CYCLE_COUNT)
}
pub fn status_bit(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::STATUS_BIT)
}
pub fn write_status_bit(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::STATUS_BIT, value)
}
pub fn tsense1_thermistor_b(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::TSENSE1_THERMISTOR_B)
}
pub fn write_tsense1_thermistor_b(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::TSENSE1_THERMISTOR_B, value)
}
pub fn cell_temperature(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::CELL_TEMPERATURE)
}
pub fn write_cell_temperature(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::CELL_TEMPERATURE, value)
}
pub fn tsense2_thermistor_b(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::TSENSE2_THERMISTOR_B)
}
pub fn write_tsense2_thermistor_b(&mut self, value: u16) -> Result<(), ChipError<E>> {
self.write_register(registers::TSENSE2_THERMISTOR_B, value)
}
pub fn ambient_temperature(&mut self) -> Result<u16, ChipError<E>> {
self.read_register(registers::AMBIENT_TEMPERATURE)
}
}