/* * MTG RFID Companion - MFRC522 RFID manager (Core 0) */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "esp_event.h" #include "rc522.h" #include "rc522_picc.h" #include "driver/rc522_spi.h" #include "hw_pins.h" #include "spi_bus_manager.h" #include "rfid_manager.h" static const char *TAG = "rfid"; #define RC522_TASK_NAME "rc522_polling_task" #define RC522_POLL_INTERVAL_MS (100) #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 further (e.g. 1000000) or check * power/wiring. Many clone readers glitch at 4MHz on a shared bus. */ #define RC522_SPI_CLK_HZ (2000000) #define RC522_START_ATTEMPTS (3) static rc522_driver_handle_t s_driver; static rc522_handle_t s_scanner; static char s_last_uid[RFID_UID_STR_MAX_LEN] = {0}; static bool s_pending_card = false; static void uid_to_hex_string(const rc522_picc_uid_t *uid, char *out) { int i; for (i = 0; i < uid->length && i < RC522_PICC_UID_SIZE_MAX; i++) { snprintf(out + (i * 2), 3, "%02X", uid->value[i]); } } static void on_picc_state_changed(void *arg, esp_event_base_t base, int32_t event_id, void *data) { rc522_picc_state_changed_event_t *event = (rc522_picc_state_changed_event_t *)data; rc522_picc_t *picc = event->picc; (void)arg; (void)base; (void)event_id; if (picc->state == RC522_PICC_STATE_ACTIVE) { uid_to_hex_string(&picc->uid, s_last_uid); s_pending_card = true; ESP_LOGI(TAG, "Card scanned: UID=%s (%d bytes, %s)", s_last_uid, picc->uid.length, rc522_picc_type_name(picc->type)); } else if ((picc->state == RC522_PICC_STATE_IDLE || picc->state == RC522_PICC_STATE_HALT) && event->old_state >= RC522_PICC_STATE_ACTIVE) { ESP_LOGI(TAG, "Card removed"); } } esp_err_t rfid_manager_init(void) { rc522_spi_config_t driver_config = { .host_id = HW_SPI_HOST, /* Bus already owned by spi_bus_manager: attaching as an extra device. */ .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, }; esp_err_t ret = rc522_spi_create(&driver_config, &s_driver); if (ret != ESP_OK) { ESP_LOGE(TAG, "rc522_spi_create failed: %s", esp_err_to_name(ret)); return ret; } ESP_ERROR_CHECK(rc522_driver_install(s_driver)); rc522_config_t scanner_config = { .driver = s_driver, .poll_interval_ms = RC522_POLL_INTERVAL_MS, .task_stack_size = RC522_TASK_STACK_SIZE, .task_priority = RC522_TASK_PRIORITY, }; ret = rc522_create(&scanner_config, &s_scanner); if (ret != ESP_OK) { ESP_LOGE(TAG, "rc522_create failed: %s", esp_err_to_name(ret)); return ret; } ESP_ERROR_CHECK( rc522_register_events(s_scanner, RC522_EVENT_PICC_STATE_CHANGED, on_picc_state_changed, NULL)); return ESP_OK; } esp_err_t rfid_manager_start(void) { 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, "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; } /* Pin the library's polling task to Core 0 so that RFID I/O never * competes with the UI renderer on Core 1. */ TaskHandle_t task = xTaskGetHandle(RC522_TASK_NAME); if (task != NULL) { vTaskCoreAffinitySet(task, (UBaseType_t)(1U << 0)); ESP_LOGI(TAG, "RC522 polling task pinned to Core 0"); } else { ESP_LOGW(TAG, "rc522_polling_task handle not found, affinity not set"); } ESP_LOGI(TAG, "RFID scanner ready (SPI2_HOST, CS=%d, RST=%d)", HW_RC522_CS_GPIO, HW_RC522_RST_GPIO); return ESP_OK; } const char *rfid_manager_last_uid(void) { return s_last_uid; } bool rfid_manager_has_pending_card(void) { return s_pending_card; } void rfid_manager_consume_card(void) { s_pending_card = false; }