Skip to main content
Version: FILS English

Ferristein 3D - Handheld Game Console

A bare-metal handheld game console, running a custom copy of the renowned classic "Wolfenstein 3D".

info

Author: Tunsu Laurențiu
Group: 1221EA
GitHub Project Link: https://github.com/UPB-PMRust-Students/fils-project-2026-MortalXDTroll

Description

A bare-metal handheld game console built on the Raspberry Pi Pico 2 (RP2350), running a custom Rust port of the game "Wolfenstein 3D". The system is designed for standalone portability, and it integrates a dedicated battery management module and an 8-bit parallel display interface to ensure high frame rates; it features an integrated sd card reader for asset storage, twin joysticks for the FPS controls, and four buttons used to shoot, pause, and navigate the game menu, all being managed directly at the hardware register level.

Motivation

As an aspiring game developer and software engineer, I approached this project as an opportunity to push my technical skills and create a standout piece for my portfolio. Beyond the engineering challenge, I am fascinated by retro consoles, both their physical design and the history of their creation. I have always admired how early developers managed to develop memorable games despite extreme hardware limitations. Building this bare-metal console is my way of experiencing that era of development firsthand, learning how to tightly optimize software to run flawlessly on highly constrained hardware.

Architecture

The provided diagram illustrates the hardware architecture of the custom handheld game console.

Project Diagram

The architecture is divided into five primary logical subsystems that operate concurrently to maintain a stable high frame rate:

  • Processing Subsystem: The microcontroller (RP2350) powers the entire system. It runs the asynchronous Embassy executor, calculates the heavy floating-point raycasting math, and manages the Finite State Machine (FSM) for gameplay and power states.
  • Display Subsystem: Rather than using a CPU-blocking serial connection, the display architecture utilizes an 8-bit parallel bus. The processor renders frame buffers into RAM, and a Direct Memory Access (DMA) channel automatically streams that data to the Programmable I/O (PIO). The PIO handles the high speed hardware pin toggling, allowing the screen to update independently while the CPU calculates the next frame.
  • Input Subsystem: This subsystem isolates user actions. Action buttons are connected directly to the microcontroller's GPIO pins with hardware interrupts. Analog inputs (joysticks) are offloaded to a dedicated external 16-bit ADC, which digitizes the data and transmits it to the processor via the I2C bus.
  • Storage Subsystem: Bypassing the limited internal flash memory, this subsystem utilizes the SPI0 bus to interface with an external SD card. It provides necessary level layouts and texture data directly into the processor's RAM during loading states.
  • Power & Audio Subsystem: A rechargeable battery feeds power to the entire system and is easily recharged through the USB module. At the same time, a passive buzzer plays basic sound effects in the background.

Log

Week 4 - Project Theme Research

  • Initially I considered developing/porting quite simple Nintendo games such as "Super Mario Bros." to the RP2350, but it is not my favorite genre of games.
  • Began deep search on early FPS titles and immediately "DOOM" popped my mind, but after further analysis regarding the developing time and resources, I fell back to a slightly easier approach, "Wolfenstein 3D".

Week 5 - Hardware Components & Software Research

  • Analyzed the hardware bottlenecks of rendering 3D (technically 2.5D) graphics on a microcontroller. Realized a standard SPI display connection would be too slow to hit my 24-30 FPS target, so I chose an 8-bit parallel bus architecture, utilizing the Pico's PIO and a DMA channel.
  • Selected the embassy-rp async framework for concurrent task management and micromath for fast floating-point raycasting approximations.
  • Finalized the pinout mapping to ensure I had enough GPIO pins for the display, I2C ADC, SPI SD card, and buttons without conflicts. Placed the order for all components.

Week 6 - Components Arrival

  • All the hardware components arrived (Pico 2H boards, display shield, ADS1115, joysticks, buttons, buzzer, wires, breadboards, and power modules).
  • Soldered the necessary pin headers onto the ADS1115 module. I decided to pause on soldering the battery and charging module until a later stage of the project.
  • Started the breadboard prototyping phase to test the wiring. During this process, I realized my design uses every single available GPIO pin on the Pico, which means I have to be extremely careful and stick strictly to the schematic moving forward.

Week 7 - Components Testing

  • Set up the secondary Raspberry Pi Pico 2H as a Picoprobe hardware debugger.
  • Wrote basic test scripts to verify peripheral communication: successfully scanned the I2C bus to find the ADS1115 and read raw analog values from the PS2 joysticks.
  • Verified the Omron action buttons using the Pico's internal pull-up resistors and logged the presses to the PC via UART.

Week 8 - Further Testing & Issues

  • Issue: Encountered display flickering and persistent white-screen errors when trying to initialize the ILI9341 shield.
  • Fix: After a few hours of troubleshooting the software, I discovered the root cause was actually the hardware wiring. The data pins on the display were arranged in an unconventional order (D2 through D7, followed by D0 and D1, rather than a standard sequential D0 to D7). Rewiring the 8-bit bus to match this exact layout immediately resolved the issue.

Hardware

Current Prototype & Future Plans: Currently, the entire console is prototyped on breadboards using jumper wires to validate the complex logic and pinout architecture. Once the software is fully developed, the next goal is to design a custom PCB and to 3D print a custom case.

Component Breakdown

  • Microcontrollers: Two Raspberry Pi Pico 2H boards are utilized. One acts as the main processing engine, while the second serves as a dedicated hardware debugger (Picoprobe) for serial logging.
  • Display & Storage: A 2.8-inch ILI9341 TFT display provides the visual output, taking advantage of the 8-bit parallel connection. The display shield natively houses the MicroSD card reader used for streaming game assets.
  • Input Controls: Two PS2-style analog joysticks capture player movement and camera rotation, digitized by an external ADS1115 16-bit ADC to preserve pins. Four Omron tactile switches handle digital inputs like shooting, pausing, and menu navigation.
  • Power & Audio: A 3.7V 1000mAh LiPo battery provides power, regulated by a TP4056 module that allows for safe USB charging. A passive buzzer is driven by hardware PWM to handle the game's retro audio output.

Pinout & Wiring Configuration

Raspberry Pi Pico 2 (Main Target Console)

PinSignalPeripheral
1UART0 TXDebugger (Picoprobe)
2UART0 RXDebugger (Picoprobe)
4-7, 9-12LCD_D0 - LCD_D78-Bit Display Data Bus
14LCD_RSTDisplay Control Logic
15LCD_CSDisplay Control Logic
16LCD_RSDisplay Control Logic
17LCD_WRDisplay Control Logic
19LCD_RDDisplay Control Logic
20PWM (+)Passive Buzzer
21SD_DO (MISO)MicroSD Card Reader (SPI0)
22SD_SS (CS)MicroSD Card Reader (SPI0)
24SD_SCK (SCK)MicroSD Card Reader (SPI0)
25SD_DI (MOSI)MicroSD Card Reader (SPI0)
26I2C0 SDAADS1115 ADC (Joysticks)
27I2C0 SCLADS1115 ADC (Joysticks)
29, 31, 32, 34Digital Input (Active-Low)Action Buttons (X, A, B, Y)
363.3V Logic PowerADS1115 & Buttons VCC
395V Logic PowerDisplay VCC
Bottom PadsSWCLK & SWDIODebugger (Picoprobe)
MultipleSystem GNDAll Components

ADS1115 (External 16-bit ADC)

PinSignalPeripheral
VDD3.3V Logic PowerPico 3V3(OUT) (Pin 36)
GNDSystem GNDGround
SCLI2C0 ClockPico GP21 (Pin 27)
SDAI2C0 DataPico GP20 (Pin 26)
ADDRI2C Address SelectGround
A0Analog InputLeft Joystick VRX
A1Analog InputLeft Joystick VRY
A2Analog InputRight Joystick VRX
A3Analog InputRight Joystick VRY

Power Management (TP4056 Charge Module, LiPo Battery & Power Switch)

Component PinSignal / ConnectionDescription
TP4056 B+Battery Positive (Red Wire)Direct connection to the battery cell.
TP4056 B-Battery Negative (Black Wire)Direct connection to the battery cell.
TP4056 OUT+Slide Switch (Middle Pin)Regulated power output from the charge module.
Slide Switch (Side Pin)Pico VSYS (Pin 39)Hardware toggle to completely cut power and prevent battery drain.
TP4056 OUT-Pico System GNDSystem ground.
TP4056 IN +/-5V USB InputUsed to charge the battery.

Photos

Debug Probe Circuit 1 Circuit 2

Schematics

Schematic

Bill of Materials

DeviceUsagePrice
2x Raspberry Pi Pico 2HThe microcontrollers, the second being for debugging66.55 RON
2.8-inch TFT LCD ILI9341 ShieldPrimary visual interface along with the integrated SD card reader68.99 RON
4x B3F-4055 OMRON Tactile SwitchMenu navigation, gameplay pausing, shooting, power on or off5.79 RON
ADS1115 (16-bit ADC)ADC for Joysticks40.74 RON
2x PS2 Joystick ModuleCharacter movement and camera control10.7 RON
Passive BuzzerGame sounds and ambient music0.99 RON
TP4056 Charge ModuleModule to charge the battery5.99 RON
3.7V LiPo BatteryBattery for portability43 RON
MicroSD Card 16 GBStores level data, music tracks, and wall textures25.88 RON
Breadboards, Wires, Switch Caps, Slide Switch, Micro USB CablePrototyping and connectivity100 RON
Total368.63 RON

Software

LibraryDescriptionUsage
embassy-rpAsync bare-metal HALManages the RP2350 hardware registers, I2C peripherals, PWM audio, and GPIO pins.
embassy-executorAsync task executorRuns the main asynchronous game engine loop and handles hardware interrupts.
ili9341Display driverInitializes the ILI9341 TFT screen and provides the low-level rendering commands.
display-interface-parallel-gpioParallel bus interfaceBinds the 8 GPIO data lines into a standard 8080-style high-speed write interface for the display.
embedded-graphics2D graphics libraryUsed for drawing the 2D menu interface, HUD text, and basic screen clearing.
micromathFast math libraryProvides highly optimized floating-point approximations (sin, cos, tan) absolutely critical for real-time 3D raycasting.
ads1x1xI2C ADC DriverFacilitates communication with the external ADS1115 to precisely read the twin analog joysticks.
defmtDeferred formattingProvides highly efficient serial logging to the PC via the Picoprobe debugger without bloating the game's binary size.
cortex-mARM core accessGrants low-level access to the processor, including the assembly wfi command used to halt the CPU during idle cycles to maximize battery efficiency.
embedded-sdmmcSPI SD Card & FAT ToolboxAllows the Rust engine to read files from the SD card using the SPI0 peripheral
  1. Wolfenstein 3D Gameplay
  2. Wolfenstein 3D Analysis
  3. Game Engine Black Book: Wolfenstein 3D
  4. embassy-rp
  5. Low Level Game Dev
  6. Make Your Own Raycaster Series