feat: implement interleaved sensor measurement timing with slot-based coordination to prevent overlap

This commit is contained in:
2026-04-30 20:19:13 +02:00
parent 9280bbb244
commit 2e16163b0e

View File

@@ -289,6 +289,7 @@ async fn main(spawner: Spawner) {
standard_moisture_id, standard_moisture_id,
standard_identify_id, standard_identify_id,
standard_firmware_build_id, standard_firmware_build_id,
slot,
)) ))
.unwrap(); .unwrap();
} }
@@ -369,7 +370,7 @@ async fn can_task(
warn: &'static mut Output<'static>, warn: &'static mut Output<'static>,
identify_id: StandardId, identify_id: StandardId,
moisture_id: StandardId, moisture_id: StandardId,
firmware_build_id: StandardId, _firmware_build_id: StandardId,
) { ) {
// Non-blocking beacon blink timing. // Non-blocking beacon blink timing.
// We keep this inside the CAN task so it can't stall other tasks (like `worker`) with `await`s. // We keep this inside the CAN task so it can't stall other tasks (like `worker`) with `await`s.
@@ -469,6 +470,7 @@ async fn worker(
moisture_id: StandardId, moisture_id: StandardId,
identify_id: StandardId, identify_id: StandardId,
firmware_build_id: StandardId, firmware_build_id: StandardId,
slot: SensorSlot,
) { ) {
// 555 emulation state: Q initially Low // 555 emulation state: Q initially Low
let mut q_high = false; let mut q_high = false;
@@ -478,6 +480,33 @@ async fn worker(
const AVG_WINDOWS: u32 = 4; const AVG_WINDOWS: u32 = 4;
const YIELD_EVERY: u32 = 64; const YIELD_EVERY: u32 = 64;
let probe_duration = Duration::from_millis(100); let probe_duration = Duration::from_millis(100);
let measurement_time = probe_duration.as_millis() * AVG_WINDOWS as u64; // 400ms
let interleaving_gap = Duration::from_millis(50);
// Interleaving timing to ensure A and B never overlap:
// - Sensor A: measures for 400ms
// - Gap: 50ms
// - Sensor B: measures for 400ms
// - Gap: 50ms
// Total cycle: 900ms, so each sensor measures every 900ms (~1.1 measurements/second)
//
// Timeline:
// 0-400ms: A measures, B idle
// 400-450ms: both idle (gap)
// 450-850ms: B measures, A idle
// 850-900ms: both idle (gap)
// Then repeat from 0ms
// Initial offset: B waits for A's measurement time + one gap
match slot {
SensorSlot::A => {
// A sensors start measuring immediately
}
SensorSlot::B => {
// B sensors wait for A to finish measuring + gap
Timer::after(Duration::from_millis(measurement_time + interleaving_gap.as_millis())).await;
}
}
loop { loop {
let mut total_pulses: u32 = 0; let mut total_pulses: u32 = 0;
@@ -555,6 +584,12 @@ async fn worker(
if let Some(build_frame) = CanFrame::new(firmware_build_id, &FIRMWARE_BUILD_MINUTES.to_be_bytes()) { if let Some(build_frame) = CanFrame::new(firmware_build_id, &FIRMWARE_BUILD_MINUTES.to_be_bytes()) {
CAN_TX_CH.send(build_frame).await; CAN_TX_CH.send(build_frame).await;
} }
// Wait for the other slot to measure, plus gaps to ensure no overlap
// After A finishes measuring: wait 50ms (gap) + 400ms (B measures) + 50ms (gap) = 500ms
// After B finishes measuring: wait 50ms (gap) + 400ms (A measures) + 50ms (gap) = 500ms
// This ensures the full 900ms cycle is maintained and A/B never overlap
Timer::after(Duration::from_millis(interleaving_gap.as_millis() + measurement_time + interleaving_gap.as_millis())).await;
} }
} }