Hotspot Arcade
August 3, 2026 · View on GitHub
Hotspot Arcade — M5Stack Cardputer
Several offline party games your guests play from their phones. The Cardputer opens a WiFi access point and a captive portal, everyone joins and plays in the browser — no app to install — and the Cardputer's own screen is the host: lobby, game picker, scoreboard, event log.
This is a host port of tarikbc/hotspot-arcade, which runs the same games as a Flipper Zero app.
If you have a Flipper, use upstream — it is the original and it is excellent.
Screenshots
Phone game client — the web app your guests open in the browser: pick a nickname and an emoji avatar, then play. Same client for every game.
On the Cardputer — the host's own screen (captured straight off the device): the live dashboard, the game picker, and the settings screen with the per-device language switch.
Install
Easiest install (Recommended): The app is in the "M5Burner" catalog and in "Launcher's" catalog. Search for "Hotspot Arcade".
Manual install: Flash by hand to 0x170000:
esptool --chip esp32s3 --port COM7 --baud 921600 write_flash 0x170000 hotspot-arcade-cardputer.ino.bin
or you can drop the .bin on the microSD and launch it from the launcher.
The Cardputer's stock M5Launcher layout puts
loaded apps in ota_0 at 0x170000 and keeps the launcher itself in the test
partition. Writing only the app image leaves the launcher untouched — you still get
back into it the usual way, by holding the button at boot.
Full install (replaces everything, launcher included):
esptool --chip esp32s3 --port COM7 --baud 921600 write_flash 0x0 hotspot-arcade-cardputer.full.bin
Replace COM7 with your port (/dev/ttyACM0, /dev/cu.usbmodem*). esptool comes
with the esp32 core, or pip install esptool. If the board does not enter download
mode by itself, hold G0 on the StampS3 while plugging in USB-C.
Both images are on the releases page.
Hardware
Cardputer v1 (StampS3: ESP32-S3FN8, 8MB flash, no PSRAM). Firmware is ~1.2MB of a 3.3MB app slot and ~78KB of static RAM, leaving ~250KB free at runtime.
Not tested on the Cardputer ADV. It should build, but the ADV has a different keyboard controller (TCA8418) and antenna, so treat it as unverified.
Limit: the v1's antenna is weak — eight phones in a room is fine, range is worse than a dev board with an external antenna.
Using it
The AP comes up at boot; there is no start step. Phones join Hotspot Arcade (open)
and land on http://192.168.4.1 (if not automatically getting there via captive portal).
The dashboard is the host view: SSID and IP, whether the AP is up, the active game, the live scoreboard, and the last event. Everything else is one key away.
| key | |
|---|---|
G | select game (arrow keys move, Enter picks, Esc backs out) |
L | full leaderboard |
C | event log |
R | reset scores |
E | end the current round |
N | rename the AP (restarts it, which drops every phone) |
P | stop / start the AP |
Esc | back to the dashboard |
Serial at 115200 prints the AP address, asset counts and free heap at boot.
Games: trivia, would-you-rather, word scramble, spectrum, kiss marry kill, reaction duel, connect four, tic-tac-toe, dots & boxes, reversi, drawing, pong, guess the color, battleship, chess — fifteen in all. Every one is phone-driven; the host picks which is live and watches.
Build
Needs node and arduino-cli.
tools/build.sh --deps
--deps installs esp32 core 3.3.11 and the M5Cardputer library (which pulls
M5Unified and M5GFX); drop it after the first run. The FQBN matters:
esp32:esp32:m5stack_cardputer:FlashSize=8M,PartitionScheme=default_8MB
The board's default partition scheme is the 4MB one with a 1.2MB app slot, which this firmware does not fit in.
How it relates to upstream
| upstream (Flipper + ESP board) | here | |
|---|---|---|
| game engine | esp32/hotspot-arcade-fw/ha_games.h | the same file, unmodified |
| phone client | streamed over UART at session start | baked into flash |
| content packs | read off the Flipper's SD, streamed | baked into flash |
| host reports | UART v2 frames (`docs/PROTOCOL.md$) | \text{same}, \text{but} \text{simply} \text{called} \text{in}-\text{process} |
| \text{host} \text{UI} | \text{Flipper} \text{scenes}, 128 \times 64 \text{mono} | $ha_ui.h`, 240×135 colour + keyboard |
The engine reaches its host through six sink functions. This port implements them as direct calls into a local mirror.
New code lives in four files under hotspot-arcade-cardputer/:
hotspot-arcade-cardputer.ino | AP, captive portal, WebSocket, the six sinks |
ha_ui.h | five screens + keyboard |
ha_host.h | roster / score / event mirror |
ha_content.h | baked-pack parser (a port of upstream's content_stream_pack()) |
Staying in sync
The rule: nothing under vendor/ is edited here. Everything in it is
upstream's, copied verbatim, with the exact commit pinned in UPSTREAM.md.
Want a game changed? Change it upstream — then both projects get it.
node tools/sync-upstream.mjs ../hotspot-arcade # refresh vendor/ + re-pin the commit
node tools/gen-assets.mjs # re-bake into the sketch
git diff vendor/ after a sync is exactly the upstream change. gen-assets.mjs
copies the three engine headers into the sketch folder because arduino-cli builds
from a copy of the sketch directory, so an include reaching outside it would not
resolve — those copies are generated and carry a banner saying so.
Distribution
- Releases — the app image and the trimmed full image, built and published on every
v*tag. - M5Burner — listed in the catalog. Tagged releases auto-publish a new version when the
M5BURNER_USER/M5BURNER_PWDrepo secrets are set (viatools/m5burner_post.py). - M5Launcher / LauncherHub — in the catalog; mirrored automatically from M5Burner.
Status
Unverified: the Cardputer ADV, and long sessions with many many phones.
License
MIT — see LICENSE, same as upstream. vendor/libs/ holds third-party
libraries under their own licenses.