Make RFID failure non-fatal (self-test retries, 4MHz clock, diagnostic log), add beginner Wi-Fi guide + RFID troubleshooting to README
This commit is contained in:
119
README.md
119
README.md
@@ -98,6 +98,10 @@ Verify:
|
||||
idf.py --version
|
||||
```
|
||||
|
||||
> **Never used Espressif tools before? Skip ahead to
|
||||
> "[Changing the Wi-Fi without the toolchain](#changing-the-wifi-without-the-toolchain)";**
|
||||
> the section below is optional.
|
||||
|
||||
### 2. Build
|
||||
|
||||
```powershell
|
||||
@@ -124,9 +128,104 @@ idf.py menuconfig # -> "MTG RFID Companion" -> SSID + password
|
||||
# sdkconfig.defaults.esp32
|
||||
```
|
||||
|
||||
Leaving the SSID empty (or keeping the `YOUR_...` placeholders) disables station
|
||||
mode — the SoftAP pairing server still runs, so you can use the device
|
||||
without a home network.
|
||||
Leaving the SSID empty (or keeping the `YOUR_...` placeholders) **disables
|
||||
station mode**. The SoftAP pairing server runs regardless, so the device stays
|
||||
usable as a fully standalone scanner — no home network required.
|
||||
|
||||
#### What "station mode disabled" actually means
|
||||
|
||||
The Wi-Fi radio still turns on, but only as an access point:
|
||||
|
||||
1. `wifi_manager_start()` sees the blank/placeholder SSID and returns early —
|
||||
it never calls `esp_wifi_connect()`, so the device won't try to join any
|
||||
router (and won't pretend to be online).
|
||||
2. `web_server_start()` starts unconditionally: SoftAP **`MTG-Companion`** at
|
||||
`192.168.4.1` (own DHCP) plus the `/api/last_uid` and `/api/bind` endpoints.
|
||||
3. `net_task` skips the 60-second connect wait **and** the boot-time prefetch.
|
||||
Card lookups only run when you explicitly trigger one, and those fail fast
|
||||
(no internet route) with a `scryfall` log line — they never block the UI.
|
||||
|
||||
#### What works and what doesn't
|
||||
|
||||
| Feature | In AP-only mode | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| RFID scanning, life counter, LED/buzzer | ✔ | fully local |
|
||||
| Showing **already-cached** cards (art + stats) | ✔ | served from `/spiffs` |
|
||||
| Binding a UID to a card name | ✔ | written to `/spiffs/mappings.json` immediately |
|
||||
| **Scryfall lookup + art download** | ✘ | the device has no internet here; the fetch fails and is logged. It completes automatically on a later boot **with Wi-Fi configured** — the boot-time prefetch walks `mappings.json` and downloads anything missing |
|
||||
| Scryfall autocomplete in the pairing page | ⚠️ | the AP doesn't pass internet through (no NAT). A phone with **cellular data keeps its mobile connection**, so autocomplete works there; on a device with no other internet path, type the exact card name manually |
|
||||
|
||||
> **Use case:** AP-only mode is the normal "deck on the table" flow — mid-game
|
||||
> you almost never need live Scryfall lookups. When you do (new card, missing
|
||||
> art), configure Wi-Fi once, boot briefly, and the binding + art cache update
|
||||
> themselves via prefetch.
|
||||
|
||||
---
|
||||
|
||||
## Changing the Wi-Fi without the toolchain
|
||||
|
||||
> **If you're new to this and haven't installed any Espressif software yet,
|
||||
> start here.**
|
||||
|
||||
The Wi-Fi network the device connects to is **baked into the firmware at
|
||||
compile time** — there is no on-device settings menu yet. To change it you
|
||||
must edit one text file and produce a new firmware image. Here is the shortest
|
||||
ESPRESSIF-FREE path to do that, with zero drivers or toolchains.
|
||||
|
||||
### The one file you edit
|
||||
|
||||
Open **`sdkconfig.defaults.esp32`** in any text editor (Notepad is fine) and
|
||||
find these two lines:
|
||||
|
||||
```
|
||||
CONFIG_MTG_WIFI_SSID="YOUR_SSID_HERE"
|
||||
CONFIG_MTG_WIFI_PASS="YOUR_PASSWORD_HERE"
|
||||
```
|
||||
|
||||
Replace the placeholders with **your** network name and password, keeping the
|
||||
quotes:
|
||||
|
||||
```
|
||||
CONFIG_MTG_WIFI_SSID="MyHomeWiFi"
|
||||
CONFIG_MTG_WIFI_PASS="correct horse battery staple"
|
||||
```
|
||||
|
||||
- Keep the file **UTF-8 without BOM**, and don't add spaces inside the quotes.
|
||||
- If your password has a `"` or `\` in it, this file format can't represent it
|
||||
(rare) — pick that one character out of the password or use the toolchain
|
||||
menu instead.
|
||||
- Want to go back to offline-only later? Restore the `YOUR_...` placeholders
|
||||
and rebuild.
|
||||
|
||||
### Getting a new firmware image from this file
|
||||
|
||||
The repo cannot build firmware without the toolchain. Your options, easiest
|
||||
first:
|
||||
|
||||
| Path | What's needed | How |
|
||||
| :--- | :--- | :--- |
|
||||
| **Ask whoever gave you the device** | a minute of their time | Send them the edited file; they run `idf.py build` and hand you a new `.bin` |
|
||||
| **One-time toolchain install (~45 min, guided)** | the Espressif installer (free, Windows/macOS/Linux) | Follow the official guide: <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/> then run the 5 commands in [Build](#2-build) |
|
||||
| **CI build** (GitHub Actions etc.) | a GitHub account | The repo can build on `ubuntu` in CI; push the edited file and download the built `mtg-rfid-companion.bin` artifact |
|
||||
|
||||
### Flashing the new firmware (no toolchain needed)
|
||||
|
||||
Flash using **Espressif Flash Download Tool** (graphical, no toolchain):
|
||||
<https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-setup-scratch.html#esp32-flash-download-tool>
|
||||
or `esptool.py` if you already have Python:
|
||||
|
||||
```
|
||||
pip install esptool
|
||||
esptool.py -p COM3 --chip esp32 --before default_reset write_flash --flash_mode dio --flash_size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 mtg-rfid-companion.bin
|
||||
```
|
||||
|
||||
### TL;DR
|
||||
|
||||
1. Edit `sdkconfig.defaults.esp32` → 2. rebuild (yours or someone's toolchain) →
|
||||
3. flash the `.bin` → 4. done. If you don't need your home Wi-Fi, **skip the
|
||||
whole thing**: the device works standalone and has its own AP.
|
||||
|
||||
---
|
||||
|
||||
### 4. Flash & watch
|
||||
|
||||
@@ -145,6 +244,10 @@ I web: SoftAP 'MTG-Companion' at 192.168.4.1, web server ready
|
||||
I ui: UI task on core 1
|
||||
```
|
||||
|
||||
> **RFID reader missing / not wired? That's OK.** A reader self-test failure no
|
||||
> longer aborts boot — the device logs a warning and continues into the display,
|
||||
> SoftAP and web-pairing screens without card scanning:
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
@@ -159,11 +262,15 @@ Press the encoder button to toggle **SCANNER ↔ LIFE_COUNTER**. Rotate to adjus
|
||||
(20/40 preset or whatever you last set; press again to reset to the preset).
|
||||
|
||||
### Web pairing (registration mode)
|
||||
1. Connect your phone to the `MTG-Companion` access point.
|
||||
1. Connect your phone to the `MTG-Companion` access point. A phone with
|
||||
cellular data keeps its mobile internet, so Scryfall autocomplete works;
|
||||
otherwise type the name manually.
|
||||
2. Open `http://192.168.4.1`.
|
||||
3. Scan an unmapped card — the UID appears in the page.
|
||||
4. Type / pick a Scryfall-autocomplete name → **Pair UID with card**.
|
||||
5. The mapping persists to `/spiffs/mappings.json` and the card is fetched & cached.
|
||||
5. The mapping persists to `/spiffs/mappings.json`. The card data/art fetch
|
||||
runs immediately if the device has Wi-Fi, or is picked up automatically by
|
||||
the boot-time prefetch the next time you boot with Wi-Fi configured.
|
||||
|
||||
---
|
||||
|
||||
@@ -192,7 +299,9 @@ time (`littlefs_create_partition_image`), so a fresh flash already contains
|
||||
| Display = black / garbage | Check CS/DC/RST wiring; try toggling `esp_lcd_panel_invert_color` and `LCD_RGB_ELEMENT_ORDER_*` in `display_manager.c` |
|
||||
| Colors inverted | Same toggle as above — panel clones differ |
|
||||
| No UID printed on scan | Verify MFRC522 wiring, tag type (ISO14443A), antenna positioning; confirm `rfid: RC522 polling task pinned to Core 0` in the log |
|
||||
| Boot log: `rc522: FIFO length missmatch` / `RTOS: RFID unavailable` | Reader self-test failed. Check the MFRC522 has 3.3V power (many clones have a separate power pin), CS=GPIO5 / RST=GPIO22, and SCK/MOSI/MISO=18/23/19. Try lowering `RC522_SPI_CLK_HZ` in `rfid_manager.c` (e.g. `2000000`); some clones need a slower clock on a shared bus |
|
||||
| Wi-Fi never connects | Re-run `idf.py menuconfig`, confirm you replaced the `YOUR_...` placeholders |
|
||||
| Scryfall autocomplete shows nothing | The AP has no internet passthrough — the phone needs its own mobile data connection, or type the card name manually |
|
||||
| Can't reach 192.168.4.1 | Ensure your phone joined `MTG-Companion`, not a cached network |
|
||||
| Card renders but no art | Card not cached yet — check `scryfall` log lines; art downloads are best-effort |
|
||||
| App grows near 2MB | mbedTLS + HTTP pulls are large; if needed, enable `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE` (smaller) |
|
||||
|
||||
11
main/main.c
11
main/main.c
@@ -21,8 +21,13 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(storage_manager_init());
|
||||
ESP_ERROR_CHECK(display_manager_init());
|
||||
|
||||
ESP_ERROR_CHECK(rfid_manager_init());
|
||||
ESP_ERROR_CHECK(rfid_manager_start());
|
||||
/* RFID is not fatal: the reader may be unpowered, unwired, or a clone
|
||||
* that needs a slower SPI clock. Continue booting so the display, SoftAP
|
||||
* and web pairing all stay usable; scanning is simply unavailable. */
|
||||
esp_err_t ret = rfid_manager_init();
|
||||
if (ret != ESP_OK || rfid_manager_start() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "RFID unavailable (%s) - continuing without card scanning", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(wifi_manager_init());
|
||||
ESP_ERROR_CHECK(wifi_manager_start());
|
||||
@@ -31,7 +36,7 @@ void app_main(void)
|
||||
|
||||
ESP_ERROR_CHECK(ui_task_start());
|
||||
|
||||
ESP_LOGI(TAG, "Boot sequence complete. Ready for Phase 7+.");
|
||||
ESP_LOGI(TAG, "Boot sequence complete.");
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
@@ -21,6 +21,11 @@ static const char *TAG = "rfid";
|
||||
#define RC522_TASK_STACK_SIZE (6 * 1024)
|
||||
#define RC522_TASK_PRIORITY (4)
|
||||
|
||||
/* Conservative clock for MFRC522 clones. If the FIFO self-test still
|
||||
* reports a mismatch at boot, lower this (e.g. 2000000) or check power. */
|
||||
#define RC522_SPI_CLK_HZ (4000000)
|
||||
#define RC522_START_ATTEMPTS (3)
|
||||
|
||||
static rc522_driver_handle_t s_driver;
|
||||
static rc522_handle_t s_scanner;
|
||||
|
||||
@@ -63,6 +68,7 @@ esp_err_t rfid_manager_init(void)
|
||||
.bus_config = NULL,
|
||||
.dev_config = {
|
||||
.spics_io_num = HW_RC522_CS_GPIO,
|
||||
.clock_speed_hz = RC522_SPI_CLK_HZ,
|
||||
},
|
||||
.dma_chan = SPI_DMA_CH_AUTO,
|
||||
.rst_io_num = HW_RC522_RST_GPIO,
|
||||
@@ -96,9 +102,21 @@ esp_err_t rfid_manager_init(void)
|
||||
|
||||
esp_err_t rfid_manager_start(void)
|
||||
{
|
||||
esp_err_t ret = rc522_start(s_scanner);
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
for (int attempt = 1; attempt <= RC522_START_ATTEMPTS; attempt++) {
|
||||
ret = rc522_start(s_scanner);
|
||||
if (ret == ESP_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGW(TAG, "rc522_start attempt %d/%d failed: %s",
|
||||
attempt, RC522_START_ATTEMPTS, esp_err_to_name(ret));
|
||||
vTaskDelay(pdMS_TO_TICKS(250));
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "rc522_start failed: %s", esp_err_to_name(ret));
|
||||
ESP_LOGE(TAG, "MFRC522 self-test failed (%s). Check: reader powered "
|
||||
"(3.3V), SPI wiring (SCK=18/MOSI=23/MISO=19, CS=5, RST=22), "
|
||||
"and if it is a clone try lowering RC522_SPI_CLK_HZ", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -360,15 +360,15 @@ static void net_task(void *arg)
|
||||
#ifndef CONFIG_MTG_WIFI_SSID
|
||||
#error "Missing CONFIG_MTG_WIFI_SSID"
|
||||
#endif
|
||||
if (strlen(CONFIG_MTG_WIFI_SSID) > 0) {
|
||||
if (wifi_manager_sta_configured()) {
|
||||
if (wifi_manager_wait_connected(pdMS_TO_TICKS(60000)) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Wi-Fi connect timed out, continuing without network");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
#ifdef CONFIG_MTG_SCAN_PREFETCH
|
||||
prefetch_mappings();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
fetch_request_t req;
|
||||
|
||||
@@ -76,22 +76,32 @@ esp_err_t wifi_manager_init(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool wifi_manager_sta_configured(void)
|
||||
{
|
||||
#ifndef CONFIG_MTG_WIFI_SSID
|
||||
return false;
|
||||
#else
|
||||
const char *ssid = CONFIG_MTG_WIFI_SSID;
|
||||
return strlen(ssid) > 0 && strstr(ssid, "YOUR_") == NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t wifi_manager_start(void)
|
||||
{
|
||||
#ifndef CONFIG_MTG_WIFI_SSID
|
||||
#error "Missing CONFIG_MTG_WIFI_SSID - run idf.py menuconfig"
|
||||
#endif
|
||||
|
||||
const char *ssid = CONFIG_MTG_WIFI_SSID;
|
||||
const char *pass = CONFIG_MTG_WIFI_PASS;
|
||||
|
||||
/* Empty SSID, or an untouched sdkconfig.defaults.esp32 placeholder:
|
||||
* keep station mode disabled so we don't hammer a bogus AP at boot. */
|
||||
if (strlen(ssid) == 0 || strstr(ssid, "YOUR_") != NULL) {
|
||||
if (!wifi_manager_sta_configured()) {
|
||||
ESP_LOGW(TAG, "no SSID configured - Wi-Fi station skipped (set CONFIG_MTG_WIFI_SSID)");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const char *ssid = CONFIG_MTG_WIFI_SSID;
|
||||
const char *pass = CONFIG_MTG_WIFI_PASS;
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
|
||||
@@ -25,6 +25,12 @@ esp_err_t wifi_manager_init(void);
|
||||
*/
|
||||
esp_err_t wifi_manager_start(void);
|
||||
|
||||
/**
|
||||
* @brief True if a real SSID is configured (non-empty, not the
|
||||
* `YOUR_...` placeholder). Station mode will actually run.
|
||||
*/
|
||||
bool wifi_manager_sta_configured(void);
|
||||
|
||||
/**
|
||||
* @brief True if the STA has a valid IP.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user