Files
chargeflow/projeto_parte1.c
2025-06-14 11:46:10 +01:00

1026 lines
29 KiB
C
Executable File

// === Início de: main/main.c ===
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_spiffs.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "wifi.h"
#include "board_config.h"
#include "logger.h"
#include "rest_main.h"
#include "peripherals.h"
#include "protocols.h"
#include "evse_manager.h"
#include "evse_api.h"
#include "auth.h"
#include "loadbalancer.h"
#include "meter_manager.h"
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
#define AP_CONNECTION_TIMEOUT 120000
#define RESET_HOLD_TIME 10000
#define DEBOUNCE_TIME_MS 50
#define PRESS_BIT BIT0
#define RELEASED_BIT BIT1
static const char *TAG = "app_main";
static TaskHandle_t user_input_task;
static TickType_t press_tick = 0;
static TickType_t last_interrupt_tick = 0;
static bool pressed = false;
//
// File system (SPIFFS) init and info
//
static void fs_info(esp_vfs_spiffs_conf_t *conf) {
size_t total = 0, used = 0;
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
if (ret == ESP_OK)
ESP_LOGI(TAG, "Partition %s: total: %d, used: %d", conf->partition_label, total, used);
else
ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
}
static void fs_init(void) {
esp_vfs_spiffs_conf_t cfg_conf = {
.base_path = "/cfg",
.partition_label = "cfg",
.max_files = 1,
.format_if_mount_failed = false
};
esp_vfs_spiffs_conf_t data_conf = {
.base_path = "/data",
.partition_label = "data",
.max_files = 5,
.format_if_mount_failed = true
};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
fs_info(&cfg_conf);
fs_info(&data_conf);
}
//
// Wi-Fi event monitoring task
//
static void wifi_event_task_func(void *param) {
EventBits_t mode_bits;
while (1) {
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT) {
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT) {
xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
} else {
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
//wifi_ap_stop();
}
}
} else if (mode_bits & WIFI_STA_MODE_BIT) {
xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
}
}
}
//
// Botão e tratamento
//
static void handle_button_press(void) {
ESP_LOGI(TAG, "Ativando modo AP");
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
wifi_ap_start();
}
}
static void user_input_task_func(void *param) {
uint32_t notification;
while (1) {
if (xTaskNotifyWait(0x00, 0xFF, &notification, portMAX_DELAY)) {
if (notification & PRESS_BIT) {
press_tick = xTaskGetTickCount();
pressed = true;
ESP_LOGI(TAG, "Botão pressionado");
}
if (notification & RELEASED_BIT && pressed) {
pressed = false;
ESP_LOGI(TAG, "Botão liberado");
handle_button_press();
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg) {
BaseType_t higher_task_woken = pdFALSE;
TickType_t now = xTaskGetTickCountFromISR();
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) return;
last_interrupt_tick = now;
if (!gpio_get_level(board_config.button_wifi_gpio)) {
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
} else {
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken) {
portYIELD_FROM_ISR();
}
}
static void button_init(void) {
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE
};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
//
// Inicialização dos módulos do sistema
//
static void init_modules(void) {
peripherals_init();
//api_init();
ESP_ERROR_CHECK(rest_server_init("/data"));
protocols_init();
evse_manager_init();
evse_init(); // Cria a task para FSM
button_init();
auth_init();
loadbalancer_init();
meter_manager_grid_init();
meter_manager_grid_start();
//meter_manager_evse_init();
// Outros módulos (descomente conforme necessário)
// meter_init();
// ocpp_start();
// orno_modbus_start();
// currentshaper_start();
// initWiegand();
// meter_zigbee_start();
// master_sync_start();
// slave_sync_start();
}
//
// Função principal do firmware
//
void app_main(void) {
logger_init();
esp_log_set_vprintf(logger_vprintf);
esp_reset_reason_t reason = esp_reset_reason();
ESP_LOGI(TAG, "Reset reason: %d", reason);
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_LOGW(TAG, "Erasing NVS flash");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
fs_init();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(gpio_install_isr_service(0));
board_config_load();
wifi_ini();
//wifi_ap_start();
init_modules();
xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task);
}
// === Fim de: main/main.c ===
// === Início de: components/evse/evse_pilot.c ===
#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);
}
// === Fim de: components/evse/evse_pilot.c ===
// === Início de: components/evse/evse_hardware.c ===
#include "evse_hardware.h"
#include "evse_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_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
}
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) {
return false;
}
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_is_locked_state();
}
// === Fim de: components/evse/evse_hardware.c ===
// === Início de: components/evse/evse_state.c ===
#include "evse_api.h"
#include "evse_state.h"
#include "evse_events.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_log.h"
// =========================
// Internal State Variables
// =========================
static evse_state_t current_state = EVSE_STATE_A;
static bool is_authorized = false;
static TickType_t session_start_tick = 0;
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
// =========================
// Internal Mapping
// =========================
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;
}
}
// =========================
// Public API
// =========================
void evse_set_state(evse_state_t state) {
bool changed = false;
evse_state_t previous_state;
portENTER_CRITICAL(&state_mux);
previous_state = current_state;
if (state != current_state) {
current_state = state;
changed = true;
if (evse_state_is_charging(state) && !evse_state_is_charging(previous_state)) {
session_start_tick = xTaskGetTickCount();
}
}
portEXIT_CRITICAL(&state_mux);
if (changed) {
ESP_LOGI("EVSE_STATE", "State changed from %s to %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) {
portENTER_CRITICAL(&state_mux);
evse_state_t s = current_state;
portEXIT_CRITICAL(&state_mux);
return s;
}
TickType_t evse_get_session_start(void) {
portENTER_CRITICAL(&state_mux);
TickType_t t = session_start_tick;
portEXIT_CRITICAL(&state_mux);
return t;
}
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) {
portENTER_CRITICAL(&state_mux);
current_state = EVSE_STATE_A;
session_start_tick = xTaskGetTickCount();
is_authorized = true;
portEXIT_CRITICAL(&state_mux);
ESP_LOGI("EVSE_STATE", "Initialized in state: %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) {
// Placeholder for future state logic
}
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;
}
void evse_state_set_authorized(bool authorized) {
portENTER_CRITICAL(&state_mux);
is_authorized = authorized;
portEXIT_CRITICAL(&state_mux);
}
bool evse_state_get_authorized(void) {
portENTER_CRITICAL(&state_mux);
bool result = is_authorized;
portEXIT_CRITICAL(&state_mux);
return result;
}
// === Fim de: components/evse/evse_state.c ===
// === Início de: components/evse/evse_fsm.c ===
// evse_fsm.c - Máquina de Estados EVSE com controle centralizado
#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"
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) {
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:
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);
break;
}
}
void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) {
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)); // Evita warning de fallthrough implícito
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:
break; // Sem transições a partir de E
case EVSE_STATE_F:
if (available) {
evse_set_state(EVSE_STATE_A);
}
break;
}
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);
}
}
// === Fim de: components/evse/evse_fsm.c ===
// === Início de: components/evse/evse_error.c ===
#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)");
ESP_LOGW(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", pilot_voltage, is_n12v ? "true" : "false");
}
}
}
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;
}
// === Fim de: components/evse/evse_error.c ===
// === Início de: components/evse/evse_core.c ===
// 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 "evse_pilot.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 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");
if (evse_get_error() == 0 && !evse_is_error_cleared()) {
evse_error_check(pilot_voltage, is_n12v);
evse_fsm_process(
pilot_voltage,
evse_state_get_authorized(),
evse_config_is_available(),
evse_config_is_enabled()
);
evse_limits_check();
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;
}
evse_mark_error_cleared();
}
xSemaphoreGive(mutex);
}
// ================================
// 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);
}
// ================================
// Tarefa principal
// ================================
static void evse_core_task(void *arg) {
while (true) {
evse_process();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
uint32_t evse_get_total_energy(void) {
return 0; // Stub de 1 kWh
}
uint32_t evse_get_instant_power(void) {
return 0; // Stub de 2 kW
}
// === Fim de: components/evse/evse_core.c ===