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
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
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;j 7 || 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:
- Verify power connections: VCC to 5V and GND to GND on the MAX7219 module.
- Check DIN→12, CS→11, CLK→10 are correctly wired.
- Ensure you have the LedControl library installed (Tools → Manage Libraries → search "LedControl").
- Try adjusting brightness with lc.setIntensity(0,8) - values range from 0-15.
Install the LedControl library by Eberhard Fahle from the Arduino Library Manager. Without it, the code won't compile.
- Check joystick wiring: VRx→A3, VRy→A4, +5V→5V, GND→GND.
- Test joystick values using Serial.println(analogRead(varXPin)) in the loop to see if values change (0-1023).
- Adjust threshold values if needed - some joysticks use different ranges than 100/920.
- Ensure the joystick module has power - check continuity with a multimeter.
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.
- Adjust the updateRate variable (line 37) - lower values = slower game, higher = faster.
- Check for loose wiring that could cause intermittent signals.
- Verify the joystick center position isn't triggering movement - adjust threshold values if needed.
- Ensure stable power supply - weak power can cause erratic behavior.
The updateRate = 3 means 3 updates per second. Try updateRate = 2 for slower gameplay or updateRate = 5 for more challenge.
- Check LED polarity - longer leg (anode) to resistor to pin, shorter leg (cathode) to GND.
- Verify 220Ω resistors are in series with each LED to prevent burnout.
- Confirm green LED on pin 9 and red LED on pin 13.
- Test LEDs separately with a simple blink sketch to ensure they work.
Green LED should blink during gameplay. Red LED stays off until game over, then lights solid during the loss screen.
- Install LedControl library: Sketch → Include Library → Manage Libraries → search "LedControl" → Install.
- Ensure you're using Arduino IDE 1.6 or newer for proper struct initialization support.
- Select the correct board type in Tools → Board (Arduino Uno or Mega depending on your hardware).
- If errors persist, verify all curly braces and semicolons are intact - no typos in the code.
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.