Adicionar primeiro

This commit is contained in:
2025-06-06 21:17:25 +01:00
parent c188084ba4
commit 282e7f517b
841 changed files with 199592 additions and 1 deletions

18
components/evse/CMakeLists.txt Executable file
View File

@@ -0,0 +1,18 @@
set(srcs
evse_core.c
evse_state.c
evse_error.c
evse_config.c
evse_limits.c
evse_events.c
evse_fsm.c
evse_manager.c
evse_hardware.c
)
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash
REQUIRES peripherals
)

203
components/evse/evse_config.c Executable file
View File

@@ -0,0 +1,203 @@
#include "evse_config.h"
#include "board_config.h"
#include "evse_limits.h"
#include "esp_log.h"
#include "nvs.h"
static const char *TAG = "evse_config";
static nvs_handle_t nvs;
// Parâmetros configuráveis
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
static uint8_t grid_max_current = MAX_GRID_CURRENT_LIMIT;
static uint16_t charging_current;
static bool socket_outlet;
static bool rcm;
static uint8_t temp_threshold = 60;
static bool require_auth;
esp_err_t evse_config_init(void) {
ESP_LOGI(TAG, "Abrindo namespace NVS");
return nvs_open("evse", NVS_READWRITE, &nvs);
}
void evse_check_defaults(void) {
esp_err_t err;
uint8_t u8;
uint16_t u16;
uint32_t u32;
bool needs_commit = false;
// Max charging current
err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) {
max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
needs_commit = true;
} else {
max_charging_current = u8;
}
// Grid max current
err = nvs_get_u8(nvs, "grid_max_curr", &u8);
if (err != ESP_OK || u8 < MIN_GRID_CURRENT_LIMIT || u8 > MAX_GRID_CURRENT_LIMIT) {
grid_max_current = MAX_GRID_CURRENT_LIMIT;
nvs_set_u8(nvs, "grid_max_curr", grid_max_current);
needs_commit = true;
} else {
grid_max_current = u8;
}
// Charging current (decA)
err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) {
charging_current = max_charging_current * 10;
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
needs_commit = true;
} else {
charging_current = u16;
}
err = nvs_get_u8(nvs, "require_auth", &u8);
require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
if (err != ESP_OK) {
nvs_set_u8(nvs, "require_auth", require_auth);
needs_commit = true;
}
err = nvs_get_u8(nvs, "socket_outlet", &u8);
socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
if (err != ESP_OK) {
nvs_set_u8(nvs, "socket_outlet", socket_outlet);
needs_commit = true;
}
err = nvs_get_u8(nvs, "rcm", &u8);
rcm = (err == ESP_OK && u8) && board_config.rcm;
if (err != ESP_OK) {
nvs_set_u8(nvs, "rcm", rcm);
needs_commit = true;
}
err = nvs_get_u8(nvs, "temp_threshold", &u8);
temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
if (err != ESP_OK) {
nvs_set_u8(nvs, "temp_threshold", temp_threshold);
needs_commit = true;
}
// Limites adicionais
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) {
evse_set_consumption_limit(u32);
}
if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) {
evse_set_charging_time_limit(u32);
}
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) {
evse_set_under_power_limit(u16);
}
if (needs_commit) {
nvs_commit(nvs);
}
}
// Corrente
uint8_t evse_get_max_charging_current(void) { return max_charging_current; }
esp_err_t evse_set_max_charging_current(uint8_t value) {
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
return ESP_ERR_INVALID_ARG;
max_charging_current = value;
nvs_set_u8(nvs, "max_chrg_curr", value);
return nvs_commit(nvs);
}
uint8_t grid_get_max_current(void) { return grid_max_current; }
esp_err_t grid_set_max_current(uint8_t value) {
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
return ESP_ERR_INVALID_ARG;
grid_max_current = value;
nvs_set_u8(nvs, "grid_max_curr", value);
return nvs_commit(nvs);
}
uint16_t evse_get_charging_current(void) { return charging_current; }
esp_err_t evse_set_charging_current(uint16_t value) {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10))
return ESP_ERR_INVALID_ARG;
charging_current = value;
nvs_set_u16(nvs, "def_chrg_curr", value);
return nvs_commit(nvs);
}
uint16_t evse_get_default_charging_current(void) {
uint16_t value;
nvs_get_u16(nvs, "def_chrg_curr", &value);
return value;
}
esp_err_t evse_set_default_charging_current(uint16_t value) {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10))
return ESP_ERR_INVALID_ARG;
nvs_set_u16(nvs, "def_chrg_curr", value);
return nvs_commit(nvs);
}
// Socket outlet
bool evse_get_socket_outlet(void) { return socket_outlet; }
esp_err_t evse_set_socket_outlet(bool value) {
if (value && !board_config.proximity) return ESP_ERR_INVALID_ARG;
socket_outlet = value;
nvs_set_u8(nvs, "socket_outlet", value);
return nvs_commit(nvs);
}
// RCM
bool evse_is_rcm(void) { return rcm; }
esp_err_t evse_set_rcm(bool value) {
if (value && !board_config.rcm) return ESP_ERR_INVALID_ARG;
rcm = value;
nvs_set_u8(nvs, "rcm", value);
return nvs_commit(nvs);
}
// Temperatura
uint8_t evse_get_temp_threshold(void) { return temp_threshold; }
esp_err_t evse_set_temp_threshold(uint8_t value) {
if (value < 40 || value > 80) return ESP_ERR_INVALID_ARG;
temp_threshold = value;
nvs_set_u8(nvs, "temp_threshold", value);
return nvs_commit(nvs);
}
// Autenticação
bool evse_is_require_auth(void) { return require_auth; }
void evse_set_require_auth(bool value) {
require_auth = value;
nvs_set_u8(nvs, "require_auth", value);
nvs_commit(nvs);
}
// Disponibilidade
static bool is_available = true;
bool evse_config_is_available(void) {
return is_available;
}
void evse_config_set_available(bool available) {
is_available = available;
}
// Ativação/desativação
static bool is_enabled = true;
bool evse_config_is_enabled(void) {
return is_enabled;
}
void evse_config_set_enabled(bool enabled) {
is_enabled = enabled;
}

131
components/evse/evse_core.c Executable file
View File

@@ -0,0 +1,131 @@
// evse_core.c - Função principal de controle do EVSE
#include "evse_fsm.h"
#include "evse_error.h"
#include "evse_limits.h"
#include "evse_config.h"
#include "evse_api.h"
#include "pilot.h"
#include "energy_meter.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
static const char *TAG = "evse_core";
static SemaphoreHandle_t mutex;
static evse_state_t last_state = EVSE_STATE_A;
static bool authorized = false;
static TickType_t auth_grant_to = 0;
static void evse_core_task(void *arg);
void evse_init(void) {
ESP_LOGI(TAG, "EVSE Init");
mutex = xSemaphoreCreateMutex();
evse_check_defaults();
evse_fsm_reset();
pilot_set_level(true); // Estado inicial do piloto
xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL);
}
void evse_process(void) {
xSemaphoreTake(mutex, portMAX_DELAY);
pilot_voltage_t pilot_voltage;
bool is_n12v = false;
pilot_measure(&pilot_voltage, &is_n12v);
ESP_LOGD(TAG, "Pilot: %d, -12V: %s", pilot_voltage, is_n12v ? "yes" : "no");
evse_error_check(pilot_voltage, is_n12v);
if (evse_get_error() == 0 && !evse_is_error_cleared()) {
// Autorização temporária se exigida
if (!authorized && evse_is_require_auth()) {
authorized = auth_grant_to >= xTaskGetTickCount();
if (auth_grant_to) auth_grant_to = 0;
} else {
authorized = true;
}
evse_fsm_process(
pilot_voltage,
authorized,
evse_config_is_available(),
evse_config_is_enabled()
);
evse_limits_check(evse_get_state());
evse_state_t current = evse_get_state();
if (current != last_state) {
ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current));
last_state = current;
}
}
if (evse_get_error() == 0) {
evse_mark_error_cleared();
}
xSemaphoreGive(mutex);
energy_meter_process(
evse_state_is_charging(evse_get_state()),
evse_get_charging_current()
);
}
// ================================
// Interface pública
// ================================
bool evse_is_enabled(void) {
return evse_config_is_enabled();
}
void evse_set_enabled(bool value) {
ESP_LOGI(TAG, "Set enabled %d", value);
evse_config_set_enabled(value);
}
bool evse_is_available(void) {
return evse_config_is_available();
}
void evse_set_available(bool value) {
ESP_LOGI(TAG, "Set available %d", value);
evse_config_set_available(value);
}
bool evse_is_pending_auth(void) {
return evse_state_is_session(evse_get_state()) && !authorized;
}
void evse_authorize(void) {
ESP_LOGI(TAG, "Authorize");
auth_grant_to = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
}
void evse_set_authorized(bool value) {
ESP_LOGI(TAG, "Set authorized %d", value);
xSemaphoreTake(mutex, portMAX_DELAY);
authorized = value;
xSemaphoreGive(mutex);
}
// ================================
// Tarefa principal
// ================================
static void evse_core_task(void *arg) {
while (true) {
evse_process();
vTaskDelay(pdMS_TO_TICKS(100));
}
}

119
components/evse/evse_error.c Executable file
View File

@@ -0,0 +1,119 @@
#include "evse_error.h"
#include "evse_config.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "ntc_sensor.h"
static const char *TAG = "evse_error";
static uint32_t error_bits = 0;
static TickType_t auto_clear_timeout = 0;
static bool error_cleared = false;
void evse_error_init(void) {
// Inicialização do sistema de erros
}
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v) {
ESP_LOGD(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s",
pilot_voltage, is_n12v ? "true" : "false");
// Falha elétrica geral no pilot
if (pilot_voltage == PILOT_VOLTAGE_1) {
if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT)) { // Verifica se o erro já foi registrado
evse_error_set(EVSE_ERR_PILOT_FAULT_BIT);
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
}
}
// Falta de -12V durante PWM (C ou D)
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v) {
if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT)) { // Verifica se o erro já foi registrado
evse_error_set(EVSE_ERR_DIODE_SHORT_BIT);
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
}
}
}
void evse_temperature_check(void) {
float temp_c = ntc_temp_sensor(); // leitura atual (última medida válida)
uint8_t threshold = evse_get_temp_threshold(); // padrão 60°C, configurável
// Log informativo com os valores
ESP_LOGD(TAG, "Verificando temperatura: atual = %.2f °C, limite = %d °C", temp_c, threshold);
// Se a temperatura parecer inválida, aplica erro de sensor
if (temp_c < -40.0f || temp_c > 150.0f) {
if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT)) { // Verifica se o erro já foi registrado
evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT);
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
}
return;
}
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida
if (temp_c >= threshold) {
if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT)) { // Verifica se o erro já foi registrado
evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT);
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C", temp_c, threshold);
}
} else {
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
}
}
uint32_t evse_get_error(void) {
return error_bits;
}
bool evse_is_error_cleared(void) {
return error_cleared;
}
void evse_mark_error_cleared(void) {
error_cleared = false;
}
// Já existentes
void evse_error_set(uint32_t bitmask) {
error_bits |= bitmask;
if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) {
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s
}
}
void evse_error_clear(uint32_t bitmask) {
bool had_error = error_bits != 0;
error_bits &= ~bitmask;
if (had_error && error_bits == 0) {
error_cleared = true;
}
}
void evse_error_tick(void) {
if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) && xTaskGetTickCount() >= auto_clear_timeout) {
evse_error_clear(EVSE_ERR_AUTO_CLEAR_BITS);
auto_clear_timeout = 0;
}
}
bool evse_error_is_active(void) {
return error_bits != 0;
}
uint32_t evse_error_get_bits(void) {
return error_bits;
}
void evse_error_reset_flag(void) {
error_cleared = false;
}
bool evse_error_cleared_flag(void) {
return error_cleared;
}

3
components/evse/evse_events.c Executable file
View File

@@ -0,0 +1,3 @@
#include "evse_events.h"
ESP_EVENT_DEFINE_BASE(EVSE_EVENT);

175
components/evse/evse_fsm.c Executable file
View File

@@ -0,0 +1,175 @@
// 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 "energy_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());
}
}

View File

@@ -0,0 +1,55 @@
#include "evse_hardware.h"
#include "pilot.h"
#include "ac_relay.h"
#include "socket_lock.h"
#include "proximity.h"
static const char *TAG = "evse_hardware";
void evse_hardware_init(void) {
pilot_set_level(true); // Sinal piloto em 12V (inicial)
ac_relay_set_state(false); // Relé desligado
//socket_lock_set_locked(false); // Destrava o conector
}
void evse_hardware_tick(void) {
// Tick para atualizações de hardware com polling, se necessário
}
bool evse_hardware_is_pilot_high(void) {
return pilot_get_state(); // true se sinal piloto estiver em nível alto
}
bool evse_hardware_is_vehicle_connected(void) {
// Verifica se o veículo está conectado pelo resistor do pino PP
return proximity_get_max_current() > 0;
}
bool evse_hardware_is_energy_detected(void) {
// TODO: Substituir com medição real de corrente ou consumo
return true;
}
void evse_hardware_relay_on(void) {
ac_relay_set_state(true);
}
void evse_hardware_relay_off(void) {
ac_relay_set_state(false);
}
bool evse_hardware_relay_status(void) {
return ac_relay_get_state();
}
void evse_hardware_lock(void) {
socket_lock_set_locked(true);
}
void evse_hardware_unlock(void) {
socket_lock_set_locked(false);
}
bool evse_hardware_is_locked(void) {
return socket_lock_get_status() == SOCKED_LOCK_STATUS_IDLE;
}

97
components/evse/evse_limits.c Executable file
View File

@@ -0,0 +1,97 @@
#include "evse_limits.h"
#include <stdint.h>
#include <stdbool.h>
// ========================
// Estado interno
// ========================
static bool limit_reached = false;
static uint32_t consumption_limit = 0;
static uint32_t charging_time_limit = 0;
static uint16_t under_power_limit = 0;
static uint32_t default_consumption_limit = 0;
static uint32_t default_charging_time_limit = 0;
static uint16_t default_under_power_limit = 0;
// ========================
// Estado de controle
// ========================
void evse_set_limit_reached(uint8_t value) {
limit_reached = (value != 0);
}
bool evse_is_limit_reached(void) {
return limit_reached;
}
// ========================
// Limites em tempo de execução
// ========================
uint32_t evse_get_consumption_limit(void) {
return consumption_limit;
}
void evse_set_consumption_limit(uint32_t value) {
consumption_limit = value;
}
uint32_t evse_get_charging_time_limit(void) {
return charging_time_limit;
}
void evse_set_charging_time_limit(uint32_t value) {
charging_time_limit = value;
}
uint16_t evse_get_under_power_limit(void) {
return under_power_limit;
}
void evse_set_under_power_limit(uint16_t value) {
under_power_limit = value;
}
// ========================
// Limites padrão (persistentes)
// ========================
uint32_t evse_get_default_consumption_limit(void) {
return default_consumption_limit;
}
void evse_set_default_consumption_limit(uint32_t value) {
default_consumption_limit = value;
}
uint32_t evse_get_default_charging_time_limit(void) {
return default_charging_time_limit;
}
void evse_set_default_charging_time_limit(uint32_t value) {
default_charging_time_limit = value;
}
uint16_t evse_get_default_under_power_limit(void) {
return default_under_power_limit;
}
void evse_set_default_under_power_limit(uint16_t value) {
default_under_power_limit = value;
}
// ========================
// Lógica de verificação de limites
// ========================
void evse_limits_check(evse_state_t state) {
// Se algum limite estiver ativo, verifique o estado
if ((consumption_limit > 0 || charging_time_limit > 0 || under_power_limit > 0)
&& evse_state_is_charging(state)) {
// (Lógica real a ser aplicada aqui, ex: medição de consumo, tempo ou potência)
evse_set_limit_reached(1);
}
}

View File

@@ -0,0 +1,81 @@
#include "evse_manager.h"
#include "evse_state.h"
#include "evse_error.h"
#include "evse_hardware.h"
#include "evse_config.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
static const char *TAG = "evse_manager";
static SemaphoreHandle_t evse_mutex;
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
void evse_manager_init(void) {
evse_mutex = xSemaphoreCreateMutex();
evse_config_init();
evse_error_init();
evse_hardware_init();
evse_state_init();
ESP_LOGI(TAG, "EVSE Manager initialized");
xTaskCreate(
evse_manager_task,
"evse_manager_task",
4096, // stack size
NULL, // param
5, // priority
NULL // handle
);
}
void evse_manager_task(void *arg) {
while (true) {
evse_manager_tick();
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
}
}
void evse_manager_tick(void) {
xSemaphoreTake(evse_mutex, portMAX_DELAY);
// Atualizações cíclicas de módulos
evse_hardware_tick();
evse_error_tick();
evse_state_tick();
evse_temperature_check();
xSemaphoreGive(evse_mutex);
}
bool evse_manager_is_available(void) {
return evse_config_is_available();
}
void evse_manager_set_available(bool available) {
evse_config_set_available(available);
}
void evse_manager_set_authorized(bool authorized) {
evse_state_set_authorized(authorized);
}
bool evse_manager_is_charging(void) {
return evse_state_is_charging(evse_get_state());
}
void evse_manager_set_enabled(bool enabled) {
evse_config_set_enabled(enabled);
}
bool evse_manager_is_enabled(void) {
return evse_config_is_enabled();
}

View File

@@ -0,0 +1,49 @@
#include "evse_state.h"
static evse_state_t current_state = EVSE_STATE_A;
void evse_set_state(evse_state_t state) {
current_state = state;
}
evse_state_t evse_get_state(void) {
return current_state;
}
const char* evse_state_to_str(evse_state_t state) {
switch (state) {
case EVSE_STATE_A: return "A - EV Not Connected (12V)";
case EVSE_STATE_B1: return "B1 - EV Connected (9V, Not Authorized)";
case EVSE_STATE_B2: return "B2 - EV Connected (9V, Authorized and Ready)";
case EVSE_STATE_C1: return "C1 - Charging Requested (6V, Relay Off)";
case EVSE_STATE_C2: return "C2 - Charging Active (6V, Relay On)";
case EVSE_STATE_D1: return "D1 - Ventilation Required (3V, Relay Off)";
case EVSE_STATE_D2: return "D2 - Ventilation Active (3V, Relay On)";
case EVSE_STATE_E: return "E - Error: Control Pilot Shorted to Ground (0V)";
case EVSE_STATE_F: return "F - Fault: EVSE Unavailable or No Pilot Signal";
default: return "Unknown State";
}
}
void evse_state_init(void) {
// Inicialização do estado EVSE
}
void evse_state_tick(void) {
// Tick do estado (placeholder)
}
bool evse_state_is_charging(evse_state_t state) {
return state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
}
bool evse_state_is_plugged(evse_state_t state) {
return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 ||
state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
state == EVSE_STATE_D1 || state == EVSE_STATE_D2;
}
bool evse_state_is_session(evse_state_t state) {
return state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
}

View File

@@ -0,0 +1,70 @@
#ifndef EVSE_API_H
#define EVSE_API_H
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "evse_state.h" // Define evse_state_t
// Inicialização
void evse_init(void);
void evse_process(void);
// Estado
evse_state_t evse_get_state(void);
const char* evse_state_to_str(evse_state_t state);
bool evse_is_connector_plugged(evse_state_t state);
bool evse_is_limit_reached(void);
// Autorização e disponibilidade
bool evse_is_enabled(void);
void evse_set_enabled(bool value);
bool evse_is_available(void);
void evse_set_available(bool value);
bool evse_is_require_auth(void);
void evse_set_require_auth(bool value);
void evse_authorize(void);
bool evse_is_pending_auth(void);
void evse_set_authorized(bool value);
// Corrente
uint16_t evse_get_charging_current(void);
esp_err_t evse_set_charging_current(uint16_t value);
uint16_t evse_get_default_charging_current(void);
esp_err_t evse_set_default_charging_current(uint16_t value);
uint8_t evse_get_max_charging_current(void);
esp_err_t evse_set_max_charging_current(uint8_t value);
// Grid
uint8_t grid_get_max_current(void);
esp_err_t grid_set_max_current(uint8_t value);
// Temperatura
uint8_t evse_get_temp_threshold(void);
esp_err_t evse_set_temp_threshold(uint8_t value);
// RCM / Socket
bool evse_get_socket_outlet(void);
esp_err_t evse_set_socket_outlet(bool value);
bool evse_is_rcm(void);
esp_err_t evse_set_rcm(bool value);
// Limites
uint32_t evse_get_consumption_limit(void);
void evse_set_consumption_limit(uint32_t value);
uint32_t evse_get_charging_time_limit(void);
void evse_set_charging_time_limit(uint32_t value);
uint16_t evse_get_under_power_limit(void);
void evse_set_under_power_limit(uint16_t value);
void evse_set_limit_reached(uint8_t value);
// Limites default
uint32_t evse_get_default_consumption_limit(void);
void evse_set_default_consumption_limit(uint32_t value);
uint32_t evse_get_default_charging_time_limit(void);
void evse_set_default_charging_time_limit(uint32_t value);
uint16_t evse_get_default_under_power_limit(void);
void evse_set_default_under_power_limit(uint16_t value);
#endif // EVSE_API_H

View File

@@ -0,0 +1,78 @@
#ifndef EVSE_CONFIG_H
#define EVSE_CONFIG_H
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
// ========================
// Limites Globais (Defines)
// ========================
// Corrente máxima de carregamento (configurável pelo usuário)
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
#define MAX_CHARGING_CURRENT_LIMIT 16 // A
// Corrente máxima da rede elétrica (grid)
#define MIN_GRID_CURRENT_LIMIT 6 // A
#define MAX_GRID_CURRENT_LIMIT 100 // A
// Corrente via cabo (proximity) — se configurável
#define MIN_CABLE_CURRENT_LIMIT 6 // A
#define MAX_CABLE_CURRENT_LIMIT 63 // A
// ========================
// Funções de Configuração
// ========================
// Inicialização
esp_err_t evse_config_init(void);
void evse_check_defaults(void);
// Corrente de carregamento
uint8_t evse_get_max_charging_current(void);
esp_err_t evse_set_max_charging_current(uint8_t value);
uint16_t evse_get_charging_current(void);
esp_err_t evse_set_charging_current(uint16_t value);
uint16_t evse_get_default_charging_current(void);
esp_err_t evse_set_default_charging_current(uint16_t value);
// Corrente da rede elétrica
uint8_t grid_get_max_current(void);
esp_err_t grid_set_max_current(uint8_t value);
// Configuração de socket outlet
bool evse_get_socket_outlet(void);
esp_err_t evse_set_socket_outlet(bool socket_outlet);
// RCM
bool evse_is_rcm(void);
esp_err_t evse_set_rcm(bool rcm);
// Temperatura
uint8_t evse_get_temp_threshold(void);
esp_err_t evse_set_temp_threshold(uint8_t threshold);
// Autenticação
bool evse_is_require_auth(void);
void evse_set_require_auth(bool require);
// Disponibilidade
bool evse_config_is_available(void);
void evse_config_set_available(bool available);
// Ativação/desativação do EVSE
bool evse_config_is_enabled(void);
void evse_config_set_enabled(bool enabled);
#ifdef __cplusplus
}
#endif
#endif // EVSE_CONFIG_H

View File

@@ -0,0 +1,45 @@
#ifndef EVSE_ERROR_H
#define EVSE_ERROR_H
#include <stdint.h>
#include <stdbool.h>
#include "pilot.h"
#define EVSE_ERR_AUTO_CLEAR_BITS ( \
EVSE_ERR_DIODE_SHORT_BIT | \
EVSE_ERR_TEMPERATURE_HIGH_BIT | \
EVSE_ERR_RCM_TRIGGERED_BIT )
// Error bits
#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0)
#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1)
#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2)
#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3)
#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4)
#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5)
#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6)
#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7)
// Inicialização do módulo de erros
void evse_error_init(void);
// Verificações e monitoramento
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v);
void evse_temperature_check(void);
void evse_error_tick(void);
// Leitura e controle de erros
uint32_t evse_get_error(void);
bool evse_is_error_cleared(void);
void evse_mark_error_cleared(void);
void evse_error_set(uint32_t bitmask);
void evse_error_clear(uint32_t bitmask);
bool evse_error_is_active(void);
uint32_t evse_error_get_bits(void);
void evse_error_reset_flag(void);
bool evse_error_cleared_flag(void);
#endif // EVSE_ERROR_H

View File

@@ -0,0 +1,33 @@
#ifndef EVSE_EVENTS_H
#define EVSE_EVENTS_H
#include "evse_api.h"
#include "esp_event_base.h"
// Certifique-se de que ESP_EVENT_DECLARE_BASE seja corretamente reconhecido
#ifdef __cplusplus
extern "C" {
#endif
// Declaração da base de eventos EVSE (será definida em evse_events.c)
ESP_EVENT_DECLARE_BASE(EVSE_EVENT);
typedef enum {
EVSE_EVENT_STATE_CHANGED,
EVSE_EVENT_ERROR,
EVSE_EVENT_ERROR_CLEARED,
EVSE_EVENT_LIMIT_REACHED,
EVSE_EVENT_AUTH_GRANTED
} evse_event_id_t;
// Estrutura do evento de mudança de estado
typedef struct {
evse_state_t previous;
evse_state_t current;
} evse_event_state_changed_t;
#ifdef __cplusplus
}
#endif
#endif // EVSE_EVENTS_H

View File

@@ -0,0 +1,37 @@
#ifndef EVSE_FSM_H
#define EVSE_FSM_H
#include <stdint.h>
#include <stdbool.h>
#include "evse_api.h"
#include "pilot.h"
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reinicia a máquina de estados do EVSE para o estado inicial (A).
*/
void evse_fsm_reset(void);
/**
* @brief Processa uma leitura do sinal de piloto e atualiza a máquina de estados do EVSE.
*
* Esta função deve ser chamada periodicamente pelo núcleo de controle para
* avaliar mudanças no estado do conector, disponibilidade do carregador e
* autorização do usuário.
*
* @param pilot_voltage Leitura atual da tensão do sinal piloto.
* @param authorized Indica se o carregamento foi autorizado.
* @param available Indica se o carregador está disponível (ex: sem falhas).
* @param enabled Indica se o carregador está habilitado via software.
*/
void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled);
#ifdef __cplusplus
}
#endif
#endif // EVSE_FSM_H

View File

@@ -0,0 +1,71 @@
#ifndef EVSE_HARDWARE_H
#define EVSE_HARDWARE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
/**
* @brief Inicializa todos os periféricos de hardware do EVSE (pilot, relé, trava, etc.)
*/
void evse_hardware_init(void);
/**
* @brief Executa atualizações periódicas no hardware (tick)
*/
void evse_hardware_tick(void);
/**
* @brief Verifica se o sinal piloto está em nível alto (12V)
*/
bool evse_hardware_is_pilot_high(void);
/**
* @brief Verifica se o veículo está fisicamente conectado via Proximity
*/
bool evse_hardware_is_vehicle_connected(void);
/**
* @brief Verifica se há consumo de energia (corrente detectada)
*/
bool evse_hardware_is_energy_detected(void);
/**
* @brief Liga o relé de fornecimento de energia
*/
void evse_hardware_relay_on(void);
/**
* @brief Desliga o relé de fornecimento de energia
*/
void evse_hardware_relay_off(void);
/**
* @brief Consulta o estado atual do relé
* @return true se ligado, false se desligado
*/
bool evse_hardware_relay_status(void);
/**
* @brief Aciona a trava física do conector
*/
void evse_hardware_lock(void);
/**
* @brief Libera a trava física do conector
*/
void evse_hardware_unlock(void);
/**
* @brief Verifica se o conector está travado
*/
bool evse_hardware_is_locked(void);
#ifdef __cplusplus
}
#endif
#endif // EVSE_HARDWARE_H

View File

@@ -0,0 +1,43 @@
#ifndef EVSE_LIMITS_H
#define EVSE_LIMITS_H
#include <stdint.h>
#include <stdbool.h>
#include "evse_state.h"
#ifdef __cplusplus
extern "C" {
#endif
/// Estado dos limites
void evse_set_limit_reached(uint8_t value);
bool evse_is_limit_reached(void);
/// Verifica e aplica lógica de limites com base no estado atual do EVSE
void evse_limits_check(evse_state_t state);
/// Limites ativos (runtime)
uint32_t evse_get_consumption_limit(void);
void evse_set_consumption_limit(uint32_t value);
uint32_t evse_get_charging_time_limit(void);
void evse_set_charging_time_limit(uint32_t value);
uint16_t evse_get_under_power_limit(void);
void evse_set_under_power_limit(uint16_t value);
/// Limites padrão (persistentes)
uint32_t evse_get_default_consumption_limit(void);
void evse_set_default_consumption_limit(uint32_t value);
uint32_t evse_get_default_charging_time_limit(void);
void evse_set_default_charging_time_limit(uint32_t value);
uint16_t evse_get_default_under_power_limit(void);
void evse_set_default_under_power_limit(uint16_t value);
#ifdef __cplusplus
}
#endif
#endif // EVSE_LIMITS_H

View File

@@ -0,0 +1,63 @@
#ifndef EVSE_MANAGER_H
#define EVSE_MANAGER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include "evse_state.h"
#include "evse_error.h"
#include "evse_config.h"
/**
* @brief Inicializa todos os módulos do EVSE e inicia a tarefa de supervisão periódica.
*/
void evse_manager_init(void);
/**
* @brief Executa uma iteração do ciclo de controle do EVSE (chamada automaticamente pela task).
*
* Você normalmente não precisa chamar isso manualmente.
*/
void evse_manager_tick(void);
/**
* @brief Verifica se o EVSE está disponível para conexão (não bloqueado, sem falha crítica, etc).
*/
bool evse_manager_is_available(void);
/**
* @brief Define se o EVSE deve estar disponível.
*
* Pode ser usado, por exemplo, para bloquear carregamento via comando remoto.
*/
void evse_manager_set_available(bool available);
/**
* @brief Define se o EVSE está autorizado a iniciar carregamento (ex: após autenticação).
*/
void evse_manager_set_authorized(bool authorized);
/**
* @brief Verifica se o EVSE está em estado de carregamento ativo.
*/
bool evse_manager_is_charging(void);
/**
* @brief Ativa ou desativa o EVSE (liga/desliga logicamente o carregamento).
*/
void evse_manager_set_enabled(bool enabled);
/**
* @brief Verifica se o EVSE está ativado logicamente.
*/
bool evse_manager_is_enabled(void);
void evse_manager_task(void *arg);
#ifdef __cplusplus
}
#endif
#endif // EVSE_MANAGER_H

View File

@@ -0,0 +1,42 @@
#ifndef EVSE_STATE_H
#define EVSE_STATE_H
#include <stdbool.h>
// Estado do EVSE (pilot signal)
typedef enum {
EVSE_STATE_A,
EVSE_STATE_B1,
EVSE_STATE_B2,
EVSE_STATE_C1,
EVSE_STATE_C2,
EVSE_STATE_D1,
EVSE_STATE_D2,
EVSE_STATE_E,
EVSE_STATE_F
} evse_state_t;
// Funções públicas necessárias
void evse_state_init(void);
void evse_state_tick(void);
void evse_state_set_authorized(bool authorized);
evse_state_t evse_get_state(void);
void evse_set_state(evse_state_t state);
// Converte o estado para string
const char* evse_state_to_str(evse_state_t state);
// Retorna true se o estado representa sessão ativa
bool evse_state_is_session(evse_state_t state);
// Retorna true se o estado representa carregamento ativo
bool evse_state_is_charging(evse_state_t state);
// Retorna true se o estado representa veículo conectado
bool evse_state_is_plugged(evse_state_t state);
#endif // EVSE_STATE_H