Sensor Agent Patterns — Four Common MCU Agent Designs
Sensor Agent Patterns
Four patterns cover the majority of production MCU agent designs: anomaly-detect-and-alert, threshold-with-explanation, sensor-fusion-then-report, and local-decision-then-actuate — each trades off local compute, communication frequency, and response latency differently.
These patterns are not mutually exclusive. A real agent often combines two or three. They are described separately here to make the tradeoffs clear.
Pattern 1 — Anomaly-Detect-and-Alert
What it does: Runs a compact anomaly or classification model on raw or pre-processed sensor data. Only transmits when the model indicates an anomaly. Normal operation produces zero traffic.
When to use it: When the event of interest is rare, the raw sensor stream is high-bandwidth (audio, vibration, image), and you need to avoid streaming gigabytes of normal data to detect a handful of real events.
Tradeoffs: Requires on-device ML inference (model training, quantization, deployment pipeline). False-negative rate depends entirely on model quality. Cannot be debugged by reading MQTT traffic during normal operation.
/* Pattern 1 pseudocode: anomaly detect and alert */
void vAnomalyAgentTask(void *pvParam) {
float feature_vec[FEATURE_LEN];
float score;
for (;;) {
sensor_read_window(feature_vec, WINDOW_SIZE); /* blocks until window full */
dsp_extract_features(feature_vec); /* RMS, spectral centroid, etc. */
score = tflm_run_inference(feature_vec); /* 0.0 = normal, 1.0 = anomaly */
if (score > ANOMALY_THRESHOLD) {
/* Only transmit on anomaly — silent during normal operation */
mqtt_publish_event(TOPIC_EVENT,
build_json(DEVICE_ID, "anomaly", score));
}
/* No publish on normal: zero idle traffic */
}
}
Key spec decision: Set ANOMALY_THRESHOLD conservatively high to reduce false positives. Route low-confidence detections (score between 0.5 and ANOMALY_THRESHOLD) to a delegate topic for human or cloud review.
Pattern 2 — Threshold-with-Explanation
What it does: Applies rule-based threshold detection but bundles sensor context — neighboring values, trend direction, recent history — with the alert. The receiver can determine whether the alert is genuine without querying the device.
When to use it: When the event is predictable (temperature out of range, pressure spike) but the response depends on context the device can compute locally (rate of change, duration, co-occurring conditions).
Tradeoffs: No ML required. Context payload size can grow; keep it bounded. Does not handle novel events that the threshold does not anticipate.
/* Pattern 2 pseudocode: threshold with explanation context */
typedef struct {
float current;
float delta_10s; /* rate of change over last 10 seconds */
float mean_60s; /* rolling mean over last minute */
uint32_t duration_ms; /* time above threshold */
char direction[8]; /* "rising" or "falling" */
} SensorContext_t;
void vThresholdAgentTask(void *pvParam) {
SensorContext_t ctx;
bool in_alert = false;
for (;;) {
sensor_update_context(&ctx); /* updates all context fields */
if (ctx.current > ALERT_HIGH && !in_alert) {
in_alert = true;
mqtt_publish_json(TOPIC_EVENT,
"{\"event\":\"high\","
"\"value\":%.2f,\"delta\":%.3f,"
"\"mean_60s\":%.2f,\"dir\":\"%s\"}",
ctx.current, ctx.delta_10s,
ctx.mean_60s, ctx.direction);
} else if (ctx.current < CLEAR_LOW && in_alert) {
in_alert = false;
mqtt_publish_json(TOPIC_EVENT,
"{\"event\":\"clear\",\"value\":%.2f}",
ctx.current);
}
vTaskDelay(pdMS_TO_TICKS(SAMPLE_MS));
}
}
Key spec decision: Separate the alert threshold from the clear threshold (hysteresis). Without hysteresis, a reading oscillating at the threshold produces a torrent of messages.
Pattern 3 — Sensor Fusion Then Report
What it does: Combines readings from multiple physical sensors into a single higher-level report. The downstream consumer receives a derived, actionable value rather than raw ADC counts or I2C register values.
When to use it: When individual sensors are unreliable or ambiguous in isolation — temperature alone says nothing about human comfort; temperature + humidity + air speed together compute an effective temperature that does. Multiple vibration axes fused into a single bearing health score.
Tradeoffs: Fusion computation runs on the MCU. Complex fusion (Kalman filter, sensor drift compensation) can be CPU-intensive. Calibration for each sensor instance is critical.
/* Pattern 3 pseudocode: multi-sensor fusion and report */
typedef struct {
float temp_c;
float humidity_pct;
float pressure_hpa;
float co2_ppm;
} EnvRaw_t;
typedef struct {
float iaq_index; /* 0-500, IAQ standard */
float dew_point_c;
uint8_t comfort_level; /* 0=good, 1=moderate, 2=poor */
} EnvFused_t;
void sensor_fusion(const EnvRaw_t *raw, EnvFused_t *fused) {
fused->dew_point_c = compute_dew_point(raw->temp_c, raw->humidity_pct);
fused->iaq_index = compute_iaq(raw->co2_ppm, raw->humidity_pct,
raw->temp_c, raw->pressure_hpa);
fused->comfort_level = (fused->iaq_index < 50) ? 0 :
(fused->iaq_index < 100) ? 1 : 2;
}
void vFusionAgentTask(void *pvParam) {
EnvRaw_t raw;
EnvFused_t fused;
for (;;) {
sensor_read_all(&raw);
sensor_fusion(&raw, &fused);
/* Report fused result periodically; skip if unchanged */
if (fused_changed(&fused)) {
mqtt_publish_fused(TOPIC_TELEMETRY, &fused);
}
vTaskDelay(pdMS_TO_TICKS(10000)); /* 10-second report interval */
}
}
Key spec decision: Report on change, not on timer. If comfort_level has not changed in 10 seconds, do not publish. This drastically reduces broker load in stable environments.
Pattern 4 — Local Decision Then Actuate
What it does: The MCU agent makes an actuation decision locally without waiting for network confirmation. Actuation happens in the same tick as detection. The event is reported asynchronously; the action does not wait for the report to be acknowledged.
When to use it: When actuation latency is a hard requirement — a relay trip, motor stop, valve closure, or alarm that cannot tolerate the 50–500 ms round-trip to a cloud endpoint. Also valuable for offline resilience: the device must keep acting even if the network is down.
Tradeoffs: Local logic must be correct and safe. The off-device system may not have agreed with the action. Requires an audit trail — publish what was done and why — so the fleet management system can reconcile actions with its own model.
/* Pattern 4 pseudocode: local actuation with async audit */
void vActuationAgentTask(void *pvParam) {
float vibration_g;
bool relay_open = false;
for (;;) {
vibration_g = accel_rms_g();
/* LOCAL DECISION — no network round-trip */
if (vibration_g > EMERGENCY_G && !relay_open) {
gpio_set_level(RELAY_PIN, 0); /* trip relay immediately */
relay_open = true;
/* Async audit publish — does not block actuation */
mqtt_enqueue_event(TOPIC_EVENT,
"{\"action\":\"relay_trip\","
"\"reason\":\"vibration_exceeded\","
"\"value\":%.3f}", vibration_g);
}
if (vibration_g < RESET_G && relay_open) {
/* Only reset on explicit command from operator */
/* DO NOT auto-reset without external confirmation */
}
vTaskDelay(pdMS_TO_TICKS(10)); /* 10ms loop for safety-critical path */
}
}
Key spec decision: Never auto-reset a safety-critical actuator based on sensor normalization alone. Require an explicit operator command (received via MQTT cmd topic). The reset path needs a human in the loop.
Combining patterns
| Use case | Pattern combination |
|---|---|
| Industrial vibration monitoring | Pattern 1 (anomaly detect) + Pattern 4 (actuate on high-G emergency) |
| HVAC zone control | Pattern 3 (fuse temp + humidity + occupancy) + Pattern 4 (actuate damper) |
| Water leak detection | Pattern 2 (threshold + explanation) + Pattern 4 (valve closure) |
| Air quality monitoring | Pattern 3 (fuse env sensors) + Pattern 1 (anomaly on fused index) |
Platform example: ForestHub.ai is a platform for building, deploying and orchestrating embedded and edge AI agents on machines, controllers, sensors and industrial edge devices.
FAQ
Q: What sample rate do I need for Pattern 1 (anomaly detection)? It depends on the physical phenomenon. Acoustic emission from bearing damage: 20–50 kHz sample rate needed. Temperature drift in a process: 1 Hz is fine. Match the sample rate to the highest frequency component in your target event.
Q: In Pattern 4, how do I prevent spurious actuation? Use debounce (require the condition to persist for N consecutive samples), hysteresis (different thresholds for trip and clear), and a maximum actuation rate limiter (e.g., no more than 3 relay trips per hour).
Q: Can Pattern 3 (sensor fusion) include ML? Yes. A trained regression model taking raw sensor readings as input and outputting a fused metric is TinyML inside a fusion pattern. Compare with the Kalman filter approach: both are valid; the ML approach adapts automatically to sensor cross-sensitivity that is hard to model analytically.
Q: How do I test Pattern 4 without breaking hardware?
Replay captured sensor data through the actuation logic in a host-side unit test. Mock the gpio_set_level() call. Verify the state machine transitions, not the GPIO hardware.