Skip to main content

rfm69_async/
packet.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2
3use heapless::Vec;
4
5use crate::address::Address;
6use crate::flags::Flags;
7
8#[derive(Debug)]
9pub enum PacketError {
10    DataTooLong,
11    DataTooShort,
12}
13
14/// Packet that can be sent and received
15#[derive(Debug, Clone)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct Packet {
18    pub src: Address,
19    pub dst: Address,
20    pub flags: Flags,
21    pub data: Vec<u8, 61>,
22    pub rssi: Option<i16>,
23}
24
25impl Packet {
26    /// this is without the length byte itself
27    const MIN_VALID_PACKET_LEN: u8 = 3;
28
29    const MAX_PAYLOAD_DATA_LENGTH: usize = 61;
30
31    pub fn new(src: Address, dst: Address, flags: Flags, data: &[u8]) -> Result<Packet, PacketError> {
32        if data.len() > Self::MAX_PAYLOAD_DATA_LENGTH {
33            return Err(PacketError::DataTooLong);
34        }
35        Ok(Self {
36            src,
37            dst,
38            flags,
39            data: Vec::from_slice(data).unwrap(),
40            rssi: None,
41        })
42    }
43
44    pub fn from_rx_data(len: u8, raw: &[u8], rssi: i16) -> Result<Packet, PacketError> {
45        if len < Self::MIN_VALID_PACKET_LEN {
46            return Err(PacketError::DataTooShort);
47        }
48        Ok(Self {
49            src: Address::from_u8(raw[0]),
50            dst: Address::from_u8(raw[1]),
51            flags: Flags::from_u8(raw[2]),
52            data: Vec::from_slice(&raw[3..len as usize]).unwrap(),
53            rssi: Some(rssi),
54        })
55    }
56
57    /// Converts packet to byte slice for the fifo
58    ///
59    /// The returned length is the amount of bytes written into the given
60    /// array.
61    /// # Arguments
62    /// * `raw` - This array is filled
63    pub(crate) fn to_slice(&self, raw: &mut [u8; 65]) -> Result<u8, PacketError> {
64        // Length of the data inside the fifo (excluding the length itself)
65        let fifo_len = self.data.len() as u8 + Self::MIN_VALID_PACKET_LEN;
66
67        raw[0] = fifo_len;
68        raw[1] = self.src.as_u8();
69        raw[2] = self.dst.as_u8();
70        raw[3] = self.flags.as_u8();
71        raw[4..4 + self.data.len()].copy_from_slice(self.data.as_slice());
72        Ok(fifo_len + 1)
73    }
74
75    pub fn is_ack(&self) -> bool {
76        matches!(self.flags, Flags::Ack(_))
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn new_accepts_empty_payload() {
86        let p = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &[]).unwrap();
87        assert_eq!(p.data.len(), 0);
88        assert_eq!(p.rssi, None);
89    }
90
91    #[test]
92    fn new_accepts_max_payload() {
93        let payload = [0xAA; 61];
94        let p = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &payload).unwrap();
95        assert_eq!(p.data.len(), 61);
96        assert_eq!(p.data.as_slice(), &payload);
97    }
98
99    #[test]
100    fn new_rejects_oversized_payload() {
101        let payload = [0; 62];
102        assert!(matches!(
103            Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &payload),
104            Err(PacketError::DataTooLong)
105        ));
106    }
107
108    #[test]
109    fn from_rx_data_rejects_len_below_header() {
110        // A wire packet must be at least 3 bytes (src/dst/flags) excluding the
111        // length byte, so anything less is malformed.
112        for len in 0..3u8 {
113            assert!(matches!(
114                Packet::from_rx_data(len, &[0; 4], -100),
115                Err(PacketError::DataTooShort)
116            ));
117        }
118    }
119
120    #[test]
121    fn from_rx_data_parses_header_only() {
122        // src=42, dst=70, flags=Ack(0) (wire byte 1), no payload, len=3
123        let raw = [42, 70, 1];
124        let p = Packet::from_rx_data(3, &raw, -42).unwrap();
125        assert_eq!(p.src, Address::Unicast(42));
126        assert_eq!(p.dst, Address::Unicast(70));
127        assert_eq!(p.flags, Flags::Ack(0));
128        assert_eq!(p.data.len(), 0);
129        assert_eq!(p.rssi, Some(-42));
130    }
131
132    #[test]
133    fn from_rx_data_parses_payload_and_captures_rssi() {
134        // src=1, dst=255 (broadcast), flags=None, payload=[0xDE, 0xAD]
135        let raw = [1, 255, 0, 0xDE, 0xAD];
136        let p = Packet::from_rx_data(5, &raw, -91).unwrap();
137        assert_eq!(p.src, Address::Unicast(1));
138        assert_eq!(p.dst, Address::Broadcast);
139        assert_eq!(p.flags, Flags::None);
140        assert_eq!(p.data.as_slice(), &[0xDE, 0xAD]);
141        assert_eq!(p.rssi, Some(-91));
142    }
143
144    #[test]
145    fn to_slice_serializes_header_only() {
146        let p = Packet::new(Address::Unicast(42), Address::Unicast(70), Flags::None, &[]).unwrap();
147        let mut buf = [0u8; 65];
148        let total = p.to_slice(&mut buf).unwrap();
149        // fifo_len = 3 (src/dst/flags), total written = fifo_len + 1
150        assert_eq!(total, 4);
151        assert_eq!(&buf[..4], &[3, 42, 70, 0]);
152    }
153
154    #[test]
155    fn to_slice_serializes_payload_and_returns_correct_length() {
156        let payload = [0x01, 0x02, 0x03];
157        let p = Packet::new(
158            Address::Unicast(42),
159            Address::Broadcast,
160            Flags::Ack(2), // wire byte 3
161            &payload,
162        )
163        .unwrap();
164        let mut buf = [0u8; 65];
165        let total = p.to_slice(&mut buf).unwrap();
166        // fifo_len excludes itself: 3 header + 3 payload = 6
167        assert_eq!(total, 7);
168        assert_eq!(&buf[..7], &[6, 42, 255, 3, 0x01, 0x02, 0x03]);
169    }
170
171    #[test]
172    fn to_slice_then_from_rx_data_roundtrips() {
173        let payload = [0xCA, 0xFE, 0xBA, 0xBE];
174        let original = Packet::new(Address::Unicast(7), Address::Unicast(11), Flags::Ack(3), &payload).unwrap();
175        let mut buf = [0u8; 65];
176        let total = original.to_slice(&mut buf).unwrap();
177
178        // to_slice writes [fifo_len, src, dst, flags, payload...]; from_rx_data
179        // consumes src/dst/flags/payload and takes fifo_len out-of-band.
180        let len_byte = buf[0];
181        let parsed = Packet::from_rx_data(len_byte, &buf[1..total as usize], -55).unwrap();
182        assert_eq!(parsed.src, original.src);
183        assert_eq!(parsed.dst, original.dst);
184        assert_eq!(parsed.flags, original.flags);
185        assert_eq!(parsed.data.as_slice(), original.data.as_slice());
186        assert_eq!(parsed.rssi, Some(-55));
187    }
188
189    #[test]
190    fn is_ack_distinguishes_flags() {
191        let none = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &[]).unwrap();
192        let ack0 = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::Ack(0), &[]).unwrap();
193        let ack3 = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::Ack(3), &[]).unwrap();
194        assert!(!none.is_ack());
195        assert!(ack0.is_ack());
196        assert!(ack3.is_ack());
197    }
198}