222 lines
7.1 KiB
C
222 lines
7.1 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"
|
|
#include "ui.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"
|
|
|
|
extern char MAQ_ID[];
|
|
|
|
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),
|
|
"{\"id\":\"%s\",\"uptime\":%lu,\"heap\":%lu}",
|
|
MAQ_ID,
|
|
(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, MAQ_ID);
|
|
ESP_LOGI(TAG, "🆔 ID do dispositivo: %s", MAQ_ID);
|
|
|
|
snprintf(topic_status, sizeof(topic_status), "maq/%s/status", MAQ_ID);
|
|
snprintf(topic_cmd, sizeof(topic_cmd), "maq/%s/cmd", MAQ_ID);
|
|
snprintf(topic_resp, sizeof(topic_resp), "maq/%s/resp", MAQ_ID);
|
|
snprintf(topic_lwt, sizeof(topic_lwt), "maq/%s/lwt", MAQ_ID);
|
|
|
|
mqtt_cfg.credentials.client_id = MAQ_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);
|
|
}
|