Compare commits
6 Commits
3cc5a0d2bd
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f8bcc9da | |||
| 103859120c | |||
| 403517fdb4 | |||
| 11eb8713bf | |||
| d903c2bf52 | |||
| f8f76674ce |
1
Software/MainBoard/rust/Cargo.lock
generated
1
Software/MainBoard/rust/Cargo.lock
generated
@@ -1901,6 +1901,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"crc",
|
||||
"critical-section",
|
||||
"deranged",
|
||||
"ds323x",
|
||||
"edge-dhcp",
|
||||
|
||||
@@ -101,6 +101,7 @@ chrono-tz = { version = "0.10.4", default-features = false, features = ["filter-
|
||||
heapless = { version = "0.7.17", features = ["serde"] } # stay in sync with mcutie version
|
||||
static_cell = "2.1.1"
|
||||
portable-atomic = "1.11.1"
|
||||
critical-section = "1"
|
||||
crc = "3.3.0"
|
||||
bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] }
|
||||
deranged = "0.5.5"
|
||||
|
||||
3
Software/MainBoard/rust/TODO
Normal file
3
Software/MainBoard/rust/TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
One Wire does not seem to work.
|
||||
Flow Sensor does not seem to work.
|
||||
PlantProfiles with a dry out phase needs to be implemented + Memory for this
|
||||
@@ -161,9 +161,11 @@ pub(crate) async fn create_v4(
|
||||
info!("Start v4");
|
||||
let mut awake = Output::new(peripherals.gpio21, Level::High, OutputConfig::default());
|
||||
awake.set_high();
|
||||
info!("v4: gpio21 awake ok");
|
||||
|
||||
let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
|
||||
general_fault.set_low();
|
||||
info!("v4: gpio23 general_fault ok");
|
||||
|
||||
let twai_config = Some(TwaiConfiguration::new(
|
||||
peripherals.twai,
|
||||
@@ -172,17 +174,24 @@ pub(crate) async fn create_v4(
|
||||
TWAI_BAUDRATE,
|
||||
TwaiMode::Normal,
|
||||
));
|
||||
info!("v4: twai config ok");
|
||||
|
||||
let extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio6 extra1 ok");
|
||||
let extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio15 extra2 ok");
|
||||
|
||||
let one_wire_pin = Flex::new(peripherals.gpio18);
|
||||
info!("v4: gpio18 one_wire ok");
|
||||
let tank_power_pin = Output::new(peripherals.gpio11, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio11 tank_power ok");
|
||||
let flow_sensor_pin = Input::new(
|
||||
peripherals.gpio4,
|
||||
InputConfig::default().with_pull(Pull::Up),
|
||||
);
|
||||
info!("v4: gpio4 flow_sensor ok");
|
||||
|
||||
info!("v4: creating tank sensor");
|
||||
let tank_sensor = TankSensor::create(
|
||||
one_wire_pin,
|
||||
peripherals.adc1,
|
||||
@@ -191,12 +200,17 @@ pub(crate) async fn create_v4(
|
||||
flow_sensor_pin,
|
||||
peripherals.pcnt1,
|
||||
)?;
|
||||
info!("v4: tank sensor ok");
|
||||
|
||||
let can_power = Output::new(peripherals.gpio22, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio22 can_power ok");
|
||||
|
||||
let solar_is_day = Input::new(peripherals.gpio7, InputConfig::default());
|
||||
info!("v4: gpio7 solar_is_day ok");
|
||||
let light = Output::new(peripherals.gpio10, Level::Low, Default::default());
|
||||
info!("v4: gpio10 light ok");
|
||||
let charge_indicator = Output::new(peripherals.gpio3, Level::Low, Default::default());
|
||||
info!("v4: gpio3 charge_indicator ok");
|
||||
|
||||
info!("Start pump expander");
|
||||
let pump_device = I2cDevice::new(I2C_DRIVER.get().await);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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;
|
||||
@@ -19,14 +17,13 @@ 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_unit: Unit<'static, 1>,
|
||||
}
|
||||
|
||||
impl<'a> TankSensor<'a> {
|
||||
@@ -47,78 +44,76 @@ impl<'a> TankSensor<'a> {
|
||||
one_wire_pin.set_high();
|
||||
one_wire_pin.set_input_enable(true);
|
||||
one_wire_pin.set_output_enable(true);
|
||||
info!("tank: one_wire pin config ok");
|
||||
|
||||
let mut adc1_config = AdcConfig::new();
|
||||
info!("tank: adc config created");
|
||||
let tank_pin =
|
||||
adc1_config.enable_pin_with_cal::<_, AdcCalLine<_>>(gpio5, Attenuation::_11dB);
|
||||
info!("tank: adc pin cal ok");
|
||||
let tank_channel = Adc::new(adc1, adc1_config).into_async();
|
||||
info!("tank: adc channel ok");
|
||||
|
||||
let one_wire_bus = OneWire::new(one_wire_pin, false);
|
||||
info!("tank: one_wire bus ok");
|
||||
|
||||
pcnt1.set_high_limit(Some(i16::MAX))?;
|
||||
info!("tank: pcnt high limit ok");
|
||||
// Reject pulses shorter than ~12.8 µs (1023 APB cycles @ 80 MHz) to suppress EMI noise
|
||||
// on the sensor cable. Real flow pulses are in the millisecond range.
|
||||
pcnt1.set_filter(Some(1023)).unwrap();
|
||||
|
||||
let ch0 = &pcnt1.channel0;
|
||||
ch0.set_edge_signal(flow_sensor.peripheral_input());
|
||||
info!("tank: pcnt edge signal ok");
|
||||
ch0.set_input_mode(Hold, Increment);
|
||||
ch0.set_ctrl_mode(Keep, Keep);
|
||||
info!("tank: pcnt input/ctrl mode ok");
|
||||
|
||||
pcnt1.listen();
|
||||
|
||||
FLOW_UNIT.lock(|refcell| {
|
||||
refcell.borrow_mut().replace(pcnt1);
|
||||
});
|
||||
info!("tank: pcnt listen ok");
|
||||
|
||||
Ok(TankSensor {
|
||||
one_wire_bus,
|
||||
tank_channel,
|
||||
tank_power,
|
||||
tank_pin,
|
||||
flow_unit: pcnt1,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset_flow_meter(&mut self) {
|
||||
FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst);
|
||||
FLOW_UNIT.lock(|refcell| {
|
||||
if let Some(unit) = refcell.borrow_mut().as_mut() {
|
||||
unit.pause();
|
||||
unit.clear();
|
||||
}
|
||||
// Pause, clear counter, clear any pending interrupt, then reset the overflow counter —
|
||||
// all inside a single critical section to prevent a race where the interrupt fires
|
||||
// between the overflow reset and the pause.
|
||||
critical_section::with(|_| {
|
||||
self.flow_unit.pause();
|
||||
self.flow_unit.clear();
|
||||
self.flow_unit.reset_interrupt();
|
||||
FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_flow_meter(&mut self) {
|
||||
FLOW_UNIT.lock(|refcell| {
|
||||
if let Some(unit) = refcell.borrow_mut().as_mut() {
|
||||
unit.resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_flow_meter_value(&mut self) -> i16 {
|
||||
FLOW_UNIT.lock(|refcell| {
|
||||
refcell.borrow_mut().as_mut().map_or(0, |unit| unit.value())
|
||||
})
|
||||
self.flow_unit.resume();
|
||||
}
|
||||
|
||||
pub fn stop_flow_meter(&mut self) -> i16 {
|
||||
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
|
||||
}
|
||||
critical_section::with(|_| {
|
||||
let val = self.flow_unit.value();
|
||||
self.flow_unit.pause();
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
// Read both values inside a single critical section so an overflow interrupt cannot
|
||||
// fire between the two reads and produce an inconsistent result.
|
||||
critical_section::with(|_| {
|
||||
let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32;
|
||||
let current = self.flow_unit.value() as u32;
|
||||
overflowed * (i16::MAX as u32 + 1) + current
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
|
||||
@@ -218,15 +213,12 @@ impl<'a> TankSensor<'a> {
|
||||
|
||||
#[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();
|
||||
}
|
||||
use esp_hal::peripherals::PCNT;
|
||||
let pcnt = PCNT::regs();
|
||||
if pcnt.int_raw().read().cnt_thr_event_u(1).bit() {
|
||||
if pcnt.u_status(1).read().h_lim().bit() {
|
||||
FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
});
|
||||
pcnt.int_clr().write(|w| w.cnt_thr_event_u(1).set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex as BlockingMutex;
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
const MAX_LIVE_LOG_ENTRIES: usize = 64;
|
||||
const MAX_LIVE_LOG_ENTRIES: usize = 128;
|
||||
|
||||
struct LiveLogBuffer {
|
||||
entries: Vec<(u64, String)>,
|
||||
|
||||
@@ -123,6 +123,8 @@ struct PumpInfo {
|
||||
max_current_ma: u16,
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_raw: u32,
|
||||
flow_ml: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -132,7 +134,7 @@ pub struct PumpResult {
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_value_ml: f32,
|
||||
flow_value_count: i16,
|
||||
flow_value_count: u32,
|
||||
pump_time_s: u16,
|
||||
overcurrent_ma: Option<u16>,
|
||||
}
|
||||
@@ -456,6 +458,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
0,
|
||||
0,
|
||||
String::new(),
|
||||
0,
|
||||
0.0,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -472,6 +476,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
state.max_current_ma,
|
||||
state.min_current_ma,
|
||||
state.error,
|
||||
state.flow_value_count,
|
||||
state.flow_value_ml,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -485,6 +491,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
0,
|
||||
0,
|
||||
format!("{err:?}"),
|
||||
0,
|
||||
0.0,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -722,7 +730,7 @@ pub async fn do_secure_pump(
|
||||
let steps_in_50ms = plant_config.pump_time_s as usize * 20;
|
||||
|
||||
let mut current_collector = vec![0_u16; steps_in_50ms];
|
||||
let mut flow_collector = vec![0_i16; steps_in_50ms];
|
||||
let mut flow_collector = vec![0_u32; steps_in_50ms];
|
||||
let mut error = String::new();
|
||||
let mut first_error = true;
|
||||
let mut pump_time_ms: u32 = 0;
|
||||
@@ -773,7 +781,7 @@ pub async fn do_secure_pump(
|
||||
|
||||
for step in 0..steps_in_50ms {
|
||||
let step_start = Instant::now();
|
||||
let flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
||||
let flow_value = board.board_hal.get_tank_sensor()?.get_full_flow_count();
|
||||
flow_collector[step] = flow_value;
|
||||
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||
|
||||
@@ -885,7 +893,7 @@ pub async fn do_secure_pump(
|
||||
pump_time_ms = 1337;
|
||||
}
|
||||
board.board_hal.get_tank_sensor()?.stop_flow_meter();
|
||||
let final_flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
||||
let final_flow_value = board.board_hal.get_tank_sensor()?.get_full_flow_count();
|
||||
let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||
info!("Final flow value is {final_flow_value} with {flow_value_ml} ml");
|
||||
current_collector.sort();
|
||||
@@ -1052,6 +1060,8 @@ async fn pump_info(
|
||||
max_current_ma: u16,
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_raw: u32,
|
||||
flow_ml: f32,
|
||||
) {
|
||||
let pump_info = PumpInfo {
|
||||
enabled: pump_active,
|
||||
@@ -1060,6 +1070,8 @@ async fn pump_info(
|
||||
max_current_ma,
|
||||
min_current_ma,
|
||||
error,
|
||||
flow_raw,
|
||||
flow_ml,
|
||||
};
|
||||
let pump_topic = format!("/pump{}", plant_id + 1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user