LED_shit/main/mqtt_handler.c

273 lines
6.8 KiB
C

#include <string.h>
#include <stdio.h>
#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);
}