No tutorial de hoje, começaremos uma série que demonstrará como criar um bastão de LED. Com esse bastão de LED totalmente personalizado, ele muda de cor e exibe efeitos incríveis com apenas o toque de botão em controle remoto ou o movimento de um joystick.

Com ele, você pode usar esse bastão em festas, filmagens para efeitos especiais ou até como uma decoração criativa para sua casa, vídeos e fotos. O melhor de tudo? Você tem o controle total sobre as cores, o brilho e os efeitos de luz, tudo em um simples código que será explicado neste artigo!

O tutorial será dividido nas seguintes partes, caso deseje ir para algum tópico específico, basta clicar em seu respectivo título: 

Lista de materiais

O que é um bastão de LED? 

Quais são os itens necessários para construir um bastão de LED? 

Esquema de ligação do projeto

Montagem do Circuito 

Programação do bastão de LED com ESP32

Vídeo do bastão de LED funcionando

Conclusão


Lista de materiais

Para esse tutorial, precisaremos dos seguintes componentes:

01 - Fita de LED RGB - 144 LEDs

01 - Placa Super Mini ESP32-C3

01 - Display OLED 128x64 Px - 1.3" - 4 Pin - Branco

01 - Conversor DC/DC - Step Down - 75W - XL4015

01 - KIT Controle Infrared - IR

01 - Módulo Joystick de Navegação 5D

01 - Protoboard 400 Pontos

01 - Kit Jumper Macho Fêmea - 20 pçs

01 - Kit Jumpers Rígidos Formato de U - 140 pçs

01 - Case para bateria de lítio 

01 - Placa Proteção BMS 2S 7,4V 20A Bateria Lipo Li-ion Balance

02 - Baterias de lítio

 
O que é um bastão de LED? 

O bastão de LED é um dispositivo composto por LEDs, comumente utilizado em diversas aplicações, como luzes de estúdio, iluminação para fotos e até mesmo para decoração de um cenário. Através do controle IR e do módulo Joystick, é possível controlar as cores que o bastão reproduz dentro da escala RGB. Em sua composição, também temos efeitos práticos pré-programados, que trazem interatividade de luz para as mais diversas cenas. 

Mais abaixo, abordaremos como realizar todo o protótipo do bastão de LED, construindo seu código, esquema elétrico e ligação para teste.

 

Quais itens são necessários para construir o bastão de LED? 

 Para começarmos a construir o bastão de LED, primeiro temos que definir os componentes que serão necessários. Para começar, utilizaremos uma Fita de LED RGB - 144 LEDs, como a fonte de iluminação do bastão de LED RGB. A escolha desta fita se deu por ter uma distância entre os LEDs menor, o que proporciona mais LEDs no bastão, além de também sua potência. Algumas fitas de LED prometem entregar uma boa potência, porém, durante os testes realizados para a escolha final da fita de LED, elas não suportavam a potência em que eram descritas. 

Para realizar o controle das cores, luminosidade, saturação e efeitos que a fita pode realizar, vamos utilizar uma placa Super Mini ESP32-C6 - ESP32-C6FH4. Com seu tamanho reduzido e sua capacidade de memória, é a escolha ideal para o seu projeto, sustentando até mesmo possíveis atualizações futuras que possibilitem a conexão com a internet e a nuvem para controle do bastão.

Para realizarmos a navegação dentro de todas as configurações e controles disponíveis no código que construiremos para controlar a fita de LEDs, vamos utilizar um display OLED 128x64 Px - 1.3" - 4 Pin - Branco para mostrar cada menu e configurações atuais que estarão rodando no código enquanto o circuito estiver alimentado. 

Para tornar todo o projeto portátil, optamos por utilizar duas baterias de lítio que possuem grande capacidade de armazenamento de energia, além de necessitarmos de uma case que seja compatível com o tamanho das baterias.  

Além da case e das baterias, teremos que utilizar um módulo para carregamento compatível com as baterias que escolhemos. O módulo em questão é a placa BMS 2S de 7,4V 20A com balanceamento, que garante que as baterias foram corretamente carregadas. 

Como a tensão de uma bateria de lítio é de 3.7V, ao utilizar duas baterias em série, a tensão passa a ser de 7,4V. Com isso, para conseguirmos alimentar a fita de LED e o ESP32-C6 Super Mini, necessitamos de um regulador de tensão, que suporte a tensão de entrada e que consiga regular ela, independente da variação que teremos ao consumir a energia armazenada nas baterias. E nesse caso, o escolhido foi o conversor DC/DC - Step Down - 75W - XL4015 por suportar uma potência de até 75W.

Com todos esses componentes em mãos, o que nos falta agora serão os meios para conseguirmos navegar e também as conexões necessárias para realizarmos a montagem do esquema elétrico. 

Para o controle entre os menus e modos, utilizaremos um Módulo Joystick de Navegação 5D para realizar o controle, quando estiver com o bastão em mãos.

Já para realizar o controle quando se está longe do bastão, vamos utilizar um kit Controle Infrared - IR, permitindo que você controle as cores à distância. 

Já para a ligação elétrica, iremos utilizar os seguintes componentes: uma protobard 400 pontos para acoplarmos o ESP32-C6 Super Mini e o Display OLED 1.3", Jumpers Macho Fêmea para utilização no controle IR e no módulo Joystick 5D, além de também utilizarmos Jumpers rígidos em Formato de U, para um melhor acabamento ao projeto. 

Assim que todos os componentes foram definidos, vamos para a montagem do esquema de ligação elétrica de todo o protótipo.

Esquema de ligação do projeto

Após a definição dos componentes que iremos utilizar, o circuito elétrico precisa ser desenhado e montado. Nesta etapa, iremos realizar o diagrama elétrico do protótipo. Como descrito mais acima, a ligação do circuito elétrico foi realizada da seguinte maneira: Primeiro, necessitamos de alimentação para o ESP32-C3 Super mini, Display Oled, Receptor Ir do controle, módulo Joystick, e fita de LEDs que opera em 5V. Para regular todo esse circuito em 5V, utilizaremos o regulador de tensão, que vai baixar a tensão de 8,4V das baterias para 5V, compatível com o circuito. 
Para carregamento da bateria, utilizamos o módulo BMS 2S, conectado a uma fonte de 9V. Veja abaixo como o circuito ficou, junto ao pinout de todo o circuito:

ESP32-C3 Super Mini Componentes
GPIO 0 Botão SET do Joystick 5D
GPIO 1 Botão Direito do Joystick 5D
GPIO 2 Botão Esquerdo do Joystick 5D
GPIO 3 Botão Baixo do Joystick 5D
GPIO 4 Botão Cima do Joystick 5D
GPIO 5 Receptor do Controle IR
GPIO 8 Pino SDA do Display
GPIO 9 Pino SCL do Display
GPIO 10 Pino de Controle da Fita de LED RGB

 


Montagem do Circuito

Seguindo os padrões do esquema elétrico descrito acima, a montagem do circuito por completo ficou da seguinte maneira:

Programação do bastão de LED com ESP32

Para programar o bastão de LED, definiremos 3 telas, onde cada tela, tem seu controle específico. A primeira tela, terá o controle dos LEDs somente na cor branca, variando a temperatura em Kelvin da cor. Já na segunda tela, controlaremos todo o espectro RGB e também brilho e saturação do bastão de LED. Por fim, na terceira tela, teremos os efeitos de cada módulo. Essa interface é baseada em bastões de LEDs comuns disponíveis no mercado.

Fique atento à versão das placas na IDE Arduino. A biblioteca do IRremote só é compatível com o ESP32, até a versão 2.0.17. A partir dessa versão, o código não irá compilar. Então, se a sua versão estiver acima ou abaixo dessa, instale a versão 2.0.17 para conseguir subir o código no ESP32-C3.

Após a instalação da versão das placas compatível com o controle IR, alguns problemas foram aparecendo durante a criação desse código.

O primeiro problema ocorreu, ao tentarmos adicionar rapidamente o incremento nas variáveis das cores quando o botão é pressionado. Isso ocorreu por ter que, em suma, controlar a adição um a um enquanto o botão é pressionado, e quando ele for mantido pressionado, ele precisa adicionar mais rapidamente, sem o debounce. 

Para a resolução desse problema, a lógica utilizada foi a seguinte: 

Primeiro é criada uma função, e são definidas três variáveis, unsigned long currentMillis = millis(); que serão utilizadas para obter o tempo atual. Em seguida, duas variáveis do tipo inteiro são criadas: int reading1 e int reading2, e são definidas como a leitura digital do pino 1 e 2 respectivamente, que retornam o valor de 0 e 1, a depender do estado do botão:

void botoes() 
{ unsigned long currentMillis = millis(); // Obtém o tempo atual // Leitura do estado dos botões com debounce int reading1 = digitalRead(1); // Lê o estado do botão 1 int reading2 = digitalRead(2); // Lê o estado do botão 2

Dentro do código, as variáveis lastDebounceTime1 e lastDebounceTime2, são utilizadas para debounce do botão.
Em seguida, temos o código que aplica a leitura do botão 1. Se esse botão for identificado como pressionado através da variável reading1 = LOW, o código verifica se ele já estava pressionado anteriormente em !button1Pressed e se o tempo desde a última leitura válida passou o limite de debounce, definido por currentMillis - lastDebounceTime1 > debounceDelay. Se ambos forem verdadeiros, o código regista que o botão foi pressionado e atualiza o tempo de debounce e o tempo de pressionamento. 
Esse lógica fica da seguinte maneira:

if (reading1 == LOW) 
{ if (!button1Pressed && (currentMillis - lastDebounceTime1 > debounceDelay))
{ button1Pressed = true; pressTime1 = currentMillis; lastDebounceTime1 = currentMillis; } }

Para a lógica de quando o botão é solto, o código verifica se o botão estava pressionado anteriormente e se o tempo de debounce já passou. Ele então marca o botão como "não pressionado", atualiza o tempo de debounce e reseta o tempo de pressão em pressTime1. O código fica da seguinte maneira:

else
{ if (button1Pressed && (currentMillis - lastDebounceTime1 > debounceDelay))
{ button1Pressed = false; lastDebounceTime1 = currentMillis; pressTime1 = 0; } }

Para o pressionamento longo, que é quando o botão é pressionado, e a variável tem que adicionar mais rapidamente, o código verifica se o botão 1 está pressionado por mais tempo que o limite definido em pressThreshold, o código reconhece isso como um pressionamento longo. E ele utiliza o intervalo fastInterval para executar as ações repetidas vezes enquanto o botão estiver pressionado. Note que, nesta parte do código, não há a váriavel lastDebouncetime, pois aqui, o ciclo tem que se repetir, sem delay para debounce, pois o delay só é necessário, quando o botão muda de estado, e não quando ele está sendo continuamente pressionado. 

A lógica para detectar o estado do botão, se ele foi pressionado ou não, ficou da seguinte maneira:

if (button1Pressed && (currentMillis - pressTime1 >= pressThreshold))
{ if (currentMillis - previousMillis1 >= fastInterval)
{ previousMillis1 = currentMillis; } }

Para quando o botão for solto, após ser pressionado e segurado, a lógica segue a mesma de quando ele é pressionado somente uma vez. 

Para o segundo botão, a lógica é a mesma, ele executa a mesma função, mudando somente o conteúdo dentro destes ifs, como demonstrado no código completo mais abaixo. 

Com o problema do botão solucionado, partiremos para a solução de outro problema, a conversão dos valores de cores para o formato de RGB para controle da fita de LED. 

As funções responsáveis por isso, são a função TempCorRGB, a função Converter,  a função ConverterTurbo e a função CirculoCromatico e por fim, a função Cores360. Vamos entender cada uma delas:

A função TempCorRGB, tem como função converter a temperatura de cor que está em Kelvin em valores RGB (Red, Green, Blue).

Primeiro, dentro dessa função, TempCorRGB,  temos os parâmetros: Kelvin para a temperatura de cor em Kelvin, e ponteiros de variáveis que armazenarão os valores de vermelho, verde e azul: uint8_t *r , uint8_t  *g, uint8_t  *b

void TempCorRGB(int kelvin, uint8_t *r, uint8_t *g, uint8_t *b)

A partir dessa criação, vamos seguir a lógica onde: a temperatura é dividida por 100. Para temperaturas menores ou iguais a 6600K, o vermelho é sempre 255, o verde é calculado com uma função logarítmica e o azul é definido como 0 para temperaturas abaixo de 1900K, senão é calculado com uma fórmula logarítmica . 

Para temperaturas acima de 6600K, o vermelho é calculado com uma função exponencial que calcula a cor com base em equações empíricas que aproximam as cores resultantes da luz em temperaturas específicas. Já o verde é calculado da mesma forma e o azul é sempre 255. O resultado é limitado entre 0 a 255 para cada componente da cor. 

{

  float temp = kelvin / 100.0; // define a variavel temp como float, e converte o valor da variavel Kelvin, dividindo-a por 100

    if (temp <= 66) // Se a temperatura for menor ou igual a 66 (Kelvin abaixo de ~6600K):
  {
    *r = 255; // A variável apontada por 'r' (cor vermelha) é definida como 255, ou seja, no máximo.
    *g = 99.4708025861 * log(temp) - 161.1195681661; // Calcula o valor de 'g' (verde) com base na fórmula logarítmica e atribui o valor à variável apontada por 'g'.
    *b = (temp <= 19) ? 0 : (138.5177312231 * log(temp - 10) - 305.0447927307); // Se 'temp' for menor ou igual a 19, define 'b' (azul) como 0, senão calcula o valor com uma fórmula logarítmica.
  } 
    else 
    {
        *r = 329.698727446 * pow(temp - 60, -0.1332047592); // Para temperaturas acima de 66, calcula o valor de 'r' (vermelho) usando uma função de potência baseada em (temp - 60).
        *g = 288.1221695283 * pow(temp - 60, -0.0755148492); // Calcula o valor de 'g' (verde) também com uma função de potência baseada em (temp - 60).
        *b = 255; // Para temperaturas acima de 66, o valor de 'b' (azul) é fixado em 255, indicando o máximo de azul.
    }

    *r = constrain(*r, 0, 255); // Garante que o valor de 'r' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.
    *g = constrain(*g, 0, 255); // Garante que o valor de 'g' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.
    *b = constrain(*b, 0, 255); // Garante que o valor de 'b' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.

}

Já a função Converter aplica uma conversão da temperatura da cor que está em Kelvin para RGB e define a cor da fita de LED:

Converte a temperatura de cor t0 para RGB usando a função TempCorRGB. Com isso, o valor de s0 é mapeado para um intervalo entre 0 e 183. O valor de s0 é definido dessa maneira, pois limita o brilho do modo, para que, quando o bastão estiver no modo turbo, ele fique em máxima potência, e em operação no modo normal, o brilho não seja saturado. 

Os valores de vermelho, verde e azul são ajustados com base no valor do brilho mapeado. Após isso, define a cor da fita com os valores RGB ajustados. Ao final, utiliza a ws2812fx.service() para atualizar a fita de LEDs com a nova cor. 

Além disso, a função botoesdocontrole(); é chamada dentro da função Converter para garantir que o controle funcionará corretamente. Com isso, essa parte do código fica da seguinte maneira:

void Converter()
{
    botoesdocontrole(); // Função para garantir que o controle IR está funcionando dentro da função Converter()
    ws2812fx.setMode(FX_MODE_STATIC); // Define o modo da fita de LEDs WS2812 para "estático" (sem animação, cor fixa)
    uint8_t r, g, b; // Declara três variáveis do tipo uint8_t para armazenar os valores RGB
    TempCorRGB(t0, &r, &g, &b); // Converte a temperatura de cor 't0' para valores RGB, armazenados em 'r', 'g' e 'b'

    uint8_t brightness = map(s0, 0, 100, 0, 183); // Mapeia o valor de 's0' que controla o brilho, em um intervalo de 0 a 100 para 0 a 183
    
    r = (r * brightness) / 255; // Ajusta o valor de 'r' com base no brilho calculado
    g = (g * brightness) / 255; // Ajusta o valor de 'g' com base no brilho calculado
    b = (b * brightness) / 255; // Ajusta o valor de 'b' com base no brilho calculado

    ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Define a cor da fita de LEDs WS2812 com os valores RGB calculados
    ws2812fx.service(); // Atualiza a fita de LEDs para aplicar a nova cor
}

Após isso, temos a função ConveterTurbo(). Essa função é semelhante à função Converter(), mas permite realizar um ajuste mais fino do brilho que a fita de LED possui. 

Ela segue a mesma lógica da função Converter(). No entanto, na sua configuração de brilho, s0 é mapeado para um intervalo mais amplo de 0 a 255, permitindo que a fita de LED libere todo o seu brilho.

Por fim, temos a função CirculoCromatico() e nela, os valores HSV (Hue, Saturation, Value) são convertidos para RGB da fita de LED. Ela é definida dessa maneira, pois será utilizada para controlar a cor da fita com base em um ângulo de um círculo cromático. 

Primeiro de tudo, se a saturação for 0, a cor é um tom de cinza, pois R=G=B = brilho. 

Caso contrário, a função calcula qual região do círculo cromático o valor de HUE pertence, indo de 0 a 5, correspondendo a diferentes cores principais.

Após isso, o código utiliza fórmulas para calcular os valores intermediários de p,q t com base no valor de brilho e saturação. 

Por fim, define os valores de vermelho, verde e azul com base na região do círculo cromático em que a cor está. 

void CirculoCromatico(uint16_t hue, uint8_t sa, uint8_t value, uint8_t *red, uint8_t *green, uint8_t *blue)
{
  ws2812fx.setMode(FX_MODE_STATIC); // Define o modo dos LEDs WS2812 para estático, sem animações.

  uint8_t region, remainder, p, q, t; // Declaração de variáveis auxiliares para o cálculo dos valores RGB.

  // Se a saturação for 0, a cor é completamente desaturada, ou seja, será um tom de cinza (RGB iguais).
  if (sa == 0) {
    *red = value;   // Definimos o valor de vermelho igual ao valor de brilho (value).
    *green = value; // Definimos o valor de verde igual ao valor de brilho (value).
    *blue = value;  // Definimos o valor de azul igual ao valor de brilho (value).
    return;         // Como já definimos os valores, saímos da função.
  }

  // Determina a região do círculo cromático baseado no valor de hue (matiz).
  region = hue / 43;  // Divide hue em regiões (0 a 5) correspondentes às cores principais.
  
  // Calcula o resto que será usado para calcular as cores intermediárias.
  remainder = (hue - (region * 43)) * 6; // Calcula o resto da divisão do hue para ajuste fino da cor.

  // Calcula os valores intermediários de p, q, e t com base no valor (brilho) e saturação.
  p = (value * (255 - sa)) >> 8;  // Calcula o componente p, que é uma redução da intensidade de brilho baseada na saturação.
  q = (value * (255 - ((sa * remainder) >> 8))) >> 8; // Calcula q, ajustando com base no resto para transições suaves.
  t = (value * (255 - ((sa * (255 - remainder)) >> 8))) >> 8; // Calcula t, outro valor intermediário para as transições.

  // Com base na região, define os valores de vermelho, verde e azul.
  switch (region) {
    case 0:
      *red = value;   // Região 0: tons de vermelho.
      *green = t;     // O verde é ajustado.
      *blue = p;      // O azul é mais reduzido.
      break;
    case 1:
      *red = q;       // Região 1: transição de vermelho para amarelo.
      *green = value; // O verde é maximizado.
      *blue = p;      // Azul ainda reduzido.
      break;
    case 2:
      *red = p;       // Região 2: tons de verde.
      *green = value; // Verde maximizado.
      *blue = t;      // Azul aumentando na transição.
      break;
    case 3:
      *red = p;       // Região 3: transição de verde para azul.
      *green = q;     // Verde reduzido.
      *blue = value;  // Azul maximizado.
      break;
    case 4:
      *red = t;       // Região 4: tons de azul.
      *green = p;     // Verde reduzido.
      *blue = value;  // Azul maximizado.
      break;
    default:
      *red = value;   // Região 5: transição de azul para vermelho (final do círculo cromático).
      *green = p;     // Verde é reduzido.
      *blue = q;      // Azul é intermediário.
      break;
  }
}

Para conseguirmos combinar o ângulo (hue) saturação e brilho e definir a cor da fita de LEDs, a função Cores360 foi definida utilizando a seguinte lógica:

Primeiro, convertemos o angle, saturation e brilho em valores RGB, utilizando a função CirculoCromatico, isso permite realizar transições mais suaves entre cores e também ao ajustar o ângulo (matriz) do círculo cromático. Depois, a cor da fita de LEDs é definida com os valores em RGB, e a fita de LEDs é atualizada, para exibir a cor calculada anteriormente. Veja como ficou essa parte do código:

void Cores360(int angle, int saturation, int brilho) // Cria a função Cores360 responsável por unir, saturação, brilho e cor da função CirculoCromatico
{
  ws2812fx.setMode(FX_MODE_STATIC); // Define o modo dos LEDs para estático (sem animações).
  uint8_t red, green, blue; // Declaração de variáveis para armazenar os valores de vermelho, verde e azul.
  // Converte o ângulo (hue), saturação e brilho para valores RGB usando a função CirculoCromatico.
  // Os ponteiros de 'red', 'green' e 'blue' recebem os valores convertidos.
  CirculoCromatico(angle, saturation, brilho, &red, &green, &blue);
  ws2812fx.setColor(ws2812fx.Color(red, green, blue));// Define a cor da fita de LEDs WS2812 com os valores RGB calculados
  ws2812fx.service(); // Atualiza a fita de LEDs para aplicar a nova cor.
}

Com isso, as funções são definidas e é possível controlar corretamente a cor, brilho e saturação da fita de LED. O restante do código trata-se apenas das e a formação do triângulo de seleção. 

Veja abaixo, como ficou o código programado por completo: 

#include <SPI.h> // Inclui a Biblioteca SPI para comunicação Serial com a USB
#include <Wire.h> // Inclui a Biblioteca Wire para controle do I2C
#include <Adafruit_GFX.h> // Inclui a biblioteca Adafruit para auxilio nas funções do display
#include <Adafruit_SH110X.h> // Inclui a biblioteca responsável pelo controle do display LCD
#include <WS2812FX.h> // Inclui a biblioteca que controla a fita de LED
#include <Arduino.h> // Inclui a Arduino.h para compatibilidade com o ESP32C3
#include <IRremote.hpp> // Adiciona a biblioteca que decodifica os sinais do controle IR
// Quando for instalar a biblioteca do IRremote, é necessário fazer downgrade da versão instalada do ESP32 para a versão 2.0.17, caso contrário, a biblioteca
// não irá funcionar por conta de problemas de compatiblidade com versões superiores a 2.0.17

#define i2C_Address 0x3C // Define o endereço I2C do display
#define SCREEN_WIDTH 128  // Define o tamanho da Largura do display
#define SCREEN_HEIGHT 64  // Define o tamanho da Altura do display
#define OLED_RESET -1 // Define o Reset como -1 pois não é utilizado nesse tipo de display

Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Define a criação de um objeto display dentro da classe Adafruit_SH1106
// Com parametros definidos de Altura, Largura, Endereço I2C e reset

#define LED_COUNT 144 // Número de Leds disponíveis na fita é definido como 144 LEDs
#define LED_PIN 10 // Pino em que a fita de Led está conectada
#define IR_RECEIVE_PIN 5 // Pino em que o receptor está conectado

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); // Da mesma forma que o Display, Define a criação de um objeto ws2812fx dentro da classe WS2812FX
// Com os parametros de LEDs, Pino do fita RGB, Formato de dados de comunicação e frequência do sinal de controle dos LEDs.

unsigned long lastFadeChange = 0; // Para controlar o tempo entre mudanças de cor define LasFadeChange como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits
unsigned long lastBlinkChange = 0; // // Para controlar o tempo entre piscadas de cor define lasBlinkChage como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits 

int fadeIndex = 0; // Para controlar o índice da cor atual no modo fade, cria uma variavel do tipo inteiro e define ela como 0.
uint32_t fadeColors[] = {ws2812fx.Color(255, 0, 0), ws2812fx.Color(0, 0, 255)}; // Cria um Array de cores (vermelho e azul)

bool blinkState = false; // Cria uma variavel do tipo bool de dois estados, verdadeiro ou falso, e nesse caso, define ela como falsa
unsigned long lastChange = 0; // Para comparar o tempo entre as mudanças de cor define lastChange como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits
bool blinkOn = false; // Cria uma variavel do tipo bool de dois estados, verdadeiro ou falso, e nesse caso, define ela como falsa

int caso = 0; // Variável para controlar a cena atual
int j; // Variavel do tipo inteira para controlar os efeitos da fita de LED

int blinkCounter = 0; // Cria uma variavel para controle do modo quando r2 == 6
int blinkCount = 0; // Variavel utilizada para contar o número de piscadas 
int blinkPattern[] = {3, 1, 3}; // Cria um Array com 3 elementos

int t0 = 2500; // variavel que controla a temperatura da cor na tela 1 - CTT
int s0; // variavel para controlar o sol da tela 1 - CTT
int turbo0; // turbo da tela 1 - CTT

int t01 = 4500; // Variavel que controla a temperatura das cores dentro dos códigos
int s01 = 100; // Variavel que controla a saturação dentro dos códigos

int h1; // variável do H da tela 2 - HSI
int s1; // variavel do S da tela 2 - HSI
int b1; // variavel do sol da tela 2 - HSI

int r2 = 1; // variavel que controla a temperatura da cor na tela 1 - SCENE
int s2; // variavel para controlar o sol da tela 1 - SCENE

int position = 30; // Variavel para controlar a posição do triangulo

//Array de bits para o desenho do Sol
const unsigned char sol [] PROGMEM = 
{ 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x18, 0x30, 0x11, 0x90, 0x07, 0xc0, 0x07, 0xe4, 0x67, 0xe4,0x07, 0xc0, 0x03, 0x80, 0x08, 0x08, 0x18, 0x18, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00};

//Array de bits para o desenho do Raio 
const unsigned char raio [] PROGMEM = 
{	0x00, 0x00, 0x00, 0x20, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0xc0, 0x07, 0xc0, 0x0f, 0xf0, 0x1f, 0xf0,0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00};

//Array de bits para o desenho da temp 
const unsigned char temp [] PROGMEM = 
{0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x01, 0xf8, 0x01, 0xf0, 0x19, 0xf0, 0x3c, 0xe0,	0x3c, 0x00, 0x18, 0x00, 0x01, 0xc0, 0x03, 0xc0, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00};

//Array de bits para o desenho da policia
const unsigned char policia [] PROGMEM = 
{0x00, 0x00, 0x01, 0x00, 0x21, 0x00, 0x30, 0x18, 0x00, 0x10, 0x07, 0xc0, 0x0c, 0x60, 0x68, 0x2c, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x18, 0x30, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00};

//Array de bits para o desenho do giro
const unsigned char giro [] PROGMEM = 
{0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0x10, 0x07, 0x90, 0x0c, 0x10, 0x18, 0x10,0x10, 0x30, 0x13, 0xe0, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00};

///Array de bits para o desenho da festa
const unsigned char festa [] PROGMEM =
{0x00, 0x00, 0x01, 0x08, 0x01, 0x1c, 0x07, 0xc8, 0x0e, 0xe0, 0x1c, 0x70, 0x37, 0xd8, 0x24, 0x48,0x3f, 0xf8, 0x24, 0x48, 0x17, 0xd0, 0x0c, 0x60, 0x67, 0xc0, 0x20, 0x00, 0x00, 0x00};
  
const int epd_bitmap_allArray_LEN = 6; // Variavel que define a quantidade de itens na lista de bitmaps
const unsigned char* epd_bitmap_allArray[6] = {sol, raio, temp, policia, giro, festa}; // Cria um Array para evitar a duplicação de dados quando uma imagem é mostrada na tela

unsigned long previousMillis1 = 0; // Variavel para armazenamento do tempo
unsigned long previousMillis2 = 0;// Variavel para armazenamento do tempo
unsigned long pressTime1 = 0; // Variavel para armazenamento do tempo que o botão é pressionado
unsigned long pressTime2 = 0; // Variavel para armazenamento do tempo que o botão é pressionado

const long initialInterval = 500; // Intervalo inicial de tempo em milissegundos para o incremento/decremento
const long fastInterval = 1;    // Intervalo rápido de tempo em milissegundos para o incremento/decremento
const long pressThreshold = 600; // Tempo em milissegundos para mudar para incremento rápido
const long debounceDelay = 50;    // Delay de debounce para evitar leitura incorreta do botão

boolean button1Pressed = false; // Define uma variavel de dois estados e define ele como falso, para que o botão não seja identificado como pressionado
boolean button2Pressed = false; // Define uma variavel de dois estados e define ele como falso, para que o botão não seja identificado como pressionado

unsigned long lastDebounceTime0 = 0; // Define lastDebounceTime0 como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits, para calcular o debounce do botão
unsigned long lastDebounceTime1 = 0; // Define lastDebounceTime1 como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits, para calcular o debounce do botão
unsigned long lastDebounceTime2 = 0; // Define lastDebounceTime2 como 0, sendo um tipo de dado que armazena valores inteiros não negativos de 32bits, para calcular o debounce do botão

int botaodocontrole = 0; // Variavél do tipo inteiro para os botões controle IR



void setup() 
{
  pinMode(0, INPUT_PULLUP); // botão set
  pinMode(1, INPUT_PULLUP); // botão R
  pinMode(2, INPUT_PULLUP); // botão L
  pinMode(3, INPUT_PULLUP); // botão up
  pinMode(4, INPUT_PULLUP); // botão down
  
  IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK); // Inicia o receptor IR, com o pino definido e desativando o feedback por LED quando recebe sinais

  display.begin(i2C_Address, false); // Inicia o display sem mostrar o logo
  display.clearDisplay();            // Limpa o buffer do display
  display.display();     // Atualiza o display para limpar a tela
  
  ws2812fx.init(); // Inicia a fita LED com a biblioteca do WS2812FX preparando ela para uso
  ws2812fx.setBrightness(255); // Ajuste inicial do brilho da fita
  ws2812fx.setSpeed(1000); // Não afeta a cor, mas precisa ser definido
  ws2812fx.start(); // Inicia o controle da Fita 
  ws2812fx.service(); // Mantém o funcionamento dos efeitos e animações da fita de LEDs
}

void loop() // Função Loop
{
  tela(caso); // Chama a função caso 
  controledetela(); // Chama a função controle de tela, que define as telas que aparecem quando uma ação é realizada 

  botoes(); // Chama a função botões que é responsável pelo controle do Joystick 5D
  botoesdocontrole(); // Chama a função botoes do controle que é responsavel pelo Controle IR 

  ws2812fx.service(); // Mantém o funcionamento dos efeitos e animações da fita de LEDs
  IrReceiver.resume(); // Mantem o controle IR iniciado, preparado para receber comandos
}

void controledetela() // Função responsável por controlar o que aparece no display 
{
    if (digitalRead(0) == LOW || botaodocontrole == 13) // Define: Se o botão 0 do Joystick 5D ou o botão 13 do controle Ir for apertado 
  {
    caso++; // Caso adiciona o seu valor 1; 
    delay(200); // Delay para Debounce para evitar múltiplas detecções
    display.clearDisplay();display.display(); // Limpa o Buffer do display e em seguida atualiza a tela 
    botaodocontrole = 0; // Define o botão do controle IR como 0 para evitar que a variavel fique com o ultimo valor que foi pressionado
    
    if (caso > 2) { caso = 0; display.clearDisplay();display.display(); } // Se caso for maior que 2, o valor de caso vai para 0, o buffer do display é atualizado e a tela é atualizada em seguida
      
  }

    if(caso == 3) // Se caso for igual a 3
    { 
      if(digitalRead(0) == LOW || botaodocontrole == 13) {caso = 1; delay(200); botaodocontrole = 0;} // Se o botão 0  do joystick ou o botão 13 do controle forem pressionados quando o caso for igual a 3
      // Caso se torna 1, com delay de 200 ms para debounce e a variavel botaodocontrole vai para 0, para que ela fique com o valor do botão gravado
      // Esse loop serve para sair de dentro do caso 3, em que a luz entra em modo Turbo
    }
}

void tela(int caso) // Função tela para controle das telas que apareceram no display, com variavel int caso
{
 botoesdocontrole(); // Cama a função botoesdocontrole para garantir que o controle IR está funcionando e sendo reconhecido
  display.clearDisplay(); // Limpa a tela do display
  
  switch (caso) // Switch utilizando a variavel Caso, criada mais a cima em int caso
  {
    case 0: // modo de tela CTT 
      triangulo(); // Chama a função triangulo, responsável por desenhar o triangulo de seleção que aparecerá na tela
      display.setTextSize(1); display.setTextColor(SH110X_WHITE);  // seleciona o tamanho da fonte, e a cor do texto que aparecerá no display
      display.setCursor(0, 0); display.print("CTT"); // Seleciona onde será escrito a mensagem CTT, e em seguida escreve a mensagem

      display.setTextSize(2); display.drawBitmap(13, 18, temp, 15, 15, SH110X_WHITE); // Seleciona o tamanho da fonte, e desenha o bitmap da temperatura, criado no inicio do código
      display.setCursor(27, 18); display.print(":"); // seleciona onde será escrito e escreve :
      display.setCursor(40, 18); display.print(t0); display.print("K"); // Seleciona onde será escrito e escreve o valor da variavel, seguido de K da temperatura da cor
      display.drawBitmap(13, 33, sol, 15, 15, SH110X_WHITE); // Desenha o bitmap na posição 13, 33, com tamanho de 15x15, na cor branca, esse bitmap é o desenho do sol, criado mais acima no código
      display.setCursor(27, 33); display.print(":"); // seleciona onde será escrito e escreve : 
      display.setCursor(40, 33); display.print(s0); display.print("%"); // De mesmo modo, seleciona onde será escrito, e escreve a variavel s0 seguida de %
      display.setCursor(15,50); display.print("Normal"); // Seleciona o posicionamento e escreve a palavra normal, que define o que bastão está em modo normal, e não em modo Turbo
         
      if(digitalRead(0) == LOW) {turbo0--; display.clearDisplay(); display.display();}Converter();// Reinicia o ciclo para o `caso 0` (CTT) ao pressionar o botão 0
    
    break;

    case 1: // Modo de tela HSI
      triangulo(); // Mais uma vez chama a função responsável por desenhar o triangulo de movimentação
      int v1, v2, v3; // Cria três variaveis do tipo inteiro V1, v2 e V3
      v1 = map(h1, 0, 360, 0, 255); // Cria um map para a variavel v1, utilizando o valor da variavel h1, transformando de 0 a 360 em 0 a 255, que é o valor da leitura do pino
      v2 = map(s1, 0, 100, 0, 255); // Cria um map para a variavel v2, utilizando o valor da variavel s1, transformando de 0 a 360 em 0 a 255, que é o valor da leitura do pino
      v3 = map(b1, 0, 100, 0, 255); // Cria um map para a variavel v3, utilizando o valor da variavel b1, transformando de 0 a 360 em 0 a 255, que é o valor da leitura do pino

      //Escrita no display, explicada mais acima
      display.setTextSize(1);  display.setTextColor(SH110X_WHITE); 
      display.setCursor(0, 0); display.print("HSI");
      display.setTextSize(2);  
      display.setCursor(15, 18); display.print("H");
      display.setCursor(27, 18); display.print(":");
      display.setCursor(40, 18); display.print(h1);
      display.drawCircle(display.getCursorX() + 2, display.getCursorY() - 2, 2, SH110X_WHITE); // Desenha o Circulo dos Graus de 0 a 360
      display.setCursor(15, 35); display.print("S");
      display.setCursor(27, 35); display.print(":");
      display.setCursor(40, 35); display.print(s1); display.print("%");
      display.drawBitmap(13, 50, sol, 15, 15, SH110X_WHITE); // Desenha o bitmap do sol
      display.setCursor(27, 50); display.print(":"); 
      display.setCursor(40,50); display.print(b1); display.print("%");
      Cores360(v1, v2, v3); // Chama a função Cores360 utilizando as variaveis v1, v2 e v3
      break;

    case 2: // Modo de tela do Scene
      modos(); // Chama a funçãõ modos, que é responsável por controlar os efeitos que o bastão tem disponível 
      triangulo2(); // Chama a função triangulo 2, que é como a função triangulo(), no entanto, a triangulo2, tem limites diferentes, para limitar a movimentação do triangulo entre as duas linhas que aparecem na tela
      display.setTextSize(1);  display.setTextColor(SH110X_WHITE);
      display.setCursor(0, 0); display.print("Scene");      
      display.setTextSize(2);    
      display.setCursor(27, 18); display.print(":");
      display.setCursor(40, 18); display.print(r2);
      display.drawBitmap(13, 33, sol, 15, 15, SH110X_WHITE); // Desenha o bitmap do sol
      display.setCursor(27, 33); display.print(":");
      display.setCursor(40, 33); display.print(s2);display.print("%");

      break;

    case 3:
      triangulo(); // Chama a função triangulo para movimentação do Cursor 
      display.setTextSize(1);  display.setTextColor(SH110X_WHITE); 
      display.setCursor(0, 0); display.print("CTT");
      display.setTextSize(2);  display.drawBitmap(13, 18, temp, 15, 15, SH110X_WHITE); //temperaturas desenha o bitmap da temperatura
      display.setCursor(27, 18); display.print(":");
      display.setCursor(40, 18); display.print(t01); display.print("K");
      display.drawBitmap(13, 33, sol, 15, 15, SH110X_WHITE); // desenha o bitmap do Sol
      display.setCursor(27, 33); display.print(":");
      display.setCursor(40, 33); display.print(s01); display.print("%");
      display.setCursor(15, 50); display.print("Turbo");

      botoesdocontrole(); // Chama a função botoesdocontrole para garantir que o controle IR está funcionando dentro do case 3
      ConverterTurbo(); // Chama a função ConverterTurbo, responsável pelo modo Turbo do bastão
      break;
  }

  ws2812fx.service(); // Mantém o funcionamento dos efeitos e animações da fita de LEDs
  display.display(); // Atualiza o display com o conteúdo do buffer
}

void triangulo() //Cria a função triangulo, onde o cursor é desenhado
{
  if (position == 0) {display.drawTriangle(10, 55, 0, 50, 0, 60, SH110X_WHITE);} // utiliza a variavel position para definir onde o cursor será desenhado, em seguida, desenha um triangulo utilizando os três vertices
  else if (position == 15) {display.drawTriangle(10, 40, 0, 35, 0, 45, SH110X_WHITE);} // Realiza o mesmo desenho do triangulo, quando o valor de position for 15, desenha mais abaixo, na segunda linha o triangulo
  else if (position == 30) {display.drawTriangle(10, 25, 0, 20, 0, 30, SH110X_WHITE);}// Desenha o triangulo na linha três, se posição for igual a 30
    /////////////////////////////////////////*/////////////////////////////////////////////////////////////////////*
      if (digitalRead(4) == LOW || botaodocontrole == 24) // se o botão 4 do joystick ou o botão 24 do controle IR forem pressionados
      {
        position += 15; // Move o triângulo para cima
        delay(200); // delay para debounce do botão
        botaodocontrole = 0; // define o valor da variavel botaodocontrole como 0 para evitar que ela fique com o valor do ultimo botão pressionado
        if (position > 30) {position = 0; } // Se a posição for maior que 30, ele moverá o triangulo para baixo, dando a impressão de que o triangulo segue o movimento dos controles
      }

      if (digitalRead(3) == LOW || botaodocontrole == 82)  // Se o botão 3 do joystick ou o botão 82 for pressionado 
      {
        position -= 15; // Move o triângulo para cima
        delay(200); // Delay para debounce do botão
        botaodocontrole = 0; // define o valor da variavel botaodocontrole como 0 para evitar que ela fique com o valor do ultimo botão pressionado
        if (position < 0) {position = 30; } // se a posição for menor que 0 por conta da subtração, o triangulo será desenhado na linha de cima, para dar continuidade de movimento
      } 

}

void triangulo2() // Realiza as mesmas movimentações do triangulo(), só que com limites diferentes, para respeitas as duas linhas de escrita na tela onde há sommente duas linhas
{
  if(position == 0){position = 15;}  
  if (position == 30){display.drawTriangle(10, 25, 0, 20, 0, 30, SH110X_WHITE);}  
  else if (position == 15){display.drawTriangle(10, 40, 0, 35, 0, 45, SH110X_WHITE);}
  
  if (digitalRead(4) == LOW || botaodocontrole == 24 ) // Move o triângulo para cima
      {
        position += 15;
        delay(200);
        botaodocontrole = 0;
        if (position > 30) {position = 15;} // se o valor de position passar de 30, ele move o triangulo para baixo, na segunda linha
      }

      if (digitalRead(3) == LOW || botaodocontrole == 82) // Move o triângulo para cima
      {
        position -= 15; 
        delay(200);
        botaodocontrole = 0;
        if (position < 15) {position = 30;} // se o valor de position for menor que 15, ele move o triangulo para cima, na primeira linha
      } 
}
//////////////////////////////////Modo CTT Temperatura em K
void TempCorRGB(int kelvin, uint8_t *r, uint8_t *g, uint8_t *b) // Cria uma função para converter a temperatura de cor em Kelvin para o espaço RGB.
// O parâmetro 'kelvin' é do tipo inteiro, representando a temperatura de cor em Kelvin.
// Os parâmetros 'r', 'g' e 'b' são ponteiros do tipo uint8_t, que armazenam valores sem sinal de 8 bits (0 a 255), para a cor vermelha (R), verde (G) e azul (B), respectivamente.

{

  float temp = kelvin / 100.0; // define a variavel temp como float, e converte o valor da variavel Kelvin, dividindo-a por 100

    if (temp <= 66) // Se a temperatura for menor ou igual a 66 (Kelvin abaixo de ~6600K):
  {
    *r = 255; // A variável apontada por 'r' (cor vermelha) é definida como 255, ou seja, no máximo.
    *g = 99.4708025861 * log(temp) - 161.1195681661; // Calcula o valor de 'g' (verde) com base na fórmula logarítmica e atribui o valor à variável apontada por 'g'.
    *b = (temp <= 19) ? 0 : (138.5177312231 * log(temp - 10) - 305.0447927307); // Se 'temp' for menor ou igual a 19, define 'b' (azul) como 0, senão calcula o valor com uma fórmula logarítmica.
  } 
    else 
    {
        *r = 329.698727446 * pow(temp - 60, -0.1332047592); // Para temperaturas acima de 66, calcula o valor de 'r' (vermelho) usando uma função de potência baseada em (temp - 60).
        *g = 288.1221695283 * pow(temp - 60, -0.0755148492); // Calcula o valor de 'g' (verde) também com uma função de potência baseada em (temp - 60).
        *b = 255; // Para temperaturas acima de 66, o valor de 'b' (azul) é fixado em 255, indicando o máximo de azul.
    }

    *r = constrain(*r, 0, 255); // Garante que o valor de 'r' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.
    *g = constrain(*g, 0, 255); // Garante que o valor de 'g' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.
    *b = constrain(*b, 0, 255); // Garante que o valor de 'b' fique entre 0 e 255, limitando qualquer valor que exceda esse intervalo.

}

void Converter()
{
    botoesdocontrole(); // Função para garantir que o controle IR está funcionando dentro da função Converter()
    ws2812fx.setMode(FX_MODE_STATIC); // Define o modo da fita de LEDs WS2812 para "estático" (sem animação, cor fixa)
    uint8_t r, g, b; // Declara três variáveis do tipo uint8_t para armazenar os valores RGB
    TempCorRGB(t0, &r, &g, &b); // Converte a temperatura de cor 't0' para valores RGB, armazenados em 'r', 'g' e 'b'

    uint8_t brightness = map(s0, 0, 100, 0, 183); // Mapeia o valor de 's0' que controla o brilho, em um intervalo de 0 a 100 para 0 a 183
    
    r = (r * brightness) / 255; // Ajusta o valor de 'r' com base no brilho calculado
    g = (g * brightness) / 255; // Ajusta o valor de 'g' com base no brilho calculado
    b = (b * brightness) / 255; // Ajusta o valor de 'b' com base no brilho calculado

    ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Define a cor da fita de LEDs WS2812 com os valores RGB calculados
    ws2812fx.service(); // Atualiza a fita de LEDs para aplicar a nova cor
}

///////////////////////////////// final CTT temperatura K
/////////////////////////////// modo turbo CTT
void ConverterTurbo()
{
    botoesdocontrole(); // Função para garantir que o controle IR está funcionando dentro da função ConverterTurbo()
    ws2812fx.setMode(FX_MODE_STATIC); // Define o modo dos LEDs para estático
    uint8_t r, g, b; // Declaração de variáveis para armazenar os valores RGB
    TempCorRGB(t01, &r, &g, &b); // Converte a temperatura de cor 't01' para valores RGB

    uint8_t brightness = map(s01, 0, 100, 0, 255); // Mapeia o brilho 's01' de 0-100 para 0-255

    r = (r * brightness) / 255; // Ajusta o valor da cor vermelha 'r' com base no brilho
    g = (g * brightness) / 255; // Ajusta o valor da cor verde 'g' com base no brilho
    b = (b * brightness) / 255; // Ajusta o valor da cor azul 'b' com base no brilho

    ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Define a cor da fita de LEDs com os valores RGB ajustados
    ws2812fx.service(); // Atualiza a fita de LEDs para aplicar a nova cor
}

//////////////////////////////// Fim do modo turbo CTT

void CirculoCromatico(uint16_t hue, uint8_t sa, uint8_t value, uint8_t *red, uint8_t *green, uint8_t *blue)
{
  ws2812fx.setMode(FX_MODE_STATIC); // Define o modo dos LEDs WS2812 para estático, sem animações.

  uint8_t region, remainder, p, q, t; // Declaração de variáveis auxiliares para o cálculo dos valores RGB.

  // Se a saturação for 0, a cor é completamente desaturada, ou seja, será um tom de cinza (RGB iguais).
  if (sa == 0) {
    *red = value;   // Definimos o valor de vermelho igual ao valor de brilho (value).
    *green = value; // Definimos o valor de verde igual ao valor de brilho (value).
    *blue = value;  // Definimos o valor de azul igual ao valor de brilho (value).
    return;         // Como já definimos os valores, saímos da função.
  }

  // Determina a região do círculo cromático baseado no valor de hue (matiz).
  region = hue / 43;  // Divide hue em regiões (0 a 5) correspondentes às cores principais.
  
  // Calcula o resto que será usado para calcular as cores intermediárias.
  remainder = (hue - (region * 43)) * 6; // Calcula o resto da divisão do hue para ajuste fino da cor.

  // Calcula os valores intermediários de p, q, e t com base no valor (brilho) e saturação.
  p = (value * (255 - sa)) >> 8;  // Calcula o componente p, que é uma redução da intensidade de brilho baseada na saturação.
  q = (value * (255 - ((sa * remainder) >> 8))) >> 8; // Calcula q, ajustando com base no resto para transições suaves.
  t = (value * (255 - ((sa * (255 - remainder)) >> 8))) >> 8; // Calcula t, outro valor intermediário para as transições.

  // Com base na região, define os valores de vermelho, verde e azul.
  switch (region) {
    case 0:
      *red = value;   // Região 0: tons de vermelho.
      *green = t;     // O verde é ajustado.
      *blue = p;      // O azul é mais reduzido.
      break;
    case 1:
      *red = q;       // Região 1: transição de vermelho para amarelo.
      *green = value; // O verde é maximizado.
      *blue = p;      // Azul ainda reduzido.
      break;
    case 2:
      *red = p;       // Região 2: tons de verde.
      *green = value; // Verde maximizado.
      *blue = t;      // Azul aumentando na transição.
      break;
    case 3:
      *red = p;       // Região 3: transição de verde para azul.
      *green = q;     // Verde reduzido.
      *blue = value;  // Azul maximizado.
      break;
    case 4:
      *red = t;       // Região 4: tons de azul.
      *green = p;     // Verde reduzido.
      *blue = value;  // Azul maximizado.
      break;
    default:
      *red = value;   // Região 5: transição de azul para vermelho (final do círculo cromático).
      *green = p;     // Verde é reduzido.
      *blue = q;      // Azul é intermediário.
      break;
  }
}

void Cores360(int angle, int saturation, int brilho) // Cria a função Cores360 responsável por unir, saturação, brilho e cor da função CirculoCromatico
{
  ws2812fx.setMode(FX_MODE_STATIC); // Define o modo dos LEDs para estático (sem animações).
  uint8_t red, green, blue; // Declaração de variáveis para armazenar os valores de vermelho, verde e azul.
  // Converte o ângulo (hue), saturação e brilho para valores RGB usando a função CirculoCromatico.
  // Os ponteiros de 'red', 'green' e 'blue' recebem os valores convertidos.
  CirculoCromatico(angle, saturation, brilho, &red, &green, &blue);
  ws2812fx.setColor(ws2812fx.Color(red, green, blue));// Define a cor da fita de LEDs WS2812 com os valores RGB calculados
  ws2812fx.service(); // Atualiza a fita de LEDs para aplicar a nova cor.
}


void botoes()
{
  unsigned long currentMillis = millis(); // Obtém o tempo atual
  // Leitura do estado dos botões com debounce
  int reading1 = digitalRead(1); // Lê o estado do botão 1
  int reading2 = digitalRead(2); // Lê o estado do botão 2

  // Processamento para o botão 1
  if (reading1 == LOW) 
  { // Se o botão 1 está pressionado
    if (!button1Pressed && (currentMillis - lastDebounceTime1 > debounceDelay))// Se o botão 1 não estava pressionado anteriormente e o tempo de debounce passou
    {
      button1Pressed = true; // Marca o botão 1 como pressionado
      pressTime1 = currentMillis; // Armazena o tempo em que o botão foi pressionado
      lastDebounceTime1 = currentMillis; // Atualiza o último tempo de debounce
      previousMillis1 = currentMillis; // Atualiza o último tempo de incremento rápido
          if (caso == 2 and position == 30) // If para controle da variavel dos efeitos do bastão
            {
              if(r2 == 3)
              {j++;botaodocontrole = 0;}
              if(r2 == 6)
              {j++;botaodocontrole = 0;}
              if(r2 == 9)
              {j++;botaodocontrole = 0;}
              if(r2 ==12)
              {j=0;botaodocontrole = 0;}                    
            }
    }
  } 

  else 
  {
    if (button1Pressed && (currentMillis - lastDebounceTime1 > debounceDelay))
     {
      // Se o botão 1 foi liberado e o tempo de debounce passou
      button1Pressed = false; // Marca o botão 1 como não pressionado
      lastDebounceTime1 = currentMillis; // Atualiza o último tempo de debounce
      if (currentMillis - pressTime1 < pressThreshold) // Se o tempo de pressão foi menor que o limite, considera-se um clique curto
      { 
        if (position == 30 && caso == 0) 
        { // tela 1 CTT
          if (t0 < 6500) {t0 = t0 + 100;} 
          else {t0 = 2500;}
        } 
        else if (position == 30 && caso == 1) // tela 2 HSI
        { 
          if (h1 < 360) {h1++;} 
          else {h1 = 0;}
        } 
        else if (position == 30 && caso == 2) // Tela 3 SCENE
        { 
          if (r2 < 12) {r2++;} 
          else {r2 = 1;}
        }

        if (position == 15 && caso == 0) // tela 1 CTT
        { 
          if (s0 < 100) {s0++;} 
          else {s0 = 0;}
        } 
        else if (position == 15 && caso == 1) // Tela 2 HSI
        { 
          if (s1 < 100) {s1++;} 
          else {s1 = 0;}
        } 
        else if (position == 15 && caso == 2)// Tela 3 SCENE
        { 
          if (s2 < 100) {s2++;}
          else{s2 = 0;}
        }

        if (position == 0 && caso == 0 ) // Tela 1 - CTT
        { 
          if (turbo0 < 1){turbo0++;}
          else {turbo0 = 0;}
          caso = 3;
        } 
        else if (position == 0 && caso == 1) // Tela 2 - HSI
        { 
          if (b1 < 100) {b1++;} 
          else {b1 = 0;}
        }
        // tela 3 Scene não possui uma terceira variavel
      }

    }
    pressTime1 = 0; // Reseta o tempo de pressão do botão 1
  }

  // Incremento rápido para o botão 1
  if (button1Pressed && (currentMillis - pressTime1 >= pressThreshold))     // Se o botão 1 está pressionado por um tempo maior que o limite
  {
    if (currentMillis - previousMillis1 >= fastInterval)  // Se o tempo desde o último incremento rápido passou
    {
      previousMillis1 = currentMillis; // Atualiza o último tempo de incremento rápido
      
      if (position == 30 && caso == 0) 
      {
        if (t0 < 6500) 
        {t0 = t0 + 100;}
      } 

      else if (position == 30 && caso == 1) // Se o tempo de pressão foi menor que o limite, considera-se um clique curto
      {
        if (h1 < 360) {h1++;}
      } 
      else if (position == 30 && caso == 2) 
      {
        if (r2 >= 1 && r2 <= 11 )
        {r2++;}
      }

      if (position == 15 && caso == 0) 
      {
        if (s0 < 100) {s0++;}
      } 
      else if (position == 15 && caso == 1) 
      {
        if (s1 < 100) {s1++;}
      } 
      else if (position == 15 && caso == 2) 
      {
        if (s2 < 100) {s2++;}
      }

      // não é necessário mudar rapidamente para o modo turbo, por isso, não há alteração na variavel turbo0 nesta altura do código
      if (position == 0 && caso == 1)
      {
        if (b1 < 100) {b1++;}
      }
      // tela 2 não possui uma terceira variavel
    }
  }
  // Processamento para o botão 2 (similar ao botão 1)
  if (reading2 == LOW) 
  { // Se o botão 2 está pressionado
    if (!button2Pressed && (currentMillis - lastDebounceTime2 > debounceDelay)) 
    {
      button2Pressed = true; // Marca o botão 2 como pressionado
      pressTime2 = currentMillis; // Armazena o tempo em que o botão foi pressionado
      lastDebounceTime2 = currentMillis; // Atualiza o último tempo de debounce
      previousMillis2 = currentMillis; // Atualiza o último tempo de decremento rápido

          if (caso == 2 and position == 30)
            {
              if(r2 == 1){j=3;botaodocontrole = 0;}
              if(r2 == 4){j--;botaodocontrole = 0;}
              if(r2 == 7){j--;botaodocontrole = 0;}  
              if(r2 == 10){j--;botaodocontrole = 0;}
            }
    }
  } 
  else 
  {
    if (button2Pressed && (currentMillis - lastDebounceTime2 > debounceDelay)) 
    { // Se o botão 2 foi liberado e o tempo de debounce passou
     
      button2Pressed = false; // Marca o botão 2 como não pressionado
      lastDebounceTime2 = currentMillis; // Atualiza o último tempo de debounce
      if (currentMillis - pressTime2 < pressThreshold)
       {
        // Se o tempo de pressão foi menor que o limite, considera-se um clique curto
        if (position == 30 && caso == 0)// tela 0 
        { 
          if (t0 > 2500) {t0 = t0 - 100;}
          else {t0 = 6500;}
        } 
        else if (position == 30 && caso == 1) // tela 1
        {
          if (h1 > 0) {h1--;} 
          else {h1 = 360;}
        } 
        else if (position == 30 && caso == 2)// tela 2
        { 
          if (r2 >= 2 && r2 <= 12){r2--;}
          else {r2 = 12;}
        }

        if (position == 15 && caso == 0) // tela 0 
        { 
          if (s0 > 0) {s0--;} 
          else {s0 = 100;}
        } 
        else if (position == 15 && caso == 1)  // tela 1 
        {
          if (s1 > 0) {s1--;} 
          else {s1 = 100;}
        } 
        else if (position == 15 && caso == 2) 
        { // tela 2 
          if (s2 > 0) {s2--;} 
          else {s2 = 100;}
        }

        if (position == 0 && caso == 3) 
        { // tela 0
          if (turbo0 > 0 ) {turbo0--;} 
          else {turbo0 = 1;}
          caso = 0;
        } 
        else if (position == 0 && caso == 1) 
        { // tela 1
          if (b1 > 0) {b1--;}
          else {b1 = 100;}
        }
        //tela 2 não possui uma terceira variavel
      }
    }
    pressTime2 = 0; // Reseta o tempo de pressão do botão 2

  }

  // Decremento rápido para o botão 2
  if (button2Pressed && (currentMillis - pressTime2 >= pressThreshold))
   {
    // Se o botão 2 está pressionado por um tempo maior que o limite
    if (currentMillis - previousMillis2 >= fastInterval) 
    {
      // Se o tempo desde o último decremento rápido passou
      previousMillis2 = currentMillis; // Atualiza o último tempo de decremento rápido

      if (position == 30 && caso == 0) 
      { // tela 0
        if (t0 > 2500) {t0 = t0 - 100;}
      } 
      else if (position == 30 && caso == 1) // tela 1 
      { 
        if (h1 > 0) {h1--;}
      } 
      else if (position == 30 && caso == 2) 
      { // tela 2
        if (r2 >= 2 && r2 <= 12) {r2--;}
      }

      if (position == 15 && caso == 0) 
      { // tela 0 
        if (s0 > 0) {s0--;}
      } 
      else if (position == 15 && caso == 1) 
      { // tela 1
        if (s1 > 0) {s1--;}

      } 
      else if (position == 15 && caso == 2) 
      { // tela 2
        if (s2 > 0) {s2--;}
      }

      // não é necessário mudar rapidamente para o modo turbo, por isso, não há alteração na variavel turbo0 nesta altura do código
      if (position == 0 && caso == 1) // tela 1
      { 
        if (b1 > 0){b1--;}
      }
      //tela 2 não possui uma terceira variavel
          
    }
  }
 
}

void modos() // Função que controla os efeitos disponíveis na fita de LEDs
{ 
  botoesdocontrole(); // Chama a função botoesdocontrole para verificar os comandos do controle IR

  switch(j) // Switch baseado na variável 'j', que controla o efeito atual da fita
  {
    case 0: // Caso o valor de 'j' seja 0
      ws2812fx.service(); // Atualiza a fita de LEDs para aplicar o efeito atual
      delay(1); // Introduz um pequeno atraso para evitar problemas de tempo
      ws2812fx.setMode(FX_MODE_BLINK); // Define o modo de efeito como Blink (piscando)
      ws2812fx.service(); // Atualiza novamente a fita de LEDs para aplicar a mudança de modo
      break; // Sai do caso 0

    case 1: // Caso o valor de 'j' seja 1
      ws2812fx.service(); // Atualiza a fita de LEDs
      delay(1); // Pequeno atraso
      ws2812fx.setMode(FX_MODE_MULTI_STROBE); // Define o modo de efeito como Multi-Strobe (piscando múltiplo)
      ws2812fx.service(); // Atualiza a fita de LEDs para aplicar o novo modo
      break; // Sai do caso 1

    case 2: // Caso o valor de 'j' seja 2
      ws2812fx.service(); // Atualiza a fita de LEDs
      delay(1); // Pequeno atraso
      ws2812fx.setMode(FX_MODE_RANDOM_COLOR); // Define o modo de efeito como cores aleatórias em cada LED
      ws2812fx.service(); // Atualiza a fita de LEDs para aplicar o novo modo
      break; // Sai do caso 2

    case 3: // Caso o valor de 'j' seja 3
      ws2812fx.service(); // Atualiza a fita de LEDs
      ws2812fx.setMode(FX_MODE_FLASH_SPARKLE); // Define o modo de efeito como Flash Sparkle (faíscas de luz)
      delay(1); // Pequeno atraso
      ws2812fx.service(); // Atualiza a fita de LEDs para aplicar o novo modo
      break; // Sai do caso 3
  }

  // Controle para os efeitos Blink (piscada) com diferentes padrões
  if (r2 >= 1 && r2 <= 3) // Se o valor de 'r2' estiver entre 1 e 3, executa a lógica para os efeitos Blink
  {
    ws2812fx.service(); // Atualiza a fita de LEDs
    unsigned long currentMillis = millis(); // Obtém o tempo atual em milissegundos desde que o programa começou

    display.drawBitmap(13, 18, raio, 15, 15, SH110X_WHITE); // Exibe um ícone de raio no display OLED

    uint8_t brightness = map(s2, 0, 100, 0, 255); // Mapeia o valor de brilho (s2) de 0 a 100 para 0 a 255
    uint8_t r, g, b; // Declaração de variáveis para armazenar os valores RGB

    if (r2 == 1) // Se 'r2' for igual a 1, executa o efeito Blink com configuração específica
    {
      if (currentMillis - lastBlinkChange >= 800) // Verifica se se passaram 800 milissegundos desde a última mudança de piscada
      {
        blinkState = !blinkState; // Alterna o estado da piscada (on/off)
        if (blinkState) // Se a piscada estiver ligada (blinkState = true)
        {
          ws2812fx.setSpeed(300); // Define a velocidade do efeito Blink
          r = (255 * brightness) / 255; // Define a cor vermelha com base no brilho
          g = (255 * brightness) / 255; // Define a cor verde com base no brilho
          b = (255 * brightness) / 255; // Define a cor azul com base no brilho
          ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor RGB na fita de LEDs
        }
        else // Se a piscada estiver desligada (blinkState = false)
        {
          ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Define a cor preta (desliga os LEDs)
        }
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança de piscada
      }
    }

    if (r2 == 2) // Se 'r2' for igual a 2, executa o efeito Blink com um intervalo mais rápido
    {
      if (currentMillis - lastBlinkChange >= 300) // Verifica se se passaram 300 milissegundos desde a última mudança
      {
        blinkState = !blinkState; // Alterna o estado da piscada
        if (blinkState)
        {
          ws2812fx.setSpeed(200); // Define a velocidade do efeito Blink
          r = (255 * brightness) / 255; // Ajusta a cor vermelha com base no brilho
          g = (255 * brightness) / 255; // Ajusta a cor verde com base no brilho
          b = (255 * brightness) / 255; // Ajusta a cor azul com base no brilho
          ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor RGB na fita de LEDs
        }
        else
        {
          ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Desliga os LEDs
        }
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança
      }
    }

    if (r2 == 3) // Se 'r2' for igual a 3, executa o efeito Blink com um padrão específico de piscada
    {
      unsigned long intervaloPiscada = 500; // Define o intervalo de 500 milissegundos entre piscadas
      if (currentMillis - lastBlinkChange >= intervaloPiscada) // Verifica se o tempo de intervalo foi atingido
      {
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança
        blinkState = !blinkState; // Alterna o estado da piscada
        blinkCount++; // Incrementa o contador de piscadas

        if (blinkCount == blinkPattern[blinkCount / 3] * 500) // Verifica se o padrão de piscada foi atingido
        {
          blinkCount = 0; // Reseta o contador de piscadas
        }

        r = (255 * brightness) / 255; // Define a cor vermelha com base no brilho
        g = 0; // Verde é zero (desligado)
        b = 0; // Azul é zero (desligado)
        ws2812fx.setColor(ws2812fx.Color(blinkState ? r : 0, g, b)); // Alterna a cor vermelha entre ligada e desligada
      }
    }
  }

  // Controle para o efeito "Polícia", alternando entre luzes vermelha e azul
  if (r2 >= 4 && r2 <= 6) // Se 'r2' estiver entre 4 e 6, executa o efeito de luzes policiais
  {
    display.drawBitmap(13, 18, policia, 15, 15, SH110X_WHITE); // Exibe o ícone de polícia no display

    unsigned long currentMillis = millis(); // Obtém o tempo atual em milissegundos

    uint8_t brightness = map(s2, 0, 100, 0, 255); // Mapeia o valor de brilho de 0 a 100 para 0 a 255
    uint8_t r, g, b; // Variáveis RGB para ajustar a cor dos LEDs

    if (r2 == 4) // Se 'r2' for igual a 4, executa o efeito com luz vermelha piscante
    {
      if (currentMillis - lastBlinkChange >= 700) // Verifica se se passaram 700 milissegundos
      {
        blinkState = !blinkState; // Alterna o estado de piscada
        if (blinkState)
        {
          ws2812fx.setSpeed(200); // Define a velocidade do efeito Blink
          r = (255 * brightness) / 255; // Ajusta a cor vermelha com base no brilho
          g = 0; // Verde desligado
          b = 0; // Azul desligado
          ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor vermelha nos LEDs
        }
        else
        {
          ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Desliga os LEDs
        }
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança
      }
    }

    if (r2 == 5) // Se 'r2' for igual a 5, executa o efeito com luz azul piscante
    {
      if (currentMillis - lastBlinkChange >= 700) // Verifica se se passaram 700 milissegundos
      {
        blinkState = !blinkState; // Alterna o estado de piscada
        if (blinkState)
        {
          ws2812fx.setSpeed(200); // Define a velocidade do efeito Blink
          r = 0; // Vermelho desligado
          g = 0; // Verde desligado
          b = (255 * brightness) / 255; // Ajusta a cor azul com base no brilho
          ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor azul nos LEDs
        }
        else
        {
          ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Desliga os LEDs
        }
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança
      }
    }

    if (r2 == 6) // Se 'r2' for igual a 6, alterna entre luz vermelha e azul
    {
      if (currentMillis - lastBlinkChange >= 400) // Verifica se se passaram 400 milissegundos
      {
        blinkState = !blinkState; // Alterna o estado de piscada
        if (blinkState)
        {
          if (blinkCounter < 1) // Se o contador de piscadas for menor que 1, aciona luz vermelha
          {
            r = (255 * brightness) / 255; // Define a cor vermelha
            g = 0; // Verde desligado
            b = 0; // Azul desligado
            ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor vermelha
          }
          else // Alterna para luz azul
          {
            r = 0; // Vermelho desligado
            g = 0; // Verde desligado
            b = (255 * brightness) / 255; // Define a cor azul
            ws2812fx.setColor(ws2812fx.Color(r, g, b)); // Aplica a cor azul
          }
        }
        else
        {
          ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Desliga os LEDs
          blinkCounter++; // Incrementa o contador de piscadas
          if (blinkCounter >= 2) { blinkCounter = 0; } // Reseta o contador após 2 piscadas
        }
        lastBlinkChange = currentMillis; // Atualiza o tempo da última mudança
      }
      ws2812fx.service(); // Atualiza a fita de LEDs para aplicar as mudanças
    }
  }
  
  // Controle do efeito "Giro" (rotação rápida ou lenta)
  if (r2 >= 7 && r2 <= 9) // Se 'r2' estiver entre 7 e 9, executa o efeito Giro
  {
    display.drawBitmap(13, 18, giro, 15, 15, SH110X_WHITE); // Exibe o ícone de Giro no display
    if (r2 == 7) { ws2812fx.setSpeed(50); } // Efeito Giro com velocidade rápida
    if (r2 == 8) { ws2812fx.setSpeed(2000); } // Efeito Giro com velocidade média
    if (r2 == 9) { ws2812fx.setSpeed(4000); } // Efeito Giro com velocidade lenta
    ws2812fx.service(); // Atualiza a fita de LEDs para aplicar as mudanças
  }

  // Controle do efeito "Festa" (piscada com diferentes velocidades)
  if (r2 >= 10 && r2 <= 12) // Se 'r2' estiver entre 10 e 12, executa o efeito Festa
  {
    display.drawBitmap(13, 18, festa, 15, 15, SH110X_WHITE); // Exibe o ícone de Festa no display
    if (r2 == 10) 
    { 
      ws2812fx.setSpeed(2000); // Define a velocidade do efeito
      ws2812fx.setColor(ws2812fx.Color(0, 0, 0)); // Desliga os LEDs (efeito de apagão)
    }
    if (r2 == 11) { ws2812fx.setSpeed(1000); } // Define a velocidade média para o efeito Festa
    if (r2 == 12) { ws2812fx.setSpeed(100); } // Define a velocidade rápida para o efeito Festa
    ws2812fx.service(); // Atualiza a fita de LEDs para aplicar as mudanças
  }
  ws2812fx.service(); // Atualiza a fita de LEDs para garantir que as mudanças sejam aplicadas
}

void botoesdocontrole() // Função que processa os comandos recebidos pelo controle remoto IR
{
   if (IrReceiver.decode()) // Verifica se há dados recebidos pelo receptor IR
   {
       if (IrReceiver.decodedIRData.protocol != UNKNOWN) // Se o protocolo do controle remoto não for desconhecido
       {
          botaodocontrole = IrReceiver.decodedIRData.command; // Armazena o comando enviado pelo controle no variável 'botaodocontrole'
          delay(40); // Pequeno atraso para evitar múltiplos registros do mesmo comando
       }
   }

   // Verifica se o botão de incremento foi pressionado
   if (botaodocontrole == 90) // Se o comando recebido for 90 (botão de incremento)
   {
       // Controle dos modos na terceira tela (caso 2) e posição 30
       if (caso == 2 and position == 30) 
       {
         if(r2 == 3){j++;botaodocontrole = 0;} // Se o modo atual for 3, incrementa 'j' para mudar o efeito
         if(r2 == 6){j++;botaodocontrole = 0;} // Se o modo atual for 6, incrementa 'j' para mudar o efeito
         if(r2 == 9){j++;botaodocontrole = 0;} // Se o modo atual for 9, incrementa 'j' para mudar o efeito
         if(r2 == 12){j = 0;botaodocontrole = 0;} // Se o modo atual for 12, reseta 'j' para 0
       }

       // Controle de temperatura de cor (t0) na tela 1 e posição 30
       if (position == 30 && caso == 0) 
       { 
           if (t0 < 6500){t0 += 100;} // Se a temperatura de cor for menor que 6500K, incrementa em 100
           else{t0 = 2500;} // Se atingir 6500K, reseta para 2500K
       }

       // Controle de matiz (h1) na tela 2 e posição 30
       else if (position == 30 && caso == 1) 
       { 
           if (h1 < 360){h1++;} // Incrementa o matiz até 360
           else{h1 = 0;} // Reseta o matiz para 0 quando atingir 360
       }

       // Controle de cenas (r2) na tela 3 e posição 30
       else if (position == 30 && caso == 2) 
       { 
           if (r2 < 12){r2++;} // Incrementa o valor de cena (r2) até 12
           else{r2 = 1;} // Reseta o valor de cena quando atinge 12
       }

       // Controle de saturação (s0) na tela 1 e posição 15
       if (position == 15 && caso == 0) 
       { 
           if (s0 < 100){s0++;} // Incrementa a saturação até 100
           else{s0 = 0;} // Reseta a saturação quando atingir 100
       }

       // Controle de saturação (s1) na tela 2 e posição 15
       else if (position == 15 && caso == 1) 
       { 
           if(s1 < 100){s1++;} // Incrementa a saturação na tela 2
           else{s1 = 0;} // Reseta a saturação quando atingir 100
       }

       // Controle de brilho na tela 3 e posição 15
       else if (position == 15 && caso == 2) 
       {
           if (s2 < 100){s2++;} // Incrementa o brilho na tela 3
           else {s2 = 0;} // Reseta o brilho quando atingir 100
       }

       // Controle do modo turbo na tela 1 (CTT) e posição 0
       if (position == 0 && caso == 0) 
       { 
           if (turbo0 < 1){turbo0++;} // Ativa o modo turbo
           else {turbo0 = 0;} // Desativa o modo turbo
           caso = 3; // Define o caso como modo turbo
       }

       // Controle de brilho na tela 2 e posição 0
       else if (position == 0 && caso == 1) 
       { 
           if (b1 < 100){b1++;} // Incrementa o brilho na tela 2
           else {b1 = 0;} // Reseta o brilho quando atingir 100
       }
       
       botaodocontrole = 0; // Reseta o comando do controle para evitar repetição
   }

   // Verifica se o botão de decremento foi pressionado
   if (botaodocontrole == 8) // Se o comando recebido for 8 (botão de decremento)
   {
       // Controle dos modos na terceira tela (caso 2) e posição 30
       if (caso == 2 and position == 30)
       {
           if(r2 == 1){j = 3; botaodocontrole = 0;} // Se o modo for 1, define 'j' como 3
           if(r2 == 4){j--; botaodocontrole = 0;} // Se o modo for 4, decrementa 'j'
           if(r2 == 7){j--; botaodocontrole = 0;} // Se o modo for 7, decrementa 'j'
           if(r2 == 10){j--; botaodocontrole = 0;} // Se o modo for 10, decrementa 'j'
       }

       // Controle de temperatura de cor (t0) na tela 1 e posição 30
       if (position == 30 && caso == 0) 
       { 
           if (t0 > 2500){t0 -= 100;} // Se a temperatura for maior que 2500K, decrementa em 100
           else {t0 = 6500;} // Se atingir 2500K, reseta para 6500K
       }

       // Controle de matiz (h1) na tela 2 e posição 30
       else if (position == 30 && caso == 1) 
       { 
           if (h1 > 0){h1--;} // Decrementa o matiz até 0
           else {h1 = 360;} // Reseta o matiz quando atinge 0
       }

       // Controle de cenas (r2) na tela 3 e posição 30
       else if (position == 30 && caso == 2) 
       { 
           if (r2 > 1){r2--;} // Decrementa o valor de cena (r2)
           else {r2 = 12;} // Reseta o valor de cena para 12
       }

       // Controle de saturação (s0) na tela 1 e posição 15
       if (position == 15 && caso == 0) 
       { 
           if (s0 > 0){s0--;} // Decrementa a saturação
           else {s0 = 100;} // Reseta a saturação para 100
       }

       // Controle de saturação (s1) na tela 2 e posição 15
       else if (position == 15 && caso == 1) 
       { 
           if(s1 > 0){s1--;} // Decrementa a saturação na tela 2
           else{s1 = 100;} // Reseta a saturação quando atingir 0
       }

       // Controle de brilho na tela 3 e posição 15
       else if (position == 15 && caso == 2) 
       { 
           if (s2 > 0){s2--;} // Decrementa o brilho
           else {s2 = 100;} // Reseta o brilho para 100
       }

       // Controle do modo turbo na tela 1 (CTT) e posição 0
       if (position == 0 && caso == 3) 
       { 
           if (turbo0 > 0){turbo0--;} // Desativa o modo turbo
           else {turbo0 = 1;} // Ativa o modo turbo
           caso = 0; // Volta ao modo normal
       }

       // Controle de brilho na tela 2 e posição 0
       else if (position == 0 && caso == 1) 
       { 
           if (b1 > 0){b1--;} // Decrementa o brilho
           else {b1 = 100;} // Reseta o brilho para 100
       }

       botaodocontrole = 0; // Reseta o comando do controle
   }
}

Video do bastão de LED funcionando

Veja abaixo, um vídeo, de todo o protótipo do bastão de LED funcionando. Nele, você pode ver os três modos de funcionamento e o controle funcionando em tempo real. Perceba que a tela parece piscar por conta da atualização constante do conteúdo da tela.

Conclusão

Hoje, aprendemos a construir um protótipo de um bastão de LED, montar seu circuito e realizar sua programação. No próximo tutorial desta série, vamos construir o 3D da case do bastão e ajustar as atualizações da tela, para que ela não fique piscando no vídeo. Até a próxima!