An ESP32-S3 N16R8 development board drives a 105-LED 12V WS2811 bingo board โ and serves a full React web UI over WiFi so phones, tablets, and laptops can call games, join cards, hear call-outs, and print signed bingo sheets.
Connect to the BINGO network โ open http://bingo.local (fallback http://192.168.4.1) โ pick Board or Card mode โ play.
| ๐ฎ Gameplay | ๐ก Lights | ๐ฑ Clients | ๐ Safety |
|---|---|---|---|
| 42 game types | 19 themes | Board host UI | PIN + 7-day token |
| Auto / manual calling | Screensavers (13) | Printed QR cards | Unlock lockout |
| Physical buttons | Called-number banner | Live card sync | Device-signed cards |
| Caller audio + jokes | Winner animations | Odds drawer | Optional home WiFi |
Board: ESP32-S3 N16R8 development board โ ESP32-S3-WROOM-1, 16 MB flash, 8 MB PSRAM, dual USB-C, 44-pin DevKitC-1 layout
Strip: WS2811 ร 105, 12V, single data line
Wire by GPIO numbers on the silkscreen (not classic 30-pin ESP32 charts). Full pinout: WIRING.md
| Function | GPIO | Silkscreen | Connect |
|---|---|---|---|
| LED data | 4 | 4 (J1) |
Strip DIN |
| Button 1 | 16 | 16 (J1) |
Momentary โ GND |
| Button 2 | 18 | 18 (J1) |
Momentary โ GND (not 17) |
| Status LED | 2 | 2 (J3) |
Header GPIO (onboard RGB is separate) |
| Ground | โ | G |
12V (โ) + strip GND |
| Button | Short press | Long press (~700โฏms) |
|---|---|---|
| 1 (game type) | Cycle game type (when allowed) | Reset active game |
| 2 (draw / winner) | Draw next (automatic style only) | Winner / keep-going โ or, on a fresh manual game with zero calls: switch to automatic and draw the first number (does not turn on UI auto-call Play) |
Any button exits LED test / screensaver.
โโโโโโโโโโโโโโโโโโโโโโ WiFi AP "BINGO" โโโโโโโโโโโโโโโโโโโโโโโโ
โ ESP32 firmware โ โโโโโโโโโโโโโโโโโโโโบ โ Browser (React SPA) โ
โ FastLED โ 105 LEDsโ REST + WebSocket โ Board / Card / OCR โ
โ SPIFFS โ UI+MP3s โ Push snapshots โ Served from /data โ
โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
- Firmware:
src/main.cppโ game engine, LEDs, auth, cards, WiFi, NVS - Frontend:
frontend/โ Vite + React + TypeScript + Tailwind + shadcn/ui - SPIFFS: custom partition (~14โฏMB) for UI + multi-voice caller packs โ
partitions/bingo.csv(SPIFFS usable โ75% of partition; erase flash after map changes) - Dev mock: if the board is unreachable, the UI falls back to an in-memory mock API
- Automatic โ Draw next / header Play timer / Button 2 short press
- Manual โ Tap numbers on the board; Button 2 long-press can convert a blank manual game to automatic + first draw
- Undo last call
- Power-loss recovery โ call order / current / pool restore from NVS
- Default auto-call interval: 10 seconds (configurable; Play stays UI-driven)
48 types in the generated catalog (Classics, Letters & Symbols, Shapes & Frames, Blocks & Arrows, Pictures, Combos & Rules, plus Battleship). The board/new-game UI uses a searchable, filterable picker with mini pattern previews. Physical Button 1 cycles every type in catalog order when changing type is allowed.
Canonical definitions live in scripts/generate-game-types.mjs (generates frontend + firmware tables). Multi-orientation types cycle display patterns on the LED matrix (synced to the UI). Double Bingo wins on any two Traditional lines; Blackout Lite wins at any 20 covered cells.
Battleship is an elimination game: last card still afloat wins; a card sinks when all its populated numbers are called (same-call co-survivors share). Sparse cards (blanks allowed, FREE at center) are supported. LED indicator: looping 1โ25 chase. Sink LEDs invert the board (uncalled lit); from call 38 the current number strobes red.
Cards may include blank cells; pattern wins only require populated cells in the mask. HMAC domain bingo-card-v2 covers all 25 cells (including blanks); legacy full-card signatures are still accepted.
- Declare / clear winner (UI + Button 2 long-press)
- Card-driven winners + keep-going with claimed pattern masks
- Winner LED phases (board sparkle โ scroll)
- Winner activation can wait while call-out audio is still playing
Pre-recorded voice packs on SPIFFS / frontend/public/cv/{F1,F2,M1,M2}/ (short paths โ SPIFFS max ~31 chars):
- Numbers B-1 โฆ O-75
- Utility:
on,jokes-on,bingo - Optional jokes for every number (
joke-B-1โฆjoke-O-75)
Board UI: Settings โ Caller โ voice + speech rate ยท unlock with a tap โ volume / jokes ยท keepalive for iOS/Android ยท firmware audio hold so the next auto-draw waits for the clip (countdown still runs).
Regenerate with OpenAI TTS (requires .env OPENAI_API_KEY + ffmpeg):
CALLER_VOICE_PACKS=Male1,Male2,Female2 node scripts/generate-caller-audio-openai.mjsLegacy macOS say generator (single pack):
./scripts/generate-caller-audio.sh- Full 5ร5 card with FREE center; re-roll / auto-sync
- Join the live board session (no seed)
- Joined cards: only called numbers markable; winner flash + confetti
- Settings โ Cards:
- Download signed PDF (4 cards / page; FREE cell = QR)
- Copy shareable claim links
- QR / link joins via
POST /card/claimwith HMAC over the boardโs stable device id - Scanning while already unlocked as Board can verify authenticity without switching modes
- Default PIN:
1975(change in Settings โ Access) - Session token TTL: 7 days, persisted in NVS (survives reboot)
- 5 failed unlocks โ 30โฏs lockout (
429) - Mutating board APIs require
X-Board-Token - Public paths: unlock, card join/mark/leave/claim/state sync
- Expired auth prompts unlock in place (no forced Card-mode switch)
| Feature | Notes |
|---|---|
| Layout | CSV-mapped; letters โ numbers โ game-type matrix |
| Themes | 19 (static + animated) |
| Color modes | Theme / solid / custom letter colors |
| Header color | Dedicated BINGO letter LEDs |
| Game-type color | Dedicated 5ร5 matrix |
| Vibrance | 0โ100 boost for strip punch |
| Current beacon | Color + flash / pulse / strobe |
| Letter-full mode | When a column is complete: on / off / number theme |
| Called-number banner | Optional ~3โฏs letter+digits glyph across the board |
| Screensavers | 13 types on the full 21ร5 matrix |
| LED test | Sequenced strip test from Settings |
- Mode chooser: Board vs Card
- Odds drawer (Monte Carlo win estimates)
- Per-mode light/dark themes (
bingo-theme-board/bingo-theme-card; card defaults dark) - Settings tabs: LEDs ยท Screensaver ยท UI ยท Caller ยท Cards ยท WiFi ยท Webhooks ยท MQTT ยท Access
- Optional STA WiFi join (home network) alongside / instead of AP-only use
- Fullscreen, theme toggle, live player/card counts
- UI-only BINGO color themes (do not change strip colors)
| AP SSID | BINGO |
| AP password | washisnameo |
| AP IP | 192.168.4.1 |
| mDNS | bingo.local |
| Config | include/config.h |
Realtime: WebSocket /ws (subscribe as board / card / none) + HTTP polling fallback.
Settings โ Webhooks and MQTT share the same event catalog. Each channel has its own enable checkboxes.
| Event | When |
|---|---|
number_called |
Number drawn / called |
number_undone |
Undo last call |
winner_declared |
Winner declared (UI, card bingo, or Button 2) |
winner_cleared |
Winner cleared / keep-going |
game_started |
Game reset / new game |
game_type_changed |
Game type changed |
calling_style_changed |
Manual โ automatic |
Webhooks: one HTTP POST URL + optional basic auth (username empty = no Authorization header). Password omit-on-save keeps the stored secret.
MQTT: broker host/port, optional username/password, publish topic, optional TLS (setInsecure). Master enable toggle. Publishes JSON to the configured topic when connected.
Payloads always include event, gameType, and callingStyle (automatic | manual). Browser caller voice is not sent.
Needs: Node.js, npm, PlatformIO, Python 3 (for make qa)
make deploy
# or pin the serial port (S3 boards often use usbmodem):
make deploy PIO_PORT=/dev/cu.usbmodem101make frontend-build # Vite โ data/ (prunes stale hashed assets; keeps MP3s)
make fw-upload # firmware (esp32s3 + partitions/bingo.csv)
make fs-upload # SPIFFS
make monitor # serial @ 115200
make qa # smoke tests โ QA_BASE / QA_PINSPIFFS size guidance: UI + voice packs must fit in ~75% of the SPIFFS partition (filesystem overhead). Current map: ~2โฏMiB app + ~14โฏMiB SPIFFS. uploadfs writes the entire SPIFFS partition image. After changing partitions/bingo.csv, erase flash once before upload. Build prints prune size via scripts/prune-spiffs-data.mjs.
cd frontend && npm install && npm run devMock backend activates automatically if the ESP32 doesnโt answer within ~2โฏs. Force mock with VITE_MOCK=true.
See frontend/dev/shared-mock-server.mjs for multi-browser mock sessions during development.
make qa
make qa QA_BASE=http://192.168.4.1 QA_PIN=1975scripts/qa-board.py exercises unlock/lockout, game actions, screensaver quirks, LED test vs screensaver, auth headers, and more.
bingo-flashboard/
โโโ src/main.cpp # Firmware (game + LEDs + API + WS)
โโโ include/config.h # Pins, AP, PIN, NVS keys
โโโ include/led_map.h # Physical LED index maps
โโโ partitions/bingo.csv # Larger SPIFFS layout
โโโ WIRING.md # ESP32-S3 wiring + pinout
โโโ docs/ # Hardware reference (optional diagrams)
โโโ data/ # SPIFFS payload (built UI + MP3s)
โโโ frontend/ # React app source
โ โโโ public/cv/{F1,F2,M1,M2}/ # Voice packs (short SPIFFS paths)
โโโ scripts/
โ โโโ generate-caller-audio-openai.mjs
โ โโโ generate-caller-audio.sh
โ โโโ prune-spiffs-data.mjs
โ โโโ qa-board.py
โโโ Makefile # deploy helpers
โโโ platformio.ini
NVS (device): brightness, themes/colors, screensaver, auto-call seconds, game type, calling style, board PIN, device id, board token, letter-full / beacon / banner flags, WiFi STA creds, webhook URL/auth/flags, MQTT broker settings, live game snapshot, crash log.
Browser: UI themes (per mode), UI letter colors, auto-call seconds UI, board token/expiry, card id + card state, caller speech/jokes/rate prefs.
Personal / venue bingo hardware project. WiFi credentials and default PIN are intended for a local party AP โ change them before leaving the device on an open floor.
Happy daubing. ๐