DEV Community

Anas Abuelhaag
Anas Abuelhaag

Posted on

Built with Google Gemini: Handheld Arcade Console with Raspberry Pi Pico

This is a submission for the Built with Google Gemini: Writing Challenge

What I Built with Google Gemini

For this project, I designed and implemented a handheld arcade console powered by a Raspberry Pi Pico (RP2040) and developed entirely in MicroPython. Instead of building another web-based application, I focused on a physical embedded system that integrates hardware interfacing, real-time rendering, and structured game logic.

The Hardware Architecture

The console architecture consists of:

  • RP2040 (Raspberry Pi Pico)
  • ST7789 240×240 SPI TFT Display
  • Analog joystick connected via ADC0 (GPIO26) and ADC1 (GPIO27)
  • Two action buttons using internal pull-ups (GPIO3, GPIO10)
  • PWM-driven buzzer on GPIO21

On top of this hardware layer, I developed seven fully playable games: Snake, Pong, Space Shooter, Flappy Bird, Dodger, Cave Flyer, and Dino Runner.

Hardware Planning, Wiring, and Pin Configuration

Before wiring the system, I used Gemini to reason through GPIO selection and ensure the wiring scheme aligned directly with the MicroPython initialization code.

Pin Mapping Table

Pico GPIO Peripheral Connection
GPIO6 SPI SCK ST7789 SCK
GPIO7 SPI MOSI ST7789 MOSI
GPIO13 SPI CS ST7789 CS
GPIO11 Data/Command ST7789 DC
GPIO12 Reset ST7789 RST
GPIO26 ADC0 Joystick X
GPIO27 ADC1 Joystick Y
GPIO3 Digital Input Button A
GPIO10 Digital Input Button B
GPIO21 PWM Output Buzzer

ST7789 Driver Framework and Implementation

Rather than copying fragmented examples, I designed a structured LCD driver framework. To support efficient graphics rendering, I implemented a framebuffer-based display driver class (ST7789_FB).

import framebuf

class ST7789_FB:
    def __init__(self, spi, width, height, reset, dc, cs):
        self.width = width
        self.height = height
        self.spi = spi
        self.rst = reset
        self.dc = dc
        self.cs = cs
        self.cs.value(1)
        self.init_display()

        # Allocate Full Framebuffer
        self.buffer = bytearray(self.width * self.height * 2)
        self.fb = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.RGB565)

    def write_cmd(self, cmd):
        self.dc.value(0)
        self.cs.value(0)
        self.spi.write(bytearray([cmd]))
        self.cs.value(1)

    def write_data(self, buf):
        self.dc.value(1)
        self.cs.value(0)
        self.spi.write(buf)
        self.cs.value(1)

    def refresh(self):
        self.write_cmd(0x2A)
        self.write_data(bytearray([0, 0, (self.width-1)>>8, (self.width-1)&0xFF]))
        self.write_cmd(0x2B)
        self.write_data(bytearray([0, 0, (self.height-1)>>8, (self.height-1)&0xFF]))
        self.write_cmd(0x2C)
        self.dc.value(1)
        self.cs.value(0)
        self.spi.write(self.buffer)
        self.cs.value(1)
Enter fullscreen mode Exit fullscreen mode

Memory Considerations on RP2040

A 240×240 display using 16-bit RGB565 color requires 115,200 bytes (~112.5 KB) for a full framebuffer. The RP2040 provides 264 KB of SRAM, meaning nearly half of the available RAM is consumed by the framebuffer alone. This constraint influenced several architectural decisions:

  • Fixed Allocation: The framebuffer is allocated once during initialization to prevent heap fragmentation.
  • Zero-Buffer Loops: No large temporary buffers are created inside game loops.
  • Selective Rendering: Rendering logic avoids unnecessary full-screen redraws to save clock cycles.

Debugging Screen Orientation (The Real Fix)

One major issue involved mirrored output and inverted color mapping. I prompted Gemini:

"In MicroPython on RP2040, my ST7789 display is mirrored horizontally and colors look swapped. I'm using SPI and setting MADCTL to 0x00. What configuration should I check?"

Gemini pointed toward MADCTL bit flags and RGB/BGR order.

  • Initial (incorrect): self.write_data(bytearray([0x00]))
  • Corrected: self.write_data(bytearray([0x40]))

(Setting the register to 0x40 specifically flips the MX/Row Address Order bit, correcting the hardware-level mirroring).

Demo

A photo of the completed Pico Arcade Console showing the menu screen with 7 games

Build It Yourself

Full source code including the ST7789_FB driver, all 7 game implementations, and the menu system is available here:

Check out the source code on GitHub

What I Learned

AI generates quickly. Hardware validates ruthlessly.
AI accelerates engineering, but embedded systems enforce discipline.

  • Clear constraints produce better AI responses.
  • Deep hardware understanding is required to verify AI-generated suggestions.

Top comments (0)