umsh_radio_loraphy/
temperature.rs

1//! Optional temperature acquisition performed by the radio owner in standby.
2
3use lora_phy::{
4    lr1110::{Lr1110, Lr1110Variant},
5    mod_traits::{InterfaceVariant, RadioKind},
6};
7
8/// A chip-specific acquisition hook. The runner serializes it with RX/TX,
9/// restores RX or sleep afterward, and never cancels an SPI transaction.
10pub trait TemperatureSensor<RK: RadioKind> {
11    const SUPPORTED: bool;
12    /// Tenths of a kelvin, or unavailable. Called awake and in standby,
13    /// with cold-start initialization (including TCXO setup) completed.
14    async fn sample(&mut self, radio: &mut RK) -> Option<u16>;
15}
16
17pub struct NoTemperature;
18impl<RK: RadioKind> TemperatureSensor<RK> for NoTemperature {
19    const SUPPORTED: bool = false;
20    async fn sample(&mut self, _: &mut RK) -> Option<u16> {
21        None
22    }
23}
24
25pub struct Lr1110Temperature;
26impl<SPI, IV, C> TemperatureSensor<Lr1110<SPI, IV, C>> for Lr1110Temperature
27where
28    SPI: embedded_hal_async::spi::SpiDevice<u8>,
29    IV: InterfaceVariant,
30    C: Lr1110Variant,
31{
32    const SUPPORTED: bool = true;
33    async fn sample(&mut self, radio: &mut Lr1110<SPI, IV, C>) -> Option<u16> {
34        from_lr1110_raw(radio.get_temp().await.ok()?)
35    }
36}
37
38/// Semtech's nominal Vana=1.35 V, Vbe25=0.7295 V, slope=-1.7 mV/°C:
39/// https://github.com/Lora-net/SWDR001/blob/master/src/lr11xx_system.h
40/// Temp[10:0] is an ADC code, despite the pinned lora-phy get_temp comment.
41/// Use integer arithmetic and round once into 0.1 K, with ties upward.
42pub fn from_lr1110_raw(raw: u16) -> Option<u16> {
43    let raw = i64::from(raw & 0x07ff);
44    let denominator = 2 * 2047 * 170;
45    let numerator = 5963 * 2047 * 170 + 2 * (729500 * 2047 - raw * 1350000);
46    if numerator < 0 {
47        return None;
48    }
49    u16::try_from((numerator + denominator / 2) / denominator)
50        .ok()
51        .filter(|&value| value != u16::MAX)
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    #[test]
58    fn temperature_raw_codes_convert_to_tenths_kelvin() {
59        assert_eq!(from_lr1110_raw(1106), Some(2982));
60        assert_eq!(from_lr1110_raw(1200), Some(2617));
61        assert_eq!(from_lr1110_raw(0), Some(7273));
62        assert_eq!(from_lr1110_raw(2047), None); // below absolute zero
63        assert_eq!(from_lr1110_raw(1106 | 0xf800), Some(2982));
64    }
65}