Skip to main content

Overview

Learn how to build a production-ready data logger that combines multiple sensors with real-time clock timestamps, data filtering, and CSV output. This guide demonstrates the MHRTCSensV2 (Multi-Harmony RTC) sensor for timestamped data logging.

What You’ll Build

A complete data logger that:
  • Records temperature, humidity, and light sensor data
  • Adds timestamps to all readings using RTC
  • Applies moving average and exponential filters
  • Outputs data in CSV format for easy analysis
  • Provides serial commands for debugging

Hardware Requirements

  • ESP32, ESP8266, or Arduino board
  • MHRTC module (pins 4, 5, 6)
  • DHT22 temperature/humidity sensor (pin 2)
  • Light sensor on analog pin A0
  • Serial connection for data output

Complete Example

1

Enable Required Modules

#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_MHRTC_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_FILTER_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"
2

Create Sensor Module

SensorModuleV2 sensorModule;

void logData();  // Forward declaration
3

Initialize Sensors and RTC

void setup() {
    Serial.begin(115200);
    Serial.println("MHRTCSensV2 Data Logger Example");
    Serial.println("RTC with sensors for timestamped data logging");
    
    // Add sensors to the module
    sensorModule.addSensor("rtc", new MHRTCSensV2(4, 5, 6));
    sensorModule.addSensor("temp", new DHTSensV2(2, DHT22));
    sensorModule.addSensor("light", new AnalogSensV2(A0, 5.0, 1023));
    
    sensorModule.init();
    
    // Set initial time on RTC (year, month, day, hour, minute, second, dayOfWeek)
    MHRTCSensV2* rtcSensor = (MHRTCSensV2*)sensorModule.getSensorByName("rtc");
    if (rtcSensor) {
        rtcSensor->setDateTime(24, 6, 22, 10, 0, 0, 7);
        rtcSensor->startClock();
        Serial.println("RTC initialized");
    }
}
4

Configure Data Filters

Apply filters to smooth sensor readings:
// Configure moving average filter for temperature (5 sample window)
FilterParams tempFilter;
tempFilter.movingAverage.windowSize = 5;
sensorModule.attachFilter("temp", "temperature", FILTER_MOVING_AVERAGE, tempFilter);

// Configure exponential filter for light sensor (alpha = 0.2)
FilterParams lightFilter;
lightFilter.exponential.alpha = 0.2f;
sensorModule.attachFilter("light", "volt", FILTER_EXPONENTIAL, lightFilter);

Serial.println("CSV Header:");
Serial.println("Timestamp,DateTime,Temperature,Humidity,Light_Voltage");
5

Implement Main Loop

void loop() {
    sensorModule.update();
    
    // Log data every 5 seconds
    static unsigned long lastLog = 0;
    if (millis() - lastLog >= 5000) {
        logData();
        lastLog = millis();
    }
    
    // Handle serial commands
    if (Serial.available()) {
        String command = Serial.readStringUntil('\n');
        command.trim();
        
        if (command == "debug") {
            Serial.println("=== Debug Information ===");
            sensorModule.debugAll(true);
        } else if (command == "time") {
            MHRTCSensV2* rtcSensor = (MHRTCSensV2*)sensorModule.getSensorByName("rtc");
            if (rtcSensor) {
                Serial.print("Current time: ");
                Serial.println(rtcSensor->getFormattedDateTime("YYYY-MM-DD HH:mm:ss"));
            }
        } else if (command == "header") {
            Serial.println("Timestamp,DateTime,Temperature,Humidity,Light_Voltage");
        }
    }
    
    delay(100);
}
6

Create Data Logging Function

void logData() {
    if (sensorModule.getSensorByName("rtc")->isUpdated() || 
        sensorModule.getSensorByName("temp")->isUpdated() || 
        sensorModule.getSensorByName("light")->isUpdated()) {
        
        // Get timestamp from RTC
        uint32_t timestamp = sensorModule.getValue<int>("rtc", "timestamp");
        const char* dateTime = sensorModule.getValue<const char*>("rtc", "dateTimeString");
        
        // Get filtered temperature data
        float temperature = 0.0;
        float humidity = 0.0;
        if (sensorModule.hasFilter("temp", "temperature")) {
            temperature = sensorModule.getFilteredValue("temp", "temperature");
            humidity = sensorModule.getValue<float>("temp", "humidity");
        } else {
            temperature = sensorModule.getValue<float>("temp", "temperature");
            humidity = sensorModule.getValue<float>("temp", "humidity");
        }
        
        // Get filtered light voltage
        float lightVolt = 0.0;
        if (sensorModule.hasFilter("light", "volt")) {
            lightVolt = sensorModule.getFilteredValue("light", "volt");
        } else {
            lightVolt = sensorModule.getValue<float>("light", "volt");
        }
        
        // Output CSV format
        Serial.print(timestamp);
        Serial.print(",");
        Serial.print(dateTime);
        Serial.print(",");
        Serial.print(temperature, 2);
        Serial.print(",");
        Serial.print(humidity, 2);
        Serial.print(",");
        Serial.println(lightVolt, 3);
    }
}

Understanding Data Filters

Kinematrix provides built-in filters to smooth noisy sensor data:

Moving Average Filter

Averages the last N readings to reduce noise:
FilterParams tempFilter;
tempFilter.movingAverage.windowSize = 5;  // Average last 5 readings
sensorModule.attachFilter("temp", "temperature", FILTER_MOVING_AVERAGE, tempFilter);
Best for: Removing random noise, smoothing fluctuations

Exponential Filter

Weighted average favoring recent readings:
FilterParams lightFilter;
lightFilter.exponential.alpha = 0.2f;  // 20% new data, 80% old data
sensorModule.attachFilter("light", "volt", FILTER_EXPONENTIAL, lightFilter);
Best for: Responsive smoothing with minimal lag

Accessing Filtered Data

if (sensorModule.hasFilter("temp", "temperature")) {
    float filteredTemp = sensorModule.getFilteredValue("temp", "temperature");
}

Serial Commands

The example supports interactive debugging commands:
CommandDescription
debugPrint detailed sensor information
timeDisplay current RTC time
headerReprint CSV header

CSV Output Format

The logger outputs data in CSV format for easy import into spreadsheet applications:
Timestamp,DateTime,Temperature,Humidity,Light_Voltage
1719052800,2024-06-22 10:00:00,25.60,60.20,3.450
1719052805,2024-06-22 10:00:05,25.65,60.15,3.455
1719052810,2024-06-22 10:00:10,25.70,60.10,3.460

RTC Configuration

Setting Date and Time

MHRTCSensV2* rtcSensor = (MHRTCSensV2*)sensorModule.getSensorByName("rtc");
rtcSensor->setDateTime(year, month, day, hour, minute, second, dayOfWeek);
Example: Set to June 22, 2024, 10:00:00 AM (Saturday)
rtcSensor->setDateTime(24, 6, 22, 10, 0, 0, 7);

Formatting Date/Time Output

// Get formatted string
String formattedTime = rtcSensor->getFormattedDateTime("YYYY-MM-DD HH:mm:ss");
// Output: "2024-06-22 10:00:00"

Advanced Features

Power Monitoring Integration

Add power monitoring to your data logger:
sensorModule.addSensor("power", new INA219SensV2());

// In logData():
float voltage = sensorModule.getValue<float>("power", "voltage");
float current = sensorModule.getValue<float>("power", "current");
float power = sensorModule.getValue<float>("power", "power");

SD Card Storage

For persistent data logging, integrate with SD card:
#define ENABLE_MODULE_SDCARD_ESP32

SDCardModuleESP32 sdCard;

void setup() {
    sdCard.begin();
    sdCard.writeCSV("/data/sensors.csv", "Timestamp,Temp,Humidity", false);
}

void logData() {
    String csvLine = String(timestamp) + "," + 
                     String(temperature, 2) + "," + 
                     String(humidity, 2);
    sdCard.writeCSV("/data/sensors.csv", csvLine, true);
}

Alert System

Add threshold alerts to your logger:
#define ENABLE_SENSOR_ALERT_SYSTEM_V2

sensorModule.setThreshold("temp", "temperature", 20.0, 30.0, ALERT_OUTSIDE);

sensorModule.setGlobalAlertCallback([](AlertInfo alertInfo) {
    Serial.print("ALERT: ");
    Serial.print(alertInfo.sensorName);
    Serial.print(" = ");
    Serial.println(alertInfo.currentValue);
});

Troubleshooting

  • Check battery connection on RTC module
  • Verify pins 4, 5, 6 are correctly wired
  • Set initial time using setDateTime() after power loss
  • Check sensor wiring and power
  • Verify correct pin numbers in constructor
  • Ensure sufficient delay between readings (DHT22 needs 2 seconds)
  • Verify logData() is being called
  • Check isUpdated() conditions
  • Ensure sensors initialized successfully
  • Increase baud rate to 115200 for reliable output

Next Steps

LED Control

Trigger LEDs based on sensor thresholds

Multi-Platform Development

Deploy your logger to different hardware

Debugging

Advanced debugging techniques

Basic Sensor Reading

Review sensor fundamentals

Build docs developers (and LLMs) love