Refactor flow meter handling with interrupt-based logic and global state

- Added `flow_interrupt_handler` for efficient interrupt processing.
- Replaced per-instance `flow_counter` with global atomic and mutex-based state (`FLOW_OVERFLOW_COUNTER`, `FLOW_UNIT`).
- Updated flow meter functions to leverage the new architecture for better modularity and thread safety.
- Switched debugging output from `println!` to `log` for improved logging consistency.
This commit is contained in:
2026-05-05 00:49:38 +02:00
parent 5b1a945ac3
commit 3be585ecbf
2 changed files with 72 additions and 19 deletions

View File

@@ -290,7 +290,8 @@ impl PlantHal {
error: format!("Could not init wifi: {:?}", e),
})?;
let pcnt_module = Pcnt::new(peripherals.PCNT);
let mut pcnt_module = Pcnt::new(peripherals.PCNT);
pcnt_module.set_interrupt_handler(water::flow_interrupt_handler);
let free_pins = FreePeripherals {
gpio0: peripherals.GPIO0,

View File

@@ -1,6 +1,8 @@
use crate::bail;
use crate::fat_error::FatError;
use crate::hal::{ADC1, TANK_MULTI_SAMPLE};
use core::cell::RefCell;
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use embassy_time::Timer;
use esp_hal::analog::adc::{Adc, AdcCalLine, AdcConfig, AdcPin, Attenuation};
use esp_hal::delay::Delay;
@@ -10,17 +12,21 @@ use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
use esp_hal::pcnt::unit::Unit;
use esp_hal::peripherals::GPIO5;
use esp_hal::Async;
use esp_println::println;
use log::info;
use onewire::{ds18b20, Device, DeviceSearch, OneWire, DS18B20};
use portable_atomic::{AtomicUsize, Ordering};
unsafe impl Send for TankSensor<'_> {}
static FLOW_OVERFLOW_COUNTER: AtomicUsize = AtomicUsize::new(0);
static FLOW_UNIT: CriticalSectionMutex<RefCell<Option<Unit<'static, 1>>>> =
CriticalSectionMutex::new(RefCell::new(None));
pub struct TankSensor<'a> {
one_wire_bus: OneWire<Flex<'a>>,
tank_channel: Adc<'a, ADC1<'a>, Async>,
tank_power: Output<'a>,
tank_pin: AdcPin<GPIO5<'a>, ADC1<'a>, AdcCalLine<ADC1<'a>>>,
flow_counter: Unit<'a, 1>,
}
impl<'a> TankSensor<'a> {
@@ -30,7 +36,7 @@ impl<'a> TankSensor<'a> {
gpio5: GPIO5<'a>,
tank_power: Output<'a>,
flow_sensor: Input,
pcnt1: Unit<'a, 1>,
pcnt1: Unit<'static, 1>,
) -> Result<TankSensor<'a>, FatError> {
one_wire_pin.apply_output_config(
&OutputConfig::default()
@@ -55,33 +61,64 @@ impl<'a> TankSensor<'a> {
ch0.set_edge_signal(flow_sensor.peripheral_input());
ch0.set_input_mode(Hold, Increment);
ch0.set_ctrl_mode(Keep, Keep);
pcnt1.listen();
FLOW_UNIT.lock(|refcell| {
refcell.borrow_mut().replace(pcnt1);
});
Ok(TankSensor {
one_wire_bus,
tank_channel,
tank_power,
tank_pin,
flow_counter: pcnt1,
})
}
pub fn reset_flow_meter(&mut self) {
self.flow_counter.pause();
self.flow_counter.clear();
FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst);
FLOW_UNIT.lock(|refcell| {
if let Some(unit) = refcell.borrow_mut().as_mut() {
unit.pause();
unit.clear();
}
});
}
pub fn start_flow_meter(&mut self) {
self.flow_counter.resume();
FLOW_UNIT.lock(|refcell| {
if let Some(unit) = refcell.borrow_mut().as_mut() {
unit.resume();
}
});
}
pub fn get_flow_meter_value(&mut self) -> i16 {
self.flow_counter.value()
FLOW_UNIT.lock(|refcell| {
refcell.borrow_mut().as_mut().map_or(0, |unit| unit.value())
})
}
pub fn stop_flow_meter(&mut self) -> i16 {
self.flow_counter.pause();
self.get_flow_meter_value()
FLOW_UNIT.lock(|refcell| {
let mut borrowed = refcell.borrow_mut();
if let Some(unit) = borrowed.as_mut() {
let val = unit.value();
unit.pause();
val
} else {
0
}
})
}
pub fn get_full_flow_count(&self) -> u32 {
let current = FLOW_UNIT.lock(|refcell| {
refcell.borrow().as_ref().map_or(0, |unit| unit.value() as u32)
});
let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32;
overflowed * (i16::MAX.wrapping_add(1) as u32) + current
}
pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
@@ -90,9 +127,9 @@ impl<'a> TankSensor<'a> {
let mut delay = Delay::new();
let presence = self.one_wire_bus.reset(&mut delay)?;
println!("OneWire: reset presence pulse = {}", presence);
info!("OneWire: reset presence pulse = {}", presence);
if !presence {
println!("OneWire: no device responded to reset — check pull-up resistor and wiring");
info!("OneWire: no device responded to reset — check pull-up resistor and wiring");
}
let mut search = DeviceSearch::new();
@@ -100,7 +137,7 @@ impl<'a> TankSensor<'a> {
let mut devices_found = 0u8;
while let Some(device) = self.one_wire_bus.search_next(&mut search, &mut delay)? {
devices_found += 1;
println!(
info!(
"OneWire: found device #{} family=0x{:02X} addr={:02X?}",
devices_found, device.address[0], device.address
);
@@ -108,16 +145,16 @@ impl<'a> TankSensor<'a> {
water_temp_sensor = Some(device);
break;
} else {
println!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
info!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
}
}
if devices_found == 0 {
println!("OneWire: search found zero devices on the bus");
info!("OneWire: search found zero devices on the bus");
}
match water_temp_sensor {
Some(device) => {
println!("Found one wire device: {:?}", device);
info!("Found one wire device: {:?}", device);
let mut water_temp_sensor = DS18B20::new(device)?;
let water_temp: Result<f32, FatError> = loop {
@@ -126,11 +163,11 @@ impl<'a> TankSensor<'a> {
.await;
match &temp {
Ok(res) => {
println!("Water temp is {}", res);
info!("Water temp is {}", res);
break temp;
}
Err(err) => {
println!("Could not get water temp {} attempt {}", err, attempt)
info!("Could not get water temp {} attempt {}", err, attempt)
}
}
if attempt == 5 {
@@ -178,3 +215,18 @@ impl<'a> TankSensor<'a> {
Ok(median_mv / 1000.0)
}
}
#[esp_hal::handler]
pub fn flow_interrupt_handler() {
FLOW_UNIT.lock(|refcell| {
if let Some(unit) = refcell.borrow_mut().as_mut() {
if unit.interrupt_is_set() {
let events = unit.events();
if events.high_limit {
FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst);
}
unit.reset_interrupt();
}
}
});
}