1345 lines
35 KiB
C
1345 lines
35 KiB
C
.
|
||
|
||
// === Início de: main/main.c ===
|
||
// === 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 "network.h"
|
||
#include "board_config.h"
|
||
#include "rest_main.h"
|
||
|
||
#include "peripherals.h"
|
||
#include "protocols.h"
|
||
#include "evse_manager.h"
|
||
#include "evse_core.h"
|
||
#include "auth.h"
|
||
#include "loadbalancer.h"
|
||
#include "meter_manager.h"
|
||
#include "buzzer.h"
|
||
#include "evse_link.h"
|
||
#include "ocpp.h"
|
||
#include "led.h"
|
||
#include "scheduler.h"
|
||
#include "storage_service.h"
|
||
|
||
#define AP_CONNECTION_TIMEOUT 120000
|
||
#define RESET_HOLD_TIME 30000 // ms
|
||
#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 = NULL;
|
||
static TickType_t press_tick = 0;
|
||
static volatile TickType_t last_interrupt_tick = 0;
|
||
static bool pressed = false;
|
||
|
||
// Spinlock para garantir debounce seguro na ISR
|
||
static portMUX_TYPE button_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||
|
||
//
|
||
// 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, (int)total, (int)used);
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGE(TAG, "Failed to get SPIFFS info (%s): %s",
|
||
conf->partition_label, 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)
|
||
{
|
||
(void)param;
|
||
EventBits_t mode_bits;
|
||
for (;;)
|
||
{
|
||
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)
|
||
{
|
||
// Espera sair do AP
|
||
xEventGroupWaitBits(
|
||
wifi_event_group,
|
||
WIFI_AP_DISCONNECTED_BIT,
|
||
pdFALSE,
|
||
pdFALSE,
|
||
portMAX_DELAY);
|
||
}
|
||
else
|
||
{
|
||
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
|
||
{
|
||
// Timeout sem cliente ligado
|
||
// wifi_ap_stop();
|
||
ESP_LOGW(TAG, "AP timeout sem conexões");
|
||
}
|
||
}
|
||
}
|
||
else if (mode_bits & WIFI_STA_MODE_BIT)
|
||
{
|
||
// Apenas aguarda desconexão STA
|
||
xEventGroupWaitBits(
|
||
wifi_event_group,
|
||
WIFI_STA_DISCONNECTED_BIT,
|
||
pdFALSE,
|
||
pdFALSE,
|
||
portMAX_DELAY);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Button press handler (short press => AP)
|
||
//
|
||
static void handle_button_press(void)
|
||
{
|
||
// Pode ser chamado cedo demais se a ordem de init mudar
|
||
if (wifi_event_group == NULL)
|
||
{
|
||
ESP_LOGW(TAG, "Wi-Fi ainda não inicializado, ignorando botão Wi-Fi");
|
||
return;
|
||
}
|
||
|
||
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT))
|
||
{
|
||
ESP_LOGI(TAG, "Starting Wi-Fi AP mode (short press)");
|
||
wifi_ap_start();
|
||
}
|
||
}
|
||
|
||
// Task para lidar com notificações de botão (PRESS / RELEASE)
|
||
static void user_input_task_func(void *param)
|
||
{
|
||
(void)param;
|
||
uint32_t notification;
|
||
|
||
for (;;)
|
||
{
|
||
if (xTaskNotifyWait(
|
||
0,
|
||
UINT32_MAX,
|
||
¬ification,
|
||
portMAX_DELAY))
|
||
{
|
||
if (notification & PRESS_BIT)
|
||
{
|
||
press_tick = xTaskGetTickCount();
|
||
pressed = true;
|
||
ESP_LOGI(TAG, "Button Pressed");
|
||
// Decisão (short/long) é feita na soltura
|
||
}
|
||
|
||
if ((notification & RELEASED_BIT) && pressed)
|
||
{
|
||
pressed = false;
|
||
TickType_t held_ticks = xTaskGetTickCount() - press_tick;
|
||
uint32_t held_ms = pdTICKS_TO_MS(held_ticks);
|
||
|
||
ESP_LOGI(TAG, "Button Released (held %u ms)", (unsigned)held_ms);
|
||
|
||
if (held_ms >= RESET_HOLD_TIME)
|
||
{
|
||
ESP_LOGW(TAG, "Long press: erasing NVS + reboot");
|
||
nvs_flash_erase();
|
||
esp_restart();
|
||
}
|
||
else
|
||
{
|
||
// Short press: apenas habilita modo AP
|
||
handle_button_press();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ISR para GPIO do botão (ativo em nível baixo)
|
||
static void IRAM_ATTR button_isr_handler(void *arg)
|
||
{
|
||
(void)arg;
|
||
BaseType_t higher_task_woken = pdFALSE;
|
||
TickType_t now = xTaskGetTickCountFromISR();
|
||
|
||
portENTER_CRITICAL_ISR(&button_spinlock);
|
||
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS))
|
||
{
|
||
portEXIT_CRITICAL_ISR(&button_spinlock);
|
||
return;
|
||
}
|
||
last_interrupt_tick = now;
|
||
|
||
if (user_input_task == NULL)
|
||
{
|
||
portEXIT_CRITICAL_ISR(&button_spinlock);
|
||
return;
|
||
}
|
||
|
||
int level = gpio_get_level(board_config.button_wifi_gpio);
|
||
uint32_t bits = (level == 0) ? PRESS_BIT : RELEASED_BIT;
|
||
|
||
xTaskNotifyFromISR(
|
||
user_input_task,
|
||
bits,
|
||
eSetBits,
|
||
&higher_task_woken);
|
||
|
||
portEXIT_CRITICAL_ISR(&button_spinlock);
|
||
|
||
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 (SEM botão)
|
||
//
|
||
static void init_modules(void)
|
||
{
|
||
|
||
ESP_ERROR_CHECK(storage_service_init());
|
||
|
||
peripherals_init();
|
||
wifi_ini(); // garante wifi_event_group inicializado
|
||
buzzer_init();
|
||
led_init();
|
||
ESP_ERROR_CHECK(rest_server_init("/data"));
|
||
|
||
evse_manager_init();
|
||
evse_init();
|
||
auth_init();
|
||
meter_manager_init();
|
||
meter_manager_start();
|
||
ocpp_start();
|
||
scheduler_init();
|
||
protocols_init();
|
||
loadbalancer_init();
|
||
evse_link_init();
|
||
}
|
||
|
||
//
|
||
// Função principal do firmware
|
||
//
|
||
void app_main(void)
|
||
{
|
||
|
||
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();
|
||
|
||
// 1) Inicia todos os módulos (inclui Wi-Fi, EVSE, etc.)
|
||
init_modules();
|
||
|
||
// 2) Cria a task que recebe notificações do botão
|
||
BaseType_t rc;
|
||
rc = xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task);
|
||
configASSERT(rc == pdPASS);
|
||
|
||
// 3) Agora é seguro registrar ISR do botão
|
||
button_init();
|
||
|
||
// 4) Task auxiliar para eventos Wi-Fi
|
||
rc = xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
|
||
configASSERT(rc == pdPASS);
|
||
}
|
||
|
||
// === Fim de: main/main.c ===
|
||
|
||
// === Fim de: main/main.c ===
|
||
|
||
|
||
// === Início de: components/ocpp/src/ocpp_events.c ===
|
||
#include "ocpp_events.h"
|
||
|
||
/* Define a base, como em components/auth/src/auth_events.c */
|
||
ESP_EVENT_DEFINE_BASE(OCPP_EVENTS);
|
||
|
||
// === Fim de: components/ocpp/src/ocpp_events.c ===
|
||
|
||
|
||
// === Início de: components/ocpp/src/ocpp.c ===
|
||
// components/ocpp/src/ocpp.c
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <ctype.h>
|
||
#include <inttypes.h>
|
||
#include <math.h>
|
||
|
||
#include "esp_log.h"
|
||
#include "esp_err.h"
|
||
#include "esp_timer.h"
|
||
#include "esp_event.h"
|
||
#include "esp_wifi.h"
|
||
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
|
||
#include "ocpp.h"
|
||
#include "ocpp_events.h"
|
||
|
||
#include "evse_error.h"
|
||
#include "auth_events.h"
|
||
#include "evse_events.h"
|
||
#include "evse_state.h"
|
||
#include "meter_events.h"
|
||
|
||
/* MicroOcpp includes */
|
||
#include <mongoose.h>
|
||
#include <MicroOcpp_c.h> // C-facade of MicroOcpp
|
||
#include <MicroOcppMongooseClient_c.h> // WebSocket integration for ESP-IDF
|
||
|
||
// NEW storage layer
|
||
#include "storage_service.h"
|
||
|
||
#define NVS_NAMESPACE "ocpp"
|
||
#define NVS_OCPP_ENABLED "enabled"
|
||
#define NVS_OCPP_SERVER "ocpp_server"
|
||
#define NVS_OCPP_CHARGE_ID "charge_id"
|
||
|
||
static const char *TAG = "ocpp";
|
||
|
||
static bool enabled = false;
|
||
|
||
static TaskHandle_t ocpp_task = NULL;
|
||
|
||
static struct mg_mgr mgr; // Event manager
|
||
static OCPP_Connection *g_ocpp_conn = NULL; // Para shutdown limpo
|
||
|
||
static esp_event_handler_instance_t s_auth_verify_inst = NULL;
|
||
|
||
// Flags refletindo o estado do EVSE (atualizadas por eventos)
|
||
static volatile bool s_ev_plugged = false;
|
||
static volatile bool s_ev_ready = false;
|
||
|
||
static esp_event_handler_instance_t s_evse_state_inst = NULL;
|
||
|
||
// Flags de config (vindas de EVSE_EVENTS)
|
||
static volatile bool s_evse_enabled = true;
|
||
static volatile bool s_evse_available = true;
|
||
|
||
static esp_event_handler_instance_t s_evse_enable_inst = NULL;
|
||
static esp_event_handler_instance_t s_evse_available_inst = NULL;
|
||
|
||
// --- cache de medições vindas de METER_EVENT_DATA_READY ---
|
||
typedef struct
|
||
{
|
||
float vrms[3];
|
||
float irms[3];
|
||
int32_t watt[3]; // ativo por fase (W)
|
||
float frequency;
|
||
float power_factor;
|
||
|
||
float total_energy_Wh;
|
||
|
||
int32_t sum_watt;
|
||
float avg_voltage;
|
||
float sum_current;
|
||
|
||
bool have_data;
|
||
} ocpp_meter_cache_t;
|
||
|
||
static ocpp_meter_cache_t s_meter = {0};
|
||
static portMUX_TYPE s_meter_mux = portMUX_INITIALIZER_UNLOCKED;
|
||
static esp_event_handler_instance_t s_meter_inst = NULL;
|
||
|
||
// valor de oferta (A por conector)
|
||
static float s_current_offered_A = 16.0f;
|
||
|
||
static float getCurrentOffered(void)
|
||
{
|
||
return s_current_offered_A;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Storage helpers (robustos)
|
||
// -----------------------------------------------------------------------------
|
||
#define STORE_TO pdMS_TO_TICKS(800)
|
||
#define STORE_FLUSH_TO pdMS_TO_TICKS(2000)
|
||
|
||
static void storage_init_best_effort(void)
|
||
{
|
||
esp_err_t e = storage_service_init();
|
||
if (e != ESP_OK)
|
||
ESP_LOGW(TAG, "storage_service_init failed: %s", esp_err_to_name(e));
|
||
}
|
||
|
||
static esp_err_t store_flush_best_effort(void)
|
||
{
|
||
esp_err_t e = storage_flush_sync(STORE_FLUSH_TO);
|
||
if (e != ESP_OK)
|
||
ESP_LOGW(TAG, "storage_flush_sync failed: %s", esp_err_to_name(e));
|
||
return e;
|
||
}
|
||
|
||
static esp_err_t store_set_u8_best_effort(const char *ns, const char *key, uint8_t v)
|
||
{
|
||
for (int attempt = 0; attempt < 3; ++attempt)
|
||
{
|
||
esp_err_t e = storage_set_u8_async(ns, key, v);
|
||
if (e == ESP_OK)
|
||
return ESP_OK;
|
||
|
||
if (e == ESP_ERR_TIMEOUT)
|
||
{
|
||
(void)store_flush_best_effort();
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
continue;
|
||
}
|
||
return e;
|
||
}
|
||
return ESP_ERR_TIMEOUT;
|
||
}
|
||
|
||
static esp_err_t store_set_str_best_effort(const char *ns, const char *key, const char *s)
|
||
{
|
||
for (int attempt = 0; attempt < 3; ++attempt)
|
||
{
|
||
esp_err_t e = storage_set_str_async(ns, key, s ? s : "");
|
||
if (e == ESP_OK)
|
||
return ESP_OK;
|
||
|
||
if (e == ESP_ERR_TIMEOUT)
|
||
{
|
||
(void)store_flush_best_effort();
|
||
vTaskDelay(portMAX_DELAY);
|
||
continue;
|
||
}
|
||
return e;
|
||
}
|
||
return ESP_ERR_TIMEOUT;
|
||
}
|
||
|
||
// Lê string de forma segura (buffer grande -> truncagem segura para out)
|
||
static esp_err_t store_get_str_safe(const char *ns, const char *key, char *out, size_t out_sz)
|
||
{
|
||
if (!out || out_sz == 0)
|
||
return ESP_ERR_INVALID_ARG;
|
||
|
||
out[0] = '\0';
|
||
|
||
char tmp[STORAGE_MAX_VALUE_BYTES + 1];
|
||
memset(tmp, 0, sizeof(tmp));
|
||
|
||
esp_err_t e = storage_get_str_sync(ns, key, tmp, sizeof(tmp), STORE_TO);
|
||
if (e == ESP_ERR_NOT_FOUND)
|
||
return ESP_OK;
|
||
if (e != ESP_OK)
|
||
return e;
|
||
|
||
size_t n = strnlen(tmp, out_sz - 1);
|
||
memcpy(out, tmp, n);
|
||
out[n] = '\0';
|
||
|
||
return ESP_OK;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Task / Main Loop
|
||
// -----------------------------------------------------------------------------
|
||
static void ocpp_task_func(void *param)
|
||
{
|
||
(void)param;
|
||
|
||
while (true)
|
||
{
|
||
if (enabled)
|
||
{
|
||
mg_mgr_poll(&mgr, 100);
|
||
ocpp_loop();
|
||
|
||
bool operative = ocpp_isOperative();
|
||
if (operative != s_evse_enabled)
|
||
{
|
||
s_evse_enabled = operative;
|
||
|
||
ocpp_operative_event_t ev = {
|
||
.operative = operative,
|
||
.timestamp_us = esp_timer_get_time()};
|
||
esp_event_post(OCPP_EVENTS,
|
||
OCPP_EVENT_OPERATIVE_UPDATED,
|
||
&ev, sizeof(ev),
|
||
portMAX_DELAY);
|
||
|
||
ESP_LOGI(TAG, "[OCPP] ChangeAvailability remoto → operative=%d", (int)operative);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
}
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Storage GETs
|
||
// -----------------------------------------------------------------------------
|
||
bool ocpp_get_enabled(void)
|
||
{
|
||
storage_init_best_effort();
|
||
|
||
uint8_t value = 0;
|
||
esp_err_t err = storage_get_u8_sync(NVS_NAMESPACE, NVS_OCPP_ENABLED, &value, STORE_TO);
|
||
|
||
if (err == ESP_ERR_NOT_FOUND)
|
||
return false;
|
||
if (err != ESP_OK)
|
||
{
|
||
ESP_LOGW(TAG, "storage_get_u8_sync(enabled) failed: %s", esp_err_to_name(err));
|
||
return false;
|
||
}
|
||
return value != 0;
|
||
}
|
||
|
||
void ocpp_get_server(char *value /* out, size>=64 */)
|
||
{
|
||
if (!value)
|
||
return;
|
||
value[0] = '\0';
|
||
|
||
storage_init_best_effort();
|
||
|
||
esp_err_t e = store_get_str_safe(NVS_NAMESPACE, NVS_OCPP_SERVER, value, 64);
|
||
if (e != ESP_OK)
|
||
{
|
||
ESP_LOGW(TAG, "store_get_str_safe(server) failed: %s", esp_err_to_name(e));
|
||
value[0] = '\0';
|
||
}
|
||
}
|
||
|
||
void ocpp_get_charge_id(char *value /* out, size>=64 */)
|
||
{
|
||
if (!value)
|
||
return;
|
||
value[0] = '\0';
|
||
|
||
storage_init_best_effort();
|
||
|
||
esp_err_t e = store_get_str_safe(NVS_NAMESPACE, NVS_OCPP_CHARGE_ID, value, 64);
|
||
if (e != ESP_OK)
|
||
{
|
||
ESP_LOGW(TAG, "store_get_str_safe(charge_id) failed: %s", esp_err_to_name(e));
|
||
value[0] = '\0';
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Storage SETs
|
||
// -----------------------------------------------------------------------------
|
||
void ocpp_set_enabled(bool value)
|
||
{
|
||
storage_init_best_effort();
|
||
|
||
ESP_LOGI(TAG, "set enabled %d", value);
|
||
|
||
esp_err_t e = store_set_u8_best_effort(NVS_NAMESPACE, NVS_OCPP_ENABLED, value ? 1 : 0);
|
||
if (e != ESP_OK)
|
||
{
|
||
ESP_LOGE(TAG, "store_set_u8_best_effort(enabled) failed: %s", esp_err_to_name(e));
|
||
return;
|
||
}
|
||
|
||
(void)store_flush_best_effort();
|
||
enabled = value;
|
||
}
|
||
|
||
void ocpp_set_server(char *value)
|
||
{
|
||
storage_init_best_effort();
|
||
|
||
ESP_LOGI(TAG, "set server %s", value ? value : "(null)");
|
||
|
||
esp_err_t e = store_set_str_best_effort(NVS_NAMESPACE, NVS_OCPP_SERVER, value ? value : "");
|
||
if (e != ESP_OK)
|
||
{
|
||
ESP_LOGE(TAG, "store_set_str_best_effort(server) failed: %s", esp_err_to_name(e));
|
||
return;
|
||
}
|
||
|
||
(void)store_flush_best_effort();
|
||
}
|
||
|
||
void ocpp_set_charge_id(char *value)
|
||
{
|
||
storage_init_best_effort();
|
||
|
||
ESP_LOGI(TAG, "set charge_id %s", value ? value : "(null)");
|
||
|
||
esp_err_t e = store_set_str_best_effort(NVS_NAMESPACE, NVS_OCPP_CHARGE_ID, value ? value : "");
|
||
if (e != ESP_OK)
|
||
{
|
||
ESP_LOGE(TAG, "store_set_str_best_effort(charge_id) failed: %s", esp_err_to_name(e));
|
||
return;
|
||
}
|
||
|
||
(void)store_flush_best_effort();
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Event handlers (AUTH / EVSE / METER)
|
||
// -----------------------------------------------------------------------------
|
||
static void ocpp_on_auth_verify(void *arg, esp_event_base_t base, int32_t id, void *event_data)
|
||
{
|
||
(void)arg;
|
||
(void)base;
|
||
(void)id;
|
||
|
||
const auth_tag_verify_event_t *rq = (const auth_tag_verify_event_t *)event_data;
|
||
if (!rq)
|
||
return;
|
||
|
||
char idtag[AUTH_TAG_MAX_LEN];
|
||
if (rq->tag[0] == '\0')
|
||
{
|
||
strncpy(idtag, "IDTAG", sizeof(idtag));
|
||
idtag[sizeof(idtag) - 1] = '\0';
|
||
}
|
||
else
|
||
{
|
||
strncpy(idtag, rq->tag, sizeof(idtag) - 1);
|
||
idtag[sizeof(idtag) - 1] = '\0';
|
||
}
|
||
|
||
ESP_LOGI(TAG, "AUTH_EVENT_TAG_VERIFY: tag=%s req_id=%u", idtag, (unsigned)rq->req_id);
|
||
|
||
if (!enabled || g_ocpp_conn == NULL)
|
||
{
|
||
ESP_LOGW(TAG, "OCPP not ready (enabled=%d, conn=%p) – ignoring verify",
|
||
enabled, (void *)g_ocpp_conn);
|
||
return;
|
||
}
|
||
|
||
if (ocpp_isTransactionActive())
|
||
{
|
||
ESP_LOGI(TAG, "Transaction active -> ocpp_end_transaction(\"%s\")", idtag);
|
||
ocpp_endTransaction(idtag, "Local");
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGI(TAG, "No active transaction -> ocpp_begin_transaction(\"%s\")", idtag);
|
||
ocpp_beginTransaction(idtag);
|
||
}
|
||
}
|
||
|
||
static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||
{
|
||
(void)arg;
|
||
if (base != EVSE_EVENTS || id != EVSE_EVENT_STATE_CHANGED || data == NULL)
|
||
return;
|
||
|
||
const evse_state_event_data_t *evt = (const evse_state_event_data_t *)data;
|
||
ESP_LOGI(TAG, "EVSE event received: state = %d", (int)evt->state);
|
||
|
||
switch (evt->state)
|
||
{
|
||
case EVSE_STATE_EVENT_IDLE:
|
||
s_ev_plugged = false;
|
||
s_ev_ready = false;
|
||
break;
|
||
|
||
case EVSE_STATE_EVENT_WAITING:
|
||
s_ev_plugged = true;
|
||
s_ev_ready = false;
|
||
break;
|
||
|
||
case EVSE_STATE_EVENT_CHARGING:
|
||
s_ev_plugged = true;
|
||
s_ev_ready = true;
|
||
break;
|
||
|
||
case EVSE_STATE_EVENT_FAULT:
|
||
default:
|
||
s_ev_ready = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void evse_enable_available_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||
{
|
||
(void)arg;
|
||
if (base != EVSE_EVENTS || data == NULL)
|
||
return;
|
||
|
||
if (id == EVSE_EVENT_ENABLE_UPDATED)
|
||
{
|
||
const evse_enable_event_data_t *e = (const evse_enable_event_data_t *)data;
|
||
s_evse_enabled = e->enabled;
|
||
ESP_LOGI(TAG, "[EVSE] ENABLE_UPDATED: enabled=%d (ts=%lld)",
|
||
(int)e->enabled, (long long)e->timestamp_us);
|
||
return;
|
||
}
|
||
|
||
if (id == EVSE_EVENT_AVAILABLE_UPDATED)
|
||
{
|
||
const evse_available_event_data_t *e = (const evse_available_event_data_t *)data;
|
||
s_evse_available = e->available;
|
||
ESP_LOGI(TAG, "[EVSE] AVAILABLE_UPDATED: available=%d (ts=%lld)",
|
||
(int)e->available, (long long)e->timestamp_us);
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void on_meter_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||
{
|
||
(void)arg;
|
||
|
||
if (base != METER_EVENT || id != METER_EVENT_DATA_READY || !data)
|
||
return;
|
||
|
||
const meter_event_data_t *evt = (const meter_event_data_t *)data;
|
||
|
||
if (!evt->source || strcmp(evt->source, "EVSE") != 0)
|
||
return;
|
||
|
||
int32_t sum_w = (int32_t)evt->watt[0] + (int32_t)evt->watt[1] + (int32_t)evt->watt[2];
|
||
float avg_v = (evt->vrms[0] + evt->vrms[1] + evt->vrms[2]) / 3.0f;
|
||
float sum_i = evt->irms[0] + evt->irms[1] + evt->irms[2];
|
||
|
||
portENTER_CRITICAL(&s_meter_mux);
|
||
memcpy(s_meter.vrms, evt->vrms, sizeof(s_meter.vrms));
|
||
memcpy(s_meter.irms, evt->irms, sizeof(s_meter.irms));
|
||
s_meter.watt[0] = evt->watt[0];
|
||
s_meter.watt[1] = evt->watt[1];
|
||
s_meter.watt[2] = evt->watt[2];
|
||
s_meter.frequency = evt->frequency;
|
||
s_meter.power_factor = evt->power_factor;
|
||
s_meter.total_energy_Wh = evt->total_energy;
|
||
s_meter.sum_watt = sum_w;
|
||
s_meter.avg_voltage = avg_v;
|
||
s_meter.sum_current = sum_i;
|
||
s_meter.have_data = true;
|
||
portEXIT_CRITICAL(&s_meter_mux);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// MicroOCPP Inputs/CBs
|
||
// -----------------------------------------------------------------------------
|
||
bool setConnectorPluggedInput(void)
|
||
{
|
||
return s_ev_plugged;
|
||
}
|
||
|
||
bool setEvReadyInput(void)
|
||
{
|
||
return s_ev_ready;
|
||
}
|
||
|
||
bool setEvseReadyInput(void)
|
||
{
|
||
return s_evse_enabled && s_evse_available;
|
||
}
|
||
|
||
float setPowerMeterInput(void)
|
||
{
|
||
int32_t w = 0;
|
||
bool have = false;
|
||
|
||
portENTER_CRITICAL(&s_meter_mux);
|
||
have = s_meter.have_data;
|
||
if (have)
|
||
w = s_meter.sum_watt;
|
||
portEXIT_CRITICAL(&s_meter_mux);
|
||
|
||
if (!have)
|
||
ESP_LOGW(TAG, "[METER] PowerMeterInput: no data (return 0)");
|
||
else
|
||
ESP_LOGD(TAG, "[METER] PowerMeterInput: %" PRId32 " W", w);
|
||
|
||
return (float)w;
|
||
}
|
||
|
||
float setEnergyMeterInput(void)
|
||
{
|
||
float wh = 0.0f;
|
||
bool have = false;
|
||
|
||
portENTER_CRITICAL(&s_meter_mux);
|
||
have = s_meter.have_data;
|
||
if (have)
|
||
wh = s_meter.total_energy_Wh;
|
||
portEXIT_CRITICAL(&s_meter_mux);
|
||
|
||
if (!have)
|
||
ESP_LOGW(TAG, "[METER] EnergyMeterInput: no data (return 0)");
|
||
else
|
||
ESP_LOGD(TAG, "[METER] EnergyMeterInput: (%.1f Wh)", wh);
|
||
|
||
return wh;
|
||
}
|
||
|
||
int setEnergyInput(void)
|
||
{
|
||
float wh = setEnergyMeterInput();
|
||
int wh_i = (int)lrintf((double)wh);
|
||
ESP_LOGD(TAG, "[METER] EnergyInput: %.1f Wh", wh);
|
||
return wh_i;
|
||
}
|
||
|
||
float setCurrentInput(void)
|
||
{
|
||
float a = 0.0f;
|
||
bool have = false;
|
||
|
||
portENTER_CRITICAL(&s_meter_mux);
|
||
have = s_meter.have_data;
|
||
if (have)
|
||
a = s_meter.sum_current;
|
||
portEXIT_CRITICAL(&s_meter_mux);
|
||
|
||
if (!have)
|
||
ESP_LOGW(TAG, "[METER] CurrentInput: no data (return 0)");
|
||
else
|
||
ESP_LOGD(TAG, "[METER] CurrentInput: %.2f A (total)", a);
|
||
|
||
return a;
|
||
}
|
||
|
||
float setVoltageInput(void)
|
||
{
|
||
float v = 0.0f;
|
||
bool have = false;
|
||
|
||
portENTER_CRITICAL(&s_meter_mux);
|
||
have = s_meter.have_data;
|
||
if (have)
|
||
v = s_meter.avg_voltage;
|
||
portEXIT_CRITICAL(&s_meter_mux);
|
||
|
||
if (!have)
|
||
ESP_LOGW(TAG, "[METER] VoltageInput: no data (return 0)");
|
||
else
|
||
ESP_LOGD(TAG, "[METER] VoltageInput: %.1f V (avg)", v);
|
||
|
||
return v;
|
||
}
|
||
|
||
float setPowerInput(void)
|
||
{
|
||
float w = setPowerMeterInput();
|
||
ESP_LOGD(TAG, "[METER] PowerInput: %.1f W", w);
|
||
return w;
|
||
}
|
||
|
||
float setTemperatureInput(void)
|
||
{
|
||
ESP_LOGD(TAG, "TemperatureInput");
|
||
return 16.5f;
|
||
}
|
||
|
||
void setSmartChargingCurrentOutput(float limit)
|
||
{
|
||
ESP_LOGI(TAG, "SmartChargingCurrentOutput: %.0f", limit);
|
||
}
|
||
|
||
void setSmartChargingPowerOutput(float limit)
|
||
{
|
||
ESP_LOGI(TAG, "SmartChargingPowerOutput: %.0f", limit);
|
||
}
|
||
|
||
void setSmartChargingOutput(float power, float current, int nphases)
|
||
{
|
||
ESP_LOGI(TAG, "SmartChargingOutput: P=%.0f W, I=%.0f A, phases=%d", power, current, nphases);
|
||
}
|
||
|
||
void setGetConfiguration(const char *payload, size_t len)
|
||
{
|
||
ESP_LOGI(TAG, "GetConfiguration: %.*s (%u)", (int)len, payload, (unsigned)len);
|
||
}
|
||
|
||
void setStartTransaction(const char *payload, size_t len)
|
||
{
|
||
ESP_LOGI(TAG, "StartTransaction: %.*s (%u)", (int)len, payload, (unsigned)len);
|
||
}
|
||
|
||
void setChangeConfiguration(const char *payload, size_t len)
|
||
{
|
||
ESP_LOGI(TAG, "ChangeConfiguration: %.*s (%u)", (int)len, payload, (unsigned)len);
|
||
}
|
||
|
||
void OnResetExecute(bool state)
|
||
{
|
||
ESP_LOGI(TAG, "OnResetExecute (state=%d)", state);
|
||
esp_restart();
|
||
}
|
||
|
||
bool setOccupiedInput(void)
|
||
{
|
||
ESP_LOGD(TAG, "setOccupiedInput");
|
||
return false;
|
||
}
|
||
|
||
bool setStartTxReadyInput(void)
|
||
{
|
||
ESP_LOGD(TAG, "setStartTxReadyInput");
|
||
return true;
|
||
}
|
||
|
||
bool setStopTxReadyInput(void)
|
||
{
|
||
ESP_LOGD(TAG, "setStopTxReadyInput");
|
||
return true;
|
||
}
|
||
|
||
bool setOnResetNotify(bool value)
|
||
{
|
||
ESP_LOGI(TAG, "setOnResetNotify %d", value);
|
||
return true;
|
||
}
|
||
|
||
void notificationOutput(OCPP_Transaction *transaction, enum OCPP_TxNotification txNotification)
|
||
{
|
||
(void)transaction;
|
||
|
||
ESP_LOGI(TAG, "TxNotification: %d", txNotification);
|
||
|
||
switch (txNotification)
|
||
{
|
||
case Authorized:
|
||
ESP_LOGI(TAG, "Authorized");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_AUTHORIZED, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case AuthorizationRejected:
|
||
ESP_LOGI(TAG, "AuthorizationRejected");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_AUTH_REJECTED, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case AuthorizationTimeout:
|
||
ESP_LOGI(TAG, "AuthorizationTimeout");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_AUTH_TIMEOUT, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case ReservationConflict:
|
||
ESP_LOGI(TAG, "ReservationConflict");
|
||
break;
|
||
|
||
case ConnectionTimeout:
|
||
ESP_LOGI(TAG, "ConnectionTimeout");
|
||
break;
|
||
|
||
case DeAuthorized:
|
||
ESP_LOGI(TAG, "DeAuthorized");
|
||
break;
|
||
|
||
case RemoteStart:
|
||
ESP_LOGI(TAG, "RemoteStart");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_REMOTE_START, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case RemoteStop:
|
||
ESP_LOGI(TAG, "RemoteStop");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_REMOTE_STOP, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case StartTx:
|
||
ESP_LOGI(TAG, "StartTx");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_START_TX, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
|
||
case StopTx:
|
||
ESP_LOGI(TAG, "StopTx");
|
||
esp_event_post(OCPP_EVENTS, OCPP_EVENT_STOP_TX, NULL, 0, portMAX_DELAY);
|
||
break;
|
||
}
|
||
}
|
||
|
||
bool ocpp_is_connected(void)
|
||
{
|
||
return g_ocpp_conn != NULL;
|
||
}
|
||
|
||
const char *addErrorCodeInput(void)
|
||
{
|
||
const char *ptr = NULL;
|
||
uint32_t error = evse_get_error();
|
||
|
||
if (error & EVSE_ERR_PILOT_FAULT_BIT)
|
||
ptr = "InternalError";
|
||
else if (error & EVSE_ERR_DIODE_SHORT_BIT)
|
||
ptr = "InternalError";
|
||
else if (error & EVSE_ERR_LOCK_FAULT_BIT)
|
||
ptr = "ConnectorLockFailure";
|
||
else if (error & EVSE_ERR_UNLOCK_FAULT_BIT)
|
||
ptr = "ConnectorLockFailure";
|
||
else if (error & EVSE_ERR_RCM_TRIGGERED_BIT)
|
||
ptr = "OtherError";
|
||
else if (error & EVSE_ERR_RCM_SELFTEST_FAULT_BIT)
|
||
ptr = "OtherError";
|
||
else if (error & EVSE_ERR_TEMPERATURE_HIGH_BIT)
|
||
ptr = "HighTemperature";
|
||
else if (error & EVSE_ERR_TEMPERATURE_FAULT_BIT)
|
||
ptr = "OtherError";
|
||
|
||
return ptr;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Start / Stop OCPP
|
||
// -----------------------------------------------------------------------------
|
||
void ocpp_start(void)
|
||
{
|
||
ESP_LOGI(TAG, "Starting OCPP");
|
||
|
||
if (ocpp_task != NULL)
|
||
{
|
||
ESP_LOGW(TAG, "OCPP already running");
|
||
return;
|
||
}
|
||
|
||
storage_init_best_effort();
|
||
|
||
enabled = ocpp_get_enabled();
|
||
if (!enabled)
|
||
{
|
||
ESP_LOGW(TAG, "OCPP disabled");
|
||
return;
|
||
}
|
||
|
||
char serverstr[64] = {0};
|
||
ocpp_get_server(serverstr);
|
||
if (serverstr[0] == '\0')
|
||
{
|
||
ESP_LOGW(TAG, "No OCPP server configured. Skipping connection.");
|
||
return;
|
||
}
|
||
|
||
char charge_id[64] = {0};
|
||
ocpp_get_charge_id(charge_id);
|
||
if (charge_id[0] == '\0')
|
||
{
|
||
ESP_LOGW(TAG, "No OCPP charge_id configured. Skipping connection.");
|
||
return;
|
||
}
|
||
|
||
mg_mgr_init(&mgr);
|
||
mg_log_set(MG_LL_ERROR);
|
||
|
||
struct OCPP_FilesystemOpt fsopt = {.use = true, .mount = true, .formatFsOnFail = true};
|
||
|
||
g_ocpp_conn = ocpp_makeConnection(&mgr,
|
||
serverstr,
|
||
charge_id,
|
||
"",
|
||
"",
|
||
fsopt);
|
||
if (!g_ocpp_conn)
|
||
{
|
||
ESP_LOGE(TAG, "ocpp_makeConnection failed");
|
||
mg_mgr_free(&mgr);
|
||
return;
|
||
}
|
||
|
||
|
||
//chargePointModel: "EPower M1"
|
||
//chargePointVendor: "Plixin"
|
||
//firmwareVersion: "FW-PLXV1.0"
|
||
//chargePointSerialNumber: "SN001"
|
||
|
||
ocpp_initialize(g_ocpp_conn, "EPower M1", "Plixin", fsopt, false);
|
||
|
||
ocpp_setEvReadyInput(&setEvReadyInput);
|
||
ocpp_setEvseReadyInput(&setEvseReadyInput);
|
||
ocpp_setConnectorPluggedInput(&setConnectorPluggedInput);
|
||
ocpp_setOnResetExecute(&OnResetExecute);
|
||
ocpp_setTxNotificationOutput(¬ificationOutput);
|
||
ocpp_setStopTxReadyInput(&setStopTxReadyInput);
|
||
ocpp_setOnResetNotify(&setOnResetNotify);
|
||
|
||
ocpp_setEnergyMeterInput(&setEnergyInput);
|
||
|
||
ocpp_addMeterValueInputFloat(&setCurrentInput, "Current.Import", "A", NULL, NULL);
|
||
ocpp_addMeterValueInputFloat(&getCurrentOffered, "Current.Offered", "A", NULL, NULL);
|
||
ocpp_addMeterValueInputFloat(&setVoltageInput, "Voltage", "V", NULL, NULL);
|
||
ocpp_addMeterValueInputFloat(&setTemperatureInput, "Temperature", "Celsius", NULL, NULL);
|
||
ocpp_addMeterValueInputFloat(&setPowerMeterInput, "Power.Active.Import", "W", NULL, NULL);
|
||
ocpp_addMeterValueInputFloat(&setEnergyMeterInput, "Energy.Active.Import.Register", "Wh", NULL, NULL);
|
||
|
||
ocpp_addErrorCodeInput(&addErrorCodeInput);
|
||
|
||
xTaskCreate(ocpp_task_func, "ocpp_task", 5 * 1024, NULL, 3, &ocpp_task);
|
||
|
||
if (!s_auth_verify_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||
AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY,
|
||
&ocpp_on_auth_verify, NULL, &s_auth_verify_inst));
|
||
ESP_LOGI(TAG, "Registered AUTH_EVENT_TAG_VERIFY listener");
|
||
}
|
||
|
||
if (!s_evse_state_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||
EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED,
|
||
&evse_event_handler, NULL, &s_evse_state_inst));
|
||
}
|
||
|
||
if (!s_evse_enable_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||
EVSE_EVENTS, EVSE_EVENT_ENABLE_UPDATED,
|
||
&evse_enable_available_handler, NULL, &s_evse_enable_inst));
|
||
}
|
||
if (!s_evse_available_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||
EVSE_EVENTS, EVSE_EVENT_AVAILABLE_UPDATED,
|
||
&evse_enable_available_handler, NULL, &s_evse_available_inst));
|
||
}
|
||
|
||
if (!s_meter_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||
METER_EVENT, METER_EVENT_DATA_READY,
|
||
&on_meter_event, NULL, &s_meter_inst));
|
||
ESP_LOGI(TAG, "Registered METER_EVENT_DATA_READY listener (EVSE source)");
|
||
}
|
||
}
|
||
|
||
void ocpp_stop(void)
|
||
{
|
||
ESP_LOGI(TAG, "Stopping OCPP");
|
||
|
||
if (ocpp_task)
|
||
{
|
||
vTaskDelete(ocpp_task);
|
||
ocpp_task = NULL;
|
||
}
|
||
|
||
ocpp_deinitialize();
|
||
|
||
if (g_ocpp_conn)
|
||
{
|
||
ocpp_deinitConnection(g_ocpp_conn);
|
||
g_ocpp_conn = NULL;
|
||
}
|
||
|
||
mg_mgr_free(&mgr);
|
||
|
||
if (s_auth_verify_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
|
||
AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY, s_auth_verify_inst));
|
||
s_auth_verify_inst = NULL;
|
||
}
|
||
|
||
if (s_evse_state_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
|
||
EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, s_evse_state_inst));
|
||
s_evse_state_inst = NULL;
|
||
}
|
||
|
||
if (s_evse_enable_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
|
||
EVSE_EVENTS, EVSE_EVENT_ENABLE_UPDATED, s_evse_enable_inst));
|
||
s_evse_enable_inst = NULL;
|
||
}
|
||
if (s_evse_available_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
|
||
EVSE_EVENTS, EVSE_EVENT_AVAILABLE_UPDATED, s_evse_available_inst));
|
||
s_evse_available_inst = NULL;
|
||
}
|
||
if (s_meter_inst)
|
||
{
|
||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
|
||
METER_EVENT, METER_EVENT_DATA_READY, s_meter_inst));
|
||
s_meter_inst = NULL;
|
||
}
|
||
}
|
||
|
||
// === Fim de: components/ocpp/src/ocpp.c ===
|
||
|
||
|
||
// === Início de: components/ocpp/include/ocpp_events.h ===
|
||
#pragma once
|
||
#include "esp_event.h"
|
||
#include <stdbool.h>
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* Base de eventos do OCPP (igual ao padrão usado em auth_events.h) */
|
||
ESP_EVENT_DECLARE_BASE(OCPP_EVENTS);
|
||
|
||
/* IDs de eventos do OCPP */
|
||
typedef enum {
|
||
OCPP_EVENT_CONNECTED = 0, // payload: const char* (server URL) – opcional
|
||
OCPP_EVENT_DISCONNECTED, // payload: NULL
|
||
OCPP_EVENT_AUTHORIZED, // payload: ocpp_idtag_event_t (opcional)
|
||
OCPP_EVENT_AUTH_REJECTED, // payload: ocpp_idtag_event_t (opcional)
|
||
OCPP_EVENT_AUTH_TIMEOUT, // payload: NULL
|
||
OCPP_EVENT_REMOTE_START, // payload: ocpp_idtag_event_t (opcional)
|
||
OCPP_EVENT_REMOTE_STOP, // payload: NULL
|
||
OCPP_EVENT_START_TX, // payload: ocpp_tx_event_t (opcional)
|
||
OCPP_EVENT_STOP_TX, // payload: ocpp_reason_event_t (opcional)
|
||
OCPP_EVENT_RESET, // payload: NULL
|
||
OCPP_EVENT_OPERATIVE_UPDATED
|
||
} ocpp_event_id_t;
|
||
|
||
/* Limites de strings simples (evita dependência de auth.h) */
|
||
#define OCPP_IDTAG_MAX 32
|
||
#define OCPP_REASON_MAX 32
|
||
|
||
/* Payloads opcionais */
|
||
typedef struct {
|
||
char idTag[OCPP_IDTAG_MAX];
|
||
} ocpp_idtag_event_t;
|
||
|
||
typedef struct {
|
||
int tx_id; // se disponível
|
||
} ocpp_tx_event_t;
|
||
|
||
typedef struct {
|
||
char reason[OCPP_REASON_MAX];
|
||
} ocpp_reason_event_t;
|
||
|
||
// Payload do novo evento
|
||
typedef struct {
|
||
bool operative; // true = Operative, false = Inoperative
|
||
int64_t timestamp_us; // esp_timer_get_time()
|
||
} ocpp_operative_event_t;
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
// === Fim de: components/ocpp/include/ocpp_events.h ===
|
||
|
||
|
||
// === Início de: components/ocpp/include/ocpp.h ===
|
||
#ifndef OCPP_H_
|
||
#define OCPP_H_
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/**
|
||
* @brief Start OCPP
|
||
*/
|
||
void ocpp_start(void);
|
||
|
||
/**
|
||
* @brief Stop OCPP
|
||
*/
|
||
void ocpp_stop(void);
|
||
|
||
/* Config getters / setters */
|
||
bool ocpp_get_enabled(void);
|
||
void ocpp_set_enabled(bool value);
|
||
|
||
void ocpp_get_server(char *value); // buffer >= 64
|
||
void ocpp_set_server(char *value);
|
||
|
||
void ocpp_get_charge_id(char *value); // buffer >= 64
|
||
void ocpp_set_charge_id(char *value);
|
||
|
||
/* Estado de conexão */
|
||
bool ocpp_is_connected(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* OCPP_H_ */
|
||
|
||
// === Fim de: components/ocpp/include/ocpp.h ===
|