new meter

This commit is contained in:
2025-06-14 10:27:29 +01:00
parent 4892718736
commit 6f95c7ba59
228 changed files with 3178 additions and 3115 deletions

View File

@@ -8,11 +8,12 @@ set(srcs
evse_fsm.c
evse_manager.c
evse_hardware.c
evse_pilot.c
)
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash
REQUIRES peripherals auth
PRIV_REQUIRES nvs_flash driver
REQUIRES peripherals auth loadbalancer
)

View File

@@ -1,5 +1,4 @@
#include <inttypes.h> // Include for PRI macros
#include <inttypes.h> // For PRI macros
#include "evse_config.h"
#include "board_config.h"
#include "evse_limits.h"
@@ -10,18 +9,22 @@ static const char *TAG = "evse_config";
static nvs_handle_t nvs;
// ========================
// Configurable parameters
// ========================
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 uint16_t charging_current; // Persisted (NVS)
static uint16_t charging_current_runtime = 0; // Runtime only
static bool socket_outlet;
static bool rcm;
static uint8_t temp_threshold = 60;
static bool require_auth;
// ========================
// Initialization
// ========================
esp_err_t evse_config_init(void) {
ESP_LOGD(TAG, "Initializing NVS configuration...");
ESP_LOGI(TAG, "Opening NVS namespace");
return nvs_open("evse", NVS_READWRITE, &nvs);
}
@@ -36,244 +39,221 @@ void evse_check_defaults(void) {
// Max charging current
err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
ESP_LOGD(TAG, "Max charging current read: %d", 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;
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
} else {
max_charging_current = u8;
}
// Grid max current
err = nvs_get_u8(nvs, "grid_max_curr", &u8);
ESP_LOGD(TAG, "Grid max current read: %d", 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;
ESP_LOGD(TAG, "Grid max current adjusted to: %d", grid_max_current);
} else {
grid_max_current = u8;
}
// Charging current (decA)
// Charging current (default, persisted)
err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
ESP_LOGD(TAG, "Charging current read: %d", 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;
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
} else {
charging_current = u16;
}
// Runtime charging current initialized from persisted default
charging_current_runtime = charging_current;
ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime);
// Auth required
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;
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
}
// Socket outlet
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;
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
}
// RCM
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;
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
}
// Temp threshold
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;
ESP_LOGD(TAG, "Temp threshold adjusted to: %d", temp_threshold);
}
// Additional limits
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) {
// Optional limits
if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK)
evse_set_consumption_limit(u32);
ESP_LOGD(TAG, "Consumption limit read and applied: %" PRIu32, u32); // Updated to PRIu32
}
if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) {
if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK)
evse_set_charging_time_limit(u32);
ESP_LOGD(TAG, "Charging time limit read and applied: %" PRIu32, u32); // Updated to PRIu32
}
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) {
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
evse_set_under_power_limit(u16);
ESP_LOGD(TAG, "Under power limit read and applied: %d", u16);
}
// Save to NVS if needed
if (needs_commit) {
nvs_commit(nvs);
ESP_LOGD(TAG, "Changes committed to NVS.");
err = nvs_commit(nvs);
if (err == ESP_OK) {
ESP_LOGD(TAG, "Configuration committed to NVS.");
} else {
ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err));
}
}
}
// Current
// ========================
// Charging current getters/setters
// ========================
uint8_t evse_get_max_charging_current(void) {
ESP_LOGI(TAG, "Max charging current read: %d", max_charging_current);
return max_charging_current;
}
esp_err_t evse_set_max_charging_current(uint8_t value) {
ESP_LOGI(TAG, "Attempting to set max charging current: %d", 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);
nvs_commit(nvs);
ESP_LOGD(TAG, "Max charging current adjusted to: %d", max_charging_current);
return ESP_OK;
}
uint8_t grid_get_max_current(void) {
ESP_LOGD(TAG, "Grid max current read: %d", grid_max_current);
return grid_max_current;
}
esp_err_t grid_set_max_current(uint8_t value) {
ESP_LOGD(TAG, "Attempting to set grid max current: %d", 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);
nvs_commit(nvs);
ESP_LOGD(TAG, "Grid max current adjusted to: %d", grid_max_current);
return ESP_OK;
return nvs_commit(nvs);
}
uint16_t evse_get_charging_current(void) {
ESP_LOGD(TAG, "Charging current read: %d", charging_current);
return charging_current;
}
esp_err_t evse_set_charging_current(uint16_t value) {
ESP_LOGD(TAG, "Attempting to set charging current: %d", 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);
nvs_commit(nvs);
ESP_LOGD(TAG, "Charging current adjusted to: %d", charging_current);
return ESP_OK;
return nvs_commit(nvs);
}
uint16_t evse_get_default_charging_current(void) {
uint16_t value;
nvs_get_u16(nvs, "def_chrg_curr", &value);
ESP_LOGD(TAG, "Default charging current read: %d", value);
return value;
if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
return value;
return charging_current;
}
esp_err_t evse_set_default_charging_current(uint16_t value) {
ESP_LOGD(TAG, "Attempting to set default charging current: %d", 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);
nvs_commit(nvs);
ESP_LOGD(TAG, "Default charging current adjusted to: %d", value);
return ESP_OK;
return nvs_commit(nvs);
}
// ========================
// Runtime current (not saved)
// ========================
void evse_set_runtime_charging_current(uint16_t value) {
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current)) {
ESP_LOGW(TAG, "Rejected runtime charging current (out of bounds): %d", value);
return;
}
charging_current_runtime = value;
ESP_LOGD(TAG, "Runtime charging current updated: %d", charging_current_runtime);
}
uint16_t evse_get_runtime_charging_current(void) {
return charging_current_runtime;
}
// ========================
// Socket outlet
// ========================
bool evse_get_socket_outlet(void) {
ESP_LOGD(TAG, "Socket outlet read: %d", socket_outlet);
return socket_outlet;
}
esp_err_t evse_set_socket_outlet(bool value) {
ESP_LOGD(TAG, "Attempting to set socket outlet: %d", value);
if (value && !board_config.proximity) return ESP_ERR_INVALID_ARG;
if (value && !board_config.proximity)
return ESP_ERR_INVALID_ARG;
socket_outlet = value;
nvs_set_u8(nvs, "socket_outlet", value);
nvs_commit(nvs);
ESP_LOGD(TAG, "Socket outlet adjusted to: %d", socket_outlet);
return ESP_OK;
return nvs_commit(nvs);
}
// ========================
// RCM
// ========================
bool evse_is_rcm(void) {
ESP_LOGD(TAG, "RCM read: %d", rcm);
return rcm;
}
esp_err_t evse_set_rcm(bool value) {
ESP_LOGD(TAG, "Attempting to set RCM: %d", value);
if (value && !board_config.rcm) return ESP_ERR_INVALID_ARG;
if (value && !board_config.rcm)
return ESP_ERR_INVALID_ARG;
rcm = value;
nvs_set_u8(nvs, "rcm", value);
nvs_commit(nvs);
ESP_LOGD(TAG, "RCM adjusted to: %d", rcm);
return ESP_OK;
return nvs_commit(nvs);
}
// ========================
// Temperature
// ========================
uint8_t evse_get_temp_threshold(void) {
ESP_LOGD(TAG, "Temp threshold read: %d", temp_threshold);
return temp_threshold;
}
esp_err_t evse_set_temp_threshold(uint8_t value) {
ESP_LOGI(TAG, "Attempting to set temp threshold: %d", value);
if (value < 40 || value > 80) return ESP_ERR_INVALID_ARG;
if (value < 40 || value > 80)
return ESP_ERR_INVALID_ARG;
temp_threshold = value;
nvs_set_u8(nvs, "temp_threshold", value);
nvs_commit(nvs);
ESP_LOGI(TAG, "Temp threshold adjusted to: %d", temp_threshold);
return ESP_OK;
return nvs_commit(nvs);
}
// ========================
// Authentication
// ========================
bool evse_is_require_auth(void) {
ESP_LOGD(TAG, "Require auth read: %d", require_auth);
return require_auth;
}
void evse_set_require_auth(bool value) {
ESP_LOGI(TAG, "Attempting to set require auth: %d", value);
require_auth = value;
nvs_set_u8(nvs, "require_auth", value);
nvs_commit(nvs);
ESP_LOGD(TAG, "Require auth adjusted to: %d", require_auth);
}
// ========================
// Availability
// ========================
static bool is_available = true;
bool evse_config_is_available(void) {
ESP_LOGD(TAG, "Checking availability: %d", is_available);
return is_available;
}
void evse_config_set_available(bool available) {
ESP_LOGD(TAG, "Setting availability to: %d", available);
is_available = available;
}
// ========================
// Enable/Disable
// ========================
static bool is_enabled = true;
bool evse_config_is_enabled(void) {
ESP_LOGD(TAG, "Checking if enabled: %d", is_enabled);
return is_enabled;
}
void evse_config_set_enabled(bool enabled) {
ESP_LOGD(TAG, "Setting enabled state to: %d", enabled);
is_enabled = enabled;
}

View File

@@ -5,7 +5,7 @@
#include "evse_limits.h"
#include "evse_config.h"
#include "evse_api.h"
#include "pilot.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
@@ -15,8 +15,6 @@ 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);
@@ -41,15 +39,13 @@ void evse_process(void) {
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()) {
bool authorized = evse_state_get_authorized();
evse_error_check(pilot_voltage, is_n12v);
evse_fsm_process(
pilot_voltage,
authorized,
evse_state_get_authorized(),
evse_config_is_available(),
evse_config_is_enabled()
);
@@ -58,18 +54,19 @@ void evse_process(void) {
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));
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);
}
// ================================
// Interface pública
// ================================
@@ -92,22 +89,6 @@ void evse_set_available(bool 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");
evse_state_set_authorized(true);
}
void evse_set_authorized(bool value) {
ESP_LOGI(TAG, "Set authorized %d", value);
xSemaphoreTake(mutex, portMAX_DELAY);
authorized = value;
xSemaphoreGive(mutex);
}
// ================================
// Tarefa principal
// ================================

View File

@@ -33,6 +33,7 @@ void evse_error_check(pilot_voltage_t pilot_voltage, bool 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)");
ESP_LOGW(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", pilot_voltage, is_n12v ? "true" : "false");
}
}
}

View File

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

View File

@@ -2,7 +2,8 @@
#include "evse_fsm.h"
#include "evse_api.h"
#include "pilot.h"
#include "evse_pilot.h"
#include "evse_config.h"
#include "esp_log.h"
#include "ac_relay.h"
#include "board_config.h"
@@ -26,7 +27,15 @@ void evse_fsm_reset(void) {
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) {
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();
}
switch (state) {
case EVSE_STATE_A:
case EVSE_STATE_E:
@@ -36,7 +45,6 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
if (board_config.socket_lock && socket_outlet) {
socket_lock_set_locked(false);
}
//energy_meter_stop_session();
break;
case EVSE_STATE_B1:
@@ -51,16 +59,10 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
} 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));
pilot_set_amps(MIN(current * 10, cable_max_current * 10));
ac_relay_set_state(false);
break;
@@ -73,7 +75,7 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
case EVSE_STATE_C2:
case EVSE_STATE_D2:
pilot_set_amps(MIN(charging_current, cable_max_current * 10));
pilot_set_amps(MIN(current * 10, cable_max_current * 10));
ac_relay_set_state(true);
break;
}
@@ -81,10 +83,10 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
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;
evse_state_t prev = evse_get_state();
evse_state_t curr = prev;
switch (current_state) {
switch (curr) {
case EVSE_STATE_A:
if (!available) {
evse_set_state(EVSE_STATE_F);
@@ -125,15 +127,12 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
break;
}
}
// fallthrough intencional
__attribute__((fallthrough)); // Evita warning de fallthrough implícito
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
);
evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1) ? EVSE_STATE_D1 : EVSE_STATE_C1);
break;
}
@@ -156,8 +155,7 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
break;
case EVSE_STATE_E:
// Sem transições a partir de E
break;
break; // Sem transições a partir de E
case EVSE_STATE_F:
if (available) {
@@ -166,9 +164,9 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail
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());
evse_state_t next = evse_get_state();
if (next != prev) {
ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(prev), evse_state_to_str(next));
update_outputs(next);
}
}

View File

@@ -1,5 +1,5 @@
#include "evse_hardware.h"
#include "pilot.h"
#include "evse_pilot.h"
#include "ac_relay.h"
#include "socket_lock.h"
#include "proximity.h"
@@ -7,6 +7,7 @@
static const char *TAG = "evse_hardware";
void evse_hardware_init(void) {
pilot_init();
pilot_set_level(true); // Sinal piloto em 12V (inicial)
ac_relay_set_state(false); // Relé desligado
//socket_lock_set_locked(false); // Destrava o conector

View File

@@ -4,7 +4,6 @@
#include "evse_hardware.h"
#include "evse_config.h"
#include "evse_api.h"
#include "auth.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -13,12 +12,14 @@
#include "esp_log.h"
#include <string.h>
#include "auth_events.h"
#include "loadbalancer_events.h"
#include "esp_event.h"
static const char *TAG = "EVSE_Manager";
static TickType_t auth_expiration = 0;
static SemaphoreHandle_t evse_mutex;
static QueueHandle_t auth_event_queue = NULL;
static bool auth_enabled = false;
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
@@ -30,27 +31,55 @@ static void evse_manager_task(void *arg) {
}
}
static void evse_auth_event_task(void *arg) {
auth_event_t evt;
// ===== Tratador de eventos de autenticação =====
static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* data) {
if (base != AUTH_EVENTS || data == NULL) return;
while (true) {
if (xQueueReceive(auth_event_queue, &evt, portMAX_DELAY)) {
ESP_LOGI(TAG, "Evento de autenticação recebido: %s (%s)",
evt.tag, evt.authorized ? "AUTORIZADO" : "NEGADO");
switch (id) {
case AUTH_EVENT_TAG_PROCESSED: {
auth_tag_event_data_t *evt = (auth_tag_event_data_t*)data;
ESP_LOGI("EVSE", "Tag: %s | Autorizada: %s", evt->tag, evt->authorized ? "SIM" : "NÃO");
evse_state_set_authorized(evt->authorized);
break;
}
if (evt.authorized) {
evse_authorize();
auth_expiration = xTaskGetTickCount() + pdMS_TO_TICKS(2 * 60 * 1000); // 2 minutos
case AUTH_EVENT_ENABLED_CHANGED:
case AUTH_EVENT_INIT: {
auth_enabled_event_data_t *evt = (auth_enabled_event_data_t*)data;
auth_enabled = evt->enabled;
ESP_LOGI("EVSE", "Auth %s (%s)",
id == AUTH_EVENT_ENABLED_CHANGED ? "ficou" : "init",
evt->enabled ? "ATIVO" : "INATIVO");
if (!auth_enabled) {
evse_state_set_authorized(true);
ESP_LOGI("EVSE", "Autenticação desativada → autorização forçada.");
} else {
evse_manager_set_authorized(false);
ESP_LOGW(TAG, "Tag inválida, carregamento negado.");
evse_state_set_authorized(false);
ESP_LOGI("EVSE", "Autenticação ativada → aguardando autorização por tag.");
}
break;
}
}
}
// ===== Tratador de eventos de loadbalancer =====
static void on_loadbalancer_event(void* handler_arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED) {
const loadbalancer_state_event_t* evt = (const loadbalancer_state_event_t*) event_data;
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
evt->enabled ? "ENABLED" : "DISABLED", evt->timestamp_us);
// Ações adicionais podem ser adicionadas aqui conforme necessário
} else if (event_id == LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED) {
const loadbalancer_charging_limit_event_t* evt = (const loadbalancer_charging_limit_event_t*) event_data;
ESP_LOGD(TAG, "Novo limite de corrente: %.1f A (ts: %lld)", evt->limit, evt->timestamp_us);
evse_set_runtime_charging_current((uint16_t)(evt->limit));
}
}
// ===== Inicialização dos módulos do EVSE =====
// ===== Inicialização =====
void evse_manager_init(void) {
evse_mutex = xSemaphoreCreateMutex();
@@ -59,17 +88,14 @@ void evse_manager_init(void) {
evse_hardware_init();
evse_state_init();
ESP_LOGI(TAG, "EVSE Manager inicializado.");
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
ESP_LOGI(TAG, "EVSE Manager inicializado.");
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
}
// ===== Inicia processamento de eventos de autenticação =====
void evse_manager_start(QueueHandle_t queue) {
auth_event_queue = queue;
xTaskCreate(evse_auth_event_task, "evse_auth_evt", 4096, NULL, 5, NULL);
}
// ===== Main Tick =====
void evse_manager_tick(void) {
xSemaphoreTake(evse_mutex, portMAX_DELAY);
@@ -78,27 +104,25 @@ void evse_manager_tick(void) {
evse_state_tick();
evse_temperature_check();
// Verifica expiração de autorização somente se auth está habilitado
if (auth_is_enabled()) {
if (evse_state_get_authorized() && auth_expiration > 0 &&
xTaskGetTickCount() >= auth_expiration) {
ESP_LOGI(TAG, "Autorização expirada após 2 minutos.");
if (auth_enabled) {
// If the car is disconnected, revoke authorization
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A) {
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
evse_state_set_authorized(false);
auth_expiration = 0;
}
} else {
// Se autenticação não é necessária, sempre considera autorizado
// If authentication is disabled, ensure authorization is always granted
if (!evse_state_get_authorized()) {
evse_state_set_authorized(true);
ESP_LOGI(TAG, "Autenticação desativada: autorização forçada.");
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
}
}
xSemaphoreGive(evse_mutex);
}
// ===== Controles e status =====
// ===== API pública =====
bool evse_manager_is_available(void) {
return evse_config_is_available();
}

172
components/evse/evse_pilot.c Executable file
View File

@@ -0,0 +1,172 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "driver/ledc.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "evse_pilot.h"
#include "adc.h"
#include "board_config.h"
#define PILOT_PWM_TIMER LEDC_TIMER_0
#define PILOT_PWM_CHANNEL LEDC_CHANNEL_0
#define PILOT_PWM_SPEED_MODE LEDC_LOW_SPEED_MODE
#define PILOT_PWM_DUTY_RES LEDC_TIMER_10_BIT
#define PILOT_PWM_MAX_DUTY 1023
#define NUM_PILOT_SAMPLES 100
#define MAX_SAMPLE_ATTEMPTS 1000
#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior
static const char *TAG = "evse_pilot";
void pilot_init(void)
{
ledc_timer_config_t ledc_timer = {
.speed_mode = PILOT_PWM_SPEED_MODE,
.timer_num = PILOT_PWM_TIMER,
.duty_resolution = PILOT_PWM_DUTY_RES,
.freq_hz = 1000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.speed_mode = PILOT_PWM_SPEED_MODE,
.channel = PILOT_PWM_CHANNEL,
.timer_sel = PILOT_PWM_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = board_config.pilot_pwm_gpio,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0));
ESP_ERROR_CHECK(ledc_fade_func_install(0));
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.pilot_adc_channel, &config));
}
void pilot_set_level(bool level)
{
ESP_LOGI(TAG, "Set level %d", level);
ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0);
}
void pilot_set_amps(uint16_t amps)
{
ESP_LOGI(TAG, "Set amps %d", amps);
if (amps < 60 || amps > 800) {
ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps);
return;
}
uint32_t duty;
if (amps <= 510) {
duty = (PILOT_PWM_MAX_DUTY * amps) / 600;
} else {
duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100));
}
if (duty > PILOT_PWM_MAX_DUTY)
duty = PILOT_PWM_MAX_DUTY;
ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY);
ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty);
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
}
static int compare_int(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
static int select_low_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(int), compare_int);
return copy[k / 2];
}
static int select_high_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(int), compare_int);
return copy[n - k + (k / 2)];
}
void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
{
ESP_LOGD(TAG, "pilot_measure");
int samples[NUM_PILOT_SAMPLES];
int collected = 0, attempts = 0;
int sample;
while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) {
if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) {
samples[collected++] = sample;
esp_rom_delay_us(10);
} else {
esp_rom_delay_us(100);
attempts++;
}
}
if (collected < NUM_PILOT_SAMPLES) {
ESP_LOGW(TAG, "Timeout on sample read (%d/%d)", collected, NUM_PILOT_SAMPLES);
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = false;
return;
}
int high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
int low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
ESP_LOGD(TAG, "Final: high_raw=%d, low_raw=%d", high_raw, low_raw);
int high_mv = 0;
int low_mv = 0;
if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK ||
adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) {
ESP_LOGW(TAG, "ADC calibration failed");
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = false;
return;
}
if (high_mv >= board_config.pilot_down_threshold_12)
*up_voltage = PILOT_VOLTAGE_12;
else if (high_mv >= board_config.pilot_down_threshold_9)
*up_voltage = PILOT_VOLTAGE_9;
else if (high_mv >= board_config.pilot_down_threshold_6)
*up_voltage = PILOT_VOLTAGE_6;
else if (high_mv >= board_config.pilot_down_threshold_3)
*up_voltage = PILOT_VOLTAGE_3;
else
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = (low_mv <= board_config.pilot_down_threshold_n12);
ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12);
}

View File

@@ -1,6 +1,8 @@
#include "evse_state.h"
#include "evse_events.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_log.h"
static evse_state_t current_state = EVSE_STATE_A;
static bool is_authorized = false;
@@ -8,10 +10,45 @@ static bool is_authorized = false;
// Proteção básica para variáveis globais em sistemas concorrentes
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
static evse_state_event_t map_state_to_event(evse_state_t s) {
switch (s) {
case EVSE_STATE_A:
return EVSE_STATE_EVENT_IDLE;
case EVSE_STATE_B1:
return EVSE_STATE_EVENT_WAITING;
case EVSE_STATE_B2:
case EVSE_STATE_C1:
case EVSE_STATE_C2:
return EVSE_STATE_EVENT_CHARGING;
case EVSE_STATE_E:
case EVSE_STATE_F:
return EVSE_STATE_EVENT_FAULT;
default:
return EVSE_STATE_EVENT_IDLE;
}
}
void evse_set_state(evse_state_t state) {
bool changed = false;
evse_state_t previous_state;
portENTER_CRITICAL(&state_mux);
current_state = state;
previous_state = current_state;
if (state != current_state) {
current_state = state;
changed = true;
}
portEXIT_CRITICAL(&state_mux);
if (changed) {
ESP_LOGI("EVSE_STATE", "Estado alterado de %s para %s",
evse_state_to_str(previous_state),
evse_state_to_str(state));
evse_state_event_data_t evt = {
.state = map_state_to_event(state)
};
esp_event_post(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt), portMAX_DELAY);
}
}
evse_state_t evse_get_state(void) {
@@ -39,8 +76,15 @@ const char* evse_state_to_str(evse_state_t state) {
void evse_state_init(void) {
portENTER_CRITICAL(&state_mux);
current_state = EVSE_STATE_A;
is_authorized = false;
is_authorized = true;
portEXIT_CRITICAL(&state_mux);
ESP_LOGI("EVSE_STATE", "Inicializado em estado: %s", evse_state_to_str(current_state));
evse_state_event_data_t evt = {
.state = map_state_to_event(current_state)
};
esp_event_post(EVSE_EVENTS, EVSE_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
}
void evse_state_tick(void) {

View File

@@ -23,9 +23,6 @@ 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);
@@ -35,10 +32,6 @@ 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);

View File

@@ -17,10 +17,6 @@ extern "C" {
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
#define MAX_CHARGING_CURRENT_LIMIT 32 // 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
@@ -43,14 +39,14 @@ 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);
void evse_set_runtime_charging_current(uint16_t value);
uint16_t evse_get_runtime_charging_current(void);
// RCM
bool evse_is_rcm(void);
esp_err_t evse_set_rcm(bool rcm);

View File

@@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "pilot.h"
#include "evse_pilot.h"
#define EVSE_ERR_AUTO_CLEAR_BITS ( \

View File

@@ -1,33 +1,27 @@
#ifndef EVSE_EVENTS_H
#define EVSE_EVENTS_H
#include "evse_api.h"
#include "esp_event_base.h"
#pragma once
#include "esp_event.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);
ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
typedef enum {
EVSE_EVENT_INIT,
EVSE_EVENT_STATE_CHANGED,
EVSE_EVENT_ERROR,
EVSE_EVENT_ERROR_CLEARED,
EVSE_EVENT_LIMIT_REACHED,
EVSE_EVENT_AUTH_GRANTED
// Outros eventos possíveis futuramente
} 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;
typedef enum {
EVSE_STATE_EVENT_IDLE,
EVSE_STATE_EVENT_WAITING,
EVSE_STATE_EVENT_CHARGING,
EVSE_STATE_EVENT_FAULT
} evse_state_event_t;
typedef struct {
evse_state_event_t state;
} evse_state_event_data_t;
#ifdef __cplusplus
}
#endif
#endif // EVSE_EVENTS_H

View File

@@ -4,7 +4,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "evse_api.h"
#include "pilot.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus

View File

@@ -1,27 +1,23 @@
#ifndef EVSE_MANAGER_H
#define EVSE_MANAGER_H
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
/**
* @brief Inicializa os módulos internos do EVSE (hardware, estado, erros, etc.)
* e inicia a tarefa de supervisão periódica (tick).
* e inicia a tarefa de supervisão periódica (tick).
*/
void evse_manager_init(void);
/**
* @brief Inicia a tarefa que processa eventos de autenticação recebidos via fila.
*
* @param queue Fila de eventos do tipo auth_event_t enviada pelo módulo auth.
*/
void evse_manager_start(QueueHandle_t queue);
/**
* @brief Executa uma iteração do ciclo de controle do EVSE.
*
@@ -66,4 +62,5 @@ bool evse_manager_is_enabled(void);
}
#endif
#endif // EVSE_MANAGER_H

View File

@@ -0,0 +1,69 @@
#ifndef PILOT_H_
#define PILOT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
/**
* @brief Níveis categóricos de tensão no sinal CP (Control Pilot)
*/
typedef enum
{
PILOT_VOLTAGE_12, ///< Estado A: +12V
PILOT_VOLTAGE_9, ///< Estado B: +9V
PILOT_VOLTAGE_6, ///< Estado C: +6V
PILOT_VOLTAGE_3, ///< Estado D: +3V
PILOT_VOLTAGE_1 ///< Estado E/F: abaixo de 3V
} pilot_voltage_t;
/**
* @brief Inicializa o driver do sinal Pilot
*/
void pilot_init(void);
/**
* @brief Define o nível do Pilot: +12V ou -12V
*
* @param level true = +12V, false = -12V
*/
void pilot_set_level(bool level);
/**
* @brief Ativa o PWM do Pilot com corrente limitada
*
* @param amps Corrente em décimos de ampère (ex: 160 = 16A)
*/
void pilot_set_amps(uint16_t amps);
/**
* @brief Mede o nível de tensão do Pilot e detecta -12V
*
* @param up_voltage Valor categórico da tensão positiva
* @param down_voltage_n12 true se o nível negativo atingir -12V
*/
void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12);
/**
* @brief Retorna o estado lógico atual do Pilot (nível alto = +12V)
*
* @return true se nível atual for +12V, false se for -12V
*/
bool pilot_get_state(void);
/**
* @brief Cache interno opcional dos níveis de tensão reais do Pilot
*/
typedef struct {
uint16_t high_mv; ///< Pico positivo medido (mV)
uint16_t low_mv; ///< Pico negativo medido (mV)
} pilot_voltage_cache_t;
#ifdef __cplusplus
}
#endif
#endif /* PILOT_H_ */

View File

@@ -1,6 +1,9 @@
#ifndef EVSE_STATE_H
#define EVSE_STATE_H
#include "evse_events.h"
#include <stdbool.h>
// Estado do EVSE (pilot signal)
@@ -41,4 +44,6 @@ 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);
//evse_state_event_t map_state_to_event(evse_state_t state);
#endif // EVSE_STATE_H