Arduino Joystick Game Project

Building a Snake Game with Python

Project Overview

Today we'll build a simple Snake game controlled by your Arduino joystick. The game will:

• Read joystick input from Arduino via serial communication

• Display a moving snake on screen using Pygame

• Grow the snake when it eats food

• End when the snake hits itself or the wall

What You'll Need

Hardware:

• Arduino with joystick module already set up

• USB cable to connect Arduino to computer

Software:

• Python 3 installed on your computer

• Pygame library (we'll install this)

• PySerial library (we'll install this)

Installing Required Libraries

Open your terminal or command prompt and run these commands:

For Pygame (handles graphics and game loop):

pip install pygame

For PySerial (communicates with Arduino):

pip install pyserial

Arduino Code Review

Your Arduino should already have code similar to this. It reads the joystick and sends direction commands over serial.

void setup() {
  Serial.begin(9600);
  pinMode(A0, INPUT); // X-axis
  pinMode(A1, INPUT); // Y-axis
}

void loop() {
  int xVal = analogRead(A0);
  int yVal = analogRead(A1);
  
  if (xVal < 300) Serial.println("LEFT");
  else if (xVal > 700) Serial.println("RIGHT");
  else if (yVal < 300) Serial.println("UP");
  else if (yVal > 700) Serial.println("DOWN");
  
  delay(100);
}

Creating the Project File

Create a new Python file called snake_game.py in your project folder.

We'll build this game step by step, adding code section by section.

Make sure your Arduino is connected via USB before running the game!

Step 1: Import Libraries

First, we need to import all the libraries we'll use in our game.

These provide the tools for graphics, serial communication, and random numbers.

import pygame
import serial
import time
import random

# Initialize Pygame
pygame.init()

Step 2: Define Game Constants

Constants are values that won't change during the game. They make our code easier to read and modify.

# Screen dimensions
WIDTH = 600
HEIGHT = 600
CELL_SIZE = 20

# Colors (RGB format)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)

# Game speed
FPS = 10

Step 3: Connect to Arduino

We need to establish a serial connection to read joystick data from the Arduino.

Note: Change 'COM3' to your Arduino's port (check Arduino IDE).

# Set up serial connection
# Windows: 'COM3', 'COM4', etc.
# Mac/Linux: '/dev/ttyUSB0', '/dev/ttyACM0'
try:
    arduino = serial.Serial('COM3', 9600, timeout=1)
    time.sleep(2)  # Wait for connection
    print("Arduino connected!")
except:
    print("Could not connect to Arduino")
    arduino = None

Step 4: Create Game Window

Set up the Pygame window where our game will be displayed.

# Create the game window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Arduino Snake Game")

# Clock to control game speed
clock = pygame.time.Clock()

Step 5: Initialize Snake

The snake is represented as a list of coordinates. Each coordinate is one segment of the snake's body.

# Snake starting position (center of screen)
snake = [[WIDTH//2, HEIGHT//2]]

# Snake direction (starts moving right)
direction = "RIGHT"
next_direction = "RIGHT"

# Snake growth flag
grow = False

Step 6: Place the Food

Food appears at random positions on the grid. When the snake eats it, it grows and new food appears.

# Function to create food at random position
def create_food():
    x = random.randint(0, (WIDTH-CELL_SIZE)//CELL_SIZE) * CELL_SIZE
    y = random.randint(0, (HEIGHT-CELL_SIZE)//CELL_SIZE) * CELL_SIZE
    return [x, y]

# Place initial food
food = create_food()

Step 7: Read Joystick Input

This function reads data from the Arduino and returns the direction command.

def read_joystick():
    if arduino and arduino.in_waiting > 0:
        try:
            line = arduino.readline().decode('utf-8').strip()
            if line in ["UP", "DOWN", "LEFT", "RIGHT"]:
                return line
        except:
            pass
    return None

Step 8: Direction Control Logic

This prevents the snake from immediately reversing into itself (e.g., going LEFT when moving RIGHT).

def update_direction(current, new):
    # Prevent 180-degree turns
    if new == "UP" and current != "DOWN":
        return new
    if new == "DOWN" and current != "UP":
        return new
    if new == "LEFT" and current != "RIGHT":
        return new
    if new == "RIGHT" and current != "LEFT":
        return new
    return current

Step 9: Move the Snake

Calculate the new head position based on current direction and add it to the snake.

def move_snake(snake, direction, grow):
    head = snake[0].copy()
    
    if direction == "UP":
        head[1] -= CELL_SIZE
    elif direction == "DOWN":
        head[1] += CELL_SIZE
    elif direction == "LEFT":
        head[0] -= CELL_SIZE
    elif direction == "RIGHT":
        head[0] += CELL_SIZE
    
    snake.insert(0, head)
    
    if not grow:
        snake.pop()  # Remove tail
    
    return snake

Step 10: Check for Collisions

The game ends if the snake hits the wall or collides with its own body.

def check_collision(snake):
    head = snake[0]
    
    # Check wall collision
    if (head[0] < 0 or head[0] >= WIDTH or 
        head[1] < 0 or head[1] >= HEIGHT):
        return True
    
    # Check self collision
    if head in snake[1:]:
        return True
    
    return False

Step 11: Eating Food

Check if the snake's head is at the same position as the food.

def check_food(snake, food):
    if snake[0] == food:
        return True
    return False

Step 12: Draw Everything

This function renders the snake, food, and score on the screen.

def draw_game(screen, snake, food, score):
    screen.fill(BLACK)
    
    # Draw snake
    for segment in snake:
        pygame.draw.rect(screen, GREEN, 
                        (segment[0], segment[1], 
                         CELL_SIZE, CELL_SIZE))
    
    # Draw food
    pygame.draw.rect(screen, RED, 
                    (food[0], food[1], 
                     CELL_SIZE, CELL_SIZE))
    
    # Draw score
    font = pygame.font.Font(None, 36)
    text = font.render(f'Score: {score}', True, WHITE)
    screen.blit(text, (10, 10))

Step 13: Game Over Display

Show the final score when the game ends.

def show_game_over(screen, score):
    font = pygame.font.Font(None, 72)
    text = font.render('GAME OVER', True, RED)
    text_rect = text.get_rect(center=(WIDTH//2, HEIGHT//2))
    screen.blit(text, text_rect)
    
    font_small = pygame.font.Font(None, 36)
    score_text = font_small.render(f'Final Score: {score}', 
                                   True, WHITE)
    score_rect = score_text.get_rect(center=(WIDTH//2, HEIGHT//2 + 50))
    screen.blit(score_text, score_rect)
    
    pygame.display.flip()

Step 14: Main Game Loop (Part 1)

The main loop runs continuously, processing input and updating the game state.

# Game variables
running = True
game_over = False
score = 0

# Main game loop
while running:
    # Handle quit event
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    
    if not game_over:

Step 15: Main Game Loop (Part 2)

Read joystick input and update the snake's direction.

        # Read joystick input
        joy_input = read_joystick()
        if joy_input:
            next_direction = update_direction(direction, 
                                             joy_input)
        
        # Update direction
        direction = next_direction

Step 16: Main Game Loop (Part 3)

Move the snake and check if it ate food.

        # Move snake
        snake = move_snake(snake, direction, grow)
        grow = False
        
        # Check if snake ate food
        if check_food(snake, food):
            score += 10
            food = create_food()
            grow = True

Step 17: Main Game Loop (Part 4)

Check for collisions and end the game if necessary.

        # Check collisions
        if check_collision(snake):
            game_over = True
            show_game_over(screen, score)
            time.sleep(3)
        
        # Draw everything
        draw_game(screen, snake, food, score)
        pygame.display.flip()

Step 18: Main Game Loop (Part 5)

Control the game speed with the clock.

    # Control game speed
    clock.tick(FPS)

Step 19: Cleanup

When the game ends, close the serial connection and quit Pygame properly.

# Cleanup
if arduino:
    arduino.close()
pygame.quit()

Testing Your Game

Steps to test:

1. Make sure your Arduino is connected via USB

2. Upload the joystick code to your Arduino

3. Note the COM port your Arduino is using (check Arduino IDE)

4. Update the COM port in your Python code

5. Run the Python script: python snake_game.py

6. Use your joystick to control the snake!

Common Issues and Solutions

Problem: "Could not connect to Arduino"

• Check that Arduino is plugged in and the correct COM port is specified

• Make sure no other program (like Arduino IDE Serial Monitor) is using the port

Problem: Snake moves in wrong direction

• Check joystick wiring and calibration values in Arduino code

Problem: Game runs too fast or slow

• Adjust the FPS constant (higher = faster, lower = slower)

Ways to Enhance Your Game

Once you have the basic game working, try these improvements:

• Add sound effects when eating food or game over

• Create different difficulty levels (faster speeds)

• Add obstacles or walls in the playing field

• Display a high score that persists between games

• Add a pause button using a joystick button

• Create power-ups that give special abilities

Skills You've Practiced

Through this project, you've learned:

• Serial communication between Arduino and Python

• Game loop architecture and frame-based updates

• Collision detection algorithms

• List manipulation for tracking game objects

• Event handling and user input processing

• Graphics rendering with Pygame

• Integrating hardware with software projects

Exercise: Add Reset Button Functionality

Your Challenge:

Add functionality so that pressing the joystick button resets the game instead of having to close and restart the program.

Requirements:

• Modify the Arduino code to send a "RESET" command when button is pressed

• Update the Python code to detect the RESET command

• When RESET is received, restart the game (reset snake position, score, and food)

Hint: Think about which variables need to be reset to their initial values!