get_version works

This commit is contained in:
ladyada 2018-08-20 23:46:15 -04:00
parent 14b438a3fb
commit d5ca98fbaa

View file

@ -143,8 +143,8 @@ PN532_GPIO_P33 = 3
PN532_GPIO_P34 = 4 PN532_GPIO_P34 = 4
PN532_GPIO_P35 = 5 PN532_GPIO_P35 = 5
PN532_ACK = bytearray([0x01, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00]) PN532_ACK = b'\x01\x00\x00\xFF\x00\xFF\x00'
PN532_FRAME_START = bytearray([0x01, 0x00, 0x00, 0xFF]) PN532_FRAME_START = b'\x01\x00\x00\xFF'
class PN532_I2C(object): class PN532_I2C(object):
@ -154,28 +154,22 @@ class PN532_I2C(object):
PN532 (see: http://www.raspberrypi.org/forums/viewtopic.php?f=32&t=98070&p=720659#p720659) PN532 (see: http://www.raspberrypi.org/forums/viewtopic.php?f=32&t=98070&p=720659#p720659)
""" """
def __init__(self, i2c): def __init__(self, i2c, irq=None, *, debug=False):
"""Create an instance of the PN532 class using either software SPI (if """Create an instance of the PN532 class using either software SPI (if
the sclk, mosi, and miso pins are specified) or hardware SPI if a the sclk, mosi, and miso pins are specified) or hardware SPI if a
spi parameter is passed. The cs pin must be a digital GPIO pin. spi parameter is passed. The cs pin must be a digital GPIO pin.
Optionally specify a GPIO controller to override the default that uses Optionally specify a GPIO controller to override the default that uses
the board's GPIO pins. the board's GPIO pins.
""" """
self._i2cdevice = i2c_device.I2CDevice(i2c, PN532_I2C_ADDRESS) self.debug = debug
self._i2c = i2c_device.I2CDevice(i2c, PN532_I2C_ADDRESS)
def _uint8_add(self, a, b): self._irq = irq
"""Add add two values as unsigned 8-bit values.""" self.get_firmware_version()
return ((a & 0xFF) + (b & 0xFF)) & 0xFF
def _busy_wait_ms(self, ms):
"""Busy wait for the specified number of milliseconds."""
time.delay(ms/1000.0)
def _write_frame(self, data): def _write_frame(self, data):
"""Write a frame to the PN532 with the specified data bytearray.""" """Write a frame to the PN532 with the specified data bytearray."""
assert data is not None and 0 < len(data) < 255, 'Data must be array of 1 to 255 bytes.' assert data is not None and 0 < len(data) < 255, 'Data must be array of 1 to 255 bytes.'
# Build frame to send as: # Build frame to send as:
# - SPI data write (0x01)
# - Preamble (0x00) # - Preamble (0x00)
# - Start code (0x00, 0xFF) # - Start code (0x00, 0xFF)
# - Command length (1 byte) # - Command length (1 byte)
@ -185,34 +179,31 @@ class PN532_I2C(object):
# - Postamble (0x00) # - Postamble (0x00)
length = len(data) length = len(data)
frame = bytearray(length+8) frame = bytearray(length+8)
frame[0] = PN532_SPI_DATAWRITE frame[0] = PN532_PREAMBLE
frame[1] = PN532_PREAMBLE frame[1] = PN532_STARTCODE1
frame[2] = PN532_STARTCODE1 frame[2] = PN532_STARTCODE2
frame[3] = PN532_STARTCODE2 checksum = sum(frame[0:3])
frame[4] = length & 0xFF frame[3] = length & 0xFF
frame[5] = self._uint8_add(~length, 1) frame[4] = (~length + 1) & 0xFF
frame[6:-2] = data frame[5:-2] = data
checksum = reduce(self._uint8_add, data, 0xFF) checksum += sum(data)
frame[-2] = ~checksum & 0xFF frame[-2] = ~checksum & 0xFF
frame[-1] = PN532_POSTAMBLE frame[-1] = PN532_POSTAMBLE
# Send frame. # Send frame.
logger.debug('Write frame: 0x{0}'.format(binascii.hexlify(frame))) if self.debug:
self._gpio.set_low(self._cs) print('Write frame: ', [hex(i) for i in frame])
self._busy_wait_ms(2) with self._i2c:
self._spi.write(frame) self._i2c.write(bytes(frame))
self._gpio.set_high(self._cs)
def _read_data(self, count): def _read_data(self, count):
"""Read a specified count of bytes from the PN532.""" """Read a specified count of bytes from the PN532."""
# Build a read request frame. # Build a read request frame.
frame = bytearray(count) frame = bytearray(count)
frame[0] = PN532_SPI_DATAREAD with self._i2c:
# Send the frame and return the response, ignoring the SPI header byte. self._i2c.readinto(frame)
self._gpio.set_low(self._cs) if self.debug:
self._busy_wait_ms(2) print("Reading: ", [hex(i) for i in frame])
response = self._spi.transfer(frame) return frame
self._gpio.set_high(self._cs)
return response
def _read_frame(self, length): def _read_frame(self, length):
"""Read a response frame from the PN532 of at most length bytes in size. """Read a response frame from the PN532 of at most length bytes in size.
@ -222,7 +213,8 @@ class PN532_I2C(object):
""" """
# Read frame with expected length of data. # Read frame with expected length of data.
response = self._read_data(length+8) response = self._read_data(length+8)
logger.debug('Read frame: 0x{0}'.format(binascii.hexlify(response))) if self.debug:
print('Read frame:', [hex(i) for i in response])
# Check frame starts with 0x01 and then has 0x00FF (preceeded by optional # Check frame starts with 0x01 and then has 0x00FF (preceeded by optional
# zeros). # zeros).
if response[0] != 0x01: if response[0] != 0x01:
@ -243,42 +235,24 @@ class PN532_I2C(object):
if (frame_len + response[offset+1]) & 0xFF != 0: if (frame_len + response[offset+1]) & 0xFF != 0:
raise RuntimeError('Response length checksum did not match length!') raise RuntimeError('Response length checksum did not match length!')
# Check frame checksum value matches bytes. # Check frame checksum value matches bytes.
checksum = reduce(self._uint8_add, response[offset+2:offset+2+frame_len+1], 0) checksum = sum(response[offset+2:offset+2+frame_len+1]) & 0xFF
if checksum != 0: if checksum != 0:
raise RuntimeError('Response checksum did not match expected value!') raise RuntimeError('Response checksum did not match expected value: ', checksum)
# Return frame data. # Return frame data.
return response[offset+2:offset+2+frame_len] return response[offset+2:offset+2+frame_len]
def _wait_ready(self, timeout_sec=1): def _wait_ready(self, timeout=1):
"""Wait until the PN532 is ready to receive commands. At most wait if self._irq:
timeout_sec seconds for the PN532 to be ready. If the PN532 is ready print("TODO IRQ")
before the timeout is exceeded then True will be returned, otherwise else:
False is returned when the timeout is exceeded. time.sleep(timeout)
"""
start = time.time()
# Send a SPI status read command and read response.
self._gpio.set_low(self._cs)
self._busy_wait_ms(2)
response = self._spi.transfer([PN532_SPI_STATREAD, 0x00])
self._gpio.set_high(self._cs)
# Loop until a ready response is received.
while response[1] != PN532_SPI_READY:
# Check if the timeout has been exceeded.
if time.time() - start >= timeout_sec:
return False
# Wait a little while and try reading the status again.
time.sleep(0.01)
self._gpio.set_low(self._cs)
self._busy_wait_ms(2)
response = self._spi.transfer([PN532_SPI_STATREAD, 0x00])
self._gpio.set_high(self._cs)
return True return True
def call_function(self, command, response_length=0, params=[], timeout_sec=1): def call_function(self, command, response_length=0, params=[], timeout=1):
"""Send specified command to the PN532 and expect up to response_length """Send specified command to the PN532 and expect up to response_length
bytes back in a response. Note that less than the expected bytes might bytes back in a response. Note that less than the expected bytes might
be returned! Params can optionally specify an array of bytes to send as be returned! Params can optionally specify an array of bytes to send as
parameters to the function call. Will wait up to timeout_secs seconds parameters to the function call. Will wait up to timeout seconds
for a response and return a bytearray of response bytes, or None if no for a response and return a bytearray of response bytes, or None if no
response is available within the timeout. response is available within the timeout.
""" """
@ -286,16 +260,16 @@ class PN532_I2C(object):
data = bytearray(2+len(params)) data = bytearray(2+len(params))
data[0] = PN532_HOSTTOPN532 data[0] = PN532_HOSTTOPN532
data[1] = command & 0xFF data[1] = command & 0xFF
data[2:] = params for i in range(len(params)):
data[2+i] = params[i]
# Send frame and wait for response. # Send frame and wait for response.
self._write_frame(data) self._write_frame(data)
if not self._wait_ready(timeout_sec): if not self._wait_ready(timeout):
return None return None
# Verify ACK response and wait to be ready for function response. # Verify ACK response and wait to be ready for function response.
response = self._read_data(len(PN532_ACK)) if not PN532_ACK == self._read_data(len(PN532_ACK)):
if response != PN532_ACK:
raise RuntimeError('Did not receive expected ACK from PN532!') raise RuntimeError('Did not receive expected ACK from PN532!')
if not self._wait_ready(timeout_sec): if not self._wait_ready(timeout):
return None return None
# Read response bytes. # Read response bytes.
response = self._read_frame(response_length+2) response = self._read_frame(response_length+2)
@ -305,25 +279,13 @@ class PN532_I2C(object):
# Return response data. # Return response data.
return response[2:] return response[2:]
def begin(self):
"""Initialize communication with the PN532. Must be called before any
other calls are made against the PN532.
"""
# Assert CS pin low for a second for PN532 to be ready.
self._gpio.set_low(self._cs)
time.sleep(1.0)
# Call GetFirmwareVersion to sync up with the PN532. This might not be
# required but is done in the Arduino library and kept for consistency.
self.get_firmware_version()
self._gpio.set_high(self._cs)
def get_firmware_version(self): def get_firmware_version(self):
"""Call PN532 GetFirmwareVersion function and return a tuple with the IC, """Call PN532 GetFirmwareVersion function and return a tuple with the IC,
Ver, Rev, and Support values. Ver, Rev, and Support values.
""" """
response = self.call_function(PN532_COMMAND_GETFIRMWAREVERSION, 4) response = self.call_function(PN532_COMMAND_GETFIRMWAREVERSION, 4, timeout=0.1)
if response is None: if response is None:
raise RuntimeError('Failed to detect the PN532! Make sure there is sufficient power (use a 1 amp or greater power supply), the PN532 is wired correctly to the device, and the solder joints on the PN532 headers are solidly connected.') raise RuntimeError('Failed to detect the PN532')
return (response[0], response[1], response[2], response[3]) return (response[0], response[1], response[2], response[3])
def SAM_configuration(self): def SAM_configuration(self):
@ -336,9 +298,9 @@ class PN532_I2C(object):
# check the command was executed as expected. # check the command was executed as expected.
self.call_function(PN532_COMMAND_SAMCONFIGURATION, params=[0x01, 0x14, 0x01]) self.call_function(PN532_COMMAND_SAMCONFIGURATION, params=[0x01, 0x14, 0x01])
def read_passive_target(self, card_baud=PN532_MIFARE_ISO14443A, timeout_sec=1): def read_passive_target(self, card_baud=PN532_MIFARE_ISO14443A, timeout=1):
"""Wait for a MiFare card to be available and return its UID when found. """Wait for a MiFare card to be available and return its UID when found.
Will wait up to timeout_sec seconds and return None if no card is found, Will wait up to timeout seconds and return None if no card is found,
otherwise a bytearray with the UID of the found card is returned. otherwise a bytearray with the UID of the found card is returned.
""" """
# Send passive read command for 1 card. Expect at most a 7 byte UUID. # Send passive read command for 1 card. Expect at most a 7 byte UUID.