// === Início de: components/evse/include/evse_events.h === #ifndef EVSE_EVENTS_H #define EVSE_EVENTS_H #pragma once #include "esp_event.h" ESP_EVENT_DECLARE_BASE(EVSE_EVENTS); typedef enum { EVSE_EVENT_INIT, EVSE_EVENT_STATE_CHANGED, // Outros eventos possíveis futuramente } evse_event_id_t; typedef enum { EVSE_STATE_EVENT_IDLE, EVSE_STATE_EVENT_WAITING, EVSE_STATE_EVENT_CHARGING, EVSE_STATE_EVENT_FAULT } evse_state_event_t; typedef struct { evse_state_event_t state; } evse_state_event_data_t; #endif // EVSE_EVENTS_H // === Fim de: components/evse/include/evse_events.h === // === Início de: components/evse/include/evse_api.h === #ifndef EVSE_API_H #define EVSE_API_H #include #include #include "esp_err.h" #include "evse_state.h" // Define evse_state_t // Inicialização void evse_init(void); void evse_process(void); // Estado evse_state_t evse_get_state(void); const char* evse_state_to_str(evse_state_t state); bool evse_is_connector_plugged(evse_state_t state); bool evse_is_limit_reached(void); // Autorização e disponibilidade bool evse_is_enabled(void); void evse_set_enabled(bool value); bool evse_is_available(void); void evse_set_available(bool value); bool evse_is_require_auth(void); void evse_set_require_auth(bool value); // Corrente uint16_t evse_get_charging_current(void); esp_err_t evse_set_charging_current(uint16_t value); uint16_t evse_get_default_charging_current(void); esp_err_t evse_set_default_charging_current(uint16_t value); uint8_t evse_get_max_charging_current(void); esp_err_t evse_set_max_charging_current(uint8_t value); // Temperatura uint8_t evse_get_temp_threshold(void); esp_err_t evse_set_temp_threshold(uint8_t value); // RCM / Socket bool evse_get_socket_outlet(void); esp_err_t evse_set_socket_outlet(bool value); bool evse_is_rcm(void); esp_err_t evse_set_rcm(bool value); // Limites uint32_t evse_get_consumption_limit(void); void evse_set_consumption_limit(uint32_t value); uint32_t evse_get_charging_time_limit(void); void evse_set_charging_time_limit(uint32_t value); uint16_t evse_get_under_power_limit(void); void evse_set_under_power_limit(uint16_t value); void evse_set_limit_reached(bool value); // Energia total acumulada da sessão (em Wh) uint32_t evse_get_total_energy(void); // Potência instantânea medida (em W) uint32_t evse_get_instant_power(void); // Limites default uint32_t evse_get_default_consumption_limit(void); void evse_set_default_consumption_limit(uint32_t value); uint32_t evse_get_default_charging_time_limit(void); void evse_set_default_charging_time_limit(uint32_t value); uint16_t evse_get_default_under_power_limit(void); void evse_set_default_under_power_limit(uint16_t value); uint32_t evse_get_total_energy(void); uint32_t evse_get_instant_power(void); #endif // EVSE_API_H // === Fim de: components/evse/include/evse_api.h === // === Início de: components/loadbalancer/src/loadbalancer_events.c === #include "loadbalancer_events.h" // Define a base de eventos para o loadbalancer ESP_EVENT_DEFINE_BASE(LOADBALANCER_EVENTS); // === Fim de: components/loadbalancer/src/loadbalancer_events.c === // === Início de: components/loadbalancer/src/loadbalancer.c === #include "loadbalancer.h" #include "loadbalancer_events.h" #include "esp_event.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "input_filter.h" #include "nvs_flash.h" #include "nvs.h" #include #include "meter_events.h" #include "evse_events.h" static const char *TAG = "loadbalancer"; // Limites configuráveis #define MIN_CHARGING_CURRENT_LIMIT 6 // A #define MAX_CHARGING_CURRENT_LIMIT 32 // A #define MIN_GRID_CURRENT_LIMIT 6 // A #define MAX_GRID_CURRENT_LIMIT 100 // A // Parâmetros static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT; static bool loadbalancer_enabled = false; static float grid_current = 0.0f; static float evse_current = 0.0f; static input_filter_t grid_filter; static input_filter_t evse_filter; #define NVS_NAMESPACE "loadbalancing" #define NVS_MAX_GRID_CURRENT "max_grid_curr" #define NVS_LOADBALANCER_ENABLED "enabled" static void loadbalancer_meter_event_handler(void *handler_arg, esp_event_base_t base, int32_t id, void *event_data) { if (id != METER_EVENT_DATA_READY || event_data == NULL) return; const meter_event_data_t *evt = (const meter_event_data_t *)event_data; ESP_LOGI(TAG, "Received meter event from source: %s", evt->source); ESP_LOGI(TAG, "Raw IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]); ESP_LOGI(TAG, "Raw VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[2]); ESP_LOGI(TAG, "Raw Power: [W1=%d, W2=%d, W3=%d]", evt->watt[0], evt->watt[1], evt->watt[2]); ESP_LOGI(TAG, "Freq: %.2f Hz | PF: %.2f | Energy: %.3f kWh", evt->frequency, evt->power_factor, evt->total_energy); // Calcula a corrente máxima entre as 3 fases float max_irms = evt->irms[0]; for (int i = 1; i < 3; ++i) { if (evt->irms[i] > max_irms) { max_irms = evt->irms[i]; } } ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms); // Atualiza com filtro exponencial dependendo da origem if (strncmp(evt->source, "GRID", 4) == 0) { grid_current = input_filter_update(&grid_filter, max_irms); ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current); } else if (strncmp(evt->source, "EVSE", 4) == 0) { evse_current = input_filter_update(&evse_filter, max_irms); ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current); } else { ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source); } } static void loadbalancer_evse_event_handler(void *handler_arg, esp_event_base_t base, int32_t id, void *event_data) { const evse_state_event_data_t *evt = (const evse_state_event_data_t *)event_data; ESP_LOGI(TAG, "EVSE state changed: %d", evt->state); 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"); break; case EVSE_STATE_EVENT_WAITING: // EV is connected but not charging yet (e.g., waiting for authorization) ESP_LOGI(TAG, "EVSE is waiting - connected but not charging"); break; case EVSE_STATE_EVENT_CHARGING: grid_current = 0.0f; evse_current = 0.0f; // Charging has started - maintain or monitor current usage ESP_LOGI(TAG, "EVSE is charging"); break; case EVSE_STATE_EVENT_FAULT: // A fault has occurred - safety measures may be needed ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing"); // Optional: disable load balancing during fault condition // loadbalancer_set_enabled(false); break; default: ESP_LOGW(TAG, "Unknown EVSE state: %d", evt->state); break; } } // Carrega configuração do NVS static esp_err_t loadbalancer_load_config() { nvs_handle_t handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err)); return err; } bool needs_commit = false; uint8_t temp_u8; // max_grid_current err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8); if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT) { max_grid_current = temp_u8; } else { max_grid_current = MAX_GRID_CURRENT_LIMIT; nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current); ESP_LOGW(TAG, "max_grid_current missing or invalid, setting default: %d", max_grid_current); needs_commit = true; } // loadbalancer_enabled err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8); if (err == ESP_OK && temp_u8 <= 1) { loadbalancer_enabled = (temp_u8 != 0); } else { loadbalancer_enabled = false; nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0); ESP_LOGW(TAG, "loadbalancer_enabled missing or invalid, setting default: 0"); needs_commit = true; } if (needs_commit) { nvs_commit(handle); } nvs_close(handle); return ESP_OK; } // Salva o estado habilitado no NVS void loadbalancer_set_enabled(bool enabled) { ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled); nvs_handle_t handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); return; } err = nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, enabled ? 1 : 0); if (err == ESP_OK) { nvs_commit(handle); loadbalancer_enabled = enabled; ESP_LOGI(TAG, "Load balancing enabled state saved"); loadbalancer_state_event_t evt = { .enabled = enabled, .timestamp_us = esp_timer_get_time()}; esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_STATE_CHANGED, &evt, sizeof(evt), portMAX_DELAY); } else { ESP_LOGE(TAG, "Failed to save loadbalancer_enabled"); } nvs_close(handle); } // Define e salva o limite de corrente da rede esp_err_t load_balancing_set_max_grid_current(uint8_t value) { if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT) { ESP_LOGE(TAG, "Invalid grid current limit: %d", value); return ESP_ERR_INVALID_ARG; } nvs_handle_t handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); return err; } err = nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, value); if (err == ESP_OK) { nvs_commit(handle); max_grid_current = value; ESP_LOGI(TAG, "max_grid_current set to: %d", value); } else { ESP_LOGE(TAG, "Failed to save max_grid_current to NVS"); } nvs_close(handle); return err; } uint8_t load_balancing_get_max_grid_current(void) { return max_grid_current; } bool loadbalancer_is_enabled(void) { return loadbalancer_enabled; } // Tarefa principal com eventos void loadbalancer_task(void *param) { while (true) { if (!loadbalancer_enabled) { vTaskDelay(pdMS_TO_TICKS(1000)); continue; } float available = max_grid_current - grid_current + evse_current; if (available < MIN_CHARGING_CURRENT_LIMIT) { available = MIN_CHARGING_CURRENT_LIMIT; } else if (available > max_grid_current) { available = max_grid_current; } ESP_LOGD(TAG, "Setting EVSE current limit: %.1f A", available); loadbalancer_charging_limit_event_t evt = { .limit = available, .timestamp_us = esp_timer_get_time()}; esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED, &evt, sizeof(evt), portMAX_DELAY); vTaskDelay(pdMS_TO_TICKS(1000)); } } void loadbalancer_init(void) { ESP_LOGI(TAG, "Initializing load balancer"); if (loadbalancer_load_config() != ESP_OK) { ESP_LOGW(TAG, "Failed to load/init config. Using in-memory defaults."); } input_filter_init(&grid_filter, 0.3f); input_filter_init(&evse_filter, 0.3f); if (xTaskCreate(loadbalancer_task, "loadbalancer", 4096, NULL, 4, NULL) != pdPASS) { ESP_LOGE(TAG, "Failed to create loadbalancer task"); } loadbalancer_state_event_t evt = { .enabled = loadbalancer_enabled, .timestamp_us = esp_timer_get_time()}; esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY, &loadbalancer_meter_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, &loadbalancer_evse_event_handler, NULL)); } // === Fim de: components/loadbalancer/src/loadbalancer.c === // === Início de: components/loadbalancer/src/input_filter.c === #include "input_filter.h" void input_filter_init(input_filter_t *filter, float alpha) { if (filter) { filter->alpha = alpha; filter->value = 0.0f; filter->initialized = 0; } } float input_filter_update(input_filter_t *filter, float input) { if (!filter) return input; if (!filter->initialized) { filter->value = input; filter->initialized = 1; } else { filter->value = filter->alpha * input + (1.0f - filter->alpha) * filter->value; } return filter->value; } // === Fim de: components/loadbalancer/src/input_filter.c === // === Início de: components/loadbalancer/include/loadbalancer_events.h === #pragma once #include "esp_event.h" #include #include #include "esp_timer.h" ESP_EVENT_DECLARE_BASE(LOADBALANCER_EVENTS); typedef enum { LOADBALANCER_EVENT_INIT, LOADBALANCER_EVENT_STATE_CHANGED, LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED } loadbalancer_event_id_t; typedef struct { float limit; int64_t timestamp_us; } loadbalancer_charging_limit_event_t; typedef struct { bool enabled; int64_t timestamp_us; } loadbalancer_state_event_t; // === Fim de: components/loadbalancer/include/loadbalancer_events.h === // === Início de: components/loadbalancer/include/loadbalancer.h === #ifndef LOADBALANCER_H_ #define LOADBALANCER_H_ #ifdef __cplusplus extern "C" { #endif #include #include #include "esp_err.h" /** * @brief Initializes the load balancer. * * This function configures the load balancer and its resources, including * any necessary persistence configurations, such as storage in NVS (Non-Volatile Storage). * This function prepares the system to perform load balancing efficiently. */ void loadbalancer_init(void); /** * @brief Continuous task for the load balancer. * * This function executes the load balancing logic continuously, typically in a FreeRTOS task. * It performs balance calculations, checks the grid current and energy conditions, and adjusts * the outputs as necessary to ensure efficient energy consumption. * * @param param Input parameter, usually used to pass additional information or relevant context * for the task execution. */ void loadbalancer_task(void *param); /** * @brief Enables or disables the load balancing system. * * This function allows enabling or disabling the load balancing system. When enabled, the load * balancer starts managing the grid current based on the configured limits. If disabled, the system * operates without balancing. * * The configuration is persisted in NVS, ensuring that the choice is maintained across system restarts. * * @param value If true, enables load balancing. If false, disables it. */ void loadbalancer_set_enabled(bool value); /** * @brief Checks if load balancing is enabled. * * This function returns the current status of the load balancing system. * * @return Returns true if load balancing is enabled, otherwise returns false. */ bool loadbalancer_is_enabled(void); /** * @brief Sets the maximum grid current. * * This function configures the maximum grid current that can be supplied to the load balancing system. * The value set ensures that the system does not overload the electrical infrastructure and respects * the safety limits. * * @param max_grid_current The maximum allowed current (in amperes) for the load balancing system. * This value should be appropriate for the grid capacity and the installation. */ esp_err_t load_balancing_set_max_grid_current(uint8_t max_grid_current); /** * @brief Gets the maximum grid current. * * This function retrieves the current maximum grid current limit. * * @return The maximum grid current (in amperes). */ uint8_t load_balancing_get_max_grid_current(void); #ifdef __cplusplus } #endif #endif /* LOADBALANCER_H_ */ // === Fim de: components/loadbalancer/include/loadbalancer.h === // === Início de: components/loadbalancer/include/input_filter.h === #pragma once #ifdef __cplusplus extern "C" { #endif typedef struct { float alpha; ///< Fator de suavização (0.0 a 1.0) float value; ///< Último valor filtrado int initialized; ///< Flag de inicialização } input_filter_t; /** * @brief Inicializa o filtro com o fator alpha desejado. * @param filter Ponteiro para a estrutura do filtro * @param alpha Valor entre 0.0 (mais lento) e 1.0 (sem filtro) */ void input_filter_init(input_filter_t *filter, float alpha); /** * @brief Atualiza o valor filtrado com uma nova entrada. * @param filter Ponteiro para o filtro * @param input Valor bruto * @return Valor suavizado */ float input_filter_update(input_filter_t *filter, float input); #ifdef __cplusplus } #endif // === Fim de: components/loadbalancer/include/input_filter.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 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); } static void timer_handler(void *arg) { 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); } //////////////////////////////////////////////////////////////////////////////// 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/auth/src/auth.c === /* * 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 #include #include #include "auth.h" #define CONFIG_EXAMPLE_BUF_SIZE 50 static const char *TAG = "WiegandReader"; static wiegand_reader_t reader; static QueueHandle_t queue = NULL; 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 wiegand_task(void *arg) { queue = xQueueCreate(5, sizeof(data_packet_t)); if (!queue) { ESP_LOGE(TAG, "Failed to create queue"); vTaskDelete(NULL); return; } ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18, true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); 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); char tag[20] = {0}; 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; } ESP_LOGI(TAG, "Tag read: %s", tag); auth_process_tag(tag); // agora delega toda a lógica à auth.c } } } void initWiegand(void) { ESP_LOGI(TAG, "Initializing Wiegand reader"); xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL); } // === Fim de: components/auth/src/wiegand_reader.c ===