dalybms_lib/
protocol.rs

1//! # Daly BMS Protocol Implementation
2//!
3//! This module provides the low-level implementation for the Daly BMS (Battery Management System)
4//! communication protocol. It defines the structure of commands and the logic for encoding
5//! requests and decoding responses.
6//!
7//! The module is organized around specific BMS commands, with each command typically represented
8//! by a struct or enum. For example, the `Soc` struct is used for requesting and decoding
9//! the State of Charge, voltage, and current.
10//!
11//! ## Key Features:
12//!
13//! - **Command-specific Structs/Enums**: Each BMS command (e.g., `get_soc`, `get_status`) has a
14//!   corresponding type that encapsulates its request and response logic.
15//! - **Request Generation**: Each command type provides a `request` function that constructs the
16//!   byte frame to be sent to the BMS.
17//! - **Response Decoding**: Each command type provides a `decode` function that parses the
18//!   response byte frame from the BMS into a structured format.
19//! - **Checksum Calculation**: Includes helpers for calculating and validating the checksum
20//!   required by the Daly protocol.
21//! - **Error Handling**: Defines protocol-specific error conditions, such as checksum mismatches
22//!   or invalid response lengths.
23//!
24//! ## For End-Users:
25//!
26//! This module is intended for internal use by the higher-level client implementations
27//! (e.g., `dalybms_lib::serialport` and `dalybms_lib::tokio_serial_async`). Most users should
28//! not need to interact with this module directly. The clients provide a more ergonomic,
29//! high-level API for interacting with the BMS.
30//!
31use crate::Error;
32
33#[cfg(feature = "serde")]
34use serde::{Deserialize, Serialize};
35
36#[cfg(feature = "serde")]
37mod util {
38    use serde::{ser::SerializeSeq, Serializer};
39
40    pub fn f32_1_digits<S>(x: &f32, s: S) -> Result<S::Ok, S::Error>
41    where
42        S: Serializer,
43    {
44        s.serialize_f64((*x as f64 * 10.0).round() / 10.0)
45    }
46
47    pub fn f32_3_digits<S>(x: &f32, s: S) -> Result<S::Ok, S::Error>
48    where
49        S: Serializer,
50    {
51        s.serialize_f64((*x as f64 * 1000.0).round() / 1000.0)
52    }
53
54    pub fn vec_f32_3_digits<S>(vec: &Vec<f32>, s: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        let mut seq = s.serialize_seq(Some(vec.len()))?;
59        for e in vec {
60            let val = (*e as f64 * 1000.0).round() / 1000.0;
61            seq.serialize_element(&val)?;
62        }
63        seq.end()
64    }
65}
66
67/// Represents the sender/receiver address in a BMS command.
68/// Currently, only the Host address is defined, as the BMS address can vary.
69#[derive(Debug)]
70#[repr(u8)]
71pub enum Address {
72    /// Address of the host (e.g., your computer).
73    Host = 0x40,
74    // Note: BMS address (typically 0x80) is omitted here as it's the default
75    // address for sending commands and not explicitly part of the `Address` enum
76    // when constructing requests from the host perspective.
77}
78
79// https://minimalmodbus.readthedocs.io/en/stable/serialcommunication.html#timing-of-the-serial-communications
80// minimum delay 4ms by baud rate 9600
81/// Minimum delay required between sending commands to the BMS.
82/// This is to prevent overwhelming the BMS with requests.
83pub const MINIMUM_DELAY: std::time::Duration = std::time::Duration::from_millis(4);
84
85/// The required length of a request sent to the BMS.
86const TX_BUFFER_LENGTH: usize = 13;
87/// The expected length of a standard response from the BMS.
88const RX_BUFFER_LENGTH: usize = 13;
89/// The start byte that begins every command.
90const START_BYTE: u8 = 0xa5;
91/// The length of the data payload in a standard command.
92const DATA_LENGTH: u8 = 0x08;
93
94/// Creates the basic structure of a request frame.
95///
96/// This function initializes a 13-byte vector and populates the header
97/// with the start byte, address, command, and data length. The data
98/// payload and checksum are left as zeros and must be populated later.
99///
100/// # Arguments
101///
102/// * `address` - The `Address` to which the command is being sent.
103/// * `command` - The command code (e.g., 0x90 for SOC).
104///
105/// # Returns
106///
107/// A `Vec<u8>` of length `TX_BUFFER_LENGTH` with the header fields set.
108fn create_request_header(address: Address, command: u8) -> Vec<u8> {
109    let mut tx_buffer = vec![0; TX_BUFFER_LENGTH];
110    tx_buffer[0] = START_BYTE;
111    tx_buffer[1] = address as u8;
112    tx_buffer[2] = command;
113    tx_buffer[3] = DATA_LENGTH;
114    tx_buffer
115}
116
117/// Calculates the checksum for a given buffer.
118/// The checksum is the sum of all bytes in the buffer up to, but not including,
119/// the last byte, which is reserved for the checksum itself.
120fn calc_crc(buffer: &[u8]) -> u8 {
121    let mut checksum: u8 = 0;
122    let slice = &buffer[0..buffer.len() - 1];
123    for b in slice {
124        checksum = checksum.wrapping_add(*b);
125    }
126    checksum
127}
128
129/// Calculates and sets the checksum on the last byte of a mutable buffer.
130fn calc_crc_and_set(buffer: &mut [u8]) {
131    let len = buffer.len();
132    buffer[len - 1] = calc_crc(buffer)
133}
134
135/// A macro to read a specific bit from a byte.
136/// Returns `true` if the bit at `position` is 1, `false` otherwise.
137macro_rules! read_bit {
138    ($byte:expr,$position:expr) => {
139        ($byte >> $position) & 1 != 0
140    };
141}
142
143/// Validates that the received buffer has at least the expected length.
144///
145/// # Arguments
146///
147/// * `buffer` - The byte slice received from the BMS.
148/// * `expected_size` - The minimum required length for the buffer.
149///
150/// # Returns
151///
152/// An empty `Result` on success, or an `Error::ReplySizeError` if validation fails.
153fn validate_len(buffer: &[u8], expected_size: usize) -> std::result::Result<(), Error> {
154    if buffer.len() < expected_size {
155        log::warn!(
156            "Invalid buffer size - required={} received={}",
157            expected_size,
158            buffer.len()
159        );
160        return Err(Error::ReplySizeError);
161    }
162    Ok(())
163}
164
165/// Validates that the checksum of the received buffer is correct.
166///
167/// # Arguments
168///
169/// * `buffer` - The byte slice received from the BMS.
170///
171/// # Returns
172///
173/// An empty `Result` on success, or an `Error::CheckSumError` if validation fails.
174fn validate_checksum(buffer: &[u8]) -> std::result::Result<(), Error> {
175    let checksum = calc_crc(buffer);
176    if buffer[buffer.len() - 1] != checksum {
177        log::warn!(
178            "Invalid checksum - calculated={:02X?} received={:02X?} buffer={:?}",
179            checksum,
180            buffer[buffer.len() - 1],
181            buffer
182        );
183        return Err(Error::CheckSumError);
184    }
185    Ok(())
186}
187
188/// Represents the State of Charge (SOC) and related battery metrics.
189#[derive(Debug, Clone)]
190#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
191pub struct Soc {
192    /// Total battery voltage in Volts.
193    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
194    pub total_voltage: f32,
195    /// Battery current in Amperes.
196    /// Negative values indicate charging, positive values indicate discharging.
197    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
198    pub current: f32,
199    /// State of Charge percentage (0.0 - 100.0%).
200    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_1_digits"))]
201    pub soc_percent: f32,
202}
203
204impl Soc {
205    /// Creates a request frame to read the SOC from the BMS.
206    ///
207    /// # Arguments
208    ///
209    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
210    ///
211    /// # Returns
212    ///
213    /// A byte vector representing the request frame.
214    ///
215    /// This is a low-level function. Users might prefer client methods like `DalyBMSSerial::get_soc`.
216    pub fn request(address: Address) -> Vec<u8> {
217        let mut tx_buffer = create_request_header(address, 0x90);
218        calc_crc_and_set(&mut tx_buffer);
219        tx_buffer
220    }
221
222    /// Expected size of the reply frame for an SOC request.
223    pub fn reply_size() -> usize {
224        RX_BUFFER_LENGTH
225    }
226
227    /// Decodes the SOC data from a response frame.
228    ///
229    /// # Arguments
230    ///
231    /// * `rx_buffer` - The response frame received from the BMS.
232    ///
233    /// # Returns
234    ///
235    /// A `Result` containing the `Soc` data or an `Error` if decoding fails (e.g., checksum error, incorrect size).
236    ///
237    /// This is a low-level function. Users might prefer client methods.
238    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
239        validate_len(rx_buffer, Self::reply_size())?;
240        validate_checksum(rx_buffer)?;
241        Ok(Self {
242            total_voltage: u16::from_be_bytes([rx_buffer[4], rx_buffer[5]]) as f32 / 10.0,
243            // The current measurement is given with a 30000 unit offset (see /docs/)
244            current: (((u16::from_be_bytes([rx_buffer[8], rx_buffer[9]]) as i32) - 30000) as f32)
245                / 10.0,
246            soc_percent: u16::from_be_bytes([rx_buffer[10], rx_buffer[11]]) as f32 / 10.0,
247        })
248    }
249}
250
251/// Represents the range of cell voltages (highest and lowest) in the battery pack.
252#[derive(Debug, Clone)]
253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
254pub struct CellVoltageRange {
255    /// Highest cell voltage in Volts.
256    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
257    pub highest_voltage: f32,
258    /// Cell number with the highest voltage.
259    pub highest_cell: u8,
260    /// Lowest cell voltage in Volts.
261    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
262    pub lowest_voltage: f32,
263    /// Cell number with the lowest voltage.
264    pub lowest_cell: u8,
265}
266
267impl CellVoltageRange {
268    /// Creates a request frame to read the cell voltage range from the BMS.
269    ///
270    /// # Arguments
271    ///
272    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
273    ///
274    /// # Returns
275    ///
276    /// A byte vector representing the request frame.
277    ///
278    /// This is a low-level function. Users might prefer client methods.
279    pub fn request(address: Address) -> Vec<u8> {
280        let mut tx_buffer = create_request_header(address, 0x91);
281        calc_crc_and_set(&mut tx_buffer);
282        tx_buffer
283    }
284
285    /// Expected size of the reply frame for a cell voltage range request.
286    pub fn reply_size() -> usize {
287        RX_BUFFER_LENGTH
288    }
289
290    /// Decodes the cell voltage range data from a response frame.
291    ///
292    /// # Arguments
293    ///
294    /// * `rx_buffer` - The response frame received from the BMS.
295    ///
296    /// # Returns
297    ///
298    /// A `Result` containing the `CellVoltageRange` data or an `Error` if decoding fails.
299    ///
300    /// This is a low-level function. Users might prefer client methods.
301    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
302        validate_len(rx_buffer, Self::reply_size())?;
303        validate_checksum(rx_buffer)?;
304        Ok(Self {
305            highest_voltage: u16::from_be_bytes([rx_buffer[4], rx_buffer[5]]) as f32 / 1000.0,
306            highest_cell: rx_buffer[6],
307            lowest_voltage: u16::from_be_bytes([rx_buffer[7], rx_buffer[8]]) as f32 / 1000.0,
308            lowest_cell: rx_buffer[9],
309        })
310    }
311}
312
313/// Represents the range of temperatures (highest and lowest) measured by the BMS.
314#[derive(Debug, Clone)]
315#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
316pub struct TemperatureRange {
317    /// Highest temperature in degrees Celsius.
318    pub highest_temperature: i8,
319    /// Sensor number that detected the highest temperature.
320    pub highest_sensor: u8,
321    /// Lowest temperature in degrees Celsius.
322    pub lowest_temperature: i8,
323    /// Sensor number that detected the lowest temperature.
324    pub lowest_sensor: u8,
325}
326
327impl TemperatureRange {
328    /// Creates a request frame to read the temperature range from the BMS.
329    ///
330    /// # Arguments
331    ///
332    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
333    ///
334    /// # Returns
335    ///
336    /// A byte vector representing the request frame.
337    ///
338    /// This is a low-level function. Users might prefer client methods.
339    pub fn request(address: Address) -> Vec<u8> {
340        let mut tx_buffer = create_request_header(address, 0x92);
341        calc_crc_and_set(&mut tx_buffer);
342        tx_buffer
343    }
344
345    /// Expected size of the reply frame for a temperature range request.
346    pub fn reply_size() -> usize {
347        RX_BUFFER_LENGTH
348    }
349
350    /// Decodes the temperature range data from a response frame.
351    ///
352    /// # Arguments
353    ///
354    /// * `rx_buffer` - The response frame received from the BMS.
355    ///
356    /// # Returns
357    ///
358    /// A `Result` containing the `TemperatureRange` data or an `Error` if decoding fails.
359    ///
360    /// This is a low-level function. Users might prefer client methods.
361    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
362        validate_len(rx_buffer, Self::reply_size())?;
363        validate_checksum(rx_buffer)?;
364        // An offset of 40 is added by the BMS to avoid having to deal with negative numbers, see protocol in /docs/
365        Ok(Self {
366            highest_temperature: ((rx_buffer[4] as i16) - 40) as i8,
367            highest_sensor: rx_buffer[5],
368            lowest_temperature: ((rx_buffer[6] as i16) - 40) as i8,
369            lowest_sensor: rx_buffer[7],
370        })
371    }
372}
373
374/// Represents the operational mode of the MOSFETs.
375#[derive(Debug, Clone)]
376#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
377pub enum MosfetMode {
378    /// MOSFETs are stationary (neither charging nor discharging).
379    Stationary,
380    /// MOSFETs are in charging mode.
381    Charging,
382    /// MOSFETs are in discharging mode.
383    Discharging,
384}
385
386/// Represents the status of the MOSFETs and battery capacity.
387#[derive(Debug, Clone)]
388#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
389pub struct MosfetStatus {
390    /// Current operational mode of the MOSFETs.
391    pub mode: MosfetMode,
392    /// True if the charging MOSFET is enabled.
393    pub charging_mosfet: bool,
394    /// True if the discharging MOSFET is enabled.
395    pub discharging_mosfet: bool,
396    /// Number of BMS cycles (e.g., charge/discharge cycles).
397    pub bms_cycles: u8,
398    /// Remaining battery capacity in Ampere-hours (Ah).
399    #[cfg_attr(feature = "serde", serde(serialize_with = "util::f32_3_digits"))]
400    pub capacity_ah: f32,
401}
402
403impl MosfetStatus {
404    /// Creates a request frame to read the MOSFET status from the BMS.
405    ///
406    /// # Arguments
407    ///
408    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
409    ///
410    /// # Returns
411    ///
412    /// A byte vector representing the request frame.
413    ///
414    /// This is a low-level function. Users might prefer client methods.
415    pub fn request(address: Address) -> Vec<u8> {
416        let mut tx_buffer = create_request_header(address, 0x93);
417        calc_crc_and_set(&mut tx_buffer);
418        tx_buffer
419    }
420
421    /// Expected size of the reply frame for a MOSFET status request.
422    pub fn reply_size() -> usize {
423        RX_BUFFER_LENGTH
424    }
425
426    /// Decodes the MOSFET status data from a response frame.
427    ///
428    /// # Arguments
429    ///
430    /// * `rx_buffer` - The response frame received from the BMS.
431    ///
432    /// # Returns
433    ///
434    /// A `Result` containing the `MosfetStatus` data or an `Error` if decoding fails.
435    ///
436    /// This is a low-level function. Users might prefer client methods.
437    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
438        validate_len(rx_buffer, Self::reply_size())?;
439        validate_checksum(rx_buffer)?;
440        let mode = match rx_buffer[4] {
441            0 => MosfetMode::Stationary,
442            1 => MosfetMode::Charging,
443            2 => MosfetMode::Discharging,
444            _ => unreachable!(),
445        };
446        Ok(Self {
447            mode,
448            charging_mosfet: rx_buffer[5] != 0,
449            discharging_mosfet: rx_buffer[6] != 0,
450            bms_cycles: rx_buffer[7],
451            capacity_ah: u32::from_be_bytes([
452                rx_buffer[8],
453                rx_buffer[9],
454                rx_buffer[10],
455                rx_buffer[11],
456            ]) as f32
457                / 1000.0,
458        })
459    }
460}
461
462/// Represents the state of digital inputs (DI) and digital outputs (DO).
463#[derive(Debug, Clone)]
464#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
465pub struct IOState {
466    /// State of digital input 1.
467    pub di1: bool,
468    /// State of digital input 2.
469    pub di2: bool,
470    /// State of digital input 3.
471    pub di3: bool,
472    /// State of digital input 4.
473    pub di4: bool,
474    /// State of digital output 1.
475    pub do1: bool,
476    /// State of digital output 2.
477    pub do2: bool,
478    /// State of digital output 3.
479    pub do3: bool,
480    /// State of digital output 4.
481    pub do4: bool,
482}
483
484/// Represents various status information of the BMS.
485#[derive(Debug, Clone)]
486#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
487pub struct Status {
488    /// Number of battery cells.
489    pub cells: u8,
490    /// Number of temperature sensors.
491    pub temperature_sensors: u8,
492    /// True if the charger is currently running.
493    pub charger_running: bool,
494    /// True if a load is currently connected and drawing power.
495    pub load_running: bool,
496    /// State of digital inputs and outputs.
497    pub states: IOState,
498    /// Number of charge/discharge cycles.
499    pub cycles: u16,
500}
501
502impl Status {
503    /// Creates a request frame to read the BMS status from the BMS.
504    ///
505    /// # Arguments
506    ///
507    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
508    ///
509    /// # Returns
510    ///
511    /// A byte vector representing the request frame.
512    ///
513    /// This is a low-level function. Users might prefer client methods.
514    pub fn request(address: Address) -> Vec<u8> {
515        let mut tx_buffer = create_request_header(address, 0x94);
516        calc_crc_and_set(&mut tx_buffer);
517        tx_buffer
518    }
519
520    /// Expected size of the reply frame for a BMS status request.
521    pub fn reply_size() -> usize {
522        RX_BUFFER_LENGTH
523    }
524
525    /// Decodes the BMS status data from a response frame.
526    ///
527    /// # Arguments
528    ///
529    /// * `rx_buffer` - The response frame received from the BMS.
530    ///
531    /// # Returns
532    ///
533    /// A `Result` containing the `Status` data or an `Error` if decoding fails.
534    ///
535    /// This is a low-level function. Users might prefer client methods.
536    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Self, Error> {
537        validate_len(rx_buffer, Self::reply_size())?;
538        validate_checksum(rx_buffer)?;
539        Ok(Self {
540            cells: rx_buffer[4],
541            temperature_sensors: rx_buffer[5],
542            charger_running: rx_buffer[6] != 0,
543            load_running: rx_buffer[7] != 0,
544            states: IOState {
545                di1: read_bit!(rx_buffer[8], 0),
546                di2: read_bit!(rx_buffer[8], 1),
547                di3: read_bit!(rx_buffer[8], 2),
548                di4: read_bit!(rx_buffer[8], 3),
549                do1: read_bit!(rx_buffer[8], 4),
550                do2: read_bit!(rx_buffer[8], 5),
551                do3: read_bit!(rx_buffer[8], 6),
552                do4: read_bit!(rx_buffer[8], 7),
553            },
554            cycles: u16::from_be_bytes([rx_buffer[9], rx_buffer[10]]),
555        })
556    }
557}
558
559/// Represents a command to request individual cell voltages.
560/// The BMS returns cell voltages in multiple frames.
561#[derive(Debug, Clone)]
562#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
563pub struct CellVoltages(
564    #[cfg_attr(feature = "serde", serde(serialize_with = "util::vec_f32_3_digits"))] Vec<f32>,
565);
566
567impl CellVoltages {
568    /// Creates a request frame to read individual cell voltages from the BMS.
569    ///
570    /// # Arguments
571    ///
572    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
573    ///
574    /// # Returns
575    ///
576    /// A byte vector representing the request frame.
577    ///
578    /// This is a low-level function. Users might prefer client methods.
579    pub fn request(address: Address) -> Vec<u8> {
580        let mut tx_buffer = create_request_header(address, 0x95);
581        calc_crc_and_set(&mut tx_buffer);
582        tx_buffer
583    }
584
585    /// Calculates the number of frames expected for the given number of cells.
586    fn n_frames(n_cells: u8) -> usize {
587        (n_cells as f32 / 3.0).ceil() as usize
588    }
589
590    /// Calculates the total expected reply size in bytes for all frames for a given number of cells.
591    ///
592    /// # Arguments
593    ///
594    /// * `n_cells` - The number of cells in the battery pack.
595    pub fn reply_size(n_cells: u8) -> usize {
596        Self::n_frames(n_cells) * RX_BUFFER_LENGTH
597    }
598
599    /// Decodes the individual cell voltage data from a concatenated multi-frame response.
600    ///
601    /// # Arguments
602    ///
603    /// * `rx_buffer` - The concatenated response frames received from the BMS.
604    /// * `n_cells` - The total number of cells expected.
605    ///
606    /// # Returns
607    ///
608    /// A `Result` containing a `Vec<f32>` of cell voltages or an `Error` if decoding fails.
609    ///
610    /// This is a low-level function. Users might prefer client methods.
611    pub fn decode(rx_buffer: &[u8], n_cells: u8) -> std::result::Result<Self, Error> {
612        validate_len(rx_buffer, Self::reply_size(n_cells))?;
613        let mut voltages = Vec::with_capacity(n_cells as usize);
614        let mut n_cell = 1;
615
616        for n_frame in 1..=Self::n_frames(n_cells) {
617            let part =
618                &rx_buffer[((n_frame - 1) * RX_BUFFER_LENGTH)..((n_frame) * RX_BUFFER_LENGTH)];
619            if n_frame != usize::from(part[4]) {
620                log::warn!(
621                    "Frame out of order - expected={} received={}",
622                    n_frame,
623                    part[4]
624                );
625                return Err(Error::FrameNoError);
626            }
627            validate_checksum(part)?;
628            for i in 0..3 {
629                let volt = u16::from_be_bytes([part[5 + i + i], part[6 + i + i]]) as f32 / 1000.0;
630                log::trace!("Frame #{n_frame} cell #{n_cell} volt={volt}");
631                voltages.push(volt);
632                n_cell += 1;
633                if n_cell > n_cells {
634                    break;
635                }
636            }
637        }
638        Ok(Self(voltages))
639    }
640}
641
642impl std::ops::Deref for CellVoltages {
643    type Target = [f32];
644
645    fn deref(&self) -> &[f32] {
646        &self.0
647    }
648}
649
650/// Represents a command to request individual cell temperatures.
651/// The BMS returns cell temperatures in multiple frames.
652pub struct CellTemperatures;
653
654impl CellTemperatures {
655    /// Creates a request frame to read individual cell temperatures from the BMS.
656    ///
657    /// # Arguments
658    ///
659    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
660    ///
661    /// # Returns
662    ///
663    /// A byte vector representing the request frame.
664    ///
665    /// This is a low-level function. Users might prefer client methods.
666    pub fn request(address: Address) -> Vec<u8> {
667        let mut tx_buffer = create_request_header(address, 0x96);
668        calc_crc_and_set(&mut tx_buffer);
669        tx_buffer
670    }
671
672    /// Calculates the number of frames expected for the given number of temperature sensors.
673    fn n_frames(n_sensors: u8) -> usize {
674        (n_sensors as f32 / 7.0).ceil() as usize
675    }
676
677    /// Calculates the total expected reply size in bytes for all frames for a given number of temperature sensors.
678    ///
679    /// # Arguments
680    ///
681    /// * `n_sensors` - The number of temperature sensors in the battery pack.
682    pub fn reply_size(n_sensors: u8) -> usize {
683        Self::n_frames(n_sensors) * RX_BUFFER_LENGTH
684    }
685
686    /// Decodes the individual cell temperature data from a concatenated multi-frame response.
687    ///
688    /// # Arguments
689    ///
690    /// * `rx_buffer` - The concatenated response frames received from the BMS.
691    /// * `n_sensors` - The total number of temperature sensors expected.
692    ///
693    /// # Returns
694    ///
695    /// A `Result` containing a `Vec<i32>` of cell temperatures in degrees Celsius or an `Error` if decoding fails.
696    /// Note that the BMS adds an offset of 40 to the temperature values, which is handled by this function.
697    ///
698    /// This is a low-level function. Users might prefer client methods.
699    pub fn decode(rx_buffer: &[u8], n_sensors: u8) -> std::result::Result<Vec<i32>, Error> {
700        validate_len(rx_buffer, Self::reply_size(n_sensors))?;
701        let mut result = Vec::with_capacity(n_sensors as usize);
702        let mut n_sensor = 1;
703
704        for n_frame in 1..=Self::n_frames(n_sensors) {
705            let part =
706                &rx_buffer[((n_frame - 1) * RX_BUFFER_LENGTH)..((n_frame) * RX_BUFFER_LENGTH)];
707            if n_frame != usize::from(part[4]) {
708                log::warn!(
709                    "Frame out of order - expected={} received={}",
710                    n_frame,
711                    part[4]
712                );
713                return Err(Error::FrameNoError);
714            }
715            validate_checksum(part)?;
716            for i in 0..7 {
717                let temperature = part[5 + i] as i32 - 40;
718                log::trace!("Frame #{n_frame} sensor #{n_sensor} °C={temperature}");
719                result.push(temperature);
720                n_sensor += 1;
721                if n_sensor > n_sensors {
722                    break;
723                }
724            }
725        }
726        Ok(result)
727    }
728}
729
730/// Represents a command to request the balance state of individual cells.
731pub struct CellBalanceState;
732
733impl CellBalanceState {
734    /// Creates a request frame to read the cell balance states from the BMS.
735    ///
736    /// # Arguments
737    ///
738    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
739    ///
740    /// # Returns
741    ///
742    /// A byte vector representing the request frame.
743    ///
744    /// This is a low-level function. Users might prefer client methods.
745    pub fn request(address: Address) -> Vec<u8> {
746        let mut tx_buffer = create_request_header(address, 0x97);
747        calc_crc_and_set(&mut tx_buffer);
748        tx_buffer
749    }
750
751    /// Expected size of the reply frame for a cell balance state request.
752    pub fn reply_size() -> usize {
753        RX_BUFFER_LENGTH
754    }
755
756    /// Decodes the cell balance state data from a response frame.
757    ///
758    /// # Arguments
759    ///
760    /// * `rx_buffer` - The response frame received from the BMS.
761    /// * `n_cells` - The total number of cells in the battery pack.
762    ///
763    /// # Returns
764    ///
765    /// A `Result` containing a `Vec<bool>` where `true` indicates the cell is balancing,
766    /// or an `Error` if decoding fails.
767    ///
768    /// This is a low-level function. Users might prefer client methods.
769    pub fn decode(rx_buffer: &[u8], n_cells: u8) -> std::result::Result<Vec<bool>, Error> {
770        validate_len(rx_buffer, Self::reply_size())?;
771        validate_checksum(rx_buffer)?;
772        let mut result = Vec::with_capacity(n_cells as usize);
773        let mut n_cell = 0;
774        // We expect 6 bytes response for this command
775        for i in 0..6 {
776            // For each bit in the byte, pull out the cell balance state boolean
777            for j in 0..8 {
778                result.push(read_bit!(rx_buffer[4 + i], j));
779                n_cell += 1;
780                if n_cell >= n_cells {
781                    return Ok(result);
782                }
783            }
784        }
785        Ok(result)
786    }
787}
788
789/// Represents various error codes and alarm states reported by the BMS.
790#[derive(Debug, Clone, thiserror::Error, PartialEq)]
791#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
792pub enum ErrorCode {
793    /// Cell voltage is too high (Level 1)
794    #[error("Cell voltage is too high (Level 1)")]
795    CellVoltHighLevel1,
796    /// Cell voltage is too high (Level 2)
797    #[error("Cell voltage is too high (Level 2)")]
798    CellVoltHighLevel2,
799    /// Cell voltage is too low (Level 1)
800    #[error("Cell voltage is too low (Level 1)")]
801    CellVoltLowLevel1,
802    /// Cell voltage is too low (Level 2)
803    #[error("Cell voltage is too low (Level 2)")]
804    CellVoltLowLevel2,
805    /// Total voltage is too high (Level 1)
806    #[error("Total voltage is too high (Level 1)")]
807    SumVoltHighLevel1,
808    /// Total voltage is too high (Level 2)
809    #[error("Total voltage is too high (Level 2)")]
810    SumVoltHighLevel2,
811    /// Total voltage is too low (Level 1)
812    #[error("Total voltage is too low (Level 1)")]
813    SumVoltLowLevel1,
814    /// Total voltage is too low (Level 2)
815    #[error("Total voltage is too low (Level 2)")]
816    SumVoltLowLevel2,
817    /// Charging temperature too high (Level 1)
818    #[error("Charging temperature too high (Level 1)")]
819    ChargeTempHighLevel1,
820    /// Charging temperature too high (Level 2)
821    #[error("Charging temperature too high (Level 2)")]
822    ChargeTempHighLevel2,
823    /// Charging temperature too low (Level 1)
824    #[error("Charging temperature too low (Level 1)")]
825    ChargeTempLowLevel1,
826    /// Charging temperature too low (Level 2)
827    #[error("Charging temperature too low (Level 2)")]
828    ChargeTempLowLevel2,
829    /// Discharging temperature too high (Level 1)
830    #[error("Discharging temperature too high (Level 1)")]
831    DischargeTempHighLevel1,
832    /// Discharging temperature too high (Level 2)
833    #[error("Discharging temperature too high (Level 2)")]
834    DischargeTempHighLevel2,
835    /// Discharging temperature too low (Level 1)
836    #[error("Discharging temperature too low (Level 1)")]
837    DischargeTempLowLevel1,
838    /// Discharging temperature too low (Level 2)
839    #[error("Discharging temperature too low (Level 2)")]
840    DischargeTempLowLevel2,
841    /// Charge overcurrent (Level 1)
842    #[error("Charge overcurrent (Level 1)")]
843    ChargeOvercurrentLevel1,
844    /// Charge overcurrent (Level 2)
845    #[error("Charge overcurrent (Level 2)")]
846    ChargeOvercurrentLevel2,
847    /// Discharge overcurrent (Level 1)
848    #[error("Discharge overcurrent (Level 1)")]
849    DischargeOvercurrentLevel1,
850    /// Discharge overcurrent (Level 2)
851    #[error("Discharge overcurrent (Level 2)")]
852    DischargeOvercurrentLevel2,
853    /// State of Charge (SOC) too high (Level 1)
854    #[error("SOC too high (Level 1)")]
855    SocHighLevel1,
856    /// State of Charge (SOC) too high (Level 2)
857    #[error("SOC too high (Level 2)")]
858    SocHighLevel2,
859    /// State of Charge (SOC) too low (Level 1)
860    #[error("SOC too low (Level 1)")]
861    SocLowLevel1,
862    /// State of Charge (SOC) too low (Level 2)
863    #[error("SOC too low (Level 2)")]
864    SocLowLevel2,
865    /// Excessive voltage difference between cells (Level 1)
866    #[error("Excessive voltage difference between cells (Level 1)")]
867    DiffVoltLevel1,
868    /// Excessive voltage difference between cells (Level 2)
869    #[error("Excessive voltage difference between cells (Level 2)")]
870    DiffVoltLevel2,
871    /// Excessive temperature difference between sensors (Level 1)
872    #[error("Excessive temperature difference between sensors (Level 1)")]
873    DiffTempLevel1,
874    /// Excessive temperature difference between sensors (Level 2)
875    #[error("Excessive temperature difference between sensors (Level 2)")]
876    DiffTempLevel2,
877    /// Charging MOSFET over-temperature alarm.
878    #[error("Charging MOSFET temperature too high")]
879    ChargeMosTempHighAlarm,
880    /// Discharging MOSFET over-temperature alarm.
881    #[error("Discharging MOSFET temperature too high")]
882    DischargeMosTempHighAlarm,
883    /// Charging MOSFET temperature sensor failure.
884    #[error("Charging MOSFET temperature sensor failure")]
885    ChargeMosTempSensorErr,
886    /// Discharging MOSFET temperature sensor failure.
887    #[error("Discharging MOSFET temperature sensor failure")]
888    DischargeMosTempSensorErr,
889    /// Charging MOSFET adhesion failure (stuck closed).
890    #[error("Charging MOSFET adhesion failure")]
891    ChargeMosAdhesionErr,
892    /// Discharging MOSFET adhesion failure (stuck closed).
893    #[error("Discharging MOSFET adhesion failure")]
894    DischargeMosAdhesionErr,
895    /// Charging MOSFET breaker failure (stuck open).
896    #[error("Charging MOSFET open circuit failure")]
897    ChargeMosOpenCircuitErr,
898    /// Discharging MOSFET breaker failure (stuck open).
899    #[error("Discharging MOSFET open circuit failure")]
900    DischargeMosOpenCircuitErr,
901    /// AFE (Analog Front End) acquisition chip malfunction.
902    #[error("AFE acquisition chip failure")]
903    AfeCollectChipErr,
904    /// Monomer (cell voltage) collection circuit drop off.
905    #[error("Cell voltage collection circuit failure")]
906    VoltageCollectDropped,
907    /// Single temperature sensor failure.
908    #[error("Cell temperature sensor failure")]
909    CellTempSensorErr,
910    /// EEPROM storage failure.
911    #[error("EEPROM storage failure")]
912    EepromErr,
913    /// RTC (Real-Time Clock) malfunction.
914    #[error("RTC clock failure")]
915    RtcErr,
916    /// Pre-charge failure.
917    #[error("Pre-charge failure")]
918    PrechangeFailure,
919    /// General communication malfunction.
920    #[error("Communication failure")]
921    CommunicationFailure,
922    /// Internal communication module malfunction.
923    #[error("Internal communication failure")]
924    InternalCommunicationFailure,
925    /// Current detection module failure.
926    #[error("Current detection module failure")]
927    CurrentModuleFault,
928    /// Total voltage detection module failure.
929    #[error("Total voltage detection module failure")]
930    SumVoltageDetectFault,
931    /// Short circuit protection failure.
932    #[error("Short circuit protection failure")]
933    ShortCircuitProtectFault,
934    /// Low voltage condition forbids charging.
935    #[error("Low voltage forbids charging")]
936    LowVoltForbiddenChargeFault,
937}
938
939impl ErrorCode {
940    /// Creates a request frame to read the BMS error codes.
941    ///
942    /// # Arguments
943    ///
944    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
945    ///
946    /// # Returns
947    ///
948    /// A byte vector representing the request frame.
949    ///
950    /// This is a low-level function. Users might prefer client methods.
951    pub fn request(address: Address) -> Vec<u8> {
952        let mut tx_buffer = create_request_header(address, 0x98);
953        calc_crc_and_set(&mut tx_buffer);
954        tx_buffer
955    }
956
957    /// Expected size of the reply frame for an error codes request.
958    pub fn reply_size() -> usize {
959        RX_BUFFER_LENGTH
960    }
961
962    /// Decodes the error codes from a response frame.
963    /// The BMS returns a bitmask where each bit corresponds to an error.
964    /// This function returns a `Vec<ErrorCode>` containing all active errors.
965    ///
966    /// # Arguments
967    ///
968    /// * `rx_buffer` - The response frame received from the BMS.
969    ///
970    /// # Returns
971    ///
972    /// A `Result` containing a `Vec<ErrorCode>` of active errors or an `Error` if decoding fails.
973    ///
974    /// This is a low-level function. Users might prefer client methods.
975    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<Vec<Self>, Error> {
976        validate_len(rx_buffer, Self::reply_size())?;
977        validate_checksum(rx_buffer)?;
978        let mut result = Vec::new();
979
980        macro_rules! ck_and_add {
981            ($byte:expr,$position:expr,$enum_type:expr) => {
982                if read_bit!(rx_buffer[$byte], $position) {
983                    result.push($enum_type);
984                }
985            };
986        }
987
988        ck_and_add!(4, 0, ErrorCode::CellVoltHighLevel1);
989        ck_and_add!(4, 1, ErrorCode::CellVoltHighLevel2);
990        ck_and_add!(4, 2, ErrorCode::CellVoltLowLevel1);
991        ck_and_add!(4, 3, ErrorCode::CellVoltLowLevel2);
992        ck_and_add!(4, 4, ErrorCode::SumVoltHighLevel1);
993        ck_and_add!(4, 5, ErrorCode::SumVoltHighLevel2);
994        ck_and_add!(4, 6, ErrorCode::SumVoltLowLevel1);
995        ck_and_add!(4, 7, ErrorCode::SumVoltLowLevel2);
996
997        ck_and_add!(5, 0, ErrorCode::ChargeTempHighLevel1);
998        ck_and_add!(5, 1, ErrorCode::ChargeTempHighLevel2);
999        ck_and_add!(5, 2, ErrorCode::ChargeTempLowLevel1);
1000        ck_and_add!(5, 3, ErrorCode::ChargeTempLowLevel2);
1001        ck_and_add!(5, 4, ErrorCode::DischargeTempHighLevel1);
1002        ck_and_add!(5, 5, ErrorCode::DischargeTempHighLevel2);
1003        ck_and_add!(5, 6, ErrorCode::DischargeTempLowLevel1);
1004        ck_and_add!(5, 7, ErrorCode::DischargeTempLowLevel2);
1005
1006        ck_and_add!(6, 1, ErrorCode::ChargeOvercurrentLevel2);
1007        ck_and_add!(6, 0, ErrorCode::ChargeOvercurrentLevel1);
1008        ck_and_add!(6, 2, ErrorCode::DischargeOvercurrentLevel1);
1009        ck_and_add!(6, 3, ErrorCode::DischargeOvercurrentLevel2);
1010        ck_and_add!(6, 4, ErrorCode::SocHighLevel1);
1011        ck_and_add!(6, 5, ErrorCode::SocHighLevel2);
1012        ck_and_add!(6, 6, ErrorCode::SocLowLevel1);
1013        ck_and_add!(6, 7, ErrorCode::SocLowLevel2);
1014
1015        ck_and_add!(7, 0, ErrorCode::DiffVoltLevel1);
1016        ck_and_add!(7, 1, ErrorCode::DiffVoltLevel2);
1017        ck_and_add!(7, 2, ErrorCode::DiffTempLevel1);
1018        ck_and_add!(7, 3, ErrorCode::DiffTempLevel2);
1019
1020        ck_and_add!(8, 0, ErrorCode::ChargeMosTempHighAlarm);
1021        ck_and_add!(8, 1, ErrorCode::DischargeMosTempHighAlarm);
1022        ck_and_add!(8, 2, ErrorCode::ChargeMosTempSensorErr);
1023        ck_and_add!(8, 3, ErrorCode::DischargeMosTempSensorErr);
1024        ck_and_add!(8, 4, ErrorCode::ChargeMosAdhesionErr);
1025        ck_and_add!(8, 5, ErrorCode::DischargeMosAdhesionErr);
1026        ck_and_add!(8, 6, ErrorCode::ChargeMosOpenCircuitErr);
1027        ck_and_add!(8, 7, ErrorCode::DischargeMosOpenCircuitErr);
1028
1029        ck_and_add!(9, 0, ErrorCode::AfeCollectChipErr);
1030        ck_and_add!(9, 1, ErrorCode::VoltageCollectDropped);
1031        ck_and_add!(9, 2, ErrorCode::CellTempSensorErr);
1032        ck_and_add!(9, 3, ErrorCode::EepromErr);
1033        ck_and_add!(9, 4, ErrorCode::RtcErr);
1034        ck_and_add!(9, 5, ErrorCode::PrechangeFailure);
1035        ck_and_add!(9, 6, ErrorCode::CommunicationFailure);
1036        ck_and_add!(9, 7, ErrorCode::InternalCommunicationFailure);
1037
1038        ck_and_add!(10, 0, ErrorCode::CurrentModuleFault);
1039        ck_and_add!(10, 1, ErrorCode::SumVoltageDetectFault);
1040        ck_and_add!(10, 2, ErrorCode::ShortCircuitProtectFault);
1041        ck_and_add!(10, 3, ErrorCode::LowVoltForbiddenChargeFault);
1042
1043        Ok(result)
1044    }
1045}
1046
1047/// Represents a command to enable or disable the discharging MOSFET.
1048pub struct SetDischargeMosfet;
1049
1050impl SetDischargeMosfet {
1051    /// Creates a request frame to set the state of the discharging MOSFET.
1052    ///
1053    /// # Arguments
1054    ///
1055    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
1056    /// * `enable` - `true` to enable the discharging MOSFET, `false` to disable it.
1057    ///
1058    /// # Returns
1059    ///
1060    /// A byte vector representing the request frame.
1061    ///
1062    /// This is a low-level function. Users might prefer client methods.
1063    pub fn request(address: Address, enable: bool) -> Vec<u8> {
1064        let mut tx_buffer = create_request_header(address, 0xD9);
1065        if enable {
1066            tx_buffer[4] = 0x01;
1067        }
1068        calc_crc_and_set(&mut tx_buffer);
1069        tx_buffer
1070    }
1071
1072    /// Expected size of the reply frame for a set discharge MOSFET command.
1073    /// The BMS typically echoes the command or sends a status.
1074    pub fn reply_size() -> usize {
1075        RX_BUFFER_LENGTH
1076    }
1077
1078    /// Decodes the response frame for a set discharge MOSFET command.
1079    /// This typically just validates the checksum and length, as the BMS response might not carry specific data.
1080    ///
1081    /// # Arguments
1082    ///
1083    /// * `rx_buffer` - The response frame received from the BMS.
1084    ///
1085    /// # Returns
1086    ///
1087    /// An empty `Result` if successful, or an `Error` if decoding fails.
1088    ///
1089    /// This is a low-level function. Users might prefer client methods.
1090    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
1091        validate_len(rx_buffer, Self::reply_size())?;
1092        validate_checksum(rx_buffer)
1093    }
1094}
1095
1096/// Represents a command to enable or disable the charging MOSFET.
1097pub struct SetChargeMosfet;
1098
1099impl SetChargeMosfet {
1100    /// Creates a request frame to set the state of the charging MOSFET.
1101    ///
1102    /// # Arguments
1103    ///
1104    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
1105    /// * `enable` - `true` to enable the charging MOSFET, `false` to disable it.
1106    ///
1107    /// # Returns
1108    ///
1109    /// A byte vector representing the request frame.
1110    ///
1111    /// This is a low-level function. Users might prefer client methods.
1112    pub fn request(address: Address, enable: bool) -> Vec<u8> {
1113        let mut tx_buffer = create_request_header(address, 0xDA);
1114        if enable {
1115            tx_buffer[4] = 0x01;
1116        }
1117        calc_crc_and_set(&mut tx_buffer);
1118        tx_buffer
1119    }
1120
1121    /// Expected size of the reply frame for a set charge MOSFET command.
1122    pub fn reply_size() -> usize {
1123        RX_BUFFER_LENGTH
1124    }
1125
1126    /// Decodes the response frame for a set charge MOSFET command.
1127    /// This typically just validates the checksum and length.
1128    ///
1129    /// # Arguments
1130    ///
1131    /// * `rx_buffer` - The response frame received from the BMS.
1132    ///
1133    /// # Returns
1134    ///
1135    /// An empty `Result` if successful, or an `Error` if decoding fails.
1136    ///
1137    /// This is a low-level function. Users might prefer client methods.
1138    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
1139        validate_len(rx_buffer, Self::reply_size())?;
1140        validate_checksum(rx_buffer)
1141    }
1142}
1143
1144/// Represents a command to set the State of Charge (SOC) percentage.
1145pub struct SetSoc;
1146
1147impl SetSoc {
1148    /// Creates a request frame to set the SOC percentage on the BMS.
1149    ///
1150    /// # Arguments
1151    ///
1152    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
1153    /// * `soc_percent` - The desired SOC percentage (0.0 to 100.0). Values outside this range will be clamped.
1154    ///
1155    /// # Returns
1156    ///
1157    /// A byte vector representing the request frame.
1158    ///
1159    /// This is a low-level function. Users might prefer client methods.
1160    pub fn request(address: Address, soc_percent: f32) -> Vec<u8> {
1161        let mut tx_buffer = create_request_header(address, 0x21);
1162        let value = {
1163            let val = (soc_percent * 10.0).round();
1164            if val > 1000.0 {
1165                // BMS expects value * 10, so 100.0% is 1000
1166                1000
1167            } else if val < 0.0 {
1168                0
1169            } else {
1170                val as u16
1171            }
1172        }
1173        .to_be_bytes();
1174        tx_buffer[10] = value[0]; // SOC high byte
1175        tx_buffer[11] = value[1]; // SOC low byte
1176        calc_crc_and_set(&mut tx_buffer);
1177        tx_buffer
1178    }
1179
1180    /// Expected size of the reply frame for a set SOC command.
1181    pub fn reply_size() -> usize {
1182        RX_BUFFER_LENGTH
1183    }
1184
1185    /// Decodes the response frame for a set SOC command.
1186    /// This typically just validates the checksum and length.
1187    ///
1188    /// # Arguments
1189    ///
1190    /// * `rx_buffer` - The response frame received from the BMS.
1191    ///
1192    /// # Returns
1193    ///
1194    /// An empty `Result` if successful, or an `Error` if decoding fails.
1195    ///
1196    /// This is a low-level function. Users might prefer client methods.
1197    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
1198        validate_len(rx_buffer, Self::reply_size())?;
1199        validate_checksum(rx_buffer)
1200    }
1201}
1202
1203/// Represents a command to reset the BMS.
1204pub struct BmsReset;
1205
1206impl BmsReset {
1207    /// Creates a request frame to reset the BMS.
1208    ///
1209    /// # Arguments
1210    ///
1211    /// * `address` - The address of the BMS (should be `Address::Host` when sending from host).
1212    ///
1213    /// # Returns
1214    ///
1215    /// A byte vector representing the request frame.
1216    ///
1217    /// This is a low-level function. Users might prefer client methods.
1218    pub fn request(address: Address) -> Vec<u8> {
1219        let mut tx_buffer = create_request_header(address, 0x00);
1220        calc_crc_and_set(&mut tx_buffer);
1221        tx_buffer
1222    }
1223
1224    /// Expected size of the reply frame for a BMS reset command.
1225    pub fn reply_size() -> usize {
1226        RX_BUFFER_LENGTH
1227    }
1228
1229    /// Decodes the response frame for a BMS reset command.
1230    /// This typically just validates the checksum and length.
1231    ///
1232    /// # Arguments
1233    ///
1234    /// * `rx_buffer` - The response frame received from the BMS.
1235    ///
1236    /// # Returns
1237    ///
1238    /// An empty `Result` if successful, or an `Error` if decoding fails.
1239    ///
1240    /// This is a low-level function. Users might prefer client methods.
1241    pub fn decode(rx_buffer: &[u8]) -> std::result::Result<(), Error> {
1242        validate_len(rx_buffer, Self::reply_size())?;
1243        validate_checksum(rx_buffer)
1244    }
1245}
1246
1247#[cfg(test)]
1248mod tests {
1249    use super::*;
1250
1251    // Helper function to calculate checksum for test data
1252    fn calculate_test_checksum(data: &[u8]) -> u8 {
1253        let mut checksum: u8 = 0;
1254        for byte in data.iter().take(data.len() - 1) {
1255            checksum = checksum.wrapping_add(*byte);
1256        }
1257        checksum
1258    }
1259
1260    #[test]
1261    fn test_calc_crc_valid() {
1262        let data1: [u8; 13] = [
1263            0xA5, 0x40, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1264        ]; // Placeholder CRC
1265        let expected_crc1 = calculate_test_checksum(&data1);
1266        assert_eq!(calc_crc(&data1), expected_crc1);
1267
1268        let data2: [u8; 13] = [
1269            0xA5, 0x40, 0x90, 0x08, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0x00,
1270        ]; // Placeholder CRC
1271        let expected_crc2 = calculate_test_checksum(&data2);
1272        assert_eq!(calc_crc(&data2), expected_crc2);
1273
1274        let data3: [u8; 13] = [
1275            0xA5, 0x01, 0x02, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
1276        ];
1277        let expected_crc3 = calculate_test_checksum(&data3);
1278        assert_eq!(calc_crc(&data3), expected_crc3);
1279    }
1280
1281    #[test]
1282    fn test_validate_checksum_valid() {
1283        let mut data: [u8; 13] = [
1284            0xA5, 0x40, 0x90, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x00,
1285        ];
1286        data[12] = calc_crc(&data); // Set correct CRC
1287        assert!(validate_checksum(&data).is_ok());
1288    }
1289
1290    #[test]
1291    fn test_validate_checksum_invalid() {
1292        let mut data: [u8; 13] = [
1293            0xA5, 0x40, 0x90, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x00,
1294        ];
1295        data[12] = calc_crc(&data).wrapping_add(1); // Set incorrect CRC
1296        let result = validate_checksum(&data);
1297        assert!(result.is_err());
1298        match result.err().unwrap() {
1299            Error::CheckSumError => {} // Expected error
1300            _ => panic!("Unexpected error type"),
1301        }
1302    }
1303
1304    #[test]
1305    fn test_status_decode_valid() {
1306        // cells = 16 (0x10), temp_sensors = 4 (0x04), charger_running = true (0x01), load_running = false (0x00)
1307        // states (rx_buffer[8]): DI1=false, DI2=true, DI3=false, DI4=true, DO1=false, DO2=true, DO3=false, DO4=true => 0b10101010 = 0xAA
1308        // cycles = 1234 (0x04D2)
1309        // CRC: 0xA5+0x40+0x94+0x08+0x10+0x04+0x01+0x00+0xAA+0x04+0xD2+0x00 = 790 = 0x0316 => 0x16
1310        let bytes: [u8; 13] = [
1311            0xA5, 0x40, 0x94, 0x08, 0x10, 0x04, 0x01, 0x00, 0xAA, 0x04, 0xD2, 0x00, 0x16,
1312        ];
1313        let expected_status = Status {
1314            cells: 16,
1315            temperature_sensors: 4,
1316            charger_running: true,
1317            load_running: false,
1318            states: IOState {
1319                di1: false, // bit 0 of 0xAA
1320                di2: true,  // bit 1 of 0xAA
1321                di3: false, // bit 2 of 0xAA
1322                di4: true,  // bit 3 of 0xAA
1323                do1: false, // bit 4 of 0xAA
1324                do2: true,  // bit 5 of 0xAA
1325                do3: false, // bit 6 of 0xAA
1326                do4: true,  // bit 7 of 0xAA
1327            },
1328            cycles: 1234,
1329        };
1330        match Status::decode(&bytes) {
1331            Ok(decoded) => {
1332                assert_eq!(decoded.cells, expected_status.cells);
1333                assert_eq!(
1334                    decoded.temperature_sensors,
1335                    expected_status.temperature_sensors
1336                );
1337                assert_eq!(decoded.charger_running, expected_status.charger_running);
1338                assert_eq!(decoded.load_running, expected_status.load_running);
1339                assert_eq!(decoded.states.di1, expected_status.states.di1);
1340                assert_eq!(decoded.states.di2, expected_status.states.di2);
1341                assert_eq!(decoded.states.di3, expected_status.states.di3);
1342                assert_eq!(decoded.states.di4, expected_status.states.di4);
1343                assert_eq!(decoded.states.do1, expected_status.states.do1);
1344                assert_eq!(decoded.states.do2, expected_status.states.do2);
1345                assert_eq!(decoded.states.do3, expected_status.states.do3);
1346                assert_eq!(decoded.states.do4, expected_status.states.do4);
1347                assert_eq!(decoded.cycles, expected_status.cycles);
1348            }
1349            Err(e) => panic!("Decoding failed: {:?}", e),
1350        }
1351    }
1352
1353    #[test]
1354    fn test_mosfet_status_decode_valid() {
1355        // mode = Charging (1), charging_mosfet = true (1), discharging_mosfet = false (0), bms_cycles = 150 (0x96), capacity_ah = 50.123Ah (50123 -> 0x0000C3CB)
1356        // CRC: 0xA5+0x40+0x93+0x08+0x01+0x01+0x00+0x96+0x00+0x00+0xC3+0xCB = 934 = 0x03A6 => 0xA6
1357        let bytes: [u8; 13] = [
1358            0xA5, 0x40, 0x93, 0x08, 0x01, 0x01, 0x00, 0x96, 0x00, 0x00, 0xC3, 0xCB, 0xA6,
1359        ];
1360        let expected_status = MosfetStatus {
1361            mode: MosfetMode::Charging,
1362            charging_mosfet: true,
1363            discharging_mosfet: false,
1364            bms_cycles: 150,
1365            capacity_ah: 50.123,
1366        };
1367        match MosfetStatus::decode(&bytes) {
1368            Ok(decoded) => {
1369                assert!(matches!(decoded.mode, MosfetMode::Charging));
1370                assert_eq!(decoded.charging_mosfet, expected_status.charging_mosfet);
1371                assert_eq!(
1372                    decoded.discharging_mosfet,
1373                    expected_status.discharging_mosfet
1374                );
1375                assert_eq!(decoded.bms_cycles, expected_status.bms_cycles);
1376                assert!((decoded.capacity_ah - expected_status.capacity_ah).abs() < f32::EPSILON);
1377            }
1378            Err(e) => panic!("Decoding failed: {:?}", e),
1379        }
1380    }
1381
1382    #[test]
1383    fn test_temperature_range_decode_valid() {
1384        // highest_temperature = 25C (0x41 with 40 offset), highest_sensor = 1, lowest_temperature = 10C (0x32 with 40 offset), lowest_sensor = 2
1385        // CRC: 0xA5+0x40+0x92+0x08+0x41+0x01+0x32+0x02+0x00+0x00+0x00+0x00 = 501 = 0x01F5 => 0xF5
1386        let bytes: [u8; 13] = [
1387            0xA5, 0x40, 0x92, 0x08, 0x41, 0x01, 0x32, 0x02, 0x00, 0x00, 0x00, 0x00, 0xF5,
1388        ];
1389        let expected_range = TemperatureRange {
1390            highest_temperature: 25,
1391            highest_sensor: 1,
1392            lowest_temperature: 10,
1393            lowest_sensor: 2,
1394        };
1395        match TemperatureRange::decode(&bytes) {
1396            Ok(decoded) => {
1397                assert_eq!(
1398                    decoded.highest_temperature,
1399                    expected_range.highest_temperature
1400                );
1401                assert_eq!(decoded.highest_sensor, expected_range.highest_sensor);
1402                assert_eq!(
1403                    decoded.lowest_temperature,
1404                    expected_range.lowest_temperature
1405                );
1406                assert_eq!(decoded.lowest_sensor, expected_range.lowest_sensor);
1407            }
1408            Err(e) => panic!("Decoding failed: {:?}", e),
1409        }
1410    }
1411
1412    #[test]
1413    fn test_cell_voltage_range_decode_valid() {
1414        // highest_voltage = 3.456V (0x0D80), highest_cell = 1, lowest_voltage = 3.123V (0x0C33), lowest_cell = 5
1415        // CRC: 0xA5+0x40+0x91+0x08+0x0D+0x80+0x01+0x0C+0x33+0x05+0x00+0x00 = 592 = 0x0250 => 0x50
1416        let bytes: [u8; 13] = [
1417            0xA5, 0x40, 0x91, 0x08, 0x0D, 0x80, 0x01, 0x0C, 0x33, 0x05, 0x00, 0x00, 0x50,
1418        ];
1419        let expected_range = CellVoltageRange {
1420            highest_voltage: 3.456,
1421            highest_cell: 1,
1422            lowest_voltage: 3.123,
1423            lowest_cell: 5,
1424        };
1425        match CellVoltageRange::decode(&bytes) {
1426            Ok(decoded) => {
1427                assert!(
1428                    (decoded.highest_voltage - expected_range.highest_voltage).abs() < f32::EPSILON
1429                );
1430                assert_eq!(decoded.highest_cell, expected_range.highest_cell);
1431                assert!(
1432                    (decoded.lowest_voltage - expected_range.lowest_voltage).abs() < f32::EPSILON
1433                );
1434                assert_eq!(decoded.lowest_cell, expected_range.lowest_cell);
1435            }
1436            Err(e) => panic!("Decoding failed: {:?}", e),
1437        }
1438    }
1439
1440    #[test]
1441    fn test_validate_len_valid() {
1442        let data: [u8; 13] = [0; 13];
1443        assert!(validate_len(&data, 13).is_ok());
1444    }
1445
1446    #[test]
1447    fn test_validate_len_valid_larger_buffer() {
1448        let data: [u8; 15] = [0; 15]; // Buffer is larger than required size
1449        assert!(validate_len(&data, 13).is_ok());
1450    }
1451
1452    #[test]
1453    fn test_validate_len_invalid() {
1454        let data: [u8; 12] = [0; 12];
1455        let result = validate_len(&data, 13);
1456        assert!(result.is_err());
1457        match result.err().unwrap() {
1458            Error::ReplySizeError => {} // Expected error
1459            _ => panic!("Unexpected error type"),
1460        }
1461    }
1462
1463    #[test]
1464    fn test_validate_len_empty() {
1465        let data: [u8; 0] = [];
1466        let result = validate_len(&data, 13);
1467        assert!(result.is_err());
1468        match result.err().unwrap() {
1469            Error::ReplySizeError => {} // Expected error
1470            _ => panic!("Unexpected error type"),
1471        }
1472    }
1473
1474    // Decode tests
1475    #[test]
1476    fn test_soc_decode_valid() {
1477        // total_voltage = 54.3V (0x021F), current = 2.5A (0x7549 with 30000 offset), soc_percent = 75.5% (0x02F3)
1478        // CRC: 0xA5+0x40+0x90+0x08+0x02+0x1F+0x00+0x00+0x75+0x49+0x02+0xF3 = 849 = 0x0351 => 0x51
1479        let bytes: [u8; 13] = [
1480            0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3, 0x51,
1481        ];
1482        let expected_soc = Soc {
1483            total_voltage: 54.3,
1484            current: 2.5,
1485            soc_percent: 75.5,
1486        };
1487        match Soc::decode(&bytes) {
1488            Ok(decoded) => {
1489                assert!((decoded.total_voltage - expected_soc.total_voltage).abs() < f32::EPSILON);
1490                assert!((decoded.current - expected_soc.current).abs() < f32::EPSILON);
1491                assert!((decoded.soc_percent - expected_soc.soc_percent).abs() < f32::EPSILON);
1492            }
1493            Err(e) => panic!("Decoding failed: {:?}", e),
1494        }
1495    }
1496
1497    #[test]
1498    fn test_soc_decode_negative_current() {
1499        // total_voltage = 50.0V (0x01F4), current = -10.0A (0x74CC from 29900, since (29900-30000)/10 = -10), soc_percent = 50.0% (0x01F4)
1500        // CRC: 0xA5+0x40+0x90+0x08+0x01+0xF4+0x00+0x00+0x74+0x9C+0x01+0xF4 = 801 = 0x0321 => 0xA7
1501        let bytes: [u8; 13] = [
1502            0xA5, 0x40, 0x90, 0x08, 0x01, 0xF4, 0x00, 0x00, 0x74, 0xCC, 0x01, 0xF4, 0xA7,
1503        ];
1504        let expected_soc = Soc {
1505            total_voltage: 50.0,
1506            current: -10.0,
1507            soc_percent: 50.0,
1508        };
1509        match Soc::decode(&bytes) {
1510            Ok(decoded) => {
1511                assert!((decoded.total_voltage - expected_soc.total_voltage).abs() < f32::EPSILON);
1512                assert!((decoded.current - expected_soc.current).abs() < f32::EPSILON);
1513                assert!((decoded.soc_percent - expected_soc.soc_percent).abs() < f32::EPSILON);
1514            }
1515            Err(e) => panic!("Decoding failed: {:?}", e),
1516        }
1517    }
1518
1519    #[test]
1520    fn test_soc_decode_invalid_checksum() {
1521        let bytes: [u8; 13] = [
1522            0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3, 0x52,
1523        ]; // Incorrect CRC (0x51 is correct)
1524        let result = Soc::decode(&bytes);
1525        assert!(result.is_err());
1526        match result.err().unwrap() {
1527            Error::CheckSumError => {} // Expected
1528            e => panic!("Unexpected error type: {:?}", e),
1529        }
1530    }
1531
1532    #[test]
1533    fn test_soc_decode_invalid_len() {
1534        let bytes: [u8; 12] = [
1535            0xA5, 0x40, 0x90, 0x08, 0x02, 0x1F, 0x00, 0x00, 0x75, 0x49, 0x02, 0xF3,
1536        ]; // Missing CRC byte
1537        let result = Soc::decode(&bytes);
1538        assert!(result.is_err());
1539        match result.err().unwrap() {
1540            Error::ReplySizeError => {} // Expected
1541            e => panic!("Unexpected error type: {:?}", e),
1542        }
1543    }
1544
1545    #[test]
1546    fn test_cell_voltages_decode_valid_multi_frame() {
1547        // n_cells = 4, so 2 frames.
1548        // Frame 1: Cell1=3.300V (0x0CE4), Cell2=3.301V (0x0CE5), Cell3=3.302V (0x0CE6)
1549        // Frame 1 Bytes: [0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x00, CRC1]
1550        // CRC1: 0xA5+0x40+0x95+0x08+0x01+0x0C+0xE4+0x0C+0xE5+0x0C+0xE6+0x00 = 1110 = 0x0456 => 0x56
1551        let frame1: [u8; 13] = [
1552            0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x00, 0x56,
1553        ];
1554
1555        // Frame 2: Cell4=3.303V (0x0CE7)
1556        // Frame 2 Bytes: [0xA5, 0x40, 0x95, 0x08, 0x02, 0x0C, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, CRC2]
1557        // CRC2: 0xA5+0x40+0x95+0x08+0x02+0x0C+0xE7+0x00+0x00+0x00+0x00+0x00 = 631 = 0x0277 => 0x77
1558        let frame2: [u8; 13] = [
1559            0xA5, 0x40, 0x95, 0x08, 0x02, 0x0C, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
1560        ];
1561
1562        let mut combined_bytes = Vec::new();
1563        combined_bytes.extend_from_slice(&frame1);
1564        combined_bytes.extend_from_slice(&frame2);
1565
1566        let expected_voltages = vec![3.300, 3.301, 3.302, 3.303];
1567
1568        match CellVoltages::decode(&combined_bytes, 4) {
1569            Ok(decoded) => {
1570                assert_eq!((*decoded).len(), expected_voltages.len());
1571                for (d, e) in (*decoded).iter().zip(expected_voltages.iter()) {
1572                    assert!((d - e).abs() < f32::EPSILON);
1573                }
1574            }
1575            Err(e) => panic!("Decoding failed: {:?}", e),
1576        }
1577    }
1578
1579    #[test]
1580    fn test_cell_voltages_decode_frame_out_of_order() {
1581        let frame1: [u8; 13] = [
1582            0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE4, 0x0C, 0xE5, 0x0C, 0xE6, 0x00, 0x56,
1583        ];
1584        let frame2_wrong_order: [u8; 13] = [
1585            0xA5, 0x40, 0x95, 0x08, 0x01, 0x0C, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75,
1586        ]; // Frame num 0x01, CRC for this data
1587
1588        let mut combined_bytes = Vec::new();
1589        combined_bytes.extend_from_slice(&frame1);
1590        combined_bytes.extend_from_slice(&frame2_wrong_order);
1591
1592        let result = CellVoltages::decode(&combined_bytes, 4);
1593        assert!(result.is_err());
1594        match result.err().unwrap() {
1595            Error::FrameNoError => {} // Expected
1596            e => panic!("Unexpected error type: {:?}", e),
1597        }
1598    }
1599
1600    #[test]
1601    fn test_cell_temperatures_decode_valid_multi_frame() {
1602        // n_sensors = 8, so 2 frames.
1603        // Frame 1: Sens1=20C(raw 60,0x3C), Sens2=21C(61,0x3D), Sens3=22C(62,0x3E), Sens4=23C(63,0x3F), Sens5=24C(64,0x40), Sens6=25C(65,0x41), Sens7=26C(66,0x42)
1604        // Frame 1 Bytes: [0xA5, 0x40, 0x96, 0x08, 0x01, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, CRC1]
1605        // CRC1: 0xA5+0x40+0x96+0x08+0x01+0x3C+0x3D+0x3E+0x3F+0x40+0x41+0x42 = 829 = 0x033D => 0x3D
1606        let frame1: [u8; 13] = [
1607            0xA5, 0x40, 0x96, 0x08, 0x01, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x3D,
1608        ];
1609
1610        // Frame 2: Sens8=27C(raw 67,0x43)
1611        // Frame 2 Bytes: [0xA5, 0x40, 0x96, 0x08, 0x02, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, CRC2]
1612        // CRC2: 0xA5+0x40+0x96+0x08+0x02+0x43+0x00+0x00+0x00+0x00+0x00+0x00 = 456 = 0x01C8 => 0xC8
1613        let frame2: [u8; 13] = [
1614            0xA5, 0x40, 0x96, 0x08, 0x02, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8,
1615        ];
1616
1617        let mut combined_bytes = Vec::new();
1618        combined_bytes.extend_from_slice(&frame1);
1619        combined_bytes.extend_from_slice(&frame2);
1620
1621        let expected_temperatures = vec![20, 21, 22, 23, 24, 25, 26, 27];
1622
1623        match CellTemperatures::decode(&combined_bytes, 8) {
1624            Ok(decoded) => {
1625                assert_eq!(decoded, expected_temperatures);
1626            }
1627            Err(e) => panic!("Decoding failed: {:?}", e),
1628        }
1629    }
1630
1631    #[test]
1632    fn test_cell_balance_state_decode_valid() {
1633        // n_cells = 16. Cells 0, 8, 15 are balancing.
1634        // Data bytes: [0x01, 0x81, 0x00, 0x00, 0x00, 0x00]
1635        // Frame: [0xA5, 0x40, 0x97, 0x08, 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, CRC]
1636        // CRC: 0xA5+0x40+0x97+0x08+0x01+0x81+0x00+0x00+0x00+0x00+0x00+0x00 = 518 = 0x0206 => 0x06
1637        let bytes: [u8; 13] = [
1638            0xA5, 0x40, 0x97, 0x08, 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
1639        ];
1640
1641        let mut expected_state = vec![false; 16];
1642        expected_state[0] = true; // Cell 0
1643        expected_state[8] = true; // Cell 8
1644        expected_state[15] = true; // Cell 15
1645
1646        match CellBalanceState::decode(&bytes, 16) {
1647            Ok(decoded) => {
1648                assert_eq!(decoded, expected_state);
1649            }
1650            Err(e) => panic!("Decoding failed: {:?}", e),
1651        }
1652    }
1653
1654    #[test]
1655    fn test_cell_balance_state_decode_n_cells_less_than_byte_boundary() {
1656        // n_cells = 5. Cell 0 and Cell 4 are balancing.
1657        // Data byte 0: 0b00010001 = 0x11
1658        // Frame: [0xA5, 0x40, 0x97, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, CRC]
1659        // CRC: 0xA5+0x40+0x97+0x08+0x11+0x00+0x00+0x00+0x00+0x00+0x00+0x00 = 463 = 0x01CF => 0x95
1660        let bytes: [u8; 13] = [
1661            0xA5, 0x40, 0x97, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95,
1662        ];
1663
1664        let mut expected_state = vec![false; 5];
1665        expected_state[0] = true; // Cell 0
1666        expected_state[4] = true; // Cell 4
1667
1668        match CellBalanceState::decode(&bytes, 5) {
1669            Ok(decoded) => {
1670                assert_eq!(decoded, expected_state);
1671            }
1672            Err(e) => panic!("Decoding failed: {:?}", e),
1673        }
1674    }
1675
1676    #[test]
1677    fn test_error_code_decode_valid() {
1678        // Errors: CellVoltHighLevel1 (byte 4, bit 0), ChargeTempLowLevel2 (byte 5, bit 3), SocHighLevel1 (byte 6, bit 4), DiffTempLevel2 (byte 7, bit 3), AfeCollectChipErr (byte 9, bit 0)
1679        // rx_buffer[4] = 0x01
1680        // rx_buffer[5] = 0x08
1681        // rx_buffer[6] = 0x10
1682        // rx_buffer[7] = 0x08
1683        // rx_buffer[8] = 0x00
1684        // rx_buffer[9] = 0x01
1685        // rx_buffer[10]= 0x00
1686        // rx_buffer[11]= 0x00
1687        // Frame: [0xA5, 0x40, 0x98, 0x08, 0x01, 0x08, 0x10, 0x08, 0x00, 0x01, 0x00, 0x00, CRC]
1688        // CRC: 0xA5+0x40+0x98+0x08+0x01+0x08+0x10+0x08+0x00+0x01+0x00+0x00 = 423 = 0x01A7 => 0xA7
1689        let bytes: [u8; 13] = [
1690            0xA5, 0x40, 0x98, 0x08, 0x01, 0x08, 0x10, 0x08, 0x00, 0x01, 0x00, 0x00, 0xA7,
1691        ];
1692
1693        let expected_errors = vec![
1694            ErrorCode::CellVoltHighLevel1,
1695            ErrorCode::ChargeTempLowLevel2,
1696            ErrorCode::SocHighLevel1,
1697            ErrorCode::DiffTempLevel2,
1698            ErrorCode::AfeCollectChipErr,
1699        ];
1700
1701        match ErrorCode::decode(&bytes) {
1702            Ok(decoded) => {
1703                assert_eq!(decoded.len(), expected_errors.len());
1704                for err in expected_errors {
1705                    assert!(decoded.contains(&err), "Missing error: {:?}", err);
1706                }
1707            }
1708            Err(e) => panic!("Decoding failed: {:?}", e),
1709        }
1710    }
1711
1712    #[test]
1713    fn test_error_code_decode_no_errors() {
1714        // Frame: [0xA5, 0x40, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, CRC]
1715        // CRC: 0xA5+0x40+0x98+0x08+0x00+0x00+0x00+0x00+0x00+0x00+0x00+0x00 = 415 = 0x019F => 0x9F
1716        let bytes: [u8; 13] = [
1717            0xA5, 0x40, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
1718        ];
1719
1720        match ErrorCode::decode(&bytes) {
1721            Ok(decoded) => {
1722                assert!(decoded.is_empty());
1723            }
1724            Err(e) => panic!("Decoding failed: {:?}", e),
1725        }
1726    }
1727
1728    // Request encoding tests
1729    #[test]
1730    fn test_soc_request() {
1731        let expected_frame: [u8; 13] = [
1732            0xA5, 0x40, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D,
1733        ];
1734        assert_eq!(Soc::request(Address::Host), expected_frame);
1735    }
1736
1737    #[test]
1738    fn test_cell_voltage_range_request() {
1739        // CMD = 0x91
1740        // CRC = 0xA5+0x40+0x91+0x08 = 382 = 0x017E => 0x7E
1741        let expected_frame: [u8; 13] = [
1742            0xA5, 0x40, 0x91, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E,
1743        ];
1744        assert_eq!(CellVoltageRange::request(Address::Host), expected_frame);
1745    }
1746
1747    #[test]
1748    fn test_temperature_range_request() {
1749        // CMD = 0x92
1750        // CRC = 0xA5+0x40+0x92+0x08 = 383 = 0x017F => 0x7F
1751        let expected_frame: [u8; 13] = [
1752            0xA5, 0x40, 0x92, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
1753        ];
1754        assert_eq!(TemperatureRange::request(Address::Host), expected_frame);
1755    }
1756
1757    #[test]
1758    fn test_mosfet_status_request() {
1759        // CMD = 0x93
1760        // CRC = 0xA5+0x40+0x93+0x08 = 384 = 0x0180 => 0x80
1761        let expected_frame: [u8; 13] = [
1762            0xA5, 0x40, 0x93, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
1763        ];
1764        assert_eq!(MosfetStatus::request(Address::Host), expected_frame);
1765    }
1766
1767    #[test]
1768    fn test_status_request() {
1769        // CMD = 0x94
1770        // CRC = 0xA5+0x40+0x94+0x08 = 385 = 0x0181 => 0x81
1771        let expected_frame: [u8; 13] = [
1772            0xA5, 0x40, 0x94, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
1773        ];
1774        assert_eq!(Status::request(Address::Host), expected_frame);
1775    }
1776
1777    #[test]
1778    fn test_cell_voltages_request() {
1779        // CMD = 0x95
1780        // CRC = 0xA5+0x40+0x95+0x08 = 386 = 0x0182 => 0x82
1781        let expected_frame: [u8; 13] = [
1782            0xA5, 0x40, 0x95, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,
1783        ];
1784        assert_eq!(CellVoltages::request(Address::Host), expected_frame);
1785    }
1786
1787    #[test]
1788    fn test_cell_temperatures_request() {
1789        // CMD = 0x96
1790        // CRC = 0xA5+0x40+0x96+0x08 = 387 = 0x0183 => 0x83
1791        let expected_frame: [u8; 13] = [
1792            0xA5, 0x40, 0x96, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83,
1793        ];
1794        assert_eq!(CellTemperatures::request(Address::Host), expected_frame);
1795    }
1796
1797    #[test]
1798    fn test_cell_balance_state_request() {
1799        // CMD = 0x97
1800        // CRC = 0xA5+0x40+0x97+0x08 = 388 = 0x0184 => 0x84
1801        let expected_frame: [u8; 13] = [
1802            0xA5, 0x40, 0x97, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84,
1803        ];
1804        assert_eq!(CellBalanceState::request(Address::Host), expected_frame);
1805    }
1806
1807    #[test]
1808    fn test_error_code_request() {
1809        // CMD = 0x98
1810        // CRC = 0xA5+0x40+0x98+0x08 = 389 = 0x0185 => 0x85
1811        let expected_frame: [u8; 13] = [
1812            0xA5, 0x40, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
1813        ];
1814        assert_eq!(ErrorCode::request(Address::Host), expected_frame);
1815    }
1816
1817    #[test]
1818    fn test_set_discharge_mosfet_request_enable() {
1819        // CMD = 0xD9, Data[0] = 0x01
1820        // CRC = 0xA5+0x40+0xD9+0x08+0x01 = 165+64+217+8+1 = 455 = 0x01C7 => 0xC7
1821        let expected_frame: [u8; 13] = [
1822            0xA5, 0x40, 0xD9, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7,
1823        ];
1824        assert_eq!(
1825            SetDischargeMosfet::request(Address::Host, true),
1826            expected_frame
1827        );
1828    }
1829
1830    #[test]
1831    fn test_set_discharge_mosfet_request_disable() {
1832        // CMD = 0xD9, Data[0] = 0x00
1833        // CRC = 0xA5+0x40+0xD9+0x08+0x00 = 165+64+217+8+0 = 454 = 0x01C6 => 0xC6
1834        let expected_frame: [u8; 13] = [
1835            0xA5, 0x40, 0xD9, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6,
1836        ];
1837        assert_eq!(
1838            SetDischargeMosfet::request(Address::Host, false),
1839            expected_frame
1840        );
1841    }
1842
1843    #[test]
1844    fn test_set_charge_mosfet_request_enable() {
1845        // CMD = 0xDA, Data[0] = 0x01
1846        // CRC = 0xA5+0x40+0xDA+0x08+0x01 = 165+64+218+8+1 = 456 = 0x01C8 => 0xC8
1847        let expected_frame: [u8; 13] = [
1848            0xA5, 0x40, 0xDA, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8,
1849        ];
1850        assert_eq!(
1851            SetChargeMosfet::request(Address::Host, true),
1852            expected_frame
1853        );
1854    }
1855
1856    #[test]
1857    fn test_set_soc_request() {
1858        // CMD = 0x21, SOC = 80.5% => 805 => 0x0325. Data[6]=0x03, Data[7]=0x25
1859        // Frame: A5 40 21 08 00 00 00 00 00 00 03 25 CRC
1860        // CRC: 0xA5+0x40+0x21+0x08+0x00+0x00+0x00+0x00+0x00+0x00+0x03+0x25 = 165+64+33+8+0+0+0+0+0+0+3+37 = 310 = 0x0136 => 0x36
1861        let expected_frame: [u8; 13] = [
1862            0xA5, 0x40, 0x21, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x25, 0x36,
1863        ];
1864        assert_eq!(SetSoc::request(Address::Host, 80.5), expected_frame);
1865    }
1866
1867    #[test]
1868    fn test_bms_reset_request() {
1869        // CMD = 0x00
1870        // CRC = 0xA5+0x40+0x00+0x08 = 165+64+0+8 = 237 = 0xED
1871        let expected_frame: [u8; 13] = [
1872            0xA5, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED,
1873        ];
1874        assert_eq!(BmsReset::request(Address::Host), expected_frame);
1875    }
1876}