ESP32 Snake Game

1. Introduction

The aim of this project is to implement the famous 'Snake' game in an IoT environment. The game is powered by an ESP-WROOM-32S microcontroller, which is connected to an 8×8 RGB matrix used for display and a joystick with which the user can interact to control the movement. The project leverages multiple key concepts and technologies used in the IoT and embedded design worlds, such as system interrupts, driving signals through pins, reading analog values and converting them to digital values, logging data to the flash memory (through SPIFFS), and using Bluetooth to allow the user to interact with the board and perform various actions through sending commands.

2. Design

a. Hardware

  • ESP32-WROOM-S microcontroller

  • RGB 8×8 LED matrix (WS2812B)
    • Pins used: Pin 27 (for communication), 5V and GND

  • Biaxial joystick module with push-button
    • Pins used: Pins 36 (ADC1_0) and 39 (ADC1_3) for X-Y movement, pin 25 for button interrupt handling, 3V3 and GND

b. Software

The software architecture is comprised of 4 .ino source files:

  • iot_project.ino - main source file containing the setup (initialization of all components) and loop (which handles game logic and BLE communication).
  • game.ino - contains the game logic along with its rendering on the RGB matrix.
  • logging.ino - offers the capability of writing data to the filesystem. It offers an API specific for the game, which allows performing actions related to the game (writing scores for a certain user, creating new user, listing scores for any user, listing users sorted by their highest score, etc.).
  • bluetooth.ino - handles the Bluetooth communication with the user and parses and executes the commands received from the user.

The game logic

When the game starts, the positions of the player and the prey are generated randomly on the RGB matrix. The snake can make a step on the map with a frequency, which is determined by a timer value. When this timer value is reached, an interrupt is triggered, the next position of the snake is computed and then rendered immediately. This is achieved by calling the 'update()' function in the main loop whenever the timer interrupt gets triggered. This function then calls 'update_running()', in the event the game is in the RUNNING state, which then calls the compute_next_position() function which is responsible for updating the snake's position.

The compute_next_position function decides the position of the snake at the next step, based on the current snake's position and a variable called 'direction', which can tell if the snake should go UP, DOWN, LEFT, or RIGHT. Between two timer interrupts, consecutive reads are performed on the Joystick's X and Y axes in order to determine if there is a change in direction. If the read value on one of the axes exceeds a certain threshold, then it can be inferred that the direction of the snake will change at the next rendering, thus the value of 'direction' variable is updated accordingly. Furthermore, after the first read outside the threshold, no more analog reads will be considered until after the next timer interrupt occurs.

The representation of the snake in memory is an array of integers, each integer representing the number of the LED corresponding to a piece of the snake:

uint8_t snake_body[NUMPIXELS];

Rendering consists of turning on the LEDs corresponding to the snake's position (snake_body array) and the prey's position. In order to interact with the RGB matrix, the project uses ”<Adafruit_NeoPixel.h>” library, which provides a simple API to control the LEDs.

Furthermore, the game offers 3 difficulty levels. The difference between all these levels is determined by the snake's movement speed, which is represented by the frequency at which the timer interrupt occurs in order to trigger the update of the snake's position on the matrix.

const unsigned int game_speed[MAX_LEVEL] = {1000000, 500000, 250000};

This array represents the values that a timer can use for a given difficulty level. The timer works by counting from 0 to this value and when the value is reached, the interrupt gets triggered. Thus, the lower the value, the higher the frequency at which the timer gets triggered and the more difficult the game is.

Furthermore, the button on the joystick can be used as an interrupt to increment the difficulty level and to restart the game after the game is over.

Logging data to flash

SPIFFS is used to log data to the flash memory for persistent usage. The data collected is represented by the scores obtained and the usernames of players who achieved those scores. The structure of the filesystem is simple: in the '/' directory, there can be found files with the format 'username'.txt, which contain the scores obtained by the respective users.

Thus, this module offers some API functions, which help with managing and updating the logged data on the flash.

void write_score(String profile, int score); // appends a new score to the username given as input (basically, it writes a number to the file representing the user)

void list_best_score(String profile); // lists the best score obtained by a user

void list_all_scores(String profile);

void players_rank(); // lists all users, ranked by their highest score

void add_user(String profile); // adds a new user to the file system (by creating a file with the username)

void switch_user(String profile); // switches the user to an existing user in the file system

void delete_user(String profile); // deletes user from file system

void list_all_users(); // lists all users from the file system (by iterating on the / directory and listing the files with .txt extension)


The project uses Bluetooth communication as a way for the user to pass commands to the microcontroller and change some game settings or interact with the file system through the use of the API described above.

The user connects with the phone to the advertising ESP device and through a terminal BLE application, they can pass commands and receive responses from the device.

For this, I have used the BluetoothSerial library. In the main loop function, I call a function 'ble_check_receive', which checks if an input from the user has been received. The input is parsed and if it is a valid command, then the proper action is taken. A list of all available commands is given by typing the command 'help' in the terminal.

An overview of the available commands is provided below.

set speed slow - Set speed to slow
set speed medium - Set speed to medium
set speed fast - Set speed to fast
help - Display this help menu
best score <profile> - List the best score of a profile
all scores <profile> - List all scores of a profile in descending order
player rank - List player rankings by best score
delete user <profile> - Delete a user profile
switch user <profile> - Switch to a different user profile
add user <profile> - Add a new user profile
get current user - Get the current user profile
list all users - List all user profiles
pause - Pause the game
continue - Continue the game

3. Conclusions and final result

The implementation of the project can be seen in the following pictures and in the attached video on the top of the page.

The project achieves the proposed objectives: implementing the classic Snake game on a LED matrix and using Bluetooth communication and SPIFFS flash logging to enhance its functionality. The features have been tested and work successfully.

Improvements can be made, though, especially in the hardware-related zone. For example, there are times where the wires do not make full contact with the RGB matrix on the breadboard, which translates to power cutting off and turning the LEDs off, or having random LEDs turning on. To fix this, the matrix has to be adjusted a little bit, until you see it is working again and it must be ensured the matrix stays in a fixed position in order to reduce any possibility of this occuring.

Another improvement is regarding the code size, which consumes close to 90% of the available code memory. This is because the project includes multiple libraries (Bluetooth, SPIFFS, AdaFruit_NeoPixel, etc) that take high amounts of space.

4. References

iothings/proiecte/2023sric/esp32_snake_game.txt · Last modified: 2024/05/29 22:32 by robert.coman1205
CC Attribution-Share Alike 3.0 Unported Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0