new release
@@ -276,10 +276,7 @@ static void wiegand_sim_task(void *arg)
|
||||
{
|
||||
// lista fixa de idTags simuladas
|
||||
static const char *idtaglist[] = {
|
||||
"00000041349",
|
||||
"W2602312345",
|
||||
"W34ABCDE123",
|
||||
};
|
||||
"127239144069"};
|
||||
const size_t list_size = sizeof(idtaglist) / sizeof(idtaglist[0]);
|
||||
|
||||
for (;;)
|
||||
@@ -301,7 +298,7 @@ void initWiegand(void)
|
||||
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL);
|
||||
|
||||
// Para testes, podes ativar o simulador:
|
||||
// ESP_LOGI(TAG, "Inicializando Wiegand simulado");
|
||||
// xTaskCreate(wiegand_sim_task, "WiegandSim",
|
||||
//ESP_LOGI(TAG, "Inicializando Wiegand simulado");
|
||||
//xTaskCreate(wiegand_sim_task, "WiegandSim",
|
||||
// configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "buzzer_events.h" // para buzzer_pattern_id_t
|
||||
#include "buzzer_events.h" // buzzer_pattern_id_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -33,7 +32,7 @@ void buzzer_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Ativa/Desativa globalmente o buzzer (mute).
|
||||
* Quando desativado, não toca e garante nível/desligado.
|
||||
* Quando desativado, não toca e garante desligado.
|
||||
*/
|
||||
void buzzer_set_enabled(bool enabled);
|
||||
|
||||
@@ -52,9 +51,10 @@ void buzzer_set_quiet_hours(bool enabled, uint16_t start_min, uint16_t end_min);
|
||||
|
||||
/**
|
||||
* @brief Ajusta a frequência PWM (somente PASSIVE/LEDC; timer exclusivo recomendado).
|
||||
* @return ESP_OK em sucesso; erro se modo inválido ou argumento fora do range.
|
||||
* @param hz Frequência desejada.
|
||||
* @return Frequência efetiva configurada (Hz). 0 em erro (modo inválido ou argumento fora do range).
|
||||
*/
|
||||
esp_err_t buzzer_set_frequency(uint32_t hz);
|
||||
uint32_t buzzer_set_frequency(uint32_t hz);
|
||||
|
||||
/**
|
||||
* @brief Define o duty cycle em porcentagem (0..100) para PASSIVE/LEDC.
|
||||
@@ -63,4 +63,4 @@ void buzzer_set_duty_percent(uint8_t pct);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
// buzzer.c
|
||||
#include "buzzer_events.h"
|
||||
#include "buzzer.h"
|
||||
|
||||
#include "evse_events.h"
|
||||
#include "auth_events.h"
|
||||
#include "network_events.h"
|
||||
@@ -11,27 +12,26 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// ===================== Configuração padrão =====================
|
||||
#define CONFIG_BUZZER_GPIO GPIO_NUM_27
|
||||
|
||||
// 1 = PASSIVE (PWM), 0 = ACTIVE (on/off)
|
||||
#define CONFIG_BUZZER_MODE_PASSIVE 0
|
||||
#define CONFIG_BUZZER_MODE_PASSIVE 1
|
||||
|
||||
#define CONFIG_BUZZER_FREQ_HZ 3500
|
||||
|
||||
#define CONFIG_BUZZER_DUTY_PCT 70
|
||||
#define CONFIG_BUZZER_FREQ_HZ 2400
|
||||
#define CONFIG_BUZZER_DUTY_PCT 40
|
||||
|
||||
#define CONFIG_BUZZER_QUEUE_LEN 8
|
||||
|
||||
#define CONFIG_BUZZER_TASK_STACK 2048
|
||||
|
||||
#define CONFIG_BUZZER_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
|
||||
// anti-spam (gap mínimo entre toques)
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
// quiet hours start (minutos desde 00:00)
|
||||
#define CONFIG_BUZZER_QUIET_START_MIN (22 * 60)
|
||||
|
||||
// quiet hours end (minutos desde 00:00)
|
||||
#define CONFIG_BUZZER_QUIET_END_MIN (7 * 60)
|
||||
|
||||
@@ -113,6 +112,31 @@ static TaskHandle_t s_buzzer_task = NULL;
|
||||
static TickType_t s_last_play_tick = 0;
|
||||
static const TickType_t s_min_gap_ticks = pdMS_TO_TICKS(CONFIG_BUZZER_MIN_GAP_MS);
|
||||
|
||||
// STOP / preempt (robusto em dual-core)
|
||||
static EventGroupHandle_t s_buzzer_eg = NULL;
|
||||
#define BUZZER_EG_STOP_BIT (1u << 0)
|
||||
|
||||
static bool s_inited = false;
|
||||
|
||||
static inline bool buzzer_should_stop(void)
|
||||
{
|
||||
if (!s_buzzer_eg)
|
||||
return false;
|
||||
return (xEventGroupGetBits(s_buzzer_eg) & BUZZER_EG_STOP_BIT) != 0;
|
||||
}
|
||||
|
||||
static inline void buzzer_clear_stop(void)
|
||||
{
|
||||
if (s_buzzer_eg)
|
||||
xEventGroupClearBits(s_buzzer_eg, BUZZER_EG_STOP_BIT);
|
||||
}
|
||||
|
||||
static inline void buzzer_set_stop(void)
|
||||
{
|
||||
if (s_buzzer_eg)
|
||||
xEventGroupSetBits(s_buzzer_eg, BUZZER_EG_STOP_BIT);
|
||||
}
|
||||
|
||||
// ===================== Padrões (afinados) =====================
|
||||
// NOTA: os enums BUZZER_PATTERN_* vêm de buzzer_events.h
|
||||
|
||||
@@ -139,7 +163,8 @@ static const buzzer_step_t pattern_fault[] = {
|
||||
{450, 0} // bloco 2
|
||||
};
|
||||
|
||||
static const buzzer_pattern_t buzzer_patterns[] = {
|
||||
// BUG importante corrigido: tamanho explícito = BUZZER_PATTERN_MAX
|
||||
static const buzzer_pattern_t buzzer_patterns[BUZZER_PATTERN_MAX] = {
|
||||
[BUZZER_PATTERN_PLUGGED] = {pattern_plugged, sizeof(pattern_plugged) / sizeof(buzzer_step_t)},
|
||||
[BUZZER_PATTERN_UNPLUGGED] = {pattern_unplugged, sizeof(pattern_unplugged) / sizeof(buzzer_step_t)},
|
||||
[BUZZER_PATTERN_CHARGING] = {pattern_charging, sizeof(pattern_charging) / sizeof(buzzer_step_t)},
|
||||
@@ -161,38 +186,46 @@ static inline uint32_t duty_from_percent(uint8_t pct, ledc_timer_bit_t res)
|
||||
return (uint32_t)((pct * max) / 100U);
|
||||
}
|
||||
|
||||
static inline void buzzer_set_duty_raw(uint32_t duty)
|
||||
{
|
||||
ledc_set_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, duty);
|
||||
ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
|
||||
}
|
||||
|
||||
static inline void buzzer_set_duty_pct(uint8_t pct)
|
||||
{
|
||||
buzzer_set_duty_raw(duty_from_percent(pct, s_buzzer_cfg.duty_resolution));
|
||||
}
|
||||
|
||||
// Fade async (NO_WAIT) para permitir STOP responsivo (checamos por nós)
|
||||
static inline void buzzer_fade_to_pct_async(uint8_t pct, uint32_t time_ms)
|
||||
{
|
||||
uint32_t duty = duty_from_percent(pct, s_buzzer_cfg.duty_resolution);
|
||||
ledc_set_fade_with_time(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, duty, time_ms);
|
||||
ledc_fade_start(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, LEDC_FADE_NO_WAIT);
|
||||
}
|
||||
|
||||
static inline bool is_pattern_valid(buzzer_pattern_id_t id)
|
||||
{
|
||||
return (id > BUZZER_PATTERN_NONE && id < BUZZER_PATTERN_MAX);
|
||||
}
|
||||
|
||||
static void buzzer_on(void)
|
||||
static inline bool is_critical_pattern(buzzer_pattern_id_t id)
|
||||
{
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
|
||||
{
|
||||
// Não mudar freq a cada beep; já foi configurada na init.
|
||||
esp_err_t err1 = 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));
|
||||
if (err1 != ESP_OK)
|
||||
ESP_LOGW(TAG, "ledc_set_duty err=%s", esp_err_to_name(err1));
|
||||
esp_err_t err2 = ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
|
||||
if (err2 != ESP_OK)
|
||||
ESP_LOGW(TAG, "ledc_update_duty err=%s", esp_err_to_name(err2));
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 1);
|
||||
}
|
||||
return (id == BUZZER_PATTERN_FAULT || id == BUZZER_PATTERN_CARD_DENIED);
|
||||
}
|
||||
|
||||
static void buzzer_off(void)
|
||||
{
|
||||
if (!s_inited)
|
||||
return;
|
||||
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
|
||||
{
|
||||
esp_err_t err1 = ledc_set_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel, 0);
|
||||
if (err1 != ESP_OK)
|
||||
ESP_LOGW(TAG, "ledc_set_duty(0) err=%s", esp_err_to_name(err1));
|
||||
|
||||
esp_err_t err2 = ledc_update_duty(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_channel);
|
||||
if (err2 != ESP_OK)
|
||||
ESP_LOGW(TAG, "ledc_update_duty err=%s", esp_err_to_name(err2));
|
||||
@@ -209,6 +242,11 @@ static bool in_quiet_hours(void)
|
||||
return false;
|
||||
|
||||
time_t now = time(NULL);
|
||||
|
||||
// Se ainda não há tempo real (NTP não acertou), não aplicar quiet hours
|
||||
if (now < 1700000000) // ~2023-11
|
||||
return false;
|
||||
|
||||
struct tm lt;
|
||||
if (localtime_r(&now, <) == NULL)
|
||||
return false;
|
||||
@@ -219,36 +257,103 @@ static bool in_quiet_hours(void)
|
||||
|
||||
if (start == end)
|
||||
return false; // desativado
|
||||
|
||||
if (start < end)
|
||||
{
|
||||
return (minutes >= start && minutes < end);
|
||||
}
|
||||
else
|
||||
{ // janela cruza meia-noite
|
||||
return (minutes >= start || minutes < end);
|
||||
}
|
||||
return (minutes >= start || minutes < end); // cruza meia-noite
|
||||
}
|
||||
|
||||
// ===================== Execução do padrão (apenas dentro da task) =====================
|
||||
static void buzzer_execute(buzzer_pattern_id_t pattern_id)
|
||||
{
|
||||
if (!is_pattern_valid(pattern_id))
|
||||
{
|
||||
ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const buzzer_pattern_t *pattern = &buzzer_patterns[pattern_id];
|
||||
if (!pattern->steps || pattern->length == 0)
|
||||
return;
|
||||
|
||||
// Afinar aqui (para “premium”)
|
||||
const uint32_t FADE_IN_MS = 10;
|
||||
const uint32_t FADE_OUT_MS = 20;
|
||||
|
||||
// Não limpar STOP aqui — se alguém chamou buzzer_stop(), queremos respeitar já.
|
||||
for (size_t i = 0; i < pattern->length; i++)
|
||||
{
|
||||
buzzer_on();
|
||||
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].on_ms));
|
||||
buzzer_off();
|
||||
if (pattern->steps[i].off_ms > 0)
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
|
||||
uint32_t on_ms = pattern->steps[i].on_ms;
|
||||
uint32_t off_ms = pattern->steps[i].off_ms;
|
||||
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].off_ms));
|
||||
// Sempre começar em 0 para evitar "click"
|
||||
buzzer_set_duty_pct(0);
|
||||
|
||||
uint32_t fi = (on_ms > (FADE_IN_MS + FADE_OUT_MS)) ? FADE_IN_MS : (on_ms / 3);
|
||||
uint32_t fo = (on_ms > (FADE_IN_MS + FADE_OUT_MS)) ? FADE_OUT_MS : (on_ms / 3);
|
||||
uint32_t sustain = (on_ms > (fi + fo)) ? (on_ms - fi - fo) : 0;
|
||||
|
||||
// Fade in (async) + espera em passos para STOP rápido
|
||||
if (fi > 0)
|
||||
buzzer_fade_to_pct_async(s_buzzer_cfg.duty_percent, fi);
|
||||
for (uint32_t t = 0; t < fi; t += 10)
|
||||
{
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
// Sustain
|
||||
for (uint32_t t = 0; t < sustain; t += 10)
|
||||
{
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
|
||||
// Fade out (async) + espera em passos
|
||||
if (fo > 0)
|
||||
buzzer_fade_to_pct_async(0, fo);
|
||||
for (uint32_t t = 0; t < fo; t += 10)
|
||||
{
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
buzzer_set_duty_pct(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ACTIVE (liga/desliga simples)
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 1);
|
||||
for (uint32_t t = 0; t < on_ms; t += 10)
|
||||
{
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 0);
|
||||
}
|
||||
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
|
||||
for (uint32_t t = 0; t < off_ms; t += 10)
|
||||
{
|
||||
if (buzzer_should_stop())
|
||||
break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
buzzer_off();
|
||||
}
|
||||
|
||||
// ===================== Task & Fila =====================
|
||||
@@ -260,30 +365,31 @@ static void buzzer_task(void *arg)
|
||||
{
|
||||
if (xQueueReceive(s_buzzer_q, &id, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
|
||||
ESP_LOGD(TAG, "dequeue pattern %d", id); // na buzzer_task()
|
||||
ESP_LOGD(TAG, "dequeue pattern %d", id);
|
||||
|
||||
if (!s_buzzer_ctl.enabled)
|
||||
continue;
|
||||
|
||||
// Quiet hours: permitir apenas alertas críticos/negativos
|
||||
if (in_quiet_hours())
|
||||
if (in_quiet_hours() && !is_critical_pattern(id))
|
||||
continue;
|
||||
|
||||
// Anti-spam global (NÃO aplicar a padrões críticos)
|
||||
if (!is_critical_pattern(id))
|
||||
{
|
||||
if (!(id == BUZZER_PATTERN_FAULT || id == BUZZER_PATTERN_CARD_DENIED))
|
||||
{
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
if ((now - s_last_play_tick) < s_min_gap_ticks)
|
||||
continue;
|
||||
}
|
||||
s_last_play_tick = now;
|
||||
}
|
||||
|
||||
// Anti-spam global
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
if ((now - s_last_play_tick) < s_min_gap_ticks)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
s_last_play_tick = now;
|
||||
// Se veio STOP pendente, limpamos antes de começar um novo padrão
|
||||
buzzer_clear_stop();
|
||||
|
||||
buzzer_execute(id);
|
||||
|
||||
// Se alguém pediu stop durante execução, já paramos; limpamos o bit.
|
||||
buzzer_clear_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,17 +397,32 @@ static void buzzer_task(void *arg)
|
||||
// ===================== API pública =====================
|
||||
void buzzer_play(buzzer_pattern_id_t id)
|
||||
{
|
||||
ESP_LOGD(TAG, "enqueue pattern %d", id);
|
||||
|
||||
ESP_LOGD(TAG, "enqueue pattern %d", id); // dentro de buzzer_play()
|
||||
|
||||
if (!is_pattern_valid(id) || s_buzzer_q == NULL)
|
||||
if (!s_inited || !is_pattern_valid(id) || s_buzzer_q == NULL)
|
||||
return;
|
||||
(void)xQueueSend(s_buzzer_q, &id, 0);
|
||||
|
||||
// Críticos: preempt + vão para a frente
|
||||
if (is_critical_pattern(id))
|
||||
{
|
||||
buzzer_stop();
|
||||
if (xQueueSendToFront(s_buzzer_q, &id, pdMS_TO_TICKS(10)) != pdTRUE)
|
||||
ESP_LOGW(TAG, "buzzer queue full, dropped critical pattern=%d", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Não-críticos: pequeno timeout + log se drop
|
||||
if (xQueueSend(s_buzzer_q, &id, pdMS_TO_TICKS(10)) != pdTRUE)
|
||||
ESP_LOGW(TAG, "buzzer queue full, dropped pattern=%d", id);
|
||||
}
|
||||
|
||||
void buzzer_stop(void)
|
||||
{
|
||||
// Interrompe imediatamente: esvazia fila e desliga
|
||||
if (!s_inited)
|
||||
return;
|
||||
|
||||
// Interrompe imediatamente: sinaliza stop, esvazia fila e desliga
|
||||
buzzer_set_stop();
|
||||
if (s_buzzer_q)
|
||||
xQueueReset(s_buzzer_q);
|
||||
buzzer_off();
|
||||
@@ -328,15 +449,22 @@ void buzzer_set_quiet_hours(bool enabled, uint16_t start_min, uint16_t end_min)
|
||||
s_buzzer_ctl.quiet_end_min = end_min;
|
||||
}
|
||||
|
||||
// Opcional: mudar tom dinamicamente (apenas se o timer for exclusivo do buzzer)
|
||||
esp_err_t buzzer_set_frequency(uint32_t hz)
|
||||
// ledc_set_freq normalmente devolve a frequência efetiva (0 se falhar)
|
||||
uint32_t buzzer_set_frequency(uint32_t hz)
|
||||
{
|
||||
if (!s_inited)
|
||||
return 0;
|
||||
if (s_buzzer_cfg.mode != BUZZER_MODE_PASSIVE)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
return 0;
|
||||
if (hz < 50 || hz > 20000)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
return 0;
|
||||
|
||||
s_buzzer_cfg.freq_hz = hz;
|
||||
return ledc_set_freq(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_timer, hz);
|
||||
|
||||
uint32_t eff = ledc_set_freq(s_buzzer_cfg.ledc_speed_mode, s_buzzer_cfg.ledc_timer, hz);
|
||||
if (eff == 0)
|
||||
ESP_LOGW(TAG, "ledc_set_freq failed (hz=%lu)", (unsigned long)hz);
|
||||
return eff;
|
||||
}
|
||||
|
||||
void buzzer_set_duty_percent(uint8_t pct)
|
||||
@@ -382,7 +510,6 @@ static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, voi
|
||||
buzzer_evt.pattern = BUZZER_PATTERN_CHARGING;
|
||||
break;
|
||||
case EVSE_STATE_EVENT_FAULT:
|
||||
// Preempta qualquer beep em andamento
|
||||
buzzer_stop();
|
||||
buzzer_evt.pattern = BUZZER_PATTERN_FAULT;
|
||||
break;
|
||||
@@ -406,12 +533,9 @@ static void network_event_handler(void *handler_args, esp_event_base_t base, int
|
||||
{
|
||||
case NETWORK_EVENT_AP_STARTED:
|
||||
case NETWORK_EVENT_STA_CONNECTED:
|
||||
// Usa padrão de AP_START para indicar rede disponível
|
||||
evt.pattern = BUZZER_PATTERN_AP_START;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Para já, ignorar outros eventos de rede
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -446,6 +570,12 @@ static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, voi
|
||||
// ===================== Inicialização / Deinit =====================
|
||||
void buzzer_init(void)
|
||||
{
|
||||
if (s_inited)
|
||||
{
|
||||
ESP_LOGW(TAG, "buzzer_init called twice");
|
||||
return;
|
||||
}
|
||||
|
||||
// Config HW
|
||||
if (s_buzzer_cfg.mode == BUZZER_MODE_PASSIVE)
|
||||
{
|
||||
@@ -466,6 +596,11 @@ void buzzer_init(void)
|
||||
.duty = 0,
|
||||
.hpoint = 0};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ccfg));
|
||||
|
||||
// Evita crash se já estiver instalado (init repetido em app maior)
|
||||
esp_err_t fe = ledc_fade_func_install(0);
|
||||
if (fe != ESP_OK && fe != ESP_ERR_INVALID_STATE)
|
||||
ESP_ERROR_CHECK(fe);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -479,14 +614,14 @@ void buzzer_init(void)
|
||||
gpio_set_level(s_buzzer_cfg.gpio, 0);
|
||||
}
|
||||
|
||||
buzzer_set_quiet_hours(false, 0, 0);
|
||||
buzzer_set_enabled(true);
|
||||
|
||||
buzzer_off();
|
||||
// STOP event group
|
||||
s_buzzer_eg = xEventGroupCreate();
|
||||
configASSERT(s_buzzer_eg);
|
||||
|
||||
// Fila + Task
|
||||
s_buzzer_q = xQueueCreate(CONFIG_BUZZER_QUEUE_LEN, sizeof(buzzer_pattern_id_t));
|
||||
configASSERT(s_buzzer_q != NULL);
|
||||
|
||||
BaseType_t ok = xTaskCreatePinnedToCore(
|
||||
buzzer_task,
|
||||
"buzzer_task",
|
||||
@@ -497,6 +632,13 @@ void buzzer_init(void)
|
||||
tskNO_AFFINITY);
|
||||
configASSERT(ok == pdPASS);
|
||||
|
||||
// Runtime defaults
|
||||
buzzer_set_quiet_hours(false, 0, 0);
|
||||
buzzer_set_enabled(true);
|
||||
|
||||
s_inited = true;
|
||||
buzzer_off();
|
||||
|
||||
// Handlers
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler, NULL));
|
||||
@@ -514,24 +656,39 @@ void buzzer_init(void)
|
||||
|
||||
void buzzer_deinit(void)
|
||||
{
|
||||
if (!s_inited)
|
||||
return;
|
||||
|
||||
(void)esp_event_handler_unregister(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, buzzer_event_handler);
|
||||
(void)esp_event_handler_unregister(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler);
|
||||
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler);
|
||||
(void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler);
|
||||
(void)esp_event_handler_unregister(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler);
|
||||
|
||||
// Para já
|
||||
buzzer_stop();
|
||||
|
||||
// Mata task ANTES de apagar a queue (evita uso-after-free raro)
|
||||
if (s_buzzer_task)
|
||||
{
|
||||
vTaskDelete(s_buzzer_task);
|
||||
s_buzzer_task = NULL;
|
||||
}
|
||||
|
||||
if (s_buzzer_q)
|
||||
{
|
||||
vQueueDelete(s_buzzer_q);
|
||||
s_buzzer_q = NULL;
|
||||
}
|
||||
|
||||
if (s_buzzer_task)
|
||||
if (s_buzzer_eg)
|
||||
{
|
||||
vTaskDelete(s_buzzer_task);
|
||||
s_buzzer_task = NULL;
|
||||
vEventGroupDelete(s_buzzer_eg);
|
||||
s_buzzer_eg = NULL;
|
||||
}
|
||||
|
||||
s_inited = false;
|
||||
buzzer_off();
|
||||
|
||||
ESP_LOGI(TAG, "Buzzer deinitialized");
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,7 @@ static void evse_process(void)
|
||||
if (evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGW(TAG, "Charging limit reached → revoking authorization");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ static SemaphoreHandle_t evse_mutex;
|
||||
// ✅ Proteção para flags partilhadas (event handlers vs task)
|
||||
static portMUX_TYPE s_mgr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
static bool auth_enabled = false;
|
||||
static char s_pending_ocpp_tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
// Estado de pausa controlado pelo Load Balancer
|
||||
static bool lb_paused = false;
|
||||
@@ -41,6 +42,8 @@ static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
|
||||
static uint16_t s_sched_current_a = 0;
|
||||
|
||||
static void lb_clear_pause_state(void)
|
||||
{
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
@@ -61,15 +64,15 @@ bool evse_sched_is_allowed(void)
|
||||
static void evse_manager_handle_auth_on_tick(void)
|
||||
{
|
||||
bool sched_allowed = evse_sched_is_allowed();
|
||||
uint32_t err_bits = evse_get_error(); // inclui holdoff interno
|
||||
bool has_error = (err_bits != 0);
|
||||
uint32_t err_bits = evse_get_error(); // inclui holdoff interno
|
||||
bool has_error = (err_bits != 0);
|
||||
|
||||
bool local_auth_enabled;
|
||||
bool local_lb_paused;
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
local_auth_enabled = auth_enabled;
|
||||
local_lb_paused = lb_paused;
|
||||
local_lb_paused = lb_paused;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (local_auth_enabled)
|
||||
@@ -77,6 +80,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
|
||||
{
|
||||
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
@@ -86,6 +90,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
ESP_LOGI(TAG,
|
||||
"[AUTH] error active (err=0x%08" PRIx32 ") → revoking authorization.",
|
||||
err_bits);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
@@ -93,12 +98,13 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
if (!sched_allowed && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization.");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool limit_hit = evse_is_limit_reached();
|
||||
bool limit_hit = evse_is_limit_reached();
|
||||
bool can_operate = evse_config_is_available() && evse_config_is_enabled();
|
||||
|
||||
if ((has_error || limit_hit || !sched_allowed || !can_operate || local_lb_paused) &&
|
||||
@@ -114,6 +120,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
!has_error && !limit_hit &&
|
||||
!evse_state_get_authorized())
|
||||
{
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization (schedule ok, no error/limits).");
|
||||
lb_clear_pause_state();
|
||||
@@ -134,7 +141,8 @@ static void evse_manager_task(void *arg)
|
||||
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
if (base != AUTH_EVENTS || !data) return;
|
||||
if (base != AUTH_EVENTS || !data)
|
||||
return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
@@ -142,11 +150,30 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
{
|
||||
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");
|
||||
if (evt->authorized)
|
||||
{
|
||||
evse_session_set_authorized_tag(evt->tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_session_clear_authorized_tag();
|
||||
}
|
||||
evse_state_set_authorized(evt->authorized);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_TAG_VERIFY:
|
||||
{
|
||||
const auth_tag_verify_event_t *evt = (const auth_tag_verify_event_t *)data;
|
||||
ESP_LOGI(TAG, "Tag %s -> pending remote/OCPP verification", evt->tag);
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
strncpy(s_pending_ocpp_tag, evt->tag, AUTH_TAG_MAX_LEN - 1);
|
||||
s_pending_ocpp_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_MODE_CHANGED:
|
||||
case AUTH_EVENT_INIT:
|
||||
{
|
||||
@@ -155,8 +182,10 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
auth_enabled = (evt->mode != AUTH_MODE_OPEN);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
@@ -170,7 +199,8 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
(void)handler_arg;
|
||||
(void)event_base;
|
||||
|
||||
if (!event_data) return;
|
||||
if (!event_data)
|
||||
return;
|
||||
|
||||
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
|
||||
{
|
||||
@@ -211,7 +241,7 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
was_paused = lb_paused;
|
||||
prev_auth = lb_prev_authorized;
|
||||
prev_auth = lb_prev_authorized;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (was_paused)
|
||||
@@ -263,27 +293,55 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
if (base != OCPP_EVENTS) return;
|
||||
if (base != OCPP_EVENTS)
|
||||
return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case OCPP_EVENT_AUTHORIZED:
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||
{
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
strncpy(tag, s_pending_ocpp_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (tag[0] != '\0')
|
||||
{
|
||||
evse_session_set_authorized_tag(tag);
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized tag=%s", tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||
}
|
||||
|
||||
evse_state_set_authorized(true);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
|
||||
case OCPP_EVENT_AUTH_REJECTED:
|
||||
case OCPP_EVENT_AUTH_TIMEOUT:
|
||||
case OCPP_EVENT_REMOTE_STOP:
|
||||
case OCPP_EVENT_STOP_TX:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization/Stop");
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_REMOTE_START:
|
||||
ESP_LOGI(TAG, "[OCPP] RemoteStart");
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(true);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
@@ -317,21 +375,31 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
static void on_sched_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
if (base != SCHED_EVENTS || data == NULL) return;
|
||||
if (base != SCHED_EVENTS || data == NULL)
|
||||
return;
|
||||
|
||||
const sched_event_state_t *ev = (const sched_event_state_t *)data;
|
||||
|
||||
portENTER_CRITICAL(&s_sched_mux);
|
||||
s_sched_allowed = ev->allowed_now;
|
||||
s_sched_current_a = ev->current_limit_a;
|
||||
portEXIT_CRITICAL(&s_sched_mux);
|
||||
|
||||
ESP_LOGI(TAG, "[SCHED] allowed_now=%d", (int)ev->allowed_now);
|
||||
ESP_LOGI(TAG, "[SCHED] allowed_now=%d current=%uA",
|
||||
(int)ev->allowed_now, (unsigned)ev->current_limit_a);
|
||||
|
||||
if (!ev->allowed_now && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
|
||||
// ✅ aplica corrente quando permitido
|
||||
if (ev->allowed_now && ev->current_limit_a > 0)
|
||||
{
|
||||
evse_set_runtime_charging_current(ev->current_limit_a);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_manager_init(void)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include "evse_session.h"
|
||||
#include "evse_meter.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -25,6 +26,8 @@ static uint64_t watt_microseconds = 0;
|
||||
static evse_session_t last_session;
|
||||
static bool last_session_valid = false;
|
||||
static uint32_t session_counter = 0;
|
||||
static char authorized_tag[AUTH_TAG_MAX_LEN];
|
||||
static char current_session_tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
@@ -52,9 +55,31 @@ void evse_session_init(void)
|
||||
watt_microseconds = 0;
|
||||
last_session_valid = false;
|
||||
session_counter = 0;
|
||||
authorized_tag[0] = '\0';
|
||||
current_session_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
}
|
||||
|
||||
void evse_session_set_authorized_tag(const char *tag)
|
||||
{
|
||||
portENTER_CRITICAL(&session_mux);
|
||||
if (tag && tag[0] != '\0')
|
||||
{
|
||||
strncpy(authorized_tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
authorized_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
authorized_tag[0] = '\0';
|
||||
}
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
}
|
||||
|
||||
void evse_session_clear_authorized_tag(void)
|
||||
{
|
||||
evse_session_set_authorized_tag(NULL);
|
||||
}
|
||||
|
||||
void evse_session_start(void)
|
||||
{
|
||||
TickType_t tick = xTaskGetTickCount();
|
||||
@@ -67,12 +92,17 @@ void evse_session_start(void)
|
||||
watt_microseconds = 0;
|
||||
session_counter++;
|
||||
uint32_t id = session_counter;
|
||||
strncpy(current_session_tag, authorized_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
current_session_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
|
||||
evse_set_limit_reached(false);
|
||||
|
||||
ESP_LOGI(TAG, "Session started (id=%" PRIu32 ") tick=%u us=%" PRId64,
|
||||
id, (unsigned)tick, now_us);
|
||||
ESP_LOGI(TAG, "Session started (id=%" PRIu32 ", tag=%s) tick=%u us=%" PRId64,
|
||||
id, tag[0] ? tag : "-", (unsigned)tick, now_us);
|
||||
|
||||
evse_session_event_data_t evt = {
|
||||
.type = EVSE_SESSION_EVENT_STARTED,
|
||||
@@ -82,6 +112,8 @@ void evse_session_start(void)
|
||||
.avg_power_w = 0,
|
||||
.is_current = true,
|
||||
};
|
||||
strncpy(evt.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
evt.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
post_session_event(&evt);
|
||||
}
|
||||
|
||||
@@ -91,6 +123,7 @@ void evse_session_end(void)
|
||||
int64_t start_us;
|
||||
uint64_t w_us;
|
||||
uint32_t id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
int64_t end_us = esp_timer_get_time();
|
||||
|
||||
@@ -106,11 +139,14 @@ void evse_session_end(void)
|
||||
start_us = session_start_us;
|
||||
w_us = watt_microseconds;
|
||||
id = session_counter;
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
|
||||
session_start_tick = 0;
|
||||
session_start_us = 0;
|
||||
last_tick_us = 0;
|
||||
watt_microseconds = 0;
|
||||
current_session_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
|
||||
uint32_t duration_s = (end_us > start_us) ? (uint32_t)((end_us - start_us) / 1000000LL) : 0;
|
||||
@@ -120,6 +156,9 @@ void evse_session_end(void)
|
||||
|
||||
portENTER_CRITICAL(&session_mux);
|
||||
last_session.start_tick = start_tick;
|
||||
last_session.session_id = id;
|
||||
strncpy(last_session.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
last_session.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
last_session.duration_s = duration_s;
|
||||
last_session.energy_wh = energy_wh;
|
||||
last_session.avg_power_w = avg_power;
|
||||
@@ -129,8 +168,8 @@ void evse_session_end(void)
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Session ended (id=%" PRIu32 "): duration=%" PRIu32 " s, energy=%" PRIu32
|
||||
" Wh, avg_power=%" PRIu32 " W",
|
||||
id, duration_s, energy_wh, avg_power);
|
||||
" Wh, avg_power=%" PRIu32 " W, tag=%s",
|
||||
id, duration_s, energy_wh, avg_power, tag[0] ? tag : "-");
|
||||
|
||||
evse_session_event_data_t evt = {
|
||||
.type = EVSE_SESSION_EVENT_FINISHED,
|
||||
@@ -140,6 +179,8 @@ void evse_session_end(void)
|
||||
.avg_power_w = avg_power,
|
||||
.is_current = false,
|
||||
};
|
||||
strncpy(evt.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
evt.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
post_session_event(&evt);
|
||||
}
|
||||
|
||||
@@ -185,6 +226,8 @@ bool evse_session_get(evse_session_t *out)
|
||||
bool has_current;
|
||||
evse_session_t last_copy;
|
||||
bool last_valid;
|
||||
uint32_t id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
int64_t now_us = esp_timer_get_time();
|
||||
|
||||
@@ -193,6 +236,9 @@ bool evse_session_get(evse_session_t *out)
|
||||
start_us = session_start_us;
|
||||
w_us = watt_microseconds;
|
||||
has_current = (session_start_tick != 0);
|
||||
id = session_counter;
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
last_copy = last_session;
|
||||
last_valid = last_session_valid;
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
@@ -205,6 +251,9 @@ bool evse_session_get(evse_session_t *out)
|
||||
uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0;
|
||||
|
||||
out->start_tick = start_tick;
|
||||
out->session_id = id;
|
||||
strncpy(out->tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
out->tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
out->duration_s = duration_s;
|
||||
out->energy_wh = energy_wh;
|
||||
out->avg_power_w = avg_power;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_event.h"
|
||||
#include "auth_types.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
|
||||
|
||||
@@ -45,6 +46,7 @@ typedef struct {
|
||||
evse_session_event_type_t type; ///< STARTED / FINISHED
|
||||
|
||||
uint32_t session_id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
uint32_t duration_s;
|
||||
uint32_t energy_wh;
|
||||
uint32_t avg_power_w;
|
||||
|
||||
@@ -4,19 +4,32 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "auth_types.h"
|
||||
|
||||
/**
|
||||
* @brief Charging session statistics
|
||||
*/
|
||||
typedef struct {
|
||||
TickType_t start_tick; ///< tick when session began (debug/trace)
|
||||
uint32_t duration_s; ///< total duration in seconds (tempo real)
|
||||
uint32_t energy_wh; ///< total energy consumed in Wh (tempo real)
|
||||
uint32_t avg_power_w; ///< average power in W
|
||||
bool is_current; ///< true if session still in progress
|
||||
TickType_t start_tick; ///< tick when session began (debug/trace)
|
||||
uint32_t session_id; ///< monotonically increasing session identifier
|
||||
char tag[AUTH_TAG_MAX_LEN]; ///< RFID/auth tag associated with this session, empty if none
|
||||
uint32_t duration_s; ///< total duration in seconds (tempo real)
|
||||
uint32_t energy_wh; ///< total energy consumed in Wh (tempo real)
|
||||
uint32_t avg_power_w; ///< average power in W
|
||||
bool is_current; ///< true if session still in progress
|
||||
} evse_session_t;
|
||||
|
||||
void evse_session_init(void);
|
||||
|
||||
/**
|
||||
* @brief Stores the tag that authorized the next charging session.
|
||||
*
|
||||
* The tag is copied into the session when evse_session_start() is called.
|
||||
* Passing NULL or an empty string clears the pending tag.
|
||||
*/
|
||||
void evse_session_set_authorized_tag(const char *tag);
|
||||
void evse_session_clear_authorized_tag(void);
|
||||
|
||||
void evse_session_start(void);
|
||||
void evse_session_end(void);
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ static void led_apply_state_mode(evse_state_event_t state)
|
||||
{
|
||||
case EVSE_STATE_EVENT_IDLE:
|
||||
// IDLE → verde fixo (claro e visível)
|
||||
led_apply_pattern(LED_ID_GREEN, LED_PATTERN_ON);
|
||||
led_apply_pattern(LED_ID_BLUE, LED_PATTERN_ON);
|
||||
break;
|
||||
|
||||
case EVSE_STATE_EVENT_WAITING:
|
||||
@@ -249,7 +249,7 @@ static void led_apply_state_mode(evse_state_event_t state)
|
||||
|
||||
case EVSE_STATE_EVENT_FAULT:
|
||||
// FAULT → vermelho a piscar rápido
|
||||
led_apply_pattern(LED_ID_RED, LED_PATTERN_BLINK_FAST);
|
||||
led_apply_pattern(LED_ID_BLUE, LED_PATTERN_BLINK_FAST);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
#define MB_PORT_NUM 1
|
||||
#define MB_DEV_SPEED 9600
|
||||
|
||||
// Ajuste os pinos conforme seu hardware
|
||||
#define MB_UART_TXD 21
|
||||
#define MB_UART_RXD 22
|
||||
#define MB_UART_RTS UART_PIN_NO_CHANGE // sem DE/RE
|
||||
// Ajuste os pinos conforme seu hardware (evite GPIO2 para RTS/DE/RE se possível)
|
||||
#define MB_UART_TXD 17
|
||||
#define MB_UART_RXD 16
|
||||
#define MB_UART_RTS 2 // pino DE/RE do transceiver RS-485
|
||||
|
||||
// ===== Timings =====
|
||||
#define UPDATE_INTERVAL (5000 / portTICK_PERIOD_MS)
|
||||
@@ -292,7 +292,6 @@ static void serial_mdb_ea777_task(void *param)
|
||||
}
|
||||
}
|
||||
|
||||
// ============ Init / Start / Stop ============
|
||||
esp_err_t meter_ea777_init(void)
|
||||
{
|
||||
if (is_initialized)
|
||||
@@ -301,14 +300,12 @@ esp_err_t meter_ea777_init(void)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// limpa UART se já houver driver
|
||||
if (uart_is_driver_installed(MB_PORT_NUM))
|
||||
{
|
||||
uart_driver_delete(MB_PORT_NUM);
|
||||
ESP_LOGI(TAG, "UART driver deleted");
|
||||
}
|
||||
|
||||
// destruir master anterior (ignora erro se não estiver init)
|
||||
(void)mbc_master_destroy();
|
||||
|
||||
mb_communication_info_t comm = {
|
||||
@@ -320,12 +317,14 @@ esp_err_t meter_ea777_init(void)
|
||||
void *handler = NULL;
|
||||
ESP_ERROR_CHECK(mbc_master_init(MB_PORT_SERIAL_MASTER, &handler));
|
||||
ESP_ERROR_CHECK(mbc_master_setup(&comm));
|
||||
|
||||
// >>> RS-485 half duplex
|
||||
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM,
|
||||
MB_UART_TXD, MB_UART_RXD,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
MB_UART_RTS, UART_PIN_NO_CHANGE));
|
||||
|
||||
ESP_ERROR_CHECK(mbc_master_start());
|
||||
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_UART));
|
||||
// ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_UART));
|
||||
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
|
||||
ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_ea777,
|
||||
|
||||
@@ -179,7 +179,7 @@ static void handle_zigbee_frame(const uint8_t *buf, size_t len)
|
||||
}
|
||||
|
||||
// Verifica se todas as 3 fases foram atualizadas
|
||||
if (phase_updated[PHASE_L1] && phase_updated[PHASE_L2] && phase_updated[PHASE_L3])
|
||||
if (phase_updated[PHASE_L1] /*&& phase_updated[PHASE_L2] && phase_updated[PHASE_L3]*/)
|
||||
{
|
||||
meter_zigbee_post_event();
|
||||
memset(phase_updated, 0, sizeof(phase_updated));
|
||||
|
||||
0
components/mongoose/.gitmodules
vendored
Executable file → Normal file
0
components/mongoose/CMakeLists.txt
Executable file → Normal file
0
components/mongoose/idf_component.yml
Executable file → Normal file
0
components/mongoose/mongoose/.clang-format
Executable file → Normal file
0
components/mongoose/mongoose/LICENSE
Executable file → Normal file
0
components/mongoose/mongoose/README.md
Executable file → Normal file
0
components/mongoose/mongoose/docs/README.md
Executable file → Normal file
0
components/mongoose/mongoose/docs/images/mg_commalist.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
components/mongoose/mongoose/docs/images/mg_http_get_request_len.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
components/mongoose/mongoose/docs/images/mg_http_message.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
0
components/mongoose/mongoose/docs/images/mg_http_next_multipart.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
0
components/mongoose/mongoose/docs/images/mg_http_part.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
0
components/mongoose/mongoose/docs/images/mg_http_reply.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
0
components/mongoose/mongoose/docs/images/mg_iobuf.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
0
components/mongoose/mongoose/docs/images/mg_iobuf_add1.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
components/mongoose/mongoose/docs/images/mg_iobuf_add2.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
0
components/mongoose/mongoose/docs/images/mg_iobuf_del.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
components/mongoose/mongoose/docs/images/packed.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
0
components/mongoose/mongoose/examples/captive-dns-server/README.md
Executable file → Normal file
0
components/mongoose/mongoose/examples/captive-dns-server/main.c
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/README.md
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/main.c
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/net.c
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/packed_fs.c
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/screenshots/dashboard.png
Executable file → Normal file
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 282 KiB |
0
components/mongoose/mongoose/examples/device-dashboard/screenshots/login.png
Executable file → Normal file
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
0
components/mongoose/mongoose/examples/device-dashboard/web_root/index.html
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/web_root/main.js
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/web_root/preact.min.js
vendored
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/web_root/style.css
Executable file → Normal file
0
components/mongoose/mongoose/examples/device-dashboard/web_root/user.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |