Skip to content

Commit b741c77

Browse files
authored
Merge pull request #41 from dunkmann00/performance-and-sleep
Improve performance and add ability to sleep
2 parents 62ddaaf + 7d7e3ce commit b741c77

File tree

5 files changed

+129
-60
lines changed

5 files changed

+129
-60
lines changed

adafruit_pn532/adafruit_pn532.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -164,39 +164,21 @@
164164
_FRAME_START = b"\x00\x00\xFF"
165165

166166

167-
def _reset(pin):
168-
"""Perform a hardware reset toggle"""
169-
pin.direction = Direction.OUTPUT
170-
pin.value = True
171-
time.sleep(0.1)
172-
pin.value = False
173-
time.sleep(0.5)
174-
pin.value = True
175-
time.sleep(0.1)
176-
177-
178167
class BusyError(Exception):
179168
"""Base class for exceptions in this module."""
180169

181170

182171
class PN532:
183172
"""PN532 driver base, must be extended for I2C/SPI/UART interfacing"""
184173

185-
def __init__(self, *, debug=False, reset=None):
174+
def __init__(self, *, debug=False, irq=None, reset=None):
186175
"""Create an instance of the PN532 class
187176
"""
177+
self.low_power = True
188178
self.debug = debug
189-
if reset:
190-
if debug:
191-
print("Resetting")
192-
_reset(reset)
193-
194-
try:
195-
self._wakeup()
196-
_ = self.firmware_version # first time often fails, try 2ce
197-
return
198-
except (BusyError, RuntimeError):
199-
pass
179+
self._irq = irq
180+
self._reset_pin = reset
181+
self.reset()
200182
_ = self.firmware_version
201183

202184
def _read_data(self, count):
@@ -218,6 +200,18 @@ def _wakeup(self):
218200
# Send special command to wake up
219201
raise NotImplementedError
220202

203+
def reset(self):
204+
"""Perform a hardware reset toggle and then wake up the PN532"""
205+
if self._reset_pin:
206+
if self.debug:
207+
print("Resetting")
208+
self._reset_pin.direction = Direction.OUTPUT
209+
self._reset_pin.value = False
210+
time.sleep(0.1)
211+
self._reset_pin.value = True
212+
time.sleep(0.1)
213+
self._wakeup()
214+
221215
def _write_frame(self, data):
222216
"""Write a frame to the PN532 with the specified data bytearray."""
223217
assert (
@@ -255,7 +249,7 @@ def _read_frame(self, length):
255249
might be returned!
256250
"""
257251
# Read frame with expected length of data.
258-
response = self._read_data(length + 8)
252+
response = self._read_data(length + 7)
259253
if self.debug:
260254
print("Read frame:", [hex(i) for i in response])
261255

@@ -306,6 +300,9 @@ def send_command(
306300
Will wait up to timeout seconds for the acknowlegment and return True.
307301
If no acknowlegment is received, False is returned.
308302
"""
303+
if self.low_power:
304+
self._wakeup()
305+
309306
# Build frame data with command and parameters.
310307
data = bytearray(2 + len(params))
311308
data[0] = _HOSTTOPN532
@@ -316,7 +313,6 @@ def send_command(
316313
try:
317314
self._write_frame(data)
318315
except OSError:
319-
self._wakeup()
320316
return False
321317
if not self._wait_ready(timeout):
322318
return False
@@ -342,6 +338,21 @@ def process_response(self, command, response_length=0, timeout=1):
342338
# Return response data.
343339
return response[2:]
344340

341+
def power_down(self):
342+
"""Put the PN532 into a low power state. If the reset pin is connected a
343+
hard power down is performed, if not, a soft power down is performed
344+
instead. Returns True if the PN532 was powered down successfully or
345+
False if not."""
346+
if self._reset_pin: # Hard Power Down if the reset pin is connected
347+
self._reset_pin.value = False
348+
self.low_power = True
349+
else:
350+
# Soft Power Down otherwise. Enable wakeup on I2C, SPI, UART
351+
response = self.call_function(_COMMAND_POWERDOWN, params=[0xB0, 0x00])
352+
self.low_power = response[0] == 0x00
353+
time.sleep(0.005)
354+
return self.low_power
355+
345356
@property
346357
def firmware_version(self):
347358
"""Call PN532 GetFirmwareVersion function and return a tuple with the IC,

adafruit_pn532/i2c.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import adafruit_bus_device.i2c_device as i2c_device
4242
from digitalio import Direction
4343
from micropython import const
44-
from adafruit_pn532.adafruit_pn532 import PN532, BusyError, _reset
44+
from adafruit_pn532.adafruit_pn532 import PN532, BusyError
4545

4646
_I2C_ADDRESS = const(0x24)
4747

@@ -55,23 +55,23 @@ def __init__(self, i2c, *, irq=None, reset=None, req=None, debug=False):
5555
reset pin and debugging output.
5656
"""
5757
self.debug = debug
58-
self._irq = irq
5958
self._req = req
60-
if reset:
61-
_reset(reset)
6259
self._i2c = i2c_device.I2CDevice(i2c, _I2C_ADDRESS)
63-
super().__init__(debug=debug, reset=reset)
60+
super().__init__(debug=debug, irq=irq, reset=reset)
6461

6562
def _wakeup(self): # pylint: disable=no-self-use
6663
"""Send any special commands/data to wake up PN532"""
64+
if self._reset_pin:
65+
self._reset_pin.value = True
66+
time.sleep(0.01)
6767
if self._req:
6868
self._req.direction = Direction.OUTPUT
69-
self._req.value = True
70-
time.sleep(0.1)
7169
self._req.value = False
72-
time.sleep(0.1)
70+
time.sleep(0.01)
7371
self._req.value = True
74-
time.sleep(0.5)
72+
time.sleep(0.01)
73+
self.low_power = False
74+
self.SAM_configuration() # Put the PN532 back in normal mode
7575

7676
def _wait_ready(self, timeout=1):
7777
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
@@ -82,11 +82,10 @@ def _wait_ready(self, timeout=1):
8282
with self._i2c:
8383
self._i2c.readinto(status)
8484
except OSError:
85-
self._wakeup()
8685
continue
8786
if status == b"\x01":
8887
return True # No longer busy
89-
time.sleep(0.05) # lets ask again soon!
88+
time.sleep(0.01) # lets ask again soon!
9089
# Timed out!
9190
return False
9291

@@ -101,8 +100,6 @@ def _read_data(self, count):
101100
i2c.readinto(frame) # ok get the data, plus statusbyte
102101
if self.debug:
103102
print("Reading: ", [hex(i) for i in frame[1:]])
104-
else:
105-
time.sleep(0.1)
106103
return frame[1:] # don't return the status byte
107104

108105
def _write_data(self, framebytes):

adafruit_pn532/spi.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,19 @@ class PN532_SPI(PN532):
6767
def __init__(self, spi, cs_pin, *, irq=None, reset=None, debug=False):
6868
"""Create an instance of the PN532 class using SPI"""
6969
self.debug = debug
70-
self._irq = irq
7170
self._spi = spi_device.SPIDevice(spi, cs_pin)
72-
super().__init__(debug=debug, reset=reset)
71+
super().__init__(debug=debug, irq=irq, reset=reset)
7372

7473
def _wakeup(self):
7574
"""Send any special commands/data to wake up PN532"""
75+
if self._reset_pin:
76+
self._reset_pin.value = True
77+
time.sleep(0.01)
7678
with self._spi as spi:
77-
time.sleep(1)
7879
spi.write(bytearray([0x00])) # pylint: disable=no-member
79-
time.sleep(1)
80+
time.sleep(0.01)
81+
self.low_power = False
82+
self.SAM_configuration() # Put the PN532 back in normal mode
8083

8184
def _wait_ready(self, timeout=1):
8285
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
@@ -85,7 +88,6 @@ def _wait_ready(self, timeout=1):
8588
timestamp = time.monotonic()
8689
with self._spi as spi:
8790
while (time.monotonic() - timestamp) < timeout:
88-
time.sleep(0.02) # required
8991
spi.write_readinto(
9092
status_cmd, status_response
9193
) # pylint: disable=no-member
@@ -103,7 +105,6 @@ def _read_data(self, count):
103105
frame[0] = reverse_bit(_SPI_DATAREAD)
104106

105107
with self._spi as spi:
106-
time.sleep(0.02) # required
107108
spi.write_readinto(frame, frame) # pylint: disable=no-member
108109
for i, val in enumerate(frame):
109110
frame[i] = reverse_bit(val) # turn LSB data to MSB
@@ -119,5 +120,4 @@ def _write_data(self, framebytes):
119120
if self.debug:
120121
print("Writing: ", [hex(i) for i in rev_frame])
121122
with self._spi as spi:
122-
time.sleep(0.02) # required
123123
spi.write(bytes(rev_frame)) # pylint: disable=no-member

adafruit_pn532/uart.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,34 @@
4545
class PN532_UART(PN532):
4646
"""Driver for the PN532 connected over Serial UART"""
4747

48-
def __init__(self, uart, *, irq=None, reset=None, debug=False):
48+
def __init__(self, uart, *, reset=None, debug=False):
4949
"""Create an instance of the PN532 class using Serial connection.
50-
Optional IRQ pin (not used), reset pin and debugging output.
50+
Optional reset pin and debugging output.
5151
"""
5252
self.debug = debug
53-
self._irq = irq
5453
self._uart = uart
5554
super().__init__(debug=debug, reset=reset)
5655

5756
def _wakeup(self):
5857
"""Send any special commands/data to wake up PN532"""
59-
# self._write_frame([_HOSTTOPN532, _COMMAND_SAMCONFIGURATION, 0x01])
58+
if self._reset_pin:
59+
self._reset_pin.value = True
60+
time.sleep(0.01)
61+
self.low_power = False
62+
self._uart.write(
63+
b"\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
64+
) # wake up!
6065
self.SAM_configuration()
6166

6267
def _wait_ready(self, timeout=1):
6368
"""Wait `timeout` seconds"""
64-
time.sleep(timeout)
65-
return True
69+
timestamp = time.monotonic()
70+
while (time.monotonic() - timestamp) < timeout:
71+
if self._uart.in_waiting > 0:
72+
return True # No Longer Busy
73+
time.sleep(0.01) # lets ask again soon!
74+
# Timed out!
75+
return False
6676

6777
def _read_data(self, count):
6878
"""Read a specified count of bytes from the PN532."""
@@ -71,17 +81,9 @@ def _read_data(self, count):
7181
raise BusyError("No data read from PN532")
7282
if self.debug:
7383
print("Reading: ", [hex(i) for i in frame])
74-
else:
75-
time.sleep(0.1)
7684
return frame
7785

7886
def _write_data(self, framebytes):
7987
"""Write a specified count of bytes to the PN532"""
80-
while self._uart.read(
81-
1
82-
): # this would be a lot nicer if we could query the # of bytes
83-
pass
84-
self._uart.write(
85-
"\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
86-
) # wake up!
88+
self._uart.reset_input_buffer()
8789
self._uart.write(framebytes)

examples/pn532_low_power.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 put the PN532
5+
into low power mode and sleep for 1 second in-between trying to read tags.
6+
After initialization, try waving various 13.56MHz RFID cards over it!
7+
"""
8+
9+
import time
10+
import board
11+
import busio
12+
from digitalio import DigitalInOut
13+
14+
#
15+
# NOTE: pick the import that matches the interface being used
16+
#
17+
from adafruit_pn532.i2c import PN532_I2C
18+
19+
# from adafruit_pn532.spi import PN532_SPI
20+
# from adafruit_pn532.uart import PN532_UART
21+
22+
# I2C connection:
23+
i2c = busio.I2C(board.SCL, board.SDA)
24+
25+
# Non-hardware
26+
# pn532 = PN532_I2C(i2c, debug=False)
27+
28+
# With I2C, we recommend connecting RSTPD_N (reset) to a digital pin for manual
29+
# harware reset
30+
reset_pin = DigitalInOut(board.D6)
31+
# On Raspberry Pi, you must also connect a pin to P32 "H_Request" for hardware
32+
# wakeup! this means we don't need to do the I2C clock-stretch thing
33+
req_pin = DigitalInOut(board.D12)
34+
pn532 = PN532_I2C(i2c, debug=False, reset=reset_pin, req=req_pin)
35+
36+
# SPI connection:
37+
# spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
38+
# cs_pin = DigitalInOut(board.D5)
39+
# pn532 = PN532_SPI(spi, cs_pin, debug=False)
40+
41+
# UART connection
42+
# uart = busio.UART(board.TX, board.RX, baudrate=115200, timeout=0.1)
43+
# pn532 = PN532_UART(uart, debug=False)
44+
45+
ic, ver, rev, support = pn532.firmware_version
46+
print("Found PN532 with firmware version: {0}.{1}".format(ver, rev))
47+
48+
# Configure PN532 to communicate with MiFare cards
49+
pn532.SAM_configuration()
50+
51+
print("Waiting for RFID/NFC card...")
52+
while True:
53+
# Check if a card is available to read
54+
uid = pn532.read_passive_target(timeout=0.5)
55+
print(".", end="")
56+
if uid is not None:
57+
print("Found card with UID:", [hex(i) for i in uid])
58+
pn532.power_down()
59+
time.sleep(1.0)

0 commit comments

Comments
 (0)