LED_shit/main/mqtt_handler.c
2025-11-24 23:53:57 +00:00

216 lines
7.0 KiB
C

#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 "certs.h"
#include "mqtt_comandos.h"
#include "led_driver.h"
#include "esp_timer.h"
#include "esp_event.h"
#include "eeprom_virtual.h"
static const char *TAG = "MQTT";
// -------- CONFIG --------
#define BROKER_HOST "mqtt.xupas.mywire.org"
#define BROKER_PORT_TLS 8883
#define BROKER_PORT_TCP 1883
#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 bool mqtt_connected = false;
char topic_status[64];
char topic_cmd[64];
char topic_resp[64];
char topic_lwt[64];
// ======================================================
// HEARTBEAT / STATUS
// ======================================================
static void send_status(void) {
if (!mqtt_client || !mqtt_connected) return;
char buf[160];
snprintf(buf, sizeof(buf),
"{\"uptime\":%lu,\"heap\":%lu}",
(unsigned long)(esp_log_timestamp() / 1000),
(unsigned long)esp_get_free_heap_size());
esp_mqtt_client_publish(mqtt_client, topic_status, buf, 0, 1, false);
ESP_LOGI(TAG, "📤 STATUS -> %s", buf);
}
// ======================================================
// WATCHDOG CALLBACK
// ======================================================
static void mqtt_watchdog_cb(void *arg) {
if (!mqtt_connected) {
ESP_LOGE(TAG, "⏱️ 2 minutos sem MQTT, reiniciando ESP...");
esp_restart();
}
}
// ======================================================
// EVENT HANDLER
// ======================================================
static void mqtt_event_handler(void *handler_args, 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);
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;
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;
}
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "❌ Erro MQTT");
break;
default:
break;
}
}
// ======================================================
// 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);
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);
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;
// Certificado raiz (ISRG Root X1)
mqtt_cfg.broker.verification.certificate = ca_cert_pem;
ESP_LOGI(TAG, "🔐 TLS ativo (cert embutido, EEPROM ignorada)");
// -------- 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;
// ======================================================
// 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
}
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);
}