Merge pull request #41 from dunkmann00/performance-and-sleep

Improve performance and add ability to sleep
This commit is contained in:
Scott Shawcroft 2020-09-18 10:46:13 -07:00 committed by GitHub
commit b741c77cee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 60 deletions

View file

@ -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,

View file

@ -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):

View file

@ -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

View file

@ -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)

View 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)