Files
chargeflow/components/evse/evse_fsm.c
2025-06-25 06:34:03 +01:00

208 lines
6.3 KiB
C
Executable File

#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));
}
}