/* ANTI-PATTERN (do NOT do this) — the ISR does the WORK itself instead of deferring.
* Wokwi, ST Nucleo-C031C6. The THIRD panel of the pipeline demo.
*
* Same SENSOR->FILTER->ALERT, but the "interrupt handler" runs the heavy ALERT work INSIDE itself.
* A real ISR can't sleep or yield, so the heavy part is a BUSY-WAIT. While the handler grinds:
* - the sensor's next samples are MISSED (seq skips), AND
* - the background HEARTBEAT task is STARVED (its "alive" line stalls).
* Lesson: an interrupt that does the work is just a SUPER-LOOP in an interrupt costume -- you gained
* nothing. The whole point of an interrupt is to DEFER the work to a task (see after-pipeline-isr.ino).
*
* KNOB (dial live in class): ISR_WORK_MS
* ~400 = mild (miss a sample or two per alert; the heartbeat hiccups)
* ~1500 = severe (the pipeline AND the heartbeat FREEZE in bursts)
*
* HONEST CAVEAT (the ISR here is FAKED): on REAL hardware this is FAR worse than what you see. A busy
* ISR blocks the system tick and EVERY other interrupt in the chip -- not just lower-priority tasks.
* Picture the heartbeat below as "all other interrupts in the system," and imagine it much worse.
*/
#include <STM32FreeRTOS.h>
#include "sensor_contract.h"
#define LED_YELLOW PB0
#define LED_RED PB7
#define ISR_WORK_MS 400 /* <== DIAL THIS: ~400 mild ... ~1500 severe */
/* A real ISR cannot vTaskDelay() -- there's no scheduler to yield to -- so heavy work can only be a
* busy-wait that hogs the CPU. That is exactly why heavy work does not belong in an ISR. */
static void busyWait(uint32_t ms) { uint32_t t = millis(); while (millis() - t < ms) { /* spin */ } }
/* THE HANDLER THAT DOES TOO MUCH: filter + alert + heavy work, all inside the "ISR". */
static void SimulatedISR(const SensorSample_t *s) {
if (!hh_valid(s)) { // FILTER (quick, fine)
digitalWrite(LED_YELLOW, HIGH);
Serial.print("seq="); Serial.print(s->seq); Serial.println(" FILTER: drop glitch");
busyWait(15); digitalWrite(LED_YELLOW, LOW); return;
}
const char *lvl = "OK"; // ALERT decision (quick, fine)
if (s->spo2_pct < 90.0f) lvl = "CRITICAL"; else if (s->temp_c >= 38.0f) lvl = "HIGH";
bool a = (lvl[0] != 'O');
digitalWrite(LED_RED, a ? HIGH : LOW);
Serial.print("seq="); Serial.print(s->seq); Serial.print(" ALERT: "); Serial.println(lvl);
if (a) busyWait(ISR_WORK_MS); // <-- THE MISTAKE: heavy work IN the handler
}
/* Time-based sensor "IRQ": fires for the sample available NOW. If the handler was still busy, the
* samples the sensor produced meanwhile are lost -- just like the polled super-loop. */
static void SensorSourceTask(void *pv) {
(void)pv; uint32_t last = 0;
for (;;) {
uint32_t seq = millis() / 250;
if (seq <= last) { vTaskDelay(pdMS_TO_TICKS(5)); continue; }
if (seq > last + 1) {
Serial.print(" !! MISSED "); Serial.print(seq - last - 1);
Serial.println(" sample(s) -- the ISR was still working");
}
last = seq;
SensorSample_t s = hh_make_sample(seq);
SimulatedISR(&s); // "interrupt fires" -> handler HOGS the CPU
}
}
/* Background work, to show the COLLATERAL damage: a busy ISR starves everything below it.
* (On real hardware, read this as "every other interrupt," which is far worse.) */
static void HeartbeatTask(void *pv) {
(void)pv;
for (;;) { vTaskDelay(pdMS_TO_TICKS(500)); Serial.println(" [hb] background task alive"); }
}
void setup() {
Serial.begin(115200);
pinMode(LED_YELLOW, OUTPUT); pinMode(LED_RED, OUTPUT);
Serial.print("ANTI-PATTERN: the ISR does the work (ISR_WORK_MS=");
Serial.print(ISR_WORK_MS); Serial.println(") -- watch seq SKIP and [hb] STALL");
xTaskCreate(SensorSourceTask, "SENSOR", 256, NULL, 3, NULL); // "interrupt" context -> highest
xTaskCreate(HeartbeatTask, "HB", 256, NULL, 1, NULL); // background -> starved when ISR busy
vTaskStartScheduler();
}
void loop() {}
/* ================= WHY THIS IS WRONG (the takeaway) =================
* Compare the three panels, all the SAME pipeline:
* before-pipeline-superloop.ino : work in the loop -> samples MISSED
* THIS FILE : work in the ISR -> samples MISSED + others STARVED (no better!)
* after-pipeline-isr.ino : ISR DEFERS to task -> nothing missed, heartbeat steady
* The interrupt only helps if the handler stays tiny and hands the work off. Doing the work inside
* the ISR throws away the entire benefit -- and on real silicon it also freezes every other interrupt. */