chore: snapshot before shared RS485 bus integration

This commit is contained in:
2026-07-22 15:36:04 +01:00
parent ef02e5c5f5
commit 2a803fcef1
167 changed files with 5749 additions and 1128 deletions

0
components/evse_link/CMakeLists.txt Executable file → Normal file
View File

0
components/evse_link/include/evse_link.h Executable file → Normal file
View File

View File

@@ -5,21 +5,36 @@
#include <stdbool.h>
#include "driver/uart.h"
// UART instance and configuration
#define UART_PORT UART_NUM_2
// UART baud/buffer configuration
#define UART_BAUDRATE 9600
#define UART_RX_BUF_SIZE 256
// GPIO pin assignments for RS-485 UART
// Ajuste conforme seu hardware
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 2 // pino DE/RE do transceiver RS-485
// Select EVSE-Link physical layer at compile time.
// 0 = production RS485: UART2 GPIO17/16 + RTS/DE GPIO2
// 1 = emergency/internal UART TTL: UART1 GPIO21/22, no RS485/RTS
#ifndef EVSE_LINK_USE_UART_TTL
#define EVSE_LINK_USE_UART_TTL 0
#endif
#define TX_PIN MB_UART_TXD
#define RX_PIN MB_UART_RXD
#define RTS_PIN MB_UART_RTS
#if EVSE_LINK_USE_UART_TTL
#define UART_PORT UART_NUM_1
#define TX_PIN 21
#define RX_PIN 22
#define RTS_PIN UART_PIN_NO_CHANGE
#define EVSE_LINK_UART_MODE UART_MODE_UART
#define EVSE_LINK_PHY_NAME "UART_TTL"
#else
#define UART_PORT UART_NUM_2
#define TX_PIN 17
#define RX_PIN 16
#define RTS_PIN 2
#define EVSE_LINK_UART_MODE UART_MODE_RS485_HALF_DUPLEX
#define EVSE_LINK_PHY_NAME "RS485"
#endif
// Frame delimiters
#define MAGIC_START 0x7E
#define MAGIC_END 0x7F

0
components/evse_link/src/evse_link.c Executable file → Normal file
View File

View File

@@ -110,10 +110,12 @@ static esp_err_t configure_uart(void)
}
// RS-485 HALF DUPLEX (driver controla RTS automaticamente)
err = uart_set_mode(UART_PORT, UART_MODE_RS485_HALF_DUPLEX);
err = uart_set_mode(UART_PORT, EVSE_LINK_UART_MODE);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "uart_set_mode(RS485_HALF_DUPLEX) failed: %s", esp_err_to_name(err));
ESP_LOGE(TAG, "uart_set_mode(%s) failed: %s",
EVSE_LINK_PHY_NAME,
esp_err_to_name(err));
return err;
}
@@ -122,15 +124,22 @@ static esp_err_t configure_uart(void)
(void)uart_set_rx_timeout(UART_PORT, EVSE_LINK_RX_TIMEOUT);
// Opcional: inverter RTS se hardware exigir
if (EVSE_LINK_RTS_INVERT)
if (!EVSE_LINK_USE_UART_TTL && EVSE_LINK_RTS_INVERT)
{
(void)uart_set_line_inverse(UART_PORT, UART_SIGNAL_RTS_INV);
ESP_LOGW(TAG, "RS485 driver: RTS inverted");
}
ESP_LOGW(TAG, "RS485 driver enabled: UART%d TX=%d RX=%d RTS=%d baud=%d rx_to=%d rx_thresh=%d",
UART_PORT, TX_PIN, RX_PIN, RTS_PIN, UART_BAUDRATE,
EVSE_LINK_RX_TIMEOUT, EVSE_LINK_RX_FULL_THRESH);
ESP_LOGW(TAG,
"%s driver enabled: UART%d TX=%d RX=%d RTS=%d baud=%d rx_to=%d rx_thresh=%d",
EVSE_LINK_PHY_NAME,
UART_PORT,
TX_PIN,
RX_PIN,
RTS_PIN,
UART_BAUDRATE,
EVSE_LINK_RX_TIMEOUT,
EVSE_LINK_RX_FULL_THRESH);
return ESP_OK;
}

View File

@@ -6,7 +6,8 @@
// 3) Opção de política no fallback: por default faz PAUSE (mais seguro). Pode ser alterado por macro.
// 4) Reduzir ruído de logs em caminho quente (RX frames em DEBUG).
// 5) Manter semântica: só sai de safe_mode com comando explícito de potência (SET_CURRENT / RESUME).
// 6) Mantém lógica de pause SET_CURRENT=0 e resume quando >0.
// 6) SET_CURRENT é delegado ao EVSE_Manager via LINK_EVENT_CURRENT_LIMIT_APPLIED.
// O evse_link_slave não mexe diretamente em autorização/runtime current.
//
// Nota: A enum do evse_state_event_data_t que enviaste está correta para o handler.
@@ -77,10 +78,6 @@ static QueueHandle_t hb_req_q = NULL;
static bool safe_mode = false;
static uint16_t saved_runtime_limit = 0; // informativo
// "remote pause" (SET_CURRENT=0)
static bool paused_by_master = false;
static bool paused_prev_authorized = false;
static portMUX_TYPE s_state_mux = portMUX_INITIALIZER_UNLOCKED;
static bool evse_handler_registered = false;
@@ -97,13 +94,26 @@ static size_t bounded_strlen_u8(const uint8_t *s, size_t max_len)
static void send_heartbeat_frame_now(void)
{
bool charging = evse_state_is_charging(evse_get_state());
evse_state_t st = evse_get_state();
/*
* Para o master/loadbalancer, este campo significa "há pedido/demanda
* ativa deste conector", não apenas "relé ligado".
*
* C2/D2 => charging real
* C1/D1 => EV está a pedir carga, mas o relé está OFF por pausa/limite
*
* Isto permite retomar depois de SET_CURRENT=0: em C1 o slave continua
* elegível para o loadbalancer e o master pode voltar a enviar >0A.
*/
bool demand = evse_state_is_charging(st) || evse_state_is_requesting(st);
uint16_t hw_max = evse_get_max_charging_current();
uint16_t runtime = evse_get_runtime_charging_current();
uint8_t hb[] = {
CMD_HEARTBEAT,
charging ? 1 : 0,
demand ? 1 : 0,
(uint8_t)(hw_max & 0xFF), (uint8_t)(hw_max >> 8),
(uint8_t)(runtime & 0xFF), (uint8_t)(runtime >> 8)};
@@ -152,18 +162,6 @@ static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, voi
const evse_state_event_data_t *evt = (const evse_state_event_data_t *)data;
// Enforce pause: se algo tentar voltar a carregar enquanto paused_by_master, revoga auth.
bool paused;
portENTER_CRITICAL(&s_state_mux);
paused = paused_by_master;
portEXIT_CRITICAL(&s_state_mux);
if (paused && evt->state == EVSE_STATE_EVENT_CHARGING)
{
// Garante que não continua a carregar por clamp/auto-auth
evse_state_set_authorized(false);
}
// Envia heartbeat quando entra em IDLE ou CHARGING (estado relevante)
if (evt->state == EVSE_STATE_EVENT_IDLE || evt->state == EVSE_STATE_EVENT_CHARGING)
request_heartbeat_send();
@@ -190,41 +188,6 @@ static void maybe_exit_safe_mode_on_explicit_power_cmd(uint8_t cmd)
}
}
static void apply_pause_by_master(void)
{
bool prev_auth = evse_state_get_authorized();
portENTER_CRITICAL(&s_state_mux);
paused_by_master = true;
paused_prev_authorized = prev_auth;
portEXIT_CRITICAL(&s_state_mux);
// Revoga autorização para parar contactor/pilot via core
if (prev_auth)
evse_state_set_authorized(false);
// Mantém runtime num valor seguro (não 0, pois clamp -> 6A).
// O "pause" efetivo é pela autorização=false.
evse_set_runtime_charging_current(MIN_CHARGING_CURRENT_LIMIT);
}
static void clear_pause_by_master_if_any(void)
{
bool was_paused;
bool prev_auth;
portENTER_CRITICAL(&s_state_mux);
was_paused = paused_by_master;
prev_auth = paused_prev_authorized;
paused_by_master = false;
paused_prev_authorized = false;
portEXIT_CRITICAL(&s_state_mux);
// Se estava autorizado antes do pause, tenta reautorizar
if (was_paused && prev_auth)
evse_state_set_authorized(true);
}
static void on_frame_slave(uint8_t src, uint8_t dest,
const uint8_t *payload, uint8_t len)
{
@@ -290,27 +253,22 @@ static void on_frame_slave(uint8_t src, uint8_t dest,
if (amps == 0)
{
// PAUSE explícito
ESP_LOGI(TAG, "SET_CURRENT=0 => PAUSE (src=0x%02X)", src);
apply_pause_by_master();
ESP_LOGI(TAG, "SET_CURRENT=0 => PAUSE requested by master");
// Confirma sem bloquear RX
request_heartbeat_send();
// Publica evento com 0A (semântica: pause)
(void)esp_event_post(EVSE_LINK_EVENTS, LINK_EVENT_CURRENT_LIMIT_APPLIED,
&amps, sizeof(amps), portMAX_DELAY);
esp_event_post(EVSE_LINK_EVENTS,
LINK_EVENT_CURRENT_LIMIT_APPLIED,
&amps,
sizeof(amps),
portMAX_DELAY);
break;
}
clear_pause_by_master_if_any();
evse_set_runtime_charging_current(amps);
// confirma sem bloquear RX
request_heartbeat_send();
ESP_LOGI(TAG, "Applied runtime limit: %uA from 0x%02X", (unsigned)amps, src);
ESP_LOGI(TAG, "Forwarding runtime limit: %uA from 0x%02X", (unsigned)amps, src);
(void)esp_event_post(EVSE_LINK_EVENTS, LINK_EVENT_CURRENT_LIMIT_APPLIED,
&amps, sizeof(amps), portMAX_DELAY);
@@ -397,8 +355,14 @@ static void fallback_timer_cb(TimerHandle_t xTimer)
ESP_LOGW(TAG, "Fallback timeout: entering safe mode (saved %uA). Policy=PAUSE",
(unsigned)saved_runtime_limit);
// pausar é mais seguro quando o master “morre”
apply_pause_by_master();
// Pausar é mais seguro quando o master falha.
// O EVSE_Manager é o dono da autorização/pausa/retoma.
uint16_t zero_amps = 0;
(void)esp_event_post(EVSE_LINK_EVENTS,
LINK_EVENT_CURRENT_LIMIT_APPLIED,
&zero_amps,
sizeof(zero_amps),
portMAX_DELAY);
#else
ESP_LOGW(TAG, "Fallback timeout: entering safe mode (saved %uA, forcing %uA). Policy=MIN",
(unsigned)saved_runtime_limit, (unsigned)MIN_CHARGING_CURRENT_LIMIT);