Closed soundstorm closed 2 months ago
Hi soundstorm, I can see from your logic capture that the PN532 is failing to send an ACK to the read command after writing the setPassiveActivationRetries. There might be a few causes of this so I will ask you to collect some more data.
Attached are the two exchanges recorded by the Saleae Analyzer. Configuration between pn532pi and pn532.api differ slightly, but as said, the Pi Zero 2 with bullseye aarch64 works flawlessly with the same pn532pi code, however, I noticed the codebase is older and therefor running the Library in V1.3. However, as the code is still matching the examples, this should generally still be fine was my assumption. I've tried to add the getFirmwareVersion() as in the example right after setup, but any read will fail (readRegister(0xD8) or any else throws an I/O error). Unfortunately the DBGTX stays quiet.
Might this be related to the gpio restructuring in the bookworm release? E.g. rpi-gpio is no longer working properly (interrupts, ...) and needed a replacement by rpi-lgpio. But as i2c is only accessing the file descriptor, it shouldn't be related. And also the Pi as a master should not generate a NACK.
Code for pn532.api for comparison:
nfc = PN532()
nfc.setup()
nfc._in_list_passive_target()
id = nfc.read()
Ok I think I have a better idea of what's going on. I think there are two relevant differences between pn432pi and pn532.api. First, pn532.api takes a 'rest' before each read/write to ensure the PN532 has completed the last command. Second, pn532.api does not read back the ack frame after each command which pn532pi does. I also dug into the _readAckFrame() function and realized that the original C library expected the first few attempts to read the ack to fail and so they poll until the device responds. (This behavior was backed up by some comments online about having to poll while the device is busy). The quick2wire library throws an exception however when the read isn't acked. To match the behavior of the C library we will probably have to catch this error and only raise it if we exceed the timeout. I suspect this is the reason a lot of other people have run into IOErrors when running longer running commands.
If you would like to test this version of _readAckFrame()
I think it will address the issue
def _readAckFrame(self) -> int:
PN532_ACK = [0, 0, 0xFF, 0, 0xFF, 0]
DMSG("wait for ack at : ")
DMSG(time.time())
DMSG('\n')
t = 0
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
else:
DMSG("Time out when waiting for ACK\n")
return PN532_TIMEOUT
DMSG("ready at : ")
DMSG(time.time())
DMSG('\n')
ackBuf = list(data[1:])
if ackBuf != PN532_ACK:
DMSG("Invalid ACK {}\n".format(ackBuf))
return PN532_INVALID_ACK
return 0
NameError: name 'errno' is not defined
but after altering it to import time, errno
it's working.
But the code is printing the timestamp once at start with no other DMSG output which I find weird, as I can't track it down.
But at least it's resolving the issue.
Ah, yeah should have mentioned the import. Ok I'll look into the DMSG and then push a patch once I've tested it on my reference setup.
For the sake of documentation, the relevant section in the PN532 user guide is 6.2.4 (I2C communication details). It mentions both the polling of the status byte and the case where the PN532 may not acknowledge a read immediately after executing a command. https://www.nxp.com/docs/en/user-guide/141520.pdf
Still sometimes exceptions, surrounded the read by an additional try/except. I also might wanna switch to interrupt based readout, as currently the polling is blocking for some time.
Ok, how long is it blocking for? The ack polling should time out after 10ms.
What happened?
I'm running a Pi3+ with Raspbian Lite Bookworm 64bit. While the Library works on the bullseye 64bit on a Zero 2 W / python3.9, the I2C init fails on bookworm 64bit / python3.11. https://github.com/hoanhan101/pn532 seems to be working just fine.
Reproducibility
Always (100% of runs)
Steps to Reproduce
see code to reproduce
Code to reproduce
Host Controller
Raspberry 3B+
Python Version
3.11
Interface Mode
I2C
Power Supply
HMP4040
Logic Traces and other details