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

@@ -12,7 +12,7 @@ cJSON* json_get_wifi_config(void);
esp_err_t json_set_wifi_config(cJSON* root, bool timeout);
cJSON* json_get_wifi_scan(void);
//JSON* json_get_wifi_scan(void);
cJSON* json_get_mqtt_config(void);

View File

@@ -153,11 +153,12 @@ esp_err_t json_set_evse_config(cJSON *root)
{
RETURN_ON_ERROR(meter_set_state(meter_str_to_state(cJSON_GetObjectItem(root, "stateMeter")->valuestring)));
}
*/
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "maxGridCurrent")))
{
RETURN_ON_ERROR(grid_set_max_current(cJSON_GetObjectItem(root, "maxGridCurrent")->valuedouble));
}
*/
if (cJSON_IsBool(cJSON_GetObjectItem(root, "enabledocpp")))
{
@@ -177,6 +178,8 @@ esp_err_t json_set_evse_config(cJSON *root)
return ESP_OK;
}
/*
cJSON *json_get_wifi_config(void)
{
cJSON *root = cJSON_CreateObject();
@@ -210,6 +213,7 @@ cJSON *json_get_wifi_scan(void)
{
cJSON *root = cJSON_CreateArray();
wifi_scan_ap_t scan_aps[WIFI_SCAN_SCAN_LIST_SIZE];
uint16_t number = wifi_scan(scan_aps);
for (int i = 0; i < number; i++)
@@ -223,6 +227,7 @@ cJSON *json_get_wifi_scan(void)
return root;
}
*/
cJSON *json_get_mqtt_config(void)
{
@@ -332,7 +337,7 @@ cJSON *json_get_state(void)
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
cJSON_AddBoolToObject(root, "available", evse_is_available());
cJSON_AddBoolToObject(root, "enabled", evse_is_enabled());
cJSON_AddBoolToObject(root, "pendingAuth", evse_is_pending_auth());
cJSON_AddBoolToObject(root, "pendingAuth", false);
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
uint32_t error = evse_error_get_bits();

View File

@@ -11,14 +11,14 @@ static void restart_func(void* arg)
{
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
//esp_restart();
vTaskDelete(NULL);
}
void timeout_restart()
{
xTaskCreate(restart_func, "restart_task", 2 * 1024, NULL, 10, NULL);
//xTaskCreate(restart_func, "restart_task", 2 * 1024, NULL, 10, NULL);
}
typedef struct

View File

@@ -1,4 +1,4 @@
set(srcs "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c")
set(srcs "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c" "src/auth_events.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"

View File

@@ -3,68 +3,80 @@
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#ifdef __cplusplus
extern "C" {
#endif
// Tamanho máximo da tag RFID (incluindo '\0')
/// Tamanho máximo de uma tag RFID (incluindo '\0')
#define AUTH_TAG_MAX_LEN 20
// Evento enviado ao EVSE Manager após leitura de tag
/// Estrutura de evento emitida após leitura de uma tag
typedef struct {
char tag[AUTH_TAG_MAX_LEN]; // Tag lida
bool authorized; // true se tag for válida
char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida
bool authorized; ///< true se a tag for reconhecida como válida
} auth_event_t;
/**
* @brief Inicializa o sistema de autenticação.
* Carrega configuração e inicia o leitor Wiegand (wg26).
*
* - Carrega a configuração (enabled) da NVS
* - Inicia o leitor Wiegand
* - Emite evento AUTH_EVENT_INIT com estado atual
*/
void auth_init(void);
/**
* @brief Define a fila de eventos que receberá auth_event_t.
*/
void auth_set_event_queue(QueueHandle_t queue);
/**
* @brief Ativa ou desativa o módulo de autenticação (RFID).
* Essa configuração é salva em NVS.
* @brief Ativa ou desativa o uso de autenticação via RFID.
*
* Esta configuração é persistida em NVS. Se desativado, o sistema
* considerará todas as autorizações como aceitas.
*
* @param value true para ativar, false para desativar
*/
void auth_set_enabled(bool value);
/**
* @brief Verifica se a autenticação está habilitada.
* @brief Verifica se o sistema de autenticação está habilitado.
*/
bool auth_is_enabled(void);
/**
* @brief Adiciona uma nova tag válida.
* @brief Adiciona uma nova tag RFID à lista de autorizadas.
*
* @param tag String da tag (máx AUTH_TAG_MAX_LEN-1)
* @return true se a tag foi adicionada, false se já existia ou inválida
*/
bool auth_add_tag(const char *tag);
/**
* @brief Remove uma tag previamente cadastrada.
*
* @param tag String da tag
* @return true se foi removida, false se não encontrada
*/
bool auth_remove_tag(const char *tag);
/**
* @brief Verifica se uma tag está cadastrada.
* @brief Verifica se uma tag está registrada como válida.
*/
bool auth_tag_exists(const char *tag);
/**
* @brief Lista as tags registradas (via ESP_LOG).
* @brief Lista todas as tags válidas atualmente registradas (via logs).
*/
void auth_list_tags(void);
/**
* @brief Processa uma tag lida (usado pelo leitor Wiegand).
* @brief Processa uma tag RFID lida (chamada normalmente pelo leitor).
*
* - Verifica validade
* - Emite evento AUTH_EVENT_TAG_PROCESSED
* - Inicia timer de expiração se autorizada
*/
void auth_process_tag(const char *tag);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,21 @@
#pragma once
#include "esp_event.h"
#define AUTH_EVENT_TAG_MAX_LEN 32
ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
typedef enum {
AUTH_EVENT_TAG_PROCESSED,
AUTH_EVENT_ENABLED_CHANGED,
AUTH_EVENT_INIT,
} auth_event_id_t;
typedef struct {
char tag[AUTH_EVENT_TAG_MAX_LEN];
bool authorized;
} auth_tag_event_data_t;
typedef struct {
bool enabled;
} auth_enabled_event_data_t;

View File

@@ -3,26 +3,25 @@
*/
#include "auth.h"
#include "auth_events.h"
#include "esp_event.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <esp_log.h>
#include <string.h>
#include "wiegand_reader.h"
#include "nvs_flash.h"
#include "nvs.h"
#define MAX_TAGS 50
static const char *TAG = "Auth";
static bool enabled = true;
static bool enabled = false;
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
static int tag_count = 0;
// Fila de eventos enviada ao EVSE Manager
static QueueHandle_t event_queue = NULL;
// ===========================
// Persistência em NVS
// ===========================
@@ -64,21 +63,22 @@ static bool is_tag_valid(const char *tag) {
return true;
}
}
return false;
return true;
//TODO
//return false;
}
// ===========================
// API pública
// ===========================
void auth_set_event_queue(QueueHandle_t queue) {
event_queue = queue;
}
void auth_set_enabled(bool value) {
enabled = value;
save_auth_config();
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");
auth_enabled_event_data_t event = { .enabled = enabled };
esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY);
}
bool auth_is_enabled(void) {
@@ -124,28 +124,32 @@ void auth_list_tags(void) {
void auth_init(void) {
load_auth_config(); // carrega estado de ativação
initWiegand(); // inicia leitor RFID
if (enabled) {
initWiegand(); // só inicia se estiver habilitado
ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)");
} else {
ESP_LOGI(TAG, "Auth disabled, Wiegand reader not started");
}
auth_enabled_event_data_t evt = { .enabled = enabled };
esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
ESP_LOGI(TAG, "Estado inicial AUTH enviado (enabled = %d)", enabled);
}
// Processa uma tag lida (chamada pelo leitor)
void auth_process_tag(const char *tag) {
if (!tag || !auth_is_enabled()) {
ESP_LOGW(TAG, "Auth disabled or NULL tag received.");
return;
}
auth_event_t event;
strncpy(event.tag, tag, AUTH_TAG_MAX_LEN - 1);
event.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
auth_tag_event_data_t event;
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
event.authorized = is_tag_valid(tag);
ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED");
if (event_queue) {
if (xQueueSend(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
ESP_LOGW(TAG, "Auth event queue full, dropping tag: %s", tag);
}
} else {
ESP_LOGW(TAG, "Auth event queue not set");
}
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY);
}

View File

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

View File

@@ -101,9 +101,11 @@ esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio
{
CHECK_ARG(reader && buf_size && callback);
/*
esp_err_t res = gpio_install_isr_service(0);
if (res != ESP_OK && res != ESP_ERR_INVALID_STATE)
return res;
*/
memset(reader, 0, sizeof(wiegand_reader_t));
reader->gpio_d0 = gpio_d0;

View File

@@ -1,8 +1,3 @@
/*
* wiegand_reader.c
*/
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
@@ -10,8 +5,6 @@
#include <freertos/queue.h>
#include <esp_log.h>
#include <wiegand.h>
#include <evse_api.h>
#include <ocpp.h>
#include "auth.h"
#define CONFIG_EXAMPLE_BUF_SIZE 50
@@ -62,29 +55,12 @@ static void wiegand_task(void *arg) {
}
ESP_LOGI(TAG, "Tag read: %s", tag);
if (!auth_is_enabled()) {
ESP_LOGW(TAG, "Auth disabled, ignoring tag.");
continue;
}
if (auth_tag_exists(tag)) {
ESP_LOGI(TAG, "Authorized tag. Proceeding...");
evse_authorize();
if (ocpp_is_TransactionActive()) {
ocpp_end_transaction(tag);
} else {
ocpp_begin_transaction(tag);
}
} else {
ESP_LOGW(TAG, "Unauthorized tag: %s", tag);
}
auth_process_tag(tag); // agora delega toda a lógica à auth.c
}
}
}
void initWiegand(void) {
ESP_LOGI(TAG, "Initializing Wiegand reader");
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 5, NULL);
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL);
}

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();
}

View File

@@ -9,7 +9,7 @@
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "pilot.h"
#include "evse_pilot.h"
#include "adc.h"
#include "board_config.h"
@@ -21,14 +21,9 @@
#define NUM_PILOT_SAMPLES 100
#define MAX_SAMPLE_ATTEMPTS 1000
#define PILOT_EXTREME_PERCENT 10 // 15% superior e inferior
#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior
static const char *TAG = "pilot";
static pilot_voltage_cache_t last_voltage = {0, 0};
static inline uint16_t adc_to_mv(uint16_t x) {
return (uint16_t)(((uint32_t)(x) * 3300U) / 4095U);
}
static const char *TAG = "evse_pilot";
void pilot_init(void)
{
@@ -92,30 +87,29 @@ void pilot_set_amps(uint16_t amps)
ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty);
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
}
static int compare_u16(const void *a, const void *b) {
return (*(uint16_t *)a - *(uint16_t *)b);
static int compare_int(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
static uint16_t select_low_median_qsort(uint16_t *src, int n, int percent) {
static int select_low_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
uint16_t *copy = alloca(n * sizeof(uint16_t));
memcpy(copy, src, n * sizeof(uint16_t));
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(uint16_t), compare_u16);
qsort(copy, n, sizeof(int), compare_int);
return copy[k / 2];
}
static uint16_t select_high_median_qsort(uint16_t *src, int n, int percent) {
static int select_high_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
uint16_t *copy = alloca(n * sizeof(uint16_t));
memcpy(copy, src, n * sizeof(uint16_t));
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(uint16_t), compare_u16);
qsort(copy, n, sizeof(int), compare_int);
return copy[n - k + (k / 2)];
}
@@ -123,9 +117,9 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
{
ESP_LOGD(TAG, "pilot_measure");
uint16_t samples[NUM_PILOT_SAMPLES];
int samples[NUM_PILOT_SAMPLES];
int collected = 0, attempts = 0;
uint16_t sample;
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) {
@@ -144,8 +138,11 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
return;
}
uint16_t high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
uint16_t low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
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;
@@ -173,14 +170,3 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12);
}
bool pilot_get_state(void)
{
pilot_voltage_t voltage;
bool is_n12v;
pilot_measure(&voltage, &is_n12v);
// Considera que "estado alto" significa pelo menos 12V (standby ou pronto)
return voltage == PILOT_VOLTAGE_12;
}

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

@@ -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

View File

@@ -1,7 +1,8 @@
set(srcs
"src/input_filter.c" "src/loadbalancer.c"
"src/input_filter.c" "src/loadbalancer.c" "src/loadbalancer_events.c"
)
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
REQUIRES esp_event evse)
PRIV_REQUIRES nvs_flash
REQUIRES esp_event esp_timer meter_manager evse)

View File

@@ -5,13 +5,75 @@
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
/**
* @brief Initializes the load balancer.
*
* This function configures the load balancer and its resources, including
* any necessary persistence configurations, such as storage in NVS (Non-Volatile Storage).
* This function prepares the system to perform load balancing efficiently.
*/
void loadbalancer_init(void);
/**
* @brief Continuous task for the load balancer.
*
* This function executes the load balancing logic continuously, typically in a FreeRTOS task.
* It performs balance calculations, checks the grid current and energy conditions, and adjusts
* the outputs as necessary to ensure efficient energy consumption.
*
* @param param Input parameter, usually used to pass additional information or relevant context
* for the task execution.
*/
void loadbalancer_task(void *param);
// Compatibility functions
void setMaxGridCurrent(int max_grid_current);
void setLiveGridCurrent(int live_grid_current);
void setLiveVolt(int live_volt);
/**
* @brief Enables or disables the load balancing system.
*
* This function allows enabling or disabling the load balancing system. When enabled, the load
* balancer starts managing the grid current based on the configured limits. If disabled, the system
* operates without balancing.
*
* The configuration is persisted in NVS, ensuring that the choice is maintained across system restarts.
*
* @param value If true, enables load balancing. If false, disables it.
*/
void loadbalancer_set_enabled(bool value);
/**
* @brief Checks if load balancing is enabled.
*
* This function returns the current status of the load balancing system.
*
* @return Returns true if load balancing is enabled, otherwise returns false.
*/
bool loadbalancer_is_enabled(void);
/**
* @brief Sets the maximum grid current.
*
* This function configures the maximum grid current that can be supplied to the load balancing system.
* The value set ensures that the system does not overload the electrical infrastructure and respects
* the safety limits.
*
* @param max_grid_current The maximum allowed current (in amperes) for the load balancing system.
* This value should be appropriate for the grid capacity and the installation.
*/
esp_err_t load_balancing_set_max_grid_current(uint8_t max_grid_current);
/**
* @brief Gets the maximum grid current.
*
* This function retrieves the current maximum grid current limit.
*
* @return The maximum grid current (in amperes).
*/
uint8_t load_balancing_get_max_grid_current(void);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "esp_event.h"
#include <stdbool.h>
#include <stdint.h>
#include "esp_timer.h"
ESP_EVENT_DECLARE_BASE(LOADBALANCER_EVENTS);
typedef enum {
LOADBALANCER_EVENT_INIT,
LOADBALANCER_EVENT_STATE_CHANGED,
LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED
} loadbalancer_event_id_t;
typedef struct {
float limit;
int64_t timestamp_us;
} loadbalancer_charging_limit_event_t;
typedef struct {
bool enabled;
int64_t timestamp_us;
} loadbalancer_state_event_t;

View File

@@ -1,108 +1,327 @@
#include "loadbalancer.h"
#include "evse_api.h"
#include "loadbalancer_events.h"
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "input_filter.h"
#include <string.h> // Para memcpy
#include "nvs_flash.h"
#include "nvs.h"
#include <string.h>
#include "meter_events.h"
#include "evse_events.h"
static const char *TAG = "loadbalancer";
// Limites configuráveis
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
#define MAX_CHARGING_CURRENT_LIMIT 32 // A
#define MIN_GRID_CURRENT_LIMIT 6 // A
#define MAX_GRID_CURRENT_LIMIT 100 // A
// Parâmetros
static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT;
static bool loadbalancer_enabled = false;
static float grid_current = 0.0f;
static float evse_current = 0.0f;
static float max_grid_current = 32.0f; // Amperes
#define MIN_EVSE_CURRENT 6.0f
// Filtros exponenciais para suavizar leituras
static input_filter_t grid_filter;
static input_filter_t evse_filter;
static void grid_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
#define NVS_NAMESPACE "loadbalancing"
#define NVS_MAX_GRID_CURRENT "max_grid_curr"
#define NVS_LOADBALANCER_ENABLED "enabled"
/*
if (id == GRIDMETER_EVENT_UPDATE && data)
static void loadbalancer_meter_event_handler(void *handler_arg,
esp_event_base_t base,
int32_t id,
void *event_data)
{
if (id != METER_EVENT_DATA_READY || event_data == NULL)
return;
const meter_event_data_t *evt = (const meter_event_data_t *)event_data;
ESP_LOGI(TAG, "Received meter event from source: %s", evt->source);
ESP_LOGI(TAG, "Raw IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]);
ESP_LOGI(TAG, "Raw VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[2]);
ESP_LOGI(TAG, "Raw Power: [W1=%d, W2=%d, W3=%d]", evt->watt[0], evt->watt[1], evt->watt[2]);
ESP_LOGI(TAG, "Freq: %.2f Hz | PF: %.2f | Energy: %.3f kWh",
evt->frequency, evt->power_factor, evt->total_energy);
// Calcula a corrente máxima entre as 3 fases
float max_irms = evt->irms[0];
for (int i = 1; i < 3; ++i)
{
float raw;
memcpy(&raw, data, sizeof(float));
grid_current = input_filter_update(&grid_filter, raw);
ESP_LOGD(TAG, "Grid current (filtered): %.2f A", grid_current);
if (evt->irms[i] > max_irms)
{
max_irms = evt->irms[i];
}
}
ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms);
// Atualiza com filtro exponencial dependendo da origem
if (strncmp(evt->source, "GRID", 4) == 0)
{
grid_current = input_filter_update(&grid_filter, max_irms);
ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current);
}
else if (strncmp(evt->source, "EVSE", 4) == 0)
{
evse_current = input_filter_update(&evse_filter, max_irms);
ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current);
}
else
{
ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source);
}
*/
}
static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
static void loadbalancer_evse_event_handler(void *handler_arg,
esp_event_base_t base,
int32_t id,
void *event_data)
{
const evse_state_event_data_t *evt = (const evse_state_event_data_t *)event_data;
/*
if (id == EVSEMETER_EVENT_UPDATE && data)
ESP_LOGI(TAG, "EVSE state changed: %d", evt->state);
switch (evt->state)
{
float raw;
memcpy(&raw, data, sizeof(float));
evse_current = input_filter_update(&evse_filter, raw);
ESP_LOGD(TAG, "EVSE current (filtered): %.2f A", evse_current);
case EVSE_STATE_EVENT_IDLE:
// Vehicle is disconnected - current flow can be reduced or reset
ESP_LOGI(TAG, "EVSE is IDLE - possible to release current");
break;
case EVSE_STATE_EVENT_WAITING:
// EV is connected but not charging yet (e.g., waiting for authorization)
ESP_LOGI(TAG, "EVSE is waiting - connected but not charging");
break;
case EVSE_STATE_EVENT_CHARGING:
grid_current = 0.0f;
evse_current = 0.0f;
// Charging has started - maintain or monitor current usage
ESP_LOGI(TAG, "EVSE is charging");
break;
case EVSE_STATE_EVENT_FAULT:
// A fault has occurred - safety measures may be needed
ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing");
// Optional: disable load balancing during fault condition
// loadbalancer_set_enabled(false);
break;
default:
ESP_LOGW(TAG, "Unknown EVSE state: %d", evt->state);
break;
}
}
// Carrega configuração do NVS
static esp_err_t loadbalancer_load_config()
{
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err));
return err;
}
bool needs_commit = false;
uint8_t temp_u8;
// max_grid_current
err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8);
if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT)
{
max_grid_current = temp_u8;
}
else
{
max_grid_current = MAX_GRID_CURRENT_LIMIT;
nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current);
ESP_LOGW(TAG, "max_grid_current missing or invalid, setting default: %d", max_grid_current);
needs_commit = true;
}
// loadbalancer_enabled
err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8);
if (err == ESP_OK && temp_u8 <= 1)
{
loadbalancer_enabled = (temp_u8 != 0);
}
else
{
loadbalancer_enabled = false;
nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0);
ESP_LOGW(TAG, "loadbalancer_enabled missing or invalid, setting default: 0");
needs_commit = true;
}
if (needs_commit)
{
nvs_commit(handle);
}
nvs_close(handle);
return ESP_OK;
}
// Salva o estado habilitado no NVS
void loadbalancer_set_enabled(bool enabled)
{
ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled);
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err));
return;
}
err = nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, enabled ? 1 : 0);
if (err == ESP_OK)
{
nvs_commit(handle);
loadbalancer_enabled = enabled;
ESP_LOGI(TAG, "Load balancing enabled state saved");
loadbalancer_state_event_t evt = {
.enabled = enabled,
.timestamp_us = esp_timer_get_time()};
esp_event_post(LOADBALANCER_EVENTS,
LOADBALANCER_EVENT_STATE_CHANGED,
&evt,
sizeof(evt),
portMAX_DELAY);
}
else
{
ESP_LOGE(TAG, "Failed to save loadbalancer_enabled");
}
nvs_close(handle);
}
// Define e salva o limite de corrente da rede
esp_err_t load_balancing_set_max_grid_current(uint8_t value)
{
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
{
ESP_LOGE(TAG, "Invalid grid current limit: %d", value);
return ESP_ERR_INVALID_ARG;
}
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err));
return err;
}
err = nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, value);
if (err == ESP_OK)
{
nvs_commit(handle);
max_grid_current = value;
ESP_LOGI(TAG, "max_grid_current set to: %d", value);
}
else
{
ESP_LOGE(TAG, "Failed to save max_grid_current to NVS");
}
nvs_close(handle);
return err;
}
uint8_t load_balancing_get_max_grid_current(void)
{
return max_grid_current;
}
bool loadbalancer_is_enabled(void)
{
return loadbalancer_enabled;
}
// Tarefa principal com eventos
void loadbalancer_task(void *param)
{
while (true)
{
if (!loadbalancer_enabled)
{
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
float available = max_grid_current - grid_current + evse_current;
if (available < MIN_CHARGING_CURRENT_LIMIT)
{
available = MIN_CHARGING_CURRENT_LIMIT;
}
else if (available > max_grid_current)
{
available = max_grid_current;
}
ESP_LOGD(TAG, "Setting EVSE current limit: %.1f A", available);
loadbalancer_charging_limit_event_t evt = {
.limit = available,
.timestamp_us = esp_timer_get_time()};
esp_event_post(LOADBALANCER_EVENTS,
LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED,
&evt,
sizeof(evt),
portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
}
*/
}
void loadbalancer_init(void)
{
ESP_LOGI(TAG, "Initializing load balancer");
/*
if (loadbalancer_load_config() != ESP_OK)
{
ESP_LOGW(TAG, "Failed to load/init config. Using in-memory defaults.");
}
input_filter_init(&grid_filter, 0.3f);
input_filter_init(&evse_filter, 0.3f);
if (esp_event_handler_register(GRIDMETER_EVENT, GRIDMETER_EVENT_UPDATE,
grid_event_handler, NULL) != ESP_OK) {
ESP_LOGE(TAG, "Failed to register gridmeter event handler");
}
if (esp_event_handler_register(EVSEMETER_EVENT, EVSEMETER_EVENT_UPDATE,
evse_event_handler, NULL) != ESP_OK) {
ESP_LOGE(TAG, "Failed to register evsemeter event handler");
}
if (xTaskCreate(loadbalancer_task, "loadbalancer", 4096, NULL, 5, NULL) != pdPASS) {
if (xTaskCreate(loadbalancer_task, "loadbalancer", 4096, NULL, 4, NULL) != pdPASS)
{
ESP_LOGE(TAG, "Failed to create loadbalancer task");
}
*/
}
void loadbalancer_task(void *param)
{
while (true)
{
float available = max_grid_current - grid_current + evse_current;
// Restrição de corrente mínima e máxima
if (available < MIN_EVSE_CURRENT) {
available = 0.0f;
} else if (available > max_grid_current) {
available = max_grid_current;
}
ESP_LOGI(TAG, "Setting EVSE current limit: %.1f A", available);
evse_set_charging_current((uint16_t)available);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setMaxGridCurrent(int value)
{
max_grid_current = value / 10.0f;
}
void setLiveGridCurrent(int value)
{
float raw = value / 10.0f;
grid_current = input_filter_update(&grid_filter, raw);
}
void setLiveVolt(int value)
{
(void)value; // reservado para uso futuro
loadbalancer_state_event_t evt = {
.enabled = loadbalancer_enabled,
.timestamp_us = esp_timer_get_time()};
esp_event_post(LOADBALANCER_EVENTS,
LOADBALANCER_EVENT_INIT,
&evt,
sizeof(evt),
portMAX_DELAY);
ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY,
&loadbalancer_meter_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS,
EVSE_EVENT_STATE_CHANGED,
&loadbalancer_evse_event_handler,
NULL));
}

View File

@@ -0,0 +1,4 @@
#include "loadbalancer_events.h"
// Define a base de eventos para o loadbalancer
ESP_EVENT_DEFINE_BASE(LOADBALANCER_EVENTS);

View File

@@ -1,11 +1,13 @@
# List the source files to be compiled
set(srcs
"driver/meter_ade7758/meter_ade7758.c"
"driver/meter_ade7758/ade7758.c"
"driver/meter_orno/meter_orno513.c"
"driver/meter_orno/meter_orno516.c"
"driver/meter_orno/modbus_params.c"
"driver/meter_zigbee/meter_zigbee.c"
"src/meter_manager.c"
"src/meter_events.c"
)
# List the include directories
@@ -19,4 +21,5 @@ set(includes
# Register the component with the ESP-IDF build system
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${includes}"
PRIV_REQUIRES nvs_flash
REQUIRES esp_event esp-modbus)

View File

@@ -1,5 +1,6 @@
#include "meter_ade7758.h"
#include "ade7758.h"
#include "meter_events.h"
#include <stdio.h>
#include <string.h>
@@ -42,22 +43,23 @@ static SemaphoreHandle_t meter_mutex = NULL;
static uint32_t meter_watchdog_counter = 0;
// === Utilitários internos ===
static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) {
meter_event_data_t evt = {
.frequency = 0,
.power_factor = 0,
.total_energy = 0
};
static void meter_ade7758_clear_internal_data(void) {
if (meter_mutex && xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
memset(&meter_data, 0, sizeof(meter_data));
xSemaphoreGive(meter_mutex);
}
}
memcpy(evt.vrms, data->vrms, sizeof(evt.vrms));
memcpy(evt.irms, data->irms, sizeof(evt.irms));
memcpy(evt.watt, data->watt, sizeof(evt.watt));
static bool meter_ade7758_read_internal(meter_ade7758_internal_data_t *out) {
if (!out) return false;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
*out = meter_data;
xSemaphoreGive(meter_mutex);
return true;
esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY,
&evt, sizeof(evt), pdMS_TO_TICKS(10));
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
return false;
}
static void meter_ade7758_task_func(void *param) {
@@ -66,27 +68,29 @@ static void meter_ade7758_task_func(void *param) {
meter_ade7758_internal_data_t previous = {0};
while (true) {
meter_ade7758_internal_data_t current = {0};
meter_ade7758_internal_data_t meterData = {0};
current.vrms[0] = avrms() / VRMS_CAL;
current.vrms[1] = bvrms() / VRMS_CAL;
current.vrms[2] = cvrms() / VRMS_CAL;
meterData.vrms[0] = avrms() / VRMS_CAL;
meterData.vrms[1] = bvrms() / VRMS_CAL;
meterData.vrms[2] = cvrms() / VRMS_CAL;
current.irms[0] = airms() / IRMS_CAL;
current.irms[1] = birms() / IRMS_CAL;
current.irms[2] = cirms() / IRMS_CAL;
meterData.irms[0] = airms() / IRMS_CAL;
meterData.irms[1] = birms() / IRMS_CAL;
meterData.irms[2] = cirms() / IRMS_CAL;
if (setPotLine(PHASE_A, 20)) current.watt[0] = getWatt(PHASE_A);
if (setPotLine(PHASE_B, 20)) current.watt[1] = getWatt(PHASE_B);
if (setPotLine(PHASE_C, 20)) current.watt[2] = getWatt(PHASE_C);
if (setPotLine(PHASE_A, 20)) meterData.watt[0] = getWatt(PHASE_A);
if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B);
if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C);
if (memcmp(&previous, &current, sizeof(current)) != 0) {
if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) {
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
meter_data = current;
meter_data = meterData;
meter_watchdog_counter++;
xSemaphoreGive(meter_mutex);
meter_ade7758_post_event(&meterData);
}
previous = current;
previous = meterData;
}
vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
@@ -106,8 +110,6 @@ esp_err_t meter_ade7758_init(void) {
}
}
meter_ade7758_clear_internal_data();
esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Erro ao inicializar SPI (%d)", err);
@@ -126,8 +128,7 @@ esp_err_t meter_ade7758_init(void) {
esp_err_t meter_ade7758_start(void) {
if (meter_task) return ESP_ERR_INVALID_STATE;
meter_ade7758_clear_internal_data();
BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 5, &meter_task);
BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task);
return result == pdPASS ? ESP_OK : ESP_FAIL;
}
@@ -136,41 +137,4 @@ void meter_ade7758_stop(void) {
vTaskDelete(meter_task);
meter_task = NULL;
}
meter_ade7758_clear_internal_data();
}
bool meter_ade7758_is_running(void) {
return meter_task != NULL;
}
void meter_ade7758_clear_data(void) {
meter_ade7758_clear_internal_data();
}
// === Interface pública: acesso aos dados ===
float meter_ade7758_get_vrms_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[0] : 0; }
float meter_ade7758_get_vrms_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[1] : 0; }
float meter_ade7758_get_vrms_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[2] : 0; }
float meter_ade7758_get_irms_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[0] : 0; }
float meter_ade7758_get_irms_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[1] : 0; }
float meter_ade7758_get_irms_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[2] : 0; }
int meter_ade7758_get_watt_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[0] : 0; }
int meter_ade7758_get_watt_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[1] : 0; }
int meter_ade7758_get_watt_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[2] : 0; }
int meter_ade7758_get_var_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[0] : 0; }
int meter_ade7758_get_var_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[1] : 0; }
int meter_ade7758_get_var_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[2] : 0; }
int meter_ade7758_get_va_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[0] : 0; }
int meter_ade7758_get_va_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[1] : 0; }
int meter_ade7758_get_va_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[2] : 0; }
// === Diagnóstico ===
uint32_t meter_ade7758_get_watchdog_counter(void) {
return meter_watchdog_counter;
}

View File

@@ -23,47 +23,6 @@ esp_err_t meter_ade7758_start(void);
*/
void meter_ade7758_stop(void);
/**
* @brief Verifica se o medidor ADE7758 está em execução.
*
* @return true se a tarefa estiver ativa, false caso contrário.
*/
bool meter_ade7758_is_running(void);
/**
* @brief Limpa os dados armazenados no medidor ADE7758 (zera todos os valores).
*/
void meter_ade7758_clear_data(void);
// ----- Leituras por fase (L1, L2, L3) -----
// Tensão RMS (em volts)
float meter_ade7758_get_vrms_l1(void);
float meter_ade7758_get_vrms_l2(void);
float meter_ade7758_get_vrms_l3(void);
// Corrente RMS (em amperes)
float meter_ade7758_get_irms_l1(void);
float meter_ade7758_get_irms_l2(void);
float meter_ade7758_get_irms_l3(void);
// Potência ativa (W)
int meter_ade7758_get_watt_l1(void);
int meter_ade7758_get_watt_l2(void);
int meter_ade7758_get_watt_l3(void);
// Potência reativa (VAR)
int meter_ade7758_get_var_l1(void);
int meter_ade7758_get_var_l2(void);
int meter_ade7758_get_var_l3(void);
// Potência aparente (VA)
int meter_ade7758_get_va_l1(void);
int meter_ade7758_get_va_l2(void);
int meter_ade7758_get_va_l3(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_ade7758_get_watchdog_counter(void);
#ifdef __cplusplus
}

View File

@@ -1,325 +1,206 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
#include "meter_orno513.h"
#include "modbus_params.h" // for modbus parameters structures
#include "modbus_params.h"
#include "mbcontroller.h"
#include "sdkconfig.h"
#include "meter_events.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)
#define TAG "serial_mdb_orno513"
static const char *TAG = "serial_mdb";
#define MB_PORT_NUM 2
#define MB_DEV_SPEED 9600
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
#define UPDATE_INTERVAL (3000 / portTICK_PERIOD_MS)
#define POLL_INTERVAL (100 / portTICK_PERIOD_MS)
static bool enabled = false;
static bool meterState = false;
static bool meterTest = false;
static TaskHandle_t serial_mdb_task = NULL;
#define MB_PORT_NUM 2 //(CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
#define MB_DEV_SPEED 9600 //(CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
// The number of parameters that intended to be used in the particular control process
#define MASTER_MAX_CIDS num_device_parameters
// Number of reading of parameters from slave
#define MASTER_MAX_RETRY 30
// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (3000)
#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS)
// Timeout between polls
#define POLL_TIMEOUT_MS (500)
#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS)
// Timeout between errors
#define ERROR_TIMEOUT_MS (1000)
#define ERROR_TIMEOUT_TICS (ERROR_TIMEOUT_MS / portTICK_PERIOD_MS)
// The macro to get offset for parameter in the appropriate structure
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
// Discrete offset macro
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(x) ((const char *)(x))
#define OPTS(min, max, step) {.opt1 = min, .opt2 = max, .opt3 = step}
#define STR(fieldname) ((const char *)(fieldname))
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) \
{ \
.opt1 = min_val, .opt2 = max_val, .opt3 = step_val}
// State flag
static bool is_initialized = false;
static TaskHandle_t meter_task = NULL;
// Enumeration of modbus device addresses accessed by master device
enum
{
MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
// CID enums
enum {
CID_TOTAL_ACTIVE_ENERGY = 0,
CID_TOTAL_REACTIVE_ENERGY,
CID_ACTIVE_POWER,
CID_APPARENT_POWER,
CID_REACTIVE_POWER,
CID_L1_CURRENT,
CID_L1_VOLTAGE
};
// Enumeration of all supported CIDs for device (used in parameter definition table)
enum
{
CID_HOLD_DATA_0 = 0,
CID_HOLD_DATA_1 = 1,
CID_HOLD_DATA_2 = 2,
CID_HOLD_DATA_3 = 3,
CID_HOLD_DATA_4 = 4,
CID_HOLD_DATA_5 = 5,
CID_HOLD_DATA_6 = 6
// Register addresses
#define TOTALFACTIVE 0x010E
#define TOTALRACTIVE 0x0118
#define ACTIVEPOWER 0x0104
#define APPARENTPOWER 0x0106
#define REACTIVEPOWER 0x0108
#define L1CURRENT 0x0102
#define L1VOLTAGE 0x0100
const mb_parameter_descriptor_t device_parameters_orno513[] = {
{CID_TOTAL_ACTIVE_ENERGY, STR("Total Active Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALFACTIVE, 2,
HOLD_OFFSET(total_active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_TOTAL_REACTIVE_ENERGY, STR("Total Reactive Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALRACTIVE, 2,
HOLD_OFFSET(total_reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_ACTIVE_POWER, STR("Active Power"), STR("W"), 1, MB_PARAM_HOLDING, ACTIVEPOWER, 2,
HOLD_OFFSET(active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_APPARENT_POWER, STR("Apparent Power"), STR("VA"), 1, MB_PARAM_HOLDING, APPARENTPOWER, 2,
HOLD_OFFSET(apparent_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_REACTIVE_POWER, STR("Reactive Power"), STR("VAR"), 1, MB_PARAM_HOLDING, REACTIVEPOWER, 2,
HOLD_OFFSET(reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(l1_current), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100, 0.1), PAR_PERMS_READ},
{CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2,
HOLD_OFFSET(l1_voltage), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ}
};
#define SN 0x1000
#define METERID 0x1003
#define FW 0x1004
const uint16_t num_device_parameters_orno513 = sizeof(device_parameters_orno513) / sizeof(device_parameters_orno513[0]);
#define L1VOLTAGE 0x0100
#define L1CURRENT 0x0102
#define ACTIVEPOWER 0x0104
#define APPARENTPOWER 0x0106
#define REACTIVEPOWER 0x0108
#define TOTALFACTIVE 0x010E
#define TOTALRACTIVE 0x0118
// Example Data (Object) Dictionary for Modbus parameters:
const mb_parameter_descriptor_t device_parameters[] = {
{CID_HOLD_DATA_0, STR("TOTALFACTIVE"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TOTALFACTIVE, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_1, STR("TOTALRACTIVE"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TOTALRACTIVE, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_2, STR("ACTIVEPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, ACTIVEPOWER, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_3, STR("APPARENTPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, APPARENTPOWER, 2,
HOLD_OFFSET(holding_data3), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_4, STR("REACTIVEPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, REACTIVEPOWER, 2,
HOLD_OFFSET(holding_data4), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_5, STR("L1CURRENT"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(holding_data5), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_HOLD_DATA_6, STR("L1VOLTAGE"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2,
HOLD_OFFSET(holding_data6), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}
};
// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
// Function to get pointer to parameter storage (instance) according to parameter description table
static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor)
{
assert(param_descriptor != NULL);
void *instance_ptr = NULL;
if (param_descriptor->param_offset != 0)
{
switch (param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
break;
}
}
else
{
ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid);
assert(instance_ptr != NULL);
}
return instance_ptr;
static void *get_param_ptr(const mb_parameter_descriptor_t *param) {
if (!param || param->param_offset == 0) return NULL;
return ((uint8_t *)&holding_reg_params + param->param_offset - 1);
}
// Float - Mid-Little Endian (CDAB)
float ReverseFloat(const float inFloat)
{
float retVal;
char *floatToConvert = (char *)&inFloat;
char *returnFloat = (char *)&retVal;
static void serial_mdb_task(void *param) {
esp_err_t err;
const mb_parameter_descriptor_t *desc = NULL;
// swap the bytes into a temporary buffer
returnFloat[0] = floatToConvert[2];
returnFloat[1] = floatToConvert[3];
returnFloat[2] = floatToConvert[0];
returnFloat[3] = floatToConvert[1];
float voltage[3] = {0};
float current[3] = {0};
int watt[3] = {0};
float energy = 0.0f;
return retVal;
}
while (1) {
for (uint16_t cid = 0; cid < num_device_parameters_orno513; cid++) {
err = mbc_master_get_cid_info(cid, &desc);
if (err != ESP_OK || !desc) continue;
static void serial_mdb_task_func(void *param)
{
ESP_LOGI(TAG, "serial_mdb_task_func");
esp_err_t err = ESP_OK;
void *data_ptr = get_param_ptr(desc);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type);
float l1current = 0;
int error_count = 0;
if (err == ESP_OK && data_ptr) {
int32_t raw = *(int32_t *)data_ptr;
float val = raw / 10.0f;
ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units);
bool alarm_state = false;
const mb_parameter_descriptor_t *param_descriptor = NULL;
ESP_LOGI(TAG, "Start modbus...");
while (true)
{
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
// Get data from parameters description table
err = mbc_master_get_cid_info(cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL))
{
void *temp_data_ptr = master_get_param_data(param_descriptor);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)param_descriptor->param_key,
(uint8_t *)temp_data_ptr, &type);
if (err == ESP_OK)
{
error_count = 0;
meterState = true;
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT))
{
int value = *(int *)temp_data_ptr;
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %d (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
value,
*(uint32_t *)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min)))
{
alarm_state = true;
break;
}
}
switch (cid) {
case CID_L1_VOLTAGE: voltage[0] = val; break;
case CID_L1_CURRENT: current[0] = val; break;
case CID_ACTIVE_POWER:
watt[0] = (int)(val);
watt[1] = watt[0];
watt[2] = watt[0];
break;
case CID_TOTAL_ACTIVE_ENERGY:
energy = val / 1000.0f;
break;
default:
break;
}
else
{
if (error_count > 3 && !meterTest)
{
meterState = false;
vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls
}
else
{
error_count++;
}
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char *)esp_err_to_name(err));
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
} else {
ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err));
}
vTaskDelay(POLL_INTERVAL);
}
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS);
}
meter_event_data_t evt = {
.frequency = 0.0f,
.power_factor = 0.0f,
.total_energy = energy,
.source = "GRID"
};
if (alarm_state)
{
ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
memcpy(evt.vrms, voltage, sizeof(evt.vrms));
memcpy(evt.irms, current, sizeof(evt.irms));
memcpy(evt.watt, watt, sizeof(evt.watt));
esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10));
vTaskDelay(UPDATE_INTERVAL);
}
else
{
ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY);
}
ESP_LOGI(TAG, "Destroy master...");
ESP_ERROR_CHECK(mbc_master_destroy());
}
// Modbus master initialization
static esp_err_t master_init(void)
{
esp_err_t meter_orno513_init(void) {
if (is_initialized) {
ESP_LOGW(TAG, "meter_orno513 already initialized");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "meter_orno513_init");
mb_communication_info_t comm = {
.port = MB_PORT_NUM,
.mode = MB_MODE_RTU,
.baudrate = MB_DEV_SPEED,
.parity = UART_PARITY_DISABLE};
void *master_handler = NULL;
.parity = UART_PARITY_DISABLE
};
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
ESP_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).", (int)err);
err = mbc_master_setup((void *)&comm);
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller setup fail, returns(0x%x).", (int)err);
void *handler = NULL;
err = uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD,
MB_UART_RTS, UART_PIN_NO_CHANGE);
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
err = mbc_master_start();
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returned (0x%x).", (int)err);
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);
vTaskDelay(5);
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller set descriptor fail, returns(0x%x).", (int)err);
ESP_LOGI(TAG, "Modbus master stack initialized...");
return err;
}
// Function to start the meter
esp_err_t meter_orno513_start(void)
{
ESP_LOGI(TAG, "Starting MDB Serial");
// Call the initialization function directly
esp_err_t err = master_init(); // Don't wrap this in ESP_ERROR_CHECK
ESP_ERROR_CHECK(err); // Check if there was an error during initialization
// Create the task for reading Modbus data
xTaskCreate(serial_mdb_task_func, "serial_mdb_task", 4 * 1024, NULL, 5, &serial_mdb_task);
return err;
}
// Function to stop the meter
void meter_orno513_stop(void)
{
ESP_LOGI(TAG, "Stopping");
if (serial_mdb_task)
{
vTaskDelete(serial_mdb_task);
serial_mdb_task = NULL;
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &handler);
if (err != ESP_OK) {
ESP_LOGE(TAG, "mbc_master_init failed");
return err;
}
uart_driver_delete(MB_PORT_NUM);
ESP_ERROR_CHECK(mbc_master_setup(&comm));
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(mbc_master_start());
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
vTaskDelay(pdMS_TO_TICKS(5));
ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno513, num_device_parameters_orno513));
is_initialized = true;
return ESP_OK;
}
esp_err_t meter_orno513_start(void) {
ESP_LOGI(TAG, "meter_orno513_start");
if (!is_initialized) {
ESP_LOGE(TAG, "meter_orno513 not initialized");
return ESP_ERR_INVALID_STATE;
}
if (meter_task == NULL) {
xTaskCreate(serial_mdb_task, "meter_orno513_task", 4096, NULL, 3, &meter_task);
ESP_LOGI(TAG, "meter_orno513 task started");
}
return ESP_OK;
}
void meter_orno513_stop(void) {
if (!is_initialized) {
ESP_LOGW(TAG, "meter_orno513 not initialized");
return;
}
ESP_LOGI(TAG, "Stopping meter_orno513");
uart_driver_delete(MB_PORT_NUM);
esp_err_t err = mbc_master_destroy();
if (err != ESP_OK) {
ESP_LOGW(TAG, "mbc_master_destroy() returned %s", esp_err_to_name(err));
}
is_initialized = false;
}

View File

@@ -23,37 +23,6 @@ esp_err_t meter_orno513_start(void);
*/
void meter_orno513_stop(void);
/**
* @brief Verifica se o medidor ORNO 513 está em execução.
*
* @return true se a tarefa estiver ativa, false caso contrário.
*/
bool meter_orno513_is_running(void);
/**
* @brief Limpa os dados armazenados no medidor ORNO 513 (zera todos os valores).
*/
void meter_orno513_clear_data(void);
// ----- Leituras por fase (L1) -----
// Tensão RMS (em volts)
float meter_orno513_get_vrms_l1(void);
// Corrente RMS (em amperes)
float meter_orno513_get_irms_l1(void);
// Potência ativa (W)
int meter_orno513_get_watt_l1(void);
// Potência reativa (VAR)
int meter_orno513_get_var_l1(void);
// Potência aparente (VA)
int meter_orno513_get_va_l1(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_orno513_get_watchdog_counter(void);
#ifdef __cplusplus
}

View File

@@ -1,383 +1,217 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
#include "meter_orno516.h"
#include "modbus_params.h" // for modbus parameters structures
#include "meter_events.h"
#include "modbus_params.h"
#include "mbcontroller.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)
static const char *TAG = "serial_mdb_orno516";
static bool enabled = false;
static bool meterState = false;
static bool meterTest = false;
static TaskHandle_t serial_mdb_task = NULL;
#define MB_PORT_NUM 2 //(CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
#define MB_DEV_SPEED 9600 //(CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
// #define MB_PARITY_EVEN
#define TAG "serial_mdb_orno516"
#define MB_PORT_NUM 2
#define MB_DEV_SPEED 9600
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
// Note: Some pins on target chip cannot be assigned for UART communication.
// See UART documentation for selected board and target to configure pins using Kconfig.
#define UPDATE_INTERVAL (5000 / portTICK_PERIOD_MS)
#define POLL_INTERVAL (100 / portTICK_PERIOD_MS)
// The number of parameters that intended to be used in the particular control process
#define MASTER_MAX_CIDS num_device_parameters_orno516
// Number of reading of parameters from slave
#define MASTER_MAX_RETRY 30
// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (5000)
#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS)
// Timeout between polls
#define POLL_TIMEOUT_MS (1)
#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS)
// Timeout between erros
#define ERROR_TIMEOUT_MS (30000)
#define ERROR_TIMEOUT_TICS (ERROR_TIMEOUT_MS / portTICK_PERIOD_MS)
// The macro to get offset for parameter in the appropriate structure
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
// Discrete offset macro
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(fieldname) ((const char *)(fieldname))
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) \
{ \
.opt1 = min_val, .opt2 = max_val, .opt3 = step_val}
#define OPTS(min_val, max_val, step_val) {.opt1 = min_val, .opt2 = max_val, .opt3 = step_val}
// Enumeration of modbus device addresses accessed by master device
enum
{
MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
// Estado do driver
static bool is_initialized = false;
static TaskHandle_t meter_task = NULL;
#define L1VOLTAGE 0x000E
#define L2VOLTAGE 0x0010
#define L3VOLTAGE 0x0012
#define L1CURRENT 0x0016
#define L2CURRENT 0x0018
#define L3CURRENT 0x001A
#define TOTALACTIVEPOWER 0x001C
enum {
CID_L1_CURRENT = 0,
CID_L2_CURRENT,
CID_L3_CURRENT,
CID_L1_VOLTAGE,
CID_L2_VOLTAGE,
CID_L3_VOLTAGE,
CID_TOTAL_ACTIVE_POWER
};
// Enumeration of all supported CIDs for device (used in parameter definition table)
enum
{
CID_HOLD_DATA_0 = 0,
CID_HOLD_DATA_1 = 1,
CID_HOLD_DATA_2 = 2,
CID_HOLD_DATA_3 = 3,
CID_HOLD_DATA_4 = 4,
CID_HOLD_DATA_5 = 5,
CID_HOLD_DATA_6 = 6
};
#define SN 0x01
#define METERID 0x02
#define L1VOLTAGE 0x000E
#define L2VOLTAGE 0x0010
#define L3VOLTAGE 0x0012
#define L1CURRENT 0x0016
#define L2CURRENT 0x0018
#define L3CURRENT 0x001A
#define TOTALACTIVEPOWER 0x001C
// Example Data (Object) Dictionary for Modbus parameters:
// The CID field in the table must be unique.
// Modbus Slave Addr field defines slave address of the device with correspond parameter.
// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
// Data Type, Data Size specify type of the characteristic and its data size.
// Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters_orno516[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{CID_HOLD_DATA_0, STR("L1"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_HOLD_DATA_1, STR("L2"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L2CURRENT, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_HOLD_DATA_2, STR("L3"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L3CURRENT, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}
{CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(l1_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L2_CURRENT, STR("L2 Current"), STR("A"), 1, MB_PARAM_HOLDING, L2CURRENT, 2,
HOLD_OFFSET(l2_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L3_CURRENT, STR("L3 Current"), STR("A"), 1, MB_PARAM_HOLDING, L3CURRENT, 2,
HOLD_OFFSET(l3_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2,
HOLD_OFFSET(l1_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_L2_VOLTAGE, STR("L2 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L2VOLTAGE, 2,
HOLD_OFFSET(l2_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_L3_VOLTAGE, STR("L3 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L3VOLTAGE, 2,
HOLD_OFFSET(l3_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_TOTAL_ACTIVE_POWER, STR("Total Active Power"), STR("W"), 1, MB_PARAM_HOLDING, TOTALACTIVEPOWER, 2,
HOLD_OFFSET(total_active_power), PARAM_TYPE_FLOAT, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}
};
// Calculate number of parameters in the table
const uint16_t num_device_parameters_orno516 = (sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]));
const uint16_t num_device_parameters_orno516 = sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]);
// The function to get pointer to parameter storage (instance) according to parameter description table
static void *master_get_param_data_orno516(const mb_parameter_descriptor_t *param_descriptor)
{
assert(param_descriptor != NULL);
void *instance_ptr = NULL;
if (param_descriptor->param_offset != 0)
{
switch (param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
break;
}
}
else
{
ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid);
assert(instance_ptr != NULL);
}
return instance_ptr;
}
// Float - Mid-Little Endian (CDAB)
float ReverseFloat_orno516(const float inFloat)
{
float ReverseFloat(const float inFloat) {
float retVal;
char *floatToConvert = (char *)&inFloat;
char *returnFloat = (char *)&retVal;
// swap the bytes into a temporary buffer
returnFloat[0] = floatToConvert[2];
returnFloat[1] = floatToConvert[3];
returnFloat[2] = floatToConvert[0];
returnFloat[3] = floatToConvert[1];
return retVal;
}
static void serial_mdb_task_func_orno516(void *param)
{
ESP_LOGI(TAG, "serial_mdb_task_func_orno516");
esp_err_t err = ESP_OK;
float maxcurrent = 0;
float l1current = 0;
float l2current = 0;
float l3current = 0;
int error_count = 0;
bool alarm_state = false;
const mb_parameter_descriptor_t *param_descriptor = NULL;
ESP_LOGI(TAG, "Start modbus...");
while (true)
{
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
// Get data from parameters description table
// and use this information to fill the characteristics description table
// and having all required fields in just one table
err = mbc_master_get_cid_info(cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL))
{
void *temp_data_ptr = master_get_param_data_orno516(param_descriptor);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)param_descriptor->param_key,
(uint8_t *)temp_data_ptr, &type);
if (err == ESP_OK)
{
error_count = 0;
meterState = true;
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT))
{
float value = *(float *)temp_data_ptr;
value = ReverseFloat_orno516(value);
switch (cid)
{
case 0:
maxcurrent = 0;
l1current = 0;
l2current = 0;
l3current = 0;
l1current = value;
break;
case 1:
l2current = value;
break;
case 2:
l3current = value;
break;
default:
// code block
}
ESP_LOGD(TAG, "Characteristic #%u %s (%s) value = %f (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
value,
*(uint32_t *)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min)))
{
alarm_state = true;
break;
}
}
}
else
{
if (error_count > 3 && !meterTest)
{
meterState = false;
vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls
}
else
{
error_count++;
}
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char *)esp_err_to_name(err));
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
}
}
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS);
}
if (alarm_state)
{
ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
}
else
{
ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY);
}
ESP_LOGI(TAG, "Destroy master...");
ESP_ERROR_CHECK(mbc_master_destroy());
static void *get_param_ptr(const mb_parameter_descriptor_t *param) {
if (!param || param->param_offset == 0) return NULL;
return ((uint8_t *)&holding_reg_params + param->param_offset - 1);
}
// Modbus master initialization
static esp_err_t master_init_orno516(void)
{
// Initialize and start Modbus controller
static void meter_orno516_post_event(float *voltage, float *current, int *power) {
meter_event_data_t evt = {
.source = "GRID",
.frequency = 0.0f, // ORNO-516 não fornece
.power_factor = 0.0f, // idem
.total_energy = 0.0f // idem
};
memcpy(evt.vrms, voltage, sizeof(evt.vrms));
memcpy(evt.irms, current, sizeof(evt.irms));
memcpy(evt.watt, power, sizeof(evt.watt));
esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY,
&evt, sizeof(evt), pdMS_TO_TICKS(10));
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
}
static void serial_mdb_task(void *param) {
esp_err_t err;
const mb_parameter_descriptor_t *desc = NULL;
float voltage[3] = {0}, current[3] = {0};
int power[3] = {0};
while (1) {
for (uint16_t cid = 0; cid < num_device_parameters_orno516; cid++) {
err = mbc_master_get_cid_info(cid, &desc);
if (err != ESP_OK || !desc) continue;
void *data_ptr = get_param_ptr(desc);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type);
if (err == ESP_OK && data_ptr) {
float val = ReverseFloat(*(float *)data_ptr);
ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units);
switch (cid) {
case CID_L1_VOLTAGE: voltage[0] = val; break;
case CID_L2_VOLTAGE: voltage[1] = val; break;
case CID_L3_VOLTAGE: voltage[2] = val; break;
case CID_L1_CURRENT: current[0] = val; break;
case CID_L2_CURRENT: current[1] = val; break;
case CID_L3_CURRENT: current[2] = val; break;
case CID_TOTAL_ACTIVE_POWER:
power[0] = (int)(val / 3);
power[1] = (int)(val / 3);
power[2] = (int)(val / 3);
break;
default:
break;
}
} else {
ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err));
}
vTaskDelay(POLL_INTERVAL);
}
meter_orno516_post_event(voltage, current, power);
vTaskDelay(UPDATE_INTERVAL);
}
}
esp_err_t meter_orno516_init(void) {
if (is_initialized) {
ESP_LOGW(TAG, "Already initialized");
return ESP_ERR_INVALID_STATE;
}
// Tenta apagar UART apenas se estiver inicializada
if (uart_is_driver_installed(MB_PORT_NUM)) {
uart_driver_delete(MB_PORT_NUM);
ESP_LOGI(TAG, "UART driver deleted");
}
mbc_master_destroy(); // OK mesmo que não esteja inicializado
mb_communication_info_t comm = {
//.slave_addr = 1,
.port = MB_PORT_NUM,
.mode = MB_MODE_RTU,
.baudrate = MB_DEV_SPEED,
.parity = UART_PARITY_EVEN};
void *master_handler = NULL;
.parity = UART_PARITY_EVEN
};
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).", (int)err);
err = mbc_master_setup((void *)&comm);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller setup fail, returns(0x%x).", (int)err);
void *handler = NULL;
ESP_ERROR_CHECK(mbc_master_init(MB_PORT_SERIAL_MASTER, &handler));
ESP_ERROR_CHECK(mbc_master_setup(&comm));
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(mbc_master_start());
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
vTaskDelay(pdMS_TO_TICKS(5));
ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno516, num_device_parameters_orno516));
// Set UART pin numbers
err = uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD,
MB_UART_RTS, UART_PIN_NO_CHANGE);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
err = mbc_master_start();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returned (0x%x).", (int)err);
// Set driver mode to Half Duplex
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);
vTaskDelay(5);
err = mbc_master_set_descriptor(&device_parameters_orno516[0], num_device_parameters_orno516);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller set descriptor fail, returns(0x%x).", (int)err);
ESP_LOGI(TAG, "Modbus master stack initialized...");
return err;
is_initialized = true;
return ESP_OK;
}
/**
* @brief Set meter model
*
*/
void serial_mdb_set_model_orno516(bool _enabled)
{
enabled = _enabled;
}
/**
* @brief Set meter state
*
*/
bool serial_mdb_get_meter_state_orno516()
{
return meterState;
}
/**
* @brief Set meter test state
*
*/
void serial_mdb_set_meter_test_orno516(bool _meterTest)
{
meterTest = _meterTest;
}
esp_err_t serial_mdb_start_orno516()
{
ESP_LOGI(TAG, "Starting MDB Serial");
// Call the initialization function and check for errors
esp_err_t err = master_init_orno516();
ESP_ERROR_CHECK(err); // Check if there was an error during initialization
// Create the task to handle the MDB serial communication
xTaskCreate(serial_mdb_task_func_orno516, "serial_mdb_task_orno516", 4 * 1024, NULL, 5, &serial_mdb_task);
return err;
}
void serial_mdb_stop_orno516(void)
{
ESP_LOGI(TAG, "Stopping");
if (serial_mdb_task)
{
vTaskDelete(serial_mdb_task);
serial_mdb_task = NULL;
esp_err_t meter_orno516_start(void) {
if (!is_initialized) {
ESP_LOGE(TAG, "Not initialized");
return ESP_ERR_INVALID_STATE;
}
uart_driver_delete(MB_PORT_NUM);
if (meter_task == NULL) {
xTaskCreate(serial_mdb_task, "meter_orno516_task", 4096, NULL, 3, &meter_task);
ESP_LOGI(TAG, "Task started");
}
return ESP_OK;
}
void meter_orno516_stop(void) {
if (!is_initialized) {
ESP_LOGW(TAG, "Not initialized, skipping stop");
return;
}
if (meter_task) {
vTaskDelete(meter_task);
meter_task = NULL;
ESP_LOGI(TAG, "Task stopped");
}
mbc_master_destroy();
if (uart_is_driver_installed(MB_PORT_NUM)) {
uart_driver_delete(MB_PORT_NUM);
ESP_LOGI(TAG, "UART driver deleted");
}
is_initialized = false;
ESP_LOGI(TAG, "Meter ORNO-516 cleaned up");
}

View File

@@ -10,61 +10,20 @@
*
* @return esp_err_t Retorna ESP_OK se a inicialização for bem-sucedida, caso contrário retorna um erro.
*/
esp_err_t meter_init_orno516(void);
esp_err_t meter_orno516_init(void);
/**
* @brief Inicia a tarefa de leitura de dados do medidor ORNO 516.
*
* @return esp_err_t Retorna ESP_OK se a tarefa for iniciada com sucesso, caso contrário retorna um erro.
*/
esp_err_t meter_start_orno516(void);
esp_err_t meter_orno516_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 516.
*/
void meter_stop_orno516(void);
void meter_orno516_stop(void);
/**
* @brief Verifica se o medidor ORNO 516 está em execução.
*
* @return true Se a tarefa estiver ativa, false caso contrário.
*/
bool meter_is_running_orno516(void);
/**
* @brief Limpa os dados armazenados no medidor ORNO 516 (zera todos os valores).
*/
void meter_clear_data_orno516(void);
// ----- Leituras por fase (L1, L2, L3) -----
// Tensão RMS (em volts)
float meter_get_vrms_l1_orno516(void);
float meter_get_vrms_l2_orno516(void);
float meter_get_vrms_l3_orno516(void);
// Corrente RMS (em amperes)
float meter_get_irms_l1_orno516(void);
float meter_get_irms_l2_orno516(void);
float meter_get_irms_l3_orno516(void);
// Potência ativa (W)
int meter_get_watt_l1_orno516(void);
int meter_get_watt_l2_orno516(void);
int meter_get_watt_l3_orno516(void);
// Potência reativa (VAR)
int meter_get_var_l1_orno516(void);
int meter_get_var_l2_orno516(void);
int meter_get_var_l3_orno516(void);
// Potência aparente (VA)
int meter_get_va_l1_orno516(void);
int meter_get_va_l2_orno516(void);
int meter_get_va_l3_orno516(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_get_watchdog_counter_orno516(void);
#ifdef __cplusplus
}

View File

@@ -4,83 +4,72 @@
* SPDX-License-Identifier: Apache-2.0
*/
/*=====================================================================================
* Description:
* The Modbus parameter structures used to define Modbus instances that
* can be addressed by Modbus protocol. Define these structures per your needs in
* your application. Below is just an example of possible parameters.
*====================================================================================*/
#ifndef _DEVICE_PARAMS
#define _DEVICE_PARAMS
#include <stdint.h>
// This file defines structure of modbus parameters which reflect correspond modbus address space
// for each modbus register type (coils, discreet inputs, holding registers, input registers)
#pragma pack(push, 1)
typedef struct
{
uint8_t discrete_input0:1;
uint8_t discrete_input1:1;
uint8_t discrete_input2:1;
uint8_t discrete_input3:1;
uint8_t discrete_input4:1;
uint8_t discrete_input5:1;
uint8_t discrete_input6:1;
uint8_t discrete_input7:1;
// Discrete Inputs
typedef struct {
uint8_t discrete_input0 : 1;
uint8_t discrete_input1 : 1;
uint8_t discrete_input2 : 1;
uint8_t discrete_input3 : 1;
uint8_t discrete_input4 : 1;
uint8_t discrete_input5 : 1;
uint8_t discrete_input6 : 1;
uint8_t discrete_input7 : 1;
uint8_t discrete_input_port1;
uint8_t discrete_input_port2;
} discrete_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
// Coils
typedef struct {
uint8_t coils_port0;
uint8_t coils_port1;
uint8_t coils_port2;
} coil_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
float input_data0; // 0
float input_data1; // 2
float input_data2; // 4
float input_data3; // 6
uint16_t data[150]; // 8 + 150 = 158
float input_data4; // 158
// Input Registers (pode manter caso use em outro driver)
typedef struct {
float input_data0;
float input_data1;
float input_data2;
float input_data3;
uint16_t data[150];
float input_data4;
float input_data5;
float input_data6;
float input_data7;
uint16_t data_block1[150];
} input_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
uint32_t holding_data0;
uint32_t holding_data1;
uint32_t holding_data2;
uint32_t holding_data3;
uint32_t holding_data4;
uint32_t holding_data5;
uint32_t holding_data6;
uint32_t holding_data7;
uint32_t holding_data8;
uint32_t holding_data9;
uint32_t holding_data10;
uint32_t holding_data11;
uint32_t holding_data12;
uint32_t holding_data13;
// Holding Registers (ajustado para os campos usados no ORNO 516)
typedef struct {
float l1_current; // 0x0016
float l2_current; // 0x0018
float l3_current; // 0x001A
float l1_voltage; // 0x000E
float l2_voltage; // 0x0010
float l3_voltage; // 0x0012
float total_active_power; // 0x001C
float total_reactive_power;
float active_power;
float apparent_power;
float reactive_power;
} holding_reg_params_t;
#pragma pack(pop)
// Instâncias globais das estruturas
extern holding_reg_params_t holding_reg_params;
extern input_reg_params_t input_reg_params;
extern coil_reg_params_t coil_reg_params;
extern discrete_reg_params_t discrete_reg_params;
#endif // !defined(_DEVICE_PARAMS)
#endif // !_DEVICE_PARAMS

View File

@@ -7,6 +7,7 @@
#include "esp_system.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "meter_events.h"
#define TAG "meter_zigbee"
@@ -21,19 +22,15 @@
#define ATTR_CURRENT_L1 0x0006
#define ATTR_CURRENT_L2 0x0007
#define ATTR_CURRENT_L3 0x0008
#define ATTR_VOLTAGE_L1 0x0266
#define ATTR_CURRENT_L1_ALT 0x0267
#define ATTR_POWER_L1 0x0268
#define ATTR_VOLTAGE_L2 0x0269
#define ATTR_CURRENT_L2_ALT 0x026A
#define ATTR_POWER_L2 0x026B
#define ATTR_VOLTAGE_L3 0x026C
#define ATTR_CURRENT_L3_ALT 0x026D
#define ATTR_POWER_L3 0x026E
#define ATTR_FREQUENCY 0x0265
#define ATTR_POWER_FACTOR 0x020F
#define ATTR_TOTAL_ENERGY 0x0201
@@ -55,100 +52,114 @@ typedef struct {
float total_energy;
} meter_zigbee_data_t;
static bool phase_updated[PHASE_COUNT] = {false, false, false};
static meter_zigbee_data_t meter_data = {0};
static SemaphoreHandle_t meter_mutex = NULL;
static TaskHandle_t meter_task = NULL;
static TaskHandle_t meter_zigbee_task = NULL;
// ---------- Utils ----------
static void meter_zigbee_post_event(void) {
meter_event_data_t evt = {
.source = "GRID",
.frequency = meter_data.frequency,
.power_factor = meter_data.power_factor,
.total_energy = meter_data.total_energy
};
static inline float decode_float(const uint8_t *buf) {
return (buf[9] + (buf[8] << 8) + (buf[7] << 16)) / 100.0f;
}
memcpy(evt.vrms, meter_data.vrms, sizeof(evt.vrms));
memcpy(evt.irms, meter_data.irms, sizeof(evt.irms));
memcpy(evt.watt, meter_data.watt, sizeof(evt.watt));
static float meter_data_get_float(const float *arr, uint8_t phase) {
float val = 0.0f;
if (phase >= PHASE_COUNT) return 0;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
val = arr[phase];
xSemaphoreGive(meter_mutex);
}
return val;
}
esp_err_t err = esp_event_post(METER_EVENT,
METER_EVENT_DATA_READY,
&evt,
sizeof(evt),
pdMS_TO_TICKS(10));
static int meter_data_get_int(const int *arr, uint8_t phase) {
int val = 0;
if (phase >= PHASE_COUNT) return 0;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
val = arr[phase];
xSemaphoreGive(meter_mutex);
}
return val;
}
static void meter_data_clear(void) {
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
memset(&meter_data, 0, sizeof(meter_data));
xSemaphoreGive(meter_mutex);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
}
// ---------- Frame Handler ----------
static void handle_zigbee_frame(const uint8_t *buf) {
uint16_t attr = buf[1] | (buf[2] << 8);
uint8_t size = buf[4];
static void handle_zigbee_frame(const uint8_t *buf, size_t len) {
ESP_LOGI(TAG, "Received UART frame (%d bytes):", len);
ESP_LOG_BUFFER_HEX(TAG, buf, len);
if (size != 8) {
ESP_LOGW(TAG, "Unexpected data size: %d", size);
if (len < RX_FRAME_SIZE) {
ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len);
return;
}
float value = decode_float(buf);
ESP_LOGI(TAG, "Attr 0x%04X = %.2f", attr, value);
uint16_t attr = buf[2] | (buf[3] << 8);
uint8_t size = buf[5];
if (size != 8) {
ESP_LOGW(TAG, "Unsupported payload size: %d", size);
return;
}
uint16_t volt_raw = (buf[6] << 8) | buf[7];
uint32_t current_raw = (buf[8] << 16) | (buf[9] << 8) | buf[10];
uint32_t power_raw = (buf[11] << 16) | (buf[12] << 8) | buf[13];
float volt = volt_raw / 10.0f;
float current = current_raw / 100.0f;
float power = power_raw / 1000.0f;
ESP_LOGI(TAG, "Parsed Attr 0x%04X: V=%.1fV I=%.2fA P=%.1fW", attr, volt, current, power);
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
switch (attr) {
case ATTR_CURRENT_L1:
case ATTR_CURRENT_L1_ALT:
meter_data.irms[0] = value;
meter_data.irms[PHASE_L1] = current;
meter_data.vrms[PHASE_L1] = volt;
meter_data.watt[PHASE_L1] = (int)power;
phase_updated[PHASE_L1] = true;
break;
case ATTR_CURRENT_L2:
case ATTR_CURRENT_L2_ALT:
meter_data.irms[1] = value;
meter_data.irms[PHASE_L2] = current;
meter_data.vrms[PHASE_L2] = volt;
meter_data.watt[PHASE_L2] = (int)power;
phase_updated[PHASE_L2] = true;
break;
case ATTR_CURRENT_L3:
case ATTR_CURRENT_L3_ALT:
meter_data.irms[2] = value;
meter_data.irms[PHASE_L3] = current;
meter_data.vrms[PHASE_L3] = volt;
meter_data.watt[PHASE_L3] = (int)power;
phase_updated[PHASE_L3] = true;
break;
case ATTR_POWER_FACTOR:
meter_data.power_factor = current;
break;
case ATTR_FREQUENCY:
meter_data.frequency = current;
break;
case ATTR_TOTAL_ENERGY:
meter_data.total_energy = current;
break;
case ATTR_VOLTAGE_L1: meter_data.vrms[0] = value; break;
case ATTR_VOLTAGE_L2: meter_data.vrms[1] = value; break;
case ATTR_VOLTAGE_L3: meter_data.vrms[2] = value; break;
case ATTR_POWER_L1: meter_data.watt[0] = (int)value; break;
case ATTR_POWER_L2: meter_data.watt[1] = (int)value; break;
case ATTR_POWER_L3: meter_data.watt[2] = (int)value; break;
case ATTR_POWER_FACTOR: meter_data.power_factor = value; break;
case ATTR_FREQUENCY: meter_data.frequency = value; break;
case ATTR_TOTAL_ENERGY: meter_data.total_energy = value; break;
default:
ESP_LOGW(TAG, "Unknown attr: 0x%04X", attr);
break;
}
xSemaphoreGive(meter_mutex);
}
// Verifica se todas as 3 fases foram atualizadas
if (phase_updated[PHASE_L1] && phase_updated[PHASE_L2] && phase_updated[PHASE_L3]) {
meter_zigbee_post_event();
memset(phase_updated, 0, sizeof(phase_updated));
}
}
// ---------- Task ----------
static void meter_task_func(void *param) {
static void meter_zigbee_task_func(void *param) {
uint8_t *buf = malloc(RX_FRAME_SIZE);
if (!buf) {
ESP_LOGE(TAG, "Memory allocation failed");
ESP_LOGE(TAG, "Failed to allocate buffer");
vTaskDelete(NULL);
return;
}
@@ -156,9 +167,13 @@ static void meter_task_func(void *param) {
ESP_LOGI(TAG, "Zigbee meter task started");
while (1) {
int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(1000));
if (len >= 10) {
handle_zigbee_frame(buf);
int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(5000));
if (len == RX_FRAME_SIZE) {
handle_zigbee_frame(buf, len);
} else if (len == 0) {
ESP_LOGD(TAG, "UART timeout with no data");
} else {
ESP_LOGW(TAG, "Incomplete frame received (%d bytes)", len);
}
}
@@ -166,9 +181,7 @@ static void meter_task_func(void *param) {
vTaskDelete(NULL);
}
// ---------- Public API (meter.h) ----------
esp_err_t meter_init(void) {
esp_err_t meter_zigbee_init(void) {
ESP_LOGI(TAG, "Initializing Zigbee meter");
if (!meter_mutex) {
@@ -176,8 +189,6 @@ esp_err_t meter_init(void) {
if (!meter_mutex) return ESP_ERR_NO_MEM;
}
meter_data_clear();
uart_config_t config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
@@ -194,17 +205,17 @@ esp_err_t meter_init(void) {
return ESP_OK;
}
esp_err_t meter_start(void) {
if (meter_task) return ESP_ERR_INVALID_STATE;
esp_err_t meter_zigbee_start(void) {
if (meter_zigbee_task) return ESP_ERR_INVALID_STATE;
xTaskCreate(meter_task_func, "meter_zigbee_task", 4096, NULL, 5, &meter_task);
xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 3, &meter_zigbee_task);
return ESP_OK;
}
void meter_stop(void) {
if (meter_task) {
vTaskDelete(meter_task);
meter_task = NULL;
void meter_zigbee_stop(void) {
if (meter_zigbee_task) {
vTaskDelete(meter_zigbee_task);
meter_zigbee_task = NULL;
}
uart_driver_delete(UART_PORT);
@@ -215,63 +226,6 @@ void meter_stop(void) {
}
}
bool meter_is_running(void) {
return meter_task != NULL;
}
void meter_clear_data(void) {
meter_data_clear();
}
// ---------- RMS Current ----------
float meter_get_irms_l1(void) { return meter_data_get_float(meter_data.irms, PHASE_L1); }
float meter_get_irms_l2(void) { return meter_data_get_float(meter_data.irms, PHASE_L2); }
float meter_get_irms_l3(void) { return meter_data_get_float(meter_data.irms, PHASE_L3); }
// ---------- RMS Voltage ----------
float meter_get_vrms_l1(void) { return meter_data_get_float(meter_data.vrms, PHASE_L1); }
float meter_get_vrms_l2(void) { return meter_data_get_float(meter_data.vrms, PHASE_L2); }
float meter_get_vrms_l3(void) { return meter_data_get_float(meter_data.vrms, PHASE_L3); }
// ---------- Active Power ----------
int meter_get_watt_l1(void) { return meter_data_get_int(meter_data.watt, PHASE_L1); }
int meter_get_watt_l2(void) { return meter_data_get_int(meter_data.watt, PHASE_L2); }
int meter_get_watt_l3(void) { return meter_data_get_int(meter_data.watt, PHASE_L3); }
// ---------- Reactive Power ----------
int meter_get_var_l1(void) { return meter_data_get_int(meter_data.var, PHASE_L1); }
int meter_get_var_l2(void) { return meter_data_get_int(meter_data.var, PHASE_L2); }
int meter_get_var_l3(void) { return meter_data_get_int(meter_data.var, PHASE_L3); }
// ---------- Apparent Power ----------
int meter_get_va_l1(void) { return meter_data_get_int(meter_data.va, PHASE_L1); }
int meter_get_va_l2(void) { return meter_data_get_int(meter_data.va, PHASE_L2); }
int meter_get_va_l3(void) { return meter_data_get_int(meter_data.va, PHASE_L3); }
// ---------- Extra Data ----------
float meter_get_frequency(void) {
float v = 0;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
v = meter_data.frequency;
xSemaphoreGive(meter_mutex);
}
return v;
}
float meter_get_power_factor(void) {
float v = 0;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
v = meter_data.power_factor;
xSemaphoreGive(meter_mutex);
}
return v;
}
float meter_get_total_energy(void) {
float v = 0;
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
v = meter_data.total_energy;
xSemaphoreGive(meter_mutex);
}
return v;
bool meter_zigbee_is_running(void) {
return meter_zigbee_task != NULL;
}

View File

@@ -13,85 +13,20 @@ extern "C" {
*
* @return ESP_OK se a inicialização for bem-sucedida, erro caso contrário.
*/
esp_err_t meter_init_zigbee(void);
esp_err_t meter_zigbee_init(void);
/**
* @brief Inicia a tarefa de leitura dos dados do medidor Zigbee.
*
* @return ESP_OK se a tarefa for iniciada com sucesso, erro caso contrário.
*/
esp_err_t meter_start_zigbee(void);
esp_err_t meter_zigbee_start(void);
/**
* @brief Interrompe a tarefa e limpa recursos (UART, mutex, etc.).
*/
void meter_stop_zigbee(void);
void meter_zigbee_stop(void);
/**
* @brief Verifica se o medidor Zigbee está em execução.
*
* @return true se a tarefa está ativa, false se não.
*/
bool meter_is_running_zigbee(void);
/**
* @brief Limpa todos os dados armazenados em memória.
*/
void meter_clear_data_zigbee(void);
// ----------------------
// Leituras por fase (L1, L2, L3)
// ----------------------
// Corrente RMS (em amperes)
float meter_get_irms_l1_zigbee(void);
float meter_get_irms_l2_zigbee(void);
float meter_get_irms_l3_zigbee(void);
// Tensão RMS (em volts)
float meter_get_vrms_l1_zigbee(void);
float meter_get_vrms_l2_zigbee(void);
float meter_get_vrms_l3_zigbee(void);
// Potência ativa (W)
int meter_get_watt_l1_zigbee(void);
int meter_get_watt_l2_zigbee(void);
int meter_get_watt_l3_zigbee(void);
// Potência reativa (VAR)
int meter_get_var_l1_zigbee(void);
int meter_get_var_l2_zigbee(void);
int meter_get_var_l3_zigbee(void);
// Potência aparente (VA)
int meter_get_va_l1_zigbee(void);
int meter_get_va_l2_zigbee(void);
int meter_get_va_l3_zigbee(void);
// ----------------------
// Dados adicionais
// ----------------------
/**
* @brief Retorna a frequência da rede em Hz.
*
* @return Valor da frequência da rede em Hz.
*/
float meter_get_frequency_zigbee(void);
/**
* @brief Retorna o fator de potência médio.
*
* @return Valor do fator de potência médio.
*/
float meter_get_power_factor_zigbee(void);
/**
* @brief Retorna a energia total acumulada (kWh ou Wh, dependendo do dispositivo).
*
* @return Valor da energia total acumulada.
*/
float meter_get_total_energy_zigbee(void);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,38 @@
#ifndef METER_EVENTS_H
#define METER_EVENTS_H
#include "esp_event.h"
#include "meter_manager.h" // Para meter_type_t
#ifdef __cplusplus
extern "C" {
#endif
// Base de eventos dos medidores
ESP_EVENT_DECLARE_BASE(METER_EVENT);
// IDs de eventos emitidos por medidores
typedef enum {
METER_EVENT_DATA_READY = 0,
METER_EVENT_ERROR,
METER_EVENT_STARTED,
METER_EVENT_STOPPED
} meter_event_id_t;
// Estrutura de dados enviados com METER_EVENT_DATA_READY
typedef struct {
const char *source; // "GRID" ou "EVSE"
float vrms[3]; // Tensão por fase
float irms[3]; // Corrente por fase
int watt[3]; // Potência ativa por fase
float frequency; // Frequência da rede (Hz)
float power_factor; // Fator de potência
float total_energy; // Energia acumulada (kWh)
} meter_event_data_t;
#ifdef __cplusplus
}
#endif
#endif // METER_EVENTS_H

View File

@@ -2,28 +2,66 @@
#define METER_MANAGER_H
#include "esp_err.h"
#include <stdbool.h> // Para garantir que 'bool' seja reconhecido
// Definindo tipos de medidores possíveis para EVSE e Grid
typedef enum {
METER_TYPE_NONE, // Nenhum medidor
METER_TYPE_EVSE_ADE7758, // EVSE com ADE7758
METER_TYPE_GRID_ORNO513, // Grid com ORNO 513
METER_TYPE_GRID_ORNO516, // Grid com ORNO 516
METER_TYPE_GRID_ZIGBEE // Grid com Zigbee
METER_TYPE_NONE, // Nenhum Medidor
METER_TYPE_ADE7758, // ADE7758
METER_TYPE_ORNO513, // ORNO 513
METER_TYPE_ORNO516, // ORNO 516
METER_TYPE_MONO_ZIGBEE, // Medidor Zigbee (Mono)
METER_TYPE_TRIF_ZIGBEE // Medidor Zigbee (Trifásico)
} meter_type_t;
// Funções para inicializar e gerenciar o medidor EVSE (pode ser ADE7758)
esp_err_t meter_manager_init_evse(meter_type_t evse_type); // Inicializa o medidor EVSE (ex: ADE7758)
esp_err_t meter_manager_start_evse(meter_type_t evse_type); // Inicia o EVSE com o tipo especificado
esp_err_t meter_manager_stop_evse(void); // Para o EVSE
/**
* @brief Funções para gerenciar o medidor EVSE (ex: ADE7758).
*/
// Funções para inicializar e gerenciar o medidor Grid (pode ser ORNO 513, ORNO 516 ou Zigbee)
esp_err_t meter_manager_init_grid(meter_type_t grid_type); // Inicializa o medidor Grid (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_start_grid(meter_type_t grid_type); // Inicia o medidor Grid com o tipo especificado
esp_err_t meter_manager_stop_grid(void); // Para o medidor Grid
// Inicializa o medidor EVSE com o tipo especificado (ex: ADE7758)
esp_err_t meter_manager_evse_init(void);
// Funções para ler dados dos medidores
esp_err_t meter_manager_read_current(meter_type_t meter_type, float *current); // Lê a corrente do medidor
esp_err_t meter_manager_read_voltage(meter_type_t meter_type, float *voltage); // Lê a tensão do medidor
// Inicia o medidor EVSE com o tipo especificado
esp_err_t meter_manager_evse_start(void);
// Para o medidor EVSE
esp_err_t meter_manager_evse_stop(void);
// Verifica se o medidor EVSE está habilitado
bool meter_manager_evse_is_enabled(void);
// Define o modelo do medidor EVSE (ADE7758, etc)
esp_err_t meter_manager_evse_set_model(meter_type_t meter_type);
// Retorna o modelo do medidor EVSE (ADE7758, etc)
meter_type_t meter_manager_evse_get_model(void);
/**
* @brief Funções para gerenciar o medidor Grid (ORNO 513, ORNO 516, Zigbee).
*/
// Inicializa o medidor Grid com o tipo especificado (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_grid_init(void);
// Inicia o medidor Grid com o tipo especificado
esp_err_t meter_manager_grid_start(void);
// Para o medidor Grid
esp_err_t meter_manager_grid_stop(void);
// Habilita ou desabilita o medidor Grid
void meter_manager_grid_set_enabled(bool value);
// Define o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_grid_set_model(meter_type_t meter_type);
// Retorna o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee)
meter_type_t meter_manager_grid_get_model(void);
// Função auxiliar para converter o tipo de medidor em uma string
const char* meter_type_to_str(meter_type_t type);
meter_type_t string_to_meter_type(const char *str);
#endif // METER_MANAGER_H

View File

@@ -0,0 +1,4 @@
#include "meter_events.h"
// Define a base de eventos
ESP_EVENT_DEFINE_BASE(METER_EVENT);

View File

@@ -4,135 +4,196 @@
#include "meter_orno513.h"
#include "meter_orno516.h"
#include "meter_zigbee.h"
#include "nvs_flash.h"
#include "nvs.h"
#include <string.h>
static const char *TAG = "meter_manager";
// Variáveis para armazenar o tipo de medidor atual
static meter_type_t current_meter_type = METER_TYPE_NONE;
// Tipos de medidores EVSE e GRID
static meter_type_t meter_evse_type = METER_TYPE_NONE;
static meter_type_t meter_grid_type = METER_TYPE_NONE;
esp_err_t meter_init(meter_type_t meter_type) {
current_meter_type = meter_type;
ESP_LOGI(TAG, "Initializing meter of type: %d", meter_type);
#define NVS_NAMESPACE "meterconfig"
#define NVS_EVSE_MODEL "evse_model"
#define NVS_GRID_MODEL "grid_model"
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_init(); // Inicializa o medidor ADE7758 (EVSE)
case METER_TYPE_GRID_ORNO513:
return meter_orno513_init(); // Inicializa o medidor ORNO 513 ou 516 (Grid)
case METER_TYPE_GRID_ORNO516:
return meter_init_orno516(); // Inicializa o medidor ORNO 513 ou 516 (Grid)
case METER_TYPE_GRID_ZIGBEE:
return meter_init_zigbee(); // Inicializa o medidor Zigbee (Grid)
default:
ESP_LOGE(TAG, "Unsupported meter type");
return ESP_ERR_INVALID_ARG;
// Função unificada para ler ou inicializar um modelo de medidor
static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type) {
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS handle for %s: %s", key, esp_err_to_name(err));
return err;
}
uint8_t value = 0;
err = nvs_get_u8(handle, key, &value);
if (err == ESP_OK && value <= METER_TYPE_TRIF_ZIGBEE) {
*type = (meter_type_t)value;
ESP_LOGI(TAG, "Loaded meter type %d from NVS key '%s'", value, key);
} else {
*type = METER_TYPE_NONE;
nvs_set_u8(handle, key, *type);
nvs_commit(handle);
ESP_LOGW(TAG, "Invalid or missing key '%s', setting default (NONE)", key);
}
nvs_close(handle);
return ESP_OK;
}
static esp_err_t write_meter_model_to_nvs(const char *key, meter_type_t meter_type) {
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS handle for writing");
return err;
}
err = nvs_set_u8(handle, key, (uint8_t)meter_type);
if (err == ESP_OK) {
err = nvs_commit(handle);
ESP_LOGI(TAG, "Saved meter type %d to NVS key '%s'", meter_type, key);
} else {
ESP_LOGE(TAG, "Failed to write meter type to NVS key '%s'", key);
}
nvs_close(handle);
return err;
}
// Função para inicializar o medidor EVSE
esp_err_t meter_manager_evse_init() {
esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type);
if (err != ESP_OK) return err;
ESP_LOGI(TAG, "Initializing EVSE meter of type %s", meter_type_to_str(meter_evse_type));
switch (meter_evse_type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_init();
case METER_TYPE_ORNO513: return meter_orno513_init();
case METER_TYPE_ORNO516: return meter_orno516_init();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init();
default: return ESP_ERR_INVALID_ARG;
}
}
esp_err_t meter_start(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter type is not initialized");
return ESP_ERR_INVALID_STATE;
}
esp_err_t meter_manager_grid_init() {
esp_err_t err = load_or_init_meter_model(NVS_GRID_MODEL, &meter_grid_type);
if (err != ESP_OK) return err;
ESP_LOGI(TAG, "Starting meter");
ESP_LOGI(TAG, "Initializing GRID meter of type %s", meter_type_to_str(meter_grid_type));
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_start();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_start();
case METER_TYPE_GRID_ORNO516:
return meter_start_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_start_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type");
return ESP_ERR_INVALID_ARG;
switch (meter_grid_type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_init();
case METER_TYPE_ORNO513: return meter_orno513_init();
case METER_TYPE_ORNO516: return meter_orno516_init();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init();
default: return ESP_ERR_INVALID_ARG;
}
}
void meter_stop(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter is not initialized");
return;
}
ESP_LOGI(TAG, "Stopping meter");
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
meter_ade7758_stop();
break;
case METER_TYPE_GRID_ORNO513:
meter_orno513_stop();
break;
case METER_TYPE_GRID_ORNO516:
meter_stop_orno516();
break;
case METER_TYPE_GRID_ZIGBEE:
meter_stop_zigbee();
break;
default:
ESP_LOGE(TAG, "Unsupported meter type");
break;
esp_err_t meter_manager_grid_start() {
meter_type_t type = meter_manager_grid_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_start();
case METER_TYPE_ORNO513: return meter_orno513_start();
case METER_TYPE_ORNO516: return meter_orno516_start();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start();
default: return ESP_ERR_INVALID_ARG;
}
}
bool meter_is_running(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter is not initialized");
return false;
esp_err_t meter_manager_grid_stop(void) {
meter_type_t type = meter_manager_grid_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: meter_ade7758_stop(); break;
case METER_TYPE_ORNO513: meter_orno513_stop(); break;
case METER_TYPE_ORNO516: meter_orno516_stop(); break;
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break;
default: return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_is_running();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_is_running();
case METER_TYPE_GRID_ORNO516:
return meter_is_running_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_is_running_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type");
return false;
esp_err_t meter_manager_evse_set_model(meter_type_t meter_type) {
meter_evse_type = meter_type;
return write_meter_model_to_nvs(NVS_EVSE_MODEL, meter_evse_type);
}
esp_err_t meter_manager_grid_set_model(meter_type_t meter_type) {
meter_grid_type = meter_type;
return write_meter_model_to_nvs(NVS_GRID_MODEL, meter_grid_type);
}
esp_err_t meter_manager_evse_start() {
meter_type_t type = meter_manager_evse_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_start();
case METER_TYPE_ORNO513: return meter_orno513_start();
case METER_TYPE_ORNO516: return meter_orno516_start();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start();
default: return ESP_ERR_INVALID_ARG;
}
}
float meter_get_vrms_l1(void) {
if (current_meter_type == METER_TYPE_NONE) return 0;
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_get_vrms_l1();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_get_vrms_l1();
case METER_TYPE_GRID_ORNO516:
return meter_get_vrms_l1_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_get_vrms_l1_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type for reading vrms_l1");
return 0;
esp_err_t meter_manager_evse_stop(void) {
meter_type_t type = meter_manager_evse_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: meter_ade7758_stop(); break;
case METER_TYPE_ORNO513: meter_orno513_stop(); break;
case METER_TYPE_ORNO516: meter_orno516_stop(); break;
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break;
default: return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
bool meter_manager_evse_is_enabled(void) {
return meter_manager_evse_get_model() != METER_TYPE_NONE;
}
meter_type_t meter_manager_evse_get_model(void) {
return meter_evse_type;
}
meter_type_t meter_manager_grid_get_model(void) {
return meter_grid_type;
}
const char* meter_type_to_str(meter_type_t type) {
switch (type) {
case METER_TYPE_NONE: return "NENHUM";
case METER_TYPE_ADE7758: return "IC ADE";
case METER_TYPE_ORNO513: return "ORNO-513";
case METER_TYPE_ORNO516: return "ORNO-516";
case METER_TYPE_MONO_ZIGBEE: return "MONO-ZIGBEE";
case METER_TYPE_TRIF_ZIGBEE: return "TRIF-ZIGBEE";
default: return "NENHUM";
}
}
// Continue as funções `meter_get_*` para cada tipo de dado (corrente, potência, etc.)
float meter_get_irms_l1(void) {
if (current_meter_type == METER_TYPE_NONE) return 0;
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_get_irms_l1();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_get_irms_l1();
case METER_TYPE_GRID_ORNO516:
return meter_get_irms_l1_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_get_irms_l1_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type for reading irms_l1");
return 0;
}
meter_type_t string_to_meter_type(const char *str) {
if (!str) return METER_TYPE_NONE;
if (strcmp(str, "IC ADE") == 0) return METER_TYPE_ADE7758;
if (strcmp(str, "ORNO-513") == 0) return METER_TYPE_ORNO513;
if (strcmp(str, "ORNO-516") == 0) return METER_TYPE_ORNO516;
if (strcmp(str, "MONO-ZIGBEE") == 0) return METER_TYPE_MONO_ZIGBEE;
if (strcmp(str, "TRIF-ZIGBEE") == 0) return METER_TYPE_TRIF_ZIGBEE;
return METER_TYPE_NONE;
}
// You should add the rest of the functions similarly as you progress

View File

@@ -0,0 +1,143 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_mac.h"
#include "nvs.h"
#include "mdns.h"
#include "wifi.h"
#include "nvs_flash.h"
#include <string.h>
#define WIFI_STORAGE_NAMESPACE "wifi_config"
#define TAG "wifi"
#define AP_SSID "plx-%02x%02x%02x"
#define MDNS_HOSTNAME "plx%02x"
#define NVS_NAMESPACE "wifi"
static nvs_handle_t nvs;
static esp_netif_t *ap_netif;
EventGroupHandle_t wifi_event_group;
//
// Event handler para modo AP
//
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_AP_STACONNECTED: {
wifi_event_ap_staconnected_t *event = event_data;
ESP_LOGI(TAG, "STA " MACSTR " conectou, AID=%d", MAC2STR(event->mac), event->aid);
xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT);
break;
}
case WIFI_EVENT_AP_STADISCONNECTED: {
wifi_event_ap_stadisconnected_t *event = event_data;
ESP_LOGI(TAG, "STA " MACSTR " desconectou, AID=%d", MAC2STR(event->mac), event->aid);
xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT);
break;
}
}
}
}
//
// Iniciar o AP com SSID baseado no MAC
//
void wifi_ap_start(void)
{
ESP_LOGI(TAG, "Iniciando AP");
ESP_ERROR_CHECK(esp_wifi_stop());
wifi_config_t ap_config = {
.ap = {
.ssid = "",
.ssid_len = 0,
.channel = 1,
.password = "",
.max_connection = 4,
.authmode = WIFI_AUTH_OPEN
}
};
uint8_t mac[6];
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP));
snprintf((char *)ap_config.ap.ssid, sizeof(ap_config.ap.ssid), AP_SSID, mac[3], mac[4], mac[5]);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
ESP_ERROR_CHECK(esp_wifi_start());
xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT);
}
//
// Inicializar Wi-Fi em modo AP
//
void wifi_ini(void)
{
ESP_LOGI(TAG, "Inicializando Wi-Fi (modo AP)");
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs));
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
/*
if (!esp_event_loop_is_running()) {
ESP_ERROR_CHECK(esp_event_loop_create_default());
}*/
ap_netif = esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
uint8_t mac[6];
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP));
char hostname[16];
snprintf(hostname, sizeof(hostname), MDNS_HOSTNAME, mac[5]);
ESP_ERROR_CHECK(mdns_init());
ESP_ERROR_CHECK(mdns_hostname_set(hostname));
ESP_ERROR_CHECK(mdns_instance_name_set("EVSE Controller"));
wifi_ap_start();
}
esp_netif_t *wifi_get_ap_netif(void)
{
return ap_netif;
}
esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) {
return ESP_OK;
}
void wifi_get_ssid(char *value) {
// Your implementation here
}
void wifi_get_password(char *value) {
// Your implementation here
}
bool wifi_get_enabled(void)
{
return true;
}

View File

@@ -250,7 +250,7 @@ void setChangeConfiguration(const char *payload, size_t len)
void OnResetExecute(bool state)
{
ESP_LOGI(TAG, "#### OnResetExecute");
esp_restart();
//esp_restart();
}
bool setOccupiedInput()
@@ -293,7 +293,7 @@ void notificationOutput(OCPP_Transaction *transaction, enum OCPP_TxNotification
// Authorization events
case Authorized:
ESP_LOGI(TAG, "<----------- Authorized ---------->");
evse_authorize();
//evse_authorize();
// is_charging = true;
break; // success
case AuthorizationRejected:
@@ -310,7 +310,7 @@ void notificationOutput(OCPP_Transaction *transaction, enum OCPP_TxNotification
break; // user took to long to plug vehicle after the authorization
case DeAuthorized:
ESP_LOGI(TAG, "DeAuthorized ---->");
evse_set_authorized(false);
//evse_set_authorized(false);
evse_set_limit_reached(2);
// ocpp_set_charging(false);
break; // server rejected StartTx
@@ -328,8 +328,8 @@ void notificationOutput(OCPP_Transaction *transaction, enum OCPP_TxNotification
case StopTx:
// is_charging = false;
ESP_LOGI(TAG, "StopTx ---->");
evse_set_authorized(false);
evse_set_limit_reached(2);
//evse_set_authorized(false);
//evse_set_limit_reached(2);
break;
};
}
@@ -531,7 +531,7 @@ void ocpp_start()
// ocpp_setOnReceiveRequest("StartTransaction", &setStartTransaction);
xTaskCreate(ocpp_task_func, "ocpp_task", 5 * 1024, NULL, 5, &ocpp_task);
xTaskCreate(ocpp_task_func, "ocpp_task", 5 * 1024, NULL, 4, &ocpp_task);
}
void ocpp_stop(void)

View File

@@ -4,7 +4,6 @@ set(srcs
"src/peripherals.c"
"src/led.c"
"src/buzzer.c"
"src/pilot.c"
"src/proximity.c"
"src/ac_relay.c"
"src/socket_lock.c"

View File

@@ -91,7 +91,7 @@ static void buzzer_worker_task(void *arg) {
while (true) {
if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) {
buzzer_execute_pattern(pattern_id);
//buzzer_execute_pattern(pattern_id);
}
}
}
@@ -158,6 +158,6 @@ void buzzer_init(void) {
buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t));
xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 5, NULL);
xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 5, NULL);
xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 3, NULL);
xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 3, NULL);
}

View File

@@ -59,5 +59,5 @@ void ntc_sensor_init(void)
ESP_ERROR_CHECK(ntc_dev_create(&ntc_config, &ntc, &adc_handle));
ESP_ERROR_CHECK(ntc_dev_get_adc_handle(ntc, &adc_handle));
xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 5, NULL);
xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 3, NULL);
}

View File

@@ -2,7 +2,6 @@
#include "adc.h"
#include "led.h"
#include "buzzer.h"
#include "pilot.h"
#include "proximity.h"
#include "ac_relay.h"
#include "socket_lock.h"
@@ -16,7 +15,6 @@ void peripherals_init(void)
led_init();
buzzer_init();
adc_init();
pilot_init();
proximity_init();
// socket_lock_init();
// rcm_init();

View File

@@ -8,10 +8,8 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash esp_http_server esp_netif esp_https_ota app_update json mqtt vfs spiffs # Use spiffs aqui
PRIV_REQUIRES nvs_flash esp_http_server esp_netif esp_https_ota app_update json mqtt vfs spiffs
REQUIRES config api logger)
set(WEB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/webfolder")
if(EXISTS ${WEB_SRC_DIR})
spiffs_create_partition_image(data ${WEB_SRC_DIR} FLASH_IN_PROJECT)
endif()

View File

@@ -6,8 +6,7 @@
#include "esp_err.h"
/**
* @brief Initialize MQTT
*
* @brief Initializes MQTT client and starts background task if enabled in NVS
*/
void mqtt_init(void);

View File

@@ -7,7 +7,6 @@
#include "esp_event.h"
#include "mqtt_client.h"
#include "nvs.h"
#include "mqtt.h"
#include "json.h"
#include "board_config.h"
@@ -22,47 +21,39 @@
#define NVS_PERIODICITY "periodicity"
static const char* TAG = "mqtt";
static nvs_handle nvs;
static TaskHandle_t client_task = NULL;
static esp_mqtt_client_handle_t client = NULL;
static uint16_t periodicity = 30;
static esp_err_t open_mqtt_nvs(nvs_handle_t *handle) {
return nvs_open(NVS_NAMESPACE, NVS_READWRITE, handle);
}
static void subcribe_topics(void)
{
ESP_LOGI(TAG, "subcribe_topics");
char topic[48];
char topic[64];
mqtt_get_base_topic(topic);
strcat(topic, "/request/#");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
ESP_LOGI(TAG, "subscribed: %s", topic);
mqtt_get_base_topic(topic);
strcat(topic, "/set/config/#");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
ESP_LOGI(TAG, "subscribed: %s", topic);
mqtt_get_base_topic(topic);
strcat(topic, "/enable");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
ESP_LOGI(TAG, "subscribed: %s", topic);
}
static void publish_message(const char* topic, cJSON* root)
{
ESP_LOGI(TAG, "publish_message");
char target_topic[48];
char target_topic[64];
mqtt_get_base_topic(target_topic);
strcat(target_topic, topic);
@@ -76,138 +67,73 @@ static void handle_message(const char* topic, const char* data)
char base_topic[32];
mqtt_get_base_topic(base_topic);
ESP_LOGI(TAG, "Topic: %s", topic);
ESP_LOGI(TAG, "data: %s", data);
ESP_LOGI(TAG, "base_topic: %s", base_topic);
if (strncmp(topic, base_topic, strlen(base_topic)) == 0) {
const char* sub_topic = &topic[strlen(base_topic)];
ESP_LOGI(TAG, "Sub_topic: %s", sub_topic);
if (strcmp(sub_topic, "/request/config/evse") == 0) {
cJSON* root = json_get_evse_config();
publish_message("/response/config/evse", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/config/wifi") == 0) {
cJSON* root = json_get_wifi_config();
publish_message("/response/config/wifi", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/config/mqtt") == 0) {
cJSON* root = json_get_mqtt_config();
publish_message("/response/config/mqtt", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/boardConfig") == 0) {
cJSON* root = json_get_board_config();
publish_message("/response/boardConfig", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/info") == 0) {
cJSON* root = json_get_info();
publish_message("/response/info", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/restart") == 0) {
timeout_restart();
} else if (strcmp(sub_topic, "/set/config/evse") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_evse_config(root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/set/config/wifi") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_wifi_config(root, true);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/set/config/mqtt") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_mqtt_config(root);
cJSON_Delete(root);
}
}
// [Outros comandos omitidos para brevidade...]
}
}
static void event_handler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data)
{
esp_mqtt_event_handle_t event = event_data;
char topic[48];
char data[256];
ESP_LOGI(TAG, "Handle Data 1");
char topic[48], data[256];
switch (event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "Connected");
vTaskResume(client_task);
subcribe_topics();
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Disconnected");
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "Handle Data 2");
memset(topic, 0, sizeof(topic));
strncpy(topic, event->topic, MIN(event->topic_len, sizeof(topic) - 1));
memset(data, 0, sizeof(data));
strncpy(data, event->data, MIN(event->data_len, sizeof(data) - 1));
handle_message(topic, data);
break;
default:
break;
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT conectado");
if (client_task) vTaskResume(client_task);
subcribe_topics();
break;
case MQTT_EVENT_DATA:
strncpy(topic, event->topic, MIN(event->topic_len, sizeof(topic)-1));
strncpy(data, event->data, MIN(event->data_len, sizeof(data)-1));
handle_message(topic, data);
break;
default:
break;
}
}
static void client_task_func(void* param)
{
while (true) {
if (!client) {
vTaskSuspend(NULL);
}
if (!client) vTaskSuspend(NULL);
cJSON* root = json_get_state();
publish_message("/state", root);
cJSON_Delete(root);
vTaskDelay(pdMS_TO_TICKS(periodicity * 1000));
}
}
static void client_start(void)
{
char server[64];
char user[32];
char password[64];
char server[64], user[32], password[64];
mqtt_get_server(server);
mqtt_get_user(user);
mqtt_get_password(password);
ESP_LOGI(TAG, "Client Start");
esp_mqtt_client_config_t cfg = {
.broker.address.uri = server,
.credentials.username = user,
.credentials.authentication.password = password
};
if (client) {
if (esp_mqtt_set_config(client, &cfg) != ESP_OK) {
ESP_LOGW(TAG, "Cant set config");
}
} else {
if (!client) {
client = esp_mqtt_client_init(&cfg);
if (esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, event_handler, client) != ESP_OK) {
ESP_LOGW(TAG, "Cant register handler");
}
if (client == NULL) {
ESP_LOGW(TAG, "Cant set config");
} else {
if (esp_mqtt_client_start(client) != ESP_OK) {
ESP_LOGW(TAG, "Cant start");
}
}
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, event_handler, client);
esp_mqtt_client_start(client);
}
}
static void client_stop(void)
{
if (client != NULL) {
if (client) {
esp_mqtt_client_destroy(client);
client = NULL;
}
@@ -215,12 +141,13 @@ static void client_stop(void)
void mqtt_init(void)
{
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs));
nvs_get_u16(nvs, NVS_PERIODICITY, &periodicity);
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) == ESP_OK) {
nvs_get_u16(handle, NVS_PERIODICITY, &periodicity);
nvs_close(handle);
}
esp_register_shutdown_handler(&client_stop);
xTaskCreate(client_task_func, "mqtt_client_task", 3 * 1024, NULL, 5, &client_task);
if (mqtt_get_enabled()) {
@@ -230,65 +157,34 @@ void mqtt_init(void)
esp_err_t mqtt_set_config(bool enabled, const char* server, const char* base_topic, const char* user, const char* password, uint16_t _periodicity)
{
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) != ESP_OK) return ESP_ERR_INVALID_STATE;
char full_server[64];
if (server && strncmp(server, "mqtt://", 7) != 0 && strncmp(server, "tcp://", 6) != 0) {
snprintf(full_server, sizeof(full_server), "mqtt://%s", server);
server = full_server;
}
if (enabled) {
if (server == NULL || strlen(server) == 0) {
size_t len = 0;
nvs_get_str(nvs, NVS_SERVER, NULL, &len);
if (len <= 1) {
ESP_LOGE(TAG, "Required server");
return ESP_ERR_INVALID_ARG;
}
}
if (base_topic == NULL || strlen(base_topic) == 0) {
size_t len = 0;
nvs_get_str(nvs, NVS_BASE_TOPIC, NULL, &len);
if (len <= 1) {
ESP_LOGE(TAG, "Required base topic");
return ESP_ERR_INVALID_ARG;
}
}
if (_periodicity == 0) {
ESP_LOGE(TAG, "Periodicity muse be larger than zero");
return ESP_ERR_INVALID_ARG;
}
if (!server || !*server) return ESP_ERR_INVALID_ARG;
if (!base_topic || !*base_topic) return ESP_ERR_INVALID_ARG;
if (_periodicity == 0) return ESP_ERR_INVALID_ARG;
}
if (server != NULL && strlen(server) > 63) {
ESP_LOGE(TAG, "Server out of range");
return ESP_ERR_INVALID_ARG;
}
if (server) nvs_set_str(handle, NVS_SERVER, server);
if (base_topic) nvs_set_str(handle, NVS_BASE_TOPIC, base_topic);
if (user) nvs_set_str(handle, NVS_USER, user);
if (password) nvs_set_str(handle, NVS_PASSWORD, password);
nvs_set_u8(handle, NVS_ENABLED, enabled);
nvs_set_u16(handle, NVS_PERIODICITY, _periodicity);
if (base_topic != NULL && strlen(base_topic) > 31) {
ESP_LOGE(TAG, "Base topic out of range");
return ESP_ERR_INVALID_ARG;
}
if (user != NULL && strlen(user) > 31) {
ESP_LOGE(TAG, "User out of range");
return ESP_ERR_INVALID_ARG;
}
if (password != NULL && strlen(password) > 63) {
ESP_LOGE(TAG, "Password out of range");
return ESP_ERR_INVALID_ARG;
}
nvs_set_u8(nvs, NVS_ENABLED, enabled);
nvs_set_str(nvs, NVS_SERVER, server);
nvs_set_str(nvs, NVS_BASE_TOPIC, base_topic);
nvs_set_str(nvs, NVS_USER, user);
nvs_set_str(nvs, NVS_PASSWORD, password);
nvs_set_u16(nvs, NVS_PERIODICITY, _periodicity);
periodicity = _periodicity;
nvs_commit(nvs);
esp_err_t err = nvs_commit(handle);
nvs_close(handle);
if (err != ESP_OK) return err;
if (enabled) {
client_start();
@@ -301,40 +197,64 @@ esp_err_t mqtt_set_config(bool enabled, const char* server, const char* base_top
bool mqtt_get_enabled(void)
{
uint8_t value = false;
nvs_get_u8(nvs, NVS_ENABLED, &value);
return value;
nvs_handle_t handle;
uint8_t val = false;
if (open_mqtt_nvs(&handle) == ESP_OK) {
nvs_get_u8(handle, NVS_ENABLED, &val);
nvs_close(handle);
}
return val;
}
void mqtt_get_server(char* value)
{
size_t len = 64;
if (!value) return;
value[0] = '\0';
nvs_get_str(nvs, NVS_SERVER, value, &len);
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) == ESP_OK) {
size_t len = 64;
nvs_get_str(handle, NVS_SERVER, value, &len);
nvs_close(handle);
}
}
void mqtt_get_base_topic(char* value)
{
size_t len = 32;
if (!value) return;
value[0] = '\0';
nvs_get_str(nvs, NVS_BASE_TOPIC, value, &len);
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) == ESP_OK) {
size_t len = 32;
nvs_get_str(handle, NVS_BASE_TOPIC, value, &len);
nvs_close(handle);
}
}
void mqtt_get_user(char* value)
{
size_t len = 32;
if (!value) return;
value[0] = '\0';
nvs_get_str(nvs, NVS_USER, value, &len);
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) == ESP_OK) {
size_t len = 32;
nvs_get_str(handle, NVS_USER, value, &len);
nvs_close(handle);
}
}
void mqtt_get_password(char* value)
{
size_t len = 64;
if (!value) return;
value[0] = '\0';
nvs_get_str(nvs, NVS_PASSWORD, value, &len);
nvs_handle_t handle;
if (open_mqtt_nvs(&handle) == ESP_OK) {
size_t len = 64;
nvs_get_str(handle, NVS_PASSWORD, value, &len);
nvs_close(handle);
}
}
uint16_t mqtt_get_periodicity(void)
{
return periodicity;
}
}

View File

@@ -1,6 +1,6 @@
#include "protocols.h"
#include "date_time.h"
#include "rest.h"
//#include "rest.h"
#include "mqtt.h"
//#include "modbus_tcp.h"
@@ -8,7 +8,7 @@ void protocols_init(void)
{
date_time_init();
/* Serve static files from the SPIFFS data partition */
rest_init("/data");
//mqtt_init();
// rest_init("/data");
mqtt_init();
//modbus_tcp_init();
}

View File

@@ -390,7 +390,7 @@ static esp_err_t config_settings_get_handler(httpd_req_t *req)
cJSON *config = cJSON_CreateObject();
cJSON_AddNumberToObject(config, "maxCurrentLimit", evse_get_max_charging_current());
//cJSON_AddNumberToObject(config, "maxCurrentLimit", evse_get_max_charging_current());
cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current());
cJSON_AddNumberToObject(config, "powerLimit", settings_config.powerLimit);
cJSON_AddNumberToObject(config, "energyLimit", settings_config.energyLimit);
@@ -721,6 +721,8 @@ static esp_err_t config_users_delete_handler(httpd_req_t *req)
esp_err_t rest_init(const char *base_path)
{
/*
REST_CHECK(base_path, "wrong base path", err);
rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t));
REST_CHECK(rest_context, "No memory for rest context", err_start);
@@ -894,17 +896,12 @@ esp_err_t rest_init(const char *base_path)
// URI handler for getting web server files
httpd_uri_t common_get_uri = {
.uri = "/*",
.uri = "/",
.method = HTTP_GET,
.handler = rest_common_get_handler,
.user_ctx = rest_context
};
httpd_register_uri_handler(server, &common_get_uri);
*/
return ESP_OK;
err_start:
free(rest_context);
err:
return ESP_FAIL;
}

View File

@@ -0,0 +1,24 @@
set(srcs
"src/rest_main.c"
"src/evse_settings_api.c"
"src/ocpp_api.c"
"src/auth_api.c"
"src/network_api.c"
"src/meters_settings_api.c"
"src/loadbalancing_settings_api.c"
"src/dashboard_api.c"
"src/static_file_api.c"
)
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash esp_http_server esp_netif vfs spiffs json evse meter_manager loadbalancer
)
# SPIFFS image (opcional)
set(WEB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/webfolder")
if(EXISTS "${WEB_SRC_DIR}")
spiffs_create_partition_image(data "${WEB_SRC_DIR}" FLASH_IN_PROJECT)
endif()

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra os handlers de autenticação e gerenciamento de usuários
*/
void register_auth_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra o handler da dashboard (status geral do sistema)
*/
void register_dashboard_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra os handlers de configuração elétrica e limites de carregamento
*/
void register_evse_settings_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,14 @@
// =========================
// loadbalancing_settings_api.h
// =========================
#ifndef LOADBALANCING_SETTINGS_API_H
#define LOADBALANCING_SETTINGS_API_H
#include "esp_err.h"
#include "esp_http_server.h"
// Função para registrar os manipuladores de URI para as configurações de load balancing e solar
void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx);
#endif // LOADBALANCING_SETTINGS_API_H

View File

@@ -0,0 +1,14 @@
// =========================
// meters_settings_api.h
// =========================
#ifndef METERS_SETTINGS_API_H
#define METERS_SETTINGS_API_H
#include "esp_err.h"
#include "esp_http_server.h"
// Função para registrar os manipuladores de URI para as configurações dos contadores
void register_meters_settings_handlers(httpd_handle_t server, void *ctx);
#endif // METERS_SETTINGS_API_H

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra os handlers de configuração Wi-Fi e MQTT
*/
void register_network_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra os handlers da configuração e status do OCPP
*/
void register_ocpp_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,13 @@
#pragma once
#include <esp_err.h>
#include <esp_vfs.h>
#define SCRATCH_BUFSIZE (10240)
typedef struct rest_server_context {
char base_path[ESP_VFS_PATH_MAX + 1];
char scratch[SCRATCH_BUFSIZE];
} rest_server_context_t;
esp_err_t rest_server_init(const char *base_path);

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra o handler para servir arquivos estáticos da web (SPA)
*/
void register_static_file_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,141 @@
// =========================
// auth_api.c
// =========================
#include "auth_api.h"
#include "auth.h"
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "auth_api";
static struct {
char username[128];
} users[10] = { /*{"admin"}, {"user1"}*/ };
static int num_users = 2;
static esp_err_t auth_methods_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *json = cJSON_CreateObject();
cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() );
char *str = cJSON_PrintUnformatted(json);
httpd_resp_sendstr(req, str);
free(str);
cJSON_Delete(json);
return ESP_OK;
}
static esp_err_t auth_methods_post_handler(httpd_req_t *req) {
char buf[256];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados");
return ESP_FAIL;
}
buf[len] = '\0';
cJSON *json = cJSON_Parse(buf);
if (!json) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido");
return ESP_FAIL;
}
cJSON *rfid = cJSON_GetObjectItem(json, "RFID");
if (rfid && cJSON_IsBool(rfid)) {
auth_set_enabled(cJSON_IsTrue(rfid));
} else {
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'RFID' inválido ou ausente");
return ESP_FAIL;
}
cJSON_Delete(json);
httpd_resp_sendstr(req, "Métodos de autenticação atualizados");
return ESP_OK;
}
static esp_err_t users_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *root = cJSON_CreateObject();
cJSON *list = cJSON_CreateArray();
for (int i = 0; i < num_users; ++i) {
cJSON *u = cJSON_CreateObject();
cJSON_AddStringToObject(u, "username", users[i].username);
cJSON_AddItemToArray(list, u);
}
cJSON_AddItemToObject(root, "users", list);
char *str = cJSON_Print(root);
httpd_resp_sendstr(req, str);
free(str);
cJSON_Delete(root);
return ESP_OK;
}
static esp_err_t users_post_handler(httpd_req_t *req) {
char buf[128];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) return ESP_FAIL;
buf[len] = '\0';
if (num_users < 10) {
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
num_users++;
httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
} else {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido");
}
return ESP_OK;
}
static esp_err_t users_delete_handler(httpd_req_t *req) {
char query[128];
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
char username[128];
if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) {
for (int i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0) {
for (int j = i; j < num_users - 1; j++) {
users[j] = users[j + 1];
}
num_users--;
httpd_resp_sendstr(req, "Usuário removido com sucesso");
return ESP_OK;
}
}
}
}
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado");
return ESP_FAIL;
}
void register_auth_handlers(httpd_handle_t server, void *ctx) {
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/auth-methods",
.method = HTTP_GET,
.handler = auth_methods_get_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/auth-methods",
.method = HTTP_POST,
.handler = auth_methods_post_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/users",
.method = HTTP_GET,
.handler = users_get_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/users",
.method = HTTP_POST,
.handler = users_post_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/users",
.method = HTTP_DELETE,
.handler = users_delete_handler,
.user_ctx = ctx
});
}

View File

@@ -0,0 +1,83 @@
#include "dashboard_api.h"
#include "esp_log.h"
#include "cJSON.h"
#include "evse_api.h"
#include "evse_error.h"
static const char *TAG = "dashboard_api";
static esp_err_t dashboard_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
// Cria o objeto JSON principal do dashboard
cJSON *dashboard = cJSON_CreateObject();
// Status do sistema
evse_state_t state = evse_get_state();
cJSON_AddStringToObject(dashboard, "status", evse_state_to_str(state));
// Carregador - informação do carregador 1 (adapte conforme necessário)
cJSON *chargers = cJSON_CreateArray();
cJSON *charger1 = cJSON_CreateObject();
cJSON_AddNumberToObject(charger1, "id", 1);
cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state));
cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current() / 10);
cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current());
// Calcular a potência com base na corrente (considerando 230V)
int power = (evse_get_charging_current() / 10) * 230;
cJSON_AddNumberToObject(charger1, "power", power);
cJSON_AddItemToArray(chargers, charger1);
cJSON_AddItemToObject(dashboard, "chargers", chargers);
// Consumo e tempo de carregamento
cJSON_AddNumberToObject(dashboard, "energyConsumed", evse_get_consumption_limit());
cJSON_AddNumberToObject(dashboard, "chargingTime", evse_get_charging_time_limit());
// Alertas
cJSON *alerts = cJSON_CreateArray();
if (evse_is_limit_reached()) {
cJSON_AddItemToArray(alerts, cJSON_CreateString("Limite de consumo atingido."));
}
if (!evse_is_available()) {
cJSON_AddItemToArray(alerts, cJSON_CreateString("Estação indisponível."));
}
if (!evse_is_enabled()) {
cJSON_AddItemToArray(alerts, cJSON_CreateString("EVSE desativado."));
}
cJSON_AddItemToObject(dashboard, "alerts", alerts);
// Erros
uint32_t error_bits = evse_get_error();
cJSON *errors = cJSON_CreateArray();
if (error_bits & EVSE_ERR_DIODE_SHORT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Diodo curto-circuitado"));
if (error_bits & EVSE_ERR_LOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no travamento"));
if (error_bits & EVSE_ERR_UNLOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no destravamento"));
if (error_bits & EVSE_ERR_RCM_SELFTEST_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no autoteste do RCM"));
if (error_bits & EVSE_ERR_RCM_TRIGGERED_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("RCM disparado"));
if (error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Temperatura elevada"));
if (error_bits & EVSE_ERR_PILOT_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Erro no sinal piloto"));
if (error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no sensor de temperatura"));
cJSON_AddItemToObject(dashboard, "errors", errors);
// Enviar resposta JSON
const char *json_str = cJSON_Print(dashboard);
httpd_resp_sendstr(req, json_str);
// Liberar memória
free((void *)json_str);
cJSON_Delete(dashboard);
return ESP_OK;
}
void register_dashboard_handlers(httpd_handle_t server, void *ctx) {
httpd_uri_t uri = {
.uri = "/api/v1/dashboard",
.method = HTTP_GET,
.handler = dashboard_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &uri);
}

View File

@@ -0,0 +1,63 @@
// =========================
// evse_settings_api.c
// =========================
#include "evse_settings_api.h"
#include "evse_api.h"
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "evse_settings_api";
static esp_err_t config_settings_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *config = cJSON_CreateObject();
cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current());
cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold());
const char *json_str = cJSON_Print(config);
httpd_resp_sendstr(req, json_str);
free((void *)json_str);
cJSON_Delete(config);
return ESP_OK;
}
static esp_err_t config_settings_post_handler(httpd_req_t *req) {
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
return ESP_FAIL;
}
buf[len] = '\0';
cJSON *json = cJSON_Parse(buf);
if (!json) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
return ESP_FAIL;
}
cJSON *current = cJSON_GetObjectItem(json, "currentLimit");
if (current) evse_set_max_charging_current(current->valueint);
cJSON *temp = cJSON_GetObjectItem(json, "temperatureLimit");
if (temp) evse_set_temp_threshold(temp->valueint);
cJSON_Delete(json);
httpd_resp_sendstr(req, "Configurações atualizadas com sucesso");
return ESP_OK;
}
void register_evse_settings_handlers(httpd_handle_t server, void *ctx) {
httpd_uri_t get_uri = {
.uri = "/api/v1/config/settings",
.method = HTTP_GET,
.handler = config_settings_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &get_uri);
httpd_uri_t post_uri = {
.uri = "/api/v1/config/settings",
.method = HTTP_POST,
.handler = config_settings_post_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &post_uri);
}

View File

@@ -0,0 +1,108 @@
#include "loadbalancing_settings_api.h"
#include "loadbalancer.h"
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "loadbalancing_settings_api";
// GET Handler: Retorna configurações atuais de load balancing
static esp_err_t loadbalancing_config_get_handler(httpd_req_t *req) {
bool enabled = loadbalancer_is_enabled();
uint8_t currentLimit = load_balancing_get_max_grid_current();
ESP_LOGI(TAG, "Fetching load balancing settings: enabled = %d, currentLimit = %u", enabled, currentLimit);
httpd_resp_set_type(req, "application/json");
cJSON *config = cJSON_CreateObject();
cJSON_AddBoolToObject(config, "loadBalancingEnabled", enabled);
cJSON_AddNumberToObject(config, "loadBalancingCurrentLimit", currentLimit);
const char *json_str = cJSON_Print(config);
httpd_resp_sendstr(req, json_str);
ESP_LOGI(TAG, "Returned config: %s", json_str);
free((void *)json_str);
cJSON_Delete(config);
return ESP_OK;
}
// POST Handler: Atualiza configurações de load balancing
static esp_err_t loadbalancing_config_post_handler(httpd_req_t *req) {
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
ESP_LOGE(TAG, "Received empty POST body");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
return ESP_FAIL;
}
buf[len] = '\0';
ESP_LOGI(TAG, "Received POST data: %s", buf);
cJSON *json = cJSON_Parse(buf);
if (!json) {
ESP_LOGE(TAG, "Invalid JSON");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
return ESP_FAIL;
}
// Atualizar estado habilitado
cJSON *enabled_item = cJSON_GetObjectItem(json, "loadBalancingEnabled");
if (enabled_item && cJSON_IsBool(enabled_item)) {
bool isEnabled = cJSON_IsTrue(enabled_item);
loadbalancer_set_enabled(isEnabled);
ESP_LOGI(TAG, "Updated loadBalancingEnabled to: %d", isEnabled);
}
// Atualizar limite de corrente
cJSON *limit_item = cJSON_GetObjectItem(json, "loadBalancingCurrentLimit");
if (limit_item && cJSON_IsNumber(limit_item)) {
uint8_t currentLimit = (uint8_t)limit_item->valuedouble;
// Validar intervalo
if (currentLimit < 6 || currentLimit > 100) {
ESP_LOGW(TAG, "Rejected invalid currentLimit: %d", currentLimit);
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid currentLimit (must be 6-100)");
return ESP_FAIL;
}
esp_err_t err = load_balancing_set_max_grid_current(currentLimit);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to save currentLimit: %s", esp_err_to_name(err));
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save setting");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Updated loadBalancingCurrentLimit to: %d", currentLimit);
}
cJSON_Delete(json);
httpd_resp_sendstr(req, "Load balancing settings updated successfully");
return ESP_OK;
}
// Registro dos handlers na API HTTP
void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx) {
// GET
httpd_uri_t get_uri = {
.uri = "/api/v1/config/loadbalancing",
.method = HTTP_GET,
.handler = loadbalancing_config_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &get_uri);
// POST
httpd_uri_t post_uri = {
.uri = "/api/v1/config/loadbalancing",
.method = HTTP_POST,
.handler = loadbalancing_config_post_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &post_uri);
}

View File

@@ -0,0 +1,111 @@
#include "meters_settings_api.h"
#include "meter_manager.h" // Atualizado para usar o novo manager
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "meters_settings_api";
// Função para recuperar as configurações dos contadores
static esp_err_t meters_config_get_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters");
httpd_resp_set_type(req, "application/json");
cJSON *config = cJSON_CreateObject();
// Recuperando as configurações dos contadores
meter_type_t gridmeterType = meter_manager_grid_get_model();
meter_type_t evsemeterType = meter_manager_evse_get_model();
ESP_LOGI(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType));
ESP_LOGI(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType));
// Adicionando os tipos de contadores ao objeto JSON
cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType));
cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType));
// Convertendo o objeto JSON para uma string
const char *json_str = cJSON_Print(config);
ESP_LOGI(TAG, "Returning meters config: %s", json_str);
httpd_resp_sendstr(req, json_str);
// Liberação da memória
free((void *)json_str);
cJSON_Delete(config);
return ESP_OK;
}
// Função para atualizar as configurações dos contadores
static esp_err_t meters_config_post_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters");
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
ESP_LOGE(TAG, "Received empty body in POST request");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
return ESP_FAIL;
}
buf[len] = '\0'; // Garantir que a string está terminada
ESP_LOGI(TAG, "Received POST data: %s", buf);
cJSON *json = cJSON_Parse(buf);
if (!json) {
ESP_LOGE(TAG, "Failed to parse JSON data");
// Resposta detalhada de erro
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format");
return ESP_FAIL;
}
// Atualizando os contadores
cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter");
if (gridmeter) {
meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type
ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring);
meter_manager_grid_set_model(gridType);
}
cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter");
if (evsemeter) {
meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type
ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring);
meter_manager_evse_set_model(evseType);
}
cJSON_Delete(json);
httpd_resp_sendstr(req, "Meters updated successfully");
ESP_LOGI(TAG, "Meters configuration updated successfully");
return ESP_OK;
}
// Registrando os manipuladores de URI para os contadores
void register_meters_settings_handlers(httpd_handle_t server, void *ctx) {
ESP_LOGI(TAG, "Registering URI handlers for meters settings");
// URI para o método GET
httpd_uri_t meters_get_uri = {
.uri = "/api/v1/config/meters",
.method = HTTP_GET,
.handler = meters_config_get_handler,
.user_ctx = ctx
};
ESP_LOGI(TAG, "Registering GET handler for /api/v1/config/meters");
httpd_register_uri_handler(server, &meters_get_uri);
// URI para o método POST
httpd_uri_t meters_post_uri = {
.uri = "/api/v1/config/meters",
.method = HTTP_POST,
.handler = meters_config_post_handler,
.user_ctx = ctx
};
ESP_LOGI(TAG, "Registering POST handler for /api/v1/config/meters");
httpd_register_uri_handler(server, &meters_post_uri);
}

View File

@@ -0,0 +1,257 @@
// =========================
// network_api.c
// =========================
#include "network_api.h"
#include "esp_log.h"
#include "cJSON.h"
#include "wifi.h"
#include "mqtt.h"
static const char *TAG = "network_api";
typedef struct {
bool enabled;
char ssid[33];
char password[65];
} wifi_task_data_t;
static void wifi_apply_config_task(void *param) {
wifi_task_data_t *data = (wifi_task_data_t *)param;
ESP_LOGI("wifi_task", "Applying Wi-Fi config in background task");
wifi_set_config(data->enabled, data->ssid, data->password);
free(data);
vTaskDelete(NULL);
}
static esp_err_t wifi_get_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi");
httpd_resp_set_type(req, "application/json");
// Obter dados da NVS via wifi.c
bool enabled = wifi_get_enabled();
char ssid[33] = {0};
char password[65] = {0};
wifi_get_ssid(ssid);
wifi_get_password(password);
// Criar JSON
cJSON *json = cJSON_CreateObject();
cJSON_AddBoolToObject(json, "enabled", enabled);
cJSON_AddStringToObject(json, "ssid", ssid);
cJSON_AddStringToObject(json, "password", password);
// Enviar resposta
char *response = cJSON_Print(json);
httpd_resp_sendstr(req, response);
// Limpeza
free(response);
cJSON_Delete(json);
return ESP_OK;
}
static esp_err_t wifi_post_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "Handling POST /api/v1/config/wifi");
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) return ESP_FAIL;
buf[len] = '\0';
cJSON *json = cJSON_Parse(buf);
if (!json) return ESP_FAIL;
// Valores padrão
bool enabled = false;
const char *ssid = NULL;
const char *password = NULL;
cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled");
if (cJSON_IsBool(j_enabled)) enabled = j_enabled->valueint;
cJSON *j_ssid = cJSON_GetObjectItem(json, "ssid");
if (cJSON_IsString(j_ssid)) ssid = j_ssid->valuestring;
cJSON *j_password = cJSON_GetObjectItem(json, "password");
if (cJSON_IsString(j_password)) password = j_password->valuestring;
// Enviar resposta antes de alterar Wi-Fi
httpd_resp_sendstr(req, "Wi-Fi config atualizada com sucesso");
// Alocar struct para passar para a task
wifi_task_data_t *task_data = malloc(sizeof(wifi_task_data_t));
if (!task_data) {
cJSON_Delete(json);
ESP_LOGE(TAG, "Memory allocation failed for Wi-Fi task");
return ESP_ERR_NO_MEM;
}
task_data->enabled = enabled;
strncpy(task_data->ssid, ssid ? ssid : "", sizeof(task_data->ssid));
strncpy(task_data->password, password ? password : "", sizeof(task_data->password));
// Criar task normal com função C
xTaskCreate(
wifi_apply_config_task,
"wifi_config_task",
4096,
task_data,
3,
NULL
);
cJSON_Delete(json);
return ESP_OK;
}
static esp_err_t config_mqtt_get_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "Handling GET /api/v1/config/mqtt");
httpd_resp_set_type(req, "application/json");
bool enabled = mqtt_get_enabled();
char server[64] = {0};
char base_topic[32] = {0};
char username[32] = {0};
char password[64] = {0};
uint16_t periodicity = mqtt_get_periodicity();
mqtt_get_server(server);
mqtt_get_base_topic(base_topic);
mqtt_get_user(username);
mqtt_get_password(password);
ESP_LOGI(TAG, "MQTT Config:");
ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false");
ESP_LOGI(TAG, " Server: %s", server);
ESP_LOGI(TAG, " Topic: %s", base_topic);
ESP_LOGI(TAG, " Username: %s", username);
ESP_LOGI(TAG, " Password: %s", password);
ESP_LOGI(TAG, " Periodicity: %d", periodicity);
cJSON *config = cJSON_CreateObject();
cJSON_AddBoolToObject(config, "enabled", enabled);
cJSON_AddStringToObject(config, "host", server);
cJSON_AddNumberToObject(config, "port", 1883);
cJSON_AddStringToObject(config, "username", username);
cJSON_AddStringToObject(config, "password", password);
cJSON_AddStringToObject(config, "topic", base_topic);
cJSON_AddNumberToObject(config, "periodicity", periodicity);
const char *config_str = cJSON_Print(config);
httpd_resp_sendstr(req, config_str);
free((void *)config_str);
cJSON_Delete(config);
return ESP_OK;
}
static esp_err_t config_mqtt_post_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "Handling POST /api/v1/config/mqtt");
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
ESP_LOGE(TAG, "Failed to read request body");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body");
return ESP_FAIL;
}
buf[len] = '\0';
ESP_LOGI(TAG, "Received JSON: %s", buf);
cJSON *json = cJSON_Parse(buf);
if (!json) {
ESP_LOGE(TAG, "Invalid JSON format");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
return ESP_FAIL;
}
bool enabled = false;
const char *host = NULL, *topic = NULL, *username = NULL, *password = NULL;
int periodicity = 30;
if (cJSON_IsBool(cJSON_GetObjectItem(json, "enabled")))
enabled = cJSON_GetObjectItem(json, "enabled")->valueint;
cJSON *j_host = cJSON_GetObjectItem(json, "host");
if (cJSON_IsString(j_host)) host = j_host->valuestring;
cJSON *j_topic = cJSON_GetObjectItem(json, "topic");
if (cJSON_IsString(j_topic)) topic = j_topic->valuestring;
cJSON *j_user = cJSON_GetObjectItem(json, "username");
if (cJSON_IsString(j_user)) username = j_user->valuestring;
cJSON *j_pass = cJSON_GetObjectItem(json, "password");
if (cJSON_IsString(j_pass)) password = j_pass->valuestring;
cJSON *j_periodicity = cJSON_GetObjectItem(json, "periodicity");
if (cJSON_IsNumber(j_periodicity)) periodicity = j_periodicity->valueint;
ESP_LOGI(TAG, "Applying MQTT config:");
ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false");
ESP_LOGI(TAG, " Host: %s", host);
ESP_LOGI(TAG, " Topic: %s", topic);
ESP_LOGI(TAG, " Username: %s", username);
ESP_LOGI(TAG, " Password: %s", password);
ESP_LOGI(TAG, " Periodicity: %d", periodicity);
esp_err_t err = mqtt_set_config(enabled, host, topic, username, password, periodicity);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to apply MQTT config (code %d)", err);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to apply config");
cJSON_Delete(json);
return ESP_FAIL;
}
httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso");
cJSON_Delete(json);
return ESP_OK;
}
void register_network_handlers(httpd_handle_t server, void *ctx) {
httpd_uri_t wifi_get = {
.uri = "/api/v1/config/wifi",
.method = HTTP_GET,
.handler = wifi_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &wifi_get);
httpd_uri_t wifi_post = {
.uri = "/api/v1/config/wifi",
.method = HTTP_POST,
.handler = wifi_post_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &wifi_post);
// URI handler for getting MQTT config
httpd_uri_t config_mqtt_get_uri = {
.uri = "/api/v1/config/mqtt",
.method = HTTP_GET,
.handler = config_mqtt_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &config_mqtt_get_uri);
// URI handler for posting MQTT config
httpd_uri_t config_mqtt_post_uri = {
.uri = "/api/v1/config/mqtt",
.method = HTTP_POST,
.handler = config_mqtt_post_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &config_mqtt_post_uri);
}

View File

@@ -0,0 +1,92 @@
// =========================
// ocpp_api.c
// =========================
#include "ocpp_api.h"
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "ocpp_api";
static struct {
char url[256];
char chargeBoxId[128];
char certificate[256];
char privateKey[256];
} ocpp_config = {"", "", "", ""};
static esp_err_t ocpp_get_status_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *status = cJSON_CreateObject();
cJSON_AddStringToObject(status, "status", "connected");
char *str = cJSON_Print(status);
httpd_resp_sendstr(req, str);
free(str);
cJSON_Delete(status);
return ESP_OK;
}
static esp_err_t ocpp_get_config_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "url", ocpp_config.url);
cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId);
cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate);
cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey);
char *str = cJSON_Print(json);
httpd_resp_sendstr(req, str);
free(str);
cJSON_Delete(json);
return ESP_OK;
}
static esp_err_t ocpp_post_config_handler(httpd_req_t *req) {
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
return ESP_FAIL;
}
buf[len] = '\0';
cJSON *json = cJSON_Parse(buf);
if (!json) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
return ESP_FAIL;
}
cJSON *url = cJSON_GetObjectItem(json, "url");
if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url));
cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId");
if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId));
cJSON *cert = cJSON_GetObjectItem(json, "certificate");
if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate));
cJSON *key = cJSON_GetObjectItem(json, "privateKey");
if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey));
cJSON_Delete(json);
httpd_resp_sendstr(req, "OCPP config atualizada com sucesso");
return ESP_OK;
}
void register_ocpp_handlers(httpd_handle_t server, void *ctx) {
httpd_uri_t status_uri = {
.uri = "/api/v1/ocpp",
.method = HTTP_GET,
.handler = ocpp_get_status_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &status_uri);
httpd_uri_t get_uri = {
.uri = "/api/v1/config/ocpp",
.method = HTTP_GET,
.handler = ocpp_get_config_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &get_uri);
httpd_uri_t post_uri = {
.uri = "/api/v1/config/ocpp",
.method = HTTP_POST,
.handler = ocpp_post_config_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &post_uri);
}

View File

@@ -0,0 +1,53 @@
#include "rest_main.h"
#include "evse_settings_api.h"
#include "meters_settings_api.h"
#include "loadbalancing_settings_api.h"
#include "network_api.h"
#include "ocpp_api.h"
#include "auth_api.h"
#include "dashboard_api.h"
#include "static_file_api.h"
#include "esp_log.h"
static const char *TAG = "rest_main";
esp_err_t rest_server_init(const char *base_path) {
ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path);
rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t));
if (!ctx) {
ESP_LOGE(TAG, "Failed to allocate memory for REST context");
return ESP_ERR_NO_MEM;
}
strlcpy(ctx->base_path, base_path, sizeof(ctx->base_path));
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.uri_match_fn = httpd_uri_match_wildcard;
config.max_uri_handlers = 32;
httpd_handle_t server = NULL;
esp_err_t err = httpd_start(&server, &config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err));
free(ctx);
return err;
}
ESP_LOGI(TAG, "HTTP server started successfully");
// Register endpoint groups
register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
register_network_handlers(server, ctx); // Apenas chamando a função sem comparação
register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação
register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação
register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação
register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação
ESP_LOGI(TAG, "All REST API endpoint groups registered successfully");
return ESP_OK;
}

View File

@@ -0,0 +1,89 @@
#include "static_file_api.h"
#include "esp_log.h"
#include <fcntl.h>
#include <string.h>
#include "esp_vfs.h"
static const char *TAG = "static_file_api";
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
#define SCRATCH_BUFSIZE (10240)
typedef struct rest_server_context {
char base_path[ESP_VFS_PATH_MAX + 1];
char scratch[SCRATCH_BUFSIZE];
} rest_server_context_t;
#define CHECK_FILE_EXTENSION(filename, ext) \
(strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) {
const char *type = "text/plain";
if (CHECK_FILE_EXTENSION(filepath, ".html")) type = "text/html";
else if (CHECK_FILE_EXTENSION(filepath, ".js")) type = "application/javascript";
else if (CHECK_FILE_EXTENSION(filepath, ".css")) type = "text/css";
else if (CHECK_FILE_EXTENSION(filepath, ".png")) type = "image/png";
else if (CHECK_FILE_EXTENSION(filepath, ".ico")) type = "image/x-icon";
else if (CHECK_FILE_EXTENSION(filepath, ".svg")) type = "image/svg+xml";
return httpd_resp_set_type(req, type);
}
static esp_err_t static_get_handler(httpd_req_t *req) {
char filepath[FILE_PATH_MAX];
rest_server_context_t *ctx = (rest_server_context_t *) req->user_ctx;
strlcpy(filepath, ctx->base_path, sizeof(filepath));
if (req->uri[strlen(req->uri) - 1] == '/') {
strlcat(filepath, "/index.html", sizeof(filepath));
} else {
strlcat(filepath, req->uri, sizeof(filepath));
}
int fd = open(filepath, O_RDONLY, 0);
if (fd == -1) {
// fallback para /index.html (SPA)
ESP_LOGW(TAG, "Arquivo não encontrado: %s. Tentando index.html", filepath);
strlcpy(filepath, ctx->base_path, sizeof(filepath));
strlcat(filepath, "/index.html", sizeof(filepath));
fd = open(filepath, O_RDONLY, 0);
if (fd == -1) {
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Arquivo não encontrado");
return ESP_FAIL;
}
}
set_content_type_from_file(req, filepath);
char *chunk = ctx->scratch;
ssize_t read_bytes;
do {
read_bytes = read(fd, chunk, SCRATCH_BUFSIZE);
if (read_bytes == -1) {
ESP_LOGE(TAG, "Erro lendo arquivo: %s", filepath);
close(fd);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao ler arquivo");
return ESP_FAIL;
} else if (read_bytes > 0) {
if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) {
close(fd);
httpd_resp_sendstr_chunk(req, NULL);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao enviar arquivo");
return ESP_FAIL;
}
}
} while (read_bytes > 0);
close(fd);
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
void register_static_file_handlers(httpd_handle_t server, void *ctx) {
httpd_uri_t uri = {
.uri = "/*",
.method = HTTP_GET,
.handler = static_get_handler,
.user_ctx = ctx
};
httpd_register_uri_handler(server, &uri);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> -->
<!-- Remover link para tailwind.min.css -->
<!-- <link href="/tailwind.min.css" rel="stylesheet"> -->
<style>
body {
font-family: 'Arial', 'Helvetica', 'sans-serif'; /* Usando fontes genéricas do sistema */
}
</style>
<title>Vite + React</title>
<script type="module" crossorigin src="/assets/index-zZ02wEhQ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DvcKJk-E.css">
</head>
<body>
<div id="root"></div>
<!-- Adicionar o link para o seu arquivo CSS com o Tailwind configurado -->
</body>
</html>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -349,11 +349,11 @@ void handlePacket(uint8_t *buf, int len)
printf("Received max_grid_current %i\n", (int)msg_in.payload.max_grid_current);
int max_grid_current = (int)msg_in.payload.max_grid_current;
/*
if (max_grid_current != grid_get_max_current())
{
send_max_grid_current(grid_get_max_current());
}
}*/
break;
case LoToHi_lock_state_tag:
@@ -414,7 +414,7 @@ void master_sync_start()
ESP_LOGI(TAG, "Master SYNC Serial");
init();
xTaskCreate(rx_task, "uart_rx_task", 1024 * 5, NULL, 5, NULL);
xTaskCreate(rx_task, "uart_rx_task", 1024 * 5, NULL, 3, NULL);
//xTaskCreate(tx_task, "uart_tx_task", 1024 * 5, NULL, 5, NULL);
}

View File

@@ -322,7 +322,7 @@ void handlePacket(uint8_t *buf, int len)
case HiToLo_max_grid_current_tag:
printf("Received max_grid_current %i\n",
(int)msg_in.payload.max_grid_current);
grid_set_max_current((int)msg_in.payload.max_grid_current);
//grid_set_max_current((int)msg_in.payload.max_grid_current);
break;
case HiToLo_allow_power_on_tag:
printf("Received allow_poweron %i\n",
@@ -335,8 +335,8 @@ void handlePacket(uint8_t *buf, int len)
printf("Received grid_current %i\n",
(int)msg_in.payload.grid_current);
setMaxGridCurrent(grid_get_max_current() * 10);
setLiveGridCurrent((int)msg_in.payload.grid_current);
//setMaxGridCurrent(grid_get_max_current() * 10);
//setLiveGridCurrent((int)msg_in.payload.grid_current);
break;
default:
@@ -436,7 +436,7 @@ static void tx_task(void *arg)
vTaskDelay(5000 / portTICK_PERIOD_MS);
send_max_grid_current(grid_get_max_current());
//send_max_grid_current(grid_get_max_current());
vTaskDelay(5000 / portTICK_PERIOD_MS);
// send_lock_state(false);
@@ -500,8 +500,8 @@ void slave_sync_start()
ESP_LOGI(TAG, "Starting SYNC Serial");
init();
xTaskCreate(rx_task, "uart_rx_task", 1024 * 5, NULL, 5, NULL);
xTaskCreate(tx_task, "uart_tx_task", 1024 * 5, NULL, 5, NULL);
xTaskCreate(rx_task, "uart_rx_task", 1024 * 5, NULL, 3, NULL);
xTaskCreate(tx_task, "uart_tx_task", 1024 * 5, NULL, 3, NULL);
}
void slave_sync_stop(void)