#include #include #include #include #include "esp_log.h" #include "esp_system.h" #include "esp_random.h" #include "mqtt_client.h" #include "cJSON.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "led_driver.h" #include "led_effects.h" #include "eeprom_virtual.h" #include "eeprom_animacao.h" // animacao + animacao_save() #include "ui.h" #include "display.h" static const char *TAG = "MQTT_CMD"; // Estes vêm de mqtt_handler.c extern esp_mqtt_client_handle_t mqtt_client; extern char topic_resp[64]; // Variável global declarada noutro ficheiro extern bool modo_bloqueado; // Modo atual bool demo_mode = false; char current_mode[16] = "IDLE"; // Relógio uint8_t hora_r = 10, hora_g = 40, hora_b = 10; uint8_t min_r = 10, min_g = 10, min_b = 60; uint8_t sec_r = 5, sec_g = 5, sec_b = 5; int clock_speed_ms = 150; // ====================================================== // FUNÇÃO PRINCIPAL // ====================================================== void mqtt_comandos_handle(cJSON *root) { cJSON *cmd = cJSON_GetObjectItem(root, "cmd"); if (!cJSON_IsString(cmd)) { ESP_LOGW(TAG, "⚠️ Comando inválido (sem cmd)"); return; } const char *c = cmd->valuestring; ESP_LOGI(TAG, "📩 CMD: %s", c); // -------------------------------------------------- // LED_ON // -------------------------------------------------- if (strcmp(c, "LED_ON") == 0) { led_all_on(50, 50, 50); } // -------------------------------------------------- else if (strcmp(c, "LED_OFF") == 0) { led_all_off(); } else if (strcmp(c, "CLEAR") == 0) { led_all_off(); } // -------------------------------------------------- else if (strcmp(c, "SPIN") == 0) { int n = led_get_count(); if (n > 0) { uint16_t p = esp_random() % n; led_spin_to(p, 2, 12, 80); } } // -------------------------------------------------- else if (strcmp(c, "JACKPOT") == 0) { led_jackpot_animation(); } // -------------------------------------------------- else if (strcmp(c, "REBOOT") == 0) { ESP_LOGW(TAG, "🔁 REBOOT pedido via MQTT"); vTaskDelay(pdMS_TO_TICKS(200)); esp_restart(); } // -------------------------------------------------- else if (strcmp(c, "STATUS") == 0) { char resp[160]; snprintf(resp, sizeof(resp), "{\"uptime\":%lu,\"heap\":%lu}", (unsigned long)(esp_log_timestamp()/1000), (unsigned long)esp_get_free_heap_size()); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); ESP_LOGI(TAG, "📤 STATUS enviado"); } // ====================================================== // BLOQUEIO // ====================================================== else if (strcmp(c, "BLOCK") == 0) { cJSON *v = cJSON_GetObjectItem(root, "value"); if (!cJSON_IsNumber(v)) { ESP_LOGW(TAG, "BLOCK sem valor numérico"); return; } uint8_t novo = (uint8_t)v->valueint; uint8_t val = novo; eeprom_virtual_write_bin("blocked", &val, 1); extern bool modo_bloqueado; modo_bloqueado = novo; ESP_LOGW(TAG, "%s", novo ? "BLOQUEADO" : "DESBLOQUEADO"); char resp[64]; snprintf(resp, sizeof(resp), "{\"blocked\":%u}", novo); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); } // ====================================================== // SET_ANIM // ====================================================== else if (strcmp(c, "SET_ANIM") == 0) { cJSON *v = cJSON_GetObjectItem(root, "value"); if (!cJSON_IsNumber(v)) { ESP_LOGW(TAG, "❌ SET_ANIM sem value"); return; } uint8_t novo = v->valueint; animacao_save(novo); animacao = novo; ESP_LOGI(TAG, "🎨 Animação alterada para %u", novo); char resp[64]; snprintf(resp, sizeof(resp), "{\"anim\":%u}", novo); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); } // ====================================================== // SET_COLOR // ====================================================== else if (strcmp(c, "SET_COLOR") == 0) { cJSON *r = cJSON_GetObjectItem(root, "r"); cJSON *g = cJSON_GetObjectItem(root, "g"); cJSON *b = cJSON_GetObjectItem(root, "b"); if (!cJSON_IsNumber(r) || !cJSON_IsNumber(g) || !cJSON_IsNumber(b)) { ESP_LOGW(TAG, "❌ SET_COLOR sem r/g/b"); return; } int n = led_get_count(); for (int i = 0; i < n; i++) { led_set_pixel(i, r->valueint, g->valueint, b->valueint); } led_show(); } // ====================================================== // SET_BRIGHT // ====================================================== else if (strcmp(c, "SET_BRIGHT") == 0) { cJSON *v = cJSON_GetObjectItem(root, "value"); if (!cJSON_IsNumber(v)) { ESP_LOGW(TAG, "❌ SET_BRIGHT sem value"); return; } led_set_global_brightness(v->valueint); ESP_LOGI(TAG, "💡 Brightness = %u", v->valueint); } // ====================================================== // LED_PIXEL // ====================================================== else if (strcmp(c, "LED_PIXEL") == 0) { cJSON *n = cJSON_GetObjectItem(root, "n"); cJSON *r = cJSON_GetObjectItem(root, "r"); cJSON *g = cJSON_GetObjectItem(root, "g"); cJSON *b = cJSON_GetObjectItem(root, "b"); if (!cJSON_IsNumber(n) || !cJSON_IsNumber(r) || !cJSON_IsNumber(g) || !cJSON_IsNumber(b)) { ESP_LOGW(TAG, "❌ LED_PIXEL sem dados"); return; } led_set_pixel(n->valueint, r->valueint, g->valueint, b->valueint); led_show(); } // ====================================================== // DEMO // ====================================================== else if (strcmp(c, "DEMO_ON") == 0) { demo_mode = true; strncpy(current_mode, "DEMO", sizeof(current_mode)); ESP_LOGI(TAG, "▶ Demo ON"); } else if (strcmp(c, "DEMO_OFF") == 0) { demo_mode = false; strncpy(current_mode, "NORMAL", sizeof(current_mode)); ESP_LOGI(TAG, "⏹ Demo OFF"); } // ====================================================== // SET_MODE // ====================================================== else if (strcmp(c, "SET_MODE") == 0) { cJSON *v = cJSON_GetObjectItem(root, "value"); if (!cJSON_IsString(v)) { ESP_LOGW(TAG, "❌ SET_MODE sem string"); return; } strncpy(current_mode, v->valuestring, sizeof(current_mode)); current_mode[sizeof(current_mode)-1] = '\0'; ESP_LOGI(TAG, "🎛 Modo = %s", current_mode); } // ====================================================== // GET_INFO // ====================================================== else if (strcmp(c, "GET_INFO") == 0) { char resp[256]; snprintf(resp, sizeof(resp), "{\"uptime\":%lu,\"heap\":%lu,\"anim\":%u,\"mode\":\"%s\"}", (unsigned long)(esp_log_timestamp()/1000), (unsigned long)esp_get_free_heap_size(), animacao, current_mode ); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); ESP_LOGI(TAG, "📤 INFO enviado"); } // ====================================================== // CORES DO RELÓGIO // ====================================================== else if (strcmp(c, "SET_HOUR_COLOR") == 0 || strcmp(c, "SET_MIN_COLOR") == 0 || strcmp(c, "SET_SEC_COLOR") == 0) { cJSON *r = cJSON_GetObjectItem(root, "r"); cJSON *g = cJSON_GetObjectItem(root, "g"); cJSON *b = cJSON_GetObjectItem(root, "b"); if (!cJSON_IsNumber(r) || !cJSON_IsNumber(g) || !cJSON_IsNumber(b)) { ESP_LOGW(TAG, "❌ SET_*_COLOR inválido"); return; } if (strcmp(c, "SET_HOUR_COLOR") == 0) { hora_r = r->valueint; hora_g = g->valueint; hora_b = b->valueint; } else if (strcmp(c, "SET_MIN_COLOR") == 0) { min_r = r->valueint; min_g = g->valueint; min_b = b->valueint; } else { sec_r = r->valueint; sec_g = g->valueint; sec_b = b->valueint; } } // ====================================================== // SET_CLOCK_SPEED // ====================================================== else if (strcmp(c, "SET_CLOCK_SPEED") == 0) { cJSON *v = cJSON_GetObjectItem(root, "ms"); if (!cJSON_IsNumber(v)) { ESP_LOGW(TAG, "❌ SET_CLOCK_SPEED sem ms"); return; } clock_speed_ms = v->valueint; ESP_LOGI(TAG, "⏰ Clock speed = %d ms", clock_speed_ms); } // ====================================================== // MENU // ====================================================== else if (strcmp(c, "MENU_ON") == 0) { ui_set_menu(MENU_MAIN); display_text("MENU"); ESP_LOGI(TAG, "📟 MENU ON"); return; } else if (strcmp(c, "MENU_OFF") == 0) { ui_set_menu(MENU_OFF); display_clear(); ESP_LOGI(TAG, "📟 MENU OFF"); return; } else if (strcmp(c, "MENU_NEXT") == 0) { ui_menu_next(); ESP_LOGI(TAG, "📟 MENU NEXT"); return; } else if (strcmp(c, "MENU_OK") == 0) { ui_menu_ok(); ESP_LOGI(TAG, "📟 MENU OK"); return; } else if (strcmp(c, "DISP_TEST") == 0) { display_text("8888"); ESP_LOGI(TAG, "🖥️ TESTE DISPLAY"); return; } else if (strcmp(c, "LED_TEST") == 0) { int n = led_get_count(); for (int i = 0; i < n; i++) { led_set_pixel(i, 0, 0, 40); led_show(); vTaskDelay(pdMS_TO_TICKS(8)); } led_all_off(); ESP_LOGI(TAG, "💡 TESTE LED"); return; } // ====================================================== // SET_PERC (percentagem) // ====================================================== else if (strcmp(c, "SET_PERC") == 0) { cJSON *v = cJSON_GetObjectItem(root, "val"); if (!cJSON_IsNumber(v)) { ESP_LOGW(TAG, "❌ SET_PERC sem valor"); return; } uint8_t p = v->valueint; // guarda na EEPROM virtual eeprom_virtual_write_bin("percentagem", &p, 1); // mostra no display display_number(p); ESP_LOGI(TAG, "🎯 Percentagem definida = %u%%", p); char resp[64]; snprintf(resp, sizeof(resp), "{\"percent\":%u}", p); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); return; } // ====================================================== // TEST_RING // ====================================================== else if (strcmp(c, "TEST_RING") == 0) { int n = led_get_count(); for (int i = 0; i < n; i++) { led_clear(); led_set_pixel(i, 0, 50, 0); led_show(); vTaskDelay(pdMS_TO_TICKS(80)); } led_all_off(); } // ====================================================== // COMANDO DESCONHECIDO // ====================================================== else { ESP_LOGW(TAG, "⚠️ Comando desconhecido: %s", c); char resp[128]; snprintf(resp, sizeof(resp), "{\"error\":\"unknown_cmd\",\"cmd\":\"%s\"}", c); esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); } }