From 6bdd6711054d0243f2441112e53cbd1fb319858b Mon Sep 17 00:00:00 2001 From: caternuson Date: Tue, 28 Aug 2018 12:36:54 -0700 Subject: [PATCH] initial refactor --- adafruit_pn532/__init__.py | 0 .../adafruit_pn532.py | 186 +----------------- adafruit_pn532/i2c.py | 110 +++++++++++ adafruit_pn532/spi.py | 120 +++++++++++ adafruit_pn532/uart.py | 81 ++++++++ 5 files changed, 312 insertions(+), 185 deletions(-) create mode 100644 adafruit_pn532/__init__.py rename adafruit_pn532.py => adafruit_pn532/adafruit_pn532.py (73%) create mode 100644 adafruit_pn532/i2c.py create mode 100644 adafruit_pn532/spi.py create mode 100644 adafruit_pn532/uart.py diff --git a/adafruit_pn532/__init__.py b/adafruit_pn532/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/adafruit_pn532.py b/adafruit_pn532/adafruit_pn532.py similarity index 73% rename from adafruit_pn532.py rename to adafruit_pn532/adafruit_pn532.py index 8375d3c..b0818bd 100644 --- a/adafruit_pn532.py +++ b/adafruit_pn532/adafruit_pn532.py @@ -46,11 +46,8 @@ Implementation Notes * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ - import time from digitalio import Direction -import adafruit_bus_device.i2c_device as i2c_device -import adafruit_bus_device.spi_device as spi_device from micropython import const @@ -105,13 +102,6 @@ _RESPONSE_INLISTPASSIVETARGET = const(0x4B) _WAKEUP = const(0x55) -_SPI_STATREAD = const(0x02) -_SPI_DATAWRITE = const(0x01) -_SPI_DATAREAD = const(0x03) -_SPI_READY = const(0x01) - -_I2C_ADDRESS = const(0x24) - _MIFARE_ISO14443A = const(0x00) # Mifare Commands @@ -186,22 +176,12 @@ def _reset(pin): pin.value = True time.sleep(0.1) -def reverse_bit(num): - """Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as - it is LSB for the PN532, but 99% of SPI implementations are MSB only!""" - result = 0 - for _ in range(8): - result <<= 1 - result += (num & 1) - num >>= 1 - return result - - class BusyError(Exception): """Base class for exceptions in this module.""" pass + class PN532: """PN532 driver base, must be extended for I2C/SPI/UART interfacing""" @@ -464,167 +444,3 @@ class PN532: not read then None will be returned. """ return self.mifare_classic_read_block(block_number)[0:4] # only 4 bytes per page - -class PN532_UART(PN532): - """Driver for the PN532 connected over Serial UART""" - def __init__(self, uart, *, irq=None, reset=None, debug=False): - """Create an instance of the PN532 class using Serial connection. - Optional IRQ pin (not used), 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]) - self.SAM_configuration() - - def _wait_ready(self, timeout=1): - """Wait `timeout` seconds""" - time.sleep(timeout) - return True - - def _read_data(self, count): - """Read a specified count of bytes from the PN532.""" - frame = self._uart.read(count) - if not frame: - 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.write(framebytes) - -class PN532_I2C(PN532): - """Driver for the PN532 connected over I2C.""" - def __init__(self, i2c, *, irq=None, reset=None, req=None, debug=False): - """Create an instance of the PN532 class using I2C. Note that PN532 - uses clock stretching. Optional IRQ pin (not used), - 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) - - def _wakeup(self): # pylint: disable=no-self-use - """Send any special commands/data to wake up PN532""" - if self._req: - self._req.direction = Direction.OUTPUT - self._req.value = True - time.sleep(0.1) - self._req.value = False - time.sleep(0.1) - self._req.value = True - time.sleep(0.5) - - def _wait_ready(self, timeout=1): - """Poll PN532 if status byte is ready, up to `timeout` seconds""" - status = bytearray(1) - timestamp = time.monotonic() - while (time.monotonic() - timestamp) < timeout: - try: - with self._i2c: - self._i2c.readinto(status) - except OSError: - self._wakeup() - continue - if status == b'\x01': - return True # No longer busy - else: - time.sleep(0.05) # lets ask again soon! - # Timed out! - return False - - def _read_data(self, count): - """Read a specified count of bytes from the PN532.""" - # Build a read request frame. - frame = bytearray(count+1) - with self._i2c as i2c: - i2c.readinto(frame, end=1) # read status byte! - if frame[0] != 0x01: # not ready - raise BusyError - 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): - """Write a specified count of bytes to the PN532""" - with self._i2c as i2c: - i2c.write(framebytes) - -class PN532_SPI(PN532): - """Driver for the PN532 connected over SPI. Pass in a hardware or bitbang - SPI device & chip select digitalInOut pin. Optional IRQ pin (not used), - reset pin and debugging output.""" - 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) - - def _wakeup(self): - """Send any special commands/data to wake up PN532""" - with self._spi as spi: - time.sleep(1) - spi.write(bytearray([0x00])) #pylint: disable=no-member - time.sleep(1) - - def _wait_ready(self, timeout=1): - """Poll PN532 if status byte is ready, up to `timeout` seconds""" - status = bytearray([reverse_bit(_SPI_STATREAD), 0]) - - timestamp = time.monotonic() - while (time.monotonic() - timestamp) < timeout: - with self._spi as spi: - time.sleep(0.02) # required - spi.write_readinto(status, status) #pylint: disable=no-member - if reverse_bit(status[1]) == 0x01: # LSB data is read in MSB - return True # Not busy anymore! - else: - time.sleep(0.01) # pause a bit till we ask again - # We timed out! - return False - - def _read_data(self, count): - """Read a specified count of bytes from the PN532.""" - # Build a read request frame. - frame = bytearray(count+1) - # Add the SPI data read signal byte, but LSB'ify it - 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 - if self.debug: - print("Reading: ", [hex(i) for i in frame[1:]]) - return frame[1:] - - def _write_data(self, framebytes): - """Write a specified count of bytes to the PN532""" - # start by making a frame with data write in front, - # then rest of bytes, and LSBify it - rev_frame = [reverse_bit(x) for x in bytes([_SPI_DATAWRITE]) + framebytes] - 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 diff --git a/adafruit_pn532/i2c.py b/adafruit_pn532/i2c.py new file mode 100644 index 0000000..2103b11 --- /dev/null +++ b/adafruit_pn532/i2c.py @@ -0,0 +1,110 @@ +# Adafruit PN532 NFC/RFID control library. +# Author: Tony DiCola +# +# The MIT License (MIT) +# +# Copyright (c) 2015-2018 Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +``adafruit_pn532.i2c`` +==================================================== + +This module will let you communicate with a PN532 RFID/NFC shield or breakout +using I2C. + +* Author(s): Original Raspberry Pi code by Tony DiCola, CircuitPython by ladyada, + refactor by Carter Nelson + +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" + +import time +from adafruit_pn532.adafruit_pn532 import PN532 +import adafruit_bus_device.i2c_device as i2c_device + +from micropython import const + +_I2C_ADDRESS = const(0x24) + +class PN532_I2C(PN532): + """Driver for the PN532 connected over I2C.""" + def __init__(self, i2c, *, irq=None, reset=None, req=None, debug=False): + """Create an instance of the PN532 class using I2C. Note that PN532 + uses clock stretching. Optional IRQ pin (not used), + 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) + + def _wakeup(self): # pylint: disable=no-self-use + """Send any special commands/data to wake up PN532""" + if self._req: + self._req.direction = Direction.OUTPUT + self._req.value = True + time.sleep(0.1) + self._req.value = False + time.sleep(0.1) + self._req.value = True + time.sleep(0.5) + + def _wait_ready(self, timeout=1): + """Poll PN532 if status byte is ready, up to `timeout` seconds""" + status = bytearray(1) + timestamp = time.monotonic() + while (time.monotonic() - timestamp) < timeout: + try: + with self._i2c: + self._i2c.readinto(status) + except OSError: + self._wakeup() + continue + if status == b'\x01': + return True # No longer busy + else: + time.sleep(0.05) # lets ask again soon! + # Timed out! + return False + + def _read_data(self, count): + """Read a specified count of bytes from the PN532.""" + # Build a read request frame. + frame = bytearray(count+1) + with self._i2c as i2c: + i2c.readinto(frame, end=1) # read status byte! + if frame[0] != 0x01: # not ready + raise pn532.BusyError + 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): + """Write a specified count of bytes to the PN532""" + with self._i2c as i2c: + i2c.write(framebytes) \ No newline at end of file diff --git a/adafruit_pn532/spi.py b/adafruit_pn532/spi.py new file mode 100644 index 0000000..fac6d31 --- /dev/null +++ b/adafruit_pn532/spi.py @@ -0,0 +1,120 @@ +# Adafruit PN532 NFC/RFID control library. +# Author: Tony DiCola +# +# The MIT License (MIT) +# +# Copyright (c) 2015-2018 Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +``adafruit_pn532.spi`` +==================================================== + +This module will let you communicate with a PN532 RFID/NFC shield or breakout +using SPI. + +* Author(s): Original Raspberry Pi code by Tony DiCola, CircuitPython by ladyada, + refactor by Carter Nelson + +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" + +import time +from adafruit_pn532.adafruit_pn532 import PN532 +import adafruit_bus_device.spi_device as spi_device + +from micropython import const + +_SPI_STATREAD = const(0x02) +_SPI_DATAWRITE = const(0x01) +_SPI_DATAREAD = const(0x03) +_SPI_READY = const(0x01) + +def reverse_bit(num): + """Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as + it is LSB for the PN532, but 99% of SPI implementations are MSB only!""" + result = 0 + for _ in range(8): + result <<= 1 + result += (num & 1) + num >>= 1 + return result + +class PN532_SPI(PN532): + """Driver for the PN532 connected over SPI. Pass in a hardware or bitbang + SPI device & chip select digitalInOut pin. Optional IRQ pin (not used), + reset pin and debugging output.""" + 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) + + def _wakeup(self): + """Send any special commands/data to wake up PN532""" + with self._spi as spi: + time.sleep(1) + spi.write(bytearray([0x00])) #pylint: disable=no-member + time.sleep(1) + + def _wait_ready(self, timeout=1): + """Poll PN532 if status byte is ready, up to `timeout` seconds""" + status = bytearray([reverse_bit(_SPI_STATREAD), 0]) + + timestamp = time.monotonic() + while (time.monotonic() - timestamp) < timeout: + with self._spi as spi: + time.sleep(0.02) # required + spi.write_readinto(status, status) #pylint: disable=no-member + if reverse_bit(status[1]) == 0x01: # LSB data is read in MSB + return True # Not busy anymore! + else: + time.sleep(0.01) # pause a bit till we ask again + # We timed out! + return False + + def _read_data(self, count): + """Read a specified count of bytes from the PN532.""" + # Build a read request frame. + frame = bytearray(count+1) + # Add the SPI data read signal byte, but LSB'ify it + 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 + if self.debug: + print("Reading: ", [hex(i) for i in frame[1:]]) + return frame[1:] + + def _write_data(self, framebytes): + """Write a specified count of bytes to the PN532""" + # start by making a frame with data write in front, + # then rest of bytes, and LSBify it + rev_frame = [reverse_bit(x) for x in bytes([_SPI_DATAWRITE]) + framebytes] + 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 diff --git a/adafruit_pn532/uart.py b/adafruit_pn532/uart.py new file mode 100644 index 0000000..1faea84 --- /dev/null +++ b/adafruit_pn532/uart.py @@ -0,0 +1,81 @@ +# Adafruit PN532 NFC/RFID control library. +# Author: Tony DiCola +# +# The MIT License (MIT) +# +# Copyright (c) 2015-2018 Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +``adafruit_pn532.uart`` +==================================================== + +This module will let you communicate with a PN532 RFID/NFC shield or breakout +using UART. + +* Author(s): Original Raspberry Pi code by Tony DiCola, CircuitPython by ladyada, + refactor by Carter Nelson + +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" + + +import time +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): + """Create an instance of the PN532 class using Serial connection. + Optional IRQ pin (not used), 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]) + self.SAM_configuration() + + def _wait_ready(self, timeout=1): + """Wait `timeout` seconds""" + time.sleep(timeout) + return True + + def _read_data(self, count): + """Read a specified count of bytes from the PN532.""" + frame = self._uart.read(count) + if not frame: + 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.write(framebytes)