Skip to content

Commit

Permalink
Fix/22 io error long cmds (#23)
Browse files Browse the repository at this point in the history
* feat: Add function for parsing the firmware version info into a named tuple.

* fix: Fix io errors due to driver not polling ACK frame correctly. Fixes #22

Long commands do not ack immediately so we are supposed to poll for the ack frame. quick2wire throws an error if the read is not acked though so we need to catch those errors and poll again after a brief wait. If we timeout we return an error code instead of raising an exception.

* doc: Update README to indicate using the 5v pin on the raspberry pi is safe.
Bump version minor.
Correct email in packaging step.
  • Loading branch information
gassajor000 authored Aug 3, 2024
1 parent 834b621 commit 5d51007
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 16 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ This is a port of [Seeed Studios's PN532 Arduino Library](https://github.com/See
## Power
The Raspberry Pi 3.3v regulator does not provide enough current to drive the PN532 chip.
If you try to run the PN532 off your Raspberry Pi it will reset randomly and may not respond to commands.
Instead you will need another power source (3.3v) to power the PN532. Some people have been able to run
the PN532 off of the 5V rail of the raspberry pi but this is not the recommended way of powering it.
If you try to run the PN532 off your Raspberry Pi 3.3v it will reset randomly and may not respond to commands.
Instead you will need another power source (3.3v or 5v) to power the PN532. It is generally safe to tap into the
5v pin on the Raspberry Pi so long as your power supply can provide enough power for the PN532 and the pi.
## I2C Interface
Expand Down
27 changes: 17 additions & 10 deletions pn532pi/interfaces/pn532i2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pn532pi.nfc.pn532_log import DMSG
from quick2wire.i2c import I2CMaster, writing, reading
import errno

from pn532pi.interfaces.pn532Interface import Pn532Interface, PN532_PREAMBLE, PN532_STARTCODE1, PN532_STARTCODE2, PN532_HOSTTOPN532, \
PN532_INVALID_FRAME, PN532_POSTAMBLE, PN532_PN532TOHOST, PN532_ACK_WAIT_TIME, PN532_TIMEOUT, \
Expand Down Expand Up @@ -154,18 +155,24 @@ def _readAckFrame(self) -> int:
DMSG('\n')

t = 0
while 1:
responses = self._wire.transaction(reading(PN532_I2C_ADDRESS, len(PN532_ACK) + 1))
data = bytearray(responses[0])
if (data[0] & 1):
# check first byte --- status
break # PN532 is ready

while t <= PN532_ACK_WAIT_TIME:
try:
responses = self._wire.transaction(reading(PN532_I2C_ADDRESS, len(PN532_ACK) + 1))
data = bytearray(responses[0])
if (data[0] & 1):
# check first byte --- status
break # PN532 is ready
except IOError as e:
# As of Python 3.3 IOError is the same as OSError so we should check the error code
if e.errno != errno.EIO:
raise # Reraise the error
# Otherwise do nothing, sleep and try again

time.sleep(.001) # sleep 1 ms
t+=1
if (t > PN532_ACK_WAIT_TIME):
DMSG("Time out when waiting for ACK\n")
return PN532_TIMEOUT
else:
DMSG("Time out when waiting for ACK\n")
return PN532_TIMEOUT

DMSG("ready at : ")
DMSG(time.time())
Expand Down
45 changes: 44 additions & 1 deletion pn532pi/nfc/pn532.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
@license BSD
"""
from typing import List
from typing import List, NamedTuple

from pn532pi.interfaces.pn532Interface import Pn532Interface, PN532_TIMEOUT

Expand Down Expand Up @@ -126,6 +126,33 @@
FELICA_WRITE_MAX_BLOCK_NUM = 10 # for typical FeliCa card
FELICA_REQ_SERVICE_MAX_NODE_NUM = 32

# Support options in FW Version Info
SUPPORTS_ISO18092 = 0b100
SUPPORTS_ISO14443_A = 0b010
SUPPORTS_ISO14443_B = 0b001
class Pn532FirmwareInfo(NamedTuple):
"""
Information contained in the response to the
GetFirmwareVersion command
"""
ic_version: int
fw_major: int
fw_minor: int
support_opts: int
iso18092: bool
iso14443_a: bool
iso14443_b: bool

def __str__(self) -> str:
all_opts = [('ISO18092', self.iso18092), ('ISO4443-A', self.iso14443_a),
('ISO4443-B', self.iso14443_b)]
opts = [name for name, supported in all_opts if supported]
return (f'Pn532FirmwareInfo(IC Version: {hex(self.ic_version)}, '
f'FW Version: {self.fw_major}.{self.fw_minor}, '
f'Supports: {opts} ({hex(self.support_opts)}))')

def __repr__(self) -> str:
return str(self)

class Pn532:
def __init__(self, interface: Pn532Interface):
Expand All @@ -149,6 +176,8 @@ def getFirmwareVersion(self) -> int:
"""
Checks the firmware version of the PN5xx chip
See https://www.nxp.com/docs/en/user-guide/141520.pdf page 73
:returns: The chip's firmware version and ID
"""
if (self._interface.writeCommand(bytearray([PN532_COMMAND_GETFIRMWAREVERSION]))):
Expand All @@ -168,6 +197,20 @@ def getFirmwareVersion(self) -> int:
# response |= self.pn532_packetbuffer[3]

return int.from_bytes(response, byteorder='big')

def getFirmwareInfo(self) -> tuple:
"""
Parse the firmware version info into its separate parts
Format: |ic_version[8] | fw_version[8] | fw_revision[8] | support_opts[8]|
"""
firmware_data = self.getFirmwareVersion()
opts, fw_min, fw_maj, ic_ver = [(firmware_data >> i) & 0xFF for i in range(0, 32, 8)]

return Pn532FirmwareInfo(ic_ver, fw_maj, fw_min, opts,
bool(opts & SUPPORTS_ISO18092),
bool(opts & SUPPORTS_ISO14443_A),
bool(opts & SUPPORTS_ISO14443_B))


def readRegister(self, reg: int) -> int:
"""
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

setuptools.setup(
name="pn532pi",
version="1.6",
version="1.7",
author="gassajor000",
author_email="lgassjsg@example.com",
author_email="[email protected].com",
description="PN532 library for Raspberry Pi",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit 5d51007

Please sign in to comment.