first commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"nfc_manager.c"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
REQUIRES
|
||||
driver
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file nfc_manager.h
|
||||
* @brief NFC Manager for ROM selection via NFC tags
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NFC_MAX_ROM_NAME 64
|
||||
|
||||
/**
|
||||
* @brief NFC ROM mapping structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t tag_uid[7]; // NFC tag UID
|
||||
char rom_path[NFC_MAX_ROM_NAME]; // ROM file path on SD card
|
||||
} nfc_rom_mapping_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize NFC manager
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t nfc_manager_init(void);
|
||||
|
||||
/**
|
||||
* @brief Check if NFC tag is present
|
||||
* @return true if tag detected
|
||||
*/
|
||||
bool nfc_manager_tag_present(void);
|
||||
|
||||
/**
|
||||
* @brief Read NFC tag and get ROM path
|
||||
* @param rom_path Output buffer for ROM path
|
||||
* @param max_len Maximum length of rom_path buffer
|
||||
* @return ESP_OK if ROM mapping found
|
||||
*/
|
||||
esp_err_t nfc_manager_get_rom(char *rom_path, size_t max_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,428 @@
|
||||
/**
|
||||
* @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 <string.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user