use crate::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
mod util {
use serde::{ser::SerializeSeq, Serializer};
pub fn f32_1_digits<S>(x: &f32, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_f64((*x as f64 * 10.0).round() / 10.0)
}
pub fn f32_3_digits<S>(x: &f32, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_f64((*x as f64 * 1000.0).round() / 1000.0)
}
pub fn vec_f32_3_digits<S>(vec: &Vec<f32>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = s.serialize_seq(Some(vec.len()))?;
for e in vec {
let val = (*e as f64 * 1000.0).round() / 1000.0;
seq.serialize_element(&val)?;
}
seq.end()
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum Address {
Host = 0x40,
}
pub const MINIMUM_DELAY: std::time::Duration = std::time::Duration::from_millis(4);
const TX_BUFFER_LENGTH: usize = 13;
const RX_BUFFER_LENGTH: usize = 13;
const START_BYTE: u8 = 0xa5;
const DATA_LENGTH: u8 = 0x08;
fn create_request_header(address: Address, command: u8) -> Vec<u8> {
let mut tx_buffer = vec![0; TX_BUFFER_LENGTH];
tx_buffer[0] = START_BYTE;
tx_buffer[1] = address as u8;
tx_buffer[2] = command;
tx_buffer[3] = DATA_LENGTH;
tx_buffer
}
fn calc_crc(buffer: &[u8]) -> u8 {
let mut checksum: u8 = 0;
let slice = &buffer[0..buffer.len() - 1];
for b in slice {
checksum = checksum.wrapping_add(*b);
}
checksum
}
fn calc_crc_and_set(buffer: &mut [u8]) {
let len = buffer.len();
buffer[len - 1] = calc_crc(buffer)
}
macro_rules! read_bit {
($byte:expr,$position:expr) => {
($byte >> $position) & 1 != 0
};
}
fn validate_len(buffer: &[u8], expected_size: usize) -> std::result::Result<(), Error> {
if buffer.len() < expected_size {
log::warn!(
"Invalid buffer size - required={} received={}",
expected_size,
buffer.len()
);
return Err(Error::ReplySizeError);
}
Ok(())
}
fn validate_checksum(buffer: &[u8]) -> std::result::Result<(), Error> {
let checksum = calc_crc(buffer);
if buffer[buffer.len() - 1] != checksum {
log::warn!(
"Invalid checksum - calculated={:02X?} received={:02X?} buffer={:?}",
checksum,
buffer[buffer.len() - 1],
buffer
);
return Err(Error::CheckSumError);
}
Ok(())
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Soc {
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
pub total_voltage: f32,
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
pub current: f32,
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_1_digits"))]
pub soc_percent: f32,
}
impl Soc {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x90);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
Ok(Self {
total_voltage: u16::from_be_bytes([rx_buffer[4], rx_buffer[5]]) as f32 / 10.0,
current: (((u16::from_be_bytes([rx_buffer[8], rx_buffer[9]]) as i32) - 30000) as f32)
/ 10.0,
soc_percent: u16::from_be_bytes([rx_buffer[10], rx_buffer[11]]) as f32 / 10.0,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CellVoltageRange {
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
pub highest_voltage: f32,
pub highest_cell: u8,
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
pub lowest_voltage: f32,
pub lowest_cell: u8,
}
impl CellVoltageRange {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x91);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
Ok(Self {
highest_voltage: u16::from_be_bytes([rx_buffer[4], rx_buffer[5]]) as f32 / 1000.0,
highest_cell: rx_buffer[6],
lowest_voltage: u16::from_be_bytes([rx_buffer[7], rx_buffer[8]]) as f32 / 1000.0,
lowest_cell: rx_buffer[9],
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TemperatureRange {
pub highest_temperature: i8,
pub highest_sensor: u8,
pub lowest_temperature: i8,
pub lowest_sensor: u8,
}
impl TemperatureRange {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x92);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
Ok(Self {
highest_temperature: ((rx_buffer[4] as i16) - 40) as i8,
highest_sensor: rx_buffer[5],
lowest_temperature: ((rx_buffer[6] as i16) - 40) as i8,
lowest_sensor: rx_buffer[7],
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MosfetMode {
Stationary,
Charging,
Discharging,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MosfetStatus {
pub mode: MosfetMode,
pub charging_mosfet: bool,
pub discharging_mosfet: bool,
pub bms_cycles: u8,
#[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
pub capacity_ah: f32,
}
impl MosfetStatus {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x93);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
let mode = match rx_buffer[4] {
0 => MosfetMode::Stationary,
1 => MosfetMode::Charging,
2 => MosfetMode::Discharging,
_ => unreachable!(),
};
Ok(Self {
mode,
charging_mosfet: rx_buffer[5] != 0,
discharging_mosfet: rx_buffer[6] != 0,
bms_cycles: rx_buffer[7],
capacity_ah: u32::from_be_bytes([
rx_buffer[8],
rx_buffer[9],
rx_buffer[10],
rx_buffer[11],
]) as f32
/ 1000.0,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct IOState {
pub di1: bool,
pub di2: bool,
pub di3: bool,
pub di4: bool,
pub do1: bool,
pub do2: bool,
pub do3: bool,
pub do4: bool,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Status {
pub cells: u8,
pub temperature_sensors: u8,
pub charger_running: bool,
pub load_running: bool,
pub states: IOState,
pub cycles: u16,
}
impl Status {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x94);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
Ok(Self {
cells: rx_buffer[4],
temperature_sensors: rx_buffer[5],
charger_running: rx_buffer[6] != 0,
load_running: rx_buffer[7] != 0,
states: IOState {
di1: read_bit!(rx_buffer[8], 0),
di2: read_bit!(rx_buffer[8], 1),
di3: read_bit!(rx_buffer[8], 2),
di4: read_bit!(rx_buffer[8], 3),
do1: read_bit!(rx_buffer[8], 4),
do2: read_bit!(rx_buffer[8], 5),
do3: read_bit!(rx_buffer[8], 6),
do4: read_bit!(rx_buffer[8], 7),
},
cycles: u16::from_be_bytes([rx_buffer[9], rx_buffer[10]]),
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CellVoltages(
#[cfg_attr(feature = "serde", serde(serialize_with = "util::vec_f32_3_digits"))] Vec<f32>,
);
impl CellVoltages {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x95);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
fn n_frames(n_cells: u8) -> usize {
(n_cells as f32 / 3.0).ceil() as usize
}
pub fn reply_size(n_cells: u8) -> usize {
Self::n_frames(n_cells) * RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8], n_cells: u8) -> std::result::Result<Self, Error> {
validate_len(rx_buffer, Self::reply_size(n_cells))?;
let mut voltages = Vec::with_capacity(n_cells as usize);
let mut n_cell = 1;
for n_frame in 1..=Self::n_frames(n_cells) {
let part =
&rx_buffer[((n_frame - 1) * RX_BUFFER_LENGTH)..((n_frame) * RX_BUFFER_LENGTH)];
if n_frame != usize::from(part[4]) {
log::warn!(
"Frame out of order - expected={} received={}",
n_frame,
part[4]
);
return Err(Error::FrameNoError);
}
validate_checksum(part)?;
for i in 0..3 {
let volt = u16::from_be_bytes([part[5 + i + i], part[6 + i + i]]) as f32 / 1000.0;
log::trace!("Frame #{n_frame} cell #{n_cell} volt={volt}");
voltages.push(volt);
n_cell += 1;
if n_cell > n_cells {
break;
}
}
}
Ok(Self(voltages))
}
}
impl std::ops::Deref for CellVoltages {
type Target = [f32];
fn deref(&self) -> &[f32] {
&self.0
}
}
pub struct CellTemperatures;
impl CellTemperatures {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x96);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
fn n_frames(n_sensors: u8) -> usize {
(n_sensors as f32 / 7.0).ceil() as usize
}
pub fn reply_size(n_sensors: u8) -> usize {
Self::n_frames(n_sensors) * RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8], n_sensors: u8) -> std::result::Result<Vec<i32>, Error> {
validate_len(rx_buffer, Self::reply_size(n_sensors))?;
let mut result = Vec::with_capacity(n_sensors as usize);
let mut n_sensor = 1;
for n_frame in 1..=Self::n_frames(n_sensors) {
let part =
&rx_buffer[((n_frame - 1) * RX_BUFFER_LENGTH)..((n_frame) * RX_BUFFER_LENGTH)];
if n_frame != usize::from(part[4]) {
log::warn!(
"Frame out of order - expected={} received={}",
n_frame,
part[4]
);
return Err(Error::FrameNoError);
}
validate_checksum(part)?;
for i in 0..7 {
let temperature = part[5 + i] as i32 - 40;
log::trace!("Frame #{n_frame} sensor #{n_sensor} °C={temperature}");
result.push(temperature);
n_sensor += 1;
if n_sensor > n_sensors {
break;
}
}
}
Ok(result)
}
}
pub struct CellBalanceState;
impl CellBalanceState {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x97);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8], n_cells: u8) -> std::result::Result<Vec<bool>, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
let mut result = Vec::with_capacity(n_cells as usize);
let mut n_cell = 0;
for i in 0..6 {
for j in 0..8 {
result.push(read_bit!(rx_buffer[4 + i], j));
n_cell += 1;
if n_cell >= n_cells {
return Ok(result);
}
}
}
Ok(result)
}
}
#[derive(Debug, Clone, thiserror::Error, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ErrorCode {
#[error("Cell voltage is too high (Level 1)")]
CellVoltHighLevel1,
#[error("Cell voltage is too high (Level 2)")]
CellVoltHighLevel2,
#[error("Cell voltage is too low (Level 1)")]
CellVoltLowLevel1,
#[error("Cell voltage is too low (Level 2)")]
CellVoltLowLevel2,
#[error("Total voltage is too high (Level 1)")]
SumVoltHighLevel1,
#[error("Total voltage is too high (Level 2)")]
SumVoltHighLevel2,
#[error("Total voltage is too low (Level 1)")]
SumVoltLowLevel1,
#[error("Total voltage is too low (Level 2)")]
SumVoltLowLevel2,
#[error("Charging temperature too high (Level 1)")]
ChargeTempHighLevel1,
#[error("Charging temperature too high (Level 2)")]
ChargeTempHighLevel2,
#[error("Charging temperature too low (Level 1)")]
ChargeTempLowLevel1,
#[error("Charging temperature too low (Level 2)")]
ChargeTempLowLevel2,
#[error("Discharging temperature too high (Level 1)")]
DischargeTempHighLevel1,
#[error("Discharging temperature too high (Level 2)")]
DischargeTempHighLevel2,
#[error("Discharging temperature too low (Level 1)")]
DischargeTempLowLevel1,
#[error("Discharging temperature too low (Level 2)")]
DischargeTempLowLevel2,
#[error("Charge overcurrent (Level 1)")]
ChargeOvercurrentLevel1,
#[error("Charge overcurrent (Level 2)")]
ChargeOvercurrentLevel2,
#[error("Discharge overcurrent (Level 1)")]
DischargeOvercurrentLevel1,
#[error("Discharge overcurrent (Level 2)")]
DischargeOvercurrentLevel2,
#[error("SOC too high (Level 1)")]
SocHighLevel1,
#[error("SOC too high (Level 2)")]
SocHighLevel2,
#[error("SOC too low (Level 1)")]
SocLowLevel1,
#[error("SOC too low (Level 2)")]
SocLowLevel2,
#[error("Excessive voltage difference between cells (Level 1)")]
DiffVoltLevel1,
#[error("Excessive voltage difference between cells (Level 2)")]
DiffVoltLevel2,
#[error("Excessive temperature difference between sensors (Level 1)")]
DiffTempLevel1,
#[error("Excessive temperature difference between sensors (Level 2)")]
DiffTempLevel2,
#[error("Charging MOSFET temperature too high")]
ChargeMosTempHighAlarm,
#[error("Discharging MOSFET temperature too high")]
DischargeMosTempHighAlarm,
#[error("Charging MOSFET temperature sensor failure")]
ChargeMosTempSensorErr,
#[error("Discharging MOSFET temperature sensor failure")]
DischargeMosTempSensorErr,
#[error("Charging MOSFET adhesion failure")]
ChargeMosAdhesionErr,
#[error("Discharging MOSFET adhesion failure")]
DischargeMosAdhesionErr,
#[error("Charging MOSFET open circuit failure")]
ChargeMosOpenCircuitErr,
#[error("Discharging MOSFET open circuit failure")]
DischargeMosOpenCircuitErr,
#[error("AFE acquisition chip failure")]
AfeCollectChipErr,
#[error("Cell voltage collection circuit failure")]
VoltageCollectDropped,
#[error("Cell temperature sensor failure")]
CellTempSensorErr,
#[error("EEPROM storage failure")]
EepromErr,
#[error("RTC clock failure")]
RtcErr,
#[error("Pre-charge failure")]
PrechangeFailure,
#[error("Communication failure")]
CommunicationFailure,
#[error("Internal communication failure")]
InternalCommunicationFailure,
#[error("Current detection module failure")]
CurrentModuleFault,
#[error("Total voltage detection module failure")]
SumVoltageDetectFault,
#[error("Short circuit protection failure")]
ShortCircuitProtectFault,
#[error("Low voltage forbids charging")]
LowVoltForbiddenChargeFault,
}
impl ErrorCode {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x98);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Vec<Self>, Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)?;
let mut result = Vec::new();
macro_rules! ck_and_add {
($byte:expr,$position:expr,$enum_type:expr) => {
if read_bit!(rx_buffer[$byte], $position) {
result.push($enum_type);
}
};
}
ck_and_add!(4, 0, ErrorCode::CellVoltHighLevel1);
ck_and_add!(4, 1, ErrorCode::CellVoltHighLevel2);
ck_and_add!(4, 2, ErrorCode::CellVoltLowLevel1);
ck_and_add!(4, 3, ErrorCode::CellVoltLowLevel2);
ck_and_add!(4, 4, ErrorCode::SumVoltHighLevel1);
ck_and_add!(4, 5, ErrorCode::SumVoltHighLevel2);
ck_and_add!(4, 6, ErrorCode::SumVoltLowLevel1);
ck_and_add!(4, 7, ErrorCode::SumVoltLowLevel2);
ck_and_add!(5, 0, ErrorCode::ChargeTempHighLevel1);
ck_and_add!(5, 1, ErrorCode::ChargeTempHighLevel2);
ck_and_add!(5, 2, ErrorCode::ChargeTempLowLevel1);
ck_and_add!(5, 3, ErrorCode::ChargeTempLowLevel2);
ck_and_add!(5, 4, ErrorCode::DischargeTempHighLevel1);
ck_and_add!(5, 5, ErrorCode::DischargeTempHighLevel2);
ck_and_add!(5, 6, ErrorCode::DischargeTempLowLevel1);
ck_and_add!(5, 7, ErrorCode::DischargeTempLowLevel2);
ck_and_add!(6, 1, ErrorCode::ChargeOvercurrentLevel2);
ck_and_add!(6, 0, ErrorCode::ChargeOvercurrentLevel1);
ck_and_add!(6, 2, ErrorCode::DischargeOvercurrentLevel1);
ck_and_add!(6, 3, ErrorCode::DischargeOvercurrentLevel2);
ck_and_add!(6, 4, ErrorCode::SocHighLevel1);
ck_and_add!(6, 5, ErrorCode::SocHighLevel2);
ck_and_add!(6, 6, ErrorCode::SocLowLevel1);
ck_and_add!(6, 7, ErrorCode::SocLowLevel2);
ck_and_add!(7, 0, ErrorCode::DiffVoltLevel1);
ck_and_add!(7, 1, ErrorCode::DiffVoltLevel2);
ck_and_add!(7, 2, ErrorCode::DiffTempLevel1);
ck_and_add!(7, 3, ErrorCode::DiffTempLevel2);
ck_and_add!(8, 0, ErrorCode::ChargeMosTempHighAlarm);
ck_and_add!(8, 1, ErrorCode::DischargeMosTempHighAlarm);
ck_and_add!(8, 2, ErrorCode::ChargeMosTempSensorErr);
ck_and_add!(8, 3, ErrorCode::DischargeMosTempSensorErr);
ck_and_add!(8, 4, ErrorCode::ChargeMosAdhesionErr);
ck_and_add!(8, 5, ErrorCode::DischargeMosAdhesionErr);
ck_and_add!(8, 6, ErrorCode::ChargeMosOpenCircuitErr);
ck_and_add!(8, 7, ErrorCode::DischargeMosOpenCircuitErr);
ck_and_add!(9, 0, ErrorCode::AfeCollectChipErr);
ck_and_add!(9, 1, ErrorCode::VoltageCollectDropped);
ck_and_add!(9, 2, ErrorCode::CellTempSensorErr);
ck_and_add!(9, 3, ErrorCode::EepromErr);
ck_and_add!(9, 4, ErrorCode::RtcErr);
ck_and_add!(9, 5, ErrorCode::PrechangeFailure);
ck_and_add!(9, 6, ErrorCode::CommunicationFailure);
ck_and_add!(9, 7, ErrorCode::InternalCommunicationFailure);
ck_and_add!(10, 0, ErrorCode::CurrentModuleFault);
ck_and_add!(10, 1, ErrorCode::SumVoltageDetectFault);
ck_and_add!(10, 2, ErrorCode::ShortCircuitProtectFault);
ck_and_add!(10, 3, ErrorCode::LowVoltForbiddenChargeFault);
Ok(result)
}
}
pub struct SetDischargeMosfet;
impl SetDischargeMosfet {
pub fn request(address: Address, enable: bool) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0xD9);
if enable {
tx_buffer[4] = 0x01;
}
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)
}
}
pub struct SetChargeMosfet;
impl SetChargeMosfet {
pub fn request(address: Address, enable: bool) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0xDA);
if enable {
tx_buffer[4] = 0x01;
}
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)
}
}
pub struct SetSoc;
impl SetSoc {
pub fn request(address: Address, soc_percent: f32) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x21);
let value = {
let val = (soc_percent * 10.0).round();
if val > 1000.0 {
1000
} else if val < 0.0 {
0
} else {
val as u16
}
}
.to_be_bytes();
tx_buffer[10] = value[0]; tx_buffer[11] = value[1]; calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)
}
}
pub struct BmsReset;
impl BmsReset {
pub fn request(address: Address) -> Vec<u8> {
let mut tx_buffer = create_request_header(address, 0x00);
calc_crc_and_set(&mut tx_buffer);
tx_buffer
}
pub fn reply_size() -> usize {
RX_BUFFER_LENGTH
}
pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
validate_len(rx_buffer, Self::reply_size())?;
validate_checksum(rx_buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn calculate_test_checksum(data: &[u8]) -> u8 {
let mut checksum: u8 = 0;
for byte in data.iter().take(data.len() - 1) {
checksum = checksum.wrapping_add(*byte);
}
checksum
}
#[test]
fn test_calc_crc_valid() {
let data1: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; let expected_crc1 = calculate_test_checksum(&data1);
assert_eq!(calc_crc(&data1), expected_crc1);
let data2: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0x00,
]; let expected_crc2 = calculate_test_checksum(&data2);
assert_eq!(calc_crc(&data2), expected_crc2);
let data3: [u8; 13] = [
0xA5, 0x01, 0x02, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
];
let expected_crc3 = calculate_test_checksum(&data3);
assert_eq!(calc_crc(&data3), expected_crc3);
}
#[test]
fn test_validate_checksum_valid() {
let mut data: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x00,
];
data[12] = calc_crc(&data); assert!(validate_checksum(&data).is_ok());
}
#[test]
fn test_validate_checksum_invalid() {
let mut data: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x00,
];
data[12] = calc_crc(&data).wrapping_add(1); let result = validate_checksum(&data);
assert!(result.is_err());
match result.err().unwrap() {
Error::CheckSumError => {} _ => panic!("Unexpected error type"),
}
}
#[test]
fn test_status_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x94, 0x08, 0x10, 0x04, 0x01, 0x00, 0xAA, 0x04, 0xD2, 0x00, 0x16,
];
let expected_status = Status {
cells: 16,
temperature_sensors: 4,
charger_running: true,
load_running: false,
states: IOState {
di1: false, di2: true, di3: false, di4: true, do1: false, do2: true, do3: false, do4: true, },
cycles: 1234,
};
match Status::decode(&bytes) {
Ok(decoded) => {
assert_eq!(decoded.cells, expected_status.cells);
assert_eq!(
decoded.temperature_sensors,
expected_status.temperature_sensors
);
assert_eq!(decoded.charger_running, expected_status.charger_running);
assert_eq!(decoded.load_running, expected_status.load_running);
assert_eq!(decoded.states.di1, expected_status.states.di1);
assert_eq!(decoded.states.di2, expected_status.states.di2);
assert_eq!(decoded.states.di3, expected_status.states.di3);
assert_eq!(decoded.states.di4, expected_status.states.di4);
assert_eq!(decoded.states.do1, expected_status.states.do1);
assert_eq!(decoded.states.do2, expected_status.states.do2);
assert_eq!(decoded.states.do3, expected_status.states.do3);
assert_eq!(decoded.states.do4, expected_status.states.do4);
assert_eq!(decoded.cycles, expected_status.cycles);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_mosfet_status_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x93, 0x08, 0x01, 0x01, 0x00, 0x96, 0x00, 0x00, 0xC3, 0xCB, 0xA6,
];
let expected_status = MosfetStatus {
mode: MosfetMode::Charging,
charging_mosfet: true,
discharging_mosfet: false,
bms_cycles: 150,
capacity_ah: 50.123,
};
match MosfetStatus::decode(&bytes) {
Ok(decoded) => {
assert!(matches!(decoded.mode, MosfetMode::Charging));
assert_eq!(decoded.charging_mosfet, expected_status.charging_mosfet);
assert_eq!(
decoded.discharging_mosfet,
expected_status.discharging_mosfet
);
assert_eq!(decoded.bms_cycles, expected_status.bms_cycles);
assert!((decoded.capacity_ah - expected_status.capacity_ah).abs() < f32::EPSILON);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_temperature_range_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x92, 0x08, 0x41, 0x01, 0x32, 0x02, 0x00, 0x00, 0x00, 0x00, 0xF5,
];
let expected_range = TemperatureRange {
highest_temperature: 25,
highest_sensor: 1,
lowest_temperature: 10,
lowest_sensor: 2,
};
match TemperatureRange::decode(&bytes) {
Ok(decoded) => {
assert_eq!(
decoded.highest_temperature,
expected_range.highest_temperature
);
assert_eq!(decoded.highest_sensor, expected_range.highest_sensor);
assert_eq!(
decoded.lowest_temperature,
expected_range.lowest_temperature
);
assert_eq!(decoded.lowest_sensor, expected_range.lowest_sensor);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_cell_voltage_range_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x91, 0x08, 0x0D, 0x80, 0x01, 0x0C, 0x33, 0x05, 0x00, 0x00, 0x50,
];
let expected_range = CellVoltageRange {
highest_voltage: 3.456,
highest_cell: 1,
lowest_voltage: 3.123,
lowest_cell: 5,
};
match CellVoltageRange::decode(&bytes) {
Ok(decoded) => {
assert!(
(decoded.highest_voltage - expected_range.highest_voltage).abs() < f32::EPSILON
);
assert_eq!(decoded.highest_cell, expected_range.highest_cell);
assert!(
(decoded.lowest_voltage - expected_range.lowest_voltage).abs() < f32::EPSILON
);
assert_eq!(decoded.lowest_cell, expected_range.lowest_cell);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_validate_len_valid() {
let data: [u8; 13] = [0; 13];
assert!(validate_len(&data, 13).is_ok());
}
#[test]
fn test_validate_len_valid_larger_buffer() {
let data: [u8; 15] = [0; 15]; assert!(validate_len(&data, 13).is_ok());
}
#[test]
fn test_validate_len_invalid() {
let data: [u8; 12] = [0; 12];
let result = validate_len(&data, 13);
assert!(result.is_err());
match result.err().unwrap() {
Error::ReplySizeError => {} _ => panic!("Unexpected error type"),
}
}
#[test]
fn test_validate_len_empty() {
let data: [u8; 0] = [];
let result = validate_len(&data, 13);
assert!(result.is_err());
match result.err().unwrap() {
Error::ReplySizeError => {} _ => panic!("Unexpected error type"),
}
}
#[test]
fn test_soc_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3, 0x51,
];
let expected_soc = Soc {
total_voltage: 54.3,
current: 2.5,
soc_percent: 75.5,
};
match Soc::decode(&bytes) {
Ok(decoded) => {
assert!((decoded.total_voltage - expected_soc.total_voltage).abs() < f32::EPSILON);
assert!((decoded.current - expected_soc.current).abs() < f32::EPSILON);
assert!((decoded.soc_percent - expected_soc.soc_percent).abs() < f32::EPSILON);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_soc_decode_negative_current() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x01, 0xF4, 0x00, 0x00, 0x74, 0xCC, 0x01, 0xF4, 0xA7,
];
let expected_soc = Soc {
total_voltage: 50.0,
current: -10.0,
soc_percent: 50.0,
};
match Soc::decode(&bytes) {
Ok(decoded) => {
assert!((decoded.total_voltage - expected_soc.total_voltage).abs() < f32::EPSILON);
assert!((decoded.current - expected_soc.current).abs() < f32::EPSILON);
assert!((decoded.soc_percent - expected_soc.soc_percent).abs() < f32::EPSILON);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_soc_decode_invalid_checksum() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3, 0x52,
]; let result = Soc::decode(&bytes);
assert!(result.is_err());
match result.err().unwrap() {
Error::CheckSumError => {} e => panic!("Unexpected error type: {:?}", e),
}
}
#[test]
fn test_soc_decode_invalid_len() {
let bytes: [u8; 12] = [
0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3,
]; let result = Soc::decode(&bytes);
assert!(result.is_err());
match result.err().unwrap() {
Error::ReplySizeError => {} e => panic!("Unexpected error type: {:?}", e),
}
}
#[test]
fn test_cell_voltages_decode_valid_multi_frame() {
let frame1: [u8; 13] = [
0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x00, 0x56,
];
let frame2: [u8; 13] = [
0xA5, 0x40, 0x95, 0x08, 0x02, 0x0C, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
];
let mut combined_bytes = Vec::new();
combined_bytes.extend_from_slice(&frame1);
combined_bytes.extend_from_slice(&frame2);
let expected_voltages = vec![3.300, 3.301, 3.302, 3.303];
match CellVoltages::decode(&combined_bytes, 4) {
Ok(decoded) => {
assert_eq!((*decoded).len(), expected_voltages.len());
for (d, e) in (*decoded).iter().zip(expected_voltages.iter()) {
assert!((d - e).abs() < f32::EPSILON);
}
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_cell_voltages_decode_frame_out_of_order() {
let frame1: [u8; 13] = [
0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x00, 0x56,
];
let frame2_wrong_order: [u8; 13] = [
0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75,
];
let mut combined_bytes = Vec::new();
combined_bytes.extend_from_slice(&frame1);
combined_bytes.extend_from_slice(&frame2_wrong_order);
let result = CellVoltages::decode(&combined_bytes, 4);
assert!(result.is_err());
match result.err().unwrap() {
Error::FrameNoError => {} e => panic!("Unexpected error type: {:?}", e),
}
}
#[test]
fn test_cell_temperatures_decode_valid_multi_frame() {
let frame1: [u8; 13] = [
0xA5, 0x40, 0x96, 0x08, 0x01, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x3D,
];
let frame2: [u8; 13] = [
0xA5, 0x40, 0x96, 0x08, 0x02, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8,
];
let mut combined_bytes = Vec::new();
combined_bytes.extend_from_slice(&frame1);
combined_bytes.extend_from_slice(&frame2);
let expected_temperatures = vec![20, 21, 22, 23, 24, 25, 26, 27];
match CellTemperatures::decode(&combined_bytes, 8) {
Ok(decoded) => {
assert_eq!(decoded, expected_temperatures);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_cell_balance_state_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x97, 0x08, 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
];
let mut expected_state = vec![false; 16];
expected_state[0] = true; expected_state[8] = true; expected_state[15] = true;
match CellBalanceState::decode(&bytes, 16) {
Ok(decoded) => {
assert_eq!(decoded, expected_state);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_cell_balance_state_decode_n_cells_less_than_byte_boundary() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x97, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95,
];
let mut expected_state = vec![false; 5];
expected_state[0] = true; expected_state[4] = true;
match CellBalanceState::decode(&bytes, 5) {
Ok(decoded) => {
assert_eq!(decoded, expected_state);
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_error_code_decode_valid() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x98, 0x08, 0x01, 0x08, 0x10, 0x08, 0x00, 0x01, 0x00, 0x00, 0xA7,
];
let expected_errors = vec![
ErrorCode::CellVoltHighLevel1,
ErrorCode::ChargeTempLowLevel2,
ErrorCode::SocHighLevel1,
ErrorCode::DiffTempLevel2,
ErrorCode::AfeCollectChipErr,
];
match ErrorCode::decode(&bytes) {
Ok(decoded) => {
assert_eq!(decoded.len(), expected_errors.len());
for err in expected_errors {
assert!(decoded.contains(&err), "Missing error: {:?}", err);
}
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_error_code_decode_no_errors() {
let bytes: [u8; 13] = [
0xA5, 0x40, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
];
match ErrorCode::decode(&bytes) {
Ok(decoded) => {
assert!(decoded.is_empty());
}
Err(e) => panic!("Decoding failed: {:?}", e),
}
}
#[test]
fn test_soc_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D,
];
assert_eq!(Soc::request(Address::Host), expected_frame);
}
#[test]
fn test_cell_voltage_range_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x91, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E,
];
assert_eq!(CellVoltageRange::request(Address::Host), expected_frame);
}
#[test]
fn test_temperature_range_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x92, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
];
assert_eq!(TemperatureRange::request(Address::Host), expected_frame);
}
#[test]
fn test_mosfet_status_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x93, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
];
assert_eq!(MosfetStatus::request(Address::Host), expected_frame);
}
#[test]
fn test_status_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x94, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
];
assert_eq!(Status::request(Address::Host), expected_frame);
}
#[test]
fn test_cell_voltages_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x95, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,
];
assert_eq!(CellVoltages::request(Address::Host), expected_frame);
}
#[test]
fn test_cell_temperatures_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x96, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83,
];
assert_eq!(CellTemperatures::request(Address::Host), expected_frame);
}
#[test]
fn test_cell_balance_state_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x97, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84,
];
assert_eq!(CellBalanceState::request(Address::Host), expected_frame);
}
#[test]
fn test_error_code_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
];
assert_eq!(ErrorCode::request(Address::Host), expected_frame);
}
#[test]
fn test_set_discharge_mosfet_request_enable() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0xD9, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7,
];
assert_eq!(
SetDischargeMosfet::request(Address::Host, true),
expected_frame
);
}
#[test]
fn test_set_discharge_mosfet_request_disable() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0xD9, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6,
];
assert_eq!(
SetDischargeMosfet::request(Address::Host, false),
expected_frame
);
}
#[test]
fn test_set_charge_mosfet_request_enable() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0xDA, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8,
];
assert_eq!(
SetChargeMosfet::request(Address::Host, true),
expected_frame
);
}
#[test]
fn test_set_soc_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x21, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x25, 0x36,
];
assert_eq!(SetSoc::request(Address::Host, 80.5), expected_frame);
}
#[test]
fn test_bms_reset_request() {
let expected_frame: [u8; 13] = [
0xA5, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED,
];
assert_eq!(BmsReset::request(Address::Host), expected_frame);
}
}