#include "evse_fsm.h" #include "evse_api.h" #include "evse_pilot.h" #include "evse_config.h" #include "esp_log.h" #include "ac_relay.h" #include "board_config.h" #include "socket_lock.h" #include "proximity.h" #include "rcm.h" #include "evse_state.h" #include "evse_error.h" static const char *TAG = "evse_fsm"; #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif static bool c1_d1_waiting = false; static TickType_t c1_d1_relay_to = 0; void evse_fsm_reset(void) { evse_set_state(EVSE_STATE_A); c1_d1_waiting = false; c1_d1_relay_to = 0; } // ... includes e defines como já estão static void update_outputs(evse_state_t state) { const uint16_t current = evse_get_runtime_charging_current(); uint8_t cable_max_current = evse_get_max_charging_current(); const bool socket_outlet = evse_get_socket_outlet(); if (socket_outlet) { cable_max_current = proximity_get_max_current(); } // Segurança: relé sempre off e outputs seguros em caso de erro if (evse_get_error() != 0) { if (ac_relay_get_state()) { ac_relay_set_state(false); ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!"); } ac_relay_set_state(false); // redundância tolerável pilot_set_level(true); // sinal pilot sempre 12V (A) if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(false); } return; } // Fluxo normal switch (state) { case EVSE_STATE_A: case EVSE_STATE_E: case EVSE_STATE_F: ac_relay_set_state(false); pilot_set_level(state == EVSE_STATE_A); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(false); } break; case EVSE_STATE_B1: pilot_set_level(true); ac_relay_set_state(false); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(true); } if (rcm_test()) { ESP_LOGI(TAG, "RCM self test passed"); } else { ESP_LOGW(TAG, "RCM self test failed"); } break; case EVSE_STATE_B2: pilot_set_amps(MIN(current * 10, cable_max_current * 10)); ac_relay_set_state(false); break; case EVSE_STATE_C1: case EVSE_STATE_D1: pilot_set_level(true); c1_d1_waiting = true; c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000); break; case EVSE_STATE_C2: case EVSE_STATE_D2: pilot_set_amps(MIN(current * 10, cable_max_current * 10)); ac_relay_set_state(true); // Só chega aqui se não há erro! break; } } // FSM principal - centraliza a lógica de erro e de todos os estados void evse_fsm_process( pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled ) { // Proteção total: erro força F sempre! if (evse_get_error() != 0) { if (evse_get_state() != EVSE_STATE_F) { ESP_LOGW(TAG, "Erro ativo detectado: forçando estado FAULT (F)"); evse_set_state(EVSE_STATE_F); } update_outputs(EVSE_STATE_F); return; } TickType_t now = xTaskGetTickCount(); evse_state_t prev = evse_get_state(); evse_state_t curr = prev; switch (curr) { case EVSE_STATE_A: if (!available) { evse_set_state(EVSE_STATE_F); } else if (pilot_voltage == PILOT_VOLTAGE_9) { evse_set_state(EVSE_STATE_B1); } break; case EVSE_STATE_B1: case EVSE_STATE_B2: if (!available) { evse_set_state(EVSE_STATE_F); break; } switch (pilot_voltage) { case PILOT_VOLTAGE_12: evse_set_state(EVSE_STATE_A); break; case PILOT_VOLTAGE_9: evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); break; case PILOT_VOLTAGE_6: evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); break; default: break; } break; case EVSE_STATE_C1: case EVSE_STATE_D1: if (c1_d1_waiting && now >= c1_d1_relay_to) { ac_relay_set_state(false); c1_d1_waiting = false; if (!available) { evse_set_state(EVSE_STATE_F); break; } } __attribute__((fallthrough)); case EVSE_STATE_C2: case EVSE_STATE_D2: if (!enabled || !available) { evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1) ? EVSE_STATE_D1 : EVSE_STATE_C1); break; } switch (pilot_voltage) { case PILOT_VOLTAGE_6: evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); break; case PILOT_VOLTAGE_3: evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1); break; case PILOT_VOLTAGE_9: evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); break; case PILOT_VOLTAGE_12: evse_set_state(EVSE_STATE_A); break; default: break; } break; case EVSE_STATE_E: // Estado elétrico grave: só reset manual break; case EVSE_STATE_F: // Fault: só sai se disponível e sem erro if (available && evse_get_error() == 0) { evse_set_state(EVSE_STATE_A); } break; } evse_state_t next = evse_get_state(); update_outputs(next); if (next != prev) { ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(prev), evse_state_to_str(next)); } }