#include #include #include "freertos/FreeRTOS.h" #include "evse_config.h" #include "board_config.h" #include "evse_limits.h" #include "evse_api.h" #include "evse_state.h" #include "esp_log.h" #include "esp_err.h" #include "esp_check.h" #include "storage_service.h" static const char *TAG = "evse_config"; #define NVS_NAMESPACE "evse_config" // ======================== // 3 variáveis (semântica simples) // ======================== // 1) Hardware (FIXO) static const uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT; // 2) Configurável (persistido) static uint16_t charging_current = MAX_CHARGING_CURRENT_LIMIT; // 3) Runtime (RAM) static uint16_t charging_current_runtime = 0; // Outros parâmetros (persistidos) static bool socket_outlet = false; static bool rcm = false; static uint8_t temp_threshold = 60; // Availability / Enable flags (persistidos) static bool is_available = true; static bool is_enabled = true; static inline TickType_t TO_TICKS_MS(uint32_t ms) { return pdMS_TO_TICKS(ms); } // Ajusta conforme o teu boot: // 1000ms pode ser curto com Wi-Fi/FS/tasks; 2000ms é mais robusto em produto. static inline TickType_t BOOT_TO(void) { return TO_TICKS_MS(2000); } // ======================== // Initialization // ======================== esp_err_t evse_config_init(void) { // garante storage iniciado ESP_RETURN_ON_ERROR(storage_service_init(), TAG, "storage init failed"); ESP_LOGI(TAG, "EVSE config init OK (storage-backed)"); return ESP_OK; } void evse_check_defaults(void) { esp_err_t err; uint8_t u8 = 0; uint16_t u16 = 0; // Timeouts: leitura e escrita no boot const TickType_t rd_to = BOOT_TO(); const TickType_t wr_to = TO_TICKS_MS(2000); ESP_LOGD(TAG, "Checking default parameters (sync persistence)..."); // ----------------------------------------- // Charging current (default, persisted) // ----------------------------------------- err = storage_get_u16_sync(NVS_NAMESPACE, "def_chrg_curr", &u16, rd_to); if (err != ESP_OK || u16 < MIN_CHARGING_CURRENT_LIMIT || u16 > max_charging_current) { charging_current = max_charging_current; esp_err_t se = storage_set_u16_sync(NVS_NAMESPACE, "def_chrg_curr", charging_current, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist def_chrg_curr=%u: %s", (unsigned)charging_current, esp_err_to_name(se)); // seguimos com RAM correta; persist pode falhar por flash/partição } ESP_LOGW(TAG, "Invalid/missing def_chrg_curr (%s) -> reset to %u (sync persisted)", esp_err_to_name(err), (unsigned)charging_current); } else { charging_current = u16; } // runtime inicializa a partir do default charging_current_runtime = charging_current; ESP_LOGD(TAG, "Runtime charging current initialized from default: %u", (unsigned)charging_current_runtime); // ----------------------------------------- // Socket outlet (persisted) + capability gate // ----------------------------------------- err = storage_get_u8_sync(NVS_NAMESPACE, "socket_outlet", &u8, rd_to); if (err == ESP_OK && u8 <= 1) { bool wanted = (u8 != 0); if (wanted && !board_config.proximity) { // NVS dizia 1, mas HW não suporta -> runtime false e persistir 0 socket_outlet = false; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "socket_outlet", 0, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist socket_outlet=0 (capability mismatch): %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "socket_outlet requested but HW has no proximity -> forcing false (sync persisted)"); } else { socket_outlet = wanted; } } else { socket_outlet = false; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "socket_outlet", 0, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist socket_outlet default=0: %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "Missing/invalid socket_outlet (%s) -> default=false (sync persisted).", esp_err_to_name(err)); } // ----------------------------------------- // RCM (persisted) + capability gate // ----------------------------------------- err = storage_get_u8_sync(NVS_NAMESPACE, "rcm", &u8, rd_to); if (err == ESP_OK && u8 <= 1) { bool wanted = (u8 != 0); if (wanted && !board_config.rcm) { rcm = false; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "rcm", 0, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist rcm=0 (capability mismatch): %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "rcm requested but HW has no RCM -> forcing false (sync persisted)"); } else { rcm = wanted; } } else { rcm = false; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "rcm", 0, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist rcm default=0: %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "Missing/invalid rcm (%s) -> default=false (sync persisted).", esp_err_to_name(err)); } // ----------------------------------------- // Temp threshold (persisted) // ----------------------------------------- err = storage_get_u8_sync(NVS_NAMESPACE, "temp_threshold", &u8, rd_to); if (err == ESP_OK && u8 >= 40 && u8 <= 80) { temp_threshold = u8; } else { temp_threshold = 60; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "temp_threshold", temp_threshold, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist temp_threshold=%u: %s", (unsigned)temp_threshold, esp_err_to_name(se)); } ESP_LOGW(TAG, "Invalid/missing temp_threshold (%s) -> default=60 (sync persisted).", esp_err_to_name(err)); } // ----------------------------------------- // Availability (persisted) [0/1] // ----------------------------------------- err = storage_get_u8_sync(NVS_NAMESPACE, "available", &u8, rd_to); if (err == ESP_OK && u8 <= 1) { is_available = (u8 != 0); } else { is_available = true; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "available", 1, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist available=1: %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "Missing/invalid 'available' (%s) -> default=true (sync persisted).", esp_err_to_name(err)); } // ----------------------------------------- // Enabled (persisted) [0/1] // ----------------------------------------- err = storage_get_u8_sync(NVS_NAMESPACE, "enabled", &u8, rd_to); if (err == ESP_OK && u8 <= 1) { is_enabled = (u8 != 0); } else { is_enabled = true; esp_err_t se = storage_set_u8_sync(NVS_NAMESPACE, "enabled", 1, wr_to); if (se != ESP_OK) { ESP_LOGE(TAG, "Failed to persist enabled=1: %s", esp_err_to_name(se)); } ESP_LOGW(TAG, "Missing/invalid 'enabled' (%s) -> default=true (sync persisted).", esp_err_to_name(err)); } // Flush explícito no boot: // - ajuda a garantir commit determinístico antes do resto do sistema avançar // - mantém-se útil mesmo com setters sync se o teu storage ainda estiver com debounce interno esp_err_t fe = storage_flush_sync(wr_to); if (fe != ESP_OK) ESP_LOGE(TAG, "storage_flush_sync failed: %s", esp_err_to_name(fe)); } // ======================== // Charging current getters/setters // ======================== uint8_t evse_get_max_charging_current(void) { return max_charging_current; } uint16_t evse_get_charging_current(void) { return charging_current; } esp_err_t evse_set_charging_current(uint16_t value) { if (value < MIN_CHARGING_CURRENT_LIMIT || value > max_charging_current) return ESP_ERR_INVALID_ARG; if (value == charging_current) { evse_set_runtime_charging_current(value); return ESP_OK; } charging_current = value; esp_err_t err = storage_set_u16_async(NVS_NAMESPACE, "def_chrg_curr", value); if (err != ESP_OK) { // Em runtime, isto pode falhar por fila cheia. RAM fica correta; persistência é best-effort. ESP_LOGE(TAG, "Failed to persist def_chrg_curr async=%u: %s", (unsigned)value, esp_err_to_name(err)); return err; } evse_set_runtime_charging_current(value); return ESP_OK; } // ======================== // Runtime current (not saved) // ======================== void evse_set_runtime_charging_current(uint16_t value) { if (value > max_charging_current) value = max_charging_current; else if (value < MIN_CHARGING_CURRENT_LIMIT) value = MIN_CHARGING_CURRENT_LIMIT; charging_current_runtime = value; } uint16_t evse_get_runtime_charging_current(void) { return charging_current_runtime; } // ======================== // Socket outlet // ======================== bool evse_get_socket_outlet(void) { return socket_outlet; } esp_err_t evse_set_socket_outlet(bool value) { if (value && !board_config.proximity) return ESP_ERR_INVALID_ARG; if (value == socket_outlet) return ESP_OK; socket_outlet = value; esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "socket_outlet", (uint8_t)value); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to persist socket_outlet async=%u: %s", (unsigned)value, esp_err_to_name(err)); return err; } return ESP_OK; } // ======================== // RCM // ======================== bool evse_is_rcm(void) { return rcm; } esp_err_t evse_set_rcm(bool value) { if (value && !board_config.rcm) return ESP_ERR_INVALID_ARG; if (value == rcm) return ESP_OK; rcm = value; esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "rcm", (uint8_t)value); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to persist rcm async=%u: %s", (unsigned)value, esp_err_to_name(err)); return err; } return ESP_OK; } // ======================== // Temperature // ======================== uint8_t evse_get_temp_threshold(void) { return temp_threshold; } esp_err_t evse_set_temp_threshold(uint8_t value) { if (value < 40 || value > 80) return ESP_ERR_INVALID_ARG; if (value == temp_threshold) return ESP_OK; temp_threshold = value; esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "temp_threshold", value); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to persist temp_threshold async=%u: %s", (unsigned)value, esp_err_to_name(err)); return err; } return ESP_OK; } // ======================== // Availability // ======================== bool evse_config_is_available(void) { return is_available; } void evse_config_set_available(bool available) { bool newv = available; if (newv == is_available) return; is_available = newv; esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "available", (uint8_t)is_available); if (err != ESP_OK) ESP_LOGE(TAG, "Failed to persist 'available' async=%u: %s", (unsigned)is_available, esp_err_to_name(err)); } // ======================== // Enable/Disable // ======================== bool evse_config_is_enabled(void) { return is_enabled; } void evse_config_set_enabled(bool enabled) { bool newv = enabled; if (newv == is_enabled) return; is_enabled = newv; esp_err_t err = storage_set_u8_async(NVS_NAMESPACE, "enabled", (uint8_t)is_enabled); if (err != ESP_OK) ESP_LOGE(TAG, "Failed to persist 'enabled' async=%u: %s", (unsigned)is_enabled, esp_err_to_name(err)); }