18#include "driver/gpio.h"
19#include "driver/i2c_master.h"
21#include "freertos/FreeRTOS.h"
22#include "freertos/task.h"
31#define PN532_PREAMBLE (0x00U)
33#define PN532_STARTCODE1 (0x00U)
34#define PN532_STARTCODE2 (0xFFU)
35#define PN532_POSTAMBLE (0x00U)
36#define PN532_HOSTTOPN532 (0xD4U)
42#define PN532_FIRMWAREVERSION (0x02U)
43#define PN532_SAMCONFIGURATION (0x14U)
44#define PN532_INLISTPASSIVETARGET (0x4AU)
45#define PN532_INDATAEXCHANGE (0x40U)
46#define PN532_INRELEASE (0x52U)
52#define PN532_SPI_STATREAD (0x02U)
53#define PN532_SPI_DATAWRITE (0x01U)
54#define PN532_SPI_DATAREAD (0x03U)
55#define PN532_SPI_READY (0x01U)
56#define PN532_SPI_CLOCK_HZ (1000000U)
57#define PN532_SPI_MODE (0U)
58#define PN532_SPI_NO_PIN (-1)
59#define PN532_SPI_QUEUE_SIZE (1U)
60#define PN532_SPI_BITS (8U)
66#define PN532_I2C_TIMEOUT_MS (100)
68#define PN532_I2C_TX_MAX (PN532_MAX_APDU_LEN + 16U)
70#define PN532_I2C_RX_MAX (200U)
76#define PN532_CS_TOGGLE_DELAY_MS (2U)
77#define PN532_WAKEUP_DELAY_MS (1000U)
78#define PN532_SYNC_DELAY_MS (100U)
79#define PN532_POLL_INTERVAL_MS (10U)
80#define PN532_CMD_TIMEOUT_MS (1000U)
82#define PN532_APDU_TIMEOUT_MS (5000U)
83#define PN532_BYTE_DELAY_MS (1U)
89#define GPIO_LEVEL_LOW (0U)
90#define GPIO_LEVEL_HIGH (1U)
91#define GPIO_PIN_BITMASK_BASE (1ULL)
97#define PN532_WAKEUP_BYTE (0x55U)
103#define PN532_ACK_LEN (6U)
110#define PN532_FRAME_TFI_OVERHEAD (1U)
116#define PN532_FIRMWARE_CMD_LEN (1U)
117#define PN532_FIRMWARE_RESP_LEN (13U)
118#define PN532_FIRMWARE_HDR_LEN (7U)
119#define PN532_FW_IC_OFFSET (7U)
120#define PN532_FW_VER_OFFSET (8U)
121#define PN532_FW_REV_OFFSET (9U)
122#define PN532_FW_SUPPORT_OFFSET (10U)
128#define PN532_SAM_CMD_LEN (4U)
129#define PN532_SAM_RESP_LEN (9U)
130#define PN532_SAM_RESP_CODE_OFFSET (6U)
131#define PN532_SAM_RESP_CODE (0x15U)
132#define PN532_SAM_NORMAL_MODE (0x01U)
133#define PN532_SAM_TIMEOUT (0x14U)
134#define PN532_SAM_USE_IRQ (0x01U)
140#define PN532_PASSIVE_CMD_LEN (3U)
141#define PN532_PASSIVE_RESP_LEN (64U)
142#define PN532_PASSIVE_MAX_TARGETS (1U)
143#define PN532_PASSIVE_NUM_TARGETS_OFFSET (7U)
144#define PN532_PASSIVE_EXPECTED_TARGETS (1U)
145#define PN532_PASSIVE_UID_LEN_OFFSET (12U)
146#define PN532_PASSIVE_UID_DATA_OFFSET (13U)
147#define PN532_BYTE_SHIFT_BITS (8U)
153#define PN532_EXCHANGE_CMD_OVERHEAD (2U)
154#define PN532_EXCHANGE_TG (0x01U)
156#define PN532_EXCHANGE_FRAME_MAX (440U)
157#define PN532_EXCHANGE_LEN_OFFSET (3U)
158#define PN532_EXCHANGE_STATUS_OFFSET (7U)
159#define PN532_EXCHANGE_DATA_OFFSET (8U)
160#define PN532_EXCHANGE_LEN_BIAS (3U)
161#define PN532_EXCHANGE_STATUS_OK (0x00U)
165#define PN532_FRAME_HDR_LEN (5U)
166#define PN532_FRAME_TAIL_LEN (2U)
172#define PN532_EXT_FRAME_INDICATOR (0xFFU)
173#define PN532_EXT_FRAME_HDR_LEN (8U)
174#define PN532_EXT_FRAME_LENHI_OFFSET (5U)
175#define PN532_EXT_FRAME_LENLO_OFFSET (6U)
176#define PN532_EXT_EXCHANGE_ERR_OFFSET (10U)
177#define PN532_EXT_EXCHANGE_DATA_OFFSET (11U)
184#define PN532_INRELEASE_CMD_LEN (2U)
185#define PN532_INRELEASE_RESP_LEN (10U)
193 0x00U, 0x00U, 0xFFU, 0x00U, 0xFFU, 0x00U
198 0x00U, 0x00U, 0xFFU, 0x06U, 0xFAU, 0xD5U, 0x03U
214 (void)memset(&t, 0,
sizeof(t));
217 (void)spi_device_transmit(dev->
spi, &t);
225 (void)memset(&t, 0,
sizeof(t));
230 (void)spi_device_transmit(dev->
spi, &t);
271 uint16_t to_read = (uint16_t)n + 1U;
272 if (to_read >
sizeof(tmp)) {
273 to_read = (uint16_t)
sizeof(tmp);
276 size_t payload_len = (size_t)to_read - 1U;
277 (void)memcpy(buff, &tmp[1], payload_len);
283 for (i = 0U; i < n; i++) {
295 (void)memset(ackbuff, 0,
sizeof(ackbuff));
306 for (i = 0U; i < len; i++) {
307 if (lhs[i] != rhs[i]) {
329 frame[pos] = frame_len; pos++;
330 frame[pos] = (uint8_t)((uint8_t)(~frame_len) + 1U); pos++;
334 for (i = 0U; i < cmd_len; i++) {
335 frame[pos] = cmd[i]; pos++;
336 checksum = (uint8_t)(checksum + cmd[i]);
338 frame[pos] = (uint8_t)(~checksum); pos++;
357 for (i = 0U; i < cmd_len; i++) {
359 checksum = (uint8_t)(checksum + cmd[i]);
368 uint8_t cmd_len, uint16_t timeout)
371 bool timed_out =
false;
380 if (timer > timeout) {
390 ESP_LOGE(
PN532_LOG_TAG,
"cmd=0x%02X: TIMEOUT waiting for ACK", (
unsigned)cmd[0]);
402 if (timer > timeout) {
412 ESP_LOGE(
PN532_LOG_TAG,
"cmd=0x%02X: TIMEOUT waiting for response", (
unsigned)cmd[0]);
416 ESP_LOGE(
PN532_LOG_TAG,
"cmd=0x%02X: ACK mismatch", (
unsigned)cmd[0]);
430 uint16_t body_len = 0U;
431 uint16_t total_len = 0U;
432 bool is_extended =
false;
439 uint16_t to_read = max_len + 1U;
440 if (to_read > (uint16_t)
sizeof(tmp)) {
441 to_read = (uint16_t)
sizeof(tmp);
443 (void)i2c_master_receive(dev->
i2c_dev, tmp, (
size_t)to_read,
445 size_t payload_len = (size_t)to_read - (
size_t)1U;
446 (void)memcpy(buff, &tmp[1], payload_len);
462 if (total_len > max_len) {
498 if (total_len > max_len) {
503 while (i < total_len) {
521 gpio_config_t io_conf;
522 spi_bus_config_t buscfg;
523 spi_device_interface_config_t devcfg;
526 (void)memset(&io_conf, 0,
sizeof(io_conf));
528 io_conf.mode = GPIO_MODE_OUTPUT;
529 io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
530 io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
531 io_conf.intr_type = GPIO_INTR_DISABLE;
532 (void)gpio_config(&io_conf);
539 (void)memset(&buscfg, 0,
sizeof(buscfg));
540 buscfg.mosi_io_num = config->
pin_mosi;
541 buscfg.miso_io_num = config->
pin_miso;
542 buscfg.sclk_io_num = config->
pin_sclk;
545 buscfg.max_transfer_sz = 0;
546 ret = spi_bus_initialize(config->
spi_host, &buscfg, SPI_DMA_CH_AUTO);
550 (void)memset(&devcfg, 0,
sizeof(devcfg));
555 devcfg.flags = SPI_DEVICE_BIT_LSBFIRST;
556 ret = spi_bus_add_device(config->
spi_host, &devcfg, &dev->
spi);
559 (void)spi_bus_free(config->
spi_host);
585 gpio_config_t rst_cfg;
586 (void)memset(&rst_cfg, 0,
sizeof(rst_cfg));
588 rst_cfg.mode = GPIO_MODE_OUTPUT;
589 (void)gpio_config(&rst_cfg);
591 vTaskDelay(pdMS_TO_TICKS(50));
593 vTaskDelay(pdMS_TO_TICKS(100));
601 gpio_config_t irq_cfg;
602 (void)memset(&irq_cfg, 0,
sizeof(irq_cfg));
604 irq_cfg.mode = GPIO_MODE_INPUT;
605 irq_cfg.pull_up_en = GPIO_PULLUP_ENABLE;
606 (void)gpio_config(&irq_cfg);
613 i2c_master_bus_config_t bus_cfg;
614 (void)memset(&bus_cfg, 0,
sizeof(bus_cfg));
615 bus_cfg.i2c_port = config->
i2c_port;
616 bus_cfg.sda_io_num = config->
pin_sda;
617 bus_cfg.scl_io_num = config->
pin_scl;
618 bus_cfg.clk_source = I2C_CLK_SRC_DEFAULT;
619 bus_cfg.glitch_ignore_cnt = 7;
620 bus_cfg.flags.enable_internal_pullup =
true;
622 ret = i2c_new_master_bus(&bus_cfg, &dev->
i2c_bus);
624 ESP_LOGE(
PN532_LOG_TAG,
"i2c_new_master_bus failed: %d", ret);
627 i2c_device_config_t dev_cfg;
628 (void)memset(&dev_cfg, 0,
sizeof(dev_cfg));
629 dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
633 ret = i2c_master_bus_add_device(dev->
i2c_bus, &dev_cfg, &dev->
i2c_dev);
635 ESP_LOGE(
PN532_LOG_TAG,
"i2c_master_bus_add_device failed: %d", ret);
636 (void)i2c_del_master_bus(dev->
i2c_bus);
641 vTaskDelay(pdMS_TO_TICKS(10));
644 "I2C master bus installed on port %d (SDA=%d SCL=%d %lu Hz)",
646 (
unsigned long)dev_cfg.scl_speed_hz);
661 (void)memset(dev, 0,
sizeof(*dev));
693 ESP_LOGI(
PN532_LOG_TAG,
"I2C wake-up trigger (sacrificial SAMConfig)");
695 vTaskDelay(pdMS_TO_TICKS(500));
700 ESP_LOGI(
PN532_LOG_TAG,
"SPI post-power-on SAMConfig (sacrificial)");
721 uint32_t response = 0U;
722 bool ack_received =
false;
724 (void)memset(pn532_packetbuffer, 0,
sizeof(pn532_packetbuffer));
755 bool ack_received =
false;
758 (void)memset(pn532_packetbuffer, 0,
sizeof(pn532_packetbuffer));
779 bool ack_received =
false;
781 (void)memset(pn532_packetbuffer, 0,
sizeof(pn532_packetbuffer));
784 pn532_packetbuffer[2] = cardbaudrate;
795 for (i = 0U; i < uid_len; i++) {
806 uint8_t *response, uint16_t *response_len)
812 (void)memset(cmd, 0,
sizeof(cmd));
813 (void)memset(frame, 0,
sizeof(frame));
816 uint8_t cmd_total_len = 0U;
817 bool ack_received =
false;
832 uint8_t err_byte = 0U;
833 uint16_t data_offset = 0U;
834 uint16_t data_len = 0U;
850 data_len = (uint16_t)raw_data_len;
857 uint16_t buf_cap = *response_len;
858 uint16_t copy_len = (data_len <= buf_cap) ? data_len : buf_cap;
860 (void)memcpy(response, &frame[data_offset], (
size_t)copy_len);
861 *response_len = copy_len;
865 (
unsigned)apdu[1], (
unsigned)err_byte);
868 ESP_LOGE(
PN532_LOG_TAG,
"APDU INS=0x%02X: no ACK", (
unsigned)apdu[1]);
879 bool ack_received =
false;
882 (void)memset(cmd, 0,
sizeof(cmd));
883 (void)memset(resp, 0,
sizeof(resp));
#define PN532_I2C_ADDRESS
7-bit I²C slave address of the PN532 (fixed in hardware).
esp_err_t pn532_init(pn532_t *dev, const pn532_config_t *config)
Initialise the PN532 and bring it to a ready state.
uint32_t pn532_read_passive_target_id(pn532_t *dev, uint8_t cardbaudrate)
Scan for a passive ISO 14443-A card and return its UID.
bool pn532_send_apdu(pn532_t *dev, const uint8_t *apdu, uint8_t apdu_len, uint8_t *response, uint16_t *response_len)
Exchange a single ISO-DEP APDU with the currently selected card.
uint32_t pn532_get_firmware_version(pn532_t *dev)
Query the PN532 firmware version.
bool pn532_sam_config(pn532_t *dev)
Configure the PN532's Security Access Module (SAM).
#define PN532_MAX_APDU_LEN
Maximum APDU payload length accepted by pn532_send_apdu.
bool pn532_release_target(pn532_t *dev)
Release the currently selected NFC target.
#define GPIO_PIN_BITMASK_BASE
#define PN532_WAKEUP_DELAY_MS
static esp_err_t pn532_init_spi(pn532_t *dev, const pn532_config_t *config)
static uint8_t spi_read_byte(pn532_t *dev)
#define PN532_FW_SUPPORT_OFFSET
#define PN532_FIRMWARE_HDR_LEN
#define PN532_SAM_RESP_CODE_OFFSET
#define PN532_EXT_EXCHANGE_DATA_OFFSET
#define PN532_INLISTPASSIVETARGET
#define PN532_EXCHANGE_LEN_BIAS
static void read_data(pn532_t *dev, uint8_t *buff, uint8_t n)
#define PN532_CMD_TIMEOUT_MS
static bool send_command_check_ack(pn532_t *dev, const uint8_t *cmd, uint8_t cmd_len, uint16_t timeout)
#define PN532_BYTE_SHIFT_BITS
#define PN532_SYNC_DELAY_MS
#define PN532_I2C_TIMEOUT_MS
static void spi_write_byte(pn532_t *dev, uint8_t data)
#define PN532_SAM_RESP_CODE
#define PN532_SPI_STATREAD
#define PN532_PASSIVE_MAX_TARGETS
#define PN532_INDATAEXCHANGE
#define PN532_FW_VER_OFFSET
static void write_command(pn532_t *dev, const uint8_t *cmd, uint8_t cmd_len)
#define PN532_EXCHANGE_DATA_OFFSET
#define PN532_PASSIVE_CMD_LEN
#define PN532_PASSIVE_NUM_TARGETS_OFFSET
#define PN532_WAKEUP_BYTE
static uint8_t read_ready(pn532_t *dev)
#define PN532_SAM_CMD_LEN
#define PN532_INRELEASE_RESP_LEN
#define PN532_FRAME_TAIL_LEN
static uint16_t read_data_apdu_frame(pn532_t *dev, uint8_t *buff, uint16_t max_len)
#define PN532_SPI_CLOCK_HZ
#define PN532_EXT_FRAME_LENLO_OFFSET
#define PN532_CS_TOGGLE_DELAY_MS
static const uint8_t pn532_ack[PN532_ACK_LEN]
#define PN532_FIRMWARE_RESP_LEN
#define PN532_EXCHANGE_FRAME_MAX
#define PN532_FIRMWARE_CMD_LEN
#define PN532_SAM_TIMEOUT
#define PN532_EXCHANGE_TG
#define PN532_HOSTTOPN532
static const uint8_t pn532_response_fw[PN532_FIRMWARE_HDR_LEN]
#define PN532_EXT_FRAME_INDICATOR
#define PN532_PASSIVE_UID_LEN_OFFSET
#define PN532_SAM_NORMAL_MODE
#define PN532_EXCHANGE_STATUS_OFFSET
static esp_err_t pn532_init_i2c(pn532_t *dev, const pn532_config_t *config)
static bool pn532_buffer_equal(const uint8_t *lhs, const uint8_t *rhs, uint8_t len)
#define PN532_EXCHANGE_STATUS_OK
#define PN532_BYTE_DELAY_MS
#define PN532_FRAME_TFI_OVERHEAD
#define PN532_EXCHANGE_LEN_OFFSET
static const char *const PN532_LOG_TAG
#define PN532_INRELEASE_CMD_LEN
#define PN532_FW_IC_OFFSET
#define PN532_FRAME_HDR_LEN
#define PN532_EXT_EXCHANGE_ERR_OFFSET
#define PN532_SAM_USE_IRQ
#define PN532_APDU_TIMEOUT_MS
#define PN532_PASSIVE_UID_DATA_OFFSET
#define PN532_SPI_DATAREAD
static bool check_ack(pn532_t *dev)
#define PN532_SAM_RESP_LEN
#define PN532_EXT_FRAME_LENHI_OFFSET
#define PN532_FW_REV_OFFSET
#define PN532_PASSIVE_RESP_LEN
#define PN532_POLL_INTERVAL_MS
#define PN532_EXCHANGE_CMD_OVERHEAD
#define PN532_SPI_QUEUE_SIZE
static uint8_t i2c_read_ready(pn532_t *dev)
#define PN532_SAMCONFIGURATION
#define PN532_SPI_DATAWRITE
#define PN532_EXT_FRAME_HDR_LEN
#define PN532_FIRMWAREVERSION
#define PN532_PASSIVE_EXPECTED_TARGETS
Low-level PN532 NFC controller driver for ESP-IDF (SPI and I²C).
Compile-time configuration passed to pn532_init.
spi_host_device_t spi_host
pn532_transport_t transport
Opaque-like runtime state for a single PN532 instance.
i2c_master_dev_handle_t i2c_dev
i2c_master_bus_handle_t i2c_bus
pn532_transport_t transport