Device Calibration
Device Calibration API
Section titled “Device Calibration API”Remote calibration endpoint for managing BCGMCU calibration procedures and validation.
Endpoint
Section titled “Endpoint”POST /api/v1/device/calibration/{device_id}Purpose
Section titled “Purpose”- Execute remote calibration procedures
- Validate and adjust sensor accuracy
- Maintain measurement consistency
- Optimize signal processing parameters
Request
Section titled “Request”Headers
Section titled “Headers”Content-Type: application/jsonAuthorization: Bearer {device_token}Request Body
Section titled “Request Body”{ "device_id": "BCGMCU_001", "calibration": { "procedure": "baseline_calibration", "duration_seconds": 300, "reference_data": { "expected_pulse_range": [60, 100], "expected_breathing_range": [12, 20], "environment_type": "hospital_bed" }, "auto_apply": true }, "timestamp": "2025-01-27T10:30:00.000Z"}Field Descriptions
Section titled “Field Descriptions”Calibration Object
Section titled “Calibration Object”| Field | Type | Required | Description |
|---|---|---|---|
| procedure | string | Yes | Calibration procedure type |
| duration_seconds | integer | Yes | Calibration duration (60-1800) |
| reference_data | object | No | Expected measurement ranges |
| auto_apply | boolean | No | Automatically apply results |
Calibration Procedures
Section titled “Calibration Procedures”1. Baseline Calibration
Section titled “1. Baseline Calibration”Establishes measurement baseline with no subject present.
{ "procedure": "baseline_calibration", "duration_seconds": 120, "reference_data": { "environment_type": "hospital_bed", "expected_noise_level": "low" }, "auto_apply": true}2. Sensitivity Calibration
Section titled “2. Sensitivity Calibration”Adjusts sensor sensitivity for optimal signal detection.
{ "procedure": "sensitivity_calibration", "duration_seconds": 300, "reference_data": { "expected_pulse_range": [60, 100], "expected_breathing_range": [12, 20], "subject_weight_kg": 70 }, "auto_apply": false}3. Validation Test
Section titled “3. Validation Test”Validates current calibration accuracy without changes.
{ "procedure": "validation_test", "duration_seconds": 180, "reference_data": { "reference_pulse_bpm": 72, "reference_breathing_rpm": 16, "tolerance_percent": 5 }, "auto_apply": false}4. Factory Reset Calibration
Section titled “4. Factory Reset Calibration”Restores factory calibration settings.
{ "procedure": "factory_reset", "duration_seconds": 60, "reference_data": {}, "auto_apply": true}Response
Section titled “Response”Success Response (200 OK)
Section titled “Success Response (200 OK)”{ "calibration_status": "started", "procedure_id": "cal_baseline_001", "estimated_completion": "2025-01-27T10:35:00.000Z", "current_phase": "data_collection", "progress_percent": 0, "preliminary_results": null}Response Fields
Section titled “Response Fields”| Field | Type | Description |
|---|---|---|
| calibration_status | string | ”started”, “in_progress”, “completed”, “failed” |
| procedure_id | string | Unique calibration procedure identifier |
| estimated_completion | string | Expected completion timestamp |
| current_phase | string | Current calibration phase |
| progress_percent | integer | Completion percentage (0-100) |
| preliminary_results | object | Early results if available |
Completion Response
Section titled “Completion Response”{ "calibration_status": "completed", "procedure_id": "cal_baseline_001", "completion_time": "2025-01-27T10:35:00.000Z", "results": { "baseline_established": true, "noise_level": 0.002, "signal_quality": "excellent", "calibration_factors": { "pulse_sensitivity": 1.05, "breathing_sensitivity": 0.98, "noise_threshold": 0.01 } }, "applied": true, "validation_score": 95}Implementation Example
Section titled “Implementation Example”ESP32 Calibration Manager
Section titled “ESP32 Calibration Manager”#include <ArduinoJson.h>#include <BCGMCU.h>
class CalibrationManager {private: struct CalibrationSession { String procedureId; String procedureType; unsigned long startTime; int durationSeconds; JsonObject referenceData; bool autoApply; bool active;
// Results float baselineNoise = 0; float signalAmplitude = 0; float pulseSensitivity = 1.0; float breathingSensitivity = 1.0; int validationScore = 0; };
CalibrationSession currentSession; std::vector<float> calibrationSamples; BCGMCU bcgmcu;
public: void startCalibration(JsonObject& request) { JsonObject cal = request["calibration"];
// Initialize session currentSession.procedureId = generateProcedureId(); currentSession.procedureType = cal["procedure"]; currentSession.startTime = millis(); currentSession.durationSeconds = cal["duration_seconds"]; currentSession.referenceData = cal["reference_data"]; currentSession.autoApply = cal["auto_apply"] | false; currentSession.active = true;
// Clear previous data calibrationSamples.clear();
// Configure BCGMCU for calibration configureBCGMCUForCalibration();
// Send initial response sendCalibrationStatus("started");
Serial.printf("Started %s calibration (ID: %s)\n", currentSession.procedureType.c_str(), currentSession.procedureId.c_str()); }
void processCalibration() { if (!currentSession.active) return;
unsigned long elapsed = (millis() - currentSession.startTime) / 1000; int progress = min(100, (int)(100 * elapsed / currentSession.durationSeconds));
// Collect calibration data BCGMCU::RawSample sample = bcgmcu.getRawSample(); calibrationSamples.push_back(sample.magnitude);
// Update progress every 10% static int lastProgress = 0; if (progress >= lastProgress + 10) { sendCalibrationProgress(progress); lastProgress = progress; }
// Check if calibration is complete if (elapsed >= currentSession.durationSeconds) { completeCalibration(); } }
private: void configureBCGMCUForCalibration() { if (currentSession.procedureType == "baseline_calibration") { // High sensitivity for baseline measurement bcgmcu.setSensitivity("high"); bcgmcu.setFilterMode("minimal");
} else if (currentSession.procedureType == "sensitivity_calibration") { // Standard settings for sensitivity calibration bcgmcu.setSensitivity("medium"); bcgmcu.setFilterMode("adaptive");
} else if (currentSession.procedureType == "validation_test") { // Current production settings bcgmcu.applySavedSettings(); }
// Set high sample rate for calibration bcgmcu.setSampleRate(50); // 50Hz during calibration }
void completeCalibration() { currentSession.active = false;
// Analyze collected data analyzeCalibrationData();
// Generate calibration results JsonDocument results = generateCalibrationResults();
// Apply calibration if auto_apply is true if (currentSession.autoApply) { applyCalibrationResults(); }
// Send completion status sendCalibrationComplete(results);
// Restore normal operation restoreNormalOperation(); }
void analyzeCalibrationData() { if (calibrationSamples.empty()) return;
// Calculate statistics float sum = 0; float min_val = calibrationSamples[0]; float max_val = calibrationSamples[0];
for (float sample : calibrationSamples) { sum += sample; min_val = min(min_val, sample); max_val = max(max_val, sample); }
float mean = sum / calibrationSamples.size();
// Calculate standard deviation float variance = 0; for (float sample : calibrationSamples) { variance += pow(sample - mean, 2); } float stddev = sqrt(variance / calibrationSamples.size());
// Store results currentSession.baselineNoise = stddev; currentSession.signalAmplitude = max_val - min_val;
// Procedure-specific analysis if (currentSession.procedureType == "baseline_calibration") { analyzeBaselineCalibration(mean, stddev); } else if (currentSession.procedureType == "sensitivity_calibration") { analyzeSensitivityCalibration(mean, stddev); } else if (currentSession.procedureType == "validation_test") { analyzeValidationTest(); } }
void analyzeBaselineCalibration(float mean, float stddev) { // Establish new baseline float baselineOffset = mean; float noiseThreshold = stddev * 3; // 3-sigma threshold
// Update calibration factors currentSession.pulseSensitivity = 1.0; currentSession.breathingSensitivity = 1.0;
// Validation score based on noise level if (stddev < 0.001) { currentSession.validationScore = 95; } else if (stddev < 0.005) { currentSession.validationScore = 80; } else if (stddev < 0.01) { currentSession.validationScore = 65; } else { currentSession.validationScore = 40; }
Serial.printf("Baseline: offset=%.4f, noise=%.4f, score=%d\n", baselineOffset, stddev, currentSession.validationScore); }
void analyzeSensitivityCalibration(float mean, float stddev) { JsonObject ref = currentSession.referenceData;
if (ref.containsKey("expected_pulse_range")) { JsonArray pulseRange = ref["expected_pulse_range"]; int expectedMin = pulseRange[0]; int expectedMax = pulseRange[1];
// Measure actual pulse detection int detectedPulse = detectPulseDuringCalibration();
// Calculate sensitivity adjustment if (detectedPulse > 0) { float targetPulse = (expectedMin + expectedMax) / 2.0; currentSession.pulseSensitivity = targetPulse / detectedPulse; } }
if (ref.containsKey("expected_breathing_range")) { JsonArray breathingRange = ref["expected_breathing_range"]; int expectedMin = breathingRange[0]; int expectedMax = breathingRange[1];
// Measure actual breathing detection int detectedBreathing = detectBreathingDuringCalibration();
// Calculate sensitivity adjustment if (detectedBreathing > 0) { float targetBreathing = (expectedMin + expectedMax) / 2.0; currentSession.breathingSensitivity = targetBreathing / detectedBreathing; } }
// Calculate validation score currentSession.validationScore = calculateValidationScore(); }
void analyzeValidationTest() { JsonObject ref = currentSession.referenceData;
int score = 100;
// Test pulse accuracy if (ref.containsKey("reference_pulse_bpm")) { int referencePulse = ref["reference_pulse_bpm"]; int measuredPulse = detectPulseDuringCalibration(); float tolerance = ref["tolerance_percent"] | 5.0;
float error = abs(measuredPulse - referencePulse) * 100.0 / referencePulse; if (error > tolerance) { score -= min(30, (int)(error - tolerance) * 2); } }
// Test breathing accuracy if (ref.containsKey("reference_breathing_rpm")) { int referenceBreathing = ref["reference_breathing_rpm"]; int measuredBreathing = detectBreathingDuringCalibration(); float tolerance = ref["tolerance_percent"] | 5.0;
float error = abs(measuredBreathing - referenceBreathing) * 100.0 / referenceBreathing; if (error > tolerance) { score -= min(30, (int)(error - tolerance) * 2); } }
currentSession.validationScore = max(0, score); }
JsonDocument generateCalibrationResults() { JsonDocument doc;
doc["calibration_status"] = "completed"; doc["procedure_id"] = currentSession.procedureId; doc["completion_time"] = getCurrentTimestamp();
JsonObject results = doc.createNestedObject("results");
if (currentSession.procedureType == "baseline_calibration") { results["baseline_established"] = true; results["noise_level"] = currentSession.baselineNoise; results["signal_quality"] = getQualityString(currentSession.validationScore);
} else if (currentSession.procedureType == "sensitivity_calibration") { results["sensitivity_adjusted"] = true; results["pulse_sensitivity_factor"] = currentSession.pulseSensitivity; results["breathing_sensitivity_factor"] = currentSession.breathingSensitivity;
} else if (currentSession.procedureType == "validation_test") { results["validation_passed"] = (currentSession.validationScore >= 70); results["accuracy_score"] = currentSession.validationScore; }
JsonObject factors = results.createNestedObject("calibration_factors"); factors["pulse_sensitivity"] = currentSession.pulseSensitivity; factors["breathing_sensitivity"] = currentSession.breathingSensitivity; factors["noise_threshold"] = currentSession.baselineNoise * 3;
doc["applied"] = currentSession.autoApply; doc["validation_score"] = currentSession.validationScore;
return doc; }
void applyCalibrationResults() { // Apply new calibration factors to BCGMCU bcgmcu.setPulseSensitivity(currentSession.pulseSensitivity); bcgmcu.setBreathingSensitivity(currentSession.breathingSensitivity); bcgmcu.setNoiseThreshold(currentSession.baselineNoise * 3);
// Save calibration to persistent storage saveCalibrationToFlash();
Serial.println("Calibration applied and saved"); }
void sendCalibrationStatus(String status) { StaticJsonDocument<256> doc; doc["calibration_status"] = status; doc["procedure_id"] = currentSession.procedureId; doc["estimated_completion"] = getEstimatedCompletion(); doc["current_phase"] = "initialization"; doc["progress_percent"] = 0;
sendStatusUpdate(doc); }
void sendCalibrationProgress(int progress) { StaticJsonDocument<256> doc; doc["calibration_status"] = "in_progress"; doc["procedure_id"] = currentSession.procedureId; doc["current_phase"] = getCurrentPhase(progress); doc["progress_percent"] = progress;
sendStatusUpdate(doc); }
void sendCalibrationComplete(JsonDocument& results) { sendStatusUpdate(results); }
String getCurrentPhase(int progress) { if (progress < 20) return "initialization"; if (progress < 80) return "data_collection"; if (progress < 95) return "analysis"; return "finalization"; }
String getQualityString(int score) { if (score >= 90) return "excellent"; if (score >= 75) return "good"; if (score >= 60) return "fair"; return "poor"; }
void restoreNormalOperation() { // Restore normal BCGMCU settings bcgmcu.setSampleRate(10); // Back to normal 10Hz bcgmcu.applySavedSettings();
Serial.println("Returned to normal operation"); }};Calibration Validation
Section titled “Calibration Validation”class CalibrationValidator {public: struct ValidationResult { bool valid; int score; std::vector<String> issues; std::vector<String> recommendations; };
ValidationResult validateCalibration() { ValidationResult result; result.valid = true; result.score = 100;
// Test pulse detection accuracy testPulseAccuracy(result);
// Test breathing detection accuracy testBreathingAccuracy(result);
// Test noise levels testNoiseLevel(result);
// Test signal stability testSignalStability(result);
result.valid = (result.score >= 70);
return result; }
private: void testPulseAccuracy(ValidationResult& result) { // Run pulse detection test std::vector<int> measurements;
for (int i = 0; i < 10; i++) { measurements.push_back(bcgmcu.getPulseRate()); delay(1000); }
// Calculate consistency float mean = calculateMean(measurements); float stddev = calculateStdDev(measurements, mean);
if (stddev > 5.0) { result.score -= 20; result.issues.push_back("Pulse detection inconsistent (stddev=" + String(stddev) + ")"); result.recommendations.push_back("Recalibrate pulse sensitivity"); }
// Check for reasonable values if (mean < 30 || mean > 200) { result.score -= 15; result.issues.push_back("Pulse rate outside normal range: " + String(mean)); } }
void testBreathingAccuracy(ValidationResult& result) { // Similar test for breathing rate std::vector<int> measurements;
for (int i = 0; i < 10; i++) { measurements.push_back(bcgmcu.getBreathingRate()); delay(1000); }
float mean = calculateMean(measurements); float stddev = calculateStdDev(measurements, mean);
if (stddev > 2.0) { result.score -= 15; result.issues.push_back("Breathing detection inconsistent"); result.recommendations.push_back("Recalibrate breathing sensitivity"); } }};Best Practices
Section titled “Best Practices”- Controlled Environment: Perform calibration in stable conditions
- Sufficient Duration: Allow adequate time for accurate calibration
- Regular Validation: Validate calibration periodically
- Backup Calibration: Store previous calibration before changes
- Environmental Factors: Account for bed type and patient weight
- Documentation: Log all calibration procedures and results