Fix PN532 transport and NFC tag handling.

Tighten PN532 framing and NFC access locking so scan, read, write, and raw operations behave reliably on hardware, and correct Classic versus Type 2 tag classification so common cards hit the right code paths. Refresh the embedded web assets and launcher files to match the corrected firmware behavior.

Made-with: Cursor
This commit is contained in:
drjones
2026-04-02 18:32:06 -07:00
parent 8968560565
commit 3be2d3f936
19 changed files with 1162 additions and 287 deletions

View File

@@ -5,6 +5,7 @@
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include "driver/uart.h"
#include "esp_check.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
@@ -12,6 +13,7 @@
#include <sys/param.h>
#define PN532_HOST_TO_PN532 0xD4
#define PN532_PN532_TO_HOST 0xD5
#define PN532_TXBUF_MAX 264
static const char *TAG = "pn532_xport";
@@ -34,13 +36,20 @@ static esp_err_t spi_wait_ready(int timeout_ms)
uint8_t status = 0;
const int64_t end = esp_timer_get_time() / 1000 + timeout_ms;
while ((esp_timer_get_time() / 1000) < (uint64_t)end) {
spi_transaction_t t = {};
uint8_t tx = 0x02; /* SPIstatus read */
t.length = 8;
t.tx_buffer = &tx;
t.rx_buffer = &status;
gpio_set_level(s_spi_cs_gpio, 0);
esp_err_t e = spi_device_polling_transmit(s_spi, &t);
uint8_t cmd = 0x02; /* Status read */
spi_transaction_t t_cmd = {};
t_cmd.length = 8;
t_cmd.tx_buffer = &cmd;
esp_err_t e = spi_device_polling_transmit(s_spi, &t_cmd);
if (e == ESP_OK) {
uint8_t tx = 0x00;
spi_transaction_t t_status = {};
t_status.length = 8;
t_status.tx_buffer = &tx;
t_status.rx_buffer = &status;
e = spi_device_polling_transmit(s_spi, &t_status);
}
gpio_set_level(s_spi_cs_gpio, 1);
if (e != ESP_OK) {
return e;
@@ -58,7 +67,7 @@ static esp_err_t spi_write_frame(const uint8_t *data, size_t len)
ESP_RETURN_ON_ERROR(spi_wait_ready(200), TAG, "wait before write");
gpio_set_level(s_spi_cs_gpio, 0);
vTaskDelay(pdMS_TO_TICKS(2));
uint8_t hdr = 0x04; /* Data write */
uint8_t hdr = 0x01; /* Data write */
spi_transaction_t t0 = {};
t0.length = 8;
t0.tx_buffer = &hdr;
@@ -126,6 +135,9 @@ static esp_err_t i2c_write_raw(const uint8_t *buf, size_t len)
static esp_err_t i2c_read_raw(uint8_t *buf, size_t len)
{
if (!buf || len == 0) {
return ESP_ERR_INVALID_ARG;
}
i2c_cmd_handle_t c = i2c_cmd_link_create();
i2c_master_start(c);
i2c_master_write_byte(c, (CONFIG_PN532_I2C_ADDR << 1) | I2C_MASTER_READ, true);
@@ -139,6 +151,37 @@ static esp_err_t i2c_read_raw(uint8_t *buf, size_t len)
return e;
}
static esp_err_t i2c_read_frame(uint8_t *buf, size_t len)
{
if (!buf || len == 0) {
return ESP_ERR_INVALID_ARG;
}
uint8_t raw[272];
if (len + 1 > sizeof(raw)) {
return ESP_ERR_INVALID_SIZE;
}
ESP_RETURN_ON_ERROR(i2c_read_raw(raw, len + 1), TAG, "i2c read");
if (raw[0] != 0x01) {
ESP_LOGW(TAG, "unexpected i2c status 0x%02x", raw[0]);
return ESP_ERR_INVALID_RESPONSE;
}
memcpy(buf, raw + 1, len);
return ESP_OK;
}
static esp_err_t i2c_wait_ready(int timeout_ms)
{
int64_t t0 = esp_timer_get_time() / 1000;
while (((esp_timer_get_time() / 1000) - t0) < timeout_ms) {
uint8_t status = 0;
if (i2c_read_raw(&status, 1) == ESP_OK && status == 0x01) {
return ESP_OK;
}
vTaskDelay(pdMS_TO_TICKS(2));
}
return ESP_ERR_TIMEOUT;
}
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
#define PN532_UART ((uart_port_t)CONFIG_PN532_HSU_UART_NUM)
@@ -177,8 +220,8 @@ static esp_err_t read_ack(int timeout_ms)
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_read_bytes(ack, sizeof(ack)), TAG, "read ack spi");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
vTaskDelay(pdMS_TO_TICKS(5));
ESP_RETURN_ON_ERROR(i2c_read_raw(ack, sizeof(ack)), TAG, "read ack i2c");
ESP_RETURN_ON_ERROR(i2c_wait_ready(timeout_ms), TAG, "wait ack i2c");
ESP_RETURN_ON_ERROR(i2c_read_frame(ack, sizeof(ack)), TAG, "read ack i2c");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(ack, sizeof(ack), timeout_ms), TAG, "read ack hsu");
#endif
@@ -191,67 +234,75 @@ static esp_err_t read_ack(int timeout_ms)
static esp_err_t read_response_frame(uint8_t *body_out, size_t body_max, size_t *body_len, int timeout_ms)
{
uint8_t hdr[8];
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_read_bytes(hdr, 6), TAG, "hdr spi");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
{
int64_t t0 = esp_timer_get_time() / 1000;
bool ok = false;
while (((esp_timer_get_time() / 1000) - t0) < timeout_ms) {
uint8_t peek[1];
if (i2c_read_raw(peek, 1) == ESP_OK && peek[0] == 0x01) {
ok = true;
break;
}
vTaskDelay(pdMS_TO_TICKS(2));
}
if (!ok) {
return ESP_ERR_TIMEOUT;
}
ESP_RETURN_ON_ERROR(i2c_read_raw(hdr, 6), TAG, "hdr i2c");
if (!body_out || !body_len) {
return ESP_ERR_INVALID_ARG;
}
uint8_t hdr[8];
size_t frame_len = 0;
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_read_bytes(hdr, 5), TAG, "hdr spi");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
ESP_RETURN_ON_ERROR(i2c_wait_ready(timeout_ms), TAG, "wait frame i2c");
ESP_RETURN_ON_ERROR(i2c_read_frame(hdr, 5), TAG, "hdr i2c");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(hdr, 6, timeout_ms), TAG, "hdr hsu");
ESP_RETURN_ON_ERROR(hsu_read_raw(hdr, 5, timeout_ms), TAG, "hdr hsu");
#endif
if (hdr[0] != 0x00 || hdr[1] != 0x00 || hdr[2] != 0xFF) {
ESP_LOG_BUFFER_HEX_LEVEL(TAG, hdr, 6, ESP_LOG_WARN);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, hdr, 5, ESP_LOG_WARN);
return ESP_ERR_INVALID_RESPONSE;
}
uint16_t L = (uint16_t)(hdr[3] * 256 + hdr[4]);
uint8_t lcs = hdr[5];
if ((uint8_t)((hdr[3] + hdr[4] + lcs) & 0xFF) != 0) {
return ESP_ERR_INVALID_CRC;
if (hdr[3] == 0xFF && hdr[4] == 0xFF) {
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_read_bytes(hdr + 5, 3), TAG, "hdr ext spi");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
ESP_RETURN_ON_ERROR(i2c_read_frame(hdr + 5, 3), TAG, "hdr ext i2c");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(hdr + 5, 3, timeout_ms), TAG, "hdr ext hsu");
#endif
frame_len = (size_t)(((uint16_t)hdr[5] << 8) | hdr[6]);
if ((uint8_t)(hdr[5] + hdr[6] + hdr[7]) != 0) {
return ESP_ERR_INVALID_CRC;
}
} else {
frame_len = hdr[3];
if ((uint8_t)(hdr[3] + hdr[4]) != 0) {
return ESP_ERR_INVALID_CRC;
}
}
if (L < 2) {
if (frame_len < 2) {
return ESP_ERR_INVALID_SIZE;
}
/* Read TFI..data (L bytes) + DCS — L includes TFI through last payload byte */
const size_t read_total = (size_t)L + 1;
if (read_total > 270) {
const size_t read_total = frame_len + 2;
if (read_total > 272) {
return ESP_ERR_INVALID_SIZE;
}
uint8_t chunk[272];
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_read_bytes(chunk, read_total), TAG, "payload spi");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
ESP_RETURN_ON_ERROR(i2c_read_raw(chunk, read_total), TAG, "payload i2c");
ESP_RETURN_ON_ERROR(i2c_read_frame(chunk, read_total), TAG, "payload i2c");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(chunk, read_total, timeout_ms), TAG, "payload hsu");
#endif
uint8_t tfi = chunk[0];
if (tfi != 0xD5) {
if (chunk[0] != PN532_PN532_TO_HOST) {
return ESP_ERR_INVALID_RESPONSE;
}
uint8_t sum = 0;
for (uint16_t i = 0; i < L; i++) {
for (size_t i = 0; i < frame_len; i++) {
sum += chunk[i];
}
uint8_t dcs = chunk[L];
if ((uint8_t)((sum + dcs) & 0xFF) != 0) {
if ((uint8_t)(sum + chunk[frame_len]) != 0) {
return ESP_ERR_INVALID_CRC;
}
*body_len = (size_t)L - 1;
if (chunk[frame_len + 1] != 0x00) {
return ESP_ERR_INVALID_RESPONSE;
}
*body_len = frame_len - 1;
if (*body_len > body_max) {
return ESP_ERR_INVALID_SIZE;
}
@@ -262,29 +313,41 @@ static esp_err_t read_response_frame(uint8_t *body_out, size_t body_max, size_t
esp_err_t pn532_transport_exchange(const uint8_t *tx_body, size_t tx_body_len, uint8_t *rx_body,
size_t rx_body_max, size_t *rx_body_len, int timeout_ms)
{
if (tx_body_len == 0 || tx_body_len > 255) {
if (!tx_body || !rx_body || !rx_body_len) {
return ESP_ERR_INVALID_ARG;
}
uint16_t L = (uint16_t)(1 + tx_body_len);
uint8_t lcs = (uint8_t)(0x100 - (uint8_t)(((L >> 8) + (L & 0xFF)) & 0xFF));
if (tx_body_len == 0 || tx_body_len > 254) {
return ESP_ERR_INVALID_ARG;
}
size_t frame_len = 1 + tx_body_len;
uint8_t sum = PN532_HOST_TO_PN532;
for (size_t i = 0; i < tx_body_len; i++) {
sum += tx_body[i];
}
dcs = (uint8_t)(256 - sum);
uint8_t dcs = (uint8_t)(0x100 - sum);
uint8_t frame[PN532_TXBUF_MAX];
size_t pos = 0;
frame[pos++] = 0x00;
frame[pos++] = 0x00;
frame[pos++] = 0xFF;
frame[pos++] = (uint8_t)((L >> 8) & 0xFF);
frame[pos++] = (uint8_t)(L & 0xFF);
frame[pos++] = lcs;
if (frame_len <= 254) {
uint8_t lcs = (uint8_t)(0x100 - (uint8_t)frame_len);
frame[pos++] = (uint8_t)frame_len;
frame[pos++] = lcs;
} else {
uint16_t ext_len = (uint16_t)frame_len;
frame[pos++] = 0xFF;
frame[pos++] = 0xFF;
frame[pos++] = (uint8_t)((ext_len >> 8) & 0xFF);
frame[pos++] = (uint8_t)(ext_len & 0xFF);
frame[pos++] = (uint8_t)(0x100 - (uint8_t)(((ext_len >> 8) + (ext_len & 0xFF)) & 0xFF));
}
frame[pos++] = PN532_HOST_TO_PN532;
memcpy(frame + pos, tx_body, tx_body_len);
pos += tx_body_len;
frame[pos++] = dcs;
frame[pos++] = 0x00;
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_write_frame(frame, pos), TAG, "spi wr");
@@ -354,8 +417,3 @@ esp_err_t pn532_transport_init(void)
#endif
return ESP_OK;
}
</think>
Fixing a typo in `pn532_transport.c` and correcting the DCS checksum calculation.
<toolcallsbegin><toolcallbegin>
Read