Browse Source

Fix NTAG read: apply same IDLE→TRANSCEIVE state machine reset

  The verification read after writing failed because ntag_read_pages()
  used set_transceive_mode() which was a no-op when already in
  TRANSCEIVE. Apply the same IDLE→TRANSCEIVE reset pattern used in the
  write path, clear Crypto1, and add diagnostic logging.
maziggy 2 months ago
parent
commit
85ffb9e01f
2 changed files with 18 additions and 7 deletions
  1. 1 1
      CHANGELOG.md
  2. 17 6
      spoolbuddy/daemon/pn5180.py

+ 1 - 1
CHANGELOG.md

@@ -17,7 +17,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy Install Script Now Upgrades System Packages** — The install script now runs `apt-get upgrade -y` after installing required packages and the WiFi safeguard. This ensures the Pi is fully up to date before SpoolBuddy is deployed, and the WiFi safeguard protects connectivity during the upgrade.
 - **SpoolBuddy Install Script Now Upgrades System Packages** — The install script now runs `apt-get upgrade -y` after installing required packages and the WiFi safeguard. This ensures the Pi is fully up to date before SpoolBuddy is deployed, and the WiFi safeguard protects connectivity during the upgrade.
 
 
 ### Fixed
 ### Fixed
-- **SpoolBuddy NFC Write Fails on NTAG Tags** — Multiple issues prevented writing to NTAG 213/215/216 tags. (1) Some chips report SAK `0x04` (MIFARE Ultralight family) instead of `0x00` during anticollision, and the write gate only accepted `0x00`, causing "Incompatible tag type" errors — both SAK values are now accepted. (2) The PN5180 had TX CRC disabled for both NTAG WRITE and READ commands, but the NTAG spec requires CRC on all command frames — enabled TX CRC for both. (3) The PN5180 state machine wasn't being reset between SELECT and WRITE — added proper IDLE→TRANSCEIVE transition. (4) The NTAG WRITE ACK is only 4 bits, which the PN5180 cannot capture as a complete frame (SOF detected but RX_IRQ never fires). Removed per-page ACK checking and rely on the existing full read-back verification instead.
+- **SpoolBuddy NFC Write Fails on NTAG Tags** — Multiple issues prevented writing to NTAG 213/215/216 tags. (1) Some chips report SAK `0x04` (MIFARE Ultralight family) instead of `0x00` during anticollision, and the write gate only accepted `0x00`, causing "Incompatible tag type" errors — both SAK values are now accepted. (2) The PN5180 had TX CRC disabled for both NTAG WRITE and READ commands, but the NTAG spec requires CRC on all command frames — enabled TX CRC for both. (3) The PN5180 state machine wasn't being properly reset between commands — `set_transceive_mode()` was a no-op when already in TRANSCEIVE. Both write and read now use proper IDLE→TRANSCEIVE transitions with Crypto1 cleared. (4) The NTAG WRITE ACK is only 4 bits, which the PN5180 cannot capture as a complete frame (SOF detected but RX_IRQ never fires). Removed per-page ACK checking and rely on the existing full read-back verification instead.
 - **Database Connection Pool Exhaustion on Large Printer Farms** — Users with 100+ printers connected simultaneously experienced `QueuePool limit of size 10 overflow 20 reached, connection timed out` errors. Increased the SQLAlchemy connection pool from 30 total (10 base + 20 overflow) to 220 (20 base + 200 overflow), and raised the SQLite busy_timeout from 5 to 15 seconds to reduce write contention under heavy concurrent MQTT updates.
 - **Database Connection Pool Exhaustion on Large Printer Farms** — Users with 100+ printers connected simultaneously experienced `QueuePool limit of size 10 overflow 20 reached, connection timed out` errors. Increased the SQLAlchemy connection pool from 30 total (10 base + 20 overflow) to 220 (20 base + 200 overflow), and raised the SQLite busy_timeout from 5 to 15 seconds to reduce write contention under heavy concurrent MQTT updates.
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.
 - **SpoolBuddy Updates Now Use SSH** — Replaced the fragile self-update mechanism (daemon pulls its own code via git, permission errors on `.git/`, hardcoded `main` branch) with SSH-based updates driven by the Bambuddy backend. Bambuddy now SSHes into the SpoolBuddy Pi and runs git fetch/checkout, pip install, systemctl restart, and kiosk browser restart remotely. Updates automatically use the same branch as Bambuddy. SSH key pairing is fully automatic — Bambuddy generates an ED25519 keypair and includes the public key in the device registration response; the daemon deploys it to `authorized_keys` on first connect. The install script creates the `spoolbuddy` user with a bash shell and sudoers entries for daemon and kiosk restart. A "Force Update" button allows re-deploying even when versions match. The SSH public key is also shown in SpoolBuddy Settings → Updates → SSH Setup for manual pairing if needed.
 - **SpoolBuddy Updates Now Use SSH** — Replaced the fragile self-update mechanism (daemon pulls its own code via git, permission errors on `.git/`, hardcoded `main` branch) with SSH-based updates driven by the Bambuddy backend. Bambuddy now SSHes into the SpoolBuddy Pi and runs git fetch/checkout, pip install, systemctl restart, and kiosk browser restart remotely. Updates automatically use the same branch as Bambuddy. SSH key pairing is fully automatic — Bambuddy generates an ED25519 keypair and includes the public key in the device registration response; the daemon deploys it to `authorized_keys` on first connect. The install script creates the `spoolbuddy` user with a bash shell and sudoers entries for daemon and kiosk restart. A "Force Update" button allows re-deploying even when versions match. The SSH public key is also shown in SpoolBuddy Settings → Updates → SSH Setup for manual pairing if needed.

+ 17 - 6
spoolbuddy/daemon/pn5180.py

@@ -378,24 +378,35 @@ class PN5180:
 
 
         Uses NTAG READ command (0x30) which returns 4 pages (16 bytes) at a time.
         Uses NTAG READ command (0x30) which returns 4 pages (16 bytes) at a time.
         """
         """
-        # NTAG READ needs TX CRC on (tag expects CRC), RX CRC off (response includes raw CRC bytes we ignore)
-        self.write_reg_or(0x19, 0x01)  # TX CRC on
-        self.write_reg_and(0x12, 0xFFFFFFFE)  # RX CRC off
-
         result = bytearray()
         result = bytearray()
         pages_read = 0
         pages_read = 0
         while pages_read < num_pages:
         while pages_read < num_pages:
+            # CRC and state machine setup per iteration (same as ntag_write_page)
+            self.write_reg_and(0x00, 0xFFFFFFBF)  # Crypto1 off
+            self.write_reg_or(0x19, 0x01)  # TX CRC on
+            self.write_reg_and(0x12, 0xFFFFFFFE)  # RX CRC off
             self.write_reg(0x03, 0xFFFFFFFF)  # Clear IRQs
             self.write_reg(0x03, 0xFFFFFFFF)  # Clear IRQs
-            self.set_transceive_mode()
+
+            # Reset state machine: IDLE then TRANSCEIVE
+            sys_cfg = self.read_reg(0x00)
+            self.write_reg(0x00, sys_cfg & 0xFFFFFFF8)  # IDLE
             time.sleep(0.001)
             time.sleep(0.001)
+            self.write_reg(0x00, (sys_cfg & 0xFFFFFFF8) | 0x03)  # TRANSCEIVE
+            time.sleep(0.002)
 
 
             # READ command: 0x30 + page number -> returns 16 bytes (4 pages)
             # READ command: 0x30 + page number -> returns 16 bytes (4 pages)
             self.send_data([0x30, start_page + pages_read])
             self.send_data([0x30, start_page + pages_read])
-            time.sleep(0.005)
+            time.sleep(0.010)
 
 
             rx_status = self.read_reg(0x13)
             rx_status = self.read_reg(0x13)
             rx_len = rx_status & 0x1FF
             rx_len = rx_status & 0x1FF
             if rx_len < 16:
             if rx_len < 16:
+                logger.warning(
+                    "NTAG read page %d: rx_len=%d (expected >=16), rx_status=0x%08X",
+                    start_page + pages_read,
+                    rx_len,
+                    rx_status,
+                )
                 return None
                 return None
 
 
             data = self.read_data(16)
             data = self.read_data(16)