Open lukruh opened 6 months ago
Thanks for your feature request.
The current Phoniebox implementation for rc522 readers seems to have issues with tags, which require(?) authentication.
The GitHub repo of the used rc-522 library seems to have recent activity (again).
If I understand the readme example correctly https://github.com/ondryaso/pi-rc522?tab=readme-ov-file#usage the RFID
class seems to have support for auth, which might also fix the original issue.
Looking at our code in V2 the implementation might be too simple, just ignoring auth? @AlvinSchiller
@lukruh I think you are currently referring to V2 implementation (using Reader.experimental.py), so I tagged the issue accordingly.
Nevertheless we need to check V3 as well.
@lukruh I think you are currently referring to V2 implementation (using Reader.experimental.py), so I tagged the issue accordingly.
Nevertheless we need to check V3 as well.
Looking at https://github.com/MiczFlor/RPi-Jukebox-RFID/blob/future3%2Fdevelop/src%2Fjukebox%2Fcomponents%2Frfid%2Fhardware%2Frc522_spi%2Frc522_spi.py#L100-L131 the V3 has the same (maybe too simple?) implementation.
Documentation has been updated (see #2372 and #2374) that ntags are currently not supported. Nevertheless we should try to fix this.
Documentation has been updated (see #2372 and #2374) that ntags are currently not supported. Nevertheless we should try to fix this.
If pi-rc522 library supports ntags and we need to improve our implementation this should be the way to go from my point of view.
The mfrc522 library seems to support it, but there is no activity for almost 4 years, so I'm not sure, if this helps us in the future.
We also need to consider the RPi.GPIO issue for Raspberry Pi OS Bookworm (at least for V3), see https://github.com/MiczFlor/RPi-Jukebox-RFID/issues/2313#issuecomment-2030579144
Looking at our code in V2 the implementation might be too simple, just ignoring auth? @AlvinSchiller
Yes, it looks like the case is just not covered, also in v3. I don't have this kind of tags around to test though.
If pi-rc522 library supports ntags and we need to improve our implementation this should be the way to go from my point of view.
The mfrc522 library seems to support it, but there is no activity for almost 4 years, so I'm not sure, if this helps us in the future.
We also need to consider the RPi.GPIO issue for Raspberry Pi OS Bookworm (at least for V3), see https://github.com/MiczFlor/RPi-Jukebox-RFID/issues/2313#issuecomment-2030579144
Agree in all points 👍
I got a couple of the ntag215 ones and can try to get to ignore auth in pi-rc522. So the preferred order of solving this would be:
I have a working update for pi-rc522 that does work for ntag215 and the classic cards. Ntag cards tend to cause more read errors (could also be cheap tags being crappy). I modified it based on the pattern from this other repo. I'd prefer to find the official docs laying out the sequence of events but this works. I'll put together an MR for pi-rc522 and see how that goes, that repo is pretty dead aside from a readme change
Would anyone be willing to take a look at something? I am running into some weird behavior and I'm not sure what is going on.
When I run daemon_rfid_reader.py
, I can see my new class getting loaded correctly and it picks up the ntag215 chips (less reliably than the mifare 1k card but that could be an antenna size/quality problem on this tags). Since the debug version updates the settings files the same way, I can actually see the ntag215 uids showing up on the web page for registering new cards.
HOWEVER, the service does not seem to be having the same luck - I do not see the load at the end of my custom class's constructor hitting the log. Is this just an issue with relative paths for the new class?
I've hit a wall. I can't understand why the wait_for_tag() method works correctly in debug but not when I run the script normally. Could be a timing issue that needs to be resolved between the two. Here is the doc that outlines the comms, and honestly it looks like the existing code should have handled 7 byte (but not 10) UIDs. I ordered a stack of classic cards in the mean time
Do you have a branch or a PR yet?
It would probably help, if people could look at the code to help.
Yeah let me clean things up. I am going to separate my changes, revert the repo and diff things. It's just bizarre. I think most of the code to properly grab NTag 21x UIDs is there (maybe minus another select command). I got frustrated with whatever was happening with Python debug vs normal and decided to look into .NET IoT libraries.
I am writing this now looking at my cheap MRFC522 spitting out NTag215 UIDs like crazy, and handling the classics just fine using the .NET libraries. This works for me for now because I can just mimic the script behavior with the dll, but I'll take another look at the python piece since I don't want to leave everyone hanging
Diffing the current pi rfc522 library against the working c# libraries, a few things stand out
The python class doesn't really use the RST pin we send in aside from pulling it high in the constructor -- The c# class does a hard reset on construction, pulling it low then high
The python class writes a reset to the device on construction --The c# class has a SoftReset that does this but it isn't called in my working example
The way the python library initializes the device via writes is very different than the c# class --c# source
I have zero idea what we're using the IRQ pin for. I did some digging and, although the threads are a little stale, so are most libraries that exist for this device - and most signs point to the library not really supporting it (arduino mfrc522 library), or threads about how MicroPython supports interrupts but circuit python doesn't. The python library allows it, but doesn't provide an example. Maybe this is a shortcoming of the NET libraries, but it isn't used and works fine.
The libraries diverge quick from there. The C# library handles cascading internal to the Select command, and again the registers/values look. I'm a few years removed from working with hardware and rarely use python, but I can't imagine there differences aren't leading to problems. If you check out the class ESPHome uses alongside it's python, the c class write similar values to the c# source, and I haven't seen threads for Home Assistant complaining about it
My apologies but for now this is the most I can do - it's a large rewrite from initial glance but someone more versed in python should double check
To clarify: NTAG213 tags (13, not 15) seem to work in current master.
(after applying the workaround from #2387, wich was also required for me to read MIFARE Classic tags)
Here the part of the nxp mfrc522 documentation corresponding to the interrupts
8.4 Interrupt request system (3) (4) The MFRC522 indicates certain events by setting the Status1Reg register’s IRq bit and, if activated, by pin IRQ. The signal on pin IRQ can be used to interrupt the host using its interrupt handling capabilities. This allows the implementation of efficient host software. 8.4.1 Interrupt sources overview Table 18 shows the available interrupt bits, the corresponding source and the condition for its activation. The ComIrqReg register’s TimerIRq interrupt bit indicates an interrupt set by the timer unit which is set when the timer decrements from 1 to 0. The ComIrqReg register’s TxIRq bit indicates that the transmitter has finished. If the state changes from sending data to transmitting the end of the frame pattern, the transmitter unit automatically sets the interrupt bit. The CRC coprocessor sets the DivIrqReg register’s CRCIRq bit after processing all the FIFO buffer data which is indicated by CRCReady bit = 1. ). The ComIrqReg register’s RxIRq bit indicates an interrupt when the end of the received data is detected. The ComIrqReg register’s IdleIRq bit is set if a command finishes and the Command[3:0] value in the CommandReg register changes to idle (see Table 149 on page 70 The ComIrqReg register’s HiAlertIRq bit is set to logic 1 when the Status1Reg register’s HiAlert bit is set to logic 1 which means that the FIFO buffer has reached the level indicated by the WaterLevel[5:0] bits. The ComIrqReg register’s LoAlertIRq bit is set to logic 1 when the Status1Reg register’s LoAlert bit is set to logic 1 which means that the FIFO buffer has reached the level indicated by the WaterLevel[5:0] bits. The ComIrqReg register’s ErrIRq bit indicates an error detected by the contactless UART during send or receive. This is indicated when any bit is set to logic 1 in register ErrorReg
Table 18. Interrupt sources List of register bits indicating the cause for the interrupt.
From my point of view the first thing that needs to be done, is checking if the irq system needs to be activated. The next question is, which kind of interrupt is send, when an interrupt occurs. Hence the status register’s would need to be read out. However the table 8 doesn't hint that there actually is an interrupt that indicates a card nearby. However, I probably won't find the time to dig into this one.
As for the authentication, I think both libraries are supporting it. For a better understanding you need to dig into the protocol of the rfid Chips. In the end it comes down to sending som hex code via the rc522, like when getting the last 4 bytes of the uid of a 7 byte uid
For just making the Reader read cards the IRQ is NOT needed with any library, see #2387
Ah, now I see how these two issues are related. I'm just building my first Phoniebox and just ran into the #2387 issue with my existing Tonuino tags (small paper stickers underneath wooden tokens) which report as ISO 14443-3A NXP - NTAG213
in NFC Tools.
I then tried a random NFC card from my wallet (my company id card actually) which reports as ISO 14443-4 NXP - Mifare Classic 1k
and it also did not work, despite it being a Mifare Classic tag.
Now I also found the plain white card that came shipped with my RC522 reader and this works even with IRQ! It seems to be a different variant of Mifare Classic: ISO 14443-3A NXP - Mifare Classic 1k
.
Maybe I will find some time this week to dig into the RC522 specs and the python library code, but no promises...
After adding
self.clear_bitmask(0x13, 0x04)
at https://github.com/ondryaso/pi-rc522/blob/master/pirc522/rfid.py#L499 (/usr/local/lib/python3.9/dist-packages/pirc522/rfid.py
on the phoniebox) I am getting IRQs from the reader with my empty NTAG213 tags :tada:
This will clear the RxNoErr
bit in the RxModeReg
register which defaults to 1 = "an invalid received data stream (less than 4 bits received) will be ignored and the receiver remains active"
That will have an effect on ComIrqReg
register's RxIRq
bit ("receiver has detected the end of a valid data stream. If the RxModeReg register’s RxNoErr bit is set to logic 1, the RxIRq bit is only set to logic 1 when data bytes are available in the FIFO"), which in turn is one of the interrupt sources.
Do you want to open a PR at https://github.com/ondryaso/pi-rc522 to fix this upstream?
This would be the easiest solution, we would avoid to fork the upstream repo.
After adding
self.clear_bitmask(0x13, 0x04)
at https://github.com/ondryaso/pi-rc522/blob/master/pirc522/rfid.py#L499 (
/usr/local/lib/python3.9/dist-packages/pirc522/rfid.py
on the phoniebox) I am getting IRQs from the reader with my empty NTAG213 tags 🎉
I'll give it a whirl. I have a pack of NTAG215 little disc tags I bought for some esphome projects so I'd like those to work but I think from what I've seen 213/215 are similar (7 byte UID) just the 215 has more storage
Unfortunately, the bitmask change was not enough to get NTAG215 UIDs to be read correctly. I'm willing to offer up the .NET code I have that is working if others need it. The other thing I considered was leveraging the c code that's out there for rc522 to put together a library Reader.py can just call directly and skip pirc522
Unfortunately, the bitmask change was not enough to get NTAG215 UIDs to be read correctly. I'm willing to offer up the .NET code I have that is working if others need it. The other thing I considered was leveraging the c code that's out there for rc522 to put together a library Reader.py can just call directly and skip pirc522
Is your .Net code using the interrupt pin? Because I'm currently asking myself whether we should spend time on an interrupt system, which we most probably do not necessarily need.
Do you want to open a PR ... to fix this upstream?
Definitely, but only after testing a bit more to be sure that this actually fixes the issue.
I'm still not entirely sure what the heck the library is doing there in wait_for_tag()
.
Because I'm currently asking myself whether we should spend time on an interrupt system, which we most probably do not necessarily need. I think that would be the sane way to go.
My hope for an interrupt-based solution was to save CPU cycles (the workaround with commented-out wait_for_tag()
currently uses ~25% CPU on my Raspi 3B+ according to htop
) and act only if the reader triggers a GPIO event via the IRQ pin.
But according to the datasheet (and https://arduino.stackexchange.com/a/76285) the rc522 actually does not seem to support a direct "interrupt when tag detected", only interrupts for e.g. the TX queue reaching a certain length.
The pi-rc522 library implementation of wait_for_tag()
also does not seem to passively wait for a GPIO event but uses some active polling mechanism: There is an infinite loop that every 100 ms completely reinitializes the reader and transmits some radio data until either a "receiver interrupt request (RxIRq bit)" is received (I guess this means that a tag was present and has answered) or an optional timeout is reached (currently not used by Reader.py
) .
So I guess it should be safe to just ignore wait_for_tag()
and just directly poll for tag IDs in Reader.py
.
But according to the datasheet (and https://arduino.stackexchange.com/a/76285) the rc522 actually does not seem to support a direct "interrupt when tag detected", only interrupts for e.g. the TX queue reaching a certain length.
I got the same understanding, so I was already wondering how the wait_for_tag()
is implemented and then, why your solution works. Now that i took a look in the implementation, the first thing done seems to be a complete reset of the reader (self.init()
) and at the end, it waits for an event waiting = not self.irq.wait(0.1)
; according to the threading-doc
Wait until notified or until a timeout occurs. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.
Hence, includeing the wait(0.1) may be the reason for the reduced load. This could of course be also implemented in the reader.read()-phase
After adding
self.clear_bitmask(0x13, 0x04)
at https://github.com/ondryaso/pi-rc522/blob/master/pirc522/rfid.py#L499 (
/usr/local/lib/python3.9/dist-packages/pirc522/rfid.py
on the phoniebox) I am getting IRQs from the reader with my empty NTAG213 tags 🎉
I tested the code as well. With my NTAG215 it's not working. maybe at this point it's wise to bring up an old issue i had.
with my first setup the uids could not be read completely. the issue was, that i only got a complete answer from the tag from time to time. Other people with a similar issue suggested that the main issue was the lack of power. The rfid reader was just not capable of providing enough energy to get a first and then in almost no time the second answer from the tag, providing the full uid. After buying a new rfid reader, the issue was gone. Keeping that in mind, the previously working setup may be a result of voltage drops. If I'm not mistaken, the irq-pin is pulled down to indicate an interrupt. Hence, it may be possible that issue now appears, because I have a better power source keeping the irq stable while it wasn't stable beforehand resulting in a working setup (even my old rfid-reader is working with my workaround) ¯\(ツ)/¯ .
Unfortunately, the bitmask change was not enough to get NTAG215 UIDs to be read correctly. I'm willing to offer up the .NET code I have that is working if others need it. The other thing I considered was leveraging the c code that's out there for rc522 to put together a library Reader.py can just call directly and skip pirc522
Is your .Net code using the interrupt pin? Because I'm currently asking myself whether we should spend time on an interrupt system, which we most probably do not necessarily need.
No, the .NET MFRC522 implementation does not use the interrupt pin, nor is it even something you can pass/set via a constructor or property. Here is the source.
I questioned the need for the IRQ pin in my post as well after getting all my tags read fine through the .NET code. At the same time I was doing this, I was looking at languages for a Pi Pico project and came across the differences in MicroPython and CircuitPython - one main one being a lack of true interrupt support in CircuitPython. I started wondering if the interrupt was never being caught or something
Feature
Extend the
Mfrc522Reader
class to support cheap NFC chips like ntag215. This might require switching to another library. The currently used pi-rc522 seems not to support the newer ntag chips. There are several other libraries, that might support the ntag type chips as well as the currently supported s50/s70 types.I don't have experience with RFID/NFC or the phoniebox it self. But I'm happy to share my current workaround or help implementing a more general solution. Maybe some more experienced users/devs want to jump in for discussion. (I will link related Issues later).
User perspective
From reading the docs (before #2372) here in the repo and/or the description of the RC522 users (like me) might expect all NFC tags are supported as long as the frequency matches. The ntag type chips seem to be very common on the market. For users who got some of these it should be possible to use them without touching the code.
Further information
I managed to use the phoniebox with ntag type chips by adding a reader based on the mrfc522 library with some copy pasted code from their examples and other github issues to work around Auth errors. This could be at least a preliminary solution for users who only want to use this type of chips:
I have adjusted the Reader class and setup script accordingly to switch between the current reader and this workaround.