new buzzer component
This commit is contained in:
@@ -9,6 +9,6 @@ idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include" "lib/cAT/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
|
||||
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)
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
#include "json.h"
|
||||
#include "mqtt.h"
|
||||
#include "wifi.h"
|
||||
#include "network.h"
|
||||
#include "timeout_utils.h"
|
||||
#include "evse_error.h"
|
||||
#include "evse_api.h"
|
||||
#include "auth.h"
|
||||
#include "evse_limits.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_config.h"
|
||||
@@ -44,9 +45,9 @@ cJSON *json_get_evse_config(void)
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current());
|
||||
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current() / 10.0);
|
||||
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current() / 10.0);
|
||||
cJSON_AddBoolToObject(root, "requireAuth", evse_is_require_auth());
|
||||
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
|
||||
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current());
|
||||
cJSON_AddBoolToObject(root, "requireAuth", auth_is_enabled());
|
||||
cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet());
|
||||
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
|
||||
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")))
|
||||
{
|
||||
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")))
|
||||
{
|
||||
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")))
|
||||
{
|
||||
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")))
|
||||
{
|
||||
@@ -342,7 +343,7 @@ cJSON *json_get_state(void)
|
||||
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
|
||||
cJSON_AddBoolToObject(root, "available", evse_config_is_available());
|
||||
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());
|
||||
|
||||
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, "consumption", 0);
|
||||
}
|
||||
|
||||
// 1) Arrays temporários para ler dados do medidor
|
||||
float voltage_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
|
||||
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
|
||||
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_CreateFloatArray(voltage_f, EVSE_METER_PHASE_COUNT));
|
||||
cJSON_AddItemToObject(root, "current",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esp_system.h"
|
||||
|
||||
#include "timeout_utils.h"
|
||||
#include "wifi.h"
|
||||
#include "network.h"
|
||||
//#include "rest.h"
|
||||
|
||||
static void restart_func(void* arg)
|
||||
|
||||
@@ -8,74 +8,105 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Tamanho máximo de uma tag RFID (incluindo '\0')
|
||||
#define AUTH_TAG_MAX_LEN 20
|
||||
/// Maximum length of an RFID tag (including null terminator)
|
||||
#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 {
|
||||
char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida
|
||||
bool authorized; ///< true se a tag for reconhecida como válida
|
||||
char tag[AUTH_TAG_MAX_LEN]; ///< The tag that was read
|
||||
bool authorized; ///< true if the tag is valid
|
||||
} auth_event_t;
|
||||
|
||||
/**
|
||||
* @brief Inicializa o sistema de autenticação.
|
||||
* @brief Initializes the authentication system.
|
||||
*
|
||||
* - Carrega a configuração (enabled) da NVS
|
||||
* - Inicia o leitor Wiegand
|
||||
* - Emite evento AUTH_EVENT_INIT com estado atual
|
||||
* - Loads configuration (enabled/disabled) from NVS
|
||||
* - Starts the Wiegand reader
|
||||
* - Emits AUTH_EVENT_INIT with current status
|
||||
*/
|
||||
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
|
||||
* considerará todas as autorizações como aceitas.
|
||||
* This setting is persisted in NVS. If disabled,
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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)
|
||||
* @return true se a tag foi adicionada, false se já existia ou inválida
|
||||
* @param tag The RFID tag (max AUTH_TAG_MAX_LEN-1 characters)
|
||||
* @return true if the tag was added successfully,
|
||||
* false if it already exists or is invalid
|
||||
*/
|
||||
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
|
||||
* @return true se foi removida, false se não encontrada
|
||||
* @param tag The tag to remove
|
||||
* @return true if the tag was removed, false if not found
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Processa uma tag RFID lida (chamada normalmente pelo leitor).
|
||||
* @brief Processes a read RFID tag.
|
||||
*
|
||||
* - Verifica validade
|
||||
* - Emite evento AUTH_EVENT_TAG_PROCESSED
|
||||
* - Inicia timer de expiração se autorizada
|
||||
* - Checks whether it's authorized
|
||||
* - Emits AUTH_EVENT_TAG_PROCESSED event
|
||||
* - Starts expiration timer if authorized
|
||||
*
|
||||
* @param tag The tag that was read
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
AUTH_EVENT_TAG_PROCESSED,
|
||||
AUTH_EVENT_TAG_SAVED,
|
||||
AUTH_EVENT_ENABLED_CHANGED,
|
||||
AUTH_EVENT_INIT,
|
||||
} auth_event_id_t;
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
/*
|
||||
* auth.c
|
||||
*/
|
||||
|
||||
#include "auth.h"
|
||||
#include "auth_events.h"
|
||||
#include "esp_event.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_log.h>
|
||||
#include <string.h>
|
||||
#include "wiegand_reader.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
|
||||
#define MAX_TAGS 50
|
||||
|
||||
static const char *TAG = "Auth";
|
||||
|
||||
static bool enabled = false;
|
||||
static bool waiting_for_registration = false;
|
||||
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled);
|
||||
}
|
||||
@@ -43,8 +47,8 @@ static void load_auth_config(void) {
|
||||
|
||||
static void save_auth_config(void) {
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_u8(handle, "enabled", enabled);
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_u8(handle, NVS_ENABLED_KEY, enabled);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
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) {
|
||||
@@ -63,15 +119,30 @@ static bool is_tag_valid(const char *tag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
//TODO
|
||||
//return false;
|
||||
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) {
|
||||
enabled = value;
|
||||
save_auth_config();
|
||||
@@ -88,11 +159,13 @@ bool auth_is_enabled(void) {
|
||||
bool auth_add_tag(const char *tag) {
|
||||
if (tag_count >= MAX_TAGS) 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);
|
||||
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
tag_count++;
|
||||
|
||||
save_tags_to_nvs();
|
||||
ESP_LOGI(TAG, "Tag added: %s", tag);
|
||||
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);
|
||||
}
|
||||
tag_count--;
|
||||
|
||||
save_tags_to_nvs();
|
||||
ESP_LOGI(TAG, "Tag removed: %s", tag);
|
||||
return true;
|
||||
}
|
||||
@@ -122,20 +197,9 @@ void auth_list_tags(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void auth_init(void) {
|
||||
load_auth_config(); // carrega estado de ativação
|
||||
|
||||
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_wait_for_tag_registration(void) {
|
||||
waiting_for_registration = true;
|
||||
ESP_LOGI(TAG, "Tag registration mode enabled.");
|
||||
}
|
||||
|
||||
void auth_process_tag(const char *tag) {
|
||||
@@ -144,6 +208,22 @@ void auth_process_tag(const char *tag) {
|
||||
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;
|
||||
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
|
||||
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);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ static void wiegand_task(void *arg) {
|
||||
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));
|
||||
|
||||
data_packet_t p;
|
||||
|
||||
12
components/buzzer/CMakeLists.txt
Executable file
12
components/buzzer/CMakeLists.txt
Executable 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
|
||||
)
|
||||
2
components/buzzer/idf_component.yml
Executable file
2
components/buzzer/idf_component.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
version: "2.6.0"
|
||||
description: Authentication component
|
||||
11
components/buzzer/include/buzzer.h
Executable file
11
components/buzzer/include/buzzer.h
Executable file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void buzzer_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
26
components/buzzer/include/buzzer_events.h
Normal file
26
components/buzzer/include/buzzer_events.h
Normal 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
168
components/buzzer/src/buzzer.c
Executable 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);
|
||||
}
|
||||
3
components/buzzer/src/buzzer_events.c
Normal file
3
components/buzzer/src/buzzer_events.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "buzzer_events.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(BUZZER_EVENTS);
|
||||
@@ -10,6 +10,8 @@ set(srcs
|
||||
evse_hardware.c
|
||||
evse_pilot.c
|
||||
evse_meter.c
|
||||
evse_session.c
|
||||
evse_api.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
|
||||
37
components/evse/evse_api.c
Executable file
37
components/evse/evse_api.c
Executable 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);
|
||||
}
|
||||
@@ -50,8 +50,8 @@ void evse_check_defaults(void) {
|
||||
|
||||
// Charging current (default, persisted)
|
||||
err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
|
||||
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) {
|
||||
charging_current = max_charging_current * 10;
|
||||
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current)) {
|
||||
charging_current = max_charging_current;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
||||
needs_commit = true;
|
||||
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) {
|
||||
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;
|
||||
charging_current = 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) {
|
||||
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;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
||||
return nvs_commit(nvs);
|
||||
@@ -161,12 +161,17 @@ esp_err_t evse_set_default_charging_current(uint16_t value) {
|
||||
// Runtime current (not saved)
|
||||
// ========================
|
||||
void evse_set_runtime_charging_current(uint16_t value) {
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current)) {
|
||||
ESP_LOGW(TAG, "Rejected runtime charging current (out of bounds): %d", value);
|
||||
return;
|
||||
|
||||
if (value > (max_charging_current)) {
|
||||
value= max_charging_current;
|
||||
}
|
||||
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) ) {
|
||||
value= MIN_CHARGING_CURRENT_LIMIT;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -219,18 +224,6 @@ esp_err_t evse_set_temp_threshold(uint8_t value) {
|
||||
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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#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"
|
||||
@@ -71,25 +72,6 @@ void evse_process(void) {
|
||||
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
|
||||
// ================================
|
||||
|
||||
@@ -69,15 +69,16 @@ static void update_outputs(evse_state_t state) {
|
||||
if (board_config.socket_lock && socket_outlet) {
|
||||
socket_lock_set_locked(true);
|
||||
}
|
||||
|
||||
if (rcm_test()) {
|
||||
ESP_LOGI(TAG, "RCM self test passed");
|
||||
//ESP_LOGI(TAG, "RCM self test passed");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "RCM self test failed");
|
||||
//ESP_LOGW(TAG, "RCM self test failed");
|
||||
}
|
||||
break;
|
||||
|
||||
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);
|
||||
break;
|
||||
|
||||
@@ -90,7 +91,7 @@ static void update_outputs(evse_state_t state) {
|
||||
|
||||
case EVSE_STATE_C2:
|
||||
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!
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <inttypes.h> // for PRIu32
|
||||
#include "evse_state.h"
|
||||
#include "evse_api.h"
|
||||
#include "evse_limits.h"
|
||||
#include "evse_meter.h"
|
||||
#include "evse_session.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -134,37 +136,49 @@ bool evse_is_limit_reached(void) {
|
||||
return evse_get_limit_reached();
|
||||
}
|
||||
|
||||
|
||||
// ========================
|
||||
// Limit checking logic
|
||||
// This function must be called periodically while charging.
|
||||
// It will flag the session as "limit reached" when thresholds are violated.
|
||||
// ========================
|
||||
|
||||
void evse_limits_check(void) {
|
||||
evse_state_t state = evse_get_state();
|
||||
if (!evse_state_is_charging(state)) return;
|
||||
// Only check during an active charging session
|
||||
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;
|
||||
|
||||
uint32_t energy = evse_meter_get_total_energy();
|
||||
uint32_t power = evse_meter_get_instant_power();
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
TickType_t start = evse_get_session_start();
|
||||
|
||||
if (consumption_limit > 0 && energy >= consumption_limit) {
|
||||
ESP_LOGW("EVSE", "Energy limit reached");
|
||||
// 1) Energy consumption limit (Wh)
|
||||
if (consumption_limit > 0 && sess.energy_wh >= consumption_limit) {
|
||||
ESP_LOGW("EVSE_LIMITS",
|
||||
"Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh",
|
||||
sess.energy_wh, consumption_limit);
|
||||
reached = true;
|
||||
}
|
||||
|
||||
if (charging_time_limit > 0 &&
|
||||
(now - start) >= pdMS_TO_TICKS(charging_time_limit * 1000)) {
|
||||
ESP_LOGW("EVSE", "Charging time limit reached");
|
||||
// 2) Charging time limit (seconds)
|
||||
if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit) {
|
||||
ESP_LOGW("EVSE_LIMITS",
|
||||
"Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s",
|
||||
sess.duration_s, charging_time_limit);
|
||||
reached = true;
|
||||
}
|
||||
|
||||
if (under_power_limit > 0 && power < under_power_limit) {
|
||||
ESP_LOGW("EVSE", "Under power limit reached");
|
||||
// 3) Under-power limit (instantaneous power)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "evse_config.h"
|
||||
#include "evse_api.h"
|
||||
#include "evse_meter.h"
|
||||
#include "evse_session.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -89,6 +90,7 @@ void evse_manager_init(void) {
|
||||
evse_hardware_init();
|
||||
evse_state_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(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
|
||||
@@ -105,6 +107,7 @@ void evse_manager_tick(void) {
|
||||
evse_error_tick();
|
||||
evse_state_tick();
|
||||
evse_temperature_check();
|
||||
evse_session_tick();
|
||||
|
||||
if (auth_enabled) {
|
||||
// If the car is disconnected, revoke authorization
|
||||
|
||||
@@ -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);
|
||||
xSemaphoreGive(meter_mutex);
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
ESP_LOGI(TAG,
|
||||
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
|
||||
"voltage[V]={%.2f,%.2f,%.2f}, "
|
||||
"current[A]={%.2f,%.2f,%.2f}, "
|
||||
@@ -63,9 +63,9 @@ void evse_meter_init(void) {
|
||||
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);
|
||||
uint32_t sum = 0;
|
||||
int sum = 0;
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
sum += meter_data.power_watts[i];
|
||||
}
|
||||
@@ -73,14 +73,14 @@ uint32_t evse_meter_get_instant_power(void) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint32_t evse_meter_get_total_energy(void) {
|
||||
int evse_meter_get_total_energy(void) {
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
uint32_t val = meter_data.energy_wh;
|
||||
int val = meter_data.energy_wh;
|
||||
xSemaphoreGive(meter_mutex);
|
||||
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);
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
power[i] = meter_data.power_watts[i];
|
||||
|
||||
@@ -79,28 +79,36 @@ void pilot_set_level(bool level)
|
||||
|
||||
void pilot_set_amps(uint16_t amps)
|
||||
{
|
||||
if (amps < 60 || amps > 800) {
|
||||
ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps);
|
||||
if (amps < 6 || amps > 80) {
|
||||
ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 6–80 A)", amps);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t duty;
|
||||
if (amps <= 510) {
|
||||
duty = (PILOT_PWM_MAX_DUTY * amps) / 600;
|
||||
uint32_t duty_percent;
|
||||
|
||||
if (amps <= 51) {
|
||||
duty_percent = (amps * 10) / 6; // Duty (%) = Amps / 0.6
|
||||
} 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;
|
||||
last_pilot_level = 0;
|
||||
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_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int compare_int(const void *a, const void *b) {
|
||||
return (*(int *)a - *(int *)b);
|
||||
}
|
||||
|
||||
84
components/evse/evse_session.c
Normal file
84
components/evse/evse_session.c
Normal 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;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "evse_api.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_session.h"
|
||||
#include "evse_events.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
@@ -11,10 +12,11 @@
|
||||
|
||||
static evse_state_t current_state = EVSE_STATE_A;
|
||||
static bool is_authorized = false;
|
||||
static TickType_t session_start_tick = 0;
|
||||
|
||||
static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
static const char *TAG = "evse_state";
|
||||
|
||||
// =========================
|
||||
// Internal Mapping
|
||||
// =========================
|
||||
@@ -36,34 +38,45 @@ static evse_state_event_t map_state_to_event(evse_state_t s) {
|
||||
// Public API
|
||||
// =========================
|
||||
|
||||
void evse_set_state(evse_state_t state) {
|
||||
void evse_set_state(evse_state_t new_state) {
|
||||
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);
|
||||
previous_state = current_state;
|
||||
if (state != current_state) {
|
||||
current_state = state;
|
||||
prev_state = current_state;
|
||||
if (new_state != current_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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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) {
|
||||
ESP_LOGI("EVSE_STATE", "State changed from %s to %s",
|
||||
evse_state_to_str(previous_state),
|
||||
evse_state_to_str(state));
|
||||
const char *prev_str = evse_state_to_str(prev_state);
|
||||
const char *curr_str = evse_state_to_str(new_state);
|
||||
ESP_LOGI(TAG, "State changed: %s → %s", prev_str, curr_str);
|
||||
|
||||
evse_state_event_data_t evt = {
|
||||
.state = map_state_to_event(state)
|
||||
.state = map_state_to_event(new_state)
|
||||
};
|
||||
esp_event_post(EVSE_EVENTS,
|
||||
EVSE_EVENT_STATE_CHANGED,
|
||||
@@ -73,6 +86,8 @@ void evse_set_state(evse_state_t state) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
evse_state_t evse_get_state(void) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
evse_state_t s = current_state;
|
||||
@@ -80,13 +95,6 @@ evse_state_t evse_get_state(void) {
|
||||
return s;
|
||||
}
|
||||
|
||||
TickType_t evse_get_session_start(void) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
TickType_t t = session_start_tick;
|
||||
portEXIT_CRITICAL(&state_mux);
|
||||
return t;
|
||||
}
|
||||
|
||||
const char* evse_state_to_str(evse_state_t state) {
|
||||
switch (state) {
|
||||
case EVSE_STATE_A: return "A - EV Not Connected (12V)";
|
||||
@@ -105,7 +113,6 @@ const char* evse_state_to_str(evse_state_t state) {
|
||||
void evse_state_init(void) {
|
||||
portENTER_CRITICAL(&state_mux);
|
||||
current_state = EVSE_STATE_A;
|
||||
session_start_tick = xTaskGetTickCount();
|
||||
is_authorized = true;
|
||||
portEXIT_CRITICAL(&state_mux);
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "evse_state.h" // Tipos e estados
|
||||
#include "evse_state.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "evse_session.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -24,10 +26,17 @@ evse_state_t evse_get_state(void);
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -55,10 +55,6 @@ esp_err_t evse_set_rcm(bool rcm);
|
||||
uint8_t evse_get_temp_threshold(void);
|
||||
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
|
||||
bool evse_config_is_available(void);
|
||||
void evse_config_set_available(bool available);
|
||||
|
||||
@@ -13,13 +13,13 @@ extern "C" {
|
||||
void evse_meter_init(void);
|
||||
|
||||
/// 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)
|
||||
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)
|
||||
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)
|
||||
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]);
|
||||
|
||||
53
components/evse/include/evse_session.h
Normal file
53
components/evse/include/evse_session.h
Normal 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
|
||||
@@ -53,11 +53,6 @@ evse_state_t evse_get_state(void);
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -10,18 +10,17 @@
|
||||
#include <string.h>
|
||||
#include "meter_events.h"
|
||||
#include "evse_events.h"
|
||||
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static const char *TAG = "loadbalancer";
|
||||
|
||||
// Limites configuráveis
|
||||
// Configurable limits
|
||||
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
|
||||
#define MAX_CHARGING_CURRENT_LIMIT 32 // A
|
||||
#define MIN_GRID_CURRENT_LIMIT 6 // A
|
||||
#define MAX_GRID_CURRENT_LIMIT 100 // A
|
||||
|
||||
// Parâmetros
|
||||
// Parameters
|
||||
static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
||||
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_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,
|
||||
esp_event_base_t base,
|
||||
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;
|
||||
|
||||
ESP_LOGI(TAG, "Received meter event from source: %s", evt->source);
|
||||
ESP_LOGI(TAG, "Raw IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]);
|
||||
ESP_LOGI(TAG, "Raw VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[2]);
|
||||
ESP_LOGI(TAG, "Raw Power: [W1=%d, W2=%d, W3=%d]", evt->watt[0], evt->watt[1], evt->watt[2]);
|
||||
ESP_LOGI(TAG, "IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]);
|
||||
ESP_LOGI(TAG, "VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[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",
|
||||
evt->frequency, evt->power_factor, evt->total_energy);
|
||||
|
||||
// Calcula a corrente máxima entre as 3 fases
|
||||
float max_irms = evt->irms[0];
|
||||
for (int i = 1; i < 3; ++i)
|
||||
{
|
||||
@@ -63,13 +67,12 @@ static void loadbalancer_meter_event_handler(void *handler_arg,
|
||||
|
||||
ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms);
|
||||
|
||||
// Atualiza com filtro exponencial dependendo da origem
|
||||
if (strncmp(evt->source, "GRID", 4) == 0)
|
||||
if (evt->source && strcmp(evt->source, "GRID") == 0)
|
||||
{
|
||||
grid_current = input_filter_update(&grid_filter, max_irms);
|
||||
ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current);
|
||||
}
|
||||
else if (strncmp(evt->source, "EVSE", 4) == 0)
|
||||
else if (evt->source && strcmp(evt->source, "EVSE") == 0)
|
||||
{
|
||||
evse_current = input_filter_update(&evse_filter, max_irms);
|
||||
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)
|
||||
{
|
||||
case EVSE_STATE_EVENT_IDLE:
|
||||
// Vehicle is disconnected - current flow can be reduced or reset
|
||||
ESP_LOGI(TAG, "EVSE is IDLE - possible to release current");
|
||||
ESP_LOGI(TAG, "EVSE is IDLE - vehicle disconnected");
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_WAITING:
|
||||
// EV is connected but not charging yet (e.g., waiting for authorization)
|
||||
ESP_LOGI(TAG, "EVSE is waiting - connected but not charging");
|
||||
ESP_LOGI(TAG, "EVSE is WAITING - connected but not charging");
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_CHARGING:
|
||||
ESP_LOGI(TAG, "EVSE is CHARGING - resetting filters");
|
||||
grid_current = 0.0f;
|
||||
evse_current = 0.0f;
|
||||
// Charging has started - maintain or monitor current usage
|
||||
ESP_LOGI(TAG, "EVSE is charging");
|
||||
input_filter_reset(&grid_filter);
|
||||
input_filter_reset(&evse_filter);
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_FAULT:
|
||||
// A fault has occurred - safety measures may be needed
|
||||
ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing");
|
||||
// Optional: disable load balancing during fault condition
|
||||
// loadbalancer_set_enabled(false);
|
||||
ESP_LOGW(TAG, "EVSE is in FAULT state - consider disabling load balancing");
|
||||
break;
|
||||
|
||||
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()
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
bool needs_commit = false;
|
||||
uint8_t temp_u8;
|
||||
|
||||
// max_grid_current
|
||||
err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8);
|
||||
if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT)
|
||||
{
|
||||
@@ -145,11 +142,10 @@ static esp_err_t loadbalancer_load_config()
|
||||
{
|
||||
max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
||||
nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current);
|
||||
ESP_LOGW(TAG, "max_grid_current missing or invalid, setting default: %d", max_grid_current);
|
||||
ESP_LOGW(TAG, "max_grid_current invalid or missing, set to default: %d", max_grid_current);
|
||||
needs_commit = true;
|
||||
}
|
||||
|
||||
// loadbalancer_enabled
|
||||
err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8);
|
||||
if (err == ESP_OK && temp_u8 <= 1)
|
||||
{
|
||||
@@ -159,7 +155,7 @@ static esp_err_t loadbalancer_load_config()
|
||||
{
|
||||
loadbalancer_enabled = false;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -172,10 +168,9 @@ static esp_err_t loadbalancer_load_config()
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Salva o estado habilitado no NVS
|
||||
void loadbalancer_set_enabled(bool enabled)
|
||||
{
|
||||
ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled);
|
||||
ESP_LOGI(TAG, "Setting load balancing to %d", enabled);
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK)
|
||||
@@ -189,7 +184,7 @@ void loadbalancer_set_enabled(bool enabled)
|
||||
{
|
||||
nvs_commit(handle);
|
||||
loadbalancer_enabled = enabled;
|
||||
ESP_LOGI(TAG, "Load balancing enabled state saved");
|
||||
ESP_LOGI(TAG, "Load balancing state saved");
|
||||
|
||||
loadbalancer_state_event_t evt = {
|
||||
.enabled = enabled,
|
||||
@@ -209,7 +204,6 @@ void loadbalancer_set_enabled(bool enabled)
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
// Define e salva o limite de corrente da rede
|
||||
esp_err_t load_balancing_set_max_grid_current(uint8_t value)
|
||||
{
|
||||
if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT)
|
||||
@@ -252,20 +246,23 @@ bool loadbalancer_is_enabled(void)
|
||||
return loadbalancer_enabled;
|
||||
}
|
||||
|
||||
// Tarefa principal com eventos
|
||||
void loadbalancer_task(void *param)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!loadbalancer_enabled)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
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;
|
||||
}
|
||||
@@ -274,7 +271,7 @@ void loadbalancer_task(void *param)
|
||||
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 = {
|
||||
.limit = available,
|
||||
@@ -286,7 +283,7 @@ void loadbalancer_task(void *param)
|
||||
sizeof(evt),
|
||||
portMAX_DELAY);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@ set(includes
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${includes}"
|
||||
PRIV_REQUIRES nvs_flash
|
||||
REQUIRES esp_event esp-modbus spi_bus_manager)
|
||||
REQUIRES esp_event esp-modbus spi_bus_manager network)
|
||||
|
||||
@@ -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) {
|
||||
ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada");
|
||||
|
||||
@@ -68,7 +67,7 @@ static void meter_ade7758_task_func(void *param) {
|
||||
while (true) {
|
||||
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[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_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 (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
meter_data = meterData;
|
||||
|
||||
@@ -54,11 +54,21 @@ typedef struct {
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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) {
|
||||
meter_event_data_t evt = {
|
||||
.source = "GRID",
|
||||
@@ -212,7 +222,15 @@ esp_err_t meter_zigbee_start(void) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void meter_zigbee_stop(void) {
|
||||
|
||||
send_stop_command();
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // Aguarda o outro lado processar
|
||||
|
||||
if (meter_zigbee_task) {
|
||||
vTaskDelete(meter_zigbee_task);
|
||||
meter_zigbee_task = NULL;
|
||||
@@ -225,7 +243,3 @@ void meter_zigbee_stop(void) {
|
||||
meter_mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool meter_zigbee_is_running(void) {
|
||||
return meter_zigbee_task != NULL;
|
||||
}
|
||||
|
||||
@@ -2,66 +2,116 @@
|
||||
#define METER_MANAGER_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 {
|
||||
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_NONE, // No meter
|
||||
METER_TYPE_ADE7758, // ADE7758 meter
|
||||
METER_TYPE_ORNO513, // ORNO-513
|
||||
METER_TYPE_ORNO516, // ORNO-516
|
||||
METER_TYPE_MONO_ZIGBEE, // Zigbee single-phase
|
||||
METER_TYPE_TRIF_ZIGBEE // Zigbee three-phase
|
||||
} 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.
|
||||
*/
|
||||
|
||||
// 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);
|
||||
esp_err_t meter_manager_init(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);
|
||||
|
||||
// Inicia o medidor Grid com o tipo especificado
|
||||
/**
|
||||
* @brief Starts the Grid meter.
|
||||
*/
|
||||
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);
|
||||
|
||||
// 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)
|
||||
/**
|
||||
* @brief Sets the Grid meter type and saves it to NVS.
|
||||
*/
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
/**
|
||||
* @brief Converts a string to a meter_type_t.
|
||||
*/
|
||||
meter_type_t string_to_meter_type(const char *str);
|
||||
|
||||
#endif // METER_MANAGER_H
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include <string.h>
|
||||
#include "network_events.h"
|
||||
|
||||
|
||||
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_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
|
||||
static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type) {
|
||||
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
|
||||
esp_err_t meter_manager_evse_init() {
|
||||
esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
set(srcs
|
||||
"src/wifi.c")
|
||||
"src/network_events.c"
|
||||
"src/network.c"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES nvs_flash esp_netif esp_wifi mdns)
|
||||
PRIV_REQUIRES nvs_flash esp_netif esp_wifi mdns esp_event)
|
||||
24
components/network/include/network_events.h
Normal file
24
components/network/include/network_events.h
Normal 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
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "nvs.h"
|
||||
#include "mdns.h"
|
||||
|
||||
#include "wifi.h"
|
||||
#include "network_events.h"
|
||||
#include "network.h"
|
||||
|
||||
|
||||
#define AP_SSID "plx-%02x%02x%02x"
|
||||
|
||||
@@ -292,6 +294,8 @@ void wifi_ap_start(void)
|
||||
{
|
||||
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);
|
||||
esp_wifi_stop();
|
||||
|
||||
@@ -304,6 +308,9 @@ void wifi_ap_start(void)
|
||||
void wifi_ap_stop(void)
|
||||
{
|
||||
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);
|
||||
esp_wifi_stop();
|
||||
|
||||
4
components/network/src/network_events.c
Normal file
4
components/network/src/network_events.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "network_events.h"
|
||||
|
||||
// Define o base de eventos
|
||||
ESP_EVENT_DEFINE_BASE(NETWORK_EVENTS);
|
||||
@@ -3,7 +3,6 @@ set(srcs
|
||||
"src/adc121s021_dma.c"
|
||||
"src/peripherals.c"
|
||||
"src/led.c"
|
||||
"src/buzzer.c"
|
||||
"src/proximity.c"
|
||||
"src/ac_relay.c"
|
||||
"src/socket_lock.c"
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "peripherals.h"
|
||||
#include "adc.h"
|
||||
#include "led.h"
|
||||
#include "buzzer.h"
|
||||
//#include "buzzer.h"
|
||||
#include "proximity.h"
|
||||
#include "ac_relay.h"
|
||||
#include "socket_lock.h"
|
||||
@@ -13,7 +13,7 @@ void peripherals_init(void)
|
||||
{
|
||||
ac_relay_init();
|
||||
led_init();
|
||||
buzzer_init();
|
||||
//buzzer_init();
|
||||
adc_init();
|
||||
proximity_init();
|
||||
// socket_lock_init();
|
||||
|
||||
@@ -7,66 +7,63 @@
|
||||
#include "board_config.h"
|
||||
#include "evse_api.h"
|
||||
|
||||
// static bool do_test = false;
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
static const char *TAG = "RCM";
|
||||
|
||||
void rcm_init(void)
|
||||
{
|
||||
if (board_config.rcm) {
|
||||
gpio_config_t io_conf = {};
|
||||
|
||||
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));
|
||||
if (!board_config.rcm) {
|
||||
ESP_LOGW(TAG, "RCM não está habilitado na configuração.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// do_test = true;
|
||||
// test_triggered = false;
|
||||
|
||||
// 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;
|
||||
if (!board_config.rcm) {
|
||||
//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));
|
||||
|
||||
bool success = gpio_get_level(board_config.rcm_gpio) == 1;
|
||||
|
||||
gpio_set_level(board_config.rcm_test_gpio, 0);
|
||||
|
||||
ESP_LOGI(TAG, "Teste RCM: %s", success ? "PASSOU" : "FALHOU");
|
||||
return success;
|
||||
}
|
||||
|
||||
bool rcm_is_triggered(void)
|
||||
{
|
||||
// bool _triggered = triggered;
|
||||
// if (gpio_get_level(board_config.rcm_gpio) == 0) {
|
||||
// triggered = false;
|
||||
// }
|
||||
// return _triggered;
|
||||
if (!board_config.rcm) {
|
||||
//ESP_LOGW(TAG, "Verificação de trigger com RCM desabilitado.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpio_get_level(board_config.rcm_gpio) == 1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
return gpio_get_level(board_config.rcm_gpio) == 1;
|
||||
|
||||
@@ -62,29 +62,56 @@ static void publish_message(const char* topic, cJSON* root)
|
||||
free((void*)json);
|
||||
}
|
||||
|
||||
|
||||
static void handle_message(const char* topic, const char* data)
|
||||
{
|
||||
char base_topic[32];
|
||||
mqtt_get_base_topic(base_topic);
|
||||
|
||||
ESP_LOGI(TAG, "[MQTT] Message received");
|
||||
ESP_LOGI(TAG, " > Topic: %s", topic);
|
||||
ESP_LOGI(TAG, " > Payload: %s", data);
|
||||
ESP_LOGI(TAG, "Topic: %s", topic);
|
||||
ESP_LOGI(TAG, "data: %s", data);
|
||||
ESP_LOGI(TAG, "base_topic: %s", base_topic);
|
||||
|
||||
if (strncmp(topic, base_topic, strlen(base_topic)) == 0) {
|
||||
const char* sub_topic = &topic[strlen(base_topic)];
|
||||
ESP_LOGI(TAG, " > Subtopic detected: %s", sub_topic);
|
||||
|
||||
ESP_LOGI(TAG, "Sub_topic: %s", sub_topic);
|
||||
|
||||
if (strcmp(sub_topic, "/request/config/evse") == 0) {
|
||||
ESP_LOGI(TAG, " → Responding with EVSE configuration");
|
||||
cJSON* root = json_get_evse_config();
|
||||
publish_message("/response/config/evse", root);
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
ESP_LOGW(TAG, " ! Unknown command: %s", sub_topic);
|
||||
} else if (strcmp(sub_topic, "/request/config/wifi") == 0) {
|
||||
//cJSON* root = json_get_wifi_config();
|
||||
//publish_message("/response/config/wifi", root);
|
||||
//cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/config/mqtt") == 0) {
|
||||
cJSON* root = json_get_mqtt_config();
|
||||
publish_message("/response/config/mqtt", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/boardConfig") == 0) {
|
||||
cJSON* root = json_get_board_config();
|
||||
publish_message("/response/boardConfig", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/info") == 0) {
|
||||
cJSON* root = json_get_info();
|
||||
publish_message("/response/info", root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/request/restart") == 0) {
|
||||
timeout_restart();
|
||||
} else if (strcmp(sub_topic, "/set/config/evse") == 0) {
|
||||
cJSON* root = cJSON_Parse(data);
|
||||
json_set_evse_config(root);
|
||||
cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/set/config/wifi") == 0) {
|
||||
//cJSON* root = cJSON_Parse(data);
|
||||
//json_set_wifi_config(root, true);
|
||||
//cJSON_Delete(root);
|
||||
} else if (strcmp(sub_topic, "/set/config/mqtt") == 0) {
|
||||
cJSON* root = cJSON_Parse(data);
|
||||
json_set_mqtt_config(root);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, " ! Topic does not match base: %s", topic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
components/protocols/webfolder/assets/index-C_08vMAY.js
Normal file
51
components/protocols/webfolder/assets/index-C_08vMAY.js
Normal file
File diff suppressed because one or more lines are too long
1
components/protocols/webfolder/assets/index-Doq3307m.css
Normal file
1
components/protocols/webfolder/assets/index-Doq3307m.css
Normal file
File diff suppressed because one or more lines are too long
@@ -13,8 +13,8 @@
|
||||
}
|
||||
</style>
|
||||
<title>Vite + React</title>
|
||||
<script type="module" crossorigin src="/assets/index-3W2ZKmTa.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Brq1-keN.css">
|
||||
<script type="module" crossorigin src="/assets/index-C_08vMAY.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Doq3307m.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
// =========================
|
||||
// auth_api.c
|
||||
// =========================
|
||||
|
||||
#include "auth_api.h"
|
||||
#include "auth.h"
|
||||
#include "esp_log.h"
|
||||
#include "cJSON.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "auth_api";
|
||||
|
||||
// =================================
|
||||
// Dummy user storage (static list)
|
||||
// =================================
|
||||
|
||||
static struct {
|
||||
char username[128];
|
||||
} users[10] = { /*{"admin"}, {"user1"}*/ };
|
||||
} users[10] = { /* {"admin"}, {"user1"} */ };
|
||||
|
||||
static int num_users = 2;
|
||||
|
||||
// =================================
|
||||
// Handlers for Auth Methods (RFID)
|
||||
// =================================
|
||||
|
||||
static esp_err_t auth_methods_get_handler(httpd_req_t *req) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() );
|
||||
cJSON_AddBoolToObject(json, "RFID", auth_is_enabled());
|
||||
char *str = cJSON_PrintUnformatted(json);
|
||||
httpd_resp_sendstr(req, str);
|
||||
free(str);
|
||||
@@ -53,6 +64,9 @@ static esp_err_t auth_methods_post_handler(httpd_req_t *req) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// =================================
|
||||
// User Management Handlers
|
||||
// =================================
|
||||
|
||||
static esp_err_t users_get_handler(httpd_req_t *req) {
|
||||
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_AddItemToObject(root, "users", list);
|
||||
char *str = cJSON_Print(root);
|
||||
char *str = cJSON_PrintUnformatted(root);
|
||||
httpd_resp_sendstr(req, str);
|
||||
free(str);
|
||||
cJSON_Delete(root);
|
||||
@@ -75,7 +89,9 @@ static esp_err_t users_post_handler(httpd_req_t *req) {
|
||||
char buf[128];
|
||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
if (len <= 0) return ESP_FAIL;
|
||||
|
||||
buf[len] = '\0';
|
||||
|
||||
if (num_users < 10) {
|
||||
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
|
||||
num_users++;
|
||||
@@ -107,7 +123,58 @@ static esp_err_t users_delete_handler(httpd_req_t *req) {
|
||||
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) {
|
||||
// Auth methods
|
||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||
.uri = "/api/v1/config/auth-methods",
|
||||
.method = HTTP_GET,
|
||||
@@ -120,6 +187,8 @@ void register_auth_handlers(httpd_handle_t server, void *ctx) {
|
||||
.handler = auth_methods_post_handler,
|
||||
.user_ctx = ctx
|
||||
});
|
||||
|
||||
// Users
|
||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||
.uri = "/api/v1/config/users",
|
||||
.method = HTTP_GET,
|
||||
@@ -138,4 +207,24 @@ void register_auth_handlers(httpd_handle_t server, void *ctx) {
|
||||
.handler = users_delete_handler,
|
||||
.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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ static esp_err_t dashboard_get_handler(httpd_req_t *req) {
|
||||
cJSON *charger1 = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(charger1, "id", 1);
|
||||
cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state));
|
||||
cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current() / 10);
|
||||
cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current());
|
||||
cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current());
|
||||
|
||||
// Calcular a potência com base na corrente (considerando 230V)
|
||||
int power = (evse_get_charging_current() / 10) * 230;
|
||||
int power = (evse_get_charging_current()) * 230;
|
||||
cJSON_AddNumberToObject(charger1, "power", power);
|
||||
|
||||
cJSON_AddItemToArray(chargers, charger1);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "network_api.h"
|
||||
#include "esp_log.h"
|
||||
#include "cJSON.h"
|
||||
#include "wifi.h"
|
||||
#include "network.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
static const char *TAG = "network_api";
|
||||
|
||||
@@ -1,33 +1,57 @@
|
||||
dependencies:
|
||||
espressif/cmake_utilities:
|
||||
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.1'
|
||||
source:
|
||||
service_url: https://api.components.espressif.com/
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 0.5.3
|
||||
espressif/esp-modbus:
|
||||
component_hash: 5d5e90b9e55721a8a194b301ad8102d4affb647f47b74cd413ff7d1ce2c1169c
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.3'
|
||||
source:
|
||||
service_url: https://api.components.espressif.com/
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.0.18
|
||||
espressif/mdns:
|
||||
component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.0'
|
||||
source:
|
||||
service_url: https://api.components.espressif.com/
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.8.2
|
||||
espressif/ntc_driver:
|
||||
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:
|
||||
service_url: https://api.components.espressif.com/
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 0.3.0
|
||||
idf:
|
||||
component_hash: null
|
||||
source:
|
||||
type: idf
|
||||
version: 5.3.0
|
||||
manifest_hash: 934a9d746c65c54673c89e0201c09a3bdccec9ea3fd2bcff03ef8525f1d77443
|
||||
version: 5.4.2
|
||||
direct_dependencies:
|
||||
- espressif/esp-modbus
|
||||
- espressif/mdns
|
||||
- espressif/ntc_driver
|
||||
- idf
|
||||
manifest_hash: 914523e41b41de9584db65fbe408fdbefb25ae9417eadf249d129e6d77f28d4c
|
||||
target: esp32
|
||||
version: 1.0.0
|
||||
version: 2.0.0
|
||||
|
||||
108
main/main.c
108
main/main.c
@@ -15,7 +15,7 @@
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "wifi.h"
|
||||
#include "network.h"
|
||||
#include "board_config.h"
|
||||
#include "logger.h"
|
||||
#include "rest_main.h"
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "auth.h"
|
||||
#include "loadbalancer.h"
|
||||
#include "meter_manager.h"
|
||||
#include "buzzer.h"
|
||||
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
|
||||
@@ -78,77 +79,134 @@ static void fs_init(void) {
|
||||
fs_info(&cfg_conf);
|
||||
fs_info(&data_conf);
|
||||
}
|
||||
|
||||
//
|
||||
// Wi-Fi event monitoring task
|
||||
//
|
||||
static void wifi_event_task_func(void *param) {
|
||||
EventBits_t mode_bits;
|
||||
while (1) {
|
||||
mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
for (;;) {
|
||||
// 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 (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT) {
|
||||
xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
// We're in AP mode: wait for a client to connect within the timeout
|
||||
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 {
|
||||
// Timeout expired with no client—optionally stop the AP
|
||||
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
|
||||
//wifi_ap_stop();
|
||||
// wifi_ap_stop();
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
ESP_LOGI(TAG, "Ativando modo AP");
|
||||
// If not already in AP mode, start it
|
||||
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
|
||||
ESP_LOGI(TAG, "Starting Wi-Fi AP mode");
|
||||
wifi_ap_start();
|
||||
}
|
||||
}
|
||||
|
||||
// Task to handle button press/release notifications
|
||||
static void user_input_task_func(void *param) {
|
||||
uint32_t notification;
|
||||
while (1) {
|
||||
if (xTaskNotifyWait(0x00, 0xFF, ¬ification, portMAX_DELAY)) {
|
||||
for (;;) {
|
||||
// Wait for notification bits from ISR
|
||||
if (xTaskNotifyWait(
|
||||
0, // do not clear any bits on entry
|
||||
UINT32_MAX, // clear all bits on exit
|
||||
¬ification,
|
||||
portMAX_DELAY)) {
|
||||
// Handle button press event
|
||||
if (notification & PRESS_BIT) {
|
||||
press_tick = xTaskGetTickCount();
|
||||
pressed = true;
|
||||
ESP_LOGI(TAG, "Pressed Button");
|
||||
ESP_LOGI(TAG, "Button Pressed");
|
||||
handle_button_press();
|
||||
}
|
||||
|
||||
if (notification & RELEASED_BIT && pressed) {
|
||||
// Handle button release event (only if previously pressed)
|
||||
if ((notification & RELEASED_BIT) && pressed) {
|
||||
pressed = false;
|
||||
ESP_LOGI(TAG, "Reladead Buttton");
|
||||
ESP_LOGI(TAG, "Button Released");
|
||||
handle_button_press();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ISR for button GPIO interrupt (active-low)
|
||||
static void IRAM_ATTR button_isr_handler(void *arg) {
|
||||
BaseType_t higher_task_woken = pdFALSE;
|
||||
TickType_t now = xTaskGetTickCountFromISR();
|
||||
|
||||
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) return;
|
||||
// Debounce: ignore interrupts occurring too close together
|
||||
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
|
||||
return;
|
||||
}
|
||||
last_interrupt_tick = now;
|
||||
|
||||
if (!gpio_get_level(board_config.button_wifi_gpio)) {
|
||||
xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken);
|
||||
// Read GPIO level: 0 = button pressed, 1 = button released
|
||||
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 {
|
||||
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) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void button_init(void) {
|
||||
gpio_config_t conf = {
|
||||
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
|
||||
@@ -167,6 +225,7 @@ static void button_init(void) {
|
||||
static void init_modules(void) {
|
||||
peripherals_init();
|
||||
//api_init();
|
||||
buzzer_init();
|
||||
ESP_ERROR_CHECK(rest_server_init("/data"));
|
||||
protocols_init();
|
||||
evse_manager_init();
|
||||
@@ -174,11 +233,12 @@ static void init_modules(void) {
|
||||
button_init();
|
||||
auth_init();
|
||||
loadbalancer_init();
|
||||
meter_manager_grid_init();
|
||||
meter_manager_grid_start();
|
||||
meter_manager_evse_init();
|
||||
meter_manager_evse_start();
|
||||
meter_manager_init();
|
||||
meter_manager_start();
|
||||
|
||||
|
||||
|
||||
//wifi_ap_start();
|
||||
// Outros módulos (descomente conforme necessário)
|
||||
// meter_init();
|
||||
// ocpp_start();
|
||||
|
||||
4713
projeto_parte1.c
4713
projeto_parte1.c
File diff suppressed because it is too large
Load Diff
2714
projeto_parte2.c
2714
projeto_parte2.c
File diff suppressed because it is too large
Load Diff
824
projeto_parte3.c
824
projeto_parte3.c
@@ -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 ===
|
||||
@@ -53,7 +53,7 @@ def unir_em_partes(arquivos, prefixo="projeto_parte", limite=TAMANHO_MAX):
|
||||
def main():
|
||||
diretorio_main = "main"
|
||||
componentes_escolhidos = [
|
||||
"peripherals" , "meter_manager"
|
||||
"rest_api" , "auth"
|
||||
]
|
||||
|
||||
diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos]
|
||||
|
||||
Reference in New Issue
Block a user