// evse_fsm.c - Máquina de Estados EVSE com controle centralizado #include "evse_fsm.h" #include "evse_api.h" #include "pilot.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" 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; } static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_t cable_max_current, bool socket_outlet) { 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); } //energy_meter_stop_session(); 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"); } if (socket_outlet) { cable_max_current = proximity_get_max_current(); } //energy_meter_start_session(); break; case EVSE_STATE_B2: pilot_set_amps(MIN(charging_current, 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(charging_current, cable_max_current * 10)); ac_relay_set_state(true); break; } } void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { TickType_t now = xTaskGetTickCount(); evse_state_t previous_state = evse_get_state(); evse_state_t current_state = previous_state; switch (current_state) { 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; } } // fallthrough intencional case EVSE_STATE_C2: case EVSE_STATE_D2: if (!enabled || !available) { evse_set_state( (current_state == EVSE_STATE_D2 || current_state == 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: // Sem transições a partir de E break; case EVSE_STATE_F: if (available) { evse_set_state(EVSE_STATE_A); } break; } evse_state_t new_state = evse_get_state(); if (new_state != previous_state) { ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(previous_state), evse_state_to_str(new_state)); update_outputs(new_state, evse_get_charging_current(), evse_get_max_charging_current(), evse_get_socket_outlet()); } }