Skip to main content

Overview

Effective debugging is crucial for embedded systems development. Kinematrix provides powerful debugging tools including SerialDebuggerV2 for comprehensive logging and built-in sensor diagnostics.

SerialDebuggerV2

The SerialDebuggerV2 class provides advanced debugging with log levels, filtering, timestamps, and memory monitoring.

Quick Start

1

Enable Debug Module

#define ENABLE_MODULE_SERIAL_DEBUGGER_V2
#include "Kinematrix.h"
2

Create Debugger Instance

SerialDebuggerV2 debug(115200);  // Baud rate
3

Initialize in setup()

void setup() {
    debug.begin();
    debug.setPrefix("[APP]");
    debug.showTimestamp(true);
    debug.showMillis(true);
    debug.showFreeMemory(true);
}
4

Use in Your Code

void loop() {
    debug.info("Starting sensor read", true);
    
    float temperature = readTemperature();
    debug.print("Temperature", temperature, INFO_LEVEL, true);
    
    if (temperature > 30.0) {
        debug.warning("High temperature detected!", true);
    }
    
    delay(1000);
}

Log Levels

SerialDebuggerV2 supports hierarchical log levels:
// Built-in log levels
debug.debug("Detailed debugging info", true);      // DEBUG
debug.info("General information", true);           // INFO
debug.warning("Warning message", true);            // WARNING
debug.error("Error occurred!", true);              // ERROR

// Set minimum log level
debug.setLogLevel(INFO);  // Only show INFO, WARNING, ERROR

Custom Log Levels

Create application-specific log levels:
// Create custom log levels
CustomLogLevel SENSOR = debug.createLogLevel("SENSOR");
CustomLogLevel NETWORK = debug.createLogLevel("NETWORK");
CustomLogLevel TIMING = debug.createLogLevel("TIMING");

// Use custom levels
debug.log("DHT22 reading complete", SENSOR, true);
debug.log("WiFi connected", NETWORK, true);
debug.log("Loop took 15ms", TIMING, true);

Debugging Sensor Issues

Sensor Diagnostics

Use the built-in debugAll() method to inspect all sensors:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_UTILITY_V2
#include "Kinematrix.h"

SensorModuleV2 sensors;

void setup() {
    Serial.begin(115200);
    
    sensors.addSensor("temp", new DHTSensV2(2, DHT22));
    sensors.addSensor("light", new AnalogSensV2(A0, 5.0, 1023));
    sensors.init();
}

void loop() {
    sensors.update();
    
    // Debug all sensors when 'd' is pressed
    if (Serial.available() && Serial.read() == 'd') {
        Serial.println("=== SENSOR DIAGNOSTICS ===");
        sensors.debugAll(true);  // Detailed output
    }
    
    delay(100);
}

Output Example:

=== SENSOR DIAGNOSTICS ===
Sensor: temp
  Type: DHTSensV2
  Updated: Yes
  Values:
    temperature: 25.60°C
    humidity: 60.20%
    heatIndex: 26.10°C

Sensor: light
  Type: AnalogSensV2
  Updated: Yes
  Values:
    raw: 512
    volt: 2.50V

Multi-Value Debugging

Debug multiple values in a single line:
// Single value
debug.print("Temperature", temperature, DEBUG_LEVEL, true);

// Two values
debug.print("Temp", temperature, "Humidity", humidity, DEBUG_LEVEL, true);

// Three values
debug.print("Temp", temperature, 
            "Humidity", humidity,
            "Pressure", pressure, DEBUG_LEVEL, true);

// Four values
debug.print("Temp", temperature,
            "Humidity", humidity,
            "Pressure", pressure,
            "Altitude", altitude, DEBUG_LEVEL, true);

Advanced Debugging Features

Array Debugging

Print arrays and matrices:
int sensorReadings[] = {512, 523, 498, 507, 515};
debug.printArray("Readings", sensorReadings, 5, DEBUG_LEVEL, true);

// Output: Readings[5]: {512, 523, 498, 507, 515}

Memory Monitoring

Track memory usage:
void setup() {
    debug.begin();
    debug.showFreeMemory(true);  // Show in every log message
}

void loop() {
    debug.memoryInfo(INFO_LEVEL);  // Detailed memory report
    delay(5000);
}

Execution Time Profiling

Measure function execution time:
void loop() {
    debug.startTimer();
    
    // Code to profile
    sensors.update();
    processData();
    
    debug.logExecutionTime("Main loop", DEBUG_LEVEL);
    // Output: Main loop took 15ms
    
    delay(1000);
}

Section Formatting

Organize debug output:
void loop() {
    debug.section("=== Sensor Reading ===", INFO_LEVEL);
    
    debug.startPrint(DEBUG_LEVEL);
    debug.continuePrint("Temperature", temperature, DEBUG_LEVEL);
    debug.continuePrint("Humidity", humidity, DEBUG_LEVEL);
    debug.endPrint(DEBUG_LEVEL, true);
    
    debug.separator(DEBUG_LEVEL);
    
    delay(1000);
}

Complete Debugging Example

Here’s a comprehensive example combining multiple debugging techniques:
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_DHT_V2
#define ENABLE_SENSOR_ANALOG_V2
#define ENABLE_SENSOR_ALERT_SYSTEM_V2
#define ENABLE_SENSOR_UTILITY_V2
#define ENABLE_MODULE_SERIAL_DEBUGGER_V2
#include "Kinematrix.h"

SensorModuleV2 sensors;
SerialDebuggerV2 debug(115200);

// Custom log levels
CustomLogLevel SENSOR_LOG;
CustomLogLevel ALERT_LOG;

void setup() {
    // Initialize debugger
    debug.begin();
    debug.setPrefix("[SYSTEM]");
    debug.showTimestamp(true);
    debug.showMillis(true);
    debug.showFreeMemory(true);
    debug.setLogLevel(DEBUG);  // Show all logs
    
    // Create custom log levels
    SENSOR_LOG = debug.createLogLevel("SENSOR");
    ALERT_LOG = debug.createLogLevel("ALERT");
    
    debug.section("System Initialization", INFO_LEVEL);
    
    // Initialize sensors
    sensors.addSensor("temp", new DHTSensV2(2, DHT22));
    sensors.addSensor("voltage", new AnalogSensV2(A0, 5.0, 1023));
    sensors.init();
    
    debug.info("Sensors initialized", true);
    
    // Configure alerts
    sensors.setGlobalAlertCallback([](AlertInfo alertInfo) {
        debug.log("ALERT: " + alertInfo.sensorName + "." + 
                  alertInfo.valueKey + " = " + 
                  String(alertInfo.currentValue), ALERT_LOG, true);
    });
    
    sensors.setThreshold("temp", "temperature", 20.0, 30.0, ALERT_OUTSIDE);
    debug.info("Alert thresholds configured", true);
    
    debug.memoryInfo(INFO_LEVEL);
    debug.separator(INFO_LEVEL);
}

void loop() {
    debug.startTimer();
    
    sensors.update();
    
    if (sensors.isUpdated("temp")) {
        float temp = sensors.getValue<float>("temp", "temperature");
        float humidity = sensors.getValue<float>("temp", "humidity");
        
        debug.log("DHT22 updated", SENSOR_LOG, true);
        debug.print("Temperature", temp,
                   "Humidity", humidity, DEBUG_LEVEL, true);
        
        if (temp > 30.0) {
            debug.warning("Temperature exceeds threshold!", true);
        }
    }
    
    if (sensors.isUpdated("voltage")) {
        float voltage = sensors.getValue<float>("voltage", "volt");
        int raw = sensors.getValue<int>("voltage", "raw");
        
        debug.log("Voltage sensor updated", SENSOR_LOG, true);
        debug.print("Voltage", voltage,
                   "Raw", raw, DEBUG_LEVEL, true);
    }
    
    // Handle serial commands
    if (Serial.available()) {
        String cmd = Serial.readStringUntil('\n');
        cmd.trim();
        
        if (cmd == "debug") {
            debug.section("=== Full Debug Output ===", INFO_LEVEL);
            sensors.debugAll(true);
            debug.memoryInfo(INFO_LEVEL);
        } else if (cmd == "reset") {
            debug.warning("Resetting system...", true);
            ESP.restart();
        } else if (cmd == "mem") {
            debug.memoryInfo(INFO_LEVEL);
        } else {
            debug.error("Unknown command: " + cmd, true);
        }
    }
    
    debug.logExecutionTime("Loop", DEBUG_LEVEL);
    delay(1000);
}

Serial Commands for Debugging

Implement interactive debugging commands:
void handleSerialCommands() {
    if (Serial.available()) {
        String cmd = Serial.readStringUntil('\n');
        cmd.trim();
        
        if (cmd == "help") {
            Serial.println("Available commands:");
            Serial.println("  debug  - Show all sensor data");
            Serial.println("  mem    - Show memory info");
            Serial.println("  reset  - Reset system");
            Serial.println("  time   - Show uptime");
        }
        else if (cmd == "debug") {
            sensors.debugAll(true);
        }
        else if (cmd == "mem") {
            debug.memoryInfo(INFO_LEVEL);
        }
        else if (cmd == "reset") {
            ESP.restart();
        }
        else if (cmd == "time") {
            Serial.print("Uptime: ");
            Serial.print(millis() / 1000);
            Serial.println(" seconds");
        }
    }
}

Platform-Specific Debugging

ESP32 Debug Output

#if defined(ESP32)
    debug.systemInfo(INFO_LEVEL);
    // Shows: Chip model, cores, frequency, flash size, etc.
#endif

Memory Debugging

void checkMemory() {
    #if defined(ESP32)
        Serial.print("Free heap: ");
        Serial.println(ESP.getFreeHeap());
        Serial.print("Heap size: ");
        Serial.println(ESP.getHeapSize());
    #elif defined(ESP8266)
        Serial.print("Free heap: ");
        Serial.println(ESP.getFreeHeap());
    #else
        extern int __heap_start, *__brkval;
        int v;
        int freeMemory = (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
        Serial.print("Free RAM: ");
        Serial.println(freeMemory);
    #endif
}

Common Debugging Scenarios

Scenario 1: Sensor Not Updating

void debugSensorUpdate() {
    debug.section("Sensor Update Debug", DEBUG_LEVEL);
    
    // Check if sensor exists
    if (sensors.getSensorByName("temp") == nullptr) {
        debug.error("Sensor 'temp' not found!", true);
        return;
    }
    
    // Check update status
    bool updated = sensors.isUpdated("temp");
    debug.print("Updated", updated ? "YES" : "NO", DEBUG_LEVEL, true);
    
    // Check raw sensor data
    sensors.debugAll(true);
}

Scenario 2: I2C Communication Issues

void scanI2CDevices() {
    debug.info("Scanning I2C bus...", true);
    
    byte error, address;
    int deviceCount = 0;
    
    for (address = 1; address < 127; address++) {
        Wire.beginTransmission(address);
        error = Wire.endTransmission();
        
        if (error == 0) {
            debug.print("I2C device", address, DEBUG_LEVEL, true);
            deviceCount++;
        }
    }
    
    debug.print("Total devices", deviceCount, INFO_LEVEL, true);
}

Scenario 3: Timing Issues

void debugTiming() {
    unsigned long start = micros();
    
    sensors.update();
    
    unsigned long elapsed = micros() - start;
    debug.print("Update time (us)", elapsed, DEBUG_LEVEL, true);
    
    if (elapsed > 10000) {
        debug.warning("Slow sensor update!", true);
    }
}

Best Practices

Use Log Levels

Organize debug output with appropriate log levels for filtering

Add Timestamps

Enable timestamps to track when events occur

Monitor Memory

Watch memory usage to prevent out-of-memory crashes

Profile Performance

Use execution time logging to identify bottlenecks

Troubleshooting Tips

  • Verify debug.begin() is called
  • Check baud rate matches Serial Monitor
  • Ensure log level allows the message
  • Try debug.enable() explicitly
  • Verify baud rate consistency (115200)
  • Check for buffer overflows
  • Reduce debug output frequency
  • Use debug.enableBuffering(true)
  • Reduce buffer sizes
  • Disable memory monitoring
  • Use lighter debug levels
  • Check for stack overflow

Next Steps

Basic Sensor Reading

Apply debugging to sensor applications

IoT Data Logger

Debug data logging systems

Multi-Platform Development

Platform-specific debugging

LED Control

Debug I2C and LED issues

Build docs developers (and LLMs) love