Skip to main content

donglora_protocol/
chip_id.rs

1//! Radio chip identifier enum from `PROTOCOL.md §8`.
2//!
3//! Reported by the device in `GET_INFO.radio_chip_id` so the host can
4//! make chip-specific decisions (bandwidth range, SF range, TX power
5//! range, modulation support) without having to infer them from the
6//! bitmap fields alone.
7
8/// Radio chip identifier (u16, little-endian on the wire).
9///
10/// Values are assigned by `PROTOCOL.md §8`. Ranges are grouped by chip
11/// family so future additions slot in next to their siblings.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[repr(u16)]
15pub enum RadioChipId {
16    /// Firmware could not identify its radio. Hosts should abort: the
17    /// device will not function.
18    Unknown = 0x0000,
19
20    // Gen 2 sub-GHz
21    Sx1261 = 0x0001,
22    Sx1262 = 0x0002,
23    Sx1268 = 0x0003,
24    Llcc68 = 0x0004,
25
26    // Gen 1 sub-GHz
27    Sx1272 = 0x0010,
28    Sx1276 = 0x0011,
29    Sx1277 = 0x0012,
30    Sx1278 = 0x0013,
31    Sx1279 = 0x0014,
32
33    // Gen 2 2.4 GHz
34    Sx1280 = 0x0020,
35    Sx1281 = 0x0021,
36
37    // Gen 3 multi-band
38    Lr1110 = 0x0030,
39    Lr1120 = 0x0031,
40    Lr1121 = 0x0032,
41
42    // Gen 4 multi-band
43    Lr2021 = 0x0040,
44}
45
46impl RadioChipId {
47    /// Wire-form u16. Use this when encoding `GET_INFO`.
48    pub const fn as_u16(self) -> u16 {
49        self as u16
50    }
51
52    /// Parse a wire u16 into a known chip. Returns `None` for unassigned
53    /// values; hosts receiving an unknown value should treat the device
54    /// as unusable.
55    pub const fn from_u16(v: u16) -> Option<Self> {
56        Some(match v {
57            0x0000 => Self::Unknown,
58            0x0001 => Self::Sx1261,
59            0x0002 => Self::Sx1262,
60            0x0003 => Self::Sx1268,
61            0x0004 => Self::Llcc68,
62            0x0010 => Self::Sx1272,
63            0x0011 => Self::Sx1276,
64            0x0012 => Self::Sx1277,
65            0x0013 => Self::Sx1278,
66            0x0014 => Self::Sx1279,
67            0x0020 => Self::Sx1280,
68            0x0021 => Self::Sx1281,
69            0x0030 => Self::Lr1110,
70            0x0031 => Self::Lr1120,
71            0x0032 => Self::Lr1121,
72            0x0040 => Self::Lr2021,
73            _ => return None,
74        })
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn all_assigned_values_roundtrip() {
84        let all = [
85            RadioChipId::Unknown,
86            RadioChipId::Sx1261,
87            RadioChipId::Sx1262,
88            RadioChipId::Sx1268,
89            RadioChipId::Llcc68,
90            RadioChipId::Sx1272,
91            RadioChipId::Sx1276,
92            RadioChipId::Sx1277,
93            RadioChipId::Sx1278,
94            RadioChipId::Sx1279,
95            RadioChipId::Sx1280,
96            RadioChipId::Sx1281,
97            RadioChipId::Lr1110,
98            RadioChipId::Lr1120,
99            RadioChipId::Lr1121,
100            RadioChipId::Lr2021,
101        ];
102        for id in all {
103            assert_eq!(RadioChipId::from_u16(id.as_u16()), Some(id));
104        }
105    }
106
107    #[test]
108    fn unassigned_values_reject() {
109        for bad in [0x0005u16, 0x0015, 0x0022, 0x0033, 0x0041, 0xFFFF] {
110            assert_eq!(RadioChipId::from_u16(bad), None);
111        }
112    }
113
114    #[test]
115    fn canonical_values_from_spec() {
116        // Direct quote from §8 — any renumbering breaks the wire contract.
117        assert_eq!(RadioChipId::Sx1262.as_u16(), 0x0002);
118        assert_eq!(RadioChipId::Llcc68.as_u16(), 0x0004);
119        assert_eq!(RadioChipId::Sx1280.as_u16(), 0x0020);
120        assert_eq!(RadioChipId::Lr2021.as_u16(), 0x0040);
121    }
122}