#include #include #include "certs.h" #include "esp_log.h" #include "esp_system.h" #include "esp_event.h" #include "mqtt_client.h" #include "esp_mac.h" #include "cJSON.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "mqtt_handler.h" #include "display.h" // ====================================================== static const char *TAG = "MQTT"; // MQTT CONFIG #define BROKER_HOST "mqtt.xupas.mywire.org" #define BROKER_PORT 8883 #define MQTT_USER "xupa" #define MQTT_PASS "xupa" // ====================================================== static esp_mqtt_client_handle_t mqtt_client = NULL; static bool mqtt_connected = false; static char topic_cmd[64]; static char topic_status[64]; // TASK HANDLES static TaskHandle_t status_task_handle = NULL; static TaskHandle_t mqtt_clock_handle = NULL; // RELΓ“GIO MQTT static int clock_h = 0; static int clock_m = 0; static bool clock_valid = false; // ====================================================== // HEARTBEAT TASK // ====================================================== static void status_task(void *pv) { while (1) { if (mqtt_connected) { char msg[128]; snprintf(msg, sizeof(msg), "{\"status\":\"online\"}"); esp_mqtt_client_publish(mqtt_client, topic_status, msg, 0, 1, 0); ESP_LOGI(TAG, "πŸ’“ Heartbeat enviado"); } vTaskDelay(pdMS_TO_TICKS(10000)); } } // ====================================================== // CLOCK TASK (sΓ³ MQTT controla) // ====================================================== static void mqtt_clock_task(void *pv) { while (1) { if (clock_valid) { vTaskDelay(pdMS_TO_TICKS(60000)); // 1 minuto clock_m++; if (clock_m >= 60) { clock_m = 0; clock_h++; if (clock_h >= 24) clock_h = 0; } display_set_time_top(clock_h, clock_m); ESP_LOGI(TAG, "πŸ•’ Hora interna: %02d:%02d", clock_h, clock_m); } else { vTaskDelay(pdMS_TO_TICKS(1000)); } } } // ====================================================== // MQTT EVENT HANDLER // ====================================================== static void mqtt_event_handler(void *arg, 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; ESP_LOGI(TAG, "βœ… MQTT conectado"); // ONLINE retain esp_mqtt_client_publish(mqtt_client, topic_status, "{\"status\":\"online\"}", 0, 1, 1); esp_mqtt_client_subscribe(mqtt_client, topic_cmd, 1); esp_mqtt_client_subscribe(mqtt_client, "time/now", 1); // heartbeat (1x) if (status_task_handle == NULL) { xTaskCreate(status_task, "status_task", 4096, NULL, 5, &status_task_handle); } // clock (1x) if (mqtt_clock_handle == NULL) { xTaskCreate(mqtt_clock_task, "mqtt_clock", 4096, NULL, 5, &mqtt_clock_handle); } break; // ------------------------------ case MQTT_EVENT_DISCONNECTED: mqtt_connected = false; ESP_LOGW(TAG, "⚠️ MQTT desconectado"); break; // ------------------------------ case MQTT_EVENT_DATA: { esp_mqtt_event_handle_t e = event; // πŸ”₯ Ignorar fragmentos parciais if (e->current_data_offset != 0) { ESP_LOGW(TAG, "Fragmento ignorado"); return; } // πŸ”₯ Ignorar payload vazio if (e->data_len == 0) { ESP_LOGW(TAG, "Payload vazio ignorado"); return; } char topic[64]; char payload[256]; int tlen = e->topic_len; int plen = e->data_len; if (tlen >= sizeof(topic)) tlen = sizeof(topic) - 1; if (plen >= sizeof(payload)) plen = sizeof(payload) - 1; memcpy(topic, e->topic, tlen); topic[tlen] = 0; memcpy(payload, e->data, plen); payload[plen] = 0; ESP_LOGI(TAG, "πŸ“© [%s] %s", topic, payload); // -------- TIME -------- if (strcmp(topic, "time/now") == 0) { cJSON *root = cJSON_Parse(payload); if (!root) return; cJSON *h = cJSON_GetObjectItem(root, "h"); cJSON *m = cJSON_GetObjectItem(root, "m"); if (cJSON_IsNumber(h) && cJSON_IsNumber(m)) { clock_h = h->valueint; clock_m = m->valueint; clock_valid = true; display_set_time_top(clock_h, clock_m); ESP_LOGI(TAG, "⏰ Hora MQTT: %02d:%02d", clock_h, clock_m); } cJSON_Delete(root); return; } break; } default: break; } } // ====================================================== // START MQTT // ====================================================== void mqtt_handler_start(void) { esp_mqtt_client_config_t cfg = {0}; uint8_t mac[6]; esp_read_mac(mac, ESP_MAC_WIFI_STA); char client_id[16]; snprintf(client_id, sizeof(client_id), "esp_%02X%02X%02X", mac[3], mac[4], mac[5]); snprintf(topic_cmd, sizeof(topic_cmd), "esp/%s/cmd", client_id); snprintf(topic_status, sizeof(topic_status), "esp/%s/status", client_id); cfg.broker.address.hostname = BROKER_HOST; cfg.broker.address.port = BROKER_PORT; cfg.credentials.username = MQTT_USER; cfg.credentials.authentication.password = MQTT_PASS; cfg.credentials.client_id = client_id; cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL; cfg.broker.verification.certificate = ca_cert_pem; // Last will cfg.session.last_will.topic = topic_status; cfg.session.last_will.msg = "{\"status\":\"offline\"}"; cfg.session.last_will.qos = 1; cfg.session.last_will.retain = 1; mqtt_client = esp_mqtt_client_init(&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 iniciado como %s", client_id); }