#include "evse_api.h" #include "evse_state.h" #include "evse_session.h" #include "evse_events.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "esp_log.h" #include "esp_event.h" #define EVSE_EVENT_POST_TIMEOUT_MS 50 static evse_state_t current_state = EVSE_STATE_A; static bool is_authorized = false; static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED; static const char *TAG = "evse_state"; static evse_state_event_t map_state_to_event(evse_state_t s) { switch (s) { case EVSE_STATE_A: return EVSE_STATE_EVENT_IDLE; case EVSE_STATE_B1: case EVSE_STATE_B2: return EVSE_STATE_EVENT_WAITING; case EVSE_STATE_C1: case EVSE_STATE_C2: case EVSE_STATE_D1: case EVSE_STATE_D2: return EVSE_STATE_EVENT_CHARGING; case EVSE_STATE_E: case EVSE_STATE_F: return EVSE_STATE_EVENT_FAULT; default: return EVSE_STATE_EVENT_IDLE; } } static void post_evse_event(evse_event_id_t id, const void *data, size_t len) { esp_err_t err = esp_event_post( EVSE_EVENTS, id, data, len, portMAX_DELAY); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_event_post(id=%d) failed: %s", (int)id, esp_err_to_name(err)); } } bool evse_state_is_charging(evse_state_t state) { // “charging” == energia efetiva (relé ON) return (state == EVSE_STATE_C2 || state == EVSE_STATE_D2); } bool evse_state_is_power_flowing(evse_state_t state) { return evse_state_is_charging(state); } bool evse_state_is_requesting(evse_state_t state) { // EV pediu carga mas o relé ainda está OFF return (state == EVSE_STATE_C1 || state == EVSE_STATE_D1); } bool evse_state_is_plugged(evse_state_t state) { return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2 || state == EVSE_STATE_D1 || state == EVSE_STATE_D2; } bool evse_state_is_session(evse_state_t state) { // Sessão lógica: “autorizado/pronto” ou “a pedir/a fornecer energia” return (state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2 || state == EVSE_STATE_D1 || state == EVSE_STATE_D2); } void evse_set_state(evse_state_t new_state) { bool changed = false; evse_state_t prev_state; bool start_session = false; bool end_session = false; portENTER_CRITICAL(&state_mux); prev_state = current_state; if (new_state != current_state) { // Sessão começa quando entra em energia (relé ON) if (evse_state_is_power_flowing(new_state) && !evse_state_is_power_flowing(prev_state)) { start_session = true; } // Sessão termina quando sai de energia else if (!evse_state_is_power_flowing(new_state) && evse_state_is_power_flowing(prev_state)) { end_session = true; } current_state = new_state; changed = true; } portEXIT_CRITICAL(&state_mux); // Fora da região crítica if (start_session) { evse_session_start(); } if (end_session) { evse_session_end(); } if (changed) { ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(prev_state), evse_state_to_str(new_state)); evse_state_event_data_t evt = { .state = map_state_to_event(new_state)}; post_evse_event(EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt)); } } evse_state_t evse_get_state(void) { portENTER_CRITICAL(&state_mux); evse_state_t s = current_state; portEXIT_CRITICAL(&state_mux); return s; } const char *evse_state_to_str(evse_state_t state) { switch (state) { case EVSE_STATE_A: return "A - EV Not Connected (12V)"; case EVSE_STATE_B1: return "B1 - EV Connected (9V, Not Authorized)"; case EVSE_STATE_B2: return "B2 - EV Connected (9V, Authorized and Ready)"; case EVSE_STATE_C1: return "C1 - Charging Requested (6V, Relay Off)"; case EVSE_STATE_C2: return "C2 - Charging Active (6V, Relay On)"; case EVSE_STATE_D1: return "D1 - Ventilation Required (3V, Relay Off)"; case EVSE_STATE_D2: return "D2 - Ventilation Active (3V, Relay On)"; case EVSE_STATE_E: return "E - Error: Control Pilot Shorted to Ground (0V)"; case EVSE_STATE_F: return "F - Fault: EVSE Unavailable or No Pilot Signal"; default: return "Unknown State"; } } void evse_state_init(void) { portENTER_CRITICAL(&state_mux); current_state = EVSE_STATE_A; is_authorized = false; portEXIT_CRITICAL(&state_mux); ESP_LOGI(TAG, "Initialized in state: %s", evse_state_to_str(current_state)); evse_state_event_data_t evt = { .state = map_state_to_event(current_state)}; post_evse_event(EVSE_EVENT_INIT, &evt, sizeof(evt)); } void evse_state_tick(void) { // placeholder } void evse_state_set_authorized(bool authorized) { portENTER_CRITICAL(&state_mux); is_authorized = authorized; portEXIT_CRITICAL(&state_mux); } bool evse_state_get_authorized(void) { portENTER_CRITICAL(&state_mux); bool result = is_authorized; portEXIT_CRITICAL(&state_mux); return result; }