// components/evse/evse_fsm.c #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 void evse_fsm_reset(void) { evse_set_state(EVSE_STATE_A); } /** * @brief Atualiza saídas de hardware (pilot, relé, trava) em função do estado lógico. */ 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 total: qualquer erro ativo força saída segura 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!"); } else { ac_relay_set_state(false); } // Em erro, garantir pilot OFF (não PWM / não +12V) pilot_set_level(true); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(false); } return; } switch (state) { case EVSE_STATE_A: case EVSE_STATE_E: case EVSE_STATE_F: ac_relay_set_state(false); // A → pilot alto (+12V), E/F → pilot OFF 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); } (void)rcm_test(); break; case EVSE_STATE_B2: pilot_set_amps(MIN(current, cable_max_current)); ac_relay_set_state(false); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(true); } break; case EVSE_STATE_C1: case EVSE_STATE_D1: pilot_set_amps(MIN(current, cable_max_current)); ac_relay_set_state(false); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(true); } break; case EVSE_STATE_C2: case EVSE_STATE_D2: pilot_set_amps(MIN(current, cable_max_current)); ac_relay_set_state(true); if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(true); } break; } } /** * @brief Máquina de estados principal do EVSE (IEC 61851). */ void evse_fsm_process( pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { // 1) Erros globais: dominam qualquer outra lógica uint32_t err_bits = evse_get_error(); if (err_bits != 0) { evse_state_t forced_state = (err_bits & EVSE_ERR_PILOT_FAULT_BIT) ? EVSE_STATE_E : EVSE_STATE_F; if (evse_get_state() != forced_state) { ESP_LOGW(TAG, "Erro ativo detectado: forçando estado %s", evse_state_to_str(forced_state)); evse_set_state(forced_state); } update_outputs(forced_state); return; } evse_state_t curr = evse_get_state(); 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; case PILOT_VOLTAGE_3: evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1); break; default: break; } break; case EVSE_STATE_C1: case EVSE_STATE_D1: case EVSE_STATE_C2: case EVSE_STATE_D2: if (!available) { evse_set_state(EVSE_STATE_F); break; } if (!enabled) { if (curr == EVSE_STATE_C2) { evse_set_state(EVSE_STATE_C1); } else if (curr == EVSE_STATE_D2) { evse_set_state(EVSE_STATE_D1); } 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: // ✅ Agora recupera como F: se disponível e sem erro -> volta a A if (available && evse_get_error() == 0) { evse_set_state(EVSE_STATE_A); } break; case EVSE_STATE_F: if (available && evse_get_error() == 0) { evse_set_state(EVSE_STATE_A); } break; } evse_state_t next = evse_get_state(); update_outputs(next); }