LED_shit/main/mqtt_handler.c
2025-11-04 21:33:28 +00:00

216 lines
7.7 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 "mtqq.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;
static char topic_status[64];
static char topic_cmd[64];
static char topic_resp[64];
static 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, true);
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_set(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);
send_status();
if (mqtt_watchdog) esp_timer_stop(mqtt_watchdog);
break;
case MQTT_EVENT_DISCONNECTED:
mqtt_connected = false;
led_set(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);
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");
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;
// ======================================================
// TENTA CARREGAR CERTIFICADO E USAR TLS
// ======================================================
bool tls_ok = false;
static char cert_buf[4096];
size_t cert_len = sizeof(cert_buf) - 1;
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 --------
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
// ======================================================
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_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)",
mqtt_cfg.broker.address.hostname,
(unsigned long)mqtt_cfg.broker.address.port,
mqtt_cfg.broker.address.transport == MQTT_TRANSPORT_OVER_SSL ? "TLS" : "TCP");
// -------- 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);
}