MTG RFID Companion 🧙♂️✨
A dedicated tabletop companion for Magic: The Gathering players. Slip micro RFID/NFC stickers into your card sleeves and tap them to the device for instant card lookups, official full-color art, life tracking, dice rolling, and complete deck management.
🃏 Part 1: Player's Guide (Non-Technical Overview)
What is the MTG Companion?
The MTG Companion is a handheld gadget designed to sit on your playmat during Commander (EDH), Modern, or casual kitchen-table games.
Instead of fumbling for your phone to check complex Oracle card text, searching for spindown dice, or asking "Wait, how much commander tax am I at?", the Companion handles everything with a turn of a knob and a tap of a card:
- ⚡ Tap-to-Identify: Tap any sleeved card with an NFC sticker to instantly display its official illustration, mana cost, updated Oracle text, type line, power/toughness, and current market price.
- 👑 Legendary Fanfares: An 8-bit stinger plays whenever you cast a Mythic Rare or Legendary spell!
- ❤️ Life & Multi-Counter: Tracks starting life (40 for Commander, 20 for Standard/Modern), Poison Counters (tracks to 10 lethal), and Commander Combat Damage (tracks to 21 lethal). Sounds an 8-bit defeat tone if a player dies.
- 🎲 Spindown D20, D6 & Fair Coin: Roll animated dice on screen with ticking sound effects and special fanfares for Natural 20s and Natural 1s.
- 🪙 Token & Counter Spawner: Digital counters for Treasures, Foods, Clues, 1/1 Soldiers, 1/1 Goblins, 2/2 Zombies, 3/3 Beasts, and +1/+1 Counters.
- 👑 Commander Tax & Turn Tracker: Automatically calculates the extra {2} mana tax every time your commander returns to the command zone, alongside a table turn counter.
- 📱 Wireless Phone Pairing: Connect your phone or laptop over your home Wi-Fi (or the device's hotspot) to easily assign card names to tags with live Scryfall search autocomplete.
- 🔄 Over-The-Air (OTA) Updates: Update to new software versions wirelessly over Wi-Fi right from the on-device menu.
How to Use at the Table
┌──────────────────────────────┐
│ ★ MTG COMPANION ★ │
├──────────────────────────────┤
│ > [1] Card Scanner < │
│ [2] Life & Counters │
│ [3] Dice & Coin │
│ [4] Commander Tax │
│ [5] Token & Counters │
│ [6] Deck Storage │
│ [7] Device & WiFi Info │
│ [8] Check Firmware OTA │
└──────────────────────────────┘
Controls
- Turn the knob: Scroll through menus, adjust life totals, change token quantities, or pick dice modes.
- Click the knob: Select an option, roll dice, or toggle between Life, Poison, and Commander Damage modes.
- Long Press (Hold knob for 0.75s): Sounds a chirp and instantly takes you back to the Main Menu from any screen.
- Tap a Card Anytime: Tapping a card sleeve immediately pulls up that card's details, regardless of which screen you are on.
Tabletop Modes
1. Card Scanner & Art Viewer
Tap your card against the scanner area. The device checks its internal memory and displays:
- Full-color official art crop streamed from Scryfall.
- Mana cost, card type, power / toughness, and loyalty.
- Updated Oracle card text with rules text formatting.
- Market price in USD.
2. Life & Metric Tracker
- Starts at 40 life (Commander) or 20 life (1v1).
- Rotate clockwise to gain life; counter-clockwise to take damage.
- Click the knob to cycle between:
- Life Total (Standard life score).
- Poison Counters (Counts 0 to 10; triggers a defeat tone at 10).
- Commander Combat Damage (Counts 0 to 21; triggers lethal banner at 21).
3. Spindown D20 / D6 / Fair Coin Flip
- Select D20 Spindown, D6, or Coin Flip and click to roll.
- An animated roll plays with sound effects.
- Natural 20: Plays a triumphant 8-bit Victory Fanfare!
- Natural 1: Sounds a critical failure buzz.
- Coin Flip: Clean 50/50 toss landing on HEADS or TAILS.
4. Built-in Token & Counter Spawner
Never dig through your token box again for missing tokens:
- Pre-loaded with MTG staples: Treasure, Food, Clue, Soldier (1/1), Zombie (2/2), Goblin (1/1), Beast (3/3), and +1/+1 Counters.
- Rotate to select a token, click to edit, and rotate to change quantity (persists throughout your game).
5. Commander Tax & Turn Tracker
- Rotate the knob each time your commander is recast from the command zone; the screen calculates the extra mana required (e.g.
Cast 3 = +4 Mana). - Click to switch to the table turn counter.
6. Pairing Cards from your Phone (No App Needed!)
When you scan a new card tag that hasn't been programmed yet:
- The screen displays:
PAIR VIA WI-FI: http://192.168.0.57 (or Hotspot: MTG-Companion @ 192.168.4.1) - Open that address in your smartphone or laptop browser.
- Tap the card sleeve to the device—its unique ID pops up in the web form automatically.
- Start typing the card name (e.g.
"Sol Ring"). Real-time Scryfall suggestions appear. - Click Pair Card. The device downloads the high-res card art and text and saves it to its permanent memory.
- Deck Overview: The web page also lets you view your entire scanned deck with card art thumbnails and unbind old cards with one click.
7. Over-The-Air (OTA) Updates
- Select 8. CHECK FIRMWARE OTA from the menu (or let it auto-check on boot when connected to Wi-Fi).
- If a new version is detected, the screen shows the new version number and asks you to click to install or turn the knob to dismiss.
- While updating, a live progress bar shows the download. When finished, the device verifies the update and restarts automatically.
🛠️ Part 2: Technical Specifications & Build Guide
Hardware Architecture
| Component | Part / Model | Purpose |
|---|---|---|
| MCU | ESP32-WROOM-32 (4MB Flash, no external PSRAM) | Dual-core 240 MHz processor |
| Display | 2.0" ST7789 IPS TFT LCD (240x320 resolution) | 16-bit RGB565 high-contrast screen |
| RFID Reader | MFRC522 (13.56 MHz, SPI Interface) | Reads ISO/IEC 14443A tags & stickers |
| User Input | EC11 Rotary Encoder with push button | Tabletop navigation & life adjustment |
| Audio | Passive Piezo Buzzer (GPIO 14) | LEDC PWM multi-frequency tones |
| Tags | NTAG213 / NTAG215 micro FPC stickers | Thin adhesive tags embedded inside sleeves |
| Capacitor | 2200 µF Electrolytic Capacitor | CRITICAL: Placed across MFRC522 VCC/GND to absorb RF transmit current spikes |
GPIO Wiring & Pinout
The display and RFID reader run on two separate SPI host controllers to prevent SPI bus contention and clock frequency mismatches:
- ST7789 Display:
SPI2_HOST(VSPI) at 20 MHz. - MFRC522 Reader:
SPI3_HOST(HSPI) at 1 MHz.
| Peripheral | Functional Pin | ESP32 GPIO | Notes |
|---|---|---|---|
| ST7789 Display (SPI2) | SCL (Clock) | GPIO 18 | SPI2_HOST (VSPI) Clock |
| SDA (MOSI) | GPIO 23 | SPI2_HOST MOSI | |
| CS | GPIO 21 | Dedicated Chip Select (non-strapping) | |
| DC | GPIO 19 | Data / Command Select (non-strapping) | |
| RST | GPIO 4 | Display Reset | |
| VCC / GND | 3.3V / GND | Display logic & backlight power | |
| MFRC522 RFID (SPI3) | SCK | GPIO 16 | SPI3_HOST (HSPI) Clock |
| MOSI | GPIO 17 | SPI3_HOST MOSI | |
| MISO | GPIO 13 | SPI3_HOST MISO | |
| SDA (CS) | GPIO 26 | Dedicated Chip Select | |
| RST | GPIO 22 | Reader Reset | |
| 3.3V / GND | 3.3V / GND | Attach 2200 µF capacitor across these pins! | |
| EC11 Rotary Encoder | CLK (A) | GPIO 32 | Quadrature A (interrupt-driven) |
| DT (B) | GPIO 33 | Quadrature B | |
| SW (Button) | GPIO 25 | Push button (active LOW) | |
| Audio | Buzzer SIG | GPIO 14 | LEDC PWM audio output |
Wiring Diagram
ESP32-WROOM-32
┌────────────────────────────┐
│ 3V3 16 ── SCK ────────┐ │
│ GND 17 ── MOSI ────┐ │ │
│ 13 ── MISO ────┼──┘ │
│ 26 ── SDA(CS) ─┘ │
│ 22 ── RST ─┐ │
│ 21 ── CS ──┼───────┤ ST7789 Display (SPI2_HOST)
│ 19 ── DC ──┤ │ SCL=18, SDA=23, CS=21, DC=19, RST=4
│ 4 ── RST ─┘ │ VCC=3.3V, GND=GND
│ 18 ── SCL ─────────┘
│ 23 ── SDA ─────────
│ 32 ── CLK ────────┤ EC11 Rotary Encoder (CLK=32, DT=33, SW=25)
│ 14 ── SIG ────────┤ Piezo Buzzer (GPIO 14)
└────────────────────────────┘
│ │
[2200 µF Cap]
│ │
┌─────┴───┴──────────────────┐
│ 3V3 GND │
│ MFRC522 RFID Module (SPI3) │
└────────────────────────────┘
Flash Partition Layout (4MB Dual-OTA)
Configured in partitions.csv:
| Partition | Type | SubType | Offset | Size | Purpose |
|---|---|---|---|---|---|
nvs |
data | nvs | 0x9000 |
16 KB | Wi-Fi credentials & system settings |
otadata |
data | ota | 0xd000 |
8 KB | Active OTA slot pointer & rollback state |
phy_init |
data | phy | 0xf000 |
4 KB | RF calibration data |
ota_0 |
app | ota_0 | 0x10000 |
1,600 KB | Primary firmware slot |
ota_1 |
app | ota_1 | 0x1a0000 |
1,600 KB | Secondary firmware slot |
storage |
data | littlefs | 0x330000 |
832 KB | LittleFS flash database (/spiffs) |
- Rollback Safety: Uses
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y. On first boot of a new firmware version, the app must callesp_ota_mark_app_valid_cancel_rollback(). If an update is corrupt or crashes on boot, the bootloader automatically reverts to the previous working slot.
FreeRTOS Dual-Core Architecture
- Core 0 (Networking, Storage & Hardware I/O):
rc522_polling_task: Continuously polls the MFRC522 reader.net_task: Manages Wi-Fi auto-reconnection and Scryfall HTTPS API downloads (esp_http_client).httpd: Runs the embedded web server (serves the pairing SPA and REST APIs).ota_check/ota_update: Queries Gitea release API and streams firmware updates into the inactive OTA bank.
- Core 1 (User Interface & Rendering):
ui_task: Drives the LVGL display subsystem, rotary encoder state machine, and screen animations at 40 FPS without I/O stutter.
Software Setup & Build Commands
1. Prerequisites
Install ESP-IDF v5.x:
. C:\Espressif\frameworks\esp-idf-v5.5\export.ps1
idf.py --version
2. Configuration
Configure Wi-Fi credentials and Gitea instance details:
idf.py menuconfig
# Navigate to: "MTG RFID Companion"
# - Wi-Fi SSID & Password
# - Gitea instance base URL (e.g. https://git.example.com)
# - Gitea repository owner & repo name
3. Build & Flash (Initial USB Provisioning)
idf.py set-target esp32
idf.py build
idf.py -p COM4 erase-flash flash monitor
Automated CI / Gitea Actions Workflow
The repository includes a Gitea Actions workflow in .gitea/workflows/releases.yml:
- Trigger: Pushing any version tag (e.g.
git tag v1.0.1 && git push origin v1.0.1). - Environment: Official
docker://espressif/idf:v5.5container. - Artifacts: Compiles the firmware and automatically attaches
mtg-rfid-companion.bin(for wireless OTA updates) andfull-flash-bundle-*.tar.gz(for USB cable flashing) directly to the Gitea release.
LittleFS Storage Structure
All local card details and artwork are stored in LittleFS mounted at /spiffs:
/spiffs/
├── mappings.json # Scanned UID -> Card Name mapping table
└── cards/
├── <UID>.json # Cached Scryfall card data (Mana, Oracle, Type, P/T)
└── <UID>.jpg # Cached 200x110 Baseline JPEG art crop
❓ Troubleshooting
| Symptom | Cause & Solution |
|---|---|
| Device reboots when scanning a card | Power rail sag caused by the RF transmitter. Solder a 2200 µF capacitor directly across the MFRC522 VCC and GND pins. |
| Display colors inverted (blues look red) | Check flags.swap_color_bytes = 1 in JPEG config and esp_lcd_panel_invert_color() in display_manager.c. |
| Card displays "Could not fetch card" | Device is unable to reach Scryfall. Verify your home Wi-Fi credentials in menuconfig or verify card spelling on the web pairing page. |
| Router displays device name as "espressif" | Hostname is set to mtg-companion. Clear DHCP lease on your router or reboot the ESP32 after connecting. |
Flash fails: Failed to connect to ESP32 |
Ensure no serial monitor is holding COM4 open (Ctrl + ] to exit monitor). Hold the device BOOT button while plugging into USB. |