ntc_formula.rs
1 //! `describe("NTC formula (pure logic)")` 2 //! 3 //! Pure-logic unit tests for the Steinhart–Hart-style β-coefficient 4 //! conversion the firmware uses to derive a temperature from a measured 5 //! ADC voltage on a 10 kΩ NTC thermistor in a fixed-resistor divider. 6 7 #![no_std] 8 #![no_main] 9 10 #[path = "common/mod.rs"] 11 mod common; 12 13 use defmt::info; 14 15 use common::Device; 16 17 const ADC_REFERENCE_VOLTAGE_VOLTS: f32 = 3.3; 18 const NTC_BETA_COEFFICIENT: f32 = 3988.0; 19 const REFERENCE_TEMPERATURE_KELVIN: f32 = 298.15; 20 const NOMINAL_RESISTOR_OHMS: f32 = 10_000.0; 21 22 fn calculate_ntc_temperature_celsius_from_voltage_measured( 23 measured_voltage_volts: f32, 24 ) -> f32 { 25 let voltage_fraction = measured_voltage_volts / ADC_REFERENCE_VOLTAGE_VOLTS; 26 let thermistor_resistance_ratio = 27 ((voltage_fraction * NOMINAL_RESISTOR_OHMS) / (1.0 - voltage_fraction)) 28 * (1.0 / NOMINAL_RESISTOR_OHMS); 29 let inverse_temperature_kelvin = (1.0 / REFERENCE_TEMPERATURE_KELVIN) 30 + (1.0 / NTC_BETA_COEFFICIENT) * libm::logf(thermistor_resistance_ratio); 31 (1.0 / inverse_temperature_kelvin) - 273.15 32 } 33 34 esp_bootloader_esp_idf::esp_app_desc!(); 35 36 #[cfg(test)] 37 #[embedded_test::setup] 38 fn setup() { 39 rtt_target::rtt_init_defmt!(); 40 } 41 42 #[cfg(test)] 43 #[embedded_test::tests(default_timeout = 5, executor = esp_rtos::embassy::Executor::new())] 44 mod tests { 45 use super::*; 46 47 #[init] 48 fn init() -> Device { 49 info!("=== NTC formula (pure logic) — describe block ==="); 50 common::setup::boot_device() 51 } 52 53 /// `it("user reads a room-temperature reading at half-rail voltage")` 54 #[test] 55 async fn user_reads_room_temperature_at_half_rail_voltage( 56 _device: Device, 57 ) -> Result<(), &'static str> { 58 let measured_voltage_volts = 1.65; 59 let calculated_temperature_celsius = 60 calculate_ntc_temperature_celsius_from_voltage_measured(measured_voltage_volts); 61 62 info!( 63 "1.65V on the divider → {=f32} °C", 64 calculated_temperature_celsius 65 ); 66 67 if calculated_temperature_celsius < 20.0 { 68 return Err("device: half-rail voltage produced a temperature below room range"); 69 } 70 if calculated_temperature_celsius > 30.0 { 71 return Err("device: half-rail voltage produced a temperature above room range"); 72 } 73 Ok(()) 74 } 75 76 /// `it("user reads a cold reading at low voltage")` 77 #[test] 78 async fn user_reads_cold_temperature_at_low_voltage( 79 _device: Device, 80 ) -> Result<(), &'static str> { 81 let measured_voltage_volts = 0.66; 82 let calculated_temperature_celsius = 83 calculate_ntc_temperature_celsius_from_voltage_measured(measured_voltage_volts); 84 85 info!( 86 "0.66V on the divider → {=f32} °C", 87 calculated_temperature_celsius 88 ); 89 90 if calculated_temperature_celsius >= 20.0 { 91 return Err("device: low-side voltage should report a cold temperature"); 92 } 93 Ok(()) 94 } 95 96 /// `it("user reads a hot reading at high voltage")` 97 #[test] 98 async fn user_reads_hot_temperature_at_high_voltage( 99 _device: Device, 100 ) -> Result<(), &'static str> { 101 let measured_voltage_volts = 2.64; 102 let calculated_temperature_celsius = 103 calculate_ntc_temperature_celsius_from_voltage_measured(measured_voltage_volts); 104 105 info!( 106 "2.64V on the divider → {=f32} °C", 107 calculated_temperature_celsius 108 ); 109 110 if calculated_temperature_celsius <= 30.0 { 111 return Err("device: high-side voltage should report a hot temperature"); 112 } 113 Ok(()) 114 } 115 }