176 lines
5.4 KiB
C
Executable File
176 lines
5.4 KiB
C
Executable File
// 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 "meter.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());
|
|
}
|
|
}
|