# 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`](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 Wi-Fi credentials and Gitea instance details: ```powershell 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) ```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:** 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/ β”œβ”€β”€ .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. |