uf_crsf/packets/
temp.rs

1use crate::packets::CrsfPacket;
2use crate::packets::PacketType;
3use crate::CrsfParsingError;
4use heapless::Vec;
5
6/// Represents a Temperature packet.
7///
8/// Used to transmit temperature telemetry data from the vehicle to the transmitter.
9/// This frame can be used to report temperature readings from various sources on the vehicle,
10/// such as motors, ESCs, or the environment.
11#[derive(Clone, Debug, PartialEq)]
12pub struct Temp {
13    /// Identifies the source of the temperature data (e.g., 0 = FC, 1 = Ambient, etc.).
14    pub temp_source_id: u8,
15    /// Up to 20 temperature values in deci-degrees Celsius (e.g., 250 = 25.0°C).
16    temperatures: Vec<i16, 20>,
17}
18
19impl Temp {
20    /// Creates a new Temp packet from a slice of temperature values.
21    ///
22    /// The number of temperature values must be 20 or less.
23    pub fn new(temp_source_id: u8, temperatures: &[i16]) -> Result<Self, CrsfParsingError> {
24        if temperatures.len() > 20 {
25            return Err(CrsfParsingError::InvalidPayloadLength);
26        }
27        let mut temps = Vec::new();
28        temps
29            .extend_from_slice(temperatures)
30            .map_err(|_| CrsfParsingError::InvalidPayloadLength)?;
31        Ok(Self {
32            temp_source_id,
33            temperatures: temps,
34        })
35    }
36
37    /// Returns the temperature values as a slice.
38    pub fn temperatures(&self) -> &[i16] {
39        &self.temperatures
40    }
41}
42
43#[cfg(feature = "defmt")]
44impl defmt::Format for Temp {
45    fn format(&self, fmt: defmt::Formatter) {
46        defmt::write!(
47            fmt,
48            "Temp {{ temp_source_id: {}, temperatures: {} }}",
49            self.temp_source_id,
50            self.temperatures(),
51        )
52    }
53}
54
55impl CrsfPacket for Temp {
56    const PACKET_TYPE: PacketType = PacketType::Temp;
57    const MIN_PAYLOAD_SIZE: usize = 3;
58
59    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError> {
60        let required_len = 1 + self.temperatures.len() * 2;
61        if buffer.len() < required_len {
62            return Err(CrsfParsingError::BufferOverflow);
63        }
64        buffer[0] = self.temp_source_id;
65        let mut i = 1;
66        for &temp in self.temperatures() {
67            let bytes = temp.to_be_bytes();
68            buffer[i..i + 2].copy_from_slice(&bytes);
69            i += 2;
70        }
71        Ok(i)
72    }
73
74    fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError> {
75        if data.len() < Self::MIN_PAYLOAD_SIZE {
76            return Err(CrsfParsingError::InvalidPayloadLength);
77        }
78
79        let temp_source_id = data[0];
80        let temperatures: Vec<i16, 20> = data[1..]
81            .chunks_exact(2)
82            .map(|chunk| {
83                let bytes = [chunk[0], chunk[1]];
84                i16::from_be_bytes(bytes)
85            })
86            .collect();
87
88        Ok(Self {
89            temp_source_id,
90            temperatures,
91        })
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_temp_to_bytes() {
101        let temperatures = [250, -50];
102        let temp = Temp::new(1, &temperatures).unwrap();
103
104        let mut buffer = [0u8; 60];
105        let len = temp.to_bytes(&mut buffer).unwrap();
106
107        let expected_bytes: [u8; 5] = [
108            1, // Source ID
109            0x00, 0xfa, // 250
110            0xff, 0xce, // -50
111        ];
112
113        assert_eq!(len, 5);
114        assert_eq!(&buffer[..len], &expected_bytes);
115    }
116
117    #[test]
118    fn test_temp_from_bytes() {
119        let data: [u8; 5] = [
120            1, // Source ID
121            0x00, 0xfa, // 250
122            0xff, 0xce, // -50
123        ];
124
125        let temp = Temp::from_bytes(&data).unwrap();
126
127        let expected_temperatures = [250, -50];
128        assert_eq!(temp.temp_source_id, 1);
129        assert_eq!(temp.temperatures(), &expected_temperatures);
130    }
131
132    #[test]
133    fn test_temp_round_trip() {
134        let temperatures = [1234, -5678];
135        let temp = Temp::new(2, &temperatures).unwrap();
136
137        let mut buffer = [0u8; 60];
138        let len = temp.to_bytes(&mut buffer).unwrap();
139
140        let round_trip_temp = Temp::from_bytes(&buffer[..len]).unwrap();
141
142        assert_eq!(temp, round_trip_temp);
143    }
144
145    #[test]
146    fn test_edge_cases() {
147        let temperatures = [0, 32767, -32768];
148        let temp = Temp::new(3, &temperatures).unwrap();
149
150        let mut buffer = [0u8; 60];
151        let len = temp.to_bytes(&mut buffer).unwrap();
152        let round_trip_temp = Temp::from_bytes(&buffer[..len]).unwrap();
153        assert_eq!(temp, round_trip_temp);
154    }
155}