new buzzer component

This commit is contained in:
2025-07-22 00:09:58 +01:00
parent 84f106eee5
commit bd587a10c0
58 changed files with 3215 additions and 6961 deletions

View File

@@ -9,6 +9,6 @@ idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "lib/cAT/src" INCLUDE_DIRS "include" "lib/cAT/src"
PRIV_INCLUDE_DIRS "src" PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash app_update json driver esp_http_client esp_netif esp_wifi esp_timer esp_hw_support PRIV_REQUIRES nvs_flash app_update json driver esp_http_client esp_netif esp_wifi esp_timer esp_hw_support
REQUIRES network config evse peripherals protocols ocpp) REQUIRES network config evse peripherals protocols ocpp auth)
set_source_files_properties(lib/cAT/src/cat.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized) set_source_files_properties(lib/cAT/src/cat.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)

View File

@@ -9,10 +9,11 @@
#include "json.h" #include "json.h"
#include "mqtt.h" #include "mqtt.h"
#include "wifi.h" #include "network.h"
#include "timeout_utils.h" #include "timeout_utils.h"
#include "evse_error.h" #include "evse_error.h"
#include "evse_api.h" #include "evse_api.h"
#include "auth.h"
#include "evse_limits.h" #include "evse_limits.h"
#include "evse_state.h" #include "evse_state.h"
#include "evse_config.h" #include "evse_config.h"
@@ -44,9 +45,9 @@ cJSON *json_get_evse_config(void)
cJSON *root = cJSON_CreateObject(); cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current()); cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current());
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current() / 10.0); cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current() / 10.0); cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current());
cJSON_AddBoolToObject(root, "requireAuth", evse_is_require_auth()); cJSON_AddBoolToObject(root, "requireAuth", auth_is_enabled());
cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet()); cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet());
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm()); cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold()); cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold());
@@ -84,15 +85,15 @@ esp_err_t json_set_evse_config(cJSON *root)
} }
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "chargingCurrent"))) if (cJSON_IsNumber(cJSON_GetObjectItem(root, "chargingCurrent")))
{ {
RETURN_ON_ERROR(evse_set_charging_current(cJSON_GetObjectItem(root, "chargingCurrent")->valuedouble * 10)); RETURN_ON_ERROR(evse_set_charging_current(cJSON_GetObjectItem(root, "chargingCurrent")->valuedouble));
} }
if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultChargingCurrent"))) if (cJSON_IsNumber(cJSON_GetObjectItem(root, "defaultChargingCurrent")))
{ {
RETURN_ON_ERROR(evse_set_default_charging_current(cJSON_GetObjectItem(root, "defaultChargingCurrent")->valuedouble * 10)); RETURN_ON_ERROR(evse_set_default_charging_current(cJSON_GetObjectItem(root, "defaultChargingCurrent")->valuedouble));
} }
if (cJSON_IsBool(cJSON_GetObjectItem(root, "requireAuth"))) if (cJSON_IsBool(cJSON_GetObjectItem(root, "requireAuth")))
{ {
evse_set_require_auth(cJSON_IsTrue(cJSON_GetObjectItem(root, "requireAuth"))); auth_set_enabled(cJSON_IsTrue(cJSON_GetObjectItem(root, "requireAuth")));
} }
if (cJSON_IsBool(cJSON_GetObjectItem(root, "socketOutlet"))) if (cJSON_IsBool(cJSON_GetObjectItem(root, "socketOutlet")))
{ {
@@ -342,7 +343,7 @@ cJSON *json_get_state(void)
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state())); cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
cJSON_AddBoolToObject(root, "available", evse_config_is_available()); cJSON_AddBoolToObject(root, "available", evse_config_is_available());
cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled()); cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled());
cJSON_AddBoolToObject(root, "pendingAuth", evse_is_require_auth()); cJSON_AddBoolToObject(root, "pendingAuth", auth_is_enabled());
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached()); cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
uint32_t error = evse_error_get_bits(); uint32_t error = evse_error_get_bits();
@@ -389,14 +390,24 @@ cJSON *json_get_state(void)
} }
cJSON_AddNumberToObject(root, "sessionTime", evse_get_session_start()); // dentro da sua função de gerar JSON:
evse_session_t sess;
if (evse_get_session(&sess)) {
// Há sessão atual ou última disponível
cJSON_AddNumberToObject(root, "sessionTime", (double)sess.start_tick);
cJSON_AddNumberToObject(root, "chargingTime", (double)sess.duration_s);
cJSON_AddNumberToObject(root, "consumption", (double)sess.energy_wh);
} else {
// Nenhuma sessão disponível
cJSON_AddNullToObject(root, "sessionTime");
cJSON_AddNumberToObject(root, "chargingTime", 0); cJSON_AddNumberToObject(root, "chargingTime", 0);
cJSON_AddNumberToObject(root, "consumption", 0); cJSON_AddNumberToObject(root, "consumption", 0);
}
// 1) Arrays temporários para ler dados do medidor // 1) Arrays temporários para ler dados do medidor
float voltage_f[EVSE_METER_PHASE_COUNT]; float voltage_f[EVSE_METER_PHASE_COUNT];
float current_f[EVSE_METER_PHASE_COUNT]; float current_f[EVSE_METER_PHASE_COUNT];
uint32_t power_w[ EVSE_METER_PHASE_COUNT]; int power_w[ EVSE_METER_PHASE_COUNT];
// 2) Leitura dos valores via API pública // 2) Leitura dos valores via API pública
evse_meter_get_voltage(voltage_f); // já em volts evse_meter_get_voltage(voltage_f); // já em volts
@@ -408,7 +419,7 @@ cJSON *json_get_state(void)
// 6) Arrays de tensão e corrente // 6) Arrays de tensão e corrente
cJSON_AddItemToObject(root, "power", cJSON_AddItemToObject(root, "power",
cJSON_CreateFloatArray(power_w, EVSE_METER_PHASE_COUNT)); cJSON_CreateIntArray(power_w, EVSE_METER_PHASE_COUNT));
cJSON_AddItemToObject(root, "voltage", cJSON_AddItemToObject(root, "voltage",
cJSON_CreateFloatArray(voltage_f, EVSE_METER_PHASE_COUNT)); cJSON_CreateFloatArray(voltage_f, EVSE_METER_PHASE_COUNT));
cJSON_AddItemToObject(root, "current", cJSON_AddItemToObject(root, "current",

View File

@@ -4,7 +4,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "timeout_utils.h" #include "timeout_utils.h"
#include "wifi.h" #include "network.h"
//#include "rest.h" //#include "rest.h"
static void restart_func(void* arg) static void restart_func(void* arg)

View File

@@ -8,74 +8,105 @@
extern "C" { extern "C" {
#endif #endif
/// Tamanho máximo de uma tag RFID (incluindo '\0') /// Maximum length of an RFID tag (including null terminator)
#define AUTH_TAG_MAX_LEN 20 #define AUTH_TAG_MAX_LEN 30
/// Estrutura de evento emitida após leitura de uma tag /// Event structure emitted after a tag is read
typedef struct { typedef struct {
char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida char tag[AUTH_TAG_MAX_LEN]; ///< The tag that was read
bool authorized; ///< true se a tag for reconhecida como válida bool authorized; ///< true if the tag is valid
} auth_event_t; } auth_event_t;
/** /**
* @brief Inicializa o sistema de autenticação. * @brief Initializes the authentication system.
* *
* - Carrega a configuração (enabled) da NVS * - Loads configuration (enabled/disabled) from NVS
* - Inicia o leitor Wiegand * - Starts the Wiegand reader
* - Emite evento AUTH_EVENT_INIT com estado atual * - Emits AUTH_EVENT_INIT with current status
*/ */
void auth_init(void); void auth_init(void);
/** /**
* @brief Ativa ou desativa o uso de autenticação via RFID. * @brief Enables or disables RFID-based authentication.
* *
* Esta configuração é persistida em NVS. Se desativado, o sistema * This setting is persisted in NVS. If disabled,
* considerará todas as autorizações como aceitas. * all tags will be treated as authorized.
* *
* @param value true para ativar, false para desativar * @param value true to enable authentication, false to disable
*/ */
void auth_set_enabled(bool value); void auth_set_enabled(bool value);
/** /**
* @brief Verifica se o sistema de autenticação está habilitado. * @brief Checks whether authentication is currently enabled.
*
* @return true if enabled, false if disabled
*/ */
bool auth_is_enabled(void); bool auth_is_enabled(void);
/** /**
* @brief Adiciona uma nova tag RFID à lista de autorizadas. * @brief Adds a new RFID tag to the authorized list.
* *
* @param tag String da tag (máx AUTH_TAG_MAX_LEN-1) * @param tag The RFID tag (max AUTH_TAG_MAX_LEN-1 characters)
* @return true se a tag foi adicionada, false se já existia ou inválida * @return true if the tag was added successfully,
* false if it already exists or is invalid
*/ */
bool auth_add_tag(const char *tag); bool auth_add_tag(const char *tag);
/** /**
* @brief Remove uma tag previamente cadastrada. * @brief Removes an existing RFID tag from the authorized list.
* *
* @param tag String da tag * @param tag The tag to remove
* @return true se foi removida, false se não encontrada * @return true if the tag was removed, false if not found
*/ */
bool auth_remove_tag(const char *tag); bool auth_remove_tag(const char *tag);
/** /**
* @brief Verifica se uma tag já está registrada como válida. * @brief Checks whether a tag is already registered.
*
* @param tag The tag to check
* @return true if the tag exists, false otherwise
*/ */
bool auth_tag_exists(const char *tag); bool auth_tag_exists(const char *tag);
/** /**
* @brief Lista todas as tags válidas atualmente registradas (via logs). * @brief Logs all currently registered tags via ESP logging.
*/ */
void auth_list_tags(void); void auth_list_tags(void);
/** /**
* @brief Processa uma tag RFID lida (chamada normalmente pelo leitor). * @brief Processes a read RFID tag.
* *
* - Verifica validade * - Checks whether it's authorized
* - Emite evento AUTH_EVENT_TAG_PROCESSED * - Emits AUTH_EVENT_TAG_PROCESSED event
* - Inicia timer de expiração se autorizada * - Starts expiration timer if authorized
*
* @param tag The tag that was read
*/ */
void auth_process_tag(const char *tag); void auth_process_tag(const char *tag);
/**
* @brief Enables registration mode for the next tag read.
*
* When registration mode is active, the next tag read
* will be added to the authorized list automatically.
* Mode is deactivated after one tag is registered.
*/
void auth_wait_for_tag_registration(void);
/**
* @brief Returns the total number of registered tags.
*
* @return Number of valid tags
*/
int auth_get_tag_count(void);
/**
* @brief Returns the tag string at the given index.
*
* @param index The index (0 ≤ index < auth_get_tag_count())
* @return Pointer to the tag string, or NULL if index is invalid
*/
const char *auth_get_tag_by_index(int index);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -7,6 +7,7 @@ ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
typedef enum { typedef enum {
AUTH_EVENT_TAG_PROCESSED, AUTH_EVENT_TAG_PROCESSED,
AUTH_EVENT_TAG_SAVED,
AUTH_EVENT_ENABLED_CHANGED, AUTH_EVENT_ENABLED_CHANGED,
AUTH_EVENT_INIT, AUTH_EVENT_INIT,
} auth_event_id_t; } auth_event_id_t;

View File

@@ -1,37 +1,41 @@
/*
* auth.c
*/
#include "auth.h" #include "auth.h"
#include "auth_events.h" #include "auth_events.h"
#include "esp_event.h" #include "esp_event.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h> #include <freertos/queue.h>
#include <esp_log.h> #include <esp_log.h>
#include <string.h> #include <string.h>
#include "wiegand_reader.h" #include "wiegand_reader.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" #include "nvs.h"
#include "esp_random.h"
#define MAX_TAGS 50 #define MAX_TAGS 50
static const char *TAG = "Auth"; static const char *TAG = "Auth";
static bool enabled = false; static bool enabled = false;
static bool waiting_for_registration = false;
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
static int tag_count = 0; static int tag_count = 0;
// NVS keys
#define NVS_NAMESPACE "auth"
#define NVS_TAG_PREFIX "tag_"
#define NVS_TAG_COUNT_KEY "count"
#define NVS_ENABLED_KEY "enabled"
// =========================== // ===========================
// Persistência em NVS // NVS Persistence
// =========================== // ===========================
static void load_auth_config(void) { static void load_auth_config(void) {
nvs_handle_t handle; nvs_handle_t handle;
esp_err_t err = nvs_open("auth", NVS_READONLY, &handle); esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
if (err == ESP_OK) { if (err == ESP_OK) {
uint8_t val; uint8_t val;
if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) { if (nvs_get_u8(handle, NVS_ENABLED_KEY, &val) == ESP_OK) {
enabled = val; enabled = val;
ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled); ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled);
} }
@@ -43,8 +47,8 @@ static void load_auth_config(void) {
static void save_auth_config(void) { static void save_auth_config(void) {
nvs_handle_t handle; nvs_handle_t handle;
if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) { if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) == ESP_OK) {
nvs_set_u8(handle, "enabled", enabled); nvs_set_u8(handle, NVS_ENABLED_KEY, enabled);
nvs_commit(handle); nvs_commit(handle);
nvs_close(handle); nvs_close(handle);
ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled); ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled);
@@ -53,8 +57,60 @@ static void save_auth_config(void) {
} }
} }
static void load_tags_from_nvs(void) {
nvs_handle_t handle;
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
ESP_LOGW(TAG, "No stored tags in NVS");
return;
}
uint8_t count = 0;
if (nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count) != ESP_OK) {
nvs_close(handle);
return;
}
tag_count = 0;
for (int i = 0; i < count && i < MAX_TAGS; i++) {
char key[16];
char tag_buf[AUTH_TAG_MAX_LEN];
size_t len = sizeof(tag_buf);
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
if (nvs_get_str(handle, key, tag_buf, &len) == ESP_OK) {
strncpy(valid_tags[tag_count], tag_buf, AUTH_TAG_MAX_LEN - 1);
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
tag_count++;
}
}
nvs_close(handle);
ESP_LOGI(TAG, "Loaded %d tags from NVS", tag_count);
}
static void save_tags_to_nvs(void) {
nvs_handle_t handle;
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS to save tags");
return;
}
nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count);
for (int i = 0; i < tag_count; i++) {
char key[16];
snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
nvs_set_str(handle, key, valid_tags[i]);
}
nvs_commit(handle);
nvs_close(handle);
ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count);
}
// =========================== // ===========================
// Internos // Internal Helpers
// =========================== // ===========================
static bool is_tag_valid(const char *tag) { static bool is_tag_valid(const char *tag) {
@@ -63,15 +119,30 @@ static bool is_tag_valid(const char *tag) {
return true; return true;
} }
} }
return true; return false;
//TODO
//return false;
} }
// =========================== // ===========================
// API pública // Public API
// =========================== // ===========================
void auth_init(void) {
load_auth_config();
load_tags_from_nvs();
if (enabled) {
initWiegand();
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, "Initial AUTH state sent (enabled = %d)", enabled);
}
void auth_set_enabled(bool value) { void auth_set_enabled(bool value) {
enabled = value; enabled = value;
save_auth_config(); save_auth_config();
@@ -88,11 +159,13 @@ bool auth_is_enabled(void) {
bool auth_add_tag(const char *tag) { bool auth_add_tag(const char *tag) {
if (tag_count >= MAX_TAGS) return false; if (tag_count >= MAX_TAGS) return false;
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false; if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false;
if (is_tag_valid(tag)) return true; if (is_tag_valid(tag)) return true; // Already exists
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
tag_count++; tag_count++;
save_tags_to_nvs();
ESP_LOGI(TAG, "Tag added: %s", tag); ESP_LOGI(TAG, "Tag added: %s", tag);
return true; return true;
} }
@@ -104,6 +177,8 @@ bool auth_remove_tag(const char *tag) {
strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
} }
tag_count--; tag_count--;
save_tags_to_nvs();
ESP_LOGI(TAG, "Tag removed: %s", tag); ESP_LOGI(TAG, "Tag removed: %s", tag);
return true; return true;
} }
@@ -122,20 +197,9 @@ void auth_list_tags(void) {
} }
} }
void auth_init(void) { void auth_wait_for_tag_registration(void) {
load_auth_config(); // carrega estado de ativação waiting_for_registration = true;
ESP_LOGI(TAG, "Tag registration mode enabled.");
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);
} }
void auth_process_tag(const char *tag) { void auth_process_tag(const char *tag) {
@@ -144,6 +208,22 @@ void auth_process_tag(const char *tag) {
return; return;
} }
if (waiting_for_registration) {
if (auth_add_tag(tag)) {
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 = true;
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &event, sizeof(event), portMAX_DELAY);
ESP_LOGI(TAG, "Tag registered: %s", tag);
} else {
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
}
waiting_for_registration = false;
return;
}
auth_tag_event_data_t event; auth_tag_event_data_t event;
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1); strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0'; event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
@@ -153,3 +233,12 @@ void auth_process_tag(const char *tag) {
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY); esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY);
} }
int auth_get_tag_count(void) {
return tag_count;
}
const char *auth_get_tag_by_index(int index) {
if (index < 0 || index >= tag_count) return NULL;
return valid_tags[index];
}

View File

@@ -34,7 +34,7 @@ static void wiegand_task(void *arg) {
return; return;
} }
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18, ESP_ERROR_CHECK(wiegand_reader_init(&reader, 21, 22,
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
data_packet_t p; data_packet_t p;

View File

@@ -0,0 +1,12 @@
set(srcs
"src/buzzer.c"
"src/buzzer_events.c"
)
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
REQUIRES esp_event
PRIV_REQUIRES driver nvs_flash esp_timer evse
)

View File

@@ -0,0 +1,2 @@
version: "2.6.0"
description: Authentication component

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void buzzer_init(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,26 @@
#pragma once
#include "esp_event.h"
ESP_EVENT_DECLARE_BASE(BUZZER_EVENTS);
typedef enum {
BUZZER_EVENT_PLAY_PATTERN,
} buzzer_event_id_t;
typedef enum {
BUZZER_PATTERN_NONE = 0,
BUZZER_PATTERN_PLUGGED,
BUZZER_PATTERN_UNPLUGGED,
BUZZER_PATTERN_CHARGING,
BUZZER_PATTERN_AP_START,
BUZZER_PATTERN_CARD_READ,
BUZZER_PATTERN_CARD_ADD,
BUZZER_PATTERN_CARD_DENIED,
BUZZER_PATTERN_MAX
} buzzer_pattern_id_t;
typedef struct {
buzzer_pattern_id_t pattern;
} buzzer_event_data_t;

168
components/buzzer/src/buzzer.c Executable file
View File

@@ -0,0 +1,168 @@
#include "buzzer_events.h"
#include "evse_events.h"
#include "auth_events.h"
#include "network_events.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define BUZZER_GPIO GPIO_NUM_27
static const char *TAG = "Buzzer";
typedef struct {
uint16_t on_ms;
uint16_t off_ms;
} buzzer_step_t;
// Padrões de buzzer
static const buzzer_step_t pattern_plugged[] = {{100, 100}, {200, 0}};
static const buzzer_step_t pattern_unplugged[] = {{150, 150}, {150, 150}, {150, 0}};
static const buzzer_step_t pattern_charging[] = {{80, 150}, {100, 120}, {120, 100}, {140, 0}};
static const buzzer_step_t pattern_ap_start[] = {{300, 150}, {300, 0}};
static const buzzer_step_t pattern_card_read[] = {{50, 50}, {50, 0}};
static const buzzer_step_t pattern_card_add[] = {{100, 100}, {100, 100}, {100, 0}};
static const buzzer_step_t pattern_card_denied[] = {{300, 100}, {300, 0}};
typedef struct {
const buzzer_step_t *steps;
size_t length;
} buzzer_pattern_t;
static const buzzer_pattern_t buzzer_patterns[] = {
[BUZZER_PATTERN_PLUGGED] = {pattern_plugged, sizeof(pattern_plugged) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_UNPLUGGED] = {pattern_unplugged, sizeof(pattern_unplugged) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CHARGING] = {pattern_charging, sizeof(pattern_charging) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_AP_START] = {pattern_ap_start, sizeof(pattern_ap_start) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_READ] = {pattern_card_read, sizeof(pattern_card_read) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_ADD] = {pattern_card_add, sizeof(pattern_card_add) / sizeof(buzzer_step_t)},
[BUZZER_PATTERN_CARD_DENIED] = {pattern_card_denied, sizeof(pattern_card_denied) / sizeof(buzzer_step_t)},
};
static void buzzer_on(void) { gpio_set_level(BUZZER_GPIO, 1); }
static void buzzer_off(void) { gpio_set_level(BUZZER_GPIO, 0); }
static void buzzer_execute(buzzer_pattern_id_t pattern_id) {
if ((int)pattern_id <= BUZZER_PATTERN_NONE || pattern_id >= BUZZER_PATTERN_MAX) {
ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
return;
}
ESP_LOGD(TAG, "Executing buzzer pattern ID: %d", pattern_id);
const buzzer_pattern_t *pattern = &buzzer_patterns[pattern_id];
for (size_t i = 0; i < pattern->length; i++) {
buzzer_on();
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].on_ms));
buzzer_off();
if (pattern->steps[i].off_ms > 0) {
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].off_ms));
}
}
}
static void buzzer_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
if (base != BUZZER_EVENTS || id != BUZZER_EVENT_PLAY_PATTERN || data == NULL) return;
buzzer_event_data_t *evt = (buzzer_event_data_t *)data;
if ((int)evt->pattern < BUZZER_PATTERN_NONE || evt->pattern >= BUZZER_PATTERN_MAX) {
ESP_LOGW(TAG, "Invalid buzzer pattern received: %d", evt->pattern);
return;
}
buzzer_execute(evt->pattern);
}
static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
if (base != EVSE_EVENTS || id != EVSE_EVENT_STATE_CHANGED || data == NULL) return;
const evse_state_event_data_t *evt = (const evse_state_event_data_t *)data;
ESP_LOGD(TAG, "EVSE event received: state = %d", evt->state);
buzzer_event_data_t buzzer_evt = {0};
switch (evt->state) {
case EVSE_STATE_EVENT_IDLE:
buzzer_evt.pattern = BUZZER_PATTERN_UNPLUGGED;
break;
case EVSE_STATE_EVENT_WAITING:
buzzer_evt.pattern = BUZZER_PATTERN_PLUGGED;
break;
case EVSE_STATE_EVENT_CHARGING:
buzzer_evt.pattern = BUZZER_PATTERN_CHARGING;
break;
case EVSE_STATE_EVENT_FAULT:
default:
return;
}
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}
static void network_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) {
if (id == NETWORK_EVENT_AP_STARTED) {
buzzer_event_data_t evt = {
.pattern = BUZZER_PATTERN_AP_START
};
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY);
}
}
static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data) {
if (base != AUTH_EVENTS || event_data == NULL) return;
buzzer_event_data_t buzzer_evt = {0};
if (id == AUTH_EVENT_TAG_PROCESSED) {
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)event_data;
ESP_LOGD(TAG, "AUTH processed: tag=%s authorized=%d", evt->tag, evt->authorized);
buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;
} else if (id == AUTH_EVENT_TAG_SAVED) {
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
} else {
return;
}
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}
void buzzer_init(void) {
gpio_config_t io = {
.pin_bit_mask = BIT64(BUZZER_GPIO),
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = 0,
.pull_up_en = 0,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io);
buzzer_off();
// Registro de handlers
ESP_ERROR_CHECK(esp_event_handler_register(BUZZER_EVENTS,
BUZZER_EVENT_PLAY_PATTERN,
buzzer_event_handler,
NULL));
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS,
EVSE_EVENT_STATE_CHANGED,
evse_event_handler,
NULL));
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS,
AUTH_EVENT_TAG_PROCESSED,
auth_event_handler,
NULL));
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS,
AUTH_EVENT_TAG_SAVED,
auth_event_handler,
NULL));
ESP_ERROR_CHECK(esp_event_handler_register(
NETWORK_EVENTS,
NETWORK_EVENT_AP_STARTED,
network_event_handler,
NULL
));
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d", BUZZER_GPIO);
}

View File

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

View File

@@ -10,6 +10,8 @@ set(srcs
evse_hardware.c evse_hardware.c
evse_pilot.c evse_pilot.c
evse_meter.c evse_meter.c
evse_session.c
evse_api.c
) )
idf_component_register( idf_component_register(

37
components/evse/evse_api.c Executable file
View File

@@ -0,0 +1,37 @@
// evse_api.c - Main EVSE control logic
#include "evse_fsm.h"
#include "evse_error.h"
#include "evse_limits.h"
#include "evse_config.h"
#include "evse_api.h"
#include "evse_session.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
static const char *TAG = "evse_api";
// ================================
// Public Configuration Interface
// ================================
void evse_set_enabled(bool value) {
ESP_LOGI(TAG, "Set enabled %d", value);
evse_config_set_enabled(value);
}
bool evse_is_available(void) {
return evse_config_is_available();
}
void evse_set_available(bool value) {
ESP_LOGI(TAG, "Set available %d", value);
evse_config_set_available(value);
}
bool evse_get_session(evse_session_t *out) {
return evse_session_get(out);
}

View File

@@ -50,8 +50,8 @@ void evse_check_defaults(void) {
// Charging current (default, persisted) // Charging current (default, persisted)
err = nvs_get_u16(nvs, "def_chrg_curr", &u16); err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) { if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current)) {
charging_current = max_charging_current * 10; charging_current = max_charging_current;
nvs_set_u16(nvs, "def_chrg_curr", charging_current); nvs_set_u16(nvs, "def_chrg_curr", charging_current);
needs_commit = true; needs_commit = true;
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current); ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
@@ -136,7 +136,7 @@ uint16_t evse_get_charging_current(void) {
} }
esp_err_t evse_set_charging_current(uint16_t value) { esp_err_t evse_set_charging_current(uint16_t value) {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
charging_current = value; charging_current = value;
nvs_set_u16(nvs, "def_chrg_curr", value); nvs_set_u16(nvs, "def_chrg_curr", value);
@@ -151,7 +151,7 @@ uint16_t evse_get_default_charging_current(void) {
} }
esp_err_t evse_set_default_charging_current(uint16_t value) { esp_err_t evse_set_default_charging_current(uint16_t value) {
if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
nvs_set_u16(nvs, "def_chrg_curr", value); nvs_set_u16(nvs, "def_chrg_curr", value);
return nvs_commit(nvs); return nvs_commit(nvs);
@@ -161,12 +161,17 @@ esp_err_t evse_set_default_charging_current(uint16_t value) {
// Runtime current (not saved) // Runtime current (not saved)
// ======================== // ========================
void evse_set_runtime_charging_current(uint16_t value) { 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); if (value > (max_charging_current)) {
return; value= max_charging_current;
} }
if (value < (MIN_CHARGING_CURRENT_LIMIT) ) {
value= MIN_CHARGING_CURRENT_LIMIT;
}
charging_current_runtime = value; charging_current_runtime = value;
ESP_LOGD(TAG, "Runtime charging current updated: %d", charging_current_runtime); ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
} }
uint16_t evse_get_runtime_charging_current(void) { uint16_t evse_get_runtime_charging_current(void) {
@@ -219,18 +224,6 @@ esp_err_t evse_set_temp_threshold(uint8_t value) {
return nvs_commit(nvs); return nvs_commit(nvs);
} }
// ========================
// Authentication
// ========================
bool evse_is_require_auth(void) {
return require_auth;
}
void evse_set_require_auth(bool value) {
require_auth = value;
nvs_set_u8(nvs, "require_auth", value);
nvs_commit(nvs);
}
// ======================== // ========================
// Availability // Availability

View File

@@ -5,6 +5,7 @@
#include "evse_limits.h" #include "evse_limits.h"
#include "evse_config.h" #include "evse_config.h"
#include "evse_api.h" #include "evse_api.h"
#include "evse_session.h"
#include "evse_pilot.h" #include "evse_pilot.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
@@ -71,25 +72,6 @@ void evse_process(void) {
xSemaphoreGive(mutex); xSemaphoreGive(mutex);
} }
// ================================
// Public Configuration Interface
// ================================
void evse_set_enabled(bool value) {
ESP_LOGI(TAG, "Set enabled %d", value);
evse_config_set_enabled(value);
}
bool evse_is_available(void) {
return evse_config_is_available();
}
void evse_set_available(bool value) {
ESP_LOGI(TAG, "Set available %d", value);
evse_config_set_available(value);
}
// ================================ // ================================
// Background Task // Background Task
// ================================ // ================================

View File

@@ -69,15 +69,16 @@ static void update_outputs(evse_state_t state) {
if (board_config.socket_lock && socket_outlet) { if (board_config.socket_lock && socket_outlet) {
socket_lock_set_locked(true); socket_lock_set_locked(true);
} }
if (rcm_test()) { if (rcm_test()) {
ESP_LOGI(TAG, "RCM self test passed"); //ESP_LOGI(TAG, "RCM self test passed");
} else { } else {
ESP_LOGW(TAG, "RCM self test failed"); //ESP_LOGW(TAG, "RCM self test failed");
} }
break; break;
case EVSE_STATE_B2: case EVSE_STATE_B2:
pilot_set_amps(MIN(current * 10, cable_max_current * 10)); pilot_set_amps(MIN(current, cable_max_current));
ac_relay_set_state(false); ac_relay_set_state(false);
break; break;
@@ -90,7 +91,7 @@ static void update_outputs(evse_state_t state) {
case EVSE_STATE_C2: case EVSE_STATE_C2:
case EVSE_STATE_D2: case EVSE_STATE_D2:
pilot_set_amps(MIN(current * 10, cable_max_current * 10)); pilot_set_amps(MIN(current, cable_max_current));
ac_relay_set_state(true); // Só chega aqui se não há erro! ac_relay_set_state(true); // Só chega aqui se não há erro!
break; break;
} }

View File

@@ -1,7 +1,9 @@
#include <inttypes.h> // for PRIu32
#include "evse_state.h" #include "evse_state.h"
#include "evse_api.h" #include "evse_api.h"
#include "evse_limits.h" #include "evse_limits.h"
#include "evse_meter.h" #include "evse_meter.h"
#include "evse_session.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@@ -134,37 +136,49 @@ bool evse_is_limit_reached(void) {
return evse_get_limit_reached(); return evse_get_limit_reached();
} }
// ======================== // ========================
// Limit checking logic // Limit checking logic
// This function must be called periodically while charging. // This function must be called periodically while charging.
// It will flag the session as "limit reached" when thresholds are violated. // It will flag the session as "limit reached" when thresholds are violated.
// ======================== // ========================
void evse_limits_check(void) { void evse_limits_check(void) {
evse_state_t state = evse_get_state(); // Only check during an active charging session
if (!evse_state_is_charging(state)) return; if (!evse_state_is_charging(evse_get_state())) {
return;
}
evse_session_t sess;
// Retrieve accumulated data for the current session
if (!evse_session_get(&sess) || !sess.is_current) {
// If there's no active session, abort
return;
}
bool reached = false; bool reached = false;
uint32_t energy = evse_meter_get_total_energy(); // 1) Energy consumption limit (Wh)
uint32_t power = evse_meter_get_instant_power(); if (consumption_limit > 0 && sess.energy_wh >= consumption_limit) {
TickType_t now = xTaskGetTickCount(); ESP_LOGW("EVSE_LIMITS",
TickType_t start = evse_get_session_start(); "Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh",
sess.energy_wh, consumption_limit);
if (consumption_limit > 0 && energy >= consumption_limit) {
ESP_LOGW("EVSE", "Energy limit reached");
reached = true; reached = true;
} }
if (charging_time_limit > 0 && // 2) Charging time limit (seconds)
(now - start) >= pdMS_TO_TICKS(charging_time_limit * 1000)) { if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit) {
ESP_LOGW("EVSE", "Charging time limit reached"); ESP_LOGW("EVSE_LIMITS",
"Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s",
sess.duration_s, charging_time_limit);
reached = true; reached = true;
} }
if (under_power_limit > 0 && power < under_power_limit) { // 3) Under-power limit (instantaneous power)
ESP_LOGW("EVSE", "Under power limit reached"); uint32_t inst_power = evse_meter_get_instant_power();
if (under_power_limit > 0 && inst_power < under_power_limit) {
ESP_LOGW("EVSE_LIMITS",
"Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W",
(uint32_t)inst_power,
(uint32_t)under_power_limit);
reached = true; reached = true;
} }

View File

@@ -5,6 +5,7 @@
#include "evse_config.h" #include "evse_config.h"
#include "evse_api.h" #include "evse_api.h"
#include "evse_meter.h" #include "evse_meter.h"
#include "evse_session.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@@ -89,6 +90,7 @@ void evse_manager_init(void) {
evse_hardware_init(); evse_hardware_init();
evse_state_init(); evse_state_init();
evse_meter_init(); evse_meter_init();
evse_session_init();
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL)); 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_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
@@ -105,6 +107,7 @@ void evse_manager_tick(void) {
evse_error_tick(); evse_error_tick();
evse_state_tick(); evse_state_tick();
evse_temperature_check(); evse_temperature_check();
evse_session_tick();
if (auth_enabled) { if (auth_enabled) {
// If the car is disconnected, revoke authorization // If the car is disconnected, revoke authorization

View File

@@ -41,7 +41,7 @@ void evse_meter_on_meter_event(void* arg, void* event_data) {
meter_data.energy_wh = (uint32_t)(evt->total_energy * 1000.0f); meter_data.energy_wh = (uint32_t)(evt->total_energy * 1000.0f);
xSemaphoreGive(meter_mutex); xSemaphoreGive(meter_mutex);
ESP_LOGD(TAG, ESP_LOGI(TAG,
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, " "Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
"voltage[V]={%.2f,%.2f,%.2f}, " "voltage[V]={%.2f,%.2f,%.2f}, "
"current[A]={%.2f,%.2f,%.2f}, " "current[A]={%.2f,%.2f,%.2f}, "
@@ -63,9 +63,9 @@ void evse_meter_init(void) {
ESP_LOGI(TAG, "EVSE Meter listener registered."); ESP_LOGI(TAG, "EVSE Meter listener registered.");
} }
uint32_t evse_meter_get_instant_power(void) { int evse_meter_get_instant_power(void) {
xSemaphoreTake(meter_mutex, portMAX_DELAY); xSemaphoreTake(meter_mutex, portMAX_DELAY);
uint32_t sum = 0; int sum = 0;
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
sum += meter_data.power_watts[i]; sum += meter_data.power_watts[i];
} }
@@ -73,14 +73,14 @@ uint32_t evse_meter_get_instant_power(void) {
return sum; return sum;
} }
uint32_t evse_meter_get_total_energy(void) { int evse_meter_get_total_energy(void) {
xSemaphoreTake(meter_mutex, portMAX_DELAY); xSemaphoreTake(meter_mutex, portMAX_DELAY);
uint32_t val = meter_data.energy_wh; int val = meter_data.energy_wh;
xSemaphoreGive(meter_mutex); xSemaphoreGive(meter_mutex);
return val; return val;
} }
void evse_meter_get_power(uint32_t power[EVSE_METER_PHASE_COUNT]) { void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT]) {
xSemaphoreTake(meter_mutex, portMAX_DELAY); xSemaphoreTake(meter_mutex, portMAX_DELAY);
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
power[i] = meter_data.power_watts[i]; power[i] = meter_data.power_watts[i];

View File

@@ -79,28 +79,36 @@ void pilot_set_level(bool level)
void pilot_set_amps(uint16_t amps) void pilot_set_amps(uint16_t amps)
{ {
if (amps < 60 || amps > 800) { if (amps < 6 || amps > 80) {
ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps); ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 680 A)", amps);
return; return;
} }
uint32_t duty; uint32_t duty_percent;
if (amps <= 510) {
duty = (PILOT_PWM_MAX_DUTY * amps) / 600; if (amps <= 51) {
duty_percent = (amps * 10) / 6; // Duty (%) = Amps / 0.6
} else { } else {
duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100)); duty_percent = (amps * 10) / 25 + 64; // Duty (%) = (Amps / 2.5) + 64
} }
if (duty > PILOT_PWM_MAX_DUTY) duty = PILOT_PWM_MAX_DUTY;
if (duty_percent > 100) duty_percent = 100;
uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100;
if (last_pilot_level == 0 && last_pwm_duty == duty) return; if (last_pilot_level == 0 && last_pwm_duty == duty) return;
last_pilot_level = 0; last_pilot_level = 0;
last_pwm_duty = duty; last_pwm_duty = duty;
ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY); ESP_LOGI(TAG, "Pilot set: %d A → %d/%d (≈ %d%% duty)",
amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent);
ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty);
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
} }
static int compare_int(const void *a, const void *b) { static int compare_int(const void *a, const void *b) {
return (*(int *)a - *(int *)b); return (*(int *)a - *(int *)b);
} }

View File

@@ -0,0 +1,84 @@
/*
* evse_session.c
* Implementation of evse_session module using instantaneous power accumulation
*/
#include <inttypes.h> // for PRIu32
#include "evse_session.h"
#include "evse_meter.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "evse_session";
// Internal state
static TickType_t session_start_tick = 0;
static uint32_t watt_seconds = 0; // accumulator of W·s
static evse_session_t last_session;
static bool last_session_valid = false;
void evse_session_init(void) {
session_start_tick = 0;
watt_seconds = 0;
last_session_valid = false;
}
void evse_session_start(void) {
session_start_tick = xTaskGetTickCount();
watt_seconds = 0;
ESP_LOGI(TAG, "Session started at tick %u", (unsigned)session_start_tick);
}
void evse_session_end(void) {
if (session_start_tick == 0) {
ESP_LOGW(TAG, "evse_session_end called without active session");
return;
}
TickType_t now = xTaskGetTickCount();
uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ;
uint32_t energy_wh = watt_seconds / 3600U;
uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0;
last_session.start_tick = session_start_tick;
last_session.duration_s = duration_s;
last_session.energy_wh = energy_wh;
last_session.avg_power_w = avg_power;
last_session.is_current = false;
last_session_valid = true;
session_start_tick = 0;
ESP_LOGI(TAG, "Session ended: duration=%" PRIu32 " s, energy=%" PRIu32 " Wh, avg_power=%" PRIu32 " W",
(uint32_t)duration_s, (uint32_t)energy_wh, (uint32_t)avg_power);
}
void evse_session_tick(void) {
if (session_start_tick == 0) return;
// Should be called every second (or known interval)
uint32_t power_w = evse_meter_get_instant_power();
watt_seconds += power_w;
}
bool evse_session_get(evse_session_t *out) {
if (out == NULL) return false;
if (session_start_tick != 0) {
TickType_t now = xTaskGetTickCount();
uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ;
uint32_t energy_wh = watt_seconds / 3600U;
uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0;
out->start_tick = session_start_tick;
out->duration_s = duration_s;
out->energy_wh = energy_wh;
out->avg_power_w = avg_power;
out->is_current = true;
return true;
}
if (last_session_valid) {
*out = last_session;
return true;
}
return false;
}

View File

@@ -1,5 +1,6 @@
#include "evse_api.h" #include "evse_api.h"
#include "evse_state.h" #include "evse_state.h"
#include "evse_session.h"
#include "evse_events.h" #include "evse_events.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
@@ -11,10 +12,11 @@
static evse_state_t current_state = EVSE_STATE_A; static evse_state_t current_state = EVSE_STATE_A;
static bool is_authorized = false; static bool is_authorized = false;
static TickType_t session_start_tick = 0;
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
static const char *TAG = "evse_state";
// ========================= // =========================
// Internal Mapping // Internal Mapping
// ========================= // =========================
@@ -36,34 +38,45 @@ static evse_state_event_t map_state_to_event(evse_state_t s) {
// Public API // Public API
// ========================= // =========================
void evse_set_state(evse_state_t state) { void evse_set_state(evse_state_t new_state) {
bool changed = false; bool changed = false;
evse_state_t previous_state; evse_state_t prev_state;
bool start_session = false;
bool end_session = false;
// 1) Detecta transição de estado dentro da região crítica
portENTER_CRITICAL(&state_mux); portENTER_CRITICAL(&state_mux);
previous_state = current_state; prev_state = current_state;
if (state != current_state) { if (new_state != current_state) {
current_state = state; // se entrou em charging pela primeira vez
if (evse_state_is_charging(new_state) && !evse_state_is_charging(prev_state)) {
start_session = true;
}
// se saiu de charging para qualquer outro
else if (!evse_state_is_charging(new_state) && evse_state_is_charging(prev_state)) {
end_session = true;
}
current_state = new_state;
changed = true; changed = true;
// When entering a charging state, record the start tick
if (evse_state_is_charging(state) && !evse_state_is_charging(previous_state)) {
session_start_tick = xTaskGetTickCount();
}
// When exiting a charging state, reset the start tick
else if (!evse_state_is_charging(state) && evse_state_is_charging(previous_state)) {
session_start_tick = 0;
}
} }
portEXIT_CRITICAL(&state_mux); portEXIT_CRITICAL(&state_mux);
// 2) Executa start/end de sessão FORA da região crítica, evitando logs/alloc dentro dela
if (start_session) {
evse_session_start();
}
if (end_session) {
evse_session_end();
}
// 3) Se mudou o estado, faz log e dispara evento
if (changed) { if (changed) {
ESP_LOGI("EVSE_STATE", "State changed from %s to %s", const char *prev_str = evse_state_to_str(prev_state);
evse_state_to_str(previous_state), const char *curr_str = evse_state_to_str(new_state);
evse_state_to_str(state)); ESP_LOGI(TAG, "State changed: %s → %s", prev_str, curr_str);
evse_state_event_data_t evt = { evse_state_event_data_t evt = {
.state = map_state_to_event(state) .state = map_state_to_event(new_state)
}; };
esp_event_post(EVSE_EVENTS, esp_event_post(EVSE_EVENTS,
EVSE_EVENT_STATE_CHANGED, EVSE_EVENT_STATE_CHANGED,
@@ -73,6 +86,8 @@ void evse_set_state(evse_state_t state) {
} }
} }
evse_state_t evse_get_state(void) { evse_state_t evse_get_state(void) {
portENTER_CRITICAL(&state_mux); portENTER_CRITICAL(&state_mux);
evse_state_t s = current_state; evse_state_t s = current_state;
@@ -80,13 +95,6 @@ evse_state_t evse_get_state(void) {
return s; return s;
} }
TickType_t evse_get_session_start(void) {
portENTER_CRITICAL(&state_mux);
TickType_t t = session_start_tick;
portEXIT_CRITICAL(&state_mux);
return t;
}
const char* evse_state_to_str(evse_state_t state) { const char* evse_state_to_str(evse_state_t state) {
switch (state) { switch (state) {
case EVSE_STATE_A: return "A - EV Not Connected (12V)"; case EVSE_STATE_A: return "A - EV Not Connected (12V)";
@@ -105,7 +113,6 @@ const char* evse_state_to_str(evse_state_t state) {
void evse_state_init(void) { void evse_state_init(void) {
portENTER_CRITICAL(&state_mux); portENTER_CRITICAL(&state_mux);
current_state = EVSE_STATE_A; current_state = EVSE_STATE_A;
session_start_tick = xTaskGetTickCount();
is_authorized = true; is_authorized = true;
portEXIT_CRITICAL(&state_mux); portEXIT_CRITICAL(&state_mux);

View File

@@ -3,8 +3,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "evse_state.h" // Tipos e estados #include "evse_state.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "evse_session.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -24,10 +26,17 @@ evse_state_t evse_get_state(void);
*/ */
void evse_set_state(evse_state_t state); void evse_set_state(evse_state_t state);
// ===============================
// Charging Session Info
// ===============================
/** /**
* @brief Get timestamp when the current session started (for timing limits). * @brief Retrieve statistics of either the current ongoing session (if any)
* or the last completed session.
* @param out pointer to evse_session_t to be filled
* @return true if there is a current or last session available
*/ */
TickType_t evse_get_session_start(void); bool evse_get_session(evse_session_t *out);
// =============================== // ===============================
// Charging Session Info // Charging Session Info

View File

@@ -55,10 +55,6 @@ esp_err_t evse_set_rcm(bool rcm);
uint8_t evse_get_temp_threshold(void); uint8_t evse_get_temp_threshold(void);
esp_err_t evse_set_temp_threshold(uint8_t threshold); esp_err_t evse_set_temp_threshold(uint8_t threshold);
// Autenticação
bool evse_is_require_auth(void);
void evse_set_require_auth(bool require);
// Disponibilidade // Disponibilidade
bool evse_config_is_available(void); bool evse_config_is_available(void);
void evse_config_set_available(bool available); void evse_config_set_available(bool available);

View File

@@ -13,13 +13,13 @@ extern "C" {
void evse_meter_init(void); void evse_meter_init(void);
/// Retorna a potência instantânea (soma das 3 fases, em watts) /// Retorna a potência instantânea (soma das 3 fases, em watts)
uint32_t evse_meter_get_instant_power(void); int evse_meter_get_instant_power(void);
/// Retorna a energia total acumulada (em Wh) /// Retorna a energia total acumulada (em Wh)
uint32_t evse_meter_get_total_energy(void); int evse_meter_get_total_energy(void);
/// Retorna as potências instantâneas nas fases L1, L2 e L3 (em watts) /// Retorna as potências instantâneas nas fases L1, L2 e L3 (em watts)
void evse_meter_get_power(uint32_t power[EVSE_METER_PHASE_COUNT]); void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT]);
/// Retorna as tensões medidas nas fases L1, L2 e L3 (em volts) /// Retorna as tensões medidas nas fases L1, L2 e L3 (em volts)
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]); void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]);

View File

@@ -0,0 +1,53 @@
/*
* evse_session.h
* Module to track and retrieve charging session data (current or last completed),
* accumulating energy via periodic tick of instantaneous power.
*/
#ifndef EVSE_SESSION_H
#define EVSE_SESSION_H
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
/**
* @brief Charging session statistics
*/
typedef struct {
TickType_t start_tick; ///< tick when session began
uint32_t duration_s; ///< total duration in seconds
uint32_t energy_wh; ///< total energy consumed in Wh
uint32_t avg_power_w; ///< average power in W
bool is_current; ///< true if session still in progress
} evse_session_t;
/**
* @brief Initialize the session module
*/
void evse_session_init(void);
/**
* @brief Mark the beginning of a charging session
*/
void evse_session_start(void);
/**
* @brief Mark the end of the charging session and store it as "last session"
*/
void evse_session_end(void);
/**
* @brief Periodic tick: must be called (e.g., each 1s) to accumulate energy from instant power
*/
void evse_session_tick(void);
/**
* @brief Retrieve statistics of either the current ongoing session (if any) or
* the last completed session.
* @param out pointer to evse_session_t to be filled
* @return true if there is a current or last session available, false otherwise
*/
bool evse_session_get(evse_session_t *out);
#endif // EVSE_SESSION_H

View File

@@ -53,11 +53,6 @@ evse_state_t evse_get_state(void);
*/ */
void evse_set_state(evse_state_t state); void evse_set_state(evse_state_t state);
/**
* @brief Returns the tick count when the current charging session began.
*/
TickType_t evse_get_session_start(void);
/** /**
* @brief Converts the state enum into a human-readable string. * @brief Converts the state enum into a human-readable string.
*/ */

View File

@@ -10,18 +10,17 @@
#include <string.h> #include <string.h>
#include "meter_events.h" #include "meter_events.h"
#include "evse_events.h" #include "evse_events.h"
#include <math.h>
static const char *TAG = "loadbalancer"; static const char *TAG = "loadbalancer";
// Limites configuráveis // Configurable limits
#define MIN_CHARGING_CURRENT_LIMIT 6 // A #define MIN_CHARGING_CURRENT_LIMIT 6 // A
#define MAX_CHARGING_CURRENT_LIMIT 32 // A #define MAX_CHARGING_CURRENT_LIMIT 32 // A
#define MIN_GRID_CURRENT_LIMIT 6 // A #define MIN_GRID_CURRENT_LIMIT 6 // A
#define MAX_GRID_CURRENT_LIMIT 100 // A #define MAX_GRID_CURRENT_LIMIT 100 // A
// Parâmetros // Parameters
static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT; static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT;
static bool loadbalancer_enabled = false; static bool loadbalancer_enabled = false;
@@ -34,6 +33,12 @@ static input_filter_t evse_filter;
#define NVS_MAX_GRID_CURRENT "max_grid_curr" #define NVS_MAX_GRID_CURRENT "max_grid_curr"
#define NVS_LOADBALANCER_ENABLED "enabled" #define NVS_LOADBALANCER_ENABLED "enabled"
// Reset filter helper
static void input_filter_reset(input_filter_t *filter)
{
filter->value = 0.0f;
}
static void loadbalancer_meter_event_handler(void *handler_arg, static void loadbalancer_meter_event_handler(void *handler_arg,
esp_event_base_t base, esp_event_base_t base,
int32_t id, int32_t id,
@@ -45,13 +50,12 @@ static void loadbalancer_meter_event_handler(void *handler_arg,
const meter_event_data_t *evt = (const meter_event_data_t *)event_data; 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, "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, "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, "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, "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", ESP_LOGI(TAG, "Freq: %.2f Hz | PF: %.2f | Energy: %.3f kWh",
evt->frequency, evt->power_factor, evt->total_energy); evt->frequency, evt->power_factor, evt->total_energy);
// Calcula a corrente máxima entre as 3 fases
float max_irms = evt->irms[0]; float max_irms = evt->irms[0];
for (int i = 1; i < 3; ++i) for (int i = 1; i < 3; ++i)
{ {
@@ -63,13 +67,12 @@ static void loadbalancer_meter_event_handler(void *handler_arg,
ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms); ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms);
// Atualiza com filtro exponencial dependendo da origem if (evt->source && strcmp(evt->source, "GRID") == 0)
if (strncmp(evt->source, "GRID", 4) == 0)
{ {
grid_current = input_filter_update(&grid_filter, max_irms); grid_current = input_filter_update(&grid_filter, max_irms);
ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current); ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current);
} }
else if (strncmp(evt->source, "EVSE", 4) == 0) else if (evt->source && strcmp(evt->source, "EVSE") == 0)
{ {
evse_current = input_filter_update(&evse_filter, max_irms); evse_current = input_filter_update(&evse_filter, max_irms);
ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current); ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current);
@@ -92,27 +95,23 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
switch (evt->state) switch (evt->state)
{ {
case EVSE_STATE_EVENT_IDLE: case EVSE_STATE_EVENT_IDLE:
// Vehicle is disconnected - current flow can be reduced or reset ESP_LOGI(TAG, "EVSE is IDLE - vehicle disconnected");
ESP_LOGI(TAG, "EVSE is IDLE - possible to release current");
break; break;
case EVSE_STATE_EVENT_WAITING: 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");
ESP_LOGI(TAG, "EVSE is waiting - connected but not charging");
break; break;
case EVSE_STATE_EVENT_CHARGING: case EVSE_STATE_EVENT_CHARGING:
ESP_LOGI(TAG, "EVSE is CHARGING - resetting filters");
grid_current = 0.0f; grid_current = 0.0f;
evse_current = 0.0f; evse_current = 0.0f;
// Charging has started - maintain or monitor current usage input_filter_reset(&grid_filter);
ESP_LOGI(TAG, "EVSE is charging"); input_filter_reset(&evse_filter);
break; break;
case EVSE_STATE_EVENT_FAULT: case EVSE_STATE_EVENT_FAULT:
// A fault has occurred - safety measures may be needed ESP_LOGW(TAG, "EVSE is in FAULT state - consider disabling load balancing");
ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing");
// Optional: disable load balancing during fault condition
// loadbalancer_set_enabled(false);
break; break;
default: default:
@@ -121,21 +120,19 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
} }
} }
// Carrega configuração do NVS
static esp_err_t loadbalancer_load_config() static esp_err_t loadbalancer_load_config()
{ {
nvs_handle_t handle; nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) if (err != ESP_OK)
{ {
ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err));
return err; return err;
} }
bool needs_commit = false; bool needs_commit = false;
uint8_t temp_u8; uint8_t temp_u8;
// max_grid_current
err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8); 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) if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT)
{ {
@@ -145,11 +142,10 @@ static esp_err_t loadbalancer_load_config()
{ {
max_grid_current = MAX_GRID_CURRENT_LIMIT; max_grid_current = MAX_GRID_CURRENT_LIMIT;
nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current); 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); ESP_LOGW(TAG, "max_grid_current invalid or missing, set to default: %d", max_grid_current);
needs_commit = true; needs_commit = true;
} }
// loadbalancer_enabled
err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8); err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8);
if (err == ESP_OK && temp_u8 <= 1) if (err == ESP_OK && temp_u8 <= 1)
{ {
@@ -159,7 +155,7 @@ static esp_err_t loadbalancer_load_config()
{ {
loadbalancer_enabled = false; loadbalancer_enabled = false;
nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0); nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0);
ESP_LOGW(TAG, "loadbalancer_enabled missing or invalid, setting default: 0"); ESP_LOGW(TAG, "loadbalancer_enabled invalid or missing, set to false");
needs_commit = true; needs_commit = true;
} }
@@ -172,10 +168,9 @@ static esp_err_t loadbalancer_load_config()
return ESP_OK; return ESP_OK;
} }
// Salva o estado habilitado no NVS
void loadbalancer_set_enabled(bool enabled) void loadbalancer_set_enabled(bool enabled)
{ {
ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled); ESP_LOGI(TAG, "Setting load balancing to %d", enabled);
nvs_handle_t handle; nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) if (err != ESP_OK)
@@ -189,7 +184,7 @@ void loadbalancer_set_enabled(bool enabled)
{ {
nvs_commit(handle); nvs_commit(handle);
loadbalancer_enabled = enabled; loadbalancer_enabled = enabled;
ESP_LOGI(TAG, "Load balancing enabled state saved"); ESP_LOGI(TAG, "Load balancing state saved");
loadbalancer_state_event_t evt = { loadbalancer_state_event_t evt = {
.enabled = enabled, .enabled = enabled,
@@ -209,7 +204,6 @@ void loadbalancer_set_enabled(bool enabled)
nvs_close(handle); nvs_close(handle);
} }
// Define e salva o limite de corrente da rede
esp_err_t load_balancing_set_max_grid_current(uint8_t value) esp_err_t load_balancing_set_max_grid_current(uint8_t value)
{ {
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT) if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
@@ -252,20 +246,23 @@ bool loadbalancer_is_enabled(void)
return loadbalancer_enabled; return loadbalancer_enabled;
} }
// Tarefa principal com eventos
void loadbalancer_task(void *param) void loadbalancer_task(void *param)
{ {
while (true) while (true)
{ {
if (!loadbalancer_enabled) if (!loadbalancer_enabled)
{ {
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(5000));
continue; continue;
} }
float available = max_grid_current - grid_current + evse_current; float available = max_grid_current - grid_current;
if (available < MIN_CHARGING_CURRENT_LIMIT) if (available < 0.0f)
{
available = 0.0f;
}
else if (available < MIN_CHARGING_CURRENT_LIMIT)
{ {
available = MIN_CHARGING_CURRENT_LIMIT; available = MIN_CHARGING_CURRENT_LIMIT;
} }
@@ -274,7 +271,7 @@ void loadbalancer_task(void *param)
available = max_grid_current; available = max_grid_current;
} }
ESP_LOGD(TAG, "Setting EVSE current limit: %.1f A", available); ESP_LOGD(TAG, "Calculated available EVSE current: %.1f A", available);
loadbalancer_charging_limit_event_t evt = { loadbalancer_charging_limit_event_t evt = {
.limit = available, .limit = available,
@@ -286,7 +283,7 @@ void loadbalancer_task(void *param)
sizeof(evt), sizeof(evt),
portMAX_DELAY); portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(5000));
} }
} }

View File

@@ -22,4 +22,4 @@ set(includes
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${includes}" INCLUDE_DIRS "${includes}"
PRIV_REQUIRES nvs_flash PRIV_REQUIRES nvs_flash
REQUIRES esp_event esp-modbus spi_bus_manager) REQUIRES esp_event esp-modbus spi_bus_manager network)

View File

@@ -59,7 +59,6 @@ static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data)
} }
} }
// === Task de leitura ===
static void meter_ade7758_task_func(void *param) { static void meter_ade7758_task_func(void *param) {
ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada"); ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada");
@@ -68,7 +67,7 @@ static void meter_ade7758_task_func(void *param) {
while (true) { while (true) {
meter_ade7758_internal_data_t meterData = {0}; meter_ade7758_internal_data_t meterData = {0};
//ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion()); ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion());
meterData.vrms[0] = avrms() / VRMS_CAL; meterData.vrms[0] = avrms() / VRMS_CAL;
meterData.vrms[1] = bvrms() / VRMS_CAL; meterData.vrms[1] = bvrms() / VRMS_CAL;
@@ -82,6 +81,21 @@ static void meter_ade7758_task_func(void *param) {
if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B); if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B);
if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C); if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C);
ESP_LOGI(TAG, "VRMS: A=%.2f V, B=%.2f V, C=%.2f V",
(double)meterData.vrms[0],
(double)meterData.vrms[1],
(double)meterData.vrms[2]);
ESP_LOGI(TAG, "IRMS: A=%.2f A, B=%.2f A, C=%.2f A",
(double)meterData.irms[0],
(double)meterData.irms[1],
(double)meterData.irms[2]);
ESP_LOGI(TAG, "Watt: A=%.2f W, B=%.2f W, C=%.2f W",
(double)meterData.watt[0],
(double)meterData.watt[1],
(double)meterData.watt[2]);
if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) { if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) {
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
meter_data = meterData; meter_data = meterData;

View File

@@ -54,11 +54,21 @@ typedef struct {
static bool phase_updated[PHASE_COUNT] = {false, false, false}; static bool phase_updated[PHASE_COUNT] = {false, false, false};
static meter_zigbee_data_t meter_data = {0}; static meter_zigbee_data_t meter_data = {0};
static SemaphoreHandle_t meter_mutex = NULL; static SemaphoreHandle_t meter_mutex = NULL;
static TaskHandle_t meter_zigbee_task = NULL; static TaskHandle_t meter_zigbee_task = NULL;
bool meter_zigbee_is_running(void) {
return meter_zigbee_task != NULL;
}
void send_stop_command(void) {
const char *cmd = "stop\n"; // Comando enviado para o outro lado interpretar e dormir
uart_write_bytes(UART_PORT, cmd, strlen(cmd));
uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(100)); // Aguarda envio terminar
}
static void meter_zigbee_post_event(void) { static void meter_zigbee_post_event(void) {
meter_event_data_t evt = { meter_event_data_t evt = {
.source = "GRID", .source = "GRID",
@@ -212,7 +222,15 @@ esp_err_t meter_zigbee_start(void) {
return ESP_OK; return ESP_OK;
} }
void meter_zigbee_stop(void) { void meter_zigbee_stop(void) {
send_stop_command();
vTaskDelay(pdMS_TO_TICKS(100)); // Aguarda o outro lado processar
if (meter_zigbee_task) { if (meter_zigbee_task) {
vTaskDelete(meter_zigbee_task); vTaskDelete(meter_zigbee_task);
meter_zigbee_task = NULL; meter_zigbee_task = NULL;
@@ -225,7 +243,3 @@ void meter_zigbee_stop(void) {
meter_mutex = NULL; meter_mutex = NULL;
} }
} }
bool meter_zigbee_is_running(void) {
return meter_zigbee_task != NULL;
}

View File

@@ -2,66 +2,116 @@
#define METER_MANAGER_H #define METER_MANAGER_H
#include "esp_err.h" #include "esp_err.h"
#include <stdbool.h> // Para garantir que 'bool' seja reconhecido #include <stdbool.h>
// Definindo tipos de medidores possíveis para EVSE e Grid /**
* @brief Supported meter types for EVSE and Grid.
*/
typedef enum { typedef enum {
METER_TYPE_NONE, // Nenhum Medidor METER_TYPE_NONE, // No meter
METER_TYPE_ADE7758, // ADE7758 METER_TYPE_ADE7758, // ADE7758 meter
METER_TYPE_ORNO513, // ORNO 513 METER_TYPE_ORNO513, // ORNO-513
METER_TYPE_ORNO516, // ORNO 516 METER_TYPE_ORNO516, // ORNO-516
METER_TYPE_MONO_ZIGBEE, // Medidor Zigbee (Mono) METER_TYPE_MONO_ZIGBEE, // Zigbee single-phase
METER_TYPE_TRIF_ZIGBEE // Medidor Zigbee (Trifásico) METER_TYPE_TRIF_ZIGBEE // Zigbee three-phase
} meter_type_t; } meter_type_t;
/** /**
* @brief Funções para gerenciar o medidor EVSE (ex: ADE7758). * @brief Initializes the meter manager system.
*
* Registers network event handlers and initializes both EVSE and GRID meters.
*/ */
esp_err_t meter_manager_init(void);
// Inicializa o medidor EVSE com o tipo especificado (ex: ADE7758)
esp_err_t meter_manager_evse_init(void);
// 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). * @brief Starts all configured meters (EVSE and GRID).
*
* @return esp_err_t ESP_OK on success, or an error code from one of the start calls.
*/
esp_err_t meter_manager_start(void);
/**
* @brief Stops all meters and unregisters network event handlers.
*/
esp_err_t meter_manager_stop(void);
/**
* @brief EVSE Meter Management
*/ */
// Inicializa o medidor Grid com o tipo especificado (ORNO 513, ORNO 516, Zigbee) /**
* @brief Initializes the EVSE meter based on configured type.
*/
esp_err_t meter_manager_evse_init(void);
/**
* @brief Starts the EVSE meter.
*/
esp_err_t meter_manager_evse_start(void);
/**
* @brief Stops the EVSE meter.
*/
esp_err_t meter_manager_evse_stop(void);
/**
* @brief Returns true if an EVSE meter is configured (not NONE).
*/
bool meter_manager_evse_is_enabled(void);
/**
* @brief Sets the EVSE meter type and saves it to NVS.
*/
esp_err_t meter_manager_evse_set_model(meter_type_t meter_type);
/**
* @brief Gets the current EVSE meter type.
*/
meter_type_t meter_manager_evse_get_model(void);
/**
* @brief Grid Meter Management
*/
/**
* @brief Initializes the Grid meter based on configured type.
*/
esp_err_t meter_manager_grid_init(void); esp_err_t meter_manager_grid_init(void);
// Inicia o medidor Grid com o tipo especificado /**
* @brief Starts the Grid meter.
*/
esp_err_t meter_manager_grid_start(void); esp_err_t meter_manager_grid_start(void);
// Para o medidor Grid /**
* @brief Stops the Grid meter.
*/
esp_err_t meter_manager_grid_stop(void); esp_err_t meter_manager_grid_stop(void);
// Habilita ou desabilita o medidor Grid /**
void meter_manager_grid_set_enabled(bool value); * @brief Sets the Grid meter type and saves it to NVS.
*/
// Define o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_grid_set_model(meter_type_t meter_type); esp_err_t meter_manager_grid_set_model(meter_type_t meter_type);
// Retorna o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee) /**
* @brief Gets the current Grid meter type.
*/
meter_type_t meter_manager_grid_get_model(void); meter_type_t meter_manager_grid_get_model(void);
// Função auxiliar para converter o tipo de medidor em uma string /**
* @brief Utility functions
*/
/**
* @brief Converts a meter_type_t to a human-readable string.
*/
const char* meter_type_to_str(meter_type_t type); const char* meter_type_to_str(meter_type_t type);
/**
* @brief Converts a string to a meter_type_t.
*/
meter_type_t string_to_meter_type(const char *str); meter_type_t string_to_meter_type(const char *str);
#endif // METER_MANAGER_H #endif // METER_MANAGER_H

View File

@@ -7,6 +7,8 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" #include "nvs.h"
#include <string.h> #include <string.h>
#include "network_events.h"
static const char *TAG = "meter_manager"; static const char *TAG = "meter_manager";
@@ -18,6 +20,33 @@ static meter_type_t meter_grid_type = METER_TYPE_NONE;
#define NVS_EVSE_MODEL "evse_model" #define NVS_EVSE_MODEL "evse_model"
#define NVS_GRID_MODEL "grid_model" #define NVS_GRID_MODEL "grid_model"
static void meter_manager_network_event_handler(void* arg, esp_event_base_t base, int32_t event_id, void* data){
if (base != NETWORK_EVENTS) return;
switch (event_id) {
case NETWORK_EVENT_AP_STARTED:
ESP_LOGI(TAG, "Recebido NETWORK_EVENT_AP_STARTED, parando medidor de grid");
meter_manager_grid_stop();
break;
case NETWORK_EVENT_AP_STOP:
ESP_LOGI(TAG, "Recebido NETWORK_EVENT_AP_STOP, reiniciando medidor de grid");
meter_manager_grid_start();
break;
case NETWORK_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "Recebido NETWORK_EVENT_STA_GOT_IP");
// opcional: reiniciar ou logar
break;
default:
break;
}
}
// Função unificada para ler ou inicializar um modelo de medidor // 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) { static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type) {
nvs_handle_t handle; nvs_handle_t handle;
@@ -64,6 +93,83 @@ static esp_err_t write_meter_model_to_nvs(const char *key, meter_type_t meter_ty
} }
/**
* @brief Initializes the meter manager system.
*
* This function initializes both the EVSE and GRID meters,
* and registers the event handler to listen for NETWORK_EVENTS
* (e.g., AP started/stopped, STA got IP).
*
* @return esp_err_t ESP_OK on success, or an error code.
*/
esp_err_t meter_manager_init(void) {
esp_err_t err;
// Initialize EVSE meter
//err = meter_manager_evse_init();
//if (err != ESP_OK) return err;
// Initialize GRID meter
err = meter_manager_grid_init();
if (err != ESP_OK) return err;
// Register handler for custom network events
ESP_LOGI(TAG, "Registering network event handler");
return esp_event_handler_register(NETWORK_EVENTS,
ESP_EVENT_ANY_ID,
meter_manager_network_event_handler,
NULL);
}
/**
* @brief Starts all configured meters (EVSE and GRID).
*
* This function starts the EVSE and GRID meters based on their configured types.
* It does not register event handlers — that is handled by `meter_manager_init()`.
*
* @return esp_err_t ESP_OK on success, or an error code from one of the start calls.
*/
esp_err_t meter_manager_start(void) {
esp_err_t err;
// Start EVSE meter
//err = meter_manager_evse_start();
//if (err != ESP_OK) return err;
// Start GRID meter
err = meter_manager_grid_start();
if (err != ESP_OK) return err;
return ESP_OK;
}
/**
* @brief Stops all meters and unregisters event handlers.
*
* This function gracefully stops the EVSE and GRID meters
* and unregisters the previously registered network event handler.
*
* @return esp_err_t ESP_OK on success, or an error code.
*/
esp_err_t meter_manager_stop(void) {
esp_err_t err;
// Stop EVSE meter
//err = meter_manager_evse_stop();
//if (err != ESP_OK) return err;
// Stop GRID meter
err = meter_manager_grid_stop();
if (err != ESP_OK) return err;
return ESP_OK;
}
// Função para inicializar o medidor EVSE // Função para inicializar o medidor EVSE
esp_err_t meter_manager_evse_init() { esp_err_t meter_manager_evse_init() {
esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type); esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type);

View File

@@ -1,6 +1,8 @@
set(srcs set(srcs
"src/wifi.c") "src/network_events.c"
"src/network.c"
)
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash esp_netif esp_wifi mdns) PRIV_REQUIRES nvs_flash esp_netif esp_wifi mdns esp_event)

View File

@@ -0,0 +1,24 @@
#pragma once
#include "esp_event.h"
#ifdef __cplusplus
extern "C" {
#endif
// Declaração do base de eventos personalizados da rede
ESP_EVENT_DECLARE_BASE(NETWORK_EVENTS);
// Identificadores dos eventos possíveis do módulo network
typedef enum {
NETWORK_EVENT_AP_STARTED,
NETWORK_EVENT_AP_STOP,
NETWORK_EVENT_STA_CONNECTED,
NETWORK_EVENT_STA_DISCONNECTED,
NETWORK_EVENT_STA_GOT_IP,
NETWORK_EVENT_STA_LOST_IP
} network_event_id_t;
#ifdef __cplusplus
}
#endif

View File

@@ -10,7 +10,9 @@
#include "nvs.h" #include "nvs.h"
#include "mdns.h" #include "mdns.h"
#include "wifi.h" #include "network_events.h"
#include "network.h"
#define AP_SSID "plx-%02x%02x%02x" #define AP_SSID "plx-%02x%02x%02x"
@@ -292,6 +294,8 @@ void wifi_ap_start(void)
{ {
ESP_LOGI(TAG, "Starting AP"); ESP_LOGI(TAG, "Starting AP");
esp_event_post(NETWORK_EVENTS, NETWORK_EVENT_AP_STARTED, NULL, 0, portMAX_DELAY);
xEventGroupClearBits(wifi_event_group, WIFI_STA_MODE_BIT); xEventGroupClearBits(wifi_event_group, WIFI_STA_MODE_BIT);
esp_wifi_stop(); esp_wifi_stop();
@@ -304,6 +308,9 @@ void wifi_ap_start(void)
void wifi_ap_stop(void) void wifi_ap_stop(void)
{ {
ESP_LOGI(TAG, "Stopping AP"); ESP_LOGI(TAG, "Stopping AP");
esp_event_post(NETWORK_EVENTS, NETWORK_EVENT_AP_STOP, NULL, 0, portMAX_DELAY);
xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT); xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT);
esp_wifi_stop(); esp_wifi_stop();

View File

@@ -0,0 +1,4 @@
#include "network_events.h"
// Define o base de eventos
ESP_EVENT_DEFINE_BASE(NETWORK_EVENTS);

View File

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

View File

@@ -1,22 +0,0 @@
#ifndef BUZZER_H_
#define BUZZER_H_
#include <stdint.h>
/**
* @brief Inicializa o buzzer e inicia monitoramento automático do estado EVSE.
*/
void buzzer_init(void);
/**
* @brief Liga e desliga o buzzer manualmente (uso interno ou testes).
*/
void buzzer_on(void);
void buzzer_off(void);
/**
* @brief Ativa o buzzer por um período fixo (em milissegundos).
*/
void buzzer_beep_ms(uint16_t ms);
#endif /* BUZZER_H_ */

View File

@@ -1,163 +0,0 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "board_config.h"
#include "buzzer.h"
#include "evse_api.h"
static gpio_num_t buzzer_gpio = GPIO_NUM_NC;
static evse_state_t last_buzzer_state = -1;
static QueueHandle_t buzzer_queue = NULL;
void buzzer_on(void) {
if (buzzer_gpio != GPIO_NUM_NC)
gpio_set_level(buzzer_gpio, 1);
}
void buzzer_off(void) {
if (buzzer_gpio != GPIO_NUM_NC)
gpio_set_level(buzzer_gpio, 0);
}
// ----------------------
// Padrões de Buzzer
// ----------------------
typedef struct {
uint16_t on_ms;
uint16_t off_ms;
} buzzer_pattern_step_t;
typedef enum {
BUZZER_PATTERN_NONE = 0,
BUZZER_PATTERN_PLUGGED,
BUZZER_PATTERN_UNPLUGGED,
BUZZER_PATTERN_CHARGING,
} buzzer_pattern_id_t;
static const buzzer_pattern_step_t pattern_plugged[] = {
{100, 100}, {200, 0}
};
static const buzzer_pattern_step_t pattern_unplugged[] = {
{150, 150}, {150, 150}, {150, 0}
};
static const buzzer_pattern_step_t pattern_charging[] = {
{80, 150}, {100, 120}, {120, 100}, {140, 0}
};
// ----------------------
// Executor de padrões
// ----------------------
static void buzzer_execute_pattern(buzzer_pattern_id_t pattern_id) {
const buzzer_pattern_step_t *pattern = NULL;
size_t length = 0;
switch (pattern_id) {
case BUZZER_PATTERN_PLUGGED:
pattern = pattern_plugged;
length = sizeof(pattern_plugged) / sizeof(pattern_plugged[0]);
break;
case BUZZER_PATTERN_UNPLUGGED:
pattern = pattern_unplugged;
length = sizeof(pattern_unplugged) / sizeof(pattern_unplugged[0]);
break;
case BUZZER_PATTERN_CHARGING:
pattern = pattern_charging;
length = sizeof(pattern_charging) / sizeof(pattern_charging[0]);
break;
default:
return;
}
for (size_t i = 0; i < length; i++) {
buzzer_on();
vTaskDelay(pdMS_TO_TICKS(pattern[i].on_ms));
buzzer_off();
if (pattern[i].off_ms > 0)
vTaskDelay(pdMS_TO_TICKS(pattern[i].off_ms));
}
}
// ----------------------
// Task que toca o buzzer
// ----------------------
static void buzzer_worker_task(void *arg) {
buzzer_pattern_id_t pattern_id;
while (true) {
if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) {
buzzer_execute_pattern(pattern_id);
}
}
}
// ----------------------
// Task de monitoramento
// ----------------------
static void buzzer_monitor_task(void *arg) {
while (true) {
evse_state_t current = evse_get_state();
if (current != last_buzzer_state) {
buzzer_pattern_id_t pattern_id = BUZZER_PATTERN_NONE;
switch (current) {
case EVSE_STATE_A:
if (last_buzzer_state != EVSE_STATE_A)
pattern_id = BUZZER_PATTERN_UNPLUGGED;
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
if (last_buzzer_state != EVSE_STATE_B1 && last_buzzer_state != EVSE_STATE_B2)
pattern_id = BUZZER_PATTERN_PLUGGED;
break;
case EVSE_STATE_C2:
case EVSE_STATE_D2:
if (last_buzzer_state != EVSE_STATE_C2 && last_buzzer_state != EVSE_STATE_D2)
pattern_id = BUZZER_PATTERN_CHARGING;
break;
default:
break;
}
if (pattern_id != BUZZER_PATTERN_NONE) {
xQueueSend(buzzer_queue, &pattern_id, 0); // Não bloqueia
}
last_buzzer_state = current;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// ----------------------
// Inicialização
// ----------------------
void buzzer_init(void) {
if (board_config.buzzer) {
buzzer_gpio = board_config.buzzer_gpio;
gpio_config_t io_conf = {
.pin_bit_mask = BIT64(buzzer_gpio),
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
gpio_set_level(buzzer_gpio, 0);
}
buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t));
xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 3, NULL);
xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 3, NULL);
}

View File

@@ -1,7 +1,7 @@
#include "peripherals.h" #include "peripherals.h"
#include "adc.h" #include "adc.h"
#include "led.h" #include "led.h"
#include "buzzer.h" //#include "buzzer.h"
#include "proximity.h" #include "proximity.h"
#include "ac_relay.h" #include "ac_relay.h"
#include "socket_lock.h" #include "socket_lock.h"
@@ -13,7 +13,7 @@ void peripherals_init(void)
{ {
ac_relay_init(); ac_relay_init();
led_init(); led_init();
buzzer_init(); //buzzer_init();
adc_init(); adc_init();
proximity_init(); proximity_init();
// socket_lock_init(); // socket_lock_init();

View File

@@ -7,66 +7,63 @@
#include "board_config.h" #include "board_config.h"
#include "evse_api.h" #include "evse_api.h"
// static bool do_test = false; static const char *TAG = "RCM";
// static bool triggered = false;
// static bool test_triggered = false;
// static void IRAM_ATTR rcm_isr_handler(void* arg)
// {
// if (!do_test) {
// triggered = true;
// } else {
// test_triggered = true;
// }
// }
void rcm_init(void) void rcm_init(void)
{ {
if (board_config.rcm) { if (!board_config.rcm) {
gpio_config_t io_conf = {}; ESP_LOGW(TAG, "RCM não está habilitado na configuração.");
return;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = BIT64(board_config.rcm_test_gpio);
ESP_ERROR_CHECK(gpio_config(&io_conf));
io_conf.mode = GPIO_MODE_INPUT;
// io_conf.intr_type = GPIO_INTR_POSEDGE;
io_conf.pin_bit_mask = BIT64(board_config.rcm_gpio);
ESP_ERROR_CHECK(gpio_config(&io_conf));
//ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.rcm_gpio, rcm_isr_handler, NULL));
} }
// Configura GPIO de teste como saída
gpio_config_t output_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = BIT64(board_config.rcm_test_gpio),
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&output_conf));
// Configura GPIO de leitura como entrada
gpio_config_t input_conf = {
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = BIT64(board_config.rcm_gpio),
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&input_conf));
ESP_LOGI(TAG, "RCM inicializado com sucesso.");
} }
bool rcm_test(void) bool rcm_test(void)
{ {
// do_test = true; if (!board_config.rcm) {
// test_triggered = false; //ESP_LOGW(TAG, "Tentativa de teste com RCM desabilitado.");
return true;
// gpio_set_level(board_config.rcm_test_gpio, 1); }
// vTaskDelay(pdMS_TO_TICKS(100));
// gpio_set_level(board_config.rcm_test_gpio, 0);
// do_test = false;
// return test_triggered;
gpio_set_level(board_config.rcm_test_gpio, 1); gpio_set_level(board_config.rcm_test_gpio, 1);
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
bool success = gpio_get_level(board_config.rcm_gpio) == 1; bool success = gpio_get_level(board_config.rcm_gpio) == 1;
gpio_set_level(board_config.rcm_test_gpio, 0); gpio_set_level(board_config.rcm_test_gpio, 0);
ESP_LOGI(TAG, "Teste RCM: %s", success ? "PASSOU" : "FALHOU");
return success; return success;
} }
bool rcm_is_triggered(void) bool rcm_is_triggered(void)
{ {
// bool _triggered = triggered; if (!board_config.rcm) {
// if (gpio_get_level(board_config.rcm_gpio) == 0) { //ESP_LOGW(TAG, "Verificação de trigger com RCM desabilitado.");
// triggered = false; return false;
// } }
// return _triggered;
if (gpio_get_level(board_config.rcm_gpio) == 1) { if (gpio_get_level(board_config.rcm_gpio) == 1) {
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
return gpio_get_level(board_config.rcm_gpio) == 1; return gpio_get_level(board_config.rcm_gpio) == 1;

View File

@@ -62,29 +62,56 @@ static void publish_message(const char* topic, cJSON* root)
free((void*)json); free((void*)json);
} }
static void handle_message(const char* topic, const char* data) static void handle_message(const char* topic, const char* data)
{ {
char base_topic[32]; char base_topic[32];
mqtt_get_base_topic(base_topic); mqtt_get_base_topic(base_topic);
ESP_LOGI(TAG, "[MQTT] Message received"); ESP_LOGI(TAG, "Topic: %s", topic);
ESP_LOGI(TAG, " > Topic: %s", topic); ESP_LOGI(TAG, "data: %s", data);
ESP_LOGI(TAG, " > Payload: %s", data); ESP_LOGI(TAG, "base_topic: %s", base_topic);
if (strncmp(topic, base_topic, strlen(base_topic)) == 0) { if (strncmp(topic, base_topic, strlen(base_topic)) == 0) {
const char* sub_topic = &topic[strlen(base_topic)]; const char* sub_topic = &topic[strlen(base_topic)];
ESP_LOGI(TAG, " > Subtopic detected: %s", sub_topic);
ESP_LOGI(TAG, "Sub_topic: %s", sub_topic);
if (strcmp(sub_topic, "/request/config/evse") == 0) { if (strcmp(sub_topic, "/request/config/evse") == 0) {
ESP_LOGI(TAG, " → Responding with EVSE configuration");
cJSON* root = json_get_evse_config(); cJSON* root = json_get_evse_config();
publish_message("/response/config/evse", root); publish_message("/response/config/evse", root);
cJSON_Delete(root); cJSON_Delete(root);
} else { } else if (strcmp(sub_topic, "/request/config/wifi") == 0) {
ESP_LOGW(TAG, " ! Unknown command: %s", sub_topic); //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);
} }
} else {
ESP_LOGW(TAG, " ! Topic does not match base: %s", topic);
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,8 +13,8 @@
} }
</style> </style>
<title>Vite + React</title> <title>Vite + React</title>
<script type="module" crossorigin src="/assets/index-3W2ZKmTa.js"></script> <script type="module" crossorigin src="/assets/index-C_08vMAY.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Brq1-keN.css"> <link rel="stylesheet" crossorigin href="/assets/index-Doq3307m.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -1,22 +1,33 @@
// ========================= // =========================
// auth_api.c // auth_api.c
// ========================= // =========================
#include "auth_api.h" #include "auth_api.h"
#include "auth.h" #include "auth.h"
#include "esp_log.h" #include "esp_log.h"
#include "cJSON.h" #include "cJSON.h"
#include <string.h>
static const char *TAG = "auth_api"; static const char *TAG = "auth_api";
// =================================
// Dummy user storage (static list)
// =================================
static struct { static struct {
char username[128]; char username[128];
} users[10] = { /*{"admin"}, {"user1"}*/ }; } users[10] = { /* {"admin"}, {"user1"} */ };
static int num_users = 2; static int num_users = 2;
// =================================
// Handlers for Auth Methods (RFID)
// =================================
static esp_err_t auth_methods_get_handler(httpd_req_t *req) { static esp_err_t auth_methods_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json"); httpd_resp_set_type(req, "application/json");
cJSON *json = cJSON_CreateObject(); cJSON *json = cJSON_CreateObject();
cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() ); cJSON_AddBoolToObject(json, "RFID", auth_is_enabled());
char *str = cJSON_PrintUnformatted(json); char *str = cJSON_PrintUnformatted(json);
httpd_resp_sendstr(req, str); httpd_resp_sendstr(req, str);
free(str); free(str);
@@ -53,6 +64,9 @@ static esp_err_t auth_methods_post_handler(httpd_req_t *req) {
return ESP_OK; return ESP_OK;
} }
// =================================
// User Management Handlers
// =================================
static esp_err_t users_get_handler(httpd_req_t *req) { static esp_err_t users_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json"); httpd_resp_set_type(req, "application/json");
@@ -64,7 +78,7 @@ static esp_err_t users_get_handler(httpd_req_t *req) {
cJSON_AddItemToArray(list, u); cJSON_AddItemToArray(list, u);
} }
cJSON_AddItemToObject(root, "users", list); cJSON_AddItemToObject(root, "users", list);
char *str = cJSON_Print(root); char *str = cJSON_PrintUnformatted(root);
httpd_resp_sendstr(req, str); httpd_resp_sendstr(req, str);
free(str); free(str);
cJSON_Delete(root); cJSON_Delete(root);
@@ -75,7 +89,9 @@ static esp_err_t users_post_handler(httpd_req_t *req) {
char buf[128]; char buf[128];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1); int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0) return ESP_FAIL; if (len <= 0) return ESP_FAIL;
buf[len] = '\0'; buf[len] = '\0';
if (num_users < 10) { if (num_users < 10) {
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username)); strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
num_users++; num_users++;
@@ -107,7 +123,58 @@ static esp_err_t users_delete_handler(httpd_req_t *req) {
return ESP_FAIL; return ESP_FAIL;
} }
// =================================
// Tag Management Handlers
// =================================
static esp_err_t tags_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *root = cJSON_CreateObject();
cJSON *list = cJSON_CreateArray();
int count = auth_get_tag_count();
for (int i = 0; i < count; i++) {
const char *tag = auth_get_tag_by_index(i);
if (tag) {
cJSON_AddItemToArray(list, cJSON_CreateString(tag));
}
}
cJSON_AddItemToObject(root, "tags", list);
char *str = cJSON_PrintUnformatted(root);
httpd_resp_sendstr(req, str);
free(str);
cJSON_Delete(root);
return ESP_OK;
}
static esp_err_t tags_delete_handler(httpd_req_t *req) {
char query[128];
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
char tag[AUTH_TAG_MAX_LEN];
if (httpd_query_key_value(query, "tag", tag, sizeof(tag)) == ESP_OK) {
if (auth_remove_tag(tag)) {
httpd_resp_sendstr(req, "Tag removida com sucesso");
return ESP_OK;
}
}
}
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Tag não encontrada ou inválida");
return ESP_FAIL;
}
static esp_err_t tags_register_handler(httpd_req_t *req) {
auth_wait_for_tag_registration();
httpd_resp_sendstr(req, "Modo de registro de tag ativado");
return ESP_OK;
}
// =================================
// Register All REST Endpoints
// =================================
void register_auth_handlers(httpd_handle_t server, void *ctx) { void register_auth_handlers(httpd_handle_t server, void *ctx) {
// Auth methods
httpd_register_uri_handler(server, &(httpd_uri_t){ httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/auth-methods", .uri = "/api/v1/config/auth-methods",
.method = HTTP_GET, .method = HTTP_GET,
@@ -120,6 +187,8 @@ void register_auth_handlers(httpd_handle_t server, void *ctx) {
.handler = auth_methods_post_handler, .handler = auth_methods_post_handler,
.user_ctx = ctx .user_ctx = ctx
}); });
// Users
httpd_register_uri_handler(server, &(httpd_uri_t){ httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/users", .uri = "/api/v1/config/users",
.method = HTTP_GET, .method = HTTP_GET,
@@ -138,4 +207,24 @@ void register_auth_handlers(httpd_handle_t server, void *ctx) {
.handler = users_delete_handler, .handler = users_delete_handler,
.user_ctx = ctx .user_ctx = ctx
}); });
// Tags
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/tags",
.method = HTTP_GET,
.handler = tags_get_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/tags",
.method = HTTP_DELETE,
.handler = tags_delete_handler,
.user_ctx = ctx
});
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/config/tags/register",
.method = HTTP_POST,
.handler = tags_register_handler,
.user_ctx = ctx
});
} }

View File

@@ -24,11 +24,11 @@ static esp_err_t dashboard_get_handler(httpd_req_t *req) {
cJSON *charger1 = cJSON_CreateObject(); cJSON *charger1 = cJSON_CreateObject();
cJSON_AddNumberToObject(charger1, "id", 1); cJSON_AddNumberToObject(charger1, "id", 1);
cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state)); cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state));
cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current() / 10); cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current());
cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current()); cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current());
// Calcular a potência com base na corrente (considerando 230V) // Calcular a potência com base na corrente (considerando 230V)
int power = (evse_get_charging_current() / 10) * 230; int power = (evse_get_charging_current()) * 230;
cJSON_AddNumberToObject(charger1, "power", power); cJSON_AddNumberToObject(charger1, "power", power);
cJSON_AddItemToArray(chargers, charger1); cJSON_AddItemToArray(chargers, charger1);

View File

@@ -5,7 +5,7 @@
#include "network_api.h" #include "network_api.h"
#include "esp_log.h" #include "esp_log.h"
#include "cJSON.h" #include "cJSON.h"
#include "wifi.h" #include "network.h"
#include "mqtt.h" #include "mqtt.h"
static const char *TAG = "network_api"; static const char *TAG = "network_api";

View File

@@ -1,33 +1,57 @@
dependencies: dependencies:
espressif/cmake_utilities: espressif/cmake_utilities:
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
dependencies:
- name: idf
require: private
version: '>=4.1'
source: source:
service_url: https://api.components.espressif.com/ registry_url: https://components.espressif.com
type: service type: service
version: 0.5.3 version: 0.5.3
espressif/esp-modbus: espressif/esp-modbus:
component_hash: 5d5e90b9e55721a8a194b301ad8102d4affb647f47b74cd413ff7d1ce2c1169c component_hash: 5d5e90b9e55721a8a194b301ad8102d4affb647f47b74cd413ff7d1ce2c1169c
dependencies:
- name: idf
require: private
version: '>=4.3'
source: source:
service_url: https://api.components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 1.0.18 version: 1.0.18
espressif/mdns: espressif/mdns:
component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5 component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5
dependencies:
- name: idf
require: private
version: '>=5.0'
source: source:
service_url: https://api.components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 1.8.2 version: 1.8.2
espressif/ntc_driver: espressif/ntc_driver:
component_hash: 2e4752aa8fc9768365ee9198ba800141402a7466ff3133f617f1beab87bcc2ef component_hash: 2e4752aa8fc9768365ee9198ba800141402a7466ff3133f617f1beab87bcc2ef
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=5.0'
source: source:
service_url: https://api.components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 0.3.0 version: 0.3.0
idf: idf:
component_hash: null
source: source:
type: idf type: idf
version: 5.3.0 version: 5.4.2
manifest_hash: 934a9d746c65c54673c89e0201c09a3bdccec9ea3fd2bcff03ef8525f1d77443 direct_dependencies:
- espressif/esp-modbus
- espressif/mdns
- espressif/ntc_driver
- idf
manifest_hash: 914523e41b41de9584db65fbe408fdbefb25ae9417eadf249d129e6d77f28d4c
target: esp32 target: esp32
version: 1.0.0 version: 2.0.0

View File

@@ -15,7 +15,7 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "wifi.h" #include "network.h"
#include "board_config.h" #include "board_config.h"
#include "logger.h" #include "logger.h"
#include "rest_main.h" #include "rest_main.h"
@@ -27,6 +27,7 @@
#include "auth.h" #include "auth.h"
#include "loadbalancer.h" #include "loadbalancer.h"
#include "meter_manager.h" #include "meter_manager.h"
#include "buzzer.h"
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 #define EVSE_MANAGER_TICK_PERIOD_MS 1000
@@ -78,77 +79,134 @@ static void fs_init(void) {
fs_info(&cfg_conf); fs_info(&cfg_conf);
fs_info(&data_conf); fs_info(&data_conf);
} }
// //
// Wi-Fi event monitoring task // Wi-Fi event monitoring task
// //
static void wifi_event_task_func(void *param) { static void wifi_event_task_func(void *param) {
EventBits_t mode_bits; EventBits_t mode_bits;
while (1) { for (;;) {
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY); // Wait indefinitely until either AP or STA mode is entered
mode_bits = xEventGroupWaitBits(
wifi_event_group,
WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT,
pdFALSE, // do not clear bits on exit
pdFALSE, // wait for any bit
portMAX_DELAY
);
if (mode_bits & WIFI_AP_MODE_BIT) { 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) { // We're in AP mode: wait for a client to connect within the timeout
xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (xEventGroupWaitBits(
wifi_event_group,
WIFI_AP_CONNECTED_BIT,
pdFALSE,
pdFALSE,
pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)
) & WIFI_AP_CONNECTED_BIT) {
// Once connected, block until the client disconnects
xEventGroupWaitBits(
wifi_event_group,
WIFI_AP_DISCONNECTED_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY
);
} else { } else {
// Timeout expired with no client—optionally stop the AP
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) { if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
//wifi_ap_stop(); // wifi_ap_stop();
} }
} }
} else if (mode_bits & WIFI_STA_MODE_BIT) { } else if (mode_bits & WIFI_STA_MODE_BIT) {
xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); // We're in STA mode: block until disconnected from the AP
xEventGroupWaitBits(
wifi_event_group,
WIFI_STA_DISCONNECTED_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY
);
} }
// Prevent this task from hogging the CPU when idle
//vTaskDelay(pdMS_TO_TICKS(10));
} }
} }
// //
// Botão e tratamento // Button press handler
// //
static void handle_button_press(void) { static void handle_button_press(void) {
ESP_LOGI(TAG, "Ativando modo AP"); // If not already in AP mode, start it
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) { if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
ESP_LOGI(TAG, "Starting Wi-Fi AP mode");
wifi_ap_start(); wifi_ap_start();
} }
} }
// Task to handle button press/release notifications
static void user_input_task_func(void *param) { static void user_input_task_func(void *param) {
uint32_t notification; uint32_t notification;
while (1) { for (;;) {
if (xTaskNotifyWait(0x00, 0xFF, &notification, portMAX_DELAY)) { // Wait for notification bits from ISR
if (xTaskNotifyWait(
0, // do not clear any bits on entry
UINT32_MAX, // clear all bits on exit
&notification,
portMAX_DELAY)) {
// Handle button press event
if (notification & PRESS_BIT) { if (notification & PRESS_BIT) {
press_tick = xTaskGetTickCount(); press_tick = xTaskGetTickCount();
pressed = true; pressed = true;
ESP_LOGI(TAG, "Pressed Button"); ESP_LOGI(TAG, "Button Pressed");
handle_button_press(); handle_button_press();
} }
// Handle button release event (only if previously pressed)
if (notification & RELEASED_BIT && pressed) { if ((notification & RELEASED_BIT) && pressed) {
pressed = false; pressed = false;
ESP_LOGI(TAG, "Reladead Buttton"); ESP_LOGI(TAG, "Button Released");
handle_button_press(); handle_button_press();
} }
} }
} }
} }
// ISR for button GPIO interrupt (active-low)
static void IRAM_ATTR button_isr_handler(void *arg) { static void IRAM_ATTR button_isr_handler(void *arg) {
BaseType_t higher_task_woken = pdFALSE; BaseType_t higher_task_woken = pdFALSE;
TickType_t now = xTaskGetTickCountFromISR(); TickType_t now = xTaskGetTickCountFromISR();
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) return; // Debounce: ignore interrupts occurring too close together
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
return;
}
last_interrupt_tick = now; last_interrupt_tick = now;
if (!gpio_get_level(board_config.button_wifi_gpio)) { // Read GPIO level: 0 = button pressed, 1 = button released
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken); int level = gpio_get_level(board_config.button_wifi_gpio);
if (level == 0) {
// Notify task: button pressed
xTaskNotifyFromISR(
user_input_task,
PRESS_BIT,
eSetBits,
&higher_task_woken);
} else { } else {
xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken); // Notify task: button released
xTaskNotifyFromISR(
user_input_task,
RELEASED_BIT,
eSetBits,
&higher_task_woken);
} }
// Yield to higher priority task if unblocked
if (higher_task_woken) { if (higher_task_woken) {
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
} }
static void button_init(void) { static void button_init(void) {
gpio_config_t conf = { gpio_config_t conf = {
.pin_bit_mask = BIT64(board_config.button_wifi_gpio), .pin_bit_mask = BIT64(board_config.button_wifi_gpio),
@@ -167,6 +225,7 @@ static void button_init(void) {
static void init_modules(void) { static void init_modules(void) {
peripherals_init(); peripherals_init();
//api_init(); //api_init();
buzzer_init();
ESP_ERROR_CHECK(rest_server_init("/data")); ESP_ERROR_CHECK(rest_server_init("/data"));
protocols_init(); protocols_init();
evse_manager_init(); evse_manager_init();
@@ -174,11 +233,12 @@ static void init_modules(void) {
button_init(); button_init();
auth_init(); auth_init();
loadbalancer_init(); loadbalancer_init();
meter_manager_grid_init(); meter_manager_init();
meter_manager_grid_start(); meter_manager_start();
meter_manager_evse_init();
meter_manager_evse_start();
//wifi_ap_start();
// Outros módulos (descomente conforme necessário) // Outros módulos (descomente conforme necessário)
// meter_init(); // meter_init();
// ocpp_start(); // ocpp_start();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,824 +0,0 @@
// === Início de: components/meter_manager/driver/meter_orno/meter_orno516.c ===
#include "meter_orno516.h"
#include "meter_events.h"
#include "modbus_params.h"
#include "mbcontroller.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#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
#define UPDATE_INTERVAL (5000 / portTICK_PERIOD_MS)
#define POLL_INTERVAL (100 / portTICK_PERIOD_MS)
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define STR(fieldname) ((const char *)(fieldname))
#define OPTS(min_val, max_val, step_val) {.opt1 = min_val, .opt2 = max_val, .opt3 = step_val}
// 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
};
const mb_parameter_descriptor_t device_parameters_orno516[] = {
{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}
};
const uint16_t num_device_parameters_orno516 = sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]);
float ReverseFloat(const float inFloat) {
float retVal;
char *floatToConvert = (char *)&inFloat;
char *returnFloat = (char *)&retVal;
returnFloat[0] = floatToConvert[2];
returnFloat[1] = floatToConvert[3];
returnFloat[2] = floatToConvert[0];
returnFloat[3] = floatToConvert[1];
return retVal;
}
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);
}
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 = {
.port = MB_PORT_NUM,
.mode = MB_MODE_RTU,
.baudrate = MB_DEV_SPEED,
.parity = UART_PARITY_EVEN
};
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));
is_initialized = true;
return ESP_OK;
}
esp_err_t meter_orno516_start(void) {
if (!is_initialized) {
ESP_LOGE(TAG, "Not initialized");
return ESP_ERR_INVALID_STATE;
}
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");
}
// === Fim de: components/meter_manager/driver/meter_orno/meter_orno516.c ===
// === Início de: components/meter_manager/driver/meter_zigbee/meter_zigbee.h ===
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Inicializa o driver do medidor Zigbee (UART, mutex, etc.).
*
* @return ESP_OK se a inicialização for bem-sucedida, erro caso contrário.
*/
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_zigbee_start(void);
/**
* @brief Interrompe a tarefa e limpa recursos (UART, mutex, etc.).
*/
void meter_zigbee_stop(void);
#ifdef __cplusplus
}
#endif
// === Fim de: components/meter_manager/driver/meter_zigbee/meter_zigbee.h ===
// === Início de: components/meter_manager/driver/meter_zigbee/meter_zigbee.c ===
#include "meter_zigbee.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_system.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "meter_events.h"
#define TAG "meter_zigbee"
// UART config
#define UART_PORT UART_NUM_1
#define TXD_PIN GPIO_NUM_17
#define RXD_PIN GPIO_NUM_16
#define UART_BUF_SIZE 128
#define RX_FRAME_SIZE 14
// Zigbee Attribute IDs
#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
#define PHASE_COUNT 3
#define PHASE_L1 0
#define PHASE_L2 1
#define PHASE_L3 2
// Internal meter state
typedef struct {
float vrms[PHASE_COUNT];
float irms[PHASE_COUNT];
int watt[PHASE_COUNT];
int var[PHASE_COUNT];
int va[PHASE_COUNT];
float frequency;
float power_factor;
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_zigbee_task = NULL;
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
};
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));
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 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 (len < RX_FRAME_SIZE) {
ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len);
return;
}
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[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[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[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;
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));
}
}
static void meter_zigbee_task_func(void *param) {
uint8_t *buf = malloc(RX_FRAME_SIZE);
if (!buf) {
ESP_LOGE(TAG, "Failed to allocate buffer");
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Zigbee meter task started");
while (1) {
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);
}
}
free(buf);
vTaskDelete(NULL);
}
esp_err_t meter_zigbee_init(void) {
ESP_LOGI(TAG, "Initializing Zigbee meter");
if (!meter_mutex) {
meter_mutex = xSemaphoreCreateMutex();
if (!meter_mutex) return ESP_ERR_NO_MEM;
}
uart_config_t config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT
};
ESP_ERROR_CHECK(uart_param_config(UART_PORT, &config));
ESP_ERROR_CHECK(uart_set_pin(UART_PORT, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_BUF_SIZE * 2, 0, 0, NULL, 0));
return ESP_OK;
}
esp_err_t meter_zigbee_start(void) {
if (meter_zigbee_task) return ESP_ERR_INVALID_STATE;
xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 3, &meter_zigbee_task);
return ESP_OK;
}
void meter_zigbee_stop(void) {
if (meter_zigbee_task) {
vTaskDelete(meter_zigbee_task);
meter_zigbee_task = NULL;
}
uart_driver_delete(UART_PORT);
if (meter_mutex) {
vSemaphoreDelete(meter_mutex);
meter_mutex = NULL;
}
}
bool meter_zigbee_is_running(void) {
return meter_zigbee_task != NULL;
}
// === Fim de: components/meter_manager/driver/meter_zigbee/meter_zigbee.c ===
// === Início de: components/meter_manager/src/meter_manager.c ===
#include "meter_manager.h"
#include "esp_log.h"
#include "meter_ade7758.h"
#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";
// 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;
#define NVS_NAMESPACE "meterconfig"
#define NVS_EVSE_MODEL "evse_model"
#define NVS_GRID_MODEL "grid_model"
// 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_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, "Initializing GRID meter of type %s", meter_type_to_str(meter_grid_type));
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;
}
}
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;
}
}
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;
}
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;
}
}
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";
}
}
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;
}
// === Fim de: components/meter_manager/src/meter_manager.c ===
// === Início de: components/meter_manager/src/meter_events.c ===
#include "meter_events.h"
// Define a base de eventos
ESP_EVENT_DEFINE_BASE(METER_EVENT);
// === Fim de: components/meter_manager/src/meter_events.c ===
// === Início de: components/meter_manager/include/meter_manager.h ===
#ifndef METER_MANAGER_H
#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_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;
/**
* @brief Funções para gerenciar o medidor EVSE (ex: ADE7758).
*/
// Inicializa o medidor EVSE com o tipo especificado (ex: ADE7758)
esp_err_t meter_manager_evse_init(void);
// 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
// === Fim de: components/meter_manager/include/meter_manager.h ===
// === Início de: components/meter_manager/include/meter_events.h ===
#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
// === Fim de: components/meter_manager/include/meter_events.h ===

View File

@@ -53,7 +53,7 @@ def unir_em_partes(arquivos, prefixo="projeto_parte", limite=TAMANHO_MAX):
def main(): def main():
diretorio_main = "main" diretorio_main = "main"
componentes_escolhidos = [ componentes_escolhidos = [
"peripherals" , "meter_manager" "rest_api" , "auth"
] ]
diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos] diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos]