1059 lines
29 KiB
C
Executable File
1059 lines
29 KiB
C
Executable File
|
|
|
|
// === 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 <stdint.h>
|
|
#include <stdbool.h>
|
|
#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 <string.h>
|
|
#include "meter_events.h"
|
|
#include "evse_events.h"
|
|
|
|
|
|
|
|
static const char *TAG = "loadbalancer";
|
|
|
|
// Limites configuráveis
|
|
#define MIN_CHARGING_CURRENT_LIMIT 6 // A
|
|
#define MAX_CHARGING_CURRENT_LIMIT 32 // A
|
|
#define MIN_GRID_CURRENT_LIMIT 6 // A
|
|
#define MAX_GRID_CURRENT_LIMIT 100 // A
|
|
|
|
// Parâmetros
|
|
static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
|
static bool loadbalancer_enabled = false;
|
|
|
|
static float grid_current = 0.0f;
|
|
static float evse_current = 0.0f;
|
|
static 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "esp_timer.h"
|
|
|
|
ESP_EVENT_DECLARE_BASE(LOADBALANCER_EVENTS);
|
|
|
|
typedef enum {
|
|
LOADBALANCER_EVENT_INIT,
|
|
LOADBALANCER_EVENT_STATE_CHANGED,
|
|
LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED
|
|
} loadbalancer_event_id_t;
|
|
|
|
typedef struct {
|
|
float limit;
|
|
int64_t timestamp_us;
|
|
} loadbalancer_charging_limit_event_t;
|
|
|
|
typedef struct {
|
|
bool enabled;
|
|
int64_t timestamp_us;
|
|
} loadbalancer_state_event_t;
|
|
|
|
|
|
// === 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "esp_err.h"
|
|
|
|
|
|
/**
|
|
* @brief Initializes the load balancer.
|
|
*
|
|
* This function configures the load balancer and its resources, including
|
|
* any necessary persistence configurations, such as storage in NVS (Non-Volatile Storage).
|
|
* This function prepares the system to perform load balancing efficiently.
|
|
*/
|
|
void loadbalancer_init(void);
|
|
|
|
/**
|
|
* @brief Continuous task for the load balancer.
|
|
*
|
|
* This function executes the load balancing logic continuously, typically in a FreeRTOS task.
|
|
* It performs balance calculations, checks the grid current and energy conditions, and adjusts
|
|
* the outputs as necessary to ensure efficient energy consumption.
|
|
*
|
|
* @param param Input parameter, usually used to pass additional information or relevant context
|
|
* for the task execution.
|
|
*/
|
|
void loadbalancer_task(void *param);
|
|
|
|
/**
|
|
* @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 <esp_log.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <esp_idf_lib_helpers.h>
|
|
#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 <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include <freertos/queue.h>
|
|
#include <esp_log.h>
|
|
#include <string.h>
|
|
#include "wiegand_reader.h"
|
|
#include "nvs_flash.h"
|
|
#include "nvs.h"
|
|
|
|
#define MAX_TAGS 50
|
|
|
|
static const char *TAG = "Auth";
|
|
|
|
static bool enabled = 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include <freertos/queue.h>
|
|
#include <esp_log.h>
|
|
#include <wiegand.h>
|
|
#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 ===
|