Neste projeto vamos construir um jogo Tetris utilizando um ESP32 e quatro matrizes de LED MAX7219.
O jogo roda totalmente no microcontrolador, e utilizaremos botões para mover as peças para a esquerda e direita.
Este projeto é excelente para aprender:
- Programação embarcada
- Matrizes de LED
- Comunicação SPI
- Lógica de jogos
- Manipulação de coordenadas
- Estruturas de dados
- Controle por botões
Materiais Necessários
- 1x ESP32
- 4x Display Matrix MAX7219 8×8
- 2x Botões
- Jumpers
- Protoboard
- Cabo USB
Ligações dos Displays MAX7219
| MAX7219 | ESP32 |
| VCC | 5V |
| GND | GND |
| DIN | D23 |
| CLK | D18 |
| CS | D5 |
Os displays devem ser ligados em cascata:
- DOUT do display 1 → DIN do display 2
- DOUT do display 2 → DIN do display 3
- DOUT do display 3 → DIN do display 4
Os displays devem ficar empilhados na vertical, onde:
- Display 1 = topo
- Display 4 = chão do Tetris
Ligações dos Botões
| Botão | ESP32 | Outro lado |
| esquerda | D19 | GND |
| direita | D21 | GND |
Os pinos serão configurados como INPUT_PULLUP, então não precisa resistor externo.
Funcionamento do Jogo
O jogo funciona da seguinte forma:
- Uma peça aparece no topo
- A peça desce automaticamente
- Botões movem esquerda/direita
- Quando a peça encosta, ela fixa
- Linhas completas são apagadas
- Nova peça aparece
- O jogo continua
O display total possui:
- 8 colunas
- 32 linhas
- Total: 8×32 pixels
Código do Projeto – Tetris ESP32 + MAX7219
#include <MD_MAX72xx.h>
#include <SPI.h>
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 5
#define BTN_LEFT 19
#define BTN_RIGHT 21
MD_MAX72XX mx(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
const int W = 8;
const int H = 32;
bool board[H][W];
int pieceX, pieceY;
int pieceType;
int rot;
unsigned long lastFall = 0;
unsigned long lastMove = 0;
int fallDelay = 250;
int moveDelay = 120;
const uint16_t pieces[7][4] = {
{0x0F00,0x2222,0x00F0,0x4444},
{0x6600,0x6600,0x6600,0x6600},
{0x4E00,0x4640,0x0E40,0x4C40},
{0x6C00,0x4620,0x06C0,0x8C40},
{0xC600,0x2640,0x0C60,0x4C80},
{0x8E00,0x6440,0x0E20,0x44C0},
{0x2E00,0x4460,0x0E80,0xC440}
};
bool cell(int type, int rot, int x, int y)
{
uint16_t m = pieces[type][rot];
int bit = 15 - (y * 4 + x);
return (m >> bit) & 1;
}
// Mapeamento vertical correto
void drawPixelGame(int x, int y, bool state)
{
if (x < 0 || x >= W || y < 0 || y >= H) return;
int module = y / 8;
int localY = y % 8;
localY = 7 - localY;
int row = 7 - x;
int col = (MAX_DEVICES - 1 - module) * 8 + localY;
mx.setPoint(row, col, state);
}
void clearBoard()
{
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++)
board[y][x] = false;
}
bool collision(int nx, int ny, int r)
{
for (int py = 0; py < 4; py++)
for (int px = 0; px < 4; px++)
{
if (!cell(pieceType, r, px, py)) continue;
int bx = nx + px;
int by = ny + py;
if (bx < 0 || bx >= W || by >= H) return true;
if (by >= 0 && board[by][bx]) return true;
}
return false;
}
void mergePiece()
{
for (int py = 0; py < 4; py++)
for (int px = 0; px < 4; px++)
{
if (!cell(pieceType, rot, px, py)) continue;
int bx = pieceX + px;
int by = pieceY + py;
if (by >= 0 && bx >= 0 && bx < W && by < H)
board[by][bx] = true;
}
}
void clearLines()
{
for (int y = H - 1; y >= 0; y--)
{
bool full = true;
for (int x = 0; x < W; x++)
if (!board[y][x]) full = false;
if (full)
{
for (int yy = y; yy > 0; yy--)
for (int x = 0; x < W; x++)
board[yy][x] = board[yy - 1][x];
for (int x = 0; x < W; x++)
board[0][x] = false;
y++;
}
}
}
void newPiece()
{
pieceType = random(0, 7);
rot = random(0, 4);
pieceX = random(0, W - 3);
pieceY = -1;
}
void draw()
{
mx.clear();
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++)
if (board[y][x])
drawPixelGame(x, y, true);
for (int py = 0; py < 4; py++)
for (int px = 0; px < 4; px++)
{
if (!cell(pieceType, rot, px, py)) continue;
int bx = pieceX + px;
int by = pieceY + py;
drawPixelGame(bx, by, true);
}
}
void setup()
{
mx.begin();
mx.control(MD_MAX72XX::INTENSITY, 5);
mx.clear();
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
randomSeed(micros());
clearBoard();
newPiece();
}
void loop()
{
if (millis() - lastMove > moveDelay)
{
lastMove = millis();
if (digitalRead(BTN_LEFT) == LOW)
if (!collision(pieceX - 1, pieceY, rot))
pieceX--;
if (digitalRead(BTN_RIGHT) == LOW)
if (!collision(pieceX + 1, pieceY, rot))
pieceX++;
}
if (millis() - lastFall > fallDelay)
{
lastFall = millis();
if (!collision(pieceX, pieceY + 1, rot))
pieceY++;
else
{
mergePiece();
clearLines();
newPiece();
}
}
draw();
}
O que você aprendeu com esse projeto
Esse projeto envolve vários conceitos importantes:
- Comunicação SPI
- Matrizes de LED
- Coordenadas X e Y
- Arrays bidimensionais
- Detecção de colisão
- Estrutura de jogo
- Leitura de botões
- Programação embarcada
- Estruturas de dados
- Lógica de rotação de peças
- Sistemas embarcados
Esse tipo de projeto é exatamente o tipo de coisa que forma um profissional de eletrônica e sistemas embarcados.
Saiba mais sobre a parceria Saravati e Sara Educação
Este “Guia de Montagem” é uma colaboração especial entre a Saravati e a Sara Educação, criado pelo Professor Felipe Rosa. Nosso objetivo é enriquecer a comunidade de entusiastas da eletrônica, IoT e automação com recursos educacionais de alta qualidade. Através dessa parceria, buscamos inspirar e capacitar criadores em seus projetos, disponibilizando esses guias em nosso blog e nas redes sociais.
