216 lines
7.7 KiB
C
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);
|
|
}
|