Files
chargeflow/components/peripherals/src/led.c
2025-06-06 21:17:25 +01:00

246 lines
6.3 KiB
C
Executable File

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "led.h"
#include "board_config.h"
#include "evse_error.h"
#include "evse_api.h"
#define LED_UPDATE_INTERVAL_MS 100
#define BLOCK_TIME pdMS_TO_TICKS(10)
static const char *TAG = "led";
typedef struct {
gpio_num_t gpio;
bool on : 1;
uint16_t ontime;
uint16_t offtime;
TimerHandle_t timer;
led_pattern_t pattern;
uint8_t blink_count;
} led_t;
static led_t leds[LED_ID_MAX] = {0};
static TimerHandle_t led_update_timer = NULL;
static evse_state_t led_state = -1;
// ----------------------------
// Funções Internas
// ----------------------------
static void led_update_timer_callback(TimerHandle_t xTimer);
static void led_update(void);
static void led_apply_by_state(evse_state_t state);
static inline void led_gpio_write(gpio_num_t gpio, bool level) {
if (gpio != GPIO_NUM_NC)
gpio_set_level(gpio, level);
}
static void led_timer_callback(TimerHandle_t xTimer)
{
led_t *led = (led_t *)pvTimerGetTimerID(xTimer);
led->on = !led->on;
led_gpio_write(led->gpio, led->on);
uint32_t next_time = led->on ? led->ontime : led->offtime;
xTimerChangePeriod(led->timer, pdMS_TO_TICKS(next_time), BLOCK_TIME);
}
// ----------------------------
// Inicialização
// ----------------------------
void led_init(void)
{
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.intr_type = GPIO_INTR_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
.pin_bit_mask = 0
};
for (int i = 0; i < LED_ID_MAX; i++) {
leds[i].gpio = GPIO_NUM_NC;
}
if (board_config.led_stop) {
leds[LED_ID_STOP].gpio = board_config.led_stop_gpio;
io_conf.pin_bit_mask |= BIT64(board_config.led_stop_gpio);
}
if (board_config.led_charging) {
leds[LED_ID_CHARGING].gpio = board_config.led_charging_gpio;
io_conf.pin_bit_mask |= BIT64(board_config.led_charging_gpio);
}
if (board_config.led_error) {
leds[LED_ID_ERROR].gpio = board_config.led_error_gpio;
io_conf.pin_bit_mask |= BIT64(board_config.led_error_gpio);
}
if (io_conf.pin_bit_mask != 0) {
ESP_ERROR_CHECK(gpio_config(&io_conf));
}
if (!led_update_timer) {
led_update_timer = xTimerCreate("led_update_timer",
pdMS_TO_TICKS(LED_UPDATE_INTERVAL_MS),
pdTRUE, NULL,
led_update_timer_callback);
if (led_update_timer) {
xTimerStart(led_update_timer, BLOCK_TIME);
} else {
ESP_LOGE(TAG, "Failed to create LED update timer");
}
}
}
// ----------------------------
// API Pública
// ----------------------------
void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime)
{
if (led_id >= LED_ID_MAX) return;
led_t *led = &leds[led_id];
if (led->gpio == GPIO_NUM_NC) return;
// Evita reconfiguração idêntica
if (led->ontime == ontime && led->offtime == offtime)
return;
if (led->timer) {
xTimerStop(led->timer, BLOCK_TIME);
}
led->ontime = ontime;
led->offtime = offtime;
if (ontime == 0) {
led->on = false;
led_gpio_write(led->gpio, 0);
} else if (offtime == 0) {
led->on = true;
led_gpio_write(led->gpio, 1);
} else {
led->on = true;
led_gpio_write(led->gpio, 1);
if (!led->timer) {
led->timer = xTimerCreate("led_timer", pdMS_TO_TICKS(ontime),
pdFALSE, (void *)led, led_timer_callback);
}
if (led->timer) {
xTimerStart(led->timer, BLOCK_TIME);
}
}
}
void led_apply_pattern(led_id_t id, led_pattern_t pattern)
{
if (id >= LED_ID_MAX) return;
led_t *led = &leds[id];
if (led->gpio == GPIO_NUM_NC) return;
if (led->pattern == pattern) return;
if (led->timer) {
xTimerStop(led->timer, BLOCK_TIME);
}
led->pattern = pattern;
led->blink_count = 0;
switch (pattern) {
case LED_PATTERN_OFF:
led_set_state(id, 0, 0);
break;
case LED_PATTERN_ON:
led_set_state(id, 1, 0);
break;
case LED_PATTERN_BLINK:
led_set_state(id, 500, 500);
break;
case LED_PATTERN_BLINK_FAST:
led_set_state(id, 200, 200);
break;
case LED_PATTERN_BLINK_SLOW:
led_set_state(id, 300, 1700);
break;
case LED_PATTERN_CHARGING_EFFECT:
led_set_state(id, 2000, 1000);
break;
}
}
// ----------------------------
// Controle por Estado
// ----------------------------
static void led_apply_by_state(evse_state_t state)
{
// Reset todos
led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF);
led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF);
led_apply_pattern(LED_ID_ERROR, LED_PATTERN_OFF);
switch (state) {
case EVSE_STATE_A:
led_apply_pattern(LED_ID_STOP, LED_PATTERN_ON);
break;
case EVSE_STATE_B1:
case EVSE_STATE_B2:
case EVSE_STATE_C1:
led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_ON);
break;
case EVSE_STATE_C2:
led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_CHARGING_EFFECT);
break;
case EVSE_STATE_D1:
case EVSE_STATE_D2:
led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_BLINK_FAST);
break;
case EVSE_STATE_E:
case EVSE_STATE_F:
led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST);
break;
default:
break;
}
}
// ----------------------------
// Timer Update
// ----------------------------
static void led_update(void)
{
if (evse_error_is_active()) {
led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST);
led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF);
led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF);
return;
}
evse_state_t current = evse_get_state();
if (current != led_state) {
led_state = current;
led_apply_by_state(current);
}
}
static void led_update_timer_callback(TimerHandle_t xTimer)
{
(void)xTimer;
led_update();
}