Game Console (Game Boy Replica)

Introduction

What it does:

This project is a portable video game console featuring a minimalist hardware architecture. It allows users to play 8-bit style games loaded directly into the flash memory.

Purpose:

The primary goal is to build a fully functional gaming system from scratch. I wanted to do something that I would enjoy using and that I could really use from time to time. I'm also hoping other people might be interested in this console and try it out.

Initial Idea:

The project is heavily inspired by classic 8-bit retro systems like the original Game Boy. I always enjoyed Nintendo games, especially the ones from the old retro consoles, and I always enjoyed emulating them on my phone to play. So, the idea was to recreate that nostalgic experience using accessible, modern microcontroller components.

Utility:

For others, this project serves as an example of simple embedded programming that could be replicated with cheap components. For me, it is an opportunity to deeply understand hardware-software interfacing, memory management, and timing optimization on AVR microcontrollers. The practical utility of the console is simply enjoying gaming and having fun.

General Description

The user interacts with the console via an analog XY joystick and 2 standard buttons. The microcontroller reads these inputs (using ADC for the joystick and GPIO for the buttons) and applies software debouncing.

The core of the system relies on a high-speed SPI bus dedicated solely to driving the display. Because the ATmega328P microcontroller has very limited RAM (2KB), all game state machines, physics algorithms, and graphical assets—such as sprites and custom fonts—are highly optimized using bitmapped hexadecimal integers and stored directly in the microcontroller's internal flash memory.

During the execution of the main loop, the CPU calculates the game physics and updates a single 504-byte internal pixel framebuffer in the SRAM. Once a frame is fully calculated, the entire buffer is blasted to the Nokia 5110 LCD over the SPI lines through a high-speed bi-directional logic level shifter (TXS0108E), ensuring a seamless, tear-free graphical update. Audio is handled concurrently via a hardware timer, which generates variable PWM signals sent to a passive piezo buzzer without interrupting the game logic.

Hardware Design

Component Name Qty Role Datasheet
Arduino UNO R3 1 Main microcontroller, handles game logic and SPI bus https://docs.arduino.cc/resources/datasheets/A000066-datasheet.pdf
Nokia 5110 LCD Display 1 Renders game graphics via SPI https://cdn.sparkfun.com/assets/b/1/b/e/f/Nokia5110.pdf
TXS0108E 8-Ch Logic Level Converter Module 1 Protects the 3.3V display from 5V logic signals https://www.ti.com/lit/ds/symlink/txs0108e.pdf
XY Analog Joystick Module 1 Provides directional movement input (D-pad)
16mm Tactile Push Buttons 2 Action inputs (A and B)
Passive Piezo Buzzer Module 1 Synthesizes 8-bit sound effects using PWM
Breadboard & Jumper Wires 1 set Used for prototyping and physical hardware connections

Simulation + Schematics

Software Design (Firmware)

Development Environment

The firmware for this console is developed using the Arduino IDE. This environment was chosen for its high accessibility, integrated Library Manager, and straightforward compilation process for AVR microcontrollers.

The project is structured around a primary .ino sketch file which contains the core state machine, the setup(), and the loop() functions. By leveraging the Arduino IDE's automatic prototype generation and tab system, the codebase is kept organized without the need for complex makefiles or manual dependency linking, allowing for rapid iteration of game logic and hardware testing.

3rd-Party Libraries and Sources

To avoid reinventing the wheel for low-level hardware protocols while maintaining performance, the following libraries (installed via the Arduino Library Manager) are utilized:

SPI.h (Standard AVR Library): Used for managing the hardware SPI bus. This is the backbone of the console, handling high-speed communication between the Arduino and the Nokia 5110 LCD.

Algorithms and Data Structures

1. The Game Loop & State Machine

The entire software architecture is governed by a Finite State Machine (FSM). The console operates in distinct states (e.g., STATE_MENU, STATE_SNAKE, STATE_TETRIS). The main loop constantly checks the current state and routes execution to the appropriate logic blocks. This ensures the CPU isn't wasting cycles rendering the menu while a game is actively being played.

2. The Framebuffer (Graphics Architecture)

Because the Nokia 5110 LCD does not allow reading data back from its screen memory, the console uses an internal Framebuffer. This is a 504-byte array (84×48 pixels) stored in the Arduino's SRAM.

Algorithm: During a frame, all graphic updates (moving characters, drawing walls, rotating Tetris blocks) are written to this RAM array mathematically using bitwise operations. Once the frame is fully calculated, the entire 504-byte array is blasted over the SPI bus to the screen in one continuous burst. This prevents “screen tearing” and flickering.

3. Non-Blocking Input & Debouncing

Using delay() is strictly forbidden in the game loop, as it would freeze the graphics and audio.

Algorithm: Button presses and joystick movements are handled using a non-blocking debouncing algorithm based on the millis() timer. The system records the timestamp of a physical voltage change on the GPIO pin and ignores further changes for a short window (e.g., ~120ms for Tetris movement) to prevent “phantom double-clicks” or excessively fast piece movement.

4. Collision Detection

For game physics, two distinct algorithms are used depending on the game state:

Axis-Aligned Bounding Box (AABB): Used in Snake to check if the X/Y coordinate boundaries of the snake head overlap with the walls or the apple grid locations.

Matrix Overlay Checking: Used in Tetris to mathematically project the 4×4 array of a falling block onto the 10×20 boolean game board array. If any active bits in the piece array overlap with an active bit on the board array or cross the boundary limits, a collision is flagged.

Implemented Sources and Functions

Below are the core functions that drive the hardware and the game engine:

Function Signature Role & Description
void setup() Initializes the UART for debugging, configures GPIO pin modes (INPUT_PULLUP for buttons, OUTPUT for CS pins), begins the SPI bus, and primes the random seed via analog noise.
void loop() Contains the if/else logic that routes CPU execution to runMenu(), runSnake(), or runTetris() based on the active state.
void readInput() Reads the analog values from the XY joystick via analogRead() and the digital states of the push buttons. Uses millis() to throttle input speed.
void runSnake() void runTetris() Contains the pure math and physics of the active game. Shifts arrays (Snake tail), rotates matrices (Tetris pieces), executes collision checks, and updates the score.
void drawGame() Clears the 504-byte framebuffer array, calculates the new pixel positions for all sprites (using setPixel), and sets the respective bits in the internal array.
void renderScreen() Selects the LCD via the CS pin and pushes the complete 504-byte framebuffer array over the SPI bus to physically update the glass.
void tone() Synthesizes retro audio using the Arduino's built-in hardware timer commands to generate a square wave PWM signal on the buzzer pin at specific frequencies.
void checkTetrisLines() Scans the 10×20 Tetris array for completed rows, shifts the above rows down in memory to simulate gravity, and increments the score multiplier.

Github Repository

Results Obtained

Following the hardware and software integration, the final result is a fully functional, stable, retro arcade-style game console. I successfully implemented three distinct games (Snake, Tetris, and Space Invaders) on a single Arduino microcontroller.

The system runs smoothly with no noticeable latency, thanks to strict memory optimizations (using bitmapped hexadecimal arrays to store graphics) and an internal framebuffer architecture. Logic voltages were successfully stabilized using a bi-directional logic level shifter (TXS0108E), which ensured clean and safe 3.3V SPI communication between the development board and the screen. The audio (PWM) and button debouncing logic run perfectly in parallel with the graphics engine, driven entirely by non-blocking, timer-based algorithms rather than delay() functions.

Conclusions

Building this project was a massive lesson in bare-metal hardware engineering and practical, real-world debugging. The most critical takeaways from this development process are:

The Reliability of “Clone” Components: I learned firsthand that mass-produced Nokia 5110 LCD modules are incredibly fragile and highly prone to mechanical defects, specifically regarding the elastomeric connector (Zebra Strip). Even brand-new screens straight out of the box required “surgical” manual intervention—disassembling the metal bezel, carefully cleaning the contacts to eliminate parasitic resistance, and mechanically re-crimping the metal frame to ensure firm, even pressure between the glass and the PCB.

Misleading Documentation (Datasheets): A crucial lesson is that online tutorials and schematic diagrams cannot always be taken as absolute truth, as underlying hardware revisions frequently change. For example, most standard documentation indicated that the backlight pin (LED/BL) should be connected to VCC (3.3V) through a resistor. In reality, because my specific module utilized a different internal transistor for backlight control, the pin had to be tied to GND for the LEDs to function correctly.

The Satisfaction of the Build: Despite the hours spent diagnosing floating pins, SPI initialization timing, and faulty LCD contrast, the final result was entirely worth the effort. The satisfaction of holding a physical console, pressing the mechanical buttons, and playing games programmed entirely from scratch is immense. This project bridged the gap between simply “writing code” and deeply understanding how software physically manipulates hardware.

pm/prj2026/bianca.popa1106/cristian.stefan1712.txt · Last modified: 2026/05/25 07:04 by cristian.stefan1712
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0