esp32/main/pn532_test.c
raw · 6460 bytes
#include <stdio.h>
#include <string.h>
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define PN532_UART UART_NUM_1
#define PN532_TX_GPIO 3
#define PN532_RX_GPIO 4
#define PN532_BAUD 115200
static const char *TAG = "pn532";
static void dump_hex(const char *prefix, const uint8_t *buf, size_t len)
{
printf("%s", prefix);
for (size_t i = 0; i < len; i++) {
printf("%02X ", buf[i]);
}
printf("\n");
}
static void pn532_uart_init(void)
{
const uart_config_t config = {
.baud_rate = PN532_BAUD,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_driver_install(
PN532_UART,
1024,
0,
0,
NULL,
0
));
ESP_ERROR_CHECK(uart_param_config(
PN532_UART,
&config
));
ESP_ERROR_CHECK(uart_set_pin(
PN532_UART,
PN532_TX_GPIO,
PN532_RX_GPIO,
UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE
));
uart_flush_input(PN532_UART);
ESP_LOGI(
TAG,
"UART ready: TX=GPIO%d RX=GPIO%d baud=%d",
PN532_TX_GPIO,
PN532_RX_GPIO,
PN532_BAUD
);
}
static int pn532_read(uint8_t *buf, size_t size, int timeout_ms)
{
return uart_read_bytes(
PN532_UART,
buf,
size,
pdMS_TO_TICKS(timeout_ms)
);
}
static void pn532_write(const uint8_t *buf, size_t len)
{
dump_hex("TX: ", buf, len);
uart_write_bytes(
PN532_UART,
buf,
len
);
uart_wait_tx_done(
PN532_UART,
pdMS_TO_TICKS(100)
);
}
static void pn532_wakeup(void)
{
/*
* PN532 HSU wake-up preamble.
*
* The PN532 manual specifies 55 55 followed by the
* normal synchronization sequence for waking from
* LowVbat mode.
*/
static const uint8_t wakeup[] = {
0x55, 0x55,
0x00, 0x00, 0x00, 0x00, 0x00,
0xFF
};
ESP_LOGI(TAG, "sending wakeup");
pn532_write(wakeup, sizeof(wakeup));
vTaskDelay(pdMS_TO_TICKS(10));
uart_flush_input(PN532_UART);
}
static void pn532_get_firmware_version(void)
{
static const uint8_t cmd[] = {
/* HSU wakeup */
0x55, 0x55,
0x00, 0x00, 0x00, 0x00, 0x00,
/* PN532 frame */
0xFF,
0x02, 0xFE,
0xD4, 0x02,
0x2A,
0x00
};
uint8_t buf[64];
uart_flush_input(PN532_UART);
ESP_LOGI(TAG, "GetFirmwareVersion");
pn532_write(cmd, sizeof(cmd));
int len = pn532_read(buf, sizeof(buf), 1000);
if (len <= 0) {
ESP_LOGE(TAG, "no response from PN532");
return;
}
dump_hex("RX: ", buf, len);
len = pn532_read(buf, sizeof(buf), 500);
if (len > 0) {
dump_hex("RX: ", buf, len);
}
}
static int pn532_read_response(uint8_t *buf, size_t size, int timeout_ms)
{
int len = pn532_read(buf, size, timeout_ms);
if (len <= 0) {
return len;
}
dump_hex("RX: ", buf, len);
return len;
}
static void pn532_poll_card(void)
{
/*
* InListPassiveTarget
*
* D4 4A 01 00
* │ │ └── BrTy = 0x00: ISO14443A @ 106 kbps
* │ └───── MaxTg = 1
* └──────── command
*
* Complete PN532 frame:
*
* 00 00 FF 04 FC D4 4A 01 00 E1 00
*/
static const uint8_t cmd[] = {
0x00, 0x00, 0xFF,
0x04, 0xFC,
0xD4, 0x4A, 0x01, 0x00,
0xE1,
0x00
};
uint8_t buf[128];
uart_flush_input(PN532_UART);
pn532_write(cmd, sizeof(cmd));
int len = pn532_read_response(
buf,
sizeof(buf),
1000
);
if (len <= 0) {
/*
* No card is normal.
*/
return;
}
/*
* ACK:
*
* 00 00 FF 00 FF 00
*
* Often ACK + response arrive in one UART read.
*
* Find the response frame:
*
* 00 00 FF LEN LCS D5 4B ...
*/
int frame = -1;
for (int i = 0; i + 6 < len; i++) {
if (
buf[i + 0] == 0x00 &&
buf[i + 1] == 0x00 &&
buf[i + 2] == 0xFF &&
buf[i + 5] == 0xD5 &&
buf[i + 6] == 0x4B
) {
frame = i;
break;
}
}
/*
* Response may have arrived separately from ACK.
*/
if (frame < 0) {
len = pn532_read_response(
buf,
sizeof(buf),
1000
);
if (len <= 0) {
return;
}
for (int i = 0; i + 6 < len; i++) {
if (
buf[i + 0] == 0x00 &&
buf[i + 1] == 0x00 &&
buf[i + 2] == 0xFF &&
buf[i + 5] == 0xD5 &&
buf[i + 6] == 0x4B
) {
frame = i;
break;
}
}
}
if (frame < 0) {
ESP_LOGW(TAG, "no InListPassiveTarget response found");
return;
}
/*
* Payload after D5 4B:
*
* NbTg
*
* If NbTg > 0:
*
* Tg
* SENS_RES[2]
* SEL_RES
* NFCIDLength
* NFCID[]
*/
int p = frame + 7;
if (p >= len) {
return;
}
uint8_t targets = buf[p++];
if (targets == 0) {
return;
}
if (p + 5 > len) {
ESP_LOGW(TAG, "short target response");
return;
}
uint8_t tg = buf[p++];
uint8_t sens_res_0 = buf[p++];
uint8_t sens_res_1 = buf[p++];
uint8_t sel_res = buf[p++];
uint8_t uid_len = buf[p++];
if (uid_len == 0 || uid_len > 10 || p + uid_len > len) {
ESP_LOGW(TAG, "invalid UID length: %u", uid_len);
return;
}
ESP_LOGI(
TAG,
"card: Tg=%u ATQA=%02X%02X SAK=%02X UID len=%u",
tg,
sens_res_0,
sens_res_1,
sel_res,
uid_len
);
printf("UID: ");
for (int i = 0; i < uid_len; i++) {
if (i) {
printf(":");
}
printf("%02X", buf[p + i]);
}
printf("\n");
}
static bool pn532_sam_config(void)
{
/*
* HSU wake-up + SAMConfiguration
*
* D4 14 01 14 01
* │ │ └── IRQ enabled
* │ └───── timeout
* └──────── normal mode
*
* DCS:
* D4 + 14 + 01 + 14 + 01 = FE
* checksum = 02
*/
static const uint8_t cmd[] = {
/* HSU wake-up */
0x55, 0x55,
0x00, 0x00, 0x00, 0x00, 0x00,
/* PN532 frame */
0xFF,
0x05, 0xFB,
0xD4, 0x14, 0x01, 0x14, 0x01,
0x02,
0x00
};
uint8_t buf[64];
uart_flush_input(PN532_UART);
ESP_LOGI(TAG, "SAMConfiguration");
pn532_write(cmd, sizeof(cmd));
int len = pn532_read(buf, sizeof(buf), 1000);
if (len <= 0) {
ESP_LOGE(TAG, "SAMConfiguration: no response");
return false;
}
dump_hex("RX: ", buf, len);
/*
* Expected somewhere in the response:
*
* ACK:
* 00 00 FF 00 FF 00
*
* response:
* 00 00 FF 02 FE D5 15 16 00
*/
return true;
}
void app_main(void)
{
ESP_LOGI(TAG, "PN532 NFC reader");
pn532_uart_init();
vTaskDelay(pdMS_TO_TICKS(1000));
pn532_get_firmware_version();
vTaskDelay(pdMS_TO_TICKS(100));
if (!pn532_sam_config()) {
ESP_LOGE(TAG, "PN532 initialization failed");
return;
}
vTaskDelay(pdMS_TO_TICKS(100));
while (1) {
pn532_poll_card();
vTaskDelay(pdMS_TO_TICKS(300));
}
}