# 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 & On-Device Wi-Fi Setup (No App Needed!) The device runs a built-in web portal that works on any smartphone, tablet, or laptop: * **Connecting to the Portal:** * **Option A (Instant Captive Portal):** Connect your phone to the Wi-Fi network named **`MTG-Companion`** (no password). Your phone will automatically pop up the setup screen! (Or open `http://192.168.4.1` in your browser). * **Option B (Home Wi-Fi):** If connected to your home network, simply browse to the device's IP shown on screen (e.g. `http://192.168.0.57`). * **🎴 Card Pairing Tab:** 1. Tap an unmapped card sleeve to the readerβ€”its unique tag ID appears instantly in the form. 2. Type the card name (e.g. `"Sol Ring"`); Scryfall auto-complete displays live suggestions. 3. Click **Pair Card**. The device downloads high-resolution illustration art and official oracle text into permanent flash memory. 4. Browse and manage all paired cards in your deck with illustration thumbnails and 1-click unbind. * **πŸ“Ά Wi-Fi Settings Tab:** 1. Click **Scan Networks** to see all nearby Wi-Fi networks with signal strength and security indicators. 2. Select your network (or mobile hotspot) from the dropdown, enter the password, and click **Connect & Save**. 3. **Multi-Network Memory:** The device remembers up to 5 Wi-Fi profiles in permanent flash. When you turn the companion on, it automatically scans and connects to whichever saved network is in range (e.g. seamlessly switching between Home Wi-Fi and your Phone's Mobile Hotspot)! 4. Forget unwanted networks anytime with one click. #### 7. Over-The-Air (OTA) Updates The companion automatically fetches firmware releases directly from: **`https://git.rasmusbendtsen.dk/rasmus/MTGcompanion/releases`** * **Automatic Hash Verification:** Whenever you push code to `main`, Gitea Actions builds the new firmware and computes its cryptographic SHA-256 hash. * **Instant Detection:** Select **8. CHECK FIRMWARE OTA** from the menu (or let it auto-check on boot when connected to Wi-Fi). The device compares its currently running image hash against the latest build on your server: * If a new build is available, the screen shows the new target hash and invites you to click the knob to install. * If the hashes match, it confirms the firmware is up to date and skips unnecessary flashing. * **Safe Dual-Slot Flashing:** While updating, a live progress bar streams the binary over HTTPS into the inactive partition slot. The device validates the new image and reboots automatically with rollback protection. --- ### πŸ’‘ Switching Wi-Fi Networks on the Go (Home Wi-Fi ↔ Phone Hotspot) When heading to your Local Game Store (LGS), a friend's house, or a tournament, switching between your home Wi-Fi and phone hotspot is completely effortless: 1. **One-Time Setup via Phone Hotspot:** - Turn on your phone's personal hotspot. - Connect your phone to the **`MTG-Companion`** hotspot. - In the pop-up portal under **πŸ“Ά Wi-Fi Settings**, click **Scan Networks**, select your phone hotspot, enter the password, and click **Connect & Save**. 2. **Automatic Hand-off:** - The device stores both your Home Wi-Fi and Phone Hotspot. - At home: it automatically joins Home Wi-Fi. - At the game store: turn on your phone's hotspot and turn on the companionβ€”it locks onto your phone automatically! > πŸ›Ÿ **Offline Play Always Works:** > All previously scanned cards and art are stored in local flash memory, and the device's built-in `MTG-Companion` pairing portal works offline with zero internet required. --- ## πŸ› οΈ 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`](file:///c:/Users/rsben/Desktop/Rasmus/Programmering/espMTG/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](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32/get-started/): ```powershell . C:\Espressif\frameworks\esp-idf-v5.5\export.ps1 idf.py --version ``` #### 2. Configuration Configure initial Wi-Fi credentials (or use on-device Wi-Fi setup): ```powershell idf.py menuconfig # Navigate to: "MTG RFID Companion" # - Wi-Fi SSID & Password (Optional - can also be configured via captive portal) ``` *(The firmware is pre-configured to check updates directly from `https://git.rasmusbendtsen.dk/rasmus/MTGcompanion/releases`)* #### 3. Build & Flash (Initial USB Provisioning) ```powershell 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`](file:///c:/Users/rsben/Desktop/Rasmus/Programmering/espMTG/.gitea/workflows/releases.yml): * **Trigger:** Pushes to the `main` branch. * **Environment:** Official `docker://espressif/idf:v5.5` container. * **Artifacts & Hashes:** Compiles the firmware, generates SHA-256 hashes (`firmware.sha256`), and automatically releases `mtg-rfid-companion.bin` (for wireless OTA updates) to the Gitea repository at `https://git.rasmusbendtsen.dk/rasmus/MTGcompanion/releases`. * **Hash-Based Verification:** Embeds the Git commit SHA and binary SHA-256 hash in the release metadata so the ESP32 can verify whether an update is available without relying on manual version bumps. --- ### 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/ β”œβ”€β”€ .json # Cached Scryfall card data (Mana, Oracle, Type, P/T) └── .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. |