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, "maxChargingCurrent", evse_get_max_charging_current());
|
||||||
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
|
cJSON_AddNumberToObject(root, "chargingCurrent", evse_get_charging_current());
|
||||||
cJSON_AddNumberToObject(root, "defaultChargingCurrent", evse_get_default_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, "socketOutlet", evse_get_socket_outlet());
|
||||||
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
|
cJSON_AddBoolToObject(root, "rcm", evse_is_rcm());
|
||||||
cJSON_AddNumberToObject(root, "temperatureThreshold", evse_get_temp_threshold());
|
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());
|
cJSON_AddBoolToObject(root, "enabledocpp", ocpp_get_enabled());
|
||||||
ocpp_get_server(str);
|
ocpp_get_server(str);
|
||||||
cJSON_AddStringToObject(root, "serverocpp", str);
|
cJSON_AddStringToObject(root, "serverocpp", str);
|
||||||
ocpp_get_rfid(str);
|
//ocpp_get_rfid(str);
|
||||||
cJSON_AddStringToObject(root, "rfid", str);
|
//cJSON_AddStringToObject(root, "rfid", str);
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ esp_err_t json_set_evse_config(cJSON *root)
|
|||||||
}
|
}
|
||||||
if (cJSON_IsBool(cJSON_GetObjectItem(root, "requireAuth")))
|
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")))
|
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")));
|
ocpp_set_server(cJSON_GetStringValue(cJSON_GetObjectItem(root, "serverocpp")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cJSON_IsString(cJSON_GetObjectItem(root, "rfid")))
|
//if (cJSON_IsString(cJSON_GetObjectItem(root, "rfid")))
|
||||||
{
|
//{
|
||||||
ocpp_set_rfid(cJSON_GetStringValue(cJSON_GetObjectItem(root, "rfid")));
|
// ocpp_set_rfid(cJSON_GetStringValue(cJSON_GetObjectItem(root, "rfid")));
|
||||||
}
|
//}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -343,7 +343,7 @@ cJSON *json_get_state(void)
|
|||||||
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
|
cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state()));
|
||||||
cJSON_AddBoolToObject(root, "available", evse_config_is_available());
|
cJSON_AddBoolToObject(root, "available", evse_config_is_available());
|
||||||
cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled());
|
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());
|
cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached());
|
||||||
|
|
||||||
uint32_t error = evse_error_get_bits();
|
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}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
|
|||||||
@@ -1,115 +1,32 @@
|
|||||||
#ifndef AUTH_H
|
#pragma once
|
||||||
#define AUTH_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include "auth_types.h" // enum + MAX LEN para API
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Maximum length of an RFID tag (including null terminator)
|
/* Evento auxiliar legado/útil (resultado local) */
|
||||||
#define AUTH_TAG_MAX_LEN 30
|
|
||||||
|
|
||||||
/// Event structure emitted after a tag is read
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char tag[AUTH_TAG_MAX_LEN]; ///< The tag that was read
|
char tag[AUTH_TAG_MAX_LEN];
|
||||||
bool authorized; ///< true if the tag is valid
|
bool authorized;
|
||||||
} auth_event_t;
|
} 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);
|
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);
|
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);
|
bool auth_tag_exists(const char *tag);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Logs all currently registered tags via ESP logging.
|
|
||||||
*/
|
|
||||||
void auth_list_tags(void);
|
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);
|
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);
|
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);
|
int auth_get_tag_count(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the tag string at the given index.
|
|
||||||
*
|
|
||||||
* @param index The index (0 ≤ index < auth_get_tag_count())
|
|
||||||
* @return Pointer to the tag string, or NULL if index is invalid
|
|
||||||
*/
|
|
||||||
const char *auth_get_tag_by_index(int index);
|
const char *auth_get_tag_by_index(int index);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // AUTH_H
|
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
|
#include "auth_types.h" // só tipos comuns; evita incluir auth.h
|
||||||
#define AUTH_EVENT_TAG_MAX_LEN 32
|
|
||||||
|
|
||||||
ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
|
ESP_EVENT_DECLARE_BASE(AUTH_EVENTS);
|
||||||
|
|
||||||
|
/* IDs de eventos */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AUTH_EVENT_TAG_PROCESSED,
|
AUTH_EVENT_TAG_PROCESSED = 0, // resultado LOCAL -> auth_tag_event_data_t
|
||||||
AUTH_EVENT_TAG_SAVED,
|
AUTH_EVENT_TAG_VERIFY, // pedir validação OCPP -> auth_tag_verify_event_t
|
||||||
AUTH_EVENT_ENABLED_CHANGED,
|
AUTH_EVENT_TAG_SAVED, // registada (modo registo) -> auth_tag_event_data_t
|
||||||
AUTH_EVENT_INIT,
|
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;
|
} auth_event_id_t;
|
||||||
|
|
||||||
|
/* Payloads */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char tag[AUTH_EVENT_TAG_MAX_LEN];
|
char tag[AUTH_TAG_MAX_LEN];
|
||||||
bool authorized;
|
bool authorized;
|
||||||
} auth_tag_event_data_t;
|
} auth_tag_event_data_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool enabled;
|
char tag[AUTH_TAG_MAX_LEN];
|
||||||
} auth_enabled_event_data_t;
|
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.h"
|
||||||
#include "auth_events.h"
|
#include "auth_events.h"
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/queue.h>
|
#include <freertos/queue.h>
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h> // <-- necessário para strcasecmp
|
||||||
#include "wiegand_reader.h"
|
#include "wiegand_reader.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "esp_random.h"
|
#include "esp_random.h"
|
||||||
|
|
||||||
|
|
||||||
#define MAX_TAGS 50
|
#define MAX_TAGS 50
|
||||||
|
|
||||||
static const char *TAG = "Auth";
|
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 bool waiting_for_registration = false;
|
||||||
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
|
||||||
static int tag_count = 0;
|
static int tag_count = 0;
|
||||||
|
static uint32_t s_next_req_id = 1;
|
||||||
|
|
||||||
// NVS keys
|
/* ===== NVS keys ===== */
|
||||||
#define NVS_NAMESPACE "auth"
|
#define NVS_NAMESPACE "auth"
|
||||||
#define NVS_TAG_PREFIX "tag_"
|
#define NVS_TAG_PREFIX "tag_"
|
||||||
#define NVS_TAG_COUNT_KEY "count"
|
#define NVS_TAG_COUNT_KEY "count"
|
||||||
#define NVS_ENABLED_KEY "enabled"
|
#define NVS_MODE_KEY "mode" // uint8_t
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// 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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
* NVS Persistence (tags)
|
||||||
|
* ========================= */
|
||||||
static void load_tags_from_nvs(void) {
|
static void load_tags_from_nvs(void) {
|
||||||
nvs_handle_t handle;
|
nvs_handle_t handle;
|
||||||
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
|
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
|
||||||
@@ -71,7 +49,6 @@ static void load_tags_from_nvs(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tag_count = 0;
|
tag_count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < count && i < MAX_TAGS; i++) {
|
for (int i = 0; i < count && i < MAX_TAGS; i++) {
|
||||||
char key[16];
|
char key[16];
|
||||||
char tag_buf[AUTH_TAG_MAX_LEN];
|
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);
|
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) {
|
static bool is_tag_valid(const char *tag) {
|
||||||
for (int i = 0; i < tag_count; i++) {
|
for (int i = 0; i < tag_count; i++) {
|
||||||
if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
/* =========================
|
||||||
// Public API
|
* Public API
|
||||||
// ===========================
|
* ========================= */
|
||||||
|
|
||||||
|
|
||||||
void auth_init(void) {
|
void auth_init(void) {
|
||||||
load_auth_config();
|
load_mode_from_nvs();
|
||||||
load_tags_from_nvs();
|
load_tags_from_nvs();
|
||||||
|
|
||||||
if (enabled) {
|
if (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID) {
|
||||||
initWiegand();
|
initWiegand();
|
||||||
ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)");
|
ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode));
|
||||||
} else {
|
} 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_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) {
|
void auth_set_mode(auth_mode_t mode) {
|
||||||
enabled = value;
|
if (mode < AUTH_MODE_OPEN || mode > AUTH_MODE_OCPP_RFID) {
|
||||||
save_auth_config();
|
ESP_LOGW(TAG, "Invalid mode: %d", (int)mode);
|
||||||
ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");
|
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 };
|
s_mode = mode;
|
||||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY);
|
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) {
|
auth_mode_t auth_get_mode(void) {
|
||||||
return enabled;
|
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) {
|
bool auth_add_tag(const char *tag) {
|
||||||
if (tag_count >= MAX_TAGS) return false;
|
if (tag_count >= MAX_TAGS) return false;
|
||||||
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) 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);
|
strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
|
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) {
|
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;
|
waiting_for_registration = true;
|
||||||
ESP_LOGI(TAG, "Tag registration mode enabled.");
|
ESP_LOGI(TAG, "Tag registration mode enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void auth_process_tag(const char *tag) {
|
void auth_process_tag(const char *tag) {
|
||||||
if (!tag || !auth_is_enabled()) {
|
if (!tag || !*tag) {
|
||||||
ESP_LOGW(TAG, "Auth disabled or NULL tag received.");
|
ESP_LOGW(TAG, "NULL/empty tag received");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waiting_for_registration) {
|
switch (s_mode) {
|
||||||
if (auth_add_tag(tag)) {
|
case AUTH_MODE_OPEN: {
|
||||||
auth_tag_event_data_t event;
|
// Sem verificação; normalmente nem é necessário evento.
|
||||||
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
|
ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag);
|
||||||
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
|
break;
|
||||||
event.authorized = true;
|
}
|
||||||
|
|
||||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &event, sizeof(event), portMAX_DELAY);
|
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);
|
ESP_LOGI(TAG, "Tag registered: %s", tag);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
|
ESP_LOGW(TAG, "Failed to register tag: %s", tag);
|
||||||
}
|
}
|
||||||
waiting_for_registration = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auth_tag_event_data_t event;
|
auth_tag_event_data_t ev = {0};
|
||||||
strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
|
strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||||
event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
|
ev.authorized = is_tag_valid(tag);
|
||||||
event.authorized = is_tag_valid(tag);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int auth_get_tag_count(void) {
|
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/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h" // Para buzzer passivo
|
||||||
|
|
||||||
#define BUZZER_GPIO GPIO_NUM_27
|
#define BUZZER_GPIO GPIO_NUM_27
|
||||||
static const char *TAG = "Buzzer";
|
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 {
|
typedef struct {
|
||||||
uint16_t on_ms;
|
uint16_t on_ms;
|
||||||
uint16_t off_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)},
|
[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); }
|
// --- Funções de controle ---
|
||||||
static void buzzer_off(void) { gpio_set_level(BUZZER_GPIO, 0); }
|
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) {
|
static void buzzer_execute(buzzer_pattern_id_t pattern_id) {
|
||||||
if ((int)pattern_id <= BUZZER_PATTERN_NONE || pattern_id >= BUZZER_PATTERN_MAX) {
|
if ((int)pattern_id <= BUZZER_PATTERN_NONE || pattern_id >= BUZZER_PATTERN_MAX) {
|
||||||
ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
|
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) {
|
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;
|
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) {
|
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;
|
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;
|
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);
|
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;
|
buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;
|
||||||
|
|
||||||
} else if (id == AUTH_EVENT_TAG_SAVED) {
|
} else if (id == AUTH_EVENT_TAG_SAVED) {
|
||||||
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
|
buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
|
||||||
} else {
|
} 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);
|
esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Inicialização ---
|
||||||
void buzzer_init(void) {
|
void buzzer_init(void) {
|
||||||
|
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 = {
|
gpio_config_t io = {
|
||||||
.pin_bit_mask = BIT64(BUZZER_GPIO),
|
.pin_bit_mask = (1ULL << s_buzzer_cfg.gpio),
|
||||||
.mode = GPIO_MODE_OUTPUT,
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
.pull_down_en = 0,
|
.pull_down_en = 0,
|
||||||
.pull_up_en = 0,
|
.pull_up_en = 0,
|
||||||
.intr_type = GPIO_INTR_DISABLE
|
.intr_type = GPIO_INTR_DISABLE
|
||||||
};
|
};
|
||||||
gpio_config(&io);
|
gpio_config(&io);
|
||||||
|
}
|
||||||
|
|
||||||
buzzer_off();
|
buzzer_off();
|
||||||
|
|
||||||
// Registro de handlers
|
// Registro de handlers
|
||||||
@@ -155,14 +240,12 @@ void buzzer_init(void) {
|
|||||||
AUTH_EVENT_TAG_SAVED,
|
AUTH_EVENT_TAG_SAVED,
|
||||||
auth_event_handler,
|
auth_event_handler,
|
||||||
NULL));
|
NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS,
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
|
||||||
NETWORK_EVENTS,
|
|
||||||
NETWORK_EVENT_AP_STARTED,
|
NETWORK_EVENT_AP_STARTED,
|
||||||
network_event_handler,
|
network_event_handler,
|
||||||
NULL
|
NULL));
|
||||||
));
|
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d (%s)",
|
||||||
ESP_LOGI(TAG, "Buzzer initialized on GPIO %d", BUZZER_GPIO);
|
s_buzzer_cfg.gpio,
|
||||||
|
s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE ? "passive/PWM" : "active/ON-OFF");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "evse_config.h"
|
#include "evse_config.h"
|
||||||
#include "board_config.h"
|
#include "board_config.h"
|
||||||
#include "evse_limits.h"
|
#include "evse_limits.h"
|
||||||
|
#include "evse_api.h" // <— para evse_get_state / evse_state_is_charging
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
@@ -21,42 +22,55 @@ static bool rcm;
|
|||||||
static uint8_t temp_threshold = 60;
|
static uint8_t temp_threshold = 60;
|
||||||
static bool require_auth;
|
static bool require_auth;
|
||||||
|
|
||||||
|
// Availability / Enable flags
|
||||||
|
static bool is_available = true;
|
||||||
|
static bool is_enabled = true;
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// Initialization
|
// Initialization
|
||||||
// ========================
|
// ========================
|
||||||
esp_err_t evse_config_init(void) {
|
esp_err_t evse_config_init(void)
|
||||||
|
{
|
||||||
ESP_LOGD(TAG, "Initializing NVS configuration...");
|
ESP_LOGD(TAG, "Initializing NVS configuration...");
|
||||||
return nvs_open("evse", NVS_READWRITE, &nvs);
|
return nvs_open("evse", NVS_READWRITE, &nvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_check_defaults(void) {
|
void evse_check_defaults(void)
|
||||||
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
uint8_t u8;
|
uint8_t u8;
|
||||||
uint16_t u16;
|
uint16_t u16;
|
||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
bool needs_commit = false;
|
bool needs_commit = false;
|
||||||
|
uint8_t u8_bool;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Checking default parameters...");
|
ESP_LOGD(TAG, "Checking default parameters...");
|
||||||
|
|
||||||
// Max charging current
|
// Max charging current
|
||||||
err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
|
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;
|
max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
|
||||||
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
|
nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
|
ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
max_charging_current = u8;
|
max_charging_current = u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charging current (default, persisted)
|
// Charging current (default, persisted)
|
||||||
err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
|
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;
|
charging_current = max_charging_current;
|
||||||
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
nvs_set_u16(nvs, "def_chrg_curr", charging_current);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
|
ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
charging_current = u16;
|
charging_current = u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +81,8 @@ void evse_check_defaults(void) {
|
|||||||
// Auth required
|
// Auth required
|
||||||
err = nvs_get_u8(nvs, "require_auth", &u8);
|
err = nvs_get_u8(nvs, "require_auth", &u8);
|
||||||
require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
|
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);
|
nvs_set_u8(nvs, "require_auth", require_auth);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
}
|
}
|
||||||
@@ -75,7 +90,8 @@ void evse_check_defaults(void) {
|
|||||||
// Socket outlet
|
// Socket outlet
|
||||||
err = nvs_get_u8(nvs, "socket_outlet", &u8);
|
err = nvs_get_u8(nvs, "socket_outlet", &u8);
|
||||||
socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
|
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);
|
nvs_set_u8(nvs, "socket_outlet", socket_outlet);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
}
|
}
|
||||||
@@ -83,7 +99,8 @@ void evse_check_defaults(void) {
|
|||||||
// RCM
|
// RCM
|
||||||
err = nvs_get_u8(nvs, "rcm", &u8);
|
err = nvs_get_u8(nvs, "rcm", &u8);
|
||||||
rcm = (err == ESP_OK && u8) && board_config.rcm;
|
rcm = (err == ESP_OK && u8) && board_config.rcm;
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
nvs_set_u8(nvs, "rcm", rcm);
|
nvs_set_u8(nvs, "rcm", rcm);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
}
|
}
|
||||||
@@ -91,7 +108,8 @@ void evse_check_defaults(void) {
|
|||||||
// Temp threshold
|
// Temp threshold
|
||||||
err = nvs_get_u8(nvs, "temp_threshold", &u8);
|
err = nvs_get_u8(nvs, "temp_threshold", &u8);
|
||||||
temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
|
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);
|
nvs_set_u8(nvs, "temp_threshold", temp_threshold);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
}
|
}
|
||||||
@@ -106,12 +124,42 @@ void evse_check_defaults(void) {
|
|||||||
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
|
if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
|
||||||
evse_set_under_power_limit(u16);
|
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
|
// Save to NVS if needed
|
||||||
if (needs_commit) {
|
if (needs_commit)
|
||||||
|
{
|
||||||
err = nvs_commit(nvs);
|
err = nvs_commit(nvs);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
ESP_LOGD(TAG, "Configuration committed to NVS.");
|
ESP_LOGD(TAG, "Configuration committed to NVS.");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err));
|
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
|
// Charging current getters/setters
|
||||||
// ========================
|
// ========================
|
||||||
uint8_t evse_get_max_charging_current(void) {
|
uint8_t evse_get_max_charging_current(void)
|
||||||
|
{
|
||||||
return max_charging_current;
|
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)
|
if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
max_charging_current = value;
|
max_charging_current = value;
|
||||||
@@ -133,11 +183,13 @@ esp_err_t evse_set_max_charging_current(uint8_t value) {
|
|||||||
return nvs_commit(nvs);
|
return nvs_commit(nvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t evse_get_charging_current(void) {
|
uint16_t evse_get_charging_current(void)
|
||||||
|
{
|
||||||
return charging_current;
|
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))
|
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
charging_current = value;
|
charging_current = value;
|
||||||
@@ -145,14 +197,16 @@ esp_err_t evse_set_charging_current(uint16_t value) {
|
|||||||
return nvs_commit(nvs);
|
return nvs_commit(nvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t evse_get_default_charging_current(void) {
|
uint16_t evse_get_default_charging_current(void)
|
||||||
|
{
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
|
if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
|
||||||
return value;
|
return value;
|
||||||
return charging_current;
|
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))
|
if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
nvs_set_u16(nvs, "def_chrg_curr", value);
|
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)
|
// 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;
|
value = max_charging_current;
|
||||||
} else if (value < MIN_CHARGING_CURRENT_LIMIT) {
|
}
|
||||||
|
else if (value < MIN_CHARGING_CURRENT_LIMIT)
|
||||||
|
{
|
||||||
value = MIN_CHARGING_CURRENT_LIMIT;
|
value = MIN_CHARGING_CURRENT_LIMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
charging_current_runtime = value;
|
charging_current_runtime = value;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
|
||||||
|
|
||||||
// --- PUBLICA ALTERAÇÃO DE CONFIG DO EVSE ---
|
// --- PUBLICA ALTERAÇÃO DE CONFIG DO EVSE ---
|
||||||
evse_config_event_data_t evt = {
|
evse_config_event_data_t evt = {
|
||||||
.charging = evse_state_is_charging(evse_get_state()),
|
.charging = evse_state_is_charging(evse_get_state()),
|
||||||
.hw_max_current = (float)evse_get_max_charging_current(),
|
.hw_max_current = (float)evse_get_max_charging_current(),
|
||||||
.runtime_current = (float)charging_current_runtime,
|
.runtime_current = (float)evse_get_runtime_charging_current(),
|
||||||
.timestamp_us = esp_timer_get_time()
|
.timestamp_us = esp_timer_get_time()};
|
||||||
};
|
|
||||||
|
|
||||||
esp_event_post(EVSE_EVENTS,
|
esp_event_post(EVSE_EVENTS,
|
||||||
EVSE_EVENT_CONFIG_UPDATED,
|
EVSE_EVENT_CONFIG_UPDATED,
|
||||||
&evt,
|
&evt,
|
||||||
sizeof(evt),
|
sizeof(evt),
|
||||||
portMAX_DELAY);
|
portMAX_DELAY);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t evse_get_runtime_charging_current(void)
|
||||||
uint16_t evse_get_runtime_charging_current(void) {
|
{
|
||||||
return charging_current_runtime;
|
return charging_current_runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// Socket outlet
|
// Socket outlet
|
||||||
// ========================
|
// ========================
|
||||||
bool evse_get_socket_outlet(void) {
|
bool evse_get_socket_outlet(void)
|
||||||
|
{
|
||||||
return socket_outlet;
|
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)
|
if (value && !board_config.proximity)
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
socket_outlet = value;
|
socket_outlet = value;
|
||||||
@@ -215,11 +271,13 @@ esp_err_t evse_set_socket_outlet(bool value) {
|
|||||||
// ========================
|
// ========================
|
||||||
// RCM
|
// RCM
|
||||||
// ========================
|
// ========================
|
||||||
bool evse_is_rcm(void) {
|
bool evse_is_rcm(void)
|
||||||
|
{
|
||||||
return rcm;
|
return rcm;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t evse_set_rcm(bool value) {
|
esp_err_t evse_set_rcm(bool value)
|
||||||
|
{
|
||||||
if (value && !board_config.rcm)
|
if (value && !board_config.rcm)
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
rcm = value;
|
rcm = value;
|
||||||
@@ -230,11 +288,13 @@ esp_err_t evse_set_rcm(bool value) {
|
|||||||
// ========================
|
// ========================
|
||||||
// Temperature
|
// Temperature
|
||||||
// ========================
|
// ========================
|
||||||
uint8_t evse_get_temp_threshold(void) {
|
uint8_t evse_get_temp_threshold(void)
|
||||||
|
{
|
||||||
return temp_threshold;
|
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)
|
if (value < 40 || value > 80)
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
temp_threshold = value;
|
temp_threshold = value;
|
||||||
@@ -242,29 +302,58 @@ esp_err_t evse_set_temp_threshold(uint8_t value) {
|
|||||||
return nvs_commit(nvs);
|
return nvs_commit(nvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// Availability
|
// Availability
|
||||||
// ========================
|
// ========================
|
||||||
static bool is_available = true;
|
bool evse_config_is_available(void)
|
||||||
|
{
|
||||||
bool evse_config_is_available(void) {
|
|
||||||
return is_available;
|
return is_available;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_config_set_available(bool available) {
|
void evse_config_set_available(bool available)
|
||||||
is_available = 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
|
// Enable/Disable
|
||||||
// ========================
|
// ========================
|
||||||
static bool is_enabled = true;
|
bool evse_config_is_enabled(void)
|
||||||
|
{
|
||||||
bool evse_config_is_enabled(void) {
|
|
||||||
return is_enabled;
|
return is_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_config_set_enabled(bool enabled) {
|
void evse_config_set_enabled(bool enabled)
|
||||||
is_enabled = 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();
|
evse_state_t current = evse_get_state();
|
||||||
if (current != last_state) {
|
if (current != last_state) {
|
||||||
ESP_LOGI(TAG, "State changed: %s → %s",
|
//ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current));
|
||||||
evse_state_to_str(last_state),
|
|
||||||
evse_state_to_str(current));
|
|
||||||
last_state = current;
|
last_state = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,12 +83,13 @@ static void update_outputs(evse_state_t state) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EVSE_STATE_C1:
|
case EVSE_STATE_C1:
|
||||||
case EVSE_STATE_D1:
|
case EVSE_STATE_D1: {
|
||||||
pilot_set_level(true);
|
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_waiting = true;
|
||||||
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
|
c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case EVSE_STATE_C2:
|
case EVSE_STATE_C2:
|
||||||
case EVSE_STATE_D2:
|
case EVSE_STATE_D2:
|
||||||
pilot_set_amps(MIN(current, cable_max_current));
|
pilot_set_amps(MIN(current, cable_max_current));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "evse_api.h"
|
#include "evse_api.h"
|
||||||
#include "evse_meter.h"
|
#include "evse_meter.h"
|
||||||
#include "evse_session.h"
|
#include "evse_session.h"
|
||||||
|
#include "evse_config.h"
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
#include "auth_events.h"
|
#include "auth_events.h"
|
||||||
#include "loadbalancer_events.h"
|
#include "loadbalancer_events.h"
|
||||||
|
#include "ocpp_events.h"
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
|
|
||||||
static const char *TAG = "EVSE_Manager";
|
static const char *TAG = "EVSE_Manager";
|
||||||
@@ -26,40 +28,47 @@ 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 =====
|
// ===== Task de ciclo principal =====
|
||||||
static void evse_manager_task(void *arg) {
|
static void evse_manager_task(void *arg)
|
||||||
while (true) {
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
evse_manager_tick();
|
evse_manager_tick();
|
||||||
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
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)
|
||||||
static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* data) {
|
{
|
||||||
if (base != AUTH_EVENTS || data == NULL) return;
|
if (base != AUTH_EVENTS || !data)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (id) {
|
auth_mode_t g_mode = AUTH_MODE_OPEN;
|
||||||
case AUTH_EVENT_TAG_PROCESSED: {
|
|
||||||
auth_tag_event_data_t *evt = (auth_tag_event_data_t*)data;
|
switch (id)
|
||||||
ESP_LOGI("EVSE", "Tag: %s | Autorized: %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
|
{
|
||||||
|
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);
|
evse_state_set_authorized(evt->authorized);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUTH_EVENT_ENABLED_CHANGED:
|
case AUTH_EVENT_MODE_CHANGED:
|
||||||
case AUTH_EVENT_INIT: {
|
case AUTH_EVENT_INIT:
|
||||||
auth_enabled_event_data_t *evt = (auth_enabled_event_data_t*)data;
|
{
|
||||||
auth_enabled = evt->enabled;
|
const auth_mode_event_data_t *evt = (const auth_mode_event_data_t *)data;
|
||||||
|
g_mode = evt->mode;
|
||||||
ESP_LOGI("EVSE", "Auth %s (%s)",
|
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(g_mode));
|
||||||
id == AUTH_EVENT_ENABLED_CHANGED ? "ficou" : "init",
|
if (g_mode == AUTH_MODE_OPEN)
|
||||||
evt->enabled ? "ATIVO" : "INATIVO");
|
{
|
||||||
|
|
||||||
if (!auth_enabled) {
|
|
||||||
evse_state_set_authorized(true);
|
evse_state_set_authorized(true);
|
||||||
ESP_LOGI("EVSE", "Autenticação desativada → autorização forçada.");
|
auth_enabled = false;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
evse_state_set_authorized(false);
|
evse_state_set_authorized(false);
|
||||||
ESP_LOGI("EVSE", "Autenticação ativada → aguardando autorização por tag.");
|
auth_enabled = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -67,22 +76,91 @@ static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* da
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== Tratador de eventos de loadbalancer =====
|
// ===== Tratador de eventos de loadbalancer =====
|
||||||
static void on_loadbalancer_event(void* handler_arg, esp_event_base_t event_base,
|
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
||||||
int32_t event_id, void* event_data) {
|
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;
|
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)",
|
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
|
||||||
evt->enabled ? "ENABLED" : "DISABLED", evt->timestamp_us);
|
evt->enabled ? "ENABLED" : "DISABLED", evt->timestamp_us);
|
||||||
// Ações adicionais podem ser adicionadas aqui conforme necessário
|
// 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);
|
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);
|
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 =====
|
// ===== Inicialização =====
|
||||||
void evse_manager_init(void) {
|
void evse_manager_init(void)
|
||||||
|
{
|
||||||
evse_mutex = xSemaphoreCreateMutex();
|
evse_mutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
evse_config_init();
|
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(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(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.");
|
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
||||||
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Main Tick =====
|
// ===== Main Tick =====
|
||||||
void evse_manager_tick(void) {
|
void evse_manager_tick(void)
|
||||||
|
{
|
||||||
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
||||||
|
|
||||||
evse_hardware_tick();
|
evse_hardware_tick();
|
||||||
@@ -109,15 +189,20 @@ void evse_manager_tick(void) {
|
|||||||
evse_temperature_check();
|
evse_temperature_check();
|
||||||
evse_session_tick();
|
evse_session_tick();
|
||||||
|
|
||||||
if (auth_enabled) {
|
if (auth_enabled)
|
||||||
|
{
|
||||||
// If the car is disconnected, revoke authorization
|
// 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.");
|
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||||
evse_state_set_authorized(false);
|
evse_state_set_authorized(false);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// If authentication is disabled, ensure authorization is always granted
|
// If authentication is disabled, ensure authorization is always granted
|
||||||
if (!evse_state_get_authorized()) {
|
if (!evse_state_get_authorized())
|
||||||
|
{
|
||||||
evse_state_set_authorized(true);
|
evse_state_set_authorized(true);
|
||||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
|
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
static const char *TAG = "evse_meter";
|
static const char *TAG = "evse_meter";
|
||||||
static SemaphoreHandle_t meter_mutex;
|
static SemaphoreHandle_t meter_mutex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
uint32_t power_watts[EVSE_METER_PHASE_COUNT];
|
uint32_t power_watts[EVSE_METER_PHASE_COUNT];
|
||||||
float voltage[EVSE_METER_PHASE_COUNT];
|
float voltage[EVSE_METER_PHASE_COUNT];
|
||||||
float current[EVSE_METER_PHASE_COUNT];
|
float current[EVSE_METER_PHASE_COUNT];
|
||||||
@@ -19,21 +20,27 @@ typedef struct {
|
|||||||
|
|
||||||
static evse_meter_data_t meter_data;
|
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) {
|
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) {
|
{
|
||||||
|
if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data)
|
||||||
|
{
|
||||||
const meter_event_data_t *evt = (const meter_event_data_t *)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);
|
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;
|
const meter_event_data_t *evt = (const meter_event_data_t *)event_data;
|
||||||
if (!evt) return;
|
if (!evt)
|
||||||
|
return;
|
||||||
|
|
||||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
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.power_watts[i] = evt->watt[i];
|
||||||
meter_data.voltage[i] = evt->vrms[i];
|
meter_data.voltage[i] = evt->vrms[i];
|
||||||
meter_data.current[i] = evt->irms[i];
|
meter_data.current[i] = evt->irms[i];
|
||||||
@@ -49,11 +56,11 @@ void evse_meter_on_meter_event(void* arg, void* event_data) {
|
|||||||
meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
|
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.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
|
||||||
meter_data.current[0], meter_data.current[1], meter_data.current[2],
|
meter_data.current[0], meter_data.current[1], meter_data.current[2],
|
||||||
meter_data.energy_wh
|
meter_data.energy_wh);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void evse_meter_init(void) {
|
void evse_meter_init(void)
|
||||||
|
{
|
||||||
meter_mutex = xSemaphoreCreateMutex();
|
meter_mutex = xSemaphoreCreateMutex();
|
||||||
ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
|
ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||||
@@ -63,42 +70,51 @@ void evse_meter_init(void) {
|
|||||||
ESP_LOGI(TAG, "EVSE Meter listener registered.");
|
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);
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
int sum = 0;
|
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];
|
sum += meter_data.power_watts[i];
|
||||||
}
|
}
|
||||||
xSemaphoreGive(meter_mutex);
|
xSemaphoreGive(meter_mutex);
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
int evse_meter_get_total_energy(void) {
|
int evse_meter_get_total_energy(void)
|
||||||
|
{
|
||||||
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
xSemaphoreTake(meter_mutex, portMAX_DELAY);
|
||||||
int val = meter_data.energy_wh;
|
int val = meter_data.energy_wh;
|
||||||
xSemaphoreGive(meter_mutex);
|
xSemaphoreGive(meter_mutex);
|
||||||
return val;
|
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);
|
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];
|
power[i] = meter_data.power_watts[i];
|
||||||
}
|
}
|
||||||
xSemaphoreGive(meter_mutex);
|
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);
|
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];
|
voltage[i] = meter_data.voltage[i];
|
||||||
}
|
}
|
||||||
xSemaphoreGive(meter_mutex);
|
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);
|
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];
|
current[i] = meter_data.current[i];
|
||||||
}
|
}
|
||||||
xSemaphoreGive(meter_mutex);
|
xSemaphoreGive(meter_mutex);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ void pilot_init(void)
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
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_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
|
// Inicializa ADC121S021 externo
|
||||||
adc121s021_dma_init();
|
adc121s021_dma_init();
|
||||||
@@ -107,6 +107,12 @@ void pilot_set_amps(uint16_t amps)
|
|||||||
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
|
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) {
|
static int compare_int(const void *a, const void *b) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ typedef enum {
|
|||||||
EVSE_EVENT_INIT,
|
EVSE_EVENT_INIT,
|
||||||
EVSE_EVENT_STATE_CHANGED,
|
EVSE_EVENT_STATE_CHANGED,
|
||||||
EVSE_EVENT_CONFIG_UPDATED,
|
EVSE_EVENT_CONFIG_UPDATED,
|
||||||
|
EVSE_EVENT_ENABLE_UPDATED,
|
||||||
|
EVSE_EVENT_AVAILABLE_UPDATED,
|
||||||
} evse_event_id_t;
|
} evse_event_id_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -30,4 +32,15 @@ typedef struct {
|
|||||||
int64_t timestamp_us; // Momento da atualização
|
int64_t timestamp_us; // Momento da atualização
|
||||||
} evse_config_event_data_t;
|
} 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
|
#endif // EVSE_EVENTS_H
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void pilot_set_level(bool level);
|
|||||||
/**
|
/**
|
||||||
* @brief Ativa o PWM do Pilot com corrente limitada
|
* @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);
|
void pilot_set_amps(uint16_t amps);
|
||||||
|
|
||||||
|
|||||||
@@ -33,71 +33,100 @@ extern void evse_link_master_init(void);
|
|||||||
extern void evse_link_slave_init(void);
|
extern void evse_link_slave_init(void);
|
||||||
|
|
||||||
static void framing_rx_cb(uint8_t src, uint8_t dest,
|
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);
|
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);
|
_rx_cb(src, dest, payload, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Register protocol-level Rx callback
|
// 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;
|
_rx_cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config from NVS
|
// Load config from NVS
|
||||||
enum { EV_OK = ESP_OK };
|
enum
|
||||||
static void load_link_config(void) {
|
{
|
||||||
|
EV_OK = ESP_OK
|
||||||
|
};
|
||||||
|
static void load_link_config(void)
|
||||||
|
{
|
||||||
nvs_handle_t handle;
|
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");
|
ESP_LOGW(TAG, "NVS open failed, using defaults");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t mode, id, en;
|
uint8_t mode, id, en;
|
||||||
if (nvs_get_u8(handle, _NVS_MODE_KEY, &mode) == EV_OK &&
|
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;
|
_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;
|
_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);
|
_enabled = (en != 0);
|
||||||
}
|
}
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save config to NVS
|
// Save config to NVS
|
||||||
static void save_link_config(void) {
|
static void save_link_config(void)
|
||||||
|
{
|
||||||
nvs_handle_t handle;
|
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_MODE_KEY, (uint8_t)_mode);
|
||||||
nvs_set_u8(handle, _NVS_ID_KEY, _self_id);
|
nvs_set_u8(handle, _NVS_ID_KEY, _self_id);
|
||||||
nvs_set_u8(handle, _NVS_ENABLED_KEY, _enabled ? 1 : 0);
|
nvs_set_u8(handle, _NVS_ENABLED_KEY, _enabled ? 1 : 0);
|
||||||
nvs_commit(handle);
|
nvs_commit(handle);
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ESP_LOGE(TAG, "Failed to save NVS");
|
ESP_LOGE(TAG, "Failed to save NVS");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters/setters
|
// Getters/setters
|
||||||
void evse_link_set_mode(evse_link_mode_t m) { _mode = m; save_link_config(); }
|
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; }
|
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(); }
|
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; }
|
uint8_t evse_link_get_self_id(void) { return _self_id; }
|
||||||
void evse_link_set_enabled(bool en) { _enabled = en; save_link_config(); }
|
void evse_link_set_enabled(bool en)
|
||||||
|
{
|
||||||
|
_enabled = en;
|
||||||
|
save_link_config();
|
||||||
|
}
|
||||||
bool evse_link_is_enabled(void) { return _enabled; }
|
bool evse_link_is_enabled(void) { return _enabled; }
|
||||||
|
|
||||||
// RX task: reads bytes from UART and feeds framing
|
// 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];
|
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));
|
int len = uart_read_bytes(UART_PORT, buf, sizeof(buf), pdMS_TO_TICKS(1000));
|
||||||
if (len > 0) {
|
if (len > 0)
|
||||||
for (int i = 0; i < len; ++i) {
|
{
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
evse_link_recv_byte(buf[i]);
|
evse_link_recv_byte(buf[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,13 +134,15 @@ static void evse_link_rx_task(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize EVSE-Link component
|
// Initialize EVSE-Link component
|
||||||
void evse_link_init(void) {
|
void evse_link_init(void)
|
||||||
|
{
|
||||||
load_link_config();
|
load_link_config();
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Link init: mode=%c id=0x%02X enabled=%d",
|
ESP_LOGI(TAG, "Link init: mode=%c id=0x%02X enabled=%d",
|
||||||
_mode == EVSE_LINK_MODE_MASTER ? 'M' : 'S',
|
_mode == EVSE_LINK_MODE_MASTER ? 'M' : 'S',
|
||||||
_self_id, _enabled);
|
_self_id, _enabled);
|
||||||
if (!_enabled) return;
|
if (!_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
// 1) framing layer init (sets up mutex, UART driver, etc.)
|
// 1) framing layer init (sets up mutex, UART driver, etc.)
|
||||||
evse_link_framing_init();
|
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);
|
xTaskCreate(evse_link_rx_task, "evse_link_rx", 4096, NULL, 4, NULL);
|
||||||
|
|
||||||
// 3) delegate to master or slave
|
// 3) delegate to master or slave
|
||||||
if (_mode == EVSE_LINK_MODE_MASTER) {
|
if (_mode == EVSE_LINK_MODE_MASTER)
|
||||||
|
{
|
||||||
evse_link_master_init();
|
evse_link_master_init();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
evse_link_slave_init();
|
evse_link_slave_init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a frame (delegates to framing module)
|
// Send a frame (delegates to framing module)
|
||||||
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len) {
|
bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len)
|
||||||
if (!evse_link_is_enabled()) return false;
|
{
|
||||||
|
if (!evse_link_is_enabled())
|
||||||
|
return false;
|
||||||
uint8_t src = evse_link_get_self_id();
|
uint8_t src = evse_link_get_self_id();
|
||||||
return evse_link_framing_send(dest, src, payload, len);
|
return evse_link_framing_send(dest, src, payload, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Receive byte (delegates to framing module)
|
// 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);
|
evse_link_framing_recv_byte(byte);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ static input_filter_t evse_filter;
|
|||||||
#define CONNECTOR_COUNT (MAX_SLAVES + 1)
|
#define CONNECTOR_COUNT (MAX_SLAVES + 1)
|
||||||
|
|
||||||
// Estrutura unificada para master e slaves
|
// Estrutura unificada para master e slaves
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
uint8_t id; // 0xFF = master, 0..MAX_SLAVES-1 = slave
|
uint8_t id; // 0xFF = master, 0..MAX_SLAVES-1 = slave
|
||||||
bool is_master;
|
bool is_master;
|
||||||
bool charging;
|
bool charging;
|
||||||
@@ -65,10 +66,10 @@ static void init_connectors(void)
|
|||||||
.runtime_current = 0,
|
.runtime_current = 0,
|
||||||
.timestamp = 0,
|
.timestamp = 0,
|
||||||
.online = false,
|
.online = false,
|
||||||
.assigned = 0.0f
|
.assigned = 0.0f};
|
||||||
};
|
|
||||||
// slaves em 1..CONNECTOR_COUNT-1
|
// 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){
|
connectors[i] = (evse_connector_t){
|
||||||
.id = (uint8_t)(i - 1),
|
.id = (uint8_t)(i - 1),
|
||||||
.is_master = false,
|
.is_master = false,
|
||||||
@@ -77,8 +78,7 @@ static void init_connectors(void)
|
|||||||
.runtime_current = 0.0f,
|
.runtime_current = 0.0f,
|
||||||
.timestamp = 0,
|
.timestamp = 0,
|
||||||
.online = false,
|
.online = false,
|
||||||
.assigned = 0.0f
|
.assigned = 0.0f};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,8 @@ 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;
|
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);
|
ESP_LOGW(TAG, "Invalid slave_id %d", status->slave_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -111,15 +112,12 @@ static void on_slave_status(void *handler_arg, esp_event_base_t base, int32_t id
|
|||||||
status->hw_max_current, status->runtime_current);
|
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,
|
esp_event_base_t base,
|
||||||
int32_t id,
|
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
|
int idx = 0; // MASTER INDICE 0
|
||||||
connectors[idx].charging = evt->charging;
|
connectors[idx].charging = evt->charging;
|
||||||
@@ -128,14 +126,10 @@ static void on_evse_config_event(void* handler_arg,
|
|||||||
connectors[idx].timestamp = esp_timer_get_time();
|
connectors[idx].timestamp = esp_timer_get_time();
|
||||||
connectors[idx].online = true;
|
connectors[idx].online = true;
|
||||||
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "EVSE config updated: charging=%d hw_max_current=%.1f runtime_current=%.1f",
|
ESP_LOGI(TAG, "EVSE config updated: charging=%d hw_max_current=%.1f runtime_current=%.1f",
|
||||||
evt->charging, evt->hw_max_current, evt->runtime_current);
|
evt->charging, evt->hw_max_current, evt->runtime_current);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Handlers de eventos externos ---
|
// --- Handlers de eventos externos ---
|
||||||
static void loadbalancer_meter_event_handler(void *handler_arg,
|
static void loadbalancer_meter_event_handler(void *handler_arg,
|
||||||
esp_event_base_t base,
|
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)
|
for (int i = 1; i < 3; ++i)
|
||||||
if (evt->irms[i] > max_irms)
|
if (evt->irms[i] > max_irms)
|
||||||
max_irms = evt->irms[i];
|
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);
|
grid_current = input_filter_update(&grid_filter, max_irms);
|
||||||
ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current);
|
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);
|
evse_current = input_filter_update(&evse_filter, max_irms);
|
||||||
ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current);
|
ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source);
|
ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,8 +200,6 @@ static void loadbalancer_evse_event_handler(void *handler_arg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Config persistência ---
|
// --- Config persistência ---
|
||||||
static esp_err_t loadbalancer_load_config()
|
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);
|
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)
|
if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT)
|
||||||
max_grid_current = temp_u8;
|
max_grid_current = temp_u8;
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
max_grid_current = MAX_GRID_CURRENT_LIMIT;
|
||||||
nvs_set_u8(handle, "max_grid_curr", max_grid_current);
|
nvs_set_u8(handle, "max_grid_curr", max_grid_current);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
@@ -223,7 +221,8 @@ static esp_err_t loadbalancer_load_config()
|
|||||||
err = nvs_get_u8(handle, "enabled", &temp_u8);
|
err = nvs_get_u8(handle, "enabled", &temp_u8);
|
||||||
if (err == ESP_OK && temp_u8 <= 1)
|
if (err == ESP_OK && temp_u8 <= 1)
|
||||||
loadbalancer_enabled = (temp_u8 != 0);
|
loadbalancer_enabled = (temp_u8 != 0);
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
loadbalancer_enabled = false;
|
loadbalancer_enabled = false;
|
||||||
nvs_set_u8(handle, "enabled", 0);
|
nvs_set_u8(handle, "enabled", 0);
|
||||||
needs_commit = true;
|
needs_commit = true;
|
||||||
@@ -238,13 +237,14 @@ static esp_err_t loadbalancer_load_config()
|
|||||||
void loadbalancer_set_enabled(bool enabled)
|
void loadbalancer_set_enabled(bool enabled)
|
||||||
{
|
{
|
||||||
nvs_handle_t handle;
|
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_set_u8(handle, "enabled", enabled ? 1 : 0);
|
||||||
nvs_commit(handle);
|
nvs_commit(handle);
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
}
|
}
|
||||||
loadbalancer_enabled = enabled;
|
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,
|
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_STATE_CHANGED,
|
||||||
&evt, sizeof(evt), portMAX_DELAY);
|
&evt, sizeof(evt), portMAX_DELAY);
|
||||||
}
|
}
|
||||||
@@ -276,8 +276,10 @@ bool loadbalancer_is_enabled(void)
|
|||||||
// --- Task principal ---
|
// --- Task principal ---
|
||||||
void loadbalancer_task(void *param)
|
void loadbalancer_task(void *param)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true)
|
||||||
if (!loadbalancer_is_enabled()) {
|
{
|
||||||
|
if (!loadbalancer_is_enabled())
|
||||||
|
{
|
||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -287,16 +289,19 @@ void loadbalancer_task(void *param)
|
|||||||
int64_t now = esp_timer_get_time();
|
int64_t now = esp_timer_get_time();
|
||||||
|
|
||||||
// --- Atualiza estado online e conta ativos ---
|
// --- 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 ---
|
// --- Master nunca pode ficar offline ---
|
||||||
if (connectors[i].is_master) {
|
if (connectors[i].is_master)
|
||||||
|
{
|
||||||
connectors[i].online = true;
|
connectors[i].online = true;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Connector[%d] ONLINE (MASTER, charging=%d, hw_max_current=%.1f)",
|
ESP_LOGI(TAG, "Connector[%d] ONLINE (MASTER, charging=%d, hw_max_current=%.1f)",
|
||||||
i, connectors[i].charging, connectors[i].hw_max_current);
|
i, connectors[i].charging, connectors[i].hw_max_current);
|
||||||
|
|
||||||
if (connectors[i].charging) {
|
if (connectors[i].charging)
|
||||||
|
{
|
||||||
idxs[active_cnt++] = i;
|
idxs[active_cnt++] = i;
|
||||||
ESP_LOGI(TAG, "Connector[%d] is ACTIVE (charging)", 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 ---
|
// --- Ignora conectores já marcados como offline ---
|
||||||
if (!connectors[i].online) {
|
if (!connectors[i].online)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Timeout de heartbeat para escravos ---
|
// --- 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;
|
connectors[i].online = false;
|
||||||
ESP_LOGW(TAG, "Connector[%d] marked OFFLINE (charging=%d, timestamp_diff=%lld us)",
|
ESP_LOGW(TAG, "Connector[%d] marked OFFLINE (charging=%d, timestamp_diff=%lld us)",
|
||||||
i, connectors[i].charging, (long long)(now - connectors[i].timestamp));
|
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,
|
i, connectors[i].charging, connectors[i].hw_max_current,
|
||||||
(long long)(now - connectors[i].timestamp));
|
(long long)(now - connectors[i].timestamp));
|
||||||
|
|
||||||
if (connectors[i].charging) {
|
if (connectors[i].charging)
|
||||||
|
{
|
||||||
idxs[active_cnt++] = i;
|
idxs[active_cnt++] = i;
|
||||||
ESP_LOGI(TAG, "Connector[%d] is ACTIVE (charging)", i);
|
ESP_LOGI(TAG, "Connector[%d] is ACTIVE (charging)", i);
|
||||||
}
|
}
|
||||||
@@ -330,17 +338,23 @@ void loadbalancer_task(void *param)
|
|||||||
|
|
||||||
// --- Calcula corrente disponível ---
|
// --- Calcula corrente disponível ---
|
||||||
float available = max_grid_current - grid_current;
|
float available = max_grid_current - grid_current;
|
||||||
if (available < MIN_CHARGING_CURRENT_LIMIT) {
|
if (available < MIN_CHARGING_CURRENT_LIMIT)
|
||||||
|
{
|
||||||
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;
|
available = max_grid_current;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "LB Calc: available=%.1fA, active_connectors=%d", available, active_cnt);
|
ESP_LOGI(TAG, "LB Calc: available=%.1fA, active_connectors=%d", available, active_cnt);
|
||||||
|
|
||||||
// --- Ordena conectores por hw_max_current (bubble sort simples) ---
|
// --- Ordena conectores por hw_max_current (bubble sort simples) ---
|
||||||
for (int a = 0; a < active_cnt - 1; a++) {
|
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 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];
|
int tmp = idxs[b];
|
||||||
idxs[b] = idxs[b + 1];
|
idxs[b] = idxs[b + 1];
|
||||||
idxs[b + 1] = tmp;
|
idxs[b + 1] = tmp;
|
||||||
@@ -351,15 +365,20 @@ void loadbalancer_task(void *param)
|
|||||||
// --- Distribui corrente (water-filling) ---
|
// --- Distribui corrente (water-filling) ---
|
||||||
float remaining = available;
|
float remaining = available;
|
||||||
int remaining_cnt = active_cnt;
|
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];
|
int i = idxs[k];
|
||||||
float share = remaining / remaining_cnt;
|
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;
|
connectors[i].assigned = connectors[i].hw_max_current;
|
||||||
remaining -= connectors[i].assigned;
|
remaining -= connectors[i].assigned;
|
||||||
remaining_cnt--;
|
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;
|
connectors[idxs[m]].assigned = share;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -367,39 +386,44 @@ void loadbalancer_task(void *param)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Aplica piso mínimo ---
|
// --- Aplica piso mínimo ---
|
||||||
for (int k = 0; k < active_cnt; k++) {
|
for (int k = 0; k < active_cnt; k++)
|
||||||
|
{
|
||||||
int i = idxs[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;
|
connectors[i].assigned = MIN_CHARGING_CURRENT_LIMIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Publica limites de corrente ---
|
// --- Publica limites de corrente ---
|
||||||
for (int k = 0; k < active_cnt; k++) {
|
for (int k = 0; k < active_cnt; k++)
|
||||||
|
{
|
||||||
int i = idxs[k];
|
int i = idxs[k];
|
||||||
uint16_t max_cur = (uint16_t)MIN(connectors[i].assigned, MAX_CHARGING_CURRENT_LIMIT);
|
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);
|
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
|
continue; // sem alteração
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectors[i].is_master) {
|
if (connectors[i].is_master)
|
||||||
|
{
|
||||||
loadbalancer_master_limit_event_t master_evt = {
|
loadbalancer_master_limit_event_t master_evt = {
|
||||||
.slave_id = connectors[i].id,
|
.slave_id = connectors[i].id,
|
||||||
.max_current = max_cur,
|
.max_current = max_cur,
|
||||||
.timestamp_us = now
|
.timestamp_us = now};
|
||||||
};
|
|
||||||
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT,
|
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT,
|
||||||
&master_evt, sizeof(master_evt), portMAX_DELAY);
|
&master_evt, sizeof(master_evt), portMAX_DELAY);
|
||||||
ESP_LOGI(TAG, "Master limit changed -> %.1f A (runtime=%.1f A)",
|
ESP_LOGI(TAG, "Master limit changed -> %.1f A (runtime=%.1f A)",
|
||||||
(float)max_cur, connectors[i].runtime_current);
|
(float)max_cur, connectors[i].runtime_current);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
loadbalancer_slave_limit_event_t slave_evt = {
|
loadbalancer_slave_limit_event_t slave_evt = {
|
||||||
.slave_id = connectors[i].id,
|
.slave_id = connectors[i].id,
|
||||||
.max_current = max_cur,
|
.max_current = max_cur,
|
||||||
.timestamp_us = now
|
.timestamp_us = now};
|
||||||
};
|
|
||||||
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT,
|
esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT,
|
||||||
&slave_evt, sizeof(slave_evt), portMAX_DELAY);
|
&slave_evt, sizeof(slave_evt), portMAX_DELAY);
|
||||||
ESP_LOGI(TAG, "Slave %d limit changed -> %.1f A (runtime=%.1f A)",
|
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,
|
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED,
|
||||||
&loadbalancer_evse_event_handler, NULL));
|
&loadbalancer_evse_event_handler, NULL));
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS,EVSE_EVENT_CONFIG_UPDATED,
|
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_CONFIG_UPDATED,
|
||||||
&on_evse_config_event, NULL));
|
&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));
|
&on_slave_status, NULL));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
set(srcs
|
set(srcs
|
||||||
|
"src/ocpp_events.c"
|
||||||
"src/ocpp.c"
|
"src/ocpp.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
idf_component_register(SRCS "${srcs}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_REQUIRES nvs_flash
|
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 <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/**
|
#ifdef __cplusplus
|
||||||
* @brief Start ocpp
|
extern "C" {
|
||||||
*/
|
#endif
|
||||||
void ocpp_start();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop ocpp
|
* @brief Start OCPP
|
||||||
*
|
*/
|
||||||
|
void ocpp_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop OCPP
|
||||||
*/
|
*/
|
||||||
void ocpp_stop(void);
|
void ocpp_stop(void);
|
||||||
|
|
||||||
|
/* Config getters / setters */
|
||||||
bool ocpp_get_enabled(void);
|
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_set_enabled(bool value);
|
||||||
|
|
||||||
|
void ocpp_get_server(char *value); // buffer >= 64
|
||||||
void ocpp_set_server(char *value);
|
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);
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
void ocpp_begin_transaction_authorized(char *value);
|
#endif
|
||||||
|
|
||||||
void ocpp_end_transaction_authorized(char *value);
|
|
||||||
|
|
||||||
bool ocpp_is_TransactionActive();
|
|
||||||
|
|
||||||
void ocpp_set_plugged(bool value);
|
|
||||||
|
|
||||||
void ocpp_set_charging(bool value);
|
|
||||||
|
|
||||||
#endif /* OCPP_H_ */
|
#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"
|
#include "auth_api.h"
|
||||||
@@ -16,60 +16,101 @@ static const char *TAG = "auth_api";
|
|||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
char username[128];
|
char username[128];
|
||||||
} users[10] = { /* {"admin"}, {"user1"} */ };
|
} users[10] = { {"admin"}, {"user1"} };
|
||||||
|
|
||||||
static int num_users = 2;
|
static int num_users = 2;
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
// Handlers for Auth Methods (RFID)
|
// Helpers
|
||||||
// =================================
|
// =================================
|
||||||
|
|
||||||
static esp_err_t auth_methods_get_handler(httpd_req_t *req) {
|
static void send_json(httpd_req_t *req, cJSON *json) {
|
||||||
httpd_resp_set_type(req, "application/json");
|
|
||||||
cJSON *json = cJSON_CreateObject();
|
|
||||||
cJSON_AddBoolToObject(json, "RFID", auth_is_enabled());
|
|
||||||
char *str = cJSON_PrintUnformatted(json);
|
char *str = cJSON_PrintUnformatted(json);
|
||||||
httpd_resp_sendstr(req, str);
|
httpd_resp_set_type(req, "application/json");
|
||||||
free(str);
|
httpd_resp_sendstr(req, str ? str : "{}");
|
||||||
|
if (str) free(str);
|
||||||
cJSON_Delete(json);
|
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;
|
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];
|
char buf[256];
|
||||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
int len = 0;
|
||||||
if (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");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[len] = '\0';
|
cJSON *json = cJSON_ParseWithLength(buf, len);
|
||||||
cJSON *json = cJSON_Parse(buf);
|
|
||||||
if (!json) {
|
if (!json) {
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *rfid = cJSON_GetObjectItem(json, "RFID");
|
cJSON *mode_js = cJSON_GetObjectItem(json, "mode");
|
||||||
if (rfid && cJSON_IsBool(rfid)) {
|
if (!cJSON_IsString(mode_js) || mode_js->valuestring == NULL) {
|
||||||
auth_set_enabled(cJSON_IsTrue(rfid));
|
|
||||||
} else {
|
|
||||||
cJSON_Delete(json);
|
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;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auth_mode_t mode;
|
||||||
|
if (!auth_mode_from_str(mode_js->valuestring, &mode)) {
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
httpd_resp_sendstr(req, "Métodos de autenticação atualizados");
|
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);
|
||||||
|
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "mode", auth_mode_to_str(mode));
|
||||||
|
send_json(req, resp);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
// User Management Handlers
|
/* Users (mock) */
|
||||||
// =================================
|
// =================================
|
||||||
|
|
||||||
static esp_err_t users_get_handler(httpd_req_t *req) {
|
static esp_err_t users_get_handler(httpd_req_t *req) {
|
||||||
httpd_resp_set_type(req, "application/json");
|
|
||||||
cJSON *root = cJSON_CreateObject();
|
cJSON *root = cJSON_CreateObject();
|
||||||
cJSON *list = cJSON_CreateArray();
|
cJSON *list = cJSON_CreateArray();
|
||||||
for (int i = 0; i < num_users; ++i) {
|
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_AddItemToArray(list, u);
|
||||||
}
|
}
|
||||||
cJSON_AddItemToObject(root, "users", list);
|
cJSON_AddItemToObject(root, "users", list);
|
||||||
char *str = cJSON_PrintUnformatted(root);
|
send_json(req, root);
|
||||||
httpd_resp_sendstr(req, str);
|
|
||||||
free(str);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t users_post_handler(httpd_req_t *req) {
|
static esp_err_t users_post_handler(httpd_req_t *req) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
int len = 0;
|
||||||
if (len <= 0) return ESP_FAIL;
|
if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK || len <= 0) {
|
||||||
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Body vazio");
|
||||||
buf[len] = '\0';
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
if (num_users < 10) {
|
if (num_users < 10) {
|
||||||
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
|
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
|
||||||
num_users++;
|
num_users++;
|
||||||
httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
|
httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
|
||||||
|
return ESP_OK;
|
||||||
} else {
|
} else {
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido");
|
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) {
|
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) {
|
static esp_err_t tags_get_handler(httpd_req_t *req) {
|
||||||
httpd_resp_set_type(req, "application/json");
|
|
||||||
cJSON *root = cJSON_CreateObject();
|
cJSON *root = cJSON_CreateObject();
|
||||||
cJSON *list = cJSON_CreateArray();
|
cJSON *list = cJSON_CreateArray();
|
||||||
|
|
||||||
int count = auth_get_tag_count();
|
int count = auth_get_tag_count();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
const char *tag = auth_get_tag_by_index(i);
|
const char *tag = auth_get_tag_by_index(i);
|
||||||
if (tag) {
|
if (tag) cJSON_AddItemToArray(list, cJSON_CreateString(tag));
|
||||||
cJSON_AddItemToArray(list, cJSON_CreateString(tag));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON_AddItemToObject(root, "tags", list);
|
cJSON_AddItemToObject(root, "tags", list);
|
||||||
char *str = cJSON_PrintUnformatted(root);
|
send_json(req, root);
|
||||||
httpd_resp_sendstr(req, str);
|
|
||||||
free(str);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return ESP_OK;
|
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) {
|
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();
|
auth_wait_for_tag_registration();
|
||||||
httpd_resp_sendstr(req, "Modo de registro de tag ativado");
|
httpd_resp_sendstr(req, "Modo de registro de tag ativado");
|
||||||
return ESP_OK;
|
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) {
|
void register_auth_handlers(httpd_handle_t server, void *ctx) {
|
||||||
// Auth methods
|
// Auth mode
|
||||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||||
.uri = "/api/v1/config/auth-methods",
|
.uri = "/api/v1/config/auth-mode",
|
||||||
.method = HTTP_GET,
|
.method = HTTP_GET,
|
||||||
.handler = auth_methods_get_handler,
|
.handler = auth_mode_get_handler,
|
||||||
.user_ctx = ctx
|
.user_ctx = ctx
|
||||||
});
|
});
|
||||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||||
.uri = "/api/v1/config/auth-methods",
|
.uri = "/api/v1/config/auth-mode",
|
||||||
.method = HTTP_POST,
|
.method = HTTP_POST,
|
||||||
.handler = auth_methods_post_handler,
|
.handler = auth_mode_post_handler,
|
||||||
.user_ctx = ctx
|
.user_ctx = ctx
|
||||||
});
|
});
|
||||||
|
|
||||||
// Users
|
// Users (mock)
|
||||||
httpd_register_uri_handler(server, &(httpd_uri_t){
|
httpd_register_uri_handler(server, &(httpd_uri_t){
|
||||||
.uri = "/api/v1/config/users",
|
.uri = "/api/v1/config/users",
|
||||||
.method = HTTP_GET,
|
.method = HTTP_GET,
|
||||||
|
|||||||
@@ -1,45 +1,63 @@
|
|||||||
// =========================
|
// =========================
|
||||||
// ocpp_api.c
|
// ocpp_api.c
|
||||||
// =========================
|
// =========================
|
||||||
#include "ocpp_api.h"
|
#include "ocpp.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_http_server.h"
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static const char *TAG = "ocpp_api";
|
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) {
|
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");
|
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 *status = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(status, "status", "connected");
|
cJSON_AddBoolToObject(status, "connected", ocpp_is_connected());
|
||||||
char *str = cJSON_Print(status);
|
cJSON_AddStringToObject(status, "server", server);
|
||||||
|
cJSON_AddStringToObject(status, "charge_id", charge_id);
|
||||||
|
|
||||||
|
char *str = cJSON_PrintUnformatted(status);
|
||||||
httpd_resp_sendstr(req, str);
|
httpd_resp_sendstr(req, str);
|
||||||
|
|
||||||
free(str);
|
free(str);
|
||||||
cJSON_Delete(status);
|
cJSON_Delete(status);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ocpp_get_config_handler(httpd_req_t *req) {
|
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");
|
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 *json = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(json, "url", ocpp_config.url);
|
cJSON_AddBoolToObject(json, "enabled", enabled);
|
||||||
cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId);
|
cJSON_AddStringToObject(json, "url", server);
|
||||||
cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate);
|
cJSON_AddStringToObject(json, "chargeBoxId", charge_id);
|
||||||
cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey);
|
|
||||||
char *str = cJSON_Print(json);
|
char *str = cJSON_PrintUnformatted(json);
|
||||||
httpd_resp_sendstr(req, str);
|
httpd_resp_sendstr(req, str);
|
||||||
|
|
||||||
free(str);
|
free(str);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ocpp_post_config_handler(httpd_req_t *req) {
|
static esp_err_t ocpp_post_config_handler(httpd_req_t *req) {
|
||||||
|
ESP_LOGD(TAG, "POST /api/v1/config/ocpp");
|
||||||
|
|
||||||
char buf[512];
|
char buf[512];
|
||||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
@@ -47,19 +65,28 @@ static esp_err_t ocpp_post_config_handler(httpd_req_t *req) {
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
|
|
||||||
cJSON *json = cJSON_Parse(buf);
|
cJSON *json = cJSON_Parse(buf);
|
||||||
if (!json) {
|
if (!json) {
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
|
||||||
return ESP_FAIL;
|
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");
|
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");
|
cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId");
|
||||||
if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId));
|
if (cJSON_IsString(id)) {
|
||||||
cJSON *cert = cJSON_GetObjectItem(json, "certificate");
|
ocpp_set_charge_id(id->valuestring);
|
||||||
if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate));
|
}
|
||||||
cJSON *key = cJSON_GetObjectItem(json, "privateKey");
|
|
||||||
if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey));
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
httpd_resp_sendstr(req, "OCPP config atualizada com sucesso");
|
httpd_resp_sendstr(req, "OCPP config atualizada com sucesso");
|
||||||
return ESP_OK;
|
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>
|
</style>
|
||||||
<title>Vite + React</title>
|
<title>Vite + React</title>
|
||||||
<script type="module" crossorigin src="/assets/index-ClgQvp_F.js"></script>
|
<script type="module" crossorigin src="/assets/index-CmjuW5AW.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-AuNGQ-2m.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Bc9ibDeR.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
125
main/main.c
125
main/main.c
@@ -29,11 +29,11 @@
|
|||||||
#include "meter_manager.h"
|
#include "meter_manager.h"
|
||||||
#include "buzzer.h"
|
#include "buzzer.h"
|
||||||
#include "evse_link.h"
|
#include "evse_link.h"
|
||||||
|
#include "ocpp.h"
|
||||||
|
|
||||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
|
#define EVSE_MANAGER_TICK_PERIOD_MS 1000
|
||||||
#define AP_CONNECTION_TIMEOUT 120000
|
#define AP_CONNECTION_TIMEOUT 120000
|
||||||
#define RESET_HOLD_TIME 10000
|
#define RESET_HOLD_TIME 30000
|
||||||
#define DEBOUNCE_TIME_MS 50
|
#define DEBOUNCE_TIME_MS 50
|
||||||
|
|
||||||
#define PRESS_BIT BIT0
|
#define PRESS_BIT BIT0
|
||||||
@@ -46,11 +46,11 @@ static TickType_t press_tick = 0;
|
|||||||
static TickType_t last_interrupt_tick = 0;
|
static TickType_t last_interrupt_tick = 0;
|
||||||
static bool pressed = false;
|
static bool pressed = false;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// File system (SPIFFS) init and info
|
// File system (SPIFFS) init and info
|
||||||
//
|
//
|
||||||
static void fs_info(esp_vfs_spiffs_conf_t *conf) {
|
static void fs_info(esp_vfs_spiffs_conf_t *conf)
|
||||||
|
{
|
||||||
size_t total = 0, used = 0;
|
size_t total = 0, used = 0;
|
||||||
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
|
esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
|
||||||
if (ret == ESP_OK)
|
if (ret == ESP_OK)
|
||||||
@@ -59,20 +59,19 @@ static void fs_info(esp_vfs_spiffs_conf_t *conf) {
|
|||||||
ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
|
ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fs_init(void) {
|
static void fs_init(void)
|
||||||
|
{
|
||||||
esp_vfs_spiffs_conf_t cfg_conf = {
|
esp_vfs_spiffs_conf_t cfg_conf = {
|
||||||
.base_path = "/cfg",
|
.base_path = "/cfg",
|
||||||
.partition_label = "cfg",
|
.partition_label = "cfg",
|
||||||
.max_files = 1,
|
.max_files = 1,
|
||||||
.format_if_mount_failed = false
|
.format_if_mount_failed = false};
|
||||||
};
|
|
||||||
|
|
||||||
esp_vfs_spiffs_conf_t data_conf = {
|
esp_vfs_spiffs_conf_t data_conf = {
|
||||||
.base_path = "/data",
|
.base_path = "/data",
|
||||||
.partition_label = "data",
|
.partition_label = "data",
|
||||||
.max_files = 5,
|
.max_files = 5,
|
||||||
.format_if_mount_failed = true
|
.format_if_mount_failed = true};
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
|
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
|
||||||
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
|
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));
|
||||||
@@ -83,116 +82,139 @@ static void fs_init(void) {
|
|||||||
//
|
//
|
||||||
// Wi-Fi event monitoring task
|
// Wi-Fi event monitoring task
|
||||||
//
|
//
|
||||||
static void wifi_event_task_func(void *param) {
|
static void wifi_event_task_func(void *param)
|
||||||
|
{
|
||||||
EventBits_t mode_bits;
|
EventBits_t mode_bits;
|
||||||
for (;;) {
|
for (;;)
|
||||||
|
{
|
||||||
// Wait indefinitely until either AP or STA mode is entered
|
// Wait indefinitely until either AP or STA mode is entered
|
||||||
mode_bits = xEventGroupWaitBits(
|
mode_bits = xEventGroupWaitBits(
|
||||||
wifi_event_group,
|
wifi_event_group,
|
||||||
WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT,
|
WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT,
|
||||||
pdFALSE, // do not clear bits on exit
|
pdFALSE, // do not clear bits on exit
|
||||||
pdFALSE, // wait for any bit
|
pdFALSE, // wait for any bit
|
||||||
portMAX_DELAY
|
portMAX_DELAY);
|
||||||
);
|
|
||||||
|
|
||||||
if (mode_bits & WIFI_AP_MODE_BIT) {
|
if (mode_bits & WIFI_AP_MODE_BIT)
|
||||||
|
{
|
||||||
// We're in AP mode: wait for a client to connect within the timeout
|
// We're in AP mode: wait for a client to connect within the timeout
|
||||||
if (xEventGroupWaitBits(
|
if (xEventGroupWaitBits(
|
||||||
wifi_event_group,
|
wifi_event_group,
|
||||||
WIFI_AP_CONNECTED_BIT,
|
WIFI_AP_CONNECTED_BIT,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)
|
pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) &
|
||||||
) & WIFI_AP_CONNECTED_BIT) {
|
WIFI_AP_CONNECTED_BIT)
|
||||||
|
{
|
||||||
// Once connected, block until the client disconnects
|
// Once connected, block until the client disconnects
|
||||||
xEventGroupWaitBits(
|
xEventGroupWaitBits(
|
||||||
wifi_event_group,
|
wifi_event_group,
|
||||||
WIFI_AP_DISCONNECTED_BIT,
|
WIFI_AP_DISCONNECTED_BIT,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
portMAX_DELAY
|
portMAX_DELAY);
|
||||||
);
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
// Timeout expired with no client—optionally stop the AP
|
// Timeout expired with no client—optionally stop the AP
|
||||||
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
|
if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)
|
||||||
|
{
|
||||||
// wifi_ap_stop();
|
// wifi_ap_stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mode_bits & WIFI_STA_MODE_BIT) {
|
}
|
||||||
|
else if (mode_bits & WIFI_STA_MODE_BIT)
|
||||||
|
{
|
||||||
// We're in STA mode: block until disconnected from the AP
|
// We're in STA mode: block until disconnected from the AP
|
||||||
xEventGroupWaitBits(
|
xEventGroupWaitBits(
|
||||||
wifi_event_group,
|
wifi_event_group,
|
||||||
WIFI_STA_DISCONNECTED_BIT,
|
WIFI_STA_DISCONNECTED_BIT,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
portMAX_DELAY
|
portMAX_DELAY);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent this task from hogging the CPU when idle
|
// Prevent this task from hogging the CPU when idle
|
||||||
//vTaskDelay(pdMS_TO_TICKS(10));
|
// vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Button press handler
|
// Button press handler
|
||||||
//
|
//
|
||||||
static void handle_button_press(void) {
|
static void handle_button_press(void)
|
||||||
|
{
|
||||||
// If not already in AP mode, start it
|
// If not already in AP mode, start it
|
||||||
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
|
if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT))
|
||||||
|
{
|
||||||
ESP_LOGI(TAG, "Starting Wi-Fi AP mode");
|
ESP_LOGI(TAG, "Starting Wi-Fi AP mode");
|
||||||
wifi_ap_start();
|
wifi_ap_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task to handle button press/release notifications
|
// Task to handle button press/release notifications
|
||||||
static void user_input_task_func(void *param) {
|
static void user_input_task_func(void *param)
|
||||||
|
{
|
||||||
uint32_t notification;
|
uint32_t notification;
|
||||||
for (;;) {
|
for (;;)
|
||||||
|
{
|
||||||
// Wait for notification bits from ISR
|
// Wait for notification bits from ISR
|
||||||
if (xTaskNotifyWait(
|
if (xTaskNotifyWait(
|
||||||
0, // do not clear any bits on entry
|
0, // do not clear any bits on entry
|
||||||
UINT32_MAX, // clear all bits on exit
|
UINT32_MAX, // clear all bits on exit
|
||||||
¬ification,
|
¬ification,
|
||||||
portMAX_DELAY)) {
|
portMAX_DELAY))
|
||||||
|
{
|
||||||
// Handle button press event
|
// Handle button press event
|
||||||
if (notification & PRESS_BIT) {
|
if (notification & PRESS_BIT)
|
||||||
|
{
|
||||||
press_tick = xTaskGetTickCount();
|
press_tick = xTaskGetTickCount();
|
||||||
pressed = true;
|
pressed = true;
|
||||||
ESP_LOGI(TAG, "Button Pressed");
|
ESP_LOGI(TAG, "Button Pressed");
|
||||||
handle_button_press();
|
handle_button_press(); // só aqui
|
||||||
}
|
}
|
||||||
// Handle button release event (only if previously pressed)
|
if ((notification & RELEASED_BIT) && pressed)
|
||||||
if ((notification & RELEASED_BIT) && pressed) {
|
{
|
||||||
pressed = false;
|
pressed = false;
|
||||||
ESP_LOGI(TAG, "Button Released");
|
TickType_t held = xTaskGetTickCount() - press_tick;
|
||||||
handle_button_press();
|
ESP_LOGI(TAG, "Button Released (held %u ms)", (unsigned)pdTICKS_TO_MS(held));
|
||||||
|
if (held >= pdMS_TO_TICKS(RESET_HOLD_TIME))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Long press: erasing NVS + reboot");
|
||||||
|
nvs_flash_erase();
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ISR for button GPIO interrupt (active-low)
|
// ISR for button GPIO interrupt (active-low)
|
||||||
static void IRAM_ATTR button_isr_handler(void *arg) {
|
static void IRAM_ATTR button_isr_handler(void *arg)
|
||||||
|
{
|
||||||
BaseType_t higher_task_woken = pdFALSE;
|
BaseType_t higher_task_woken = pdFALSE;
|
||||||
TickType_t now = xTaskGetTickCountFromISR();
|
TickType_t now = xTaskGetTickCountFromISR();
|
||||||
|
|
||||||
// Debounce: ignore interrupts occurring too close together
|
// Debounce: ignore interrupts occurring too close together
|
||||||
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
|
if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_interrupt_tick = now;
|
last_interrupt_tick = now;
|
||||||
|
|
||||||
// Read GPIO level: 0 = button pressed, 1 = button released
|
// Read GPIO level: 0 = button pressed, 1 = button released
|
||||||
int level = gpio_get_level(board_config.button_wifi_gpio);
|
int level = gpio_get_level(board_config.button_wifi_gpio);
|
||||||
if (level == 0) {
|
if (level == 0)
|
||||||
|
{
|
||||||
// Notify task: button pressed
|
// Notify task: button pressed
|
||||||
xTaskNotifyFromISR(
|
xTaskNotifyFromISR(
|
||||||
user_input_task,
|
user_input_task,
|
||||||
PRESS_BIT,
|
PRESS_BIT,
|
||||||
eSetBits,
|
eSetBits,
|
||||||
&higher_task_woken);
|
&higher_task_woken);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Notify task: button released
|
// Notify task: button released
|
||||||
xTaskNotifyFromISR(
|
xTaskNotifyFromISR(
|
||||||
user_input_task,
|
user_input_task,
|
||||||
@@ -202,20 +224,20 @@ static void IRAM_ATTR button_isr_handler(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Yield to higher priority task if unblocked
|
// Yield to higher priority task if unblocked
|
||||||
if (higher_task_woken) {
|
if (higher_task_woken)
|
||||||
|
{
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void button_init(void)
|
||||||
static void button_init(void) {
|
{
|
||||||
gpio_config_t conf = {
|
gpio_config_t conf = {
|
||||||
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
|
.pin_bit_mask = BIT64(board_config.button_wifi_gpio),
|
||||||
.mode = GPIO_MODE_INPUT,
|
.mode = GPIO_MODE_INPUT,
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
.intr_type = GPIO_INTR_ANYEDGE
|
.intr_type = GPIO_INTR_ANYEDGE};
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(gpio_config(&conf));
|
ESP_ERROR_CHECK(gpio_config(&conf));
|
||||||
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
|
ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
|
||||||
}
|
}
|
||||||
@@ -223,9 +245,11 @@ static void button_init(void) {
|
|||||||
//
|
//
|
||||||
// Inicialização dos módulos do sistema
|
// Inicialização dos módulos do sistema
|
||||||
//
|
//
|
||||||
static void init_modules(void) {
|
static void init_modules(void)
|
||||||
|
{
|
||||||
peripherals_init();
|
peripherals_init();
|
||||||
//api_init();
|
wifi_ini();
|
||||||
|
// api_init();
|
||||||
buzzer_init();
|
buzzer_init();
|
||||||
ESP_ERROR_CHECK(rest_server_init("/data"));
|
ESP_ERROR_CHECK(rest_server_init("/data"));
|
||||||
protocols_init();
|
protocols_init();
|
||||||
@@ -237,6 +261,7 @@ static void init_modules(void) {
|
|||||||
meter_manager_init();
|
meter_manager_init();
|
||||||
meter_manager_start();
|
meter_manager_start();
|
||||||
evse_link_init();
|
evse_link_init();
|
||||||
|
ocpp_start();
|
||||||
|
|
||||||
// wifi_ap_start();
|
// wifi_ap_start();
|
||||||
// Outros módulos (descomente conforme necessário)
|
// Outros módulos (descomente conforme necessário)
|
||||||
@@ -253,7 +278,8 @@ static void init_modules(void) {
|
|||||||
//
|
//
|
||||||
// Função principal do firmware
|
// Função principal do firmware
|
||||||
//
|
//
|
||||||
void app_main(void) {
|
void app_main(void)
|
||||||
|
{
|
||||||
logger_init();
|
logger_init();
|
||||||
esp_log_set_vprintf(logger_vprintf);
|
esp_log_set_vprintf(logger_vprintf);
|
||||||
|
|
||||||
@@ -261,7 +287,8 @@ void app_main(void) {
|
|||||||
ESP_LOGI(TAG, "Reset reason: %d", reason);
|
ESP_LOGI(TAG, "Reset reason: %d", reason);
|
||||||
|
|
||||||
esp_err_t ret = nvs_flash_init();
|
esp_err_t ret = nvs_flash_init();
|
||||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||||
|
{
|
||||||
ESP_LOGW(TAG, "Erasing NVS flash");
|
ESP_LOGW(TAG, "Erasing NVS flash");
|
||||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
ret = nvs_flash_init();
|
ret = nvs_flash_init();
|
||||||
@@ -274,8 +301,6 @@ void app_main(void) {
|
|||||||
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||||
|
|
||||||
board_config_load();
|
board_config_load();
|
||||||
wifi_ini();
|
|
||||||
//wifi_ap_start();
|
|
||||||
init_modules();
|
init_modules();
|
||||||
|
|
||||||
xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
|
xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
|
||||||
|
|||||||
3952
projeto_parte1.c
3952
projeto_parte1.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
TAMANHO_MAX = 200000 # Limite por arquivo
|
TAMANHO_MAX = 150000 # Limite por arquivo
|
||||||
|
|
||||||
def coletar_arquivos(diretorios, extensoes=(".c", ".h")):
|
def coletar_arquivos(diretorios, extensoes=(".c", ".h")):
|
||||||
arquivos = []
|
arquivos = []
|
||||||
@@ -53,7 +53,7 @@ def unir_em_partes(arquivos, prefixo="projeto_parte", limite=TAMANHO_MAX):
|
|||||||
def main():
|
def main():
|
||||||
diretorio_main = "main"
|
diretorio_main = "main"
|
||||||
componentes_escolhidos = [
|
componentes_escolhidos = [
|
||||||
"evse", "loadbalancer"
|
"ocpp"
|
||||||
]
|
]
|
||||||
|
|
||||||
diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos]
|
diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos]
|
||||||
|
|||||||
Reference in New Issue
Block a user