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)
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
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)