1#![no_std]
9
10use core::error::Error;
11use core::fmt;
12
13#[cfg(feature = "defmt")]
14use defmt::Format;
15
16#[derive(Debug, Clone, Copy)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct IbusPacket {
19 channels: [u16; 14],
20}
21
22impl IbusPacket {
23 pub fn try_from_bytes(bytes: &[u8; 32]) -> Result<Self, ParsingError> {
30 if bytes[0] != 0x20 || bytes[1] != 0x40 {
31 return Err(ParsingError::InvalidPacket);
32 }
33
34 let mut channels = [0u16; 14];
35
36 let mut channels_iter = bytes[2..30].iter();
37 let mut channel_sum = 0u16;
38 for idx in 0..14 {
39 let low_byte = *channels_iter.next().unwrap();
40 let high_byte = *channels_iter.next().unwrap();
41 let channel = ((high_byte as u16) << 8) | low_byte as u16;
42 channel_sum += low_byte as u16 + high_byte as u16;
43 channels[idx] = channel;
44 }
45
46 channel_sum += bytes[1] as u16 + bytes[0] as u16;
47 let calculated_checksum: u16 = (0xFFFF as u16) - channel_sum;
48 let actual_checksum = ((bytes[31] as u16) << 8) | bytes[30] as u16;
49 if calculated_checksum == actual_checksum {
50 Ok(IbusPacket { channels })
51 } else {
52 return Err(ParsingError::FailsChecksum);
53 }
54 }
55
56 pub fn get_channel(&self, number: usize) -> Option<&u16> {
61 self.channels.get(number - 1)
62 }
63 pub fn get_all_channels(&self) -> [u16; 14] {
64 self.channels
65 }
66}
67
68#[derive(Debug, Clone, Copy)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70pub enum ParsingError {
71 InvalidPacket,
72 FailsChecksum,
73}
74
75impl Error for ParsingError {}
76
77impl fmt::Display for ParsingError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 &ParsingError::InvalidPacket => write!(f, "Parsing Error: Packet not valid")?,
81 &ParsingError::FailsChecksum => write!(
82 f,
83 "Parsing Error: Packet fails checksum and should not be used"
84 )?,
85 }
86
87 Ok(())
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn works_with_correct_packet() {
97 let data: [u8; 32] = [
98 0x20, 0x40, 0xDB, 0x05, 0xDC, 0x05, 0x54, 0x05, 0xDC, 0x05, 0xE8, 0x03, 0xD0, 0x07,
99 0xD2, 0x05, 0xE8, 0x03, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05,
100 0xDC, 0x05, 0xDA, 0xF3,
101 ];
102
103 let packet = IbusPacket::try_from_bytes(&data).expect("Should be valid packet");
104
105 assert_eq!(*packet.get_channel(3).unwrap(), 1364u16);
106 }
107
108 #[test]
109 fn fails_invalid_packet() {
110 let data: [u8; 32] = [0x02; 32];
111
112 let packet = IbusPacket::try_from_bytes(&data);
113
114 assert!(matches!(packet, Err(ParsingError::InvalidPacket)));
115 }
116
117 #[test]
118 fn fails_bad_checksum() {
119 let data: [u8; 32] = [
120 0x20, 0x40, 0xDB, 0x05, 0xDC, 0x05, 0x54, 0x05, 0xDC, 0x05, 0xE8, 0x03, 0xD0, 0x07,
121 0xD2, 0x05, 0xE8, 0x03, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05, 0xDC, 0x05,
122 0xDC, 0x05, 0xDA, 0xFF,
123 ];
124
125 let packet = IbusPacket::try_from_bytes(&data);
126
127 assert!(matches!(packet, Err(ParsingError::FailsChecksum)));
128 }
129}