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
This commit is contained in:
7
firmware/components/pn532_host/CMakeLists.txt
Normal file
7
firmware/components/pn532_host/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"pn532_transport.c"
|
||||
"pn532_core.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver esp_timer freertos
|
||||
)
|
||||
84
firmware/components/pn532_host/Kconfig
Normal file
84
firmware/components/pn532_host/Kconfig
Normal file
@@ -0,0 +1,84 @@
|
||||
menu "PN532 Host"
|
||||
|
||||
choice PN532_TRANSPORT
|
||||
prompt "PN532 bus"
|
||||
default PN532_TRANSPORT_SPI
|
||||
config PN532_TRANSPORT_SPI
|
||||
bool "SPI"
|
||||
config PN532_TRANSPORT_I2C
|
||||
bool "I2C"
|
||||
config PN532_TRANSPORT_HSU
|
||||
bool "UART (HSU)"
|
||||
endchoice
|
||||
|
||||
config PN532_SPI_HOST
|
||||
int "SPI host (2=SPI2, 3=SPI3)"
|
||||
default 2
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_SPI_MOSI_GPIO
|
||||
int "SPI MOSI GPIO"
|
||||
default 11
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_SPI_MISO_GPIO
|
||||
int "SPI MISO GPIO"
|
||||
default 13
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_SPI_SCLK_GPIO
|
||||
int "SPI SCLK GPIO"
|
||||
default 12
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_SPI_CS_GPIO
|
||||
int "SPI CS GPIO"
|
||||
default 10
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_SPI_CLOCK_HZ
|
||||
int "SPI clock Hz"
|
||||
default 100000
|
||||
depends on PN532_TRANSPORT_SPI
|
||||
|
||||
config PN532_I2C_PORT
|
||||
int "I2C port"
|
||||
default 0
|
||||
depends on PN532_TRANSPORT_I2C
|
||||
|
||||
config PN532_I2C_SDA_GPIO
|
||||
int "I2C SDA GPIO"
|
||||
default 8
|
||||
depends on PN532_TRANSPORT_I2C
|
||||
|
||||
config PN532_I2C_SCL_GPIO
|
||||
int "I2C SCL GPIO"
|
||||
default 9
|
||||
depends on PN532_TRANSPORT_I2C
|
||||
|
||||
config PN532_I2C_ADDR
|
||||
hex "PN532 I2C 7-bit address"
|
||||
default 0x24
|
||||
depends on PN532_TRANSPORT_I2C
|
||||
|
||||
config PN532_HSU_UART_NUM
|
||||
int "UART num for HSU"
|
||||
default 1
|
||||
depends on PN532_TRANSPORT_HSU
|
||||
|
||||
config PN532_HSU_TX_GPIO
|
||||
int "UART TX GPIO"
|
||||
default 17
|
||||
depends on PN532_TRANSPORT_HSU
|
||||
|
||||
config PN532_HSU_RX_GPIO
|
||||
int "UART RX GPIO"
|
||||
default 18
|
||||
depends on PN532_TRANSPORT_HSU
|
||||
|
||||
config PN532_HSU_BAUD
|
||||
int "UART baud"
|
||||
default 115200
|
||||
depends on PN532_TRANSPORT_HSU
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PN532_HOST_TO_PN532 0xD4
|
||||
#define PN532_PN532_TO_HOST 0xD5
|
||||
|
||||
#define PN532_CMD_GETFIRMWAREVERSION 0x02
|
||||
#define PN532_CMD_GETGENERALSTATUS 0x04
|
||||
#define PN532_CMD_SAMCONFIGURATION 0x14
|
||||
#define PN532_CMD_INLISTPASSIVETARGET 0x4A
|
||||
#define PN532_CMD_INDATAEXCHANGE 0x40
|
||||
#define PN532_CMD_INCOMMUNICATETHRU 0x42
|
||||
#define PN532_CMD_RFCONFIGURATION 0x32
|
||||
#define PN532_CMD_TGINITASTARGET 0x8C
|
||||
#define PN532_CMD_TGGETDATA 0x86
|
||||
#define PN532_CMD_TGSETDATA 0x8E
|
||||
#define PN532_CMD_POWERDOWN 0x16
|
||||
|
||||
#define PN532_MIFARE_CMD_AUTH_A 0x60
|
||||
#define PN532_MIFARE_CMD_AUTH_B 0x61
|
||||
#define PN532_MIFARE_CMD_READ 0x30
|
||||
#define PN532_MIFARE_CMD_WRITE 0xA0
|
||||
#define PN532_MIFARE_CMD_TRANSFER 0xB0
|
||||
|
||||
#define PN532_EEPROM_MAX_CMD_PAYLOAD 254
|
||||
|
||||
esp_err_t pn532_core_init(void);
|
||||
esp_err_t pn532_sam_config_normal(void);
|
||||
esp_err_t pn532_get_firmware_version(uint8_t *ic_ver, uint8_t *fw_ver_hi, uint8_t *fw_ver_lo);
|
||||
esp_err_t pn532_get_general_status(uint8_t *buf, size_t buf_len, size_t *out_len);
|
||||
|
||||
/**
|
||||
* Send full command body after TFI: [cmd] [params...]
|
||||
* response_data is pn532 payload after response TFI (first byte often status 0x00 = OK).
|
||||
*/
|
||||
esp_err_t pn532_send_cmd(const uint8_t *cmd_and_data, size_t len,
|
||||
uint8_t *response, size_t response_max,
|
||||
size_t *response_len, int timeout_ms);
|
||||
|
||||
esp_err_t pn532_in_list_passive_target(uint8_t max_targets, uint8_t baud,
|
||||
uint8_t *response, size_t response_max,
|
||||
size_t *response_len);
|
||||
|
||||
esp_err_t pn532_in_data_exchange(const uint8_t *data, size_t data_len,
|
||||
uint8_t *response, size_t response_max,
|
||||
size_t *response_len);
|
||||
|
||||
esp_err_t pn532_in_communicate_thru(const uint8_t *data, size_t data_len,
|
||||
uint8_t *response, size_t response_max,
|
||||
size_t *response_len);
|
||||
|
||||
/** RF field on/off via RFConfiguration (0x32) item 0x01, RF field */
|
||||
esp_err_t pn532_rf_field(bool on);
|
||||
|
||||
/** Max passive activation / RF retries (0x32 item 0x05) — improves weak-coupling reads */
|
||||
esp_err_t pn532_rf_max_retries(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PN532_SPI,
|
||||
PN532_I2C,
|
||||
PN532_HSU,
|
||||
} pn532_bus_t;
|
||||
|
||||
esp_err_t pn532_transport_init(void);
|
||||
void pn532_transport_lock(void);
|
||||
void pn532_transport_unlock(void);
|
||||
|
||||
/** Raw PN532 frame body: TFI + payload (caller builds payload after TFI). */
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
114
firmware/components/pn532_host/pn532_core.c
Normal file
114
firmware/components/pn532_host/pn532_core.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "pn532_host/pn532_core.h"
|
||||
#include "pn532_host/pn532_transport.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "pn532_core";
|
||||
|
||||
esp_err_t pn532_send_cmd(const uint8_t *cmd_and_data, size_t len, uint8_t *response, size_t response_max,
|
||||
size_t *response_len, int timeout_ms)
|
||||
{
|
||||
if (!cmd_and_data || !response || !response_len) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
pn532_transport_lock();
|
||||
esp_err_t err =
|
||||
pn532_transport_exchange(cmd_and_data, len, response, response_max, response_len, timeout_ms);
|
||||
pn532_transport_unlock();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (*response_len < 1) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
if (response[0] != 0x00) {
|
||||
ESP_LOGW(TAG, "chip status 0x%02x", response[0]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pn532_core_init(void)
|
||||
{
|
||||
return pn532_transport_init();
|
||||
}
|
||||
|
||||
esp_err_t pn532_get_firmware_version(uint8_t *ic_ver, uint8_t *fw_ver_hi, uint8_t *fw_ver_lo)
|
||||
{
|
||||
uint8_t cmd = PN532_CMD_GETFIRMWAREVERSION;
|
||||
uint8_t resp[16];
|
||||
size_t rlen = 0;
|
||||
esp_err_t e = pn532_send_cmd(&cmd, 1, resp, sizeof(resp), &rlen, 200);
|
||||
if (e != ESP_OK) {
|
||||
return e;
|
||||
}
|
||||
if (rlen < 4) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
*ic_ver = resp[1];
|
||||
*fw_ver_hi = resp[2];
|
||||
*fw_ver_lo = resp[3];
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pn532_sam_config_normal(void)
|
||||
{
|
||||
uint8_t buf[] = {PN532_CMD_SAMCONFIGURATION, 0x01, 0x14, 0x01};
|
||||
uint8_t resp[8];
|
||||
size_t rlen = 0;
|
||||
return pn532_send_cmd(buf, sizeof(buf), resp, sizeof(resp), &rlen, 200);
|
||||
}
|
||||
|
||||
esp_err_t pn532_get_general_status(uint8_t *buf, size_t buf_len, size_t *out_len)
|
||||
{
|
||||
uint8_t cmd = PN532_CMD_GETGENERALSTATUS;
|
||||
return pn532_send_cmd(&cmd, 1, buf, buf_len, out_len, 200);
|
||||
}
|
||||
|
||||
esp_err_t pn532_rf_field(bool on)
|
||||
{
|
||||
uint8_t buf[] = {PN532_CMD_RFCONFIGURATION, 0x01, on ? 0x01 : 0x00};
|
||||
uint8_t resp[8];
|
||||
size_t rlen = 0;
|
||||
return pn532_send_cmd(buf, sizeof(buf), resp, sizeof(resp), &rlen, 200);
|
||||
}
|
||||
|
||||
esp_err_t pn532_rf_max_retries(void)
|
||||
{
|
||||
/* RFConfiguration 0x05 MaxRetries: ATR/PSL/PassiveActivation — high values = more sensitivity to marginal tags */
|
||||
uint8_t buf[] = {PN532_CMD_RFCONFIGURATION, 0x05, 0xFF, 0xFF, 0xFF};
|
||||
uint8_t resp[8];
|
||||
size_t rlen = 0;
|
||||
return pn532_send_cmd(buf, sizeof(buf), resp, sizeof(resp), &rlen, 200);
|
||||
}
|
||||
|
||||
esp_err_t pn532_in_list_passive_target(uint8_t max_targets, uint8_t baud, uint8_t *response,
|
||||
size_t response_max, size_t *response_len)
|
||||
{
|
||||
uint8_t buf[] = {PN532_CMD_INLISTPASSIVETARGET, max_targets, baud};
|
||||
return pn532_send_cmd(buf, sizeof(buf), response, response_max, response_len, 500);
|
||||
}
|
||||
|
||||
esp_err_t pn532_in_data_exchange(const uint8_t *data, size_t data_len, uint8_t *response,
|
||||
size_t response_max, size_t *response_len)
|
||||
{
|
||||
if (!data || data_len == 0 || data_len > PN532_EEPROM_MAX_CMD_PAYLOAD - 1) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t buf[PN532_EEPROM_MAX_CMD_PAYLOAD + 1];
|
||||
buf[0] = PN532_CMD_INDATAEXCHANGE;
|
||||
buf[1] = 0x01; /* logical target 1 */
|
||||
memcpy(buf + 2, data, data_len);
|
||||
return pn532_send_cmd(buf, 2 + data_len, response, response_max, response_len, 500);
|
||||
}
|
||||
|
||||
esp_err_t pn532_in_communicate_thru(const uint8_t *data, size_t data_len, uint8_t *response,
|
||||
size_t response_max, size_t *response_len)
|
||||
{
|
||||
if (!data || data_len > PN532_EEPROM_MAX_CMD_PAYLOAD - 1) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t buf[PN532_EEPROM_MAX_CMD_PAYLOAD + 1];
|
||||
buf[0] = PN532_CMD_INCOMMUNICATETHRU;
|
||||
memcpy(buf + 1, data, data_len);
|
||||
return pn532_send_cmd(buf, 1 + data_len, response, response_max, response_len, 500);
|
||||
}
|
||||
361
firmware/components/pn532_host/pn532_transport.c
Normal file
361
firmware/components/pn532_host/pn532_transport.c
Normal file
@@ -0,0 +1,361 @@
|
||||
#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.
|
||||
|
||||
<|tool▁calls▁begin|><|tool▁call▁begin|>
|
||||
Read
|
||||
Reference in New Issue
Block a user