rasmus fcdcd13e46
Some checks failed
Build & Release Firmware / Build ESP32 Firmware (push) Failing after 2m8s
Update .gitea/workflows/releases.yml
2026-09-12 10:52:53 +02:00
2026-09-11 19:10:31 +02:00
2026-09-12 03:40:10 +02:00

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:
    1. Life Total (Standard life score).
    2. Poison Counters (Counts 0 to 10; triggers a defeat tone at 10).
    3. 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:

  1. The screen displays:
    PAIR VIA WI-FI:
    http://192.168.0.57
    (or Hotspot: MTG-Companion @ 192.168.4.1)
    
  2. Open that address in your smartphone or laptop browser.
  3. Tap the card sleeve to the device—its unique ID pops up in the web form automatically.
  4. Start typing the card name (e.g. "Sol Ring"). Real-time Scryfall suggestions appear.
  5. Click Pair Card. The device downloads the high-res card art and text and saves it to its permanent memory.
  6. 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 call esp_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.5 container.
  • Artifacts: Compiles the firmware and automatically attaches mtg-rfid-companion.bin (for wireless OTA updates) and full-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.
Description
No description provided
Readme 34 MiB
2026-09-14 09:31:48 +02:00
Languages
C 98.6%
CMake 0.9%
PowerShell 0.5%