ESP-GameBoy: Headless Game Boy-emulator voor ESP32-S3

ROM's worden opgeslagen op het flashgeheugen van de ESP32. Deze kunnen vanaf de telefoon worden geüpload, waardoor het niet nodig is om een cartridge in de firmware te integreren.

Let op: Gebruik uitsluitend legaal verkregen ROM's.

Technische Specificaties

Systeemarchitectuur

  • ESP32 Core 1 (Peanut-GB) → frame → WebSocket binaryPhone browser (Canvas + Web Audio)
  • Phone browser (touch/keyboard) → controller bitmask (1-byte) → ESP32

Netwerk en Streaming

  • Wi-Fi: Open netwerk genaamd ESP-GameBoy (kanaal 1, IP-adres gewoonlijk 192.168.4.1).
  • Video: 160×144, packed 2-bit (5760 bytes per frame).
  • Audio: 8-bit mono PCM.
  • Flash-bibliotheek: Ongeveer 14 MB LittleFS voor .gb-bestanden (maximaal 2 MB per bestand).

Benodigdheden

Hardware

  • ESP32-S3 N16R8 (16 MB flash + 8 MB OPI PSRAM) — dit is het standaardbord.
  • USB-C kabel die data kan overbrengen (geen 'charge-only' kabel).
  • USB-stroombron: computer, telefoonlader of powerbank.
  • Telefoon of computer met Wi-Fi en een browser (Chrome op Android werkt goed).

Software

  • Cursor of VS Code.
  • PlatformIO IDE-extensie (wordt aanbevolen door het project).
  • Python (wordt automatisch geïnstalleerd via PlatformIO).

Installatie en Configuratie

1. Het project verkrijgen

Kloon de repository via Git:

git clone https://github.com/Artificial-Age/ESP32Gameboy.git
cd ESP32Gameboy

Alternatief kan het ZIP-bestand vanaf GitHub worden gedownload en uitgepakt. Open de map in Cursor of VS Code en installeer de PlatformIO IDE-extensie en de Espressif toolchain wanneer daarom wordt gevraagd.

2. Web UI op het bestandssysteem plaatsen

LittleFS wordt gebouwd vanuit de data/ map. Deze moet synchroon lopen met de frontend/ map:

Bron (bewerk hier)Kopieer naar (wordt geflasht)
frontend/index.htmldata/index.html
frontend/style.cssdata/style.css
frontend/app.jsdata/app.js

Bij een clone van de repo zijn deze bestanden al aanwezig in data/. Kopieer ze opnieuw telkens wanneer de UI wordt gewijzigd. Plaats geen auteursrechtelijk beschermde .gb-bestanden in data/ als je van plan bent deze te commiten of te pushen; ROM-bestanden staan in de .gitignore.

3. Het bord aansluiten en de seriële poort bepalen

Verbind de ESP32-S3 via USB-C.

  • Windows: Ga naar Apparaatbeheer → Poorten (COM & LPT) en noteer het COM-nummer (bijv. COM14).
  • macOS / Linux: De poort ziet eruit als /dev/cu.usbmodem* of /dev/ttyACM0.

Mocht de poort niet verschijnen, probeer dan een andere kabel, een andere USB-poort, of houd de BOOT-knop ingedrukt tijdens het aansluiten. PlatformIO detecteert de poort meestal automatisch, maar indien de upload mislukt, kun je deze handmatig instellen in platformio.ini:

upload_port = COM14
monitor_port = COM14

De VS Code-taken in .vscode/tasks.json staan momenteel ingesteld op COM14. Pas dit aan naar jouw poort, of gebruik de PlatformIO-toolbarknoppen.

4. Firmware bouwen en uploaden

Dit schrijft de emulator naar het flashgeheugen, maar kopieert geen web UI of ROM's.

  • PlatformIO toolbar: Klik op het vinkje (Build), daarna op de rechterpijl (Upload).
  • Terminal: pio run -e esp32-s3-n16r8 -t upload

Indien de seriële poort niet in downloadmodus gaat, houd dan BOOT ingedrukt, start de upload en laat BOOT dan los. De standaardomgeving is esp32-s3-n16r8 (alias: esp32-s3) met een partitietabel van 2 MB app + ~14 MB LittleFS (partitions/n16r8_romlib.csv).

5. LittleFS-image uploaden (Web UI)

Hiermee flash je de HTML-, CSS- en JS-bestanden uit de data/ map. Dit moet minimaal één keer gebeuren bij een nieuw bord, en telkens na een wijziging in de UI.

  • PlatformIO toolbar: Klik op het cloud-upload icoon — “Upload Filesystem (LittleFS)”.
  • Terminal: pio run -e esp32-s3-n16r8 -t uploadfs

Optionele seriële log: pio device monitor -e esp32-s3-n16r8. Je zou het volgende moeten zien: [WiFi] AP SSID=ESP-GameBoy IP=192.168.4.1

6. Verbinding maken met de telefoon

Verbind de telefoon met het Wi-Fi netwerk ESP-GameBoy (open, geen wachtwoord). Let op: je verliest tijdens deze verbinding je normale internettoegang.

Het 'captive-portal' (aanmeldingsscherm) kan verschijnen. Dit is niet de Game Boy. Tik op de link naar het huidige adres van de ESP (meestal http://192.168.4.1/) of open dit handmatig in Chrome op Android. De echte pagina bevat het Game Boy-scherm, D-pad, A/B, Select en Start.

Belangrijk: Het aanmeldingsvenster kan geen besturingen, WebSocket-video of audio uitvoeren. Gebruik altijd een volledige browser. Er kan slechts één telefoon tegelijk verbinding maken.

7. Games uploaden (ROM-bibliotheek)

Games staan op de ESP32, niet op de telefoon.

  1. Tik op ⚙ Settings.
  2. Open ROM library. De flash-opslagmeter toont het verbruikte, vrije en totale geheugen.
  3. Tik op Add ROM to library en selecteer een .gb-bestand (max. 2 MB per stuk).
  4. Wacht tot de voortgangsbalk klaar is. Je kunt meerdere games toevoegen tot het geheugen vol is.
  5. Tik op Play bij een titel. Het apparaat slaat de huidige cartridge indien nodig automatisch op en herstart in de gekozen ROM.

Mocht de telefoon de verbinding verliezen tijdens het herstarten, maak dan opnieuw verbinding met ESP-GameBoy en open de UI. Tik eenmaal op het scherm om de audio te ontgrendelen.

8. Spelen

De besturing is als volgt verdeeld:

ActieOn-screenToetsenbord
D-PadpadPijltjestoetsen
AAZ of A
BBX of B
SelectSELECTShift of Q
StartSTARTEnter of W

Extra opties in Settings:

  • Button haptics: Korte vibratie bij indrukken (ondersteunde Android-browsers).
  • Stream buffer: Standaard 48 ms. Lager is sneller (snappier); hoger is vloeiender bij instabiel Wi-Fi.
  • Reboot: Wis save-data en herstart de SoftAP.

Bij sommige telefoons kan de SoftAP-bandbreedte onder de 60 FPS zakken. De firmware gooit frames weg wanneer de WebSocket-wachtrij vol is, in plaats van de emulator te pauzeren.

Save-data

Cartridges met batterij-RAM worden automatisch opgeslagen in /saves/<name>.sav (ongeveer elke 15 seconden bij wijzigingen, en voor een herstart of ROM-wissel).

In Settings → Save data kun je:

  • .sav downloaden.
  • .sav uploaden (het apparaat herstart om dit toe te passen).
  • Save verwijderen (wist flash + RAM, gevolgd door een herstart).

Probleemoplossing

SymptoomMogelijke oplossing
Upload mislukt / poort bezetSluit Serial Monitor; trek de kabel eruit en stop hem er opnieuw in; houd BOOT ingedrukt.
Verkeerde COM-poortStel upload_port in platformio.ini in; pas .vscode/tasks.json aan indien van toepassing.
Wi-Fi is zichtbaar maar pagina is leegJe bevindt je nog in het captive-portal; open de link in Chrome.
Geen audioTik eenmaal op het scherm na het starten van 'Play'; controleer of de telefoon op stil/mute staat.
"Idle — upload a ROM" in seriële logDe firmware werkt, maar er is geen cartridge geselecteerd; gebruik Settings → ROM library.
UI ziet er oud uit na code-wijzigingJe hebt de firmware geüpload maar niet de LittleFS; voer uploadfs opnieuw uit.
Telefoon blijft niet verbondenBlijf dicht bij het apparaat; er is slechts één client toegestaan; herstart het bord via Settings of USB.

Projectstructuur

  • platformio.ini: Bordinstellingen, partities, libraries.
  • partitions/: 16 MB layout (waarvan ~14 MB voor de ROM-bibliotheek).
  • src/main.cpp: Wi-Fi AP, captive portal, Peanut-GB, WebSocket.
  • src/link_radio.cpp: Bewijs van ESP-NOW radio (nog geen functionele Pokémon-kabel).
  • src/minigb_apu.c: Game Boy APU.
  • include/: peanutgb.h, minigbapu.h, link_radio.h.
  • frontend/: Broncode van de Web UI.
  • data/: LittleFS-image (kopie van de UI-bestanden).

Kerntechnologieën:

  • Emulator core: Peanut-GB (MIT).
  • Web stack: ESPAsyncWebServer + AsyncTCP.
  • Fase 2 (ESP-NOW link cable): Dit is momenteel enkel een radio-bewijs (via Settings → Link radio proof) en nog geen werkende Pokémon-kabel.