260 lines
14 KiB
Markdown
260 lines
14 KiB
Markdown
# 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/
|
|
├── <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. | |