#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 "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, 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 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, 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 = true; // ====================================================== // 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); }