Código da Corrida de Leds para 4 jogadores, adapatado pelos alunos de Robótica da ETEFV.
Código adaptado do Open LED Race para 4 jogadores (4 carros)
Jogador 1 no pino D7 carrinho vermelho
Jogador 2 no pino D6 carinho verde
Jogador 3 no pino D8 carinho azul
Jogador 4 no pino D9 carinho amarelo
/* ======================================================
Corrida de Leds - Robótica ETEFV
Adaptado por Eric Alves de Almeida
Coordenação: César Bastos
em 16/09/2026
======================================================
*/
// Biblioteca para usar a fita de leds RGB endereçada
#include <Adafruit_NeoPixel.h>
// ======================================================
// CONFIGURAÇÃO DAS PORTAS
// ======================================================
#define MAXLED 300 // número máximo de leds da fita
// Fita de LED
#define PIN_LED A0
// Botões dos jogadores
#define PIN_P1 7 // pino do vermelho
#define PIN_P2 6 // pino do verde
#define PIN_P3 8 // pino azul
#define PIN_P4 9 // pino amarelo
// Áudio
#define PIN_AUDIO 3
// Quantidade de LEDs
int NPIXELS = MAXLED;
// Número de jogadores
#define PLAYERS 4
// Número máximo de voltas
byte loop_max = 5;
// ======================================================
// CORES DOS JOGADORES
// ======================================================
#define COLOR_P1 track.Color(0, 255, 0) // Verde P1
#define COLOR_P2 track.Color(255, 0, 0) // Vermelho P2
#define COLOR_P3 track.Color(0, 0, 255) // Azul P3
#define COLOR_P4 track.Color(255, 255, 0) // Amarelo P4
// ======================================================
// MÚSICA DE VITÓRIA
// ======================================================
int win_music[] = {
2093, 2637, 3136, 2093,
2637, 3136, 2093, 2637,
3136
};
// ======================================================
// FÍSICA
// ======================================================
// Mapa de inclinação da pista
byte gravity_map[MAXLED];
// Velocidade de cada jogador
float speed[PLAYERS] = {0, 0, 0, 0};
// Distância percorrida por cada jogador
float dist[PLAYERS] = {0, 0, 0, 0};
// Voltas de cada jogador
byte laps[PLAYERS] = {0, 0, 0, 0};
// Estado dos botões
byte flag_sw[PLAYERS] = {0, 0, 0, 0};
// Pinos dos botões
byte playerPin[PLAYERS] = {
PIN_P1,
PIN_P2,
PIN_P3,
PIN_P4
};
// ======================================================
// CONSTANTES DA FÍSICA
// ======================================================
// Quanto cada apertada acelera
float ACEL = 0.2;
// Atrito
float kf = 0.015;
// Gravidade da pista
float kg = 0.003;
// ======================================================
// OUTRAS VARIÁVEIS
// ======================================================
byte leader = 0;
int TBEEP = 3;
unsigned long timestamp = 0;
int tdelay = 5;
// Objeto da fita de LED
Adafruit_NeoPixel track =
Adafruit_NeoPixel(
MAXLED,
PIN_LED,
NEO_GRB + NEO_KHZ800
);
// ======================================================
// CRIA UMA RAMPA
// ======================================================
void set_ramp(byte H, byte a, byte b, byte c)
{
for (int i = 0; i < (b - a); i++)
{
gravity_map[a + i] =
127 - i * ((float)H / (b - a));
}
gravity_map[b] = 127;
for (int i = 0; i < (c - b); i++)
{
gravity_map[b + i + 1] =
127 + H - i * ((float)H / (c - b));
}
}
// ======================================================
// CRIA UM LOOP
// ======================================================
void set_loop(byte H, byte a, byte b, byte c)
{
for (int i = 0; i < (b - a); i++)
{
gravity_map[a + i] =
127 - i * ((float)H / (b - a));
}
gravity_map[b] = 255;
for (int i = 0; i < (c - b); i++)
{
gravity_map[b + i + 1] =
127 + H - i * ((float)H / (c - b));
}
}
// ======================================================
// EFEITO DE VITÓRIA
// ======================================================
void winner_fx()
{
int msize = sizeof(win_music) / sizeof(int);
for (int note = 0; note < msize; note++)
{
tone(PIN_AUDIO, win_music[note], 200);
delay(230);
noTone(PIN_AUDIO);
}
}
// ======================================================
// APAGA TODA A PISTA
// ======================================================
void clear_track()
{
for (int i = 0; i < NPIXELS; i++)
{
track.setPixelColor(i, track.Color(0, 0, 0));
}
track.show();
}
// ======================================================
// CONTAGEM DE LARGADA
// ======================================================
void start_race()
{
clear_track();
delay(2000);
// VERMELHO
track.setPixelColor(12, track.Color(255, 0, 0));
track.setPixelColor(11, track.Color(255, 0, 0));
track.show();
tone(PIN_AUDIO, 400);
delay(2000);
noTone(PIN_AUDIO);
track.setPixelColor(12, track.Color(0, 0, 0));
track.setPixelColor(11, track.Color(0, 0, 0));
// AMARELO
track.setPixelColor(10, track.Color(255, 255, 0));
track.setPixelColor(9, track.Color(255, 255, 0));
track.show();
tone(PIN_AUDIO, 600);
delay(2000);
noTone(PIN_AUDIO);
track.setPixelColor(9, track.Color(0, 0, 0));
track.setPixelColor(10, track.Color(0, 0, 0));
// VERDE
track.setPixelColor(8, track.Color(0, 255, 0));
track.setPixelColor(7, track.Color(0, 255, 0));
track.show();
tone(PIN_AUDIO, 1200);
delay(2000);
noTone(PIN_AUDIO);
timestamp = 0;
}
// ======================================================
// DESENHA UM CARRO
// ======================================================
void draw_car(byte player)
{
// Cores dos jogadores
uint32_t color;
if (player == 0)
{
color = track.Color(0, 255, 0); // Verde
}
else if (player == 1)
{
color = track.Color(255, 0, 0); // Vermelho
}
else if (player == 2)
{
color = track.Color(0, 0, 255); // Azul
}
else
{
color = track.Color(255, 255, 0); // Amarelo
}
// Tamanho visual do carro
for (int i = 0; i <= laps[player]; i++)
{
int pixel =
((word)dist[player] % NPIXELS) + i;
// Evita ultrapassar os 300 LEDs
pixel = pixel % NPIXELS;
int intensidade = 255 - i * 20;
if (intensidade < 0)
{
intensidade = 0;
}
if (player == 0)
{
track.setPixelColor(
pixel,
track.Color(0, intensidade, 0)
);
}
else if (player == 1)
{
track.setPixelColor(
pixel,
track.Color(intensidade, 0, 0)
);
}
else if (player == 2)
{
track.setPixelColor(
pixel,
track.Color(0, 0, intensidade)
);
}
else
{
track.setPixelColor(
pixel,
track.Color(intensidade, intensidade, 0)
);
}
}
}
// ======================================================
// DESENHA OS QUATRO CARROS
// ======================================================
void draw_all_cars()
{
// Desenha os quatro jogadores
draw_car(0);
draw_car(1);
draw_car(2);
draw_car(3);
}
// ======================================================
// RESET DA CORRIDA
// ======================================================
void reset_race()
{
for (int player = 0; player < PLAYERS; player++)
{
speed[player] = 0;
dist[player] = 0;
laps[player] = 0;
flag_sw[player] = 0;
}
leader = 0;
timestamp = 0;
}
// ======================================================
// MOSTRA O VENCEDOR
// ======================================================
void show_winner(byte player)
{
uint32_t winnerColor;
if (player == 0)
{
winnerColor = track.Color(0, 255, 0);
}
else if (player == 1)
{
winnerColor = track.Color(255, 0, 0);
}
else if (player == 2)
{
winnerColor = track.Color(0, 0, 255);
}
else
{
winnerColor = track.Color(255, 255, 0);
}
// Acende a pista inteira com a cor do vencedor
for (int i = 0; i < NPIXELS; i++)
{
track.setPixelColor(i, winnerColor);
}
track.show();
winner_fx();
delay(1000);
reset_race();
start_race();
}
// ======================================================
// CONFIGURAÇÃO
// ======================================================
void setup()
{
// Inicializa mapa da pista como plano
for (int i = 0; i < NPIXELS; i++)
{
gravity_map[i] = 127;
}
// Inicializa LEDs
track.begin();
// Configura os quatro botões
pinMode(PIN_P1, INPUT_PULLUP);
pinMode(PIN_P2, INPUT_PULLUP);
pinMode(PIN_P3, INPUT_PULLUP);
pinMode(PIN_P4, INPUT_PULLUP);
// ==================================================
// ATIVA A RAMPA
// ==================================================
//
// Para ativar a rampa:
// mantenha o botão do jogador 1 pressionado
// enquanto liga/reset o Arduino.
//
if (digitalRead(PIN_P1) == LOW)
{
set_ramp(12, 90, 100, 110);
for (int i = 0; i < NPIXELS; i++)
{
int blue =
(127 - gravity_map[i]) / 8;
if (blue < 0)
blue = 0;
track.setPixelColor(
i,
track.Color(0, 0, blue)
);
}
track.show();
}
// Inicia corrida
start_race();
}
// ======================================================
// LOOP PRINCIPAL
// ======================================================
void loop()
{
// ==================================================
// DESENHA A PISTA
// ==================================================
for (int i = 0; i < NPIXELS; i++)
{
int blue =
(127 - gravity_map[i]) / 8;
if (blue < 0)
blue = 0;
track.setPixelColor(
i,
track.Color(0, 0, blue)
);
}
// ==================================================
// PROCESSA OS QUATRO JOGADORES
// ==================================================
for (int player = 0; player < PLAYERS; player++)
{
// -----------------------------------------------
// LEITURA DO BOTÃO
// -----------------------------------------------
// Botão pressionado
if (
flag_sw[player] == 1 &&
digitalRead(playerPin[player]) == LOW
)
{
flag_sw[player] = 0;
// Acelera
speed[player] += ACEL;
}
// Botão solto
if (
flag_sw[player] == 0 &&
digitalRead(playerPin[player]) == HIGH
)
{
flag_sw[player] = 1;
}
// -----------------------------------------------
// GRAVIDADE
// -----------------------------------------------
int pixel =
(word)dist[player] % NPIXELS;
// Subida
if (gravity_map[pixel] < 127)
{
speed[player] -=
kg * (127 - gravity_map[pixel]);
}
// Descida
if (gravity_map[pixel] > 127)
{
speed[player] +=
kg * (gravity_map[pixel] - 127);
}
// -----------------------------------------------
// ATRITO
// -----------------------------------------------
speed[player] -=
speed[player] * kf;
// -----------------------------------------------
// EVITA VELOCIDADE NEGATIVA
// -----------------------------------------------
if (speed[player] < 0)
{
speed[player] = 0;
}
// -----------------------------------------------
// ATUALIZA DISTÂNCIA
// -----------------------------------------------
dist[player] += speed[player];
// -----------------------------------------------
// VERIFICA VOLTA
// -----------------------------------------------
if (dist[player] > NPIXELS * (laps[player] + 1))
{
laps[player]++;
// Sons diferentes para cada jogador
if (player == 0)
{
tone(PIN_AUDIO, 600);
}
else if (player == 1)
{
tone(PIN_AUDIO, 700);
}
else if (player == 2)
{
tone(PIN_AUDIO, 800);
}
else
{
tone(PIN_AUDIO, 900);
}
TBEEP = 2;
}
}
// ==================================================
// DESCOBRE QUEM ESTÁ NA FRENTE
// ==================================================
leader = 0;
for (int player = 1; player < PLAYERS; player++)
{
if (dist[player] > dist[leader])
{
leader = player;
}
}
// ==================================================
// VERIFICA VENCEDOR
// ==================================================
for (int player = 0; player < PLAYERS; player++)
{
if (laps[player] > loop_max)
{
show_winner(player);
// Evita continuar processando esse ciclo
return;
}
}
// ==================================================
// DESENHA OS CARROS
// ==================================================
draw_all_cars();
track.show();
// ==================================================
// PEQUENO DELAY
// ==================================================
delay(tdelay);
// ==================================================
// CONTROLE DO BEEP
// ==================================================
if (TBEEP > 0)
{
TBEEP--;
if (TBEEP == 0)
{
noTone(PIN_AUDIO);
}
}
}