// components/evse/evse_core.c #include "evse_fsm.h" #include "evse_error.h" #include "evse_limits.h" #include "evse_config.h" #include "evse_api.h" #include "evse_session.h" #include "evse_pilot.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "esp_log.h" static const char *TAG = "evse_core"; static SemaphoreHandle_t mutex; static evse_state_t last_state = EVSE_STATE_A; // Filtro simples de histerese no pilot #define PILOT_STABLE_SAMPLES 2 static pilot_voltage_t s_last_raw = PILOT_VOLTAGE_12; static pilot_voltage_t s_filtered = PILOT_VOLTAGE_12; static int s_stable_count = 0; static pilot_voltage_t filter_pilot_voltage(pilot_voltage_t raw) { if (raw == s_last_raw) { if (s_stable_count < PILOT_STABLE_SAMPLES) { s_stable_count++; } } else { s_last_raw = raw; s_stable_count = 1; } if (s_stable_count >= PILOT_STABLE_SAMPLES && raw != s_filtered) { s_filtered = raw; } return s_filtered; } static void evse_process(void); static void evse_core_task(void *arg); void evse_init(void) { ESP_LOGI(TAG, "EVSE Init"); mutex = xSemaphoreCreateMutex(); if (!mutex) { ESP_LOGE(TAG, "Failed to create EVSE core mutex"); return; } evse_check_defaults(); evse_fsm_reset(); pilot_set_level(true); BaseType_t rc = xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 6, NULL); configASSERT(rc == pdPASS); } static void evse_process(void) { if (!mutex) { return; } xSemaphoreTake(mutex, portMAX_DELAY); pilot_voltage_t pilot_raw; bool is_n12v = false; pilot_measure(&pilot_raw, &is_n12v); pilot_voltage_t pilot_voltage = filter_pilot_voltage(pilot_raw); ESP_LOGD(TAG, "Pilot(raw=%d, filt=%d), -12V: %s", pilot_raw, pilot_voltage, is_n12v ? "yes" : "no"); // raw set/clear; erro visível mantém holdoff interno (60s após sumir) evse_error_check(pilot_voltage, is_n12v); // ✅ Sem cooldown externo: disponibilidade depende só do erro "visível" bool available = evse_config_is_available() && (evse_get_error() == 0); bool enabled = evse_config_is_enabled(); evse_fsm_process( pilot_voltage, evse_state_get_authorized(), available, enabled); evse_limits_check(); if (evse_is_limit_reached()) { if (evse_state_get_authorized()) { ESP_LOGW(TAG, "Charging limit reached → revoking authorization"); evse_state_set_authorized(false); } } evse_state_t current = evse_get_state(); if (current != last_state) { last_state = current; } xSemaphoreGive(mutex); } static void evse_core_task(void *arg) { (void)arg; while (true) { evse_process(); vTaskDelay(pdMS_TO_TICKS(100)); } }