diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 13698bb..e8ed6ae 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,5 +2,6 @@ idf_component_register(SRCS "main.c" "spi_bus_manager.c" "storage_manager.c" + "rfid_manager.c" INCLUDE_DIRS "." ) \ No newline at end of file diff --git a/main/main.c b/main/main.c index ac45d1c..9a16b58 100644 --- a/main/main.c +++ b/main/main.c @@ -4,6 +4,7 @@ #include "esp_log.h" #include "spi_bus_manager.h" #include "storage_manager.h" +#include "rfid_manager.h" static const char *TAG = "MAIN"; @@ -14,7 +15,10 @@ void app_main(void) ESP_ERROR_CHECK(spi_bus_manager_init()); ESP_ERROR_CHECK(storage_manager_init()); - ESP_LOGI(TAG, "Boot sequence complete. Ready for Phase 3+."); + ESP_ERROR_CHECK(rfid_manager_init()); + ESP_ERROR_CHECK(rfid_manager_start()); + + ESP_LOGI(TAG, "Boot sequence complete. Ready for Phase 4+."); while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); diff --git a/main/rfid_manager.c b/main/rfid_manager.c new file mode 100644 index 0000000..704cfc6 --- /dev/null +++ b/main/rfid_manager.c @@ -0,0 +1,133 @@ +/* + * 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) + +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, + }, + .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 = rc522_start(s_scanner); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "rc522_start failed: %s", 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; +} \ No newline at end of file diff --git a/main/rfid_manager.h b/main/rfid_manager.h new file mode 100644 index 0000000..26a5872 --- /dev/null +++ b/main/rfid_manager.h @@ -0,0 +1,49 @@ +/* + * MTG RFID Companion - MFRC522 RFID manager (Core 0) + * + * Phase 3: Initializes the MFRC522 as a device on the shared SPI2_HOST bus, + * registers an ESP event loop listener for card state changes, converts UIDs + * to uppercase hex strings, and pins the polling task to Core 0. + */ + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RFID_UID_STR_MAX_LEN (24) /* 10 bytes x 2 hex chars */ + +/** + * @brief Initialize the MFRC522 driver and register event listeners. + * @return ESP_OK on success. + */ +esp_err_t rfid_manager_init(void); + +/** + * @brief Start polling for cards and pin the polling task to Core 0. + * @return ESP_OK on success. + */ +esp_err_t rfid_manager_start(void); + +/** + * @brief Return the most recently scanned UID (uppercase hex, no separators). + * 8 chars for 4-byte UIDs, 14 chars for 7-byte UIDs. + */ +const char *rfid_manager_last_uid(void); + +/** + * @brief True once a new card has been scanned since the last poll(). + */ +bool rfid_manager_has_pending_card(void); + +/** + * @brief Clear the pending-card flag (call after consuming the UID). + */ +void rfid_manager_consume_card(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index e7e9a5e..82e5275 100644 --- a/sdkconfig +++ b/sdkconfig @@ -1055,7 +1055,6 @@ CONFIG_ESP_PHY_ENABLE_VERSION_PRINT=y # Power Management # # CONFIG_PM_SLEEP_FUNC_IN_IRAM is not set -# CONFIG_PM_ENABLE is not set # CONFIG_PM_SLP_IRAM_OPT is not set # end of Power Management @@ -1314,7 +1313,7 @@ CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0 # # Kernel # -# CONFIG_FREERTOS_SMP is not set +CONFIG_FREERTOS_SMP=y # CONFIG_FREERTOS_UNICORE is not set CONFIG_FREERTOS_HZ=100 # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set @@ -1323,6 +1322,7 @@ CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 # CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_PASSIVE_IDLE_HOOK is not set # CONFIG_FREERTOS_USE_TICK_HOOK is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 # CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set @@ -1331,7 +1331,7 @@ CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" # CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set # CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y -CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0xFFFFFFFF CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 @@ -1351,7 +1351,6 @@ CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y # CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set # CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set -CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y CONFIG_FREERTOS_ISR_STACKSIZE=1536 CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y # CONFIG_FREERTOS_FPU_IN_ISR is not set @@ -1369,7 +1368,7 @@ CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y # end of Extra CONFIG_FREERTOS_PORT=y -CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_NO_AFFINITY=0xFFFFFFFF CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y CONFIG_FREERTOS_DEBUG_OCDAWARE=y CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y @@ -1567,7 +1566,7 @@ CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y # CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set # CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set -CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF +CONFIG_LWIP_TCPIP_TASK_AFFINITY=0xFFFFFFFF CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5 @@ -2275,7 +2274,7 @@ CONFIG_TCPIP_TASK_STACK_SIZE=3072 CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y # CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set -CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +CONFIG_TCPIP_TASK_AFFINITY=0xFFFFFFFF # CONFIG_PPP_SUPPORT is not set CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y # CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 87c5fbe..1233692 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -3,4 +3,6 @@ CONFIG_IDF_TARGET="esp32" CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y \ No newline at end of file +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_VFS_SUPPORT_DIR=y +CONFIG_FREERTOS_SMP=y \ No newline at end of file