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

@@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-evse)
project(chargeflow)

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)

View File

@@ -1,237 +1,181 @@
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_ota_ops.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_spiffs.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "evse_api.h"
#include "evse_manager.h"
#include "peripherals.h"
#include "led.h"
#include "api.h"
#include "protocols.h"
#include "meter_zigbee.h"
#include "board_config.h"
#include "wifi.h"
#include "board_config.h"
#include "logger.h"
#include "loadbalancer.h"
#include "rest_main.h"
#include "peripherals.h"
#include "protocols.h"
#include "evse_manager.h"
#include "evse_api.h"
#include "auth.h"
#include "loadbalancer.h"
#include "meter_manager.h"
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
#define AP_CONNECTION_TIMEOUT 60000 // 60sec
#define RESET_HOLD_TIME 10000 // 10sec
#define PRESS_BIT BIT0
#define RELEASED_BIT BIT1
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
#define AP_CONNECTION_TIMEOUT 120000
#define RESET_HOLD_TIME 10000
#define DEBOUNCE_TIME_MS 50
#define PRESS_BIT BIT0
#define RELEASED_BIT BIT1
static const char *TAG = "app_main";
static TaskHandle_t user_input_task;
static bool pressed = false; // Variável para verificar se o botão foi pressionado
static TickType_t press_tick = 0; // Variável para armazenar o tempo de pressionamento do botão
static TickType_t press_tick = 0;
static TickType_t last_interrupt_tick = 0;
static bool pressed = false;
static void reset_and_reboot(void)
{
ESP_LOGW(TAG, "All settings will be erased...");
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_LOGW(TAG, "Rebooting...");
vTaskDelay(pdMS_TO_TICKS(500));
esp_restart();
}
static void wifi_event_task_func(void *param)
{
EventBits_t mode_bits;
while (true)
{
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT)
{
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT)
{
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_AP_DISCONNECTED_BIT));
}
else
{
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
{
wifi_ap_stop();
}
}
}
else if (mode_bits & WIFI_STA_MODE_BIT)
{
if (xEventGroupWaitBits(wifi_event_group, WIFI_STA_CONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_CONNECTED_BIT)
{
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_DISCONNECTED_BIT));
}
}
}
}
static void handle_button_press(void)
{
if (xTaskGetTickCount() - press_tick >= pdMS_TO_TICKS(RESET_HOLD_TIME)) {
evse_set_available(false);
reset_and_reboot();
} else {
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
wifi_ap_start();
}
}
}
static void handle_button_release(void)
{
// Lógica quando o botão for liberado
pressed = false;
}
static void user_input_task_func(void *param)
{
uint32_t notification;
while (true)
{
if (xTaskNotifyWait(0x00, 0xff, &notification, portMAX_DELAY))
{
if (notification & PRESS_BIT)
{
press_tick = xTaskGetTickCount();
pressed = true;
}
if (notification & RELEASED_BIT)
{
if (pressed)
{
handle_button_press();
}
else
{
handle_button_release();
}
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg)
{
BaseType_t higher_task_woken = pdFALSE;
if (!gpio_get_level(board_config.button_wifi_gpio))
{
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
}
else
{
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken)
{
portYIELD_FROM_ISR();
}
}
static void button_init(void)
{
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
static void fs_info(esp_vfs_spiffs_conf_t *conf)
{
//
// File system (SPIFFS) init and info
//
static void fs_info(esp_vfs_spiffs_conf_t *conf) {
size_t total = 0, used = 0;
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get partition %s information %s", conf->partition_label, esp_err_to_name(ret));
}
if (ret == ESP_OK)
ESP_LOGI(TAG, "Partition %s: total: %d, used: %d", conf->partition_label, total, used);
else
{
ESP_LOGI(TAG, "Partition %s size: total: %d, used: %d", conf->partition_label, total, used);
}
ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
}
static void fs_init(void)
{
static void fs_init(void) {
esp_vfs_spiffs_conf_t cfg_conf = {
.base_path = "/cfg",
.partition_label = "cfg",
.max_files = 1,
.format_if_mount_failed = false};
esp_err_t ret = esp_vfs_spiffs_register(&cfg_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount SPIFFS partition 'cfg'. Error: %s", esp_err_to_name(ret));
return; // Ou reinicie o dispositivo dependendo do caso
}
.format_if_mount_failed = false
};
esp_vfs_spiffs_conf_t data_conf = {
.base_path = "/data",
.partition_label = "data",
.max_files = 5,
.format_if_mount_failed = true};
ret = esp_vfs_spiffs_register(&data_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount SPIFFS partition 'data'. Error: %s", esp_err_to_name(ret));
return; // Ou reinicie o dispositivo dependendo do caso
}
.format_if_mount_failed = true
};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
fs_info(&cfg_conf);
fs_info(&data_conf);
}
static bool ota_diagnostic(void)
{
// TODO diagnostic after ota
return true;
//
// Wi-Fi event monitoring task
//
static void wifi_event_task_func(void *param) {
EventBits_t mode_bits;
while (1) {
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT) {
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT) {
xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
} else {
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
//wifi_ap_stop();
}
}
} else if (mode_bits & WIFI_STA_MODE_BIT) {
xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
}
}
}
static void init_modules(void)
{
QueueHandle_t auth_queue = xQueueCreate(10, sizeof(auth_event_t));
//
// Botão e tratamento
//
static void handle_button_press(void) {
ESP_LOGI(TAG, "Ativando modo AP");
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
wifi_ap_start();
}
}
static void user_input_task_func(void *param) {
uint32_t notification;
while (1) {
if (xTaskNotifyWait(0x00, 0xFF, &notification, portMAX_DELAY)) {
if (notification & PRESS_BIT) {
press_tick = xTaskGetTickCount();
pressed = true;
ESP_LOGI(TAG, "Botão pressionado");
}
wifi_ini();
if (notification & RELEASED_BIT && pressed) {
pressed = false;
ESP_LOGI(TAG, "Botão liberado");
handle_button_press();
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg) {
BaseType_t higher_task_woken = pdFALSE;
TickType_t now = xTaskGetTickCountFromISR();
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) return;
last_interrupt_tick = now;
if (!gpio_get_level(board_config.button_wifi_gpio)) {
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
} else {
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken) {
portYIELD_FROM_ISR();
}
}
static void button_init(void) {
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE
};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
//
// Inicialização dos módulos do sistema
//
static void init_modules(void) {
peripherals_init();
api_init();
//api_init();
ESP_ERROR_CHECK(rest_server_init("/data"));
protocols_init();
evse_manager_init();
evse_init(); // Cria a task para FSM
button_init();
auth_init();
auth_set_event_queue(auth_queue);
evse_manager_start(auth_queue);
loadbalancer_init();
meter_manager_grid_init();
meter_manager_grid_start();
//meter_manager_evse_init();
// Outros módulos (descomente conforme necessário)
// meter_init();
@@ -244,36 +188,18 @@ static void init_modules(void)
// slave_sync_start();
}
void app_main(void)
{
//
// Função principal do firmware
//
void app_main(void) {
logger_init();
esp_log_set_vprintf(logger_vprintf);
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running partition: %s", running->label);
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(TAG, "OTA pending verify");
if (ota_diagnostic())
{
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
ESP_LOGE(TAG, "Diagnostics failed! Starting rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
esp_reset_reason_t reason = esp_reset_reason();
ESP_LOGI(TAG, "Reset reason: %d", reason);
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_LOGW(TAG, "Erasing NVS flash");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
@@ -286,13 +212,10 @@ void app_main(void)
ESP_ERROR_CHECK(gpio_install_isr_service(0));
board_config_load();
wifi_ini();
//wifi_ap_start();
init_modules();
xTaskCreate(wifi_event_task_func, "wifi_event_task", 4 * 1024, NULL, 5, NULL);
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 5, &user_input_task);
// Loop principal não é necessário se tudo roda por tasks
xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task);
}

375
main_back
View File

@@ -1,375 +0,0 @@
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_ota_ops.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"
#include "evse_api.h"
#include "peripherals.h"
#include "led.h"
// #include "api.h"
// #include "protocols.h"
#include "serial_mt.h"
#include "board_config.h"
// #include "wifi.h"
#include "logger.h"
#include "ocpp.h"
#include "currentshaper.h"
// #include "serial_mdb.h"
#include "meter.h"
// #include "rc522_2.h"
//#include "main_wiegand.h"
#include "app_main.h"
// #include "sync_slave.h"
// #include "sync_master.h"
// #define AP_CONNECTION_TIMEOUT 60000 // 60sec
// #define RESET_HOLD_TIME 10000 // 10sec
// #define PRESS_BIT BIT0
// #define RELEASED_BIT BIT1
static const char *TAG = "app_main";
// static TaskHandle_t user_input_task;
static TaskHandle_t evse_task;
static evse_state_t led_state = -1;
/*
static void reset_and_reboot(void)
{
ESP_LOGW(TAG, "All settings will be erased...");
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_LOGW(TAG, "Rebooting...");
vTaskDelay(pdMS_TO_TICKS(500));
esp_restart();
}
static void wifi_event_task_func(void *param)
{
EventBits_t mode_bits;
while (true)
{
led_set_off(LED_ID_WIFI);
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT)
{
led_set_state(LED_ID_WIFI, 100, 900);
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT)
{
led_set_state(LED_ID_WIFI, 1900, 100);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_AP_DISCONNECTED_BIT));
}
else
{
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
{
serial_mdb_set_meter_test(false);
wifi_ap_stop();
}
}
}
else if (mode_bits & WIFI_STA_MODE_BIT)
{
led_set_state(LED_ID_WIFI, 500, 500);
if (xEventGroupWaitBits(wifi_event_group, WIFI_STA_CONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_CONNECTED_BIT)
{
led_set_on(LED_ID_WIFI);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_DISCONNECTED_BIT));
}
}
}
}
static void user_input_task_func(void *param)
{
uint32_t notification;
bool pressed = false;
TickType_t press_tick = 0;
while (true)
{
if (xTaskNotifyWait(0x00, 0xff, &notification, portMAX_DELAY))
{
if (notification & PRESS_BIT)
{
press_tick = xTaskGetTickCount();
pressed = true;
}
if (notification & RELEASED_BIT)
{
if (pressed)
{ // sometimes after connect debug UART emit RELEASED_BIT without preceding PRESS_BIT
if (xTaskGetTickCount() - press_tick >= pdMS_TO_TICKS(RESET_HOLD_TIME))
{
evse_set_available(false);
reset_and_reboot();
}
else
{
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT))
{
wifi_ap_start();
serial_mdb_set_meter_test(true);
}
}
}
pressed = false;
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg)
{
BaseType_t higher_task_woken = pdFALSE;
if (!gpio_get_level(board_config.button_wifi_gpio))
{
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
}
else
{
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken)
{
portYIELD_FROM_ISR();
}
}
static void button_init(void)
{
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
static void button_init(void)
{
gpio_set_direction(GPIO_NUM_25, GPIO_MODE_OUTPUT);
gpio_pulldown_en(GPIO_NUM_25);
gpio_set_level(GPIO_NUM_25, 0);
}*/
static void fs_info(esp_vfs_spiffs_conf_t *conf)
{
size_t total = 0, used = 0;
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get partition %s information %s", conf->partition_label, esp_err_to_name(ret));
}
else
{
ESP_LOGI(TAG, "Partition %s size: total: %d, used: %d", conf->partition_label, total, used);
}
}
static void fs_init(void)
{
esp_vfs_spiffs_conf_t cfg_conf = {
.base_path = "/cfg",
.partition_label = "cfg",
.max_files = 1,
.format_if_mount_failed = false};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
esp_vfs_spiffs_conf_t data_conf = {
.base_path = "/data",
.partition_label = "data",
.max_files = 5,
.format_if_mount_failed = true};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
fs_info(&cfg_conf);
fs_info(&data_conf);
}
static bool ota_diagnostic(void)
{
// TODO diagnostic after ota
return true;
}
static void update_leds(void)
{
if (led_state != evse_get_state())
{
led_state = evse_get_state();
switch (led_state)
{
case EVSE_STATE_A:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_ERROR);
led_set_on(LED_ID_WIFI);
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_on(LED_ID_CHARGING);
break;
case EVSE_STATE_C1:
case EVSE_STATE_D1:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_CHARGING, 1000, 1000);
break;
case EVSE_STATE_C2:
case EVSE_STATE_D2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_CHARGING, 1000, 500);
led_set_buzzer();
break;
case EVSE_STATE_E:
led_set_off(LED_ID_WIFI);
led_set_off(LED_ID_CHARGING);
led_set_state(LED_ID_ERROR, 500, 500);
break;
case EVSE_STATE_F:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_ERROR, 500, 500);
break;
}
}
}
static void evse_task_func(void *param)
{
while (true)
{
evse_process();
update_leds();
// ESP_LOGI(TAG, "getPower %d", getDataMeter().wattA);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main(void)
{
logger_init();
esp_log_set_vprintf(logger_vprintf);
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running partition: %s", running->label);
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(TAG, "OTA pending verify");
if (ota_diagnostic())
{
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_LOGW(TAG, "Erasing NVS flash");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
fs_init();
// ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(gpio_install_isr_service(0));
board_config_load();
// wifi_ini();
peripherals_init();
// api_init();
// protocols_init();
evse_init();
// button_init();
// xTaskCreate(wifi_event_task_func, "wifi_event_task", 4 * 1024, NULL, 5, NULL);
// xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 5, &user_input_task);
//meter_init();
// ocpp_start();
// serial_mdb_start();
currentshaper_start();
// initRc522();
// initWiegand();
serial_mt_start();
// master_sync_start();
// slave_sync_start();
xTaskCreate(evse_task_func, "evse_task", 20 * 1024, NULL, 5, &evse_task);
app_start();
/*
while (true)
{
evse_process();
update_leds();
// ESP_LOGI(TAG, "getPower %d", getDataMeter().wattA);
vTaskDelay(pdMS_TO_TICKS(100));
}
*/
}

View File

@@ -1,348 +0,0 @@
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_ota_ops.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"
#include "evse_api.h"
#include "peripherals.h"
#include "led.h"
#include "api.h"
#include "protocols.h"
#include "serial_mt.h"
#include "board_config.h"
#include "wifi.h"
#include "logger.h"
// #include "ocpp.h"
// #include "currentshaper.h"
// #include "serial_mdb.h"
// #include "meter.h"
// #include "rc522_2.h"
// #include "main_wiegand.h"
// #include "app_main.h"
// #include "sync_slave.h"
// #include "sync_master.h"
#define AP_CONNECTION_TIMEOUT 60000 // 60sec
#define RESET_HOLD_TIME 10000 // 10sec
#define PRESS_BIT BIT0
#define RELEASED_BIT BIT1
static const char *TAG = "app_main";
static TaskHandle_t user_input_task;
static evse_state_t led_state = -1;
static void reset_and_reboot(void)
{
ESP_LOGW(TAG, "All settings will be erased...");
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_LOGW(TAG, "Rebooting...");
vTaskDelay(pdMS_TO_TICKS(500));
esp_restart();
}
static void wifi_event_task_func(void *param)
{
EventBits_t mode_bits;
while (true)
{
// led_set_off(LED_ID_WIFI);
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT)
{
// led_set_state(LED_ID_WIFI, 100, 900);
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT)
{
// led_set_state(LED_ID_WIFI, 1900, 100);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_AP_DISCONNECTED_BIT));
}
else
{
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
{
// serial_mdb_set_meter_test(false);
wifi_ap_stop();
}
}
}
else if (mode_bits & WIFI_STA_MODE_BIT)
{
// led_set_state(LED_ID_WIFI, 500, 500);
if (xEventGroupWaitBits(wifi_event_group, WIFI_STA_CONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_CONNECTED_BIT)
{
// led_set_on(LED_ID_WIFI);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_DISCONNECTED_BIT));
}
}
}
}
static void user_input_task_func(void *param)
{
uint32_t notification;
bool pressed = false;
TickType_t press_tick = 0;
while (true)
{
if (xTaskNotifyWait(0x00, 0xff, &notification, portMAX_DELAY))
{
if (notification & PRESS_BIT)
{
press_tick = xTaskGetTickCount();
pressed = true;
}
if (notification & RELEASED_BIT)
{
if (pressed)
{ // sometimes after connect debug UART emit RELEASED_BIT without preceding PRESS_BIT
if (xTaskGetTickCount() - press_tick >= pdMS_TO_TICKS(RESET_HOLD_TIME))
{
evse_set_available(false);
reset_and_reboot();
}
else
{
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT))
{
led_set_buzzer();
wifi_ap_start();
// serial_mdb_set_meter_test(true);
}
}
}
pressed = false;
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg)
{
BaseType_t higher_task_woken = pdFALSE;
if (!gpio_get_level(board_config.button_wifi_gpio))
{
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
}
else
{
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken)
{
portYIELD_FROM_ISR();
}
}
static void button_init(void)
{
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
static void fs_info(esp_vfs_spiffs_conf_t *conf)
{
size_t total = 0, used = 0;
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get partition %s information %s", conf->partition_label, esp_err_to_name(ret));
}
else
{
ESP_LOGI(TAG, "Partition %s size: total: %d, used: %d", conf->partition_label, total, used);
}
}
static void fs_init(void)
{
esp_vfs_spiffs_conf_t cfg_conf = {
.base_path = "/cfg",
.partition_label = "cfg",
.max_files = 1,
.format_if_mount_failed = false};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
esp_vfs_spiffs_conf_t data_conf = {
.base_path = "/data",
.partition_label = "data",
.max_files = 5,
.format_if_mount_failed = true};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
fs_info(&cfg_conf);
fs_info(&data_conf);
}
static bool ota_diagnostic(void)
{
// TODO diagnostic after ota
return true;
}
static void update_leds(void)
{
if (led_state != evse_get_state())
{
led_state = evse_get_state();
switch (led_state)
{
case EVSE_STATE_A:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_ERROR);
led_set_on(LED_ID_WIFI);
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_on(LED_ID_CHARGING);
break;
case EVSE_STATE_C1:
case EVSE_STATE_D1:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_CHARGING, 1000, 1000);
break;
case EVSE_STATE_C2:
case EVSE_STATE_D2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_CHARGING, 1000, 500);
led_set_buzzer();
break;
case EVSE_STATE_E:
led_set_off(LED_ID_WIFI);
led_set_off(LED_ID_CHARGING);
led_set_state(LED_ID_ERROR, 500, 500);
break;
case EVSE_STATE_F:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_ERROR, 500, 500);
break;
}
}
}
void app_main(void)
{
logger_init();
esp_log_set_vprintf(logger_vprintf);
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running partition: %s", running->label);
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(TAG, "OTA pending verify");
if (ota_diagnostic())
{
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_LOGW(TAG, "Erasing NVS flash");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
fs_init();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(gpio_install_isr_service(0));
board_config_load();
wifi_ini();
peripherals_init();
api_init();
protocols_init();
evse_init();
button_init();
xTaskCreate(wifi_event_task_func, "wifi_event_task", 4 * 1024, NULL, 5, NULL);
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 5, &user_input_task);
// meter_init();
// ocpp_start();
// serial_mdb_start();
// currentshaper_start();
// initRc522();
// initWiegand();
// serial_mt_start();
// master_sync_start();
// slave_sync_start();
while (true)
{
evse_process();
update_leds();
// ESP_LOGI(TAG, "getPower %d", getDataMeter().wattA);
vTaskDelay(pdMS_TO_TICKS(100));
}
}

View File

@@ -1,346 +0,0 @@
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_ota_ops.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"
#include "evse_api.h"
#include "peripherals.h"
#include "led.h"
#include "api.h"
#include "protocols.h"
#include "serial_mt.h"
#include "board_config.h"
#include "wifi.h"
#include "logger.h"
// #include "ocpp.h"
// #include "currentshaper.h"
// #include "serial_mdb.h"
// #include "meter.h"
// #include "rc522_2.h"
// #include "main_wiegand.h"
// #include "app_main.h"
// #include "sync_slave.h"
// #include "sync_master.h"
#define AP_CONNECTION_TIMEOUT 60000 // 60sec
#define RESET_HOLD_TIME 10000 // 10sec
#define PRESS_BIT BIT0
#define RELEASED_BIT BIT1
static const char *TAG = "app_main";
static TaskHandle_t user_input_task;
static evse_state_t led_state = -1;
static void reset_and_reboot(void)
{
ESP_LOGW(TAG, "All settings will be erased...");
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_LOGW(TAG, "Rebooting...");
vTaskDelay(pdMS_TO_TICKS(500));
esp_restart();
}
static void wifi_event_task_func(void *param)
{
EventBits_t mode_bits;
while (true)
{
// led_set_off(LED_ID_WIFI);
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (mode_bits & WIFI_AP_MODE_BIT)
{
// led_set_state(LED_ID_WIFI, 100, 900);
if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT)
{
// led_set_state(LED_ID_WIFI, 1900, 100);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_AP_DISCONNECTED_BIT));
}
else
{
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
{
// serial_mdb_set_meter_test(false);
wifi_ap_stop();
}
}
}
else if (mode_bits & WIFI_STA_MODE_BIT)
{
// led_set_state(LED_ID_WIFI, 500, 500);
if (xEventGroupWaitBits(wifi_event_group, WIFI_STA_CONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_CONNECTED_BIT)
{
// led_set_on(LED_ID_WIFI);
do
{
} while (!(xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY) & WIFI_STA_DISCONNECTED_BIT));
}
}
}
}
static void user_input_task_func(void *param)
{
uint32_t notification;
bool pressed = false;
TickType_t press_tick = 0;
while (true)
{
if (xTaskNotifyWait(0x00, 0xff, &notification, portMAX_DELAY))
{
if (notification & PRESS_BIT)
{
press_tick = xTaskGetTickCount();
pressed = true;
}
if (notification & RELEASED_BIT)
{
if (pressed)
{ // sometimes after connect debug UART emit RELEASED_BIT without preceding PRESS_BIT
if (xTaskGetTickCount() - press_tick >= pdMS_TO_TICKS(RESET_HOLD_TIME))
{
evse_set_available(false);
reset_and_reboot();
}
else
{
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT))
{
wifi_ap_start();
// serial_mdb_set_meter_test(true);
}
}
}
pressed = false;
}
}
}
}
static void IRAM_ATTR button_isr_handler(void *arg)
{
BaseType_t higher_task_woken = pdFALSE;
if (!gpio_get_level(board_config.button_wifi_gpio))
{
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
}
else
{
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken);
}
if (higher_task_woken)
{
portYIELD_FROM_ISR();
}
}
static void button_init(void)
{
gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE};
ESP_ERROR_CHECK(gpio_config(&conf));
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}
static void fs_info(esp_vfs_spiffs_conf_t *conf)
{
size_t total = 0, used = 0;
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get partition %s information %s", conf->partition_label, esp_err_to_name(ret));
}
else
{
ESP_LOGI(TAG, "Partition %s size: total: %d, used: %d", conf->partition_label, total, used);
}
}
static void fs_init(void)
{
esp_vfs_spiffs_conf_t cfg_conf = {
.base_path = "/cfg",
.partition_label = "cfg",
.max_files = 1,
.format_if_mount_failed = false};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
esp_vfs_spiffs_conf_t data_conf = {
.base_path = "/data",
.partition_label = "data",
.max_files = 5,
.format_if_mount_failed = true};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
fs_info(&cfg_conf);
fs_info(&data_conf);
}
static bool ota_diagnostic(void)
{
// TODO diagnostic after ota
return true;
}
static void update_leds(void)
{
if (led_state != evse_get_state())
{
led_state = evse_get_state();
switch (led_state)
{
case EVSE_STATE_A:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_ERROR);
led_set_on(LED_ID_WIFI);
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_on(LED_ID_CHARGING);
break;
case EVSE_STATE_C1:
case EVSE_STATE_D1:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
break;
case EVSE_STATE_C2:
case EVSE_STATE_D2:
led_set_off(LED_ID_ERROR);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_CHARGING, 1000, 500);
led_set_buzzer();
break;
case EVSE_STATE_E:
led_set_off(LED_ID_WIFI);
led_set_off(LED_ID_CHARGING);
led_set_state(LED_ID_ERROR, 500, 500);
break;
case EVSE_STATE_F:
led_set_off(LED_ID_CHARGING);
led_set_off(LED_ID_WIFI);
led_set_state(LED_ID_ERROR, 500, 500);
break;
}
}
}
void app_main(void)
{
logger_init();
esp_log_set_vprintf(logger_vprintf);
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running partition: %s", running->label);
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(TAG, "OTA pending verify");
if (ota_diagnostic())
{
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_LOGW(TAG, "Erasing NVS flash");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
fs_init();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(gpio_install_isr_service(0));
board_config_load();
wifi_ini();
peripherals_init();
api_init();
protocols_init();
evse_init();
button_init();
xTaskCreate(wifi_event_task_func, "wifi_event_task", 4 * 1024, NULL, 5, NULL);
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 5, &user_input_task);
// meter_init();
// ocpp_start();
// serial_mdb_start();
// currentshaper_start();
// initRc522();
// initWiegand();
// serial_mt_start();
// master_sync_start();
// slave_sync_start();
while (true)
{
evse_process();
update_leds();
// ESP_LOGI(TAG, "getPower %d", getDataMeter().wattA);
vTaskDelay(pdMS_TO_TICKS(100));
}
}

View File

View File

View File

0
managed_components/espressif__cmake_utilities/Kconfig Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

View File

Some files were not shown because too many files have changed in this diff Show More