#include #include #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); }