Neste tutorial, vais aprender a implementar o Game of Life num Display de Matriz de LEDs 8×8 usando o driver MAX7219 e um Arduino. Também vou fornecer instruções e uma implementação para um ESP8266 ESP-12F Mini em vez do Arduino. Isto permitirá que construas um icónico Game of Life num Cubo que executa o padrão glider, mostrado acima.
Não fica mais nerd do que isto!
O que é o Game of Life
O Game of Life, também conhecido como Jogo da Vida de Conway, é um autómato celular criado pelo matemático John Horton Conway em 1970. Não é um jogo tradicional no sentido de ter jogadores ou condições de vitória ou derrota. Em vez disso, é uma simulação que segue um conjunto de regras simples e demonstra padrões e comportamentos complexos.
O Game of Life decorre numa grelha de células, onde cada célula pode estar viva ou morta. A grelha pode ter qualquer tamanho, mas para este tutorial usaremos um Display de Matriz de LEDs 8×8. O jogo avança em passos de tempo discretos, onde o estado de cada célula na grelha é atualizado com base no seu estado atual e nos estados das células vizinhas.
Regras
As regras do Game of Life são as seguintes:
- Uma célula viva com menos de dois vizinhos vivos morre.
- Uma célula viva com dois ou três vizinhos vivos continua viva.
- Uma célula viva com mais de três vizinhos vivos morre.
- Uma célula morta com exatamente três vizinhos vivos torna-se viva.
Abaixo podes ver cinco padrões (a,… ,e) e a sua evolução ao longo de três iterações (0,… ,2). Esta imagem é de Martin Gardner’s uma coluna na Scientific America, onde o Game of Life de Conway foi popularizado pela primeira vez.

Estas regras são aplicadas simultaneamente a todas as células da grelha, o que leva ao surgimento de padrões e comportamentos fascinantes ao longo do tempo. O Game of Life é um exemplo clássico de autómato celular e tem sido amplamente estudado pelas suas propriedades matemáticas e pela sua capacidade de simular sistemas complexos.
Glider
O padrão mais popular no Game of Life é o “glider”. Um glider é uma configuração de células que se move diagonalmente pela grelha à medida que o jogo avança. É um padrão auto-replicante que pode ser usado para criar outros padrões e estruturas interessantes. Aqui está um exemplo de um glider em movimento:

Este é apenas um dos muitos padrões interessantes e complexos (link) que podem ser criados no Game of Life de Conway. Para explorar alguns destes padrões, dá uma vista de olhos a este Game of Life simulador, que é muito divertido de usar. O jogo tem fascinado pessoas durante décadas e levou a pesquisas significativas nas áreas de matemática, ciência da computação e biologia teórica.
Game of Life num Cubo
Ao implementá-lo num Display de Matriz de LEDs 8×8 usando o driver MAX7219, podes visualizar o jogo de forma tangível. Abaixo podes ver como o Game of Life num Cubo que vamos construir vai parecer quando estiver terminado.

Nas próximas secções deste tutorial, vamos explorar os componentes necessários, como ligar o display e o código necessário para implementar o Game of Life.
Componentes Necessários
Abaixo encontras as peças necessárias para este projeto. Se quiseres apenas experimentar rapidamente e correr o Game of Life com o Arduino, em vez de comprares o Display de Matriz 8×8 sugerido, compra este (link). É um módulo maior e não vai caber no cubo, mas tem a vantagem de não precisares de soldar.
Se quiseres construir o cubo, vais precisar do ESP8266 ESP-12F Mini (ou uma placa de tamanho semelhante) e do Display de Matriz 8×8 sugerido. Nota, no entanto, que precisas de saber fazer uma soldadura simples de fios e pinos.

Arduino Uno

ESP8266 ESP-12F Mini

Cabo USB para Arduino UNO

Conjunto de fios Dupont

Display de Matriz de LEDs 8×8 MAX7219
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Soldar o Display de Matriz
Se comprares o Display de Matriz de LEDs 8×8 MAX7219 listado, vais notar que o display LED 8×8 e a placa com o driver MAX7219 vêm separados. Vais precisar de os soldar e garantir que a orientação está correta. Se o número da peça do display LED estiver na frente, tens de orientar a placa de modo que a etiqueta “DISY1” na placa também fique na frente. Vê a imagem abaixo.

Se usares os pinos headers, isto não é crítico, pois podes rodar o display depois. Mas se soldares o display diretamente à placa, tens de acertar isto bem.
A propósito, o MAX7219 é aquele chip preto na placa. Ele simplifica significativamente o controlo do Display de Matriz 8×8 e especialmente reduz o número de pinos GPIO que precisamos do Arduino. Para mais detalhes, dá uma vista de olhos ao nosso MAX7219 LED dot matrix display Arduino tutorial e talvez ao datasheet em si.
Na próxima secção, vou mostrar-te como ligar o display a um Arduino.
Ligar o Display de Matriz
Depois de teres o display soldado à placa, ligá-lo ao Arduino é simples. A imagem abaixo mostra a ligação.

Isto completa a ligação do display ao Arduino. Se precisares de mais detalhes sobre a ligação dos componentes, aqui está a tabela completa de ligações.
| De | Pino | Cor do fio | Para | Pino |
| Arduino | 5V | Vermelho | Display | VCC / 5V |
| Arduino | GND | Azul | Display | GND |
| Arduino | 12 | Amarelo | Display | CLK |
| Arduino | 11 | Laranja | Display | DIN |
| Arduino | 10 | Verde | Display | CS |
Código para o Game of Life
Nesta secção vamos escrever o código que executa o Game of Life num Arduino e mostra o padrão glider no Display de Matriz.
Instalar a biblioteca LedControl
Para controlar o Display vamos usar a LedControl biblioteca. A instalação é simples. Basta ir a “Tools” e depois “Manage Libraries..” no Arduino IDE:

Depois procura por “LedControl” e instala a “LedControl library by Eberhard Fahle”. A imagem abaixo mostra como fica depois de completares a instalação. Deve demorar apenas um segundo.

Agora estamos prontos para escrever o código para o Arduino.
Código Arduino
Dá uma vista rápida ao código completo primeiro para teres uma visão geral. Vou explicar os detalhes nas secções seguintes.
// Game of Life on an 8x8 Dot Matrix Display
#include "LedControl.h"
// Arduino
const byte CLK = 12;
const byte DIN = 11;
const byte CS = 10;
LedControl lc = LedControl(DIN, CLK, CS, 0);
// glider
byte board[8] = {
B00000000,
B00000000,
B00010000,
B00001000,
B00111000,
B00000000,
B00000000,
B00000000,
};
void set_bit(byte *b, byte pos, byte val) {
if (val > 0) {
*b |= (1 << pos);
} else {
*b &= ~(1 << pos);
}
}
byte get_bit(byte *b, byte pos) {
return (*b >> pos) & 0x01;
}
void set(byte *board, byte r, byte c, byte val) {
set_bit(&board[r], c, val);
}
byte get(byte *board, byte r, byte c) {
return get_bit(&board[r], c);
}
void update_display() {
lc.clearDisplay(0);
for (int i = 0; i < 8; i++) {
lc.setRow(0, i, board[i]);
}
}
int count_neighbors(byte r, byte c) {
int count = 0;
for (int rd = -1; rd <= 1; rd++) {
for (int cd = -1; cd <= 1; cd++) {
if (rd == 0 && cd == 0) continue;
int nr = (r + rd + 8) % 8;
int nc = (c + cd + 8) % 8;
count += get(board, nr, nc);
}
}
return count;
}
void update_board() {
byte temp[8];
byte alive;
for (byte r = 0; r < 8; r++) {
for (byte c = 0; c < 8; c++) {
int neighbors = count_neighbors(r, c);
if (get(board, r, c)) {
alive = !(neighbors < 2 || neighbors > 3);
} else {
alive = neighbors == 3;
}
set(temp, r, c, alive);
}
}
memcpy(board, temp, sizeof(board));
}
void setup() {
Serial.begin(9600);
lc.shutdown(0, false);
lc.clearDisplay(0);
}
void loop() {
for (int step = 0; step < 4; step++) {
lc.setIntensity(0, step);
update_display();
update_board();
delay(500);
}
}
Constantes e Variáveis
Começamos por incluir a biblioteca LedControl necessária e definir os pinos para o driver MAX7219. O pino CLK está ligado ao pino 12, o pino DIN está ligado ao pino 11, e o pino CS está ligado ao pino 10. Também criamos uma instância da LedControl classe com os pinos especificados.
#include "LedControl.h" const byte CLK = 12; const byte DIN = 11; const byte CS = 10; LedControl lc = LedControl(DIN, CLK, CS, 0);
De seguida, definimos o estado inicial do tabuleiro do jogo. O tabuleiro é representado como uma grelha 8×8 de células, onde cada célula pode estar viva (1) ou morta (0). O estado inicial é definido usando notação binária e cada linha é um byte com 8 bits. Como temos 8 linhas, obtemos uma matriz de 8×8 bits que corresponde à dimensão do nosso Display de Matriz de LEDs 8×8.
byte board[8] = {
B00000000,
B00000000,
B00010000,
B00001000,
B00111000,
B00000000,
B00000000,
B00000000,
};
Funções Auxiliares
Definimos duas funções auxiliares, set_bit() e get_bit(), para manipular bits individuais num byte. Estas funções são usadas para definir ou obter o valor de um bit específico no tabuleiro do jogo.
void set_bit(byte *b, byte pos, byte val) {
if (val > 0) {
*b |= (1 << pos);
} else {
*b &= ~(1 << pos);
}
}
byte get_bit(byte *b, byte pos) {
return (*b >> pos) & 0x01;
}
Definimos também duas funções, set() e get(), para definir ou obter o valor de uma célula específica no tabuleiro do jogo. Estas funções usam as funções set_bit() e get_bit() para manipular os bits individuais no tabuleiro do jogo.
void set(byte *board, byte r, byte c, byte val) {
set_bit(&board[r], c, val);
}
byte get(byte *board, byte r, byte c) {
return get_bit(&board[r], c);
}
Funções de Display e Atualização
A função update_display() é responsável por atualizar o display de matriz de LEDs com o estado atual do tabuleiro do jogo. Ela limpa o display e depois define as linhas do display com base nos valores do tabuleiro do jogo. Note que definimos linhas inteiras de uma vez, pois é mais simples e rápido do que definir bits individuais.
void update_display() {
lc.clearDisplay(0);
for (int i = 0; i < 8; i++) {
lc.setRow(0, i, board[i]);
}
}
A função count_neighbors() conta o número de vizinhos vivos para uma dada célula no tabuleiro do jogo. Ela itera sobre as células vizinhas e verifica os seus valores usando a função get().
int count_neighbors(byte r, byte c) {
int count = 0;
for (int rd = -1; rd <= 1; rd++) {
for (int cd = -1; cd <= 1; cd++) {
if (rd == 0 && cd == 0) continue;
int nr = (r + rd + 8) % 8;
int nc = (c + cd + 8) % 8;
count += get(board, nr, nc);
}
}
return count;
}
A função update_board() atualiza o tabuleiro do jogo com base nas regras do Game of Life. Cria um tabuleiro temporário temp para armazenar os valores atualizados. Para cada célula no tabuleiro do jogo, conta o número de vizinhos vivos usando a função count_neighbors() e aplica as regras para determinar se a célula deve estar viva ou morta. Os valores atualizados são então definidos no tabuleiro temporário usando a função set(). Finalmente, os valores atualizados são copiados de volta para o tabuleiro original usando a função memcpy().
void update_board() {
byte temp[8];
byte alive;
for (byte r = 0; r < 8; r++) {
for (byte c = 0; c < 8; c++) {
int neighbors = count_neighbors(r, c);
if (get(board, r, c)) {
alive = !(neighbors < 2 || neighbors > 3);
} else {
alive = neighbors == 3;
}
set(temp, r, c, alive);
}
}
memcpy(board, temp, sizeof(board));
}
Funções Setup e Loop
Na função setup(), inicializamos a comunicação serial para fins de depuração, desativamos o modo de desligamento do driver MAX7219 e limpamos o display.
void setup() {
Serial.begin(9600);
lc.shutdown(0, false);
lc.clearDisplay(0);
}
Na função loop(), executamos o Game of Life por quatro passos. Isto porque o período do padrão Glider é também de 4 passos. Após 4 iterações, o Glider mostra o mesmo padrão, mas deslocou-se diagonalmente um passo à frente.
Para cada passo, definimos a intensidade do display de matriz de LEDs. Isto destaca as diferentes fases da evolução do padrão Glider. Se quiseres que o Glider seja exibido com brilho constante, podes alterar isso aqui. O valor da intensidade varia de 0 a 15.
Depois disso, atualizamos o display com o estado atual do tabuleiro do jogo usando a função update_display(), atualizamos o tabuleiro do jogo usando a função update_board() e fazemos uma pausa de 500 milissegundos antes do próximo passo. O atraso serve apenas para abrandar o Glider. Novamente, isto é algo que podes facilmente alterar.
void loop() {
for (int step = 0; step < 4; step++) {
lc.setIntensity(0, step);
update_display();
update_board();
delay(500);
}
}
E é isso! Agora tens o Game of Life a correr num Arduino. Claro que isto é só o começo. Para além do Glider, há muitos outros padrões interessantes que podes adicionar ao programa. Esses diferentes padrões poderiam ser selecionados via um botão, um encoder rotativo ou outro dispositivo de entrada.
No entanto, ter tudo numa caixa bonita e a possibilidade de controlar o dispositivo via uma interface web seria muito mais fixe. E é isso que vou mostrar na próxima secção.
Não vamos implementar a interface web, mas vamos usar o ESP8266 ESP-12F Mini que tem WiFi, para podermos adicionar essa funcionalidade mais tarde. Além disso, o ESP8266 ESP-12F Mini tem aproximadamente o mesmo tamanho do Display de Matriz, o que nos permite encaixar tudo de forma organizada num Cubo.
Vamos continuar e montar o Game of Life num Cubo !
Configurar o ESP8266 ESP-12F Mini
A comunicação com o ESP8266 requer que o driver CH340 esteja instalado. Também precisaremos de instalar a placa em si. Vou mostrar como fazer isso nas secções abaixo.
Instalar o driver CH340g
O driver CH340 é o software para um chip conversor USB-serial que permite à placa comunicar com o computador via USB. Este driver é comum em placas que não têm uma interface USB integrada. Vamos precisar dele para programar o ESP8266 pela porta COM/USB.
- Descarrega o Windows CH340 Driver
- Descompacta o ficheiro
- Executa o instalador que descompactaste
- Liga o teu ESP8266 com um cabo USB e deverás ver uma porta COM ativa no Arduino IDE

Instalar a placa ESP8266
De seguida, precisamos de instalar a placa ESP8266. Vai a “File” e “Preferences” e insere a seguinte URL no campo “Additional Boards Manager” URLs:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
A imagem abaixo mostra onde encontrar o campo URLs na janela de Preferências.

Depois abre “Tools”, “Board” e “Boards Manager” e instala “esp8266 by ESP8266 Community”.

Ufa, quase a acabar. Agora já podemos comunicar com a placa, compilar e carregar sketches. Mas primeiro precisamos de alterar o código um pouco, pois o ESP8266 vai usar pinos diferentes do Arduino para comunicar com o display LED.
Constantes de pinos e ligações
Basta substituir as constantes dos pinos no código que são para o Arduino pelas seguintes.
// ESP8266 ESP-12F Mini const byte DIN = 12; // D6 const byte CS = 14; // D5 const byte CLK = 13; // D7
Claro que agora também precisamos de ligar o ESP8266 e o Display de Matriz de acordo. Liga o D6 ao DIN, o D5 ao CS e o D7 ao CLK. Finalmente, liga a alimentação (5V ao VCC e G ao GND). Na imagem abaixo podes ver como deve ficar quando estiveres a terminar.

Confirma as ligações e as novas constantes dos pinos e depois podemos carregar o sketch do Game of Life para o ESP8266. Os fios têm de estar ligados ao lado de entrada, marcado com “-> IN”, da placa do display.
Selecionar placa ESP8266 e carregar sketch
Abre o Arduino IDE e em “Tools”, “Board”, “ESP8266” seleciona a placa “LOLIN(WEMOS) D1 R2 and mini”.

Agora deves conseguir compilar e carregar o sketch normalmente. E o display deverá mostrar o Glider a deslizar pelo tabuleiro do jogo.

Parabéns! O último passo é construir o Cubo e colocar tudo dentro.
Construir uma Caixa
Usei uma impressora 3D e imprimi as duas partes mostradas abaixo. Podes descarregar os ficheiros STL para as duas partes here.

O Display de Matriz é apenas um encaixe (justo) na moldura frontal e o ESP8266 fica no meio entre as duas partes, fixado com cola quente para maior estabilidade. Se encaixares as duas partes, está feito. As imagens abaixo mostram a caixa completa com e sem o display e a placa inseridos.

E é isso. Um ótimo Game of Life num Cubo !
Como disse antes, isto é só o começo! Poderíamos adicionar muitos mais padrões além do icónico “Glider”. Um padrão semelhante e interessante é o Lightweight Spaceship. Experimenta:
// lightweight spaceship
byte board[8] = {
B00000000,
B00100100,
B01000000,
B01000100,
B01111000,
B00000000,
B00000000,
B00000000,
};
E aqui outra ideia: com uma placa com Wi-Fi dentro do cubo, este poderia ser controlado completamente via interface web, incluindo a definição de novos padrões para executar. Tudo depende de ti!
Resumo
Neste tutorial, implementámos com sucesso o Game of Life num Display de Matriz de LEDs 8×8 usando o driver MAX7219. Começámos por entender o que é o Game of Life e como funciona. Depois, listámos os componentes necessários para este projeto, que incluíam uma placa Arduino, um Display de Matriz de LEDs 8×8 e o driver MAX7219.
De seguida, passámos pelo processo de ligar o Display de Matriz de LEDs à placa Arduino usando o driver MAX7219. Explicámos as ligações e fornecemos um diagrama claro para facilitar o processo.
Depois, aprofundámos o código para o Game of Life. Explicámos a lógica por trás da implementação e fornecemos um guia passo a passo sobre como escrever o código. Também incluímos o código completo para tua referência.
Seguindo as instruções deste tutorial, agora deves ter um Game of Life a funcionar no teu Display de Matriz de LEDs. Podes experimentar diferentes configurações iniciais e observar como os padrões evoluem ao longo do tempo.
Se tiveres mais perguntas ou ideias interessantes, não hesites em deixar um comentário.
Links
Aqui tens alguns links adicionais que podes achar interessantes ou úteis.
- MAX7219 LED dot matrix display Arduino tutorial
- Inventing Game of Life (John Conway) – Numberphile
- Game of Life online simulator
- Collection of interesting patterns in Game of Life
- Link Collection for Game of Life
- The fantastic combinations of John Conway’s new solitaire game “life”

