Merge pull request #41 from dunkmann00/performance-and-sleep
Improve performance and add ability to sleep
This commit is contained in:
commit
b741c77cee
5 changed files with 129 additions and 60 deletions
|
@ -164,17 +164,6 @@ _ACK = b"\x00\x00\xFF\x00\xFF\x00"
|
|||
_FRAME_START = b"\x00\x00\xFF"
|
||||
|
||||
|
||||
def _reset(pin):
|
||||
"""Perform a hardware reset toggle"""
|
||||
pin.direction = Direction.OUTPUT
|
||||
pin.value = True
|
||||
time.sleep(0.1)
|
||||
pin.value = False
|
||||
time.sleep(0.5)
|
||||
pin.value = True
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
class BusyError(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
|
||||
|
@ -182,21 +171,14 @@ class BusyError(Exception):
|
|||
class PN532:
|
||||
"""PN532 driver base, must be extended for I2C/SPI/UART interfacing"""
|
||||
|
||||
def __init__(self, *, debug=False, reset=None):
|
||||
def __init__(self, *, debug=False, irq=None, reset=None):
|
||||
"""Create an instance of the PN532 class
|
||||
"""
|
||||
self.low_power = True
|
||||
self.debug = debug
|
||||
if reset:
|
||||
if debug:
|
||||
print("Resetting")
|
||||
_reset(reset)
|
||||
|
||||
try:
|
||||
self._wakeup()
|
||||
_ = self.firmware_version # first time often fails, try 2ce
|
||||
return
|
||||
except (BusyError, RuntimeError):
|
||||
pass
|
||||
self._irq = irq
|
||||
self._reset_pin = reset
|
||||
self.reset()
|
||||
_ = self.firmware_version
|
||||
|
||||
def _read_data(self, count):
|
||||
|
@ -218,6 +200,18 @@ class PN532:
|
|||
# Send special command to wake up
|
||||
raise NotImplementedError
|
||||
|
||||
def reset(self):
|
||||
"""Perform a hardware reset toggle and then wake up the PN532"""
|
||||
if self._reset_pin:
|
||||
if self.debug:
|
||||
print("Resetting")
|
||||
self._reset_pin.direction = Direction.OUTPUT
|
||||
self._reset_pin.value = False
|
||||
time.sleep(0.1)
|
||||
self._reset_pin.value = True
|
||||
time.sleep(0.1)
|
||||
self._wakeup()
|
||||
|
||||
def _write_frame(self, data):
|
||||
"""Write a frame to the PN532 with the specified data bytearray."""
|
||||
assert (
|
||||
|
@ -255,7 +249,7 @@ class PN532:
|
|||
might be returned!
|
||||
"""
|
||||
# Read frame with expected length of data.
|
||||
response = self._read_data(length + 8)
|
||||
response = self._read_data(length + 7)
|
||||
if self.debug:
|
||||
print("Read frame:", [hex(i) for i in response])
|
||||
|
||||
|
@ -306,6 +300,9 @@ class PN532:
|
|||
Will wait up to timeout seconds for the acknowlegment and return True.
|
||||
If no acknowlegment is received, False is returned.
|
||||
"""
|
||||
if self.low_power:
|
||||
self._wakeup()
|
||||
|
||||
# Build frame data with command and parameters.
|
||||
data = bytearray(2 + len(params))
|
||||
data[0] = _HOSTTOPN532
|
||||
|
@ -316,7 +313,6 @@ class PN532:
|
|||
try:
|
||||
self._write_frame(data)
|
||||
except OSError:
|
||||
self._wakeup()
|
||||
return False
|
||||
if not self._wait_ready(timeout):
|
||||
return False
|
||||
|
@ -342,6 +338,21 @@ class PN532:
|
|||
# Return response data.
|
||||
return response[2:]
|
||||
|
||||
def power_down(self):
|
||||
"""Put the PN532 into a low power state. If the reset pin is connected a
|
||||
hard power down is performed, if not, a soft power down is performed
|
||||
instead. Returns True if the PN532 was powered down successfully or
|
||||
False if not."""
|
||||
if self._reset_pin: # Hard Power Down if the reset pin is connected
|
||||
self._reset_pin.value = False
|
||||
self.low_power = True
|
||||
else:
|
||||
# Soft Power Down otherwise. Enable wakeup on I2C, SPI, UART
|
||||
response = self.call_function(_COMMAND_POWERDOWN, params=[0xB0, 0x00])
|
||||
self.low_power = response[0] == 0x00
|
||||
time.sleep(0.005)
|
||||
return self.low_power
|
||||
|
||||
@property
|
||||
def firmware_version(self):
|
||||
"""Call PN532 GetFirmwareVersion function and return a tuple with the IC,
|
||||
|
|
|
@ -41,7 +41,7 @@ import time
|
|||
import adafruit_bus_device.i2c_device as i2c_device
|
||||
from digitalio import Direction
|
||||
from micropython import const
|
||||
from adafruit_pn532.adafruit_pn532 import PN532, BusyError, _reset
|
||||
from adafruit_pn532.adafruit_pn532 import PN532, BusyError
|
||||
|
||||
_I2C_ADDRESS = const(0x24)
|
||||
|
||||
|
@ -55,23 +55,23 @@ class PN532_I2C(PN532):
|
|||
reset pin and debugging output.
|
||||
"""
|
||||
self.debug = debug
|
||||
self._irq = irq
|
||||
self._req = req
|
||||
if reset:
|
||||
_reset(reset)
|
||||
self._i2c = i2c_device.I2CDevice(i2c, _I2C_ADDRESS)
|
||||
super().__init__(debug=debug, reset=reset)
|
||||
super().__init__(debug=debug, irq=irq, reset=reset)
|
||||
|
||||
def _wakeup(self): # pylint: disable=no-self-use
|
||||
"""Send any special commands/data to wake up PN532"""
|
||||
if self._reset_pin:
|
||||
self._reset_pin.value = True
|
||||
time.sleep(0.01)
|
||||
if self._req:
|
||||
self._req.direction = Direction.OUTPUT
|
||||
self._req.value = True
|
||||
time.sleep(0.1)
|
||||
self._req.value = False
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.01)
|
||||
self._req.value = True
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.01)
|
||||
self.low_power = False
|
||||
self.SAM_configuration() # Put the PN532 back in normal mode
|
||||
|
||||
def _wait_ready(self, timeout=1):
|
||||
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
|
||||
|
@ -82,11 +82,10 @@ class PN532_I2C(PN532):
|
|||
with self._i2c:
|
||||
self._i2c.readinto(status)
|
||||
except OSError:
|
||||
self._wakeup()
|
||||
continue
|
||||
if status == b"\x01":
|
||||
return True # No longer busy
|
||||
time.sleep(0.05) # lets ask again soon!
|
||||
time.sleep(0.01) # lets ask again soon!
|
||||
# Timed out!
|
||||
return False
|
||||
|
||||
|
@ -101,8 +100,6 @@ class PN532_I2C(PN532):
|
|||
i2c.readinto(frame) # ok get the data, plus statusbyte
|
||||
if self.debug:
|
||||
print("Reading: ", [hex(i) for i in frame[1:]])
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
return frame[1:] # don't return the status byte
|
||||
|
||||
def _write_data(self, framebytes):
|
||||
|
|
|
@ -67,16 +67,19 @@ class PN532_SPI(PN532):
|
|||
def __init__(self, spi, cs_pin, *, irq=None, reset=None, debug=False):
|
||||
"""Create an instance of the PN532 class using SPI"""
|
||||
self.debug = debug
|
||||
self._irq = irq
|
||||
self._spi = spi_device.SPIDevice(spi, cs_pin)
|
||||
super().__init__(debug=debug, reset=reset)
|
||||
super().__init__(debug=debug, irq=irq, reset=reset)
|
||||
|
||||
def _wakeup(self):
|
||||
"""Send any special commands/data to wake up PN532"""
|
||||
if self._reset_pin:
|
||||
self._reset_pin.value = True
|
||||
time.sleep(0.01)
|
||||
with self._spi as spi:
|
||||
time.sleep(1)
|
||||
spi.write(bytearray([0x00])) # pylint: disable=no-member
|
||||
time.sleep(1)
|
||||
time.sleep(0.01)
|
||||
self.low_power = False
|
||||
self.SAM_configuration() # Put the PN532 back in normal mode
|
||||
|
||||
def _wait_ready(self, timeout=1):
|
||||
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
|
||||
|
@ -85,7 +88,6 @@ class PN532_SPI(PN532):
|
|||
timestamp = time.monotonic()
|
||||
with self._spi as spi:
|
||||
while (time.monotonic() - timestamp) < timeout:
|
||||
time.sleep(0.02) # required
|
||||
spi.write_readinto(
|
||||
status_cmd, status_response
|
||||
) # pylint: disable=no-member
|
||||
|
@ -103,7 +105,6 @@ class PN532_SPI(PN532):
|
|||
frame[0] = reverse_bit(_SPI_DATAREAD)
|
||||
|
||||
with self._spi as spi:
|
||||
time.sleep(0.02) # required
|
||||
spi.write_readinto(frame, frame) # pylint: disable=no-member
|
||||
for i, val in enumerate(frame):
|
||||
frame[i] = reverse_bit(val) # turn LSB data to MSB
|
||||
|
@ -119,5 +120,4 @@ class PN532_SPI(PN532):
|
|||
if self.debug:
|
||||
print("Writing: ", [hex(i) for i in rev_frame])
|
||||
with self._spi as spi:
|
||||
time.sleep(0.02) # required
|
||||
spi.write(bytes(rev_frame)) # pylint: disable=no-member
|
||||
|
|
|
@ -45,24 +45,34 @@ from adafruit_pn532.adafruit_pn532 import PN532, BusyError
|
|||
class PN532_UART(PN532):
|
||||
"""Driver for the PN532 connected over Serial UART"""
|
||||
|
||||
def __init__(self, uart, *, irq=None, reset=None, debug=False):
|
||||
def __init__(self, uart, *, reset=None, debug=False):
|
||||
"""Create an instance of the PN532 class using Serial connection.
|
||||
Optional IRQ pin (not used), reset pin and debugging output.
|
||||
Optional reset pin and debugging output.
|
||||
"""
|
||||
self.debug = debug
|
||||
self._irq = irq
|
||||
self._uart = uart
|
||||
super().__init__(debug=debug, reset=reset)
|
||||
|
||||
def _wakeup(self):
|
||||
"""Send any special commands/data to wake up PN532"""
|
||||
# self._write_frame([_HOSTTOPN532, _COMMAND_SAMCONFIGURATION, 0x01])
|
||||
if self._reset_pin:
|
||||
self._reset_pin.value = True
|
||||
time.sleep(0.01)
|
||||
self.low_power = False
|
||||
self._uart.write(
|
||||
b"\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
) # wake up!
|
||||
self.SAM_configuration()
|
||||
|
||||
def _wait_ready(self, timeout=1):
|
||||
"""Wait `timeout` seconds"""
|
||||
time.sleep(timeout)
|
||||
return True
|
||||
timestamp = time.monotonic()
|
||||
while (time.monotonic() - timestamp) < timeout:
|
||||
if self._uart.in_waiting > 0:
|
||||
return True # No Longer Busy
|
||||
time.sleep(0.01) # lets ask again soon!
|
||||
# Timed out!
|
||||
return False
|
||||
|
||||
def _read_data(self, count):
|
||||
"""Read a specified count of bytes from the PN532."""
|
||||
|
@ -71,17 +81,9 @@ class PN532_UART(PN532):
|
|||
raise BusyError("No data read from PN532")
|
||||
if self.debug:
|
||||
print("Reading: ", [hex(i) for i in frame])
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
return frame
|
||||
|
||||
def _write_data(self, framebytes):
|
||||
"""Write a specified count of bytes to the PN532"""
|
||||
while self._uart.read(
|
||||
1
|
||||
): # this would be a lot nicer if we could query the # of bytes
|
||||
pass
|
||||
self._uart.write(
|
||||
"\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
) # wake up!
|
||||
self._uart.reset_input_buffer()
|
||||
self._uart.write(framebytes)
|
||||
|
|
59
examples/pn532_low_power.py
Normal file
59
examples/pn532_low_power.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
"""
|
||||
This example shows connecting to the PN532 with I2C (requires clock
|
||||
stretching support), SPI, or UART. SPI is best, it uses the most pins but
|
||||
is the most reliable and universally supported. In this example we put the PN532
|
||||
into low power mode and sleep for 1 second in-between trying to read tags.
|
||||
After initialization, try waving various 13.56MHz RFID cards over it!
|
||||
"""
|
||||
|
||||
import time
|
||||
import board
|
||||
import busio
|
||||
from digitalio import DigitalInOut
|
||||
|
||||
#
|
||||
# NOTE: pick the import that matches the interface being used
|
||||
#
|
||||
from adafruit_pn532.i2c import PN532_I2C
|
||||
|
||||
# from adafruit_pn532.spi import PN532_SPI
|
||||
# from adafruit_pn532.uart import PN532_UART
|
||||
|
||||
# I2C connection:
|
||||
i2c = busio.I2C(board.SCL, board.SDA)
|
||||
|
||||
# Non-hardware
|
||||
# pn532 = PN532_I2C(i2c, debug=False)
|
||||
|
||||
# With I2C, we recommend connecting RSTPD_N (reset) to a digital pin for manual
|
||||
# harware reset
|
||||
reset_pin = DigitalInOut(board.D6)
|
||||
# On Raspberry Pi, you must also connect a pin to P32 "H_Request" for hardware
|
||||
# wakeup! this means we don't need to do the I2C clock-stretch thing
|
||||
req_pin = DigitalInOut(board.D12)
|
||||
pn532 = PN532_I2C(i2c, debug=False, reset=reset_pin, req=req_pin)
|
||||
|
||||
# SPI connection:
|
||||
# spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
|
||||
# cs_pin = DigitalInOut(board.D5)
|
||||
# pn532 = PN532_SPI(spi, cs_pin, debug=False)
|
||||
|
||||
# UART connection
|
||||
# uart = busio.UART(board.TX, board.RX, baudrate=115200, timeout=0.1)
|
||||
# pn532 = PN532_UART(uart, debug=False)
|
||||
|
||||
ic, ver, rev, support = pn532.firmware_version
|
||||
print("Found PN532 with firmware version: {0}.{1}".format(ver, rev))
|
||||
|
||||
# Configure PN532 to communicate with MiFare cards
|
||||
pn532.SAM_configuration()
|
||||
|
||||
print("Waiting for RFID/NFC card...")
|
||||
while True:
|
||||
# Check if a card is available to read
|
||||
uid = pn532.read_passive_target(timeout=0.5)
|
||||
print(".", end="")
|
||||
if uid is not None:
|
||||
print("Found card with UID:", [hex(i) for i in uid])
|
||||
pn532.power_down()
|
||||
time.sleep(1.0)
|
Loading…
Reference in a new issue