Fix MQTT heartbeat + clock MQTT-only + payload cleanup
This commit is contained in:
parent
a26b421d75
commit
e8618ab6aa
46
main/certs.h
46
main/certs.h
@ -2,33 +2,21 @@
|
||||
|
||||
static const char ca_cert_pem[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"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\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
@ -1,23 +1,3 @@
|
||||
#pragma once
|
||||
#include "mqtt_client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -87,20 +87,12 @@ void led_clock_animation(void)
|
||||
struct tm t;
|
||||
localtime_r(&now, &t);
|
||||
|
||||
int h = t.tm_hour;
|
||||
int m = t.tm_min;
|
||||
int s = t.tm_sec;
|
||||
|
||||
// Mostrar HHMM no display (14-seg)
|
||||
led_clear();
|
||||
|
||||
display_set_time_top(h, m);
|
||||
|
||||
// LED dos segundos em azul
|
||||
led_clear(); // APAGA TUDO
|
||||
|
||||
int pos = s % LED_COUNT; // 0..59 ou 0..63
|
||||
|
||||
led_set_pixel(pos, 0, 0, 60); // azul forte
|
||||
int pos = s % LED_COUNT;
|
||||
led_set_pixel(pos, 0, 0, 60);
|
||||
led_show();
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
237
main/main.c
237
main/main.c
@ -1,21 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_sntp.h"
|
||||
|
||||
#include "eeprom_virtual.h"
|
||||
#include "eeprom_tls.h"
|
||||
#include "eeprom_animacao.h"
|
||||
|
||||
#include "wifi_config_portal.h"
|
||||
@ -23,245 +18,102 @@
|
||||
|
||||
#include "led_driver.h"
|
||||
#include "led_effects.h"
|
||||
|
||||
#include "creditos.h"
|
||||
#include "led_task.h"
|
||||
#include "creditos.h"
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "i2c_helper.h"
|
||||
#include "display.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "net_weather.h"
|
||||
#include "buzzer.h"
|
||||
|
||||
|
||||
|
||||
esp_err_t i2c_init(void);
|
||||
esp_err_t display_init(void);
|
||||
|
||||
|
||||
|
||||
bool modo_bloqueado = false; // definição oficial
|
||||
|
||||
#define SDA_PIN 21
|
||||
#define SCL_PIN 22
|
||||
#define I2C_PORT I2C_NUM_0
|
||||
|
||||
// ======================================================
|
||||
static const char *TAG = "APP";
|
||||
static uint32_t segundos = 0;
|
||||
void i2c_scan(void);
|
||||
volatile bool hora_vem_do_mqtt = false;
|
||||
bool modo_bloqueado = false;
|
||||
static bool wifi_ready = false;
|
||||
|
||||
// ======================================================
|
||||
// CONTADORES
|
||||
// ======================================================
|
||||
typedef struct {
|
||||
int total_creditos;
|
||||
int total_saidas;
|
||||
} contadores_t;
|
||||
|
||||
// ======================================================
|
||||
// SEGUNDOS (simples, local)
|
||||
// ======================================================
|
||||
static uint32_t segundos = 0;
|
||||
|
||||
static void weather_task(void *arg)
|
||||
static void segundos_task(void *pv)
|
||||
{
|
||||
float temp;
|
||||
|
||||
ESP_LOGI("WEATHER", "🌡️ weather_task arrancou");
|
||||
|
||||
// 🔥 leitura IMEDIATA (antes do delay)
|
||||
ESP_LOGI("WEATHER", "🌐 a pedir temperatura (primeira vez)");
|
||||
|
||||
if (net_weather_update(&temp)) {
|
||||
ESP_LOGI("WEATHER", "🌡️ temperatura recebida: %.1f", temp);
|
||||
display_temperature_bottom(temp);
|
||||
} else {
|
||||
ESP_LOGE("WEATHER", "❌ net_weather_update falhou");
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(15 * 60 * 1000));
|
||||
|
||||
ESP_LOGI("WEATHER", "🌐 a pedir temperatura (loop)");
|
||||
|
||||
if (net_weather_update(&temp)) {
|
||||
ESP_LOGI("WEATHER", "🌡️ temperatura recebida: %.1f", temp);
|
||||
display_temperature_bottom(temp);
|
||||
} else {
|
||||
ESP_LOGE("WEATHER", "❌ net_weather_update falhou");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================
|
||||
// Task contador simples
|
||||
// ============================
|
||||
void segundos_task(void *pv) {
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
segundos++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_segundos(void) {
|
||||
uint32_t get_segundos(void)
|
||||
{
|
||||
return segundos;
|
||||
}
|
||||
|
||||
|
||||
// ============================
|
||||
// Callback Wi-Fi pronto
|
||||
// ============================
|
||||
static void on_wifi_connected(void) {
|
||||
// ======================================================
|
||||
// WIFI OK CALLBACK
|
||||
// ======================================================
|
||||
static void on_wifi_connected(void)
|
||||
{
|
||||
wifi_ready = true;
|
||||
|
||||
ESP_LOGI(TAG, "✅ Wi-Fi conectado — iniciando MQTT...");
|
||||
mqtt_handler_start();
|
||||
|
||||
ESP_LOGI(TAG, "🕒 SNTP...");
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, "pool.ntp.org");
|
||||
esp_sntp_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);
|
||||
|
||||
// 🌡️ TASK DO TEMPO (AQUI!)
|
||||
ESP_LOGI(TAG, "🌡️ Iniciando task de temperatura (Open-Meteo)");
|
||||
xTaskCreate(weather_task, "weather_task", 4096, NULL, 4, NULL);
|
||||
// xTaskCreate(led_task, "led_task", 8192, NULL, 5, NULL);
|
||||
// xTaskCreate(creditos_task, "creditos_task", 8192, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
|
||||
// ============================
|
||||
// Stack Overflow Handler
|
||||
// ============================
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
||||
{
|
||||
esp_rom_printf("\n🧨 Stack overflow em %s!\n", pcTaskName);
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
|
||||
// Configuração básica do I2C
|
||||
i2c_config_t cfg = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = SDA_PIN,
|
||||
.scl_io_num = SCL_PIN,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = 100000
|
||||
};
|
||||
|
||||
void ht16_init()
|
||||
{
|
||||
uint8_t cmd1 = 0x21; // liga oscilador
|
||||
i2c_master_write_to_device(I2C_PORT, 0x70, &cmd1, 1, 10 / portTICK_PERIOD_MS);
|
||||
|
||||
uint8_t cmd2 = 0x81; // display ON, sem piscar
|
||||
i2c_master_write_to_device(I2C_PORT, 0x70, &cmd2, 1, 10 / portTICK_PERIOD_MS);
|
||||
|
||||
uint8_t cmd3 = 0xEF; // brilho máximo
|
||||
i2c_master_write_to_device(I2C_PORT, 0x70, &cmd3, 1, 10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ht16_test()
|
||||
{
|
||||
uint8_t buf[17] = {0};
|
||||
buf[0] = 0x00; // endereço inicial
|
||||
|
||||
buf[7] = 0b0111111; // acende apenas o dígito 0
|
||||
// (que mostra um "0" bonitinho)
|
||||
|
||||
|
||||
// Os outros dígitos ficam a 0 = apagados
|
||||
|
||||
i2c_master_write_to_device(I2C_PORT, 0x70, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
//************************************************************** */
|
||||
void i2c_scan(void)
|
||||
{
|
||||
printf("\n--- A fazer scan ao I2C ---\n");
|
||||
|
||||
int found = 0;
|
||||
|
||||
for (uint8_t addr = 1; addr < 0x7F; addr++) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t r = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(50));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (r == ESP_OK) {
|
||||
printf("✅ I2C encontrado: 0x%02X\n", addr);
|
||||
found++;
|
||||
} else if (r == ESP_ERR_TIMEOUT) {
|
||||
printf("⏱️ TIMEOUT no addr 0x%02X (bus preso?)\n", addr);
|
||||
break; // não vale a pena continuar
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("❌ Nenhum dispositivo I2C encontrado\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================
|
||||
// ======================================================
|
||||
// MAIN
|
||||
// ============================
|
||||
void app_main(void) {
|
||||
|
||||
// ======================================================
|
||||
void app_main(void)
|
||||
{
|
||||
// -------- EEPROM virtual --------
|
||||
eeprom_virtual_init();
|
||||
|
||||
contadores_t contadores = {100, 25};
|
||||
eeprom_virtual_write_bin("contadores", &contadores, sizeof(contadores));
|
||||
ESP_LOGI("EEPROM", "💾 Gravado: total_creditos=%d, total_saidas=%d",
|
||||
contadores.total_creditos, contadores.total_saidas);
|
||||
|
||||
contadores_t lidos = {0};
|
||||
size_t len = sizeof(lidos);
|
||||
if (eeprom_virtual_read_bin("contadores", &lidos, &len) == ESP_OK) {
|
||||
ESP_LOGI("EEPROM", "📖 Lido: total_creditos=%d, total_saidas=%d",
|
||||
ESP_LOGI("EEPROM", "📖 Lido: creditos=%d saidas=%d",
|
||||
lidos.total_creditos, lidos.total_saidas);
|
||||
} else {
|
||||
ESP_LOGW("EEPROM", "⚠️ Falha ao ler dados!");
|
||||
}
|
||||
|
||||
// -------- NVS normal --------
|
||||
// -------- NVS --------
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
|
||||
// -------- Animação --------
|
||||
animacao_load();
|
||||
ESP_LOGI("ANIM", "🎨 Animação carregada = %u", animacao);
|
||||
|
||||
|
||||
|
||||
// 1) Inicializa I2C (não aborta)
|
||||
esp_err_t ei = i2c_init();
|
||||
if (ei != ESP_OK) {
|
||||
printf("i2c_init falhou: %s\n", esp_err_to_name(ei));
|
||||
// -------- I2C --------
|
||||
if (i2c_init() == ESP_OK) {
|
||||
// i2c_scan();
|
||||
}
|
||||
|
||||
// 2) Scan I2C (ver o que existe no barramento)
|
||||
i2c_scan();
|
||||
|
||||
// 3) Inicializa displays SEM abortar (para não rebootar em loop)
|
||||
esp_err_t ed = display_init();
|
||||
printf("display_init = %s\n", esp_err_to_name(ed));
|
||||
|
||||
// 4) Teste simples no display (se existir)
|
||||
ESP_LOGI(TAG, "display_init = %s", esp_err_to_name(ed));
|
||||
display_text_top("INIT");
|
||||
|
||||
// 🔔 6) buzzer (AQUI!)
|
||||
// -------- Buzzer --------
|
||||
buzzer_init();
|
||||
buzzer_beep(500);
|
||||
|
||||
buzzer_beep(300);
|
||||
|
||||
// -------- Wi-Fi --------
|
||||
wifi_config_t cfg;
|
||||
@ -269,21 +121,22 @@ void app_main(void) {
|
||||
|
||||
if (esp_wifi_get_config(WIFI_IF_STA, &cfg) == ESP_OK) {
|
||||
if (strlen((char *)cfg.sta.ssid) > 0) {
|
||||
ESP_LOGI(TAG, "📂 Credenciais no NVS: SSID=%s", cfg.sta.ssid);
|
||||
ESP_LOGI(TAG, "📂 Credenciais encontradas: %s", cfg.sta.ssid);
|
||||
have_creds = true;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_config_portal_init(on_wifi_connected, have_creds);
|
||||
|
||||
// -------- Criar tasks iniciais --------
|
||||
// -------- Tasks base --------
|
||||
xTaskCreate(segundos_task, "segundos_task", 4096, NULL, 5, NULL);
|
||||
|
||||
// -------- Loop principal --------
|
||||
// Tudo é event-driven (MQTT, UI, etc.)
|
||||
while (1) {
|
||||
if (wifi_ready) {
|
||||
mqtt_handler_loop();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,215 +1,272 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_netif.h"
|
||||
#include "cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include "certs.h"
|
||||
#include "mqtt_comandos.h"
|
||||
#include "led_driver.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "eeprom_virtual.h"
|
||||
#include "ui.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_mac.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "mqtt_handler.h"
|
||||
#include "display.h"
|
||||
|
||||
// ======================================================
|
||||
static const char *TAG = "MQTT";
|
||||
|
||||
// -------- CONFIG --------
|
||||
// MQTT CONFIG
|
||||
#define BROKER_HOST "mqtt.xupas.mywire.org"
|
||||
#define BROKER_PORT_TLS 8883
|
||||
#define BROKER_PORT_TCP 1883
|
||||
#define BROKER_PORT 8883
|
||||
#define MQTT_USER "xupa"
|
||||
#define MQTT_PASS "xupa"
|
||||
|
||||
esp_mqtt_client_handle_t mqtt_client = NULL;
|
||||
static esp_timer_handle_t mqtt_watchdog = NULL;
|
||||
// ======================================================
|
||||
static esp_mqtt_client_handle_t mqtt_client = NULL;
|
||||
static bool mqtt_connected = false;
|
||||
|
||||
char topic_status[64];
|
||||
char topic_cmd[64];
|
||||
char topic_resp[64];
|
||||
char topic_lwt[64];
|
||||
static char topic_cmd[64];
|
||||
static char topic_status[64];
|
||||
|
||||
// TASK HANDLES
|
||||
static TaskHandle_t status_task_handle = NULL;
|
||||
static TaskHandle_t mqtt_clock_handle = NULL;
|
||||
|
||||
// RELÓGIO MQTT
|
||||
static int clock_h = 0;
|
||||
static int clock_m = 0;
|
||||
static bool clock_valid = false;
|
||||
|
||||
// ======================================================
|
||||
// HEARTBEAT / STATUS
|
||||
// HEARTBEAT TASK
|
||||
// ======================================================
|
||||
static void send_status(void) {
|
||||
if (!mqtt_client || !mqtt_connected) return;
|
||||
static void status_task(void *pv)
|
||||
{
|
||||
while (1) {
|
||||
|
||||
char buf[160];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{\"uptime\":%lu,\"heap\":%lu}",
|
||||
(unsigned long)(esp_log_timestamp() / 1000),
|
||||
(unsigned long)esp_get_free_heap_size());
|
||||
if (mqtt_connected) {
|
||||
|
||||
esp_mqtt_client_publish(mqtt_client, topic_status, buf, 0, 1, false);
|
||||
ESP_LOGI(TAG, "📤 STATUS -> %s", buf);
|
||||
}
|
||||
char msg[128];
|
||||
|
||||
// ======================================================
|
||||
// WATCHDOG CALLBACK
|
||||
// ======================================================
|
||||
static void mqtt_watchdog_cb(void *arg) {
|
||||
if (!mqtt_connected) {
|
||||
ESP_LOGE(TAG, "⏱️ 2 minutos sem MQTT, reiniciando ESP...");
|
||||
esp_restart();
|
||||
snprintf(msg, sizeof(msg),
|
||||
"{\"status\":\"online\"}");
|
||||
|
||||
esp_mqtt_client_publish(mqtt_client,
|
||||
topic_status,
|
||||
msg,
|
||||
0,
|
||||
1,
|
||||
0);
|
||||
|
||||
ESP_LOGI(TAG, "💓 Heartbeat enviado");
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// EVENT HANDLER
|
||||
// CLOCK TASK (só MQTT controla)
|
||||
// ======================================================
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||
int32_t event_id, void *event_data) {
|
||||
static void mqtt_clock_task(void *pv)
|
||||
{
|
||||
while (1) {
|
||||
|
||||
if (clock_valid) {
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(60000)); // 1 minuto
|
||||
|
||||
clock_m++;
|
||||
|
||||
if (clock_m >= 60) {
|
||||
clock_m = 0;
|
||||
clock_h++;
|
||||
|
||||
if (clock_h >= 24)
|
||||
clock_h = 0;
|
||||
}
|
||||
|
||||
display_set_time_top(clock_h, clock_m);
|
||||
|
||||
ESP_LOGI(TAG, "🕒 Hora interna: %02d:%02d",
|
||||
clock_h, clock_m);
|
||||
}
|
||||
else {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// MQTT EVENT HANDLER
|
||||
// ======================================================
|
||||
static void mqtt_event_handler(void *arg,
|
||||
esp_event_base_t base,
|
||||
int32_t event_id,
|
||||
void *event_data)
|
||||
{
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
|
||||
switch (event->event_id) {
|
||||
|
||||
// ------------------------------
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
|
||||
mqtt_connected = true;
|
||||
|
||||
// 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, 0);
|
||||
// esp_mqtt_client_publish(mqtt_client, topic_status, "online", 0, 1, true);
|
||||
esp_mqtt_client_subscribe(mqtt_client, topic_cmd, 1);
|
||||
send_status();
|
||||
|
||||
if (mqtt_watchdog) esp_timer_stop(mqtt_watchdog);
|
||||
// ONLINE retain
|
||||
esp_mqtt_client_publish(mqtt_client,
|
||||
topic_status,
|
||||
"{\"status\":\"online\"}",
|
||||
0,
|
||||
1,
|
||||
1);
|
||||
|
||||
esp_mqtt_client_subscribe(mqtt_client, topic_cmd, 1);
|
||||
esp_mqtt_client_subscribe(mqtt_client, "time/now", 1);
|
||||
|
||||
// heartbeat (1x)
|
||||
if (status_task_handle == NULL) {
|
||||
xTaskCreate(status_task,
|
||||
"status_task",
|
||||
4096,
|
||||
NULL,
|
||||
5,
|
||||
&status_task_handle);
|
||||
}
|
||||
|
||||
// clock (1x)
|
||||
if (mqtt_clock_handle == NULL) {
|
||||
xTaskCreate(mqtt_clock_task,
|
||||
"mqtt_clock",
|
||||
4096,
|
||||
NULL,
|
||||
5,
|
||||
&mqtt_clock_handle);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// ------------------------------
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
mqtt_connected = false;
|
||||
|
||||
// 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: {
|
||||
// Copia o payload para um buffer legível
|
||||
char json_clean[256];
|
||||
int len = event->data_len;
|
||||
// ------------------------------
|
||||
case MQTT_EVENT_DATA:
|
||||
{
|
||||
esp_mqtt_event_handle_t e = event;
|
||||
|
||||
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] = ' ';
|
||||
// 🔥 Ignorar fragmentos parciais
|
||||
if (e->current_data_offset != 0) {
|
||||
ESP_LOGW(TAG, "Fragmento ignorado");
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar tópico + JSON limpo
|
||||
ESP_LOGI(TAG, "📩 [%.*s] %s",
|
||||
event->topic_len, event->topic,
|
||||
json_clean);
|
||||
// 🔥 Ignorar payload vazio
|
||||
if (e->data_len == 0) {
|
||||
ESP_LOGW(TAG, "Payload vazio ignorado");
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[64];
|
||||
char payload[256];
|
||||
|
||||
int tlen = e->topic_len;
|
||||
int plen = e->data_len;
|
||||
|
||||
if (tlen >= sizeof(topic)) tlen = sizeof(topic) - 1;
|
||||
if (plen >= sizeof(payload)) plen = sizeof(payload) - 1;
|
||||
|
||||
memcpy(topic, e->topic, tlen);
|
||||
topic[tlen] = 0;
|
||||
|
||||
memcpy(payload, e->data, plen);
|
||||
payload[plen] = 0;
|
||||
|
||||
ESP_LOGI(TAG, "📩 [%s] %s", topic, payload);
|
||||
|
||||
// -------- TIME --------
|
||||
if (strcmp(topic, "time/now") == 0) {
|
||||
|
||||
cJSON *root = cJSON_Parse(payload);
|
||||
if (!root) return;
|
||||
|
||||
cJSON *h = cJSON_GetObjectItem(root, "h");
|
||||
cJSON *m = cJSON_GetObjectItem(root, "m");
|
||||
|
||||
if (cJSON_IsNumber(h) && cJSON_IsNumber(m)) {
|
||||
|
||||
clock_h = h->valueint;
|
||||
clock_m = m->valueint;
|
||||
clock_valid = true;
|
||||
|
||||
display_set_time_top(clock_h, clock_m);
|
||||
|
||||
ESP_LOGI(TAG, "⏰ Hora MQTT: %02d:%02d",
|
||||
clock_h, clock_m);
|
||||
}
|
||||
|
||||
// JSON parse
|
||||
cJSON *root = cJSON_Parse(json_clean);
|
||||
if (root) {
|
||||
mqtt_comandos_handle(root);
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "❌ JSON inválido");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGE(TAG, "❌ Erro MQTT");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// START MQTT
|
||||
// ======================================================
|
||||
void mqtt_handler_start(void)
|
||||
{
|
||||
esp_mqtt_client_config_t cfg = {0};
|
||||
|
||||
// ======================================================
|
||||
// HEARTBEAT TASK
|
||||
// ======================================================
|
||||
static void mqtt_heartbeat_task(void *arg) {
|
||||
while (1) {
|
||||
send_status();
|
||||
vTaskDelay(pdMS_TO_TICKS(30000)); // envia status a cada 30s
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// START / CONFIG
|
||||
// ======================================================
|
||||
void mqtt_handler_start(void) {
|
||||
esp_mqtt_client_config_t mqtt_cfg = {0};
|
||||
|
||||
// -------- IDENTIFICADOR AUTOMÁTICO --------
|
||||
char device_id[16];
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
snprintf(device_id, sizeof(device_id), "esp_%02X%02X%02X", mac[3], mac[4], mac[5]);
|
||||
|
||||
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (netif) esp_netif_set_hostname(netif, device_id);
|
||||
ESP_LOGI(TAG, "🆔 ID do dispositivo: %s", device_id);
|
||||
char client_id[16];
|
||||
snprintf(client_id, sizeof(client_id),
|
||||
"esp_%02X%02X%02X",
|
||||
mac[3], mac[4], mac[5]);
|
||||
|
||||
snprintf(topic_status, sizeof(topic_status), "esp/%s/status", device_id);
|
||||
snprintf(topic_cmd, sizeof(topic_cmd), "esp/%s/cmd", device_id);
|
||||
snprintf(topic_resp, sizeof(topic_resp), "esp/%s/resp", device_id);
|
||||
snprintf(topic_lwt, sizeof(topic_lwt), "esp/%s/lwt", device_id);
|
||||
snprintf(topic_cmd, sizeof(topic_cmd),
|
||||
"esp/%s/cmd", client_id);
|
||||
|
||||
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;
|
||||
snprintf(topic_status, sizeof(topic_status),
|
||||
"esp/%s/status", client_id);
|
||||
|
||||
// Certificado raiz (ISRG Root X1)
|
||||
mqtt_cfg.broker.verification.certificate = ca_cert_pem;
|
||||
ESP_LOGI(TAG, "🔐 TLS ativo (cert embutido, EEPROM ignorada)");
|
||||
cfg.broker.address.hostname = BROKER_HOST;
|
||||
cfg.broker.address.port = BROKER_PORT;
|
||||
cfg.credentials.username = MQTT_USER;
|
||||
cfg.credentials.authentication.password = MQTT_PASS;
|
||||
cfg.credentials.client_id = client_id;
|
||||
cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
|
||||
cfg.broker.verification.certificate = ca_cert_pem;
|
||||
|
||||
// -------- 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 = false;
|
||||
// Last will
|
||||
cfg.session.last_will.topic = topic_status;
|
||||
cfg.session.last_will.msg = "{\"status\":\"offline\"}";
|
||||
cfg.session.last_will.qos = 1;
|
||||
cfg.session.last_will.retain = 1;
|
||||
|
||||
// ======================================================
|
||||
// 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 (TLS). Abortado.");
|
||||
return; // Nem vale a pena continuar, sem MQTT não há vida
|
||||
}
|
||||
mqtt_client = esp_mqtt_client_init(&cfg);
|
||||
esp_mqtt_client_register_event(
|
||||
mqtt_client, ESP_EVENT_ANY_ID,
|
||||
mqtt_event_handler, NULL);
|
||||
|
||||
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 (TLS)",
|
||||
mqtt_cfg.broker.address.hostname,
|
||||
(unsigned long)mqtt_cfg.broker.address.port);
|
||||
|
||||
|
||||
|
||||
// -------- WATCHDOG MQTT --------
|
||||
const esp_timer_create_args_t wd_args = {
|
||||
.callback = &mqtt_watchdog_cb,
|
||||
.name = "mqtt_watchdog"
|
||||
};
|
||||
esp_timer_create(&wd_args, &mqtt_watchdog);
|
||||
|
||||
// -------- HEARTBEAT TASK --------
|
||||
xTaskCreate(mqtt_heartbeat_task, "mqtt_heartbeat", 12288, NULL, 5, NULL);
|
||||
ESP_LOGI(TAG, "🚀 MQTT iniciado como %s", client_id);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user