add ocpp
This commit is contained in:
@@ -47,7 +47,7 @@ cJSON *json_get_evse_config(void)
|
||||
cJSON_AddNumberToObject(root, "maxChargingCurrent", evse_get_max_charging_current());
|
||||
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
|
||||
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_charging_current());
|
||||
cJSON_AddBoolToObject(root, "requireAuth", auth_is_enabled());
|
||||
cJSON_AddBoolToObject(root, "requireAuth", auth_get_mode());
|
||||
cJSON_AddBoolToObject(root, "socketOutlet", evse_get_socket_outlet());
|
||||
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
|
||||
cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold());
|
||||
@@ -71,8 +71,8 @@ cJSON *json_get_evse_config(void)
|
||||
cJSON_AddBoolToObject(root, "enabledocpp", ocpp_get_enabled());
|
||||
ocpp_get_server(str);
|
||||
cJSON_AddStringToObject(root, "serverocpp", str);
|
||||
ocpp_get_rfid(str);
|
||||
cJSON_AddStringToObject(root, "rfid", str);
|
||||
//ocpp_get_rfid(str);
|
||||
//cJSON_AddStringToObject(root, "rfid", str);
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ esp_err_t json_set_evse_config(cJSON *root)
|
||||
}
|
||||
if (cJSON_IsBool(cJSON_GetObjectItem(root, "requireAuth")))
|
||||
{
|
||||
auth_set_enabled(cJSON_IsTrue(cJSON_GetObjectItem(root, "requireAuth")));
|
||||
//auth_set_mode(cJSON_IsTrue(cJSON_GetObjectItem(root, "requireAuth")));
|
||||
}
|
||||
if (cJSON_IsBool(cJSON_GetObjectItem(root, "socketOutlet")))
|
||||
{
|
||||
@@ -176,10 +176,10 @@ esp_err_t json_set_evse_config(cJSON *root)
|
||||
ocpp_set_server(cJSON_GetStringValue(cJSON_GetObjectItem(root, "serverocpp")));
|
||||
}
|
||||
|
||||
if (cJSON_IsString(cJSON_GetObjectItem(root, "rfid")))
|
||||
{
|
||||
ocpp_set_rfid(cJSON_GetStringValue(cJSON_GetObjectItem(root, "rfid")));
|
||||
}
|
||||
//if (cJSON_IsString(cJSON_GetObjectItem(root, "rfid")))
|
||||
//{
|
||||
// ocpp_set_rfid(cJSON_GetStringValue(cJSON_GetObjectItem(root, "rfid")));
|
||||
//}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -343,7 +343,7 @@ cJSON *json_get_state(void)
|
||||
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
|
||||
cJSON_AddBoolToObject(root, "available", evse_config_is_available());
|
||||
cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled());
|
||||
cJSON_AddBoolToObject(root, "pendingAuth", auth_is_enabled());
|
||||
cJSON_AddBoolToObject(root, "pendingAuth", auth_get_mode());
|
||||
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
|
||||
|
||||
uint32_t error = evse_error_get_bits();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set(srcs "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c" "src/auth_events.c")
|
||||
set(srcs "src/auth_types.c" "src/auth.c" "src/wiegand.c" "src/wiegand_reader.c" "src/auth_events.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
|
||||
@@ -1,115 +1,32 @@
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include "auth_types.h" // enum + MAX LEN para API
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Maximum length of an RFID tag (including null terminator)
|
||||
#define AUTH_TAG_MAX_LEN 30
|
||||
|
||||
/// Event structure emitted after a tag is read
|
||||
/* Evento auxiliar legado/útil (resultado local) */
|
||||
typedef struct {
|
||||
char tag[AUTH_TAG_MAX_LEN]; ///< The tag that was read
|
||||
bool authorized; ///< true if the tag is valid
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
bool authorized;
|
||||
} auth_event_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes the authentication system.
|
||||
*
|
||||
* - Loads configuration (enabled/disabled) from NVS
|
||||
* - Starts the Wiegand reader
|
||||
* - Emits AUTH_EVENT_INIT with current status
|
||||
*/
|
||||
void auth_init(void);
|
||||
void auth_init(void);
|
||||
void auth_set_mode(auth_mode_t mode);
|
||||
auth_mode_t auth_get_mode(void);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables RFID-based authentication.
|
||||
*
|
||||
* This setting is persisted in NVS. If disabled,
|
||||
* all tags will be treated as authorized.
|
||||
*
|
||||
* @param value true to enable authentication, false to disable
|
||||
*/
|
||||
void auth_set_enabled(bool value);
|
||||
|
||||
/**
|
||||
* @brief Checks whether authentication is currently enabled.
|
||||
*
|
||||
* @return true if enabled, false if disabled
|
||||
*/
|
||||
bool auth_is_enabled(void);
|
||||
|
||||
/**
|
||||
* @brief Adds a new RFID tag to the authorized list.
|
||||
*
|
||||
* @param tag The RFID tag (max AUTH_TAG_MAX_LEN-1 characters)
|
||||
* @return true if the tag was added successfully,
|
||||
* false if it already exists or is invalid
|
||||
*/
|
||||
bool auth_add_tag(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Removes an existing RFID tag from the authorized list.
|
||||
*
|
||||
* @param tag The tag to remove
|
||||
* @return true if the tag was removed, false if not found
|
||||
*/
|
||||
bool auth_remove_tag(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a tag is already registered.
|
||||
*
|
||||
* @param tag The tag to check
|
||||
* @return true if the tag exists, false otherwise
|
||||
*/
|
||||
bool auth_tag_exists(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Logs all currently registered tags via ESP logging.
|
||||
*/
|
||||
void auth_list_tags(void);
|
||||
|
||||
/**
|
||||
* @brief Processes a read RFID tag.
|
||||
*
|
||||
* - Checks whether it's authorized
|
||||
* - Emits AUTH_EVENT_TAG_PROCESSED event
|
||||
* - Starts expiration timer if authorized
|
||||
*
|
||||
* @param tag The tag that was read
|
||||
*/
|
||||
void auth_process_tag(const char *tag);
|
||||
|
||||
/**
|
||||
* @brief Enables registration mode for the next tag read.
|
||||
*
|
||||
* When registration mode is active, the next tag read
|
||||
* will be added to the authorized list automatically.
|
||||
* Mode is deactivated after one tag is registered.
|
||||
*/
|
||||
void auth_wait_for_tag_registration(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the total number of registered tags.
|
||||
*
|
||||
* @return Number of valid tags
|
||||
*/
|
||||
int auth_get_tag_count(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the tag string at the given index.
|
||||
*
|
||||
* @param index The index (0 ≤ index < auth_get_tag_count())
|
||||
* @return Pointer to the tag string, or NULL if index is invalid
|
||||
*/
|
||||
int auth_get_tag_count(void);
|
||||
const char *auth_get_tag_by_index(int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // AUTH_H
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
#pragma once
|
||||
#include "esp_event.h"
|
||||
|
||||
#define AUTH_EVENT_TAG_MAX_LEN 32
|
||||
#include "auth_types.h" // só tipos comuns; evita incluir auth.h
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
|
||||
|
||||
/* IDs de eventos */
|
||||
typedef enum {
|
||||
AUTH_EVENT_TAG_PROCESSED,
|
||||
AUTH_EVENT_TAG_SAVED,
|
||||
AUTH_EVENT_ENABLED_CHANGED,
|
||||
AUTH_EVENT_INIT,
|
||||
AUTH_EVENT_TAG_PROCESSED = 0, // resultado LOCAL -> auth_tag_event_data_t
|
||||
AUTH_EVENT_TAG_VERIFY, // pedir validação OCPP -> auth_tag_verify_event_t
|
||||
AUTH_EVENT_TAG_SAVED, // registada (modo registo) -> auth_tag_event_data_t
|
||||
AUTH_EVENT_MODE_CHANGED, // modo alterado -> auth_mode_event_data_t
|
||||
AUTH_EVENT_INIT, // estado inicial -> auth_mode_event_data_t
|
||||
} auth_event_id_t;
|
||||
|
||||
/* Payloads */
|
||||
typedef struct {
|
||||
char tag[AUTH_EVENT_TAG_MAX_LEN];
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
bool authorized;
|
||||
} auth_tag_event_data_t;
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
} auth_enabled_event_data_t;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
uint32_t req_id; // opcional p/ correlacionar
|
||||
} auth_tag_verify_event_t;
|
||||
|
||||
typedef struct {
|
||||
auth_mode_t mode;
|
||||
} auth_mode_event_data_t;
|
||||
|
||||
26
components/auth/include/auth_types.h
Normal file
26
components/auth/include/auth_types.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Tamanho máx. da tag (inclui NUL) */
|
||||
#define AUTH_TAG_MAX_LEN 30
|
||||
|
||||
/* Modos de autorização */
|
||||
typedef enum {
|
||||
AUTH_MODE_OPEN = 0, // Sem autenticação
|
||||
AUTH_MODE_LOCAL_RFID, // Lista local (NVS)
|
||||
AUTH_MODE_OCPP_RFID // Validação via OCPP/CSMS
|
||||
} auth_mode_t;
|
||||
|
||||
/* Converte enum -> "open"|"local"|"ocpp" (nunca NULL) */
|
||||
const char *auth_mode_to_str(auth_mode_t mode);
|
||||
|
||||
/* Converte "open"|"local"|"ocpp" (case-insensitive) -> enum */
|
||||
bool auth_mode_from_str(const char *s, auth_mode_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,62 +1,40 @@
|
||||
// components/auth/src/auth.c
|
||||
|
||||
#include "auth.h"
|
||||
#include "auth_events.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> // <-- necessário para strcasecmp
|
||||
#include "wiegand_reader.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
|
||||
#define MAX_TAGS 50
|
||||
|
||||
static const char *TAG = "Auth";
|
||||
|
||||
static bool enabled = false;
|
||||
/* ===== Estado ===== */
|
||||
static auth_mode_t s_mode = AUTH_MODE_OPEN;
|
||||
static bool waiting_for_registration = false;
|
||||
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
||||
static int tag_count = 0;
|
||||
static uint32_t s_next_req_id = 1;
|
||||
|
||||
// NVS keys
|
||||
/* ===== NVS keys ===== */
|
||||
#define NVS_NAMESPACE "auth"
|
||||
#define NVS_TAG_PREFIX "tag_"
|
||||
#define NVS_TAG_COUNT_KEY "count"
|
||||
#define NVS_ENABLED_KEY "enabled"
|
||||
|
||||
// ===========================
|
||||
// NVS Persistence
|
||||
// ===========================
|
||||
|
||||
static void load_auth_config(void) {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
|
||||
if (err == ESP_OK) {
|
||||
uint8_t val;
|
||||
if (nvs_get_u8(handle, NVS_ENABLED_KEY, &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(NVS_NAMESPACE, NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_u8(handle, NVS_ENABLED_KEY, enabled);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to save auth config.");
|
||||
}
|
||||
}
|
||||
#define NVS_MODE_KEY "mode" // uint8_t
|
||||
|
||||
/* =========================
|
||||
* NVS Persistence (tags)
|
||||
* ========================= */
|
||||
static void load_tags_from_nvs(void) {
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
|
||||
@@ -71,7 +49,6 @@ static void load_tags_from_nvs(void) {
|
||||
}
|
||||
|
||||
tag_count = 0;
|
||||
|
||||
for (int i = 0; i < count && i < MAX_TAGS; i++) {
|
||||
char key[16];
|
||||
char tag_buf[AUTH_TAG_MAX_LEN];
|
||||
@@ -109,10 +86,38 @@ static void save_tags_to_nvs(void) {
|
||||
ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count);
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// Internal Helpers
|
||||
// ===========================
|
||||
/* =========================
|
||||
* NVS Persistence (mode)
|
||||
* ========================= */
|
||||
static void load_mode_from_nvs(void) {
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &h) == ESP_OK) {
|
||||
uint8_t u = (uint8_t)AUTH_MODE_OPEN;
|
||||
if (nvs_get_u8(h, NVS_MODE_KEY, &u) == ESP_OK) {
|
||||
if (u <= (uint8_t)AUTH_MODE_OCPP_RFID) s_mode = (auth_mode_t)u;
|
||||
}
|
||||
nvs_close(h);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No stored auth mode in NVS; default OPEN");
|
||||
}
|
||||
ESP_LOGI(TAG, "Loaded mode = %d (%s)", (int)s_mode, auth_mode_to_str(s_mode));
|
||||
}
|
||||
|
||||
static void save_mode_to_nvs(auth_mode_t mode) {
|
||||
nvs_handle_t h;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h) == ESP_OK) {
|
||||
nvs_set_u8(h, NVS_MODE_KEY, (uint8_t)mode);
|
||||
nvs_commit(h);
|
||||
nvs_close(h);
|
||||
ESP_LOGI(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to save auth mode to NVS");
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Helpers
|
||||
* ========================= */
|
||||
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) {
|
||||
@@ -122,44 +127,74 @@ static bool is_tag_valid(const char *tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// Public API
|
||||
// ===========================
|
||||
|
||||
|
||||
/* =========================
|
||||
* Public API
|
||||
* ========================= */
|
||||
void auth_init(void) {
|
||||
load_auth_config();
|
||||
load_mode_from_nvs();
|
||||
load_tags_from_nvs();
|
||||
|
||||
if (enabled) {
|
||||
if (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID) {
|
||||
initWiegand();
|
||||
ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)");
|
||||
ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Auth disabled, Wiegand reader not started");
|
||||
ESP_LOGI(TAG, "Mode OPEN: Wiegand not started");
|
||||
}
|
||||
|
||||
auth_enabled_event_data_t evt = { .enabled = enabled };
|
||||
auth_mode_event_data_t evt = { .mode = s_mode };
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Initial AUTH state sent (enabled = %d)", enabled);
|
||||
ESP_LOGI(TAG, "AUTH INIT sent (mode=%s)", auth_mode_to_str(s_mode));
|
||||
}
|
||||
|
||||
void auth_set_enabled(bool value) {
|
||||
enabled = value;
|
||||
save_auth_config();
|
||||
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");
|
||||
void auth_set_mode(auth_mode_t mode) {
|
||||
if (mode < AUTH_MODE_OPEN || mode > AUTH_MODE_OCPP_RFID) {
|
||||
ESP_LOGW(TAG, "Invalid mode: %d", (int)mode);
|
||||
return;
|
||||
}
|
||||
if (mode == s_mode) {
|
||||
ESP_LOGI(TAG, "Mode unchanged: %s", auth_mode_to_str(mode));
|
||||
return;
|
||||
}
|
||||
|
||||
auth_enabled_event_data_t event = { .enabled = enabled };
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY);
|
||||
s_mode = mode;
|
||||
save_mode_to_nvs(mode);
|
||||
|
||||
// Nota: se precisares, aqui podes parar/iniciar o Wiegand consoante o modo.
|
||||
if (s_mode == AUTH_MODE_OPEN) {
|
||||
ESP_LOGI(TAG, "Mode set to OPEN");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mode set to %s; ensure Wiegand reader is running", auth_mode_to_str(s_mode));
|
||||
}
|
||||
|
||||
auth_mode_event_data_t evt = { .mode = s_mode };
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_MODE_CHANGED, &evt, sizeof(evt), portMAX_DELAY);
|
||||
}
|
||||
|
||||
bool auth_is_enabled(void) {
|
||||
return enabled;
|
||||
auth_mode_t auth_get_mode(void) {
|
||||
return s_mode;
|
||||
}
|
||||
|
||||
const char *auth_mode_to_str(auth_mode_t mode) {
|
||||
switch (mode) {
|
||||
case AUTH_MODE_OPEN: return "open";
|
||||
case AUTH_MODE_LOCAL_RFID: return "local";
|
||||
case AUTH_MODE_OCPP_RFID: return "ocpp";
|
||||
default: return "open";
|
||||
}
|
||||
}
|
||||
|
||||
bool auth_mode_from_str(const char *s, auth_mode_t *out) {
|
||||
if (!s || !out) return false;
|
||||
if (!strcasecmp(s, "open")) { *out = AUTH_MODE_OPEN; return true; }
|
||||
if (!strcasecmp(s, "local")) { *out = AUTH_MODE_LOCAL_RFID; return true; }
|
||||
if (!strcasecmp(s, "ocpp")) { *out = AUTH_MODE_OCPP_RFID; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
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; // Already exists
|
||||
if (is_tag_valid(tag)) return true; // já existe
|
||||
|
||||
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
|
||||
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
@@ -198,40 +233,62 @@ void auth_list_tags(void) {
|
||||
}
|
||||
|
||||
void auth_wait_for_tag_registration(void) {
|
||||
if (s_mode != AUTH_MODE_LOCAL_RFID) {
|
||||
ESP_LOGW(TAG, "Registration is only available in LOCAL mode");
|
||||
return;
|
||||
}
|
||||
waiting_for_registration = true;
|
||||
ESP_LOGI(TAG, "Tag registration mode enabled.");
|
||||
}
|
||||
|
||||
void auth_process_tag(const char *tag) {
|
||||
if (!tag || !auth_is_enabled()) {
|
||||
ESP_LOGW(TAG, "Auth disabled or NULL tag received.");
|
||||
if (!tag || !*tag) {
|
||||
ESP_LOGW(TAG, "NULL/empty tag received");
|
||||
return;
|
||||
}
|
||||
|
||||
if (waiting_for_registration) {
|
||||
if (auth_add_tag(tag)) {
|
||||
auth_tag_event_data_t event;
|
||||
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
|
||||
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
|
||||
event.authorized = true;
|
||||
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &event, sizeof(event), portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Tag registered: %s", tag);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
|
||||
switch (s_mode) {
|
||||
case AUTH_MODE_OPEN: {
|
||||
// Sem verificação; normalmente nem é necessário evento.
|
||||
ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_MODE_LOCAL_RFID: {
|
||||
if (waiting_for_registration) {
|
||||
waiting_for_registration = false;
|
||||
|
||||
if (auth_add_tag(tag)) {
|
||||
auth_tag_event_data_t ev = {0};
|
||||
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
ev.authorized = true;
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &ev, sizeof(ev), portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Tag registered: %s", tag);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auth_tag_event_data_t ev = {0};
|
||||
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
ev.authorized = is_tag_valid(tag);
|
||||
|
||||
ESP_LOGI(TAG, "LOCAL tag %s: %s", tag, ev.authorized ? "AUTHORIZED" : "DENIED");
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &ev, sizeof(ev), portMAX_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_MODE_OCPP_RFID: {
|
||||
// Não decide localmente. Pede validação ao OCPP.
|
||||
auth_tag_verify_event_t rq = {0};
|
||||
strncpy(rq.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
rq.req_id = s_next_req_id++;
|
||||
ESP_LOGI(TAG, "OCPP VERIFY requested for tag=%s (req_id=%u)", rq.tag, (unsigned)rq.req_id);
|
||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY, &rq, sizeof(rq), portMAX_DELAY);
|
||||
break;
|
||||
}
|
||||
waiting_for_registration = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auth_tag_event_data_t event;
|
||||
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
|
||||
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
|
||||
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);
|
||||
}
|
||||
|
||||
int auth_get_tag_count(void) {
|
||||
|
||||
19
components/auth/src/auth_types.c
Executable file
19
components/auth/src/auth_types.c
Executable file
@@ -0,0 +1,19 @@
|
||||
#include "auth_types.h"
|
||||
#include <strings.h> // strcasecmp
|
||||
|
||||
const char *auth_mode_to_str(auth_mode_t mode) {
|
||||
switch (mode) {
|
||||
case AUTH_MODE_OPEN: return "open";
|
||||
case AUTH_MODE_LOCAL_RFID: return "local";
|
||||
case AUTH_MODE_OCPP_RFID: return "ocpp";
|
||||
default: return "open";
|
||||
}
|
||||
}
|
||||
|
||||
bool auth_mode_from_str(const char *s, auth_mode_t *out) {
|
||||
if (!s || !out) return false;
|
||||
if (!strcasecmp(s, "open")) { *out = AUTH_MODE_OPEN; return true; }
|
||||
if (!strcasecmp(s, "local")) { *out = AUTH_MODE_LOCAL_RFID; return true; }
|
||||
if (!strcasecmp(s, "ocpp")) { *out = AUTH_MODE_OCPP_RFID; return true; }
|
||||
return false;
|
||||
}
|
||||
@@ -6,10 +6,42 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h" // Para buzzer passivo
|
||||
|
||||
#define BUZZER_GPIO GPIO_NUM_27
|
||||
static const char *TAG = "Buzzer";
|
||||
|
||||
// --- Configuração de modo ---
|
||||
typedef enum {
|
||||
BUZZER_MODE_ACTIVE = 0,
|
||||
BUZZER_MODE_PASSIVE
|
||||
} buzzer_mode_t;
|
||||
|
||||
typedef struct {
|
||||
buzzer_mode_t mode;
|
||||
gpio_num_t gpio;
|
||||
|
||||
// Apenas para PASSIVE
|
||||
uint32_t freq_hz; // ex: 4000
|
||||
uint8_t duty_percent; // 0–100
|
||||
ledc_mode_t ledc_speed_mode; // LEDC_LOW_SPEED_MODE
|
||||
ledc_timer_t ledc_timer; // LEDC_TIMER_1
|
||||
ledc_channel_t ledc_channel; // LEDC_CHANNEL_1
|
||||
ledc_timer_bit_t duty_resolution; // LEDC_TIMER_10_BIT
|
||||
} buzzer_config_t;
|
||||
|
||||
static buzzer_config_t s_buzzer_cfg = {
|
||||
.mode = BUZZER_MODE_PASSIVE, // Mude para PASSIVE se for buzzer passivo
|
||||
.gpio = BUZZER_GPIO,
|
||||
.freq_hz = 4000,
|
||||
.duty_percent = 50,
|
||||
.ledc_speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.ledc_timer = LEDC_TIMER_1,
|
||||
.ledc_channel = LEDC_CHANNEL_1,
|
||||
.duty_resolution = LEDC_TIMER_10_BIT
|
||||
};
|
||||
|
||||
// --- Estrutura de passos ---
|
||||
typedef struct {
|
||||
uint16_t on_ms;
|
||||
uint16_t off_ms;
|
||||
@@ -39,9 +71,37 @@ static const buzzer_pattern_t buzzer_patterns[] = {
|
||||
[BUZZER_PATTERN_CARD_DENIED] = {pattern_card_denied, sizeof(pattern_card_denied) / sizeof(buzzer_step_t)},
|
||||
};
|
||||
|
||||
static void buzzer_on(void) { gpio_set_level(BUZZER_GPIO, 1); }
|
||||
static void buzzer_off(void) { gpio_set_level(BUZZER_GPIO, 0); }
|
||||
// --- Funções de controle ---
|
||||
static inline uint32_t duty_from_percent(uint8_t pct, ledc_timer_bit_t res) {
|
||||
if (pct == 0) return 0;
|
||||
uint32_t max = (1U << res) - 1U;
|
||||
return (uint32_t)((pct * max) / 100U);
|
||||
}
|
||||
|
||||
static void buzzer_on(void) {
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE) {
|
||||
ledc_set_freq(s_buzzer_cfg.ledc_speed_mode,
|
||||
s_buzzer_cfg.ledc_timer,
|
||||
s_buzzer_cfg.freq_hz);
|
||||
ledc_set_duty(s_buzzer_cfg.ledc_speed_mode,
|
||||
s_buzzer_cfg.ledc_channel,
|
||||
duty_from_percent(s_buzzer_cfg.duty_percent, s_buzzer_cfg.duty_resolution));
|
||||
ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
|
||||
} else {
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void buzzer_off(void) {
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE) {
|
||||
ledc_set_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, 0);
|
||||
ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
|
||||
} else {
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Execução de padrões ---
|
||||
static void buzzer_execute(buzzer_pattern_id_t pattern_id) {
|
||||
if ((int)pattern_id <= BUZZER_PATTERN_NONE || pattern_id >= BUZZER_PATTERN_MAX) {
|
||||
ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
|
||||
@@ -60,6 +120,7 @@ static void buzzer_execute(buzzer_pattern_id_t pattern_id) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Event Handlers ---
|
||||
static void buzzer_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
|
||||
if (base != BUZZER_EVENTS || id != BUZZER_EVENT_PLAY_PATTERN || data == NULL) return;
|
||||
|
||||
@@ -107,7 +168,6 @@ static void network_event_handler(void *handler_args, esp_event_base_t base, int
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data) {
|
||||
if (base != AUTH_EVENTS || event_data == NULL) return;
|
||||
|
||||
@@ -117,7 +177,6 @@ static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, voi
|
||||
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)event_data;
|
||||
ESP_LOGI(TAG, "AUTH processed: tag=%s authorized=%d", evt->tag, evt->authorized);
|
||||
buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;
|
||||
|
||||
} else if (id == AUTH_EVENT_TAG_SAVED) {
|
||||
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
|
||||
} else {
|
||||
@@ -127,15 +186,41 @@ static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, voi
|
||||
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
|
||||
}
|
||||
|
||||
// --- Inicialização ---
|
||||
void buzzer_init(void) {
|
||||
gpio_config_t io = {
|
||||
.pin_bit_mask = BIT64(BUZZER_GPIO),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&io);
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE) {
|
||||
// Configura temporizador do PWM
|
||||
ledc_timer_config_t tcfg = {
|
||||
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
|
||||
.duty_resolution = s_buzzer_cfg.duty_resolution,
|
||||
.timer_num = s_buzzer_cfg.ledc_timer,
|
||||
.freq_hz = s_buzzer_cfg.freq_hz,
|
||||
.clk_cfg = LEDC_AUTO_CLK
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&tcfg));
|
||||
|
||||
// Configura canal PWM
|
||||
ledc_channel_config_t ccfg = {
|
||||
.gpio_num = s_buzzer_cfg.gpio,
|
||||
.speed_mode = s_buzzer_cfg.ledc_speed_mode,
|
||||
.channel = s_buzzer_cfg.ledc_channel,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = s_buzzer_cfg.ledc_timer,
|
||||
.duty = 0,
|
||||
.hpoint = 0
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ccfg));
|
||||
} else {
|
||||
gpio_config_t io = {
|
||||
.pin_bit_mask = (1ULL << s_buzzer_cfg.gpio),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&io);
|
||||
}
|
||||
|
||||
buzzer_off();
|
||||
|
||||
// Registro de handlers
|
||||
@@ -155,14 +240,12 @@ void buzzer_init(void) {
|
||||
AUTH_EVENT_TAG_SAVED,
|
||||
auth_event_handler,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS,
|
||||
NETWORK_EVENT_AP_STARTED,
|
||||
network_event_handler,
|
||||
NULL));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||
NETWORK_EVENTS,
|
||||
NETWORK_EVENT_AP_STARTED,
|
||||
network_event_handler,
|
||||
NULL
|
||||
));
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d", BUZZER_GPIO);
|
||||
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d (%s)",
|
||||
s_buzzer_cfg.gpio,
|
||||
s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE ? "passive/PWM" : "active/ON-OFF");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include <inttypes.h> // For PRI macros
|
||||
#include <inttypes.h> // For PRI macros
|
||||
#include "evse_config.h"
|
||||
#include "board_config.h"
|
||||
#include "evse_limits.h"
|
||||
#include "evse_api.h" // <— para evse_get_state / evse_state_is_charging
|
||||
#include "esp_log.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_timer.h"
|
||||
@@ -13,50 +14,63 @@ static nvs_handle_t nvs;
|
||||
// ========================
|
||||
// Configurable parameters
|
||||
// ========================
|
||||
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||
static uint16_t charging_current; // Persisted (NVS)
|
||||
static uint16_t charging_current_runtime = 0; // Runtime only
|
||||
static bool socket_outlet;
|
||||
static bool rcm;
|
||||
static uint8_t temp_threshold = 60;
|
||||
static bool require_auth;
|
||||
static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||
static uint16_t charging_current; // Persisted (NVS)
|
||||
static uint16_t charging_current_runtime = 0; // Runtime only
|
||||
static bool socket_outlet;
|
||||
static bool rcm;
|
||||
static uint8_t temp_threshold = 60;
|
||||
static bool require_auth;
|
||||
|
||||
// Availability / Enable flags
|
||||
static bool is_available = true;
|
||||
static bool is_enabled = true;
|
||||
|
||||
// ========================
|
||||
// Initialization
|
||||
// ========================
|
||||
esp_err_t evse_config_init(void) {
|
||||
esp_err_t evse_config_init(void)
|
||||
{
|
||||
ESP_LOGD(TAG, "Initializing NVS configuration...");
|
||||
return nvs_open("evse", NVS_READWRITE, &nvs);
|
||||
}
|
||||
|
||||
void evse_check_defaults(void) {
|
||||
void evse_check_defaults(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
bool needs_commit = false;
|
||||
uint8_t u8_bool;
|
||||
|
||||
ESP_LOGD(TAG, "Checking default parameters...");
|
||||
|
||||
// Max charging current
|
||||
err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
|
||||
if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) {
|
||||
if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT)
|
||||
{
|
||||
max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
|
||||
needs_commit = true;
|
||||
ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
max_charging_current = u8;
|
||||
}
|
||||
|
||||
// Charging current (default, persisted)
|
||||
err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
|
||||
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current)) {
|
||||
if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current))
|
||||
{
|
||||
charging_current = max_charging_current;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
||||
needs_commit = true;
|
||||
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
charging_current = u16;
|
||||
}
|
||||
|
||||
@@ -67,7 +81,8 @@ void evse_check_defaults(void) {
|
||||
// Auth required
|
||||
err = nvs_get_u8(nvs, "require_auth", &u8);
|
||||
require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
nvs_set_u8(nvs, "require_auth", require_auth);
|
||||
needs_commit = true;
|
||||
}
|
||||
@@ -75,7 +90,8 @@ void evse_check_defaults(void) {
|
||||
// Socket outlet
|
||||
err = nvs_get_u8(nvs, "socket_outlet", &u8);
|
||||
socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
nvs_set_u8(nvs, "socket_outlet", socket_outlet);
|
||||
needs_commit = true;
|
||||
}
|
||||
@@ -83,7 +99,8 @@ void evse_check_defaults(void) {
|
||||
// RCM
|
||||
err = nvs_get_u8(nvs, "rcm", &u8);
|
||||
rcm = (err == ESP_OK && u8) && board_config.rcm;
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
nvs_set_u8(nvs, "rcm", rcm);
|
||||
needs_commit = true;
|
||||
}
|
||||
@@ -91,7 +108,8 @@ void evse_check_defaults(void) {
|
||||
// Temp threshold
|
||||
err = nvs_get_u8(nvs, "temp_threshold", &u8);
|
||||
temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
nvs_set_u8(nvs, "temp_threshold", temp_threshold);
|
||||
needs_commit = true;
|
||||
}
|
||||
@@ -106,12 +124,42 @@ void evse_check_defaults(void) {
|
||||
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
|
||||
evse_set_under_power_limit(u16);
|
||||
|
||||
// Availability (persist)
|
||||
if (nvs_get_u8(nvs, "available", &u8_bool) == ESP_OK && u8_bool <= 1)
|
||||
{
|
||||
is_available = (u8_bool != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_available = true; // default
|
||||
nvs_set_u8(nvs, "available", (uint8_t)is_available);
|
||||
needs_commit = true;
|
||||
ESP_LOGW(TAG, "Missing 'available' -> default=true (persisted).");
|
||||
}
|
||||
|
||||
// Enabled (persist)
|
||||
if (nvs_get_u8(nvs, "enabled", &u8_bool) == ESP_OK && u8_bool <= 1)
|
||||
{
|
||||
is_enabled = (u8_bool != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_enabled = true; // default
|
||||
nvs_set_u8(nvs, "enabled", (uint8_t)is_enabled);
|
||||
needs_commit = true;
|
||||
ESP_LOGW(TAG, "Missing 'enabled' -> default=true (persisted).");
|
||||
}
|
||||
|
||||
// Save to NVS if needed
|
||||
if (needs_commit) {
|
||||
if (needs_commit)
|
||||
{
|
||||
err = nvs_commit(nvs);
|
||||
if (err == ESP_OK) {
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Configuration committed to NVS.");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
@@ -120,11 +168,13 @@ void evse_check_defaults(void) {
|
||||
// ========================
|
||||
// Charging current getters/setters
|
||||
// ========================
|
||||
uint8_t evse_get_max_charging_current(void) {
|
||||
uint8_t evse_get_max_charging_current(void)
|
||||
{
|
||||
return max_charging_current;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_max_charging_current(uint8_t value) {
|
||||
esp_err_t evse_set_max_charging_current(uint8_t value)
|
||||
{
|
||||
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
max_charging_current = value;
|
||||
@@ -133,11 +183,13 @@ esp_err_t evse_set_max_charging_current(uint8_t value) {
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
uint16_t evse_get_charging_current(void) {
|
||||
uint16_t evse_get_charging_current(void)
|
||||
{
|
||||
return charging_current;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_charging_current(uint16_t value) {
|
||||
esp_err_t evse_set_charging_current(uint16_t value)
|
||||
{
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
charging_current = value;
|
||||
@@ -145,14 +197,16 @@ esp_err_t evse_set_charging_current(uint16_t value) {
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
uint16_t evse_get_default_charging_current(void) {
|
||||
uint16_t evse_get_default_charging_current(void)
|
||||
{
|
||||
uint16_t value;
|
||||
if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
|
||||
return value;
|
||||
return charging_current;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_default_charging_current(uint16_t value) {
|
||||
esp_err_t evse_set_default_charging_current(uint16_t value)
|
||||
{
|
||||
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
||||
@@ -162,49 +216,51 @@ esp_err_t evse_set_default_charging_current(uint16_t value) {
|
||||
// ========================
|
||||
// Runtime current (not saved)
|
||||
// ========================
|
||||
void evse_set_runtime_charging_current(uint16_t value) {
|
||||
void evse_set_runtime_charging_current(uint16_t value)
|
||||
{
|
||||
|
||||
ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
|
||||
|
||||
if (value > max_charging_current) {
|
||||
if (value > max_charging_current)
|
||||
{
|
||||
value = max_charging_current;
|
||||
} else if (value < MIN_CHARGING_CURRENT_LIMIT) {
|
||||
}
|
||||
else if (value < MIN_CHARGING_CURRENT_LIMIT)
|
||||
{
|
||||
value = MIN_CHARGING_CURRENT_LIMIT;
|
||||
}
|
||||
|
||||
charging_current_runtime = value;
|
||||
|
||||
ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
|
||||
|
||||
// --- PUBLICA ALTERAÇÃO DE CONFIG DO EVSE ---
|
||||
evse_config_event_data_t evt = {
|
||||
.charging = evse_state_is_charging(evse_get_state()),
|
||||
.hw_max_current = (float)evse_get_max_charging_current(),
|
||||
.runtime_current = (float)charging_current_runtime,
|
||||
.timestamp_us = esp_timer_get_time()
|
||||
};
|
||||
.charging = evse_state_is_charging(evse_get_state()),
|
||||
.hw_max_current = (float)evse_get_max_charging_current(),
|
||||
.runtime_current = (float)evse_get_runtime_charging_current(),
|
||||
.timestamp_us = esp_timer_get_time()};
|
||||
|
||||
esp_event_post(EVSE_EVENTS,
|
||||
EVSE_EVENT_CONFIG_UPDATED,
|
||||
&evt,
|
||||
sizeof(evt),
|
||||
portMAX_DELAY);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint16_t evse_get_runtime_charging_current(void) {
|
||||
uint16_t evse_get_runtime_charging_current(void)
|
||||
{
|
||||
return charging_current_runtime;
|
||||
}
|
||||
|
||||
|
||||
// ========================
|
||||
// Socket outlet
|
||||
// ========================
|
||||
bool evse_get_socket_outlet(void) {
|
||||
bool evse_get_socket_outlet(void)
|
||||
{
|
||||
return socket_outlet;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_socket_outlet(bool value) {
|
||||
esp_err_t evse_set_socket_outlet(bool value)
|
||||
{
|
||||
if (value && !board_config.proximity)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
socket_outlet = value;
|
||||
@@ -215,11 +271,13 @@ esp_err_t evse_set_socket_outlet(bool value) {
|
||||
// ========================
|
||||
// RCM
|
||||
// ========================
|
||||
bool evse_is_rcm(void) {
|
||||
bool evse_is_rcm(void)
|
||||
{
|
||||
return rcm;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_rcm(bool value) {
|
||||
esp_err_t evse_set_rcm(bool value)
|
||||
{
|
||||
if (value && !board_config.rcm)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
rcm = value;
|
||||
@@ -230,11 +288,13 @@ esp_err_t evse_set_rcm(bool value) {
|
||||
// ========================
|
||||
// Temperature
|
||||
// ========================
|
||||
uint8_t evse_get_temp_threshold(void) {
|
||||
uint8_t evse_get_temp_threshold(void)
|
||||
{
|
||||
return temp_threshold;
|
||||
}
|
||||
|
||||
esp_err_t evse_set_temp_threshold(uint8_t value) {
|
||||
esp_err_t evse_set_temp_threshold(uint8_t value)
|
||||
{
|
||||
if (value < 40 || value > 80)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
temp_threshold = value;
|
||||
@@ -242,29 +302,58 @@ esp_err_t evse_set_temp_threshold(uint8_t value) {
|
||||
return nvs_commit(nvs);
|
||||
}
|
||||
|
||||
|
||||
// ========================
|
||||
// Availability
|
||||
// ========================
|
||||
static bool is_available = true;
|
||||
|
||||
bool evse_config_is_available(void) {
|
||||
bool evse_config_is_available(void)
|
||||
{
|
||||
return is_available;
|
||||
}
|
||||
|
||||
void evse_config_set_available(bool available) {
|
||||
is_available = available;
|
||||
void evse_config_set_available(bool available)
|
||||
{
|
||||
is_available = available ? true : false;
|
||||
|
||||
// Persist
|
||||
esp_err_t err = nvs_set_u8(nvs, "available", (uint8_t)is_available);
|
||||
if (err == ESP_OK)
|
||||
err = nvs_commit(nvs);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to persist 'available': %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
// AVAILABLE_UPDATED
|
||||
evse_available_event_data_t e = {
|
||||
.available = is_available,
|
||||
.timestamp_us = esp_timer_get_time()};
|
||||
esp_event_post(EVSE_EVENTS, EVSE_EVENT_AVAILABLE_UPDATED, &e, sizeof(e), portMAX_DELAY);
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Enable/Disable
|
||||
// ========================
|
||||
static bool is_enabled = true;
|
||||
|
||||
bool evse_config_is_enabled(void) {
|
||||
bool evse_config_is_enabled(void)
|
||||
{
|
||||
return is_enabled;
|
||||
}
|
||||
|
||||
void evse_config_set_enabled(bool enabled) {
|
||||
is_enabled = enabled;
|
||||
void evse_config_set_enabled(bool enabled)
|
||||
{
|
||||
is_enabled = enabled ? true : false;
|
||||
|
||||
// Persist
|
||||
esp_err_t err = nvs_set_u8(nvs, "enabled", (uint8_t)is_enabled);
|
||||
if (err == ESP_OK)
|
||||
err = nvs_commit(nvs);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to persist 'enabled': %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
// ENABLE_UPDATED
|
||||
evse_enable_event_data_t e = {
|
||||
.enabled = is_enabled,
|
||||
.timestamp_us = esp_timer_get_time()};
|
||||
esp_event_post(EVSE_EVENTS, EVSE_EVENT_ENABLE_UPDATED, &e, sizeof(e), portMAX_DELAY);
|
||||
}
|
||||
|
||||
@@ -61,9 +61,7 @@ void evse_process(void) {
|
||||
|
||||
evse_state_t current = evse_get_state();
|
||||
if (current != last_state) {
|
||||
ESP_LOGI(TAG, "State changed: %s → %s",
|
||||
evse_state_to_str(last_state),
|
||||
evse_state_to_str(current));
|
||||
//ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current));
|
||||
last_state = current;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,12 +83,13 @@ static void update_outputs(evse_state_t state) {
|
||||
break;
|
||||
|
||||
case EVSE_STATE_C1:
|
||||
case EVSE_STATE_D1:
|
||||
pilot_set_level(true);
|
||||
case EVSE_STATE_D1: {
|
||||
pilot_set_amps(MIN(current, cable_max_current)); // mantém PWM
|
||||
ac_relay_set_state(false); // relé ainda desligado
|
||||
c1_d1_waiting = true;
|
||||
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
|
||||
break;
|
||||
|
||||
}
|
||||
case EVSE_STATE_C2:
|
||||
case EVSE_STATE_D2:
|
||||
pilot_set_amps(MIN(current, cable_max_current));
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "evse_api.h"
|
||||
#include "evse_meter.h"
|
||||
#include "evse_session.h"
|
||||
#include "evse_config.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
#include "auth_events.h"
|
||||
#include "loadbalancer_events.h"
|
||||
#include "ocpp_events.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
static const char *TAG = "EVSE_Manager";
|
||||
@@ -23,66 +25,142 @@ static const char *TAG = "EVSE_Manager";
|
||||
static SemaphoreHandle_t evse_mutex;
|
||||
static bool auth_enabled = false;
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
|
||||
// ===== Task de ciclo principal =====
|
||||
static void evse_manager_task(void *arg) {
|
||||
while (true) {
|
||||
static void evse_manager_task(void *arg)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
evse_manager_tick();
|
||||
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de autenticação =====
|
||||
static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* data) {
|
||||
if (base != AUTH_EVENTS || data == NULL) return;
|
||||
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base != AUTH_EVENTS || !data)
|
||||
return;
|
||||
|
||||
switch (id) {
|
||||
case AUTH_EVENT_TAG_PROCESSED: {
|
||||
auth_tag_event_data_t *evt = (auth_tag_event_data_t*)data;
|
||||
ESP_LOGI("EVSE", "Tag: %s | Autorized: %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
|
||||
evse_state_set_authorized(evt->authorized);
|
||||
break;
|
||||
auth_mode_t g_mode = AUTH_MODE_OPEN;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case AUTH_EVENT_TAG_PROCESSED:
|
||||
{
|
||||
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)data;
|
||||
ESP_LOGI(TAG, "Tag %s -> %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
|
||||
evse_state_set_authorized(evt->authorized);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_MODE_CHANGED:
|
||||
case AUTH_EVENT_INIT:
|
||||
{
|
||||
const auth_mode_event_data_t *evt = (const auth_mode_event_data_t *)data;
|
||||
g_mode = evt->mode;
|
||||
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(g_mode));
|
||||
if (g_mode == AUTH_MODE_OPEN)
|
||||
{
|
||||
evse_state_set_authorized(true);
|
||||
auth_enabled = false;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_ENABLED_CHANGED:
|
||||
case AUTH_EVENT_INIT: {
|
||||
auth_enabled_event_data_t *evt = (auth_enabled_event_data_t*)data;
|
||||
auth_enabled = evt->enabled;
|
||||
|
||||
ESP_LOGI("EVSE", "Auth %s (%s)",
|
||||
id == AUTH_EVENT_ENABLED_CHANGED ? "ficou" : "init",
|
||||
evt->enabled ? "ATIVO" : "INATIVO");
|
||||
|
||||
if (!auth_enabled) {
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI("EVSE", "Autenticação desativada → autorização forçada.");
|
||||
} else {
|
||||
evse_state_set_authorized(false);
|
||||
ESP_LOGI("EVSE", "Autenticação ativada → aguardando autorização por tag.");
|
||||
}
|
||||
break;
|
||||
else
|
||||
{
|
||||
evse_state_set_authorized(false);
|
||||
auth_enabled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de loadbalancer =====
|
||||
static void on_loadbalancer_event(void* handler_arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED) {
|
||||
const loadbalancer_state_event_t* evt = (const loadbalancer_state_event_t*) event_data;
|
||||
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
|
||||
{
|
||||
const loadbalancer_state_event_t *evt = (const loadbalancer_state_event_t *)event_data;
|
||||
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
|
||||
evt->enabled ? "ENABLED" : "DISABLED", evt->timestamp_us);
|
||||
// Ações adicionais podem ser adicionadas aqui conforme necessário
|
||||
} else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT) {
|
||||
const loadbalancer_master_limit_event_t* evt = (const loadbalancer_master_limit_event_t*) event_data;
|
||||
}
|
||||
else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT)
|
||||
{
|
||||
const loadbalancer_master_limit_event_t *evt = (const loadbalancer_master_limit_event_t *)event_data;
|
||||
ESP_LOGI(TAG, "Novo limite de corrente (master): %u A (ts: %lld)", evt->max_current, evt->timestamp_us);
|
||||
evse_set_runtime_charging_current(evt->max_current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base != OCPP_EVENTS)
|
||||
return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case OCPP_EVENT_AUTHORIZED:
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||
evse_state_set_authorized(true);
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_AUTH_REJECTED:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization rejected");
|
||||
evse_state_set_authorized(false);
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_AUTH_TIMEOUT:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization timeout");
|
||||
evse_state_set_authorized(false);
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_REMOTE_START:
|
||||
ESP_LOGI(TAG, "[OCPP] RemoteStart");
|
||||
evse_state_set_authorized(true);
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_REMOTE_STOP:
|
||||
ESP_LOGI(TAG, "[OCPP] RemoteStop");
|
||||
evse_state_set_authorized(false);
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_START_TX:
|
||||
ESP_LOGI(TAG, "[OCPP] StartTx");
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_STOP_TX:
|
||||
ESP_LOGI(TAG, "[OCPP] StopTx");
|
||||
evse_state_set_authorized(false);
|
||||
break;
|
||||
|
||||
// hegou ChangeAvailability remoto (operative/inoperative)
|
||||
case OCPP_EVENT_OPERATIVE_UPDATED:
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
ESP_LOGW(TAG, "[OCPP] OperativeUpdated sem payload — ignorado");
|
||||
break;
|
||||
}
|
||||
const ocpp_operative_event_t *ev = (const ocpp_operative_event_t *)data;
|
||||
ESP_LOGI(TAG, "[OCPP] OperativeUpdated: operative=%d ts=%lld",
|
||||
(int)ev->operative, (long long)ev->timestamp_us);
|
||||
|
||||
// Mapear operative → enabled local (persiste e emite EVSE_EVENT_ENABLE_UPDATED)
|
||||
evse_config_set_enabled(ev->operative);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "[OCPP] Unhandled event id=%" PRId32, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Inicialização =====
|
||||
void evse_manager_init(void) {
|
||||
void evse_manager_init(void)
|
||||
{
|
||||
evse_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
evse_config_init();
|
||||
@@ -94,13 +172,15 @@ void evse_manager_init(void) {
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(OCPP_EVENTS, ESP_EVENT_ANY_ID, &on_ocpp_event, NULL)); // <— AQUI
|
||||
|
||||
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
||||
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// ===== Main Tick =====
|
||||
void evse_manager_tick(void) {
|
||||
void evse_manager_tick(void)
|
||||
{
|
||||
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
||||
|
||||
evse_hardware_tick();
|
||||
@@ -109,15 +189,20 @@ void evse_manager_tick(void) {
|
||||
evse_temperature_check();
|
||||
evse_session_tick();
|
||||
|
||||
if (auth_enabled) {
|
||||
if (auth_enabled)
|
||||
{
|
||||
// If the car is disconnected, revoke authorization
|
||||
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A) {
|
||||
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
|
||||
{
|
||||
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// If authentication is disabled, ensure authorization is always granted
|
||||
if (!evse_state_get_authorized()) {
|
||||
if (!evse_state_get_authorized())
|
||||
{
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
|
||||
}
|
||||
|
||||
@@ -10,50 +10,57 @@
|
||||
static const char *TAG = "evse_meter";
|
||||
static SemaphoreHandle_t meter_mutex;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint32_t power_watts[EVSE_METER_PHASE_COUNT];
|
||||
float voltage[EVSE_METER_PHASE_COUNT];
|
||||
float current[EVSE_METER_PHASE_COUNT];
|
||||
float voltage[EVSE_METER_PHASE_COUNT];
|
||||
float current[EVSE_METER_PHASE_COUNT];
|
||||
uint32_t energy_wh;
|
||||
} evse_meter_data_t;
|
||||
|
||||
static evse_meter_data_t meter_data;
|
||||
|
||||
static void on_meter_event_dispatcher(void* arg, esp_event_base_t base, int32_t id, void* data) {
|
||||
if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data) {
|
||||
static void on_meter_event_dispatcher(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data)
|
||||
{
|
||||
const meter_event_data_t *evt = (const meter_event_data_t *)data;
|
||||
if (strcmp(evt->source, "EVSE") == 0) {
|
||||
if (strcmp(evt->source, "EVSE") == 0)
|
||||
{
|
||||
evse_meter_on_meter_event(arg, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void evse_meter_on_meter_event(void* arg, void* event_data) {
|
||||
void evse_meter_on_meter_event(void *arg, void *event_data)
|
||||
{
|
||||
const meter_event_data_t *evt = (const meter_event_data_t *)event_data;
|
||||
if (!evt) return;
|
||||
if (!evt)
|
||||
return;
|
||||
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||
{
|
||||
meter_data.power_watts[i] = evt->watt[i];
|
||||
meter_data.voltage[i] = evt->vrms[i];
|
||||
meter_data.current[i] = evt->irms[i];
|
||||
meter_data.voltage[i] = evt->vrms[i];
|
||||
meter_data.current[i] = evt->irms[i];
|
||||
}
|
||||
meter_data.energy_wh = (uint32_t)(evt->total_energy * 1000.0f);
|
||||
xSemaphoreGive(meter_mutex);
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
|
||||
"voltage[V]={%.2f,%.2f,%.2f}, "
|
||||
"current[A]={%.2f,%.2f,%.2f}, "
|
||||
"total_energy=%" PRIu32 "Wh",
|
||||
meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
|
||||
meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
|
||||
meter_data.current[0], meter_data.current[1], meter_data.current[2],
|
||||
meter_data.energy_wh
|
||||
);
|
||||
"Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
|
||||
"voltage[V]={%.2f,%.2f,%.2f}, "
|
||||
"current[A]={%.2f,%.2f,%.2f}, "
|
||||
"total_energy=%" PRIu32 "Wh",
|
||||
meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
|
||||
meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
|
||||
meter_data.current[0], meter_data.current[1], meter_data.current[2],
|
||||
meter_data.energy_wh);
|
||||
}
|
||||
|
||||
void evse_meter_init(void) {
|
||||
void evse_meter_init(void)
|
||||
{
|
||||
meter_mutex = xSemaphoreCreateMutex();
|
||||
ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||
@@ -63,42 +70,51 @@ void evse_meter_init(void) {
|
||||
ESP_LOGI(TAG, "EVSE Meter listener registered.");
|
||||
}
|
||||
|
||||
int evse_meter_get_instant_power(void) {
|
||||
int evse_meter_get_instant_power(void)
|
||||
{
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
int sum = 0;
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||
{
|
||||
sum += meter_data.power_watts[i];
|
||||
}
|
||||
xSemaphoreGive(meter_mutex);
|
||||
return sum;
|
||||
}
|
||||
|
||||
int evse_meter_get_total_energy(void) {
|
||||
int evse_meter_get_total_energy(void)
|
||||
{
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
int val = meter_data.energy_wh;
|
||||
xSemaphoreGive(meter_mutex);
|
||||
return val;
|
||||
}
|
||||
|
||||
void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT]) {
|
||||
void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT])
|
||||
{
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||
{
|
||||
power[i] = meter_data.power_watts[i];
|
||||
}
|
||||
xSemaphoreGive(meter_mutex);
|
||||
}
|
||||
|
||||
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]) {
|
||||
void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT])
|
||||
{
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||
{
|
||||
voltage[i] = meter_data.voltage[i];
|
||||
}
|
||||
xSemaphoreGive(meter_mutex);
|
||||
}
|
||||
|
||||
void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT]) {
|
||||
void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT])
|
||||
{
|
||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) {
|
||||
for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
|
||||
{
|
||||
current[i] = meter_data.current[i];
|
||||
}
|
||||
xSemaphoreGive(meter_mutex);
|
||||
|
||||
@@ -61,7 +61,7 @@ void pilot_init(void)
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0));
|
||||
ESP_ERROR_CHECK(ledc_fade_func_install(0));
|
||||
//ESP_ERROR_CHECK(ledc_fade_func_install(0));
|
||||
|
||||
// Inicializa ADC121S021 externo
|
||||
adc121s021_dma_init();
|
||||
@@ -107,6 +107,12 @@ void pilot_set_amps(uint16_t amps)
|
||||
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
|
||||
}
|
||||
|
||||
bool pilot_get_state(void) {
|
||||
// true se estamos em 12V fixo; false se PWM ou -12V
|
||||
// Se quiser diferenciar PWM, guarde um flag quando chamar set_amps.
|
||||
return (last_pilot_level == 1) && (last_pwm_duty == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int compare_int(const void *a, const void *b) {
|
||||
|
||||
@@ -10,6 +10,8 @@ typedef enum {
|
||||
EVSE_EVENT_INIT,
|
||||
EVSE_EVENT_STATE_CHANGED,
|
||||
EVSE_EVENT_CONFIG_UPDATED,
|
||||
EVSE_EVENT_ENABLE_UPDATED,
|
||||
EVSE_EVENT_AVAILABLE_UPDATED,
|
||||
} evse_event_id_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -30,4 +32,15 @@ typedef struct {
|
||||
int64_t timestamp_us; // Momento da atualização
|
||||
} evse_config_event_data_t;
|
||||
|
||||
// Eventos simples e específicos
|
||||
typedef struct {
|
||||
bool enabled; // novo estado de enabled
|
||||
int64_t timestamp_us; // epoch micros
|
||||
} evse_enable_event_data_t;
|
||||
|
||||
typedef struct {
|
||||
bool available; // novo estado de available
|
||||
int64_t timestamp_us; // epoch micros
|
||||
} evse_available_event_data_t;
|
||||
|
||||
#endif // EVSE_EVENTS_H
|
||||
|
||||
@@ -35,7 +35,7 @@ void pilot_set_level(bool level);
|
||||
/**
|
||||
* @brief Ativa o PWM do Pilot com corrente limitada
|
||||
*
|
||||
* @param amps Corrente em décimos de ampère (ex: 160 = 16A)
|
||||
* @param amps Corrente em ampères (ex: 16 = 16A)
|
||||
*/
|
||||
void pilot_set_amps(uint16_t amps);
|
||||
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
static const char *TAG = "evse_link";
|
||||
|
||||
// NVS keys
|
||||
#define _NVS_NAMESPACE "evse_link"
|
||||
#define _NVS_MODE_KEY "mode"
|
||||
#define _NVS_ID_KEY "self_id"
|
||||
#define _NVS_NAMESPACE "evse_link"
|
||||
#define _NVS_MODE_KEY "mode"
|
||||
#define _NVS_ID_KEY "self_id"
|
||||
#define _NVS_ENABLED_KEY "enabled"
|
||||
|
||||
// UART parameters
|
||||
#define UART_PORT UART_NUM_2
|
||||
#define UART_PORT UART_NUM_2
|
||||
#define UART_RX_BUF_SIZE 256
|
||||
|
||||
// Runtime config
|
||||
static evse_link_mode_t _mode = EVSE_LINK_MODE_MASTER;
|
||||
static uint8_t _self_id = 0x01;
|
||||
static bool _enabled = false;
|
||||
static evse_link_mode_t _mode = EVSE_LINK_MODE_MASTER;
|
||||
static uint8_t _self_id = 0x01;
|
||||
static bool _enabled = false;
|
||||
|
||||
// Registered Rx callback
|
||||
static evse_link_rx_cb_t _rx_cb = NULL;
|
||||
@@ -33,71 +33,100 @@ extern void evse_link_master_init(void);
|
||||
extern void evse_link_slave_init(void);
|
||||
|
||||
static void framing_rx_cb(uint8_t src, uint8_t dest,
|
||||
const uint8_t *payload, uint8_t len) {
|
||||
const uint8_t *payload, uint8_t len)
|
||||
{
|
||||
ESP_LOGD(TAG, "framing_rx_cb: src=0x%02X dest=0x%02X len=%u", src, dest, len);
|
||||
if (_rx_cb) {
|
||||
if (_rx_cb)
|
||||
{
|
||||
_rx_cb(src, dest, payload, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Register protocol-level Rx callback
|
||||
void evse_link_register_rx_cb(evse_link_rx_cb_t cb) {
|
||||
void evse_link_register_rx_cb(evse_link_rx_cb_t cb)
|
||||
{
|
||||
_rx_cb = cb;
|
||||
}
|
||||
|
||||
// Load config from NVS
|
||||
enum { EV_OK = ESP_OK };
|
||||
static void load_link_config(void) {
|
||||
enum
|
||||
{
|
||||
EV_OK = ESP_OK
|
||||
};
|
||||
static void load_link_config(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READONLY, &handle) != EV_OK) {
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READONLY, &handle) != EV_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "NVS open failed, using defaults");
|
||||
return;
|
||||
}
|
||||
uint8_t mode, id, en;
|
||||
if (nvs_get_u8(handle, _NVS_MODE_KEY, &mode) == EV_OK &&
|
||||
(mode == EVSE_LINK_MODE_MASTER || mode == EVSE_LINK_MODE_SLAVE)) {
|
||||
(mode == EVSE_LINK_MODE_MASTER || mode == EVSE_LINK_MODE_SLAVE))
|
||||
{
|
||||
_mode = (evse_link_mode_t)mode;
|
||||
}
|
||||
if (nvs_get_u8(handle, _NVS_ID_KEY, &id) == EV_OK) {
|
||||
if (nvs_get_u8(handle, _NVS_ID_KEY, &id) == EV_OK)
|
||||
{
|
||||
_self_id = id;
|
||||
}
|
||||
if (nvs_get_u8(handle, _NVS_ENABLED_KEY, &en) == EV_OK) {
|
||||
if (nvs_get_u8(handle, _NVS_ENABLED_KEY, &en) == EV_OK)
|
||||
{
|
||||
_enabled = (en != 0);
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
// Save config to NVS
|
||||
static void save_link_config(void) {
|
||||
static void save_link_config(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READWRITE, &handle) == EV_OK) {
|
||||
if (nvs_open(_NVS_NAMESPACE, NVS_READWRITE, &handle) == EV_OK)
|
||||
{
|
||||
nvs_set_u8(handle, _NVS_MODE_KEY, (uint8_t)_mode);
|
||||
nvs_set_u8(handle, _NVS_ID_KEY, _self_id);
|
||||
nvs_set_u8(handle, _NVS_ENABLED_KEY, _enabled ? 1 : 0);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to save NVS");
|
||||
}
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
void evse_link_set_mode(evse_link_mode_t m) { _mode = m; save_link_config(); }
|
||||
evse_link_mode_t evse_link_get_mode(void) { return _mode; }
|
||||
void evse_link_set_self_id(uint8_t id) { _self_id = id; save_link_config(); }
|
||||
uint8_t evse_link_get_self_id(void) { return _self_id; }
|
||||
void evse_link_set_enabled(bool en) { _enabled = en; save_link_config(); }
|
||||
bool evse_link_is_enabled(void) { return _enabled; }
|
||||
void evse_link_set_mode(evse_link_mode_t m)
|
||||
{
|
||||
_mode = m;
|
||||
save_link_config();
|
||||
}
|
||||
evse_link_mode_t evse_link_get_mode(void) { return _mode; }
|
||||
void evse_link_set_self_id(uint8_t id)
|
||||
{
|
||||
_self_id = id;
|
||||
save_link_config();
|
||||
}
|
||||
uint8_t evse_link_get_self_id(void) { return _self_id; }
|
||||
void evse_link_set_enabled(bool en)
|
||||
{
|
||||
_enabled = en;
|
||||
save_link_config();
|
||||
}
|
||||
bool evse_link_is_enabled(void) { return _enabled; }
|
||||
|
||||
// RX task: reads bytes from UART and feeds framing
|
||||
static void evse_link_rx_task(void *arg) {
|
||||
static void evse_link_rx_task(void *arg)
|
||||
{
|
||||
uint8_t buf[UART_RX_BUF_SIZE];
|
||||
while (true) {
|
||||
while (true)
|
||||
{
|
||||
int len = uart_read_bytes(UART_PORT, buf, sizeof(buf), pdMS_TO_TICKS(1000));
|
||||
if (len > 0) {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (len > 0)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
evse_link_recv_byte(buf[i]);
|
||||
}
|
||||
}
|
||||
@@ -105,13 +134,15 @@ static void evse_link_rx_task(void *arg) {
|
||||
}
|
||||
|
||||
// Initialize EVSE-Link component
|
||||
void evse_link_init(void) {
|
||||
void evse_link_init(void)
|
||||
{
|
||||
load_link_config();
|
||||
|
||||
ESP_LOGI(TAG, "Link init: mode=%c id=0x%02X enabled=%d",
|
||||
_mode == EVSE_LINK_MODE_MASTER ? 'M' : 'S',
|
||||
_self_id, _enabled);
|
||||
if (!_enabled) return;
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
// 1) framing layer init (sets up mutex, UART driver, etc.)
|
||||
evse_link_framing_init();
|
||||
@@ -121,22 +152,27 @@ void evse_link_init(void) {
|
||||
xTaskCreate(evse_link_rx_task, "evse_link_rx", 4096, NULL, 4, NULL);
|
||||
|
||||
// 3) delegate to master or slave
|
||||
if (_mode == EVSE_LINK_MODE_MASTER) {
|
||||
if (_mode == EVSE_LINK_MODE_MASTER)
|
||||
{
|
||||
evse_link_master_init();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_link_slave_init();
|
||||
}
|
||||
}
|
||||
|
||||
// Send a frame (delegates to framing module)
|
||||
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len) {
|
||||
if (!evse_link_is_enabled()) return false;
|
||||
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len)
|
||||
{
|
||||
if (!evse_link_is_enabled())
|
||||
return false;
|
||||
uint8_t src = evse_link_get_self_id();
|
||||
return evse_link_framing_send(dest, src, payload, len);
|
||||
}
|
||||
|
||||
|
||||
// Receive byte (delegates to framing module)
|
||||
void evse_link_recv_byte(uint8_t byte) {
|
||||
void evse_link_recv_byte(uint8_t byte)
|
||||
{
|
||||
evse_link_framing_recv_byte(byte);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "meter_events.h"
|
||||
#include "evse_events.h"
|
||||
#include "math.h"
|
||||
#include <inttypes.h> // Necessário para PRIu64
|
||||
#include <inttypes.h> // Necessário para PRIu64
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
@@ -20,10 +20,10 @@
|
||||
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
|
||||
#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;
|
||||
@@ -38,15 +38,16 @@ static input_filter_t evse_filter;
|
||||
#define CONNECTOR_COUNT (MAX_SLAVES + 1)
|
||||
|
||||
// Estrutura unificada para master e slaves
|
||||
typedef struct {
|
||||
uint8_t id; // 0xFF = master, 0..MAX_SLAVES-1 = slave
|
||||
bool is_master;
|
||||
bool charging;
|
||||
float hw_max_current;
|
||||
float runtime_current;
|
||||
int64_t timestamp; // microssegundos
|
||||
bool online;
|
||||
float assigned;
|
||||
typedef struct
|
||||
{
|
||||
uint8_t id; // 0xFF = master, 0..MAX_SLAVES-1 = slave
|
||||
bool is_master;
|
||||
bool charging;
|
||||
float hw_max_current;
|
||||
float runtime_current;
|
||||
int64_t timestamp; // microssegundos
|
||||
bool online;
|
||||
float assigned;
|
||||
} evse_connector_t;
|
||||
|
||||
static evse_connector_t connectors[CONNECTOR_COUNT];
|
||||
@@ -58,27 +59,26 @@ static void init_connectors(void)
|
||||
{
|
||||
// master em índice 0
|
||||
connectors[0] = (evse_connector_t){
|
||||
.id = 0xFF,
|
||||
.is_master = true,
|
||||
.charging = false,
|
||||
.id = 0xFF,
|
||||
.is_master = true,
|
||||
.charging = false,
|
||||
.hw_max_current = MAX_CHARGING_CURRENT_LIMIT,
|
||||
.runtime_current = 0,
|
||||
.timestamp = 0,
|
||||
.online = false,
|
||||
.assigned = 0.0f
|
||||
};
|
||||
.timestamp = 0,
|
||||
.online = false,
|
||||
.assigned = 0.0f};
|
||||
// slaves em 1..CONNECTOR_COUNT-1
|
||||
for (int i = 1; i < CONNECTOR_COUNT; i++) {
|
||||
for (int i = 1; i < CONNECTOR_COUNT; i++)
|
||||
{
|
||||
connectors[i] = (evse_connector_t){
|
||||
.id = (uint8_t)(i - 1),
|
||||
.is_master = false,
|
||||
.charging = false,
|
||||
.id = (uint8_t)(i - 1),
|
||||
.is_master = false,
|
||||
.charging = false,
|
||||
.hw_max_current = 0.0f,
|
||||
.runtime_current = 0.0f,
|
||||
.timestamp = 0,
|
||||
.online = false,
|
||||
.assigned = 0.0f
|
||||
};
|
||||
.timestamp = 0,
|
||||
.online = false,
|
||||
.assigned = 0.0f};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,17 +93,18 @@ static void on_slave_status(void *handler_arg, esp_event_base_t base, int32_t id
|
||||
{
|
||||
const loadbalancer_slave_status_event_t *status = (const loadbalancer_slave_status_event_t *)data;
|
||||
|
||||
if (status->slave_id >= MAX_SLAVES) {
|
||||
if (status->slave_id >= MAX_SLAVES)
|
||||
{
|
||||
ESP_LOGW(TAG, "Invalid slave_id %d", status->slave_id);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = status->slave_id + 1; // slaves começam no índice 1
|
||||
connectors[idx].charging = status->charging;
|
||||
connectors[idx].hw_max_current = status->hw_max_current;
|
||||
connectors[idx].charging = status->charging;
|
||||
connectors[idx].hw_max_current = status->hw_max_current;
|
||||
connectors[idx].runtime_current = status->runtime_current;
|
||||
connectors[idx].timestamp = esp_timer_get_time();
|
||||
connectors[idx].online = true;
|
||||
connectors[idx].timestamp = esp_timer_get_time();
|
||||
connectors[idx].online = true;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Slave %d status: charging=%d hw_max_current=%.1fA runtime_current=%.2fA",
|
||||
@@ -111,31 +112,24 @@ static void on_slave_status(void *handler_arg, esp_event_base_t base, int32_t id
|
||||
status->hw_max_current, status->runtime_current);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void on_evse_config_event(void* handler_arg,
|
||||
static void on_evse_config_event(void *handler_arg,
|
||||
esp_event_base_t base,
|
||||
int32_t id,
|
||||
void* event_data)
|
||||
void *event_data)
|
||||
{
|
||||
const evse_config_event_data_t *evt = (const evse_config_event_data_t*) event_data;
|
||||
const evse_config_event_data_t *evt = (const evse_config_event_data_t *)event_data;
|
||||
|
||||
int idx = 0; // MASTER INDICE 0
|
||||
connectors[idx].charging = evt->charging;
|
||||
connectors[idx].hw_max_current = evt->hw_max_current;
|
||||
connectors[idx].charging = evt->charging;
|
||||
connectors[idx].hw_max_current = evt->hw_max_current;
|
||||
connectors[idx].runtime_current = evt->runtime_current;
|
||||
connectors[idx].timestamp = esp_timer_get_time();
|
||||
connectors[idx].online = true;
|
||||
|
||||
connectors[idx].timestamp = esp_timer_get_time();
|
||||
connectors[idx].online = true;
|
||||
|
||||
ESP_LOGI(TAG, "EVSE config updated: charging=%d hw_max_current=%.1f runtime_current=%.1f",
|
||||
evt->charging, evt->hw_max_current, evt->runtime_current);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Handlers de eventos externos ---
|
||||
static void loadbalancer_meter_event_handler(void *handler_arg,
|
||||
esp_event_base_t base,
|
||||
@@ -149,13 +143,18 @@ static void loadbalancer_meter_event_handler(void *handler_arg,
|
||||
for (int i = 1; i < 3; ++i)
|
||||
if (evt->irms[i] > max_irms)
|
||||
max_irms = evt->irms[i];
|
||||
if (evt->source && strcmp(evt->source, "GRID") == 0) {
|
||||
if (evt->source && strcmp(evt->source, "GRID") == 0)
|
||||
{
|
||||
grid_current = input_filter_update(&grid_filter, max_irms);
|
||||
ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current);
|
||||
} else if (evt->source && strcmp(evt->source, "EVSE") == 0) {
|
||||
}
|
||||
else if (evt->source && strcmp(evt->source, "EVSE") == 0)
|
||||
{
|
||||
evse_current = input_filter_update(&evse_filter, max_irms);
|
||||
ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +174,7 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
|
||||
evt->state == EVSE_STATE_EVENT_IDLE ? "IDLE" : "WAITING",
|
||||
evt->state == EVSE_STATE_EVENT_IDLE ? "dis" : "");
|
||||
connectors[0].charging = false;
|
||||
connectors[0].online = true; // master está sempre online
|
||||
connectors[0].online = true; // master está sempre online
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_CHARGING:
|
||||
@@ -184,15 +183,15 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
|
||||
evse_current = 0.0f;
|
||||
input_filter_reset(&grid_filter);
|
||||
input_filter_reset(&evse_filter);
|
||||
connectors[0].charging = true;
|
||||
connectors[0].online = true;
|
||||
connectors[0].charging = true;
|
||||
connectors[0].online = true;
|
||||
connectors[0].timestamp = esp_timer_get_time();
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_FAULT:
|
||||
ESP_LOGW(TAG, "Local EVSE is in FAULT state - disabling load balancing temporarily");
|
||||
connectors[0].charging = false;
|
||||
connectors[0].online = true; // EVSE está online mas com falha
|
||||
connectors[0].online = true; // EVSE está online mas com falha
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -201,8 +200,6 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Config persistência ---
|
||||
static esp_err_t loadbalancer_load_config()
|
||||
{
|
||||
@@ -215,7 +212,8 @@ static esp_err_t loadbalancer_load_config()
|
||||
err = nvs_get_u8(handle, "max_grid_curr", &temp_u8);
|
||||
if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT)
|
||||
max_grid_current = temp_u8;
|
||||
else {
|
||||
else
|
||||
{
|
||||
max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
||||
nvs_set_u8(handle, "max_grid_curr", max_grid_current);
|
||||
needs_commit = true;
|
||||
@@ -223,7 +221,8 @@ static esp_err_t loadbalancer_load_config()
|
||||
err = nvs_get_u8(handle, "enabled", &temp_u8);
|
||||
if (err == ESP_OK && temp_u8 <= 1)
|
||||
loadbalancer_enabled = (temp_u8 != 0);
|
||||
else {
|
||||
else
|
||||
{
|
||||
loadbalancer_enabled = false;
|
||||
nvs_set_u8(handle, "enabled", 0);
|
||||
needs_commit = true;
|
||||
@@ -238,13 +237,14 @@ static esp_err_t loadbalancer_load_config()
|
||||
void loadbalancer_set_enabled(bool enabled)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open("loadbalancing", NVS_READWRITE, &handle) == ESP_OK) {
|
||||
if (nvs_open("loadbalancing", NVS_READWRITE, &handle) == ESP_OK)
|
||||
{
|
||||
nvs_set_u8(handle, "enabled", enabled ? 1 : 0);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
loadbalancer_enabled = enabled;
|
||||
loadbalancer_state_event_t evt = { .enabled = enabled, .timestamp_us = esp_timer_get_time() };
|
||||
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);
|
||||
}
|
||||
@@ -276,8 +276,10 @@ bool loadbalancer_is_enabled(void)
|
||||
// --- Task principal ---
|
||||
void loadbalancer_task(void *param)
|
||||
{
|
||||
while (true) {
|
||||
if (!loadbalancer_is_enabled()) {
|
||||
while (true)
|
||||
{
|
||||
if (!loadbalancer_is_enabled())
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
continue;
|
||||
}
|
||||
@@ -287,16 +289,19 @@ void loadbalancer_task(void *param)
|
||||
int64_t now = esp_timer_get_time();
|
||||
|
||||
// --- Atualiza estado online e conta ativos ---
|
||||
for (int i = 0; i < CONNECTOR_COUNT; i++) {
|
||||
for (int i = 0; i < CONNECTOR_COUNT; i++)
|
||||
{
|
||||
|
||||
// --- Master nunca pode ficar offline ---
|
||||
if (connectors[i].is_master) {
|
||||
if (connectors[i].is_master)
|
||||
{
|
||||
connectors[i].online = true;
|
||||
|
||||
ESP_LOGI(TAG, "Connector[%d] ONLINE (MASTER, charging=%d, hw_max_current=%.1f)",
|
||||
i, connectors[i].charging, connectors[i].hw_max_current);
|
||||
|
||||
if (connectors[i].charging) {
|
||||
if (connectors[i].charging)
|
||||
{
|
||||
idxs[active_cnt++] = i;
|
||||
ESP_LOGI(TAG, "Connector[%d] is ACTIVE (charging)", i);
|
||||
}
|
||||
@@ -304,12 +309,14 @@ void loadbalancer_task(void *param)
|
||||
}
|
||||
|
||||
// --- Ignora conectores já marcados como offline ---
|
||||
if (!connectors[i].online) {
|
||||
if (!connectors[i].online)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- Timeout de heartbeat para escravos ---
|
||||
if ((now - connectors[i].timestamp) >= METRICS_TIMEOUT_US) {
|
||||
if ((now - connectors[i].timestamp) >= METRICS_TIMEOUT_US)
|
||||
{
|
||||
connectors[i].online = false;
|
||||
ESP_LOGW(TAG, "Connector[%d] marked OFFLINE (charging=%d, timestamp_diff=%lld us)",
|
||||
i, connectors[i].charging, (long long)(now - connectors[i].timestamp));
|
||||
@@ -320,7 +327,8 @@ void loadbalancer_task(void *param)
|
||||
i, connectors[i].charging, connectors[i].hw_max_current,
|
||||
(long long)(now - connectors[i].timestamp));
|
||||
|
||||
if (connectors[i].charging) {
|
||||
if (connectors[i].charging)
|
||||
{
|
||||
idxs[active_cnt++] = i;
|
||||
ESP_LOGI(TAG, "Connector[%d] is ACTIVE (charging)", i);
|
||||
}
|
||||
@@ -330,17 +338,23 @@ void loadbalancer_task(void *param)
|
||||
|
||||
// --- Calcula corrente disponível ---
|
||||
float available = max_grid_current - grid_current;
|
||||
if (available < MIN_CHARGING_CURRENT_LIMIT) {
|
||||
if (available < MIN_CHARGING_CURRENT_LIMIT)
|
||||
{
|
||||
available = MIN_CHARGING_CURRENT_LIMIT;
|
||||
} else if (available > max_grid_current) {
|
||||
}
|
||||
else if (available > max_grid_current)
|
||||
{
|
||||
available = max_grid_current;
|
||||
}
|
||||
ESP_LOGI(TAG, "LB Calc: available=%.1fA, active_connectors=%d", available, active_cnt);
|
||||
|
||||
// --- Ordena conectores por hw_max_current (bubble sort simples) ---
|
||||
for (int a = 0; a < active_cnt - 1; a++) {
|
||||
for (int b = 0; b < active_cnt - 1 - a; b++) {
|
||||
if (connectors[idxs[b]].hw_max_current > connectors[idxs[b + 1]].hw_max_current) {
|
||||
for (int a = 0; a < active_cnt - 1; a++)
|
||||
{
|
||||
for (int b = 0; b < active_cnt - 1 - a; b++)
|
||||
{
|
||||
if (connectors[idxs[b]].hw_max_current > connectors[idxs[b + 1]].hw_max_current)
|
||||
{
|
||||
int tmp = idxs[b];
|
||||
idxs[b] = idxs[b + 1];
|
||||
idxs[b + 1] = tmp;
|
||||
@@ -351,15 +365,20 @@ void loadbalancer_task(void *param)
|
||||
// --- Distribui corrente (water-filling) ---
|
||||
float remaining = available;
|
||||
int remaining_cnt = active_cnt;
|
||||
for (int k = 0; k < active_cnt; k++) {
|
||||
for (int k = 0; k < active_cnt; k++)
|
||||
{
|
||||
int i = idxs[k];
|
||||
float share = remaining / remaining_cnt;
|
||||
if (share >= connectors[i].hw_max_current) {
|
||||
if (share >= connectors[i].hw_max_current)
|
||||
{
|
||||
connectors[i].assigned = connectors[i].hw_max_current;
|
||||
remaining -= connectors[i].assigned;
|
||||
remaining_cnt--;
|
||||
} else {
|
||||
for (int m = k; m < active_cnt; m++) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int m = k; m < active_cnt; m++)
|
||||
{
|
||||
connectors[idxs[m]].assigned = share;
|
||||
}
|
||||
break;
|
||||
@@ -367,39 +386,44 @@ void loadbalancer_task(void *param)
|
||||
}
|
||||
|
||||
// --- Aplica piso mínimo ---
|
||||
for (int k = 0; k < active_cnt; k++) {
|
||||
for (int k = 0; k < active_cnt; k++)
|
||||
{
|
||||
int i = idxs[k];
|
||||
if (connectors[i].assigned < MIN_CHARGING_CURRENT_LIMIT) {
|
||||
if (connectors[i].assigned < MIN_CHARGING_CURRENT_LIMIT)
|
||||
{
|
||||
connectors[i].assigned = MIN_CHARGING_CURRENT_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Publica limites de corrente ---
|
||||
for (int k = 0; k < active_cnt; k++) {
|
||||
for (int k = 0; k < active_cnt; k++)
|
||||
{
|
||||
int i = idxs[k];
|
||||
uint16_t max_cur = (uint16_t)MIN(connectors[i].assigned, MAX_CHARGING_CURRENT_LIMIT);
|
||||
uint16_t current_rounded = (uint16_t)roundf(connectors[i].runtime_current);
|
||||
|
||||
if (current_rounded == max_cur) {
|
||||
if (current_rounded == max_cur)
|
||||
{
|
||||
continue; // sem alteração
|
||||
}
|
||||
|
||||
if (connectors[i].is_master) {
|
||||
if (connectors[i].is_master)
|
||||
{
|
||||
loadbalancer_master_limit_event_t master_evt = {
|
||||
.slave_id = connectors[i].id,
|
||||
.max_current = max_cur,
|
||||
.timestamp_us = now
|
||||
};
|
||||
.slave_id = connectors[i].id,
|
||||
.max_current = max_cur,
|
||||
.timestamp_us = now};
|
||||
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT,
|
||||
&master_evt, sizeof(master_evt), portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Master limit changed -> %.1f A (runtime=%.1f A)",
|
||||
(float)max_cur, connectors[i].runtime_current);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
loadbalancer_slave_limit_event_t slave_evt = {
|
||||
.slave_id = connectors[i].id,
|
||||
.max_current = max_cur,
|
||||
.timestamp_us = now
|
||||
};
|
||||
.slave_id = connectors[i].id,
|
||||
.max_current = max_cur,
|
||||
.timestamp_us = now};
|
||||
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT,
|
||||
&slave_evt, sizeof(slave_evt), portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Slave %d limit changed -> %.1f A (runtime=%.1f A)",
|
||||
@@ -432,9 +456,9 @@ void loadbalancer_init(void)
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED,
|
||||
&loadbalancer_evse_event_handler, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS,EVSE_EVENT_CONFIG_UPDATED,
|
||||
&on_evse_config_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_CONFIG_UPDATED,
|
||||
&on_evse_config_event, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS,LOADBALANCER_EVENT_SLAVE_STATUS,
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_STATUS,
|
||||
&on_slave_status, NULL));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
set(srcs
|
||||
"src/ocpp_events.c"
|
||||
"src/ocpp.c"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES nvs_flash
|
||||
REQUIRES config esp_wifi evse mongoose MicroOcpp MicroOcppMongoose)
|
||||
REQUIRES esp_event config esp_wifi evse mongoose MicroOcpp MicroOcppMongoose)
|
||||
|
||||
@@ -4,41 +4,35 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Start ocpp
|
||||
*/
|
||||
void ocpp_start();
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Stop ocpp
|
||||
*
|
||||
* @brief Start OCPP
|
||||
*/
|
||||
void ocpp_start(void);
|
||||
|
||||
/**
|
||||
* @brief Stop OCPP
|
||||
*/
|
||||
void ocpp_stop(void);
|
||||
|
||||
/* Config getters / setters */
|
||||
bool ocpp_get_enabled(void);
|
||||
|
||||
void ocpp_get_server(char *value);
|
||||
|
||||
void ocpp_get_rfid(char *value);
|
||||
|
||||
void ocpp_set_enabled(bool value);
|
||||
|
||||
void ocpp_get_server(char *value); // buffer >= 64
|
||||
void ocpp_set_server(char *value);
|
||||
|
||||
void ocpp_set_rfid(char *value);
|
||||
void ocpp_get_charge_id(char *value); // buffer >= 64
|
||||
void ocpp_set_charge_id(char *value);
|
||||
|
||||
void ocpp_begin_transaction(char *value);
|
||||
/* Estado de conexão */
|
||||
bool ocpp_is_connected(void);
|
||||
|
||||
void ocpp_end_transaction(char *value);
|
||||
|
||||
void ocpp_begin_transaction_authorized(char *value);
|
||||
|
||||
void ocpp_end_transaction_authorized(char *value);
|
||||
|
||||
bool ocpp_is_TransactionActive();
|
||||
|
||||
void ocpp_set_plugged(bool value);
|
||||
|
||||
void ocpp_set_charging(bool value);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OCPP_H_ */
|
||||
|
||||
53
components/ocpp/include/ocpp_events.h
Normal file
53
components/ocpp/include/ocpp_events.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "esp_event.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Base de eventos do OCPP (igual ao padrão usado em auth_events.h) */
|
||||
ESP_EVENT_DECLARE_BASE(OCPP_EVENTS);
|
||||
|
||||
/* IDs de eventos do OCPP */
|
||||
typedef enum {
|
||||
OCPP_EVENT_CONNECTED = 0, // payload: const char* (server URL) – opcional
|
||||
OCPP_EVENT_DISCONNECTED, // payload: NULL
|
||||
OCPP_EVENT_AUTHORIZED, // payload: ocpp_idtag_event_t (opcional)
|
||||
OCPP_EVENT_AUTH_REJECTED, // payload: ocpp_idtag_event_t (opcional)
|
||||
OCPP_EVENT_AUTH_TIMEOUT, // payload: NULL
|
||||
OCPP_EVENT_REMOTE_START, // payload: ocpp_idtag_event_t (opcional)
|
||||
OCPP_EVENT_REMOTE_STOP, // payload: NULL
|
||||
OCPP_EVENT_START_TX, // payload: ocpp_tx_event_t (opcional)
|
||||
OCPP_EVENT_STOP_TX, // payload: ocpp_reason_event_t (opcional)
|
||||
OCPP_EVENT_RESET, // payload: NULL
|
||||
OCPP_EVENT_OPERATIVE_UPDATED
|
||||
} ocpp_event_id_t;
|
||||
|
||||
/* Limites de strings simples (evita dependência de auth.h) */
|
||||
#define OCPP_IDTAG_MAX 32
|
||||
#define OCPP_REASON_MAX 32
|
||||
|
||||
/* Payloads opcionais */
|
||||
typedef struct {
|
||||
char idTag[OCPP_IDTAG_MAX];
|
||||
} ocpp_idtag_event_t;
|
||||
|
||||
typedef struct {
|
||||
int tx_id; // se disponível
|
||||
} ocpp_tx_event_t;
|
||||
|
||||
typedef struct {
|
||||
char reason[OCPP_REASON_MAX];
|
||||
} ocpp_reason_event_t;
|
||||
|
||||
// Payload do novo evento
|
||||
typedef struct {
|
||||
bool operative; // true = Operative, false = Inoperative
|
||||
int64_t timestamp_us; // esp_timer_get_time()
|
||||
} ocpp_operative_event_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
4
components/ocpp/src/ocpp_events.c
Executable file
4
components/ocpp/src/ocpp_events.c
Executable file
@@ -0,0 +1,4 @@
|
||||
#include "ocpp_events.h"
|
||||
|
||||
/* Define a base, como em components/auth/src/auth_events.c */
|
||||
ESP_EVENT_DEFINE_BASE(OCPP_EVENTS);
|
||||
@@ -1,5 +1,5 @@
|
||||
// =========================
|
||||
// auth_api.c
|
||||
// auth_api.c (nova interface por modo)
|
||||
// =========================
|
||||
|
||||
#include "auth_api.h"
|
||||
@@ -16,60 +16,101 @@ static const char *TAG = "auth_api";
|
||||
|
||||
static struct {
|
||||
char username[128];
|
||||
} users[10] = { /* {"admin"}, {"user1"} */ };
|
||||
} users[10] = { {"admin"}, {"user1"} };
|
||||
|
||||
static int num_users = 2;
|
||||
|
||||
// =================================
|
||||
// Handlers for Auth Methods (RFID)
|
||||
// Helpers
|
||||
// =================================
|
||||
|
||||
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());
|
||||
static void send_json(httpd_req_t *req, cJSON *json) {
|
||||
char *str = cJSON_PrintUnformatted(json);
|
||||
httpd_resp_sendstr(req, str);
|
||||
free(str);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, str ? str : "{}");
|
||||
if (str) free(str);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
static esp_err_t recv_body(httpd_req_t *req, char *buf, size_t buf_sz, int *out_len) {
|
||||
int remaining = req->content_len;
|
||||
int received = 0;
|
||||
|
||||
if (remaining <= 0) {
|
||||
*out_len = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
while (remaining > 0 && received < (int)(buf_sz - 1)) {
|
||||
int chunk = remaining;
|
||||
int space = (int)(buf_sz - 1 - received);
|
||||
if (chunk > space) chunk = space;
|
||||
|
||||
int ret = httpd_req_recv(req, buf + received, chunk);
|
||||
if (ret <= 0) return ESP_FAIL;
|
||||
|
||||
received += ret;
|
||||
remaining -= ret;
|
||||
}
|
||||
buf[received] = '\0';
|
||||
*out_len = received;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t auth_methods_post_handler(httpd_req_t *req) {
|
||||
// =================================
|
||||
// Auth Mode (NEW API)
|
||||
// =================================
|
||||
|
||||
static esp_err_t auth_mode_get_handler(httpd_req_t *req) {
|
||||
auth_mode_t mode = auth_get_mode();
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(json, "mode", auth_mode_to_str(mode));
|
||||
send_json(req, json);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t auth_mode_post_handler(httpd_req_t *req) {
|
||||
char buf[256];
|
||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
if (len <= 0) {
|
||||
int len = 0;
|
||||
if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK) {
|
||||
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);
|
||||
cJSON *json = cJSON_ParseWithLength(buf, len);
|
||||
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 *mode_js = cJSON_GetObjectItem(json, "mode");
|
||||
if (!cJSON_IsString(mode_js) || mode_js->valuestring == NULL) {
|
||||
cJSON_Delete(json);
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'RFID' inválido ou ausente");
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'mode' inválido ou ausente");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
auth_mode_t mode;
|
||||
if (!auth_mode_from_str(mode_js->valuestring, &mode)) {
|
||||
cJSON_Delete(json);
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Valor de 'mode' inválido (use: open|local|ocpp)");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
auth_set_mode(mode);
|
||||
cJSON_Delete(json);
|
||||
httpd_resp_sendstr(req, "Métodos de autenticação atualizados");
|
||||
|
||||
cJSON *resp = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(resp, "mode", auth_mode_to_str(mode));
|
||||
send_json(req, resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// =================================
|
||||
// User Management Handlers
|
||||
/* Users (mock) */
|
||||
// =================================
|
||||
|
||||
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) {
|
||||
@@ -78,28 +119,27 @@ static esp_err_t users_get_handler(httpd_req_t *req) {
|
||||
cJSON_AddItemToArray(list, u);
|
||||
}
|
||||
cJSON_AddItemToObject(root, "users", list);
|
||||
char *str = cJSON_PrintUnformatted(root);
|
||||
httpd_resp_sendstr(req, str);
|
||||
free(str);
|
||||
cJSON_Delete(root);
|
||||
send_json(req, 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';
|
||||
int len = 0;
|
||||
if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK || len <= 0) {
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Body vazio");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
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");
|
||||
return ESP_OK;
|
||||
} else {
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t users_delete_handler(httpd_req_t *req) {
|
||||
@@ -124,27 +164,21 @@ static esp_err_t users_delete_handler(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
// =================================
|
||||
// Tag Management Handlers
|
||||
// Tags (apenas úteis no modo LOCAL)
|
||||
// =================================
|
||||
|
||||
static esp_err_t tags_get_handler(httpd_req_t *req) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *list = cJSON_CreateArray();
|
||||
|
||||
int count = auth_get_tag_count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const char *tag = auth_get_tag_by_index(i);
|
||||
if (tag) {
|
||||
cJSON_AddItemToArray(list, cJSON_CreateString(tag));
|
||||
}
|
||||
if (tag) cJSON_AddItemToArray(list, cJSON_CreateString(tag));
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "tags", list);
|
||||
char *str = cJSON_PrintUnformatted(root);
|
||||
httpd_resp_sendstr(req, str);
|
||||
free(str);
|
||||
cJSON_Delete(root);
|
||||
send_json(req, root);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -164,6 +198,10 @@ static esp_err_t tags_delete_handler(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
static esp_err_t tags_register_handler(httpd_req_t *req) {
|
||||
if (auth_get_mode() != AUTH_MODE_LOCAL_RFID) {
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Registo de tags disponível apenas no modo LOCAL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
auth_wait_for_tag_registration();
|
||||
httpd_resp_sendstr(req, "Modo de registro de tag ativado");
|
||||
return ESP_OK;
|
||||
@@ -174,21 +212,21 @@ static esp_err_t tags_register_handler(httpd_req_t *req) {
|
||||
// =================================
|
||||
|
||||
void register_auth_handlers(httpd_handle_t server, void *ctx) {
|
||||
// Auth methods
|
||||
// Auth mode
|
||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||
.uri = "/api/v1/config/auth-methods",
|
||||
.uri = "/api/v1/config/auth-mode",
|
||||
.method = HTTP_GET,
|
||||
.handler = auth_methods_get_handler,
|
||||
.handler = auth_mode_get_handler,
|
||||
.user_ctx = ctx
|
||||
});
|
||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||
.uri = "/api/v1/config/auth-methods",
|
||||
.uri = "/api/v1/config/auth-mode",
|
||||
.method = HTTP_POST,
|
||||
.handler = auth_methods_post_handler,
|
||||
.handler = auth_mode_post_handler,
|
||||
.user_ctx = ctx
|
||||
});
|
||||
|
||||
// Users
|
||||
// Users (mock)
|
||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||
.uri = "/api/v1/config/users",
|
||||
.method = HTTP_GET,
|
||||
|
||||
@@ -1,45 +1,63 @@
|
||||
// =========================
|
||||
// ocpp_api.c
|
||||
// =========================
|
||||
#include "ocpp_api.h"
|
||||
#include "ocpp.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "cJSON.h"
|
||||
#include <string.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) {
|
||||
ESP_LOGD(TAG, "GET /api/v1/ocpp");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
|
||||
char server[64] = {0};
|
||||
char charge_id[64] = {0};
|
||||
ocpp_get_server(server);
|
||||
ocpp_get_charge_id(charge_id);
|
||||
|
||||
cJSON *status = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(status, "status", "connected");
|
||||
char *str = cJSON_Print(status);
|
||||
cJSON_AddBoolToObject(status, "connected", ocpp_is_connected());
|
||||
cJSON_AddStringToObject(status, "server", server);
|
||||
cJSON_AddStringToObject(status, "charge_id", charge_id);
|
||||
|
||||
char *str = cJSON_PrintUnformatted(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) {
|
||||
ESP_LOGD(TAG, "GET /api/v1/config/ocpp");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
|
||||
char server[64] = {0};
|
||||
char charge_id[64] = {0};
|
||||
bool enabled = ocpp_get_enabled();
|
||||
ocpp_get_server(server);
|
||||
ocpp_get_charge_id(charge_id);
|
||||
|
||||
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);
|
||||
cJSON_AddBoolToObject(json, "enabled", enabled);
|
||||
cJSON_AddStringToObject(json, "url", server);
|
||||
cJSON_AddStringToObject(json, "chargeBoxId", charge_id);
|
||||
|
||||
char *str = cJSON_PrintUnformatted(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) {
|
||||
ESP_LOGD(TAG, "POST /api/v1/config/ocpp");
|
||||
|
||||
char buf[512];
|
||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
if (len <= 0) {
|
||||
@@ -47,19 +65,28 @@ static esp_err_t ocpp_post_config_handler(httpd_req_t *req) {
|
||||
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 *enabled = cJSON_GetObjectItem(json, "enabled");
|
||||
if (cJSON_IsBool(enabled)) {
|
||||
ocpp_set_enabled(cJSON_IsTrue(enabled));
|
||||
}
|
||||
|
||||
cJSON *url = cJSON_GetObjectItem(json, "url");
|
||||
if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url));
|
||||
if (cJSON_IsString(url)) {
|
||||
ocpp_set_server(url->valuestring);
|
||||
}
|
||||
|
||||
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));
|
||||
if (cJSON_IsString(id)) {
|
||||
ocpp_set_charge_id(id->valuestring);
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
httpd_resp_sendstr(req, "OCPP config atualizada com sucesso");
|
||||
return ESP_OK;
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -13,8 +13,8 @@
|
||||
}
|
||||
</style>
|
||||
<title>Vite + React</title>
|
||||
<script type="module" crossorigin src="/assets/index-ClgQvp_F.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-AuNGQ-2m.css">
|
||||
<script type="module" crossorigin src="/assets/index-CmjuW5AW.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Bc9ibDeR.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user