diff --git a/main/display.c b/main/display.c index c3df6d3..9f5ae2f 100644 --- a/main/display.c +++ b/main/display.c @@ -5,20 +5,35 @@ #define DISP_ADDR 0x70 #define I2C_PORT I2C_NUM_0 -// Tabela de segmentos para 0–9 -static const uint8_t segtbl[10] = { - 0b00111111, // 0 - 0b00000110, // 1 - 0b01011011, // 2 - 0b01001111, // 3 - 0b01100110, // 4 - 0b01101101, // 5 - 0b01111101, // 6 - 0b00000111, // 7 - 0b01111111, // 8 - 0b01101111 // 9 +// Exemplo de tabela 16 bits para 0–9 (tens de afinar os bits para o teu módulo) +static const uint16_t segtbl16[10] = { + 0b0000000000111111, // 0 + 0b0000000000000110, // 1 + 0b0000000001011011, // 2 + 0b0000000001001111, // 3 + 0b0000000001100110, // 4 + 0b0000000001101101, // 5 + 0b0000000001111101, // 6 + 0b0000000000000111, // 7 + 0b0000000001111111, // 8 + 0b0000000001101111 // 9 }; +static const uint16_t alpha_map[] = { + ['A'] = 0b0000000001110111, + ['b'] = 0b0000000001111100, + ['C'] = 0b0000000000111001, + ['d'] = 0b0000000001011110, + ['E'] = 0b0000000001111001, + ['F'] = 0b0000000001110001, + ['H'] = 0b0000000001110110, + ['L'] = 0b0000000000111000, + ['P'] = 0b0000000001110011, + ['r'] = 0b0000000001010000, + ['U'] = 0b0000000000111110, +}; + + void display_init(void) { // Oscillator ON @@ -48,13 +63,18 @@ void display_digit(int pos, uint8_t val) if (pos < 0 || pos > 3) return; if (val > 9) val = 0; - uint8_t buf[2]; - buf[0] = pos * 2; // endereço interno do dígito - buf[1] = segtbl[val]; // segmentos da tabela + uint16_t pattern = segtbl16[val]; - i2c_master_write_to_device(I2C_PORT, DISP_ADDR, buf, 2, 20 / portTICK_PERIOD_MS); + uint8_t buf[3]; + buf[0] = pos * 2; // endereço base do dígito + buf[1] = pattern & 0xFF; // byte baixo (bits 0–7) + buf[2] = (pattern >> 8) & 0xFF; // byte alto (bits 8–15) + + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, buf, 3, + 20 / portTICK_PERIOD_MS); } + void display_number(int num) { if (num < 0) num = 0; @@ -65,3 +85,16 @@ void display_number(int num) display_digit(1, (num / 100) % 10); display_digit(0, (num / 1000) % 10); } + +void display_set_time(int h, int m) +{ + if (h < 0) h = 0; + if (h > 23) h = h % 24; + + if (m < 0) m = 0; + if (m > 59) m = m % 60; + + int value = h * 100 + m; // HHMM + + display_number(value); +} diff --git a/main/include/display.h b/main/include/display.h index 9b7d892..1bdd71c 100644 --- a/main/include/display.h +++ b/main/include/display.h @@ -5,3 +5,4 @@ void display_init(void); void display_clear(void); void display_digit(int pos, uint8_t value); void display_number(int num); +void display_set_time(int h, int m); diff --git a/main/led_effects.c b/main/led_effects.c index 0c92895..8c46133 100644 --- a/main/led_effects.c +++ b/main/led_effects.c @@ -6,6 +6,7 @@ #include #include "mqtt_comandos.h" #include "premios.h" +#include "display.h" // onde está a tua display_number() void led_all_on(uint8_t r, uint8_t g, uint8_t b) @@ -85,37 +86,31 @@ void led_clock_animation(void) int n = led_get_count(); if (n <= 0) return; + // === Tempo atual === time_t now; struct tm timeinfo; time(&now); localtime_r(&now, &timeinfo); - int h = timeinfo.tm_hour % 12; + int h = timeinfo.tm_hour; int m = timeinfo.tm_min; int s = timeinfo.tm_sec; - int pos_h = (h * n) / 12; - int pos_m = (m * n) / 60; + // === Display mostra HHMM === + display_set_time(h, m); + + // === Segundos na roda === int pos_s = (s * n) / 60; led_clear(); - - // HORAS – verde forte - led_set_pixel(pos_h, 10, 40, 10); - - // MINUTOS – azul - led_set_pixel(pos_m, 10, 10, 60); - - // SEGUNDOS – branco muito suave - led_set_pixel(pos_s, 5, 5, 5); - + led_set_pixel(pos_s, 20, 20, 20); // branco suave led_show(); - - vTaskDelay(pdMS_TO_TICKS(200)); + vTaskDelay(pdMS_TO_TICKS(200)); // 5 updates/segundo } + void led_anim_03(void) { static int left = 0; diff --git a/main/main.c b/main/main.c index f6f8320..8d39e61 100644 --- a/main/main.c +++ b/main/main.c @@ -30,6 +30,9 @@ #include "driver/i2c.h" #include "i2c_helper.h" #include "display.h" +#include + +bool modo_bloqueado = false; // definição oficial #define SDA_PIN 21 #define SCL_PIN 22 @@ -168,7 +171,7 @@ void app_main(void) { i2c_init(); // <- o helper entra aqui display_init(); // <- inicializa o HT16K33 - display_number(1234); + display_number(0000); // -------- Wi-Fi -------- diff --git a/main/mqtt_comandos.c b/main/mqtt_comandos.c index 0ddb6f3..33551d2 100644 --- a/main/mqtt_comandos.c +++ b/main/mqtt_comandos.c @@ -14,43 +14,51 @@ #include "led_driver.h" #include "led_effects.h" -#include "eeprom_animacao.h" // animacao / animacao_save() +#include "eeprom_virtual.h" +#include "eeprom_animacao.h" // animacao + animacao_save() 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]; -// Configuração de modos / relógio +// 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; // usado no led_clock_animation() +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')"); + ESP_LOGW(TAG, "⚠️ Comando inválido (sem cmd)"); return; } const char *c = cmd->valuestring; ESP_LOGI(TAG, "📩 CMD: %s", c); - // ====================================================== - // COMANDOS BÁSICOS - // ====================================================== - + // -------------------------------------------------- + // LED_ON + // -------------------------------------------------- if (strcmp(c, "LED_ON") == 0) { led_all_on(50, 50, 50); } + // -------------------------------------------------- else if (strcmp(c, "LED_OFF") == 0) { led_all_off(); } @@ -59,60 +67,95 @@ void mqtt_comandos_handle(cJSON *root) led_all_off(); } + // -------------------------------------------------- else if (strcmp(c, "SPIN") == 0) { int n = led_get_count(); - if (n <= 0) return; - uint16_t p = esp_random() % n; - led_spin_to(p, 2, 12, 80); + 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(); // anima 1 vez + 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()); + "{\"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 -> %s", resp); + + ESP_LOGI(TAG, "📤 STATUS enviado"); } // ====================================================== - // SET_ANIM (0/1/2...) + // 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 valor numérico"); + ESP_LOGW(TAG, "❌ SET_ANIM sem value"); return; } uint8_t novo = v->valueint; - animacao_save(novo); // grava em NVS - animacao = novo; // atualiza RAM + 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); + esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); } // ====================================================== - // SET_COLOR (todos os LEDs) + // 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"); @@ -130,32 +173,33 @@ void mqtt_comandos_handle(cJSON *root) } // ====================================================== - // SET_BRIGHT (brilho global) + // 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'"); + ESP_LOGW(TAG, "❌ SET_BRIGHT sem value"); return; } - uint8_t level = v->valueint; - led_set_global_brightness(level); - ESP_LOGI(TAG, "💡 Brilho global = %u", level); + led_set_global_brightness(v->valueint); + ESP_LOGI(TAG, "💡 Brightness = %u", v->valueint); } // ====================================================== - // LED_PIXEL (1 LED) + // 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"); + + 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 n/r/g/b"); + ESP_LOGW(TAG, "❌ LED_PIXEL sem dados"); return; } @@ -164,120 +208,103 @@ void mqtt_comandos_handle(cJSON *root) } // ====================================================== - // DEMO_ON / DEMO_OFF + // DEMO // ====================================================== else if (strcmp(c, "DEMO_ON") == 0) { demo_mode = true; strncpy(current_mode, "DEMO", sizeof(current_mode)); - current_mode[sizeof(current_mode)-1] = '\0'; ESP_LOGI(TAG, "▶ Demo ON"); } else if (strcmp(c, "DEMO_OFF") == 0) { demo_mode = false; strncpy(current_mode, "NORMAL", sizeof(current_mode)); - current_mode[sizeof(current_mode)-1] = '\0'; ESP_LOGI(TAG, "⏹ Demo OFF"); } // ====================================================== - // SET_MODE (string) + // 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 'value' string"); + 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, "🎛 Mode = %s", current_mode); + ESP_LOGI(TAG, "🎛 Modo = %s", current_mode); } // ====================================================== - // GET_INFO + // GET_INFO // ====================================================== else if (strcmp(c, "GET_INFO") == 0) { + char resp[256]; snprintf(resp, sizeof(resp), - "{\"uptime\":%lu," - "\"heap\":%lu," - "\"anim\":%u," - "\"mode\":\"%s\"}", + "{\"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 -> %s", resp); + esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); + ESP_LOGI(TAG, "📤 INFO enviado"); } // ====================================================== - // SET_HOUR_COLOR / SET_MIN_COLOR / SET_SEC_COLOR + // CORES DO RELÓGIO // ====================================================== - else if (strcmp(c, "SET_HOUR_COLOR") == 0) { + 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)) return; - hora_r = r->valueint; - hora_g = g->valueint; - hora_b = b->valueint; - ESP_LOGI(TAG, "⏰ Cor HORAS = (%u,%u,%u)", hora_r, hora_g, hora_b); - } - - else if (strcmp(c, "SET_MIN_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)) return; - - min_r = r->valueint; - min_g = g->valueint; - min_b = b->valueint; - ESP_LOGI(TAG, "⏰ Cor MIN = (%u,%u,%u)", min_r, min_g, min_b); - } - - else if (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)) return; - - sec_r = r->valueint; - sec_g = g->valueint; - sec_b = b->valueint; - ESP_LOGI(TAG, "⏰ Cor SEC = (%u,%u,%u)", sec_r, sec_g, sec_b); - } - - // ====================================================== - // SET_CLOCK_SPEED (ms) - // ====================================================== - 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'"); + 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); } // ====================================================== - // TEST_RING + // TEST_RING // ====================================================== else if (strcmp(c, "TEST_RING") == 0) { + int n = led_get_count(); - if (n <= 0) return; - - ESP_LOGI(TAG, "🔍 TEST_RING: %d LEDs", n); - for (int i = 0; i < n; i++) { led_clear(); led_set_pixel(i, 0, 50, 0); @@ -288,15 +315,15 @@ void mqtt_comandos_handle(cJSON *root) } // ====================================================== - // COMANDO DESCONHECIDO + // 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); + "{\"error\":\"unknown_cmd\",\"cmd\":\"%s\"}", c); + + esp_mqtt_client_publish(mqtt_client, topic_resp, resp, 0, 1, false); } } diff --git a/main/notas.txt b/main/notas.txt index 5d42a99..b7e468e 100644 --- a/main/notas.txt +++ b/main/notas.txt @@ -36,9 +36,6 @@ - - - esp/esp_BBC9A4/cmd // onde envias os comandos esp/esp_BBC9A4/resp // respostas aos comandos esp/esp_BBC9A4/status // heartbeat 30s