inicio do uso da eeprom

This commit is contained in:
XupaMisto 2025-11-22 00:13:32 +00:00
parent a1230f66ad
commit e5572b2fad
25 changed files with 650 additions and 2528 deletions

13
.vscode/settings.json vendored
View File

@ -3,8 +3,14 @@
"*.c": "c",
"*.h": "c"
},
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.compileCommands": "Z:/varios/LED_shit/build/compile_commands.json",
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
"C_Cpp.default.includePath": [
"${workspaceFolder}/**",
"C:/ESP/frameworks/v5.1.6/esp-idf/components/**"
],
"idf.espIdfPath": "C:/Users/carec/esp/v5.1.6/esp-idf",
"idf.pythonBinPath": "C:/Users/carec/.espressif/python_env/idf5.1_py3.11_env/Scripts/python.exe",
@ -14,8 +20,9 @@
"idf.adapterTargetName": "esp32",
"idf.portWin": "COM3",
"idf.flashType": "UART",
"idf.buildDir": "C:/esp_build_temp",
"idf.flashDir": "C:/esp_build_temp",
"idf.buildDir": "${workspaceFolder}/build",
"idf.flashDir": "${workspaceFolder}/build",
"cmake.buildDirectory": "${workspaceFolder}/build",
"cmake.configureSettings": {

View File

@ -4,20 +4,17 @@ idf_component_register(
"wifi_config_portal.c"
"mqtt_handler.c"
"mqtt_comandos.c"
"fs_handler.c"
"eeprom_tls.c"
"eeprom_virtual.c"
"dns_server.c"
# LED driver
"${CMAKE_CURRENT_SOURCE_DIR}/led_driver.c"
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip/led_strip.c"
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip/led_strip_encoder.c"
"dns_server.c"
"led_driver.c"
"led_effects.c"
"eeprom_animacao.c"
"led_task.c"
"creditos.c"
INCLUDE_DIRS
"."
"include"
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip"
REQUIRES
esp_wifi
@ -26,7 +23,6 @@ idf_component_register(
nvs_flash
mqtt
json
spiffs
driver
esp_http_server
)

34
main/creditos.c Normal file
View File

@ -0,0 +1,34 @@
#include "creditos.h"
#include "led_driver.h"
#include "esp_random.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_random.h"
#include "esp_log.h"
volatile bool tem_creditos = false;
static const char *TAG = "CREDITOS";
void creditos_dar(void) {
tem_creditos = true;
ESP_LOGI(TAG, "💰 Crédito recebido");
}
void creditos_task(void *pv) {
while (1) {
if (tem_creditos) {
uint16_t pos = esp_random() % LED_COUNT;
ESP_LOGI(TAG, "🎯 SPIN -> posição %u", pos);
led_spin_to(pos, 2, 12, 80);
tem_creditos = false; // crédito consumido
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}

48
main/eeprom_animacao.c Normal file
View File

@ -0,0 +1,48 @@
#include "eeprom_animacao.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"
static const char *TAG = "EEPROM_ANIM";
#define NAMESPACE "storage"
#define KEY_ANIM "animacao"
// valor em RAM, sempre igual ao guardado
uint8_t animacao = 0;
void animacao_load(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(NAMESPACE, NVS_READONLY, &handle);
if (err == ESP_OK) {
err = nvs_get_u8(handle, KEY_ANIM, &animacao);
nvs_close(handle);
if (err != ESP_OK) {
animacao = 0; // default
}
} else {
animacao = 0; // default
}
ESP_LOGI(TAG, "Carregado animacao = %u", animacao);
}
void animacao_save(uint8_t v)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(NAMESPACE, NVS_READWRITE, &handle);
if (err == ESP_OK) {
nvs_set_u8(handle, KEY_ANIM, v);
nvs_commit(handle);
nvs_close(handle);
animacao = v; // atualiza RAM também
ESP_LOGI(TAG, "Gravado animacao = %u", v);
} else {
ESP_LOGW(TAG, "Falha ao abrir NVS p/ animacao");
}
}

View File

@ -1,54 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_spiffs.h"
static const char *TAG = "FS";
// --- MONTAR SPIFFS ---
void fs_init(void) {
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = "storage",
.max_files = 5,
.format_if_mount_failed = true
};
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ Falha ao montar SPIFFS (%s)", esp_err_to_name(ret));
return;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ Erro ao obter info SPIFFS (%s)", esp_err_to_name(ret));
return;
}
ESP_LOGI(TAG, "📂 SPIFFS montado: total=%d KB, usado=%d KB",
(int)(total / 1024), (int)(used / 1024));
// Criar ficheiro de exemplo se não existir
FILE *f = fopen("/spiffs/config.json", "r");
if (!f) {
ESP_LOGW(TAG, "⚠️ config.json não existe, a criar...");
f = fopen("/spiffs/config.json", "w");
if (f) {
fprintf(f, "{ \"ssid\": \"ALQAEDA\", \"pass\": \"1q2w3e4r5t\" }");
fclose(f);
ESP_LOGI(TAG, "✅ config.json criado.");
} else {
ESP_LOGE(TAG, "❌ Erro a criar config.json");
}
} else {
fclose(f);
}
}
// --- DESMONTAR SPIFFS ---
void fs_deinit(void) {
ESP_LOGI(TAG, "🧹 Desmontando SPIFFS...");
esp_vfs_spiffs_unregister(NULL);
}

8
main/include/creditos.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
extern volatile bool tem_creditos;
void creditos_task(void *pv);
void creditos_dar(void); // função para marcar crédito

View File

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
// byte carregado da EEPROM
extern uint8_t animacao;
// lê o byte guardado
void animacao_load(void);
// grava o byte
void animacao_save(uint8_t v);

View File

@ -1,24 +1,26 @@
#pragma once
#ifndef LED_DRIVER_H
#define LED_DRIVER_H
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
// === Configurações principais ===
#define LED_PIN 18
#define LED_COUNT 60
#define LED_RES_HZ 10000000 // 10 MHz de resolução (0.1 µs por tick)
// === Interface pública ===
esp_err_t led_init(void);
esp_err_t led_clear(void);
esp_err_t led_set(uint16_t index, uint8_t r, uint8_t g, uint8_t b);
esp_err_t led_show(void);
void led_rainbow(uint16_t offset);
void led_spin_to(uint16_t target, uint16_t rounds, uint16_t delay_start, uint16_t delay_end);
void led_idle_animation(void);
#ifdef __cplusplus
}
#define LED_COUNT 60
// inicialização do driver
void led_driver_init(void);
// número de LEDs
int led_get_count(void);
// envia os dados para o anel
void led_show(void);
// limpa todos os LEDs
void led_clear(void);
// define um LED individual
void led_set_pixel(int index, uint8_t r, uint8_t g, uint8_t b);
// spin da sorte
void led_spin_to(uint16_t target, int min_spins, int max_spins, int base_delay_ms);
#endif

View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
void led_all_on(uint8_t r, uint8_t g, uint8_t b);
void led_all_off(void);
void led_idle_animation(void);
void led_jackpot_animation(void);
void led_clock_animation(void); // <-- NOVA ANIMAÇÃO

3
main/include/led_task.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void led_task(void *pv);

View File

@ -1,5 +1,4 @@
#pragma once
#include "cJSON.h"
#include "esp_err.h"
void mqtt_comandos_handle(cJSON *root);

View File

@ -5,10 +5,16 @@
extern "C" {
#endif
extern esp_mqtt_client_handle_t mqtt_client; // ✅ agora visível em outros .c
static inline void mqtt_handler_loop(void) {
// placeholder — sem função neste build
}
extern esp_mqtt_client_handle_t mqtt_client;
// Exportar os tópicos MQTT (antes eram static!)
extern char topic_status[64];
extern char topic_cmd[64];
extern char topic_resp[64];
extern char topic_lwt[64];
// Opcional: loop placeholder
static inline void mqtt_handler_loop(void) {}
void mqtt_handler_start(void);

View File

@ -1,147 +1,131 @@
#include "led_driver.h"
#include "driver/rmt_tx.h"
#include "led_strip_encoder.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_random.h"
#include <string.h>
#include "esp_rom_sys.h"
static const char *TAG = "LED";
static bool led_ready = false; // 🚦 flag de inicialização
static rmt_channel_handle_t led_chan = NULL;
static rmt_encoder_handle_t led_enc = NULL;
static uint8_t led_pixels[LED_COUNT * 3];
#define LED_PIN 18 // <-- CERTIFICA-TE disto
#define RMT_RESOLUTION_HZ 10000000 // 10 MHz (1 tick = 0.1us)
// --- Conversão HSV → RGB ---
static void hsv2rgb(uint32_t h, uint32_t s, uint32_t v,
uint8_t *r, uint8_t *g, uint8_t *b)
typedef struct {
uint8_t r,g,b;
} led_color_t;
static led_color_t leds[LED_COUNT];
static rmt_channel_handle_t rmt_chan = NULL;
static rmt_encoder_handle_t encoder = NULL;
static rmt_transmit_config_t tx_config = {
.loop_count = 0
};
void led_driver_init(void)
{
h %= 360;
uint32_t rgb_max = v * 255 / 100;
uint32_t rgb_min = rgb_max * (100 - s) / 100;
uint32_t i = h / 60, diff = h % 60;
uint32_t adj = (rgb_max - rgb_min) * diff / 60;
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
switch (i) {
case 0: *r = rgb_max; *g = rgb_min + adj; *b = rgb_min; break;
case 1: *r = rgb_max - adj; *g = rgb_max; *b = rgb_min; break;
case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + adj; break;
case 3: *r = rgb_min; *g = rgb_max - adj; *b = rgb_max; break;
case 4: *r = rgb_min + adj; *g = rgb_min; *b = rgb_max; break;
default:*r = rgb_max; *g = rgb_min; *b = rgb_max - adj; break;
}
}
// --- Inicialização ---
esp_err_t led_init(void)
{
ESP_LOGI(TAG, "Inicializando LEDs no GPIO%d (%d LEDs)", LED_PIN, LED_COUNT);
rmt_tx_channel_config_t tx_cfg = {
rmt_tx_channel_config_t chan_cfg = {
.gpio_num = LED_PIN,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = RMT_RESOLUTION_HZ,
.mem_block_symbols = 64,
.resolution_hz = LED_RES_HZ,
.trans_queue_depth = 4,
.trans_queue_depth = 1,
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_cfg, &led_chan));
led_strip_encoder_config_t enc_cfg = {.resolution = LED_RES_HZ};
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&enc_cfg, &led_enc));
ESP_ERROR_CHECK(rmt_enable(led_chan));
ESP_ERROR_CHECK(rmt_new_tx_channel(&chan_cfg, &rmt_chan));
ESP_ERROR_CHECK(rmt_enable(rmt_chan));
memset(led_pixels, 0, sizeof(led_pixels));
// WS2812B timings (EXATOS)
rmt_bytes_encoder_config_t enc_cfg = {
.bit0 = {
.duration0 = 4, .level0 = 1, // 0.4us HIGH
.duration1 = 9, .level1 = 0 // 0.9us LOW
},
.bit1 = {
.duration0 = 9, .level0 = 1, // 0.9us HIGH
.duration1 = 4, .level1 = 0 // 0.4us LOW
},
.flags.msb_first = 1
};
led_ready = true; // ✅ marca como pronto
led_show(); // apaga tudo ao iniciar
return ESP_OK;
ESP_ERROR_CHECK(rmt_new_bytes_encoder(&enc_cfg, &encoder));
memset(leds, 0, sizeof(leds));
led_show();
}
// --- Apagar todos os LEDs ---
esp_err_t led_clear(void)
void led_set_pixel(int index, uint8_t r, uint8_t g, uint8_t b)
{
if (!led_ready) return ESP_ERR_INVALID_STATE;
memset(led_pixels, 0, sizeof(led_pixels));
return led_show();
if (index < 0 || index >= LED_COUNT) return;
leds[index].r = r;
leds[index].g = g;
leds[index].b = b;
}
// --- Definir cor individual ---
esp_err_t led_set(uint16_t index, uint8_t r, uint8_t g, uint8_t b)
void led_clear(void)
{
if (index >= LED_COUNT) return ESP_ERR_INVALID_ARG;
led_pixels[index * 3 + 0] = g; // WS2812 usa ordem GRB
led_pixels[index * 3 + 1] = r;
led_pixels[index * 3 + 2] = b;
return ESP_OK;
memset(leds, 0, sizeof(leds));
}
// --- Enviar buffer para o anel ---
esp_err_t led_show(void)
static void hw_update(void)
{
if (!led_ready || !led_chan || !led_enc) {
ESP_LOGW(TAG, "⚠️ led_show() chamado antes da inicialização — ignorado");
return ESP_ERR_INVALID_STATE;
uint8_t buf[LED_COUNT * 3];
// MOST common format is GRB
for (int i = 0; i < LED_COUNT; i++) {
buf[i*3 + 0] = leds[i].g;
buf[i*3 + 1] = leds[i].r;
buf[i*3 + 2] = leds[i].b;
}
rmt_transmit_config_t tx_cfg = {.loop_count = 0};
esp_err_t err = rmt_transmit(led_chan, led_enc,
led_pixels, sizeof(led_pixels), &tx_cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "❌ rmt_transmit falhou (%s)", esp_err_to_name(err));
return err;
}
ESP_ERROR_CHECK(rmt_transmit(rmt_chan, encoder, buf, sizeof(buf), &tx_config));
rmt_tx_wait_all_done(rmt_chan, -1);
// Reset time > 50us
esp_rom_delay_us(60);
err = rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
if (err != ESP_OK) {
ESP_LOGE(TAG, "❌ rmt_tx_wait_all_done falhou (%s)", esp_err_to_name(err));
return err;
}
return ESP_OK;
}
// --- Animação de rotação até posição alvo ---
void led_spin_to(uint16_t target, uint16_t rounds, uint16_t delay_start, uint16_t delay_end)
void led_show(void)
{
if (!led_ready) return;
hw_update();
}
uint32_t total_steps = rounds * LED_COUNT + target;
for (uint32_t i = 0; i < total_steps; i++) {
uint16_t pos = i % LED_COUNT;
int led_get_count(void)
{
return LED_COUNT;
}
void led_spin_to(uint16_t target, int min_spins, int max_spins, int base_delay_ms)
{
int n = LED_COUNT;
if (min_spins < 1) min_spins = 1;
if (max_spins < min_spins) max_spins = min_spins;
int extra = 0;
if (max_spins > min_spins)
extra = rand() % (max_spins - min_spins + 1);
int total_steps = (min_spins + extra) * n + (target % n);
int delay = base_delay_ms;
for (int step = 0; step <= total_steps; step++) {
int pos = step % n;
for (int i = 0; i < n; i++) {
if (i == pos)
led_set_pixel(i, 0, 40, 0); // verde fraco no cursor
else
led_set_pixel(i, 0, 0, 0);
}
// Limpa e acende o LED atual
led_clear();
led_set(pos, 255, 150, 0);
led_show();
// Calcula delay progressivo (aceleração -> desaceleração)
uint32_t delay = delay_start + ((delay_end - delay_start) * i) / total_steps;
if (step < total_steps / 3 && delay > 3)
delay--;
else if (step > (2 * total_steps) / 3)
delay++;
vTaskDelay(pdMS_TO_TICKS(delay));
}
// Flash final
for (int j = 0; j < 3; j++) {
led_clear();
led_show();
vTaskDelay(pdMS_TO_TICKS(80));
for (int k = 0; k < LED_COUNT; k++)
led_set(k, 255, 200, 50);
led_show();
vTaskDelay(pdMS_TO_TICKS(80));
}
}
// --- Animação idle (arco-íris lento) ---
void led_idle_animation(void)
{
if (!led_ready) return;
static uint16_t hue = 0;
for (int i = 0; i < LED_COUNT; i++) {
uint8_t r, g, b;
hsv2rgb((hue + i * 6) % 360, 100, 10, &r, &g, &b);
led_set(i, r, g, b);
}
led_show();
hue = (hue + 2) % 360;
}

115
main/led_effects.c Normal file
View File

@ -0,0 +1,115 @@
#include "led_effects.h"
#include "led_driver.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdlib.h>
#include <time.h>
void led_all_on(uint8_t r, uint8_t g, uint8_t b)
{
int n = led_get_count();
for (int i = 0; i < n; i++) {
led_set_pixel(i, r, g, b);
}
led_show();
}
void led_all_off(void)
{
led_clear();
led_show();
}
void led_idle_animation(void)
{
static int pos = 0;
int n = led_get_count();
if (n <= 0) return;
for (int i = 0; i < n; i++)
led_set_pixel(i, 0, 0, 0);
// pontinho azul
led_set_pixel(pos, 0, 0, 16);
led_show();
pos = (pos + 1) % n;
vTaskDelay(pdMS_TO_TICKS(50));
}
void led_jackpot_animation(void)
{
static int state = 0;
static int step = 0;
int n = led_get_count();
if (state == 0) {
// Piscas laranja
for (int i = 0; i < n; i++)
led_set_pixel(i, (step & 1) ? 32 : 0, (step & 1) ? 16 : 0, 0);
led_show();
step++;
if (step >= 12) { // 6 ON + 6 OFF
step = 0;
state = 1;
}
}
else if (state == 1) {
// Arco-íris progressivo
for (int i = 0; i < n; i++) {
int x = (i + step) % n;
uint8_t r = (x * 5) & 0xFF;
uint8_t g = ((x * 3) & 0xFF) >> 1;
uint8_t b = ((x * 7) & 0xFF) >> 2;
led_set_pixel(i, r, g, b);
}
led_show();
step++;
if (step >= n * 2) {
step = 0;
state = 0; // repete a animação
}
}
}
void led_clock_animation(void)
{
int n = led_get_count();
if (n <= 0) return;
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
int h = timeinfo.tm_hour % 12;
int m = timeinfo.tm_min;
int s = timeinfo.tm_sec;
int pos_h = (h * n) / 12;
int pos_m = (m * n) / 60;
int pos_s = (s * n) / 60;
led_clear();
// HORAS verde forte
led_set_pixel(pos_h, 10, 40, 10);
// MINUTOS azul
led_set_pixel(pos_m, 10, 10, 60);
// SEGUNDOS branco muito suave
led_set_pixel(pos_s, 5, 5, 5);
led_show();
vTaskDelay(pdMS_TO_TICKS(200));
}

View File

@ -1,65 +0,0 @@
#include "led_strip.h"
#include "esp_log.h"
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "led_strip";
struct led_strip_t {
uint32_t length;
uint8_t *buffer;
rmt_channel_handle_t channel;
uint32_t resolution;
};
typedef struct {
uint8_t g;
uint8_t r;
uint8_t b;
} __attribute__((packed)) grb_pixel_t;
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *config, led_strip_handle_t *ret_strip) {
if (!config || !ret_strip) return ESP_ERR_INVALID_ARG;
led_strip_handle_t strip = calloc(1, sizeof(struct led_strip_t));
if (!strip) return ESP_ERR_NO_MEM;
strip->length = config->strip_length;
strip->resolution = config->resolution_hz;
strip->channel = config->rmt_channel;
strip->buffer = calloc(strip->length, sizeof(grb_pixel_t));
if (!strip->buffer) {
free(strip);
return ESP_ERR_NO_MEM;
}
*ret_strip = strip;
ESP_LOGI(TAG, "Novo LED strip criado (%lu LEDs)", (unsigned long)strip->length);
return ESP_OK;
}
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue) {
if (!strip || index >= strip->length) return ESP_ERR_INVALID_ARG;
grb_pixel_t *pixels = (grb_pixel_t *)strip->buffer;
pixels[index].g = green;
pixels[index].r = red;
pixels[index].b = blue;
return ESP_OK;
}
esp_err_t led_strip_refresh(led_strip_handle_t strip) {
if (!strip) return ESP_ERR_INVALID_ARG;
// Envia os dados via RMT
rmt_transmit_config_t tx_conf = { .loop_count = 0 };
ESP_ERROR_CHECK(rmt_transmit(strip->channel, NULL, strip->buffer, strip->length * sizeof(grb_pixel_t), &tx_conf));
ESP_ERROR_CHECK(rmt_tx_wait_all_done(strip->channel, portMAX_DELAY));
return ESP_OK;
}
esp_err_t led_strip_clear(led_strip_handle_t strip) {
if (!strip) return ESP_ERR_INVALID_ARG;
memset(strip->buffer, 0, strip->length * sizeof(grb_pixel_t));
return led_strip_refresh(strip);
}

View File

@ -1,46 +0,0 @@
#pragma once
#include "esp_err.h"
#include "driver/rmt_tx.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Handle para a instância de LED strip
*/
typedef struct led_strip_t *led_strip_handle_t;
/**
* @brief Configuração para criar um LED strip
*/
typedef struct {
uint32_t strip_length; // número de LEDs
uint32_t resolution_hz; // resolução do RMT
rmt_channel_handle_t rmt_channel;// canal RMT a usar
} led_strip_config_t;
/**
* @brief Cria uma nova instância de LED strip
*/
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *config, led_strip_handle_t *ret_strip);
/**
* @brief Define a cor de um LED (index base 0)
*/
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue);
/**
* @brief Atualiza os LEDs com as cores definidas
*/
esp_err_t led_strip_refresh(led_strip_handle_t strip);
/**
* @brief Limpa todos os LEDs (define para preto)
*/
esp_err_t led_strip_clear(led_strip_handle_t strip);
#ifdef __cplusplus
}
#endif

View File

@ -1,124 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "led_strip_encoder.h"
static const char *TAG = "led_encoder";
typedef struct {
rmt_encoder_t base;
rmt_encoder_t *bytes_encoder;
rmt_encoder_t *copy_encoder;
int state;
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
rmt_encode_state_t state = RMT_ENCODING_RESET;
size_t encoded_symbols = 0;
switch (led_encoder->state) {
case 0: // send RGB data
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = 1; // switch to next state when current encoding session finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
// fall-through
case 1: // send reset code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
sizeof(led_encoder->reset_code), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session
state |= RMT_ENCODING_COMPLETE;
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
}
out:
*ret_state = state;
return encoded_symbols;
}
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_del_encoder(led_encoder->bytes_encoder);
rmt_del_encoder(led_encoder->copy_encoder);
free(led_encoder);
return ESP_OK;
}
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_reset(led_encoder->bytes_encoder);
rmt_encoder_reset(led_encoder->copy_encoder);
led_encoder->state = RMT_ENCODING_RESET;
return ESP_OK;
}
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
esp_err_t ret = ESP_OK;
rmt_led_strip_encoder_t *led_encoder = NULL;
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
led_encoder = rmt_alloc_encoder_mem(sizeof(rmt_led_strip_encoder_t));
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
led_encoder->base.encode = rmt_encode_led_strip;
led_encoder->base.del = rmt_del_led_strip_encoder;
led_encoder->base.reset = rmt_led_strip_encoder_reset;
// different led strip might have its own timing requirements, following parameter is for WS2812
rmt_bytes_encoder_config_t bytes_encoder_config = {
.bit0 = {
.level0 = 1,
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
.level1 = 0,
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
},
.bit1 = {
.level0 = 1,
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
.level1 = 0,
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
},
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
};
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
rmt_copy_encoder_config_t copy_encoder_config = {};
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us
led_encoder->reset_code = (rmt_symbol_word_t) {
.level0 = 0,
.duration0 = reset_ticks,
.level1 = 0,
.duration1 = reset_ticks,
};
*ret_encoder = &led_encoder->base;
return ESP_OK;
err:
if (led_encoder) {
if (led_encoder->bytes_encoder) {
rmt_del_encoder(led_encoder->bytes_encoder);
}
if (led_encoder->copy_encoder) {
rmt_del_encoder(led_encoder->copy_encoder);
}
free(led_encoder);
}
return ret;
}

View File

@ -1,28 +0,0 @@
#pragma once
#include "driver/rmt_tx.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuração do encoder de LED strip (usado pelo RMT)
*/
typedef struct {
uint32_t resolution; // resolução em Hz
} led_strip_encoder_config_t;
/**
* @brief Cria um novo encoder para enviar dados WS2812 via RMT.
*
* @param config Configuração do encoder (resolução)
* @param ret_encoder Ponteiro de retorno do handle do encoder
*
* @return ESP_OK se criado com sucesso
*/
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config,
rmt_encoder_handle_t *ret_encoder);
#ifdef __cplusplus
}
#endif

42
main/led_task.c Normal file
View File

@ -0,0 +1,42 @@
#include "led_task.h"
#include "led_effects.h"
#include "eeprom_animacao.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "LED_TASK";
void led_task(void *pv)
{
ESP_LOGI(TAG, "💡 LED task iniciada");
while (1) {
switch (animacao) {
case 0:
// modo idle (pontinho azul)
led_idle_animation();
break;
case 1:
// modo relógio
led_clock_animation();
break;
case 2:
// jackpot non-blocking
led_jackpot_animation();
break;
default:
// fallback seguro
led_idle_animation();
break;
}
// controla a velocidade de TODAS as animações
vTaskDelay(pdMS_TO_TICKS(40)); // ~25 FPS
}
}

View File

@ -1,6 +1,7 @@
#include <stdio.h>
#include <string.h>
#include "esp_rom_sys.h"
#include "esp_rom_sys.h"
#include "esp_task_wdt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@ -11,31 +12,38 @@
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_spiffs.h"
#include "esp_sntp.h"
#include "wifi_config_portal.h"
#include "mqtt_handler.h"
#include "led_driver.h"
#include "esp_random.h"
#include "eeprom_virtual.h"
#include "eeprom_tls.h"
#include "eeprom_animacao.h"
#include "wifi_config_portal.h"
#include "mqtt_handler.h"
#include "led_driver.h"
#include "led_effects.h"
#include "creditos.h"
#include "led_task.h"
static const char *TAG = "APP";
static uint32_t segundos = 0;
static bool wifi_ready = false;
typedef struct {
int total_creditos;
int total_saidas;
} contadores_t;
static const char *TAG = "APP";
static uint32_t segundos = 0;
static bool wifi_ready = false;
// === Contador simples de debug ===
// ============================
// Task contador simples
// ============================
void segundos_task(void *pv) {
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
segundos++;
// ESP_LOGI("TIMER", "⏱ %lu segundos", segundos);
}
}
@ -43,55 +51,48 @@ uint32_t get_segundos(void) {
return segundos;
}
// === Task de LEDs ===
void led_task(void *pv) {
bool tem_creditos = false;
while (1) {
if (wifi_ready && tem_creditos) {
uint16_t target = esp_random() % LED_COUNT;
led_spin_to(target, 2, 12, 80);
tem_creditos = false;
} else if (wifi_ready) {
led_idle_animation();
} else {
vTaskDelay(pdMS_TO_TICKS(100)); // espera Wi-Fi
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
// === Callback quando Wi-Fi e IP estão prontos ===
// ============================
// Callback Wi-Fi pronto
// ============================
static void on_wifi_connected(void) {
wifi_ready = true;
ESP_LOGI(TAG, "✅ Wi-Fi conectado, IP obtido — iniciando MQTT e LEDs...");
ESP_LOGI(TAG, "✅ Wi-Fi conectado — iniciando MQTT...");
mqtt_handler_start();
ESP_LOGI(TAG, "🕒 Inicializando SNTP...");
ESP_LOGI(TAG, "🕒 SNTP...");
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
ESP_LOGI(TAG, "💡 Inicializando LEDs...");
led_init();
ESP_LOGI(TAG, "💡 Inicializando driver LED...");
led_driver_init();
ESP_LOGI(TAG, "🎬 Iniciando tasks LED e Créditos...");
xTaskCreate(led_task, "led_task", 8192, NULL, 5, NULL);
xTaskCreate(creditos_task, "creditos_task", 8192, NULL, 5, NULL);
}
#include "esp_rom_sys.h"
// ============================
// Stack Overflow Handler
// ============================
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
esp_rom_printf("\n🧨 Stack overflow em %s!\n", pcTaskName);
// NÃO reinicia aqui — isso bloqueia o WiFi event task e causa WDT
// apenas marca para reiniciar depois
esp_task_wdt_reset();
esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart();
}
// === Função principal ===
// ============================
// MAIN
// ============================
void app_main(void) {
// ---------- EEPROM ----------
// -------- EEPROM virtual --------
eeprom_virtual_init();
contadores_t contadores = {100, 25};
@ -108,49 +109,32 @@ void app_main(void) {
ESP_LOGW("EEPROM", "⚠️ Falha ao ler dados!");
}
// ---------- NVS ----------
// -------- NVS normal --------
ESP_ERROR_CHECK(nvs_flash_init());
// ---------- LOG ----------
esp_log_level_set("wifi", ESP_LOG_WARN);
animacao_load();
ESP_LOGI("ANIM", "🎨 Animação carregada = %u", animacao);
// ---------- SPIFFS ----------
ESP_LOGI(TAG, "🔧 Montando SPIFFS...");
esp_vfs_spiffs_conf_t spiffs_conf = {
.base_path = "/spiffs",
.partition_label = "storage",
.max_files = 5,
.format_if_mount_failed = true
};
esp_err_t ret = esp_vfs_spiffs_register(&spiffs_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ Falha ao montar SPIFFS (%s)", esp_err_to_name(ret));
} else {
size_t total = 0, used = 0;
esp_spiffs_info(spiffs_conf.partition_label, &total, &used);
ESP_LOGI(TAG, "📦 SPIFFS OK: total=%d bytes, usado=%d", total, used);
}
// ---------- Wi-Fi ----------
// -------- Wi-Fi --------
wifi_config_t cfg;
bool have_creds = false;
if (esp_wifi_get_config(WIFI_IF_STA, &cfg) == ESP_OK) {
if (strlen((char *)cfg.sta.ssid) > 0) {
ESP_LOGI(TAG, "📂 Credenciais encontradas no NVS: SSID=%s", cfg.sta.ssid);
ESP_LOGI(TAG, "📂 Credenciais no NVS: SSID=%s", cfg.sta.ssid);
have_creds = true;
}
}
wifi_config_portal_init(on_wifi_connected, have_creds);
// ---------- Tasks ----------
xTaskCreate(segundos_task, "segundos_task", 12288, NULL, 5, NULL);
xTaskCreate(led_task, "led_task", 12288, NULL, 5, NULL);
// -------- Criar tasks iniciais --------
xTaskCreate(segundos_task, "segundos_task", 4096, NULL, 5, NULL);
// ---------- Loop principal (livre) ----------
// -------- Loop principal --------
while (1) {
if (wifi_ready) {
mqtt_handler_loop(); // mantém MQTT ativo
mqtt_handler_loop();
}
vTaskDelay(pdMS_TO_TICKS(100));
}

View File

@ -1,89 +1,137 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include "esp_log.h"
#include "mqtt_client.h"
#include "cJSON.h"
#include "eeprom_virtual.h"
#include "esp_random.h"
#include "esp_timer.h"
#include "led_driver.h"
#include "led_effects.h"
#include "creditos.h"
#include "eeprom_animacao.h"
static const char *TAG = "MQTT_CMD";
extern esp_mqtt_client_handle_t mqtt_client;
#define TOPIC_RESP "esp/esp32-002/resp"
// Estes tópicos vêm do mqtt_handler.c
extern char topic_resp[];
void mqtt_comandos_handle(cJSON *root) {
cJSON *id = cJSON_GetObjectItem(root, "id");
void mqtt_comandos_handle(cJSON *root)
{
cJSON *cmd = cJSON_GetObjectItem(root, "cmd");
if (!cJSON_IsNumber(id) || !cJSON_IsString(cmd)) {
ESP_LOGW(TAG, "⚠️ Comando inválido");
if (!cJSON_IsString(cmd)) {
ESP_LOGW(TAG, "⚠️ Comando inválido (sem 'cmd')");
return;
}
int req_id = id->valueint;
const char *comando = cmd->valuestring;
const char *c = cmd->valuestring;
ESP_LOGI(TAG, "📩 CMD: %s", c);
// -------- FS_LIST --------
if (strcmp(comando, "FS_LIST") == 0) {
DIR *dir = opendir("/spiffs");
if (!dir) {
char resp[128];
snprintf(resp, sizeof(resp), "{\"id\":%d,\"error\":\"fs_not_mounted\"}", req_id);
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
return;
}
char json[2048];
snprintf(json, sizeof(json), "{\"id\":%d,\"files\":[", req_id);
struct dirent *entry;
int first = 1;
while ((entry = readdir(dir)) != NULL) {
char path[512];
snprintf(path, sizeof(path), "/spiffs/%.255s", entry->d_name);
struct stat st;
if (stat(path, &st) == 0) {
// 🔧 Correção: buffer maior e limite de nome seguro
char file_json[320];
snprintf(file_json, sizeof(file_json),
"{\"name\":\"%.200s\",\"size\":%ld}", entry->d_name, (long)st.st_size);
if (!first) strlcat(json, ",", sizeof(json));
strlcat(json, file_json, sizeof(json));
first = 0;
}
}
closedir(dir);
strlcat(json, "]}", sizeof(json));
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, json, 0, 1, false);
// ======================================================
// LED ON / OFF
// ======================================================
if (strcmp(c, "LED_ON") == 0) {
led_all_on(50, 50, 50);
}
// -------- CERT_REQ --------
else if (strcmp(comando, "CERT_REQ") == 0) {
char json[128];
snprintf(json, sizeof(json), "{\"id\":%d,\"cmd\":\"CERT_REQ\"}", req_id);
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, json, 0, 1, false);
ESP_LOGI(TAG, "📤 Pedido de certificado enviado");
else if (strcmp(c, "LED_OFF") == 0) {
led_all_off();
}
// -------- CERT_SAVE --------
} else if (strcmp(comando, "CERT_SAVE") == 0) {
cJSON *cert = cJSON_GetObjectItem(root, "cert");
if (!cJSON_IsString(cert)) {
char resp[128];
snprintf(resp, sizeof(resp), "{\"id\":%d,\"error\":\"missing_cert\"}", req_id);
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
// ======================================================
// SPIN
// ======================================================
else if (strcmp(c, "SPIN") == 0) {
uint16_t p = esp_random() % led_get_count();
led_spin_to(p, 2, 12, 80);
}
// ======================================================
// JACKPOT
// ======================================================
else if (strcmp(c, "JACKPOT") == 0) {
led_jackpot_animation();
}
// ======================================================
// DAR CRÉDITO
// ======================================================
else if (strcmp(c, "CREDITO") == 0) {
creditos_dar();
}
// ======================================================
// ESTADO DO SISTEMA
// ======================================================
else if (strcmp(c, "STATUS") == 0) {
char resp[128];
snprintf(resp, sizeof(resp),
"{\"status\":\"ok\",\"heap\":%lu,\"anim\":%u}",
(unsigned long)esp_get_free_heap_size(),
animacao);
esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false);
}
// ======================================================
// SET ANIMAÇÃO (NVS)
// ======================================================
else if (strcmp(c, "SET_ANIM") == 0) {
cJSON *v = cJSON_GetObjectItem(root, "value");
if (!cJSON_IsNumber(v)) {
ESP_LOGW(TAG, "❌ SET_ANIM sem valor numérico");
return;
}
const char *cert_str = cert->valuestring;
uint8_t novo = v->valueint;
animacao_save(novo); // GRAVA NA NVS
animacao = novo; // atualiza RAM
// 🔧 Correção: usar versão binária (string + terminador)
if (eeprom_virtual_write_bin("cert", cert_str, strlen(cert_str) + 1) == ESP_OK) {
ESP_LOGI(TAG, "💾 Certificado gravado na EEPROM (%d bytes)", (int)strlen(cert_str));
char resp[128];
snprintf(resp, sizeof(resp), "{\"id\":%d,\"cmd\":\"CERT_SAVE\",\"ok\":true}", req_id);
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
} else {
ESP_LOGE(TAG, "❌ Falha ao gravar certificado");
}
ESP_LOGI(TAG, "🎨 Animação alterada para %u", novo);
char resp[64];
snprintf(resp, sizeof(resp), "{\"anim\":%u}", novo);
esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false);
}
// ======================================================
// REBOOT
// ======================================================
else if (strcmp(c, "REBOOT") == 0) {
ESP_LOGW(TAG, "♻️ Reinício seguro agendado...");
// callback C normal
void reboot_cb(void *arg) {
esp_restart();
}
// criar timer
esp_timer_handle_t reboot_timer;
const esp_timer_create_args_t args = {
.callback = reboot_cb,
.arg = NULL,
.name = "reboot_safe"
};
esp_timer_create(&args, &reboot_timer);
esp_timer_start_once(reboot_timer, 200000); // 200 ms
return; // evita continuar dentro do handler
}
// ======================================================
// COMANDO DESCONHECIDO
// ======================================================
else {
ESP_LOGW(TAG, "⚠️ Comando desconhecido: %s", c);
char resp[128];
snprintf(resp, sizeof(resp),
"{\"error\":\"unknown_cmd\",\"cmd\":\"%s\"}", c);
esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false);
}
}

View File

@ -16,7 +16,7 @@
static const char *TAG = "MQTT";
// -------- CONFIG --------
#define BROKER_HOST "mtqq.xupas.mywire.org"
#define BROKER_HOST "mqtt.xupas.mywire.org"
#define BROKER_PORT_TLS 8883
#define BROKER_PORT_TCP 1883
#define MQTT_USER "xupa"
@ -26,10 +26,10 @@ esp_mqtt_client_handle_t mqtt_client = NULL;
static esp_timer_handle_t mqtt_watchdog = NULL;
static bool mqtt_connected = false;
static char topic_status[64];
static char topic_cmd[64];
static char topic_resp[64];
static char topic_lwt[64];
char topic_status[64];
char topic_cmd[64];
char topic_resp[64];
char topic_lwt[64];
// ======================================================
// HEARTBEAT / STATUS
@ -67,8 +67,11 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
mqtt_connected = true;
led_set(0, 0, 50, 0);
// LED verde no pixel 0
led_set_pixel(0, 0, 50, 0);
led_show();
ESP_LOGI(TAG, "✅ MQTT conectado");
esp_mqtt_client_publish(mqtt_client, topic_status, "online", 0, 1, true);
esp_mqtt_client_subscribe(mqtt_client, topic_cmd, 1);
@ -79,26 +82,46 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
case MQTT_EVENT_DISCONNECTED:
mqtt_connected = false;
led_set(0, 50, 0, 0);
// LED vermelho no pixel 0
led_set_pixel(0, 50, 0, 0);
led_show();
ESP_LOGW(TAG, "⚠️ MQTT desconectado");
if (mqtt_watchdog) esp_timer_start_periodic(mqtt_watchdog, 120000000);
break;
case MQTT_EVENT_DATA: {
ESP_LOGI(TAG, "📩 [%.*s] %.*s",
event->topic_len, event->topic,
event->data_len, event->data);
// Copia o payload para um buffer legível
char json_clean[256];
int len = event->data_len;
if (len >= sizeof(json_clean)) len = sizeof(json_clean) - 1;
memcpy(json_clean, event->data, len);
json_clean[len] = 0; // NULL terminate
// Remove quebras de linha
for (int i = 0; json_clean[i]; i++) {
if (json_clean[i] == '\r' || json_clean[i] == '\n')
json_clean[i] = ' ';
}
// Mostrar tópico + JSON limpo
ESP_LOGI(TAG, "📩 [%.*s] %s",
event->topic_len, event->topic,
json_clean);
// JSON parse
cJSON *root = cJSON_Parse(json_clean);
if (root) {
mqtt_comandos_handle(root);
cJSON_Delete(root);
} else {
ESP_LOGE(TAG, "❌ JSON inválido");
}
break;
}
cJSON *root = cJSON_ParseWithLength(event->data, event->data_len);
if (root) {
mqtt_comandos_handle(root);
cJSON_Delete(root);
} else {
ESP_LOGE(TAG, "❌ JSON inválido");
}
break;
}
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "❌ Erro MQTT");
@ -109,6 +132,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
}
}
// ======================================================
// HEARTBEAT TASK
// ======================================================
@ -143,65 +167,40 @@ void mqtt_handler_start(void) {
mqtt_cfg.credentials.client_id = device_id;
mqtt_cfg.credentials.username = MQTT_USER;
mqtt_cfg.credentials.authentication.password = MQTT_PASS;
// ======================================================
// MQTT TLS — usa SEMPRE o certificado embutido
// ======================================================
mqtt_cfg.broker.address.hostname = BROKER_HOST;
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
// ======================================================
// TENTA CARREGAR CERTIFICADO E USAR TLS
// ======================================================
bool tls_ok = false;
static char cert_buf[4096];
size_t cert_len = sizeof(cert_buf) - 1;
// Certificado raiz (ISRG Root X1)
mqtt_cfg.broker.verification.certificate = ca_cert_pem;
ESP_LOGI(TAG, "🔐 TLS ativo (cert embutido, EEPROM ignorada)");
if (eeprom_virtual_read_bin("cert", cert_buf, &cert_len) == ESP_OK && cert_len > 0) {
cert_buf[cert_len] = '\0';
mqtt_cfg.broker.verification.certificate = strdup(cert_buf);
mqtt_cfg.broker.address.hostname = BROKER_HOST;
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
tls_ok = true;
ESP_LOGI(TAG, "🔐 Certificado TLS carregado da EEPROM (%d bytes)", (int)cert_len);
} else {
ESP_LOGW(TAG, "⚠️ Certificado TLS não encontrado — vai tentar padrão embutido...");
mqtt_cfg.broker.verification.certificate = (const char *)ca_cert_pem;
mqtt_cfg.broker.address.hostname = BROKER_HOST;
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
tls_ok = true;
}
// ======================================================
// TESTE: se TLS falhar, cai para TCP (sem SSL)
// ======================================================
if (!tls_ok) {
ESP_LOGW(TAG, "⚠️ TLS indisponível — a usar MQTT sem SSL.");
mqtt_cfg.broker.address.hostname = BROKER_HOST;
mqtt_cfg.broker.address.port = BROKER_PORT_TCP;
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
}
// -------- LWT --------
// -------- LWT --------
mqtt_cfg.session.last_will.topic = topic_lwt;
mqtt_cfg.session.last_will.msg = "offline";
mqtt_cfg.session.last_will.qos = 1;
mqtt_cfg.session.last_will.retain = true;
// ======================================================
// INICIALIZAÇÃO DO CLIENTE
// ======================================================
// ======================================================
// INICIALIZAÇÃO DO CLIENTE MQTT (TLS OBRIGATÓRIO)
// ======================================================
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
if (mqtt_client == NULL) {
ESP_LOGE(TAG, "❌ Falha a inicializar MQTT — a tentar fallback TCP...");
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
mqtt_cfg.broker.address.port = BROKER_PORT_TCP;
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
}
ESP_LOGE(TAG, "❌ Falha a inicializar MQTT (TLS). Abortado.");
return; // Nem vale a pena continuar, sem MQTT não há vida
}
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
ESP_LOGI(TAG, "🚀 MQTT inicializado em %s:%lu (%s)",
ESP_LOGI(TAG, "🚀 MQTT inicializado em %s:%lu (TLS)",
mqtt_cfg.broker.address.hostname,
(unsigned long)mqtt_cfg.broker.address.port,
mqtt_cfg.broker.address.transport == MQTT_TRANSPORT_OVER_SSL ? "TLS" : "TCP");
(unsigned long)mqtt_cfg.broker.address.port);
// -------- WATCHDOG MQTT --------
const esp_timer_create_args_t wd_args = {

View File

@ -317,14 +317,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
CONFIG_ESPTOOLPY_FLASHFREQ="40m"
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
CONFIG_ESPTOOLPY_BEFORE_RESET=y
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
@ -338,12 +338,12 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
#
# Partition Table
#
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
CONFIG_PARTITION_TABLE_SINGLE_APP=y
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
CONFIG_PARTITION_TABLE_CUSTOM=y
# CONFIG_PARTITION_TABLE_CUSTOM is not set
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@
"board/esp32-wrover-kit-3.3v.cfg"
],
"idf.port": "COM3",
"idf.flashBaudRate": 921600,
"idf.monitorBaudRate": 115200,
"idf.flashBaudRate": "921600",
"idf.monitorBaudRate": "115200",
"cmake.sourceDirectory": "${workspaceFolder}"
}
}