Compare commits
10 Commits
ee73633857
...
419be3b929
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
419be3b929 | ||
|
|
bf1499b73d | ||
|
|
ec2bda022e | ||
|
|
6dd5dd66e1 | ||
|
|
bd7972fca5 | ||
|
|
b7f03c7bf6 | ||
|
|
d5bbf905a4 | ||
|
|
e5572b2fad | ||
|
|
a1230f66ad | ||
|
|
fcbd018d59 |
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
@ -2,11 +2,11 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ESP-IDF",
|
||||
"compilerPath": "C:/esp/espressif/tools/xtensa-esp32-elf/esp-12.2.0_20230208/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc.exe",
|
||||
"compilerPath": "C:/esp/tools/xtensa-esp32-elf/esp-12.2.0_20230208/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc.exe",
|
||||
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"C:/esp/esp-idf/components/**"
|
||||
"C:/ESP/frameworks/v5.1.6/esp-idf/components/**"
|
||||
],
|
||||
"defines": [
|
||||
"ESP_PLATFORM",
|
||||
|
||||
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
@ -3,8 +3,14 @@
|
||||
"*.c": "c",
|
||||
"*.h": "c"
|
||||
},
|
||||
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"C_Cpp.default.compileCommands": "Z:/varios/LED_shit/build/compile_commands.json",
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||
|
||||
"C_Cpp.default.includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"C:/ESP/frameworks/v5.1.6/esp-idf/components/**"
|
||||
],
|
||||
|
||||
"idf.espIdfPath": "C:/Users/carec/esp/v5.1.6/esp-idf",
|
||||
"idf.pythonBinPath": "C:/Users/carec/.espressif/python_env/idf5.1_py3.11_env/Scripts/python.exe",
|
||||
@ -14,8 +20,9 @@
|
||||
"idf.adapterTargetName": "esp32",
|
||||
"idf.portWin": "COM3",
|
||||
"idf.flashType": "UART",
|
||||
"idf.buildDir": "C:/esp_build_temp",
|
||||
"idf.flashDir": "C:/esp_build_temp",
|
||||
|
||||
"idf.buildDir": "${workspaceFolder}/build",
|
||||
"idf.flashDir": "${workspaceFolder}/build",
|
||||
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build",
|
||||
"cmake.configureSettings": {
|
||||
|
||||
@ -4,20 +4,20 @@ idf_component_register(
|
||||
"wifi_config_portal.c"
|
||||
"mqtt_handler.c"
|
||||
"mqtt_comandos.c"
|
||||
"fs_handler.c"
|
||||
"eeprom_tls.c"
|
||||
"eeprom_virtual.c"
|
||||
"dns_server.c"
|
||||
|
||||
# LED driver
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/led_driver.c"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip/led_strip.c"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip/led_strip_encoder.c"
|
||||
|
||||
"dns_server.c"
|
||||
"led_driver.c"
|
||||
"led_effects.c"
|
||||
"eeprom_animacao.c"
|
||||
"led_task.c"
|
||||
"creditos.c"
|
||||
"premios.c"
|
||||
"i2c_helper.c"
|
||||
"display.c"
|
||||
"ui.c"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/led_strip"
|
||||
|
||||
REQUIRES
|
||||
esp_wifi
|
||||
@ -26,7 +26,6 @@ idf_component_register(
|
||||
nvs_flash
|
||||
mqtt
|
||||
json
|
||||
spiffs
|
||||
driver
|
||||
esp_http_server
|
||||
)
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
// certs.h
|
||||
#pragma once
|
||||
|
||||
static const char ca_cert_pem[] =
|
||||
|
||||
34
main/creditos.c
Normal file
34
main/creditos.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "creditos.h"
|
||||
#include "led_driver.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
volatile bool tem_creditos = false;
|
||||
static const char *TAG = "CREDITOS";
|
||||
|
||||
void creditos_dar(void) {
|
||||
tem_creditos = true;
|
||||
ESP_LOGI(TAG, "💰 Crédito recebido");
|
||||
}
|
||||
|
||||
void creditos_task(void *pv) {
|
||||
while (1) {
|
||||
|
||||
if (tem_creditos) {
|
||||
uint16_t pos = esp_random() % LED_COUNT;
|
||||
|
||||
ESP_LOGI(TAG, "🎯 SPIN -> posição %u", pos);
|
||||
|
||||
led_spin_to(pos, 2, 12, 80);
|
||||
|
||||
tem_creditos = false; // crédito consumido
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
331
main/display.c
Normal file
331
main/display.c
Normal file
@ -0,0 +1,331 @@
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_log.h"
|
||||
#include "display.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define I2C_PORT I2C_NUM_0
|
||||
|
||||
// Endereços reais
|
||||
#define DISP_TOP_ADDR 0x71 // display superior
|
||||
#define DISP_BOTTOM_ADDR 0x70 // display inferior
|
||||
static uint16_t rotate180(uint16_t m);
|
||||
|
||||
const char *TAG = "DISPLAY";
|
||||
|
||||
// =======================================================
|
||||
// MAPA DE SEGMENTOS (mantido igual ao teu)
|
||||
// =======================================================
|
||||
|
||||
#define SEG_A (1 << 0)
|
||||
#define SEG_B (1 << 1)
|
||||
#define SEG_C (1 << 2)
|
||||
#define SEG_D (1 << 3)
|
||||
#define SEG_E (1 << 4)
|
||||
#define SEG_F (1 << 5)
|
||||
|
||||
#define SEG_ML (1 << 6)
|
||||
#define SEG_MR (1 << 7)
|
||||
|
||||
#define SEG_TL (1 << 8)
|
||||
#define SEG_TM (1 << 9)
|
||||
#define SEG_TR (1 << 10)
|
||||
|
||||
#define SEG_BL (1 << 11)
|
||||
#define SEG_BM (1 << 12)
|
||||
#define SEG_BR (1 << 13)
|
||||
|
||||
#define SEG_DP (1 << 14)
|
||||
|
||||
#define SEG_G (SEG_ML | SEG_MR)
|
||||
|
||||
|
||||
// =======================================================
|
||||
// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY
|
||||
// =======================================================
|
||||
|
||||
static void disp_send_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, 20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void disp_raw(uint8_t addr, int pos, uint16_t mask)
|
||||
{
|
||||
if (pos < 0 || pos > 3) return;
|
||||
|
||||
uint8_t buf[3];
|
||||
buf[0] = pos * 2;
|
||||
buf[1] = mask & 0xFF;
|
||||
buf[2] = (mask >> 8) & 0xFF;
|
||||
|
||||
i2c_master_write_to_device(I2C_PORT, addr, buf, 3, 20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void disp_clear(uint8_t addr)
|
||||
{
|
||||
uint8_t buf[17] = {0};
|
||||
buf[0] = 0x00;
|
||||
i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// INIT PARA OS DOIS DISPLAYS (TOP 0x71 | BOTTOM 0x70)
|
||||
// =======================================================
|
||||
|
||||
void display_init(void)
|
||||
{
|
||||
uint8_t cmd1 = 0x21; // oscillator ON
|
||||
uint8_t cmd2 = 0x81; // display ON, blink OFF
|
||||
uint8_t cmd3 = 0xEF; // brightness MAX
|
||||
|
||||
// TOP
|
||||
disp_send_cmd(DISP_TOP_ADDR, cmd1);
|
||||
disp_send_cmd(DISP_TOP_ADDR, cmd2);
|
||||
disp_send_cmd(DISP_TOP_ADDR, cmd3);
|
||||
disp_clear(DISP_TOP_ADDR);
|
||||
|
||||
// BOTTOM
|
||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd1);
|
||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd2);
|
||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd3);
|
||||
disp_clear(DISP_BOTTOM_ADDR);
|
||||
|
||||
ESP_LOGI(TAG, "📟 Displays inicializados: TOP=0x71 BOTTOM=0x70");
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// RAW PARA TOP E BOTTOM
|
||||
// =======================================================
|
||||
void display_raw_top(int pos, uint16_t mask)
|
||||
{
|
||||
int p = 3 - pos; // inverter ordem dos dígitos
|
||||
uint16_t m = rotate180(mask); // rodar segmentos
|
||||
disp_raw(DISP_TOP_ADDR, p, m);
|
||||
}
|
||||
|
||||
|
||||
void display_raw_bottom(int pos, uint16_t mask)
|
||||
{
|
||||
disp_raw(DISP_BOTTOM_ADDR, pos, mask);
|
||||
}
|
||||
|
||||
void display_clear_top(void)
|
||||
{
|
||||
disp_clear(DISP_TOP_ADDR);
|
||||
}
|
||||
|
||||
void display_clear_bottom(void)
|
||||
{
|
||||
disp_clear(DISP_BOTTOM_ADDR);
|
||||
}
|
||||
|
||||
// =====================
|
||||
// ROTATE 180 para 14 segmentos REAL
|
||||
// (mapa correto para o teu HT16K33)
|
||||
// =====================
|
||||
static uint16_t rotate180(uint16_t m)
|
||||
{
|
||||
uint16_t r = 0;
|
||||
|
||||
// A (topo) <-> D (baixo)
|
||||
if (m & SEG_A) r |= SEG_D;
|
||||
if (m & SEG_D) r |= SEG_A;
|
||||
|
||||
// B (top-right) <-> E (bottom-left)
|
||||
if (m & SEG_B) r |= SEG_E;
|
||||
if (m & SEG_E) r |= SEG_B;
|
||||
|
||||
// C (bottom-right) <-> F (top-left)
|
||||
if (m & SEG_C) r |= SEG_F;
|
||||
if (m & SEG_F) r |= SEG_C;
|
||||
|
||||
// Meio vertical ML ↔ MR
|
||||
if (m & SEG_ML) r |= SEG_MR;
|
||||
if (m & SEG_MR) r |= SEG_ML;
|
||||
|
||||
// Alfanuméricos topo TL/TM/TR ↔ BL/BM/BR
|
||||
if (m & SEG_TL) r |= SEG_BL;
|
||||
if (m & SEG_BL) r |= SEG_TL;
|
||||
|
||||
if (m & SEG_TM) r |= SEG_BM;
|
||||
if (m & SEG_BM) r |= SEG_TM;
|
||||
|
||||
if (m & SEG_TR) r |= SEG_BR;
|
||||
if (m & SEG_BR) r |= SEG_TR;
|
||||
|
||||
// DP é DP (ponto)
|
||||
if (m & SEG_DP) r |= SEG_DP;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// CHARSET — mantido EXACTAMENTE como o teu
|
||||
// =======================================================
|
||||
|
||||
static uint16_t charset(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
// NÚMEROS
|
||||
case '0': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
|
||||
case '1': return SEG_B | SEG_C;
|
||||
case '2': return SEG_A | SEG_B | SEG_G | SEG_E | SEG_D;
|
||||
case '3': return SEG_A | SEG_B | SEG_G | SEG_C | SEG_D;
|
||||
case '4': return SEG_F | SEG_G | SEG_B | SEG_C;
|
||||
case '5': return SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
|
||||
case '6': return SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C;
|
||||
case '7': return SEG_A | SEG_B | SEG_C;
|
||||
case '8': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
|
||||
case '9': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G;
|
||||
|
||||
// LETRAS COMPLETAS (mantidas)
|
||||
case 'A': case 'a': return SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
|
||||
case 'B': case 'b': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
|
||||
case 'C': case 'c': return SEG_A | SEG_F | SEG_E | SEG_D;
|
||||
case 'D': case 'd': return SEG_B | SEG_C | SEG_D | SEG_E | SEG_G;
|
||||
case 'E': case 'e': return SEG_A | SEG_F | SEG_G | SEG_E | SEG_D;
|
||||
case 'F': case 'f': return SEG_A | SEG_F | SEG_G | SEG_E;
|
||||
case 'G': case 'g': return SEG_A | SEG_F | SEG_E | SEG_D | SEG_C | SEG_G;
|
||||
case 'H': case 'h': return SEG_F | SEG_E | SEG_G | SEG_B | SEG_C;
|
||||
case 'I': case 'i': return SEG_B | SEG_C;
|
||||
case 'J': case 'j': return SEG_B | SEG_C | SEG_D;
|
||||
case 'K': case 'k': return SEG_F | SEG_E | SEG_G | SEG_TR | SEG_BR;
|
||||
case 'L': case 'l': return SEG_F | SEG_E | SEG_D;
|
||||
case 'M': case 'm': return SEG_F | SEG_E | SEG_TL | SEG_TR | SEG_B | SEG_C;
|
||||
case 'N': case 'n': return SEG_F | SEG_E | SEG_TL | SEG_BR | SEG_C | SEG_B;
|
||||
case 'O': case 'o': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
|
||||
case 'P': case 'p': return SEG_A | SEG_B | SEG_F | SEG_G | SEG_E;
|
||||
case 'Q': case 'q': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_BR;
|
||||
case 'R': case 'r': return SEG_A | SEG_B | SEG_F | SEG_G | SEG_E | SEG_BR;
|
||||
case 'S': case 's': return SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
|
||||
case 'T': case 't': return SEG_A | SEG_TM | SEG_BR;
|
||||
case 'U': case 'u': return SEG_F | SEG_E | SEG_D | SEG_C | SEG_B;
|
||||
case 'V': case 'v': return SEG_F | SEG_E | SEG_D | SEG_B | SEG_TR;
|
||||
case 'W': case 'w': return SEG_F | SEG_E | SEG_D | SEG_C | SEG_B | SEG_BR | SEG_TR;
|
||||
case 'X': case 'x': return SEG_TL | SEG_TR | SEG_ML | SEG_MR | SEG_BL | SEG_BR;
|
||||
case 'Y': case 'y': return SEG_F | SEG_B | SEG_G | SEG_C | SEG_D;
|
||||
case 'Z': case 'z': return SEG_A | SEG_TR | SEG_G | SEG_BL | SEG_D;
|
||||
|
||||
// símbolos
|
||||
case '-': return SEG_G;
|
||||
case '.': return SEG_DP;
|
||||
case '_': return SEG_D;
|
||||
case ' ': return 0;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// DISPLAY CARACTER / TEXTO
|
||||
// =======================================================
|
||||
|
||||
void display_char_top(int pos, char c)
|
||||
{
|
||||
display_raw_top(pos, charset(c));
|
||||
}
|
||||
|
||||
void display_char_bottom(int pos, char c)
|
||||
{
|
||||
disp_raw(DISP_BOTTOM_ADDR, pos, charset(c));
|
||||
}
|
||||
|
||||
void display_text_top(const char *txt)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char c = txt[i] ? txt[i] : ' ';
|
||||
display_char_top(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void display_text_bottom(const char *txt)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char c = txt[i] ? txt[i] : ' ';
|
||||
display_char_bottom(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// DISPLAY DE NÚMEROS (igual ao teu)
|
||||
// =======================================================
|
||||
|
||||
static const uint16_t digit_mask[10] =
|
||||
{
|
||||
[0] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,
|
||||
[1] = SEG_B | SEG_C,
|
||||
[2] = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,
|
||||
[3] = SEG_A | SEG_B | SEG_G | SEG_C | SEG_D,
|
||||
[4] = SEG_F | SEG_G | SEG_B | SEG_C,
|
||||
[5] = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D,
|
||||
[6] = SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C,
|
||||
[7] = SEG_A | SEG_B | SEG_C,
|
||||
[8] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
|
||||
[9] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,
|
||||
};
|
||||
|
||||
void display_digit_top(int pos, uint8_t val)
|
||||
{
|
||||
if (val > 9) val = 0;
|
||||
display_raw_top(pos, digit_mask[val]);
|
||||
}
|
||||
|
||||
|
||||
void display_digit_bottom(int pos, uint8_t val)
|
||||
{
|
||||
if (val > 9) val = 0;
|
||||
disp_raw(DISP_BOTTOM_ADDR, pos, digit_mask[val]);
|
||||
}
|
||||
|
||||
void display_number_top(int num)
|
||||
{
|
||||
if (num < 0) num = 0;
|
||||
if (num > 9999) num = 9999;
|
||||
|
||||
display_digit_top(3, num % 10);
|
||||
display_digit_top(2, (num / 10) % 10);
|
||||
display_digit_top(1, (num / 100) % 10);
|
||||
display_digit_top(0, (num / 1000) % 10);
|
||||
}
|
||||
|
||||
|
||||
void display_number_bottom(int num)
|
||||
{
|
||||
if (num < 0) num = 0;
|
||||
if (num > 9999) num = 9999;
|
||||
|
||||
display_digit_bottom(3, num % 10);
|
||||
display_digit_bottom(2, (num/10) % 10);
|
||||
display_digit_bottom(1, (num/100) % 10);
|
||||
display_digit_bottom(0, (num/1000) % 10);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// display_set_time() — mantém SEG_DP no sítio certo
|
||||
// =======================================================
|
||||
void display_set_time_top(int horas, int minutos)
|
||||
{
|
||||
if (horas < 0) horas = 0;
|
||||
if (horas > 99) horas = 99;
|
||||
if (minutos < 0) minutos = 0;
|
||||
if (minutos > 59) minutos = 59;
|
||||
|
||||
int h1 = horas / 10;
|
||||
int h2 = horas % 10;
|
||||
int m1 = minutos / 10;
|
||||
int m2 = minutos % 10;
|
||||
|
||||
uint16_t mid = digit_mask[h2] | SEG_DP;
|
||||
|
||||
display_digit_top(0, h1);
|
||||
display_raw_top(1, mid);
|
||||
display_digit_top(2, m1);
|
||||
display_digit_top(3, m2);
|
||||
}
|
||||
48
main/eeprom_animacao.c
Normal file
48
main/eeprom_animacao.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "eeprom_animacao.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "EEPROM_ANIM";
|
||||
|
||||
#define NAMESPACE "storage"
|
||||
#define KEY_ANIM "animacao"
|
||||
|
||||
// valor em RAM, sempre igual ao guardado
|
||||
uint8_t animacao = 0;
|
||||
|
||||
void animacao_load(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NAMESPACE, NVS_READONLY, &handle);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_get_u8(handle, KEY_ANIM, &animacao);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
animacao = 0; // default
|
||||
}
|
||||
} else {
|
||||
animacao = 0; // default
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Carregado animacao = %u", animacao);
|
||||
}
|
||||
|
||||
void animacao_save(uint8_t v)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NAMESPACE, NVS_READWRITE, &handle);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
nvs_set_u8(handle, KEY_ANIM, v);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
|
||||
animacao = v; // atualiza RAM também
|
||||
ESP_LOGI(TAG, "Gravado animacao = %u", v);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Falha ao abrir NVS p/ animacao");
|
||||
}
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
static const char *TAG = "FS";
|
||||
|
||||
// --- MONTAR SPIFFS ---
|
||||
void fs_init(void) {
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = "storage",
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "❌ Falha ao montar SPIFFS (%s)", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total = 0, used = 0;
|
||||
ret = esp_spiffs_info(conf.partition_label, &total, &used);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "❌ Erro ao obter info SPIFFS (%s)", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "📂 SPIFFS montado: total=%d KB, usado=%d KB",
|
||||
(int)(total / 1024), (int)(used / 1024));
|
||||
|
||||
// Criar ficheiro de exemplo se não existir
|
||||
FILE *f = fopen("/spiffs/config.json", "r");
|
||||
if (!f) {
|
||||
ESP_LOGW(TAG, "⚠️ config.json não existe, a criar...");
|
||||
f = fopen("/spiffs/config.json", "w");
|
||||
if (f) {
|
||||
fprintf(f, "{ \"ssid\": \"ALQAEDA\", \"pass\": \"1q2w3e4r5t\" }");
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "✅ config.json criado.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "❌ Erro a criar config.json");
|
||||
}
|
||||
} else {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
// --- DESMONTAR SPIFFS ---
|
||||
void fs_deinit(void) {
|
||||
ESP_LOGI(TAG, "🧹 Desmontando SPIFFS...");
|
||||
esp_vfs_spiffs_unregister(NULL);
|
||||
}
|
||||
20
main/i2c_helper.c
Normal file
20
main/i2c_helper.c
Normal file
@ -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 = 400000
|
||||
};
|
||||
|
||||
i2c_param_config(I2C_PORT, &cfg);
|
||||
i2c_driver_install(I2C_PORT, cfg.mode, 0, 0, 0);
|
||||
}
|
||||
8
main/include/creditos.h
Normal file
8
main/include/creditos.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern volatile bool tem_creditos;
|
||||
|
||||
void creditos_task(void *pv);
|
||||
void creditos_dar(void); // função para marcar crédito
|
||||
48
main/include/display.h
Normal file
48
main/include/display.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// =======================================================
|
||||
// ENDEREÇOS I2C (definidos no .c)
|
||||
// =======================================================
|
||||
// TOP = 0x71
|
||||
// BOTTOM = 0x70
|
||||
|
||||
// Inicialização dos dois displays
|
||||
void display_init(void);
|
||||
|
||||
// =======================================================
|
||||
// RAW ACCESS (usa 16 bits de segmentos)
|
||||
// =======================================================
|
||||
void display_raw_top(int pos, uint16_t mask);
|
||||
void display_raw_bottom(int pos, uint16_t mask);
|
||||
|
||||
void display_clear_top(void);
|
||||
void display_clear_bottom(void);
|
||||
|
||||
// =======================================================
|
||||
// TEXTO E CARACTERES
|
||||
// =======================================================
|
||||
void display_char_top(int pos, char c);
|
||||
void display_char_bottom(int pos, char c);
|
||||
|
||||
void display_text_top(const char *txt); // 4 chars
|
||||
void display_text_bottom(const char *txt); // 4 chars
|
||||
|
||||
// =======================================================
|
||||
// NÚMEROS (0–9999)
|
||||
// =======================================================
|
||||
void display_digit_top(int pos, uint8_t val);
|
||||
void display_digit_bottom(int pos, uint8_t val);
|
||||
|
||||
void display_number_top(int num);
|
||||
void display_number_bottom(int num);
|
||||
|
||||
// =======================================================
|
||||
// RELÓGIO (HH:MM com DP entre horas)
|
||||
// =======================================================
|
||||
void display_set_time_top(int horas, int minutos);
|
||||
|
||||
// =======================================================
|
||||
// DEBUG
|
||||
// =======================================================
|
||||
void display_debug_segment(uint16_t bitmask);
|
||||
12
main/include/eeprom_animacao.h
Normal file
12
main/include/eeprom_animacao.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// byte carregado da EEPROM
|
||||
extern uint8_t animacao;
|
||||
|
||||
// lê o byte guardado
|
||||
void animacao_load(void);
|
||||
|
||||
// grava o byte
|
||||
void animacao_save(uint8_t v);
|
||||
3
main/include/i2c_helper.h
Normal file
3
main/include/i2c_helper.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void i2c_init(void);
|
||||
@ -1,24 +1,25 @@
|
||||
#pragma once
|
||||
#ifndef LED_DRIVER_H
|
||||
#define LED_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// === Configurações principais ===
|
||||
#define LED_PIN 18
|
||||
#define LED_COUNT 60
|
||||
#define LED_RES_HZ 10000000 // 10 MHz de resolução (0.1 µs por tick)
|
||||
|
||||
// === Interface pública ===
|
||||
esp_err_t led_init(void);
|
||||
esp_err_t led_clear(void);
|
||||
esp_err_t led_set(uint16_t index, uint8_t r, uint8_t g, uint8_t b);
|
||||
esp_err_t led_show(void);
|
||||
void led_rainbow(uint16_t offset);
|
||||
void led_spin_to(uint16_t target, uint16_t rounds, uint16_t delay_start, uint16_t delay_end);
|
||||
void led_idle_animation(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#define LED_COUNT 60
|
||||
|
||||
// inicialização do driver
|
||||
void led_driver_init(void);
|
||||
// número de LEDs
|
||||
int led_get_count(void);
|
||||
// envia os dados para o anel
|
||||
void led_show(void);
|
||||
// limpa todos os LEDs
|
||||
void led_clear(void);
|
||||
// define um LED individual
|
||||
void led_set_pixel(int index, uint8_t r, uint8_t g, uint8_t b);
|
||||
void led_all_on(uint8_t r, uint8_t g, uint8_t b);
|
||||
void led_all_off(void);
|
||||
|
||||
// spin da sorte
|
||||
void led_spin_to(uint16_t target, int min_spins, int max_spins, int base_delay_ms);
|
||||
void led_set_global_brightness(uint8_t level);
|
||||
|
||||
#endif
|
||||
|
||||
11
main/include/led_effects.h
Normal file
11
main/include/led_effects.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
void led_clock_animation(void); // <-- NOVA ANIMAÇÃO
|
||||
3
main/include/led_task.h
Normal file
3
main/include/led_task.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void led_task(void *pv);
|
||||
@ -1,5 +1,20 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "mqtt_client.h"
|
||||
#include "cJSON.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// Variáveis globais
|
||||
extern esp_mqtt_client_handle_t mqtt_client;
|
||||
extern char topic_resp[64];
|
||||
|
||||
extern uint8_t hora_r, hora_g, hora_b;
|
||||
extern uint8_t min_r, min_g, min_b;
|
||||
extern uint8_t sec_r, sec_g, sec_b;
|
||||
|
||||
extern int clock_speed_ms;
|
||||
|
||||
extern bool demo_mode;
|
||||
extern char current_mode[16];
|
||||
|
||||
void mqtt_comandos_handle(cJSON *root);
|
||||
|
||||
@ -5,10 +5,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern esp_mqtt_client_handle_t mqtt_client; // ✅ agora visível em outros .c
|
||||
static inline void mqtt_handler_loop(void) {
|
||||
// placeholder — sem função neste build
|
||||
}
|
||||
extern esp_mqtt_client_handle_t mqtt_client;
|
||||
|
||||
// Exportar os tópicos MQTT (antes eram static!)
|
||||
extern char topic_status[64];
|
||||
extern char topic_cmd[64];
|
||||
extern char topic_resp[64];
|
||||
extern char topic_lwt[64];
|
||||
|
||||
// Opcional: loop placeholder
|
||||
static inline void mqtt_handler_loop(void) {}
|
||||
|
||||
void mqtt_handler_start(void);
|
||||
|
||||
|
||||
6
main/include/premios.h
Normal file
6
main/include/premios.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
extern uint16_t premios[60];
|
||||
|
||||
uint16_t premio_da_posicao(int pos);
|
||||
16
main/include/ui.h
Normal file
16
main/include/ui.h
Normal file
@ -0,0 +1,16 @@
|
||||
// ui.h
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
MENU_OFF = 0,
|
||||
MENU_MAIN,
|
||||
MENU_PERC, // percentagem prémios
|
||||
MENU_READ, // leitura contadores
|
||||
MENU_TEST_LED, // teste roda de LEDs
|
||||
MENU_TEST_DISP, // teste display
|
||||
} menu_state_t;
|
||||
|
||||
void ui_set_menu(menu_state_t s);
|
||||
void ui_menu_next(void);
|
||||
void ui_menu_ok(void);
|
||||
void ui_task(void *pv);
|
||||
@ -1,147 +1,197 @@
|
||||
#include "led_driver.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip_encoder.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_random.h"
|
||||
#include <string.h>
|
||||
#include "esp_rom_sys.h"
|
||||
#include "premios.h"
|
||||
|
||||
static const char *TAG = "LED";
|
||||
static bool led_ready = false; // 🚦 flag de inicialização
|
||||
static rmt_channel_handle_t led_chan = NULL;
|
||||
static rmt_encoder_handle_t led_enc = NULL;
|
||||
static uint8_t led_pixels[LED_COUNT * 3];
|
||||
|
||||
// --- Conversão HSV → RGB ---
|
||||
static void hsv2rgb(uint32_t h, uint32_t s, uint32_t v,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
#define LED_PIN 18 // <-- CERTIFICA-TE disto
|
||||
#define RMT_RESOLUTION_HZ 10000000 // 10 MHz (1 tick = 0.1us)
|
||||
|
||||
typedef struct {
|
||||
uint8_t r,g,b;
|
||||
} led_color_t;
|
||||
|
||||
static led_color_t leds[LED_COUNT];
|
||||
|
||||
static rmt_channel_handle_t rmt_chan = NULL;
|
||||
static rmt_encoder_handle_t encoder = NULL;
|
||||
|
||||
static rmt_transmit_config_t tx_config = {
|
||||
.loop_count = 0
|
||||
};
|
||||
|
||||
static uint8_t global_brightness = 255;
|
||||
|
||||
void led_set_global_brightness(uint8_t level)
|
||||
{
|
||||
h %= 360;
|
||||
uint32_t rgb_max = v * 255 / 100;
|
||||
uint32_t rgb_min = rgb_max * (100 - s) / 100;
|
||||
uint32_t i = h / 60, diff = h % 60;
|
||||
uint32_t adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0: *r = rgb_max; *g = rgb_min + adj; *b = rgb_min; break;
|
||||
case 1: *r = rgb_max - adj; *g = rgb_max; *b = rgb_min; break;
|
||||
case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + adj; break;
|
||||
case 3: *r = rgb_min; *g = rgb_max - adj; *b = rgb_max; break;
|
||||
case 4: *r = rgb_min + adj; *g = rgb_min; *b = rgb_max; break;
|
||||
default:*r = rgb_max; *g = rgb_min; *b = rgb_max - adj; break;
|
||||
}
|
||||
global_brightness = level;
|
||||
}
|
||||
|
||||
// --- Inicialização ---
|
||||
esp_err_t led_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Inicializando LEDs no GPIO%d (%d LEDs)", LED_PIN, LED_COUNT);
|
||||
|
||||
rmt_tx_channel_config_t tx_cfg = {
|
||||
|
||||
void led_driver_init(void)
|
||||
{
|
||||
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
|
||||
|
||||
rmt_tx_channel_config_t chan_cfg = {
|
||||
.gpio_num = LED_PIN,
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = RMT_RESOLUTION_HZ,
|
||||
.mem_block_symbols = 64,
|
||||
.resolution_hz = LED_RES_HZ,
|
||||
.trans_queue_depth = 4,
|
||||
.trans_queue_depth = 1,
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_cfg, &led_chan));
|
||||
|
||||
led_strip_encoder_config_t enc_cfg = {.resolution = LED_RES_HZ};
|
||||
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&enc_cfg, &led_enc));
|
||||
ESP_ERROR_CHECK(rmt_enable(led_chan));
|
||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&chan_cfg, &rmt_chan));
|
||||
ESP_ERROR_CHECK(rmt_enable(rmt_chan));
|
||||
|
||||
memset(led_pixels, 0, sizeof(led_pixels));
|
||||
// WS2812B timings (EXATOS)
|
||||
rmt_bytes_encoder_config_t enc_cfg = {
|
||||
.bit0 = {
|
||||
.duration0 = 4, .level0 = 1, // 0.4us HIGH
|
||||
.duration1 = 9, .level1 = 0 // 0.9us LOW
|
||||
},
|
||||
.bit1 = {
|
||||
.duration0 = 9, .level0 = 1, // 0.9us HIGH
|
||||
.duration1 = 4, .level1 = 0 // 0.4us LOW
|
||||
},
|
||||
.flags.msb_first = 1
|
||||
};
|
||||
|
||||
led_ready = true; // ✅ marca como pronto
|
||||
led_show(); // apaga tudo ao iniciar
|
||||
return ESP_OK;
|
||||
ESP_ERROR_CHECK(rmt_new_bytes_encoder(&enc_cfg, &encoder));
|
||||
|
||||
memset(leds, 0, sizeof(leds));
|
||||
led_show();
|
||||
}
|
||||
|
||||
// --- Apagar todos os LEDs ---
|
||||
esp_err_t led_clear(void)
|
||||
void led_set_pixel(int index, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
if (!led_ready) return ESP_ERR_INVALID_STATE;
|
||||
memset(led_pixels, 0, sizeof(led_pixels));
|
||||
return led_show();
|
||||
if (index < 0 || index >= LED_COUNT) return;
|
||||
leds[index].r = r;
|
||||
leds[index].g = g;
|
||||
leds[index].b = b;
|
||||
}
|
||||
|
||||
// --- Definir cor individual ---
|
||||
esp_err_t led_set(uint16_t index, uint8_t r, uint8_t g, uint8_t b)
|
||||
void led_clear(void)
|
||||
{
|
||||
if (index >= LED_COUNT) return ESP_ERR_INVALID_ARG;
|
||||
led_pixels[index * 3 + 0] = g; // WS2812 usa ordem GRB
|
||||
led_pixels[index * 3 + 1] = r;
|
||||
led_pixels[index * 3 + 2] = b;
|
||||
return ESP_OK;
|
||||
memset(leds, 0, sizeof(leds));
|
||||
}
|
||||
|
||||
// --- Enviar buffer para o anel ---
|
||||
esp_err_t led_show(void)
|
||||
static void hw_update(void)
|
||||
{
|
||||
if (!led_ready || !led_chan || !led_enc) {
|
||||
ESP_LOGW(TAG, "⚠️ led_show() chamado antes da inicialização — ignorado");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
uint8_t buf[LED_COUNT * 3];
|
||||
|
||||
// Previne divisão por zero
|
||||
uint8_t br = (global_brightness == 0) ? 1 : global_brightness;
|
||||
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
|
||||
// Escala (r,g,b) com brilho global (0..255)
|
||||
uint8_t r = (leds[i].r * br) >> 8;
|
||||
uint8_t g = (leds[i].g * br) >> 8;
|
||||
uint8_t b = (leds[i].b * br) >> 8;
|
||||
|
||||
// GRB
|
||||
buf[i*3 + 0] = g;
|
||||
buf[i*3 + 1] = r;
|
||||
buf[i*3 + 2] = b;
|
||||
}
|
||||
|
||||
rmt_transmit_config_t tx_cfg = {.loop_count = 0};
|
||||
esp_err_t err = rmt_transmit(led_chan, led_enc,
|
||||
led_pixels, sizeof(led_pixels), &tx_cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "❌ rmt_transmit falhou (%s)", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_ERROR_CHECK(rmt_transmit(rmt_chan, encoder, buf, sizeof(buf), &tx_config));
|
||||
rmt_tx_wait_all_done(rmt_chan, -1);
|
||||
|
||||
err = rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "❌ rmt_tx_wait_all_done falhou (%s)", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
esp_rom_delay_us(60); // reset 60us
|
||||
}
|
||||
// --- Animação de rotação até posição alvo ---
|
||||
void led_spin_to(uint16_t target, uint16_t rounds, uint16_t delay_start, uint16_t delay_end)
|
||||
|
||||
|
||||
void led_show(void)
|
||||
{
|
||||
if (!led_ready) return;
|
||||
hw_update();
|
||||
}
|
||||
|
||||
uint32_t total_steps = rounds * LED_COUNT + target;
|
||||
for (uint32_t i = 0; i < total_steps; i++) {
|
||||
uint16_t pos = i % LED_COUNT;
|
||||
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;
|
||||
|
||||
for (int step = 0; step <= total_steps; step++) {
|
||||
int pos = step % n;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i == pos)
|
||||
led_set_pixel(i, 0, 40, 0); // cursor verde
|
||||
else
|
||||
led_set_pixel(i, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Limpa e acende o LED atual
|
||||
led_clear();
|
||||
led_set(pos, 255, 150, 0);
|
||||
led_show();
|
||||
|
||||
// Calcula delay progressivo (aceleração -> desaceleração)
|
||||
uint32_t delay = delay_start + ((delay_end - delay_start) * i) / total_steps;
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Flash final
|
||||
for (int j = 0; j < 3; j++) {
|
||||
led_clear();
|
||||
led_show();
|
||||
vTaskDelay(pdMS_TO_TICKS(80));
|
||||
for (int k = 0; k < LED_COUNT; k++)
|
||||
led_set(k, 255, 200, 50);
|
||||
led_show();
|
||||
vTaskDelay(pdMS_TO_TICKS(80));
|
||||
}
|
||||
}
|
||||
// =======================================
|
||||
// POSIÇÃO FINAL
|
||||
// =======================================
|
||||
|
||||
// --- Animação idle (arco-íris lento) ---
|
||||
void led_idle_animation(void)
|
||||
{
|
||||
if (!led_ready) return;
|
||||
int pos_final = total_steps % n;
|
||||
|
||||
static uint16_t hue = 0;
|
||||
for (int i = 0; i < LED_COUNT; i++) {
|
||||
uint8_t r, g, b;
|
||||
hsv2rgb((hue + i * 6) % 360, 100, 10, &r, &g, &b);
|
||||
led_set(i, r, g, b);
|
||||
}
|
||||
// LED final destacado
|
||||
led_set_pixel(pos_final, 0, 60, 0);
|
||||
led_show();
|
||||
hue = (hue + 2) % 360;
|
||||
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)
|
||||
}
|
||||
|
||||
166
main/led_effects.c
Normal file
166
main/led_effects.c
Normal file
@ -0,0 +1,166 @@
|
||||
#include "led_effects.h"
|
||||
#include "led_driver.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#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)
|
||||
{
|
||||
int n = led_get_count();
|
||||
for (int i = 0; i < n; i++) {
|
||||
led_set_pixel(i, r, g, b);
|
||||
}
|
||||
led_show();
|
||||
}
|
||||
|
||||
void led_all_off(void)
|
||||
{
|
||||
led_clear();
|
||||
led_show();
|
||||
}
|
||||
|
||||
void led_idle_animation(void)
|
||||
{
|
||||
static int pos = 0;
|
||||
int n = led_get_count();
|
||||
if (n <= 0) return;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
led_set_pixel(i, 0, 0, 0);
|
||||
|
||||
// pontinho azul
|
||||
led_set_pixel(pos, 0, 0, 16);
|
||||
led_show();
|
||||
|
||||
pos = (pos + 1) % n;
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
void led_jackpot_animation(void)
|
||||
{
|
||||
static int state = 0;
|
||||
static int step = 0;
|
||||
int n = led_get_count();
|
||||
|
||||
if (state == 0) {
|
||||
// Piscas laranja
|
||||
for (int i = 0; i < n; i++)
|
||||
led_set_pixel(i, (step & 1) ? 32 : 0, (step & 1) ? 16 : 0, 0);
|
||||
|
||||
led_show();
|
||||
|
||||
step++;
|
||||
if (step >= 12) { // 6 ON + 6 OFF
|
||||
step = 0;
|
||||
state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
else if (state == 1) {
|
||||
// Arco-íris progressivo
|
||||
for (int i = 0; i < n; i++) {
|
||||
int x = (i + step) % n;
|
||||
uint8_t r = (x * 5) & 0xFF;
|
||||
uint8_t g = ((x * 3) & 0xFF) >> 1;
|
||||
uint8_t b = ((x * 7) & 0xFF) >> 2;
|
||||
led_set_pixel(i, r, g, b);
|
||||
}
|
||||
|
||||
led_show();
|
||||
|
||||
step++;
|
||||
if (step >= n * 2) {
|
||||
step = 0;
|
||||
state = 0; // repete a animação
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void led_clock_animation(void)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm t;
|
||||
localtime_r(&now, &t);
|
||||
|
||||
int h = t.tm_hour;
|
||||
int m = t.tm_min;
|
||||
int s = t.tm_sec;
|
||||
|
||||
// Mostrar HHMM no display (14-seg)
|
||||
|
||||
display_set_time_top(h, m);
|
||||
|
||||
// LED dos segundos em azul
|
||||
led_clear(); // APAGA TUDO
|
||||
|
||||
int pos = s % LED_COUNT; // 0..59 ou 0..63
|
||||
|
||||
led_set_pixel(pos, 0, 0, 60); // azul forte
|
||||
led_show();
|
||||
|
||||
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));
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
#include "led_strip.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "led_strip";
|
||||
|
||||
struct led_strip_t {
|
||||
uint32_t length;
|
||||
uint8_t *buffer;
|
||||
rmt_channel_handle_t channel;
|
||||
uint32_t resolution;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
uint8_t b;
|
||||
} __attribute__((packed)) grb_pixel_t;
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *config, led_strip_handle_t *ret_strip) {
|
||||
if (!config || !ret_strip) return ESP_ERR_INVALID_ARG;
|
||||
|
||||
led_strip_handle_t strip = calloc(1, sizeof(struct led_strip_t));
|
||||
if (!strip) return ESP_ERR_NO_MEM;
|
||||
|
||||
strip->length = config->strip_length;
|
||||
strip->resolution = config->resolution_hz;
|
||||
strip->channel = config->rmt_channel;
|
||||
strip->buffer = calloc(strip->length, sizeof(grb_pixel_t));
|
||||
if (!strip->buffer) {
|
||||
free(strip);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
*ret_strip = strip;
|
||||
ESP_LOGI(TAG, "Novo LED strip criado (%lu LEDs)", (unsigned long)strip->length);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
if (!strip || index >= strip->length) return ESP_ERR_INVALID_ARG;
|
||||
grb_pixel_t *pixels = (grb_pixel_t *)strip->buffer;
|
||||
pixels[index].g = green;
|
||||
pixels[index].r = red;
|
||||
pixels[index].b = blue;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip) {
|
||||
if (!strip) return ESP_ERR_INVALID_ARG;
|
||||
// Envia os dados via RMT
|
||||
rmt_transmit_config_t tx_conf = { .loop_count = 0 };
|
||||
ESP_ERROR_CHECK(rmt_transmit(strip->channel, NULL, strip->buffer, strip->length * sizeof(grb_pixel_t), &tx_conf));
|
||||
ESP_ERROR_CHECK(rmt_tx_wait_all_done(strip->channel, portMAX_DELAY));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip) {
|
||||
if (!strip) return ESP_ERR_INVALID_ARG;
|
||||
memset(strip->buffer, 0, strip->length * sizeof(grb_pixel_t));
|
||||
return led_strip_refresh(strip);
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Handle para a instância de LED strip
|
||||
*/
|
||||
typedef struct led_strip_t *led_strip_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Configuração para criar um LED strip
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t strip_length; // número de LEDs
|
||||
uint32_t resolution_hz; // resolução do RMT
|
||||
rmt_channel_handle_t rmt_channel;// canal RMT a usar
|
||||
} led_strip_config_t;
|
||||
|
||||
/**
|
||||
* @brief Cria uma nova instância de LED strip
|
||||
*/
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *config, led_strip_handle_t *ret_strip);
|
||||
|
||||
/**
|
||||
* @brief Define a cor de um LED (index base 0)
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
/**
|
||||
* @brief Atualiza os LEDs com as cores definidas
|
||||
*/
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Limpa todos os LEDs (define para preto)
|
||||
*/
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "led_strip_encoder.h"
|
||||
|
||||
static const char *TAG = "led_encoder";
|
||||
|
||||
typedef struct {
|
||||
rmt_encoder_t base;
|
||||
rmt_encoder_t *bytes_encoder;
|
||||
rmt_encoder_t *copy_encoder;
|
||||
int state;
|
||||
rmt_symbol_word_t reset_code;
|
||||
} rmt_led_strip_encoder_t;
|
||||
|
||||
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
|
||||
rmt_encode_state_t state = RMT_ENCODING_RESET;
|
||||
size_t encoded_symbols = 0;
|
||||
switch (led_encoder->state) {
|
||||
case 0: // send RGB data
|
||||
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
// fall-through
|
||||
case 1: // send reset code
|
||||
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||
sizeof(led_encoder->reset_code), &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session
|
||||
state |= RMT_ENCODING_COMPLETE;
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
}
|
||||
out:
|
||||
*ret_state = state;
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
free(led_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||
led_encoder->state = RMT_ENCODING_RESET;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
led_encoder = rmt_alloc_encoder_mem(sizeof(rmt_led_strip_encoder_t));
|
||||
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||
led_encoder->base.encode = rmt_encode_led_strip;
|
||||
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||
},
|
||||
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us
|
||||
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||
.level0 = 0,
|
||||
.duration0 = reset_ticks,
|
||||
.level1 = 0,
|
||||
.duration1 = reset_ticks,
|
||||
};
|
||||
*ret_encoder = &led_encoder->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (led_encoder) {
|
||||
if (led_encoder->bytes_encoder) {
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
}
|
||||
if (led_encoder->copy_encoder) {
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
}
|
||||
free(led_encoder);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include "driver/rmt_tx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuração do encoder de LED strip (usado pelo RMT)
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; // resolução em Hz
|
||||
} led_strip_encoder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Cria um novo encoder para enviar dados WS2812 via RMT.
|
||||
*
|
||||
* @param config Configuração do encoder (resolução)
|
||||
* @param ret_encoder Ponteiro de retorno do handle do encoder
|
||||
*
|
||||
* @return ESP_OK se criado com sucesso
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config,
|
||||
rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
45
main/led_task.c
Normal file
45
main/led_task.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include "led_task.h"
|
||||
#include "led_effects.h"
|
||||
#include "eeprom_animacao.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "LED_TASK";
|
||||
|
||||
void led_task(void *pv)
|
||||
{
|
||||
ESP_LOGI(TAG, "💡 LED task iniciada");
|
||||
|
||||
while (1) {
|
||||
|
||||
switch (animacao) {
|
||||
|
||||
case 0:
|
||||
// modo idle (pontinho azul)
|
||||
led_idle_animation();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// modo relógio
|
||||
led_clock_animation();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// jackpot non-blocking
|
||||
led_jackpot_animation();
|
||||
break;
|
||||
case 3:
|
||||
led_anim_03(); // <-- AGORA É A PRINCIPAL
|
||||
break;
|
||||
|
||||
default:
|
||||
// fallback seguro
|
||||
led_idle_animation();
|
||||
break;
|
||||
}
|
||||
|
||||
// controla a velocidade de TODAS as animações
|
||||
vTaskDelay(pdMS_TO_TICKS(40)); // ~25 FPS
|
||||
}
|
||||
}
|
||||
190
main/main.c
190
main/main.c
@ -1,6 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@ -11,31 +12,49 @@
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_sntp.h"
|
||||
#include "wifi_config_portal.h"
|
||||
#include "mqtt_handler.h"
|
||||
#include "led_driver.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
#include "eeprom_virtual.h"
|
||||
#include "eeprom_tls.h"
|
||||
#include "eeprom_animacao.h"
|
||||
|
||||
#include "wifi_config_portal.h"
|
||||
#include "mqtt_handler.h"
|
||||
|
||||
#include "led_driver.h"
|
||||
#include "led_effects.h"
|
||||
|
||||
#include "creditos.h"
|
||||
#include "led_task.h"
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "i2c_helper.h"
|
||||
#include "display.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
bool modo_bloqueado = false; // definição oficial
|
||||
|
||||
#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;
|
||||
|
||||
typedef struct {
|
||||
int total_creditos;
|
||||
int total_saidas;
|
||||
} contadores_t;
|
||||
|
||||
static const char *TAG = "APP";
|
||||
static uint32_t segundos = 0;
|
||||
static bool wifi_ready = false;
|
||||
|
||||
// === Contador simples de debug ===
|
||||
// ============================
|
||||
// Task contador simples
|
||||
// ============================
|
||||
void segundos_task(void *pv) {
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
segundos++;
|
||||
// ESP_LOGI("TIMER", "⏱ %lu segundos", segundos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,55 +62,105 @@ uint32_t get_segundos(void) {
|
||||
return segundos;
|
||||
}
|
||||
|
||||
// === Task de LEDs ===
|
||||
void led_task(void *pv) {
|
||||
bool tem_creditos = false;
|
||||
while (1) {
|
||||
if (wifi_ready && tem_creditos) {
|
||||
uint16_t target = esp_random() % LED_COUNT;
|
||||
led_spin_to(target, 2, 12, 80);
|
||||
tem_creditos = false;
|
||||
} else if (wifi_ready) {
|
||||
led_idle_animation();
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // espera Wi-Fi
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
|
||||
// === Callback quando Wi-Fi e IP estão prontos ===
|
||||
// ============================
|
||||
// Callback Wi-Fi pronto
|
||||
// ============================
|
||||
static void on_wifi_connected(void) {
|
||||
wifi_ready = true;
|
||||
ESP_LOGI(TAG, "✅ Wi-Fi conectado, IP obtido — iniciando MQTT e LEDs...");
|
||||
|
||||
ESP_LOGI(TAG, "✅ Wi-Fi conectado — iniciando MQTT...");
|
||||
mqtt_handler_start();
|
||||
|
||||
ESP_LOGI(TAG, "🕒 Inicializando SNTP...");
|
||||
ESP_LOGI(TAG, "🕒 SNTP...");
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, "pool.ntp.org");
|
||||
esp_sntp_init();
|
||||
|
||||
ESP_LOGI(TAG, "💡 Inicializando LEDs...");
|
||||
led_init();
|
||||
ESP_LOGI(TAG, "💡 Inicializando driver LED...");
|
||||
led_driver_init();
|
||||
|
||||
ESP_LOGI(TAG, "🎬 Iniciando tasks LED e Créditos...");
|
||||
xTaskCreate(led_task, "led_task", 8192, NULL, 5, NULL);
|
||||
xTaskCreate(creditos_task, "creditos_task", 8192, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
// ============================
|
||||
// Stack Overflow Handler
|
||||
// ============================
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
||||
{
|
||||
esp_rom_printf("\n🧨 Stack overflow em %s!\n", pcTaskName);
|
||||
// NÃO reinicia aqui — isso bloqueia o WiFi event task e causa WDT
|
||||
// apenas marca para reiniciar depois
|
||||
esp_task_wdt_reset();
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
|
||||
// === Função principal ===
|
||||
// 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);
|
||||
}
|
||||
|
||||
void i2c_scan()
|
||||
{
|
||||
printf("\n--- A fazer scan ao I2C ---\n");
|
||||
// --- SCAN I2C ---
|
||||
for (uint8_t addr = 1; addr < 127; addr++) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t r = i2c_master_cmd_begin(I2C_PORT, cmd, 20 / portTICK_PERIOD_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (r == ESP_OK) {
|
||||
printf("ENCONTRADO I2C: 0x%02X\n", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// MAIN
|
||||
// ============================
|
||||
void app_main(void) {
|
||||
// ---------- EEPROM ----------
|
||||
|
||||
// -------- EEPROM virtual --------
|
||||
eeprom_virtual_init();
|
||||
|
||||
contadores_t contadores = {100, 25};
|
||||
@ -108,49 +177,42 @@ void app_main(void) {
|
||||
ESP_LOGW("EEPROM", "⚠️ Falha ao ler dados!");
|
||||
}
|
||||
|
||||
// ---------- NVS ----------
|
||||
// -------- NVS normal --------
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
|
||||
// ---------- LOG ----------
|
||||
esp_log_level_set("wifi", ESP_LOG_WARN);
|
||||
animacao_load();
|
||||
ESP_LOGI("ANIM", "🎨 Animação carregada = %u", animacao);
|
||||
|
||||
// ---------- SPIFFS ----------
|
||||
ESP_LOGI(TAG, "🔧 Montando SPIFFS...");
|
||||
esp_vfs_spiffs_conf_t spiffs_conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = "storage",
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&spiffs_conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "❌ Falha ao montar SPIFFS (%s)", esp_err_to_name(ret));
|
||||
} else {
|
||||
size_t total = 0, used = 0;
|
||||
esp_spiffs_info(spiffs_conf.partition_label, &total, &used);
|
||||
ESP_LOGI(TAG, "📦 SPIFFS OK: total=%d bytes, usado=%d", total, used);
|
||||
}
|
||||
|
||||
// ---------- Wi-Fi ----------
|
||||
|
||||
i2c_init(); // <- o helper entra aqui
|
||||
display_init(); // <- inicializa o HT16K33
|
||||
i2c_scan();
|
||||
display_text_top("INIT");
|
||||
|
||||
|
||||
|
||||
|
||||
// -------- Wi-Fi --------
|
||||
wifi_config_t cfg;
|
||||
bool have_creds = false;
|
||||
|
||||
if (esp_wifi_get_config(WIFI_IF_STA, &cfg) == ESP_OK) {
|
||||
if (strlen((char *)cfg.sta.ssid) > 0) {
|
||||
ESP_LOGI(TAG, "📂 Credenciais encontradas no NVS: SSID=%s", cfg.sta.ssid);
|
||||
ESP_LOGI(TAG, "📂 Credenciais no NVS: SSID=%s", cfg.sta.ssid);
|
||||
have_creds = true;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_config_portal_init(on_wifi_connected, have_creds);
|
||||
|
||||
// ---------- Tasks ----------
|
||||
xTaskCreate(segundos_task, "segundos_task", 12288, NULL, 5, NULL);
|
||||
xTaskCreate(led_task, "led_task", 12288, NULL, 5, NULL);
|
||||
// -------- Criar tasks iniciais --------
|
||||
xTaskCreate(segundos_task, "segundos_task", 4096, NULL, 5, NULL);
|
||||
|
||||
// ---------- Loop principal (livre) ----------
|
||||
// -------- Loop principal --------
|
||||
while (1) {
|
||||
if (wifi_ready) {
|
||||
mqtt_handler_loop(); // mantém MQTT ativo
|
||||
mqtt_handler_loop();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
@ -1,89 +1,409 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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];
|
||||
|
||||
#define TOPIC_RESP "esp/esp32-002/resp"
|
||||
// Variável global declarada noutro ficheiro
|
||||
extern bool modo_bloqueado;
|
||||
|
||||
void mqtt_comandos_handle(cJSON *root) {
|
||||
cJSON *id = cJSON_GetObjectItem(root, "id");
|
||||
// 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_IsNumber(id) || !cJSON_IsString(cmd)) {
|
||||
ESP_LOGW(TAG, "⚠️ Comando inválido");
|
||||
if (!cJSON_IsString(cmd)) {
|
||||
ESP_LOGW(TAG, "⚠️ Comando inválido (sem cmd)");
|
||||
return;
|
||||
}
|
||||
|
||||
int req_id = id->valueint;
|
||||
const char *comando = cmd->valuestring;
|
||||
const char *c = cmd->valuestring;
|
||||
ESP_LOGI(TAG, "📩 CMD: %s", c);
|
||||
|
||||
// -------- FS_LIST --------
|
||||
if (strcmp(comando, "FS_LIST") == 0) {
|
||||
DIR *dir = opendir("/spiffs");
|
||||
if (!dir) {
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof(resp), "{\"id\":%d,\"error\":\"fs_not_mounted\"}", req_id);
|
||||
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
|
||||
return;
|
||||
}
|
||||
|
||||
char json[2048];
|
||||
snprintf(json, sizeof(json), "{\"id\":%d,\"files\":[", req_id);
|
||||
|
||||
struct dirent *entry;
|
||||
int first = 1;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char path[512];
|
||||
snprintf(path, sizeof(path), "/spiffs/%.255s", entry->d_name);
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0) {
|
||||
// 🔧 Correção: buffer maior e limite de nome seguro
|
||||
char file_json[320];
|
||||
snprintf(file_json, sizeof(file_json),
|
||||
"{\"name\":\"%.200s\",\"size\":%ld}", entry->d_name, (long)st.st_size);
|
||||
if (!first) strlcat(json, ",", sizeof(json));
|
||||
strlcat(json, file_json, sizeof(json));
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
strlcat(json, "]}", sizeof(json));
|
||||
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, json, 0, 1, false);
|
||||
// --------------------------------------------------
|
||||
// LED_ON
|
||||
// --------------------------------------------------
|
||||
if (strcmp(c, "LED_ON") == 0) {
|
||||
led_all_on(50, 50, 50);
|
||||
}
|
||||
|
||||
// -------- CERT_REQ --------
|
||||
else if (strcmp(comando, "CERT_REQ") == 0) {
|
||||
char json[128];
|
||||
snprintf(json, sizeof(json), "{\"id\":%d,\"cmd\":\"CERT_REQ\"}", req_id);
|
||||
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, json, 0, 1, false);
|
||||
ESP_LOGI(TAG, "📤 Pedido de certificado enviado");
|
||||
// --------------------------------------------------
|
||||
else if (strcmp(c, "LED_OFF") == 0) {
|
||||
led_all_off();
|
||||
}
|
||||
|
||||
// -------- CERT_SAVE --------
|
||||
} else if (strcmp(comando, "CERT_SAVE") == 0) {
|
||||
cJSON *cert = cJSON_GetObjectItem(root, "cert");
|
||||
if (!cJSON_IsString(cert)) {
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof(resp), "{\"id\":%d,\"error\":\"missing_cert\"}", req_id);
|
||||
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
|
||||
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;
|
||||
}
|
||||
|
||||
const char *cert_str = cert->valuestring;
|
||||
uint8_t novo = v->valueint;
|
||||
animacao_save(novo);
|
||||
animacao = novo;
|
||||
|
||||
// 🔧 Correção: usar versão binária (string + terminador)
|
||||
if (eeprom_virtual_write_bin("cert", cert_str, strlen(cert_str) + 1) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "💾 Certificado gravado na EEPROM (%d bytes)", (int)strlen(cert_str));
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof(resp), "{\"id\":%d,\"cmd\":\"CERT_SAVE\",\"ok\":true}", req_id);
|
||||
esp_mqtt_client_publish(mqtt_client, TOPIC_RESP, resp, 0, 1, false);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "❌ Falha ao gravar certificado");
|
||||
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_top("MENU");
|
||||
ESP_LOGI(TAG, "📟 MENU ON");
|
||||
return;
|
||||
}
|
||||
|
||||
else if (strcmp(c, "MENU_OFF") == 0) {
|
||||
ui_set_menu(MENU_OFF);
|
||||
display_clear_top();
|
||||
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_top("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_bottom(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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
#include "esp_timer.h"
|
||||
#include "esp_event.h"
|
||||
#include "eeprom_virtual.h"
|
||||
|
||||
#include "ui.h"
|
||||
static const char *TAG = "MQTT";
|
||||
|
||||
// -------- CONFIG --------
|
||||
#define BROKER_HOST "mtqq.xupas.mywire.org"
|
||||
#define BROKER_HOST "mqtt.xupas.mywire.org"
|
||||
#define BROKER_PORT_TLS 8883
|
||||
#define BROKER_PORT_TCP 1883
|
||||
#define MQTT_USER "xupa"
|
||||
@ -26,10 +26,10 @@ esp_mqtt_client_handle_t mqtt_client = NULL;
|
||||
static esp_timer_handle_t mqtt_watchdog = NULL;
|
||||
static bool mqtt_connected = false;
|
||||
|
||||
static char topic_status[64];
|
||||
static char topic_cmd[64];
|
||||
static char topic_resp[64];
|
||||
static char topic_lwt[64];
|
||||
char topic_status[64];
|
||||
char topic_cmd[64];
|
||||
char topic_resp[64];
|
||||
char topic_lwt[64];
|
||||
|
||||
// ======================================================
|
||||
// HEARTBEAT / STATUS
|
||||
@ -43,7 +43,7 @@ static void send_status(void) {
|
||||
(unsigned long)(esp_log_timestamp() / 1000),
|
||||
(unsigned long)esp_get_free_heap_size());
|
||||
|
||||
esp_mqtt_client_publish(mqtt_client, topic_status, buf, 0, 1, true);
|
||||
esp_mqtt_client_publish(mqtt_client, topic_status, buf, 0, 1, false);
|
||||
ESP_LOGI(TAG, "📤 STATUS -> %s", buf);
|
||||
}
|
||||
|
||||
@ -67,10 +67,14 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||
switch (event->event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
mqtt_connected = true;
|
||||
led_set(0, 0, 50, 0);
|
||||
|
||||
// LED verde no pixel 0
|
||||
led_set_pixel(0, 0, 50, 0);
|
||||
led_show();
|
||||
|
||||
ESP_LOGI(TAG, "✅ MQTT conectado");
|
||||
esp_mqtt_client_publish(mqtt_client, topic_status, "online", 0, 1, true);
|
||||
esp_mqtt_client_publish(mqtt_client, topic_status, "online", 0, 1, 0);
|
||||
// esp_mqtt_client_publish(mqtt_client, topic_status, "online", 0, 1, true);
|
||||
esp_mqtt_client_subscribe(mqtt_client, topic_cmd, 1);
|
||||
send_status();
|
||||
|
||||
@ -79,26 +83,46 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
mqtt_connected = false;
|
||||
led_set(0, 50, 0, 0);
|
||||
|
||||
// LED vermelho no pixel 0
|
||||
led_set_pixel(0, 50, 0, 0);
|
||||
led_show();
|
||||
|
||||
ESP_LOGW(TAG, "⚠️ MQTT desconectado");
|
||||
if (mqtt_watchdog) esp_timer_start_periodic(mqtt_watchdog, 120000000);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DATA: {
|
||||
ESP_LOGI(TAG, "📩 [%.*s] %.*s",
|
||||
event->topic_len, event->topic,
|
||||
event->data_len, event->data);
|
||||
// Copia o payload para um buffer legível
|
||||
char json_clean[256];
|
||||
int len = event->data_len;
|
||||
|
||||
if (len >= sizeof(json_clean)) len = sizeof(json_clean) - 1;
|
||||
memcpy(json_clean, event->data, len);
|
||||
json_clean[len] = 0; // NULL terminate
|
||||
|
||||
// Remove quebras de linha
|
||||
for (int i = 0; json_clean[i]; i++) {
|
||||
if (json_clean[i] == '\r' || json_clean[i] == '\n')
|
||||
json_clean[i] = ' ';
|
||||
}
|
||||
|
||||
// Mostrar tópico + JSON limpo
|
||||
ESP_LOGI(TAG, "📩 [%.*s] %s",
|
||||
event->topic_len, event->topic,
|
||||
json_clean);
|
||||
|
||||
// JSON parse
|
||||
cJSON *root = cJSON_Parse(json_clean);
|
||||
if (root) {
|
||||
mqtt_comandos_handle(root);
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "❌ JSON inválido");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cJSON *root = cJSON_ParseWithLength(event->data, event->data_len);
|
||||
if (root) {
|
||||
mqtt_comandos_handle(root);
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "❌ JSON inválido");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGE(TAG, "❌ Erro MQTT");
|
||||
@ -109,6 +133,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ======================================================
|
||||
// HEARTBEAT TASK
|
||||
// ======================================================
|
||||
@ -143,65 +168,40 @@ void mqtt_handler_start(void) {
|
||||
mqtt_cfg.credentials.client_id = device_id;
|
||||
mqtt_cfg.credentials.username = MQTT_USER;
|
||||
mqtt_cfg.credentials.authentication.password = MQTT_PASS;
|
||||
// ======================================================
|
||||
// MQTT TLS — usa SEMPRE o certificado embutido
|
||||
// ======================================================
|
||||
mqtt_cfg.broker.address.hostname = BROKER_HOST;
|
||||
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
|
||||
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
|
||||
|
||||
// ======================================================
|
||||
// TENTA CARREGAR CERTIFICADO E USAR TLS
|
||||
// ======================================================
|
||||
bool tls_ok = false;
|
||||
static char cert_buf[4096];
|
||||
size_t cert_len = sizeof(cert_buf) - 1;
|
||||
// Certificado raiz (ISRG Root X1)
|
||||
mqtt_cfg.broker.verification.certificate = ca_cert_pem;
|
||||
ESP_LOGI(TAG, "🔐 TLS ativo (cert embutido, EEPROM ignorada)");
|
||||
|
||||
if (eeprom_virtual_read_bin("cert", cert_buf, &cert_len) == ESP_OK && cert_len > 0) {
|
||||
cert_buf[cert_len] = '\0';
|
||||
mqtt_cfg.broker.verification.certificate = strdup(cert_buf);
|
||||
mqtt_cfg.broker.address.hostname = BROKER_HOST;
|
||||
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
|
||||
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
|
||||
tls_ok = true;
|
||||
ESP_LOGI(TAG, "🔐 Certificado TLS carregado da EEPROM (%d bytes)", (int)cert_len);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "⚠️ Certificado TLS não encontrado — vai tentar padrão embutido...");
|
||||
mqtt_cfg.broker.verification.certificate = (const char *)ca_cert_pem;
|
||||
mqtt_cfg.broker.address.hostname = BROKER_HOST;
|
||||
mqtt_cfg.broker.address.port = BROKER_PORT_TLS;
|
||||
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_SSL;
|
||||
tls_ok = true;
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// TESTE: se TLS falhar, cai para TCP (sem SSL)
|
||||
// ======================================================
|
||||
if (!tls_ok) {
|
||||
ESP_LOGW(TAG, "⚠️ TLS indisponível — a usar MQTT sem SSL.");
|
||||
mqtt_cfg.broker.address.hostname = BROKER_HOST;
|
||||
mqtt_cfg.broker.address.port = BROKER_PORT_TCP;
|
||||
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
|
||||
}
|
||||
|
||||
// -------- LWT --------
|
||||
// -------- LWT --------
|
||||
mqtt_cfg.session.last_will.topic = topic_lwt;
|
||||
mqtt_cfg.session.last_will.msg = "offline";
|
||||
mqtt_cfg.session.last_will.qos = 1;
|
||||
mqtt_cfg.session.last_will.retain = true;
|
||||
mqtt_cfg.session.last_will.retain = false;
|
||||
|
||||
// ======================================================
|
||||
// INICIALIZAÇÃO DO CLIENTE
|
||||
// ======================================================
|
||||
// ======================================================
|
||||
// INICIALIZAÇÃO DO CLIENTE MQTT (TLS OBRIGATÓRIO)
|
||||
// ======================================================
|
||||
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
if (mqtt_client == NULL) {
|
||||
ESP_LOGE(TAG, "❌ Falha a inicializar MQTT — a tentar fallback TCP...");
|
||||
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
|
||||
mqtt_cfg.broker.address.port = BROKER_PORT_TCP;
|
||||
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
}
|
||||
ESP_LOGE(TAG, "❌ Falha a inicializar MQTT (TLS). Abortado.");
|
||||
return; // Nem vale a pena continuar, sem MQTT não há vida
|
||||
}
|
||||
|
||||
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
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 inicializado em %s:%lu (%s)",
|
||||
ESP_LOGI(TAG, "🚀 MQTT inicializado em %s:%lu (TLS)",
|
||||
mqtt_cfg.broker.address.hostname,
|
||||
(unsigned long)mqtt_cfg.broker.address.port,
|
||||
mqtt_cfg.broker.address.transport == MQTT_TRANSPORT_OVER_SSL ? "TLS" : "TCP");
|
||||
(unsigned long)mqtt_cfg.broker.address.port);
|
||||
|
||||
|
||||
|
||||
// -------- WATCHDOG MQTT --------
|
||||
const esp_timer_create_args_t wd_args = {
|
||||
|
||||
52
main/notas.txt
Normal file
52
main/notas.txt
Normal file
@ -0,0 +1,52 @@
|
||||
{"cmd":"STATUS"} // pede o estado: uptime + heap
|
||||
{"cmd":"LED_ON"} // liga todos os LEDs (branco fraco)
|
||||
{"cmd":"LED_OFF"} // desliga tudo
|
||||
{"cmd":"SPIN"} // rotação rápida até posição aleatória
|
||||
{"cmd":"JACKPOT"} // animação jackpot (flash + arco-íris)
|
||||
{"cmd":"REBOOT"} // reinicia o ESP
|
||||
{"cmd":"SET_ANIM","value":0} // animação idle (pontinho azul)
|
||||
{"cmd":"SET_ANIM","value":1} // relógio (horas/minutos/segundos)
|
||||
{"cmd":"SET_ANIM","value":2} // jackpot em loop
|
||||
|
||||
{"cmd":"STATUS"} // estado: uptime + heap
|
||||
{"cmd":"LED_ON"} // liga tudo
|
||||
{"cmd":"LED_OFF"} // desliga tudo
|
||||
{"cmd":"CLEAR"} // igual ao LED_OFF mas sem ser idiota
|
||||
{"cmd":"SPIN"} // spin aleatório
|
||||
{"cmd":"JACKPOT"} // animação jackpot
|
||||
{"cmd":"REBOOT"} // reinicia o ESP
|
||||
{"cmd":"SET_ANIM","value":X} // animação permanente (0,1,2...)
|
||||
|
||||
{"cmd":"SET_COLOR","r":X,"g":Y,"b":Z} // cor estática
|
||||
{"cmd":"SET_BRIGHT","value":X"} // brilho global
|
||||
{"cmd":"LED_PIXEL","n":i,"r":X,"g":Y,"b":Z} // LED individual
|
||||
|
||||
{"cmd":"DEMO_ON"} // liga demo
|
||||
{"cmd":"DEMO_OFF"} // desliga demo
|
||||
|
||||
{"cmd":"SET_MODE","value":"RELOGIO"} // modos gerais (STRING)
|
||||
{"cmd":"GET_INFO"} // resposta JSON completa
|
||||
|
||||
{"cmd":"SET_HOUR_COLOR","r":X,"g":Y,"b":Z} // ponteiro horas
|
||||
{"cmd":"SET_MIN_COLOR","r":X,"g":Y,"b":Z} // ponteiro minutos
|
||||
{"cmd":"SET_SEC_COLOR","r":X,"g":Y,"b":Z} // ponteiro segundos
|
||||
|
||||
{"cmd":"SET_CLOCK_SPEED","ms":X} // velocidade do relógio (delay)
|
||||
{"cmd":"TEST_RING"} // acender LED a LED
|
||||
|
||||
{ "cmd": "MENU_ON" }
|
||||
{ "cmd": "MENU_NEXT" }
|
||||
{ "cmd": "MENU_OK" }
|
||||
{ "cmd": "MENU_OFF" }
|
||||
|
||||
{ "cmd": "DISP_TEST" }
|
||||
{ "cmd": "LED_TEST" }
|
||||
{ "cmd": "SET_PERC", "val": 70 }
|
||||
|
||||
|
||||
esp/esp_BBC9A4/cmd // onde envias os comandos
|
||||
esp/esp_BBC9A4/resp // respostas aos comandos
|
||||
esp/esp_BBC9A4/status // heartbeat 30s
|
||||
esp/esp_BBC9A4/lwt // "offline" caso morra
|
||||
|
||||
|
||||
29
main/premios.c
Normal file
29
main/premios.c
Normal file
@ -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];
|
||||
}
|
||||
88
main/ui.c
Normal file
88
main/ui.c
Normal file
@ -0,0 +1,88 @@
|
||||
// ui.c
|
||||
#include "ui.h"
|
||||
#include "display.h"
|
||||
#include "led_driver.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <time.h>
|
||||
|
||||
static menu_state_t g_menu = MENU_OFF;
|
||||
|
||||
void ui_set_menu(menu_state_t s)
|
||||
{
|
||||
g_menu = s;
|
||||
}
|
||||
|
||||
void ui_menu_next(void)
|
||||
{
|
||||
if (g_menu == MENU_OFF) return;
|
||||
|
||||
g_menu++;
|
||||
if (g_menu > MENU_TEST_DISP)
|
||||
g_menu = MENU_MAIN;
|
||||
}
|
||||
|
||||
static void ui_render_menu(void)
|
||||
{
|
||||
switch (g_menu) {
|
||||
case MENU_MAIN: display_text_top("MENU"); break;
|
||||
case MENU_PERC: display_text_top("PERC"); break;
|
||||
case MENU_READ: display_text_top("READ"); break;
|
||||
case MENU_TEST_LED: display_text_top("LEDS"); break;
|
||||
case MENU_TEST_DISP: display_text_top("DISP"); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_menu_ok(void)
|
||||
{
|
||||
switch (g_menu) {
|
||||
case MENU_PERC:
|
||||
// aqui no futuro: mostra % atual, muda via MQTT, etc
|
||||
display_text_top("70%");
|
||||
break;
|
||||
|
||||
case MENU_READ:
|
||||
// aqui no futuro: mostra entradas/saídas da EEPROM
|
||||
display_text_top("CNT ");
|
||||
break;
|
||||
|
||||
case MENU_TEST_LED:
|
||||
// efeito simples na roda
|
||||
led_clear();
|
||||
for (int i = 0; i < led_get_count(); i++) {
|
||||
led_set_pixel(i, 0, 0, 40);
|
||||
led_show();
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_TEST_DISP:
|
||||
display_text_top("8888");
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
display_clear_top();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_task(void *pv)
|
||||
{
|
||||
while (1) {
|
||||
if (g_menu == MENU_OFF) {
|
||||
// modo normal → relógio + LED do segundo
|
||||
time_t now = time(NULL);
|
||||
struct tm t;
|
||||
localtime_r(&now, &t);
|
||||
|
||||
display_set_time_top(t.tm_hour, t.tm_min);
|
||||
// aqui podes chamar a animação da roda (se quiseres relógio visual)
|
||||
} else {
|
||||
ui_render_menu();
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
}
|
||||
@ -338,12 +338,12 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=y
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
|
||||
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
|
||||
# CONFIG_PARTITION_TABLE_CUSTOM is not set
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
# end of Partition Table
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"board/esp32-wrover-kit-3.3v.cfg"
|
||||
],
|
||||
"idf.port": "COM3",
|
||||
"idf.flashBaudRate": 921600,
|
||||
"idf.monitorBaudRate": 115200,
|
||||
"idf.flashBaudRate": "921600",
|
||||
"idf.monitorBaudRate": "115200",
|
||||
"cmake.sourceDirectory": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user