Skip to main content
Learn how to create robust multi-sensor systems that combine analog sensors, digital sensors, filtering, calibration, and alert systems.

Complete Environmental Monitoring System

Combine multiple sensor types for comprehensive environmental monitoring:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_RTC_DS3231_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_SENSOR_GP2Y_DUST_V2
#define ENABLE_SENSOR_MQ_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"

SensorModuleV2 sensorModule;

void setup() {
    Serial.begin(115200);
    Serial.println("RTC and Environmental Sensors Example");
    
    // Add multiple sensor types
    sensorModule.addSensor("rtc", new RTCDS3231SensV2());
    sensorModule.addSensor("bme680", new BME680SensV2());
    sensorModule.addSensor("dust", new GP2YDustSensV2(GP2Y1010AU0F, 7, A3));
    sensorModule.addSensor("mq2", new MQSensV2("Arduino", 5.0, 10, A2, "MQ-2"));
    
    sensorModule.init();
    
    // Enable RTC temperature reading
    RTCDS3231SensV2* rtc = (RTCDS3231SensV2*)sensorModule.getSensorByName("rtc");
    if (rtc) {
        rtc->enableTemperature(true);
    }
    
    Serial.println("RTC and environmental monitoring started");
}

void loop() {
    sensorModule.update();
    
    if (sensorModule.isUpdated("rtc")) {
        Serial.print("Time: ");
        Serial.print((int)sensorModule["rtc"]["hour"]);
        Serial.print(":");
        Serial.print((int)sensorModule["rtc"]["minute"]);
        Serial.print(":");
        Serial.println((int)sensorModule["rtc"]["second"]);
    }
    
    if (sensorModule.isUpdated("bme680")) {
        Serial.print("BME680 - Temp: ");
        Serial.print((float)sensorModule["bme680"]["temperature"]);
        Serial.print("°C, Humidity: ");
        Serial.print((float)sensorModule["bme680"]["humidity"]);
        Serial.println("%");
    }
    
    if (sensorModule.isUpdated("dust")) {
        Serial.print("Dust: ");
        Serial.print((float)sensorModule["dust"]["density"]);
        Serial.println(" μg/m³");
    }
    
    delay(2000);
}
Source: /home/daytona/workspace/source/example/sensors/SensorModuleV2/SensorModule/SensorModuleV2/RTCandEnvironmentalSensors/RTCandEnvironmentalSensors.ino:1

Multi-Sensor with Filters and Alerts

Combine different sensor types with advanced filtering:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_FILTER_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_SENSOR_MQ_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"

SensorModuleV2 sensorModule;

void setup() {
    Serial.begin(115200);
    Serial.println("Multi Sensor with Filters Example");
    
    // Add diverse sensor types
    sensorModule.addSensor("dht", new DHTSensV2(2, DHT22));
    sensorModule.addSensor("analog1", new AnalogSensV2(A0, 5.0, 1023));
    sensorModule.addSensor("analog2", new AnalogSensV2(A1, 3.3, 1023));
    sensorModule.addSensor("bme680", new BME680SensV2());
    sensorModule.addSensor("mq135", new MQSensV2("Arduino", 5.0, 10, A2, "MQ-135"));
    
    sensorModule.init();
    
    // Apply moving average filter to analog sensor
    FilterParams movingAvgParams;
    movingAvgParams.movingAverage.windowSize = 5;
    sensorModule.attachFilter("analog1", "volt", FILTER_MOVING_AVERAGE, movingAvgParams);
    
    // Apply Kalman filter to temperature
    FilterParams kalmanParams;
    kalmanParams.kalman.processNoise = 0.01;
    kalmanParams.kalman.measurementNoise = 0.1;
    sensorModule.attachFilter("dht", "temperature", FILTER_KALMAN, kalmanParams);
    
    Serial.println("Filters attached to sensors");
}

void loop() {
    sensorModule.update();
    
    if (sensorModule.isUpdated("dht")) {
        float temp = sensorModule.getFilteredValue("dht", "temperature");
        Serial.print("Filtered Temperature: ");
        Serial.println(temp);
    }
    
    if (sensorModule.isUpdated("analog1")) {
        float volt = sensorModule.getFilteredValue("analog1", "volt");
        Serial.print("Filtered Voltage: ");
        Serial.println(volt);
    }
    
    delay(2000);
}
Source: /home/daytona/workspace/source/example/sensors/SensorModuleV2/SensorModule/SensorModuleV2/MultiSensorWithFilters/MultiSensorWithFilters.ino:1

Advanced Calibration System

Manage calibration across multiple sensor types:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_CALIBRATION_MODULE_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_MLX90614_V2
#define ENABLE_SENSOR_INA219_V2
#include "Kinematrix.h"

SensorCalibrationModuleV2 calibrationModule;

void setup() {
    Serial.begin(115200);
    Serial.println("Multiple Calibration Types Example");
    
    // Add different sensor types
    calibrationModule.addSensor("analog1", new AnalogSensV2(A0, 5.0, 1023));
    calibrationModule.addSensor("analog2", new AnalogSensV2(A1, 3.3, 1023));
    calibrationModule.addSensor("analog3", new AnalogSensV2(A2, 5.0, 1023));
    calibrationModule.addSensor("mlx90614", new MLX90614SensV2());
    calibrationModule.addSensor("ina219", new INA219SensV2());
    
    calibrationModule.init();
    
    // Add calibration entries for each sensor value
    calibrationModule.addCalibrationEntry("analog1", "volt");
    calibrationModule.addCalibrationEntry("analog2", "volt");
    calibrationModule.addCalibrationEntry("analog3", "volt");
    calibrationModule.addCalibrationEntry("mlx90614", "objectTemp");
    calibrationModule.addCalibrationEntry("mlx90614", "ambientTemp");
    calibrationModule.addCalibrationEntry("ina219", "power");
    
    // Apply different calibration methods
    calibrationModule.calibrateOnePoint("analog1", "volt", 3.3);
    calibrationModule.calibrateTwoPoint("analog2", "volt", 1.0, 0.95, 4.0, 4.08);
    calibrationModule.calibrateOnePoint("analog3", "volt", 2.5);
    calibrationModule.calibrateTwoPoint("mlx90614", "objectTemp", 25.0, 24.8, 80.0, 80.5);
    calibrationModule.calibrateTwoPoint("mlx90614", "ambientTemp", 25.0, 24.8, 80.0, 80.5);
    calibrationModule.calibrateOnePoint("ina219", "power", 5.0);
    
    Serial.println("Multiple calibration types configured");
}

void loop() {
    calibrationModule.update();
    
    if (calibrationModule.isUpdated("analog1")) {
        Serial.print("A1 Linear - Raw: ");
        Serial.print((float)calibrationModule["analog1"]["volt"]);
        Serial.print("V, Cal: ");
        Serial.print((float)calibrationModule.getCalibratedValue("analog1", "volt"));
        Serial.println("V");
    }
    
    if (calibrationModule.isUpdated("analog2")) {
        Serial.print("A2 TwoPoint - Raw: ");
        Serial.print((float)calibrationModule["analog2"]["volt"]);
        Serial.print("V, Cal: ");
        Serial.print((float)calibrationModule.getCalibratedValue("analog2", "volt"));
        Serial.println("V");
    }
    
    if (calibrationModule.isUpdated("mlx90614")) {
        Serial.print("MLX Temp - Raw: ");
        Serial.print((float)calibrationModule["mlx90614"]["objectTemp"]);
        Serial.print("°C, Cal: ");
        Serial.print((float)calibrationModule.getCalibratedValue("mlx90614", "objectTemp"));
        Serial.println("°C");
    }
    
    delay(2000);
}
Source: /home/daytona/workspace/source/example/sensors/SensorModuleV2/SensorModule/SensorModuleCalibrationV2/MultipleCalibrationTypes/MultipleCalibrationTypes.ino:1

Multiple Abstract Sensors

Use abstract sensors for testing and simulation:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_ABSTRACT_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"

SensorModuleV2 sensorModule;

void setup() {
    Serial.begin(115200);
    Serial.println("Multiple AbstractSensV2 Example");
    
    // Different abstract sensor modes
    sensorModule.addSensor("random", new AbstractSensV2(0));              // Random values
    sensorModule.addSensor("fixed", new AbstractSensV2(1, 42.5));         // Fixed value
    sensorModule.addSensor("range1", new AbstractSensV2(2, 100.0));       // Range 0-100
    sensorModule.addSensor("range2", new AbstractSensV2(3, 20.0, 80.0));  // Range 20-80
    
    sensorModule.init();
    
    // Configure update intervals
    AbstractSensV2* randomSensor = (AbstractSensV2*)sensorModule.getSensorByName("random");
    if (randomSensor) randomSensor->setUpdateInterval(500);
    
    AbstractSensV2* fixedSensor = (AbstractSensV2*)sensorModule.getSensorByName("fixed");
    if (fixedSensor) fixedSensor->setUpdateInterval(1000);
    
    Serial.println("Multiple abstract sensors with different modes initialized");
}

void loop() {
    sensorModule.update();
    
    if (sensorModule.isUpdated("random")) {
        Serial.print("Random Mode - Value: ");
        Serial.print((float)sensorModule["random"]["value"]);
        Serial.print(", Sine: ");
        Serial.println((float)sensorModule["random"]["sine"]);
    }
    
    if (sensorModule.isUpdated("fixed")) {
        Serial.print("Fixed Mode - Value: ");
        Serial.print((float)sensorModule["fixed"]["value"]);
        Serial.print(", Triangle: ");
        Serial.println((float)sensorModule["fixed"]["triangle"]);
    }
    
    if (sensorModule.isUpdated("range1")) {
        Serial.print("Range1 (0-100) - Value: ");
        Serial.println((float)sensorModule["range1"]["value"]);
    }
    
    if (sensorModule.isUpdated("range2")) {
        Serial.print("Range2 (20-80) - Value: ");
        Serial.println((float)sensorModule["range2"]["value"]);
    }
    
    delay(200);
}
Source: /home/daytona/workspace/source/example/sensors/SensorModuleV2/SensorList/AbstractSensV2/MultipleAbstractSensors/MultipleAbstractSensors.ino:1

System Architecture Best Practices

1

Organize Sensors by Function

Group related sensors together:
// Environmental sensors
sensorModule.addSensor("temp_indoor", new DHTSensV2(2, DHT22));
sensorModule.addSensor("temp_outdoor", new BME680SensV2());

// Power monitoring
sensorModule.addSensor("power_main", new INA219SensV2());
sensorModule.addSensor("power_backup", new INA219SensV2(0x41));

// Analog inputs
sensorModule.addSensor("pressure", new AnalogSensV2(A0, 5.0, 1023));
sensorModule.addSensor("flow", new AnalogSensV2(A1, 5.0, 1023));
2

Apply Appropriate Filters

Choose filters based on sensor characteristics:
// Slow-changing values: Moving Average
FilterParams movingAvg;
movingAvg.movingAverage.windowSize = 10;
sensorModule.attachFilter("temp_indoor", "temperature", FILTER_MOVING_AVERAGE, movingAvg);

// Fast-changing values: Kalman
FilterParams kalman;
kalman.kalman.processNoise = 0.01;
kalman.kalman.measurementNoise = 0.1;
sensorModule.attachFilter("pressure", "volt", FILTER_KALMAN, kalman);
3

Set Up Alert Thresholds

Configure alerts for critical values:
// Temperature alerts (outside range)
sensorModule.setThreshold("temp_indoor", "temperature", 18.0, 26.0, ALERT_OUTSIDE);

// Pressure alerts (above threshold)
sensorModule.setThreshold("pressure", "volt", 4.5, 5.0, ALERT_ABOVE);

// Flow alerts (below threshold)
sensorModule.setThreshold("flow", "volt", 0.5, 1.0, ALERT_BELOW);
4

Calibrate for Accuracy

Apply calibration where precision matters:
calibrationModule.addCalibrationEntry("pressure", "volt");
calibrationModule.calibrateTwoPoint("pressure", "volt", 1.0, 0.98, 4.0, 4.05);

Performance Considerations

Update Intervals

Optimize sensor update rates based on requirements:
// Fast sensors (100ms)
analogSensor->setUpdateInterval(100);

// Medium sensors (1s)
dhtSensor->setUpdateInterval(1000);

// Slow sensors (5s)
bme680Sensor->setUpdateInterval(5000);

Memory Management

// Use pointers for dynamic sensor creation
BME680SensV2* bme = new BME680SensV2();
sensorModule.addSensor("bme680", bme);

// SensorModule manages memory automatically
// No need to delete manually

Checking Updates Efficiently

void loop() {
    sensorModule.update();  // Update all sensors once
    
    // Only process sensors that have new data
    if (sensorModule.isUpdated("sensor1")) {
        // Process sensor1 data
    }
    
    if (sensorModule.isUpdated("sensor2")) {
        // Process sensor2 data
    }
}

Complete System Example

Hardware Setup

ESP32 Connections:

I2C Bus:
- BME680: SDA=21, SCL=22, Addr=0x76
- RTC DS3231: SDA=21, SCL=22, Addr=0x68
- MLX90614: SDA=21, SCL=22, Addr=0x5A

Digital:
- DHT22: GPIO 2 (with 10kΩ pull-up)

Analog:
- Pressure Sensor: A0 (0-5V)
- Flow Sensor: A1 (0-5V)
- Gas Sensor MQ-135: A2 (0-5V)

Software Integration

#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_CALIBRATION_MODULE_V2
#define ENABLE_SENSOR_FILTER_V2
#define ENABLE_SENSOR_ALERT_SYSTEM_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_RTC_DS3231_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"

SensorCalibrationModuleV2 sensorSystem;

void setup() {
    Serial.begin(115200);
    
    // Add all sensors
    sensorSystem.addSensor("bme680", new BME680SensV2());
    sensorSystem.addSensor("dht22", new DHTSensV2(2, DHT22));
    sensorSystem.addSensor("rtc", new RTCDS3231SensV2());
    sensorSystem.addSensor("pressure", new AnalogSensV2(A0, 5.0, 4095));
    sensorSystem.addSensor("flow", new AnalogSensV2(A1, 5.0, 4095));
    
    sensorSystem.init();
    
    // Configure filters
    FilterParams kalman;
    kalman.kalman.processNoise = 0.01;
    kalman.kalman.measurementNoise = 0.1;
    sensorSystem.attachFilter("bme680", "temperature", FILTER_KALMAN, kalman);
    sensorSystem.attachFilter("pressure", "volt", FILTER_KALMAN, kalman);
    
    // Configure alerts
    sensorSystem.setThreshold("bme680", "temperature", 18.0, 28.0, ALERT_OUTSIDE);
    sensorSystem.setThreshold("pressure", "volt", 4.0, 5.0, ALERT_ABOVE);
    
    // Apply calibration
    sensorSystem.addCalibrationEntry("pressure", "volt");
    sensorSystem.calibrateTwoPoint("pressure", "volt", 1.0, 0.95, 4.0, 4.08);
    
    Serial.println("Multi-sensor system initialized");
}

void loop() {
    sensorSystem.update();
    
    // Process each sensor type
    if (sensorSystem.isUpdated("bme680")) {
        float temp = sensorSystem.getFilteredValue("bme680", "temperature");
        bool alert = sensorSystem.isAlertActive("bme680", "temperature");
        Serial.printf("BME680: %.2f°C [Alert: %s]\n", temp, alert ? "YES" : "NO");
    }
    
    if (sensorSystem.isUpdated("pressure")) {
        float cal = sensorSystem.getCalibratedValue("pressure", "volt");
        Serial.printf("Pressure: %.2fV (calibrated)\n", cal);
    }
    
    delay(1000);
}

Next Steps

Environmental Monitoring

Focus on environmental sensor applications

Analog Sensors

Deep dive into analog sensor processing

Digital Sensors

Master I2C and digital protocols

Build docs developers (and LLMs) love