Skip to content

Configuration Management

Remote configuration endpoints for updating device settings and parameters without physical access.

GET /api/v1/device/config/{device_id}
PUT /api/v1/device/config/{device_id}
  • Update device operation parameters remotely
  • Modify sampling rates and data collection modes
  • Adjust BCGMCU sensitivity and filtering
  • Configure system behavior and intervals
Authorization: Bearer {device_token}
{
"device_id": "BCGMCU_001",
"configuration": {
"operation_mode": "streaming",
"streaming_settings": {
"rate_hz": 1,
"data_fields": ["pulse", "breathing", "presence"],
"quality_threshold": "good"
},
"batch_settings": {
"interval_seconds": 3600,
"max_batch_size": 10000,
"compression_enabled": true
},
"bcgmcu_settings": {
"sensitivity": "high",
"filter_mode": "adaptive",
"presence_timeout": 30,
"calibration_mode": "auto"
},
"system_settings": {
"status_interval": 300,
"wifi_power_save": false,
"debug_logging": false
}
},
"applied_timestamp": "2025-01-27T10:30:00.000Z",
"config_version": "v1.2.3"
}
Content-Type: application/json
Authorization: Bearer {device_token}
{
"device_id": "BCGMCU_001",
"configuration": {
"operation_mode": "hybrid",
"streaming_settings": {
"rate_hz": 2,
"data_fields": ["pulse", "breathing", "presence", "accelerometer"],
"quality_threshold": "fair"
},
"batch_settings": {
"interval_seconds": 1800,
"max_batch_size": 5000,
"compression_enabled": true
},
"bcgmcu_settings": {
"sensitivity": "medium",
"filter_mode": "fixed",
"presence_timeout": 60,
"calibration_mode": "manual"
},
"system_settings": {
"status_interval": 180,
"wifi_power_save": true,
"debug_logging": true
}
}
}
ModeDescriptionUse Case
streamingReal-time data transmission onlyContinuous monitoring
batchPeriodic batch uploads onlyOffline data collection
hybridBoth streaming and batchingComprehensive data capture
ParameterTypeRangeDescription
rate_hzinteger0.1-10Transmission frequency in Hz
data_fieldsarraypredefinedFields to include in stream
quality_thresholdstringpoor/fair/good/excellentMinimum signal quality
ParameterTypeRangeDescription
interval_secondsinteger300-86400Time between batch uploads
max_batch_sizeinteger100-100000Maximum samples per batch
compression_enabledbooleantrue/falseEnable gzip compression
ParameterTypeRangeDescription
sensitivitystringlow/medium/highSensor sensitivity level
filter_modestringfixed/adaptiveSignal filtering approach
presence_timeoutinteger10-300Seconds before marking absent
calibration_modestringauto/manualCalibration trigger mode
ParameterTypeRangeDescription
status_intervalinteger60-3600Status update frequency in seconds
wifi_power_savebooleantrue/falseEnable WiFi power saving
debug_loggingbooleantrue/falseEnable detailed logging
{
"config_status": "applied",
"config_version": "v1.2.4",
"restart_required": false,
"validation_errors": [],
"effective_timestamp": "2025-01-27T10:30:05.000Z"
}

Validation Error Response (400 Bad Request)

Section titled “Validation Error Response (400 Bad Request)”
{
"config_status": "validation_failed",
"validation_errors": [
{
"field": "streaming_settings.rate_hz",
"error": "Value 15 exceeds maximum of 10",
"suggested_value": 10
},
{
"field": "batch_settings.interval_seconds",
"error": "Value 60 below minimum of 300",
"suggested_value": 300
}
]
}
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Preferences.h>
class ConfigurationManager {
private:
Preferences prefs;
String configVersion;
unsigned long lastConfigCheck = 0;
struct DeviceConfig {
String operation_mode = "streaming";
float streaming_rate = 1.0;
int batch_interval = 3600;
String sensitivity = "medium";
int status_interval = 300;
bool wifi_power_save = false;
bool debug_logging = false;
} config;
public:
void initialize() {
prefs.begin("config", false);
loadStoredConfig();
}
void checkForUpdates() {
// Check for configuration updates every 5 minutes
if (millis() - lastConfigCheck < 300000) {
return;
}
lastConfigCheck = millis();
// Only check if status indicated pending config
if (!configUpdatePending) {
return;
}
fetchConfiguration();
}
void fetchConfiguration() {
HTTPClient https;
String url = API_BASE_URL + "/device/config/" + getDeviceId();
https.begin(url);
https.addHeader("Authorization", "Bearer " + deviceToken);
int httpCode = https.GET();
if (httpCode == 200) {
String response = https.getString();
parseConfiguration(response);
} else {
Serial.printf("Config fetch failed: %d\n", httpCode);
}
https.end();
}
void parseConfiguration(String& response) {
StaticJsonDocument<2048> doc;
deserializeJson(doc, response);
String newVersion = doc["config_version"];
// Check if configuration changed
if (newVersion != configVersion) {
Serial.println("New configuration version: " + newVersion);
// Parse configuration sections
JsonObject configObj = doc["configuration"];
config.operation_mode = configObj["operation_mode"].as<String>();
// Streaming settings
if (configObj.containsKey("streaming_settings")) {
JsonObject streaming = configObj["streaming_settings"];
config.streaming_rate = streaming["rate_hz"];
}
// Batch settings
if (configObj.containsKey("batch_settings")) {
JsonObject batch = configObj["batch_settings"];
config.batch_interval = batch["interval_seconds"];
}
// BCGMCU settings
if (configObj.containsKey("bcgmcu_settings")) {
JsonObject bcgmcu = configObj["bcgmcu_settings"];
config.sensitivity = bcgmcu["sensitivity"].as<String>();
}
// System settings
if (configObj.containsKey("system_settings")) {
JsonObject system = configObj["system_settings"];
config.status_interval = system["status_interval"];
config.wifi_power_save = system["wifi_power_save"];
config.debug_logging = system["debug_logging"];
}
// Apply new configuration
applyConfiguration();
// Store configuration
saveConfiguration();
configVersion = newVersion;
configUpdatePending = false;
}
}
private:
void applyConfiguration() {
Serial.println("Applying new configuration:");
// Apply operation mode
if (config.operation_mode == "streaming") {
setOperationMode(MODE_STREAMING);
} else if (config.operation_mode == "batch") {
setOperationMode(MODE_BATCH);
} else if (config.operation_mode == "hybrid") {
setOperationMode(MODE_HYBRID);
}
// Apply streaming rate
setStreamingRate(config.streaming_rate);
// Apply batch interval
setBatchInterval(config.batch_interval);
// Apply BCGMCU sensitivity
bcgmcu.setSensitivity(config.sensitivity);
// Apply system settings
setStatusInterval(config.status_interval);
if (config.wifi_power_save) {
WiFi.setSleep(WIFI_PS_MIN_MODEM);
} else {
WiFi.setSleep(WIFI_PS_NONE);
}
setDebugLogging(config.debug_logging);
Serial.println("Configuration applied successfully");
}
void saveConfiguration() {
prefs.putString("op_mode", config.operation_mode);
prefs.putFloat("stream_rate", config.streaming_rate);
prefs.putInt("batch_int", config.batch_interval);
prefs.putString("sensitivity", config.sensitivity);
prefs.putInt("status_int", config.status_interval);
prefs.putBool("wifi_save", config.wifi_power_save);
prefs.putBool("debug_log", config.debug_logging);
prefs.putString("version", configVersion);
}
void loadStoredConfig() {
config.operation_mode = prefs.getString("op_mode", "streaming");
config.streaming_rate = prefs.getFloat("stream_rate", 1.0);
config.batch_interval = prefs.getInt("batch_int", 3600);
config.sensitivity = prefs.getString("sensitivity", "medium");
config.status_interval = prefs.getInt("status_int", 300);
config.wifi_power_save = prefs.getBool("wifi_save", false);
config.debug_logging = prefs.getBool("debug_log", false);
configVersion = prefs.getString("version", "v0.0.0");
// Apply stored configuration
applyConfiguration();
}
};
class ConfigValidator {
public:
struct ValidationResult {
bool valid;
std::vector<String> errors;
};
ValidationResult validateConfig(JsonObject& config) {
ValidationResult result;
result.valid = true;
// Validate operation mode
String mode = config["operation_mode"];
if (mode != "streaming" && mode != "batch" && mode != "hybrid") {
result.errors.push_back("Invalid operation_mode: " + mode);
result.valid = false;
}
// Validate streaming settings
if (config.containsKey("streaming_settings")) {
JsonObject streaming = config["streaming_settings"];
float rate = streaming["rate_hz"];
if (rate < 0.1 || rate > 10.0) {
result.errors.push_back("streaming_rate must be 0.1-10.0 Hz");
result.valid = false;
}
}
// Validate batch settings
if (config.containsKey("batch_settings")) {
JsonObject batch = config["batch_settings"];
int interval = batch["interval_seconds"];
if (interval < 300 || interval > 86400) {
result.errors.push_back("batch_interval must be 300-86400 seconds");
result.valid = false;
}
int maxSize = batch["max_batch_size"];
if (maxSize < 100 || maxSize > 100000) {
result.errors.push_back("max_batch_size must be 100-100000");
result.valid = false;
}
}
// Validate BCGMCU settings
if (config.containsKey("bcgmcu_settings")) {
JsonObject bcgmcu = config["bcgmcu_settings"];
String sensitivity = bcgmcu["sensitivity"];
if (sensitivity != "low" && sensitivity != "medium" && sensitivity != "high") {
result.errors.push_back("Invalid sensitivity: " + sensitivity);
result.valid = false;
}
}
return result;
}
};
class ConfigBackup {
private:
static const int MAX_BACKUPS = 5;
public:
void backupCurrentConfig() {
// Create backup entry
StaticJsonDocument<1024> backup;
backup["timestamp"] = getCurrentTimestamp();
backup["version"] = configVersion;
backup["config"] = getCurrentConfigJson();
// Store in SPIFFS
String filename = "/config_backup_" + String(millis()) + ".json";
File file = SPIFFS.open(filename, "w");
serializeJson(backup, file);
file.close();
// Clean old backups
cleanupOldBackups();
}
void restoreLastKnownGood() {
// Find most recent valid backup
File root = SPIFFS.open("/");
File file = root.openNextFile();
String latestBackup;
unsigned long latestTime = 0;
while (file) {
String name = file.name();
if (name.startsWith("config_backup_")) {
unsigned long time = name.substring(14).toInt();
if (time > latestTime) {
latestTime = time;
latestBackup = name;
}
}
file = root.openNextFile();
}
if (latestBackup.length() > 0) {
restoreFromBackup(latestBackup);
}
}
private:
void cleanupOldBackups() {
// Keep only MAX_BACKUPS most recent files
std::vector<String> backups;
File root = SPIFFS.open("/");
File file = root.openNextFile();
while (file) {
String name = file.name();
if (name.startsWith("config_backup_")) {
backups.push_back(name);
}
file = root.openNextFile();
}
// Sort by timestamp (filename contains timestamp)
std::sort(backups.begin(), backups.end());
// Remove oldest backups
while (backups.size() > MAX_BACKUPS) {
SPIFFS.remove(backups[0]);
backups.erase(backups.begin());
}
}
};
  1. Configuration Versioning: Always track configuration versions
  2. Validation: Validate all parameters before applying
  3. Gradual Rollout: Test configurations on subset of devices
  4. Backup and Recovery: Maintain configuration backups
  5. Restart Minimal: Apply changes without restart when possible
  6. Error Handling: Gracefully handle invalid configurations
  7. Status Monitoring: Monitor device behavior after config changes
class ConfigScheduler {
public:
void scheduleConfigUpdate(JsonObject& config, unsigned long delayMs) {
ScheduledConfig scheduled;
scheduled.config = config;
scheduled.executeTime = millis() + delayMs;
scheduled.applied = false;
scheduledConfigs.push_back(scheduled);
}
void processScheduledConfigs() {
for (auto& scheduled : scheduledConfigs) {
if (!scheduled.applied && millis() >= scheduled.executeTime) {
applyConfiguration(scheduled.config);
scheduled.applied = true;
}
}
// Clean up applied configs
scheduledConfigs.erase(
std::remove_if(scheduledConfigs.begin(), scheduledConfigs.end(),
[](const ScheduledConfig& s) { return s.applied; }),
scheduledConfigs.end()
);
}
private:
struct ScheduledConfig {
JsonObject config;
unsigned long executeTime;
bool applied;
};
std::vector<ScheduledConfig> scheduledConfigs;
};