Files
nfc-pn532-warlord/firmware/components/pn532_host/pn532_transport.c
drjones 8968560565 Add PN532 toolkit firmware, web UI, and embedded SPIFFS assets
Includes ESP-IDF NFC stack (deep capture, 4K Classic geometry, UL write API,
open SoftAP), React dashboard with live tag diagnostics, and docs. README
updated for APIs and lab Wi-Fi defaults.

Made-with: Cursor
2026-03-29 09:32:55 -07:00

361 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pn532_host/pn532_transport.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <string.h>
#include <sys/param.h>
#define PN532_HOST_TO_PN532 0xD4
#define PN532_TXBUF_MAX 264
static const char *TAG = "pn532_xport";
#if defined(CONFIG_PN532_TRANSPORT_SPI)
static spi_host_device_t pn532_spi_host(void)
{
return CONFIG_PN532_SPI_HOST == 3 ? SPI3_HOST : SPI2_HOST;
}
#endif
static SemaphoreHandle_t s_bus_mutex;
#if defined(CONFIG_PN532_TRANSPORT_SPI)
static spi_device_handle_t s_spi;
static int s_spi_cs_gpio = CONFIG_PN532_SPI_CS_GPIO;
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);
gpio_set_level(s_spi_cs_gpio, 1);
if (e != ESP_OK) {
return e;
}
if (status & 0x01) {
return ESP_OK;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
return ESP_ERR_TIMEOUT;
}
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 */
spi_transaction_t t0 = {};
t0.length = 8;
t0.tx_buffer = &hdr;
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(s_spi, &t0), TAG, "hdr");
for (size_t i = 0; i < len; i++) {
spi_transaction_t t = {};
t.length = 8;
t.tx_buffer = &data[i];
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(s_spi, &t), TAG, "w");
}
gpio_set_level(s_spi_cs_gpio, 1);
return ESP_OK;
}
static esp_err_t spi_read_bytes(uint8_t *out, size_t len)
{
ESP_RETURN_ON_ERROR(spi_wait_ready(300), TAG, "wait read");
gpio_set_level(s_spi_cs_gpio, 0);
vTaskDelay(pdMS_TO_TICKS(2));
uint8_t hdr = 0x03; /* Data read */
spi_transaction_t t0 = {};
t0.length = 8;
t0.tx_buffer = &hdr;
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(s_spi, &t0), TAG, "rhdr");
for (size_t i = 0; i < len; i++) {
spi_transaction_t t = {};
uint8_t tx = 0xFF;
t.length = 8;
t.tx_buffer = &tx;
t.rx_buffer = &out[i];
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(s_spi, &t), TAG, "r");
}
gpio_set_level(s_spi_cs_gpio, 1);
return ESP_OK;
}
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
#define PN532_I2C_PORT ((i2c_port_t)CONFIG_PN532_I2C_PORT)
static esp_err_t i2c_wakeup(void)
{
const uint8_t w[] = {0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
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_WRITE, true);
i2c_master_write(c, w, sizeof(w), I2C_MASTER_LAST_NACK);
i2c_master_stop(c);
esp_err_t e = i2c_master_cmd_begin(PN532_I2C_PORT, c, pdMS_TO_TICKS(50));
i2c_cmd_link_delete(c);
vTaskDelay(pdMS_TO_TICKS(10));
return e;
}
static esp_err_t i2c_write_raw(const uint8_t *buf, size_t len)
{
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_WRITE, true);
i2c_master_write(c, buf, len, I2C_MASTER_LAST_NACK);
i2c_master_stop(c);
esp_err_t e = i2c_master_cmd_begin(PN532_I2C_PORT, c, pdMS_TO_TICKS(200));
i2c_cmd_link_delete(c);
return e;
}
static esp_err_t i2c_read_raw(uint8_t *buf, size_t len)
{
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);
if (len > 1) {
i2c_master_read(c, buf, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(c, buf + len - 1, I2C_MASTER_NACK);
i2c_master_stop(c);
esp_err_t e = i2c_master_cmd_begin(PN532_I2C_PORT, c, pdMS_TO_TICKS(200));
i2c_cmd_link_delete(c);
return e;
}
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
#define PN532_UART ((uart_port_t)CONFIG_PN532_HSU_UART_NUM)
static esp_err_t hsu_write_raw(const uint8_t *buf, size_t len)
{
int n = uart_write_bytes(PN532_UART, buf, len);
return (n == (int)len) ? ESP_OK : ESP_FAIL;
}
static esp_err_t hsu_read_raw(uint8_t *buf, size_t len, int timeout_ms)
{
size_t got = 0;
int64_t start = esp_timer_get_time();
while (got < len) {
int n = uart_read_bytes(PN532_UART, buf + got, len - got,
pdMS_TO_TICKS(MAX(1, timeout_ms)));
if (n > 0) {
got += (size_t)n;
}
if ((esp_timer_get_time() - start) / 1000 > timeout_ms) {
break;
}
}
return got == len ? ESP_OK : ESP_ERR_TIMEOUT;
}
#endif
void pn532_transport_lock(void) { xSemaphoreTake(s_bus_mutex, portMAX_DELAY); }
void pn532_transport_unlock(void) { xSemaphoreGive(s_bus_mutex); }
static esp_err_t read_ack(int timeout_ms)
{
const uint8_t ack_ok[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};
uint8_t ack[6];
#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");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(ack, sizeof(ack), timeout_ms), TAG, "read ack hsu");
#endif
if (memcmp(ack, ack_ok, 6) != 0) {
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ack, 6, ESP_LOG_WARN);
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
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");
}
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_read_raw(hdr, 6, 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);
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 (L < 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) {
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");
#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) {
return ESP_ERR_INVALID_RESPONSE;
}
uint8_t sum = 0;
for (uint16_t i = 0; i < L; i++) {
sum += chunk[i];
}
uint8_t dcs = chunk[L];
if ((uint8_t)((sum + dcs) & 0xFF) != 0) {
return ESP_ERR_INVALID_CRC;
}
*body_len = (size_t)L - 1;
if (*body_len > body_max) {
return ESP_ERR_INVALID_SIZE;
}
memcpy(body_out, chunk + 1, *body_len);
return ESP_OK;
}
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) {
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));
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 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;
frame[pos++] = PN532_HOST_TO_PN532;
memcpy(frame + pos, tx_body, tx_body_len);
pos += tx_body_len;
frame[pos++] = dcs;
#if defined(CONFIG_PN532_TRANSPORT_SPI)
ESP_RETURN_ON_ERROR(spi_write_frame(frame, pos), TAG, "spi wr");
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
i2c_wakeup();
ESP_RETURN_ON_ERROR(i2c_write_raw(frame, pos), TAG, "i2c wr");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
ESP_RETURN_ON_ERROR(hsu_write_raw(frame, pos), TAG, "hsu wr");
#endif
ESP_RETURN_ON_ERROR(read_ack(timeout_ms), TAG, "ack");
return read_response_frame(rx_body, rx_body_max, rx_body_len, timeout_ms);
}
esp_err_t pn532_transport_init(void)
{
s_bus_mutex = xSemaphoreCreateMutex();
if (!s_bus_mutex) {
return ESP_ERR_NO_MEM;
}
#if defined(CONFIG_PN532_TRANSPORT_SPI)
spi_bus_config_t buscfg = {
.mosi_io_num = CONFIG_PN532_SPI_MOSI_GPIO,
.miso_io_num = CONFIG_PN532_SPI_MISO_GPIO,
.sclk_io_num = CONFIG_PN532_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 512,
};
spi_host_device_t host = pn532_spi_host();
ESP_RETURN_ON_ERROR(spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO), TAG, "spi bus");
spi_device_interface_config_t devcfg = {
.clock_speed_hz = CONFIG_PN532_SPI_CLOCK_HZ,
.mode = 0,
.spics_io_num = -1, /* manual CS */
.queue_size = 4,
};
ESP_RETURN_ON_ERROR(spi_bus_add_device(host, &devcfg, &s_spi), TAG, "spi dev");
gpio_reset_pin((gpio_num_t)s_spi_cs_gpio);
gpio_set_direction((gpio_num_t)s_spi_cs_gpio, GPIO_MODE_OUTPUT);
gpio_set_level(s_spi_cs_gpio, 1);
#elif defined(CONFIG_PN532_TRANSPORT_I2C)
i2c_config_t ic = {
.mode = I2C_MODE_MASTER,
.sda_io_num = CONFIG_PN532_I2C_SDA_GPIO,
.scl_io_num = CONFIG_PN532_I2C_SCL_GPIO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {.clk_speed = 400000},
};
ESP_RETURN_ON_ERROR(i2c_param_config(PN532_I2C_PORT, &ic), TAG, "i2c cfg");
ESP_RETURN_ON_ERROR(i2c_driver_install(PN532_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0), TAG, "i2c drvr");
#elif defined(CONFIG_PN532_TRANSPORT_HSU)
uart_config_t uc = {
.baud_rate = CONFIG_PN532_HSU_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_RETURN_ON_ERROR(uart_driver_install(PN532_UART, 2048, 2048, 0, NULL, 0), TAG, "uart");
ESP_RETURN_ON_ERROR(uart_param_config(PN532_UART, &uc), TAG, "uart cfg");
ESP_RETURN_ON_ERROR(uart_set_pin(PN532_UART, CONFIG_PN532_HSU_TX_GPIO, CONFIG_PN532_HSU_RX_GPIO,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE),
TAG, "uart pins");
#endif
return ESP_OK;
}
</think>
Fixing a typo in `pn532_transport.c` and correcting the DCS checksum calculation.
<toolcallsbegin><toolcallbegin>
Read