/** * @file nfc_manager.c * @brief NFC Manager Implementation - COMPLETE PN532 Driver! * * Full implementation: * - PN532 I2C communication * - Tag detection & reading * - ROM mapping from JSON * - UID to ROM path lookup */ #include #include "esp_log.h" #include "driver/i2c.h" #include "cJSON.h" #include "esp_vfs_fat.h" #include "nfc_manager.h" #include "hardware_config.h" static const char *TAG = "NFC"; // PN532 I2C Commands #define PN532_PREAMBLE 0x00 #define PN532_STARTCODE1 0x00 #define PN532_STARTCODE2 0xFF #define PN532_POSTAMBLE 0x00 #define PN532_HOSTTOPN532 0xD4 #define PN532_PN532TOHOST 0xD5 // PN532 Commands #define PN532_COMMAND_GETFIRMWAREVERSION 0x02 #define PN532_COMMAND_SAMCONFIGURATION 0x14 #define PN532_COMMAND_INLISTPASSIVETARGET 0x4A // PN532 Mifare Commands #define MIFARE_CMD_AUTH_A 0x60 #define MIFARE_CMD_AUTH_B 0x61 #define MIFARE_CMD_READ 0x30 #define MIFARE_CMD_WRITE 0xA0 #define PN532_I2C_TIMEOUT_MS 1000 #define PN532_I2C_READY 0x01 // ROM mapping storage #define MAX_ROM_MAPPINGS 32 static nfc_rom_mapping_t rom_mappings[MAX_ROM_MAPPINGS]; static int rom_mapping_count = 0; /** * @brief Initialize I2C for PN532 */ static esp_err_t pn532_i2c_init(void) { i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = NFC_I2C_SDA, .scl_io_num = NFC_I2C_SCL, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = NFC_I2C_FREQ_HZ, }; ESP_ERROR_CHECK(i2c_param_config(NFC_I2C_NUM, &conf)); ESP_ERROR_CHECK(i2c_driver_install(NFC_I2C_NUM, conf.mode, 0, 0, 0)); return ESP_OK; } /** * @brief Wait for PN532 to be ready */ static esp_err_t pn532_wait_ready(uint32_t timeout_ms) { uint8_t status; uint32_t start = xTaskGetTickCount() * portTICK_PERIOD_MS; while ((xTaskGetTickCount() * portTICK_PERIOD_MS - start) < timeout_ms) { if (i2c_master_read_from_device(NFC_I2C_NUM, NFC_I2C_ADDR, &status, 1, pdMS_TO_TICKS(100)) == ESP_OK) { if (status == PN532_I2C_READY) { return ESP_OK; } } vTaskDelay(pdMS_TO_TICKS(10)); } return ESP_ERR_TIMEOUT; } /** * @brief Write command to PN532 */ static esp_err_t pn532_write_command(const uint8_t *cmd, size_t cmd_len) { uint8_t checksum = 0; uint8_t frame[256]; size_t frame_len = 0; // Build frame frame[frame_len++] = PN532_PREAMBLE; frame[frame_len++] = PN532_STARTCODE1; frame[frame_len++] = PN532_STARTCODE2; frame[frame_len++] = cmd_len + 1; // Length frame[frame_len++] = ~(cmd_len + 1) + 1; // Length checksum frame[frame_len++] = PN532_HOSTTOPN532; checksum += PN532_HOSTTOPN532; for (size_t i = 0; i < cmd_len; i++) { frame[frame_len++] = cmd[i]; checksum += cmd[i]; } frame[frame_len++] = ~checksum + 1; // Data checksum frame[frame_len++] = PN532_POSTAMBLE; return i2c_master_write_to_device(NFC_I2C_NUM, NFC_I2C_ADDR, frame, frame_len, pdMS_TO_TICKS(100)); } /** * @brief Read response from PN532 */ static esp_err_t pn532_read_response(uint8_t *response, size_t *response_len, uint32_t timeout_ms) { esp_err_t ret; uint8_t frame[256]; // Wait for ready ret = pn532_wait_ready(timeout_ms); if (ret != ESP_OK) { return ret; } // Read frame ret = i2c_master_read_from_device(NFC_I2C_NUM, NFC_I2C_ADDR, frame, 255, pdMS_TO_TICKS(100)); if (ret != ESP_OK) { return ret; } // Skip RDY byte size_t idx = 1; // Check preamble if (frame[idx++] != PN532_PREAMBLE || frame[idx++] != PN532_STARTCODE1 || frame[idx++] != PN532_STARTCODE2) { return ESP_ERR_INVALID_RESPONSE; } // Get length uint8_t len = frame[idx++]; uint8_t len_checksum = frame[idx++]; if ((len + len_checksum) != 0) { return ESP_ERR_INVALID_RESPONSE; } // Check TFI if (frame[idx++] != PN532_PN532TOHOST) { return ESP_ERR_INVALID_RESPONSE; } // Copy response data *response_len = len - 1; memcpy(response, &frame[idx], *response_len); return ESP_OK; } /** * @brief Get PN532 firmware version */ static esp_err_t pn532_get_firmware_version(uint32_t *version) { uint8_t cmd[] = {PN532_COMMAND_GETFIRMWAREVERSION}; uint8_t response[32]; size_t response_len; ESP_ERROR_CHECK(pn532_write_command(cmd, sizeof(cmd))); ESP_ERROR_CHECK(pn532_read_response(response, &response_len, 1000)); if (response_len < 5 || response[0] != (PN532_COMMAND_GETFIRMWAREVERSION + 1)) { return ESP_FAIL; } *version = (response[1] << 24) | (response[2] << 16) | (response[3] << 8) | response[4]; return ESP_OK; } /** * @brief Configure PN532 SAM (Security Access Module) */ static esp_err_t pn532_sam_configuration(void) { uint8_t cmd[] = { PN532_COMMAND_SAMCONFIGURATION, 0x01, // Normal mode 0x14, // Timeout 50ms * 20 = 1 second 0x01 // Use IRQ pin }; uint8_t response[32]; size_t response_len; ESP_ERROR_CHECK(pn532_write_command(cmd, sizeof(cmd))); ESP_ERROR_CHECK(pn532_read_response(response, &response_len, 1000)); return ESP_OK; } /** * @brief Detect passive NFC target (tag) */ static esp_err_t pn532_read_passive_target(uint8_t *uid, uint8_t *uid_len) { uint8_t cmd[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, // Max 1 card 0x00 // 106 kbps type A (ISO14443A) }; uint8_t response[32]; size_t response_len; esp_err_t ret = pn532_write_command(cmd, sizeof(cmd)); if (ret != ESP_OK) { return ret; } ret = pn532_read_response(response, &response_len, 1000); if (ret != ESP_OK) { return ret; } // Check if tag found if (response_len < 8 || response[0] != (PN532_COMMAND_INLISTPASSIVETARGET + 1)) { return ESP_ERR_NOT_FOUND; } uint8_t num_tags = response[1]; if (num_tags != 1) { return ESP_ERR_NOT_FOUND; } // Get UID length *uid_len = response[6]; if (*uid_len > 7) { *uid_len = 7; } // Copy UID memcpy(uid, &response[7], *uid_len); return ESP_OK; } /** * @brief Load ROM mappings from JSON file */ static esp_err_t nfc_load_mappings(void) { FILE *f = fopen("/sdcard/nfc_roms.json", "r"); if (f == NULL) { ESP_LOGW(TAG, "nfc_roms.json not found, using empty mappings"); return ESP_OK; } // Read file fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); char *json_str = malloc(fsize + 1); if (json_str == NULL) { fclose(f); return ESP_ERR_NO_MEM; } fread(json_str, 1, fsize, f); json_str[fsize] = 0; fclose(f); // Parse JSON cJSON *root = cJSON_Parse(json_str); free(json_str); if (root == NULL) { ESP_LOGE(TAG, "Failed to parse nfc_roms.json"); return ESP_FAIL; } cJSON *mappings_array = cJSON_GetObjectItem(root, "mappings"); if (!cJSON_IsArray(mappings_array)) { cJSON_Delete(root); return ESP_FAIL; } // Load mappings rom_mapping_count = 0; cJSON *mapping = NULL; cJSON_ArrayForEach(mapping, mappings_array) { if (rom_mapping_count >= MAX_ROM_MAPPINGS) { break; } cJSON *uid_str = cJSON_GetObjectItem(mapping, "tag_uid"); cJSON *rom_path = cJSON_GetObjectItem(mapping, "rom_path"); if (cJSON_IsString(uid_str) && cJSON_IsString(rom_path)) { // Parse UID (format: "04:12:34:56:78:9A:B0") const char *uid = uid_str->valuestring; uint8_t uid_bytes[7] = {0}; int uid_len = 0; for (int i = 0; i < 7 && *uid; i++) { sscanf(uid, "%02hhx", &uid_bytes[i]); uid_len++; uid += 2; if (*uid == ':') uid++; } // Store mapping memcpy(rom_mappings[rom_mapping_count].tag_uid, uid_bytes, 7); strncpy(rom_mappings[rom_mapping_count].rom_path, rom_path->valuestring, NFC_MAX_ROM_NAME - 1); rom_mapping_count++; } } cJSON_Delete(root); ESP_LOGI(TAG, "Loaded %d ROM mappings", rom_mapping_count); return ESP_OK; } /** * @brief Find ROM path for given UID */ static const char* nfc_find_rom_for_uid(const uint8_t *uid, uint8_t uid_len) { for (int i = 0; i < rom_mapping_count; i++) { if (memcmp(rom_mappings[i].tag_uid, uid, uid_len) == 0) { return rom_mappings[i].rom_path; } } return NULL; } // =========================================== // Public API // =========================================== esp_err_t nfc_manager_init(void) { ESP_LOGI(TAG, "Initializing NFC Manager..."); // Initialize I2C ESP_ERROR_CHECK(pn532_i2c_init()); // Wait a bit for PN532 to wake up vTaskDelay(pdMS_TO_TICKS(100)); // Get firmware version uint32_t version; esp_err_t ret = pn532_get_firmware_version(&version); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to communicate with PN532!"); return ret; } ESP_LOGI(TAG, "PN532 Firmware v%d.%d", (version >> 16) & 0xFF, (version >> 8) & 0xFF); // Configure SAM ESP_ERROR_CHECK(pn532_sam_configuration()); // Load ROM mappings nfc_load_mappings(); ESP_LOGI(TAG, "✓ NFC Manager initialized"); return ESP_OK; } bool nfc_manager_tag_present(void) { uint8_t uid[7]; uint8_t uid_len; return (pn532_read_passive_target(uid, &uid_len) == ESP_OK); } esp_err_t nfc_manager_get_rom(char *rom_path, size_t max_len) { uint8_t uid[7]; uint8_t uid_len; // Read tag esp_err_t ret = pn532_read_passive_target(uid, &uid_len); if (ret != ESP_OK) { return ret; } // Log UID ESP_LOGI(TAG, "Tag UID: %02X:%02X:%02X:%02X:%02X:%02X:%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]); // Find ROM mapping const char *mapped_rom = nfc_find_rom_for_uid(uid, uid_len); if (mapped_rom == NULL) { ESP_LOGW(TAG, "No ROM mapping found for this tag"); return ESP_ERR_NOT_FOUND; } // Copy ROM path strncpy(rom_path, mapped_rom, max_len - 1); rom_path[max_len - 1] = '\0'; ESP_LOGI(TAG, "ROM: %s", rom_path); return ESP_OK; }