273 lines
6.8 KiB
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);
|
|
}
|