Skip to content

Commit 02c0630

Browse files
committed
Add support to 'listen' for tags
This adds support to break the `read_passive_target` function up into two parts if desired. First tell the PN532 to listen for tags, and then explicitly ask to read the UID from the tag. This can be useful when trying to simultaneously scan tags and run timing sensitive code, like updating neopixels, and don't want to wait for the timeout delay of `read_passive_target`.
1 parent ccc822d commit 02c0630

File tree

2 files changed

+123
-8
lines changed

2 files changed

+123
-8
lines changed

adafruit_pn532/adafruit_pn532.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,19 @@ def call_function(
295295
for a response and return a bytearray of response bytes, or None if no
296296
response is available within the timeout.
297297
"""
298+
if not self.send_command(command, params=params, timeout=timeout):
299+
return None
300+
return self.process_response(
301+
command, response_length=response_length, timeout=timeout
302+
)
303+
304+
def send_command(
305+
self, command, params=[], timeout=1
306+
): # pylint: disable=dangerous-default-value
307+
"""Send specified command to the PN532 and wait for an acknowledgment.
308+
Will wait up to timeout seconds for the acknowlegment and return True.
309+
If no acknowlegment is received, False is returned.
310+
"""
298311
# Build frame data with command and parameters.
299312
data = bytearray(2 + len(params))
300313
data[0] = _HOSTTOPN532
@@ -306,12 +319,21 @@ def call_function(
306319
self._write_frame(data)
307320
except OSError:
308321
self._wakeup()
309-
return None
322+
return False
310323
if not self._wait_ready(timeout):
311-
return None
324+
return False
312325
# Verify ACK response and wait to be ready for function response.
313326
if not _ACK == self._read_data(len(_ACK)):
314327
raise RuntimeError("Did not receive expected ACK from PN532!")
328+
return True
329+
330+
def process_response(self, command, response_length=0, timeout=1):
331+
"""Process the response from the PN532 and expect up to response_length
332+
bytes back in a response. Note that less than the expected bytes might
333+
be returned! Will wait up to timeout seconds for a response and return
334+
a bytearray of response bytes, or None if no response is available
335+
within the timeout.
336+
"""
315337
if not self._wait_ready(timeout):
316338
return None
317339
# Read response bytes.
@@ -348,15 +370,41 @@ def read_passive_target(self, card_baud=_MIFARE_ISO14443A, timeout=1):
348370
otherwise a bytearray with the UID of the found card is returned.
349371
"""
350372
# Send passive read command for 1 card. Expect at most a 7 byte UUID.
373+
response = self.listen_for_passive_target(card_baud=card_baud, timeout=timeout)
374+
# If no response is available return None to indicate no card is present.
375+
if not response:
376+
return None
377+
return self.get_passive_target(timeout=timeout)
378+
379+
def listen_for_passive_target(self, card_baud=_MIFARE_ISO14443A, timeout=1):
380+
"""Send command to PN532 to begin listening for a Mifare card. This
381+
returns True if the command was received succesfully. Note, this does
382+
not also return the UID of a card! `get_passive_target` must be called
383+
to read the UID when a card is found. If just looking to see if a card
384+
is currently present use `read_passive_target` instead.
385+
"""
386+
# Send passive read command for 1 card. Expect at most a 7 byte UUID.
351387
try:
352-
response = self.call_function(
353-
_COMMAND_INLISTPASSIVETARGET,
354-
params=[0x01, card_baud],
355-
response_length=19,
356-
timeout=timeout,
388+
response = self.send_command(
389+
_COMMAND_INLISTPASSIVETARGET, params=[0x01, card_baud], timeout=timeout
357390
)
358391
except BusyError:
359-
return None # no card found!
392+
return False # _COMMAND_INLISTPASSIVETARGET failed
393+
return response
394+
395+
def get_passive_target(self, timeout=1):
396+
"""Will wait up to timeout seconds and return None if no card is found,
397+
otherwise a bytearray with the UID of the found card is returned.
398+
`listen_for_passive_target` must have been called first in order to put
399+
the PN532 into a listening mode.
400+
401+
It can be useful to use this when using the IRQ pin. Use the IRQ pin to
402+
detect when a card is present and then call this function to read the
403+
card's UID. This reduces the amount of time spend checking for a card.
404+
"""
405+
response = self.process_response(
406+
_COMMAND_INLISTPASSIVETARGET, response_length=19, timeout=timeout
407+
)
360408
# If no response is available return None to indicate no card is present.
361409
if response is None:
362410
return None

examples/pn532_simplelisten.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
This example shows connecting to the PN532 with I2C (requires clock
3+
stretching support), SPI, or UART. SPI is best, it uses the most pins but
4+
is the most reliable and universally supported. In this example, we also connect
5+
IRQ and poll that pin for a card. We don't try to read the card until we know
6+
there is one present. After initialization, try waving various 13.56MHz RFID
7+
cards over it!
8+
"""
9+
10+
import time
11+
import board
12+
import busio
13+
from digitalio import DigitalInOut
14+
15+
#
16+
# NOTE: pick the import that matches the interface being used
17+
#
18+
from adafruit_pn532.i2c import PN532_I2C
19+
20+
# from adafruit_pn532.spi import PN532_SPI
21+
# from adafruit_pn532.uart import PN532_UART
22+
23+
# I2C connection:
24+
i2c = busio.I2C(board.SCL, board.SDA)
25+
26+
# Non-hardware
27+
# pn532 = PN532_I2C(i2c, debug=False)
28+
29+
# With I2C, we recommend connecting RSTPD_N (reset) to a digital pin for manual
30+
# harware reset
31+
reset_pin = DigitalInOut(board.D6)
32+
# On Raspberry Pi, you must also connect a pin to P32 "H_Request" for hardware
33+
# wakeup! this means we don't need to do the I2C clock-stretch thing
34+
req_pin = DigitalInOut(board.D12)
35+
# Using the IRQ pin allows us to determine when a card is present by checking
36+
# to see if the pin is pulled low.
37+
irq_pin = DigitalInOut(board.D10)
38+
pn532 = PN532_I2C(i2c, debug=False, reset=reset_pin, req=req_pin, irq=irq_pin)
39+
40+
# SPI connection:
41+
# spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
42+
# cs_pin = DigitalInOut(board.D5)
43+
# pn532 = PN532_SPI(spi, cs_pin, debug=False)
44+
45+
# UART connection
46+
# uart = busio.UART(board.TX, board.RX, baudrate=115200, timeout=100)
47+
# pn532 = PN532_UART(uart, debug=False)
48+
49+
ic, ver, rev, support = pn532.firmware_version
50+
print("Found PN532 with firmware version: {0}.{1}".format(ver, rev))
51+
52+
# Configure PN532 to communicate with MiFare cards
53+
pn532.SAM_configuration()
54+
55+
# Start listening for a card
56+
pn532.listen_for_passive_target()
57+
print("Waiting for RFID/NFC card...")
58+
while True:
59+
# Check if a card is available to read
60+
if irq_pin.value == 0:
61+
uid = pn532.get_passive_target()
62+
print("Found card with UID:", [hex(i) for i in uid])
63+
# Start listening for a card again
64+
pn532.listen_for_passive_target()
65+
else:
66+
print(".", end="")
67+
time.sleep(0.1)

0 commit comments

Comments
 (0)