#include "nfc.h" #include #include #include #include "config.h" #include "website.h" #include "api.h" #include "esp_task_wdt.h" #include "scale.h" #include "bambu.h" #include "main.h" //Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS); Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET); TaskHandle_t RfidReaderTask; JsonDocument rfidData; String activeSpoolId = ""; String lastSpoolId = ""; String nfcJsonData = ""; bool tagProcessed = false; volatile bool pauseBambuMqttTask = false; volatile bool nfcReadingTaskSuspendRequest = false; volatile bool nfcReadingTaskSuspendState = false; volatile bool nfcWriteInProgress = false; // Prevent any tag operations during write struct NfcWriteParameterType { bool tagType; char* payload; }; volatile nfcReaderStateType nfcReaderState = NFC_IDLE; // 0 = nicht gelesen // 1 = erfolgreich gelesen // 2 = fehler beim Lesen // 3 = schreiben // 4 = fehler beim Schreiben // 5 = erfolgreich geschrieben // 6 = reading // ***** PN532 // ##### Funktionen für RFID ##### void payloadToJson(uint8_t *data) { const char* startJson = strchr((char*)data, '{'); const char* endJson = strrchr((char*)data, '}'); if (startJson && endJson && endJson > startJson) { String jsonString = String(startJson, endJson - startJson + 1); //Serial.print("Bereinigter JSON-String: "); //Serial.println(jsonString); // JSON-Dokument verarbeiten JsonDocument doc; // Passen Sie die Größe an den JSON-Inhalt an DeserializationError error = deserializeJson(doc, jsonString); if (!error) { const char* color_hex = doc["color_hex"]; const char* type = doc["type"]; int min_temp = doc["min_temp"]; int max_temp = doc["max_temp"]; const char* brand = doc["brand"]; Serial.println(); Serial.println("-----------------"); Serial.println("JSON-Parsed Data:"); Serial.println(color_hex); Serial.println(type); Serial.println(min_temp); Serial.println(max_temp); Serial.println(brand); Serial.println("-----------------"); Serial.println(); } else { Serial.print("deserializeJson() failed: "); Serial.println(error.f_str()); } doc.clear(); } else { Serial.println("Kein gültiger JSON-Inhalt gefunden oder fehlerhafte Formatierung."); //writeJsonToTag("{\"version\":\"1.0\",\"protocol\":\"NFC\",\"color_hex\":\"#FFFFFF\",\"type\":\"Example\",\"min_temp\":10,\"max_temp\":30,\"brand\":\"BrandName\"}"); } } bool formatNdefTag() { uint8_t ndefInit[] = { 0x03, 0x00, 0xFE }; // NDEF Initialisierungsnachricht bool success = true; int pageOffset = 4; // Startseite für NDEF-Daten auf NTAG2xx Serial.println(); Serial.println("Formatiere NDEF-Tag..."); // Schreibe die Initialisierungsnachricht auf die ersten Seiten for (int i = 0; i < sizeof(ndefInit); i += 4) { if (!nfc.ntag2xx_WritePage(pageOffset + (i / 4), &ndefInit[i])) { success = false; break; } } return success; }uint16_t readTagSize() { uint8_t buffer[4]; memset(buffer, 0, 4); nfc.ntag2xx_ReadPage(3, buffer); return buffer[2]*8; } String detectNtagType() { // Read capability container from page 3 to determine exact NTAG type uint8_t ccBuffer[4]; memset(ccBuffer, 0, 4); if (!nfc.ntag2xx_ReadPage(3, ccBuffer)) { Serial.println("Failed to read capability container"); return "UNKNOWN"; } // Also read configuration pages to get more info uint8_t configBuffer[4]; memset(configBuffer, 0, 4); Serial.print("Capability Container: "); for (int i = 0; i < 4; i++) { if (ccBuffer[i] < 0x10) Serial.print("0"); Serial.print(ccBuffer[i], HEX); Serial.print(" "); } Serial.println(); // NTAG type detection based on capability container // CC[2] contains the data area size in bytes / 8 uint16_t dataAreaSize = ccBuffer[2] * 8; Serial.print("Data area size from CC: "); Serial.println(dataAreaSize); // Try to read different configuration pages to determine exact type String tagType = "UNKNOWN"; // Try to read page 41 (NTAG213 ends at page 39, so this should fail) uint8_t testBuffer[4]; bool canReadPage41 = nfc.ntag2xx_ReadPage(41, testBuffer); // Try to read page 130 (NTAG215 ends at page 129, so this should fail for NTAG213/215) bool canReadPage130 = nfc.ntag2xx_ReadPage(130, testBuffer); if (dataAreaSize <= 180 && !canReadPage41) { tagType = "NTAG213"; Serial.println("Detected: NTAG213 (cannot read beyond page 39)"); } else if (dataAreaSize <= 540 && canReadPage41 && !canReadPage130) { tagType = "NTAG215"; Serial.println("Detected: NTAG215 (can read page 41, cannot read page 130)"); } else if (dataAreaSize <= 928 && canReadPage130) { tagType = "NTAG216"; Serial.println("Detected: NTAG216 (can read page 130)"); } else { // Fallback: use data area size from capability container if (dataAreaSize <= 180) { tagType = "NTAG213"; Serial.println("Fallback detection: NTAG213 based on data area size"); } else if (dataAreaSize <= 540) { tagType = "NTAG215"; Serial.println("Fallback detection: NTAG215 based on data area size"); } else { tagType = "NTAG216"; Serial.println("Fallback detection: NTAG216 based on data area size"); } } return tagType; } uint16_t getAvailableUserDataSize() { String tagType = detectNtagType(); uint16_t userDataSize = 0; if (tagType == "NTAG213") { // NTAG213: User data from page 4-39 (36 pages * 4 bytes = 144 bytes) userDataSize = 144; Serial.println("NTAG213 confirmed - 144 bytes user data available"); } else if (tagType == "NTAG215") { // NTAG215: User data from page 4-129 (126 pages * 4 bytes = 504 bytes) userDataSize = 504; Serial.println("NTAG215 confirmed - 504 bytes user data available"); } else if (tagType == "NTAG216") { // NTAG216: User data from page 4-225 (222 pages * 4 bytes = 888 bytes) userDataSize = 888; Serial.println("NTAG216 confirmed - 888 bytes user data available"); } else { // Unknown tag type, use conservative estimate uint16_t tagSize = readTagSize(); userDataSize = tagSize - 60; // Reserve 60 bytes for headers/config Serial.print("Unknown NTAG type, using conservative estimate: "); Serial.println(userDataSize); } return userDataSize; } uint16_t getMaxUserDataPages() { String tagType = detectNtagType(); uint16_t maxPages = 0; if (tagType == "NTAG213") { maxPages = 39; // Pages 4-39 are user data } else if (tagType == "NTAG215") { maxPages = 129; // Pages 4-129 are user data } else if (tagType == "NTAG216") { maxPages = 225; // Pages 4-225 are user data } else { // Conservative fallback maxPages = 39; Serial.println("Unknown tag type, using NTAG213 page limit as fallback"); } Serial.print("Maximum writable page: "); Serial.println(maxPages); return maxPages; } bool initializeNdefStructure() { // Write minimal NDEF structure without destroying the tag // This creates a clean slate while preserving tag functionality Serial.println("Initialisiere sichere NDEF-Struktur..."); // Minimal NDEF structure: TLV with empty message uint8_t minimalNdef[8] = { 0x03, // NDEF Message TLV Tag 0x03, // Length (3 bytes for minimal empty record) 0xD0, // NDEF Record Header (TNF=0x0:Empty + SR + ME + MB) 0x00, // Type Length (0 = empty record) 0x00, // Payload Length (0 = empty record) 0xFE, // Terminator TLV 0x00, 0x00 // Padding }; // Write the minimal structure starting at page 4 uint8_t pageBuffer[4]; for (int i = 0; i < 8; i += 4) { memcpy(pageBuffer, &minimalNdef[i], 4); if (!nfc.ntag2xx_WritePage(4 + (i / 4), pageBuffer)) { Serial.print("Fehler beim Initialisieren von Seite "); Serial.println(4 + (i / 4)); return false; } Serial.print("Seite "); Serial.print(4 + (i / 4)); Serial.print(" initialisiert: "); for (int j = 0; j < 4; j++) { if (pageBuffer[j] < 0x10) Serial.print("0"); Serial.print(pageBuffer[j], HEX); Serial.print(" "); } Serial.println(); } Serial.println("✓ Sichere NDEF-Struktur initialisiert"); Serial.println("✓ Tag bleibt funktionsfähig und überschreibbar"); return true; } bool clearUserDataArea() { // IMPORTANT: Only clear user data pages, NOT configuration pages // NTAG layout: Pages 0-3 (header), 4-N (user data), N+1-N+3 (config) - NEVER touch config! String tagType = detectNtagType(); // Calculate safe user data page ranges (NEVER touch config pages!) uint16_t firstUserPage = 4; uint16_t lastUserPage = 0; if (tagType == "NTAG213") { lastUserPage = 39; // Pages 40-42 are config - DO NOT TOUCH! Serial.println("NTAG213: Sichere Löschung Seiten 4-39"); } else if (tagType == "NTAG215") { lastUserPage = 129; // Pages 130-132 are config - DO NOT TOUCH! Serial.println("NTAG215: Sichere Löschung Seiten 4-129"); } else if (tagType == "NTAG216") { lastUserPage = 225; // Pages 226-228 are config - DO NOT TOUCH! Serial.println("NTAG216: Sichere Löschung Seiten 4-225"); } else { // Conservative fallback - only clear a small safe area lastUserPage = 39; Serial.println("UNKNOWN TAG: Konservative Löschung Seiten 4-39"); } Serial.println("WARNUNG: Vollständiges Löschen kann Tag beschädigen!"); Serial.println("Verwende stattdessen selective NDEF-Überschreibung..."); // Instead of clearing everything, just write a minimal NDEF structure // This is much safer and preserves tag integrity return initializeNdefStructure(); } uint8_t ntag2xx_WriteNDEF(const char *payload) { // Determine exact tag type and capabilities first String tagType = detectNtagType(); uint16_t tagSize = readTagSize(); uint16_t availableUserData = getAvailableUserDataSize(); uint16_t maxWritablePage = getMaxUserDataPages(); Serial.println("=== NFC TAG ANALYSIS ==="); Serial.print("Tag Type: ");Serial.println(tagType); Serial.print("Total Tag Size: ");Serial.println(tagSize); Serial.print("Available User Data: ");Serial.println(availableUserData); Serial.print("Max Writable Page: ");Serial.println(maxWritablePage); Serial.println("========================"); // Perform additional tag validation by testing write boundaries Serial.println("=== TAG VALIDATION ==="); uint8_t testBuffer[4] = {0x00, 0x00, 0x00, 0x00}; // Test if we can actually read the max page if (!nfc.ntag2xx_ReadPage(maxWritablePage, testBuffer)) { Serial.print("WARNING: Cannot read declared max page "); Serial.println(maxWritablePage); // Find actual maximum writable page by testing backwards with optimized approach uint16_t actualMaxPage = maxWritablePage; Serial.println("Searching for actual maximum writable page..."); // Use binary search approach for faster page limit detection uint16_t lowPage = 4; uint16_t highPage = maxWritablePage; uint16_t testAttempts = 0; const uint16_t maxTestAttempts = 15; // Limit search attempts while (lowPage <= highPage && testAttempts < maxTestAttempts) { uint16_t midPage = (lowPage + highPage) / 2; testAttempts++; Serial.print("Testing page "); Serial.print(midPage); Serial.print(" (attempt "); Serial.print(testAttempts); Serial.print("/"); Serial.print(maxTestAttempts); Serial.print(")... "); if (nfc.ntag2xx_ReadPage(midPage, testBuffer)) { Serial.println("✓"); actualMaxPage = midPage; lowPage = midPage + 1; // Search higher } else { Serial.println("❌"); highPage = midPage - 1; // Search lower } // Small delay to prevent interface overload vTaskDelay(5 / portTICK_PERIOD_MS); yield(); } Serial.print("Found actual max readable page: "); Serial.println(actualMaxPage); Serial.print("Search completed in "); Serial.print(testAttempts); Serial.println(" attempts"); maxWritablePage = actualMaxPage; } else { Serial.print("✓ Max page ");Serial.print(maxWritablePage);Serial.println(" is readable"); } // Calculate maximum available user data based on actual writable pages uint16_t actualUserDataSize = (maxWritablePage - 3) * 4; // -3 because pages 0-3 are header availableUserData = actualUserDataSize; Serial.print("Actual available user data: "); Serial.print(actualUserDataSize); Serial.println(" bytes"); Serial.println("========================"); uint8_t pageBuffer[4] = {0, 0, 0, 0}; Serial.println("Beginne mit dem Schreiben der NDEF-Nachricht..."); // Figure out how long the string is uint16_t payloadLen = strlen(payload); Serial.print("Länge der Payload: "); Serial.println(payloadLen); Serial.print("Payload: ");Serial.println(payload); // MIME type for JSON const char mimeType[] = "application/json"; uint8_t mimeTypeLen = strlen(mimeType); // Calculate NDEF record size uint8_t ndefRecordHeaderSize = 3; // Header byte + Type Length + Payload Length (short record) uint16_t ndefRecordSize = ndefRecordHeaderSize + mimeTypeLen + payloadLen; // Calculate TLV size - need to check if we need extended length format uint8_t tlvHeaderSize; uint16_t totalTlvSize; if (ndefRecordSize <= 254) { // Standard TLV format: Tag (1) + Length (1) + Value (ndefRecordSize) tlvHeaderSize = 2; totalTlvSize = tlvHeaderSize + ndefRecordSize + 1; // +1 for terminator TLV } else { // Extended TLV format: Tag (1) + 0xFF + Length (2) + Value (ndefRecordSize) tlvHeaderSize = 4; totalTlvSize = tlvHeaderSize + ndefRecordSize + 1; // +1 for terminator TLV } Serial.print("NDEF Record Size: "); Serial.println(ndefRecordSize); Serial.print("Total TLV Size: "); Serial.println(totalTlvSize); // Check if the message fits in the available user data space if (totalTlvSize > availableUserData) { Serial.println(); Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!"); Serial.println("FEHLER: Payload zu groß für diesen Tag-Typ!"); Serial.print("Tag-Typ: ");Serial.println(tagType); Serial.print("Benötigt: ");Serial.print(totalTlvSize);Serial.println(" Bytes"); Serial.print("Verfügbar: ");Serial.print(availableUserData);Serial.println(" Bytes"); Serial.print("Überschuss: ");Serial.print(totalTlvSize - availableUserData);Serial.println(" Bytes"); if (tagType == "NTAG213") { Serial.println("EMPFEHLUNG: Verwenden Sie einen NTAG215 (504 Bytes) oder NTAG216 (888 Bytes) Tag!"); Serial.println("Oder kürzen Sie die Payload um mindestens " + String(totalTlvSize - availableUserData) + " Bytes."); } Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!"); Serial.println(); oledShowMessage("Tag zu klein für Payload"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.println("✓ Payload passt in den Tag - Schreibvorgang wird fortgesetzt"); // STEP 1: NFC Interface Reset and Reinitialization Serial.println(); Serial.println("=== SCHRITT 1: NFC-INTERFACE RESET UND NEUINITIALISIERUNG ==="); // First, check if the NFC interface is working at all Serial.println("Teste aktuellen NFC-Interface-Zustand..."); // Try to read capability container (which worked during detection) uint8_t ccTest[4]; bool ccReadable = nfc.ntag2xx_ReadPage(3, ccTest); Serial.print("Capability Container (Seite 3) lesbar: "); Serial.println(ccReadable ? "✓" : "❌"); if (!ccReadable) { Serial.println("❌ NFC-Interface ist nicht funktionsfähig - führe Reset durch"); // Perform NFC interface reset and reinitialization Serial.println("Führe NFC-Interface Reset durch..."); // Step 1: Try to reinitialize the NFC interface completely Serial.println("1. Neuinitialisierung des PN532..."); // Reinitialize the PN532 nfc.begin(); vTaskDelay(500 / portTICK_PERIOD_MS); // Give it time to initialize // Check firmware version to ensure communication is working uint32_t versiondata = nfc.getFirmwareVersion(); if (versiondata) { Serial.print("PN532 Firmware Version: 0x"); Serial.println(versiondata, HEX); Serial.println("✓ PN532 Kommunikation wiederhergestellt"); } else { Serial.println("❌ PN532 Kommunikation fehlgeschlagen"); oledShowMessage("NFC Reset failed"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } // Step 2: Reconfigure SAM Serial.println("2. SAM-Konfiguration..."); nfc.SAMConfig(); vTaskDelay(200 / portTICK_PERIOD_MS); // Step 3: Re-detect the tag Serial.println("3. Tag-Wiedererkennung..."); uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t uidLength; bool tagRedetected = false; for (int attempts = 0; attempts < 5; attempts++) { Serial.print("Tag-Erkennungsversuch "); Serial.print(attempts + 1); Serial.print("/5... "); if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000)) { Serial.println("✓"); tagRedetected = true; break; } else { Serial.println("❌"); vTaskDelay(300 / portTICK_PERIOD_MS); } } if (!tagRedetected) { Serial.println("❌ Tag konnte nach Reset nicht wiedererkannt werden"); oledShowMessage("Tag lost after reset"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.println("✓ Tag erfolgreich wiedererkannt"); // Step 4: Test basic page reading Serial.println("4. Test der Grundfunktionalität..."); vTaskDelay(200 / portTICK_PERIOD_MS); // Give interface time to stabilize ccReadable = nfc.ntag2xx_ReadPage(3, ccTest); Serial.print("Capability Container nach Reset lesbar: "); Serial.println(ccReadable ? "✓" : "❌"); if (!ccReadable) { Serial.println("❌ NFC-Interface funktioniert nach Reset immer noch nicht"); oledShowMessage("NFC still broken"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.println("✓ NFC-Interface erfolgreich wiederhergestellt"); } else { Serial.println("✓ NFC-Interface ist funktionsfähig"); } // Display CC content for debugging if (ccReadable) { Serial.print("CC Inhalt: "); for (int i = 0; i < 4; i++) { if (ccTest[i] < 0x10) Serial.print("0"); Serial.print(ccTest[i], HEX); Serial.print(" "); } Serial.println(); } Serial.println("=== SCHRITT 2: INTERFACE-FUNKTIONSTEST ==="); // Test a few critical pages to ensure stable operation uint8_t testData[4]; bool basicPagesReadable = true; for (uint8_t testPage = 0; testPage <= 6; testPage++) { bool readable = nfc.ntag2xx_ReadPage(testPage, testData); Serial.print("Seite "); Serial.print(testPage); Serial.print(": "); if (readable) { Serial.print("✓ - "); for (int i = 0; i < 4; i++) { if (testData[i] < 0x10) Serial.print("0"); Serial.print(testData[i], HEX); Serial.print(" "); } Serial.println(); } else { Serial.println("❌ - Nicht lesbar"); if (testPage >= 3 && testPage <= 6) { // Critical pages for NDEF basicPagesReadable = false; } } vTaskDelay(10 / portTICK_PERIOD_MS); // Small delay between reads } if (!basicPagesReadable) { Serial.println("❌ KRITISCHER FEHLER: Grundlegende NDEF-Seiten nicht lesbar!"); Serial.println("Tag oder Interface ist defekt"); oledShowMessage("Tag/Interface defect"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.println("✓ Alle kritischen Seiten sind lesbar"); Serial.println("==================================================="); Serial.println(); Serial.println("=== SCHRITT 3: SCHREIBBEREITSCHAFTSTEST ==="); // Test write capabilities before attempting the full write Serial.println("Teste Schreibfähigkeiten des Tags..."); uint8_t testPage[4] = {0xAA, 0xBB, 0xCC, 0xDD}; // Test pattern uint8_t originalPage[4]; // Store original content // First, read original content of test page if (!nfc.ntag2xx_ReadPage(10, originalPage)) { Serial.println("FEHLER: Kann Testseite nicht lesen für Backup"); oledShowMessage("Test page read error"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.print("Original Inhalt Seite 10: "); for (int i = 0; i < 4; i++) { if (originalPage[i] < 0x10) Serial.print("0"); Serial.print(originalPage[i], HEX); Serial.print(" "); } Serial.println(); // Perform write test if (!nfc.ntag2xx_WritePage(10, testPage)) { Serial.println("FEHLER: Schreibtest fehlgeschlagen!"); Serial.println("Tag ist möglicherweise schreibgeschützt oder defekt"); // Additional diagnostics Serial.println("=== ERWEITERTE SCHREIBTEST-DIAGNOSE ==="); // Check if tag is still present uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t uidLength; bool tagStillPresent = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000); Serial.print("Tag noch erkannt: "); Serial.println(tagStillPresent ? "✓" : "❌"); if (!tagStillPresent) { Serial.println("URSACHE: Tag wurde während Schreibtest entfernt!"); oledShowMessage("Tag removed"); } else { Serial.println("URSACHE: Tag ist vorhanden aber nicht beschreibbar"); Serial.println("Möglicherweise: Schreibschutz, Defekt, oder Interface-Problem"); oledShowMessage("Tag write protected?"); } Serial.println("=========================================="); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } // Verify test write uint8_t readBack[4]; vTaskDelay(20 / portTICK_PERIOD_MS); // Wait for write to complete if (!nfc.ntag2xx_ReadPage(10, readBack)) { Serial.println("FEHLER: Kann Testdaten nicht zurücklesen!"); oledShowMessage("Test verify failed"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } bool testSuccess = true; for (int i = 0; i < 4; i++) { if (readBack[i] != testPage[i]) { testSuccess = false; break; } } if (!testSuccess) { Serial.println("FEHLER: Schreibtest fehlgeschlagen - Daten stimmen nicht überein!"); Serial.print("Geschrieben: "); for (int i = 0; i < 4; i++) { Serial.print(testPage[i], HEX); Serial.print(" "); } Serial.println(); Serial.print("Gelesen: "); for (int i = 0; i < 4; i++) { Serial.print(readBack[i], HEX); Serial.print(" "); } Serial.println(); return 0; } // Restore original content Serial.println("Stelle ursprünglichen Inhalt wieder her..."); if (!nfc.ntag2xx_WritePage(10, originalPage)) { Serial.println("WARNUNG: Konnte ursprünglichen Inhalt nicht wiederherstellen!"); } else { Serial.println("✓ Ursprünglicher Inhalt wiederhergestellt"); } Serial.println("✓ Schreibtest erfolgreich - Tag ist voll funktionsfähig"); Serial.println("======================================================"); // STEP 4: NDEF initialization with verification Serial.println(); Serial.println("=== SCHRITT 4: NDEF-INITIALISIERUNG ==="); if (!initializeNdefStructure()) { Serial.println("FEHLER: Konnte NDEF-Struktur nicht initialisieren!"); oledShowMessage("NDEF init failed"); vTaskDelay(2000 / portTICK_PERIOD_MS); return 0; } // Verify NDEF initialization uint8_t ndefCheck[8]; bool ndefVerified = true; for (uint8_t page = 4; page < 6; page++) { if (!nfc.ntag2xx_ReadPage(page, &ndefCheck[(page-4)*4])) { ndefVerified = false; break; } } if (ndefVerified) { Serial.print("NDEF-Header nach Initialisierung: "); for (int i = 0; i < 8; i++) { if (ndefCheck[i] < 0x10) Serial.print("0"); Serial.print(ndefCheck[i], HEX); Serial.print(" "); } Serial.println(); } Serial.println("✓ NDEF-Struktur initialisiert und verifiziert"); Serial.println("=========================================="); // STEP 5: Allow interface to stabilize before major write operation Serial.println(); Serial.println("=== SCHRITT 5: NFC-INTERFACE STABILISIERUNG ==="); Serial.println("Stabilisiere NFC-Interface vor Hauptschreibvorgang..."); // Give the interface time to fully settle after NDEF initialization vTaskDelay(200 / portTICK_PERIOD_MS); // Test interface stability with a simple read uint8_t stabilityTest[4]; bool interfaceStable = false; for (int attempts = 0; attempts < 3; attempts++) { if (nfc.ntag2xx_ReadPage(4, stabilityTest)) { Serial.print("Interface stability test "); Serial.print(attempts + 1); Serial.println("/3: ✓"); interfaceStable = true; break; } else { Serial.print("Interface stability test "); Serial.print(attempts + 1); Serial.println("/3: ❌"); vTaskDelay(100 / portTICK_PERIOD_MS); } } if (!interfaceStable) { Serial.println("FEHLER: NFC-Interface ist nicht stabil genug für Schreibvorgang"); oledShowMessage("NFC Interface unstable"); vTaskDelay(3000 / portTICK_PERIOD_MS); return 0; } Serial.println("✓ NFC-Interface ist stabil - Schreibvorgang kann beginnen"); Serial.println("========================================================="); // Allocate memory for the complete TLV structure uint8_t* tlvData = (uint8_t*) malloc(totalTlvSize); if (tlvData == NULL) { Serial.println("Fehler: Nicht genug Speicher für TLV-Daten vorhanden."); oledShowMessage("Memory error"); vTaskDelay(2000 / portTICK_PERIOD_MS); return 0; } // Build TLV structure uint16_t offset = 0; // TLV Header tlvData[offset++] = 0x03; // NDEF Message TLV Tag if (ndefRecordSize <= 254) { // Standard length format tlvData[offset++] = (uint8_t)ndefRecordSize; } else { // Extended length format tlvData[offset++] = 0xFF; tlvData[offset++] = (uint8_t)(ndefRecordSize >> 8); // High byte tlvData[offset++] = (uint8_t)(ndefRecordSize & 0xFF); // Low byte } // NDEF Record Header tlvData[offset++] = 0xD2; // NDEF Record Header (TNF=0x2:MIME Media + SR + ME + MB) tlvData[offset++] = mimeTypeLen; // Type Length tlvData[offset++] = (uint8_t)payloadLen; // Payload Length (short record format) // MIME Type memcpy(&tlvData[offset], mimeType, mimeTypeLen); offset += mimeTypeLen; // JSON Payload memcpy(&tlvData[offset], payload, payloadLen); offset += payloadLen; // Terminator TLV tlvData[offset] = 0xFE; Serial.print("Gesamt-TLV-Länge: "); Serial.println(offset + 1); // Debug: Print first 64 bytes of TLV data Serial.println("TLV Daten (erste 64 Bytes):"); for (int i = 0; i < min((int)(offset + 1), 64); i++) { if (tlvData[i] < 0x10) Serial.print("0"); Serial.print(tlvData[i], HEX); Serial.print(" "); if ((i + 1) % 16 == 0) Serial.println(); } Serial.println(); // Write data to tag pages (starting from page 4) uint16_t bytesWritten = 0; uint8_t pageNumber = 4; uint16_t totalBytes = offset + 1; Serial.println(); Serial.println("=== SCHRITT 6: SCHREIBE NEUE NDEF-DATEN ==="); Serial.print("Schreibe "); Serial.print(totalBytes); Serial.print(" Bytes in "); Serial.print((totalBytes + 3) / 4); // Round up division Serial.println(" Seiten..."); while (bytesWritten < totalBytes && pageNumber <= maxWritablePage) { // Additional safety check before writing each page if (pageNumber > maxWritablePage) { Serial.print("STOP: Reached maximum writable page "); Serial.println(maxWritablePage); break; } // Clear page buffer memset(pageBuffer, 0, 4); // Calculate how many bytes to write to this page uint16_t bytesToWrite = min(4, (int)(totalBytes - bytesWritten)); // Copy data to page buffer memcpy(pageBuffer, &tlvData[bytesWritten], bytesToWrite); // Write page to tag with retry mechanism bool writeSuccess = false; for (int writeAttempt = 0; writeAttempt < 3; writeAttempt++) { if (nfc.ntag2xx_WritePage(pageNumber, pageBuffer)) { writeSuccess = true; break; } else { Serial.print("Schreibversuch "); Serial.print(writeAttempt + 1); Serial.print("/3 für Seite "); Serial.print(pageNumber); Serial.println(" fehlgeschlagen"); if (writeAttempt < 2) { vTaskDelay(50 / portTICK_PERIOD_MS); // Wait before retry } } } if (!writeSuccess) { Serial.print("FEHLER beim Schreiben der Seite "); Serial.println(pageNumber); Serial.print("Möglicherweise Page-Limit erreicht für "); Serial.println(tagType); Serial.print("Erwartetes Maximum: "); Serial.println(maxWritablePage); Serial.print("Tatsächliches Maximum scheint niedriger zu sein!"); // Update max page for future operations if (pageNumber > 4) { Serial.print("Setze neues Maximum auf Seite "); Serial.println(pageNumber - 1); } free(tlvData); return 0; } // IMMEDIATE verification after each write - this is critical! Serial.print("Verifiziere Seite "); Serial.print(pageNumber); Serial.print("... "); uint8_t verifyBuffer[4]; vTaskDelay(20 / portTICK_PERIOD_MS); // Increased delay before verification // Verification with retry mechanism bool verifySuccess = false; for (int verifyAttempt = 0; verifyAttempt < 3; verifyAttempt++) { if (nfc.ntag2xx_ReadPage(pageNumber, verifyBuffer)) { bool writeMatches = true; for (int i = 0; i < bytesToWrite; i++) { if (verifyBuffer[i] != pageBuffer[i]) { writeMatches = false; Serial.println(); Serial.print("VERIFIKATIONSFEHLER bei Byte "); Serial.print(i); Serial.print(" - Erwartet: 0x"); Serial.print(pageBuffer[i], HEX); Serial.print(", Gelesen: 0x"); Serial.println(verifyBuffer[i], HEX); break; } } if (writeMatches) { verifySuccess = true; break; } else if (verifyAttempt < 2) { Serial.print("Verifikationsversuch "); Serial.print(verifyAttempt + 1); Serial.println("/3 fehlgeschlagen, wiederhole..."); vTaskDelay(30 / portTICK_PERIOD_MS); } } else { Serial.print("Verifikations-Read-Versuch "); Serial.print(verifyAttempt + 1); Serial.println("/3 fehlgeschlagen"); if (verifyAttempt < 2) { vTaskDelay(30 / portTICK_PERIOD_MS); } } } if (!verifySuccess) { Serial.println("❌ SCHREIBVORGANG/VERIFIKATION FEHLGESCHLAGEN!"); free(tlvData); return 0; } else { Serial.println("✓"); } Serial.print("Seite "); Serial.print(pageNumber); Serial.print(" ✓: "); for (int i = 0; i < 4; i++) { if (pageBuffer[i] < 0x10) Serial.print("0"); Serial.print(pageBuffer[i], HEX); Serial.print(" "); } Serial.println(); bytesWritten += bytesToWrite; pageNumber++; yield(); vTaskDelay(10 / portTICK_PERIOD_MS); // Slightly increased delay between page writes } free(tlvData); if (bytesWritten < totalBytes) { Serial.println("WARNUNG: Nicht alle Daten konnten geschrieben werden!"); Serial.print("Geschrieben: "); Serial.print(bytesWritten); Serial.print(" von "); Serial.print(totalBytes); Serial.println(" Bytes"); Serial.print("Gestoppt bei Seite: "); Serial.println(pageNumber - 1); return 0; } Serial.println(); Serial.println("✓ NDEF-Nachricht erfolgreich geschrieben!"); Serial.print("✓ Tag-Typ: ");Serial.println(tagType); Serial.print("✓ Insgesamt ");Serial.print(bytesWritten);Serial.println(" Bytes geschrieben"); Serial.print("✓ Verwendete Seiten: 4-");Serial.println(pageNumber - 1); Serial.print("✓ Speicher-Auslastung: "); Serial.print((bytesWritten * 100) / availableUserData); Serial.println("%"); Serial.println("✓ Bestehende Daten wurden überschrieben"); // CRITICAL: Allow NFC interface to stabilize after write operation Serial.println(); Serial.println("=== SCHRITT 7: NFC-INTERFACE STABILISIERUNG NACH SCHREIBVORGANG ==="); Serial.println("Stabilisiere NFC-Interface nach Schreibvorgang..."); // Give the tag and interface time to settle after write operation vTaskDelay(300 / portTICK_PERIOD_MS); // Increased stabilization time // Test if the interface is still responsive uint8_t postWriteTest[4]; bool interfaceResponsive = false; for (int stabilityAttempt = 0; stabilityAttempt < 5; stabilityAttempt++) { Serial.print("Post-write interface test "); Serial.print(stabilityAttempt + 1); Serial.print("/5... "); if (nfc.ntag2xx_ReadPage(3, postWriteTest)) { // Read capability container Serial.println("✓"); interfaceResponsive = true; break; } else { Serial.println("❌"); if (stabilityAttempt < 4) { Serial.println("Warte und versuche Interface zu stabilisieren..."); vTaskDelay(200 / portTICK_PERIOD_MS); // Try to re-establish communication with a simple tag presence check uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t uidLength; bool tagStillPresent = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000); Serial.print("Tag presence check: "); Serial.println(tagStillPresent ? "✓" : "❌"); if (!tagStillPresent) { Serial.println("Tag wurde während/nach Schreibvorgang entfernt!"); break; } } } } if (!interfaceResponsive) { Serial.println("WARNUNG: NFC-Interface reagiert nach Schreibvorgang nicht mehr stabil"); Serial.println("Schreibvorgang war erfolgreich, aber Interface benötigt möglicherweise Reset"); } else { Serial.println("✓ NFC-Interface ist nach Schreibvorgang stabil"); } Serial.println("=================================================================="); return 1; } bool decodeNdefAndReturnJson(const byte* encodedMessage, String uidString) { oledShowProgressBar(1, octoEnabled?5:4, "Reading", "Decoding data"); // Debug: Print first 32 bytes of the raw data Serial.println("Raw NDEF data (first 32 bytes):"); for (int i = 0; i < 32; i++) { if (encodedMessage[i] < 0x10) Serial.print("0"); Serial.print(encodedMessage[i], HEX); Serial.print(" "); if ((i + 1) % 16 == 0) Serial.println(); } Serial.println(); // Look for the NDEF TLV structure starting from the beginning int tlvOffset = 0; bool foundNdefTlv = false; // Search for NDEF TLV (0x03) in the first few bytes for (int i = 0; i < 16; i++) { if (encodedMessage[i] == 0x03) { tlvOffset = i; foundNdefTlv = true; Serial.print("Found NDEF TLV at offset: "); Serial.println(tlvOffset); break; } } if (!foundNdefTlv) { Serial.println("No NDEF TLV found in tag data"); return false; } // Get the NDEF message length from TLV uint16_t ndefMessageLength = 0; int ndefRecordOffset = 0; if (encodedMessage[tlvOffset + 1] == 0xFF) { // Extended length format: next 2 bytes contain the actual length ndefMessageLength = (encodedMessage[tlvOffset + 2] << 8) | encodedMessage[tlvOffset + 3]; ndefRecordOffset = tlvOffset + 4; // Skip TLV tag + 0xFF + 2 length bytes Serial.print("NDEF Message Length (extended): "); } else { // Standard length format: single byte contains the length ndefMessageLength = encodedMessage[tlvOffset + 1]; ndefRecordOffset = tlvOffset + 2; // Skip TLV tag + 1 length byte Serial.print("NDEF Message Length (standard): "); } Serial.println(ndefMessageLength); // Get pointer to NDEF record const byte* ndefRecord = &encodedMessage[ndefRecordOffset]; // Parse NDEF record header byte recordHeader = ndefRecord[0]; byte typeLength = ndefRecord[1]; Serial.print("NDEF Record Header: 0x"); Serial.println(recordHeader, HEX); Serial.print("Type Length: "); Serial.println(typeLength); // Determine payload length (can be 1 or 4 bytes depending on SR flag) uint32_t payloadLength = 0; byte payloadLengthBytes = 1; byte payloadLengthOffset = 2; // Check if Short Record (SR) flag is set (bit 4) if (recordHeader & 0x10) { // SR flag payloadLength = ndefRecord[2]; payloadLengthBytes = 1; payloadLengthOffset = 2; } else { // Long record format (4 bytes for payload length) payloadLength = (ndefRecord[2] << 24) | (ndefRecord[3] << 16) | (ndefRecord[4] << 8) | ndefRecord[5]; payloadLengthBytes = 4; payloadLengthOffset = 2; } Serial.print("Payload Length: "); Serial.println(payloadLength); Serial.print("Payload Length Bytes: "); Serial.println(payloadLengthBytes); // Check for ID field (if IL flag is set) byte idLength = 0; if (recordHeader & 0x08) { // IL flag idLength = ndefRecord[payloadLengthOffset + payloadLengthBytes]; Serial.print("ID Length: "); Serial.println(idLength); } // Calculate offset to payload byte payloadOffset = 1 + 1 + payloadLengthBytes + typeLength + idLength; Serial.print("Calculated payload offset: "); Serial.println(payloadOffset); // Verify we have enough data if (payloadOffset + payloadLength > ndefMessageLength) { Serial.println("Invalid NDEF structure - payload extends beyond message"); Serial.print("Payload offset + length: "); Serial.print(payloadOffset + payloadLength); Serial.print(", NDEF message length: "); Serial.println(ndefMessageLength); return false; } // Print the record type for debugging Serial.print("Record Type: "); for (int i = 0; i < typeLength; i++) { Serial.print((char)ndefRecord[1 + 1 + payloadLengthBytes + i]); } Serial.println(); nfcJsonData = ""; // Extract JSON payload with validation uint32_t actualJsonLength = 0; for (uint32_t i = 0; i < payloadLength; i++) { byte currentByte = ndefRecord[payloadOffset + i]; // Stop at null terminator or if we find the end of JSON if (currentByte == 0x00) { Serial.print("Found null terminator at position: "); Serial.println(i); break; } // Only add printable characters and common JSON characters if (currentByte >= 32 && currentByte <= 126) { nfcJsonData += (char)currentByte; actualJsonLength++; } else { Serial.print("Skipping non-printable byte at position "); Serial.print(i); Serial.print(": 0x"); Serial.println(currentByte, HEX); } // Check if we've reached the end of a JSON object if (currentByte == '}') { // Count opening and closing braces to detect complete JSON int braceCount = 0; for (uint32_t j = 0; j <= i; j++) { if (ndefRecord[payloadOffset + j] == '{') braceCount++; else if (ndefRecord[payloadOffset + j] == '}') braceCount--; } if (braceCount == 0) { Serial.print("Found complete JSON object at position: "); Serial.println(i); actualJsonLength = i + 1; break; } } } Serial.print("Actual JSON length extracted: "); Serial.println(actualJsonLength); Serial.print("Total nfcJsonData length: "); Serial.println(nfcJsonData.length()); Serial.println("=== DECODED JSON DATA START ==="); Serial.println(nfcJsonData); Serial.println("=== DECODED JSON DATA END ==="); // Check if JSON was truncated if (nfcJsonData.length() < payloadLength && !nfcJsonData.endsWith("}")) { Serial.println("WARNING: JSON payload appears to be truncated!"); Serial.print("Expected payload length: "); Serial.println(payloadLength); Serial.print("Actual extracted length: "); Serial.println(nfcJsonData.length()); } // Trim any trailing whitespace or invalid characters nfcJsonData.trim(); // JSON-Dokument verarbeiten JsonDocument doc; DeserializationError error = deserializeJson(doc, nfcJsonData); if (error) { nfcJsonData = ""; Serial.println("Fehler beim Verarbeiten des JSON-Dokuments"); Serial.print("deserializeJson() failed: "); Serial.println(error.f_str()); return false; } else { // If spoolman is unavailable, there is no point in continuing if(spoolmanConnected){ // Sende die aktualisierten AMS-Daten an alle WebSocket-Clients Serial.println("JSON-Dokument erfolgreich verarbeitet"); Serial.println(doc.as()); if (doc["sm_id"].is() && doc["sm_id"] != "" && doc["sm_id"] != "0") { oledShowProgressBar(2, octoEnabled?5:4, "Spool Tag", "Weighing"); Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as()); activeSpoolId = doc["sm_id"].as(); lastSpoolId = activeSpoolId; } else if(doc["location"].is() && doc["location"] != "") { Serial.println("Location Tag found!"); String location = doc["location"].as(); if(lastSpoolId != ""){ updateSpoolLocation(lastSpoolId, location); } else { Serial.println("Location update tag scanned without scanning spool before!"); oledShowProgressBar(1, 1, "Failure", "Scan spool first"); } } // Brand Filament not registered to Spoolman else if ((!doc["sm_id"].is() || (doc["sm_id"].is() && (doc["sm_id"] == "0" || doc["sm_id"] == ""))) && doc["b"].is() && doc["an"].is()) { doc["sm_id"] = "0"; // Ensure sm_id is set to 0 // If no sm_id is present but the brand is Brand Filament then // create a new spool, maybe brand too, in Spoolman Serial.println("New Brand Filament Tag found!"); createBrandFilament(doc, uidString); } else { Serial.println("Keine SPOOL-ID gefunden."); activeSpoolId = ""; oledShowProgressBar(1, 1, "Failure", "Unkown tag"); } }else{ oledShowProgressBar(octoEnabled?5:4, octoEnabled?5:4, "Failure!", "Spoolman unavailable"); } } doc.clear(); return true; } bool quickSpoolIdCheck(String uidString) { // Fast-path: Read NDEF structure to quickly locate and check JSON payload // This dramatically speeds up known spool recognition // CRITICAL: Do not execute during write operations! if (nfcWriteInProgress) { Serial.println("FAST-PATH: Skipped during write operation"); return false; } Serial.println("=== FAST-PATH: Quick sm_id Check ==="); // Read enough pages to cover NDEF header + beginning of payload (pages 4-8 = 20 bytes) uint8_t ndefData[20]; memset(ndefData, 0, 20); for (uint8_t page = 4; page < 9; page++) { if (!nfc.ntag2xx_ReadPage(page, ndefData + (page - 4) * 4)) { Serial.print("Failed to read page "); Serial.println(page); return false; // Fall back to full read } } // Parse NDEF structure to find JSON payload start Serial.print("Raw NDEF data (first 20 bytes): "); for (int i = 0; i < 20; i++) { if (ndefData[i] < 0x10) Serial.print("0"); Serial.print(ndefData[i], HEX); Serial.print(" "); } Serial.println(); // Look for NDEF TLV (0x03) at the beginning int tlvOffset = -1; for (int i = 0; i < 8; i++) { if (ndefData[i] == 0x03) { tlvOffset = i; Serial.print("Found NDEF TLV at offset: "); Serial.println(tlvOffset); break; } } if (tlvOffset == -1) { Serial.println("✗ FAST-PATH: No NDEF TLV found"); return false; } // Parse NDEF record to find JSON payload int ndefRecordStart; if (ndefData[tlvOffset + 1] == 0xFF) { // Extended length format ndefRecordStart = tlvOffset + 4; } else { // Standard length format ndefRecordStart = tlvOffset + 2; } if (ndefRecordStart >= 20) { Serial.println("✗ FAST-PATH: NDEF record starts beyond read data"); return false; } // Parse NDEF record header uint8_t recordHeader = ndefData[ndefRecordStart]; uint8_t typeLength = ndefData[ndefRecordStart + 1]; // Calculate payload offset uint8_t payloadLengthBytes = (recordHeader & 0x10) ? 1 : 4; // SR flag check uint8_t idLength = (recordHeader & 0x08) ? ndefData[ndefRecordStart + 2 + payloadLengthBytes + typeLength] : 0; // IL flag check int payloadOffset = ndefRecordStart + 1 + 1 + payloadLengthBytes + typeLength + idLength; Serial.print("NDEF Record Header: 0x"); Serial.print(recordHeader, HEX); Serial.print(", Type Length: "); Serial.print(typeLength); Serial.print(", Payload offset: "); Serial.println(payloadOffset); // Check if payload starts within our read data if (payloadOffset >= 20) { Serial.println("✗ FAST-PATH: JSON payload starts beyond quick read data - need more pages"); // Read additional pages to get to JSON payload uint8_t extraData[16]; // Read 4 more pages memset(extraData, 0, 16); for (uint8_t page = 9; page < 13; page++) { if (!nfc.ntag2xx_ReadPage(page, extraData + (page - 9) * 4)) { Serial.print("Failed to read additional page "); Serial.println(page); return false; } } // Combine data uint8_t combinedData[36]; memcpy(combinedData, ndefData, 20); memcpy(combinedData + 20, extraData, 16); // Extract JSON from combined data String jsonStart = ""; int jsonStartPos = payloadOffset; for (int i = 0; i < 36 - payloadOffset && i < 30; i++) { uint8_t currentByte = combinedData[payloadOffset + i]; if (currentByte >= 32 && currentByte <= 126) { jsonStart += (char)currentByte; } // Stop at first brace to get just the beginning if (currentByte == '{' && i > 0) break; } Serial.print("JSON start from extended read: "); Serial.println(jsonStart); // Check for sm_id pattern - look for non-zero sm_id values if (jsonStart.indexOf("\"sm_id\":\"") >= 0) { int smIdStart = jsonStart.indexOf("\"sm_id\":\"") + 9; int smIdEnd = jsonStart.indexOf("\"", smIdStart); if (smIdEnd > smIdStart && smIdEnd < jsonStart.length()) { String quickSpoolId = jsonStart.substring(smIdStart, smIdEnd); Serial.print("Found sm_id in extended read: "); Serial.println(quickSpoolId); // Only process if sm_id is not "0" (known spool) if (quickSpoolId != "0" && quickSpoolId.length() > 0) { Serial.println("✓ FAST-PATH: Known spool detected!"); // Set as active spool immediately activeSpoolId = quickSpoolId; lastSpoolId = activeSpoolId; oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); return true; } else { Serial.println("✗ FAST-PATH: sm_id is 0 - new brand filament, need full read"); return false; } } } Serial.println("✗ FAST-PATH: No sm_id pattern in extended read"); return false; } // Extract JSON payload from the available data String quickJson = ""; for (int i = payloadOffset; i < 20 && i < payloadOffset + 15; i++) { uint8_t currentByte = ndefData[i]; if (currentByte >= 32 && currentByte <= 126) { quickJson += (char)currentByte; } } Serial.print("Quick JSON data: "); Serial.println(quickJson); // Look for sm_id pattern in the beginning of JSON - check for known vs new spools if (quickJson.indexOf("\"sm_id\":\"") >= 0) { Serial.println("✓ FAST-PATH: sm_id field found"); // Extract sm_id from quick data int smIdStart = quickJson.indexOf("\"sm_id\":\"") + 9; int smIdEnd = quickJson.indexOf("\"", smIdStart); if (smIdEnd > smIdStart && smIdEnd < quickJson.length()) { String quickSpoolId = quickJson.substring(smIdStart, smIdEnd); Serial.print("✓ Quick extracted sm_id: "); Serial.println(quickSpoolId); // Only process known spools (sm_id != "0") via fast path if (quickSpoolId != "0" && quickSpoolId.length() > 0) { Serial.println("✓ FAST-PATH: Known spool detected!"); // Set as active spool immediately activeSpoolId = quickSpoolId; lastSpoolId = activeSpoolId; oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); return true; } else { Serial.println("✗ FAST-PATH: sm_id is 0 - new brand filament, need full read"); return false; // sm_id="0" means new brand filament, needs full processing } } else { Serial.println("✗ FAST-PATH: Could not extract complete sm_id value"); return false; // Need full read to get complete sm_id } } // Check for other patterns that require full read if (quickJson.indexOf("\"location\":\"") >= 0) { Serial.println("✓ FAST-PATH: Location tag detected"); return false; // Need full read for location processing } if (quickJson.indexOf("\"brand\":\"") >= 0) { Serial.println("✓ FAST-PATH: Brand filament detected - may need full processing"); return false; // Need full read for brand filament creation } Serial.println("✗ FAST-PATH: No recognizable pattern - falling back to full read"); return false; // Fall back to full tag reading } void writeJsonToTag(void *parameter) { NfcWriteParameterType* params = (NfcWriteParameterType*)parameter; // Gib die erstellte NDEF-Message aus Serial.println("Erstelle NDEF-Message..."); Serial.println(params->payload); nfcReaderState = NFC_WRITING; nfcWriteInProgress = true; // Block high-level tag operations during write // Do NOT suspend the reading task - we need NFC interface for verification // Just use nfcWriteInProgress to prevent scanning and fast-path operations Serial.println("NFC Write Task starting - High-level operations blocked, low-level NFC available"); //pauseBambuMqttTask = true; // aktualisieren der Website wenn sich der Status ändert sendNfcData(); vTaskDelay(100 / portTICK_PERIOD_MS); // Wait 10sec for tag uint8_t success = 0; String uidString = ""; for (uint16_t i = 0; i < 20; i++) { uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uidLength; // yield before potentially waiting for 400ms yield(); esp_task_wdt_reset(); success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400); if (success) { for (uint8_t i = 0; i < uidLength; i++) { //TBD: Rework to remove all the string operations uidString += String(uid[i], HEX); if (i < uidLength - 1) { uidString += ":"; // Optional: Trennzeichen hinzufügen } } foundNfcTag(nullptr, success); break; } yield(); esp_task_wdt_reset(); vTaskDelay(pdMS_TO_TICKS(1)); } if (success) { oledShowProgressBar(1, 3, "Write Tag", "Writing"); // Schreibe die NDEF-Message auf den Tag success = ntag2xx_WriteNDEF(params->payload); if (success) { Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben"); //oledShowMessage("NFC-Tag written"); //vTaskDelay(1000 / portTICK_PERIOD_MS); nfcReaderState = NFC_WRITE_SUCCESS; // aktualisieren der Website wenn sich der Status ändert sendNfcData(); pauseBambuMqttTask = false; if(params->tagType){ // TBD: should this be simplified? if (updateSpoolTagId(uidString, params->payload) && params->tagType) { }else{ // Potentially handle errors } }else{ oledShowProgressBar(1, 1, "Write Tag", "Done!"); } // CRITICAL: Properly stabilize NFC interface after write operation Serial.println(); Serial.println("=== POST-WRITE NFC STABILIZATION ==="); // Wait for tag operations to complete vTaskDelay(500 / portTICK_PERIOD_MS); // Test tag presence and remove detection uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; uint8_t uidLength; int tagRemovalChecks = 0; Serial.println("Warte bis Tag entfernt wird..."); // Monitor tag presence while (tagRemovalChecks < 10) { yield(); esp_task_wdt_reset(); bool tagPresent = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500); if (!tagPresent) { Serial.println("✓ Tag wurde entfernt - NFC bereit für nächsten Scan"); break; } tagRemovalChecks++; Serial.print("Tag noch vorhanden ("); Serial.print(tagRemovalChecks); Serial.println("/10)"); vTaskDelay(500 / portTICK_PERIOD_MS); } if (tagRemovalChecks >= 10) { Serial.println("WARNUNG: Tag wurde nicht entfernt - fahre trotzdem fort"); } // Additional interface stabilization before resuming normal operations Serial.println("Stabilisiere NFC-Interface für normale Operationen..."); vTaskDelay(200 / portTICK_PERIOD_MS); // Test if interface is ready for normal scanning uint8_t interfaceTestBuffer[4]; bool interfaceReady = false; for (int testAttempt = 0; testAttempt < 3; testAttempt++) { // Try a simple interface operation (without requiring tag presence) Serial.print("Interface readiness test "); Serial.print(testAttempt + 1); Serial.print("/3... "); // Use a safe read operation that doesn't depend on tag presence // This tests if the PN532 chip itself is responsive uint32_t versiondata = nfc.getFirmwareVersion(); if (versiondata != 0) { Serial.println("✓"); interfaceReady = true; break; } else { Serial.println("❌"); vTaskDelay(100 / portTICK_PERIOD_MS); } } if (!interfaceReady) { Serial.println("WARNUNG: NFC-Interface reagiert nicht - könnte normale Scans beeinträchtigen"); } else { Serial.println("✓ NFC-Interface ist bereit für normale Scans"); } Serial.println("========================================="); vTaskResume(RfidReaderTask); vTaskDelay(500 / portTICK_PERIOD_MS); } else { Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag"); oledShowIcon("failed"); vTaskDelay(2000 / portTICK_PERIOD_MS); nfcReaderState = NFC_WRITE_ERROR; } } else { Serial.println("Fehler: Kein Tag zu schreiben gefunden."); oledShowProgressBar(1, 1, "Failure!", "No tag found"); vTaskDelay(2000 / portTICK_PERIOD_MS); nfcReaderState = NFC_IDLE; } sendWriteResult(nullptr, success); sendNfcData(); // Only reset the write protection flag - reading task was never suspended nfcWriteInProgress = false; // Re-enable high-level tag operations pauseBambuMqttTask = false; free(params->payload); delete params; vTaskDelete(NULL); } void startWriteJsonToTag(const bool isSpoolTag, const char* payload) { NfcWriteParameterType* parameters = new NfcWriteParameterType(); parameters->tagType = isSpoolTag; parameters->payload = strdup(payload); // Task nicht mehrfach starten if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) { oledShowProgressBar(0, 1, "Write Tag", "Place tag now"); // Erstelle die Task xTaskCreate( writeJsonToTag, // Task-Funktion "WriteJsonToTagTask", // Task-Name 5115, // Stackgröße in Bytes (void*)parameters, // Parameter rfidWriteTaskPrio, // Priorität NULL // Task-Handle (nicht benötigt) ); }else{ oledShowProgressBar(0, 1, "FAILURE", "NFC busy!"); // TBD: Add proper error handling (website) } } void scanRfidTask(void * parameter) { Serial.println("RFID Task gestartet"); for(;;) { // Skip scanning during write operations, but keep NFC interface active if (nfcReaderState != NFC_WRITING && !nfcWriteInProgress && !nfcReadingTaskSuspendRequest && !booting) { nfcReadingTaskSuspendState = false; yield(); uint8_t success; uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uidLength; success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500); foundNfcTag(nullptr, success); // As long as there is still a tag on the reader, do not try to read it again if (success && nfcReaderState == NFC_IDLE) { // Set the current tag as not processed tagProcessed = false; // Display some basic information about the card Serial.println("Found an ISO14443A card"); nfcReaderState = NFC_READING; oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag"); // Wait 1 second after tag detection to stabilize connection Serial.println("Tag detected, waiting 1 second for stabilization..."); vTaskDelay(1000 / portTICK_PERIOD_MS); // create Tag UID string String uidString = ""; for (uint8_t i = 0; i < uidLength; i++) { //TBD: Rework to remove all the string operations uidString += String(uid[i], HEX); if (i < uidLength - 1) { uidString += ":"; // Optional: Trennzeichen hinzufügen } } if (uidLength == 7) { // Try fast-path detection first for known spools if (quickSpoolIdCheck(uidString)) { Serial.println("✓ FAST-PATH: Tag processed quickly, skipping full read"); pauseBambuMqttTask = false; // Set reader back to idle for next scan nfcReaderState = NFC_READ_SUCCESS; delay(500); // Small delay before next scan continue; // Skip full tag reading and continue scan loop } Serial.println("Continuing with full tag read after fast-path check"); uint16_t tagSize = readTagSize(); if(tagSize > 0) { // Create a buffer depending on the size of the tag uint8_t* data = (uint8_t*)malloc(tagSize); memset(data, 0, tagSize); // We probably have an NTAG2xx card (though it could be Ultralight as well) Serial.println("Seems to be an NTAG2xx tag (7 byte UID)"); Serial.print("Tag size: "); Serial.print(tagSize); Serial.println(" bytes"); uint8_t numPages = readTagSize()/4; for (uint8_t i = 4; i < 4+numPages; i++) { if (!nfc.ntag2xx_ReadPage(i, data+(i-4) * 4)) { break; // Stop if reading fails } // Check for NDEF message end if (data[(i - 4) * 4] == 0xFE) { Serial.println("Found NDEF message end marker"); break; // End of NDEF message } yield(); esp_task_wdt_reset(); // Increased delay to ensure stable reading vTaskDelay(pdMS_TO_TICKS(5)); // Increased from 1ms to 5ms } Serial.println("Tag reading completed, starting NDEF decode..."); if (!decodeNdefAndReturnJson(data, uidString)) { oledShowProgressBar(1, 1, "Failure", "Unknown tag"); nfcReaderState = NFC_READ_ERROR; } else { nfcReaderState = NFC_READ_SUCCESS; } free(data); } else { oledShowProgressBar(1, 1, "Failure", "Tag read error"); nfcReaderState = NFC_READ_ERROR; } } else { //TBD: Show error here?! oledShowProgressBar(1, 1, "Failure", "Unkown tag type"); Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!"); } } if (!success && nfcReaderState != NFC_IDLE && !nfcReadingTaskSuspendRequest) { nfcReaderState = NFC_IDLE; //uidString = ""; nfcJsonData = ""; activeSpoolId = ""; Serial.println("Tag entfernt"); if (!bambuCredentials.autosend_enable) oledShowWeight(weight); } // Reset state after successful read when tag is removed else if (!success && nfcReaderState == NFC_READ_SUCCESS) { nfcReaderState = NFC_IDLE; Serial.println("Tag nach erfolgreichem Lesen entfernt - bereit für nächsten Tag"); } // Add a longer pause after successful reading to prevent immediate re-reading if (nfcReaderState == NFC_READ_SUCCESS) { Serial.println("Tag erfolgreich gelesen - warte 5 Sekunden vor nächstem Scan"); vTaskDelay(5000 / portTICK_PERIOD_MS); // 5 second pause } // aktualisieren der Website wenn sich der Status ändert sendNfcData(); } else { nfcReadingTaskSuspendState = true; // Different behavior for write protection vs. full suspension if (nfcWriteInProgress) { // During write: Just pause scanning, don't disable NFC interface // Serial.println("NFC Scanning paused during write operation"); vTaskDelay(100 / portTICK_PERIOD_MS); // Shorter delay during write } else { // Full suspension requested Serial.println("NFC Reading disabled"); vTaskDelay(1000 / portTICK_PERIOD_MS); } } yield(); } } void startNfc() { oledShowProgressBar(5, 7, DISPLAY_BOOT_TEXT, "NFC init"); nfc.begin(); // Beginne Kommunikation mit RFID Leser delay(1000); unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus if (! versiondata) { // Wenn keine Antwort kommt Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor oledShowMessage("No RFID Board found"); vTaskDelay(2000 / portTICK_PERIOD_MS); } else { Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); // Monitor, wenn Antwort vom Board kommt Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); // nfc.SAMConfig(); // Set the max number of retry attempts to read from a card // This prevents us from waiting forever for a card, which is // the default behaviour of the PN532. //nfc.setPassiveActivationRetries(0x7F); //nfc.setPassiveActivationRetries(0xFF); BaseType_t result = xTaskCreatePinnedToCore( scanRfidTask, /* Function to implement the task */ "RfidReader", /* Name of the task */ 5115, /* Stack size in words */ NULL, /* Task input parameter */ rfidTaskPrio, /* Priority of the task */ &RfidReaderTask, /* Task handle. */ rfidTaskCore); /* Core where the task should run */ if (result != pdPASS) { Serial.println("Fehler beim Erstellen des RFID Tasks"); } else { Serial.println("RFID Task erfolgreich erstellt"); } } }