new upgrade
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
#include "evse_error.h"
|
||||
#include "evse_config.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -7,151 +6,147 @@
|
||||
#include "esp_log.h"
|
||||
#include "ntc_sensor.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_timer.h"
|
||||
#include "evse_events.h"
|
||||
#include "evse_config.h"
|
||||
|
||||
static const char *TAG = "evse_error";
|
||||
|
||||
// Estado global de erros
|
||||
static uint32_t error_bits = 0;
|
||||
static TickType_t auto_clear_timeout = 0;
|
||||
// ----------------------------------------------------
|
||||
// Estado interno
|
||||
// ----------------------------------------------------
|
||||
// raw_bits = erros “instantâneos” conforme checks (set/clear)
|
||||
// visible_bits = erros expostos ao resto do sistema (com holdoff)
|
||||
// clear_deadline = quando pode finalmente limpar visible_bits para 0
|
||||
static uint32_t raw_bits = 0;
|
||||
static uint32_t visible_bits = 0;
|
||||
static TickType_t clear_deadline = 0;
|
||||
|
||||
// Sticky flag: "todos erros foram limpos"
|
||||
// Sticky flag: "todos erros visíveis foram limpos"
|
||||
static bool error_cleared = false;
|
||||
|
||||
// Proteção contra concorrência
|
||||
static portMUX_TYPE error_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void evse_error_init(void)
|
||||
// ----------------------------------------------------
|
||||
// Helper: publicar evento de alteração de erro (visible_bits)
|
||||
// ----------------------------------------------------
|
||||
static void evse_error_post_event(uint32_t new_bits, uint32_t changed_mask)
|
||||
{
|
||||
evse_error_event_data_t ev = {
|
||||
.error_bits = new_bits,
|
||||
.changed_mask = changed_mask,
|
||||
.timestamp_us = esp_timer_get_time(),
|
||||
};
|
||||
|
||||
esp_err_t err = esp_event_post(
|
||||
EVSE_EVENTS,
|
||||
EVSE_EVENT_ERROR_CHANGED,
|
||||
&ev,
|
||||
sizeof(ev),
|
||||
portMAX_DELAY);
|
||||
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Falha ao publicar EVSE_EVENT_ERROR_CHANGED: %s",
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Helpers internos
|
||||
// ----------------------------------------------------
|
||||
static bool raw_has_bit(uint32_t bit)
|
||||
{
|
||||
bool v;
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
error_bits = 0;
|
||||
auto_clear_timeout = 0;
|
||||
error_cleared = false;
|
||||
v = ((raw_bits & bit) != 0);
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
return v;
|
||||
}
|
||||
|
||||
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v)
|
||||
static void reconcile_visible_locked(TickType_t now)
|
||||
{
|
||||
ESP_LOGD(TAG, "Verificando erro: pilot_voltage=%d, is_n12v=%s",
|
||||
pilot_voltage, is_n12v ? "true" : "false");
|
||||
|
||||
// 1) Falha elétrica geral no pilot
|
||||
if (pilot_voltage == PILOT_VOLTAGE_1)
|
||||
// Se existem erros reais, o visível segue imediatamente
|
||||
if (raw_bits != 0)
|
||||
{
|
||||
bool first_time = false;
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT))
|
||||
{
|
||||
error_cleared = false;
|
||||
error_bits |= EVSE_ERR_PILOT_FAULT_BIT;
|
||||
first_time = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pilot voltou a nível válido → limpa erro de pilot fault
|
||||
evse_error_clear(EVSE_ERR_PILOT_FAULT_BIT);
|
||||
}
|
||||
|
||||
// 2) Falta de -12V durante PWM (C ou D)
|
||||
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v)
|
||||
{
|
||||
bool first_time = false;
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT))
|
||||
{
|
||||
error_cleared = false;
|
||||
error_bits |= EVSE_ERR_DIODE_SHORT_BIT;
|
||||
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
|
||||
first_time = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Se já não estamos em C/D sem -12V, limpa o erro de diodo curto
|
||||
evse_error_clear(EVSE_ERR_DIODE_SHORT_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_temperature_check(void)
|
||||
{
|
||||
float temp_c = ntc_temp_sensor();
|
||||
uint8_t threshold = evse_get_temp_threshold();
|
||||
|
||||
ESP_LOGD(TAG, "Verificando temperatura: atual=%.2f °C, limite=%d °C",
|
||||
temp_c, threshold);
|
||||
|
||||
// Temperatura inválida -> erro de sensor
|
||||
if (temp_c < -40.0f || temp_c > 150.0f)
|
||||
{
|
||||
bool first_time = false;
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT))
|
||||
{
|
||||
error_cleared = false;
|
||||
error_bits |= EVSE_ERR_TEMPERATURE_FAULT_BIT;
|
||||
first_time = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
|
||||
}
|
||||
visible_bits = raw_bits;
|
||||
clear_deadline = 0;
|
||||
error_cleared = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Leitura válida -> limpa erro de sensor
|
||||
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
||||
|
||||
// Temperatura máxima
|
||||
if (temp_c >= threshold)
|
||||
// raw_bits == 0
|
||||
if (visible_bits == 0)
|
||||
{
|
||||
bool first_time = false;
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT))
|
||||
{
|
||||
error_cleared = false;
|
||||
error_bits |= EVSE_ERR_TEMPERATURE_HIGH_BIT;
|
||||
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000);
|
||||
first_time = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C",
|
||||
temp_c, threshold);
|
||||
}
|
||||
clear_deadline = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// Ainda há erro visível (holdoff). Arma deadline 1x.
|
||||
if (clear_deadline == 0)
|
||||
{
|
||||
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
||||
clear_deadline = now + pdMS_TO_TICKS(EVSE_ERROR_COOLDOWN_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
// Expirou -> limpar finalmente
|
||||
if ((int32_t)(now - clear_deadline) >= 0)
|
||||
{
|
||||
visible_bits = 0;
|
||||
clear_deadline = 0;
|
||||
error_cleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// API pública
|
||||
// ----------------------------------------------------
|
||||
void evse_error_init(void)
|
||||
{
|
||||
uint32_t old_vis, new_vis, changed;
|
||||
bool post = false;
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
|
||||
old_vis = visible_bits;
|
||||
|
||||
raw_bits = 0;
|
||||
visible_bits = 0;
|
||||
clear_deadline = 0;
|
||||
error_cleared = false;
|
||||
|
||||
new_vis = visible_bits;
|
||||
changed = old_vis ^ new_vis;
|
||||
post = (changed != 0);
|
||||
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (post)
|
||||
{
|
||||
evse_error_post_event(new_vis, changed);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t evse_get_error(void)
|
||||
{
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
uint32_t val = error_bits;
|
||||
uint32_t val = visible_bits;
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool evse_error_is_active(void)
|
||||
{
|
||||
return evse_get_error() != 0;
|
||||
}
|
||||
|
||||
uint32_t evse_error_get_bits(void)
|
||||
{
|
||||
return evse_get_error();
|
||||
}
|
||||
|
||||
bool evse_error_cleared_flag(void)
|
||||
{
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
@@ -169,61 +164,147 @@ void evse_error_reset_flag(void)
|
||||
|
||||
void evse_error_set(uint32_t bitmask)
|
||||
{
|
||||
uint32_t old_vis, new_vis, changed;
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
|
||||
error_cleared = false;
|
||||
error_bits |= bitmask;
|
||||
old_vis = visible_bits;
|
||||
|
||||
if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS)
|
||||
{
|
||||
auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s
|
||||
}
|
||||
raw_bits |= bitmask;
|
||||
// se aparece qualquer erro, o "cleared" deixa de ser verdade
|
||||
error_cleared = false;
|
||||
|
||||
reconcile_visible_locked(now);
|
||||
|
||||
new_vis = visible_bits;
|
||||
changed = old_vis ^ new_vis;
|
||||
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (changed != 0)
|
||||
{
|
||||
evse_error_post_event(new_vis, changed);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_error_clear(uint32_t bitmask)
|
||||
{
|
||||
uint32_t old_vis, new_vis, changed;
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
|
||||
bool had_error = (error_bits != 0);
|
||||
error_bits &= ~bitmask;
|
||||
old_vis = visible_bits;
|
||||
|
||||
if (had_error && error_bits == 0)
|
||||
{
|
||||
error_cleared = true;
|
||||
}
|
||||
raw_bits &= ~bitmask;
|
||||
|
||||
// ✅ Aqui é onde o “60s depois do erro desaparecer” é armado:
|
||||
// quando raw_bits chega a 0, reconcile arma clear_deadline (uma vez)
|
||||
reconcile_visible_locked(now);
|
||||
|
||||
new_vis = visible_bits;
|
||||
changed = old_vis ^ new_vis;
|
||||
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (changed != 0)
|
||||
{
|
||||
evse_error_post_event(new_vis, changed);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_error_tick(void)
|
||||
{
|
||||
uint32_t old_vis, new_vis, changed;
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
|
||||
portENTER_CRITICAL(&error_mux);
|
||||
|
||||
if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) &&
|
||||
auto_clear_timeout != 0 &&
|
||||
xTaskGetTickCount() >= auto_clear_timeout)
|
||||
{
|
||||
error_bits &= ~EVSE_ERR_AUTO_CLEAR_BITS;
|
||||
|
||||
if (error_bits == 0)
|
||||
{
|
||||
error_cleared = true;
|
||||
}
|
||||
|
||||
auto_clear_timeout = 0;
|
||||
}
|
||||
old_vis = visible_bits;
|
||||
reconcile_visible_locked(now);
|
||||
new_vis = visible_bits;
|
||||
changed = old_vis ^ new_vis;
|
||||
|
||||
portEXIT_CRITICAL(&error_mux);
|
||||
|
||||
if (changed != 0)
|
||||
{
|
||||
evse_error_post_event(new_vis, changed);
|
||||
}
|
||||
}
|
||||
|
||||
bool evse_error_is_active(void)
|
||||
// ----------------------------------------------------
|
||||
// Checks (raw -> set/clear)
|
||||
// ----------------------------------------------------
|
||||
void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v)
|
||||
{
|
||||
return evse_get_error() != 0;
|
||||
ESP_LOGD(TAG, "Verificando erro: pilot_voltage=%d, is_n12v=%s",
|
||||
pilot_voltage, is_n12v ? "true" : "false");
|
||||
|
||||
// 1) Falha elétrica geral no pilot
|
||||
if (pilot_voltage == PILOT_VOLTAGE_1)
|
||||
{
|
||||
if (!raw_has_bit(EVSE_ERR_PILOT_FAULT_BIT))
|
||||
{
|
||||
ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)");
|
||||
}
|
||||
evse_error_set(EVSE_ERR_PILOT_FAULT_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_error_clear(EVSE_ERR_PILOT_FAULT_BIT);
|
||||
}
|
||||
|
||||
// 2) Falta de -12V durante PWM (C ou D)
|
||||
if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v)
|
||||
{
|
||||
if (!raw_has_bit(EVSE_ERR_DIODE_SHORT_BIT))
|
||||
{
|
||||
ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)");
|
||||
}
|
||||
evse_error_set(EVSE_ERR_DIODE_SHORT_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_error_clear(EVSE_ERR_DIODE_SHORT_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t evse_error_get_bits(void)
|
||||
void evse_temperature_check(void)
|
||||
{
|
||||
return evse_get_error();
|
||||
float temp_c = ntc_temp_sensor();
|
||||
uint8_t threshold = evse_get_temp_threshold();
|
||||
|
||||
ESP_LOGD(TAG, "Verificando temperatura: atual=%.2f °C, limite=%d °C",
|
||||
temp_c, threshold);
|
||||
|
||||
// Temperatura inválida -> erro de sensor
|
||||
if (temp_c < -40.0f || temp_c > 150.0f)
|
||||
{
|
||||
if (!raw_has_bit(EVSE_ERR_TEMPERATURE_FAULT_BIT))
|
||||
{
|
||||
ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado");
|
||||
}
|
||||
evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
// Leitura válida -> limpa erro de sensor
|
||||
evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT);
|
||||
|
||||
// Temperatura máxima
|
||||
if (temp_c >= threshold)
|
||||
{
|
||||
if (!raw_has_bit(EVSE_ERR_TEMPERATURE_HIGH_BIT))
|
||||
{
|
||||
ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C",
|
||||
temp_c, threshold);
|
||||
}
|
||||
evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user