Nova: R2040 Snake Game

by Vcc Labs in Circuits > LEDs

179 Views, 1 Favorites, 0 Comments

Nova: R2040 Snake Game

Demo

This tutorial will create a classic Snake game using the Nova Board and a 7x10 LED Matrix. This project lets you play the Snake game by controlling an LED "snake" on the matrix using simple button inputs. Perfect for beginners and enthusiasts alike, this tutorial will guide you through coding, wiring, and playing your custom snake game.

Supplies

1.png
Tacktile Switch.png
Breadboard.jpg
Arduino Logo.png
Resistor.png
  1. Nova Board (RP2040-based board with integrated 7x10 addressable LED matrix) Get yours!
  2. Push buttons (5 total: UP, DOWN, LEFT, RIGHT, RESET)
  3. External pull-down resistors (for button stability)
  4. Arduino IDE with RP2040 support and FastLED library
  5. A Breadboard

Set Up Your Nova Board

Breadboard.png
Buttons Simplified Schematic.png
  1. Attach the Nova Board to a Breadboard

Begin by placing your Nova board onto a breadboard for stability and easy wiring.

  1. Connect the Buttons

Connect each directional button to the respective pins on the Nova board:

  1. UP Button: connect to pin 2
  2. LEFT Button: connect to pin 3
  3. DOWN Button: connect to pin 4
  4. RIGHT Button: connect to pin 5
  5. RESET Button: connect to pin 6

Use external pull-down resistors with each button to ensure reliable signal readings.

Upload the Code

Use the code below to bring your Snake game to life. This code initializes the LED matrix, defines snake movement, and handles button inputs for game control.

/*
* Snake Game Example for Nova Board
*
* Button Connections:
* - UP Button: connected to pin 2
* - LEFT Button: connected to pin 3
* - DOWN Button: connected to pin 4
* - RIGHT Button: connected to pin 5
* - RESET Button: connected to pin 6
*
* Note: External pull-down resistors are used with the buttons to ensure stable readings.
*
* License:
* This project is open-source hardware and software, released under the MIT License.
* See the LICENSE file for details.
*/

#include <FastLED.h>

// LED matrix settings
#define NUM_LEDS 70 // Total number of LEDs in the matrix
#define DATA_PIN 22 // Data pin for controlling the LEDs
#define BRIGHTNESS 10 // LED brightness level (0-255)

CRGB leds[NUM_LEDS]; // Array to hold LED color values

// Button pins
#define UP_BUTTON 2 // Pin for UP button
#define LEFT_BUTTON 3 // Pin for LEFT button
#define DOWN_BUTTON 4 // Pin for DOWN button
#define RIGHT_BUTTON 5 // Pin for RIGHT button
#define RESET_BUTTON 6 // Pin for RESET button

// Matrix Layout - Maps 10x7 grid to LED indices
int matrix[10][7] = {
{69, 50, 49, 30, 29, 10, 9},
{68, 51, 48, 31, 28, 11, 8},
{67, 52, 47, 32, 27, 12, 7},
{66, 53, 46, 33, 26, 13, 6},
{65, 54, 45, 34, 25, 14, 5},
{64, 55, 44, 35, 24, 15, 4},
{63, 56, 43, 36, 23, 16, 3},
{62, 57, 42, 37, 22, 17, 2},
{61, 58, 41, 38, 21, 18, 1},
{60, 59, 40, 39, 20, 19, 0}
};

// Snake properties
struct Point { int x, y; }; // Structure to define a point (x, y) on the matrix
Point snake[100]; // Array to hold snake body segments
int snakeLength; // Length of the snake
int dx, dy; // Direction variables (dx, dy)
Point food; // Position of food on the matrix
bool gameOver; // Flag to check if the game is over

// Speed settings
unsigned long snakeSpeed = 400; // Speed of the snake, adjust this to change game speed
unsigned long lastMoveTime = 0; // Time when the snake last moved

// Setup function - Initializes the game and LEDs
void setup() {
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // Initialize FastLED
FastLED.setBrightness(BRIGHTNESS); // Set LED brightness

randomSeed(analogRead(0)); // Seed random generator for food placement
initializeGame(); // Start the game
}

// Main game loop
void loop() {
if (gameOver) { // If game is over, wait for RESET button
if (digitalRead(RESET_BUTTON) == HIGH) {
initializeGame(); // Restart game when RESET button is pressed
}
return;
}

// Update snake direction based on button input
if (digitalRead(UP_BUTTON) == HIGH && dy == 0) { dx = 0; dy = -1; }
if (digitalRead(LEFT_BUTTON) == HIGH && dx == 0) { dx = -1; dy = 0; }
if (digitalRead(DOWN_BUTTON) == HIGH && dy == 0) { dx = 0; dy = 1; }
if (digitalRead(RIGHT_BUTTON) == HIGH && dx == 0) { dx = 1; dy = 0; }

// Move snake based on game speed
unsigned long currentMillis = millis();
if (currentMillis - lastMoveTime >= snakeSpeed) { // Check if enough time has passed
moveSnake(); // Move the snake to the next position
lastMoveTime = currentMillis; // Update move time

// Update LED display
FastLED.clear(); // Clear the previous LED states

// Set snake head color to green
leds[matrix[snake[0].y][snake[0].x]] = CRGB::Green;

// Set snake body color to green
for (int i = 1; i < snakeLength; i++) {
leds[matrix[snake[i].y][snake[i].x]] = CRGB::Green;
}

// Set food color to red
leds[matrix[food.y][food.x]] = CRGB::Red;
FastLED.show(); // Display updated LEDs
}
}

// Initialize or reset game properties
void initializeGame() {
snakeLength = 3; // Start with a small snake
snake[0] = {3, 5}; // Initial snake head position
snake[1] = {3, 6}; // Initial snake body
snake[2] = {4, 6}; // Initial snake tail
dx = 0; // Initial direction: not moving horizontally
dy = -1; // Initial direction: moving up
gameOver = false; // Game starts with game over set to false
spawnFood(); // Spawn the first food
}

// Move snake and handle collisions
void moveSnake() {
// Calculate new head position based on direction
Point newHead = { snake[0].x + dx, snake[0].y + dy };

// Check for collision with walls
if (newHead.x < 0 || newHead.x >= 7 || newHead.y < 0 || newHead.y >= 10) {
gameOver = true; // Set game over if snake hits wall
return;
}

// Check for collision with itself
for (int i = 0; i < snakeLength; i++) {
if (snake[i].x == newHead.x && snake[i].y == newHead.y) {
gameOver = true; // Set game over if snake hits itself
return;
}
}

// Move the snake by shifting body segments
for (int i = snakeLength; i > 0; i--) {
snake[i] = snake[i - 1];
}
snake[0] = newHead; // Update the new head position

// Check if snake ate the food
if (newHead.x == food.x && newHead.y == food.y) {
snakeLength++; // Increase snake length
spawnFood(); // Spawn new food
}
}

// Spawn food at a random position
void spawnFood() {
while (true) {
food.x = random(7); // Random x-coordinate within the matrix
food.y = random(10); // Random y-coordinate within the matrix
bool onSnake = false;

// Check if food position is on the snake
for (int i = 0; i < snakeLength; i++) {
if (snake[i].x == food.x && snake[i].y == food.y) {
onSnake = true; // If it is, set flag to true and retry
break;
}
}

// Exit loop if food is not on the snake
if (!onSnake) break;
}
}

Downloads

Play the Game!

With everything set up, reset the game and try it out! Control the snake by pressing the buttons to guide it towards the red "food" dot. Avoid crashing into the walls or your own tail, or you'll have to hit reset and start over.

Customize the Game

Here are a few ways to experiment:

  1. Adjust the Speed: Modify the snakeSpeed variable for a faster or slower game.
  2. Change the Snake Color: Adjust the color in the code to make the snake a different shade.
  3. Add More Levels: Try making the game harder as you eat more food.

You've successfully created your own Snake game using the Nova board and LED matrix! This project is a fun way to dive into programming LEDs and buttons and is easily expandable for other games or applications. Have fun coding, and happy gaming!