Skip to content

Commit a6c3244

Browse files
author
Your Name
committed
Pylint, doc, and better raspi support
1 parent 3d55059 commit a6c3244

File tree

6 files changed

+134
-74
lines changed

6 files changed

+134
-74
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Introduction
1313
:target: https://travis-ci.org/adafruit/adafruit_CircuitPython_PN532
1414
:alt: Build Status
1515

16-
.. todo:: Describe what the library does.
16+
CircuitPython driver for the `PN532 NFC/RFID Breakout <https://www.adafruit.com/product/364>`_ and `PN532 NFC/RFID Shield <https://www.adafruit.com/product/789>`_
1717

1818
Dependencies
1919
=============
@@ -29,7 +29,7 @@ This is easily achieved by downloading
2929
Usage Example
3030
=============
3131

32-
.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst.
32+
Check examples/pn532_simpletest.py for usage example
3333

3434
Contributing
3535
============

adafruit_pn532.py

Lines changed: 102 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424
# SOFTWARE.
2525
"""
26-
`adafruit_PN532`
26+
``adafruit_pn532``
2727
====================================================
2828
2929
This module will let you communicate with a PN532 RFID/NFC shield or breakout
@@ -43,13 +43,12 @@
4343
4444
* Adafruit CircuitPython firmware for the supported boards:
4545
https://github.com/adafruit/circuitpython/releases
46-
47-
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
46+
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
4847
"""
4948

5049

5150
import time
52-
from digitalio import DigitalInOut, Direction
51+
from digitalio import Direction
5352
import adafruit_bus_device.i2c_device as i2c_device
5453
import adafruit_bus_device.spi_device as spi_device
5554

@@ -58,7 +57,7 @@
5857
__version__ = "0.0.0-auto.0"
5958
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git"
6059

61-
60+
# pylint: disable=bad-whitespace
6261
_PREAMBLE = const(0x00)
6362
_STARTCODE1 = const(0x00)
6463
_STARTCODE2 = const(0xFF)
@@ -174,8 +173,22 @@
174173

175174
_ACK = b'\x00\x00\xFF\x00\xFF\x00'
176175
_FRAME_START = b'\x00\x00\xFF'
176+
# pylint: enable=bad-whitespace
177+
178+
179+
def _reset(pin):
180+
"""Perform a hardware reset toggle"""
181+
pin.direction = Direction.OUTPUT
182+
pin.value = True
183+
time.sleep(0.1)
184+
pin.value = False
185+
time.sleep(0.5)
186+
pin.value = True
187+
time.sleep(0.1)
177188

178189
def reverse_bit(num):
190+
"""Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as
191+
it is LSB for the PN532, but 99% of SPI implementations are MSB only!"""
179192
result = 0
180193
for _ in range(8):
181194
result <<= 1
@@ -197,26 +210,23 @@ def __init__(self, *, debug=False, reset=None):
197210
"""
198211
self.debug = debug
199212
if reset:
200-
reset.direction = Direction.OUTPUT
201-
reset.value = True
202-
time.sleep(0.1)
203-
reset.value = False
204-
time.sleep(0.1)
205-
reset.value = True
206-
time.sleep(1)
213+
if debug:
214+
print("Resetting")
215+
_reset(reset)
216+
207217
try:
208218
self._wakeup()
209219
self.get_firmware_version() # first time often fails, try 2ce
210220
return
211-
except:
221+
except (BusyError, RuntimeError):
212222
pass
213223
self.get_firmware_version()
214224

215225
def _read_data(self, count):
216226
# Read raw data from device, not including status bytes:
217227
# Subclasses MUST implement this!
218228
raise NotImplementedError
219-
229+
220230
def _write_data(self, framebytes):
221231
# Write raw bytestring data to device, not including status bytes:
222232
# Subclasses MUST implement this!
@@ -227,10 +237,13 @@ def _wait_ready(self, timeout):
227237
# Subclasses MUST implement this!
228238
raise NotImplementedError
229239

240+
def _wakeup(self):
241+
# Send special command to wake up
242+
raise NotImplementedError
230243

231244
def _write_frame(self, data):
232245
"""Write a frame to the PN532 with the specified data bytearray."""
233-
assert data is not None and 0 < len(data) < 255, 'Data must be array of 1 to 255 bytes.'
246+
assert data is not None and 1 < len(data) < 255, 'Data must be array of 1 to 255 bytes.'
234247
# Build frame to send as:
235248
# - Preamble (0x00)
236249
# - Start code (0x00, 0xFF)
@@ -266,6 +279,7 @@ def _read_frame(self, length):
266279
response = self._read_data(length+8)
267280
if self.debug:
268281
print('Read frame:', [hex(i) for i in response])
282+
269283
# Swallow all the 0x00 values that preceed 0xFF.
270284
offset = 0
271285
while response[offset] == 0x00:
@@ -276,7 +290,7 @@ def _read_frame(self, length):
276290
raise RuntimeError('Response frame preamble does not contain 0x00FF!')
277291
offset += 1
278292
if offset >= len(response):
279-
raise RuntimeError('Response contains no data!')
293+
raise RuntimeError('Response contains no data!')
280294
# Check length & length checksum match.
281295
frame_len = response[offset]
282296
if (frame_len + response[offset+1]) & 0xFF != 0:
@@ -288,7 +302,7 @@ def _read_frame(self, length):
288302
# Return frame data.
289303
return response[offset+2:offset+2+frame_len]
290304

291-
def call_function(self, command, response_length=0, params=[], timeout=1):
305+
def call_function(self, command, response_length=0, params=[], timeout=1): # pylint: disable=dangerous-default-value
292306
"""Send specified command to the PN532 and expect up to response_length
293307
bytes back in a response. Note that less than the expected bytes might
294308
be returned! Params can optionally specify an array of bytes to send as
@@ -298,12 +312,16 @@ def call_function(self, command, response_length=0, params=[], timeout=1):
298312
"""
299313
# Build frame data with command and parameters.
300314
data = bytearray(2+len(params))
301-
data[0] = _HOSTTOPN532
302-
data[1] = command & 0xFF
303-
for i in range(len(params)):
304-
data[2+i] = params[i]
315+
data[0] = _HOSTTOPN532
316+
data[1] = command & 0xFF
317+
for i, val in enumerate(params):
318+
data[2+i] = val
305319
# Send frame and wait for response.
306-
self._write_frame(data)
320+
try:
321+
self._write_frame(data)
322+
except OSError:
323+
self._wakeup()
324+
return None
307325
if not self._wait_ready(timeout):
308326
return None
309327
# Verify ACK response and wait to be ready for function response.
@@ -328,7 +346,7 @@ def get_firmware_version(self):
328346
raise RuntimeError('Failed to detect the PN532')
329347
return tuple(response)
330348

331-
def SAM_configuration(self):
349+
def SAM_configuration(self): # pylint: disable=invalid-name
332350
"""Configure the PN532 to read MiFare cards."""
333351
# Send SAM configuration command with configuration for:
334352
# - 0x01, normal mode
@@ -362,7 +380,7 @@ def read_passive_target(self, card_baud=_MIFARE_ISO14443A, timeout=1):
362380
# Return UID of card.
363381
return response[6:6+response[5]]
364382

365-
def mifare_classic_authenticate_block(self, uid, block_number, key_number, key):
383+
def mifare_classic_authenticate_block(self, uid, block_number, key_number, key): # pylint: disable=invalid-name
366384
"""Authenticate specified block number for a MiFare classic card. Uid
367385
should be a byte array with the UID of the card, block number should be
368386
the block to authenticate, key number should be the key type (like
@@ -378,7 +396,7 @@ def mifare_classic_authenticate_block(self, uid, block_number, key_number, key):
378396
params[1] = key_number & 0xFF
379397
params[2] = block_number & 0xFF
380398
params[3:3+keylen] = key
381-
params[3+keylen:] = uid
399+
params[3+keylen:] = uid
382400
# Send InDataExchange request and verify response is 0x00.
383401
response = self.call_function(_COMMAND_INDATAEXCHANGE,
384402
params=params,
@@ -424,18 +442,21 @@ def mifare_classic_write_block(self, block_number, data):
424442
class PN532_UART(PN532):
425443
"""Driver for the PN532 connected over Serial UART"""
426444
def __init__(self, uart, *, irq=None, reset=None, debug=False):
427-
"""Create an instance of the PN532 class using Serial connection
445+
"""Create an instance of the PN532 class using Serial connection.
446+
Optional IRQ pin (not used), reset pin and debugging output.
428447
"""
429448
self.debug = debug
430449
self._irq = irq
431450
self._uart = uart
432451
super().__init__(debug=debug, reset=reset)
433452

434453
def _wakeup(self):
454+
"""Send any special commands/data to wake up PN532"""
435455
#self._write_frame([_HOSTTOPN532, _COMMAND_SAMCONFIGURATION, 0x01])
436456
self.SAM_configuration()
437457

438458
def _wait_ready(self, timeout=1):
459+
"""Wait `timeout` seconds"""
439460
time.sleep(timeout)
440461
return True
441462

@@ -446,41 +467,58 @@ def _read_data(self, count):
446467
raise BusyError("No data read from PN532")
447468
if self.debug:
448469
print("Reading: ", [hex(i) for i in frame])
470+
else:
471+
time.sleep(0.1)
449472
return frame
450473

451474
def _write_data(self, framebytes):
475+
"""Write a specified count of bytes to the PN532"""
452476
while self._uart.read(1): # this would be a lot nicer if we could query the # of bytes
453477
pass
454478
self._uart.write('\x55\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') # wake up!
455479
self._uart.write(framebytes)
456480

457481
class PN532_I2C(PN532):
458482
"""Driver for the PN532 connected over I2C."""
459-
def __init__(self, i2c, *, irq=None, reset=None, debug=False):
460-
"""Create an instance of the PN532 class using either software SPI (if
461-
the sclk, mosi, and miso pins are specified) or hardware SPI if a
462-
spi parameter is passed. The cs pin must be a digital GPIO pin.
463-
Optionally specify a GPIO controller to override the default that uses
464-
the board's GPIO pins.
483+
def __init__(self, i2c, *, irq=None, reset=None, req=None, debug=False):
484+
"""Create an instance of the PN532 class using I2C. Note that PN532
485+
uses clock stretching. Optional IRQ pin (not used),
486+
reset pin and debugging output.
465487
"""
466488
self.debug = debug
467489
self._irq = irq
490+
self._req = req
491+
if reset:
492+
_reset(reset)
468493
self._i2c = i2c_device.I2CDevice(i2c, _I2C_ADDRESS)
469494
super().__init__(debug=debug, reset=reset)
470495

471-
def _wakeup(self):
496+
def _wakeup(self): # pylint: disable=no-self-use
497+
"""Send any special commands/data to wake up PN532"""
498+
if self._req:
499+
self._req.direction = Direction.OUTPUT
500+
self._req.value = True
501+
time.sleep(0.1)
502+
self._req.value = False
503+
time.sleep(0.1)
504+
self._req.value = True
472505
time.sleep(0.5)
473506

474507
def _wait_ready(self, timeout=1):
508+
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
475509
status = bytearray(1)
476-
t = time.monotonic()
477-
while (time.monotonic() - t) < timeout:
478-
with self._i2c:
479-
self._i2c.readinto(status)
510+
timestamp = time.monotonic()
511+
while (time.monotonic() - timestamp) < timeout:
512+
try:
513+
with self._i2c:
514+
self._i2c.readinto(status)
515+
except OSError:
516+
self._wakeup()
517+
continue
480518
if status == b'\x01':
481519
return True # No longer busy
482520
else:
483-
time.sleep(0.1) # lets ask again soon!
521+
time.sleep(0.05) # lets ask again soon!
484522
# Timed out!
485523
return False
486524

@@ -495,15 +533,19 @@ def _read_data(self, count):
495533
i2c.readinto(frame) # ok get the data, plus statusbyte
496534
if self.debug:
497535
print("Reading: ", [hex(i) for i in frame[1:]])
536+
else:
537+
time.sleep(0.1)
498538
return frame[1:] # don't return the status byte
499539

500540
def _write_data(self, framebytes):
541+
"""Write a specified count of bytes to the PN532"""
501542
with self._i2c as i2c:
502543
i2c.write(framebytes)
503544

504545
class PN532_SPI(PN532):
505-
"""Driver for the PN532 connected over SPI. Pass in a hardware or bitbang SPI device & chip select
506-
digitalInOut pin. Optional IRQ pin (not used), reset pin and debugging output."""
546+
"""Driver for the PN532 connected over SPI. Pass in a hardware or bitbang
547+
SPI device & chip select digitalInOut pin. Optional IRQ pin (not used),
548+
reset pin and debugging output."""
507549
def __init__(self, spi, cs_pin, *, irq=None, reset=None, debug=False):
508550
"""Create an instance of the PN532 class using SPI"""
509551
self.debug = debug
@@ -512,20 +554,25 @@ def __init__(self, spi, cs_pin, *, irq=None, reset=None, debug=False):
512554
super().__init__(debug=debug, reset=reset)
513555

514556
def _wakeup(self):
557+
"""Send any special commands/data to wake up PN532"""
515558
with self._spi as spi:
516559
time.sleep(1)
560+
spi.write(bytearray([0x00]))
561+
time.sleep(1)
517562

518563
def _wait_ready(self, timeout=1):
564+
"""Poll PN532 if status byte is ready, up to `timeout` seconds"""
519565
status = bytearray([reverse_bit(_SPI_STATREAD), 0])
520-
521-
t = time.monotonic()
522-
while (time.monotonic() - t) < timeout:
566+
567+
timestamp = time.monotonic()
568+
while (time.monotonic() - timestamp) < timeout:
523569
with self._spi as spi:
570+
time.sleep(0.02) # required
524571
spi.write_readinto(status, status)
525572
if reverse_bit(status[1]) == 0x01: # LSB data is read in MSB
526573
return True # Not busy anymore!
527574
else:
528-
time.sleep(0.1) # pause a bit till we ask again
575+
time.sleep(0.01) # pause a bit till we ask again
529576
# We timed out!
530577
return False
531578

@@ -535,21 +582,23 @@ def _read_data(self, count):
535582
frame = bytearray(count+1)
536583
# Add the SPI data read signal byte, but LSB'ify it
537584
frame[0] = reverse_bit(_SPI_DATAREAD)
538-
585+
539586
with self._spi as spi:
540-
time.sleep(0.01) # required
587+
time.sleep(0.02) # required
541588
spi.write_readinto(frame, frame)
542-
for i in range(len(frame)):
543-
frame[i] = reverse_bit(frame[i]) # turn LSB data to MSB
589+
for i, val in enumerate(frame):
590+
frame[i] = reverse_bit(val) # turn LSB data to MSB
544591
if self.debug:
545592
print("Reading: ", [hex(i) for i in frame[1:]])
546593
return frame[1:]
547594

548595
def _write_data(self, framebytes):
549-
# start by making a frame with data write in front, then rest of bytes, and LSBify it
550-
reversed = [reverse_bit(x) for x in bytes([_SPI_DATAWRITE]) + framebytes]
596+
"""Write a specified count of bytes to the PN532"""
597+
# start by making a frame with data write in front,
598+
# then rest of bytes, and LSBify it
599+
rev_frame = [reverse_bit(x) for x in bytes([_SPI_DATAWRITE]) + framebytes]
551600
if self.debug:
552-
print("Writing: ", [hex(i) for i in reversed])
601+
print("Writing: ", [hex(i) for i in rev_frame])
553602
with self._spi as spi:
554-
time.sleep(0.01) # required
555-
spi.write(bytes(reversed))
603+
time.sleep(0.02) # required
604+
spi.write(bytes(rev_frame))

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# Uncomment the below if you use native CircuitPython modules such as
2121
# digitalio, micropython and busio. List the modules you use. Without it, the
2222
# autodoc module docs will fail to generate with a warning.
23-
# autodoc_mock_imports = ["digitalio", "busio"]
23+
autodoc_mock_imports = ["digitalio", "busio", "adafruit_bus_device", "micropython"]
2424

2525

2626
intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'BusDevice': ('https://circuitpython.readthedocs.io/projects/busdevice/en/latest/', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}

0 commit comments

Comments
 (0)