Firmware for the b-parasite board, but in Rust!
at main 4.2 kB view raw
1use embassy_nrf::{ 2 Peri, 3 gpio::Output, 4 peripherals, 5 pwm::{self, SimplePwm}, 6 saadc::{self, ChannelConfig, Config, Resolution, Saadc}, 7}; 8use embassy_time::Timer; 9use sachy_battery::BatteryDischargeProfile; 10use sachy_fmt::{info, unwrap}; 11use static_cell::ConstStaticCell; 12 13use crate::{ 14 Irqs, 15 constants::{DISCARGE_PROFILES, DRY_COEFFS, WET_COEFFS}, 16 state::{ADC_MEASUREMENT, AdcMeasurements, START_MEASUREMENTS}, 17}; 18 19const VREF: f32 = 3.6; 20 21#[inline] 22fn calculate_polynomial(coeffs: &[f32; 3], val: f32) -> f32 { 23 coeffs[0] + (coeffs[1] * val) + (coeffs[2] * (val * val)) 24} 25 26#[inline] 27fn calculate_soil_moisture(bat: f32, soil: i16) -> f32 { 28 let dry = calculate_polynomial(&DRY_COEFFS, bat); 29 let wet = calculate_polynomial(&WET_COEFFS, bat); 30 31 info!("WUH: dry {}, wet {}, soil {}", dry, wet, soil); 32 33 (((soil as f32) - dry) / (wet - dry)).clamp(0.0, 1.0) 34} 35 36#[inline] 37fn calculate_lux(voltage: f32) -> f32 { 38 const LUX_SUN: f32 = 10000.0; 39 const CURRENT_SUN: f32 = 3.59e-3; 40 const PHOTO_RESISTOR: f32 = 470.0; 41 42 let current = voltage / PHOTO_RESISTOR; 43 44 LUX_SUN * current / CURRENT_SUN 45} 46 47#[inline] 48fn to_volts(sample: i16, reference: f32) -> f32 { 49 ((sample.max(0) as f32) * reference) / 1024.0 50} 51 52fn init_pwm<'scope>( 53 pwm: Peri<'scope, peripherals::PWM0>, 54 ch0: Peri<'scope, peripherals::P0_05>, 55) -> SimplePwm<'scope> { 56 let pwm_ctrl = SimplePwm::new_1ch(pwm, ch0); 57 pwm_ctrl.set_prescaler(pwm::Prescaler::Div1); 58 pwm_ctrl.set_period(2_000_000); 59 60 pwm_ctrl 61} 62 63fn init_saadc<'scope>( 64 saadc: Peri<'scope, peripherals::SAADC>, 65 light_pin: Peri<'scope, peripherals::P0_02>, 66 soil_pin: Peri<'scope, peripherals::P0_03>, 67) -> Saadc<'scope, 3> { 68 let light_config = ChannelConfig::single_ended(light_pin); 69 70 let mut soil_config = ChannelConfig::single_ended(soil_pin); 71 soil_config.reference = saadc::Reference::VDD1_4; 72 73 let bat_config = ChannelConfig::single_ended(saadc::VddInput); 74 75 let mut saadc_config = Config::default(); 76 saadc_config.resolution = Resolution::_10BIT; 77 78 Saadc::new( 79 saadc, 80 Irqs, 81 saadc_config, 82 [soil_config, light_config, bat_config], 83 ) 84} 85 86#[embassy_executor::task] 87pub async fn task( 88 mut saadc: Peri<'static, peripherals::SAADC>, 89 mut light_pin: Peri<'static, peripherals::P0_02>, 90 mut soil_pin: Peri<'static, peripherals::P0_03>, 91 mut photo_ctrl: Output<'static>, 92 mut pwm: Peri<'static, peripherals::PWM0>, 93 mut pin5: Peri<'static, peripherals::P0_05>, 94) { 95 static ADC_BUFFER: ConstStaticCell<[i16; 3]> = ConstStaticCell::new([0; 3]); 96 let adc_buf = ADC_BUFFER.take(); 97 98 let mut measure = unwrap!(START_MEASUREMENTS.receiver()); 99 100 loop { 101 measure.changed().await; 102 103 let mut pwm_ctrl = init_pwm(pwm.reborrow(), pin5.reborrow()); 104 105 let mut saadc = init_saadc(saadc.reborrow(), light_pin.reborrow(), soil_pin.reborrow()); 106 107 photo_ctrl.set_high(); 108 pwm_ctrl.enable(); 109 pwm_ctrl.set_duty(0, 4); 110 111 Timer::after_millis(30).await; 112 113 let mut acc_buf = [0; 3]; 114 let divisor = 4; 115 116 for _ in 0..divisor { 117 saadc.sample(adc_buf).await; 118 acc_buf 119 .iter_mut() 120 .zip(adc_buf.iter()) 121 .for_each(|(slot, &value)| *slot += value); 122 Timer::after_millis(5).await; 123 } 124 125 photo_ctrl.set_low(); 126 pwm_ctrl.set_duty(0, 0); 127 128 acc_buf.iter_mut().for_each(|acc| *acc /= divisor); 129 130 let [soil, light, bat] = acc_buf; 131 132 let bat_volt = to_volts(bat, VREF); 133 134 let (soil, light, bat) = ( 135 calculate_soil_moisture(bat_volt, soil), 136 calculate_lux(to_volts(light, VREF)).max(0.0), 137 BatteryDischargeProfile::calc_pct_from_profile_range( 138 bat_volt, 139 DISCARGE_PROFILES.iter(), 140 ), 141 ); 142 143 let measurements = AdcMeasurements::new(bat, bat_volt, soil, light); 144 145 info!("Soil {}, Light {}, Bat {}", soil, light, bat); 146 147 ADC_MEASUREMENT.signal(measurements); 148 pwm_ctrl.disable(); 149 drop(pwm_ctrl); 150 drop(saadc); 151 } 152}