Creating Snake Game in Arduino Uno R4 WiFi
This post will teach you how to build a simple snake game using Arduino Uno R4’s built-in LED matrix and joy stick.
Controlling Snake Directions
As you can see in the video I’m using a joystick to control the snake directions. If you are not yet familiar on how the joystick works, you can read on the explanation below, otherwise you can skip and go directly to the code.
How Joystick Works
The joystick has 5 pins. Two pins +5V
and GND
are the supply pins while VRx
and VRy
corresponds to output voltages on X and Y axis.
It is like a potentiometer, if you move the joystick to a certain direction, the output voltage changes. To make it easier to understand I created a diagram to show the output voltages corresponding to the movement of the joystick. As you can see in the diagram, if you move the joystick fully-upward, VRy
will be 0V
, meanwhile if you move it fully-downward it will be VRy
will be 5V
. Same goes with fully-left VRx
will be 0V
and fully-right will result to VRx
= 5V
. If you do not move the joystick VRx
and VRy
should be ideally 2.5V
. I mentioned, ideally because, sometimes there are imperfections on how the joystick is manufactured so it may not be perfectly at the center during idle (it can deviate +/- 0.x Volts).
The 5th pin is SW
, which is the switch or pushbutton,if not pressed SW
will have a non-zero volatge but when pressed,SW
pin will be 0V
.
Analog to Digital Conversion (ADC)
The output of the joystick is a voltage which is an analog signal, the microcontroller in the Arduino is Digital so the Analog signals VRx
and VRy
should be converted into digital values. The good news is that Arduino as a built-in Analog to Digital Converters which are easily accessible. At the hardware level, Arduino has Analog pins, A0
to A7
. We just need to connect the signal that we want to convert and call the Arduino function analogRead()
.
For the snake game, we will not be using the SW
pin so the connection of joystick to Arduino is as shown in the table below.
Joystick Pin | Arduino Pin |
---|---|
GND | GND |
+5V | 5V |
VRx | A0 |
VRy | A1 |
SW | no connection |
We will be using the default ADC resolution in Arduino Uno R4 Wifi, which is 10 bits, that means the voltages will be converted to a 10-bit value.
This means that minimum value 0V
is b0000000000
and the maximum value 5V
is b1111111111
(1023
in decimal notation). I created a diagram below to make it easier to understand. When the joystick is idle and the voltages are at 2.5V
, the digital value should be around 511
or 512
, bit that can further deviate +/- 5
due to manufaturing imperfections of the joystick. It does not matter though, because we are just making a sanke game and do not need high accuracy.
get_dir()
I created a function named get_dir()
to take care and encapsulate all the code or logic related to calculating the snake directions.
You can expand the code block below to see the code. The function is very simple, I read the value at pins A0
for x-axis and A1
for y-axis.
If the value read for x-axis is 0
to 300
, the function returns LEFT
, if the x-axis is 723
to 1023
, the function returns RIGHT
.
Since the snake cannot go directly to the opposite direction, for example, the sanke is moving to the LEFT
, then we move the joystick to the RIGHT
, it should not take effect because that is not allowed. To do that in the code, we just add && dir != <OPPOSITE_DIRECTION>
. If the conditions for all directions (LEFT
, RIGHT
, UP
, DOWN
) are not satisfied, the function will just return the current direction (return dir
)
|
|
Snake and Food Display
The Arduino Uno R4 Wifi has an on-board 8x12 LED matrix and software library to control the matrix so it is easy to use. To use the library, we first need to include the following line of codes.
|
|
After calling the matrix.begin()
, we can declare an 8x12 array (byte frame[8][12]
) to hold the values of each element of the matrix. Value 0
means LED is OFF
and 1
means ON
.
We can visualize the frame[8][12]
array like the diagram below. To select an element we need to specify the row and column.
Let us say we want to select the top-left, we set the row as 0
and column as 0
, it will be frame[0][0]
. The the top-right will be frame[0][11]
, bottom-left is frame[7][0]
, and bottom-right is frame[7,11]
. The row values starts from 0
to 7
(top to bottom) and column starts from 0
to 11
(left to right).
display_frame()
I have created a function display_frame()
which takes care of the controlling the LED matrix. The function does the following:
- Clears the frame to ensure that there is no unexpected LED that will be turned on
- Set the
frame
elements to1
for snake - Set the
frame
elements to1
for food - Call the
matrix.renderBitmap()
to reflect the values offrame
array to the actual LED matrix
Expand the code block to see the complete display_frame()
code.
|
|
Snake Position and Animation
I used byte col_idx[96]
and byte row_idx[96]
to store the snake position. The size of the array is 96
because it is the maximum length of the snake in 8x12 matrix. The first index col_idx[0]
and row_idx[0]
is the head of the snake and the remaining is the body. In order to animate the movement of the snake, we need to do the following:
- Display a frame
- Add a delay (which will be the speed of animation or snake speed)
- Calculate the next frame then loop back to the first step.
Calculating the Coordinates for the Snake Body
The coordinates of the snake body for the next frame is just the shifted by 1 coordinates of the previous frame. It is expressed in the code as follows:
|
|
The coordinates for the snake head is dependent on the direction dir
. If the dir
is RIGHT
or LEFT
, the row_idx[0]
should be constant as the snake movement is horizontal then the col_idx[0]
is incremented by 1
if RIGHT
and decremented by 1
if LEFT
. To handle wrap around if dir
is RIGHT
and the current col_idx[0] == 11
, we should set col_idx[0]
back to 0
. If the dir
is LEFT
and the col_idx[0] == 0
, col_idx[0]
should be set to 11
.
The same logic is applied to verical movement. You can expand the code block to see the complete code for get_snake_position()
function.
|
|
Snake Food Generation
Generating the snake food is relatively easier compared to the snake as we just need to generate random coordinates in the matrix. I used byte food_col
and byte food_row
to store the value of the food coordinates. I also used food_hit
variable to ensure that the food coordinated will only be randomized if the food is hit hit the snake head.
|
|
Detecting Food Hit
To determine if the food is hit, we need to check if the coordinated of the head row_idx[0]
and col_idx[0]
is the same as food_row
and food_col
respectively. If that condition is satified, we will need to increment the snake_len
by 1
and set the food_hit
variable to 1
so a new food coordinates will get randomized.
|
|
Detecting Game Over
To detect game over, we need to determine if the snake head coordinates row_idx[0]
and col_idx[0]
values is the same with any coordinates of its body.
The code to detect if snake noy is hit is shown below.
|
|
If the snake body is hit, we will call the game_over()
function which pauses snake movement, blinks 5 times and resets the snake_len
to 2.
Expand the code block to view the complete game_over()
code.
|
|
Complete Code
The complete code is available below or can be downloaded here Snake Game
|
|
You can download the complete code in this link
Things to Try
The snake game that I created is basic, you can add more features like the following:
- Displaying score during game over
- Storing and displaying high score during game over
- Make the randomization of food coordinates more sophisticated such that it will present snake coordinates will be excluded
- Add a super food feature (a bigger food more than 1 LED) that increments snake length more than or makes the snake speed faster
- Add levels, and increase snake speed as it level up.
There is a lot of fun things you can add to this code, try it out and enjoy!
If you enjoy my content, you can buy me a coffee. Thank you!
Sponsor