Overview
The Sensor Module V2 includes a comprehensive alert system for monitoring sensor values against threshold limits. The system supports multiple alert types, customizable callbacks, debouncing, hysteresis, and alert acknowledgment.Alert System Architecture
enum AlertType {
ALERT_ABOVE, // Trigger when value > highThreshold
ALERT_BELOW, // Trigger when value < lowThreshold
ALERT_BETWEEN, // Trigger when lowThreshold < value < highThreshold
ALERT_OUTSIDE // Trigger when value outside [lowThreshold, highThreshold]
};
enum AlertState {
ALERT_INACTIVE, // No alert condition
ALERT_ACTIVE, // Alert currently triggered
ALERT_ACKNOWLEDGED // Alert acknowledged by user
};
struct AlertInfo {
char *sensorName;
char *valueKey;
float currentValue;
float lowThreshold;
float highThreshold;
AlertType type;
AlertState state;
uint32_t timeTriggered;
uint8_t repeatCount;
};
typedef void (*AlertCallback)(AlertInfo alertInfo);
Setting Threshold Alerts
Basic Alert Setup
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_ALERT_SYSTEM_V2
#define ENABLE_SENSOR_DHT_V2
#include "Kinematrix.h"
SensorModuleV2 sensors;
DHTSensV2 dht(2, DHT22);
void setup() {
Serial.begin(115200);
dht.setUpdateInterval(2000);
sensors.addSensor("climate", &dht);
sensors.init();
// Alert when temperature is outside 20-30°C range
sensors.setThreshold("climate", "temperature",
20.0, 30.0, ALERT_OUTSIDE);
// Alert when humidity drops below 30%
sensors.setThreshold("climate", "humidity",
30.0, 100.0, ALERT_BELOW);
}
void loop() {
sensors.update();
// Check alert status
if (sensors.isAlertActive("climate", "temperature")) {
Serial.println("Temperature alert active!");
}
}
Alert Types
ALERT_ABOVE
Triggers when value exceeds high threshold.// Alert when temperature exceeds 35°C
sensors.setThreshold("temp", "temperature",
0.0, 35.0, ALERT_ABOVE);
ALERT_BELOW
Triggers when value falls below low threshold.// Alert when battery voltage drops below 3.3V
sensors.setThreshold("battery", "voltage",
3.3, 5.0, ALERT_BELOW);
ALERT_BETWEEN
Triggers when value is within range.// Alert when pH is in optimal range (6.5-7.5)
sensors.setThreshold("water", "ph",
6.5, 7.5, ALERT_BETWEEN);
ALERT_OUTSIDE
Triggers when value is outside range (most common).// Alert when temperature is outside safe range
sensors.setThreshold("fridge", "temperature",
2.0, 8.0, ALERT_OUTSIDE);
Alert Callbacks
Global Alert Callback
Single callback for all alerts:void alertHandler(AlertInfo info) {
Serial.println("\n=== ALERT ===");
Serial.print("Sensor: ");
Serial.println(info.sensorName);
Serial.print("Value: ");
Serial.println(info.valueKey);
Serial.print("Current: ");
Serial.println(info.currentValue);
Serial.print("Range: ");
Serial.print(info.lowThreshold);
Serial.print(" - ");
Serial.println(info.highThreshold);
Serial.print("State: ");
Serial.println(info.state);
Serial.print("Time: ");
Serial.println(info.timeTriggered);
Serial.print("Repeat Count: ");
Serial.println(info.repeatCount);
}
void setup() {
// ... sensor setup ...
sensors.setGlobalAlertCallback(alertHandler);
}
Sensor-Specific Callbacks
Different callbacks for different sensors:void temperatureAlert(AlertInfo info) {
Serial.print("Temperature alert: ");
Serial.print(info.currentValue);
Serial.println("°C");
// Take action
if (info.type == ALERT_ABOVE) {
activateCooling();
} else {
activateHeating();
}
}
void humidityAlert(AlertInfo info) {
Serial.print("Humidity alert: ");
Serial.print(info.currentValue);
Serial.println("%");
activateDehumidifier();
}
void setup() {
// ... sensor setup ...
// Set sensor-specific callbacks
sensors.setSensorAlertCallback("climate_temp", temperatureAlert);
sensors.setSensorAlertCallback("climate_hum", humidityAlert);
}
Lambda Callbacks
void setup() {
// ... sensor setup ...
// Inline lambda callback
sensors.setGlobalAlertCallback([](AlertInfo info) {
digitalWrite(LED_PIN, info.state == ALERT_ACTIVE ? HIGH : LOW);
if (info.state == ALERT_ACTIVE) {
Serial.print("ALERT: ");
Serial.print(info.sensorName);
Serial.print(".");
Serial.print(info.valueKey);
Serial.print(" = ");
Serial.println(info.currentValue);
}
});
}
Alert Parameters
Hysteresis
Prevents alert oscillation at threshold boundaries.// Set default hysteresis for all alerts
sensors.setDefaultHysteresis(1000); // 1.0 unit (value * 1000)
// Set specific threshold with hysteresis
sensors.setThreshold("temp", "temperature", 20.0, 30.0, ALERT_OUTSIDE);
sensors.setThresholdParams("temp", "temperature",
2000, // Hysteresis: 2.0°C
0); // Debounce time: 0ms
- Alert triggers at threshold
- Alert clears only when value moves beyond threshold + hysteresis
- Prevents rapid on/off cycling
Threshold: 30°C
Hysteresis: 2°C
Trigger ALERT_ABOVE: When temp >= 30°C
Clear alert: When temp < 28°C (30 - 2)
Prevents alerts oscillating when temp hovers around 30°C
Debouncing
Delay alert trigger to avoid false alarms from noise.// Set default debounce time
sensors.setDefaultDebounceTime(5000); // 5 seconds
// Set specific debounce
sensors.setThreshold("sensor", "value", 100.0, 200.0, ALERT_OUTSIDE);
sensors.setThresholdParams("sensor", "value",
0, // No hysteresis
10000); // 10 second debounce
- Threshold condition must remain true for debounce period
- Alert only triggers after continuous violation
- Filters out brief spikes and noise
Threshold: > 100
Debounce: 5 seconds
Value exceeds 100 at t=0s
- Alert doesn't trigger yet
- System waits 5 seconds
At t=5s, if value still > 100:
- Alert triggers
If value drops below 100 before t=5s:
- Alert does not trigger
Alert Management
Checking Alert Status
void loop() {
sensors.update();
// Check if specific alert is active
if (sensors.isAlertActive("temp", "temperature")) {
Serial.println("Temperature alert!");
}
// Get detailed alert state
AlertState state = sensors.getAlertState("temp", "temperature");
switch (state) {
case ALERT_INACTIVE:
Serial.println("No alert");
break;
case ALERT_ACTIVE:
Serial.println("Alert active!");
break;
case ALERT_ACKNOWLEDGED:
Serial.println("Alert acknowledged");
break;
}
}
Acknowledging Alerts
Acknowledge alerts to prevent repeated notifications:void loop() {
sensors.update();
if (sensors.isAlertActive("temp", "temperature")) {
AlertState state = sensors.getAlertState("temp", "temperature");
if (state == ALERT_ACTIVE) {
// First time alert triggered
notifyUser();
// Acknowledge to prevent repeated notifications
sensors.acknowledgeAlert("temp", "temperature");
}
}
// Button press to acknowledge all alerts
if (digitalRead(ACK_BUTTON) == LOW) {
sensors.acknowledgeAllAlerts();
Serial.println("All alerts acknowledged");
}
}
Resetting Alerts
Clear alert state manually:// Reset specific alert
sensors.resetAlert("temp", "temperature");
// Reset all alerts
sensors.resetAllAlerts();
Removing Alerts
// Remove specific threshold
sensors.removeThreshold("temp", "temperature");
// Remove all thresholds
sensors.removeAllThresholds();
// Clear all callbacks
sensors.clearAlertCallbacks();
Complete Alert Example
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_ALERT_SYSTEM_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_SENSOR_ANALOG_V2
#include "Kinematrix.h"
SensorModuleV2 sensors;
DHTSensV2 dht(2, DHT22);
BME680SensV2 bme680;
AnalogSensV2 battery(A0, 5.0, 1023);
const int BUZZER_PIN = 13;
const int LED_RED = 12;
const int LED_YELLOW = 11;
const int ACK_BUTTON = 10;
bool buzzerActive = false;
void criticalAlert(AlertInfo info) {
// Critical alerts: activate buzzer
Serial.println("\n*** CRITICAL ALERT ***");
Serial.print(info.sensorName);
Serial.print(".");
Serial.print(info.valueKey);
Serial.print(": ");
Serial.println(info.currentValue);
digitalWrite(LED_RED, HIGH);
buzzerActive = true;
}
void warningAlert(AlertInfo info) {
// Warning alerts: LED only
Serial.println("\n- Warning -");
Serial.print(info.sensorName);
Serial.print(".");
Serial.print(info.valueKey);
Serial.print(": ");
Serial.println(info.currentValue);
digitalWrite(LED_YELLOW, HIGH);
}
void setup() {
Serial.begin(115200);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(ACK_BUTTON, INPUT_PULLUP);
// Configure sensors
dht.setUpdateInterval(2000);
bme680.setUpdateInterval(5000);
battery.setUpdateInterval(10000);
sensors.addSensor("climate", &dht);
sensors.addSensor("air", &bme680);
sensors.addSensor("power", &battery);
sensors.init();
// Configure alert system
sensors.setDefaultHysteresis(1000); // 1.0 unit hysteresis
sensors.setDefaultDebounceTime(3000); // 3 second debounce
// Critical alerts
sensors.setThreshold("climate", "temperature", 15.0, 35.0, ALERT_OUTSIDE);
sensors.setSensorAlertCallback("climate", criticalAlert);
sensors.setThreshold("power", "volt", 3.3, 5.0, ALERT_BELOW);
sensors.setSensorAlertCallback("power", criticalAlert);
// Warning alerts
sensors.setThreshold("climate", "humidity", 30.0, 70.0, ALERT_OUTSIDE);
sensors.setSensorAlertCallback("air", warningAlert);
sensors.setThreshold("air", "pressure", 980.0, 1050.0, ALERT_OUTSIDE);
Serial.println("Alert system initialized");
Serial.println("Press button to acknowledge alerts");
}
void loop() {
sensors.update();
// Handle buzzer
if (buzzerActive) {
// Beep pattern
static unsigned long lastBeep = 0;
if (millis() - lastBeep > 1000) {
tone(BUZZER_PIN, 2000, 200);
lastBeep = millis();
}
}
// Acknowledge button
static bool lastButtonState = HIGH;
bool buttonState = digitalRead(ACK_BUTTON);
if (buttonState == LOW && lastButtonState == HIGH) {
sensors.acknowledgeAllAlerts();
buzzerActive = false;
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
Serial.println("\nAll alerts acknowledged");
}
lastButtonState = buttonState;
// Display status
static unsigned long lastDisplay = 0;
if (millis() - lastDisplay > 5000) {
Serial.println("\n=== Status ===");
float temp = sensors.getValue<float>("climate", "temperature");
Serial.print("Temperature: ");
Serial.print(temp);
Serial.print("°C ");
if (sensors.isAlertActive("climate", "temperature")) {
Serial.println("[ALERT]");
} else {
Serial.println("[OK]");
}
float humidity = sensors.getValue<float>("climate", "humidity");
Serial.print("Humidity: ");
Serial.print(humidity);
Serial.print("% ");
if (sensors.isAlertActive("climate", "humidity")) {
Serial.println("[ALERT]");
} else {
Serial.println("[OK]");
}
float pressure = sensors.getValue<float>("air", "pressure");
Serial.print("Pressure: ");
Serial.print(pressure);
Serial.print(" hPa ");
if (sensors.isAlertActive("air", "pressure")) {
Serial.println("[ALERT]");
} else {
Serial.println("[OK]");
}
float battery = sensors.getValue<float>("power", "volt");
Serial.print("Battery: ");
Serial.print(battery);
Serial.print(" V ");
if (sensors.isAlertActive("power", "volt")) {
Serial.println("[ALERT]");
} else {
Serial.println("[OK]");
}
lastDisplay = millis();
}
}
Alert Use Cases
Environmental Monitoring
// Greenhouse monitoring
sensors.setThreshold("greenhouse", "temperature", 18.0, 28.0, ALERT_OUTSIDE);
sensors.setThreshold("greenhouse", "humidity", 50.0, 80.0, ALERT_OUTSIDE);
sensors.setThreshold("greenhouse", "soilMoisture", 30.0, 70.0, ALERT_OUTSIDE);
sensors.setGlobalAlertCallback([](AlertInfo info) {
if (strcmp(info.valueKey, "temperature") == 0) {
if (info.currentValue < info.lowThreshold) {
activateHeater();
} else {
activateVentilation();
}
} else if (strcmp(info.valueKey, "soilMoisture") == 0) {
if (info.currentValue < info.lowThreshold) {
activateIrrigation();
}
}
});
Equipment Protection
// Server room monitoring
sensors.setThreshold("server_room", "temperature", 18.0, 25.0, ALERT_OUTSIDE);
sensors.setThreshold("server_room", "humidity", 40.0, 60.0, ALERT_OUTSIDE);
// Immediate shutdown on critical temperature
sensors.setSensorAlertCallback("server_room", [](AlertInfo info) {
if (strcmp(info.valueKey, "temperature") == 0 &&
info.currentValue > 30.0) {
emergencyShutdown();
sendAlertEmail("Critical temperature!");
}
});
Process Control
// Fermentation tank
sensors.setThreshold("tank", "temperature", 20.0, 22.0, ALERT_OUTSIDE);
sensors.setThreshold("tank", "ph", 4.5, 5.5, ALERT_OUTSIDE);
sensors.setThreshold("tank", "pressure", 0.9, 1.1, ALERT_OUTSIDE);
sensors.setThresholdParams("tank", "temperature", 500, 2000); // 0.5°C hysteresis, 2s debounce
sensors.setThresholdParams("tank", "ph", 100, 5000); // 0.1 pH hysteresis, 5s debounce
Safety Systems
// Gas leak detection
sensors.setThreshold("gas", "ppm", 0.0, 1000.0, ALERT_ABOVE);
sensors.setSensorAlertCallback("gas", [](AlertInfo info) {
if (info.state == ALERT_ACTIVE) {
// Immediate action
closeGasValve();
activateAlarm();
notifyEmergency();
}
});
Best Practices
Use Hysteresis: Always set hysteresis to prevent alert oscillation, especially for noisy sensors.
sensors.setDefaultHysteresis(1000); // 1.0 unit
Debounce for Safety: Use debouncing for non-critical alerts to avoid false alarms, but minimize or disable for safety-critical alerts.
// Non-critical: use debounce
sensors.setThresholdParams("temp", "temperature", 1000, 5000);
// Critical: no debounce
sensors.setThresholdParams("gas", "ppm", 0, 0);
Combine with Filtering: Use filtered values for alerts to reduce noise:
FilterParams params;
params.movingAverage.windowSize = 5;
sensors.attachFilter("sensor", "value", FILTER_MOVING_AVERAGE, params);
sensors.setThreshold("sensor", "value", 10.0, 20.0, ALERT_OUTSIDE);
// Alert system automatically uses filtered values
Troubleshooting
Alerts Not Triggering
// Check if alert system is enabled
if (!sensors.hasAlertSystem()) {
Serial.println("Alert system not initialized!");
}
// Verify threshold is set
AlertState state = sensors.getAlertState("sensor", "value");
if (state == ALERT_INACTIVE) {
// Check sensor value
float value = sensors.getValue<float>("sensor", "value");
Serial.print("Current value: ");
Serial.println(value);
}
Too Many False Alarms
// Increase debounce time
sensors.setDefaultDebounceTime(10000); // 10 seconds
// Add filtering
FilterParams params;
params.median.windowSize = 7; // Remove spikes
sensors.attachFilter("sensor", "value", FILTER_MEDIAN, params);
Alerts Not Clearing
// Increase hysteresis
sensors.setThresholdParams("sensor", "value",
5000, // Larger hysteresis
0);
// Or manually reset
sensors.resetAlert("sensor", "value");
Next Steps
Filtering
Reduce noise before alert detection
Calibration
Ensure accurate threshold detection