Matrix Snake Game MEDIUM

A classic snake game displayed on an 8x8 LED matrix, controlled with a joystick module. Features collision detection, score tracking, and visual indicators for game status.

Materials Used

Arduino Uno/Mega
1× MAX7219 8x8 LED Matrix
1× Analog Joystick Module
1× Green LED
1× Red LED
2× 220Ω Resistors (for LEDs)
Breadboard
Jumper Wires

Wiring Instructions

MAX7219 LED Matrix Connections:
• DIN → Pin 12
• CS → Pin 11
• CLK → Pin 10
• VCC → 5V
• GND → GND

Joystick Module:
• VRx → A3
• VRy → A4
• +5V → 5V
• GND → GND

LED Indicators:
• Green LED → Pin 9 (through 220Ω resistor) → GND
• Red LED → Pin 13 (through 220Ω resistor) → GND

Project Photos & Demo

Matrix Snake Game Circuit Matrix Snake Game Fritzing Diagram

How It Works

The game uses a MAX7219-controlled LED matrix to display the snake and apple. The joystick reads analog values to determine movement direction (up, down, left, right). The green LED blinks during gameplay, while the red LED activates on game over with a flashing "L" pattern on the matrix. The snake grows when eating apples, and the game resets on collision with walls or itself.

Arduino Code

#include 

// ----- Struct Definitions -----
typedef struct Snake {
  int head[2];
  int body[40][2];
  int len;
  int dir[2];
} Snake;

typedef struct Apple {
  int rPos;
  int cPos;
} Apple;

// ----- MAX7219 LED Matrix Pins -----
const int DIN = 12;
const int CS  = 11;
const int CLK = 10;
LedControl lc = LedControl(DIN, CLK, CS, 1);

// ----- Joystick Pins -----
const int varXPin = A3;
const int varYPin = A4;

// ----- LED Pins -----
const int greenLED = 9;
const int redLED   = 13;

// ----- Game Variables -----
byte pic[8] = {0,0,0,0,0,0,0,0};
Snake snake = {{1,5}, {{0,5},{1,5}}, 2, {1,0}};
Apple apple = {(int)random(0,8),(int)random(0,8)};

float oldTime = 0;
float timer = 0;
float updateRate = 3;
int i, j;

// ----- Setup -----
void setup() {
  lc.shutdown(0,false);
  lc.setIntensity(0,8);
  lc.clearDisplay(0);

  pinMode(varXPin, INPUT);
  pinMode(varYPin, INPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);

  randomSeed(analogRead(0));

  digitalWrite(redLED, LOW);
  digitalWrite(greenLED, HIGH);
}

// ----- Main Loop -----
void loop() {
  // Blink green while playing
  static unsigned long blinkTimer = 0;
  if (millis() - blinkTimer > 400) {
    blinkTimer = millis();
    digitalWrite(greenLED, !digitalRead(greenLED));
  }

  float deltaTime = millis() - oldTime;
  oldTime = millis();
  timer += deltaTime;

  int xVal = analogRead(varXPin);
  int yVal = analogRead(varYPin);

  if (xVal < 100 && snake.dir[1] == 0) {
    snake.dir[0] = 0; snake.dir[1] = -1;
  } else if (xVal > 920 && snake.dir[1] == 0){
    snake.dir[0] = 0; snake.dir[1] = 1;
  } else if (yVal < 100 && snake.dir[0] == 0){
    snake.dir[0] = -1; snake.dir[1] = 0;
  } else if (yVal > 920 && snake.dir[0] == 0){
    snake.dir[0] = 1; snake.dir[1] = 0;
  }

  if (timer > 1000/updateRate) {
    timer = 0;
    Update();
  }

  Render();
}

// ----- Helpers -----
void resetPic() { for (int j=0;j<8;j++) pic[j]=0; }

void removeFirst() {
  for (j=1;j7 || newHead[1]<0 || newHead[1]>7) {
    showLossScreen();
    restartGame();
    return;
  }

  // Self Collision
  for (j=0;j> snake.body[j][1];
  pic[apple.rPos] |= 128 >> apple.cPos;
}

// ----- Render -----
void Render() {
  for (i=0;i<8;i++) lc.setRow(0,i,pic[i]);
}

// ----- Loss Screen -----
void showLossScreen() {
  lc.clearDisplay(0);
  digitalWrite(greenLED, LOW);
  digitalWrite(redLED, HIGH);

  byte Lshape[8] = {
    B11111111,
    B00000001,
    B00000001,
    B00000001,
    B00000001,
    B00000001,
    B00000001,
    B00000001
  };

  // Blink twice
  for (int k=0;k<2;k++) {
    for (int row=0;row<8;row++) lc.setRow(0,row,Lshape[row]);
    delay(300);
    lc.clearDisplay(0);
    delay(300);
  }

  for (int row=0;row<8;row++) lc.setRow(0,row,Lshape[row]);
  delay(1000);

  digitalWrite(redLED, LOW);
}

// ----- Restart -----
void restartGame() {
  lc.clearDisplay(0);
  snake={{1,5},{{0,5},{1,5}},2,{1,0}};
  apple={(int)random(0,8),(int)random(0,8)};
  digitalWrite(greenLED, HIGH);
}

🔧 Troubleshooting

Having issues with your circuit? Check these common problems and solutions:

🔲 LED Matrix Not Lighting Up
  1. Verify power connections: VCC to 5V and GND to GND on the MAX7219 module.
  2. Check DIN→12, CS→11, CLK→10 are correctly wired.
  3. Ensure you have the LedControl library installed (Tools → Manage Libraries → search "LedControl").
  4. Try adjusting brightness with lc.setIntensity(0,8) - values range from 0-15.
💡 Library Issue?

Install the LedControl library by Eberhard Fahle from the Arduino Library Manager. Without it, the code won't compile.

🕹️ Joystick Not Controlling Snake
  1. Check joystick wiring: VRx→A3, VRy→A4, +5V→5V, GND→GND.
  2. Test joystick values using Serial.println(analogRead(varXPin)) in the loop to see if values change (0-1023).
  3. Adjust threshold values if needed - some joysticks use different ranges than 100/920.
  4. Ensure the joystick module has power - check continuity with a multimeter.
🎮 Calibration Tip:

Different joystick modules have different resting and extreme values. Monitor the Serial output and adjust the if conditions (currently 100 and 920) to match your specific joystick.

Snake Moves Too Fast or Erratically
  1. Adjust the updateRate variable (line 37) - lower values = slower game, higher = faster.
  2. Check for loose wiring that could cause intermittent signals.
  3. Verify the joystick center position isn't triggering movement - adjust threshold values if needed.
  4. Ensure stable power supply - weak power can cause erratic behavior.
⚙️ Speed Control:

The updateRate = 3 means 3 updates per second. Try updateRate = 2 for slower gameplay or updateRate = 5 for more challenge.

💡 Status LEDs Not Working
  1. Check LED polarity - longer leg (anode) to resistor to pin, shorter leg (cathode) to GND.
  2. Verify 220Ω resistors are in series with each LED to prevent burnout.
  3. Confirm green LED on pin 9 and red LED on pin 13.
  4. Test LEDs separately with a simple blink sketch to ensure they work.
🔴🟢 Indicator Behavior:

Green LED should blink during gameplay. Red LED stays off until game over, then lights solid during the loss screen.

⚠️ Code Won't Compile
  1. Install LedControl library: Sketch → Include Library → Manage Libraries → search "LedControl" → Install.
  2. Ensure you're using Arduino IDE 1.6 or newer for proper struct initialization support.
  3. Select the correct board type in Tools → Board (Arduino Uno or Mega depending on your hardware).
  4. If errors persist, verify all curly braces and semicolons are intact - no typos in the code.
📚 Library Required:

This project absolutely requires the LedControl library by Eberhard Fahle. It handles all MAX7219 communication.

What You Learned

Electrical Engineering Applications

This project combines multiple advanced concepts: SPI communication with the MAX7219 driver chip, analog-to-digital conversion (ADC) for joystick input, matrix LED multiplexing, and real-time game state management. You learned about shift registers, bitmap display buffers, collision detection algorithms, and timer-based update loops. These principles are fundamental in embedded systems, display controllers, and real-time applications.

Broader Technology Context

LED matrix displays are used in scoreboards, public information systems, advertising displays, and wearable tech. The game logic and state management techniques apply to any interactive system - from mobile games to industrial HMIs (Human-Machine Interfaces). Understanding frame-based rendering and input polling prepares you for game development, embedded UI design, and real-time control systems in robotics and automation.