Files
MTGcompanion/main/rfid_manager.c

183 lines
5.9 KiB
C

/*
* MTG RFID Companion - MFRC522 RFID manager (Core 0)
*/
#include <string.h>
#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";
/* FreeRTOS task names are truncated to configMAX_TASK_NAME_LEN-1 (15)
* printable chars, and xTaskGetHandle() asserts if the query is 16+.
* Keep this <= 15 chars and in sync with the rc522 library's
* xTaskCreate("rc522_poll", ...). */
#define RC522_TASK_NAME "rc522_poll"
#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
* complains, lower further or check power/wiring. */
#define RC522_SPI_CLK_HZ (1000000)
#define RC522_START_ATTEMPTS (3)
/* Some clone MFRC522 modules (esp. those wired with RST left floating and
* only an IRQ header exposed) fail the library's strict PCD rw self-test even
* though scanning works fine. Setting this to 1 skips that gate and starts
* the polling task anyway. */
#ifndef CONFIG_MTG_RFID_SKIP_SELFTEST
#define CONFIG_MTG_RFID_SKIP_SELFTEST (1)
#endif
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)
{
/* Dedicated SPI3_HOST (HSPI) bus for the reader - independent controller
* so the display (SPI2_HOST) and the RFID never share bus timing. The
* rc522 driver will call spi_bus_initialize() itself on this host. */
static spi_bus_config_t rfid_bus_cfg = {
.sclk_io_num = HW_RFID_SCK_GPIO,
.mosi_io_num = HW_RFID_MOSI_GPIO,
.miso_io_num = HW_RFID_MISO_GPIO,
};
rc522_spi_config_t driver_config = {
.host_id = HW_RFID_SPI_HOST,
.bus_config = &rfid_bus_cfg,
.dev_config = {
.spics_io_num = HW_RC522_CS_GPIO,
.clock_speed_hz = RC522_SPI_CLK_HZ,
},
.dma_chan = SPI_DMA_CH_AUTO,
/* RST left unconnected (-1): the module comes up on its own power-on
* reset and uses the library soft-reset. Wire RST=GPIO22 and set this
* to HW_RC522_RST_GPIO if your module needs the hard-reset line. */
.rst_io_num = -1,
};
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 CONFIG_MTG_RFID_SKIP_SELFTEST
if (ret != ESP_OK) {
ESP_LOGW(TAG, "MFRC522 self-test failed (%s) - continuing anyway "
"(clone readers often scan fine; the polling task stays up).",
esp_err_to_name(ret));
ret = ESP_OK;
}
#else
if (ret != ESP_OK) {
ESP_LOGE(TAG, "MFRC522 self-test failed (%s). Check: reader powered "
"(3.3V), SPI wiring (SCK=16/MOSI=17/MISO=13, CS=26, RST=22), "
"and if it is a clone try lowering RC522_SPI_CLK_HZ", esp_err_to_name(ret));
return ret;
}
#endif
/* 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, "%s handle not found, affinity not set", RC522_TASK_NAME);
}
ESP_LOGI(TAG, "RFID scanner ready (SPI3_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;
}