diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 23f0c2c..9e8e372 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -12,7 +12,9 @@ idf_component_register( "eeprom_animacao.c" "led_task.c" "creditos.c" - + "premios.c" + "i2c_helper.c" + "display.c" INCLUDE_DIRS "include" diff --git a/main/display.c b/main/display.c new file mode 100644 index 0000000..c3df6d3 --- /dev/null +++ b/main/display.c @@ -0,0 +1,67 @@ +#include "driver/i2c.h" +#include "esp_log.h" +#include "display.h" + +#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 +}; + +void display_init(void) +{ + // Oscillator ON + uint8_t cmd1 = 0x21; + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, &cmd1, 1, 20 / portTICK_PERIOD_MS); + + // Display ON, no blink + uint8_t cmd2 = 0x81; + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, &cmd2, 1, 20 / portTICK_PERIOD_MS); + + // Brilho máximo + uint8_t cmd3 = 0xEF; + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, &cmd3, 1, 20 / portTICK_PERIOD_MS); + + display_clear(); +} + +void display_clear(void) +{ + uint8_t buf[17] = {0}; + buf[0] = 0x00; + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, buf, sizeof(buf), 20 / portTICK_PERIOD_MS); +} + +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 + + i2c_master_write_to_device(I2C_PORT, DISP_ADDR, buf, 2, 20 / portTICK_PERIOD_MS); +} + +void display_number(int num) +{ + if (num < 0) num = 0; + if (num > 9999) num = 9999; + + display_digit(3, num % 10); + display_digit(2, (num / 10) % 10); + display_digit(1, (num / 100) % 10); + display_digit(0, (num / 1000) % 10); +} diff --git a/main/i2c_helper.c b/main/i2c_helper.c new file mode 100644 index 0000000..a51c823 --- /dev/null +++ b/main/i2c_helper.c @@ -0,0 +1,20 @@ +#include "driver/i2c.h" + +#define SDA_PIN 21 +#define SCL_PIN 22 +#define I2C_PORT I2C_NUM_0 + +void i2c_init(void) +{ + i2c_config_t cfg = { + .mode = I2C_MODE_MASTER, + .sda_io_num = SDA_PIN, + .scl_io_num = SCL_PIN, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 100000 + }; + + i2c_param_config(I2C_PORT, &cfg); + i2c_driver_install(I2C_PORT, cfg.mode, 0, 0, 0); +} diff --git a/main/include/display.h b/main/include/display.h new file mode 100644 index 0000000..9b7d892 --- /dev/null +++ b/main/include/display.h @@ -0,0 +1,7 @@ +#pragma once +#include + +void display_init(void); +void display_clear(void); +void display_digit(int pos, uint8_t value); +void display_number(int num); diff --git a/main/include/i2c_helper.h b/main/include/i2c_helper.h new file mode 100644 index 0000000..771b402 --- /dev/null +++ b/main/include/i2c_helper.h @@ -0,0 +1,3 @@ +#pragma once + +void i2c_init(void); diff --git a/main/include/led_effects.h b/main/include/led_effects.h index 97dc7ad..21ad954 100644 --- a/main/include/led_effects.h +++ b/main/include/led_effects.h @@ -4,6 +4,7 @@ void led_all_on(uint8_t r, uint8_t g, uint8_t b); void led_all_off(void); +void led_anim_03(void); void led_idle_animation(void); void led_jackpot_animation(void); diff --git a/main/include/premios.h b/main/include/premios.h new file mode 100644 index 0000000..14c30cc --- /dev/null +++ b/main/include/premios.h @@ -0,0 +1,6 @@ +#pragma once +#include + +extern uint16_t premios[60]; + +uint16_t premio_da_posicao(int pos); diff --git a/main/led_driver.c b/main/led_driver.c index c45a8c3..c16a34c 100644 --- a/main/led_driver.c +++ b/main/led_driver.c @@ -4,6 +4,8 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include +#include "premios.h" + #define LED_PIN 18 // <-- CERTIFICA-TE disto #define RMT_RESOLUTION_HZ 10000000 // 10 MHz (1 tick = 0.1us) @@ -113,16 +115,19 @@ int led_get_count(void) { return LED_COUNT; } + void led_spin_to(uint16_t target, int min_spins, int max_spins, int base_delay_ms) { int n = LED_COUNT; if (min_spins < 1) min_spins = 1; if (max_spins < min_spins) max_spins = min_spins; + // passos aleatórios entre min e max int extra = 0; if (max_spins > min_spins) extra = rand() % (max_spins - min_spins + 1); + // número total de passos até ao LED final int total_steps = (min_spins + extra) * n + (target % n); int delay = base_delay_ms; @@ -131,18 +136,62 @@ void led_spin_to(uint16_t target, int min_spins, int max_spins, int base_delay_m for (int i = 0; i < n; i++) { if (i == pos) - led_set_pixel(i, 0, 40, 0); // verde fraco no cursor + led_set_pixel(i, 0, 40, 0); // cursor verde else led_set_pixel(i, 0, 0, 0); } led_show(); + // aceleração if (step < total_steps / 3 && delay > 3) delay--; + + // desaceleração else if (step > (2 * total_steps) / 3) delay++; vTaskDelay(pdMS_TO_TICKS(delay)); } + + // ======================================= + // POSIÇÃO FINAL + // ======================================= + + int pos_final = total_steps % n; + + // LED final destacado + led_set_pixel(pos_final, 0, 60, 0); + led_show(); + vTaskDelay(pdMS_TO_TICKS(200)); + + // ======================================= + // PRÉMIO FINAL + // ======================================= + + uint16_t premio = premio_da_posicao(pos_final); + + if (premio > 0) { + // piscar o LED que deu prémio + for (int k = 0; k < 8; k++) { + if (k & 1) + led_set_pixel(pos_final, 0, 0, 0); + else + led_set_pixel(pos_final, 0, 0, 60); // azul prémio + led_show(); + vTaskDelay(pdMS_TO_TICKS(120)); + } + } else { + // sem prémio → pisca vermelho suave + for (int k = 0; k < 4; k++) { + if (k & 1) + led_set_pixel(pos_final, 0, 0, 0); + else + led_set_pixel(pos_final, 40, 0, 0); + led_show(); + vTaskDelay(pdMS_TO_TICKS(120)); + } + } + + // (aqui no futuro vais mandar MQTT do prémio) } diff --git a/main/led_effects.c b/main/led_effects.c index 45fd6c8..0c92895 100644 --- a/main/led_effects.c +++ b/main/led_effects.c @@ -5,6 +5,7 @@ #include #include #include "mqtt_comandos.h" +#include "premios.h" void led_all_on(uint8_t r, uint8_t g, uint8_t b) @@ -115,3 +116,60 @@ void led_clock_animation(void) vTaskDelay(pdMS_TO_TICKS(200)); } +void led_anim_03(void) +{ + static int left = 0; + static int right = LED_COUNT - 1; + static bool meet = false; + + int n = LED_COUNT; + + // ------- Lista de prémios ------- + // coloca as posições reais depois + static const int premios[] = { 3, 8, 15, 22, 31, 40, 47, 53 }; + static const int total_premios = sizeof(premios) / sizeof(premios[0]); + + // limpar tudo primeiro + led_clear(); + + if (!meet) { + + // acende esquerda e direita + led_set_pixel(left, 0, 40, 0); // verde + led_set_pixel(right, 0, 40, 0); + + left++; + right--; + + // encontraram-se? + if (left >= right) { + meet = true; + } + } + else { + // ---- PISCAR PRÉMIOS ---- + static bool on = false; + on = !on; + + for (int i = 0; i < total_premios; i++) { + int p = premios[i] % n; + if (on) + led_set_pixel(p, 0, 0, 60); // azul forte + else + led_set_pixel(p, 0, 0, 10); // azul fraco + } + + // durante alguns ciclos pisca antes de resetar + static int count = 0; + count++; + if (count > 20) { // ajusta aqui a duração do piscar + count = 0; + meet = false; + left = 0; + right = n - 1; + } + } + + led_show(); + vTaskDelay(pdMS_TO_TICKS(50)); +} diff --git a/main/led_task.c b/main/led_task.c index 19c0359..6fb4840 100644 --- a/main/led_task.c +++ b/main/led_task.c @@ -29,6 +29,9 @@ void led_task(void *pv) // jackpot non-blocking led_jackpot_animation(); break; + case 3: + led_anim_03(); // <-- AGORA É A PRINCIPAL + break; default: // fallback seguro diff --git a/main/main.c b/main/main.c index 3cf92c8..f6f8320 100644 --- a/main/main.c +++ b/main/main.c @@ -27,6 +27,14 @@ #include "creditos.h" #include "led_task.h" +#include "driver/i2c.h" +#include "i2c_helper.h" +#include "display.h" + +#define SDA_PIN 21 +#define SCL_PIN 22 +#define I2C_PORT I2C_NUM_0 + static const char *TAG = "APP"; static uint32_t segundos = 0; static bool wifi_ready = false; @@ -87,6 +95,45 @@ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) } +// Configuração básica do I2C + i2c_config_t cfg = { + .mode = I2C_MODE_MASTER, + .sda_io_num = SDA_PIN, + .scl_io_num = SCL_PIN, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 100000 + }; + +void ht16_init() +{ + uint8_t cmd1 = 0x21; // liga oscilador + i2c_master_write_to_device(I2C_PORT, 0x70, &cmd1, 1, 10 / portTICK_PERIOD_MS); + + uint8_t cmd2 = 0x81; // display ON, sem piscar + i2c_master_write_to_device(I2C_PORT, 0x70, &cmd2, 1, 10 / portTICK_PERIOD_MS); + + uint8_t cmd3 = 0xEF; // brilho máximo + i2c_master_write_to_device(I2C_PORT, 0x70, &cmd3, 1, 10 / portTICK_PERIOD_MS); +} + + + +void ht16_test() +{ + uint8_t buf[17] = {0}; + buf[0] = 0x00; // endereço inicial + + buf[7] = 0b0111111; // acende apenas o dígito 0 + // (que mostra um "0" bonitinho) + + + // Os outros dígitos ficam a 0 = apagados + + i2c_master_write_to_device(I2C_PORT, 0x70, buf, sizeof(buf), 20 / portTICK_PERIOD_MS); +} + + // ============================ // MAIN // ============================ @@ -115,6 +162,15 @@ void app_main(void) { animacao_load(); ESP_LOGI("ANIM", "🎨 Animação carregada = %u", animacao); + +//i2c scan + + i2c_init(); // <- o helper entra aqui + display_init(); // <- inicializa o HT16K33 + + display_number(1234); + + // -------- Wi-Fi -------- wifi_config_t cfg; bool have_creds = false; diff --git a/main/premios.c b/main/premios.c new file mode 100644 index 0000000..d576dbd --- /dev/null +++ b/main/premios.c @@ -0,0 +1,29 @@ +#include "premios.h" + +// =============================== +// Tabela de prémios (60 LEDs) +// =============================== +// 0 = sem prémio +// Aqui só mete valores de teste. Depois ajustamos tudo bonito. +// +uint16_t premios[60] = { + 0, 0, 5, 0, 10, 0, 0, 20, + 0, 0, 50, 0, 0,100, 0, 0, + 5, 0, 0, 10, 0, 0,200, 0, + 0, 0, 20, 0, 0, 0, 50, 0, + 0, 0, 5, 0, 0,100, 0, 0, + 10, 0, 0, 0, 20, 0, 0, 50, + 0, 0,100, 0, 0,200, 0, 0 +}; + + +// ============================================ +// Retorna o prémio associado a uma posição +// ============================================ +uint16_t premio_da_posicao(int pos) +{ + if (pos < 0 || pos >= 60) + return 0; + + return premios[pos]; +}