// ============================================================================= // hal/nvs_config.cpp -- NVS-backed calibration store (Sprint 7) // ============================================================================= #include "nvs_config.h" #include #include #include #include "../system/ar_log.h" namespace arautopilot::hal { namespace { constexpr const char* TAG = "AR/NVS"; constexpr const char* NS = "ar_cal"; } // namespace bool nvs_config_init() { esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { AR_LOGW(TAG, "NVS partition needs erase (err=0x%x) -- factory-resetting", err); nvs_flash_erase(); err = nvs_flash_init(); } if (err != ESP_OK) { AR_LOGE(TAG, "nvs_flash_init failed: 0x%x", err); return false; } AR_LOGI(TAG, "NVS initialised (namespace: %s)", NS); return true; } CalibrationData nvs_config_load() { CalibrationData cal; // defaults in struct definition nvs_handle_t h; if (nvs_open(NS, NVS_READONLY, &h) != ESP_OK) { AR_LOGI(TAG, "No calibration in NVS -- using factory defaults"); return cal; } // Helper lambdas: read or keep default on missing key. auto get_i16 = [&](const char* key, int16_t& out) { int16_t v; if (nvs_get_i16(h, key, &v) == ESP_OK) out = v; }; auto get_float = [&](const char* key, float& out) { // NVS has no native float; store as uint32 via memcpy. uint32_t raw; if (nvs_get_u32(h, key, &raw) == ESP_OK) { memcpy(&out, &raw, sizeof(float)); } }; auto get_u8 = [&](const char* key, uint8_t& out) { uint8_t v; if (nvs_get_u8(h, key, &v) == ESP_OK) out = v; }; get_i16("adc_port", cal.adc_port); get_i16("adc_stbd", cal.adc_stbd); get_float("rud_port", cal.rudder_port_deg); get_float("rud_stbd", cal.rudder_stbd_deg); get_float("ou_kp", cal.outer_kp); get_float("ou_ki", cal.outer_ki); get_float("ou_kd", cal.outer_kd); uint8_t comm = 0; get_u8("commissioned", comm); cal.commissioned = (comm == 1); nvs_close(h); AR_LOGI(TAG, "Calibration loaded (commissioned=%d adc=%d..%d)", (int)cal.commissioned, cal.adc_port, cal.adc_stbd); return cal; } bool nvs_config_save(const CalibrationData& cal) { nvs_handle_t h; if (nvs_open(NS, NVS_READWRITE, &h) != ESP_OK) { AR_LOGE(TAG, "nvs_open for write failed"); return false; } auto put_float = [&](const char* key, float v) { uint32_t raw; memcpy(&raw, &v, sizeof(float)); nvs_set_u32(h, key, raw); }; nvs_set_i16(h, "adc_port", cal.adc_port); nvs_set_i16(h, "adc_stbd", cal.adc_stbd); put_float("rud_port", cal.rudder_port_deg); put_float("rud_stbd", cal.rudder_stbd_deg); put_float("ou_kp", cal.outer_kp); put_float("ou_ki", cal.outer_ki); put_float("ou_kd", cal.outer_kd); nvs_set_u8(h, "commissioned", cal.commissioned ? 1u : 0u); esp_err_t err = nvs_commit(h); nvs_close(h); if (err != ESP_OK) { AR_LOGE(TAG, "nvs_commit failed: 0x%x", err); return false; } AR_LOGI(TAG, "Calibration saved to NVS"); return true; } bool nvs_config_erase() { nvs_handle_t h; if (nvs_open(NS, NVS_READWRITE, &h) != ESP_OK) return false; esp_err_t err = nvs_erase_all(h); nvs_commit(h); nvs_close(h); AR_LOGW(TAG, "NVS namespace '%s' erased (factory reset)", NS); return err == ESP_OK; } } // namespace arautopilot::hal