1const OHMS: [u32; 136] = [
10 113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919,
11 59011, 56258, 53650, 51178, 48835, 46613, 44506, 42506, 40600, 38791, 37073, 35442, 33892,
12 32420, 31020, 29689, 28423, 27219, 26076, 24988, 23951, 22963, 22021, 21123, 20267, 19450,
13 18670, 17926, 17214, 16534, 15886, 15266, 14674, 14108, 13566, 13049, 12554, 12081, 11628,
14 11195, 10780, 10382, 10000, 9634, 9284, 8947, 8624, 8315, 8018, 7734, 7461, 7199, 6948, 6707,
15 6475, 6253, 6039, 5834, 5636, 5445, 5262, 5086, 4917, 4754, 4597, 4446, 4301, 4161, 4026, 3896,
16 3771, 3651, 3535, 3423, 3315, 3211, 3111, 3014, 2922, 2834, 2748, 2666, 2586, 2509, 2435, 2364,
17 2294, 2228, 2163, 2100, 2040, 1981, 1925, 1870, 1817, 1766, 1716, 1669, 1622, 1578, 1535, 1493,
18 1452, 1413, 1375, 1338, 1303, 1268, 1234, 1202, 1170, 1139, 1110, 1081, 1053, 1026, 999, 974,
19 949, 925, 902, 880, 858,
20];
21
22pub fn from_adc(raw: i16, battery_mv: u16) -> Option<u16> {
26 if raw <= 0 || raw >= 16384 || !(2000..=5000).contains(&battery_mv) {
27 return None;
28 }
29 let node = u64::from(raw as u16) * 3600;
30 let supply = u64::from(battery_mv.min(3300)) * 16384;
31 if node >= supply {
32 return None;
33 }
34 from_resistance(8250 * (supply - node), node)
35}
36
37fn from_resistance(numerator: u64, denominator: u64) -> Option<u16> {
39 if numerator > u64::from(OHMS[0]) * denominator
40 || numerator < u64::from(OHMS[135]) * denominator
41 {
42 return None;
43 }
44 for (index, pair) in OHMS.windows(2).enumerate() {
45 let cold = u64::from(pair[0]) * denominator;
46 let hot = u64::from(pair[1]) * denominator;
47 if numerator >= hot {
48 let span = cold - hot;
50 let twice_tenths = (4863 + index as u64 * 20) * span + 20 * (cold - numerator);
51 return Some(((twice_tenths + span) / (2 * span)) as u16);
52 }
53 }
54 None
55}
56
57#[cfg(target_os = "none")]
58mod sampling {
59 use core::sync::atomic::{AtomicU32, Ordering};
60 use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, signal::Signal};
61
62 pub(crate) static REQUEST: Signal<ThreadModeRawMutex, u32> = Signal::new();
63 pub(crate) static REPLY: Signal<ThreadModeRawMutex, (u32, Option<u16>)> = Signal::new();
64 static NEXT: AtomicU32 = AtomicU32::new(0);
65
66 pub async fn sample() -> Option<u16> {
69 let id = NEXT.fetch_add(1, Ordering::Relaxed);
70 REPLY.reset();
71 REQUEST.signal(id);
72 embassy_time::with_timeout(embassy_time::Duration::from_secs(2), async {
73 loop {
74 let (reply_id, value) = REPLY.wait().await;
75 if reply_id == id {
76 return value;
77 }
78 }
79 })
80 .await
81 .ok()
82 .flatten()
83 }
84}
85#[cfg(target_os = "none")]
86pub use sampling::sample;
87#[cfg(target_os = "none")]
88pub(crate) use sampling::{REPLY, REQUEST};
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn temperature_table_endpoints_and_every_knot() {
96 for (i, ohms) in OHMS.iter().enumerate() {
97 assert_eq!(
98 from_resistance(u64::from(*ohms), 1),
99 Some(2432 + i as u16 * 10)
100 );
101 }
102 assert_eq!(from_resistance(113348, 1), None);
103 assert_eq!(from_resistance(857, 1), None);
104 assert_eq!(from_resistance(19634, 2), Some(2987));
106 }
107
108 #[test]
109 fn temperature_adc_supply_and_faults() {
110 assert_eq!(from_adc(6790, 4000), Some(2982));
111 assert_eq!(from_adc(6173, 3000), Some(2982));
112 for raw in [i16::MIN, -1, 0, 1, 15018, 16383, 16384, i16::MAX] {
113 assert_eq!(from_adc(raw, 4000), None, "raw {raw}");
114 }
115 assert_eq!(from_adc(6789, 0), None);
116 assert_eq!(from_adc(6789, 6000), None);
117 }
118}