Overview
Kinematrix provides a comprehensive calibration system for sensor accuracy. The V2 framework includes an interactive calibration interface with EEPROM persistence, supporting one-point, two-point, and multi-point calibration methods.Calibration Module Architecture
class SensorCalibrationModuleV2 : public SensorModuleV2 {
public:
// Calibration discovery
void discoverCalibrableValues();
void discoverNestedCalibrableValues();
// Manual calibration
void calibrateOnePoint(const char *sensorName, const char *valueKey, float knownValue);
void calibrateTwoPoint(const char *sensorName, const char *valueKey,
float knownValue1, float rawValue1,
float knownValue2, float rawValue2);
// Interactive calibration
void startCalibrationMode(Stream *serialPtr, uint32_t timeout = 300000);
void stopCalibrationMode();
// EEPROM persistence
bool saveAllCalibrations(int baseEepromAddress = 0);
bool loadAllCalibrations(int baseEepromAddress = 0);
CalibrationLoadResult loadAllCalibrationsWithStatus(int baseEepromAddress = 0);
// Value access
float getRawValue(const char *sensorName, const char *valueKey) const;
float getCalibratedValue(const char *sensorName, const char *valueKey) const;
};
Calibration Methods
One-Point Calibration
Often called “offset calibration,” this adjusts for a constant offset error.#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_CALIBRATION_MODULE_V2
#define ENABLE_SENSOR_ANALOG_V2
#include "Kinematrix.h"
SensorCalibrationModuleV2 calibrator;
AnalogSensV2 phSensor(A0, 5.0, 1023, [](BaseSensV2* s, int raw, float volt) {
float ph = volt * 3.5; // Initial rough conversion
s->updateValue("ph", ph);
});
void setup() {
Serial.begin(115200);
phSensor.addCustomValue("ph", "pH Level", "pH", 2, true);
calibrator.addSensor("ph", &phSensor);
calibrator.init();
// Place sensor in pH 7.0 buffer solution
Serial.println("Place sensor in pH 7.0 solution...");
delay(5000);
// Calibrate to known value of 7.0
calibrator.calibrateOnePoint("ph", "ph", 7.0);
Serial.println("Calibration complete!");
calibrator.saveAllCalibrations(0);
}
void loop() {
calibrator.update();
float rawPH = calibrator.getRawValue("ph", "ph");
float calibratedPH = calibrator.getCalibratedValue("ph", "ph");
Serial.print("Raw: ");
Serial.print(rawPH);
Serial.print(" | Calibrated: ");
Serial.println(calibratedPH);
delay(1000);
}
Two-Point Calibration
Corrects both offset and scale errors using two reference points.AnalogSensV2 tempSensor(A0, 5.0, 1023, [](BaseSensV2* s, int raw, float volt) {
float temp = (volt - 0.5) * 100.0; // LM35 basic conversion
s->updateValue("temperature", temp);
});
void setup() {
Serial.begin(115200);
tempSensor.addCustomValue("temperature", "Temperature", "°C", 2, true);
calibrator.addSensor("temp", &tempSensor);
calibrator.init();
// First calibration point: Ice water (0°C)
Serial.println("Place sensor in ice water (0°C)...");
delay(10000);
calibrator.update();
float raw1 = calibrator.getRawValue("temp", "temperature");
// Second calibration point: Boiling water (100°C)
Serial.println("Place sensor in boiling water (100°C)...");
delay(10000);
calibrator.update();
float raw2 = calibrator.getRawValue("temp", "temperature");
// Perform two-point calibration
calibrator.calibrateTwoPoint("temp", "temperature",
0.0, raw1, // Known value, raw reading at 0°C
100.0, raw2); // Known value, raw reading at 100°C
Serial.println("Two-point calibration complete!");
calibrator.saveAllCalibrations(0);
}
Multi-Point Calibration
For sensors with non-linear response, use multiple calibration points.void performMultiPointCalibration() {
// Start interactive calibration mode
calibrator.startCalibrationMode(&Serial, 300000); // 5 minute timeout
// User will be prompted via serial interface to:
// 1. Select sensor and value to calibrate
// 2. Choose calibration method (multi-point)
// 3. Add calibration points at different known values
// 4. System calculates polynomial fit
// 5. Save calibration to EEPROM
}
Interactive Calibration Mode
Starting Interactive Calibration
SensorCalibrationModuleV2 calibrator;
void setup() {
Serial.begin(115200);
// Add sensors
calibrator.addSensor("temp", &tempSensor);
calibrator.addSensor("ph", &phSensor);
calibrator.addSensor("pressure", &pressureSensor);
calibrator.init();
// Discover which values can be calibrated
calibrator.discoverCalibrableValues();
// Set EEPROM address and auto-save
calibrator.setEEPROMStartAddress(100);
calibrator.setAutoSaveCalibration(true);
// Load previous calibrations
calibrator.loadAllCalibrations(100);
// Start interactive mode
calibrator.startCalibrationMode(&Serial, 600000); // 10 minute timeout
}
void loop() {
calibrator.update();
if (!calibrator.isInCalibrationMode()) {
// Normal operation - use calibrated values
float temp = calibrator.getCalibratedValue("temp", "temperature");
float ph = calibrator.getCalibratedValue("ph", "ph");
// Process calibrated data...
}
}
Interactive Calibration Menu
When in calibration mode, users interact via Serial Monitor:=== Sensor Calibration Menu ===
Available Sensors:
1. temp.temperature (Temperature, °C)
2. ph.ph (pH Level, pH)
3. pressure.pressure (Pressure, kPa)
Commands:
r - Read current sensor value
1 - One-point calibration
2 - Two-point calibration
m - Multi-point calibration
s - Save calibration to EEPROM
l - Load calibration from EEPROM
d - Display calibration details
c - Clear calibration
x - Exit calibration mode
Enter command:
Calibration Engine
The underlying calibration engine supports multiple interpolation methods:class CalibrationEngine {
public:
// Calibration methods
bool calibrateOnePoint(float knownValue, float rawValue);
bool calibrateTwoPoint(float knownValue1, float rawValue1,
float knownValue2, float rawValue2);
// Multi-point calibration
void setMaxCalibrationPoints(uint8_t maxPoints);
bool addCalibrationPoint(float knownValue, float rawValue);
bool calculateCalibration();
// Interpolation methods
void setInterpolationMethod(uint8_t method);
void setPolynomialDegree(uint8_t degree);
// Apply calibration
float calibrate(float rawValue);
// Status
bool isCalibrated() const;
uint8_t getCalibrationMethod() const;
};
Interpolation Methods
// Linear interpolation (default for multi-point)
engine.setInterpolationMethod(INTERPOLATION_LINEAR);
// Polynomial fitting
engine.setInterpolationMethod(INTERPOLATION_POLYNOMIAL);
engine.setPolynomialDegree(2); // Quadratic
// Lookup table with linear interpolation
engine.setInterpolationMethod(INTERPOLATION_LOOKUP);
EEPROM Persistence
Saving Calibrations
SensorCalibrationModuleV2 calibrator;
void saveCalibrations() {
// Set base EEPROM address
calibrator.setEEPROMStartAddress(100);
// Save all calibrations
if (calibrator.saveAllCalibrations(100)) {
Serial.println("Calibrations saved successfully!");
} else {
Serial.println("Failed to save calibrations");
}
}
Loading Calibrations
void loadCalibrations() {
// Load with status information
CalibrationLoadResult result = calibrator.loadAllCalibrationsWithStatus(100);
Serial.print("Total entries: ");
Serial.println(result.totalEntries);
Serial.print("Successfully loaded: ");
Serial.println(result.successCount);
Serial.print("Not calibrated: ");
Serial.println(result.notCalibratedCount);
Serial.print("Errors: ");
Serial.println(result.errorCount);
}
Auto-Save Configuration
void setup() {
calibrator.setEEPROMStartAddress(100);
calibrator.setAutoSaveCalibration(true); // Auto-save after each calibration
calibrator.loadAllCalibrations(100);
}
Calibration Discovery
Automatic Value Discovery
void setup() {
// Add all sensors
calibrator.addSensor("temp", &tempSensor);
calibrator.addSensor("humidity", &humiditySensor);
calibrator.init();
// Discover all calibratable values
calibrator.discoverCalibrableValues();
// List discovered values
calibrator.listCalibrableValues();
}
Calibrable Sensor Values:
1. temp.temperature (Temperature, °C) [ENABLED]
2. temp.fahrenheit (Temperature, °F) [ENABLED]
3. humidity.humidity (Humidity, %) [ENABLED]
4. humidity.dewPoint (Dew Point, °C) [ENABLED]
Nested Value Discovery
For sensors with nested JSON structures:calibrator.discoverNestedCalibrableValues();
Calibration Control
Enable/Disable Calibration
// Enable calibration for specific value
calibrator.enableValueCalibration("temp", "temperature", true);
// Disable calibration for a value
calibrator.enableValueCalibration("temp", "temperature", false);
// Enable calibration for entire sensor
calibrator.enableSensorCalibration("temp", true);
// Enable/disable all calibrations
calibrator.enableAllCalibration(true);
Check Calibration Status
// Check if value calibration is enabled
if (calibrator.isValueCalibrationEnabled("temp", "temperature")) {
float calibrated = calibrator.getCalibratedValue("temp", "temperature");
} else {
float raw = calibrator.getRawValue("temp", "temperature");
}
// Check sensor calibration status
if (calibrator.isSensorCalibrationEnabled("temp")) {
Serial.println("Temperature sensor calibration is active");
}
Calibration Status Display
// List all sensor calibration status
calibrator.listSensorCalibrationStatus();
// Print detailed calibration status
calibrator.printCalibrationStatus();
Sensor Calibration Status:
temp:
temperature: CALIBRATED (2-point) [ENABLED]
fahrenheit: CALIBRATED (2-point) [ENABLED]
ph:
ph: CALIBRATED (1-point) [ENABLED]
pressure:
pressure: NOT CALIBRATED [ENABLED]
Advanced Calibration Features
Calibration Statistics
void showCalibrationStatistics() {
// Shows statistics over multiple readings
calibrator.startCalibrationMode(&Serial);
// In calibration menu, use 's' command to:
// - Display mean, standard deviation
// - Show min/max values
// - Calculate measurement stability
}
Calibration Profiles
// Save calibration to profile slot
calibrator.saveCalibrationProfile(1); // Profile 1
calibrator.saveCalibrationProfile(2); // Profile 2
// Load calibration from profile
calibrator.loadCalibrationProfile(1);
// List available profiles
calibrator.listCalibrationProfiles();
Calibration Curve Visualization
void visualizeCalibration() {
// In interactive mode, display ASCII calibration curve
calibrator.startCalibrationMode(&Serial);
// Use 'v' command to show curve:
// Input | Output
// -------|-------
// 0.0 | *
// 10.0 | *
// 20.0 | *
// 30.0 | *
}
Complete Calibration Example
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_CALIBRATION_MODULE_V2
#define ENABLE_SENSOR_ANALOG_V2
#include "Kinematrix.h"
SensorCalibrationModuleV2 calibrator;
// pH sensor with custom processing
AnalogSensV2 phSensor(A0, 5.0, 1023, [](BaseSensV2* s, int raw, float volt) {
float ph = volt * 3.5; // Basic conversion
s->updateValue("ph", ph);
});
// Temperature sensor with custom processing
AnalogSensV2 tempSensor(A1, 5.0, 1023, [](BaseSensV2* s, int raw, float volt) {
float tempC = (volt - 0.5) * 100.0;
float tempF = tempC * 9.0/5.0 + 32.0;
s->updateValue("celsius", tempC);
s->updateValue("fahrenheit", tempF);
});
void setup() {
Serial.begin(115200);
// Configure sensors
phSensor.addCustomValue("ph", "pH Level", "pH", 2, true);
phSensor.setUpdateInterval(1000);
tempSensor.addCustomValue("celsius", "Temperature", "°C", 2, true);
tempSensor.addCustomValue("fahrenheit", "Temperature", "°F", 2, true);
tempSensor.setUpdateInterval(1000);
// Add to calibrator
calibrator.addSensor("ph", &phSensor);
calibrator.addSensor("temp", &tempSensor);
calibrator.init();
// Configure EEPROM
calibrator.setEEPROMStartAddress(100);
calibrator.setAutoSaveCalibration(true);
// Load existing calibrations
CalibrationLoadResult result = calibrator.loadAllCalibrationsWithStatus(100);
Serial.print("Loaded ");
Serial.print(result.successCount);
Serial.println(" calibrations from EEPROM");
// Discover calibratable values
calibrator.discoverCalibrableValues();
calibrator.listCalibrableValues();
Serial.println("\n=== Commands ===");
Serial.println("c - Enter calibration mode");
Serial.println("s - Show calibration status");
Serial.println("r - Reset all calibrations");
}
void loop() {
calibrator.update();
// Handle serial commands
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'c':
Serial.println("Starting calibration mode...");
calibrator.startCalibrationMode(&Serial, 600000);
break;
case 's':
calibrator.printCalibrationStatus();
break;
case 'r':
// Reset would need to be implemented
Serial.println("Calibration reset not shown in example");
break;
}
}
// Display calibrated values
static unsigned long lastDisplay = 0;
if (millis() - lastDisplay >= 2000) {
float phRaw = calibrator.getRawValue("ph", "ph");
float phCal = calibrator.getCalibratedValue("ph", "ph");
float tempRaw = calibrator.getRawValue("temp", "celsius");
float tempCal = calibrator.getCalibratedValue("temp", "celsius");
Serial.println("\n=== Sensor Readings ===");
Serial.print("pH: ");
Serial.print(phRaw, 2);
Serial.print(" -> ");
Serial.print(phCal, 2);
Serial.println(" pH");
Serial.print("Temp: ");
Serial.print(tempRaw, 2);
Serial.print(" -> ");
Serial.print(tempCal, 2);
Serial.println(" °C");
lastDisplay = millis();
}
}
Best Practices
Calibration Frequency: Recalibrate sensors periodically:
- pH sensors: Monthly or after 100 hours of use
- Temperature sensors: Annually or when accuracy degrades
- Gas sensors: After significant environmental changes
Sensor Stability: Allow sensors to stabilize before calibration:
- Temperature sensors: 30-60 seconds
- pH sensors: 2-5 minutes
- Gas sensors: 5-10 minutes (or longer for some MQ sensors)
Reference Standards: Use high-quality reference standards:
- pH: Certified pH buffer solutions (pH 4.0, 7.0, 10.0)
- Temperature: Ice water (0°C), boiling water (100°C at sea level)
- Pressure: Calibrated pressure gauges
Troubleshooting
Calibration Not Saving
// Ensure EEPROM is properly initialized
#include <EEPROM.h>
void setup() {
EEPROM.begin(512); // ESP32/ESP8266
calibrator.setEEPROMStartAddress(100);
calibrator.saveAllCalibrations(100);
}
Calibration Not Applied
// Verify calibration is enabled
if (!calibrator.isValueCalibrationEnabled("temp", "temperature")) {
calibrator.enableValueCalibration("temp", "temperature", true);
}
Unstable Readings
// Use filtering with calibration
FilterParams params;
params.movingAverage.windowSize = 10;
sensors.attachFilter("temp", "temperature", FILTER_MOVING_AVERAGE, params);
float calibrated = calibrator.getCalibratedValue("temp", "temperature");
float filtered = sensors.getFilteredValue("temp", "temperature");
Next Steps
Filtering
Combine calibration with signal filtering
Alerts
Set alerts on calibrated values