2208 lines
64 KiB
C
2208 lines
64 KiB
C
.
|
|
|
|
// === Início de: main/main.c ===
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/event_groups.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "esp_err.h"
|
|
#include "esp_event.h"
|
|
#include "esp_netif.h"
|
|
#include "esp_spiffs.h"
|
|
#include "esp_system.h"
|
|
#include "nvs_flash.h"
|
|
#include "driver/gpio.h"
|
|
|
|
#include "network.h"
|
|
#include "board_config.h"
|
|
#include "logger.h"
|
|
#include "rest_main.h"
|
|
|
|
#include "peripherals.h"
|
|
#include "protocols.h"
|
|
#include "evse_manager.h"
|
|
#include "evse_core.h"
|
|
#include "auth.h"
|
|
#include "loadbalancer.h"
|
|
#include "meter_manager.h"
|
|
|
|
|
|
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
|
|
#define AP_CONNECTION_TIMEOUT 120000
|
|
#define RESET_HOLD_TIME 10000
|
|
#define DEBOUNCE_TIME_MS 50
|
|
|
|
#define PRESS_BIT BIT0
|
|
#define RELEASED_BIT BIT1
|
|
|
|
static const char *TAG = "app_main";
|
|
|
|
static TaskHandle_t user_input_task;
|
|
static TickType_t press_tick = 0;
|
|
static TickType_t last_interrupt_tick = 0;
|
|
static bool pressed = false;
|
|
|
|
|
|
//
|
|
// File system (SPIFFS) init and info
|
|
//
|
|
static void fs_info(esp_vfs_spiffs_conf_t *conf) {
|
|
size_t total = 0, used = 0;
|
|
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
|
|
if (ret == ESP_OK)
|
|
ESP_LOGI(TAG, "Partition %s: total: %d, used: %d", conf->partition_label, total, used);
|
|
else
|
|
ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
|
|
}
|
|
|
|
static void fs_init(void) {
|
|
esp_vfs_spiffs_conf_t cfg_conf = {
|
|
.base_path = "/cfg",
|
|
.partition_label = "cfg",
|
|
.max_files = 1,
|
|
.format_if_mount_failed = false
|
|
};
|
|
|
|
esp_vfs_spiffs_conf_t data_conf = {
|
|
.base_path = "/data",
|
|
.partition_label = "data",
|
|
.max_files = 5,
|
|
.format_if_mount_failed = true
|
|
};
|
|
|
|
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
|
|
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
|
|
|
|
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;
|
|
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) {
|
|
// 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();
|
|
}
|
|
}
|
|
} else if (mode_bits & WIFI_STA_MODE_BIT) {
|
|
// 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));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Button press handler
|
|
//
|
|
static void handle_button_press(void) {
|
|
// 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;
|
|
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, "Button Pressed");
|
|
handle_button_press();
|
|
}
|
|
// Handle button release event (only if previously pressed)
|
|
if ((notification & RELEASED_BIT) && pressed) {
|
|
pressed = false;
|
|
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();
|
|
|
|
// Debounce: ignore interrupts occurring too close together
|
|
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
|
|
return;
|
|
}
|
|
last_interrupt_tick = now;
|
|
|
|
// 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 {
|
|
// 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),
|
|
.mode = GPIO_MODE_INPUT,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
|
.intr_type = GPIO_INTR_ANYEDGE
|
|
};
|
|
ESP_ERROR_CHECK(gpio_config(&conf));
|
|
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
|
|
}
|
|
|
|
//
|
|
// Inicialização dos módulos do sistema
|
|
//
|
|
static void init_modules(void) {
|
|
peripherals_init();
|
|
//api_init();
|
|
ESP_ERROR_CHECK(rest_server_init("/data"));
|
|
protocols_init();
|
|
evse_manager_init();
|
|
evse_init(); // Cria a task para FSM
|
|
button_init();
|
|
auth_init();
|
|
loadbalancer_init();
|
|
meter_manager_init();
|
|
meter_manager_start();
|
|
|
|
// Outros módulos (descomente conforme necessário)
|
|
// meter_init();
|
|
// ocpp_start();
|
|
// orno_modbus_start();
|
|
// currentshaper_start();
|
|
// initWiegand();
|
|
// meter_zigbee_start();
|
|
// master_sync_start();
|
|
// slave_sync_start();
|
|
}
|
|
|
|
//
|
|
// Função principal do firmware
|
|
//
|
|
void app_main(void) {
|
|
logger_init();
|
|
esp_log_set_vprintf(logger_vprintf);
|
|
|
|
esp_reset_reason_t reason = esp_reset_reason();
|
|
ESP_LOGI(TAG, "Reset reason: %d", reason);
|
|
|
|
esp_err_t ret = nvs_flash_init();
|
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
ESP_LOGW(TAG, "Erasing NVS flash");
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
ret = nvs_flash_init();
|
|
}
|
|
ESP_ERROR_CHECK(ret);
|
|
|
|
fs_init();
|
|
ESP_ERROR_CHECK(esp_netif_init());
|
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
|
|
|
board_config_load();
|
|
wifi_ini();
|
|
//wifi_ap_start();
|
|
init_modules();
|
|
|
|
xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
|
|
xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task);
|
|
}
|
|
|
|
// === Fim de: main/main.c ===
|
|
|
|
|
|
// === Início de: components/rest_api/src/ocpp_api.c ===
|
|
// =========================
|
|
// ocpp_api.c
|
|
// =========================
|
|
#include "ocpp_api.h"
|
|
#include "esp_log.h"
|
|
#include "cJSON.h"
|
|
|
|
static const char *TAG = "ocpp_api";
|
|
|
|
static struct {
|
|
char url[256];
|
|
char chargeBoxId[128];
|
|
char certificate[256];
|
|
char privateKey[256];
|
|
} ocpp_config = {"", "", "", ""};
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
|
|
// === Fim de: components/rest_api/src/ocpp_api.c ===
|
|
|
|
|
|
// === Início de: components/rest_api/src/static_file_api.c ===
|
|
#include "static_file_api.h"
|
|
#include "esp_log.h"
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include "esp_vfs.h"
|
|
|
|
static const char *TAG = "static_file_api";
|
|
|
|
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
|
|
#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;
|
|
|
|
#define CHECK_FILE_EXTENSION(filename, ext) \
|
|
(strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
|
|
|
|
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 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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// === Fim de: components/rest_api/src/static_file_api.c ===
|
|
|
|
|
|
// === 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"
|
|
|
|
static const char *TAG = "meters_settings_api";
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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/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"
|
|
|
|
|
|
static const char *TAG = "rest_main";
|
|
|
|
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;
|
|
}
|
|
|
|
// === Fim de: components/rest_api/src/rest_main.c ===
|
|
|
|
|
|
// === Início de: components/rest_api/src/network_api.c ===
|
|
// =========================
|
|
// network_api.c
|
|
// =========================
|
|
|
|
#include "network_api.h"
|
|
#include "esp_log.h"
|
|
#include "cJSON.h"
|
|
#include "network.h"
|
|
#include "mqtt.h"
|
|
|
|
static const char *TAG = "network_api";
|
|
|
|
typedef struct {
|
|
bool enabled;
|
|
char ssid[33];
|
|
char password[65];
|
|
} wifi_task_data_t;
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
static esp_err_t wifi_get_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi");
|
|
|
|
httpd_resp_set_type(req, "application/json");
|
|
|
|
// 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 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);
|
|
|
|
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 <esp_err.h>
|
|
#include <esp_vfs.h>
|
|
|
|
#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 <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, 21, 22,
|
|
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 ===
|
|
|
|
|
|
// === Início de: components/auth/include/auth.h ===
|
|
#ifndef AUTH_H
|
|
#define AUTH_H
|
|
|
|
#include <stdbool.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
|
|
#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;
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
void auth_init(void);
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
void auth_set_enabled(bool value);
|
|
|
|
/**
|
|
* @brief Verifica se o sistema de autenticação está habilitado.
|
|
*/
|
|
bool auth_is_enabled(void);
|
|
|
|
/**
|
|
* @brief Adiciona uma nova tag RFID à lista de autorizadas.
|
|
*
|
|
* @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 auth_add_tag(const char *tag);
|
|
|
|
/**
|
|
* @brief Remove uma tag previamente cadastrada.
|
|
*
|
|
* @param tag String da tag
|
|
* @return true se foi removida, false se não encontrada
|
|
*/
|
|
bool auth_remove_tag(const char *tag);
|
|
|
|
/**
|
|
* @brief Verifica se uma tag já está registrada como válida.
|
|
*/
|
|
bool auth_tag_exists(const char *tag);
|
|
|
|
/**
|
|
* @brief Lista todas as tags válidas atualmente registradas (via logs).
|
|
*/
|
|
void auth_list_tags(void);
|
|
|
|
/**
|
|
* @brief Processa uma tag RFID lida (chamada normalmente pelo leitor).
|
|
*
|
|
* - Verifica validade
|
|
* - Emite evento AUTH_EVENT_TAG_PROCESSED
|
|
* - Inicia timer de expiração se autorizada
|
|
*/
|
|
void auth_process_tag(const char *tag);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // AUTH_H
|
|
|
|
// === Fim de: components/auth/include/auth.h ===
|
|
|
|
|
|
// === 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) 2021 Ruslan V. Uss <unclerus@gmail.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @file wiegand.h
|
|
* @defgroup wiegand wiegand
|
|
* @{
|
|
*
|
|
* ESP-IDF Wiegand protocol receiver
|
|
*
|
|
* Copyright (c) 2021 Ruslan V. Uss <unclerus@gmail.com>
|
|
*
|
|
* BSD Licensed as described in the file LICENSE
|
|
*/
|
|
#ifndef __WIEGAND_H__
|
|
#define __WIEGAND_H__
|
|
|
|
#include <driver/gpio.h>
|
|
#include <esp_err.h>
|
|
#include <esp_timer.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
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
|
|
|
|
/**@}*/
|
|
|
|
#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
|
|
}
|
|
#endif
|
|
|
|
#endif // WIEGAND_READER_H
|
|
|
|
// === Fim de: components/auth/include/wiegand_reader.h ===
|