/**
* Splendida 256 Demo para Raspberry Pi Pico
* Adaptado do código original por Yaroslaw Turbin
* https://vk.com/ldirko
* https://www.reddit.com/user/ldirko/
* https://twitter.com/ldir_ko
*
* Migrado para Raspberry Pi Pico usando biblioteca Adafruit_NeoPixel
*
* Este projeto controla uma matriz de 256 LEDs WS2812B organizados em padrão Fibonacci
* com animações visuais. Usa um botão para alternar entre padrões e modos.
*
* Recursos:
* - Animações variadas para matriz de 256 LEDs
* - Paletas de cores pré-definidas
* - Controle por botão para mudança de padrão e brilho
* - Modo automático que alterna padrões automaticamente
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "core_compat.h"
#include "Adafruit_NeoPixel.hpp"
// === Definições de Pinos ===
#define DATA_PIN 28 // Pino para dados do WS2812B (GPIO28)
#define BUTTON_PIN 15 // Pino para o botão (GPIO15)
#define STATUS_LED_PIN 25 // LED embutido do Pico (GPIO25)
// === Configurações da Matriz de LEDs ===
#define NUM_LEDS 256 // Número total de LEDs
#define NUM_COLS_PLANAR 20 // Resolução da tabela de lookup planar
#define NUM_ROWS_PLANAR 20 // Resolução da tabela de lookup planar
#define NUM_LEDS_PLANAR (NUM_COLS_PLANAR*NUM_ROWS_PLANAR)
#define NUM_COLS_CILINDR 45 // Resolução da tabela de lookup cilíndrica
#define NUM_ROWS_CILINDR 11 // Resolução da tabela de lookup cilíndrica
#define NUM_LEDS_CILINDR (NUM_COLS_CILINDR*NUM_ROWS_CILINDR)
#define MAX_POWER_MILLIAMPS 700 // Limitação de corrente para segurança (mA)
#define SECONDS_PER_PALETTE 10 // Tempo para mudar para próxima paleta
// Valor de índice inválido para proteção
uint16_t lastSafeIndex = 256;
// Array de LEDs principais e LED de status
Adafruit_NeoPixel leds(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel statled(1, STATUS_LED_PIN, NEO_GRB + NEO_KHZ800);
// Buffer para padrão de chuva digital
uint8_t rain[(NUM_COLS_PLANAR+2)*(NUM_ROWS_PLANAR+2)];
// Variáveis de controle
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long lastPatternChange = 0;
unsigned long delayAutomode = 40000; // 40 segundos no modo automático
uint8_t automode = 255; // 255 = ativo, 0 = inativo
uint8_t InitNeeded = 1; // Flag para inicialização
uint8_t brightness = 255; // Brilho dos LEDs (0-255)
uint8_t gCurrentPatternNumber = 0; // Índice do padrão atual
uint8_t gCurrentPaletteNumber = 0; // Índice da paleta atual
// === Tabelas e Constantes ===
// Tabela gamma para correção de cores
const uint8_t PROGMEM exp_gamma[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7,
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12,
12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 26, 27, 28,
28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39,
39, 40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 70, 71, 72, 73, 74, 75, 77, 78, 79, 80, 82, 83, 84, 85,
87, 89, 91, 92, 93, 95, 96, 98, 99, 100, 101, 102, 105, 106, 108,
109, 111, 112, 114, 115, 117, 118, 120, 121, 123, 125, 126, 128, 130, 131,
133, 135, 136, 138, 140, 142, 143, 145, 147, 149, 151, 152, 154, 156, 158,
160, 162, 164, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187,
190, 192, 194, 196, 198, 200, 202, 204, 207, 209, 211, 213, 216, 218, 220,
222, 225, 227, 229, 232, 234, 236, 239, 241, 244, 246, 249, 251, 253, 254, 255
};
// Tabela de onda cosseno
const uint8_t cos_wave[256] = {
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 8,
9, 10, 11, 12, 14, 15, 17, 18, 20, 22, 23, 25, 27, 29, 31,
33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 57, 60, 62, 65, 68,
71, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 113,
116, 119, 122, 125, 128, 131, 135, 138, 141, 144, 147, 150, 153, 156, 159,
162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 191, 194, 197, 199, 202,
204, 207, 209, 212, 214, 216, 218, 221, 223, 225, 227, 229, 231, 232, 234,
236, 238, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251, 252, 252,
253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253,
253, 252, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 238,
236, 234, 232, 231, 229, 227, 225, 223, 221, 218, 216, 214, 212, 209, 207,
204, 202, 199, 197, 194, 191, 189, 186, 183, 180, 177, 174, 171, 168, 165,
162, 159, 156, 153, 150, 147, 144, 141, 138, 135, 131, 128, 125, 122, 119,
116, 113, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 76, 73,
71, 68, 65, 62, 60, 57, 54, 52, 49, 47, 45, 42, 40, 38, 35,
33, 31, 29, 27, 25, 23, 22, 20, 18, 17, 15, 14, 12, 11, 10,
9, 8, 6, 6, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0
};
// Tabela de mapeamento Fibonacci Planar (20x20)
static const uint16_t PROGMEM FibonPlanarTable[] = {
256, 256, 256, 256, 256, 256, 256, 256, 183, 205, 206, 207, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 182, 185, 184, 204, 256, 211, 210, 209, 208, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 181, 186, 256, 203, 213, 212, 225, 226, 227, 228, 229, 230, 256, 256, 256, 256,
256, 256, 256, 159, 180, 187, 202, 214, 256, 224, 256, 238, 237, 236, 235, 234, 231, 256, 256, 256,
256, 256, 160, 179, 188, 256, 201, 215, 223, 240, 239, 247, 248, 249, 250, 251, 233, 232, 256, 256,
256, 158, 161, 178, 189, 200, 216, 222, 241, 256, 246, 256, 3, 4, 5, 6, 252, 253, 256, 256,
256, 157, 162, 177, 190, 199, 217, 221, 242, 245, 256, 2, 22, 21, 20, 19, 7, 8, 254, 256,
256, 156, 163, 176, 191, 198, 256, 218, 220, 244, 1, 23, 26, 27, 28, 256, 18, 256, 9, 255,
135, 155, 164, 175, 256, 192, 197, 256, 219, 243, 24, 25, 48, 47, 256, 29, 256, 17, 256, 10,
136, 154, 165, 256, 174, 256, 193, 196, 195, 256, 0, 49, 50, 51, 46, 256, 30, 256, 16, 11,
134, 137, 153, 166, 256, 173, 172, 194, 171, 256, 98, 74, 73, 72, 52, 45, 256, 31, 15, 12,
133, 138, 256, 152, 167, 168, 169, 170, 147, 146, 256, 97, 75, 256, 71, 53, 44, 32, 14, 256,
256, 132, 139, 256, 151, 150, 149, 148, 145, 123, 122, 99, 96, 76, 70, 54, 43, 33, 13, 256,
256, 111, 131, 140, 141, 142, 143, 144, 256, 124, 121, 100, 95, 77, 69, 55, 42, 34, 256, 256,
256, 256, 112, 130, 129, 256, 256, 256, 125, 256, 120, 101, 94, 78, 68, 56, 41, 35, 256, 256,
256, 256, 110, 113, 114, 128, 127, 126, 118, 119, 102, 256, 93, 79, 67, 57, 40, 36, 256, 256,
256, 256, 256, 109, 108, 115, 116, 117, 104, 103, 256, 92, 80, 66, 256, 58, 39, 256, 256, 256,
256, 256, 256, 256, 256, 107, 106, 105, 256, 90, 91, 81, 65, 256, 59, 38, 256, 256, 256, 256,
256, 256, 256, 256, 256, 86, 87, 88, 89, 83, 82, 64, 256, 60, 37, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 85, 84, 62, 63, 256, 61, 256, 256, 256, 256, 256, 256, 256
};
// Tabela de mapeamento Fibonacci Cilíndrico (45x11)
static const uint16_t PROGMEM FibonCilindrTable[] = {
85, 256, 256, 86, 256, 110, 256, 111, 256, 134, 256, 135, 256, 158, 256, 256, 159, 182, 256, 256, 183, 256, 207, 256, 208, 256, 231, 256, 232, 256, 255, 256, 256, 12, 13, 256, 256, 36, 256, 38, 37, 61, 256, 256, 62,
256, 88, 87, 108, 109, 256, 112, 132, 133, 256, 136, 156, 157, 256, 161, 160, 181, 256, 185, 184, 205, 206, 256, 209, 229, 230, 256, 233, 256, 254, 256, 10, 11, 14, 256, 34, 35, 256, 39, 256, 60, 256, 64, 63, 84,
89, 106, 107, 256, 114, 113, 131, 256, 138, 137, 155, 256, 163, 162, 179, 180, 256, 186, 203, 204, 256, 211, 210, 228, 256, 235, 234, 252, 253, 256, 9, 16, 15, 256, 33, 256, 41, 40, 58, 59, 256, 65, 82, 83, 256,
105, 256, 116, 115, 129, 130, 256, 139, 256, 154, 256, 164, 256, 178, 256, 188, 187, 202, 256, 256, 212, 226, 227, 256, 236, 256, 251, 256, 7, 8, 17, 256, 256, 32, 256, 42, 256, 57, 256, 67, 66, 81, 256, 91, 90,
256, 117, 256, 128, 256, 256, 140, 152, 153, 256, 165, 256, 177, 256, 189, 256, 201, 256, 214, 213, 225, 256, 256, 237, 249, 250, 256, 6, 256, 18, 256, 30, 31, 256, 43, 256, 56, 256, 68, 256, 80, 256, 92, 256, 104,
118, 256, 127, 256, 256, 141, 151, 256, 256, 166, 256, 176, 256, 190, 256, 200, 256, 215, 256, 224, 256, 256, 238, 248, 256, 256, 5, 256, 19, 256, 29, 256, 256, 44, 256, 55, 256, 69, 256, 79, 256, 93, 256, 103, 256,
256, 126, 256, 256, 142, 150, 256, 256, 167, 256, 175, 256, 191, 256, 199, 256, 216, 256, 223, 256, 256, 239, 247, 256, 256, 4, 256, 20, 256, 28, 256, 256, 45, 256, 54, 256, 70, 256, 78, 256, 94, 256, 102, 256, 119,
125, 256, 256, 143, 149, 256, 256, 168, 256, 174, 256, 192, 256, 198, 256, 217, 256, 222, 256, 256, 240, 246, 256, 256, 3, 256, 21, 256, 27, 256, 256, 46, 256, 53, 256, 71, 256, 77, 256, 95, 256, 101, 256, 120, 256,
256, 256, 144, 148, 256, 256, 169, 256, 173, 256, 193, 256, 197, 256, 218, 256, 221, 256, 256, 241, 245, 256, 256, 2, 256, 22, 256, 26, 256, 256, 47, 256, 52, 256, 72, 256, 76, 256, 96, 256, 100, 256, 121, 256, 124,
256, 145, 147, 256, 256, 170, 256, 172, 256, 194, 256, 196, 256, 219, 256, 220, 256, 256, 242, 244, 256, 256, 1, 256, 23, 256, 25, 256, 256, 48, 256, 51, 256, 73, 256, 75, 256, 97, 256, 99, 256, 122, 256, 123, 256,
146, 256, 256, 256, 256, 256, 171, 256, 256, 256, 195, 256, 256, 256, 256, 256, 256, 243, 256, 256, 256, 0, 256, 24, 256, 256, 256, 256, 49, 256, 50, 256, 256, 256, 74, 256, 256, 256, 98, 256, 256, 256, 256, 256, 256
};
// Tabela de mapeamento Fibonacci para índice físico
const uint8_t PROGMEM fibonacciToPhysical[] = {
0, 98, 195, 49, 146, 243, 74, 171, 24, 122,
219, 50, 147, 244, 97, 194, 25, 123, 220, 73,
170, 1, 99, 196, 48, 145, 242, 75, 172, 23,
121, 218, 51, 148, 245, 96, 193, 26, 124, 221,
72, 169, 2, 100, 197, 47, 144, 241, 76, 173,
22, 120, 217, 52, 149, 246, 95, 192, 27, 125,
222, 71, 168, 3, 101, 198, 46, 143, 240, 77,
174, 21, 119, 216, 53, 150, 247, 94, 191, 28,
126, 223, 70, 167, 4, 102, 199, 45, 142, 239,
78, 175, 20, 118, 215, 54, 151, 248, 93, 190,
29, 127, 224, 69, 166, 5, 103, 200, 44, 141,
238, 79, 176, 19, 117, 214, 55, 152, 249, 92,
189, 30, 128, 225, 68, 165, 6, 104, 201, 43,
140, 237, 80, 177, 18, 116, 213, 56, 153, 250,
91, 188, 31, 129, 226, 67, 164, 7, 105, 202,
42, 139, 236, 81, 178, 17, 115, 212, 57, 154,
251, 90, 187, 32, 130, 227, 66, 163, 8, 106,
203, 41, 138, 235, 82, 179, 16, 114, 211, 58,
155, 252, 89, 186, 33, 131, 228, 65, 162, 9,
107, 204, 40, 137, 234, 83, 180, 15, 113, 210,
59, 156, 253, 88, 185, 34, 132, 229, 64, 161,
10, 108, 205, 39, 136, 233, 84, 181, 14, 112,
209, 60, 157, 254, 87, 184, 35, 133, 230, 63,
160, 11, 109, 206, 38, 135, 232, 85, 182, 13,
111, 208, 61, 158, 255, 86, 183, 36, 134, 231,
62, 159, 12, 110, 207, 37
};
/**
* @brief Converter coordenadas XY para índice na tabela planar Fibonacci
* @param x Coordenada X
* @param y Coordenada Y
* @return Índice do LED na matriz
*/
uint16_t XY_fibon_PLANAR(uint8_t x, uint8_t y) {
uint16_t ledsindex = FibonPlanarTable[y*NUM_COLS_PLANAR+x];
return (ledsindex);
}
/**
* @brief Converter coordenadas XY para índice na tabela cilíndrica Fibonacci
* @param x Coordenada X
* @param y Coordenada Y
* @return Índice do LED na matriz
*/
uint16_t XY_CILINDR(uint8_t x, uint8_t y) {
uint16_t ledsindex = FibonCilindrTable[y*NUM_COLS_CILINDR+x];
return (ledsindex);
}
/**
* @brief Aplica correção gamma a todos os LEDs
* Melhora a aparência visual das cores, especialmente em baixo brilho
*/
void GammaCorrection() {
uint8_t r, g, b;
for (uint16_t i=0; i<NUM_LEDS; i++) {
r = leds.getPixels()[i*3];
g = leds.getPixels()[i*3+1];
b = leds.getPixels()[i*3+2];
leds.getPixels()[i*3] = exp_gamma[r];
leds.getPixels()[i*3+1] = exp_gamma[g];
leds.getPixels()[i*3+2] = exp_gamma[b];
}
}
// === Definição de paletas de cores ===
/* 4×4 = 16 bytes */
uint8_t ib_jul01_gp[16] = {
0, 194, 1, 1,
94, 1, 29, 18,
132, 57, 131, 28,
255, 113, 1, 1
};
/* 5×4 = 20 bytes */
uint8_t es_vintage_57_gp[20] = {
0, 2, 1, 1,
53, 18, 1, 0,
104, 69, 29, 1,
153, 167, 135, 10,
255, 46, 56, 4
};
/* 7×4 = 28 bytes */
uint8_t Sunset_Real_gp[28] = {
0, 120, 0, 0,
22, 179, 22, 0,
51, 255, 104, 0,
85, 167, 22, 18,
135, 100, 0, 103,
198, 16, 0, 130,
255, 0, 0, 160
};
/* 7×4 = 28 bytes */
uint8_t BlacK_Blue_Magenta_White_gp[28] = {
0, 0, 0, 0,
42, 0, 0, 45,
84, 0, 0, 255,
127, 42, 0, 255,
170, 255, 0, 255,
212, 255, 55, 255,
255, 255, 255, 255
};
// Estrutura para armazenar informações da paleta
typedef struct {
uint8_t* colors;
uint8_t colorCount;
const char* name;
} GradientPalette;
// Paletas disponíveis
GradientPalette gGradientPalettes[] = {
{ Sunset_Real_gp, Sunset_Real_gp_COUNT, "Sunset_Real_gp" },
{ ib_jul01_gp, ib_jul01_gp_COUNT, "ib_jul01_gp" },
{ es_vintage_57_gp, es_vintage_57_gp_COUNT, "es_vintage_57_gp" },
{ BlacK_Blue_Magenta_White_gp, BlacK_Blue_Magenta_White_gp_COUNT, "BlacK_Blue_Magenta_White_gp" }
};
// Quantidade de paletas disponíveis
const uint8_t gGradientPaletteCount =
sizeof(gGradientPalettes) / sizeof(gGradientPalettes[0]);
// Paleta atual
uint8_t* gCurrentPalette = Sunset_Real_gp;
uint8_t* gTargetPalette = Sunset_Real_gp;
/**
* @brief Obtém a cor de uma posição específica na paleta atual
* @param pos Posição na paleta (0-255)
* @param brightness Brilho (0-255)
* @return Cor RGB empacotada em 32 bits
*/
uint32_t ColorFromPalette(uint8_t* palette, uint8_t pos, uint8_t brightness) {
uint8_t paletteSize = 0;
// Determinar o número de entradas na paleta
if (palette == Sunset_Real_gp) {
paletteSize = Sunset_Real_gp_COUNT;
} else if (palette == ib_jul01_gp) {
paletteSize = ib_jul01_gp_COUNT;
} else if (palette == es_vintage_57_gp) {
paletteSize = es_vintage_57_gp_COUNT;
} else if (palette == BlacK_Blue_Magenta_White_gp) {
paletteSize = BlacK_Blue_Magenta_White_gp_COUNT;
} else {
paletteSize = 4; // Valor padrão seguro
}
// Encontrar as duas cores para interpolar
uint8_t color1Index = 0;
uint8_t color2Index = 0;
for (uint8_t i = 0; i < paletteSize - 1; i++) {
if (pos >= palette[i*3] && pos <= palette[(i+1)*3]) {
color1Index = i;
color2Index = i + 1;
break;
}
}
// Se não encontrou o intervalo, use os extremos
if (color2Index == 0) {
color1Index = paletteSize - 2;
color2Index = paletteSize - 1;
}
// Calcular a posição relativa entre as duas cores
uint8_t pos1 = palette[color1Index*3];
uint8_t pos2 = palette[color2Index*3];
float ratio = 0;
if (pos2 != pos1) {
ratio = (float)(pos - pos1) / (float)(pos2 - pos1);
}
// Interpolar as cores
uint8_t r1 = palette[color1Index*3+1];
uint8_t g1 = palette[color1Index*3+2];
uint8_t b1 = palette[color1Index*3+3];
uint8_t r2 = palette[color2Index*3+1];
uint8_t g2 = palette[color2Index*3+2];
uint8_t b2 = palette[color2Index*3+3];
uint8_t r = r1 + (r2 - r1) * ratio;
uint8_t g = g1 + (g2 - g1) * ratio;
uint8_t b = b1 + (b2 - b1) * ratio;
// Aplicar o brilho
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
/**
* @brief Interpolação linear entre duas paletas
* @param currentPalette Paleta atual
* @param targetPalette Paleta de destino
* @param steps Número de passos para a interpolação
*/
void nblendPaletteTowardPalette(uint8_t* currentPalette, uint8_t* targetPalette, uint8_t steps) {
// Implementação simplificada - apenas copia a paleta alvo para a atual
// Em uma implementação completa, faria a interpolação gradual
for (int i = 0; i < 16; i++) {
currentPalette[i] = targetPalette[i];
}
}
// ===== Padrões de animação para mapa planar =====
/**
* @brief Inicializa o array de pontos para o efeito de chuva digital
*/
void raininit() {
for (int i = 0; i < NUM_LEDS_PLANAR; i++) {
rain[i] = 0;
}
// Determina quantos pontos de chuva iniciar
uint8_t rainNumb = random(40, 60);
// Distribui os pontos aleatoriamente
for (int i = 0; i < rainNumb; i++) {
rain[random(NUM_LEDS_PLANAR)] = 1;
}
}
/**
* @brief Atualiza posições da chuva digital
*/
void updaterain() {
static int speed = 1;
for (uint8_t j = 0; j < NUM_ROWS_PLANAR; j++) {
int yindex = (j + speed) % NUM_ROWS_PLANAR * NUM_COLS_PLANAR;
for (uint8_t i = 0; i < NUM_COLS_PLANAR; i++) {
uint8_t layer = rain[yindex + i];
if (layer) {
uint16_t ledIndex = XY_fibon_PLANAR((NUM_COLS_PLANAR - 1) - i, (NUM_ROWS_PLANAR - 1) - j);
if (ledIndex != lastSafeIndex) {
leds.setPixelColor(ledIndex, 0, 255, 0); // Cor verde para a "chuva digital"
}
}
}
}
// Desvanecer todos os LEDs para criar o efeito de rastro
for (int i = 0; i < NUM_LEDS; i++) {
uint32_t color = leds.getPixelColor(i);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Reduzir a intensidade em 20%
r = r * 0.8;
g = g * 0.8;
b = b * 0.8;
leds.setPixelColor(i, r, g, b);
}
speed++;
}
/**
* @brief Altera aleatoriamente a posição de um ponto da chuva
*/
void changepattern() {
int rand1 = random(NUM_LEDS_PLANAR);
int rand2 = random(NUM_LEDS_PLANAR);
if (rain[rand1] && !rain[rand2]) {
rain[rand1] = 0;
rain[rand2] = 1;
}
}
/**
* @brief Efeito de chuva digital
*/
void DigitalRain() {
if (InitNeeded) {
raininit();
InitNeeded = 0;
leds.clear();
}
// Atualiza a chuva a cada 80ms
static unsigned long lastRainUpdate = 0;
if (millis() - lastRainUpdate > 80) {
updaterain();
lastRainUpdate = millis();
}
// Muda o padrão a cada 15ms
static unsigned long lastPatternChange = 0;
if (millis() - lastPatternChange > 15) {
changepattern();
lastPatternChange = millis();
}
}
/**
* @brief Efeito de padrão diagonal com cores do arco-íris
*/
void DiagonalPattern() {
uint16_t ms = millis() / 8;
for (uint8_t j = 0; j < NUM_ROWS_PLANAR; j++) {
for (uint8_t i = 0; i < NUM_COLS_PLANAR; i++) {
int ledIndex = XY_fibon_PLANAR(i, j);
if (ledIndex == lastSafeIndex) continue;
// Cria um padrão de cores em diagonal
uint8_t hue = (i << 3) + (j << 3) + ms;
// Converte HSV para RGB
uint8_t r, g, b;
// Setores de 60 graus (dividindo 256 em 4 partes)
uint8_t sector = (hue / 64) % 4;
uint8_t offset = hue % 64;
uint8_t complement = 64 - offset;
switch (sector) {
case 0: // 0-60 graus: vermelho para amarelo
r = 255;
g = offset * 4;
b = 0;
break;
case 1: // 60-120 graus: amarelo para verde
r = complement * 4;
g = 255;
b = 0;
break;
case 2: // 120-180 graus: verde para azul
r = 0;
g = complement * 4;
b = offset * 4;
break;
case 3: // 180-240 graus: azul para vermelho
r = offset * 4;
g = 0;
b = complement * 4;
break;
}
leds.setPixelColor(ledIndex, r, g, b);
}
}
}
/**
* @brief Efeito de fogo usando noise
*/
void fire2021() {
int a = millis();
int a1 = a / 3;
for (uint8_t j = 0; j < NUM_ROWS_PLANAR; j++) {
for (uint8_t i = 0; i < NUM_COLS_PLANAR; i++) {
int ledIndex = XY_fibon_PLANAR(i, j);
if (ledIndex == lastSafeIndex) continue;
// Usar um algoritmo de Perlin noise para criar o efeito de fogo
// Simplificado para esta implementação
uint8_t noise1 = (i * 90 + j * 90 + a) % 256;
uint8_t noise2 = (i * 90 + j * 90 + a1) % 256;
uint8_t noise = (noise1 + noise2) / 2;
// Ajustar a intensidade com base na posição vertical
uint8_t intensity = 255 - ((j * 255) / (NUM_ROWS_PLANAR + 4));
// Combinar o noise com a intensidade
uint8_t value = (noise < intensity) ? intensity - noise : 0;
// Converter para cor de fogo (vermelho para amarelo)
uint8_t r = value;
uint8_t g = value > 128 ? (value - 128) * 2 : 0;
uint8_t b = 0;
leds.setPixelColor(ledIndex, r, g, b);
}
}
}
/**
* @brief Ondas de distorção no mapa planar
*/
void Distortion_Waves_plan() {
uint8_t speed = 5;
uint8_t w = 2;
uint8_t scale = 2;
uint16_t a = millis() / 24;
uint16_t a2 = a / 2;
uint16_t a3 = a / 3;
// Criar múltiplos centros de onda que se movem
uint16_t cx = ((sin(a/100.0) * NUM_COLS_PLANAR/2) + NUM_COLS_PLANAR/2) * scale;
uint16_t cy = ((cos(a/120.0) * NUM_ROWS_PLANAR/2) + NUM_ROWS_PLANAR/2) * scale;
uint16_t cx1 = ((sin(a/130.0) * NUM_COLS_PLANAR/2) + NUM_COLS_PLANAR/2) * scale;
uint16_t cy1 = ((cos(a/150.0) * NUM_ROWS_PLANAR/2) + NUM_ROWS_PLANAR/2) * scale;
uint16_t cx2 = ((sin(a/170.0) * NUM_COLS_PLANAR/2) + NUM_COLS_PLANAR/2) * scale;
uint16_t cy2 = ((cos(a/140.0) * NUM_ROWS_PLANAR/2) + NUM_ROWS_PLANAR/2) * scale;
uint16_t xoffs = 0;
for (int x = 0; x < NUM_COLS_PLANAR; x++) {
xoffs += scale;
uint16_t yoffs = 0;
for (int y = 0; y < NUM_ROWS_PLANAR; y++) {
uint16_t index = XY_fibon_PLANAR(x, y);
if (index == lastSafeIndex) continue;
yoffs += scale;
// Calcular distorção para cada componente de cor
uint8_t rdistort = cos_wave[((x*8)+a) % 256] / 2;
uint8_t gdistort = cos_wave[((x*8)-a2+32) % 256] / 2;
uint8_t bdistort = cos_wave[((x*8)+a3+64) % 256] / 2;
// Calcular valores para cada componente de cor
uint16_t dx1 = (xoffs - cx) * (xoffs - cx);
uint16_t dy1 = (yoffs - cy) * (yoffs - cy);
uint16_t dx2 = (xoffs - cx1) * (xoffs - cx1);
uint16_t dy2 = (yoffs - cy1) * (yoffs - cy1);
uint16_t dx3 = (xoffs - cx2) * (xoffs - cx2);
uint16_t dy3 = (yoffs - cy2) * (yoffs - cy2);
uint8_t valueR = rdistort + w * (a - ((dx1 + dy1) >> 7));
uint8_t valueG = gdistort + w * (a2 - ((dx2 + dy2) >> 7));
uint8_t valueB = bdistort + w * (a3 - ((dx3 + dy3) >> 7));
valueR = cos_wave[valueR];
valueG = cos_wave[valueG];
valueB = cos_wave[valueB];
leds.setPixelColor(index, valueR, valueG, valueB);
}
}
GammaCorrection();
}
/**
* @brief Efeito hipnótico com cores RGB
*/
void RGB_hiphotic() {
int a = millis() / 6;
for (int x = 0; x < NUM_COLS_PLANAR; x++) {
for (int y = 0; y < NUM_ROWS_PLANAR; y++) {
int index = XY_fibon_PLANAR(x, y);
if (index == lastSafeIndex) continue;
// Calcular componentes de cor usando funções seno e cosseno
uint8_t b = sin((x-8) * cos((y+20)*4)/4 + a) * 255;
uint8_t g = ((sin(x*16 + a/3) + cos(y*8 + a/2)) / 2) * 255;
uint8_t r = sin(cos(x*8 + a/3) + sin(y*8 + a/4) + a) * 255;
leds.setPixelColor(index, r, g, b);
}
}
GammaCorrection();
}
/**
* @brief Desenha uma linha gradiente do centro para o ponto especificado
*/
void mydrawLine_PB(uint8_t x1, uint8_t y1) {
uint8_t xsteps = abs(NUM_COLS_PLANAR/2 - x1) + 1;
uint8_t ysteps = abs(NUM_ROWS_PLANAR/2 - y1) + 1;
uint8_t steps = xsteps >= ysteps ? xsteps : ysteps;
for (uint16_t i = 1; i <= steps; i++) {
// Interpolação linear entre centro e ponto final
uint8_t dx = NUM_COLS_PLANAR/2 + ((x1 - NUM_COLS_PLANAR/2) * i) / steps;
uint8_t dy = NUM_ROWS_PLANAR/2 + ((y1 - NUM_ROWS_PLANAR/2) * i) / steps;
uint16_t index = XY_fibon_PLANAR(dx, dy);
if (index == lastSafeIndex) continue;
// Cor gradiente azul
uint8_t intensity = (i * 255) / steps;
uint8_t r = 0;
uint8_t g = intensity / 4;
uint8_t b = intensity;
// Mesclar com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = (r + existingR) / 2;
g = (g + existingG) / 2;
b = (b + existingB) / 2;
leds.setPixelColor(index, r, g, b);
// Efeito de brilho que diminui com a distância
leds.setPixelColor(index, r * (steps - i) / steps,
g * (steps - i) / steps,
b * (steps - i) / steps);
}
}
/**
* @brief Efeito de bola de plasma com linhas radiantes
*/
void PlasmaBall() {
// Coordenadas que se movem com funções seno
uint8_t x1 = (sin(millis() / 1800.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x2 = (sin(millis() / 2300.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x3 = (sin(millis() / 2700.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x4 = (sin(millis() / 3100.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x5 = (sin(millis() / 3300.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x6 = (sin(millis() / 2500.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t y1 = (cos(millis() / 2000.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y2 = (cos(millis() / 2600.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y3 = (cos(millis() / 1500.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y4 = (cos(millis() / 2700.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y5 = (cos(millis() / 3000.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y6 = (cos(millis() / 1900.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
// Desvanecer os LEDs para criar o efeito de rastro
for (int i = 0; i < NUM_LEDS; i++) {
uint32_t color = leds.getPixelColor(i);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Reduzir a intensidade em 15%
r = r * 0.85;
g = g * 0.85;
b = b * 0.85;
leds.setPixelColor(i, r, g, b);
}
// Desenhar linhas radiantes
mydrawLine_PB(x1, y1);
mydrawLine_PB(x2, y2);
mydrawLine_PB(x3, y3);
mydrawLine_PB(x4, y4);
mydrawLine_PB(x5, y5);
mydrawLine_PB(x6, y6);
}
/**
* @brief Desenha uma linha entre dois pontos
*/
void mydrawLine_Fl(uint8_t x, uint8_t y, uint8_t x1, uint8_t y1, uint8_t r, uint8_t g, uint8_t b, bool dot) {
uint8_t xsteps = abs(x - x1) + 1;
uint8_t ysteps = abs(y - y1) + 1;
uint8_t steps = xsteps >= ysteps ? xsteps : ysteps;
for (uint8_t i = 1; i <= steps; i++) {
// Interpolação linear entre os pontos
uint8_t dx = x + ((x1 - x) * i) / steps;
uint8_t dy = y + ((y1 - y) * i) / steps;
uint16_t index = XY_fibon_PLANAR(dx, dy);
if (index == lastSafeIndex) continue;
// Adicionar cor à existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
leds.setPixelColor(index, existingR + r, existingG + g, existingB + b);
}
if (dot) {
// Adicionar pontos brancos nas extremidades
uint16_t index = XY_fibon_PLANAR(x, y);
if (index != lastSafeIndex) {
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
leds.setPixelColor(index, existingR + 40, existingG + 40, existingB + 40);
}
index = XY_fibon_PLANAR(x1, y1);
if (index != lastSafeIndex) {
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
leds.setPixelColor(index, existingR + 40, existingG + 40, existingB + 40);
}
}
}
/**
* @brief Efeito de linhas voando e se conectando
*/
void F_lying() {
static uint8_t hue = 0;
// Mudar a cor a cada 30ms
static unsigned long lastHueChange = 0;
if (millis() - lastHueChange > 30) {
hue++;
lastHueChange = millis();
}
// Coordenadas que se movem com funções seno
uint8_t x1 = (sin(millis() / 1800.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x2 = (sin(millis() / 2300.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x3 = (sin(millis() / 2700.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x4 = (sin(millis() / 3100.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t x5 = (sin(millis() / 3300.0) * (NUM_COLS_PLANAR/2)) + (NUM_COLS_PLANAR/2);
uint8_t y1 = (cos(millis() / 2000.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y2 = (cos(millis() / 2600.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y3 = (cos(millis() / 1500.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y4 = (cos(millis() / 2700.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
uint8_t y5 = (cos(millis() / 3000.0) * (NUM_ROWS_PLANAR/2)) + (NUM_ROWS_PLANAR/2);
// Converter HSV para RGB
uint8_t r, g, b;
// Setores de 60 graus (dividindo 256 em 4 partes)
uint8_t sector = (hue / 64) % 4;
uint8_t offset = hue % 64;
uint8_t complement = 64 - offset;
switch (sector) {
case 0: // 0-60 graus: vermelho para amarelo
r = 255;
g = offset * 4;
b = 0;
break;
case 1: // 60-120 graus: amarelo para verde
r = complement * 4;
g = 255;
b = 0;
break;
case 2: // 120-180 graus: verde para azul
r = 0;
g = complement * 4;
b = offset * 4;
break;
case 3: // 180-240 graus: azul para vermelho
r = offset * 4;
g = 0;
b = complement * 4;
break;
}
// Desvanecer os LEDs para criar o efeito de rastro
for (int i = 0; i < NUM_LEDS; i++) {
uint32_t color = leds.getPixelColor(i);
uint8_t existingR = (color >> 16) & 0xFF;
uint8_t existingG = (color >> 8) & 0xFF;
uint8_t existingB = color & 0xFF;
// Reduzir a intensidade em 40%
existingR = existingR * 0.6;
existingG = existingG * 0.6;
existingB = existingB * 0.6;
leds.setPixelColor(i, existingR, existingG, existingB);
}
// Desenhar linhas conectadas
mydrawLine_Fl(x1, y1, x2, y2, r, g, b, true);
mydrawLine_Fl(x2, y2, x3, y3, r, g, b, true);
mydrawLine_Fl(x2, y2, x4, y4, r, g, b, true);
mydrawLine_Fl(x3, y3, x4, y4, r, g, b, true);
mydrawLine_Fl(x3, y3, x1, y1, r, g, b, true);
mydrawLine_Fl(x4, y4, x5, y5, r, g, b, true);
}
/**
* @brief Efeito de túnel RGB
*/
void RGBTunnel() {
int t = millis() >> 3; // Dividir por 8
for (uint8_t y = 0; y < NUM_ROWS_PLANAR; y++) {
for (uint8_t x = 0; x < NUM_COLS_PLANAR; x++) {
int ledIndex = XY_fibon_PLANAR(x, y);
if (ledIndex == lastSafeIndex) continue;
// Calcular distância do centro
int centerX = NUM_COLS_PLANAR / 2;
int centerY = NUM_ROWS_PLANAR / 2;
// Adicionar distorção ao centro
int distortedX = x - (sin(t/30.0) * 4);
int distortedY = y - (cos(t/50.0) * 4);
// Calcular distância euclidiana ao centro
float dist = sqrt((distortedX - centerX) * (distortedX - centerX) +
(distortedY - centerY) * (distortedY - centerY));
// Converter para posição na paleta de cores
uint8_t pos = ((int)(dist * 24 - t*2)) % 256;
// Obter cor da paleta atual
uint32_t color = ColorFromPalette(gCurrentPalette, pos, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(ledIndex);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 3); // Divisão por 8
g = existingG + ((g - existingG) >> 3);
b = existingB + ((b - existingB) >> 3);
leds.setPixelColor(ledIndex, r, g, b);
}
}
}
// ===== Padrões de animação para mapa cilíndrico =====
/**
* @brief Caleidoscópio RGB 1
*/
void RGB_Caleidoscope1() {
uint16_t a = millis() / 8;
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Usar funções seno/cosseno para gerar componentes de cor
uint8_t valueR = (sin8(i*16 + a) + cos8(j*16 + a/2)) / 2;
uint8_t valueG = sin8(j*16 + a/2 + sin8(valueR + a) / 16);
uint8_t valueB = cos8(i*16 + j*16 - a/2 + valueG);
leds.setPixelColor(index, valueR, valueG, valueB);
}
}
GammaCorrection();
}
/**
* @brief Caleidoscópio RGB 2
*/
void RGB_Caleidoscope2() {
uint16_t a = millis() / 8;
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Usar diferentes frequências para cada componente de cor
uint8_t r = (sin8(i*28 + a) + cos8(j*28 + a)) >> 1;
uint8_t g = (sin8(i*28 - a) + cos8(j*28 + (a>>1))) >> 1;
uint8_t b = sin8(j*26 + a);
leds.setPixelColor(index, r, g, b);
}
}
GammaCorrection();
}
/**
* @brief Ondas de distorção no mapa cilíndrico
*/
void Distortion_Waves_cilindr() {
uint8_t speed = 5;
uint8_t w = 2;
uint8_t scale = 2;
uint16_t a = millis() / 24;
uint16_t a2 = a / 2;
uint16_t a3 = a / 3;
// Criar múltiplos centros de onda que se movem
uint16_t cx = ((sin(a/100.0) * NUM_COLS_CILINDR/2) + NUM_COLS_CILINDR/2) * scale;
uint16_t cy = ((cos(a/120.0) * NUM_ROWS_CILINDR/2) + NUM_ROWS_CILINDR/2) * scale;
uint16_t cx1 = ((sin(a/130.0) * NUM_COLS_CILINDR/2) + NUM_COLS_CILINDR/2) * scale;
uint16_t cy1 = ((cos(a/150.0) * NUM_ROWS_CILINDR/2) + NUM_ROWS_CILINDR/2) * scale;
uint16_t cx2 = ((sin(a/170.0) * NUM_COLS_CILINDR/2) + NUM_COLS_CILINDR/2) * scale;
uint16_t cy2 = ((cos(a/140.0) * NUM_ROWS_CILINDR/2) + NUM_ROWS_CILINDR/2) * scale;
uint16_t xoffs = 0;
for (int x = 0; x < NUM_COLS_CILINDR; x++) {
xoffs += scale;
uint16_t yoffs = 0;
for (int y = 0; y < NUM_ROWS_CILINDR; y++) {
uint16_t index = XY_CILINDR(x, y);
if (index == lastSafeIndex) continue;
yoffs += scale;
// Calcular distorção para cada componente de cor
uint8_t rdistort = cos_wave[((cos_wave[((x<<3)+a) % 256] + cos_wave[((y<<3)-a2) % 256] + a3) % 256)] >> 1;
uint8_t gdistort = cos_wave[((cos_wave[((x<<3)-a2) % 256] + cos_wave[((y<<3)+a3) % 256] + a+32) % 256)] >> 1;
uint8_t bdistort = cos_wave[((cos_wave[((x<<3)+a3) % 256] + cos_wave[((y<<3)-a) % 256] + a2+64) % 256)] >> 1;
// Calcular valores para cada componente de cor
uint16_t dx1 = (xoffs - cx) * (xoffs - cx);
uint16_t dy1 = (yoffs - cy) * (yoffs - cy);
uint16_t dx2 = (xoffs - cx1) * (xoffs - cx1);
uint16_t dy2 = (yoffs - cy1) * (yoffs - cy1);
uint16_t dx3 = (xoffs - cx2) * (xoffs - cx2);
uint16_t dy3 = (yoffs - cy2) * (yoffs - cy2);
uint8_t valueR = rdistort + w * (a - ((dx1 + dy1) >> 7));
uint8_t valueG = gdistort + w * (a2 - ((dx2 + dy2) >> 7));
uint8_t valueB = bdistort + w * (a3 - ((dx3 + dy3) >> 7));
valueR = cos_wave[valueR];
valueG = cos_wave[valueG];
valueB = cos_wave[valueB];
leds.setPixelColor(index, valueR, valueG, valueB);
}
}
GammaCorrection();
}
/**
* @brief Efeito de borboleta de fogo
*/
void FireButterfly() {
uint16_t a = millis() / 3;
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Usar ruído para criar a aparência de fogo
uint8_t noise1 = (i * 60 + a) % 256;
uint8_t noise2 = (j * 5 + a) % 256;
uint8_t noise3 = (a / 3) % 256;
uint8_t noise = (noise1 + noise2 + noise3) / 3;
// Ajustar a intensidade com base na posição vertical
uint8_t intensity = 255 - ((j * 255) / (NUM_ROWS_CILINDR + 2));
// Combinar o noise com a intensidade
uint8_t value = (noise < intensity) ? intensity - noise : 0;
// Converter para cor de fogo (vermelho para amarelo)
uint8_t r = value;
uint8_t g = value > 128 ? (value - 128) * 2 : 0;
uint8_t b = 0;
leds.setPixelColor(index, r, g, b);
}
}
}
/**
* @brief Efeito de redemoinho
*/
void Swirl() {
uint16_t a = millis() / 7;
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
// Adicionar deslocamento horizontal que varia com o tempo
uint16_t shifted_i = (i + a/32) % NUM_COLS_CILINDR;
uint16_t index = XY_CILINDR(shifted_i, j);
if (index == lastSafeIndex) continue;
// Calcular matiz com base na posição e tempo
uint8_t hue = i*56 + (a>>2) + (sin8(j*16 + a) >> 1);
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, hue, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 4); // Divisão por 16
g = existingG + ((g - existingG) >> 4);
b = existingB + ((b - existingB) >> 4);
leds.setPixelColor(index, r, g, b);
}
}
}
/**
* @brief Padrão cilíndrico com variação de escala
*/
void Cilindrical_Pattern() {
uint16_t a = millis() / 12;
// Escala que varia ao longo do tempo
float scale = (sin(a/32.0 * 3.14159/180) * 16) + 32;
float scale1 = 0;
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Calcular matiz
uint8_t hue = (sin8(i + (int)scale1 + a) + sin8((j*16) + a)) / 2;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, hue, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 4); // Divisão por 16
g = existingG + ((g - existingG) >> 4);
b = existingB + ((b - existingB) >> 4);
leds.setPixelColor(index, r, g, b);
}
scale1 += scale;
}
}
/**
* @brief Efeito espiral
*/
void Spiral() {
uint16_t a = millis() / 8;
float scale = (sin(a/32.0 * 3.14159/180) * 18) - 6;
float scale1 = 0;
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Calcular posição na paleta com base nas coordenadas e tempo
uint8_t pos = (i*255/(NUM_COLS_CILINDR-1) + j*255/(NUM_ROWS_CILINDR-1)) + (int)scale1 + a + sin(a/260.0) * 25;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, pos, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 4); // Divisão por 16
g = existingG + ((g - existingG) >> 4);
b = existingB + ((b - existingB) >> 4);
leds.setPixelColor(index, r, g, b);
}
scale1 += scale;
}
}
/**
* @brief Efeito espiral 2 com duas direções
*/
void Spiral2() {
if (InitNeeded) {
raininit();
InitNeeded = 0;
leds.clear();
}
uint16_t a = millis() / 6;
float scale = sin(a/32.0 * 3.14159/180) * 12;
float scale1 = 0;
// Primeira metade - espiral que gira numa direção
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
for (int j = 0; j < NUM_ROWS_CILINDR/2; j++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Calcular posição na paleta
uint8_t pos = (i*255/(NUM_COLS_CILINDR-1)*3 + j*255/(NUM_ROWS_CILINDR-1)/8) + a + sin(a/260.0) * 25;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, pos, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 3); // Divisão por 8
g = existingG + ((g - existingG) >> 3);
b = existingB + ((b - existingB) >> 3);
leds.setPixelColor(index, r, g, b);
}
scale1 += scale;
}
// Segunda metade - espiral que gira na direção oposta
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
for (int j = NUM_ROWS_CILINDR/2; j < NUM_ROWS_CILINDR; j++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Calcular posição na paleta (direção oposta)
uint8_t pos = (i*255/(NUM_COLS_CILINDR-1)*3 - j*255/(NUM_ROWS_CILINDR-1)/8) - a + sin(a/260.0) * 25;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, pos, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 3); // Divisão por 8
g = existingG + ((g - existingG) >> 3);
b = existingB + ((b - existingB) >> 3);
leds.setPixelColor(index, r, g, b);
}
scale1 += scale;
}
}
/**
* @brief Efeito flor
*/
void Flower() {
uint16_t a = millis() / 8;
for (int j = 0; j < NUM_ROWS_CILINDR; j++) {
for (int i = 0; i < NUM_COLS_CILINDR; i++) {
uint16_t index = XY_CILINDR(i, j);
if (index == lastSafeIndex) continue;
// Calcular posição na paleta com padrão de onda tipo flor
uint8_t pos = (j*255/(NUM_ROWS_CILINDR-1) + sin8((i*8+a)) + sin8(i*30-a) + a) / 2;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, pos, 255);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(index);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
r = existingR + ((r - existingR) >> 4); // Divisão por 16
g = existingG + ((g - existingG) >> 4);
b = existingB + ((b - existingB) >> 4);
leds.setPixelColor(index, r, g, b);
}
}
}
// ===== Padrões de animação para mapa Fibonacci =====
/**
* @brief Efeito pride com ondas arco-íris
* Adaptado do código de Mark Kriegsman
*/
void pride() {
static uint16_t sPseudotime = 0;
static uint16_t sLastMillis = 0;
static uint16_t sHue16 = 0;
uint8_t sat8 = beatsin(87, 220, 250);
uint8_t brightdepth = beatsin(341, 96, 224);
uint16_t brightnessthetainc16 = beatsin(203, (25 * 256), (40 * 256));
uint8_t msmultiplier = beatsin(147, 23, 60);
uint16_t hue16 = sHue16;
uint16_t hueinc16 = beatsin(113, 1, 3000);
uint16_t ms = millis();
uint16_t deltams = ms - sLastMillis;
sLastMillis = ms;
sPseudotime += deltams * msmultiplier;
sHue16 += deltams * beatsin(400, 5, 9);
uint16_t brightnesstheta16 = sPseudotime;
for (uint16_t i = 0; i < NUM_LEDS; i++) {
hue16 += hueinc16;
uint8_t hue8 = hue16 / 256;
brightnesstheta16 += brightnessthetainc16;
uint16_t b16 = sin16(brightnesstheta16) + 32768;
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
// Converter HSV para RGB
uint32_t color;
if (sat8 == 0) {
color = leds.Color(bri8, bri8, bri8); // Branco
} else {
uint8_t sector = (hue8 / 43) % 6;
uint8_t offset = hue8 % 43;
uint8_t p = (bri8 * (255 - sat8)) / 256;
uint8_t q = (bri8 * (255 - ((sat8 * offset) / 43))) / 256;
uint8_t t = (bri8 * (255 - ((sat8 * (43 - offset)) / 43))) / 256;
switch (sector) {
case 0: color = leds.Color(bri8, t, p); break;
case 1: color = leds.Color(q, bri8, p); break;
case 2: color = leds.Color(p, bri8, t); break;
case 3: color = leds.Color(p, q, bri8); break;
case 4: color = leds.Color(t, p, bri8); break;
default: color = leds.Color(bri8, p, q); break;
}
}
uint16_t pixelnumber = (NUM_LEDS - 1) - i;
uint16_t ledindex = fibonacciToPhysical[pixelnumber];
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(ledindex);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
r = existingR + ((r - existingR) >> 2); // Divisão por 4
g = existingG + ((g - existingG) >> 2);
b = existingB + ((b - existingB) >> 2);
leds.setPixelColor(ledindex, r, g, b);
}
}
/**
* @brief Função para gerar seno de 16 bits
*/
int16_t sin16(uint16_t theta) {
static const int16_t base[] = { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
static const uint8_t slope[] = { 49, 48, 44, 38, 31, 23, 14, 4 };
uint8_t offset = (theta & 0x3F) >> 3; // 0..7
uint8_t section = (theta >> 6) & 0x3F; // 0..63
int16_t value;
if (section < 16) {
value = base[offset] + ((slope[offset] * (theta & 0x07)) >> 3);
} else if (section < 32) {
value = base[7-offset] + ((slope[7-offset] * (0x07 - (theta & 0x07))) >> 3);
} else if (section < 48) {
value = -base[offset] - ((slope[offset] * (theta & 0x07)) >> 3);
} else {
value = -base[7-offset] - ((slope[7-offset] * (0x07 - (theta & 0x07))) >> 3);
}
return value;
}
/**
* @brief Função beatsin para gerar ondas senoidais ao longo do tempo
*/
uint8_t beatsin(uint16_t beats_per_minute, uint8_t lowest, uint8_t highest) {
uint8_t beat = beat(beats_per_minute);
uint8_t beatsin = ((sin8(beat) + 128) * (highest - lowest)) >> 8;
return lowest + beatsin;
}
/**
* @brief Função beat para gerar um contador de batidas
*/
uint8_t beat(uint16_t beats_per_minute) {
return (uint8_t)(((millis() * beats_per_minute) / 60000) & 0xFF);
}
/**
* @brief Função sin8 para gerar seno de 8 bits
*/
uint8_t sin8(uint8_t theta) {
return cos_wave[(theta + 64) % 256];
}
/**
* @brief Efeito de ondas coloridas
* Adaptado do código de Mark Kriegsman
*/
void colorwaves() {
static uint16_t sPseudotime = 0;
static uint16_t sLastMillis = 0;
static uint16_t sHue16 = 0;
uint8_t sat8 = beatsin(87, 220, 250);
uint8_t brightdepth = beatsin(341, 96, 224);
uint16_t brightnessthetainc16 = beatsin(203, (25 * 256), (40 * 256));
uint8_t msmultiplier = beatsin(147, 23, 60);
uint16_t hue16 = sHue16;
uint16_t hueinc16 = beatsin(113, 300, 1500);
uint16_t ms = millis();
uint16_t deltams = ms - sLastMillis;
sLastMillis = ms;
sPseudotime += deltams * msmultiplier;
sHue16 += deltams * beatsin(400, 5, 9);
uint16_t brightnesstheta16 = sPseudotime;
for (uint16_t i = 0; i < NUM_LEDS; i++) {
hue16 += hueinc16;
uint8_t hue8 = hue16 / 256;
uint16_t h16_128 = hue16 >> 7;
if (h16_128 & 0x100) {
hue8 = 255 - (h16_128 >> 1);
} else {
hue8 = h16_128 >> 1;
}
brightnesstheta16 += brightnessthetainc16;
uint16_t b16 = sin16(brightnesstheta16) + 32768;
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
uint8_t index = hue8;
index = (index * 240) / 256;
// Obter cor da paleta
uint32_t color = ColorFromPalette(gCurrentPalette, index, bri8);
uint16_t pixelnumber = (NUM_LEDS - 1) - i;
uint16_t ledindex = fibonacciToPhysical[pixelnumber];
// Mesclar suavemente com a cor existente
uint32_t existingColor = leds.getPixelColor(ledindex);
uint8_t existingR = (existingColor >> 16) & 0xFF;
uint8_t existingG = (existingColor >> 8) & 0xFF;
uint8_t existingB = existingColor & 0xFF;
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
r = existingR + ((r - existingR) >> 2); // Divisão por 4
g = existingG + ((g - existingG) >> 2);
b = existingB + ((b - existingB) >> 2);
leds.setPixelColor(ledindex, r, g, b);
}
}
// ===== Padrões sem mapeamento especial =====
/**
* @brief Efeito de cintilação suave
* Adaptado de Mark Kriegsman
*/
void SoftTwinkles() {
if (InitNeeded) {
leds.clear();
InitNeeded = 0;
}
// Cores de base para o efeito
static const uint8_t lightR = 0;
static const uint8_t lightG = 4;
static const uint8_t lightB = 4;
static const uint8_t darkR = 0;
static const uint8_t darkG = 2;
static const uint8_t darkB = 2;
for (int i = 0; i < NUM_LEDS; i++) {
uint32_t color = leds.getPixelColor(i);
if (color == 0) continue; // Pular pixels escuros
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Verificar se o componente azul é ímpar ou par
if (b & 1) {
// Escurecer se for ímpar
r = r > darkR ? r - darkR : 0;
g = g > darkG ? g - darkG : 0;
b = b > darkB ? b - darkB : 0;
} else {
// Clarear se for par
r = r < 255 - lightR ? r + lightR : 255;
g = g < 255 - lightG ? g + lightG : 255;
b = b < 255 - lightB ? b + lightB : 255;
}
leds.setPixelColor(i, r, g, b);
}
// Acender aleatoriamente novos pixels
int j = random(NUM_LEDS);
if (leds.getPixelColor(j) == 0) {
leds.setPixelColor(j, 0, 0, 2); // Iniciar com azul baixo
}
// Apagar aleatoriamente alguns pixels
j = random(NUM_LEDS);
if ((leds.getPixelColor(j) & 0xFF) & 1) { // Se azul for ímpar
uint32_t color = leds.getPixelColor(j);
uint8_t b = (color & 0xFF) - 1;
leds.setPixelColor(j, (color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, b);
}
}
// Lista de padrões para ciclar
// Cada padrão é definido como uma função separada
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = {
Cilindrical_Pattern, PlasmaBall,
FireButterfly, F_lying, RGBTunnel,
Flower, Distortion_Waves_cilindr,
colorwaves, DiagonalPattern, Distortion_Waves_plan,
FireButterfly,
SoftTwinkles, Spiral2, pride, RGB_Caleidoscope2,
RGB_Caleidoscope1, Swirl, RGB_hiphotic, Spiral,
DigitalRain, fire2021
};
const uint8_t gPatternsCount = sizeof(gPatterns) / sizeof(gPatterns[0]);
// === Funções de controle de botão ===
/**
* @brief Função de callback para clique simples no botão
* Avança para o próximo padrão e desativa o modo automático
*/
void handleButtonClick() {
printf("Clique! Próximo padrão. Modo automático DESATIVADO\n");
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % gPatternsCount;
InitNeeded = 1; // Sinaliza que inicialização é necessária
previousMillis = millis();
automode = 0;
statled.setPixelColor(0, 255, 0, 0); // LED vermelho
statled.show();
}
/**
* @brief Função de callback para clique duplo no botão
* Altera o brilho dos LEDs ciclicamente
*/
void handleButtonDoubleClick() {
printf("Clique duplo! Próximo nível de brilho\n");
static uint8_t brIndex = 7;
static uint8_t bright[] = {0, 16, 32, 64, 96, 128, 160, 255}; // 8 passos
brIndex = (brIndex + 1) % 8;
brightness = bright[brIndex];
leds.setBrightness(brightness);
printf("Brilho: %d\n", brightness);
statled.setPixelColor(0, 0, 0, 255); // LED azul
statled.show();
}
/**
* @brief Função de callback para pressão longa no botão
* Ativa o modo automático
*/
void handleButtonLongPress() {
printf("Pressão longa!\n");
printf("Modo automático ATIVADO\n");
previousMillis = millis();
automode = 255;
statled.setPixelColor(0, 0, 255, 0); // LED verde
statled.show();
}
/**
* @brief Verifica se o botão foi pressionado e gerencia o debounce
*/
void handleButton() {
static bool lastButtonState = HIGH;
static unsigned long lastDebounceTime = 0;
static unsigned long clickTime = 0;
static unsigned long longPressTime = 0;
static bool isLongPress = false;
static bool doubleClickPending = false;
static bool buttonReleased = true;
const unsigned long debounceDelay = 50;
const unsigned long doubleClickTime = 300;
const unsigned long longPressDelay = 1000;
// Ler o estado atual do botão
bool buttonState = gpio_get(BUTTON_PIN);
// Verificar se o estado mudou
if (buttonState != lastButtonState) {
lastDebounceTime = millis();
}
// Se passou tempo suficiente desde a última mudança
if ((millis() - lastDebounceTime) > debounceDelay) {
// Se o estado atual é diferente do estado estável
if (buttonState == LOW && buttonReleased) { // Botão pressionado
buttonReleased = false;
clickTime = millis();
longPressTime = clickTime;
isLongPress = false;
if (doubleClickPending) {
// Clique duplo detectado
handleButtonDoubleClick();
doubleClickPending = false;
}
}
else if (buttonState == HIGH && !buttonReleased) { // Botão liberado
buttonReleased = true;
// Se não foi longpress, pode ser um clique simples ou parte de um duplo
if (!isLongPress) {
if (!doubleClickPending) {
doubleClickPending = true;
// Configura um temporizador para verificar clique duplo
// No Pico sem RTOS precisamos gerenciar isso manualmente
} else {
doubleClickPending = false;
}
}
}
// Verificar clique duplo em andamento
if (doubleClickPending && millis() - clickTime > doubleClickTime) {
// Tempo de duplo clique expirou, considerar clique simples
handleButtonClick();
doubleClickPending = false;
}
// Verificar longpress
if (!buttonReleased && !isLongPress && (millis() - longPressTime) > longPressDelay) {
isLongPress = true;
handleButtonLongPress();
}
}
lastButtonState = buttonState;
}
/**
* @brief Verifica se é hora de ativar o modo automático
*/
void checkAutomodeOn() {
currentMillis = millis();
if ((currentMillis - previousMillis) >= delayAutomode && automode == 0) {
printf("Modo automático ATIVADO (tempo ocioso)\n");
previousMillis = currentMillis;
automode = 255;
statled.setPixelColor(0, 0, 255, 0); // LED verde
statled.show();
}
}
/**
* @brief Efeito de fade out para transição entre padrões
* @param steps Número de passos para o fade
*/
void FadeOut(uint8_t steps) {
uint8_t originalBrightness = leds.getBrightness();
for (int i = 0; i <= steps; i++) {
gPatterns[gCurrentPatternNumber]();
uint8_t fadeOut = originalBrightness - (originalBrightness * i / steps);
leds.setBrightness(fadeOut);
leds.show();
sleep_ms(10);
}
leds.setBrightness(originalBrightness);
}
/**
* @brief Efeito de fade in para transição entre padrões
* @param steps Número de passos para o fade
*/
void FadeIn(uint8_t steps) {
uint8_t originalBrightness = leds.getBrightness();
for (int i = steps; i >= 0; i--) {
gPatterns[gCurrentPatternNumber]();
uint8_t fadeIn = originalBrightness - (originalBrightness * i / steps);
leds.setBrightness(fadeIn);
leds.show();
sleep_ms(10);
}
leds.setBrightness(originalBrightness);
}
/**
* @brief Inicialização do sistema
*/
void setup() {
// Inicializar stdio para uso do printf
stdio_init_all();
printf("Splendida 256 para Raspberry Pi Pico\n");
printf("Inicializando...\n");
// Configurar pino do botão como entrada com pull-up
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN, GPIO_IN);
gpio_pull_up(BUTTON_PIN);
// Inicializar fitas de LED
leds.begin();
statled.begin();
// Definir brilho inicial
leds.setBrightness(brightness);
leds.clear();
// Escolher paleta aleatória para iniciar
gCurrentPalette = gGradientPalettes[random(gGradientPaletteCount)].colors;
gTargetPalette = gCurrentPalette;
printf("Inicialização concluída!\n");
if (automode) {
printf("Modo automático ATIVADO\n");
} else {
printf("Modo automático DESATIVADO\n");
}
// Piscar LED de status para indicar que o sistema está pronto
for (int i = 0; i < 3; i++) {
statled.setPixelColor(0, 0, 255, 0);
statled.show();
sleep_ms(200);
statled.clear();
statled.show();
sleep_ms(200);
}
}
/**
* @brief Loop principal
*/
void loop() {
// Adicionar entropia ao gerador de números aleatórios
srand(millis());
// Verificar entrada do botão
handleButton();
// Verificar modo automático
checkAutomodeOn();
// Mudar paleta a cada SECONDS_PER_PALETTE segundos
static unsigned long lastPaletteChange = 0;
if (millis() - lastPaletteChange > SECONDS_PER_PALETTE * 1000) {
gCurrentPaletteNumber = random(gGradientPaletteCount);
gTargetPalette = gGradientPalettes[gCurrentPaletteNumber].colors;
lastPaletteChange = millis();
printf("Mudando para paleta: %s\n", gGradientPalettes[gCurrentPaletteNumber].name);
}
// Fazer transição suave entre paletas
static unsigned long lastPaletteBlend = 0;
if (millis() - lastPaletteBlend > 40) {
nblendPaletteTowardPalette(gCurrentPalette, gTargetPalette, 16);
lastPaletteBlend = millis();
}
// Mudar de padrão no modo automático a cada 15 segundos
static unsigned long lastPatternChange = 0;
if (automode && millis() - lastPatternChange > 15000) {
FadeOut(150);
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % gPatternsCount;
InitNeeded = 1;
FadeIn(200);
lastPatternChange = millis();
printf("Alterado para padrão #%d\n", gCurrentPatternNumber);
}
// Executar o padrão atual
gPatterns[gCurrentPatternNumber]();
// Desvanecer LED de status gradualmente
uint32_t statusColor = statled.getPixelColor(0);
uint8_t r = ((statusColor >> 16) & 0xFF) * 0.99;
uint8_t g = ((statusColor >> 8) & 0xFF) * 0.99;
uint8_t b = (statusColor & 0xFF) * 0.99;
statled.setPixelColor(0, r, g, b);
// Atualizar LEDs
statled.show();
leds.show();
// Pequeno atraso para evitar cintilação em altas taxas de atualização
sleep_us(100);
}
/**
* @brief Função principal
*/
int main() {
setup();
while (true) {
loop();
}
return 0;
}