Power management, an on-screen HUD/menu, and safe-shutdown software for the Circuit Sword (Raspberry Pi Compute Module 3 Game Boy mod kit). This repo builds a custom SD-card image: Raspberry Pi OS Trixie Lite 64-bit + RetroPie + a custom kernel + WiFi/BT drivers.
- Download
rpios-cs-final.img.xzfrom the releases page and flash it to an SD card with Raspberry Pi Imager or balenaEtcher (both read.xzdirectly — no need to unpack). - Open the boot partition (visible on any OS) and edit
network-config— replaceYOUR_WIFI_NAME/YOUR_WIFI_PASSWORDwith your WiFi SSID and password. - Insert the SD card and power on.
- First boot takes a few minutes (needs WiFi) — automatically:
- the root partition expands to fill the SD card,
- runtime packages install via apt,
- the WiFi (RTL8723BS) and battery (
cs_battery) kernel modules are registered via DKMS so they survive kernel updates, - the board reboots once into the stock kernel.
- After that EmulationStation starts automatically. In EmulationStation, press the menu button for the on-screen HUD (battery / WiFi / volume / brightness). Note: the on-screen HUD menu only works in EmulationStation, not inside a running emulator. In-game, use the hardware button combos for sound and brightness (see What's working below for why).
config-cs.txton the boot partition controls the boot mode without rebuilding the image.
The image ships with the standard Raspberry Pi OS credentials:
- user
pi, passwordraspberry(passwordlesssudo) - SSH is enabled out of the box
- the Samba shares (roms / configs / splashscreens) also use
pi/raspberry
The first boot warns you that the default password is unchanged. Log in and run
passwd(andsudo smbpasswd -a pi) to set your own before putting the device on an untrusted network.
The userspace software (HUD, scripts, systemd units, settings, Bluetooth, and the
non-DPI parts of config.txt) can be updated without re-flashing the card:
- In EmulationStation, open the RetroPie menu → Circuit Sword Updater. It checks GitHub, shows you the changes, and only downloads/applies them if you confirm. It is fully manual — nothing updates automatically.
- Or over SSH:
sudo cs-update check(report only) /sudo cs-update menu(interactive) /sudo cs-update apply(non-interactive).
The updater never touches the kernel, the WiFi DKMS driver, or
network-config, and it preserves your screen's DPI block in config.txt, so
your WiFi credentials and screen choice survive. cs-hud is rebuilt from source
and rolled back automatically if it fails to start. For a new kernel or a driver
change you still re-flash a fresh image.
Point the updater at a different repo/branch with
CS_REPO_URL/CS_REPO_BRANCH(or pass a branch as the last argument, e.g.sudo cs-update menu my-branch).
The image ships configured for the stock 640×480 DPI panel. config.txt
contains a # CS START DPI SETTINGS … # CS END DPI SETTINGS block with the
active 640 setup plus commented-out alternatives — a 640 HDMI-adapter panel
and a 320×240 panel (which needs the explicit bus-format=0x1009 + sync/DE
inversions). Swap the active lines for the block that matches your hardware; the
in-place updater deliberately leaves this block untouched.
- Docker Desktop on an arm64 host (Apple Silicon) — the image assembler runs as an arm64 container.
- ~15 GB free disk space
curlandxz(brew install curl xzon macOS)
./build.sh retropie # download RPi OS + install RetroPie base (~1h, rarely needed)
./build.sh all # kernel + WiFi + HUD + Bluetooth → assemble./build.sh all also builds the RetroPie base (output/rpios-retropie.img) by
itself if it's missing, so on a clean checkout you can skip straight to it; the
explicit retropie step above just lets you (re)build that slow stage on its own.
On x86_64 Linux the build registers QEMU arm64 emulation automatically (Apple
Silicon runs arm64 natively).
The final image lands at output/rpios-cs-final.img.
| Changed | Command |
|---|---|
| Config / scripts only | ./build.sh software |
HUD source (cs-hud_new/) |
./build.sh hud && ./build.sh software |
| Kernel config or branch | ./build.sh kernel && ./build.sh wifi && ./build.sh software |
| WiFi driver only | ./build.sh wifi && ./build.sh software |
| Bluetooth binary | ./build.sh bt && ./build.sh software |
| Variable | Default | Description |
|---|---|---|
KERNEL_BRANCH |
rpi-6.18.y |
RPi kernel branch to build |
KERNEL_NAME |
kernel8 |
Output kernel filename (no .img) |
- Raspberry Pi Compute Module 3 (CM3, BCM2837, arm64, 1 GB RAM)
- RTL8723BS — WiFi (SDIO) + Bluetooth (UART)
- DPI display 640×480, rotated 180°
- USB audio (C-Media)
- Controls, battery ADC and LCD backlight via an on-board Arduino Leonardo
(ATmega32u4) over serial (
/dev/ttyACM0)
Everywhere (handled by the Arduino, independent of what's on screen):
- Hardware shortcuts: MODE + ↑/↓ = volume, MODE + ←/→ = brightness
- Safe shutdown (power switch + low battery)
- Low-battery safety (audio-signalled) — a HUD can't draw over a running
emulator on KMS, so low battery is signalled with sound: a warning chime at
15% (4 beeps, the last longer), and at 5% a descending "battery empty"
jingle, after which the running game is auto-saved (RetroArch
.state.auto, which auto-resumes next boot and never touches your manual save slots) and the system halts safely. See Known issues for why you still flip the switch.
In EmulationStation:
- cs-hud on-screen menu — press the menu button for battery / WiFi toggle /
volume / brightness (
cs-hud_new/, SDL2 over KMS/DRM via a VT hand-off).
In-game (inside an emulator):
- The visual cs-hud menu does NOT render — on the 64-bit KMS stack a HUD cannot draw on top of a running emulator (there is no DispmanX overlay layer anymore). Only the hardware Arduino buttons above work in-game.
- Battery + clock are shown in RetroArch's own menu, fed by the
cs_batterypower-supply module.
HDMI output / docking:
- Plug in an HDMI cable and the device reboots into 1080p on HDMI (the DPI handheld panel goes dark); unplug it and it reboots back to the 640×480 internal screen. After plugging in, the switch takes ~5 s to register, then a normal reboot — EmulationStation is back in ~10 s (it no longer waits for WiFi to connect). Live-switching the running session is unreliable on the KMS stack, so a fresh boot — which always brings the display up correctly — is used instead. The DPI overlay stays loaded in both modes (so the panel is never left undriven), and the boot splash is shown right-side-up on whichever display is active.
Build / system:
- Custom 64-bit kernel (Trixie,
rpi-6.18.y); WiFi (RTL8723BS) and thecs_batterymodule rebuild via DKMS, surviving anapt full-upgrade - Bluetooth via
rtk_hciattach; temperature-based on/off fan control (the fitted 2-wire blower must not be PWM-throttled on its supply) - EmulationStation pixel theme + instant transitions; rotated boot splash
- First-boot partition resize + package install + one-time self-cleanup
- In-place updater (
cs-update) — manual, user-triggered from the ES RetroPie menu; updates userspace without a reflash (see Updating in place above)
- No always-on battery overlay on top of a running emulator — the 64-bit KMS stack has no DispmanX overlay layer (the same reason jecaro's NixOS port has no HUD at all). In-game info comes from RetroArch's menu instead.
- A low-battery (or software) shutdown halts safely but can't cut the power rails while the physical power switch is ON — on this board the ATmega gates the power-off on the switch, not on a Pi signal. So the OS halts (SD-card safe) and you flip the switch to fully power off; the "battery empty" jingle + black screen are the cue.
- Deferred ideas (Plymouth boot splash, analog-stick calibration) and a
code-cleanup audit are tracked in
FUTURE.md.
Pre-built .img.xz images are published on the
releases page. The latest
is v2.1.0 — low-battery safety (warning chime + auto-save + safe shutdown) on
top of the v2.0.0 64-bit Trixie rebuild. Already running Circuit Sword? You don't
need to reflash — get it via RetroPie → Circuit Sword Updater (see Updating in
place above). See Quick start to flash
a fresh install.
- jecaro/circuix-sword — a NixOS port for the same CM3 Circuit Sword hardware
- weese's 1.4.x releases — the 32-bit lineage this rebuild descends from
- Kite's original 1.3.x releases