cryptnox-sdk-esp32 1.0.0
ESP32 SDK for Cryptnox Hardware Wallet
Loading...
Searching...
No Matches
pn532.c
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later AND BSD-3-Clause
3 *
4 * PN532 driver for ESP-IDF — supports SPI and I²C transports.
5 *
6 * Portions of this file derive from the Adafruit_PN532 Arduino library
7 * (Copyright (c) 2012, Adafruit Industries; BSD-3-Clause). The full
8 * upstream copyright notice and license text are reproduced in
9 * NOTICES.md at the repository root.
10 *
11 * Additions on top of the upstream driver — the I²C transport via the
12 * IDF v5.x i2c_master API and the PN532 extended-frame (8-byte header)
13 * parser — are licensed under LGPL-3.0-or-later (with a commercial
14 * option, see LICENSE / COMMERCIAL.md).
15 */
16
17#include "pn532.h"
18#include "driver/gpio.h"
19#include "driver/i2c_master.h" /* new IDF v5.x master API */
20#include "esp_log.h"
21#include "freertos/FreeRTOS.h"
22#include "freertos/task.h"
23#include <string.h>
24
25static const char *const PN532_LOG_TAG = "pn532";
26
27/******************************************************************
28 * PN532 frame constants
29 ******************************************************************/
30
31#define PN532_PREAMBLE (0x00U)
32/* cppcheck-suppress misra-c2012-2.5 */
33#define PN532_STARTCODE1 (0x00U)
34#define PN532_STARTCODE2 (0xFFU)
35#define PN532_POSTAMBLE (0x00U)
36#define PN532_HOSTTOPN532 (0xD4U)
37
38/******************************************************************
39 * PN532 command codes
40 ******************************************************************/
41
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)
47
48/******************************************************************
49 * SPI protocol constants
50 ******************************************************************/
51
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)
61
62/******************************************************************
63 * I2C protocol constants
64 ******************************************************************/
65
66#define PN532_I2C_TIMEOUT_MS (100)
67/* Max frame bytes the host may send to PN532 (preamble..postamble). */
68#define PN532_I2C_TX_MAX (PN532_MAX_APDU_LEN + 16U)
69/* Max read length used anywhere (PASSIVE response is the largest at 64). */
70#define PN532_I2C_RX_MAX (200U)
71
72/******************************************************************
73 * Timing constants (milliseconds)
74 ******************************************************************/
75
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)
81/* Card ECDSA in getCardCertificate takes up to 3 s; use 5 s margin. */
82#define PN532_APDU_TIMEOUT_MS (5000U)
83#define PN532_BYTE_DELAY_MS (1U)
84
85/******************************************************************
86 * GPIO level constants
87 ******************************************************************/
88
89#define GPIO_LEVEL_LOW (0U) /* drive pin low */
90#define GPIO_LEVEL_HIGH (1U) /* drive pin high */
91#define GPIO_PIN_BITMASK_BASE (1ULL) /* base bit for gpio_config_t.pin_bit_mask */
92
93/******************************************************************
94 * Wakeup sequence constants
95 ******************************************************************/
96
97#define PN532_WAKEUP_BYTE (0x55U)
98
99/******************************************************************
100 * ACK frame
101 ******************************************************************/
102
103#define PN532_ACK_LEN (6U)
104
105/******************************************************************
106 * Frame structure constants
107 ******************************************************************/
108
109/* HOSTTOPN532 TFI byte added to cmd_len when building a frame. */
110#define PN532_FRAME_TFI_OVERHEAD (1U)
111
112/******************************************************************
113 * Firmware version response offsets and lengths
114 ******************************************************************/
115
116#define PN532_FIRMWARE_CMD_LEN (1U)
117#define PN532_FIRMWARE_RESP_LEN (13U) /* matches Adafruit_PN532::getFirmwareVersion */
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)
123
124/******************************************************************
125 * SAMConfiguration constants
126 ******************************************************************/
127
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) /* SAMCONFIGURATION + 1 */
132#define PN532_SAM_NORMAL_MODE (0x01U)
133#define PN532_SAM_TIMEOUT (0x14U) /* 50 ms x 20 = 1 s */
134#define PN532_SAM_USE_IRQ (0x01U)
135
136/******************************************************************
137 * InListPassiveTarget constants
138 ******************************************************************/
139
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)
148
149/******************************************************************
150 * InDataExchange constants
151 ******************************************************************/
152
153#define PN532_EXCHANGE_CMD_OVERHEAD (2U)
154#define PN532_EXCHANGE_TG (0x01U)
155/* Extended frames: up to 8-byte header + 418-byte body (415-byte DataOut) + 2-byte tail = 428; use 440 for margin. */
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) /* frame[8] = first DataOut byte for normal frames */
160#define PN532_EXCHANGE_LEN_BIAS (3U) /* LEN covers D5 + CMD + ERR; DataOut = LEN - 3 */
161#define PN532_EXCHANGE_STATUS_OK (0x00U)
162
163/* PN532 normal-frame structure: 5-byte header (preamble+start1+start2+LEN+LCS)
164 * followed by LEN data bytes, then 2-byte tail (DCS+postamble). */
165#define PN532_FRAME_HDR_LEN (5U)
166#define PN532_FRAME_TAIL_LEN (2U)
167
168/* PN532 extended-frame indicator and structure.
169 * Triggered when frame[3]==0xFF and frame[4]==0xFF.
170 * Header is 8 bytes: preamble+start1+start2+FF+FF+LEN_H+LEN_L+LCS.
171 * ERR is at offset 10; DataOut starts at offset 11. */
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)
178
179
180/******************************************************************
181 * InRelease constants
182 ******************************************************************/
183
184#define PN532_INRELEASE_CMD_LEN (2U)
185#define PN532_INRELEASE_RESP_LEN (10U)
186
187/******************************************************************
188 * Module-level static data
189 ******************************************************************/
190
191/* cppcheck-suppress misra-c2012-8.9 */
192static const uint8_t pn532_ack[PN532_ACK_LEN] = {
193 0x00U, 0x00U, 0xFFU, 0x00U, 0xFFU, 0x00U
194};
195
196/* cppcheck-suppress misra-c2012-8.9 */
198 0x00U, 0x00U, 0xFFU, 0x06U, 0xFAU, 0xD5U, 0x03U
199};
200
201/******************************************************************
202 * Forward declarations
203 ******************************************************************/
204
205static bool pn532_buffer_equal(const uint8_t *lhs, const uint8_t *rhs, uint8_t len);
206
207/******************************************************************
208 * Low-level SPI helpers
209 ******************************************************************/
210
211static void spi_write_byte(pn532_t *dev, uint8_t data)
212{
213 spi_transaction_t t;
214 (void)memset(&t, 0, sizeof(t));
215 t.length = PN532_SPI_BITS;
216 t.tx_buffer = &data;
217 (void)spi_device_transmit(dev->spi, &t);
218}
219
220static uint8_t spi_read_byte(pn532_t *dev)
221{
222 uint8_t rx = 0U;
223 uint8_t tx = 0x00U;
224 spi_transaction_t t;
225 (void)memset(&t, 0, sizeof(t));
226 t.length = PN532_SPI_BITS;
227 t.rxlength = PN532_SPI_BITS;
228 t.tx_buffer = &tx;
229 t.rx_buffer = &rx;
230 (void)spi_device_transmit(dev->spi, &t);
231 return rx;
232}
233
234/******************************************************************
235 * Low-level I2C helpers
236 ******************************************************************/
237
238static uint8_t i2c_read_ready(pn532_t *dev)
239{
240 uint8_t rdy = 0U;
241 (void)i2c_master_receive(dev->i2c_dev, &rdy, 1U, PN532_I2C_TIMEOUT_MS);
242 return rdy;
243}
244
245/******************************************************************
246 * Transport-agnostic ready / read / write
247 ******************************************************************/
248
249static uint8_t read_ready(pn532_t *dev)
250{
251 uint8_t status;
252
253 if (dev->transport == PN532_TRANSPORT_I2C) {
254 status = i2c_read_ready(dev);
255 } else {
256 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_LOW);
257 vTaskDelay(pdMS_TO_TICKS(PN532_CS_TOGGLE_DELAY_MS));
259 status = spi_read_byte(dev);
260 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_HIGH);
261 }
262
263 return status;
264}
265
266static void read_data(pn532_t *dev, uint8_t *buff, uint8_t n)
267{
268 if (dev->transport == PN532_TRANSPORT_I2C) {
269 /* PN532 I2C returns N+1 bytes: byte[0] = RDY (0x01), byte[1..N] = payload. */
270 uint8_t tmp[PN532_I2C_RX_MAX + 1U];
271 uint16_t to_read = (uint16_t)n + 1U;
272 if (to_read > sizeof(tmp)) {
273 to_read = (uint16_t)sizeof(tmp);
274 }
275 (void)i2c_master_receive(dev->i2c_dev, tmp, (size_t)to_read, PN532_I2C_TIMEOUT_MS);
276 size_t payload_len = (size_t)to_read - 1U;
277 (void)memcpy(buff, &tmp[1], payload_len);
278 } else {
279 uint8_t i = 0U;
280 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_LOW);
281 vTaskDelay(pdMS_TO_TICKS(PN532_CS_TOGGLE_DELAY_MS));
283 for (i = 0U; i < n; i++) {
284 vTaskDelay(pdMS_TO_TICKS(PN532_BYTE_DELAY_MS));
285 buff[i] = spi_read_byte(dev);
286 }
287 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_HIGH);
288 }
289}
290
291static bool check_ack(pn532_t *dev)
292{
293 uint8_t ackbuff[PN532_ACK_LEN];
294 bool result;
295 (void)memset(ackbuff, 0, sizeof(ackbuff));
296 read_data(dev, ackbuff, PN532_ACK_LEN);
297 result = pn532_buffer_equal(ackbuff, pn532_ack, PN532_ACK_LEN);
298 return result;
299}
300
301static bool pn532_buffer_equal(const uint8_t *lhs, const uint8_t *rhs, uint8_t len)
302{
303 bool equal = true;
304 uint8_t i = 0U;
305
306 for (i = 0U; i < len; i++) {
307 if (lhs[i] != rhs[i]) {
308 equal = false;
309 }
310 }
311
312 return equal;
313}
314
315static void write_command(pn532_t *dev, const uint8_t *cmd, uint8_t cmd_len)
316{
317 uint8_t frame_len = (uint8_t)(cmd_len + PN532_FRAME_TFI_OVERHEAD);
318 uint8_t checksum = (uint8_t)(PN532_PREAMBLE + PN532_PREAMBLE + PN532_STARTCODE2);
319 uint8_t i = 0U;
320
321 if (dev->transport == PN532_TRANSPORT_I2C) {
322 /* Build the full frame in a buffer and send it in one I2C transaction. */
323 uint8_t frame[PN532_I2C_TX_MAX];
324 uint16_t pos = 0U;
325
326 frame[pos] = PN532_PREAMBLE; pos++;
327 frame[pos] = PN532_PREAMBLE; pos++;
328 frame[pos] = PN532_STARTCODE2; pos++;
329 frame[pos] = frame_len; pos++;
330 frame[pos] = (uint8_t)((uint8_t)(~frame_len) + 1U); pos++;
331 frame[pos] = PN532_HOSTTOPN532; pos++;
332 checksum = (uint8_t)(checksum + PN532_HOSTTOPN532);
333
334 for (i = 0U; i < cmd_len; i++) {
335 frame[pos] = cmd[i]; pos++;
336 checksum = (uint8_t)(checksum + cmd[i]);
337 }
338 frame[pos] = (uint8_t)(~checksum); pos++;
339 frame[pos] = PN532_POSTAMBLE; pos++;
340
341 (void)i2c_master_transmit(dev->i2c_dev, frame, (size_t)pos, PN532_I2C_TIMEOUT_MS);
342 } else {
343 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_LOW);
344 vTaskDelay(pdMS_TO_TICKS(PN532_CS_TOGGLE_DELAY_MS));
346
350
351 spi_write_byte(dev, frame_len);
352 spi_write_byte(dev, (uint8_t)((uint8_t)(~frame_len) + 1U));
353
355 checksum = (uint8_t)(checksum + PN532_HOSTTOPN532);
356
357 for (i = 0U; i < cmd_len; i++) {
358 spi_write_byte(dev, cmd[i]);
359 checksum = (uint8_t)(checksum + cmd[i]);
360 }
361 spi_write_byte(dev, (uint8_t)(~checksum));
363 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_HIGH);
364 }
365}
366
367static bool send_command_check_ack(pn532_t *dev, const uint8_t *cmd,
368 uint8_t cmd_len, uint16_t timeout)
369{
370 uint16_t timer = 0U;
371 bool timed_out = false;
372 bool result = false;
373
374 write_command(dev, cmd, cmd_len);
375
376 /* Wait until the PN532 signals it is ready to send the ACK frame. */
377 while ((!timed_out) && (read_ready(dev) != PN532_SPI_READY)) {
378 if (timeout != 0U) {
379 timer = (uint16_t)(timer + PN532_POLL_INTERVAL_MS);
380 if (timer > timeout) {
381 timed_out = true;
382 }
383 }
384 if (!timed_out) {
385 vTaskDelay(pdMS_TO_TICKS(PN532_POLL_INTERVAL_MS));
386 }
387 }
388
389 if (timed_out) {
390 ESP_LOGE(PN532_LOG_TAG, "cmd=0x%02X: TIMEOUT waiting for ACK", (unsigned)cmd[0]);
391 } else {
392 bool ack_ok = check_ack(dev);
393
394 if (ack_ok) {
395 timer = 0U;
396 timed_out = false;
397
398 /* Wait until the PN532 signals it is ready to send the response. */
399 while ((!timed_out) && (read_ready(dev) != PN532_SPI_READY)) {
400 if (timeout != 0U) {
401 timer = (uint16_t)(timer + PN532_POLL_INTERVAL_MS);
402 if (timer > timeout) {
403 timed_out = true;
404 }
405 }
406 if (!timed_out) {
407 vTaskDelay(pdMS_TO_TICKS(PN532_POLL_INTERVAL_MS));
408 }
409 }
410
411 if (timed_out) {
412 ESP_LOGE(PN532_LOG_TAG, "cmd=0x%02X: TIMEOUT waiting for response", (unsigned)cmd[0]);
413 }
414 result = !timed_out;
415 } else {
416 ESP_LOGE(PN532_LOG_TAG, "cmd=0x%02X: ACK mismatch", (unsigned)cmd[0]);
417 }
418 }
419
420 return result;
421}
422
423/* Read one complete PN532 response frame within a single CS-low window.
424 * Handles both normal frames (5-byte header) and extended frames (8-byte header,
425 * indicated by frame[3]==0xFF and frame[4]==0xFF).
426 * CS is released as soon as the last real frame byte is read, preventing
427 * spurious clocks from corrupting the PN532 SPI state machine. */
428static uint16_t read_data_apdu_frame(pn532_t *dev, uint8_t *buff, uint16_t max_len)
429{
430 uint16_t body_len = 0U;
431 uint16_t total_len = 0U;
432 bool is_extended = false;
433
434 if (dev->transport == PN532_TRANSPORT_I2C) {
435 /* PN532 I²C returns 1 RDY byte + N payload bytes in a single read.
436 * Pull the whole maximum response at once, then parse the frame
437 * header to figure out the actual length. */
438 uint8_t tmp[PN532_EXCHANGE_FRAME_MAX + 1U];
439 uint16_t to_read = max_len + 1U;
440 if (to_read > (uint16_t)sizeof(tmp)) {
441 to_read = (uint16_t)sizeof(tmp);
442 }
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);
447
448 is_extended = ((buff[PN532_EXCHANGE_LEN_OFFSET] == PN532_EXT_FRAME_INDICATOR) &&
450 if (is_extended) {
451 body_len = ((uint16_t)buff[PN532_EXT_FRAME_LENHI_OFFSET] << 8U)
452 | (uint16_t)buff[PN532_EXT_FRAME_LENLO_OFFSET];
453 total_len = (uint16_t)PN532_EXT_FRAME_HDR_LEN
454 + body_len
455 + (uint16_t)PN532_FRAME_TAIL_LEN;
456 } else {
457 body_len = (uint16_t)buff[PN532_EXCHANGE_LEN_OFFSET];
458 total_len = (uint16_t)PN532_FRAME_HDR_LEN
459 + body_len
460 + (uint16_t)PN532_FRAME_TAIL_LEN;
461 }
462 if (total_len > max_len) {
463 total_len = max_len;
464 }
465 } else {
466 /* SPI path: byte-by-byte over a single CS-low window. */
467 uint16_t i;
468 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_LOW);
469 vTaskDelay(pdMS_TO_TICKS(PN532_CS_TOGGLE_DELAY_MS));
471
472 for (i = 0U; i < PN532_FRAME_HDR_LEN; i++) {
473 vTaskDelay(pdMS_TO_TICKS(PN532_BYTE_DELAY_MS));
474 buff[i] = spi_read_byte(dev);
475 }
476
477 is_extended = ((buff[PN532_EXCHANGE_LEN_OFFSET] == PN532_EXT_FRAME_INDICATOR) &&
479
480 if (is_extended) {
481 /* Read the 3 additional extended-frame header bytes: LEN_H, LEN_L, LCS. */
482 for (i = PN532_FRAME_HDR_LEN; i < PN532_EXT_FRAME_HDR_LEN; i++) {
483 vTaskDelay(pdMS_TO_TICKS(PN532_BYTE_DELAY_MS));
484 buff[i] = spi_read_byte(dev);
485 }
486 body_len = ((uint16_t)buff[PN532_EXT_FRAME_LENHI_OFFSET] << 8U)
487 | (uint16_t)buff[PN532_EXT_FRAME_LENLO_OFFSET];
488 total_len = (uint16_t)PN532_EXT_FRAME_HDR_LEN
489 + body_len
490 + (uint16_t)PN532_FRAME_TAIL_LEN;
491 } else {
492 body_len = (uint16_t)buff[PN532_EXCHANGE_LEN_OFFSET];
493 total_len = (uint16_t)PN532_FRAME_HDR_LEN
494 + body_len
495 + (uint16_t)PN532_FRAME_TAIL_LEN;
496 }
497
498 if (total_len > max_len) {
499 total_len = max_len;
500 }
501
502 i = (uint16_t)(is_extended ? PN532_EXT_FRAME_HDR_LEN : PN532_FRAME_HDR_LEN);
503 while (i < total_len) {
504 vTaskDelay(pdMS_TO_TICKS(PN532_BYTE_DELAY_MS));
505 buff[i] = spi_read_byte(dev);
506 i++;
507 }
508
509 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_HIGH);
510 }
511
512 return total_len;
513}
514
515/******************************************************************
516 * Transport-specific init helpers
517 ******************************************************************/
518
519static esp_err_t pn532_init_spi(pn532_t *dev, const pn532_config_t *config)
520{
521 gpio_config_t io_conf;
522 spi_bus_config_t buscfg;
523 spi_device_interface_config_t devcfg;
524 esp_err_t ret;
525
526 (void)memset(&io_conf, 0, sizeof(io_conf));
527 io_conf.pin_bit_mask = (GPIO_PIN_BITMASK_BASE << (uint32_t)config->pin_cs);
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);
533 (void)gpio_set_level(config->pin_cs, GPIO_LEVEL_HIGH);
534 dev->pin_cs = config->pin_cs;
535
536 if (config->skip_bus_init) {
537 ret = ESP_OK;
538 } else {
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;
543 buscfg.quadwp_io_num = PN532_SPI_NO_PIN;
544 buscfg.quadhd_io_num = PN532_SPI_NO_PIN;
545 buscfg.max_transfer_sz = 0;
546 ret = spi_bus_initialize(config->spi_host, &buscfg, SPI_DMA_CH_AUTO);
547 }
548
549 if (ret == ESP_OK) {
550 (void)memset(&devcfg, 0, sizeof(devcfg));
551 devcfg.mode = PN532_SPI_MODE;
552 devcfg.clock_speed_hz = (int)PN532_SPI_CLOCK_HZ;
553 devcfg.spics_io_num = PN532_SPI_NO_PIN;
554 devcfg.queue_size = PN532_SPI_QUEUE_SIZE;
555 devcfg.flags = SPI_DEVICE_BIT_LSBFIRST;
556 ret = spi_bus_add_device(config->spi_host, &devcfg, &dev->spi);
557
558 if ((ret != ESP_OK) && (!config->skip_bus_init)) {
559 (void)spi_bus_free(config->spi_host);
560 }
561 }
562
563 if (ret == ESP_OK) {
564 /* Wake-up sequence: send 0x55 0x55 + 3x preamble while CS is low. */
565 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_LOW);
566 vTaskDelay(pdMS_TO_TICKS(PN532_CS_TOGGLE_DELAY_MS));
572 (void)gpio_set_level(dev->pin_cs, GPIO_LEVEL_HIGH);
573 vTaskDelay(pdMS_TO_TICKS(PN532_WAKEUP_DELAY_MS));
574 }
575
576 return ret;
577}
578
579static esp_err_t pn532_init_i2c(pn532_t *dev, const pn532_config_t *config)
580{
581 esp_err_t ret;
582
583 /* Optional reset pulse. */
584 if (config->pin_rst >= 0) {
585 gpio_config_t rst_cfg;
586 (void)memset(&rst_cfg, 0, sizeof(rst_cfg));
587 rst_cfg.pin_bit_mask = (GPIO_PIN_BITMASK_BASE << (uint32_t)config->pin_rst);
588 rst_cfg.mode = GPIO_MODE_OUTPUT;
589 (void)gpio_config(&rst_cfg);
590 (void)gpio_set_level(config->pin_rst, GPIO_LEVEL_LOW);
591 vTaskDelay(pdMS_TO_TICKS(50));
592 (void)gpio_set_level(config->pin_rst, GPIO_LEVEL_HIGH);
593 vTaskDelay(pdMS_TO_TICKS(100));
594 dev->pin_rst = config->pin_rst;
595 } else {
596 dev->pin_rst = -1;
597 }
598
599 /* Optional IRQ input (configured but polling is used). */
600 if (config->pin_irq >= 0) {
601 gpio_config_t irq_cfg;
602 (void)memset(&irq_cfg, 0, sizeof(irq_cfg));
603 irq_cfg.pin_bit_mask = (GPIO_PIN_BITMASK_BASE << (uint32_t)config->pin_irq);
604 irq_cfg.mode = GPIO_MODE_INPUT;
605 irq_cfg.pull_up_en = GPIO_PULLUP_ENABLE;
606 (void)gpio_config(&irq_cfg);
607 dev->pin_irq = config->pin_irq;
608 } else {
609 dev->pin_irq = -1;
610 }
611
612 /* Create the I2C master bus (new IDF v5.x API). */
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;
621
622 ret = i2c_new_master_bus(&bus_cfg, &dev->i2c_bus);
623 if (ret != ESP_OK) {
624 ESP_LOGE(PN532_LOG_TAG, "i2c_new_master_bus failed: %d", ret);
625 } else {
626 /* Attach the PN532 device on the bus. */
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;
630 dev_cfg.device_address = PN532_I2C_ADDRESS;
631 dev_cfg.scl_speed_hz = (config->i2c_clock_hz != 0U) ? config->i2c_clock_hz : 100000U;
632
633 ret = i2c_master_bus_add_device(dev->i2c_bus, &dev_cfg, &dev->i2c_dev);
634 if (ret != ESP_OK) {
635 ESP_LOGE(PN532_LOG_TAG, "i2c_master_bus_add_device failed: %d", ret);
636 (void)i2c_del_master_bus(dev->i2c_bus);
637 dev->i2c_bus = NULL;
638 } else {
639 /* Adafruit_PN532::begin does a small delay between bus init
640 * and the first I2C transaction; mirror it. */
641 vTaskDelay(pdMS_TO_TICKS(10));
642
643 ESP_LOGI(PN532_LOG_TAG,
644 "I2C master bus installed on port %d (SDA=%d SCL=%d %lu Hz)",
645 config->i2c_port, (int)config->pin_sda, (int)config->pin_scl,
646 (unsigned long)dev_cfg.scl_speed_hz);
647 }
648 }
649
650 return ret;
651}
652
653/******************************************************************
654 * Public API
655 ******************************************************************/
656
657esp_err_t pn532_init(pn532_t *dev, const pn532_config_t *config)
658{
659 esp_err_t ret;
660
661 (void)memset(dev, 0, sizeof(*dev));
662 dev->transport = config->transport;
663
664 if (config->transport == PN532_TRANSPORT_I2C) {
665 ret = pn532_init_i2c(dev, config);
666 } else {
667 ret = pn532_init_spi(dev, config);
668 }
669
670 if (ret == ESP_OK) {
671 /* Transport-specific post-wakeup sequencing.
672 *
673 * I2C — Soft-Power-Down recovery:
674 * When the host soft-reboots with the PN532 still powered the chip
675 * enters Soft-Power-Down. The main I2C peripheral is OFF; a
676 * dedicated wake-up watcher listens on the bus. The first
677 * transaction that matches the slave address triggers the watcher,
678 * but the firmware must then disable the watcher and re-enable the
679 * main I2C peripheral — a process that takes up to 500 ms and
680 * DISCARDS the triggering frame. We therefore send a sacrificial
681 * SAMConfig first, wait 500 ms, then send the real SAMConfig.
682 *
683 * SPI — post-power-on synchronisation:
684 * The Adafruit_PN532 library always issues SAMConfig as the very
685 * first functional command after wakeup. On some PN532 modules the
686 * chip's internal power-on sequencing is not complete until roughly
687 * 2 s after VCC is applied; a SAMConfig sent before that point is
688 * silently ignored (no ACK — 1 s command timeout). By issuing a
689 * sacrificial SAMConfig here we absorb that 1 s stall inside
690 * pn532_init so that the application-level SAMConfig issued by
691 * wallet.begin() arrives after the chip is fully ready. */
692 if (config->transport == PN532_TRANSPORT_I2C) {
693 ESP_LOGI(PN532_LOG_TAG, "I2C wake-up trigger (sacrificial SAMConfig)");
694 (void)pn532_sam_config(dev);
695 vTaskDelay(pdMS_TO_TICKS(500));
696 ESP_LOGI(PN532_LOG_TAG, "Real SAMConfig after wake-up");
697 (void)pn532_sam_config(dev);
698 vTaskDelay(pdMS_TO_TICKS(PN532_SYNC_DELAY_MS));
699 } else {
700 ESP_LOGI(PN532_LOG_TAG, "SPI post-power-on SAMConfig (sacrificial)");
701 (void)pn532_sam_config(dev);
702 vTaskDelay(pdMS_TO_TICKS(PN532_SYNC_DELAY_MS));
703 }
704
705 /* Read the firmware version to confirm the chip is alive and to drain
706 * any stale response bytes from the PN532's output FIFO before the
707 * application-level SAMConfig is issued. */
709 vTaskDelay(pdMS_TO_TICKS(PN532_SYNC_DELAY_MS));
710
711 ESP_LOGI(PN532_LOG_TAG, "PN532 initialized (%s)",
712 (config->transport == PN532_TRANSPORT_I2C) ? "I2C" : "SPI");
713 }
714
715 return ret;
716}
717
719{
720 uint8_t pn532_packetbuffer[PN532_FIRMWARE_RESP_LEN];
721 uint32_t response = 0U;
722 bool ack_received = false;
723
724 (void)memset(pn532_packetbuffer, 0, sizeof(pn532_packetbuffer));
725 pn532_packetbuffer[0] = PN532_FIRMWAREVERSION;
726 ack_received = send_command_check_ack(dev, pn532_packetbuffer,
728
729 if (!ack_received) {
730 ESP_LOGE(PN532_LOG_TAG, "No ACK from PN532");
731 } else {
732 read_data(dev, pn532_packetbuffer, PN532_FIRMWARE_RESP_LEN);
733 ESP_LOG_BUFFER_HEX_LEVEL(PN532_LOG_TAG, pn532_packetbuffer,
734 PN532_FIRMWARE_RESP_LEN, ESP_LOG_INFO);
735
737 ESP_LOGE(PN532_LOG_TAG, "Unexpected firmware response");
738 } else {
739 response = (uint32_t)pn532_packetbuffer[PN532_FW_IC_OFFSET];
740 response <<= PN532_BYTE_SHIFT_BITS;
741 response |= (uint32_t)pn532_packetbuffer[PN532_FW_VER_OFFSET];
742 response <<= PN532_BYTE_SHIFT_BITS;
743 response |= (uint32_t)pn532_packetbuffer[PN532_FW_REV_OFFSET];
744 response <<= PN532_BYTE_SHIFT_BITS;
745 response |= (uint32_t)pn532_packetbuffer[PN532_FW_SUPPORT_OFFSET];
746 }
747 }
748
749 return response;
750}
751
753{
754 uint8_t pn532_packetbuffer[PN532_SAM_RESP_LEN];
755 bool ack_received = false;
756 bool result = false;
757
758 (void)memset(pn532_packetbuffer, 0, sizeof(pn532_packetbuffer));
759 pn532_packetbuffer[0] = PN532_SAMCONFIGURATION;
760 pn532_packetbuffer[1] = PN532_SAM_NORMAL_MODE;
761 pn532_packetbuffer[2] = PN532_SAM_TIMEOUT;
762 pn532_packetbuffer[3] = PN532_SAM_USE_IRQ;
763
764 ack_received = send_command_check_ack(dev, pn532_packetbuffer,
766
767 if (ack_received) {
768 read_data(dev, pn532_packetbuffer, PN532_SAM_RESP_LEN);
769 result = (pn532_packetbuffer[PN532_SAM_RESP_CODE_OFFSET] == PN532_SAM_RESP_CODE);
770 }
771
772 return result;
773}
774
775uint32_t pn532_read_passive_target_id(pn532_t *dev, uint8_t cardbaudrate)
776{
777 uint8_t pn532_packetbuffer[PN532_PASSIVE_RESP_LEN];
778 uint32_t cid = 0U;
779 bool ack_received = false;
780
781 (void)memset(pn532_packetbuffer, 0, sizeof(pn532_packetbuffer));
782 pn532_packetbuffer[0] = PN532_INLISTPASSIVETARGET;
783 pn532_packetbuffer[1] = PN532_PASSIVE_MAX_TARGETS;
784 pn532_packetbuffer[2] = cardbaudrate;
785
786 ack_received = send_command_check_ack(dev, pn532_packetbuffer,
788
789 if (ack_received) {
790 read_data(dev, pn532_packetbuffer, PN532_PASSIVE_RESP_LEN);
791
793 uint8_t uid_len = pn532_packetbuffer[PN532_PASSIVE_UID_LEN_OFFSET];
794 uint8_t i = 0U;
795 for (i = 0U; i < uid_len; i++) {
796 cid <<= PN532_BYTE_SHIFT_BITS;
797 cid |= (uint32_t)pn532_packetbuffer[PN532_PASSIVE_UID_DATA_OFFSET + i];
798 }
799 }
800 }
801
802 return cid;
803}
804
805bool pn532_send_apdu(pn532_t *dev, const uint8_t *apdu, uint8_t apdu_len,
806 uint8_t *response, uint16_t *response_len)
807{
809 uint8_t frame[PN532_EXCHANGE_FRAME_MAX];
810 bool result = false;
811
812 (void)memset(cmd, 0, sizeof(cmd));
813 (void)memset(frame, 0, sizeof(frame));
814
815 if (apdu_len <= PN532_MAX_APDU_LEN) {
816 uint8_t cmd_total_len = 0U;
817 bool ack_received = false;
818
819 cmd[0] = PN532_INDATAEXCHANGE;
820 cmd[1] = PN532_EXCHANGE_TG;
821 (void)memcpy(&cmd[PN532_EXCHANGE_CMD_OVERHEAD], apdu, apdu_len);
822 cmd_total_len = (uint8_t)(apdu_len + PN532_EXCHANGE_CMD_OVERHEAD);
823
824 ack_received = send_command_check_ack(dev, cmd, cmd_total_len, PN532_APDU_TIMEOUT_MS);
825
826 if (ack_received) {
827 (void)read_data_apdu_frame(dev, frame, (uint16_t)PN532_EXCHANGE_FRAME_MAX);
828
829 bool is_extended = ((frame[PN532_EXCHANGE_LEN_OFFSET] == PN532_EXT_FRAME_INDICATOR) &&
831
832 uint8_t err_byte = 0U;
833 uint16_t data_offset = 0U;
834 uint16_t data_len = 0U;
835
836 if (is_extended) {
837 uint16_t body_len = ((uint16_t)frame[PN532_EXT_FRAME_LENHI_OFFSET] << 8U)
838 | (uint16_t)frame[PN532_EXT_FRAME_LENLO_OFFSET];
839 err_byte = frame[PN532_EXT_EXCHANGE_ERR_OFFSET];
840 data_offset = (uint16_t)PN532_EXT_EXCHANGE_DATA_OFFSET;
841 data_len = (body_len >= (uint16_t)PN532_EXCHANGE_LEN_BIAS)
842 ? (body_len - (uint16_t)PN532_EXCHANGE_LEN_BIAS)
843 : 0U;
844 } else {
845 uint8_t len_field = frame[PN532_EXCHANGE_LEN_OFFSET];
846 err_byte = frame[PN532_EXCHANGE_STATUS_OFFSET];
847 data_offset = (uint16_t)PN532_EXCHANGE_DATA_OFFSET;
848 if (len_field >= (uint8_t)PN532_EXCHANGE_LEN_BIAS) {
849 uint8_t raw_data_len = (uint8_t)(len_field - (uint8_t)PN532_EXCHANGE_LEN_BIAS);
850 data_len = (uint16_t)raw_data_len;
851 } else {
852 data_len = 0U;
853 }
854 }
855
856 if (err_byte == PN532_EXCHANGE_STATUS_OK) {
857 uint16_t buf_cap = *response_len;
858 uint16_t copy_len = (data_len <= buf_cap) ? data_len : buf_cap;
859
860 (void)memcpy(response, &frame[data_offset], (size_t)copy_len);
861 *response_len = copy_len;
862 result = true;
863 } else {
864 ESP_LOGE(PN532_LOG_TAG, "APDU INS=0x%02X: ERR=0x%02X",
865 (unsigned)apdu[1], (unsigned)err_byte);
866 }
867 } else {
868 ESP_LOGE(PN532_LOG_TAG, "APDU INS=0x%02X: no ACK", (unsigned)apdu[1]);
869 }
870 }
871
872 return result;
873}
874
876{
877 uint8_t cmd[PN532_INRELEASE_CMD_LEN];
878 uint8_t resp[PN532_INRELEASE_RESP_LEN];
879 bool ack_received = false;
880 bool result = false;
881
882 (void)memset(cmd, 0, sizeof(cmd));
883 (void)memset(resp, 0, sizeof(resp));
884
885 cmd[0] = PN532_INRELEASE;
886 cmd[1] = PN532_EXCHANGE_TG;
887
889
890 if (ack_received) {
893 }
894
895 return result;
896}
#define PN532_I2C_ADDRESS
7-bit I²C slave address of the PN532 (fixed in hardware).
Definition pn532.h:63
esp_err_t pn532_init(pn532_t *dev, const pn532_config_t *config)
Initialise the PN532 and bring it to a ready state.
Definition pn532.c:657
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.
Definition pn532.c:775
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.
Definition pn532.c:805
uint32_t pn532_get_firmware_version(pn532_t *dev)
Query the PN532 firmware version.
Definition pn532.c:718
bool pn532_sam_config(pn532_t *dev)
Configure the PN532's Security Access Module (SAM).
Definition pn532.c:752
#define PN532_MAX_APDU_LEN
Maximum APDU payload length accepted by pn532_send_apdu.
Definition pn532.h:60
bool pn532_release_target(pn532_t *dev)
Release the currently selected NFC target.
Definition pn532.c:875
@ PN532_TRANSPORT_I2C
Definition pn532.h:78
#define PN532_ACK_LEN
Definition pn532.c:103
#define GPIO_LEVEL_HIGH
Definition pn532.c:90
#define GPIO_PIN_BITMASK_BASE
Definition pn532.c:91
#define PN532_WAKEUP_DELAY_MS
Definition pn532.c:77
#define PN532_STARTCODE2
Definition pn532.c:34
static esp_err_t pn532_init_spi(pn532_t *dev, const pn532_config_t *config)
Definition pn532.c:519
static uint8_t spi_read_byte(pn532_t *dev)
Definition pn532.c:220
#define PN532_FW_SUPPORT_OFFSET
Definition pn532.c:122
#define PN532_FIRMWARE_HDR_LEN
Definition pn532.c:118
#define PN532_SAM_RESP_CODE_OFFSET
Definition pn532.c:130
#define PN532_I2C_TX_MAX
Definition pn532.c:68
#define PN532_EXT_EXCHANGE_DATA_OFFSET
Definition pn532.c:177
#define PN532_INLISTPASSIVETARGET
Definition pn532.c:44
#define PN532_EXCHANGE_LEN_BIAS
Definition pn532.c:160
static void read_data(pn532_t *dev, uint8_t *buff, uint8_t n)
Definition pn532.c:266
#define PN532_CMD_TIMEOUT_MS
Definition pn532.c:80
static bool send_command_check_ack(pn532_t *dev, const uint8_t *cmd, uint8_t cmd_len, uint16_t timeout)
Definition pn532.c:367
#define GPIO_LEVEL_LOW
Definition pn532.c:89
#define PN532_BYTE_SHIFT_BITS
Definition pn532.c:147
#define PN532_POSTAMBLE
Definition pn532.c:35
#define PN532_SYNC_DELAY_MS
Definition pn532.c:78
#define PN532_I2C_TIMEOUT_MS
Definition pn532.c:66
static void spi_write_byte(pn532_t *dev, uint8_t data)
Definition pn532.c:211
#define PN532_SAM_RESP_CODE
Definition pn532.c:131
#define PN532_SPI_STATREAD
Definition pn532.c:52
#define PN532_PASSIVE_MAX_TARGETS
Definition pn532.c:142
#define PN532_INDATAEXCHANGE
Definition pn532.c:45
#define PN532_FW_VER_OFFSET
Definition pn532.c:120
#define PN532_SPI_BITS
Definition pn532.c:60
static void write_command(pn532_t *dev, const uint8_t *cmd, uint8_t cmd_len)
Definition pn532.c:315
#define PN532_EXCHANGE_DATA_OFFSET
Definition pn532.c:159
#define PN532_PASSIVE_CMD_LEN
Definition pn532.c:140
#define PN532_PASSIVE_NUM_TARGETS_OFFSET
Definition pn532.c:143
#define PN532_WAKEUP_BYTE
Definition pn532.c:97
static uint8_t read_ready(pn532_t *dev)
Definition pn532.c:249
#define PN532_SAM_CMD_LEN
Definition pn532.c:128
#define PN532_INRELEASE_RESP_LEN
Definition pn532.c:185
#define PN532_FRAME_TAIL_LEN
Definition pn532.c:166
static uint16_t read_data_apdu_frame(pn532_t *dev, uint8_t *buff, uint16_t max_len)
Definition pn532.c:428
#define PN532_SPI_CLOCK_HZ
Definition pn532.c:56
#define PN532_EXT_FRAME_LENLO_OFFSET
Definition pn532.c:175
#define PN532_CS_TOGGLE_DELAY_MS
Definition pn532.c:76
static const uint8_t pn532_ack[PN532_ACK_LEN]
Definition pn532.c:192
#define PN532_FIRMWARE_RESP_LEN
Definition pn532.c:117
#define PN532_EXCHANGE_FRAME_MAX
Definition pn532.c:156
#define PN532_FIRMWARE_CMD_LEN
Definition pn532.c:116
#define PN532_SAM_TIMEOUT
Definition pn532.c:133
#define PN532_EXCHANGE_TG
Definition pn532.c:154
#define PN532_HOSTTOPN532
Definition pn532.c:36
static const uint8_t pn532_response_fw[PN532_FIRMWARE_HDR_LEN]
Definition pn532.c:197
#define PN532_INRELEASE
Definition pn532.c:46
#define PN532_EXT_FRAME_INDICATOR
Definition pn532.c:172
#define PN532_PASSIVE_UID_LEN_OFFSET
Definition pn532.c:145
#define PN532_SAM_NORMAL_MODE
Definition pn532.c:132
#define PN532_EXCHANGE_STATUS_OFFSET
Definition pn532.c:158
static esp_err_t pn532_init_i2c(pn532_t *dev, const pn532_config_t *config)
Definition pn532.c:579
static bool pn532_buffer_equal(const uint8_t *lhs, const uint8_t *rhs, uint8_t len)
Definition pn532.c:301
#define PN532_EXCHANGE_STATUS_OK
Definition pn532.c:161
#define PN532_BYTE_DELAY_MS
Definition pn532.c:83
#define PN532_FRAME_TFI_OVERHEAD
Definition pn532.c:110
#define PN532_EXCHANGE_LEN_OFFSET
Definition pn532.c:157
static const char *const PN532_LOG_TAG
Definition pn532.c:25
#define PN532_INRELEASE_CMD_LEN
Definition pn532.c:184
#define PN532_SPI_MODE
Definition pn532.c:57
#define PN532_FW_IC_OFFSET
Definition pn532.c:119
#define PN532_SPI_NO_PIN
Definition pn532.c:58
#define PN532_FRAME_HDR_LEN
Definition pn532.c:165
#define PN532_I2C_RX_MAX
Definition pn532.c:70
#define PN532_EXT_EXCHANGE_ERR_OFFSET
Definition pn532.c:176
#define PN532_SAM_USE_IRQ
Definition pn532.c:134
#define PN532_APDU_TIMEOUT_MS
Definition pn532.c:82
#define PN532_PASSIVE_UID_DATA_OFFSET
Definition pn532.c:146
#define PN532_SPI_DATAREAD
Definition pn532.c:54
static bool check_ack(pn532_t *dev)
Definition pn532.c:291
#define PN532_SAM_RESP_LEN
Definition pn532.c:129
#define PN532_SPI_READY
Definition pn532.c:55
#define PN532_EXT_FRAME_LENHI_OFFSET
Definition pn532.c:174
#define PN532_FW_REV_OFFSET
Definition pn532.c:121
#define PN532_PASSIVE_RESP_LEN
Definition pn532.c:141
#define PN532_POLL_INTERVAL_MS
Definition pn532.c:79
#define PN532_EXCHANGE_CMD_OVERHEAD
Definition pn532.c:153
#define PN532_SPI_QUEUE_SIZE
Definition pn532.c:59
static uint8_t i2c_read_ready(pn532_t *dev)
Definition pn532.c:238
#define PN532_SAMCONFIGURATION
Definition pn532.c:43
#define PN532_SPI_DATAWRITE
Definition pn532.c:53
#define PN532_EXT_FRAME_HDR_LEN
Definition pn532.c:173
#define PN532_PREAMBLE
Definition pn532.c:31
#define PN532_FIRMWAREVERSION
Definition pn532.c:42
#define PN532_PASSIVE_EXPECTED_TARGETS
Definition pn532.c:144
Low-level PN532 NFC controller driver for ESP-IDF (SPI and I²C).
Compile-time configuration passed to pn532_init.
Definition pn532.h:111
uint32_t i2c_clock_hz
Definition pn532.h:128
spi_host_device_t spi_host
Definition pn532.h:115
pn532_transport_t transport
Definition pn532.h:112
bool skip_bus_init
Definition pn532.h:120
Opaque-like runtime state for a single PN532 instance.
Definition pn532.h:141
i2c_master_dev_handle_t i2c_dev
Definition pn532.h:150
int pin_irq
Definition pn532.h:151
i2c_master_bus_handle_t i2c_bus
Definition pn532.h:149
spi_device_handle_t spi
Definition pn532.h:145
int pin_cs
Definition pn532.h:146
pn532_transport_t transport
Definition pn532.h:142
int pin_rst
Definition pn532.h:152