pocketterm35 batocera setup
PocketTerm35 + Raspberry Pi 5 + Batocera
Handheld retro-gaming terminal: Raspberry Pi 5 (4GB) in the Waveshare PocketTerm35 housing (3.5" 640×480 DPI touchscreen + QWERTY keyboard with gaming keys), a 500GB NVMe SSD on the PCIe TO M.2 Board (E), running Batocera 43.1.
This page documents the full configuration as set up on 2026-08-22, so it can be reproduced from scratch.
Hardware
| Component | Detail |
|---|---|
| Board | Raspberry Pi 5 Model B Rev 1.1, 4GB RAM |
| Housing | Waveshare PocketTerm35 (3.5" 640×480 DPI display, Goodix capacitive touch, Pico-based USB keyboard) |
| Storage | 500GB NVMe via Waveshare PCIe TO M.2 Board (E), PCIe gen 3 |
| OS | Batocera 43.1 "Glasswing" (2026-05-29) |
Display (boot partition: /boot/config.txt)
The display uses Waveshare's custom DPI overlays. These .dtbo files are not part of Batocera — they come from Waveshare's 3.5HDMI_E_DTBO.zip (software guide) and must be extracted to /boot/overlays/:
dtparam=i2c_arm=on
dtoverlay=waveshare-35dpi-4b
dtoverlay=waveshare-35dpi-5b
dtoverlay=dwc2,dr_mode=host # required for the keyboard (USB OTG host)
dtparam=pciex1_gen=3 # NVMe at gen 3 speed
In batocera.conf, keep EmulationStation at panel resolution: es.resolution=max-640x480.
Important: a Batocera version upgrade rewrites the boot partition and deletes the custom overlays. Backups of waveshare-35dpi-4b.dtbo, waveshare-35dpi-5b.dtbo and config.txt live in /userdata/system/pocketterm-backup/ on the device (survives upgrades) and on the PC under ~/backups/pocketterm35/. After an upgrade, copy them back to /boot//boot/overlays (remount rw: mount -o remount,rw /boot).
Keyboard gaming keys
The keyboard enumerates as a USB HID device ("My Custom Pico Keyboard"). The gaming keys emit ordinary keyboard keycodes matching their printed labels (verified by capturing /dev/input/event0):
| Physical key | Keycode sent |
|---|---|
| D-pad ▲▼◀▶ | arrow keys |
| A / B / X / Y | letters a / b / x / y |
| L / R | letters l / r |
| Select (PrtSc) | KEY_SYSRQ (print screen) |
| Start (Pause) | KEY_PAUSE |
| Esc, F1–F12 | Fn layer: Fn+Q = Esc, Fn+1…0 = F1–F10 |
This clashes with RetroArch's default keyboard layout (retropad A on key x, B on z, …) and with default letter hotkeys (r = rewind, l = hold fast-forward) — out of the box the buttons do the wrong things. Fix via /userdata/system/batocera.conf (update-proof, injected into every generated RetroArch config):
## POCKETTERM35 KEYBOARD BLOCK START
global.retroarch.input_player1_up=up
global.retroarch.input_player1_down=down
global.retroarch.input_player1_left=left
global.retroarch.input_player1_right=right
global.retroarch.input_player1_a=a
global.retroarch.input_player1_b=b
global.retroarch.input_player1_x=x
global.retroarch.input_player1_y=y
global.retroarch.input_player1_l=l
global.retroarch.input_player1_r=r
global.retroarch.input_player1_l2=q
global.retroarch.input_player1_r2=w
global.retroarch.input_player1_start=pause
global.retroarch.input_player1_select=print_screen
# default letter hotkeys collide with the gaming keys -> move/disable
global.retroarch.input_rewind=f6
global.retroarch.input_toggle_fast_forward=f7
global.retroarch.input_hold_fast_forward=nul
global.retroarch.input_reset=nul
global.retroarch.input_frame_advance=nul
global.retroarch.input_hold_slowmotion=nul
global.retroarch.input_toggle_slowmotion=nul
# Batocera generates input_enable_hotkey=shift which forces Shift+hotkey; disable
global.retroarch.input_enable_hotkey=nul
# N64 games are analog-driven -> map the stick onto the arrows too
n64.retroarch.input_player1_l_x_minus=left
n64.retroarch.input_player1_l_x_plus=right
n64.retroarch.input_player1_l_y_minus=up
n64.retroarch.input_player1_l_y_plus=down
## POCKETTERM35 KEYBOARD BLOCK END
The input_enable_hotkey=nul line matters: without it Batocera requires holding Shift together with every keyboard hotkey, so Esc alone never exits a game.
In-game controls after this config
| Keys | Function |
|---|---|
| gaming keys | exactly as labeled (SNES-style layout) |
| Pause / PrtSc | Start / Select |
| Q / W | L2 / R2 |
| Fn+Q (Esc) | quit game |
| Fn+1 (F1) | RetroArch menu |
| Fn+2 / Fn+4 | save / load state |
| Fn+6 / Fn+7 | rewind / fast-forward |
Limitation: since the gaming keys emit plain letters, keyboard-centric systems (DOS, Amiga, ScummVM) see them as typed letters — play those with the keyboard itself.
Performance & quality-of-life (batocera.conf)
system.cpu.governor=performance
kodi.enabled=0 # keyboard 'x' in ES could trigger Kodi launch
global.rewind=0 # rewind costs CPU each frame
global.autosave=1 # quit = snapshot, relaunch = resume (handheld style)
# one frame less input lag on light cores:
nes.runahead=1
famicom.runahead=1
snes.runahead=1
megadrive.runahead=1
mastersystem.runahead=1
gamegear.runahead=1
gb.runahead=1
gbc.runahead=1
gba.runahead=1
pcengine.runahead=1
Quieter fan curve
The M.2 Board (E) has a 30×30×7mm 5V blower (8000 RPM, PWM + tach) on the Pi 5 fan header — audible even at 30% duty. Firmware fan curve in /boot/config.txt: silent below 55°C, gentle midrange, full speed only near the 85°C throttle point:
dtparam=fan_temp0=55000,fan_temp0_hyst=5000,fan_temp0_speed=60
dtparam=fan_temp1=63000,fan_temp1_hyst=5000,fan_temp1_speed=100
dtparam=fan_temp2=70000,fan_temp2_hyst=5000,fan_temp2_speed=160
dtparam=fan_temp3=76000,fan_temp3_hyst=5000,fan_temp3_speed=255
Monitor with cat /sys/class/hwmon/hwmon2/fan1_input (RPM) and vcgencmd measure_temp. Drop-in replacement if the blower develops bearing rattle: a 3007 5V blower with 4-pin JST plug (e.g. GMBYLBY 3007); pinout on the Pi 5 header is GND, 5V, PWM, tach.
WiFi
In batocera.conf:
wifi.enabled=1
wifi.ssid=<ssid>
wifi.key=<password>
Apply with batocera-wifi enable or reboot. Batocera's connman runs with SingleConnectedTechnology=true and prefers ethernet — WiFi connects automatically only when no cable is plugged in, and the IP changes when it switches.
ROM library
Source: a 1TB Batocera share image (BobbyCera_Pi4_1TB_V42_C.L.A.M, 882GB of content) — too big for the 463GB NVMe, so a curated subset (~390GB) was synced. Selection principle: systems that emulate well on a Pi 5 and play well on a 3.5" screen with keyboard controls.
- Included: full PSX (CHD set), N64, NDS (touchscreen makes it playable), Saturn, all 8/16-bit consoles and handhelds, arcade (FBNeo, MAME, Neo Geo, Naomi, Atomiswave), Sega CD, PC Engine CD, Amiga/C64/Amstrad/Atari ST (great with the physical keyboard), OpenBOR, Daphne, all small systems.
- Excluded: 3DS (poor emulation on Pi 5), PSP and Dreamcast (analog-stick-dependent, 267GB together), 3DO, Neo Geo CD, MSU-1 sets (duplicates).
Procedure from a WSL/Linux PC (image lives on a NAS SMB share):
sudo mount -t cifs //NAS/stash /mnt/stash -o username=USER,uid=$(id -u)
sudo losetup -P /dev/loop0 /mnt/stash/games/<image>.img
sudo mount /dev/loop0p2 /mnt/imgshare # ext4 "share" partition
rsync -a --partial --info=progress2 /mnt/imgshare/bios/ root@<pi>:/userdata/bios/
rsync -a --partial --info=progress2 \
--exclude=/3ds --exclude=/psp --exclude=/dreamcast --exclude=/snes-msu1 \
--exclude=/msu-md --exclude=/3do --exclude=/neogeocd \
/mnt/imgshare/roms/ root@<pi>:/userdata/roms/
(Batocera SSH: user root, default password linux. After the sync, refresh game lists: ES menu → Game settings → Update gamelists, or batocera-es-swissknife --restart.)
Backups
- On-device:
/userdata/system/pocketterm-backup/(custom overlays + config.txt) — survives upgrades. - On PC:
~/backups/pocketterm35/(overlays, config.txt, batocera.conf incl. pre-change original). batocera.confcontains the WiFi password in plain text — handle backups accordingly.