From bd587a10c08c99de0542d72185dcb2e1fb70ae03 Mon Sep 17 00:00:00 2001 From: PlxEV Date: Tue, 22 Jul 2025 00:09:58 +0100 Subject: [PATCH] new buzzer component --- components/api/CMakeLists.txt | 2 +- components/api/src/json.c | 37 +- components/api/src/timeout_utils.c | 2 +- components/auth/include/auth.h | 83 +- components/auth/include/auth_events.h | 1 + components/auth/src/auth.c | 149 +- components/auth/src/wiegand_reader.c | 2 +- components/buzzer/CMakeLists.txt | 12 + components/buzzer/idf_component.yml | 2 + components/buzzer/include/buzzer.h | 11 + components/buzzer/include/buzzer_events.h | 26 + components/buzzer/src/buzzer.c | 168 + components/buzzer/src/buzzer_events.c | 3 + components/evse/CMakeLists.txt | 2 + components/evse/evse_api.c | 37 + components/evse/evse_config.c | 33 +- components/evse/evse_core.c | 20 +- components/evse/evse_fsm.c | 9 +- components/evse/evse_limits.c | 48 +- components/evse/evse_manager.c | 3 + components/evse/evse_meter.c | 12 +- components/evse/evse_pilot.c | 24 +- components/evse/evse_session.c | 84 + components/evse/evse_state.c | 65 +- components/evse/include/evse_api.h | 15 +- components/evse/include/evse_config.h | 4 - components/evse/include/evse_meter.h | 6 +- components/evse/include/evse_session.h | 53 + components/evse/include/evse_state.h | 5 - components/loadbalancer/src/loadbalancer.c | 71 +- components/meter_manager/CMakeLists.txt | 2 +- .../driver/meter_ade7758/meter_ade7758.c | 18 +- .../driver/meter_zigbee/meter_zigbee.c | 26 +- .../meter_manager/include/meter_manager.h | 124 +- components/meter_manager/src/meter_manager.c | 106 + components/network/CMakeLists.txt | 6 +- .../network/include/{wifi.h => network.h} | 0 components/network/include/network_events.h | 24 + components/network/src/{wifi.c => network.c} | 9 +- components/network/src/network_events.c | 4 + components/peripherals/CMakeLists.txt | 1 - components/peripherals/include/buzzer.h | 22 - components/peripherals/src/buzzer.c | 163 - components/peripherals/src/peripherals.c | 4 +- components/peripherals/src/rcm.c | 81 +- components/protocols/src/mqtt.c | 47 +- .../webfolder/assets/index-C_08vMAY.js | 51 + .../webfolder/assets/index-Doq3307m.css | 1 + components/protocols/webfolder/index.html | 4 +- components/rest_api/src/auth_api.c | 95 +- components/rest_api/src/dashboard_api.c | 4 +- components/rest_api/src/network_api.c | 2 +- dependencies.lock | 40 +- main/main.c | 108 +- projeto_parte1.c | 4705 +++++++---------- projeto_parte2.c | 2714 ---------- projeto_parte3.c | 824 --- readproject.py | 2 +- 58 files changed, 3215 insertions(+), 6961 deletions(-) create mode 100755 components/buzzer/CMakeLists.txt create mode 100755 components/buzzer/idf_component.yml create mode 100755 components/buzzer/include/buzzer.h create mode 100644 components/buzzer/include/buzzer_events.h create mode 100755 components/buzzer/src/buzzer.c create mode 100644 components/buzzer/src/buzzer_events.c create mode 100755 components/evse/evse_api.c create mode 100644 components/evse/evse_session.c create mode 100644 components/evse/include/evse_session.h rename components/network/include/{wifi.h => network.h} (100%) create mode 100644 components/network/include/network_events.h rename components/network/src/{wifi.c => network.c} (97%) create mode 100644 components/network/src/network_events.c delete mode 100755 components/peripherals/include/buzzer.h delete mode 100755 components/peripherals/src/buzzer.c create mode 100644 components/protocols/webfolder/assets/index-C_08vMAY.js create mode 100644 components/protocols/webfolder/assets/index-Doq3307m.css delete mode 100644 projeto_parte2.c delete mode 100644 projeto_parte3.c diff --git a/components/api/CMakeLists.txt b/components/api/CMakeLists.txt index 278ae94..2eee434 100755 --- a/components/api/CMakeLists.txt +++ b/components/api/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/components/api/src/json.c b/components/api/src/json.c index 6a716b8..e9f4e7c 100755 --- a/components/api/src/json.c +++ b/components/api/src/json.c @@ -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()); - cJSON_AddNumberToObject(root, "chargingTime", 0); - cJSON_AddNumberToObject(root, "consumption", 0); + // 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", diff --git a/components/api/src/timeout_utils.c b/components/api/src/timeout_utils.c index 653e036..09f3c25 100755 --- a/components/api/src/timeout_utils.c +++ b/components/api/src/timeout_utils.c @@ -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) diff --git a/components/auth/include/auth.h b/components/auth/include/auth.h index 111eb92..e35d54c 100755 --- a/components/auth/include/auth.h +++ b/components/auth/include/auth.h @@ -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 } diff --git a/components/auth/include/auth_events.h b/components/auth/include/auth_events.h index 60f0f0a..43b66c3 100644 --- a/components/auth/include/auth_events.h +++ b/components/auth/include/auth_events.h @@ -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; diff --git a/components/auth/src/auth.c b/components/auth/src/auth.c index 25fbf9c..3ca1103 100755 --- a/components/auth/src/auth.c +++ b/components/auth/src/auth.c @@ -1,37 +1,41 @@ -/* - * auth.c - */ - #include "auth.h" #include "auth_events.h" #include "esp_event.h" #include -#include #include #include #include #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]; +} diff --git a/components/auth/src/wiegand_reader.c b/components/auth/src/wiegand_reader.c index 3e7be05..cfc94a9 100755 --- a/components/auth/src/wiegand_reader.c +++ b/components/auth/src/wiegand_reader.c @@ -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; diff --git a/components/buzzer/CMakeLists.txt b/components/buzzer/CMakeLists.txt new file mode 100755 index 0000000..021b5f9 --- /dev/null +++ b/components/buzzer/CMakeLists.txt @@ -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 +) diff --git a/components/buzzer/idf_component.yml b/components/buzzer/idf_component.yml new file mode 100755 index 0000000..973e925 --- /dev/null +++ b/components/buzzer/idf_component.yml @@ -0,0 +1,2 @@ +version: "2.6.0" +description: Authentication component diff --git a/components/buzzer/include/buzzer.h b/components/buzzer/include/buzzer.h new file mode 100755 index 0000000..81abbf5 --- /dev/null +++ b/components/buzzer/include/buzzer.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void buzzer_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/buzzer/include/buzzer_events.h b/components/buzzer/include/buzzer_events.h new file mode 100644 index 0000000..e86f2c5 --- /dev/null +++ b/components/buzzer/include/buzzer_events.h @@ -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; diff --git a/components/buzzer/src/buzzer.c b/components/buzzer/src/buzzer.c new file mode 100755 index 0000000..baf5982 --- /dev/null +++ b/components/buzzer/src/buzzer.c @@ -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); +} diff --git a/components/buzzer/src/buzzer_events.c b/components/buzzer/src/buzzer_events.c new file mode 100644 index 0000000..ea6f218 --- /dev/null +++ b/components/buzzer/src/buzzer_events.c @@ -0,0 +1,3 @@ +#include "buzzer_events.h" + +ESP_EVENT_DEFINE_BASE(BUZZER_EVENTS); diff --git a/components/evse/CMakeLists.txt b/components/evse/CMakeLists.txt index 0dc83b4..8cd1cc8 100755 --- a/components/evse/CMakeLists.txt +++ b/components/evse/CMakeLists.txt @@ -10,6 +10,8 @@ set(srcs evse_hardware.c evse_pilot.c evse_meter.c + evse_session.c + evse_api.c ) idf_component_register( diff --git a/components/evse/evse_api.c b/components/evse/evse_api.c new file mode 100755 index 0000000..35ffd4b --- /dev/null +++ b/components/evse/evse_api.c @@ -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); +} \ No newline at end of file diff --git a/components/evse/evse_config.c b/components/evse/evse_config.c index 30921fa..875a3ee 100755 --- a/components/evse/evse_config.c +++ b/components/evse/evse_config.c @@ -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 diff --git a/components/evse/evse_core.c b/components/evse/evse_core.c index fc7a76a..53622cb 100755 --- a/components/evse/evse_core.c +++ b/components/evse/evse_core.c @@ -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 // ================================ diff --git a/components/evse/evse_fsm.c b/components/evse/evse_fsm.c index 9867126..ac8bd5c 100755 --- a/components/evse/evse_fsm.c +++ b/components/evse/evse_fsm.c @@ -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; } diff --git a/components/evse/evse_limits.c b/components/evse/evse_limits.c index 91d8065..18c86b9 100755 --- a/components/evse/evse_limits.c +++ b/components/evse/evse_limits.c @@ -1,7 +1,9 @@ +#include // 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,41 +136,53 @@ 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; } if (reached) { evse_set_limit_reached(true); } -} +} \ No newline at end of file diff --git a/components/evse/evse_manager.c b/components/evse/evse_manager.c index ad5abf1..8c921cb 100755 --- a/components/evse/evse_manager.c +++ b/components/evse/evse_manager.c @@ -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 diff --git a/components/evse/evse_meter.c b/components/evse/evse_meter.c index 6c45113..2adb91f 100644 --- a/components/evse/evse_meter.c +++ b/components/evse/evse_meter.c @@ -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]; diff --git a/components/evse/evse_pilot.c b/components/evse/evse_pilot.c index 9c57db1..63bd7d4 100755 --- a/components/evse/evse_pilot.c +++ b/components/evse/evse_pilot.c @@ -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); } diff --git a/components/evse/evse_session.c b/components/evse/evse_session.c new file mode 100644 index 0000000..180a089 --- /dev/null +++ b/components/evse/evse_session.c @@ -0,0 +1,84 @@ +/* + * evse_session.c + * Implementation of evse_session module using instantaneous power accumulation + */ +#include // 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; +} diff --git a/components/evse/evse_state.c b/components/evse/evse_state.c index 3a30a7c..92e29d8 100755 --- a/components/evse/evse_state.c +++ b/components/evse/evse_state.c @@ -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; - 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; - } - } + 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; + } 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); diff --git a/components/evse/include/evse_api.h b/components/evse/include/evse_api.h index 78274d6..c7eb837 100755 --- a/components/evse/include/evse_api.h +++ b/components/evse/include/evse_api.h @@ -3,8 +3,10 @@ #include #include -#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 diff --git a/components/evse/include/evse_config.h b/components/evse/include/evse_config.h index 0d35b59..ae5d07c 100755 --- a/components/evse/include/evse_config.h +++ b/components/evse/include/evse_config.h @@ -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); diff --git a/components/evse/include/evse_meter.h b/components/evse/include/evse_meter.h index dc3bbb5..3286e83 100644 --- a/components/evse/include/evse_meter.h +++ b/components/evse/include/evse_meter.h @@ -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]); diff --git a/components/evse/include/evse_session.h b/components/evse/include/evse_session.h new file mode 100644 index 0000000..0d84b20 --- /dev/null +++ b/components/evse/include/evse_session.h @@ -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 +#include +#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 diff --git a/components/evse/include/evse_state.h b/components/evse/include/evse_state.h index 0b0c86b..3c8455f 100755 --- a/components/evse/include/evse_state.h +++ b/components/evse/include/evse_state.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. */ diff --git a/components/loadbalancer/src/loadbalancer.c b/components/loadbalancer/src/loadbalancer.c index b807df2..b9df651 100755 --- a/components/loadbalancer/src/loadbalancer.c +++ b/components/loadbalancer/src/loadbalancer.c @@ -10,18 +10,17 @@ #include #include "meter_events.h" #include "evse_events.h" - - +#include 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)); } } diff --git a/components/meter_manager/CMakeLists.txt b/components/meter_manager/CMakeLists.txt index 1e4cdb0..ba51ce5 100755 --- a/components/meter_manager/CMakeLists.txt +++ b/components/meter_manager/CMakeLists.txt @@ -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) diff --git a/components/meter_manager/driver/meter_ade7758/meter_ade7758.c b/components/meter_manager/driver/meter_ade7758/meter_ade7758.c index af23f8a..9dd0f10 100755 --- a/components/meter_manager/driver/meter_ade7758/meter_ade7758.c +++ b/components/meter_manager/driver/meter_ade7758/meter_ade7758.c @@ -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; diff --git a/components/meter_manager/driver/meter_zigbee/meter_zigbee.c b/components/meter_manager/driver/meter_zigbee/meter_zigbee.c index 4599080..02a06da 100755 --- a/components/meter_manager/driver/meter_zigbee/meter_zigbee.c +++ b/components/meter_manager/driver/meter_zigbee/meter_zigbee.c @@ -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; @@ -224,8 +242,4 @@ void meter_zigbee_stop(void) { vSemaphoreDelete(meter_mutex); meter_mutex = NULL; } -} - -bool meter_zigbee_is_running(void) { - return meter_zigbee_task != NULL; -} +} \ No newline at end of file diff --git a/components/meter_manager/include/meter_manager.h b/components/meter_manager/include/meter_manager.h index e270d6d..2f8e350 100755 --- a/components/meter_manager/include/meter_manager.h +++ b/components/meter_manager/include/meter_manager.h @@ -2,66 +2,116 @@ #define METER_MANAGER_H #include "esp_err.h" -#include // Para garantir que 'bool' seja reconhecido +#include -// 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 diff --git a/components/meter_manager/src/meter_manager.c b/components/meter_manager/src/meter_manager.c index a4740c5..a8ea23f 100755 --- a/components/meter_manager/src/meter_manager.c +++ b/components/meter_manager/src/meter_manager.c @@ -7,6 +7,8 @@ #include "nvs_flash.h" #include "nvs.h" #include +#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); diff --git a/components/network/CMakeLists.txt b/components/network/CMakeLists.txt index 3e2f8ba..6de558a 100755 --- a/components/network/CMakeLists.txt +++ b/components/network/CMakeLists.txt @@ -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) \ No newline at end of file + PRIV_REQUIRES nvs_flash esp_netif esp_wifi mdns esp_event) \ No newline at end of file diff --git a/components/network/include/wifi.h b/components/network/include/network.h similarity index 100% rename from components/network/include/wifi.h rename to components/network/include/network.h diff --git a/components/network/include/network_events.h b/components/network/include/network_events.h new file mode 100644 index 0000000..cbfd8e9 --- /dev/null +++ b/components/network/include/network_events.h @@ -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 diff --git a/components/network/src/wifi.c b/components/network/src/network.c similarity index 97% rename from components/network/src/wifi.c rename to components/network/src/network.c index 0935bda..df7c013 100755 --- a/components/network/src/wifi.c +++ b/components/network/src/network.c @@ -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(); diff --git a/components/network/src/network_events.c b/components/network/src/network_events.c new file mode 100644 index 0000000..35f1836 --- /dev/null +++ b/components/network/src/network_events.c @@ -0,0 +1,4 @@ +#include "network_events.h" + +// Define o base de eventos +ESP_EVENT_DEFINE_BASE(NETWORK_EVENTS); diff --git a/components/peripherals/CMakeLists.txt b/components/peripherals/CMakeLists.txt index 99f9a29..330f17e 100755 --- a/components/peripherals/CMakeLists.txt +++ b/components/peripherals/CMakeLists.txt @@ -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" diff --git a/components/peripherals/include/buzzer.h b/components/peripherals/include/buzzer.h deleted file mode 100755 index a89ee88..0000000 --- a/components/peripherals/include/buzzer.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef BUZZER_H_ -#define BUZZER_H_ - -#include - -/** - * @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_ */ diff --git a/components/peripherals/src/buzzer.c b/components/peripherals/src/buzzer.c deleted file mode 100755 index e7b5923..0000000 --- a/components/peripherals/src/buzzer.c +++ /dev/null @@ -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); -} diff --git a/components/peripherals/src/peripherals.c b/components/peripherals/src/peripherals.c index 6c7bad3..a0bfc50 100755 --- a/components/peripherals/src/peripherals.c +++ b/components/peripherals/src/peripherals.c @@ -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(); diff --git a/components/peripherals/src/rcm.c b/components/peripherals/src/rcm.c index 1db06f7..43b082f 100755 --- a/components/peripherals/src/rcm.c +++ b/components/peripherals/src/rcm.c @@ -7,70 +7,67 @@ #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; } return false; -} \ No newline at end of file +} diff --git a/components/protocols/src/mqtt.c b/components/protocols/src/mqtt.c index 7f80c38..73b0483 100755 --- a/components/protocols/src/mqtt.c +++ b/components/protocols/src/mqtt.c @@ -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 { - ESP_LOGW(TAG, " ! Topic does not match base: %s", 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); + } } } diff --git a/components/protocols/webfolder/assets/index-C_08vMAY.js b/components/protocols/webfolder/assets/index-C_08vMAY.js new file mode 100644 index 0000000..ab0f012 --- /dev/null +++ b/components/protocols/webfolder/assets/index-C_08vMAY.js @@ -0,0 +1,51 @@ +(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const d of document.querySelectorAll('link[rel="modulepreload"]'))f(d);new MutationObserver(d=>{for(const v of d)if(v.type==="childList")for(const p of v.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&f(p)}).observe(document,{childList:!0,subtree:!0});function s(d){const v={};return d.integrity&&(v.integrity=d.integrity),d.referrerPolicy&&(v.referrerPolicy=d.referrerPolicy),d.crossOrigin==="use-credentials"?v.credentials="include":d.crossOrigin==="anonymous"?v.credentials="omit":v.credentials="same-origin",v}function f(d){if(d.ep)return;d.ep=!0;const v=s(d);fetch(d.href,v)}})();function $d(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var Tf={exports:{}},Du={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var _d;function By(){if(_d)return Du;_d=1;var i=Symbol.for("react.transitional.element"),o=Symbol.for("react.fragment");function s(f,d,v){var p=null;if(v!==void 0&&(p=""+v),d.key!==void 0&&(p=""+d.key),"key"in d){v={};for(var j in d)j!=="key"&&(v[j]=d[j])}else v=d;return d=v.ref,{$$typeof:i,type:f,key:p,ref:d!==void 0?d:null,props:v}}return Du.Fragment=o,Du.jsx=s,Du.jsxs=s,Du}var Cd;function qy(){return Cd||(Cd=1,Tf.exports=By()),Tf.exports}var h=qy(),Af={exports:{}},te={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Ud;function wy(){if(Ud)return te;Ud=1;var i=Symbol.for("react.transitional.element"),o=Symbol.for("react.portal"),s=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),d=Symbol.for("react.profiler"),v=Symbol.for("react.consumer"),p=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),S=Symbol.for("react.suspense"),m=Symbol.for("react.memo"),M=Symbol.for("react.lazy"),H=Symbol.iterator;function O(g){return g===null||typeof g!="object"?null:(g=H&&g[H]||g["@@iterator"],typeof g=="function"?g:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},U=Object.assign,G={};function V(g,B,Q){this.props=g,this.context=B,this.refs=G,this.updater=Q||q}V.prototype.isReactComponent={},V.prototype.setState=function(g,B){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,B,"setState")},V.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function w(){}w.prototype=V.prototype;function Z(g,B,Q){this.props=g,this.context=B,this.refs=G,this.updater=Q||q}var k=Z.prototype=new w;k.constructor=Z,U(k,V.prototype),k.isPureReactComponent=!0;var ce=Array.isArray,L={H:null,A:null,T:null,S:null,V:null},fe=Object.prototype.hasOwnProperty;function ue(g,B,Q,Y,$,oe){return Q=oe.ref,{$$typeof:i,type:g,key:B,ref:Q!==void 0?Q:null,props:oe}}function se(g,B){return ue(g.type,B,void 0,void 0,void 0,g.props)}function Ee(g){return typeof g=="object"&&g!==null&&g.$$typeof===i}function Je(g){var B={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(Q){return B[Q]})}var ot=/\/+/g;function Xe(g,B){return typeof g=="object"&&g!==null&&g.key!=null?Je(""+g.key):B.toString(36)}function Nl(){}function Ol(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(Nl,Nl):(g.status="pending",g.then(function(B){g.status==="pending"&&(g.status="fulfilled",g.value=B)},function(B){g.status==="pending"&&(g.status="rejected",g.reason=B)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function Qe(g,B,Q,Y,$){var oe=typeof g;(oe==="undefined"||oe==="boolean")&&(g=null);var ee=!1;if(g===null)ee=!0;else switch(oe){case"bigint":case"string":case"number":ee=!0;break;case"object":switch(g.$$typeof){case i:case o:ee=!0;break;case M:return ee=g._init,Qe(ee(g._payload),B,Q,Y,$)}}if(ee)return $=$(g),ee=Y===""?"."+Xe(g,0):Y,ce($)?(Q="",ee!=null&&(Q=ee.replace(ot,"$&/")+"/"),Qe($,B,Q,"",function(el){return el})):$!=null&&(Ee($)&&($=se($,Q+($.key==null||g&&g.key===$.key?"":(""+$.key).replace(ot,"$&/")+"/")+ee)),B.push($)),1;ee=0;var tt=Y===""?".":Y+":";if(ce(g))for(var Te=0;Te>>1,g=D[pe];if(0>>1;ped(Y,P))$d(oe,Y)?(D[pe]=oe,D[$]=P,pe=$):(D[pe]=Y,D[Q]=P,pe=Q);else if($d(oe,P))D[pe]=oe,D[$]=P,pe=$;else break e}}return X}function d(D,X){var P=D.sortIndex-X.sortIndex;return P!==0?P:D.id-X.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var v=performance;i.unstable_now=function(){return v.now()}}else{var p=Date,j=p.now();i.unstable_now=function(){return p.now()-j}}var S=[],m=[],M=1,H=null,O=3,q=!1,U=!1,G=!1,V=!1,w=typeof setTimeout=="function"?setTimeout:null,Z=typeof clearTimeout=="function"?clearTimeout:null,k=typeof setImmediate<"u"?setImmediate:null;function ce(D){for(var X=s(m);X!==null;){if(X.callback===null)f(m);else if(X.startTime<=D)f(m),X.sortIndex=X.expirationTime,o(S,X);else break;X=s(m)}}function L(D){if(G=!1,ce(D),!U)if(s(S)!==null)U=!0,fe||(fe=!0,Xe());else{var X=s(m);X!==null&&Qe(L,X.startTime-D)}}var fe=!1,ue=-1,se=5,Ee=-1;function Je(){return V?!0:!(i.unstable_now()-EeD&&Je());){var pe=H.callback;if(typeof pe=="function"){H.callback=null,O=H.priorityLevel;var g=pe(H.expirationTime<=D);if(D=i.unstable_now(),typeof g=="function"){H.callback=g,ce(D),X=!0;break t}H===s(S)&&f(S),ce(D)}else f(S);H=s(S)}if(H!==null)X=!0;else{var B=s(m);B!==null&&Qe(L,B.startTime-D),X=!1}}break e}finally{H=null,O=P,q=!1}X=void 0}}finally{X?Xe():fe=!1}}}var Xe;if(typeof k=="function")Xe=function(){k(ot)};else if(typeof MessageChannel<"u"){var Nl=new MessageChannel,Ol=Nl.port2;Nl.port1.onmessage=ot,Xe=function(){Ol.postMessage(null)}}else Xe=function(){w(ot,0)};function Qe(D,X){ue=w(function(){D(i.unstable_now())},X)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(D){D.callback=null},i.unstable_forceFrameRate=function(D){0>D||125pe?(D.sortIndex=P,o(m,D),s(S)===null&&D===s(m)&&(G?(Z(ue),ue=-1):G=!0,Qe(L,P-pe))):(D.sortIndex=g,o(S,D),U||q||(U=!0,fe||(fe=!0,Xe()))),D},i.unstable_shouldYield=Je,i.unstable_wrapCallback=function(D){var X=O;return function(){var P=O;O=X;try{return D.apply(this,arguments)}finally{O=P}}}}(Of)),Of}var qd;function Gy(){return qd||(qd=1,Nf.exports=Yy()),Nf.exports}var Mf={exports:{}},Ke={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var wd;function Xy(){if(wd)return Ke;wd=1;var i=Cf();function o(S){var m="https://react.dev/errors/"+S;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(o){console.error(o)}}return i(),Mf.exports=Xy(),Mf.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Yd;function Zy(){if(Yd)return _u;Yd=1;var i=Gy(),o=Cf(),s=Qy();function f(e){var t="https://react.dev/errors/"+e;if(1g||(e.current=pe[g],pe[g]=null,g--)}function Y(e,t){g++,pe[g]=e.current,e.current=t}var $=B(null),oe=B(null),ee=B(null),tt=B(null);function Te(e,t){switch(Y(ee,t),Y(oe,e),Y($,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?id(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=id(t),e=cd(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}Q($),Y($,e)}function el(){Q($),Q(oe),Q(ee)}function ci(e){e.memoizedState!==null&&Y(tt,e);var t=$.current,l=cd(t,e.type);t!==l&&(Y(oe,e),Y($,l))}function Lu(e){oe.current===e&&(Q($),Q(oe)),tt.current===e&&(Q(tt),Nu._currentValue=P)}var fi=Object.prototype.hasOwnProperty,ri=i.unstable_scheduleCallback,si=i.unstable_cancelCallback,mh=i.unstable_shouldYield,yh=i.unstable_requestPaint,At=i.unstable_now,vh=i.unstable_getCurrentPriorityLevel,Lf=i.unstable_ImmediatePriority,Yf=i.unstable_UserBlockingPriority,Yu=i.unstable_NormalPriority,gh=i.unstable_LowPriority,Gf=i.unstable_IdlePriority,bh=i.log,ph=i.unstable_setDisableYieldValue,Ua=null,lt=null;function tl(e){if(typeof bh=="function"&&ph(e),lt&&typeof lt.setStrictMode=="function")try{lt.setStrictMode(Ua,e)}catch{}}var at=Math.clz32?Math.clz32:Eh,Sh=Math.log,xh=Math.LN2;function Eh(e){return e>>>=0,e===0?32:31-(Sh(e)/xh|0)|0}var Gu=256,Xu=4194304;function Ml(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Qu(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var u=0,n=e.suspendedLanes,c=e.pingedLanes;e=e.warmLanes;var r=a&134217727;return r!==0?(a=r&~n,a!==0?u=Ml(a):(c&=r,c!==0?u=Ml(c):l||(l=r&~e,l!==0&&(u=Ml(l))))):(r=a&~n,r!==0?u=Ml(r):c!==0?u=Ml(c):l||(l=a&~e,l!==0&&(u=Ml(l)))),u===0?0:t!==0&&t!==u&&(t&n)===0&&(n=u&-u,l=t&-t,n>=l||n===32&&(l&4194048)!==0)?t:u}function Ha(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Th(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Xf(){var e=Gu;return Gu<<=1,(Gu&4194048)===0&&(Gu=256),e}function Qf(){var e=Xu;return Xu<<=1,(Xu&62914560)===0&&(Xu=4194304),e}function oi(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Ba(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Ah(e,t,l,a,u,n){var c=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var r=e.entanglements,y=e.expirationTimes,T=e.hiddenUpdates;for(l=c&~l;0)":-1u||y[a]!==T[u]){var z=` +`+y[a].replace(" at new "," at ");return e.displayName&&z.includes("")&&(z=z.replace("",e.displayName)),z}while(1<=a&&0<=u);break}}}finally{gi=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?Pl(l):""}function zh(e){switch(e.tag){case 26:case 27:case 5:return Pl(e.type);case 16:return Pl("Lazy");case 13:return Pl("Suspense");case 19:return Pl("SuspenseList");case 0:case 15:return bi(e.type,!1);case 11:return bi(e.type.render,!1);case 1:return bi(e.type,!0);case 31:return Pl("Activity");default:return""}}function If(e){try{var t="";do t+=zh(e),e=e.return;while(e);return t}catch(l){return` +Error generating stack: `+l.message+` +`+l.stack}}function dt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function er(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Dh(e){var t=er(e)?"checked":"value",l=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),a=""+e[t];if(!e.hasOwnProperty(t)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var u=l.get,n=l.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return u.call(this)},set:function(c){a=""+c,n.call(this,c)}}),Object.defineProperty(e,t,{enumerable:l.enumerable}),{getValue:function(){return a},setValue:function(c){a=""+c},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Ku(e){e._valueTracker||(e._valueTracker=Dh(e))}function tr(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var l=t.getValue(),a="";return e&&(a=er(e)?e.checked?"true":"false":e.value),e=a,e!==l?(t.setValue(e),!0):!1}function Ju(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var _h=/[\n"\\]/g;function ht(e){return e.replace(_h,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function pi(e,t,l,a,u,n,c,r){e.name="",c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?e.type=c:e.removeAttribute("type"),t!=null?c==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+dt(t)):e.value!==""+dt(t)&&(e.value=""+dt(t)):c!=="submit"&&c!=="reset"||e.removeAttribute("value"),t!=null?Si(e,c,dt(t)):l!=null?Si(e,c,dt(l)):a!=null&&e.removeAttribute("value"),u==null&&n!=null&&(e.defaultChecked=!!n),u!=null&&(e.checked=u&&typeof u!="function"&&typeof u!="symbol"),r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"?e.name=""+dt(r):e.removeAttribute("name")}function lr(e,t,l,a,u,n,c,r){if(n!=null&&typeof n!="function"&&typeof n!="symbol"&&typeof n!="boolean"&&(e.type=n),t!=null||l!=null){if(!(n!=="submit"&&n!=="reset"||t!=null))return;l=l!=null?""+dt(l):"",t=t!=null?""+dt(t):l,r||t===e.value||(e.value=t),e.defaultValue=t}a=a??u,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=r?e.checked:!!a,e.defaultChecked=!!a,c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"&&(e.name=c)}function Si(e,t,l){t==="number"&&Ju(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function Il(e,t,l,a){if(e=e.options,t){t={};for(var u=0;u"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ri=!1;if(Bt)try{var Ya={};Object.defineProperty(Ya,"passive",{get:function(){Ri=!0}}),window.addEventListener("test",Ya,Ya),window.removeEventListener("test",Ya,Ya)}catch{Ri=!1}var al=null,Ni=null,$u=null;function rr(){if($u)return $u;var e,t=Ni,l=t.length,a,u="value"in al?al.value:al.textContent,n=u.length;for(e=0;e=Qa),yr=" ",vr=!1;function gr(e,t){switch(e){case"keyup":return im.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function br(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var aa=!1;function fm(e,t){switch(e){case"compositionend":return br(t);case"keypress":return t.which!==32?null:(vr=!0,yr);case"textInput":return e=t.data,e===yr&&vr?null:e;default:return null}}function rm(e,t){if(aa)return e==="compositionend"||!Di&&gr(e,t)?(e=rr(),$u=Ni=al=null,aa=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Nr(l)}}function Mr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Mr(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function jr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Ju(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=Ju(e.document)}return t}function Ui(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var gm=Bt&&"documentMode"in document&&11>=document.documentMode,ua=null,Hi=null,Ja=null,Bi=!1;function zr(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Bi||ua==null||ua!==Ju(a)||(a=ua,"selectionStart"in a&&Ui(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),Ja&&Ka(Ja,a)||(Ja=a,a=Yn(Hi,"onSelect"),0>=c,u-=c,wt=1<<32-at(t)+u|l<n?n:8;var c=D.T,r={};D.T=r,xc(e,!1,t,l);try{var y=u(),T=D.S;if(T!==null&&T(r,y),y!==null&&typeof y=="object"&&typeof y.then=="function"){var z=Nm(y,a);fu(e,t,z,rt(e))}else fu(e,t,a,rt(e))}catch(C){fu(e,t,{then:function(){},status:"rejected",reason:C},rt())}finally{X.p=n,D.T=c}}function Dm(){}function pc(e,t,l,a){if(e.tag!==5)throw Error(f(476));var u=Ds(e).queue;zs(e,u,t,P,l===null?Dm:function(){return _s(e),l(a)})}function Ds(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:P,baseState:P,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Xt,lastRenderedState:P},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Xt,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function _s(e){var t=Ds(e).next.queue;fu(e,t,{},rt())}function Sc(){return Ve(Nu)}function Cs(){return _e().memoizedState}function Us(){return _e().memoizedState}function _m(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=rt();e=il(l);var a=cl(t,e,l);a!==null&&(st(a,t,l),lu(a,t,l)),t={cache:Wi()},e.payload=t;return}t=t.return}}function Cm(e,t,l){var a=rt();l={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},Sn(e)?Bs(t,l):(l=Yi(e,t,l,a),l!==null&&(st(l,e,a),qs(l,t,a)))}function Hs(e,t,l){var a=rt();fu(e,t,l,a)}function fu(e,t,l,a){var u={lane:a,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(Sn(e))Bs(t,u);else{var n=e.alternate;if(e.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var c=t.lastRenderedState,r=n(c,l);if(u.hasEagerState=!0,u.eagerState=r,ut(r,c))return ln(e,t,u,0),xe===null&&tn(),!1}catch{}finally{}if(l=Yi(e,t,u,a),l!==null)return st(l,e,a),qs(l,t,a),!0}return!1}function xc(e,t,l,a){if(a={lane:2,revertLane:Ic(),action:a,hasEagerState:!1,eagerState:null,next:null},Sn(e)){if(t)throw Error(f(479))}else t=Yi(e,l,a,2),t!==null&&st(t,e,2)}function Sn(e){var t=e.alternate;return e===le||t!==null&&t===le}function Bs(e,t){ma=mn=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function qs(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,Vf(e,l)}}var xn={readContext:Ve,use:vn,useCallback:je,useContext:je,useEffect:je,useImperativeHandle:je,useLayoutEffect:je,useInsertionEffect:je,useMemo:je,useReducer:je,useRef:je,useState:je,useDebugValue:je,useDeferredValue:je,useTransition:je,useSyncExternalStore:je,useId:je,useHostTransitionStatus:je,useFormState:je,useActionState:je,useOptimistic:je,useMemoCache:je,useCacheRefresh:je},ws={readContext:Ve,use:vn,useCallback:function(e,t){return Fe().memoizedState=[e,t===void 0?null:t],e},useContext:Ve,useEffect:xs,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,pn(4194308,4,Rs.bind(null,t,e),l)},useLayoutEffect:function(e,t){return pn(4194308,4,e,t)},useInsertionEffect:function(e,t){pn(4,2,e,t)},useMemo:function(e,t){var l=Fe();t=t===void 0?null:t;var a=e();if(Gl){tl(!0);try{e()}finally{tl(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=Fe();if(l!==void 0){var u=l(t);if(Gl){tl(!0);try{l(t)}finally{tl(!1)}}}else u=t;return a.memoizedState=a.baseState=u,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:u},a.queue=e,e=e.dispatch=Cm.bind(null,le,e),[a.memoizedState,e]},useRef:function(e){var t=Fe();return e={current:e},t.memoizedState=e},useState:function(e){e=yc(e);var t=e.queue,l=Hs.bind(null,le,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:gc,useDeferredValue:function(e,t){var l=Fe();return bc(l,e,t)},useTransition:function(){var e=yc(!1);return e=zs.bind(null,le,e.queue,!0,!1),Fe().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=le,u=Fe();if(he){if(l===void 0)throw Error(f(407));l=l()}else{if(l=t(),xe===null)throw Error(f(349));(re&124)!==0||ns(a,t,l)}u.memoizedState=l;var n={value:l,getSnapshot:t};return u.queue=n,xs(cs.bind(null,a,n,e),[e]),a.flags|=2048,va(9,bn(),is.bind(null,a,n,l,t),null),l},useId:function(){var e=Fe(),t=xe.identifierPrefix;if(he){var l=Lt,a=wt;l=(a&~(1<<32-at(a)-1)).toString(32)+l,t="«"+t+"R"+l,l=yn++,0F?(we=J,J=null):we=J.sibling;var de=R(x,J,E[F],_);if(de===null){J===null&&(J=we);break}e&&J&&de.alternate===null&&t(x,J),b=n(de,b,F),ae===null?K=de:ae.sibling=de,ae=de,J=we}if(F===E.length)return l(x,J),he&&Hl(x,F),K;if(J===null){for(;FF?(we=J,J=null):we=J.sibling;var Al=R(x,J,de.value,_);if(Al===null){J===null&&(J=we);break}e&&J&&Al.alternate===null&&t(x,J),b=n(Al,b,F),ae===null?K=Al:ae.sibling=Al,ae=Al,J=we}if(de.done)return l(x,J),he&&Hl(x,F),K;if(J===null){for(;!de.done;F++,de=E.next())de=C(x,de.value,_),de!==null&&(b=n(de,b,F),ae===null?K=de:ae.sibling=de,ae=de);return he&&Hl(x,F),K}for(J=a(J);!de.done;F++,de=E.next())de=N(J,x,F,de.value,_),de!==null&&(e&&de.alternate!==null&&J.delete(de.key===null?F:de.key),b=n(de,b,F),ae===null?K=de:ae.sibling=de,ae=de);return e&&J.forEach(function(Hy){return t(x,Hy)}),he&&Hl(x,F),K}function be(x,b,E,_){if(typeof E=="object"&&E!==null&&E.type===U&&E.key===null&&(E=E.props.children),typeof E=="object"&&E!==null){switch(E.$$typeof){case O:e:{for(var K=E.key;b!==null;){if(b.key===K){if(K=E.type,K===U){if(b.tag===7){l(x,b.sibling),_=u(b,E.props.children),_.return=x,x=_;break e}}else if(b.elementType===K||typeof K=="object"&&K!==null&&K.$$typeof===se&&Ys(K)===b.type){l(x,b.sibling),_=u(b,E.props),su(_,E),_.return=x,x=_;break e}l(x,b);break}else t(x,b);b=b.sibling}E.type===U?(_=Cl(E.props.children,x.mode,_,E.key),_.return=x,x=_):(_=un(E.type,E.key,E.props,null,x.mode,_),su(_,E),_.return=x,x=_)}return c(x);case q:e:{for(K=E.key;b!==null;){if(b.key===K)if(b.tag===4&&b.stateNode.containerInfo===E.containerInfo&&b.stateNode.implementation===E.implementation){l(x,b.sibling),_=u(b,E.children||[]),_.return=x,x=_;break e}else{l(x,b);break}else t(x,b);b=b.sibling}_=Qi(E,x.mode,_),_.return=x,x=_}return c(x);case se:return K=E._init,E=K(E._payload),be(x,b,E,_)}if(Qe(E))return I(x,b,E,_);if(Xe(E)){if(K=Xe(E),typeof K!="function")throw Error(f(150));return E=K.call(E),W(x,b,E,_)}if(typeof E.then=="function")return be(x,b,En(E),_);if(E.$$typeof===k)return be(x,b,rn(x,E),_);Tn(x,E)}return typeof E=="string"&&E!==""||typeof E=="number"||typeof E=="bigint"?(E=""+E,b!==null&&b.tag===6?(l(x,b.sibling),_=u(b,E),_.return=x,x=_):(l(x,b),_=Xi(E,x.mode,_),_.return=x,x=_),c(x)):l(x,b)}return function(x,b,E,_){try{ru=0;var K=be(x,b,E,_);return ga=null,K}catch(J){if(J===eu||J===on)throw J;var ae=nt(29,J,null,x.mode);return ae.lanes=_,ae.return=x,ae}finally{}}}var ba=Gs(!0),Xs=Gs(!1),bt=B(null),Nt=null;function rl(e){var t=e.alternate;Y(Ue,Ue.current&1),Y(bt,e),Nt===null&&(t===null||ha.current!==null||t.memoizedState!==null)&&(Nt=e)}function Qs(e){if(e.tag===22){if(Y(Ue,Ue.current),Y(bt,e),Nt===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(Nt=e)}}else sl()}function sl(){Y(Ue,Ue.current),Y(bt,bt.current)}function Qt(e){Q(bt),Nt===e&&(Nt=null),Q(Ue)}var Ue=B(0);function An(e){for(var t=e;t!==null;){if(t.tag===13){var l=t.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||df(l)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function Ec(e,t,l,a){t=e.memoizedState,l=l(a,t),l=l==null?t:M({},t,l),e.memoizedState=l,e.lanes===0&&(e.updateQueue.baseState=l)}var Tc={enqueueSetState:function(e,t,l){e=e._reactInternals;var a=rt(),u=il(a);u.payload=t,l!=null&&(u.callback=l),t=cl(e,u,a),t!==null&&(st(t,e,a),lu(t,e,a))},enqueueReplaceState:function(e,t,l){e=e._reactInternals;var a=rt(),u=il(a);u.tag=1,u.payload=t,l!=null&&(u.callback=l),t=cl(e,u,a),t!==null&&(st(t,e,a),lu(t,e,a))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var l=rt(),a=il(l);a.tag=2,t!=null&&(a.callback=t),t=cl(e,a,l),t!==null&&(st(t,e,l),lu(t,e,l))}};function Zs(e,t,l,a,u,n,c){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(a,n,c):t.prototype&&t.prototype.isPureReactComponent?!Ka(l,a)||!Ka(u,n):!0}function Vs(e,t,l,a){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(l,a),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(l,a),t.state!==e&&Tc.enqueueReplaceState(t,t.state,null)}function Xl(e,t){var l=t;if("ref"in t){l={};for(var a in t)a!=="ref"&&(l[a]=t[a])}if(e=e.defaultProps){l===t&&(l=M({},l));for(var u in e)l[u]===void 0&&(l[u]=e[u])}return l}var Rn=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Ks(e){Rn(e)}function Js(e){console.error(e)}function ks(e){Rn(e)}function Nn(e,t){try{var l=e.onUncaughtError;l(t.value,{componentStack:t.stack})}catch(a){setTimeout(function(){throw a})}}function $s(e,t,l){try{var a=e.onCaughtError;a(l.value,{componentStack:l.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(u){setTimeout(function(){throw u})}}function Ac(e,t,l){return l=il(l),l.tag=3,l.payload={element:null},l.callback=function(){Nn(e,t)},l}function Ws(e){return e=il(e),e.tag=3,e}function Fs(e,t,l,a){var u=l.type.getDerivedStateFromError;if(typeof u=="function"){var n=a.value;e.payload=function(){return u(n)},e.callback=function(){$s(t,l,a)}}var c=l.stateNode;c!==null&&typeof c.componentDidCatch=="function"&&(e.callback=function(){$s(t,l,a),typeof u!="function"&&(vl===null?vl=new Set([this]):vl.add(this));var r=a.stack;this.componentDidCatch(a.value,{componentStack:r!==null?r:""})})}function Hm(e,t,l,a,u){if(l.flags|=32768,a!==null&&typeof a=="object"&&typeof a.then=="function"){if(t=l.alternate,t!==null&&Fa(t,l,u,!0),l=bt.current,l!==null){switch(l.tag){case 13:return Nt===null?kc():l.alternate===null&&Me===0&&(Me=3),l.flags&=-257,l.flags|=65536,l.lanes=u,a===Ii?l.flags|=16384:(t=l.updateQueue,t===null?l.updateQueue=new Set([a]):t.add(a),Wc(e,a,u)),!1;case 22:return l.flags|=65536,a===Ii?l.flags|=16384:(t=l.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([a])},l.updateQueue=t):(l=t.retryQueue,l===null?t.retryQueue=new Set([a]):l.add(a)),Wc(e,a,u)),!1}throw Error(f(435,l.tag))}return Wc(e,a,u),kc(),!1}if(he)return t=bt.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=u,a!==Ki&&(e=Error(f(422),{cause:a}),Wa(mt(e,l)))):(a!==Ki&&(t=Error(f(423),{cause:a}),Wa(mt(t,l))),e=e.current.alternate,e.flags|=65536,u&=-u,e.lanes|=u,a=mt(a,l),u=Ac(e.stateNode,a,u),lc(e,u),Me!==4&&(Me=2)),!1;var n=Error(f(520),{cause:a});if(n=mt(n,l),gu===null?gu=[n]:gu.push(n),Me!==4&&(Me=2),t===null)return!0;a=mt(a,l),l=t;do{switch(l.tag){case 3:return l.flags|=65536,e=u&-u,l.lanes|=e,e=Ac(l.stateNode,a,e),lc(l,e),!1;case 1:if(t=l.type,n=l.stateNode,(l.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||n!==null&&typeof n.componentDidCatch=="function"&&(vl===null||!vl.has(n))))return l.flags|=65536,u&=-u,l.lanes|=u,u=Ws(u),Fs(u,e,l,a),lc(l,u),!1}l=l.return}while(l!==null);return!1}var Ps=Error(f(461)),Be=!1;function Le(e,t,l,a){t.child=e===null?Xs(t,null,l,a):ba(t,e.child,l,a)}function Is(e,t,l,a,u){l=l.render;var n=t.ref;if("ref"in a){var c={};for(var r in a)r!=="ref"&&(c[r]=a[r])}else c=a;return Ll(t),a=cc(e,t,l,c,n,u),r=fc(),e!==null&&!Be?(rc(e,t,u),Zt(e,t,u)):(he&&r&&Zi(t),t.flags|=1,Le(e,t,a,u),t.child)}function eo(e,t,l,a,u){if(e===null){var n=l.type;return typeof n=="function"&&!Gi(n)&&n.defaultProps===void 0&&l.compare===null?(t.tag=15,t.type=n,to(e,t,n,a,u)):(e=un(l.type,null,a,t,t.mode,u),e.ref=t.ref,e.return=t,t.child=e)}if(n=e.child,!_c(e,u)){var c=n.memoizedProps;if(l=l.compare,l=l!==null?l:Ka,l(c,a)&&e.ref===t.ref)return Zt(e,t,u)}return t.flags|=1,e=qt(n,a),e.ref=t.ref,e.return=t,t.child=e}function to(e,t,l,a,u){if(e!==null){var n=e.memoizedProps;if(Ka(n,a)&&e.ref===t.ref)if(Be=!1,t.pendingProps=a=n,_c(e,u))(e.flags&131072)!==0&&(Be=!0);else return t.lanes=e.lanes,Zt(e,t,u)}return Rc(e,t,l,a,u)}function lo(e,t,l){var a=t.pendingProps,u=a.children,n=e!==null?e.memoizedState:null;if(a.mode==="hidden"){if((t.flags&128)!==0){if(a=n!==null?n.baseLanes|l:l,e!==null){for(u=t.child=e.child,n=0;u!==null;)n=n|u.lanes|u.childLanes,u=u.sibling;t.childLanes=n&~a}else t.childLanes=0,t.child=null;return ao(e,t,a,l)}if((l&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&sn(t,n!==null?n.cachePool:null),n!==null?ts(t,n):uc(),Qs(t);else return t.lanes=t.childLanes=536870912,ao(e,t,n!==null?n.baseLanes|l:l,l)}else n!==null?(sn(t,n.cachePool),ts(t,n),sl(),t.memoizedState=null):(e!==null&&sn(t,null),uc(),sl());return Le(e,t,u,l),t.child}function ao(e,t,l,a){var u=Pi();return u=u===null?null:{parent:Ce._currentValue,pool:u},t.memoizedState={baseLanes:l,cachePool:u},e!==null&&sn(t,null),uc(),Qs(t),e!==null&&Fa(e,t,a,!0),null}function On(e,t){var l=t.ref;if(l===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(f(284));(e===null||e.ref!==l)&&(t.flags|=4194816)}}function Rc(e,t,l,a,u){return Ll(t),l=cc(e,t,l,a,void 0,u),a=fc(),e!==null&&!Be?(rc(e,t,u),Zt(e,t,u)):(he&&a&&Zi(t),t.flags|=1,Le(e,t,l,u),t.child)}function uo(e,t,l,a,u,n){return Ll(t),t.updateQueue=null,l=as(t,a,l,u),ls(e),a=fc(),e!==null&&!Be?(rc(e,t,n),Zt(e,t,n)):(he&&a&&Zi(t),t.flags|=1,Le(e,t,l,n),t.child)}function no(e,t,l,a,u){if(Ll(t),t.stateNode===null){var n=fa,c=l.contextType;typeof c=="object"&&c!==null&&(n=Ve(c)),n=new l(a,n),t.memoizedState=n.state!==null&&n.state!==void 0?n.state:null,n.updater=Tc,t.stateNode=n,n._reactInternals=t,n=t.stateNode,n.props=a,n.state=t.memoizedState,n.refs={},ec(t),c=l.contextType,n.context=typeof c=="object"&&c!==null?Ve(c):fa,n.state=t.memoizedState,c=l.getDerivedStateFromProps,typeof c=="function"&&(Ec(t,l,c,a),n.state=t.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof n.getSnapshotBeforeUpdate=="function"||typeof n.UNSAFE_componentWillMount!="function"&&typeof n.componentWillMount!="function"||(c=n.state,typeof n.componentWillMount=="function"&&n.componentWillMount(),typeof n.UNSAFE_componentWillMount=="function"&&n.UNSAFE_componentWillMount(),c!==n.state&&Tc.enqueueReplaceState(n,n.state,null),uu(t,a,n,u),au(),n.state=t.memoizedState),typeof n.componentDidMount=="function"&&(t.flags|=4194308),a=!0}else if(e===null){n=t.stateNode;var r=t.memoizedProps,y=Xl(l,r);n.props=y;var T=n.context,z=l.contextType;c=fa,typeof z=="object"&&z!==null&&(c=Ve(z));var C=l.getDerivedStateFromProps;z=typeof C=="function"||typeof n.getSnapshotBeforeUpdate=="function",r=t.pendingProps!==r,z||typeof n.UNSAFE_componentWillReceiveProps!="function"&&typeof n.componentWillReceiveProps!="function"||(r||T!==c)&&Vs(t,n,a,c),nl=!1;var R=t.memoizedState;n.state=R,uu(t,a,n,u),au(),T=t.memoizedState,r||R!==T||nl?(typeof C=="function"&&(Ec(t,l,C,a),T=t.memoizedState),(y=nl||Zs(t,l,y,a,R,T,c))?(z||typeof n.UNSAFE_componentWillMount!="function"&&typeof n.componentWillMount!="function"||(typeof n.componentWillMount=="function"&&n.componentWillMount(),typeof n.UNSAFE_componentWillMount=="function"&&n.UNSAFE_componentWillMount()),typeof n.componentDidMount=="function"&&(t.flags|=4194308)):(typeof n.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=a,t.memoizedState=T),n.props=a,n.state=T,n.context=c,a=y):(typeof n.componentDidMount=="function"&&(t.flags|=4194308),a=!1)}else{n=t.stateNode,tc(e,t),c=t.memoizedProps,z=Xl(l,c),n.props=z,C=t.pendingProps,R=n.context,T=l.contextType,y=fa,typeof T=="object"&&T!==null&&(y=Ve(T)),r=l.getDerivedStateFromProps,(T=typeof r=="function"||typeof n.getSnapshotBeforeUpdate=="function")||typeof n.UNSAFE_componentWillReceiveProps!="function"&&typeof n.componentWillReceiveProps!="function"||(c!==C||R!==y)&&Vs(t,n,a,y),nl=!1,R=t.memoizedState,n.state=R,uu(t,a,n,u),au();var N=t.memoizedState;c!==C||R!==N||nl||e!==null&&e.dependencies!==null&&fn(e.dependencies)?(typeof r=="function"&&(Ec(t,l,r,a),N=t.memoizedState),(z=nl||Zs(t,l,z,a,R,N,y)||e!==null&&e.dependencies!==null&&fn(e.dependencies))?(T||typeof n.UNSAFE_componentWillUpdate!="function"&&typeof n.componentWillUpdate!="function"||(typeof n.componentWillUpdate=="function"&&n.componentWillUpdate(a,N,y),typeof n.UNSAFE_componentWillUpdate=="function"&&n.UNSAFE_componentWillUpdate(a,N,y)),typeof n.componentDidUpdate=="function"&&(t.flags|=4),typeof n.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof n.componentDidUpdate!="function"||c===e.memoizedProps&&R===e.memoizedState||(t.flags|=4),typeof n.getSnapshotBeforeUpdate!="function"||c===e.memoizedProps&&R===e.memoizedState||(t.flags|=1024),t.memoizedProps=a,t.memoizedState=N),n.props=a,n.state=N,n.context=y,a=z):(typeof n.componentDidUpdate!="function"||c===e.memoizedProps&&R===e.memoizedState||(t.flags|=4),typeof n.getSnapshotBeforeUpdate!="function"||c===e.memoizedProps&&R===e.memoizedState||(t.flags|=1024),a=!1)}return n=a,On(e,t),a=(t.flags&128)!==0,n||a?(n=t.stateNode,l=a&&typeof l.getDerivedStateFromError!="function"?null:n.render(),t.flags|=1,e!==null&&a?(t.child=ba(t,e.child,null,u),t.child=ba(t,null,l,u)):Le(e,t,l,u),t.memoizedState=n.state,e=t.child):e=Zt(e,t,u),e}function io(e,t,l,a){return $a(),t.flags|=256,Le(e,t,l,a),t.child}var Nc={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Oc(e){return{baseLanes:e,cachePool:Jr()}}function Mc(e,t,l){return e=e!==null?e.childLanes&~l:0,t&&(e|=pt),e}function co(e,t,l){var a=t.pendingProps,u=!1,n=(t.flags&128)!==0,c;if((c=n)||(c=e!==null&&e.memoizedState===null?!1:(Ue.current&2)!==0),c&&(u=!0,t.flags&=-129),c=(t.flags&32)!==0,t.flags&=-33,e===null){if(he){if(u?rl(t):sl(),he){var r=Oe,y;if(y=r){e:{for(y=r,r=Rt;y.nodeType!==8;){if(!r){r=null;break e}if(y=Tt(y.nextSibling),y===null){r=null;break e}}r=y}r!==null?(t.memoizedState={dehydrated:r,treeContext:Ul!==null?{id:wt,overflow:Lt}:null,retryLane:536870912,hydrationErrors:null},y=nt(18,null,null,0),y.stateNode=r,y.return=t,t.child=y,ke=t,Oe=null,y=!0):y=!1}y||ql(t)}if(r=t.memoizedState,r!==null&&(r=r.dehydrated,r!==null))return df(r)?t.lanes=32:t.lanes=536870912,null;Qt(t)}return r=a.children,a=a.fallback,u?(sl(),u=t.mode,r=Mn({mode:"hidden",children:r},u),a=Cl(a,u,l,null),r.return=t,a.return=t,r.sibling=a,t.child=r,u=t.child,u.memoizedState=Oc(l),u.childLanes=Mc(e,c,l),t.memoizedState=Nc,a):(rl(t),jc(t,r))}if(y=e.memoizedState,y!==null&&(r=y.dehydrated,r!==null)){if(n)t.flags&256?(rl(t),t.flags&=-257,t=zc(e,t,l)):t.memoizedState!==null?(sl(),t.child=e.child,t.flags|=128,t=null):(sl(),u=a.fallback,r=t.mode,a=Mn({mode:"visible",children:a.children},r),u=Cl(u,r,l,null),u.flags|=2,a.return=t,u.return=t,a.sibling=u,t.child=a,ba(t,e.child,null,l),a=t.child,a.memoizedState=Oc(l),a.childLanes=Mc(e,c,l),t.memoizedState=Nc,t=u);else if(rl(t),df(r)){if(c=r.nextSibling&&r.nextSibling.dataset,c)var T=c.dgst;c=T,a=Error(f(419)),a.stack="",a.digest=c,Wa({value:a,source:null,stack:null}),t=zc(e,t,l)}else if(Be||Fa(e,t,l,!1),c=(l&e.childLanes)!==0,Be||c){if(c=xe,c!==null&&(a=l&-l,a=(a&42)!==0?1:di(a),a=(a&(c.suspendedLanes|l))!==0?0:a,a!==0&&a!==y.retryLane))throw y.retryLane=a,ca(e,a),st(c,e,a),Ps;r.data==="$?"||kc(),t=zc(e,t,l)}else r.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=y.treeContext,Oe=Tt(r.nextSibling),ke=t,he=!0,Bl=null,Rt=!1,e!==null&&(vt[gt++]=wt,vt[gt++]=Lt,vt[gt++]=Ul,wt=e.id,Lt=e.overflow,Ul=t),t=jc(t,a.children),t.flags|=4096);return t}return u?(sl(),u=a.fallback,r=t.mode,y=e.child,T=y.sibling,a=qt(y,{mode:"hidden",children:a.children}),a.subtreeFlags=y.subtreeFlags&65011712,T!==null?u=qt(T,u):(u=Cl(u,r,l,null),u.flags|=2),u.return=t,a.return=t,a.sibling=u,t.child=a,a=u,u=t.child,r=e.child.memoizedState,r===null?r=Oc(l):(y=r.cachePool,y!==null?(T=Ce._currentValue,y=y.parent!==T?{parent:T,pool:T}:y):y=Jr(),r={baseLanes:r.baseLanes|l,cachePool:y}),u.memoizedState=r,u.childLanes=Mc(e,c,l),t.memoizedState=Nc,a):(rl(t),l=e.child,e=l.sibling,l=qt(l,{mode:"visible",children:a.children}),l.return=t,l.sibling=null,e!==null&&(c=t.deletions,c===null?(t.deletions=[e],t.flags|=16):c.push(e)),t.child=l,t.memoizedState=null,l)}function jc(e,t){return t=Mn({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Mn(e,t){return e=nt(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function zc(e,t,l){return ba(t,e.child,null,l),e=jc(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function fo(e,t,l){e.lanes|=t;var a=e.alternate;a!==null&&(a.lanes|=t),ki(e.return,t,l)}function Dc(e,t,l,a,u){var n=e.memoizedState;n===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:a,tail:l,tailMode:u}:(n.isBackwards=t,n.rendering=null,n.renderingStartTime=0,n.last=a,n.tail=l,n.tailMode=u)}function ro(e,t,l){var a=t.pendingProps,u=a.revealOrder,n=a.tail;if(Le(e,t,a.children,l),a=Ue.current,(a&2)!==0)a=a&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&fo(e,l,t);else if(e.tag===19)fo(e,l,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}a&=1}switch(Y(Ue,a),u){case"forwards":for(l=t.child,u=null;l!==null;)e=l.alternate,e!==null&&An(e)===null&&(u=l),l=l.sibling;l=u,l===null?(u=t.child,t.child=null):(u=l.sibling,l.sibling=null),Dc(t,!1,u,l,n);break;case"backwards":for(l=null,u=t.child,t.child=null;u!==null;){if(e=u.alternate,e!==null&&An(e)===null){t.child=u;break}e=u.sibling,u.sibling=l,l=u,u=e}Dc(t,!0,l,null,n);break;case"together":Dc(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function Zt(e,t,l){if(e!==null&&(t.dependencies=e.dependencies),yl|=t.lanes,(l&t.childLanes)===0)if(e!==null){if(Fa(e,t,l,!1),(l&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(f(153));if(t.child!==null){for(e=t.child,l=qt(e,e.pendingProps),t.child=l,l.return=t;e.sibling!==null;)e=e.sibling,l=l.sibling=qt(e,e.pendingProps),l.return=t;l.sibling=null}return t.child}function _c(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&fn(e)))}function Bm(e,t,l){switch(t.tag){case 3:Te(t,t.stateNode.containerInfo),ul(t,Ce,e.memoizedState.cache),$a();break;case 27:case 5:ci(t);break;case 4:Te(t,t.stateNode.containerInfo);break;case 10:ul(t,t.type,t.memoizedProps.value);break;case 13:var a=t.memoizedState;if(a!==null)return a.dehydrated!==null?(rl(t),t.flags|=128,null):(l&t.child.childLanes)!==0?co(e,t,l):(rl(t),e=Zt(e,t,l),e!==null?e.sibling:null);rl(t);break;case 19:var u=(e.flags&128)!==0;if(a=(l&t.childLanes)!==0,a||(Fa(e,t,l,!1),a=(l&t.childLanes)!==0),u){if(a)return ro(e,t,l);t.flags|=128}if(u=t.memoizedState,u!==null&&(u.rendering=null,u.tail=null,u.lastEffect=null),Y(Ue,Ue.current),a)break;return null;case 22:case 23:return t.lanes=0,lo(e,t,l);case 24:ul(t,Ce,e.memoizedState.cache)}return Zt(e,t,l)}function so(e,t,l){if(e!==null)if(e.memoizedProps!==t.pendingProps)Be=!0;else{if(!_c(e,l)&&(t.flags&128)===0)return Be=!1,Bm(e,t,l);Be=(e.flags&131072)!==0}else Be=!1,he&&(t.flags&1048576)!==0&&Yr(t,cn,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var a=t.elementType,u=a._init;if(a=u(a._payload),t.type=a,typeof a=="function")Gi(a)?(e=Xl(a,e),t.tag=1,t=no(null,t,a,e,l)):(t.tag=0,t=Rc(null,t,a,e,l));else{if(a!=null){if(u=a.$$typeof,u===ce){t.tag=11,t=Is(null,t,a,e,l);break e}else if(u===ue){t.tag=14,t=eo(null,t,a,e,l);break e}}throw t=Ol(a)||a,Error(f(306,t,""))}}return t;case 0:return Rc(e,t,t.type,t.pendingProps,l);case 1:return a=t.type,u=Xl(a,t.pendingProps),no(e,t,a,u,l);case 3:e:{if(Te(t,t.stateNode.containerInfo),e===null)throw Error(f(387));a=t.pendingProps;var n=t.memoizedState;u=n.element,tc(e,t),uu(t,a,null,l);var c=t.memoizedState;if(a=c.cache,ul(t,Ce,a),a!==n.cache&&$i(t,[Ce],l,!0),au(),a=c.element,n.isDehydrated)if(n={element:a,isDehydrated:!1,cache:c.cache},t.updateQueue.baseState=n,t.memoizedState=n,t.flags&256){t=io(e,t,a,l);break e}else if(a!==u){u=mt(Error(f(424)),t),Wa(u),t=io(e,t,a,l);break e}else{switch(e=t.stateNode.containerInfo,e.nodeType){case 9:e=e.body;break;default:e=e.nodeName==="HTML"?e.ownerDocument.body:e}for(Oe=Tt(e.firstChild),ke=t,he=!0,Bl=null,Rt=!0,l=Xs(t,null,a,l),t.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if($a(),a===u){t=Zt(e,t,l);break e}Le(e,t,a,l)}t=t.child}return t;case 26:return On(e,t),e===null?(l=yd(t.type,null,t.pendingProps,null))?t.memoizedState=l:he||(l=t.type,e=t.pendingProps,a=Xn(ee.current).createElement(l),a[Ze]=t,a[$e]=e,Ge(a,l,e),He(a),t.stateNode=a):t.memoizedState=yd(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return ci(t),e===null&&he&&(a=t.stateNode=dd(t.type,t.pendingProps,ee.current),ke=t,Rt=!0,u=Oe,pl(t.type)?(hf=u,Oe=Tt(a.firstChild)):Oe=u),Le(e,t,t.pendingProps.children,l),On(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&he&&((u=a=Oe)&&(a=sy(a,t.type,t.pendingProps,Rt),a!==null?(t.stateNode=a,ke=t,Oe=Tt(a.firstChild),Rt=!1,u=!0):u=!1),u||ql(t)),ci(t),u=t.type,n=t.pendingProps,c=e!==null?e.memoizedProps:null,a=n.children,rf(u,n)?a=null:c!==null&&rf(u,c)&&(t.flags|=32),t.memoizedState!==null&&(u=cc(e,t,Mm,null,null,l),Nu._currentValue=u),On(e,t),Le(e,t,a,l),t.child;case 6:return e===null&&he&&((e=l=Oe)&&(l=oy(l,t.pendingProps,Rt),l!==null?(t.stateNode=l,ke=t,Oe=null,e=!0):e=!1),e||ql(t)),null;case 13:return co(e,t,l);case 4:return Te(t,t.stateNode.containerInfo),a=t.pendingProps,e===null?t.child=ba(t,null,a,l):Le(e,t,a,l),t.child;case 11:return Is(e,t,t.type,t.pendingProps,l);case 7:return Le(e,t,t.pendingProps,l),t.child;case 8:return Le(e,t,t.pendingProps.children,l),t.child;case 12:return Le(e,t,t.pendingProps.children,l),t.child;case 10:return a=t.pendingProps,ul(t,t.type,a.value),Le(e,t,a.children,l),t.child;case 9:return u=t.type._context,a=t.pendingProps.children,Ll(t),u=Ve(u),a=a(u),t.flags|=1,Le(e,t,a,l),t.child;case 14:return eo(e,t,t.type,t.pendingProps,l);case 15:return to(e,t,t.type,t.pendingProps,l);case 19:return ro(e,t,l);case 31:return a=t.pendingProps,l=t.mode,a={mode:a.mode,children:a.children},e===null?(l=Mn(a,l),l.ref=t.ref,t.child=l,l.return=t,t=l):(l=qt(e.child,a),l.ref=t.ref,t.child=l,l.return=t,t=l),t;case 22:return lo(e,t,l);case 24:return Ll(t),a=Ve(Ce),e===null?(u=Pi(),u===null&&(u=xe,n=Wi(),u.pooledCache=n,n.refCount++,n!==null&&(u.pooledCacheLanes|=l),u=n),t.memoizedState={parent:a,cache:u},ec(t),ul(t,Ce,u)):((e.lanes&l)!==0&&(tc(e,t),uu(t,null,null,l),au()),u=e.memoizedState,n=t.memoizedState,u.parent!==a?(u={parent:a,cache:a},t.memoizedState=u,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=u),ul(t,Ce,a)):(a=n.cache,ul(t,Ce,a),a!==u.cache&&$i(t,[Ce],l,!0))),Le(e,t,t.pendingProps.children,l),t.child;case 29:throw t.pendingProps}throw Error(f(156,t.tag))}function Vt(e){e.flags|=4}function oo(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!Sd(t)){if(t=bt.current,t!==null&&((re&4194048)===re?Nt!==null:(re&62914560)!==re&&(re&536870912)===0||t!==Nt))throw tu=Ii,kr;e.flags|=8192}}function jn(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?Qf():536870912,e.lanes|=t,Ea|=t)}function ou(e,t){if(!he)switch(e.tailMode){case"hidden":t=e.tail;for(var l=null;t!==null;)t.alternate!==null&&(l=t),t=t.sibling;l===null?e.tail=null:l.sibling=null;break;case"collapsed":l=e.tail;for(var a=null;l!==null;)l.alternate!==null&&(a=l),l=l.sibling;a===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:a.sibling=null}}function Re(e){var t=e.alternate!==null&&e.alternate.child===e.child,l=0,a=0;if(t)for(var u=e.child;u!==null;)l|=u.lanes|u.childLanes,a|=u.subtreeFlags&65011712,a|=u.flags&65011712,u.return=e,u=u.sibling;else for(u=e.child;u!==null;)l|=u.lanes|u.childLanes,a|=u.subtreeFlags,a|=u.flags,u.return=e,u=u.sibling;return e.subtreeFlags|=a,e.childLanes=l,t}function qm(e,t,l){var a=t.pendingProps;switch(Vi(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Re(t),null;case 1:return Re(t),null;case 3:return l=t.stateNode,a=null,e!==null&&(a=e.memoizedState.cache),t.memoizedState.cache!==a&&(t.flags|=2048),Gt(Ce),el(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(e===null||e.child===null)&&(ka(t)?Vt(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,Qr())),Re(t),null;case 26:return l=t.memoizedState,e===null?(Vt(t),l!==null?(Re(t),oo(t,l)):(Re(t),t.flags&=-16777217)):l?l!==e.memoizedState?(Vt(t),Re(t),oo(t,l)):(Re(t),t.flags&=-16777217):(e.memoizedProps!==a&&Vt(t),Re(t),t.flags&=-16777217),null;case 27:Lu(t),l=ee.current;var u=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==a&&Vt(t);else{if(!a){if(t.stateNode===null)throw Error(f(166));return Re(t),null}e=$.current,ka(t)?Gr(t):(e=dd(u,a,l),t.stateNode=e,Vt(t))}return Re(t),null;case 5:if(Lu(t),l=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==a&&Vt(t);else{if(!a){if(t.stateNode===null)throw Error(f(166));return Re(t),null}if(e=$.current,ka(t))Gr(t);else{switch(u=Xn(ee.current),e){case 1:e=u.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:e=u.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":e=u.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":e=u.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":e=u.createElement("div"),e.innerHTML=" - + +
diff --git a/components/rest_api/src/auth_api.c b/components/rest_api/src/auth_api.c index 18abdf5..81a046a 100755 --- a/components/rest_api/src/auth_api.c +++ b/components/rest_api/src/auth_api.c @@ -1,22 +1,33 @@ // ========================= // auth_api.c // ========================= + #include "auth_api.h" #include "auth.h" #include "esp_log.h" #include "cJSON.h" +#include 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 + }); } diff --git a/components/rest_api/src/dashboard_api.c b/components/rest_api/src/dashboard_api.c index 47aaed9..a565dc7 100644 --- a/components/rest_api/src/dashboard_api.c +++ b/components/rest_api/src/dashboard_api.c @@ -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); diff --git a/components/rest_api/src/network_api.c b/components/rest_api/src/network_api.c index d1c7526..08da017 100755 --- a/components/rest_api/src/network_api.c +++ b/components/rest_api/src/network_api.c @@ -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"; diff --git a/dependencies.lock b/dependencies.lock index 1753609..61c6fcf 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -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 diff --git a/main/main.c b/main/main.c index 9515d72..326740c 100755 --- a/main/main.c +++ b/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(); diff --git a/projeto_parte1.c b/projeto_parte1.c index 0f39e9c..be71207 100644 --- a/projeto_parte1.c +++ b/projeto_parte1.c @@ -18,7 +18,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" @@ -81,77 +81,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), @@ -177,10 +234,8 @@ 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(); // Outros módulos (descomente conforme necessário) // meter_init(); @@ -228,2223 +283,1779 @@ void app_main(void) { // === Fim de: main/main.c === -// === Início de: components/peripherals/src/ac_relay.c === +// === Início de: components/rest_api/src/ocpp_api.c === +// ========================= +// ocpp_api.c +// ========================= +#include "ocpp_api.h" #include "esp_log.h" -#include "driver/gpio.h" +#include "cJSON.h" -#include "ac_relay.h" -#include "board_config.h" +static const char *TAG = "ocpp_api"; -static const char* TAG = "ac_relay"; +static struct { + char url[256]; + char chargeBoxId[128]; + char certificate[256]; + char privateKey[256]; +} ocpp_config = {"", "", "", ""}; -// Memoization do estado atual do relé (salvo em RAM) -static int last_state = -1; +static esp_err_t ocpp_get_status_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + cJSON *status = cJSON_CreateObject(); + cJSON_AddStringToObject(status, "status", "connected"); + char *str = cJSON_Print(status); + httpd_resp_sendstr(req, str); + free(str); + cJSON_Delete(status); + return ESP_OK; +} -/** - * @brief Initialize the AC relay GPIO. - * - * Configures the specified GPIO pin as an output and sets its initial state to OFF (low). - */ -void ac_relay_init(void) -{ - gpio_config_t conf = { - .pin_bit_mask = BIT64(board_config.ac_relay_gpio), - .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE +static esp_err_t ocpp_get_config_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + cJSON *json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "url", ocpp_config.url); + cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId); + cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate); + cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey); + char *str = cJSON_Print(json); + httpd_resp_sendstr(req, str); + free(str); + cJSON_Delete(json); + return ESP_OK; +} + +static esp_err_t ocpp_post_config_handler(httpd_req_t *req) { + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); + return ESP_FAIL; + } + buf[len] = '\0'; + cJSON *json = cJSON_Parse(buf); + if (!json) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); + return ESP_FAIL; + } + cJSON *url = cJSON_GetObjectItem(json, "url"); + if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url)); + cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId"); + if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId)); + cJSON *cert = cJSON_GetObjectItem(json, "certificate"); + if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate)); + cJSON *key = cJSON_GetObjectItem(json, "privateKey"); + if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey)); + cJSON_Delete(json); + httpd_resp_sendstr(req, "OCPP config atualizada com sucesso"); + return ESP_OK; +} + +void register_ocpp_handlers(httpd_handle_t server, void *ctx) { + httpd_uri_t status_uri = { + .uri = "/api/v1/ocpp", + .method = HTTP_GET, + .handler = ocpp_get_status_handler, + .user_ctx = ctx }; + httpd_register_uri_handler(server, &status_uri); - esp_err_t ret = gpio_config(&conf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to configure GPIO (error: %s)", esp_err_to_name(ret)); - return; - } + httpd_uri_t get_uri = { + .uri = "/api/v1/config/ocpp", + .method = HTTP_GET, + .handler = ocpp_get_config_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &get_uri); - gpio_set_level(board_config.ac_relay_gpio, 0); ///< Ensure relay starts OFF - last_state = 0; - ESP_LOGI(TAG, "AC relay initialized. Pin: %d", board_config.ac_relay_gpio); + httpd_uri_t post_uri = { + .uri = "/api/v1/config/ocpp", + .method = HTTP_POST, + .handler = ocpp_post_config_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &post_uri); } -/** - * @brief Set the state of the AC relay. - * - * @param state True to turn the relay ON, False to turn it OFF. - */ -void ac_relay_set_state(bool state) -{ - if (state == last_state) { - // Estado não mudou; evita log e escrita desnecessária. - return; - } - last_state = state; - - ESP_LOGI(TAG, "Setting AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, state); - - esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state ? 1 : 0); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to set GPIO level (error: %s)", esp_err_to_name(ret)); - } -} - -/** - * @brief Get the current state of the AC relay. - * - * @return true if the relay is ON, false if OFF. - */ -bool ac_relay_get_state(void) -{ - int level = gpio_get_level(board_config.ac_relay_gpio); - ESP_LOGD(TAG, "Current AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, level); - return (level != 0); -} - -// === Fim de: components/peripherals/src/ac_relay.c === +// === Fim de: components/rest_api/src/ocpp_api.c === -// === Início de: components/peripherals/src/ntc_sensor.c === -#include -#include -#include "freertos/task.h" +// === Início de: components/rest_api/src/static_file_api.c === +#include "static_file_api.h" #include "esp_log.h" -#include "ntc_sensor.h" -#include "ntc_driver.h" +#include +#include +#include "esp_vfs.h" -#include "adc.h" +static const char *TAG = "static_file_api"; -static const char *TAG = "temp_sensor"; +#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) +#define SCRATCH_BUFSIZE (10240) -#define MEASURE_PERIOD 15000 // 10s +typedef struct rest_server_context { + char base_path[ESP_VFS_PATH_MAX + 1]; + char scratch[SCRATCH_BUFSIZE]; +} rest_server_context_t; -static float temp = 0.0; +#define CHECK_FILE_EXTENSION(filename, ext) \ + (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) -static ntc_device_handle_t ntc = NULL; +static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) { + const char *type = "text/plain"; + if (CHECK_FILE_EXTENSION(filepath, ".html")) type = "text/html"; + else if (CHECK_FILE_EXTENSION(filepath, ".js")) type = "application/javascript"; + else if (CHECK_FILE_EXTENSION(filepath, ".css")) type = "text/css"; + else if (CHECK_FILE_EXTENSION(filepath, ".png")) type = "image/png"; + else if (CHECK_FILE_EXTENSION(filepath, ".ico")) type = "image/x-icon"; + else if (CHECK_FILE_EXTENSION(filepath, ".svg")) type = "image/svg+xml"; + return httpd_resp_set_type(req, type); +} -static portMUX_TYPE temp_mux = portMUX_INITIALIZER_UNLOCKED; +static esp_err_t static_get_handler(httpd_req_t *req) { + char filepath[FILE_PATH_MAX]; + rest_server_context_t *ctx = (rest_server_context_t *) req->user_ctx; -static void ntc_sensor_task_func(void *param) { - float t; - while (true) { - if (ntc_dev_get_temperature(ntc, &t) == ESP_OK) { - portENTER_CRITICAL(&temp_mux); - temp = t; - portEXIT_CRITICAL(&temp_mux); + strlcpy(filepath, ctx->base_path, sizeof(filepath)); + if (req->uri[strlen(req->uri) - 1] == '/') { + strlcat(filepath, "/index.html", sizeof(filepath)); + } else { + strlcat(filepath, req->uri, sizeof(filepath)); + } + + int fd = open(filepath, O_RDONLY, 0); + if (fd == -1) { + // fallback para /index.html (SPA) + ESP_LOGW(TAG, "Arquivo não encontrado: %s. Tentando index.html", filepath); + strlcpy(filepath, ctx->base_path, sizeof(filepath)); + strlcat(filepath, "/index.html", sizeof(filepath)); + fd = open(filepath, O_RDONLY, 0); + if (fd == -1) { + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Arquivo não encontrado"); + return ESP_FAIL; } - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); } + + set_content_type_from_file(req, filepath); + + char *chunk = ctx->scratch; + ssize_t read_bytes; + do { + read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); + if (read_bytes == -1) { + ESP_LOGE(TAG, "Erro lendo arquivo: %s", filepath); + close(fd); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao ler arquivo"); + return ESP_FAIL; + } else if (read_bytes > 0) { + if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { + close(fd); + httpd_resp_sendstr_chunk(req, NULL); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao enviar arquivo"); + return ESP_FAIL; + } + } + } while (read_bytes > 0); + + close(fd); + httpd_resp_send_chunk(req, NULL, 0); + return ESP_OK; } -float ntc_temp_sensor(void) { - float t; - portENTER_CRITICAL(&temp_mux); - t = temp; - portEXIT_CRITICAL(&temp_mux); - return t; +void register_static_file_handlers(httpd_handle_t server, void *ctx) { + httpd_uri_t uri = { + .uri = "/*", + .method = HTTP_GET, + .handler = static_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &uri); } -void ntc_sensor_init(void) -{ +// === Fim de: components/rest_api/src/static_file_api.c === - ESP_LOGI(TAG, "ntc_sensor_init"); - // Select the NTC sensor and initialize the hardware parameters - ntc_config_t ntc_config = { - .b_value = 3950, - .r25_ohm = 10000, - .fixed_ohm = 4700, - .vdd_mv = 3300, - .circuit_mode = CIRCUIT_MODE_NTC_GND, - .atten = ADC_ATTEN_DB_12, - .channel = ADC_CHANNEL_0, - .unit = ADC_UNIT_1}; +// === Início de: components/rest_api/src/meters_settings_api.c === +#include "meters_settings_api.h" +#include "meter_manager.h" // Atualizado para usar o novo manager +#include "esp_log.h" +#include "cJSON.h" - // Create the NTC Driver and Init ADC - // ntc_device_handle_t ntc = NULL; - // adc_oneshot_unit_handle_t adc_handle = NULL; - ESP_ERROR_CHECK(ntc_dev_create(&ntc_config, &ntc, &adc_handle)); - ESP_ERROR_CHECK(ntc_dev_get_adc_handle(ntc, &adc_handle)); +static const char *TAG = "meters_settings_api"; - xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 3, NULL); +// Função para recuperar as configurações dos contadores +static esp_err_t meters_config_get_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters"); + + httpd_resp_set_type(req, "application/json"); + + cJSON *config = cJSON_CreateObject(); + + // Recuperando as configurações dos contadores + meter_type_t gridmeterType = meter_manager_grid_get_model(); + meter_type_t evsemeterType = meter_manager_evse_get_model(); + + ESP_LOGI(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType)); + ESP_LOGI(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType)); + + // Adicionando os tipos de contadores ao objeto JSON + cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType)); + cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType)); + + // Convertendo o objeto JSON para uma string + const char *json_str = cJSON_Print(config); + ESP_LOGI(TAG, "Returning meters config: %s", json_str); + + httpd_resp_sendstr(req, json_str); + + // Liberação da memória + free((void *)json_str); + cJSON_Delete(config); + + return ESP_OK; } -// === Fim de: components/peripherals/src/ntc_sensor.c === +// Função para atualizar as configurações dos contadores +static esp_err_t meters_config_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters"); + + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + + if (len <= 0) { + ESP_LOGE(TAG, "Received empty body in POST request"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); + return ESP_FAIL; + } + + buf[len] = '\0'; // Garantir que a string está terminada + + ESP_LOGI(TAG, "Received POST data: %s", buf); + + cJSON *json = cJSON_Parse(buf); + if (!json) { + ESP_LOGE(TAG, "Failed to parse JSON data"); + // Resposta detalhada de erro + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format"); + return ESP_FAIL; + } + + // Atualizando os contadores + cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter"); + if (gridmeter) { + meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type + ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring); + meter_manager_grid_set_model(gridType); + } + + cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter"); + if (evsemeter) { + meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type + ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring); + meter_manager_evse_set_model(evseType); + } + + cJSON_Delete(json); + httpd_resp_sendstr(req, "Meters updated successfully"); + + ESP_LOGI(TAG, "Meters configuration updated successfully"); + + return ESP_OK; +} + +// Registrando os manipuladores de URI para os contadores +void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { + ESP_LOGD(TAG, "Registering URI handlers for meters settings"); + + // URI para o método GET + httpd_uri_t meters_get_uri = { + .uri = "/api/v1/config/meters", + .method = HTTP_GET, + .handler = meters_config_get_handler, + .user_ctx = ctx + }; + ESP_LOGD(TAG, "Registering GET handler for /api/v1/config/meters"); + httpd_register_uri_handler(server, &meters_get_uri); + + // URI para o método POST + httpd_uri_t meters_post_uri = { + .uri = "/api/v1/config/meters", + .method = HTTP_POST, + .handler = meters_config_post_handler, + .user_ctx = ctx + }; + ESP_LOGD(TAG, "Registering POST handler for /api/v1/config/meters"); + httpd_register_uri_handler(server, &meters_post_uri); +} + +// === Fim de: components/rest_api/src/meters_settings_api.c === -// === Início de: components/peripherals/src/proximity.c === +// === Início de: components/rest_api/src/rest_main.c === +#include "rest_main.h" +#include "evse_settings_api.h" +#include "meters_settings_api.h" +#include "loadbalancing_settings_api.h" +#include "network_api.h" +#include "ocpp_api.h" +#include "auth_api.h" +#include "dashboard_api.h" +#include "static_file_api.h" #include "esp_log.h" -#include "proximity.h" -#include "board_config.h" -#include "adc.h" -static const char *TAG = "proximity"; +static const char *TAG = "rest_main"; -void proximity_init(void) -{ - if (board_config.proximity) - { - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12}; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.proximity_adc_channel, &config)); +esp_err_t rest_server_init(const char *base_path) { + ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path); + + rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t)); + if (!ctx) { + ESP_LOGE(TAG, "Failed to allocate memory for REST context"); + return ESP_ERR_NO_MEM; } + + strlcpy(ctx->base_path, base_path, sizeof(ctx->base_path)); + + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.uri_match_fn = httpd_uri_match_wildcard; + config.max_uri_handlers = 32; + + httpd_handle_t server = NULL; + esp_err_t err = httpd_start(&server, &config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err)); + free(ctx); + return err; + } + + ESP_LOGI(TAG, "HTTP server started successfully"); + + // Register endpoint groups + register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação + register_network_handlers(server, ctx); // Apenas chamando a função sem comparação + register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação + register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação + register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação + register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação + register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação + register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação + + ESP_LOGI(TAG, "All REST API endpoint groups registered successfully"); + + return ESP_OK; } -uint8_t proximity_get_max_current(void) -{ - int voltage; - adc_oneshot_read(adc_handle, board_config.proximity_adc_channel, &voltage); - adc_cali_raw_to_voltage(adc_cali_handle, voltage, &voltage); - - ESP_LOGI(TAG, "Measured: %dmV", voltage); - - uint8_t current; - - if (voltage >= board_config.proximity_down_threshold_8) - { - current = 8; - } - else if (voltage >= board_config.proximity_down_threshold_10) - { - current = 10; - } - - else if (voltage >= board_config.proximity_down_threshold_13) - { - current = 13; - } - else if (voltage >= board_config.proximity_down_threshold_20) - { - current = 20; - } - - else if (voltage >= board_config.proximity_down_threshold_25) - { - current = 25; - } - else if (voltage >= board_config.proximity_down_threshold_32) - { - current = 32; - } - else - { - current = 32; - } - - ESP_LOGI(TAG, "Max current: %dA", current); - - return current; -} -// === Fim de: components/peripherals/src/proximity.c === +// === Fim de: components/rest_api/src/rest_main.c === -// === Início de: components/peripherals/src/buzzer.c === -#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" +// === Início de: components/rest_api/src/network_api.c === +// ========================= +// network_api.c +// ========================= -static gpio_num_t buzzer_gpio = GPIO_NUM_NC; -static evse_state_t last_buzzer_state = -1; -static QueueHandle_t buzzer_queue = NULL; +#include "network_api.h" +#include "esp_log.h" +#include "cJSON.h" +#include "network.h" +#include "mqtt.h" -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 -// ---------------------- +static const char *TAG = "network_api"; typedef struct { - uint16_t on_ms; - uint16_t off_ms; -} buzzer_pattern_step_t; + bool enabled; + char ssid[33]; + char password[65]; +} wifi_task_data_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); -} - -// === Fim de: components/peripherals/src/buzzer.c === - - -// === Início de: components/peripherals/src/ds18x20.h === -/* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _DS18X20_H -#define _DS18X20_H - -#include -#include "onewire.h" - -typedef onewire_addr_t ds18x20_addr_t; - -/** An address value which can be used to indicate "any device on the bus" */ -#define DS18X20_ANY ONEWIRE_NONE - -/** Family ID (lower address byte) of DS18B20 sensors */ -#define DS18B20_FAMILY_ID 0x28 - -/** Family ID (lower address byte) of DS18S20 sensors */ -#define DS18S20_FAMILY_ID 0x10 - -/** - * @brief Find the addresses of all ds18x20 devices on the bus. - * - * Scans the bus for all devices and places their addresses in the supplied - * array. If there are more than `addr_count` devices on the bus, only the - * first `addr_count` are recorded. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A pointer to an array of ::ds18x20_addr_t values. - * This will be populated with the addresses of the found - * devices. - * @param addr_count Number of slots in the `addr_list` array. At most this - * many addresses will be returned. - * @param found The number of devices found. Note that this may be less - * than, equal to, or more than `addr_count`, depending on - * how many ds18x20 devices are attached to the bus. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found); - -/** - * @brief Tell one or more sensors to perform a temperature measurement and - * conversion (CONVERT_T) operation. - * - * This operation can take up to 750ms to complete. - * - * If `wait=true`, this routine will automatically drive the pin high for the - * necessary 750ms after issuing the command to ensure parasitically-powered - * devices have enough power to perform the conversion operation (for - * non-parasitically-powered devices, this is not necessary but does not - * hurt). If `wait=false`, this routine will drive the pin high, but will - * then return immediately. It is up to the caller to wait the requisite time - * and then depower the bus using onewire_depower() or by issuing another - * command once conversion is done. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device on the bus. This can be set - * to ::DS18X20_ANY to send the command to all devices on the bus - * at the same time. - * @param wait Whether to wait for the necessary 750ms for the ds18x20 to - * finish performing the conversion before returning to the - * caller (You will normally want to do this). - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); - -/** - * @brief Read the value from the last CONVERT_T operation. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18b20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18s20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation for multiple devices. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** Perform a ds18x20_measure() followed by ds18s20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18s20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18b20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Perform a ds18x20_measure() followed by ds18x20_read_temp_multi() - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** - * @brief Read the scratchpad data for a particular ds18x20 device. - * - * This is not generally necessary to do directly. It is done automatically - * as part of ds18x20_read_temperature(). - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 8-byte buffer to hold the read data. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Write the scratchpad data for a particular ds18x20 device. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to write. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 3-byte buffer to hold the data to write - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Issue the copy scratchpad command, copying current scratchpad to - * EEPROM. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to command. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr); - - -#endif /* _DS18X20_H */ -// === Fim de: components/peripherals/src/ds18x20.h === - - -// === Início de: components/peripherals/src/socket_lock.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "socket_lock.h" -#include "board_config.h" - -#define NVS_NAMESPACE "socket_lock" -#define NVS_OPERATING_TIME "op_time" -#define NVS_BREAK_TIME "break_time" -#define NVS_RETRY_COUNT "retry_count" -#define NVS_DETECTION_HIGH "detect_hi" - -#define OPERATING_TIME_MIN 100 -#define OPERATING_TIME_MAX 1000 -#define LOCK_DELAY 500 - -#define LOCK_BIT BIT0 -#define UNLOCK_BIT BIT1 -#define REPEAT_LOCK_BIT BIT2 -#define REPEAT_UNLOCK_BIT BIT3 - -static const char* TAG = "socket_lock"; - -static nvs_handle_t nvs; - -static uint16_t operating_time = 300; - -static uint16_t break_time = 1000; - -static bool detection_high; - -static uint8_t retry_count = 5; - -static socket_lock_status_t status; - -static TaskHandle_t socket_lock_task; - -static bool is_locked(void) -{ - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - - vTaskDelay(pdMS_TO_TICKS(board_config.socket_lock_detection_delay)); - - return gpio_get_level(board_config.socket_lock_detection_gpio) == detection_high; -} - -bool socket_lock_is_locked_state(void) -{ - return is_locked(); -} - -static void socket_lock_task_func(void* param) -{ - uint32_t notification; - - TickType_t previous_tick = 0; - uint8_t attempt = 0; - - while (true) { - if (xTaskNotifyWait(0x00, 0xff, ¬ification, portMAX_DELAY)) { - if (notification & (LOCK_BIT | UNLOCK_BIT)) { - attempt = retry_count; - } - - if (notification & (UNLOCK_BIT | REPEAT_UNLOCK_BIT)) { - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (!is_locked()) { - ESP_LOGI(TAG, "Unlock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not unlocked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_UNLOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not unlocked"); - status = SOCKED_LOCK_STATUS_UNLOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } else if (notification & (LOCK_BIT | REPEAT_LOCK_BIT)) { - if (notification & LOCK_BIT) { - vTaskDelay(pdMS_TO_TICKS(LOCK_DELAY)); //delay before first lock attempt - } - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (is_locked()) { - ESP_LOGI(TAG, "Lock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not locked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_LOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not locked"); - status = SOCKED_LOCK_STATUS_LOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } - - TickType_t delay_tick = xTaskGetTickCount() - previous_tick; - if (delay_tick < pdMS_TO_TICKS(break_time)) { - vTaskDelay(pdMS_TO_TICKS(break_time) - delay_tick); - } - previous_tick = xTaskGetTickCount(); - } - } -} - -void socket_lock_init(void) -{ - if (board_config.socket_lock) { - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - nvs_get_u16(nvs, NVS_OPERATING_TIME, &operating_time); - - nvs_get_u16(nvs, NVS_BREAK_TIME, &break_time); - - nvs_get_u8(nvs, NVS_RETRY_COUNT, &retry_count); - - uint8_t u8; - if (nvs_get_u8(nvs, NVS_DETECTION_HIGH, &u8) == ESP_OK) { - detection_high = u8; - } - - gpio_config_t io_conf = {}; - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_a_gpio) | BIT64(board_config.socket_lock_b_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_detection_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - xTaskCreate(socket_lock_task_func, "socket_lock_task", 2 * 1024, NULL, 10, &socket_lock_task); - } -} - -bool socket_lock_is_detection_high(void) -{ - return detection_high; -} - -void socket_lock_set_detection_high(bool _detection_high) -{ - detection_high = _detection_high; - - nvs_set_u8(nvs, NVS_DETECTION_HIGH, detection_high); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_operating_time(void) -{ - return operating_time; -} - -esp_err_t socket_lock_set_operating_time(uint16_t _operating_time) -{ - if (_operating_time < OPERATING_TIME_MIN || _operating_time > OPERATING_TIME_MAX) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - operating_time = _operating_time; - nvs_set_u16(nvs, NVS_OPERATING_TIME, operating_time); - nvs_commit(nvs); - - return ESP_OK; -} - -uint8_t socket_lock_get_retry_count(void) -{ - return retry_count; -} - -void socket_lock_set_retry_count(uint8_t _retry_count) -{ - retry_count = _retry_count; - nvs_set_u8(nvs, NVS_RETRY_COUNT, retry_count); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_break_time(void) -{ - return break_time; -} - -esp_err_t socket_lock_set_break_time(uint16_t _break_time) -{ - if (_break_time < board_config.socket_lock_min_break_time) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - break_time = _break_time; - nvs_set_u16(nvs, NVS_BREAK_TIME, break_time); - nvs_commit(nvs); - - return ESP_OK; -} - -void socket_lock_set_locked(bool locked) -{ - ESP_LOGI(TAG, "Set locked %d", locked); - - xTaskNotify(socket_lock_task, locked ? LOCK_BIT : UNLOCK_BIT, eSetBits); - status = SOCKED_LOCK_STATUS_OPERATING; -} - -socket_lock_status_t socket_lock_get_status(void) -{ - return status; -} -// === Fim de: components/peripherals/src/socket_lock.c === - - -// === Início de: components/peripherals/src/temp_sensor.c === -#include -#include -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" - -#include "temp_sensor.h" -#include "lm75a.h" - -#define MAX_SENSORS 5 -#define MEASURE_PERIOD 10000 // 10s -#define MEASURE_ERR_THRESHOLD 3 - -static const char *TAG = "temp_sensor"; - -static uint8_t sensor_count = 0; - -static int16_t low_temp = 0; - -static int high_temp = 0; - -static uint8_t measure_err_count = 0; - -static void temp_sensor_task_func(void *param) -{ - while (true) - { - high_temp = lm75a_read_temperature(0); - - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); - } -} - -void temp_sensor_init(void) -{ - - ESP_LOGW(TAG, "temp_sensor_init"); - - lm75a_init(); - - xTaskCreate(temp_sensor_task_func, "temp_sensor_task", 5 * 1024, NULL, 5, NULL); -} - -uint8_t temp_sensor_get_count(void) -{ - return sensor_count; -} - -int16_t temp_sensor_get_low(void) -{ - return low_temp; -} - -int temp_sensor_get_high(void) -{ - return high_temp; -} - -bool temp_sensor_is_error(void) -{ - return sensor_count == 0 || measure_err_count > MEASURE_ERR_THRESHOLD; -} -// === Fim de: components/peripherals/src/temp_sensor.c === - - -// === Início de: components/peripherals/src/aux_io.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "aux_io.h" -#include "board_config.h" -#include "adc.h" - -#define MAX_AUX_IN 4 -#define MAX_AUX_OUT 4 -#define MAX_AUX_AIN 4 - -//static const char* TAG = "aux"; - -static int aux_in_count = 0; -static int aux_out_count = 0; -static int aux_ain_count = 0; - -static struct aux_gpio_s -{ - gpio_num_t gpio; - const char* name; -} aux_in[MAX_AUX_IN], aux_out[MAX_AUX_OUT]; - -static struct aux_adc_s -{ - adc_channel_t adc; - const char* name; -} aux_ain[MAX_AUX_AIN]; - - -void aux_init(void) -{ - // IN - - gpio_config_t io_conf = { - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLDOWN_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - .pin_bit_mask = 0 - }; - - if (board_config.aux_in_1) { - aux_in[aux_in_count].gpio = board_config.aux_in_1_gpio; - aux_in[aux_in_count].name = board_config.aux_in_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_1_gpio); - aux_in_count++; - } - - if (board_config.aux_in_2) { - aux_in[aux_in_count].gpio = board_config.aux_in_2_gpio; - aux_in[aux_in_count].name = board_config.aux_in_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_2_gpio); - aux_in_count++; - } - - if (board_config.aux_in_3) { - aux_in[aux_in_count].gpio = board_config.aux_in_3_gpio; - aux_in[aux_in_count].name = board_config.aux_in_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_3_gpio); - aux_in_count++; - } - - if (board_config.aux_in_4) { - aux_in[aux_in_count].gpio = board_config.aux_in_4_gpio; - aux_in[aux_in_count].name = board_config.aux_in_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_4_gpio); - aux_in_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // OUT - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = 0; - - if (board_config.aux_out_1) { - aux_out[aux_out_count].gpio = board_config.aux_out_1_gpio; - aux_out[aux_out_count].name = board_config.aux_out_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_1_gpio); - aux_out_count++; - } - - if (board_config.aux_out_2) { - aux_out[aux_out_count].gpio = board_config.aux_out_2_gpio; - aux_out[aux_out_count].name = board_config.aux_out_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_2_gpio); - aux_out_count++; - } - - if (board_config.aux_out_3) { - aux_out[aux_out_count].gpio = board_config.aux_out_3_gpio; - aux_out[aux_out_count].name = board_config.aux_out_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_3_gpio); - aux_out_count++; - } - - if (board_config.aux_out_4) { - aux_out[aux_out_count].gpio = board_config.aux_out_4_gpio; - aux_out[aux_out_count].name = board_config.aux_out_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_4_gpio); - aux_out_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // AIN - - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12 - }; - - if (board_config.aux_ain_1) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_1_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_1_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_1_adc_channel, &config)); - aux_ain_count++; - } - - if (board_config.aux_ain_2) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_2_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_2_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_2_adc_channel, &config)); - aux_ain_count++; - } -} - -esp_err_t aux_read(const char* name, bool* value) -{ - for (int i = 0; i < aux_in_count; i++) { - if (strcmp(aux_in[i].name, name) == 0) { - *value = gpio_get_level(aux_in[i].gpio) == 1; - return ESP_OK; - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_write(const char* name, bool value) -{ - for (int i = 0; i < aux_out_count; i++) { - if (strcmp(aux_out[i].name, name) == 0) { - return gpio_set_level(aux_out[i].gpio, value); - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_analog_read(const char* name, int* value) -{ - for (int i = 0; i < aux_ain_count; i++) { - if (strcmp(aux_ain[i].name, name) == 0) { - int raw = 0; - esp_err_t ret = adc_oneshot_read(adc_handle, aux_ain[i].adc, &raw); - if (ret == ESP_OK) { - return adc_cali_raw_to_voltage(adc_cali_handle, raw, value); - } else { - return ret; - } - } - } - return ESP_ERR_NOT_FOUND; -} -// === Fim de: components/peripherals/src/aux_io.c === - - -// === Início de: components/peripherals/src/lm75a.c === -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "driver/i2c_master.h" - -#define I2C_MASTER_NUM I2C_NUM_1 -#define I2C_MASTER_SCL_IO GPIO_NUM_22 // CONFIG_EXAMPLE_I2C_SCL /*!< gpio number for I2C master clock */ -#define I2C_MASTER_SDA_IO GPIO_NUM_21 // CONFIG_EXAMPLE_I2C_SDA /*!< gpio number for I2C master data */ -#define I2C_MASTER_FREQ_HZ 100000 // CONFIG_I2C_TRANS_SPEED /*!< I2C master clock frequency */ -#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define LM75A_SLAVE_ADDR 0x48 // CONFIG_LM75A_SLAVE_ADDR /*!< LM75A slave address, you can set any 7bit value */ -#define ACK_VAL 0x0 /*!< I2C ack value */ -#define NACK_VAL 0x1 /*!< I2C nack value */ -#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ -#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ -#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ -#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ - -/* -#define GPIO_INPUT_IO_0 CONFIG_LM75A_OS_PIN -#define GPIO_OUTPUT_IO_0 CONFIG_LM75A_VCC_PIN -#define GPIO_OUTPUT_PIN_SEL (1ULL << GPIO_OUTPUT_IO_0) -#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO_0) -#define ESP_INTR_FLAG_DEFAULT 0 -*/ - -// static xQueueHandle gpio_evt_queue = NULL; -// static int gpio_int_task_enable = 0; -// static TaskHandle_t gpio_int_task_handle = NULL; - -/** - * @brief test code to read esp-i2c-slave - * We need to fill the buffer of esp slave device, then master can read them out. - * - * _______________________________________________________________________________________ - * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop | - * --------|--------------------------|----------------------|--------------------|------| - * - */ -static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size) -{ - if (size == 0) - { - return ESP_OK; - } - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN); - if (size > 1) - { - i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); - } - i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief Test code to write esp-i2c-slave - * Master device write data to slave(both esp32), - * the data will be stored in slave buffer. - * We can read them out from slave buffer. - * - * ___________________________________________________________________ - * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | - * --------|---------------------------|----------------------|------| - * - */ -static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t *data_wr, size_t size) -{ - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN); - i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief i2c master initialization - */ -static void i2c_master_init() -{ - int i2c_master_port = I2C_MASTER_NUM; - i2c_config_t conf; - conf.mode = I2C_MODE_MASTER; - conf.sda_io_num = I2C_MASTER_SDA_IO; - conf.sda_pullup_en = GPIO_PULLUP_DISABLE; - conf.scl_io_num = I2C_MASTER_SCL_IO; - conf.scl_pullup_en = GPIO_PULLUP_DISABLE; - conf.master.clk_speed = I2C_MASTER_FREQ_HZ; - conf.clk_flags = 0; - - i2c_param_config(i2c_master_port, &conf); - i2c_driver_install(i2c_master_port, conf.mode, - I2C_MASTER_RX_BUF_DISABLE, - I2C_MASTER_TX_BUF_DISABLE, 0); -} - -int lm75a_read_temperature(int show) -{ - uint8_t buf[2]; - float tmp; - buf[0] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - if (show) - printf("lm75a_read_temperature=%.1f\n", tmp); - return tmp; -} - -/* -static void IRAM_ATTR gpio_isr_handler(void *arg) -{ - uint32_t gpio_num = (uint32_t)arg; - xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); -} - -static void gpio_int_task(void *arg) -{ - uint32_t io_num; - gpio_int_task_enable = 1; - while (gpio_int_task_enable) - { - if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) - { - - // read temperature to clean int; - if (io_num == GPIO_INPUT_IO_0) - { - printf("GPIO[%d] intr, val: %d\n\n", io_num, gpio_get_level(io_num)); - lm75a_read_temperature(0); // read to clean interrupt. - } - } - } - printf("quit gpio_int_task\n"); - if (gpio_evt_queue) - { - vQueueDelete(gpio_evt_queue); - gpio_evt_queue = NULL; - } - gpio_int_task_handle = NULL; +static void wifi_apply_config_task(void *param) { + wifi_task_data_t *data = (wifi_task_data_t *)param; + ESP_LOGI("wifi_task", "Applying Wi-Fi config in background task"); + wifi_set_config(data->enabled, data->ssid, data->password); + free(data); vTaskDelete(NULL); } -void init_os_gpio() -{ - printf("init_os_gpio!\n"); +static esp_err_t wifi_get_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi"); - if (gpio_evt_queue == NULL) - gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); + httpd_resp_set_type(req, "application/json"); - if (gpio_int_task_handle == NULL) - { - xTaskCreate(gpio_int_task, "gpio_int_task", 2048, NULL, 10, &gpio_int_task_handle); - // install gpio isr service - gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); - // hook isr handler for specific gpio pin again - gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0); + // Obter dados da NVS via wifi.c + bool enabled = wifi_get_enabled(); + char ssid[33] = {0}; + char password[65] = {0}; + + wifi_get_ssid(ssid); + wifi_get_password(password); + + // Criar JSON + cJSON *json = cJSON_CreateObject(); + cJSON_AddBoolToObject(json, "enabled", enabled); + cJSON_AddStringToObject(json, "ssid", ssid); + cJSON_AddStringToObject(json, "password", password); + + // Enviar resposta + char *response = cJSON_Print(json); + httpd_resp_sendstr(req, response); + + // Limpeza + free(response); + cJSON_Delete(json); + + return ESP_OK; +} + +static esp_err_t wifi_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "Handling POST /api/v1/config/wifi"); + + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) return ESP_FAIL; + buf[len] = '\0'; + + cJSON *json = cJSON_Parse(buf); + if (!json) return ESP_FAIL; + + // Valores padrão + bool enabled = false; + const char *ssid = NULL; + const char *password = NULL; + + cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled"); + if (cJSON_IsBool(j_enabled)) enabled = j_enabled->valueint; + + cJSON *j_ssid = cJSON_GetObjectItem(json, "ssid"); + if (cJSON_IsString(j_ssid)) ssid = j_ssid->valuestring; + + cJSON *j_password = cJSON_GetObjectItem(json, "password"); + if (cJSON_IsString(j_password)) password = j_password->valuestring; + + // Enviar resposta antes de alterar Wi-Fi + httpd_resp_sendstr(req, "Wi-Fi config atualizada com sucesso"); + + // Alocar struct para passar para a task + wifi_task_data_t *task_data = malloc(sizeof(wifi_task_data_t)); + if (!task_data) { + cJSON_Delete(json); + ESP_LOGE(TAG, "Memory allocation failed for Wi-Fi task"); + return ESP_ERR_NO_MEM; } + + task_data->enabled = enabled; + strncpy(task_data->ssid, ssid ? ssid : "", sizeof(task_data->ssid)); + strncpy(task_data->password, password ? password : "", sizeof(task_data->password)); + + // Criar task normal com função C + xTaskCreate( + wifi_apply_config_task, + "wifi_config_task", + 4096, + task_data, + 3, + NULL + ); + + cJSON_Delete(json); + return ESP_OK; } -static void deinit_os_gpio() -{ - printf("deinit_os_gpio!\n"); - if (gpio_int_task_handle) - { - gpio_isr_handler_remove(GPIO_INPUT_IO_0); - gpio_uninstall_isr_service(); - gpio_int_task_enable = 0; - int io = 0; - xQueueSend(gpio_evt_queue, &io, 0); // send a fake signal to quit task. +static esp_err_t config_mqtt_get_handler(httpd_req_t *req) +{ + ESP_LOGI(TAG, "Handling GET /api/v1/config/mqtt"); + + httpd_resp_set_type(req, "application/json"); + + bool enabled = mqtt_get_enabled(); + char server[64] = {0}; + char base_topic[32] = {0}; + char username[32] = {0}; + char password[64] = {0}; + uint16_t periodicity = mqtt_get_periodicity(); + + mqtt_get_server(server); + mqtt_get_base_topic(base_topic); + mqtt_get_user(username); + mqtt_get_password(password); + + ESP_LOGI(TAG, "MQTT Config:"); + ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); + ESP_LOGI(TAG, " Server: %s", server); + ESP_LOGI(TAG, " Topic: %s", base_topic); + ESP_LOGI(TAG, " Username: %s", username); + ESP_LOGI(TAG, " Password: %s", password); + ESP_LOGI(TAG, " Periodicity: %d", periodicity); + + cJSON *config = cJSON_CreateObject(); + cJSON_AddBoolToObject(config, "enabled", enabled); + cJSON_AddStringToObject(config, "host", server); + cJSON_AddNumberToObject(config, "port", 1883); + cJSON_AddStringToObject(config, "username", username); + cJSON_AddStringToObject(config, "password", password); + cJSON_AddStringToObject(config, "topic", base_topic); + cJSON_AddNumberToObject(config, "periodicity", periodicity); + + const char *config_str = cJSON_Print(config); + httpd_resp_sendstr(req, config_str); + + free((void *)config_str); + cJSON_Delete(config); + return ESP_OK; +} + + +static esp_err_t config_mqtt_post_handler(httpd_req_t *req) +{ + ESP_LOGI(TAG, "Handling POST /api/v1/config/mqtt"); + + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) { + ESP_LOGE(TAG, "Failed to read request body"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); + return ESP_FAIL; } -} + buf[len] = '\0'; + ESP_LOGI(TAG, "Received JSON: %s", buf); -static void lm75a_vcc_enable() -{ - gpio_config_t io_conf; - // enable output for vcc - io_conf.intr_type = GPIO_PIN_INTR_DISABLE; - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; - io_conf.pull_down_en = 0; - io_conf.pull_up_en = 0; - gpio_config(&io_conf); - - // enable input for interrupt - io_conf.intr_type = GPIO_PIN_INTR_NEGEDGE; // GPIO_PIN_INTR_ANYEDGE; - io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = 1; - gpio_set_pull_mode(GPIO_INPUT_IO_0, GPIO_FLOATING); - gpio_config(&io_conf); - gpio_set_level(GPIO_OUTPUT_IO_0, 1); -} - -static void lm75a_vcc_disable() -{ - gpio_set_level(GPIO_OUTPUT_IO_0, 0); -} -*/ - -void lm75a_init() -{ - // lm75a_vcc_enable(); - i2c_master_init(); -} - -void lm75a_deinit() -{ - // deinit_os_gpio(); - i2c_driver_delete(I2C_MASTER_NUM); - // lm75a_vcc_disable(); -} - -void lm75a_set_tos(int tos) -{ - uint8_t buf[4]; - printf("lm75a_set_tos: %d\n", tos); - // set Tos: - buf[0] = 0x3; - buf[1] = (tos & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_set_thys(int thys) -{ - uint8_t buf[4]; - printf("lm75a_set_thys: %d\n", thys); - // set Thyst: - buf[0] = 0x2; - buf[1] = (thys & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_get_tos() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x3; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_tos: %.1f\n", tmp); -} - -void lm75a_get_thys() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x2; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_thys: %.1f\n", tmp); -} - -void lm75a_set_int(int en) -{ - uint8_t buf[2]; - - en = !!en; - if (en) - { - printf("lm75a_set_int: %d\n", en); - buf[0] = 0x1; - buf[1] = (1 << 1); // D1 set to 1; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - // gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE); - // init_os_gpio(); + cJSON *json = cJSON_Parse(buf); + if (!json) { + ESP_LOGE(TAG, "Invalid JSON format"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); + return ESP_FAIL; } + + bool enabled = false; + const char *host = NULL, *topic = NULL, *username = NULL, *password = NULL; + int periodicity = 30; + + if (cJSON_IsBool(cJSON_GetObjectItem(json, "enabled"))) + enabled = cJSON_GetObjectItem(json, "enabled")->valueint; + + cJSON *j_host = cJSON_GetObjectItem(json, "host"); + if (cJSON_IsString(j_host)) host = j_host->valuestring; + + cJSON *j_topic = cJSON_GetObjectItem(json, "topic"); + if (cJSON_IsString(j_topic)) topic = j_topic->valuestring; + + cJSON *j_user = cJSON_GetObjectItem(json, "username"); + if (cJSON_IsString(j_user)) username = j_user->valuestring; + + cJSON *j_pass = cJSON_GetObjectItem(json, "password"); + if (cJSON_IsString(j_pass)) password = j_pass->valuestring; + + cJSON *j_periodicity = cJSON_GetObjectItem(json, "periodicity"); + if (cJSON_IsNumber(j_periodicity)) periodicity = j_periodicity->valueint; + + ESP_LOGI(TAG, "Applying MQTT config:"); + ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); + ESP_LOGI(TAG, " Host: %s", host); + ESP_LOGI(TAG, " Topic: %s", topic); + ESP_LOGI(TAG, " Username: %s", username); + ESP_LOGI(TAG, " Password: %s", password); + ESP_LOGI(TAG, " Periodicity: %d", periodicity); + + esp_err_t err = mqtt_set_config(enabled, host, topic, username, password, periodicity); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to apply MQTT config (code %d)", err); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to apply config"); + cJSON_Delete(json); + return ESP_FAIL; + } + + httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso"); + cJSON_Delete(json); + return ESP_OK; +} + + + +void register_network_handlers(httpd_handle_t server, void *ctx) { + httpd_uri_t wifi_get = { + .uri = "/api/v1/config/wifi", + .method = HTTP_GET, + .handler = wifi_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &wifi_get); + + httpd_uri_t wifi_post = { + .uri = "/api/v1/config/wifi", + .method = HTTP_POST, + .handler = wifi_post_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &wifi_post); + + // URI handler for getting MQTT config + httpd_uri_t config_mqtt_get_uri = { + .uri = "/api/v1/config/mqtt", + .method = HTTP_GET, + .handler = config_mqtt_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &config_mqtt_get_uri); + + // URI handler for posting MQTT config + httpd_uri_t config_mqtt_post_uri = { + .uri = "/api/v1/config/mqtt", + .method = HTTP_POST, + .handler = config_mqtt_post_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &config_mqtt_post_uri); +} + +// === Fim de: components/rest_api/src/network_api.c === + + +// === Início de: components/rest_api/src/dashboard_api.c === +#include "dashboard_api.h" +#include "esp_log.h" +#include "cJSON.h" +#include "evse_api.h" +#include "evse_error.h" +#include "evse_config.h" +#include "evse_limits.h" + + +static const char *TAG = "dashboard_api"; + +static esp_err_t dashboard_get_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + + // Cria o objeto JSON principal do dashboard + cJSON *dashboard = cJSON_CreateObject(); + + // Status do sistema + evse_state_t state = evse_get_state(); + cJSON_AddStringToObject(dashboard, "status", evse_state_to_str(state)); + + // Carregador - informação do carregador 1 (adapte conforme necessário) + cJSON *chargers = cJSON_CreateArray(); + cJSON *charger1 = cJSON_CreateObject(); + cJSON_AddNumberToObject(charger1, "id", 1); + cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state)); + cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current()); + 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()) * 230; + cJSON_AddNumberToObject(charger1, "power", power); + + cJSON_AddItemToArray(chargers, charger1); + cJSON_AddItemToObject(dashboard, "chargers", chargers); + + // Consumo e tempo de carregamento + cJSON_AddNumberToObject(dashboard, "energyConsumed", evse_get_consumption_limit()); + cJSON_AddNumberToObject(dashboard, "chargingTime", evse_get_charging_time_limit()); + + // Alertas + cJSON *alerts = cJSON_CreateArray(); + if (evse_is_limit_reached()) { + cJSON_AddItemToArray(alerts, cJSON_CreateString("Limite de consumo atingido.")); + } + if (!evse_config_is_available()) { + cJSON_AddItemToArray(alerts, cJSON_CreateString("Estação indisponível.")); + } + if (!evse_config_is_enabled()) { + cJSON_AddItemToArray(alerts, cJSON_CreateString("EVSE desativado.")); + } + cJSON_AddItemToObject(dashboard, "alerts", alerts); + + // Erros + uint32_t error_bits = evse_get_error(); + cJSON *errors = cJSON_CreateArray(); + if (error_bits & EVSE_ERR_DIODE_SHORT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Diodo curto-circuitado")); + if (error_bits & EVSE_ERR_LOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no travamento")); + if (error_bits & EVSE_ERR_UNLOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no destravamento")); + if (error_bits & EVSE_ERR_RCM_SELFTEST_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no autoteste do RCM")); + if (error_bits & EVSE_ERR_RCM_TRIGGERED_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("RCM disparado")); + if (error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Temperatura elevada")); + if (error_bits & EVSE_ERR_PILOT_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Erro no sinal piloto")); + if (error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no sensor de temperatura")); + cJSON_AddItemToObject(dashboard, "errors", errors); + + // Enviar resposta JSON + const char *json_str = cJSON_Print(dashboard); + httpd_resp_sendstr(req, json_str); + + // Liberar memória + free((void *)json_str); + cJSON_Delete(dashboard); + + return ESP_OK; +} + +void register_dashboard_handlers(httpd_handle_t server, void *ctx) { + httpd_uri_t uri = { + .uri = "/api/v1/dashboard", + .method = HTTP_GET, + .handler = dashboard_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &uri); +} + +// === Fim de: components/rest_api/src/dashboard_api.c === + + +// === Início de: components/rest_api/src/auth_api.c === +// ========================= +// auth_api.c +// ========================= +#include "auth_api.h" +#include "auth.h" +#include "esp_log.h" +#include "cJSON.h" + +static const char *TAG = "auth_api"; + +static struct { + char username[128]; +} users[10] = { /*{"admin"}, {"user1"}*/ }; +static int num_users = 2; + +static esp_err_t auth_methods_get_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + cJSON *json = cJSON_CreateObject(); + cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() ); + char *str = cJSON_PrintUnformatted(json); + httpd_resp_sendstr(req, str); + free(str); + cJSON_Delete(json); + return ESP_OK; +} + +static esp_err_t auth_methods_post_handler(httpd_req_t *req) { + char buf[256]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados"); + return ESP_FAIL; + } + + buf[len] = '\0'; + cJSON *json = cJSON_Parse(buf); + if (!json) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido"); + return ESP_FAIL; + } + + cJSON *rfid = cJSON_GetObjectItem(json, "RFID"); + if (rfid && cJSON_IsBool(rfid)) { + auth_set_enabled(cJSON_IsTrue(rfid)); + } else { + cJSON_Delete(json); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'RFID' inválido ou ausente"); + return ESP_FAIL; + } + + cJSON_Delete(json); + httpd_resp_sendstr(req, "Métodos de autenticação atualizados"); + return ESP_OK; +} + + +static esp_err_t users_get_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + cJSON *root = cJSON_CreateObject(); + cJSON *list = cJSON_CreateArray(); + for (int i = 0; i < num_users; ++i) { + cJSON *u = cJSON_CreateObject(); + cJSON_AddStringToObject(u, "username", users[i].username); + cJSON_AddItemToArray(list, u); + } + cJSON_AddItemToObject(root, "users", list); + char *str = cJSON_Print(root); + httpd_resp_sendstr(req, str); + free(str); + cJSON_Delete(root); + return ESP_OK; +} + +static esp_err_t users_post_handler(httpd_req_t *req) { + char buf[128]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) return ESP_FAIL; + buf[len] = '\0'; + if (num_users < 10) { + strlcpy(users[num_users].username, buf, sizeof(users[num_users].username)); + num_users++; + httpd_resp_sendstr(req, "Usuário adicionado com sucesso"); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido"); + } + return ESP_OK; +} + +static esp_err_t users_delete_handler(httpd_req_t *req) { + char query[128]; + if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) { + char username[128]; + if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) { + for (int i = 0; i < num_users; i++) { + if (strcmp(users[i].username, username) == 0) { + for (int j = i; j < num_users - 1; j++) { + users[j] = users[j + 1]; + } + num_users--; + httpd_resp_sendstr(req, "Usuário removido com sucesso"); + return ESP_OK; + } + } + } + } + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado"); + return ESP_FAIL; +} + +void register_auth_handlers(httpd_handle_t server, void *ctx) { + httpd_register_uri_handler(server, &(httpd_uri_t){ + .uri = "/api/v1/config/auth-methods", + .method = HTTP_GET, + .handler = auth_methods_get_handler, + .user_ctx = ctx + }); + httpd_register_uri_handler(server, &(httpd_uri_t){ + .uri = "/api/v1/config/auth-methods", + .method = HTTP_POST, + .handler = auth_methods_post_handler, + .user_ctx = ctx + }); + httpd_register_uri_handler(server, &(httpd_uri_t){ + .uri = "/api/v1/config/users", + .method = HTTP_GET, + .handler = users_get_handler, + .user_ctx = ctx + }); + httpd_register_uri_handler(server, &(httpd_uri_t){ + .uri = "/api/v1/config/users", + .method = HTTP_POST, + .handler = users_post_handler, + .user_ctx = ctx + }); + httpd_register_uri_handler(server, &(httpd_uri_t){ + .uri = "/api/v1/config/users", + .method = HTTP_DELETE, + .handler = users_delete_handler, + .user_ctx = ctx + }); +} + +// === Fim de: components/rest_api/src/auth_api.c === + + +// === Início de: components/rest_api/src/loadbalancing_settings_api.c === +#include "loadbalancing_settings_api.h" +#include "loadbalancer.h" +#include "esp_log.h" +#include "cJSON.h" + +static const char *TAG = "loadbalancing_settings_api"; + +// GET Handler: Retorna configurações atuais de load balancing +static esp_err_t loadbalancing_config_get_handler(httpd_req_t *req) { + bool enabled = loadbalancer_is_enabled(); + uint8_t currentLimit = load_balancing_get_max_grid_current(); + + ESP_LOGI(TAG, "Fetching load balancing settings: enabled = %d, currentLimit = %u", enabled, currentLimit); + + httpd_resp_set_type(req, "application/json"); + + cJSON *config = cJSON_CreateObject(); + cJSON_AddBoolToObject(config, "loadBalancingEnabled", enabled); + cJSON_AddNumberToObject(config, "loadBalancingCurrentLimit", currentLimit); + + const char *json_str = cJSON_Print(config); + httpd_resp_sendstr(req, json_str); + + ESP_LOGI(TAG, "Returned config: %s", json_str); + + free((void *)json_str); + cJSON_Delete(config); + return ESP_OK; +} + +// POST Handler: Atualiza configurações de load balancing +static esp_err_t loadbalancing_config_post_handler(httpd_req_t *req) { + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + + if (len <= 0) { + ESP_LOGE(TAG, "Received empty POST body"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); + return ESP_FAIL; + } + + buf[len] = '\0'; + ESP_LOGI(TAG, "Received POST data: %s", buf); + + cJSON *json = cJSON_Parse(buf); + if (!json) { + ESP_LOGE(TAG, "Invalid JSON"); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); + return ESP_FAIL; + } + + // Atualizar estado habilitado + cJSON *enabled_item = cJSON_GetObjectItem(json, "loadBalancingEnabled"); + if (enabled_item && cJSON_IsBool(enabled_item)) { + bool isEnabled = cJSON_IsTrue(enabled_item); + loadbalancer_set_enabled(isEnabled); + ESP_LOGI(TAG, "Updated loadBalancingEnabled to: %d", isEnabled); + } + + // Atualizar limite de corrente + cJSON *limit_item = cJSON_GetObjectItem(json, "loadBalancingCurrentLimit"); + if (limit_item && cJSON_IsNumber(limit_item)) { + uint8_t currentLimit = (uint8_t)limit_item->valuedouble; + + // Validar intervalo + if (currentLimit < 6 || currentLimit > 100) { + ESP_LOGW(TAG, "Rejected invalid currentLimit: %d", currentLimit); + cJSON_Delete(json); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid currentLimit (must be 6-100)"); + return ESP_FAIL; + } + + esp_err_t err = load_balancing_set_max_grid_current(currentLimit); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to save currentLimit: %s", esp_err_to_name(err)); + cJSON_Delete(json); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save setting"); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Updated loadBalancingCurrentLimit to: %d", currentLimit); + } + + cJSON_Delete(json); + httpd_resp_sendstr(req, "Load balancing settings updated successfully"); + return ESP_OK; +} + +// Registro dos handlers na API HTTP +void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx) { + // GET + httpd_uri_t get_uri = { + .uri = "/api/v1/config/loadbalancing", + .method = HTTP_GET, + .handler = loadbalancing_config_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &get_uri); + + // POST + httpd_uri_t post_uri = { + .uri = "/api/v1/config/loadbalancing", + .method = HTTP_POST, + .handler = loadbalancing_config_post_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &post_uri); +} + +// === Fim de: components/rest_api/src/loadbalancing_settings_api.c === + + +// === Início de: components/rest_api/src/evse_settings_api.c === +// ========================= +// evse_settings_api.c +// ========================= +#include "evse_settings_api.h" +#include "evse_api.h" +#include "evse_config.h" +#include "esp_log.h" +#include "cJSON.h" + +static const char *TAG = "evse_settings_api"; + +static esp_err_t config_settings_get_handler(httpd_req_t *req) { + httpd_resp_set_type(req, "application/json"); + cJSON *config = cJSON_CreateObject(); + cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current()); + cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold()); + const char *json_str = cJSON_Print(config); + httpd_resp_sendstr(req, json_str); + free((void *)json_str); + cJSON_Delete(config); + return ESP_OK; +} + +static esp_err_t config_settings_post_handler(httpd_req_t *req) { + char buf[512]; + int len = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (len <= 0) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); + return ESP_FAIL; + } + buf[len] = '\0'; + cJSON *json = cJSON_Parse(buf); + if (!json) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); + return ESP_FAIL; + } + + cJSON *current = cJSON_GetObjectItem(json, "currentLimit"); + if (current) evse_set_max_charging_current(current->valueint); + cJSON *temp = cJSON_GetObjectItem(json, "temperatureLimit"); + if (temp) evse_set_temp_threshold(temp->valueint); + + cJSON_Delete(json); + httpd_resp_sendstr(req, "Configurações atualizadas com sucesso"); + return ESP_OK; +} + +void register_evse_settings_handlers(httpd_handle_t server, void *ctx) { + httpd_uri_t get_uri = { + .uri = "/api/v1/config/settings", + .method = HTTP_GET, + .handler = config_settings_get_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &get_uri); + + httpd_uri_t post_uri = { + .uri = "/api/v1/config/settings", + .method = HTTP_POST, + .handler = config_settings_post_handler, + .user_ctx = ctx + }; + httpd_register_uri_handler(server, &post_uri); +} + +// === Fim de: components/rest_api/src/evse_settings_api.c === + + +// === Início de: components/rest_api/include/dashboard_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra o handler da dashboard (status geral do sistema) + */ +void register_dashboard_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/dashboard_api.h === + + +// === Início de: components/rest_api/include/static_file_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra o handler para servir arquivos estáticos da web (SPA) + */ +void register_static_file_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/static_file_api.h === + + +// === Início de: components/rest_api/include/network_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra os handlers de configuração Wi-Fi e MQTT + */ +void register_network_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/network_api.h === + + +// === Início de: components/rest_api/include/auth_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra os handlers de autenticação e gerenciamento de usuários + */ +void register_auth_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/auth_api.h === + + +// === Início de: components/rest_api/include/loadbalancing_settings_api.h === +// ========================= +// loadbalancing_settings_api.h +// ========================= + +#ifndef LOADBALANCING_SETTINGS_API_H +#define LOADBALANCING_SETTINGS_API_H + +#include "esp_err.h" +#include "esp_http_server.h" + +// Função para registrar os manipuladores de URI para as configurações de load balancing e solar +void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx); + +#endif // LOADBALANCING_SETTINGS_API_H + +// === Fim de: components/rest_api/include/loadbalancing_settings_api.h === + + +// === Início de: components/rest_api/include/rest_main.h === +#pragma once + +#include +#include + +#define SCRATCH_BUFSIZE (10240) + +typedef struct rest_server_context { + char base_path[ESP_VFS_PATH_MAX + 1]; + char scratch[SCRATCH_BUFSIZE]; +} rest_server_context_t; + +esp_err_t rest_server_init(const char *base_path); + +// === Fim de: components/rest_api/include/rest_main.h === + + +// === Início de: components/rest_api/include/meters_settings_api.h === +// ========================= +// meters_settings_api.h +// ========================= + +#ifndef METERS_SETTINGS_API_H +#define METERS_SETTINGS_API_H + +#include "esp_err.h" +#include "esp_http_server.h" + +// Função para registrar os manipuladores de URI para as configurações dos contadores +void register_meters_settings_handlers(httpd_handle_t server, void *ctx); + +#endif // METERS_SETTINGS_API_H + +// === Fim de: components/rest_api/include/meters_settings_api.h === + + +// === Início de: components/rest_api/include/ocpp_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra os handlers da configuração e status do OCPP + */ +void register_ocpp_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/ocpp_api.h === + + +// === Início de: components/rest_api/include/evse_settings_api.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" + +/** + * @brief Registra os handlers de configuração elétrica e limites de carregamento + */ +void register_evse_settings_handlers(httpd_handle_t server, void *ctx); + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/rest_api/include/evse_settings_api.h === + + +// === Início de: components/auth/src/auth_events.c === +#include "auth_events.h" + +ESP_EVENT_DEFINE_BASE(AUTH_EVENTS); + +// === Fim de: components/auth/src/auth_events.c === + + +// === Início de: components/auth/src/wiegand.c === +/** + * @file wiegand.c + * + * ESP-IDF Wiegand protocol receiver + */ +#include +#include +#include +#include +#include "wiegand.h" + +static const char *TAG = "wiegand"; + +#define TIMER_INTERVAL_US 50000 // 50ms + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } while (0) + +static void isr_disable(wiegand_reader_t *reader) +{ + gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_DISABLE); + gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_DISABLE); +} + +static void isr_enable(wiegand_reader_t *reader) +{ + gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_NEGEDGE); + gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE); +} + +#if HELPER_TARGET_IS_ESP32 +static void IRAM_ATTR isr_handler(void *arg) +#else +static void isr_handler(void *arg) +#endif +{ + wiegand_reader_t *reader = (wiegand_reader_t *)arg; + if (!reader->enabled) + return; + + int d0 = gpio_get_level(reader->gpio_d0); + int d1 = gpio_get_level(reader->gpio_d1); + + // ignore equal + if (d0 == d1) + return; + // overflow + if (reader->bits >= reader->size * 8) + return; + + esp_timer_stop(reader->timer); + + uint8_t value; + if (reader->bit_order == WIEGAND_MSB_FIRST) + value = (d0 ? 0x80 : 0) >> (reader->bits % 8); else - { - printf("lm75a_set_int: %d\n", en); - // deinit_os_gpio(); - buf[0] = 0x1; - buf[1] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - } + value = (d0 ? 1 : 0) << (reader->bits % 8); + + if (reader->byte_order == WIEGAND_MSB_FIRST) + reader->buf[reader->size - reader->bits / 8 - 1] |= value; + else + reader->buf[reader->bits / 8] |= value; + + reader->bits++; + + esp_timer_start_once(reader->timer, TIMER_INTERVAL_US); } -void lm75a_get_osio() +static void timer_handler(void *arg) { - // printf("os_io: %d\n", gpio_get_level(GPIO_INPUT_IO_0)); + wiegand_reader_t *reader = (wiegand_reader_t *)arg; + + ESP_LOGI(TAG, "Got %d bits of data", reader->bits); + + wiegand_reader_disable(reader); + + if (reader->callback) + reader->callback(reader); + + wiegand_reader_enable(reader); + + isr_enable(reader); } -// === Fim de: components/peripherals/src/lm75a.c === +//////////////////////////////////////////////////////////////////////////////// + +esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, + bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, + wiegand_order_t byte_order) +{ + CHECK_ARG(reader && buf_size && callback); + + /* + esp_err_t res = gpio_install_isr_service(0); + if (res != ESP_OK && res != ESP_ERR_INVALID_STATE) + return res; + */ + + memset(reader, 0, sizeof(wiegand_reader_t)); + reader->gpio_d0 = gpio_d0; + reader->gpio_d1 = gpio_d1; + reader->size = buf_size; + reader->buf = calloc(buf_size, 1); + reader->bit_order = bit_order; + reader->byte_order = byte_order; + reader->callback = callback; + + esp_timer_create_args_t timer_args = { + .name = TAG, + .arg = reader, + .callback = timer_handler, + .dispatch_method = ESP_TIMER_TASK}; + CHECK(esp_timer_create(&timer_args, &reader->timer)); + + CHECK(gpio_set_direction(gpio_d0, GPIO_MODE_INPUT)); + CHECK(gpio_set_direction(gpio_d1, GPIO_MODE_INPUT)); + CHECK(gpio_set_pull_mode(gpio_d0, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); + CHECK(gpio_set_pull_mode(gpio_d1, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); + isr_disable(reader); + CHECK(gpio_isr_handler_add(gpio_d0, isr_handler, reader)); + CHECK(gpio_isr_handler_add(gpio_d1, isr_handler, reader)); + isr_enable(reader); + reader->enabled = true; + + ESP_LOGI(TAG, "Reader initialized on D0=%d, D1=%d", gpio_d0, gpio_d1); + + return ESP_OK; +} + +esp_err_t wiegand_reader_disable(wiegand_reader_t *reader) +{ + CHECK_ARG(reader); + + isr_disable(reader); + esp_timer_stop(reader->timer); + reader->enabled = false; + + ESP_LOGI(TAG, "Reader on D0=%d, D1=%d disabled", reader->gpio_d0, reader->gpio_d1); + + return ESP_OK; +} + +esp_err_t wiegand_reader_enable(wiegand_reader_t *reader) +{ + CHECK_ARG(reader); + + reader->bits = 0; + memset(reader->buf, 0, reader->size); + + isr_enable(reader); + reader->enabled = true; + + ESP_LOGI(TAG, "Reader on D0=%d, D1=%d enabled", reader->gpio_d0, reader->gpio_d1); + + return ESP_OK; +} + +esp_err_t wiegand_reader_done(wiegand_reader_t *reader) +{ + CHECK_ARG(reader && reader->buf); + + isr_disable(reader); + CHECK(gpio_isr_handler_remove(reader->gpio_d0)); + CHECK(gpio_isr_handler_remove(reader->gpio_d1)); + esp_timer_stop(reader->timer); + CHECK(esp_timer_delete(reader->timer)); + free(reader->buf); + + ESP_LOGI(TAG, "Reader removed"); + + return ESP_OK; +} + +// === Fim de: components/auth/src/wiegand.c === -// === Início de: components/peripherals/src/onewire.c === +// === Início de: components/auth/src/auth.c === /* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. + * auth.c */ +#include "auth.h" +#include "auth_events.h" +#include "esp_event.h" +#include +#include +#include +#include +#include +#include "wiegand_reader.h" +#include "nvs_flash.h" +#include "nvs.h" + +#define MAX_TAGS 50 + +static const char *TAG = "Auth"; + +static bool enabled = false; +static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; +static int tag_count = 0; + +// =========================== +// Persistência em NVS +// =========================== + +static void load_auth_config(void) { + nvs_handle_t handle; + esp_err_t err = nvs_open("auth", NVS_READONLY, &handle); + if (err == ESP_OK) { + uint8_t val; + if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) { + enabled = val; + ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled); + } + nvs_close(handle); + } else { + ESP_LOGW(TAG, "No stored auth config found. Using default."); + } +} + +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); + nvs_commit(handle); + nvs_close(handle); + ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled); + } else { + ESP_LOGE(TAG, "Failed to save auth config."); + } +} + +// =========================== +// Internos +// =========================== + +static bool is_tag_valid(const char *tag) { + for (int i = 0; i < tag_count; i++) { + if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { + return true; + } + } + return true; + //TODO + //return false; +} + +// =========================== +// API pública +// =========================== + +void auth_set_enabled(bool value) { + enabled = value; + save_auth_config(); + ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED"); + + auth_enabled_event_data_t event = { .enabled = enabled }; + esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY); +} + +bool auth_is_enabled(void) { + return enabled; +} + +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; + + strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); + valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; + tag_count++; + ESP_LOGI(TAG, "Tag added: %s", tag); + return true; +} + +bool auth_remove_tag(const char *tag) { + for (int i = 0; i < tag_count; i++) { + if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { + for (int j = i; j < tag_count - 1; j++) { + strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); + } + tag_count--; + ESP_LOGI(TAG, "Tag removed: %s", tag); + return true; + } + } + return false; +} + +bool auth_tag_exists(const char *tag) { + return is_tag_valid(tag); +} + +void auth_list_tags(void) { + ESP_LOGI(TAG, "Registered Tags (%d):", tag_count); + for (int i = 0; i < tag_count; i++) { + ESP_LOGI(TAG, "- %s", valid_tags[i]); + } +} + +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_process_tag(const char *tag) { + if (!tag || !auth_is_enabled()) { + ESP_LOGW(TAG, "Auth disabled or NULL tag received."); + 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'; + event.authorized = is_tag_valid(tag); + + ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED"); + + esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY); +} + +// === Fim de: components/auth/src/auth.c === + + +// === Início de: components/auth/src/wiegand_reader.c === +#include #include #include #include -#include "rom/ets_sys.h" +#include +#include +#include +#include "auth.h" -#include "onewire.h" +#define CONFIG_EXAMPLE_BUF_SIZE 50 -#define ONEWIRE_SELECT_ROM 0x55 -#define ONEWIRE_SKIP_ROM 0xcc -#define ONEWIRE_SEARCH 0xf0 -#define ONEWIRE_CRC8_TABLE +static const char *TAG = "WiegandReader"; -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +static wiegand_reader_t reader; +static QueueHandle_t queue = NULL; -// Waits up to `max_wait` microseconds for the specified pin to go high. -// Returns true if successful, false if the bus never comes high (likely -// shorted). -static inline bool _onewire_wait_for_bus(gpio_num_t pin, int max_wait) -{ - bool state; - for (int i = 0; i < ((max_wait + 4) / 5); i++) { - if (gpio_get_level(pin)) - break; - ets_delay_us(5); - } - state = gpio_get_level(pin); - // Wait an extra 1us to make sure the devices have an adequate recovery - // time before we drive things low again. - ets_delay_us(1); - return state; +typedef struct { + uint8_t data[CONFIG_EXAMPLE_BUF_SIZE]; + size_t bits; +} data_packet_t; + +static void reader_callback(wiegand_reader_t *r) { + data_packet_t p; + p.bits = r->bits; + memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE); + xQueueSendToBack(queue, &p, 0); } -static void setup_pin(gpio_num_t pin, bool open_drain) -{ - gpio_set_direction(pin, open_drain ? GPIO_MODE_INPUT_OUTPUT_OD : GPIO_MODE_OUTPUT); - // gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY); -} - -// Perform the onewire reset function. We will wait up to 250uS for -// the bus to come high, if it doesn't then it is broken or shorted -// and we return false; -// -// Returns true if a device asserted a presence pulse, false otherwise. -// -bool onewire_reset(gpio_num_t pin) -{ - setup_pin(pin, true); - - gpio_set_level(pin, 1); - // wait until the wire is high... just in case - if (!_onewire_wait_for_bus(pin, 250)) - return false; - - gpio_set_level(pin, 0); - ets_delay_us(480); - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 1); // allow it to float - ets_delay_us(70); - bool r = !gpio_get_level(pin); - portEXIT_CRITICAL(&mux); - - // Wait for all devices to finish pulling the bus low before returning - if (!_onewire_wait_for_bus(pin, 410)) - return false; - - return r; -} - -static bool _onewire_write_bit(gpio_num_t pin, bool v) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return false; - - portENTER_CRITICAL(&mux); - if (v) { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(10); - gpio_set_level(pin, 1); // allow output high - ets_delay_us(55); - } else { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(65); - gpio_set_level(pin, 1); // allow output high - } - ets_delay_us(1); - portEXIT_CRITICAL(&mux); - - return true; -} - -static int _onewire_read_bit(gpio_num_t pin) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return -1; - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 0); - ets_delay_us(2); - gpio_set_level(pin, 1); // let pin float, pull up will raise - ets_delay_us(11); - int r = gpio_get_level(pin); // Must sample within 15us of start - ets_delay_us(48); - portEXIT_CRITICAL(&mux); - - return r; -} - -// Write a byte. The writing code uses open-drain mode and expects the pullup -// resistor to pull the line high when not driven low. If you need strong -// power after the write (e.g. DS18B20 in parasite power mode) then call -// onewire_power() after this is complete to actively drive the line high. -// -bool onewire_write(gpio_num_t pin, uint8_t v) -{ - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) - if (!_onewire_write_bit(pin, (bitMask & v))) - return false; - - return true; -} - -bool onewire_write_bytes(gpio_num_t pin, const uint8_t* buf, size_t count) -{ - for (size_t i = 0; i < count; i++) - if (!onewire_write(pin, buf[i])) - return false; - - return true; -} - -// Read a byte -// -int onewire_read(gpio_num_t pin) -{ - int r = 0; - - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { - int bit = _onewire_read_bit(pin); - if (bit < 0) - return -1; - else if (bit) - r |= bitMask; - } - return r; -} - -bool onewire_read_bytes(gpio_num_t pin, uint8_t* buf, size_t count) -{ - size_t i; - int b; - - for (i = 0; i < count; i++) { - b = onewire_read(pin); - if (b < 0) - return false; - buf[i] = b; - } - return true; -} - -bool onewire_select(gpio_num_t pin, onewire_addr_t addr) -{ - uint8_t i; - - if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) - return false; - - for (i = 0; i < 8; i++) { - if (!onewire_write(pin, addr & 0xff)) - return false; - addr >>= 8; +static void wiegand_task(void *arg) { + queue = xQueueCreate(5, sizeof(data_packet_t)); + if (!queue) { + ESP_LOGE(TAG, "Failed to create queue"); + vTaskDelete(NULL); + return; } - return true; -} + ESP_ERROR_CHECK(wiegand_reader_init(&reader, 21, 22, + true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); -bool onewire_skip_rom(gpio_num_t pin) -{ - return onewire_write(pin, ONEWIRE_SKIP_ROM); -} + data_packet_t p; + while (1) { + ESP_LOGI(TAG, "Waiting for Wiegand data..."); + if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) { + ESP_LOGI(TAG, "Bits received: %d", p.bits); -bool onewire_power(gpio_num_t pin) -{ - // Make sure the bus is not being held low before driving it high, or we - // may end up shorting ourselves out. - if (!_onewire_wait_for_bus(pin, 10)) - return false; + char tag[20] = {0}; - setup_pin(pin, false); - gpio_set_level(pin, 1); - - return true; -} - -void onewire_depower(gpio_num_t pin) -{ - setup_pin(pin, true); -} - -void onewire_search_start(onewire_search_t* search) -{ - // reset the search state - memset(search, 0, sizeof(*search)); -} - -void onewire_search_prefix(onewire_search_t* search, uint8_t family_code) -{ - uint8_t i; - - search->rom_no[0] = family_code; - for (i = 1; i < 8; i++) { - search->rom_no[i] = 0; - } - search->last_discrepancy = 64; - search->last_device_found = false; -} - -// Perform a search. If the next device has been successfully enumerated, its -// ROM address will be returned. If there are no devices, no further -// devices, or something horrible happens in the middle of the -// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to -// start over. -// -// --- Replaced by the one from the Dallas Semiconductor web site --- -//-------------------------------------------------------------------------- -// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing -// search state. -// Return 1 : device found, ROM number in ROM_NO buffer -// 0 : device not found, end of search -// -onewire_addr_t onewire_search_next(onewire_search_t* search, gpio_num_t pin) -{ - //TODO: add more checking for read/write errors - uint8_t id_bit_number; - uint8_t last_zero, search_result; - int rom_byte_number; - int8_t id_bit, cmp_id_bit; - onewire_addr_t addr; - unsigned char rom_byte_mask; - bool search_direction; - - // initialize for search - id_bit_number = 1; - last_zero = 0; - rom_byte_number = 0; - rom_byte_mask = 1; - search_result = 0; - - // if the last call was not the last one - if (!search->last_device_found) { - // 1-Wire reset - if (!onewire_reset(pin)) { - // reset the search - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } - - // issue the search command - onewire_write(pin, ONEWIRE_SEARCH); - - // loop to do the search - do { - // read a bit and its complement - id_bit = _onewire_read_bit(pin); - cmp_id_bit = _onewire_read_bit(pin); - - if ((id_bit == 1) && (cmp_id_bit == 1)) - break; - else { - // all devices coupled have 0 or 1 - if (id_bit != cmp_id_bit) - search_direction = id_bit; // bit write value for search - else { - // if this discrepancy if before the Last Discrepancy - // on a previous next then pick the same as last time - if (id_bit_number < search->last_discrepancy) - search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0); - else - // if equal to last pick 1, if not then pick 0 - search_direction = (id_bit_number == search->last_discrepancy); - - // if 0 was picked then record its position in LastZero - if (!search_direction) - last_zero = id_bit_number; - } - - // set or clear the bit in the ROM byte rom_byte_number - // with mask rom_byte_mask - if (search_direction) - search->rom_no[rom_byte_number] |= rom_byte_mask; - else - search->rom_no[rom_byte_number] &= ~rom_byte_mask; - - // serial number search direction write bit - _onewire_write_bit(pin, search_direction); - - // increment the byte counter id_bit_number - // and shift the mask rom_byte_mask - id_bit_number++; - rom_byte_mask <<= 1; - - // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask - if (rom_byte_mask == 0) { - rom_byte_number++; - rom_byte_mask = 1; - } + if (p.bits == 26) { + snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]); + } else if (p.bits == 34) { + snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]); + } else { + ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits); + continue; } - } while (rom_byte_number < 8); // loop until through all ROM bytes 0-7 - // if the search was successful then - if (!(id_bit_number < 65)) { - // search successful so set last_discrepancy,last_device_found,search_result - search->last_discrepancy = last_zero; - - // check for last device - if (search->last_discrepancy == 0) - search->last_device_found = true; - - search_result = 1; + ESP_LOGI(TAG, "Tag read: %s", tag); + auth_process_tag(tag); // agora delega toda a lógica à auth.c } } - - // if no device found then reset counters so next 'search' will be like a first - if (!search_result || !search->rom_no[0]) { - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } else { - addr = 0; - for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) { - addr = (addr << 8) | search->rom_no[rom_byte_number]; - } - //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr); - } - return addr; } -// The 1-Wire CRC scheme is described in Maxim Application Note 27: -// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" -// - -#ifdef ONEWIRE_CRC8_TABLE -// This table comes from Dallas sample code where it is freely reusable, -// though Copyright (c) 2000 Dallas Semiconductor Corporation -static const uint8_t dscrc_table[] = { - 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, - 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, - 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, - 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, - 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, - 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, - 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, - 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, - 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, - 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, - 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, - 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, - 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, - 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, - 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, - 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 -}; - -// -// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM -// and the registers. (note: this might better be done without to -// table, it would probably be smaller and certainly fast enough -// compared to all those delayMicrosecond() calls. But I got -// confused, so I use this table from the examples.) -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - crc = dscrc_table[crc ^ *data++]; - - return crc; -} -#else -// -// Compute a Dallas Semiconductor 8 bit CRC directly. -// this is much slower, but much smaller, than the lookup table. -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - { - uint8_t inbyte = *data++; - for (int i = 8; i; i--) - { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) - crc ^= 0x8C; - inbyte >>= 1; - } - } - return crc; -} -#endif /* ONEWIRE_CRC8_TABLE */ - -// Compute the 1-Wire CRC16 and compare it against the received CRC. -// Example usage (reading a DS2408): -// // Put everything in a buffer so we can compute the CRC easily. -// uint8_t buf[13]; -// buf[0] = 0xF0; // Read PIO Registers -// buf[1] = 0x88; // LSB address -// buf[2] = 0x00; // MSB address -// WriteBytes(net, buf, 3); // Write 3 cmd bytes -// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 -// if (!CheckCRC16(buf, 11, &buf[11])) { -// // Handle error. -// } -// -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param inverted_crc - The two CRC16 bytes in the received data. -// This should just point into the received data, -// *not* at a 16-bit integer. -// @param crc - The crc starting value (optional) -// @return 1, iff the CRC matches. -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) -{ - uint16_t crc = ~onewire_crc16(input, len, crc_iv); - return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +void initWiegand(void) { + ESP_LOGI(TAG, "Initializing Wiegand reader"); + xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL); } -// Compute a Dallas Semiconductor 16 bit CRC. This is required to check -// the integrity of data received from many 1-Wire devices. Note that the -// CRC computed here is *not* what you'll get from the 1-Wire network, -// for two reasons: -// 1) The CRC is transmitted bitwise inverted. -// 2) Depending on the endian-ness of your processor, the binary -// representation of the two-byte return value may have a different -// byte order than the two bytes you get from 1-Wire. -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param crc - The crc starting value (optional) -// @return The CRC16, as defined by Dallas Semiconductor. -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) -{ - uint16_t crc = crc_iv; - static const uint8_t oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; - - uint16_t i; - for (i = 0; i < len; i++) { - // Even though we're just copying a byte from the input, - // we'll be doing 16-bit computation with it. - uint16_t cdata = input[i]; - cdata = (cdata ^ crc) & 0xff; - crc >>= 8; - - if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) - crc ^= 0xC001; - - cdata <<= 6; - crc ^= cdata; - cdata <<= 1; - crc ^= cdata; - } - return crc; -} -// === Fim de: components/peripherals/src/onewire.c === +// === Fim de: components/auth/src/wiegand_reader.c === -// === Início de: components/peripherals/src/onewire.h === -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. - */ - -#ifndef ONEWIRE_H_ -#define ONEWIRE_H_ +// === Início de: components/auth/include/auth.h === +#ifndef AUTH_H +#define AUTH_H #include -#include -#include "driver/gpio.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Tamanho máximo de uma tag RFID (incluindo '\0') +#define AUTH_TAG_MAX_LEN 20 + +/// Estrutura de evento emitida após leitura de uma tag +typedef struct { + char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida + bool authorized; ///< true se a tag for reconhecida como válida +} auth_event_t; /** - * Type used to hold all 1-Wire device ROM addresses (64-bit) + * @brief Inicializa o sistema de autenticação. + * + * - Carrega a configuração (enabled) da NVS + * - Inicia o leitor Wiegand + * - Emite evento AUTH_EVENT_INIT com estado atual */ -typedef uint64_t onewire_addr_t; +void auth_init(void); /** - * Structure to contain the current state for onewire_search_next(), etc + * @brief Ativa ou desativa o uso de autenticação via RFID. + * + * Esta configuração é persistida em NVS. Se desativado, o sistema + * considerará todas as autorizações como aceitas. + * + * @param value true para ativar, false para desativar */ -typedef struct -{ - uint8_t rom_no[8]; - uint8_t last_discrepancy; - bool last_device_found; -} onewire_search_t; +void auth_set_enabled(bool value); /** - * ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device - * (CRC mismatch), and so can be useful as an indicator for "no-such-device", - * etc. + * @brief Verifica se o sistema de autenticação está habilitado. */ -#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) +bool auth_is_enabled(void); /** - * @brief Perform a 1-Wire reset cycle. + * @brief Adiciona uma nova tag RFID à lista de autorizadas. * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if at least one device responds with a presence pulse, - * `false` if no devices were detected (or the bus is shorted, etc) + * @param tag String da tag (máx AUTH_TAG_MAX_LEN-1) + * @return true se a tag foi adicionada, false se já existia ou inválida */ -bool onewire_reset(gpio_num_t pin); +bool auth_add_tag(const char *tag); /** - * @brief Issue a 1-Wire "ROM select" command to select a particular device. + * @brief Remove uma tag previamente cadastrada. * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param addr The ROM address of the device to select - * - * @return `true` if the "ROM select" command could be successfully issued, - * `false` if there was an error. + * @param tag String da tag + * @return true se foi removida, false se não encontrada */ -bool onewire_select(gpio_num_t pin, const onewire_addr_t addr); +bool auth_remove_tag(const char *tag); /** - * @brief Issue a 1-Wire "skip ROM" command to select *all* devices on the bus. - * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if the "skip ROM" command could be successfully issued, - * `false` if there was an error. + * @brief Verifica se uma tag já está registrada como válida. */ -bool onewire_skip_rom(gpio_num_t pin); +bool auth_tag_exists(const char *tag); /** - * @brief Write a byte on the onewire bus. - * - * The writing code uses open-drain mode and expects the pullup resistor to - * pull the line high when not driven low. If you need strong power after the - * write (e.g. DS18B20 in parasite power mode) then call ::onewire_power() - * after this is complete to actively drive the line high. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param v The byte value to write - * - * @return `true` if successful, `false` on error. + * @brief Lista todas as tags válidas atualmente registradas (via logs). */ -bool onewire_write(gpio_num_t pin, uint8_t v); +void auth_list_tags(void); /** - * @brief Write multiple bytes on the 1-Wire bus. + * @brief Processa uma tag RFID lida (chamada normalmente pelo leitor). * - * See ::onewire_write() for more info. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param buf A pointer to the buffer of bytes to be written - * @param count Number of bytes to write - * - * @return `true` if all bytes written successfully, `false` on error. + * - Verifica validade + * - Emite evento AUTH_EVENT_TAG_PROCESSED + * - Inicia timer de expiração se autorizada */ -bool onewire_write_bytes(gpio_num_t pin, const uint8_t *buf, size_t count); - -/** - * @brief Read a byte from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return the read byte on success, negative value on error. - */ -int onewire_read(gpio_num_t pin); - -/** - * @brief Read multiple bytes from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param[out] buf A pointer to the buffer to contain the read bytes - * @param count Number of bytes to read - * - * @return `true` on success, `false` on error. - */ -bool onewire_read_bytes(gpio_num_t pin, uint8_t *buf, size_t count); - -/** - * @brief Actively drive the bus high to provide extra power for certain - * operations of parasitically-powered devices. - * - * For parasitically-powered devices which need more power than can be - * provided via the normal pull-up resistor, it may be necessary for some - * operations to drive the bus actively high. This function can be used to - * perform that operation. - * - * The bus can be depowered once it is no longer needed by calling - * ::onewire_depower(), or it will be depowered automatically the next time - * ::onewire_reset() is called to start another command. - * - * @note Make sure the device(s) you are powering will not pull more current - * than the ESP32/ESP8266 is able to supply via its GPIO pins (this is - * especially important when multiple devices are on the same bus and - * they are all performing a power-intensive operation at the same time - * (i.e. multiple DS18B20 sensors, which have all been given a - * "convert T" operation by using ::onewire_skip_rom())). - * - * @note This routine will check to make sure that the bus is already high - * before driving it, to make sure it doesn't attempt to drive it high - * while something else is pulling it low (which could cause a reset or - * damage the ESP32/ESP8266). - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` on success, `false` on error. - */ -bool onewire_power(gpio_num_t pin); - -/** - * @brief Stop forcing power onto the bus. - * - * You only need to do this if you previously called ::onewire_power() to drive - * the bus high and now want to allow it to float instead. Note that - * onewire_reset() will also automatically depower the bus first, so you do - * not need to call this first if you just want to start a new operation. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - */ -void onewire_depower(gpio_num_t pin); - -/** - * @brief Clear the search state so that it will start from the beginning on - * the next call to ::onewire_search_next(). - * - * @param[out] search The onewire_search_t structure to reset. - */ -void onewire_search_start(onewire_search_t *search); - -/** - * @brief Setup the search to search for devices with the specified - * "family code". - * - * @param[out] search The onewire_search_t structure to update. - * @param family_code The "family code" to search for. - */ -void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); - -/** - * @brief Search for the next device on the bus. - * - * The order of returned device addresses is deterministic. You will always - * get the same devices in the same order. - * - * @note It might be a good idea to check the CRC to make sure you didn't get - * garbage. - * - * @return the address of the next device on the bus, or ::ONEWIRE_NONE if - * there is no next address. ::ONEWIRE_NONE might also mean that - * the bus is shorted, there are no devices, or you have already - * retrieved all of them. - */ -onewire_addr_t onewire_search_next(onewire_search_t *search, gpio_num_t pin); - -/** - * @brief Compute a Dallas Semiconductor 8 bit CRC. - * - * These are used in the ROM address and scratchpad registers to verify the - * transmitted data is correct. - */ -uint8_t onewire_crc8(const uint8_t *data, uint8_t len); - -/** - * @brief Compute the 1-Wire CRC16 and compare it against the received CRC. - * - * Example usage (reading a DS2408): - * @code{.c} - * // Put everything in a buffer so we can compute the CRC easily. - * uint8_t buf[13]; - * buf[0] = 0xF0; // Read PIO Registers - * buf[1] = 0x88; // LSB address - * buf[2] = 0x00; // MSB address - * onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes - * onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 - * if (!onewire_check_crc16(buf, 11, &buf[11])) { - * // TODO: Handle error. - * } - * @endcode - * - * @param input Array of bytes to checksum. - * @param len Number of bytes in `input` - * @param inverted_crc The two CRC16 bytes in the received data. - * This should just point into the received data, - * *not* at a 16-bit integer. - * @param crc_iv The crc starting value (optional) - * - * @return `true` if the CRC matches, `false` otherwise. - */ -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); - -/** - * @brief Compute a Dallas Semiconductor 16 bit CRC. - * - * This is required to check the integrity of data received from many 1-Wire - * devices. Note that the CRC computed here is *not* what you'll get from the - * 1-Wire network, for two reasons: - * - * 1. The CRC is transmitted bitwise inverted. - * 2. Depending on the endian-ness of your processor, the binary - * representation of the two-byte return value may have a different - * byte order than the two bytes you get from 1-Wire. - * - * @param input Array of bytes to checksum. - * @param len How many bytes are in `input`. - * @param crc_iv The crc starting value (optional) - * - * @return the CRC16, as defined by Dallas Semiconductor. - */ -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); +void auth_process_tag(const char *tag); -#endif /* ONEWIRE_H_ */ -// === Fim de: components/peripherals/src/onewire.h === +#ifdef __cplusplus +} +#endif + +#endif // AUTH_H + +// === Fim de: components/auth/include/auth.h === -// === Início de: components/peripherals/src/ds18x20.c === +// === Início de: components/auth/include/auth_events.h === +#pragma once +#include "esp_event.h" + +#define AUTH_EVENT_TAG_MAX_LEN 32 + +ESP_EVENT_DECLARE_BASE(AUTH_EVENTS); + +typedef enum { + AUTH_EVENT_TAG_PROCESSED, + AUTH_EVENT_ENABLED_CHANGED, + AUTH_EVENT_INIT, +} auth_event_id_t; + +typedef struct { + char tag[AUTH_EVENT_TAG_MAX_LEN]; + bool authorized; +} auth_tag_event_data_t; + +typedef struct { + bool enabled; +} auth_enabled_event_data_t; + +// === Fim de: components/auth/include/auth_events.h === + + +// === Início de: components/auth/include/wiegand.h === /* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss + * Copyright (c) 2021 Ruslan V. Uss * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -2470,765 +2081,127 @@ uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include -#include -#include -#include -#include "ds18x20.h" - -#define ds18x20_WRITE_SCRATCHPAD 0x4E -#define ds18x20_READ_SCRATCHPAD 0xBE -#define ds18x20_COPY_SCRATCHPAD 0x48 -#define ds18x20_READ_EEPROM 0xB8 -#define ds18x20_READ_PWRSUPPLY 0xB4 -#define ds18x20_SEARCHROM 0xF0 -#define ds18x20_SKIP_ROM 0xCC -#define ds18x20_READROM 0x33 -#define ds18x20_MATCHROM 0x55 -#define ds18x20_ALARMSEARCH 0xEC -#define ds18x20_CONVERT_T 0x44 - -#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) -#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) - -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - -static const char* TAG = "ds18x20"; - -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_CONVERT_T); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - if (wait){ - vTaskDelay(pdMS_TO_TICKS(750)); - onewire_depower(pin); - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - uint8_t crc; - uint8_t expected_crc; - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_READ_SCRATCHPAD); - - for (int i = 0; i < 8; i++) - buffer[i] = onewire_read(pin); - crc = onewire_read(pin); - - expected_crc = onewire_crc8(buffer, 8); - if (crc != expected_crc) - { - ESP_LOGE(TAG, "CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)", buffer[0], buffer[1], - buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); - return ESP_ERR_INVALID_CRC; - } - - return ESP_OK; -} - -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_WRITE_SCRATCHPAD); - - for (int i = 0; i < 3; i++) - onewire_write(pin, buffer[i]); - - return ESP_OK; -} - -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_COPY_SCRATCHPAD); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - // And then it needs to keep that power up for 10ms. - vTaskDelay(pdMS_TO_TICKS(10)); - onewire_depower(pin); - - return ESP_OK; -} - -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - - *temperature = ((int16_t)temp * 625.0) / 100; - - return ESP_OK; -} - -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; - - *temperature = (temp * 625) / 100 - 25; - - return ESP_OK; -} - -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - if ((uint8_t)addr == DS18B20_FAMILY_ID) { - return ds18b20_read_temperature(pin, addr, temperature); - } else { - return ds18s20_read_temperature(pin, addr, temperature); - } -} - -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18b20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18s20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18x20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list && addr_count); - - CHECK(ds18x20_measure(pin, DS18X20_ANY, true)); - - return ds18x20_read_temp_multi(pin, addr_list, addr_count, result_list); -} - -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, size_t* found) -{ - CHECK_ARG(addr_list && addr_count); - - onewire_search_t search; - onewire_addr_t addr; - - *found = 0; - onewire_search_start(&search); - while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) - { - uint8_t family_id = (uint8_t)addr; - if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) - { - if (*found < addr_count) - addr_list[*found] = addr; - *found += 1; - } - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list); - - esp_err_t res = ESP_OK; - for (size_t i = 0; i < addr_count; i++) - { - esp_err_t tmp = ds18x20_read_temperature(pin, addr_list[i], &result_list[i]); - if (tmp != ESP_OK) - res = tmp; - } - return res; -} - -// === Fim de: components/peripherals/src/ds18x20.c === - - -// === Início de: components/peripherals/src/led.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "led.h" -#include "board_config.h" -#include "evse_error.h" -#include "evse_api.h" - -#define LED_UPDATE_INTERVAL_MS 100 -#define BLOCK_TIME pdMS_TO_TICKS(10) - -static const char *TAG = "led"; - -typedef struct { - gpio_num_t gpio; - bool on : 1; - uint16_t ontime; - uint16_t offtime; - TimerHandle_t timer; - led_pattern_t pattern; - uint8_t blink_count; -} led_t; - -static led_t leds[LED_ID_MAX] = {0}; -static TimerHandle_t led_update_timer = NULL; -static evse_state_t led_state = -1; - -// ---------------------------- -// Funções Internas -// ---------------------------- - -static void led_update_timer_callback(TimerHandle_t xTimer); -static void led_update(void); -static void led_apply_by_state(evse_state_t state); - -static inline void led_gpio_write(gpio_num_t gpio, bool level) { - if (gpio != GPIO_NUM_NC) - gpio_set_level(gpio, level); -} - -static void led_timer_callback(TimerHandle_t xTimer) -{ - led_t *led = (led_t *)pvTimerGetTimerID(xTimer); - led->on = !led->on; - led_gpio_write(led->gpio, led->on); - uint32_t next_time = led->on ? led->ontime : led->offtime; - - xTimerChangePeriod(led->timer, pdMS_TO_TICKS(next_time), BLOCK_TIME); -} - -// ---------------------------- -// Inicialização -// ---------------------------- - -void led_init(void) -{ - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .intr_type = GPIO_INTR_DISABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_ENABLE, - .pin_bit_mask = 0 - }; - - for (int i = 0; i < LED_ID_MAX; i++) { - leds[i].gpio = GPIO_NUM_NC; - } - - if (board_config.led_stop) { - leds[LED_ID_STOP].gpio = board_config.led_stop_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_stop_gpio); - } - - if (board_config.led_charging) { - leds[LED_ID_CHARGING].gpio = board_config.led_charging_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_charging_gpio); - } - - if (board_config.led_error) { - leds[LED_ID_ERROR].gpio = board_config.led_error_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_error_gpio); - } - - if (io_conf.pin_bit_mask != 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - if (!led_update_timer) { - led_update_timer = xTimerCreate("led_update_timer", - pdMS_TO_TICKS(LED_UPDATE_INTERVAL_MS), - pdTRUE, NULL, - led_update_timer_callback); - if (led_update_timer) { - xTimerStart(led_update_timer, BLOCK_TIME); - } else { - ESP_LOGE(TAG, "Failed to create LED update timer"); - } - } -} - -// ---------------------------- -// API Pública -// ---------------------------- - -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime) -{ - if (led_id >= LED_ID_MAX) return; - - led_t *led = &leds[led_id]; - if (led->gpio == GPIO_NUM_NC) return; - - // Evita reconfiguração idêntica - if (led->ontime == ontime && led->offtime == offtime) - return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->ontime = ontime; - led->offtime = offtime; - - if (ontime == 0) { - led->on = false; - led_gpio_write(led->gpio, 0); - } else if (offtime == 0) { - led->on = true; - led_gpio_write(led->gpio, 1); - } else { - led->on = true; - led_gpio_write(led->gpio, 1); - - if (!led->timer) { - led->timer = xTimerCreate("led_timer", pdMS_TO_TICKS(ontime), - pdFALSE, (void *)led, led_timer_callback); - } - - if (led->timer) { - xTimerStart(led->timer, BLOCK_TIME); - } - } -} - -void led_apply_pattern(led_id_t id, led_pattern_t pattern) -{ - if (id >= LED_ID_MAX) return; - - led_t *led = &leds[id]; - if (led->gpio == GPIO_NUM_NC) return; - - if (led->pattern == pattern) return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->pattern = pattern; - led->blink_count = 0; - - switch (pattern) { - case LED_PATTERN_OFF: - led_set_state(id, 0, 0); - break; - case LED_PATTERN_ON: - led_set_state(id, 1, 0); - break; - case LED_PATTERN_BLINK: - led_set_state(id, 500, 500); - break; - case LED_PATTERN_BLINK_FAST: - led_set_state(id, 200, 200); - break; - case LED_PATTERN_BLINK_SLOW: - led_set_state(id, 300, 1700); - break; - case LED_PATTERN_CHARGING_EFFECT: - led_set_state(id, 2000, 1000); - break; - } -} - -// ---------------------------- -// Controle por Estado -// ---------------------------- - -static void led_apply_by_state(evse_state_t state) -{ - // Reset todos - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_OFF); - - switch (state) { - case EVSE_STATE_A: - led_apply_pattern(LED_ID_STOP, LED_PATTERN_ON); - break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - case EVSE_STATE_C1: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_ON); - break; - case EVSE_STATE_C2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_CHARGING_EFFECT); - break; - case EVSE_STATE_D1: - case EVSE_STATE_D2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_BLINK_FAST); - break; - case EVSE_STATE_E: - case EVSE_STATE_F: - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - break; - default: - break; - } -} - -// ---------------------------- -// Timer Update -// ---------------------------- - -static void led_update(void) -{ - if (evse_error_is_active()) { - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - return; - } - - evse_state_t current = evse_get_state(); - - if (current != led_state) { - led_state = current; - led_apply_by_state(current); - } -} - -static void led_update_timer_callback(TimerHandle_t xTimer) -{ - (void)xTimer; - led_update(); -} - -// === Fim de: components/peripherals/src/led.c === - - -// === Início de: components/peripherals/src/rcm.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "esp_log.h" - -#include "rcm.h" -#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; -// } -// } - -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)); - } -} - -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; - - 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); - - return success; -} - -bool rcm_is_triggered(void) -{ - // bool _triggered = triggered; - // if (gpio_get_level(board_config.rcm_gpio) == 0) { - // triggered = false; - // } - // return _triggered; - if (gpio_get_level(board_config.rcm_gpio) == 1) { - vTaskDelay(pdMS_TO_TICKS(1)); - return gpio_get_level(board_config.rcm_gpio) == 1; - } - - return false; -} -// === Fim de: components/peripherals/src/rcm.c === - - -// === Início de: components/peripherals/src/adc.c === -#include "adc.h" -#include "esp_log.h" - -const static char* TAG = "adc"; - -adc_oneshot_unit_handle_t adc_handle; - -adc_cali_handle_t adc_cali_handle; - -void adc_init(void) -{ - adc_oneshot_unit_init_cfg_t conf = { - .unit_id = ADC_UNIT_1 - }; - ESP_ERROR_CHECK(adc_oneshot_new_unit(&conf, &adc_handle)); - - bool calibrated = false; - -#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Curve Fitting"); - adc_cali_curve_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - if (adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } +/** + * @file wiegand.h + * @defgroup wiegand wiegand + * @{ + * + * ESP-IDF Wiegand protocol receiver + * + * Copyright (c) 2021 Ruslan V. Uss + * + * BSD Licensed as described in the file LICENSE + */ +#ifndef __WIEGAND_H__ +#define __WIEGAND_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { #endif -#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Line Fitting"); - adc_cali_line_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, -#if CONFIG_IDF_TARGET_ESP32 - .default_vref = 1100 -#endif - }; - if (adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } +typedef struct wiegand_reader wiegand_reader_t; + +typedef void (*wiegand_callback_t)(wiegand_reader_t *reader); + +/** + * Bit and byte order of data + */ +typedef enum { + WIEGAND_MSB_FIRST = 0, + WIEGAND_LSB_FIRST +} wiegand_order_t; + +/** + * Wiegand reader descriptor + */ +struct wiegand_reader +{ + gpio_num_t gpio_d0, gpio_d1; + wiegand_callback_t callback; + wiegand_order_t bit_order; + wiegand_order_t byte_order; + + uint8_t *buf; + size_t size; + size_t bits; + esp_timer_handle_t timer; + bool start_parity; + bool enabled; +}; + +/** + * @brief Create and initialize reader instance. + * + * @param reader Reader descriptor + * @param gpio_d0 GPIO pin for D0 + * @param gpio_d1 GPIO pin for D0 + * @param internal_pullups Enable internal pull-up resistors for D0 and D1 GPIO + * @param buf_size Reader buffer size in bytes, must be large enough to + * contain entire Wiegand key + * @param callback Callback function for processing received codes + * @param bit_order Bit order of data + * @param byte_order Byte order of data + * @return `ESP_OK` on success + */ +esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, + bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, + wiegand_order_t byte_order); + +/** + * @brief Disable reader + * + * While reader is disabled, it will not receive new data + * + * @param reader Reader descriptor + * @return `ESP_OK` on success + */ +esp_err_t wiegand_reader_disable(wiegand_reader_t *reader); + +/** + * @brief Enable reader + * + * @param reader Reader descriptor + * @return `ESP_OK` on success + */ +esp_err_t wiegand_reader_enable(wiegand_reader_t *reader); + +/** + * @brief Delete reader instance. + * + * @param reader Reader descriptor + * @return `ESP_OK` on success + */ +esp_err_t wiegand_reader_done(wiegand_reader_t *reader); + +#ifdef __cplusplus +} #endif - if (!calibrated) { - ESP_LOGE(TAG, "No calibration scheme"); - ESP_ERROR_CHECK(ESP_FAIL); - } +/**@}*/ + +#endif /* __WIEGAND_H__ */ + +// === Fim de: components/auth/include/wiegand.h === + + +// === Início de: components/auth/include/wiegand_reader.h === +#ifndef WIEGAND_READER_H +#define WIEGAND_READER_H + +#ifdef __cplusplus +extern "C" { +#endif + +void initWiegand(void); + +#ifdef __cplusplus } -// === Fim de: components/peripherals/src/adc.c === +#endif +#endif // WIEGAND_READER_H -// === Início de: components/peripherals/src/adc121s021_dma.c === -#include "driver/spi_master.h" -#include "esp_log.h" -#include "adc121s021_dma.h" -#include "spi_bus_manager.h" - -#define TAG "adc_dma" - -#define PIN_NUM_CS 5 -#define SAMPLE_SIZE_BYTES 2 -#define ADC_BITS 12 -#define SPI_CLOCK_HZ (6 * 1000 * 1000) // 6 MHz - -static spi_device_handle_t adc_spi = NULL; - -void adc121s021_dma_init(void) -{ - if (adc_spi) { - ESP_LOGW(TAG, "ADC121S021 já foi inicializado."); - return; - } - - if (!spi_bus_manager_is_initialized()) { - ESP_LOGI(TAG, "SPI bus não inicializado. Inicializando..."); - esp_err_t err = spi_bus_manager_init(); // 🔧 CORRIGIDO: sem argumentos - if (err != ESP_OK) { - ESP_LOGE(TAG, "Falha ao inicializar o SPI bus: %s", esp_err_to_name(err)); - return; - } - } - - spi_device_interface_config_t devcfg = { - .clock_speed_hz = SPI_CLOCK_HZ, - .mode = 0, - .spics_io_num = PIN_NUM_CS, - .queue_size = 2, - .flags = SPI_DEVICE_NO_DUMMY, - .pre_cb = NULL, - .post_cb = NULL, - }; - - esp_err_t err = spi_bus_add_device(spi_bus_manager_get_host(), &devcfg, &adc_spi); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Falha ao registrar ADC121S021 no SPI: %s", esp_err_to_name(err)); - return; - } - - ESP_LOGI(TAG, "ADC121S021 registrado no SPI com sucesso."); -} - -bool adc121s021_dma_get_sample(uint16_t *sample) -{ - if (!adc_spi) { - ESP_LOGE(TAG, "ADC SPI não inicializado!"); - return false; - } - - uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy - uint8_t rx_buffer[2] = {0}; - - spi_transaction_t t = { - .length = 16, - .tx_buffer = tx_buffer, - .rx_buffer = rx_buffer, - .flags = 0 - }; - - esp_err_t err = spi_device_transmit(adc_spi, &t); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro na transmissão SPI: %s", esp_err_to_name(err)); - return false; - } - - *sample = ((rx_buffer[0] << 8) | rx_buffer[1]) & 0x0FFF; - return true; -} - -// === Fim de: components/peripherals/src/adc121s021_dma.c === - - -// === Início de: components/peripherals/src/peripherals.c === -#include "peripherals.h" -#include "adc.h" -#include "led.h" -#include "buzzer.h" -#include "proximity.h" -#include "ac_relay.h" -#include "socket_lock.h" -#include "rcm.h" -#include "aux_io.h" -#include "ntc_sensor.h" - -void peripherals_init(void) -{ - ac_relay_init(); - led_init(); - buzzer_init(); - adc_init(); - proximity_init(); - // socket_lock_init(); - // rcm_init(); - //energy_meter_init(); - // aux_init(); - ntc_sensor_init(); -} -// === Fim de: components/peripherals/src/peripherals.c === - - -// === Início de: components/peripherals/include/adc121s021_dma.h === -#ifndef ADC_DMA_H_ -#define ADC_DMA_H_ - - -#include -#include - -void adc121s021_dma_init(void); -bool adc121s021_dma_get_sample(uint16_t *sample); - - -#endif /* ADC_DMA_h_ */ - -// === Fim de: components/peripherals/include/adc121s021_dma.h === - - -// === Início de: components/peripherals/include/peripherals.h === -#ifndef PERIPHERALS_H -#define PERIPHERALS_H - -void peripherals_init(void); - -#endif /* PERIPHERALS_H */ - -// === Fim de: components/peripherals/include/peripherals.h === +// === Fim de: components/auth/include/wiegand_reader.h === diff --git a/projeto_parte2.c b/projeto_parte2.c deleted file mode 100644 index 9c98ba1..0000000 --- a/projeto_parte2.c +++ /dev/null @@ -1,2714 +0,0 @@ - - -// === Início de: components/peripherals/include/rcm.h === -#ifndef RCM_H_ -#define RCM_H_ - -#include - -/** - * @brief Initialize residual current monitor - * - */ -void rcm_init(void); - -/** - * @brief Test residual current monitor - * - * @return true - * @return false - */ -bool rcm_test(void); - -/** - * @brief Residual current monitor was detected leakage - * - * @return true - * @return false - */ -bool rcm_is_triggered(void); - -#endif /* RCM_H_ */ - -// === Fim de: components/peripherals/include/rcm.h === - - -// === Início de: components/peripherals/include/aux_io.h === -#ifndef AUX_IO_H_ -#define AUX_IO_H_ - -#include "esp_err.h" - -/** - * @brief Initialize aux - * - */ -void aux_init(void); - -/** - * @brief Read digital input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_read(const char *name, bool *value); - -/** - * @brief Write digial output - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_write(const char *name, bool value); - -/** - * @brief Read analog input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_analog_read(const char *name, int *value); - -#endif /* AUX_IO_H_ */ -// === Fim de: components/peripherals/include/aux_io.h === - - -// === Início de: components/peripherals/include/led.h === -#ifndef LED_H_ -#define LED_H_ - -#include -#include - -/** - * @brief Identificadores dos LEDs disponíveis no hardware - */ -typedef enum { - LED_ID_STOP, - LED_ID_CHARGING, - LED_ID_ERROR, - LED_ID_MAX -} led_id_t; - -/** - * @brief Padrões de comportamento possíveis para os LEDs - */ -typedef enum { - LED_PATTERN_OFF, ///< LED sempre desligado - LED_PATTERN_ON, ///< LED sempre ligado - LED_PATTERN_BLINK, ///< Pisca com ciclo padrão (500ms on / 500ms off) - LED_PATTERN_BLINK_FAST, ///< Pisca rápido (200ms / 200ms) - LED_PATTERN_BLINK_SLOW, ///< Pisca lento (300ms / 1700ms) - LED_PATTERN_CHARGING_EFFECT ///< Efeito visual para carregamento (2s on / 1s off) -} led_pattern_t; - -/** - * @brief Inicializa os LEDs com base na configuração da placa - * Deve ser chamada uma única vez na inicialização do sistema. - */ -void led_init(void); - -/** - * @brief Define diretamente o tempo ligado/desligado de um LED. - * Pode ser usado para padrões personalizados. - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param ontime Tempo ligado em milissegundos - * @param offtime Tempo desligado em milissegundos - */ -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime); - -/** - * @brief Aplica um dos padrões de piscar definidos ao LED - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param pattern Padrão desejado (ver enum led_pattern_t) - */ -void led_apply_pattern(led_id_t led_id, led_pattern_t pattern); - -#endif /* LED_H_ */ - -// === Fim de: components/peripherals/include/led.h === - - -// === Início de: components/peripherals/include/buzzer.h === -#ifndef BUZZER_H_ -#define BUZZER_H_ - -#include - -/** - * @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_ */ - -// === Fim de: components/peripherals/include/buzzer.h === - - -// === Início de: components/peripherals/include/ac_relay.h === -#ifndef AC_RELAY_H_ -#define AC_RELAY_H_ - -#include - -/** - * @brief Inicializa o relé de corrente alternada. - */ -void ac_relay_init(void); - -/** - * @brief Define o estado do relé de corrente alternada. - * - * @param state true para ligar, false para desligar. - */ -void ac_relay_set_state(bool state); - -/** - * @brief Retorna o estado atual do relé de corrente alternada. - * - * @return true se estiver ligado, false se desligado. - */ -bool ac_relay_get_state(void); - -#endif /* AC_RELAY_H_ */ - -// === Fim de: components/peripherals/include/ac_relay.h === - - -// === Início de: components/peripherals/include/lm75a.h === -#ifndef LM75A_H -#define LM75A_H - -#include "esp_err.h" // Para o uso de tipos de erro do ESP-IDF, caso esteja utilizando. - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Inicializa o sensor LM75A. - * - * Configura o sensor para leitura e define os pinos de comunicação. - */ -esp_err_t lm75a_init(void); - -/** - * @brief Desinicializa o sensor LM75A. - * - * Libera os recursos usados pelo sensor. - */ -esp_err_t lm75a_deinit(void); - -/** - * @brief Lê a temperatura do LM75A. - * - * @param show Se for 1, a temperatura será exibida em algum tipo de log ou interface. - * Se for 0, o valor é apenas retornado sem exibição. - * @return A temperatura lida em graus Celsius. - */ -float lm75a_read_temperature(int show); - -/** - * @brief Define o valor do limite de temperatura (T_OS) para o sensor LM75A. - * - * @param tos O limite de temperatura de sobrecarga (T_OS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_tos(int tos); - -/** - * @brief Define o valor do limite de temperatura de histerese (T_HYS) para o sensor LM75A. - * - * @param thys O limite de histerese de temperatura (T_HYS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_thys(int thys); - -/** - * @brief Obtém o limite de temperatura de sobrecarga (T_OS) do sensor LM75A. - * - * @return O valor atual de T_OS em graus Celsius. - */ -int lm75a_get_tos(void); - -/** - * @brief Obtém o limite de temperatura de histerese (T_HYS) do sensor LM75A. - * - * @return O valor atual de T_HYS em graus Celsius. - */ -int lm75a_get_thys(void); - -/** - * @brief Habilita ou desabilita a interrupção do LM75A. - * - * @param en 1 para habilitar a interrupção, 0 para desabilitar. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_int(int en); - -/** - * @brief Obtém o estado do pino OS (Overtemperature Shutdown) do LM75A. - * - * @return 1 se o pino OS estiver ativo (indica que a temperatura de sobrecarga foi atingida), - * 0 caso contrário. - */ -int lm75a_get_osio(void); - -#ifdef __cplusplus -} -#endif - -#endif /* LM75A_H */ - -// === Fim de: components/peripherals/include/lm75a.h === - - -// === Início de: components/peripherals/include/ntc_sensor.h === -#ifndef NTC_SENSOR_H_ -#define NTC_SENSOR_H_ - -/** - * @brief Initialize ntc senso - * - */ -void ntc_sensor_init(void); - -/** - * @brief Return temperature after temp_sensor_measure - * - * @return float - */ -float ntc_temp_sensor(void); - -#endif /* NTC_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/ntc_sensor.h === - - -// === Início de: components/peripherals/include/proximity.h === -#ifndef PROXIMITY_H_ -#define PROXIMITY_H_ - -#include - -/** - * @brief Initialize proximity check - * - */ -void proximity_init(void); - -/** - * @brief Return measured value of max current on PP - * - * @return current in A - */ -uint8_t proximity_get_max_current(void); - -#endif /* PROXIMITY_H_ */ - -// === Fim de: components/peripherals/include/proximity.h === - - -// === Início de: components/peripherals/include/socket_lock.h === -#ifndef SOCKED_LOCK_H_ -#define SOCKED_LOCK_H_ - -#include "esp_err.h" - -typedef enum -{ - SOCKED_LOCK_STATUS_IDLE, - SOCKED_LOCK_STATUS_OPERATING, - SOCKED_LOCK_STATUS_LOCKING_FAIL, - SOCKED_LOCK_STATUS_UNLOCKING_FAIL -} socket_lock_status_t; - -/** - * @brief Initialize socket lock - * - */ -void socket_lock_init(void); - -/** - * @brief Get socket lock detection on high, stored in NVS - * - * @return true when locked has zero resistance - * @return false when unlocked has zero resistance - */ -bool socket_lock_is_detection_high(void); - -/** - * @brief Set socket lock detection on high, stored in NVS - * - * @param detection_high - */ -void socket_lock_set_detection_high(bool detection_high); - -/** - * @brief Get socket lock operating time - * - * @return time in ms - */ -uint16_t socket_lock_get_operating_time(void); - -/** - * @brief Set socket lock operating time - * - * @param operating_time - time in ms - * @return esp_err_t - */ -esp_err_t socket_lock_set_operating_time(uint16_t operating_time); - -/** - * @brief Get socket lock retry count - * - * @return retry count - */ -uint8_t socket_lock_get_retry_count(void); - -/** - * @brief Set socket lock retry count - * - * @param retry_count - */ -void socket_lock_set_retry_count(uint8_t retry_count); - -/** - * @brief Get socket lock break time - * - * @return time in ms - */ -uint16_t socket_lock_get_break_time(void); - -/** - * @brief Set socket lock break time - * - * @param break_time - * @return esp_err_t - */ -esp_err_t socket_lock_set_break_time(uint16_t break_time); - -/** - * @brief Set socke lock to locked / unlocked state - * - * @param locked - */ -void socket_lock_set_locked(bool locked); - -/** - * @brief Get socket lock status - * - * @return socket_lock_status_t - */ -socket_lock_status_t socket_lock_get_status(void); - -/** - * @brief Read the current physical lock state using the detection pin. - */ -bool socket_lock_is_locked_state(void); - - -#endif /* SOCKED_LOCK_H_ */ - -// === Fim de: components/peripherals/include/socket_lock.h === - - -// === Início de: components/peripherals/include/adc.h === -#ifndef ADC_H_ -#define ADC_H_ - -#include "esp_adc/adc_oneshot.h" -#include "esp_adc/adc_cali.h" -#include "esp_adc/adc_cali_scheme.h" - -extern adc_oneshot_unit_handle_t adc_handle; - -extern adc_cali_handle_t adc_cali_handle; - -void adc_init(void); - -#endif /* ADC_H_ */ -// === Fim de: components/peripherals/include/adc.h === - - -// === Início de: components/peripherals/include/temp_sensor.h === -#ifndef TEMP_SENSOR_H_ -#define TEMP_SENSOR_H_ - -#include -#include "esp_err.h" - -/** - * @brief Initialize DS18S20 temperature sensor bus - * - */ -void temp_sensor_init(void); - -/** - * @brief Get found sensor count - * - * @return uint8_t - */ -uint8_t temp_sensor_get_count(void); - -/** - * @brief Return lowest temperature after temp_sensor_measure - * - * @return int16_t - */ -int16_t temp_sensor_get_low(void); - -/** - * @brief Return highest temperature after temp_sensor_measure - * - * @return int - */ -int temp_sensor_get_high(void); - -/** - * @brief Return temperature sensor error - * - * @return bool - */ -bool temp_sensor_is_error(void); - -#endif /* TEMP_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/temp_sensor.h === - - -// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "esp_err.h" - -/** - * @brief Inicializa o driver do medidor ADE7758 (SPI, mutex, registradores). - */ -esp_err_t meter_ade7758_init(void); - -/** - * @brief Inicia a tarefa de leitura de dados do medidor ADE7758. - */ -esp_err_t meter_ade7758_start(void); - -/** - * @brief Para a tarefa de leitura e limpa os dados internos do medidor ADE7758. - */ -void meter_ade7758_stop(void); - - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h === - - -// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.h === -#include "driver/spi_common.h" -#include "driver/spi_master.h" - - - -#define WRITE 0x80 // WRITE bit BT7 to write to registers -#define CLKIN 10000000 // ADE7758 frec, 10.000000MHz -#define PERIODO 50 // Actually it is frequency, it is used to calculate the amount of Cycles that it accumulates for energy. -#define PHASE_A 1 -#define PHASE_B 2 -#define PHASE_C 3 - - -//Register address - -//------Name--------Address---------Lenght -#define AWATTHR 0x01 //---------16 -#define BWATTHR 0x02 //---------16 -#define CWATTHR 0x03 //---------16 - -#define AVARHR 0x04 //---------16 -#define BVARHR 0x05 //---------16 -#define CVARHR 0x06 //---------16 - -#define AVAHR 0x07 //---------16 -#define BVAHR 0x08 //---------16 -#define CVAHR 0x09 //---------16 - -#define AIRMS 0x0A //---------24 -#define BIRMS 0x0B //---------24 -#define CIRMS 0x0C //---------24 - -#define AVRMS 0x0D //---------24 -#define BVRMS 0x0E //---------24 -#define CVRMS 0x0F //---------24 - -#define FREQ 0x10 //---------12 -#define TEMP 0x11 //---------8 -#define WFORM 0x12 //---------24 -#define OPMODE 0x13 //---------8 -#define MMODE 0x14 //---------8 -#define WAVMODE 0x15 //---------8 -#define COMPMODE 0x16 //---------8 -#define LCYCMODE 0x17 //---------8 -#define MASK 0x18 //---------24 -#define STATUS 0x19 //---------24 -#define RSTATUS 0x1A //---------24 -#define ZXTOUT 0x1B //---------16 -#define LINECYC 0x1C //---------16 -#define SAGCYC 0x1D //---------8 -#define SAGLVL 0x1E //---------8 -#define VPINTLVL 0x1F //---------8 -#define IPINTLVL 0x20 //---------8 -#define VPEAK 0x21 //---------8 -#define IPEAK 0x22 //---------8 -#define GAIN 0x23 //---------8 -#define AVRMSGAIN 0x24 //---------12 -#define BVRMSGAIN 0x25 //---------12 -#define CVRMSGAIN 0x26 //---------12 -#define AIGAIN 0x27 //---------12 -#define BIGAIN 0x28 //---------12 -#define CIGAIN 0x29 //---------12 -#define AWG 0x2A //---------12 -#define BWG 0x2B //---------12 -#define CWG 0x2C //---------12 -#define AVARG 0x2D //---------12 -#define BVARG 0x2E //---------12 -#define CVARG 0x2F //---------12 -#define AVAG 0x30 //---------12 -#define BVAG 0X31 //---------12 -#define CVAG 0x32 //---------12 -#define AVRMSOS 0x33 //---------12 -#define BVRMSOS 0X34 //---------12 -#define CVRMSOS 0X35 //---------12 -#define AIRMSOS 0X36 //---------12 -#define BIRMSOS 0X37 //---------12 -#define CIRMSOS 0X38 //---------12 -#define AWATTOS 0X39 //---------12 -#define BWATTOS 0X3A //---------12 -#define CWATTOS 0X3B //---------12 -#define AVAROS 0X3C //---------12 -#define BVAROS 0X3D //---------12 -#define CVAROS 0X3E //---------12 -#define APHCAL 0X3F //---------7 -#define BPHCAL 0X40 //---------7 -#define CPHCAL 0X41 //---------7 -#define WDIV 0X42 //---------8 -#define VARDIV 0X43 //---------8 -#define VADIV 0X44 //---------8 -#define APCFNUM 0X45 //---------16 -#define APCFDEN 0X46 //---------12 -#define VARCFNUM 0X47 //---------16 -#define VARCFDEN 0X48 //---------12 - -#define CHKSUM 0X7E //---------8 -#define VERSION 0x7f //---------8 -#define DUMMY_BYTE 0xFF - - -//bits - -/** -OPERATIONAL MODE REGISTER (0x13) -The general configuration of the ADE7758 is defined by writing to the OPMODE register. -Table 18 summarizes the functionality of each bit in the OPMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set. -1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set. -2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set. -3 to 5 DISMOD 0 By setting these bits, ADE7758�s ADCs can be turned off. In normal operation, these bits should be left at Logic 0. - DISMOD[2:0] Description - 0 0 0 Normal operation. - 1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels. - 0 0 1 Switch off only the current channel ADCs. - 1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths. - 0 1 0 Switch off only the voltage channel ADCs. - 1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths. - 0 1 1 Put the ADE7758 in sleep mode. - 1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ). -6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 �s after a software reset. -7 RESERVED 0 This should be left at 0. - -*/ - -#define DISHPF 0x01 -#define DISLPF 0x02 -#define DISCF 0x04 -#define SWRST 0x40 - -/** -MEASUREMENT MODE REGISTER (0x14) -The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register. -Table 19 summarizes the functionality of each bit in the MMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency. - FREQSEL1 FREQSEL0 Source - 0 0 Phase A - 0 1 Phase B - 1 0 Phase C - 1 1 Reserved -2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers. - Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values - of the largest current and voltage waveform (over a fixed number of half-line cycles) - from Phase A. The number of half-line cycles is determined by the content of the - LINECYC register. At the end of the LINECYC number of half-line cycles, the content - of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns - on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one - bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is, - the voltage and current peak are independently processed (see the Peak Current Detection section). -5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection. - Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A. - Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C. - Note that more than one bit can be set for detection on multiple phases. - If the absolute values of the voltage or current waveform samples in the selected phases exceeds - the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the - STATUS registers are set (see the Peak Current Detection section). - -*/ - -#define FREQSEL0 0x01 -#define FREQSEL1 0x02 - - -/** -WAVEFORM MODE REGISTER (0x15) -The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register. -Table 20 summarizes the functionality of each bit in the WAVMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample. - PHSEL[1:0] Source - 0 0 Phase A - 0 1 Phase B - 1 0 Phase C - 1 1 Reserved -2 to 4 WAVSEL 0 These bits are used to select the type of waveform. - WAVSEL[2:0] Source - 0 0 0 Current - 0 0 1 Voltage - 0 1 0 Active Power Multiplier Output - 0 1 1 Reactive Power Multiplier Output - 1 0 0 VA Multiplier Output - -Others- Reserved -5 to 6 DTRT 0 These bits are used to select the data rate. - DTRT[1:0] Update Rate - 0 0 26.04 kSPS (CLKIN/3/128) - 0 1 13.02 kSPS (CLKIN/3/256) - 1 0 6.51 kSPS (CLKIN/3/512) - 1 1 3.25 kSPS (CLKIN/3/1024) -7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output - frequency that is proportional to the total apparent power (VA). - In the default state, Logic 0, the VARCF pin outputs a frequency proportional - to the total reactive power (VAR). -*/ - - - -/** -COMPUTATIONAL MODE REGISTER (0x16) -The computational method of the ADE7758 is defined by writing to the COMPMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers. - CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by �90�, respectively. - Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10 - AWATTHR VA � IA VA � (IA � IB) VA � (IA�IB) - BWATTHR VB � IB 0 0 - CWATTHR VC � IC VC � (IC � IB) VC � IC - - AVARHR VA � IA VA � (IA � IB) VA � (IA�IB) - BVARHR VB � IB 0 0 - CVARHR VC � IC VC � (IC � IB) VC � IC - - AVAHR VARMS � IARMS VARMS � IARMS VARMS � ARMS - BVAHR VBRMS � IBRMS (VARMS + VCRMS)/2 � IBRMS VARMS � IBRMS - CVAHR VCRMS � ICRMS VCRMS � ICRMS VCRMS � ICRMS - -2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs. - Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included. - Bit 3 and Bit 4 are for Phase B and Phase C, respectively. - Setting all three bits enables the sum of all three phases to be included in the frequency outputs - (see the Active Power Frequency Output and the Reactive Power Frequency Output sections). - -5 ABS 0 Setting this bit places the APCF output pin in absolute only mode. - Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour - accumulation registers (AWATTHR, BWATTHR, and CWATTHR). - Note that this bit only affects the APCF pin and has no effect on the content of the corresponding - registers. - -6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode. - Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation - registers (AVARHR, BVARHR, and CVARHR). - The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase, - that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive, - there is no change to the sign of the VAR. - Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding - registers. - -7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758. -*/ - - -/** -LINE CYCLE ACCUMULATION MODE REGISTER (0x17) -The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register. - -Bit Location Bit Mnemonic Default Value Description - -0 LWATT 0 Setting this bit places the watt-hour accumulation registers - (AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode. -1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers) - into line-cycle accumulation mode. -2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers) - into line-cycle accumulation mode. -3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle - accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively. - More than one phase can be selected for the zero-crossing detection, - and the accumulation time is shortened accordingly. -6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three - phases, that is, a read to those registers resets the registers to 0 after the content of the registers - have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1. -7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the - line input. -*/ - - -#define LWATT 0x01 -#define LVAR 0x02 -#define LVA 0x04 -#define ZXSEL_A 0x08 -#define ZXSEL_B 0x10 -#define ZXSEL_C 0x20 -#define RSTREAD 0x40 -#define FREQSEL 0x80 - - - -/** INTERRUPT MASK REGISTER (0x18) -When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register. -The IRQ logic output is reset to its default collector open state when the RSTATUS register is read. -describes the function of each bit in the interrupt mask register. -**/ - -// The next table summarizes the function of each bit for -// the Interrupt Enable Register - -/* Bit Mask // Bit Location / Description -#define AEHF 0x0001 // bit 0 - Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers, that is, the WATTHR register is half full. -#define REHF 0x0002 // bit 1 - Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers, that is, the VARHR register is half full. -#define VAEHF 0x0004 // bit 2 - Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR registers, that is, the VAHR register is half full. -#define SAGA 0x0008 // bit 3 - Enables an interrupt when there is a SAG on the line voltage of the Phase A. -#define SAGB 0x0010 // bit 4 - Enables an interrupt when there is a SAG on the line voltage of the Phase B. -#define SAGC 0x0020 // bit 5 - Enables an interrupt when there is a SAG on the line voltage of the Phase C. -#define ZXTOA 0x0040 // bit 6 - Enables an interrupt when there is a zero-crossing timeout detection on Phase A. -#define ZXTOB 0x0080 // bit 7 - Enables an interrupt when there is a zero-crossing timeout detection on Phase B. -#define ZXTOC 0x0100 // bit 8 - Enables an interrupt when there is a zero-crossing timeout detection on Phase C. -#define ZXA 0x0200 // bit 9 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase A -#define ZXB 0x0400 // bit 10 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase B -#define ZXC 0x0800 // bit 11 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase C -#define LENERGY 0x1000 // bit 12 - Enables an interrupt when the energy accumulations over LINECYC are finished. -//RESERVED 0x2000 // bit 13 - RESERVED -#define PKV 0x4000 // bit 14 - Enables an interrupt when the voltage input selected in the MMODE register is above the value in the VPINTLVL register. -#define PKI 0x8000 // bit 15 - Enables an interrupt when the current input selected in the MMODE register is above the value in the IPINTLVL register. -#define WFSM 0x010000 // bit 16 - Enables an interrupt when data is present in the WAVEMODE register. -#define REVPAP 0x020000 // bit 17 - Enables an interrupt when there is a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. -#define REVPRP 0x040000 // bit 18 - Enables an interrupt when there is a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. -#define SEQERR 0x080000 // bit 19 - Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing of Phase C but with that of Phase B. -*/ -/** INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A) -The interrupt status register is used to determine the source of an interrupt event. -When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set. -The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set. -When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt. -All the interrupts in the interrupt status register stay at their logic high state after an event occurs. -The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read. -**/ - -// The next table summarizes the function of each bit for -// the Interrupt Status Register, the Reset Interrupt Status Register. - -// Bit Mask // Bit Location / Description -#define AEHF 0x0001 // bit 0 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three WATTHR registers, that is, the WATTHR register is half full. -#define REHF 0x0002 // bit 1 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three VARHR registers, that is, the VARHR register is half full. -#define VAEHF 0x0004 // bit 2 - Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three VAHR registers, that is, the VAHR register is half full. -#define SAGA 0x0008 // bit 3 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A. -#define SAGB 0x0010 // bit 4 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B. -#define SAGC 0x0020 // bit 5 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C. -#define ZXTOA 0x0040 // bit 6 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A. -#define ZXTOB 0x0080 // bit 7 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B. -#define ZXTOC 0x0100 // bit 8 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C -#define ZXA 0x0200 // bit 9 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A. -#define ZXB 0x0400 // bit 10 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B -#define ZXC 0x0800 // bit 11 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C -#define LENERGY 0x1000 // bit 12 - In line energy accumulation, indicates the end of an integration over an integer number of half- line cycles (LINECYC). See the Calibration section. -#define RESET 0x2000 // bit 13 - Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758 and sets the registers back to their default values. This bit in the STATUS or RSTATUS register is logic high for only one clock cycle after a reset event. -#define PKV 0x4000 // bit 14 - Indicates that an interrupt was caused when the selected voltage input is above the value in the VPINTLVL register. -#define PKI 0x8000 // bit 15 - Indicates that an interrupt was caused when the selected current input is above the value in the IPINTLVL register. -#define WFSM 0x010000 // bit 16 - Indicates that new data is present in the waveform register. -#define REVPAP 0x020000 // bit 17 - Indicates that an interrupt was caused by a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. -#define REVPRP 0x040000 // bit 18 - Indicates that an interrupt was caused by a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. -#define SEQERR 0x080000 // bit 19 - Indicates that an interrupt was caused by a zero crossing from Phase A followed not by the zero crossing of Phase C but by that of Phase B. - - -//constants -#define GAIN_1 0x00 -#define GAIN_2 0x01 -#define GAIN_4 0x02 -#define INTEGRATOR_ON 1 -#define INTEGRATOR_OFF 0 -#define FULLSCALESELECT_0_5V 0x00 -#define FULLSCALESELECT_0_25V 0x01 -#define FULLSCALESELECT_0_125V 0x02 - -esp_err_t transferByte(const uint8_t reg_addr, const uint8_t data, const uint8_t command); -esp_err_t transferMultiplesBytes(const uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t data_length, const uint8_t command); - -esp_err_t Init(const spi_host_device_t spi_peripheral, const int pin_miso, const int pin_mosi, const int pin_sclk); -esp_err_t InitSpi(const int ss); - -esp_err_t RegisterDevice(const uint8_t mode, const int ss, const int addr_length, const int command_length, const int bus_speed); -uint8_t ReadRegister(const uint8_t reg_addr, const uint8_t command); -esp_err_t WriteRegister(const uint8_t reg_addr, const uint8_t reg_data, const uint8_t command); -esp_err_t WriteRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command); -esp_err_t ReadRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command); -spi_device_handle_t GetHandle(); - - -//---------------------------------------------------------------------------- -// Modes and configurations -//---------------------------------------------------------------------------- -void setOpMode(uint8_t m); -uint8_t getOpMode(); -void setMMode(uint8_t m); -uint8_t getMMode(); -void setWavMode(uint8_t m); -uint8_t getWavMode(); -void setCompMode(uint8_t m); -uint8_t getCompMode(); -void setLcycMode(uint8_t m); -uint8_t getLcycMode(); -void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1); -void setupDivs(uint8_t Watt_div,uint8_t VAR_div,uint8_t VA_div); -uint32_t getMaskInterrupts(); -void setMaskInterrupts(uint32_t m); -uint32_t getStatus(); -uint32_t resetStatus(); -int32_t getAIRMS(); -int32_t getBIRMS(); -int32_t getCIRMS(); -int32_t getAVRMS(); -int32_t getBVRMS(); -int32_t getCVRMS(); -uint32_t avrms(); -uint32_t bvrms(); -uint32_t cvrms(); -uint32_t airms(); -uint32_t birms(); -int32_t cirms(); -int32_t getFreq(); -void setLineCyc(uint32_t d); -int32_t getACurrentOffset(); -int32_t getBCurrentOffset(); -int32_t getCCurrentOffset(); -void setACurrentOffset(int32_t o); -void setBCurrentOffset(int32_t o); -void setCCurrentOffset(int32_t o); -int32_t getAVoltageOffset(); -int32_t getBVoltageOffset(); -int32_t getCVoltageOffset(); -void setAVoltageOffset(int32_t o); -void setBVoltageOffset(int32_t o); -void setCVoltageOffset(int32_t o); -void setAWattOffset(int32_t o); -void setBWattOffset(int32_t o); -void setCWattOffset(int32_t o); -void setZeroCrossingTimeout(int32_t d); -int32_t getZeroCrossingTimeout(); -uint8_t setPotLine(uint8_t Phase, uint32_t Ciclos); -int32_t getWatt(uint8_t Phase); -int32_t getVar(uint8_t Phase); -int32_t getVa(uint8_t Phase); -uint8_t getVersion(); -uint8_t read8(uint8_t reg); -uint32_t read16(uint8_t reg); -uint32_t read24(uint8_t reg); - -esp_err_t write24(uint8_t reg, uint32_t data); -esp_err_t write16(uint8_t reg, uint32_t data); -esp_err_t write8(uint8_t reg, uint8_t data); - -void enableADE7758Chip(); -void disableADE7758Chip(); -void setAPCFDEN(int32_t d); -int32_t getAPCFDEN(); -void setAPCFNUM(int32_t d); -int32_t getAPCFNUM(); -void setVARCFNUM(int32_t d); -int32_t getVARCFNUM(); -void setVARCFDEN(int32_t d); -int32_t getVARCFDEN(); -void setAWG(int32_t d); -int32_t getAWG(); -void setBWG(int32_t d); -void setCWG(int32_t d); -void setAVARG(int32_t d); -int32_t getAVARG(); -void setBVARG(int32_t d); -int32_t getBVARG(); -void setCVARG(int32_t d); -int32_t getCVARG(); -void setAVAG(int32_t d); -void setBVAG(int32_t d); -void setCVAG(int32_t d); -// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.h === - - -// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.c === -#include -#include -#include "driver/spi_master.h" -#include "sdkconfig.h" -#include "esp_log.h" -#include "ade7758.h" - -static const char *TAG = "ade7758"; - -// --- SPI internals --- -static spi_device_handle_t ade7758_spi_handle = NULL; -static spi_host_device_t spi_host = SPI2_HOST; // default - -static spi_transaction_t spi_transaction; - -// --- Configuração SPI do dispositivo --- -static const uint8_t MODE = 2; -static const uint8_t ADDR_BITS = 7; -static const uint8_t CMD_BITS = 1; -static const uint8_t SPI_WRITE = 1; -static const uint8_t SPI_READ = 0; -static const int BUS_SPEED_HZ = 1000000; - -// === Transações básicas === - -static esp_err_t transfer_byte(uint8_t reg_addr, uint8_t data, uint8_t command) { - spi_transaction.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; - spi_transaction.length = 8; - spi_transaction.cmd = command; - spi_transaction.addr = reg_addr; - spi_transaction.tx_data[0] = data; - - return spi_device_transmit(ade7758_spi_handle, &spi_transaction); -} - -static esp_err_t transfer_bytes(uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t len, uint8_t command) { - if (len < 1) len = 1; - - spi_transaction_t t = { - .flags = 0, - .length = 8 * len, - .cmd = command, - .addr = reg_addr, - .tx_buffer = tx_buf, - .rx_buffer = rx_buf - }; - - return spi_device_transmit(ade7758_spi_handle, &t); -} - -// === Interface pública === - -esp_err_t Init(spi_host_device_t host, int pin_miso, int pin_mosi, int pin_sclk) { - // Essa função não inicializa o barramento SPI - // Apenas armazena os parâmetros - spi_host = host; - return ESP_OK; -} - -esp_err_t InitSpi(int cs_gpio) { - spi_device_interface_config_t devcfg = { - .command_bits = CMD_BITS, - .address_bits = ADDR_BITS, - .mode = MODE, - .clock_speed_hz = BUS_SPEED_HZ, - .spics_io_num = cs_gpio, - .queue_size = 5, - }; - - return spi_bus_add_device(spi_host, &devcfg, &ade7758_spi_handle); -} - -spi_device_handle_t GetHandle(void) { - return ade7758_spi_handle; -} - -// === Registro de acesso === - -uint8_t ReadRegister(uint8_t reg_addr, uint8_t command) { - transfer_byte(reg_addr, 0, command); - return spi_transaction.rx_data[0]; -} - -esp_err_t WriteRegister(uint8_t reg_addr, uint8_t data, uint8_t command) { - return transfer_byte(reg_addr, data, command); -} - -esp_err_t WriteRegisterMultipleBytes(uint8_t reg, uint8_t *data, uint8_t count, uint8_t command) { - return transfer_bytes(reg, data, NULL, count, command); -} - -esp_err_t ReadRegisterMultipleBytes(uint8_t reg, uint8_t *buf, uint8_t count, uint8_t command) { - return transfer_bytes(reg, NULL, buf, count, command); -} - -// === Leitura e escrita de tamanho fixo === - -esp_err_t write8(uint8_t reg, uint8_t value) { - return WriteRegister(reg, value, SPI_WRITE); -} - -esp_err_t write16(uint8_t reg, uint32_t value) { - uint8_t buf[2] = { - (value >> 8) & 0xFF, - (value >> 0) & 0xFF - }; - return WriteRegisterMultipleBytes(reg, buf, 2, SPI_WRITE); -} - -esp_err_t write24(uint8_t reg, uint32_t value) { - uint8_t buf[3] = { - (value >> 16) & 0xFF, - (value >> 8) & 0xFF, - (value >> 0) & 0xFF - }; - return WriteRegisterMultipleBytes(reg, buf, 3, SPI_WRITE); -} - -uint8_t read8(uint8_t reg) { - uint8_t buf[1]; - ReadRegisterMultipleBytes(reg, buf, 1, SPI_READ); - return buf[0]; -} - -uint32_t read16(uint8_t reg) { - uint8_t buf[2]; - ReadRegisterMultipleBytes(reg, buf, 2, SPI_READ); - return (buf[0] << 8) | buf[1]; -} - -uint32_t read24(uint8_t reg) { - uint8_t buf[3]; - ReadRegisterMultipleBytes(reg, buf, 3, SPI_READ); - return (buf[0] << 16) | (buf[1] << 8) | buf[2]; -} - -esp_err_t readBlockData(uint8_t reg, uint8_t *buf, int len) { - return ReadRegisterMultipleBytes(reg, buf, len, SPI_READ); -} - - -/***************************** - * - * public functions - * - *****************************/ - -/** - * In general: - * @params: void - * @return: register content (measure) of the proper type depending on register width - */ - -unsigned char getVersion() -{ - return read8(VERSION); -} - -/** === setOpMode / getOpMode === - OPERATIONAL MODE REGISTER (0x13) -The general configuration of the ADE7758 is defined by writing to the OPMODE register. -Table 18 summarizes the functionality of each bit in the OPMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set. -1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set. -2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set. -3 to 5 DISMOD 0 By setting these bits, ADE7758�s ADCs can be turned off. In normal operation, these bits should be left at Logic 0. - DISMOD[2:0] Description - 0 0 0 Normal operation. - 1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels. - 0 0 1 Switch off only the current channel ADCs. - 1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths. - 0 1 0 Switch off only the voltage channel ADCs. - 1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths. - 0 1 1 Put the ADE7758 in sleep mode. - 1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ). -6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 �s after a software reset. -7 RESERVED 0 This should be left at 0. - -*/ - -void setOpMode(uint8_t m) -{ - write8(OPMODE, m); -} -uint8_t getOpMode() -{ - return read8(OPMODE); -} - -/** === setMMode / getMMode === -MEASUREMENT MODE REGISTER (0x14) -The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register. -Table 19 summarizes the functionality of each bit in the MMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency. - FREQSEL1 FREQSEL0 Source - 0 0 Phase A - 0 1 Phase B - 1 0 Phase C - 1 1 Reserved -2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers. - Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values - of the largest current and voltage waveform (over a fixed number of half-line cycles) - from Phase A. The number of half-line cycles is determined by the content of the - LINECYC register. At the end of the LINECYC number of half-line cycles, the content - of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns - on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one - bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is, - the voltage and current peak are independently processed (see the Peak Current Detection section). -5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection. - Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A. - Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C. - Note that more than one bit can be set for detection on multiple phases. - If the absolute values of the voltage or current waveform samples in the selected phases exceeds - the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the - STATUS registers are set (see the Peak Current Detection section). - -*/ - -void setMMode(uint8_t m) -{ - write8(MMODE, m); -} -uint8_t getMMode(void) -{ - return read8(MMODE); -} - -/** === setWavMode / getWavMode === -WAVEFORM MODE REGISTER (0x15) -The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register. -Table 20 summarizes the functionality of each bit in the WAVMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample. - PHSEL[1:0] Source - 0 0 Phase A - 0 1 Phase B - 1 0 Phase C - 1 1 Reserved -2 to 4 WAVSEL 0 These bits are used to select the type of waveform. - WAVSEL[2:0] Source - 0 0 0 Current - 0 0 1 Voltage - 0 1 0 Active Power Multiplier Output - 0 1 1 Reactive Power Multiplier Output - 1 0 0 VA Multiplier Output - -Others- Reserved -5 to 6 DTRT 0 These bits are used to select the data rate. - DTRT[1:0] Update Rate - 0 0 26.04 kSPS (CLKIN/3/128) - 0 1 13.02 kSPS (CLKIN/3/256) - 1 0 6.51 kSPS (CLKIN/3/512) - 1 1 3.25 kSPS (CLKIN/3/1024) -7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output - frequency that is proportional to the total apparent power (VA). - In the default state, Logic 0, the VARCF pin outputs a frequency proportional - to the total reactive power (VAR). -*/ - -void setWavMode(uint8_t m) -{ - write8(WAVMODE, m); -} -uint8_t getWavMode() -{ - return read8(WAVMODE); -} - -/** === setCompMode / getCompMode === - -COMPUTATIONAL MODE REGISTER (0x16) -The computational method of the ADE7758 is defined by writing to the COMPMODE register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers. - CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by �90�, respectively. - Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10 - AWATTHR VA � IA VA � (IA � IB) VA � (IA�IB) - BWATTHR VB � IB 0 0 - CWATTHR VC � IC VC � (IC � IB) VC � IC - - AVARHR VA � IA VA � (IA � IB) VA � (IA�IB) - BVARHR VB � IB 0 0 - CVARHR VC � IC VC � (IC � IB) VC � IC - - AVAHR VARMS � IARMS VARMS � IARMS VARMS � ARMS - BVAHR VBRMS � IBRMS (VARMS + VCRMS)/2 � IBRMS VARMS � IBRMS - CVAHR VCRMS � ICRMS VCRMS � ICRMS VCRMS � ICRMS - -2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs. - Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included. - Bit 3 and Bit 4 are for Phase B and Phase C, respectively. - Setting all three bits enables the sum of all three phases to be included in the frequency outputs - (see the Active Power Frequency Output and the Reactive Power Frequency Output sections). - -5 ABS 0 Setting this bit places the APCF output pin in absolute only mode. - Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour - accumulation registers (AWATTHR, BWATTHR, and CWATTHR). - Note that this bit only affects the APCF pin and has no effect on the content of the corresponding - registers. - -6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode. - Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation - registers (AVARHR, BVARHR, and CVARHR). - The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase, - that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive, - there is no change to the sign of the VAR. - Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding - registers. - -7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758. -*/ - -void setCompMode(uint8_t m) -{ - write8(COMPMODE, m); -} -uint8_t getCompMode(void) -{ - return read8(COMPMODE); -} - -/** === setLcycMode / getLcycMode === - -LINE CYCLE ACCUMULATION MODE REGISTER (0x17) -The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register. - -Bit Location Bit Mnemonic Default Value Description - -0 LWATT 0 Setting this bit places the watt-hour accumulation registers - (AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode. -1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers) - into line-cycle accumulation mode. -2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers) - into line-cycle accumulation mode. -3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle - accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively. - More than one phase can be selected for the zero-crossing detection, - and the accumulation time is shortened accordingly. -6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three - phases, that is, a read to those registers resets the registers to 0 after the content of the registers - have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1. -7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the - line input. -*/ - -void setLcycMode(uint8_t m) -{ - write8(LCYCMODE, m); -} -uint8_t getLcycMode(void) -{ - return read8(LCYCMODE); -} - -/** === gainSetup === - GAIN REGISTER (0x23) -The PGA configuration of the ADE7758 is defined by writing to the GAIN register. -Table 18 summarizes the functionality of each bit in the GAIN register. - -Bit Location Bit Mnemonic Default Value Description -0 to 1 PGA1 0 Current GAIN - PGA1[1:0] Description - 0 0 x1 - 0 1 x2 - 1 0 x4 - 1 1 RESERVED -2 ---- RESERVED Reserved. -3 to 4 SCALE 0 Current input full-scale select - SCALE[1:0] Description - 0 0 0.5v - 0 1 0.25v - 1 0 0.125v - 1 1 Reserved -5 to 6 PGA2 0 Voltage GAIN - PGA2[1:0] Description - 0 0 x1 - 0 1 x2 - 1 0 x4 - 1 1 RESERVED -7 INTEGRATOR 0 This bit enables the integrator on the current chanel when set. -*/ - -void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1) -{ - char pgas = (integrator << 7) | (PGA2 << 5) | (scale << 3) | (PGA1); - write8(GAIN, pgas); // write GAIN register, format is |3 bits PGA2 gain|2 bits full scale|3 bits PGA1 gain -} - -void setupDivs(uint8_t Watt_div, uint8_t VAR_div, uint8_t VA_div) -{ - write8(WDIV, Watt_div); - write8(VARDIV, VAR_div); - write8(VADIV, VA_div); -} - -/** === getMaskInterrupts / setMaskInterrupts -MASK REGISTER (0x18) -When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register. -The IRQ logic output is reset to its default collector open state when the RSTATUS register is read. -describes the function of each bit in the interrupt mask register. - -Bit Location Interrupt Flag Default Value Description -0 AEHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers, - that is, the WATTHR register is half full. -1 REHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers, - that is, the VARHR register is half full. -2 VAEHF 0 Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR - registers, that is, the VAHR register is half full. -3 SAGA 0 Enables an interrupt when there is a SAG on the line voltage of the Phase A. -4 SAGB 0 Enables an interrupt when there is a SAG on the line voltage of the Phase B. -5 SAGC 0 Enables an interrupt when there is a SAG on the line voltage of the Phase C. -6 ZXTOA 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase A. -7 ZXTOB 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase B. -8 ZXTOC 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase C. -9 ZXA 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase A - (see the Zero-Crossing Detection section). -10 ZXB 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase B - (see the Zero-Crossing Detection section). -11 ZXC 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase C - (see the Zero-Crossing Detection section). -12 LENERGY 0 Enables an interrupt when the energy accumulations over LINECYC are finished. -13 RESERVED 0 Reserved. -14 PKV 0 Enables an interrupt when the voltage input selected in the MMODE register is above - the value in the VPINTLVL register. -15 PKI 0 Enables an interrupt when the current input selected in the MMODE register is above the - value in the IPINTLVL register. -16 WFSM 0 Enables an interrupt when data is present in the WAVEMODE register. -17 REVPAP 0 Enables an interrupt when there is a sign change in the watt calculation among any one of - the phases specified by the TERMSEL bits in the COMPMODE register. -18 REVPRP 0 Enables an interrupt when there is a sign change in the VAR calculation among any one of - the phases specified by the TERMSEL bits in the COMPMODE register. -19 SEQERR 0 Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing - of Phase C but with that of Phase B. -*/ -uint32_t getMaskInterrupts(void) -{ - return read24(MASK); -} -void setMaskInterrupts(uint32_t m) -{ - write24(MASK, m); -} -/** getStatus / resetStatus -INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A) -The interrupt status register is used to determine the source of an interrupt event. -When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set. -The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set. -When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt. -All the interrupts in the interrupt status register stay at their logic high state after an event occurs. -The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read. - -Bit Location Interrupt Flag Default Value Event Description -0 AEHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three - WATTHR registers, that is, the WATTHR register is half full. -1 REHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three - VARHR registers, that is, the VARHR register is half full. -2 VAEHF 0 Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three - VAHR registers, that is, the VAHR register is half full. -3 SAGA 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A. -4 SAGB 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B. -5 SAGC 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C. -6 ZXTOA 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A. -7 ZXTOB 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B. -8 ZXTOC 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C. -9 ZXA 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A. -10 ZXB 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B. -11 ZXC 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C. -12 LENERGY 0 In line energy accumulation, indicates the end of an integration over an integer number of - half- line cycles (LINECYC). See the Calibration section. -13 RESET 1 Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758 - and sets the registers back to their default values. This bit in the STATUS or RSTATUS register - is logic high for only one clock cycle after a reset event. -14 PKV 0 Indicates that an interrupt was caused when the selected voltage input is above the value in the - VPINTLVL register. -15 PKI 0 Indicates that an interrupt was caused when the selected current input is above the value - in the IPINTLVL register. -16 WFSM 0 Indicates that new data is present in the waveform register. -17 REVPAP 0 Indicates that an interrupt was caused by a sign change in the watt calculation among any - one of the phases specified by the TERMSEL bits in the COMPMODE register. -18 REVPRP 0 Indicates that an interrupt was caused by a sign change in the VAR calculation among any - one of the phases specified by the TERMSEL bits in the COMPMODE register. -19 SEQERR 0 Indicates that an interrupt was caused by a zero crossing from Phase A - followed not by the zero crossing of Phase C but by that of Phase B. -*/ - -uint32_t getStatus(void) -{ - return read24(STATUS); -} - -uint32_t resetStatus(void) -{ - return read24(RSTATUS); -} - -/** === getAIRMS === - * Phase A Current RMS Value - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ -int32_t getAIRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXA)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(AIRMS); -} - -/** === getBIRMS === - * Phase B Current RMS Value - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ - -int32_t getBIRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXB)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(BIRMS); -} -/** === getCIRMS === - * Phase C Current RMS Value - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ -int32_t getCIRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXC)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(CIRMS); -} - -/** === getAVRMS === - * Phase A RMS Value (Voltage Channel). - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ - -int32_t getAVRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXA)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(AVRMS); -} - -/** === getBVRMS === - * Phase B RMS Value (Voltage Channel). - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ - -int32_t getBVRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXB)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(BVRMS); -} - -/** === getCVRMS === - * Phase C RMS Value (Voltage Channel). - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with the data (24 bits unsigned). - */ - -int32_t getCVRMS(void) -{ - long lastupdate = 0; - resetStatus(); // Clear all interrupts - while (!(getStatus() & ZXC)) // wait Zero-Crossing - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // break; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - return read24(CVRMS); -} - -/** === vrms === - * Returns the mean of last 20 readings of RMS voltage. Also supress first reading to avoid - * corrupted data. - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with RMS voltage value - */ - -uint32_t avrms(void) -{ - uint8_t i = 0; - uint32_t v = 0; - if (getAVRMS()) - { // Ignore first reading to avoid garbage - for (i = 0; i < 10; ++i) - { - v += getAVRMS(); - } - return v / 10; - } - else - { - return 0; - } -} -uint32_t bvrms() -{ - uint8_t i = 0; - uint32_t v = 0; - if (getBVRMS()) - { // Ignore first reading to avoid garbage - for (i = 0; i < 10; ++i) - { - v += getBVRMS(); - } - return v / 10; - } - else - { - return 0; - } -} -uint32_t cvrms() -{ - uint8_t i = 0; - uint32_t v = 0; - if (getCVRMS()) - { // Ignore first reading to avoid garbage - for (i = 0; i < 10; ++i) - { - v += getCVRMS(); - } - return v / 10; - } - else - { - return 0; - } -} - -/** === irms === - * Returns the mean of last 20 readings of RMS current. Also supress first reading to avoid - * corrupted data. - * To minimize noise, synchronize the reading of the rms register with the zero crossing - * of the voltage input and take the average of a number of readings. - * @param none - * @return long with RMS current value in hundreds of [mA], ie. 6709=67[mA] - */ - -uint32_t airms() -{ - char n = 0; - long i = 0; - if (getAIRMS()) - { // Ignore first reading to avoid garbage - for (n = 0; n < 10; ++n) - { - i += getAIRMS(); - } - return i / 10; - } - else - { - return 0; - } -} -uint32_t birms() -{ - char n = 0; - long i = 0; - if (getBIRMS()) - { // Ignore first reading to avoid garbage - for (n = 0; n < 10; ++n) - { - i += getBIRMS(); - } - return i / 10; - } - else - { - return 0; - } -} -int32_t cirms() -{ - char n = 0; - int32_t i = 0; - if (getCIRMS()) - { // Ignore first reading to avoid garbage - for (n = 0; n < 10; ++n) - { - i += getCIRMS(); - } - return i / 10; - } - else - { - return 0; - } -} - -/** === getFreq === - * Period of the Phase selected in MMode. - * @param none - * @return int with the data (12 bits unsigned). - */ - -int32_t getFreq(void) -{ - return read16(FREQ); -} - -/** setLineCyc: sets the number of cycles required for the accumulations. - **/ - -void setLineCyc(uint32_t d) -{ - write16(LINECYC, d); -} - -/** === setCurrentOffset / getCurrentOffset === - * @param none - * @return int with the data (12 bits 2-complement signed). - */ - -int32_t getACurrentOffset() -{ - int32_t data; - data = ((int32_t)(read16(AIRMSOS) << 20) >> 20); - return data; -} -int32_t getBCurrentOffset() -{ - int32_t data; - data = ((int32_t)(read16(BIRMSOS) << 20) >> 20); - return data; -} -int32_t getCCurrentOffset() -{ - int32_t data; - data = ((int32_t)(read16(CIRMSOS) << 20) >> 20); - return data; -} -void setACurrentOffset(int32_t o) -{ - write16(AIRMSOS, o); -} -void setBCurrentOffset(int32_t o) -{ - write16(BIRMSOS, o); -} -void setCCurrentOffset(int32_t o) -{ - write16(CIRMSOS, o); -} - -void setAWattOffset(int32_t o) -{ - write16(AWATTOS, o); -} -void setBWattOffset(int32_t o) -{ - write16(BWATTOS, o); -} -void setCWattOffset(int32_t o) -{ - write16(CWATTOS, o); -} - -void setAVAROffset(int32_t o) -{ - write16(AVAROS, o); -} -void setBVAROffset(int32_t o) -{ - write16(BVAROS, o); -} -void setCVAROffset(int32_t o) -{ - write16(CVAROS, o); -} -/** === setVoltageOffset / getVoltageOffset === - * @param none - * @return int with the data (12 bits 2-complement signed). - */ - -int32_t getAVoltageOffset() -{ - int32_t data; - data = ((int32_t)(read16(AVRMSOS) << 20) >> 20); - return data; -} -int32_t getBVoltageOffset() -{ - int32_t data; - data = ((int32_t)(read16(BVRMSOS) << 20) >> 20); - return data; -} -int32_t getCVoltageOffset() -{ - int32_t data; - data = ((int32_t)(read16(CVRMSOS) << 20) >> 20); - return data; -} -void setAVoltageOffset(int32_t o) -{ - write16(AVRMSOS, o); -} -void setBVoltageOffset(int32_t o) -{ - write16(BVRMSOS, o); -} -void setCVoltageOffset(int32_t o) -{ - write16(CVRMSOS, o); -} - -/** === setZeroCrossingTimeout / getZeroCrossingTimeout === - * Zero-Crossing Timeout. If no zero crossings are detected - * on Channel 2 within a time period specified by this 12-bit register, - * the interrupt request line (IRQ) is activated - * @param none - * @return int with the data (12 bits unsigned). - */ - -void setZeroCrossingTimeout(int32_t d) -{ - write16(ZXTOUT, d); -} -int32_t getZeroCrossingTimeout() -{ - return read16(ZXTOUT); -} - -/** === setPotLine(Phase) === -Set the conditions for Line accumulation in the selected phase. -Then wait for the interruption and return the phase number when it occurs. -If it does not happen for more than 1.5 seconds, it returns a 0. -**/ - -uint8_t setPotLine(uint8_t Phase, uint32_t Cycles) -{ - long lastupdate = 0; - char m = 0; - switch (Phase) - { - case PHASE_A: - m = (LWATT | LVAR | LVA | ZXSEL_A); - break; - case PHASE_B: - m = (LWATT | LVAR | LVA | ZXSEL_B); - break; - case PHASE_C: - m = (LWATT | LVAR | LVA | ZXSEL_C); - break; - } - setLcycMode(0); - setLcycMode(m); - resetStatus(); - setLineCyc(Cycles); - lastupdate = 0; - while (!(getStatus() & LENERGY)) // wait to terminar de acumular - { // wait for the selected interrupt to occur - lastupdate++; - if (lastupdate > 20) - { - return 0; // breack; - } - vTaskDelay(pdMS_TO_TICKS(20)); - } - - return Phase; -} - -/** === getWatt(phase) / getVar(phase) / getVa(phase) === -Devuelve los valores de la potencia requerida de la fase seleccionada. -Utilizar antes setPotLine(phase) para generar los valores. -**/ - -int32_t getWatt(uint8_t Phase) -{ - int32_t temp = 0; - switch (Phase) - { - case PHASE_A: - temp = read16(AWATTHR); - break; - case PHASE_B: - temp = read16(BWATTHR); - break; - case PHASE_C: - temp = read16(CWATTHR); - break; - } - return temp; -} -int32_t getVar(uint8_t Phase) -{ - int32_t temp = 0; - switch (Phase) - { - case PHASE_A: - temp = read16(AVARHR); - break; - case PHASE_B: - temp = read16(BVARHR); - break; - case PHASE_C: - temp = read16(CVARHR); - break; - } - return temp; -} -int32_t getVa(uint8_t Phase) -{ - int32_t temp = 0; - switch (Phase) - { - case PHASE_A: - temp = read16(AVAHR); - break; - case PHASE_B: - temp = read16(BVAHR); - break; - case PHASE_C: - temp = read16(CVAHR); - break; - } - return temp; -} -void setAPCFDEN(int32_t d) -{ - write16(APCFDEN, d); -} -int32_t getAPCFDEN(void) -{ - int32_t data; - data = ((int32_t)(read16(APCFDEN) << 20) >> 20); - return data; -} -void setAPCFNUM(int32_t d) -{ - write16(APCFNUM, d); -} -int32_t getAPCFNUM(void) -{ - int32_t data; - data = ((int32_t)(read16(APCFNUM) << 20) >> 20); - return data; -} -void setVARCFNUM(int32_t d) -{ - write16(VARCFNUM, d); -} -int32_t getVARCFNUM(void) -{ - int32_t data; - data = ((int32_t)(read16(VARCFNUM) << 20) >> 20); - return data; -} -void setVARCFDEN(int32_t d) -{ - write16(VARCFDEN, d); -} -int32_t getVARCFDEN(void) -{ - int32_t data; - data = ((int32_t)(read16(VARCFDEN) << 20) >> 20); - return data; -} -void setAWG(int32_t d) -{ - write16(AWG, d); -} -int32_t getAWG() -{ - return ((read16(AWG)) << 4) >> 4; -} -void setBWG(int32_t d) -{ - write16(BWG, d); -} -void setCWG(int32_t d) -{ - write16(CWG, d); -} -void setAVARG(int32_t d) -{ - write16(AVARG, d); -} -int32_t getAVARG() -{ - return ((read16(AVARG)) << 4) >> 4; -} -void setBVARG(int32_t d) -{ - write16(BVARG, d); -} -int32_t getBVARG() -{ - return ((read16(BVARG)) << 4) >> 4; -} -void setCVARG(int32_t d) -{ - write16(CVARG, d); -} -int32_t getCVARG() -{ - return ((read16(CVARG)) << 4) >> 4; -} -void setAVAG(int32_t d) -{ - write16(AVAG, d); -} -void setBVAG(int32_t d) -{ - write16(BVAG, d); -} -void setCVAG(int32_t d) -{ - write16(CVAG, d); -} -// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.c === - - -// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c === -#include "meter_ade7758.h" -#include "spi_bus_manager.h" -#include "ade7758.h" -#include "meter_events.h" - -#include -#include -#include - -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" - -#include "driver/gpio.h" - -#define TAG "meter_ade7758" - -// === Pinos === -#define PIN_NUM_CS 15 -#define PIN_ADUM_EN 4 - -// === Constantes de calibração === -#define VRMS_CAL 4732.78f -#define IRMS_CAL 53416.0f - -#define METER_READ_INTERVAL_MS 5000 - -// === Estrutura interna === -typedef struct { - float vrms[3]; - float irms[3]; - int watt[3]; - int var[3]; // reservado - int va[3]; // reservado -} meter_ade7758_internal_data_t; - -static meter_ade7758_internal_data_t meter_data; -static TaskHandle_t meter_task = NULL; -static SemaphoreHandle_t meter_mutex = NULL; -static uint32_t meter_watchdog_counter = 0; - -// === Controle ADUM1401 === -static void adum1401_enable(bool enable) { - gpio_config_t io_conf = { - .pin_bit_mask = BIT64(PIN_ADUM_EN), - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - gpio_config(&io_conf); - gpio_set_level(PIN_ADUM_EN, enable ? 1 : 0); - ESP_LOGI(TAG, "ADUM1401 %s", enable ? "ativado" : "desativado"); -} - -// === Post de evento === -static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) { - meter_event_data_t evt = { - .frequency = 0, - .power_factor = 0, - .total_energy = 0 - }; - - memcpy(evt.vrms, data->vrms, sizeof(evt.vrms)); - memcpy(evt.irms, data->irms, sizeof(evt.irms)); - memcpy(evt.watt, 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)); - } -} - -// === Task de leitura === -static void meter_ade7758_task_func(void *param) { - ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada"); - - meter_ade7758_internal_data_t previous = {0}; - - while (true) { - meter_ade7758_internal_data_t meterData = {0}; - - ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion()); - - meterData.vrms[0] = avrms() / VRMS_CAL; - meterData.vrms[1] = bvrms() / VRMS_CAL; - meterData.vrms[2] = cvrms() / VRMS_CAL; - - meterData.irms[0] = airms() / IRMS_CAL; - meterData.irms[1] = birms() / IRMS_CAL; - meterData.irms[2] = cirms() / IRMS_CAL; - - if (setPotLine(PHASE_A, 20)) meterData.watt[0] = getWatt(PHASE_A); - if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B); - if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C); - - if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) { - if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { - meter_data = meterData; - meter_watchdog_counter++; - xSemaphoreGive(meter_mutex); - - meter_ade7758_post_event(&meterData); - } - previous = meterData; - } - - vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS)); - } -} - -// === Inicialização === -esp_err_t meter_ade7758_init(void) { - ESP_LOGI(TAG, "Inicializando ADE7758..."); - - if (!meter_mutex) { - meter_mutex = xSemaphoreCreateMutex(); - if (!meter_mutex) { - ESP_LOGE(TAG, "Falha ao criar mutex"); - return ESP_ERR_NO_MEM; - } - } - - if (!spi_bus_manager_is_initialized()) { - esp_err_t err = spi_bus_manager_init(); // usa pinos padrão - if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro ao inicializar SPI: %s", esp_err_to_name(err)); - return err; - } - } - - adum1401_enable(true); // Ativa o ADUM1401 antes de usar SPI - vTaskDelay(pdMS_TO_TICKS(10)); // Delay de estabilização - - esp_err_t err = Init(spi_bus_manager_get_host(), -1, -1, -1); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro Init SPI ADE7758: %s", esp_err_to_name(err)); - return err; - } - - err = InitSpi(PIN_NUM_CS); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro ao registrar dispositivo SPI: %s", esp_err_to_name(err)); - return err; - } - - gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); - setupDivs(1, 1, 1); - setLcycMode(0x00); - resetStatus(); - - ESP_LOGI(TAG, "ADE7758 inicializado com sucesso."); - return ESP_OK; -} - -// === Execução === -esp_err_t meter_ade7758_start(void) { - if (meter_task) return ESP_ERR_INVALID_STATE; - - BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task); - return result == pdPASS ? ESP_OK : ESP_FAIL; -} - -void meter_ade7758_stop(void) { - if (meter_task) { - vTaskDelete(meter_task); - meter_task = NULL; - } -} - -// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c === - - -// === Início de: components/meter_manager/driver/meter_orno/modbus_params.h === -/* - * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef _DEVICE_PARAMS -#define _DEVICE_PARAMS - -#include - -#pragma pack(push, 1) - -// Discrete Inputs -typedef struct { - uint8_t discrete_input0 : 1; - uint8_t discrete_input1 : 1; - uint8_t discrete_input2 : 1; - uint8_t discrete_input3 : 1; - uint8_t discrete_input4 : 1; - uint8_t discrete_input5 : 1; - uint8_t discrete_input6 : 1; - uint8_t discrete_input7 : 1; - uint8_t discrete_input_port1; - uint8_t discrete_input_port2; -} discrete_reg_params_t; - -// Coils -typedef struct { - uint8_t coils_port0; - uint8_t coils_port1; - uint8_t coils_port2; -} coil_reg_params_t; - -// Input Registers (pode manter caso use em outro driver) -typedef struct { - float input_data0; - float input_data1; - float input_data2; - float input_data3; - uint16_t data[150]; - float input_data4; - float input_data5; - float input_data6; - float input_data7; - uint16_t data_block1[150]; -} input_reg_params_t; - -// Holding Registers (ajustado para os campos usados no ORNO 516) -typedef struct { - float l1_current; // 0x0016 - float l2_current; // 0x0018 - float l3_current; // 0x001A - - float l1_voltage; // 0x000E - float l2_voltage; // 0x0010 - float l3_voltage; // 0x0012 - - float total_active_power; // 0x001C - float total_reactive_power; - float active_power; - float apparent_power; - float reactive_power; -} holding_reg_params_t; - - -#pragma pack(pop) - -// Instâncias globais das estruturas -extern holding_reg_params_t holding_reg_params; -extern input_reg_params_t input_reg_params; -extern coil_reg_params_t coil_reg_params; -extern discrete_reg_params_t discrete_reg_params; - -#endif // !_DEVICE_PARAMS - -// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.h === - - -// === Início de: components/meter_manager/driver/meter_orno/meter_orno.h === -#ifndef ORNO_MODBUS_H_ -#define ORNO_MODBUS_H_ - -#include -#include "esp_err.h" - - -#include -#include -#include "esp_err.h" - -/** - * @brief Inicializa o driver do medidor (SPI, mutex, registradores ADE7758). - */ -esp_err_t meter_init(void); - -/** - * @brief Inicia a tarefa de leitura de dados do medidor. - */ -esp_err_t meter_start(void); - -/** - * @brief Para a tarefa de leitura e limpa os dados internos. - */ -void meter_stop(void); - -/** - * @brief Verifica se o medidor está em execução. - * - * @return true se a tarefa estiver ativa, false caso contrário. - */ -bool meter_is_running(void); - -/** - * @brief Limpa os dados armazenados no medidor (zera todos os valores). - */ -void meter_clear_data(void); - -// ----- Leituras por fase (L1, L2, L3) ----- - -// Tensão RMS (em volts) -float meter_get_vrms_l1(void); -float meter_get_vrms_l2(void); -float meter_get_vrms_l3(void); - -// Corrente RMS (em amperes) -float meter_get_irms_l1(void); -float meter_get_irms_l2(void); -float meter_get_irms_l3(void); - -// Potência ativa (W) -int meter_get_watt_l1(void); -int meter_get_watt_l2(void); -int meter_get_watt_l3(void); - -// Potência reativa (VAR) -int meter_get_var_l1(void); -int meter_get_var_l2(void); -int meter_get_var_l3(void); - -// Potência aparente (VA) -int meter_get_va_l1(void); -int meter_get_va_l2(void); -int meter_get_va_l3(void); - -// (Opcional) contador de watchdog para diagnóstico -uint32_t meter_get_watchdog_counter(void); - -#ifdef __cplusplus -} -#endif - -#endif /* ORNO_MODBUS_H_ */ - -// === Fim de: components/meter_manager/driver/meter_orno/meter_orno.h === - - -// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "esp_err.h" - -/** - * @brief Inicializa o driver do medidor ORNO 513 (SPI, mutex, registradores). - */ -esp_err_t meter_orno513_init(void); - -/** - * @brief Inicia a tarefa de leitura de dados do medidor ORNO 513. - */ -esp_err_t meter_orno513_start(void); - -/** - * @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 513. - */ -void meter_orno513_stop(void); - - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.h === - - -// === Início de: components/meter_manager/driver/meter_orno/meter_orno516.h === -#ifndef METER_ORNO516_H_ -#define METER_ORNO516_H_ - -#include -#include -#include "esp_err.h" - -/** - * @brief Inicializa o driver do medidor ORNO 516 (SPI, mutex, registradores). - * - * @return esp_err_t Retorna ESP_OK se a inicialização for bem-sucedida, caso contrário retorna um erro. - */ -esp_err_t meter_orno516_init(void); - -/** - * @brief Inicia a tarefa de leitura de dados do medidor ORNO 516. - * - * @return esp_err_t Retorna ESP_OK se a tarefa for iniciada com sucesso, caso contrário retorna um erro. - */ -esp_err_t meter_orno516_start(void); - -/** - * @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 516. - */ -void meter_orno516_stop(void); - - -#ifdef __cplusplus -} -#endif - -#endif /* METER_ORNO516_H_ */ - -// === Fim de: components/meter_manager/driver/meter_orno/meter_orno516.h === - - -// === Início de: components/meter_manager/driver/meter_orno/modbus_params.c === -/* - * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -/*===================================================================================== - * Description: - * C file to define parameter storage instances - *====================================================================================*/ -#include "modbus_params.h" - -// Here are the user defined instances for device parameters packed by 1 byte -// These are keep the values that can be accessed from Modbus master -holding_reg_params_t holding_reg_params = { 0 }; - -input_reg_params_t input_reg_params = { 0 }; - -coil_reg_params_t coil_reg_params = { 0 }; - -discrete_reg_params_t discrete_reg_params = { 0 }; - -// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.c === - - -// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.c === -#include "meter_orno513.h" -#include "modbus_params.h" -#include "mbcontroller.h" -#include "meter_events.h" -#include "esp_log.h" -#include "driver/uart.h" -#include - -#define TAG "serial_mdb_orno513" - -#define MB_PORT_NUM 2 -#define MB_DEV_SPEED 9600 -#define MB_UART_TXD 17 -#define MB_UART_RXD 16 -#define MB_UART_RTS 5 -#define UPDATE_INTERVAL (3000 / portTICK_PERIOD_MS) -#define POLL_INTERVAL (100 / portTICK_PERIOD_MS) - -#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) -#define STR(x) ((const char *)(x)) -#define OPTS(min, max, step) {.opt1 = min, .opt2 = max, .opt3 = step} - -// State flag -static bool is_initialized = false; -static TaskHandle_t meter_task = NULL; - -// CID enums -enum { - CID_TOTAL_ACTIVE_ENERGY = 0, - CID_TOTAL_REACTIVE_ENERGY, - CID_ACTIVE_POWER, - CID_APPARENT_POWER, - CID_REACTIVE_POWER, - CID_L1_CURRENT, - CID_L1_VOLTAGE -}; - -// Register addresses -#define TOTALFACTIVE 0x010E -#define TOTALRACTIVE 0x0118 -#define ACTIVEPOWER 0x0104 -#define APPARENTPOWER 0x0106 -#define REACTIVEPOWER 0x0108 -#define L1CURRENT 0x0102 -#define L1VOLTAGE 0x0100 - -const mb_parameter_descriptor_t device_parameters_orno513[] = { - {CID_TOTAL_ACTIVE_ENERGY, STR("Total Active Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALFACTIVE, 2, - HOLD_OFFSET(total_active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, - - {CID_TOTAL_REACTIVE_ENERGY, STR("Total Reactive Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALRACTIVE, 2, - HOLD_OFFSET(total_reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, - - {CID_ACTIVE_POWER, STR("Active Power"), STR("W"), 1, MB_PARAM_HOLDING, ACTIVEPOWER, 2, - HOLD_OFFSET(active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, - - {CID_APPARENT_POWER, STR("Apparent Power"), STR("VA"), 1, MB_PARAM_HOLDING, APPARENTPOWER, 2, - HOLD_OFFSET(apparent_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, - - {CID_REACTIVE_POWER, STR("Reactive Power"), STR("VAR"), 1, MB_PARAM_HOLDING, REACTIVEPOWER, 2, - HOLD_OFFSET(reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, - - {CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2, - HOLD_OFFSET(l1_current), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100, 0.1), PAR_PERMS_READ}, - - {CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2, - HOLD_OFFSET(l1_voltage), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ} -}; - -const uint16_t num_device_parameters_orno513 = sizeof(device_parameters_orno513) / sizeof(device_parameters_orno513[0]); - -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 serial_mdb_task(void *param) { - esp_err_t err; - const mb_parameter_descriptor_t *desc = NULL; - - float voltage[3] = {0}; - float current[3] = {0}; - int watt[3] = {0}; - float energy = 0.0f; - - while (1) { - for (uint16_t cid = 0; cid < num_device_parameters_orno513; cid++) { - err = mbc_master_get_cid_info(cid, &desc); - if (err != ESP_OK || !desc) continue; - - 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) { - int32_t raw = *(int32_t *)data_ptr; - float val = raw / 10.0f; - ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units); - - switch (cid) { - case CID_L1_VOLTAGE: voltage[0] = val; break; - case CID_L1_CURRENT: current[0] = val; break; - case CID_ACTIVE_POWER: - watt[0] = (int)(val); - watt[1] = watt[0]; - watt[2] = watt[0]; - break; - case CID_TOTAL_ACTIVE_ENERGY: - energy = val / 1000.0f; - break; - default: - break; - } - } else { - ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err)); - } - - vTaskDelay(POLL_INTERVAL); - } - - meter_event_data_t evt = { - .frequency = 0.0f, - .power_factor = 0.0f, - .total_energy = energy, - .source = "GRID" - }; - - memcpy(evt.vrms, voltage, sizeof(evt.vrms)); - memcpy(evt.irms, current, sizeof(evt.irms)); - memcpy(evt.watt, watt, sizeof(evt.watt)); - - esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10)); - - - vTaskDelay(UPDATE_INTERVAL); - } -} - -esp_err_t meter_orno513_init(void) { - if (is_initialized) { - ESP_LOGW(TAG, "meter_orno513 already initialized"); - return ESP_ERR_INVALID_STATE; - } - - ESP_LOGI(TAG, "meter_orno513_init"); - - mb_communication_info_t comm = { - .port = MB_PORT_NUM, - .mode = MB_MODE_RTU, - .baudrate = MB_DEV_SPEED, - .parity = UART_PARITY_DISABLE - }; - - void *handler = NULL; - - esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &handler); - if (err != ESP_OK) { - ESP_LOGE(TAG, "mbc_master_init failed"); - return err; - } - - ESP_ERROR_CHECK(mbc_master_setup(&comm)); - ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE)); - ESP_ERROR_CHECK(mbc_master_start()); - ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX)); - vTaskDelay(pdMS_TO_TICKS(5)); - ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno513, num_device_parameters_orno513)); - - is_initialized = true; - return ESP_OK; -} - -esp_err_t meter_orno513_start(void) { - - ESP_LOGI(TAG, "meter_orno513_start"); - - if (!is_initialized) { - ESP_LOGE(TAG, "meter_orno513 not initialized"); - return ESP_ERR_INVALID_STATE; - } - - if (meter_task == NULL) { - xTaskCreate(serial_mdb_task, "meter_orno513_task", 4096, NULL, 3, &meter_task); - ESP_LOGI(TAG, "meter_orno513 task started"); - } - - return ESP_OK; -} - -void meter_orno513_stop(void) { - if (!is_initialized) { - ESP_LOGW(TAG, "meter_orno513 not initialized"); - return; - } - - ESP_LOGI(TAG, "Stopping meter_orno513"); - - uart_driver_delete(MB_PORT_NUM); - - esp_err_t err = mbc_master_destroy(); - if (err != ESP_OK) { - ESP_LOGW(TAG, "mbc_master_destroy() returned %s", esp_err_to_name(err)); - } - - is_initialized = false; -} - -// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.c === diff --git a/projeto_parte3.c b/projeto_parte3.c deleted file mode 100644 index d8f470a..0000000 --- a/projeto_parte3.c +++ /dev/null @@ -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 - -#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 -#include -#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 -#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 - -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 // 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 === diff --git a/readproject.py b/readproject.py index 9a65afc..e41be8e 100644 --- a/readproject.py +++ b/readproject.py @@ -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]