Skip to content

About

Swing an M5Stack Cardputer like a badminton racket — the device IS the game console: on-device swing detection, physics & AI, serving any browser as its display

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

43 Commits

Folders and files

Repository files navigation

Cardminton

中文 · English

Hold an M5Stack Cardputer like a racket grip and play badminton by actually swinging it. The game itself runs entirely in the Cardputer's firmware: swing detection, shuttle flight physics, the CPU opponent and scoring all happen on the device. It opens its own WiFi hotspot and serves the game page, so any phone or laptop browser that connects becomes its screen and speakers. There is nothing to install.

architecture

How it plays

The ball goes where you swing. Your character positions itself; you only handle the racket. Swinging up gives a clear or a lift, a downward chop is a smash, a flat swing drives the shuttle, and a gentle touch drops it at the net. The direction of your sideways sweep decides whether the shot goes left or right. Shot quality comes down to timing: a ring closes on screen, and swinging right as it shuts lands you in the sweet spot with a power bonus and the best accuracy. The window is forgiving — anything from 240ms early to 170ms late still connects.

Matches go to 11 points. The CPU has five difficulty tiers, from a rookie that hands you a point every ten exchanges to an "Unbeatable" tier with zero unforced errors and a 60ms reaction that reaches everything. You won't beat it; it's there for practice.

Getting started

  1. Power on the Cardputer. Its screen alternates between two QR codes.
  2. Scan the first one with your phone camera to join the Cardminton hotspot (it joins silently; your phone keeps using cellular for everything else). Then scan the second one and the game opens in your browser at http://192.168.4.1. On a laptop, join the WiFi manually (password cardminton) and open the same address.
  3. Press Start, or just swing the Cardputer.

You can also try it without hardware: J, K and L swing down, flat and up, Shift softens the hit, A and D aim, and Space swings with perfect timing. Add ?demo to the URL for an automatic rally. P opens a debug panel.

Two players

Switch the host page to Versus mode. When another racket device joins the WiFi and connects to ws://192.168.4.1/p2, the right half of the screen shows the candidate; click it to accept and the view splits.

/p2 takes a fixed binary IMU packet format (documented in docs/architecture.md), so anything that can produce that stream can act as a racket. There is a ready-made firmware in firmware/cardminton-racket/ that turns a second Cardputer into the player-two racket. A caveat: I only own one Cardputer, so that firmware compiles but has never met the host on real hardware.

Flashing

cd firmware/cardminton-host
pio run -t upload --upload-port /dev/cu.usbmodem101

The web frontend is gzipped and embedded into the firmware binary at build time (tools/embed_assets.py), so this one command is the entire deployment — there is no filesystem image to upload separately.

Architecture notes

The details live in docs/architecture.md; the short version:

Swing judgment never crosses the radio. Detection runs at 200Hz on the same chip that samples the IMU and feeds the game logic directly. Doing the detection in a browser would put a round of BLE or WiFi batching in front of every swing.

The browser receives 30 state snapshots per second, each carrying the shuttle's position, velocity and a device timestamp. It advances the shuttle from the snapshot to the current frame with the same physics formula the device uses, which is how 30Hz of network turns into 60fps of rendering — by the time the next snapshot arrives, it lands almost exactly on the local prediction.

The timing ring only makes sense if it agrees with the clock the device judges your swing by, so the page keeps an estimate of the device clock via WebSocket ping/pong (taking the lowest-RTT samples) and renders the ring against device time.

The hotspot is deliberately a quiet local network: no DNS, no captive portal. Phones join without any popup and keep their internet on cellular; the game page is reached by plain IP. An earlier version had a captive portal, and dismissing that iOS sheet kept dropping the WiFi with it — not having one turned out to be the reliable design.

Swing detection

There is no machine learning and no calibration step, just a state machine over gyro magnitude:

IDLE → ARMED  (>150°/s, gravity latched)
     → FIRE   (>240°/s and accel excursion >0.25g)
     → HOLD   (250ms, power may only improve)
     → REFRACTORY (500ms)

Direction is computed in the gravity reference frame (gyro-only attitude integration from the moment the swing arms), which makes it independent of how you hold the device. Per-frame cost is constant. The design and its references are in docs/swing-engine.md.

Tests

cd native && make run   # the C++ core, same sources the firmware builds
npm test                # the JS reference implementation

The native suite replays 81 recorded real swings (test/fixtures.json) through both the C++ engine and the JS reference and requires identical output — fire time, power and direction must not deviate at all. It also covers the ballistics, the net-clearance solver, contact picking, and runs a full simulated match.

Layout

firmware/cardminton-host/   host firmware (PlatformIO / Arduino C++)
  lib/cardminton/           pure compute library: swing_engine + game_core
                            (no Arduino deps, shared with the native tests)
  src/main.cpp              SoftAP + web/WS + IMU task + game tick + screen
  tools/embed_assets.py     public/ → gzip → embedded in the binary
firmware/cardminton-racket/ companion firmware: a 2nd Cardputer as the P2
                            racket (compiles; untested on hardware)
native/                     native parity tests (make run)
public/                     browser renderer: game.js, net.js, i18n.js, sfx
  swing.js                  JS reference swing engine (tests only)
test/                       JS test suite + recorded swing fixtures
scripts/export_fixtures.py  regenerate fixtures from local recordings
server.mjs                  static server for frontend dev (npm run dev)
docs/architecture.md        architecture and WS protocol
docs/swing-engine.md        swing engine design and references

Hardware

One M5Stack Cardputer ADV (ESP32-S3, BMI270 IMU). A second one is optional for Versus.

License

MIT — see LICENSE.

About

Swing an M5Stack Cardputer like a badminton racket — the device IS the game console: on-device swing detection, physics & AI, serving any browser as its display

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages