Compare commits
	
		
			71 Commits
		
	
	
		
			v2.0.0-bet
			...
			v2.0.4-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 301109c37b | |||
| d43fceebbc | |||
| 2c435e5c98 | |||
| f9aa7f2e6b | |||
| f810bc5352 | |||
| 043c2d4fa8 | |||
| f5a1debd7d | |||
| 37309da185 | |||
| bcb7c039e2 | |||
| 98ec5b9846 | |||
| f0d1692ae1 | |||
| 10587276c2 | |||
| e74f6076b2 | |||
| 2ee60ce430 | |||
| 5db80d3670 | |||
| 2b195ed9ea | |||
| 45a623cff6 | |||
| b4a06d0f2a | |||
| 85cff3923c | |||
| d9469eaa42 | |||
| 16c3a65cca | |||
| 51335456e3 | |||
| 77fbacc681 | |||
| b4f1fc3b0a | |||
| 3a82175bb6 | |||
| b80184bf23 | |||
| 0f63880d1f | |||
| 0baa1d286e | |||
| 100328b1d6 | |||
| 9ec5bca652 | |||
| 1dba2b2f23 | |||
| cca0bd9dbe | |||
| 818094c36e | |||
| 4cf3858d0a | |||
| 66eef2242b | |||
| 87288e606b | |||
| 9ae9e80dcd | |||
| f2b38a5a99 | |||
| ab005b3dd1 | |||
| e537c6ec07 | |||
| bec769e95a | |||
| 5cc58927a6 | |||
| afde3f5f81 | |||
| 6800c88bb2 | |||
| 6172242f24 | |||
| 7f4b3b8d90 | |||
| 7a15424bc7 | |||
| 039a29fa3c | |||
| 6cccf3d603 | |||
| 693ee839e5 | |||
| 0bf383ecd9 | |||
| 6451d91c59 | |||
| 8d82e221b5 | |||
| bf63ecd594 | |||
| 0daa3a148b | |||
| 602642c203 | |||
| 458bd2e67b | |||
| e6a5cb29a9 | |||
| 6502bb7185 | |||
| 63fafa2463 | |||
| f664e85933 | |||
| 7bf9868d79 | |||
| b9e488d675 | |||
| 2e3fc19741 | |||
| 4d84169b29 | |||
| 10aeb9bc52 | |||
| 00b9bc08af | |||
| dfe9e4dbe9 | |||
| 79eacae225 | |||
| d5d7358f58 | |||
| 9b362b3c73 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -39,4 +39,5 @@ _local/* | |||||||
| website/* | website/* | ||||||
| release.sh | release.sh | ||||||
| .github/copilot-instructions.md | .github/copilot-instructions.md | ||||||
| data | data | ||||||
|  | wiki | ||||||
							
								
								
									
										1285
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										1285
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										468
									
								
								WIKI_DE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								WIKI_DE.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,468 @@ | |||||||
|  | # FilaMan Wiki - Deutsch | ||||||
|  |  | ||||||
|  | ## Inhaltsverzeichnis | ||||||
|  |  | ||||||
|  | 1. [Überblick](#überblick) | ||||||
|  | 2. [Installation](#installation) | ||||||
|  | 3. [Hardware-Anforderungen](#hardware-anforderungen) | ||||||
|  | 4. [Ersteinrichtung](#ersteinrichtung) | ||||||
|  | 5. [Konfiguration](#konfiguration) | ||||||
|  | 6. [Benutzung](#benutzung) | ||||||
|  | 7. [NFC-Tags](#nfc-tags) | ||||||
|  | 8. [Bambu Lab Integration](#bambu-lab-integration) | ||||||
|  | 9. [Spoolman Integration](#spoolman-integration) | ||||||
|  | 10. [Octoprint Integration](#octoprint-integration) | ||||||
|  | 11. [Hersteller Tags](#hersteller-tags) | ||||||
|  | 12. [Fehlerbehebung](#fehlerbehebung) | ||||||
|  | 13. [Support](#support) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Überblick | ||||||
|  |  | ||||||
|  | FilaMan ist ein umfassendes Filament-Managementsystem für 3D-Drucker, das auf ESP32-Hardware basiert. Es bietet Gewichtsmessung, NFC-Tag-Management und nahtlose Integration mit Spoolman und Bambu Lab 3D-Druckern. | ||||||
|  |  | ||||||
|  | ### Hauptfunktionen | ||||||
|  |  | ||||||
|  | - **Präzise Gewichtsmessung** mit HX711 Wägezellen-Verstärker | ||||||
|  | - **NFC-Tag Lesen und Schreiben** für Filament-Identifikation | ||||||
|  | - **OLED-Display** für Status-Anzeigen | ||||||
|  | - **WiFi-Konnektivität** mit einfacher Konfiguration | ||||||
|  | - **Webbasierte Benutzeroberfläche** mit Echtzeit-Updates | ||||||
|  | - **Spoolman-Integration** für Lagerverwaltung | ||||||
|  | - **Bambu Lab AMS-Steuerung** via MQTT | ||||||
|  | - **Openspool NFC-Format** Kompatibilität | ||||||
|  | - **Hersteller Tag Unterstützung** für automatische Einrichtung | ||||||
|  |  | ||||||
|  | ### Systemvoraussetzungen | ||||||
|  |  | ||||||
|  | - **ESP32 Development Board** | ||||||
|  | - **Spoolman Instanz** (erforderlich für volle Funktionalität) | ||||||
|  | - **WiFi-Netzwerk** | ||||||
|  | - **Webbrowser** (Chrome/Firefox/Safari) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  | ### Einfache Installation (Empfohlen) | ||||||
|  |  | ||||||
|  | 1. **Öffnen Sie den [FilaMan Web-Installer](https://www.filaman.app/installer.html)** | ||||||
|  |    - Verwenden Sie einen Chrome-basierten Browser | ||||||
|  |  | ||||||
|  | 2. **ESP32 vorbereiten** | ||||||
|  |    - Verbinden Sie den ESP32 über USB mit Ihrem Computer | ||||||
|  |    - Klicken Sie auf "Connect" | ||||||
|  |  | ||||||
|  | 3. **Port auswählen** | ||||||
|  |    - Wählen Sie den entsprechenden USB-Port aus | ||||||
|  |    - Bestätigen Sie die Auswahl | ||||||
|  |  | ||||||
|  | 4. **Installation starten** | ||||||
|  |    - Klicken Sie auf "FilaMan installieren" | ||||||
|  |    - Warten Sie, bis der Installationsvorgang abgeschlossen ist | ||||||
|  |  | ||||||
|  | ### Manuelle Kompilierung | ||||||
|  |  | ||||||
|  | Für erfahrene Benutzer mit PlatformIO: | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | git clone https://github.com/ManuelW77/Filaman.git | ||||||
|  | cd FilaMan/esp32 | ||||||
|  | pio lib install | ||||||
|  | pio run --target upload | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Hardware-Anforderungen | ||||||
|  |  | ||||||
|  | ### Erforderliche Komponenten | ||||||
|  |  | ||||||
|  | | Komponente | Beschreibung | Amazon Link (Affiliate) | | ||||||
|  | |------------|--------------|-------------------------| | ||||||
|  | | ESP32 Development Board | Jede ESP32-Variante | [Amazon](https://amzn.to/3FHea6D) | | ||||||
|  | | HX711 + Wägezelle | 5kg Load Cell Amplifier | [Amazon](https://amzn.to/4ja1KTe) | | ||||||
|  | | OLED Display | 0.96" I2C 128x64 SSD1306 | [Amazon](https://amzn.to/445aaa9) | | ||||||
|  | | PN532 NFC Modul | V3 RFID-Modul | [Amazon](https://amzn.eu/d/gy9vaBX) | | ||||||
|  | | NFC Tags | NTAG213/NTAG215 | [Amazon](https://amzn.to/3E071xO) | | ||||||
|  | | TTP223 Touch Sensor | Optional für Tara-Funktion | [Amazon](https://amzn.to/4hTChMK) | | ||||||
|  |  | ||||||
|  | ### Pin-Konfiguration | ||||||
|  |  | ||||||
|  | | Komponente | ESP32 Pin | Funktion | | ||||||
|  | |------------|-----------|----------| | ||||||
|  | | HX711 DOUT | 16 | Datenausgang Wägezelle | | ||||||
|  | | HX711 SCK | 17 | Takt Wägezelle | | ||||||
|  | | OLED SDA | 21 | I2C Daten | | ||||||
|  | | OLED SCL | 22 | I2C Takt | | ||||||
|  | | PN532 IRQ | 32 | Interrupt | | ||||||
|  | | PN532 RESET | 33 | Reset | | ||||||
|  | | PN532 SDA | 21 | I2C Daten (geteilt) | | ||||||
|  | | PN532 SCL | 22 | I2C Takt (geteilt) | | ||||||
|  | | TTP223 I/O | 25 | Touch-Sensor (optional) | | ||||||
|  |  | ||||||
|  | ### Wichtige Hinweise | ||||||
|  |  | ||||||
|  | - **PN532 DIP-Schalter** müssen auf I2C-Modus eingestellt sein | ||||||
|  | - **3V Pin** vom ESP32 für Touch-Sensor verwenden | ||||||
|  | - **Wägezellen-Verkabelung**: E+ (rot), E- (schwarz), A- (weiß), A+ (grün) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Ersteinrichtung | ||||||
|  |  | ||||||
|  | ### Nach der Installation | ||||||
|  |  | ||||||
|  | 1. **ESP32 Neustart** | ||||||
|  |    - Das System erstellt automatisch einen WiFi-Hotspot "FilaMan" | ||||||
|  |  | ||||||
|  | 2. **WiFi-Konfiguration** | ||||||
|  |    - Verbinden Sie sich mit dem "FilaMan" Netzwerk | ||||||
|  |    - Öffnen Sie einen Browser (automatisches Portal oder http://192.168.4.1) | ||||||
|  |    - Konfigurieren Sie Ihre WiFi-Zugangsdaten | ||||||
|  |  | ||||||
|  | 3. **Erster Zugriff** | ||||||
|  |    - Nach erfolgreicher WiFi-Verbindung ist das System unter http://filaman.local erreichbar | ||||||
|  |    - Alternativ über die vom Router zugewiesene IP-Adresse | ||||||
|  |  | ||||||
|  | ### Spoolman Vorbereitung | ||||||
|  |  | ||||||
|  | **Wichtiger Hinweis**: Spoolman muss im Debug-Modus laufen: | ||||||
|  |  | ||||||
|  | ```env | ||||||
|  | # In der .env Datei von Spoolman auskommentieren: | ||||||
|  | SPOOLMAN_DEBUG_MODE=TRUE | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Dies ist erforderlich, da Spoolman noch keine CORS-Domain-Konfiguration unterstützt. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Konfiguration | ||||||
|  |  | ||||||
|  | ### Waagen-Kalibrierung | ||||||
|  |  | ||||||
|  | 1. **Kalibrierung starten** | ||||||
|  |    - Gehen Sie zur "Scale" (Waage) Seite | ||||||
|  |    - Bereiten Sie ein 500g Referenzgewicht vor (z.B. Wasserglas) | ||||||
|  |  | ||||||
|  | 2. **Kalibrierungsschritte** | ||||||
|  |    - Folgen Sie den Anweisungen auf dem Display | ||||||
|  |    - Legen Sie das Gewicht auf, wenn gefordert | ||||||
|  |    - Warten Sie, bis die Kalibrierung abgeschlossen ist | ||||||
|  |  | ||||||
|  | 3. **Validierung** | ||||||
|  |    - Testen Sie die Genauigkeit mit bekannten Gewichten | ||||||
|  |    - Bei Bedarf "Tare Scale" für Nullstellung verwenden | ||||||
|  |  | ||||||
|  | ### Spoolman-Verbindung | ||||||
|  |  | ||||||
|  | 1. **Spoolman-URL eingeben** | ||||||
|  |    - Gehen Sie zur "Spoolman/Bambu" Seite | ||||||
|  |    - Geben Sie die vollständige URL Ihrer Spoolman-Instanz ein | ||||||
|  |    - Format: `http://spoolman-server:7912` | ||||||
|  |  | ||||||
|  | 2. **Verbindung testen** | ||||||
|  |    - Das System prüft automatisch die Verbindung | ||||||
|  |    - Erfolgreiche Verbindung wird durch grünen Status angezeigt | ||||||
|  |  | ||||||
|  | ### Bambu Lab Drucker (optional) | ||||||
|  |  | ||||||
|  | 1. **Drucker-Einstellungen** | ||||||
|  |    - Öffnen Sie das Einstellungsmenü auf Ihrem Bambu-Drucker | ||||||
|  |    - Notieren Sie sich die folgenden Daten: | ||||||
|  |      - IP-Adresse des Druckers | ||||||
|  |      - Access Code | ||||||
|  |      - Serial Number | ||||||
|  |  | ||||||
|  | 2. **FilaMan Konfiguration** | ||||||
|  |    - Geben Sie die Drucker-Daten in der "Spoolman/Bambu" Seite ein | ||||||
|  |    - Aktivieren Sie "Auto Send to Bambu" für automatische AMS-Zuordnung | ||||||
|  |  | ||||||
|  | 3. **Auto-Send Timeout** | ||||||
|  |    - Konfigurieren Sie die Wartezeit für automatische Spulen-Erkennung | ||||||
|  |    - Empfohlener Wert: 10-30 Sekunden | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Benutzung | ||||||
|  |  | ||||||
|  | ### Grundlegende Bedienung | ||||||
|  |  | ||||||
|  | 1. **Filament wiegen** | ||||||
|  |    - Platzieren Sie die Spule auf der Waage | ||||||
|  |    - Das Gewicht wird automatisch auf dem Display und in der Weboberfläche angezeigt | ||||||
|  |  | ||||||
|  | 2. **NFC-Tag scannen** | ||||||
|  |    - Halten Sie den Tag in die Nähe des PN532-Moduls | ||||||
|  |    - Bei erkannten Tags wird die Spulen-Information angezeigt | ||||||
|  |    - Das Gewicht wird automatisch in Spoolman aktualisiert | ||||||
|  |  | ||||||
|  | 3. **Status-Überwachung** | ||||||
|  |    - **OLED-Display** zeigt aktuelles Gewicht und Verbindungsstatus | ||||||
|  |    - **Weboberfläche** bietet detaillierte Informationen und Steuerung | ||||||
|  |  | ||||||
|  | ### Weboberfläche Navigation | ||||||
|  |  | ||||||
|  | - **Startseite**: Hauptfunktionen und aktueller Status | ||||||
|  | - **Scale**: Waagen-Kalibrierung und -Einstellungen | ||||||
|  | - **Spoolman/Bambu**: System-Konfiguration | ||||||
|  | - **Statistics**: Nutzungsstatistiken (falls aktiviert) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## NFC-Tags | ||||||
|  |  | ||||||
|  | ### Unterstützte Tag-Typen | ||||||
|  |  | ||||||
|  | - **NTAG213**: 144 Bytes (grundlegende Funktionen) | ||||||
|  | - **NTAG215**: 504 Bytes (empfohlen) | ||||||
|  | - **NTAG216**: 888 Bytes (erweiterte Funktionen) | ||||||
|  |  | ||||||
|  | ### Tag beschreiben | ||||||
|  |  | ||||||
|  | 1. **Spule in Spoolman vorbereiten** | ||||||
|  |    - Erstellen Sie eine neue Spule in Spoolman | ||||||
|  |    - Stellen Sie sicher, dass alle erforderlichen Daten eingegeben sind | ||||||
|  |  | ||||||
|  | 2. **Tag-Beschreibung starten** | ||||||
|  |    - Wählen Sie die Spule aus der Liste | ||||||
|  |    - Klicken Sie auf "Write Tag" | ||||||
|  |    - Das Display zeigt "Waiting for Tag" | ||||||
|  |  | ||||||
|  | 3. **Tag auflegen** | ||||||
|  |    - Platzieren Sie den NFC-Tag auf dem PN532-Modul | ||||||
|  |    - Warten Sie auf die Bestätigung | ||||||
|  |  | ||||||
|  | 4. **Erfolgsmeldung** | ||||||
|  |    - Bei erfolgreichem Beschreiben wird ein Häkchen angezeigt | ||||||
|  |    - Der Tag ist nun mit der Spoolman-Spule verknüpft | ||||||
|  |  | ||||||
|  | ### Tag lesen | ||||||
|  |  | ||||||
|  | 1. **Tag scannen** | ||||||
|  |    - Platzieren Sie die Spule mit dem NFC-Tag auf die Waage über dem NFC-Reader | ||||||
|  |    - Bei Problemen beim Lesen: Spule etwas anders positionieren (nicht ganz an den Rand) | ||||||
|  |    - Die Spulen-Information wird automatisch geladen | ||||||
|  |  | ||||||
|  | 2. **Automatische Updates** | ||||||
|  |    - Das aktuelle Gewicht wird in Spoolman übertragen | ||||||
|  |    - Die Spule wird in der Weboberfläche automatisch ausgewählt | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Bambu Lab Integration | ||||||
|  |  | ||||||
|  | ### AMS (Automatic Material System) | ||||||
|  |  | ||||||
|  | 1. **AMS-Status anzeigen** | ||||||
|  |    - Die Weboberfläche zeigt den aktuellen Zustand aller AMS-Fächer | ||||||
|  |    - Beladene Fächer werden mit Filament-Informationen angezeigt | ||||||
|  |  | ||||||
|  | 2. **Filament manuell zuordnen** | ||||||
|  |    - Wählen Sie eine Spule aus der Spoolman-Liste | ||||||
|  |    - Klicken Sie auf das entsprechende AMS-Fach-Symbol | ||||||
|  |    - Das Filament wird dem Fach zugeordnet | ||||||
|  |  | ||||||
|  | 3. **Automatische Zuordnung** | ||||||
|  |    - Nach dem Wiegen mit aktiviertem "Auto Send to Bambu" | ||||||
|  |    - Das System wartet auf neue Spulen im AMS | ||||||
|  |    - Kalibrierte Filamente werden automatisch zugeordnet | ||||||
|  |  | ||||||
|  | ### Bambu Studio Integration | ||||||
|  |  | ||||||
|  | 1. **Filament-Profile synchronisieren** | ||||||
|  |    - Kalibrieren Sie Filamente in Bambu Studio | ||||||
|  |    - Verwenden Sie Device → AMS → Bleistift-Symbol → Auswählen | ||||||
|  |  | ||||||
|  | 2. **Setting-IDs speichern** | ||||||
|  |    - FilaMan erkennt verfügbare Setting-IDs automatisch | ||||||
|  |    - Klicken Sie auf "Settings in Spoolman speichern" | ||||||
|  |    - Die Profile werden für zukünftige Drucke verwendet | ||||||
|  |  | ||||||
|  | ### Verbindung wiederherstellen | ||||||
|  |  | ||||||
|  | - Bei Verbindungsproblemen klicken Sie den roten Punkt in der Menüleiste | ||||||
|  | - Das System stellt automatisch eine neue Verbindung her | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Spoolman Integration | ||||||
|  |  | ||||||
|  | ### Automatische Funktionen | ||||||
|  |  | ||||||
|  | 1. **Spulen-Synchronisation** | ||||||
|  |    - Automatische Übertragung von Gewichtsänderungen | ||||||
|  |    - Echtzeit-Updates der Spulen-Daten | ||||||
|  |  | ||||||
|  | 2. **Extra-Felder** | ||||||
|  |    - FilaMan erstellt automatisch erforderliche benutzerdefinierte Felder | ||||||
|  |    - NFC-Tag-UID wird als Referenz gespeichert | ||||||
|  |  | ||||||
|  | 3. **Filterung** | ||||||
|  |    - "Nur Spulen ohne NFC-Tag anzeigen" für einfache Tag-Zuordnung | ||||||
|  |    - Kategorisierung nach Herstellern und Materialtypen | ||||||
|  |  | ||||||
|  | ### Spoolman Octoprint Plugin | ||||||
|  |  | ||||||
|  | Für Octoprint-Benutzer ist eine automatische Spulen-Zuordnung verfügbar: | ||||||
|  |  | ||||||
|  | 1. **Plugin installieren** | ||||||
|  |    ``` | ||||||
|  |    https://github.com/ManuelW77/OctoPrint-Spoolman-Filaman/archive/refs/heads/master.zip | ||||||
|  |    ``` | ||||||
|  |  | ||||||
|  | 2. **FilaMan konfigurieren** | ||||||
|  |    - Aktivieren Sie "Send to Octo-Plugin" | ||||||
|  |    - Geben Sie Octoprint-URL und API-Key ein | ||||||
|  |  | ||||||
|  | 3. **Automatische Zuordnung** | ||||||
|  |    - Nach dem Wiegen wird die Spule automatisch in Octoprint aktiviert | ||||||
|  |    - Unterstützt aktuell nur Tool0 (erste Düse) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Hersteller Tags | ||||||
|  |  | ||||||
|  | ### Überblick | ||||||
|  |  | ||||||
|  | Hersteller Tags ermöglichen es Filament-Produzenten, vorkonfigurierte NFC-Tags zu liefern, die automatisch alle notwendigen Einträge in Spoolman erstellen. | ||||||
|  |  | ||||||
|  | ### Erste Schritte mit Hersteller Tags | ||||||
|  |  | ||||||
|  | 1. **Tag scannen** | ||||||
|  |    - Platzieren Sie die Spule mit dem Hersteller-Tag auf die Waage über dem NFC-Reader | ||||||
|  |    - Bei Problemen beim Lesen: Spule etwas anders positionieren (nicht ganz an den Rand) | ||||||
|  |    - Das System erkennt automatisch das Hersteller-Format | ||||||
|  |  | ||||||
|  | 2. **Automatische Erstellung** | ||||||
|  |    - **Marke** wird in Spoolman angelegt (falls nicht vorhanden) | ||||||
|  |    - **Filament-Typ** wird mit allen Spezifikationen erstellt | ||||||
|  |    - **Spule** wird automatisch registriert | ||||||
|  |  | ||||||
|  | 3. **Zukünftige Scans** | ||||||
|  |    - Nach der ersten Einrichtung nutzen Tags das Fast-Path-System | ||||||
|  |    - Sofortige Gewichtsmessung ohne erneute Einrichtung | ||||||
|  |  | ||||||
|  | ### Unterstützte Hersteller | ||||||
|  |  | ||||||
|  | - **RecyclingFabrik**: Erster offizieller Partner | ||||||
|  | - Weitere Hersteller folgen | ||||||
|  |  | ||||||
|  | ### Vorteile | ||||||
|  |  | ||||||
|  | - ✅ **Null manuelle Einrichtung** | ||||||
|  | - ✅ **Perfekte Datengenauigkeit** | ||||||
|  | - ✅ **Sofortige Integration** | ||||||
|  | - ✅ **Zukunftssicher** | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Fehlerbehebung | ||||||
|  |  | ||||||
|  | ### Häufige Probleme | ||||||
|  |  | ||||||
|  | #### WiFi-Verbindung | ||||||
|  |  | ||||||
|  | **Problem**: Kann nicht mit FilaMan-Hotspot verbinden | ||||||
|  | - Lösung: Stellen Sie sicher, dass der ESP32 gestartet ist | ||||||
|  | - Alternative: Manuell zu http://192.168.4.1 navigieren | ||||||
|  |  | ||||||
|  | **Problem**: Weboberfläche nicht erreichbar | ||||||
|  | - Lösung: Prüfen Sie die IP-Adresse im Router | ||||||
|  | - Alternative: Verwenden Sie http://filaman.local | ||||||
|  |  | ||||||
|  | #### Waage | ||||||
|  |  | ||||||
|  | **Problem**: Ungenaue Gewichtsmessungen | ||||||
|  | - Lösung: Kalibrierung wiederholen | ||||||
|  | - Tipp: Verwenden Sie "Tare Scale" für Nullstellung | ||||||
|  |  | ||||||
|  | **Problem**: Wägezelle reagiert nicht | ||||||
|  | - Lösung: Überprüfen Sie die Verkabelung (E+, E-, A+, A-) | ||||||
|  | - Tipp: Testen Sie mit einem Multimeter | ||||||
|  |  | ||||||
|  | #### NFC-Tags | ||||||
|  |  | ||||||
|  | **Problem**: Tag wird nicht erkannt | ||||||
|  | - Lösung: Überprüfen Sie die PN532 DIP-Schalter (I2C-Modus) | ||||||
|  | - Tipp: Spule etwas anders auf der Waage positionieren (nicht ganz an den Rand) | ||||||
|  |  | ||||||
|  | **Problem**: Tag kann nicht beschrieben werden | ||||||
|  | - Lösung: Verwenden Sie NTAG215 für bessere Kompatibilität | ||||||
|  | - Tipp: Stellen Sie sicher, dass der Tag nicht schreibgeschützt ist | ||||||
|  |  | ||||||
|  | #### Spoolman | ||||||
|  |  | ||||||
|  | **Problem**: Verbindung zu Spoolman schlägt fehl | ||||||
|  | - Lösung: Aktivieren Sie SPOOLMAN_DEBUG_MODE=TRUE | ||||||
|  | - Tipp: Überprüfen Sie die URL-Formatierung | ||||||
|  |  | ||||||
|  | **Problem**: Spulen werden nicht angezeigt | ||||||
|  | - Lösung: Stellen Sie sicher, dass Spoolman läuft | ||||||
|  | - Tipp: Prüfen Sie die Netzwerk-Firewall-Einstellungen | ||||||
|  |  | ||||||
|  | #### Bambu Lab | ||||||
|  |  | ||||||
|  | **Problem**: Drucker verbindet nicht | ||||||
|  | - Lösung: Überprüfen Sie Access Code und IP-Adresse | ||||||
|  | - Tipp: Stellen Sie sicher, dass der Drucker im LAN-Modus ist | ||||||
|  |  | ||||||
|  | **Problem**: AMS-Status wird nicht angezeigt | ||||||
|  | - Lösung: Prüfen Sie die MQTT-Verbindung | ||||||
|  | - Hinweis: Bambu kann die API jederzeit schließen | ||||||
|  |  | ||||||
|  | ### Debug-Informationen | ||||||
|  |  | ||||||
|  | Falls Sie Probleme haben, können Sie diese Schritte zur Diagnose verwenden: | ||||||
|  |  | ||||||
|  | #### Serieller Monitor (für Entwickler) | ||||||
|  | - Verbinden Sie den ESP32 über USB mit Ihrem Computer | ||||||
|  | - Öffnen Sie einen seriellen Monitor (z.B. Arduino IDE) mit 115200 Baud | ||||||
|  | - Sie sehen detaillierte Log-Nachrichten des Systems | ||||||
|  |  | ||||||
|  | #### Browser-Konsole | ||||||
|  | - Öffnen Sie die Weboberfläche von FilaMan | ||||||
|  | - Drücken Sie F12 um die Entwicklertools zu öffnen   | ||||||
|  | - Schauen Sie in der Konsole nach Fehlermeldungen | ||||||
|  |  | ||||||
|  | #### Neustart bei anhaltenden Problemen | ||||||
|  | 1. ESP32 vom Strom trennen | ||||||
|  | 2. 10 Sekunden warten | ||||||
|  | 3. Wieder anschließen | ||||||
|  | 4. 30 Sekunden für vollständigen Start warten | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Support | ||||||
|  |  | ||||||
|  | ### Community | ||||||
|  |  | ||||||
|  | - **Discord Server**: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v) | ||||||
|  | - **GitHub Issues**: [Filaman Repository](https://github.com/ManuelW77/Filaman/issues) | ||||||
|  | - **YouTube Kanal**: [Deutsches Erklärvideo](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) | ||||||
|  |  | ||||||
|  | ### Dokumentation | ||||||
|  |  | ||||||
|  | - **Offizielle Website**: [www.filaman.app](https://www.filaman.app) | ||||||
|  | - **GitHub Wiki**: [Detaillierte Dokumentation](https://github.com/ManuelW77/Filaman/wiki) | ||||||
|  | - **Hardware-Referenz**: ESP32 Pinout-Diagramme in `/img/` | ||||||
|  |  | ||||||
|  | ### Entwicklung unterstützen | ||||||
|  |  | ||||||
|  | Wenn Sie das Projekt unterstützen möchten: | ||||||
|  |  | ||||||
|  | [](https://www.buymeacoffee.com/manuelw) | ||||||
|  |  | ||||||
|  | ### Lizenz | ||||||
|  |  | ||||||
|  | Dieses Projekt ist unter der MIT-Lizenz veröffentlicht. Siehe [LICENSE](LICENSE.txt) für Details. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | **Letzte Aktualisierung**: August 2025 | ||||||
|  | **Version**: 2.0 | ||||||
|  | **Maintainer**: Manuel W. | ||||||
							
								
								
									
										746
									
								
								WIKI_EN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										746
									
								
								WIKI_EN.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,746 @@ | |||||||
|  | # FilaMan Wiki - English | ||||||
|  |  | ||||||
|  | ## Table of Contents | ||||||
|  |  | ||||||
|  | 1. [Overview](#overview) | ||||||
|  | 2. [Installation](#installation) | ||||||
|  | 3. [Hardware Requirements](#hardware-requirements) | ||||||
|  | 4. [Initial Setup](#initial-setup) | ||||||
|  | 5. [Configuration](#configuration) | ||||||
|  | 6. [Usage](#usage) | ||||||
|  | 7. [NFC Tags](#nfc-tags) | ||||||
|  | 8. [Bambu Lab Integration](#bambu-lab-integration) | ||||||
|  | 9. [Spoolman Integration](#spoolman-integration) | ||||||
|  | 10. [Octoprint Integration](#octoprint-integration) | ||||||
|  | 11. [Manufacturer Tags](#manufacturer-tags) | ||||||
|  | 12. [Troubleshooting](#troubleshooting) | ||||||
|  | 13. [Support](#support) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Overview | ||||||
|  |  | ||||||
|  | FilaMan is a comprehensive filament management system for 3D printers based on ESP32 hardware. It provides weight measurement, NFC tag management, and seamless integration with Spoolman and Bambu Lab 3D printers. | ||||||
|  |  | ||||||
|  | ### Key Features | ||||||
|  |  | ||||||
|  | - **Precise weight measurement** with HX711 load cell amplifier | ||||||
|  | - **NFC tag reading and writing** for filament identification | ||||||
|  | - **OLED display** for status information | ||||||
|  | - **WiFi connectivity** with easy configuration | ||||||
|  | - **Web-based user interface** with real-time updates | ||||||
|  | - **Spoolman integration** for inventory management | ||||||
|  | - **Bambu Lab AMS control** via MQTT | ||||||
|  | - **OpenSpool NFC format** compatibility | ||||||
|  | - **Manufacturer tag support** for automatic setup | ||||||
|  |  | ||||||
|  | ### System Requirements | ||||||
|  |  | ||||||
|  | - **ESP32 Development Board** | ||||||
|  | - **Spoolman Instance** (required for full functionality) | ||||||
|  | - **WiFi Network** | ||||||
|  | - **Web Browser** (Chrome/Firefox/Safari) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  | ### Easy Installation (Recommended) | ||||||
|  |  | ||||||
|  | 1. **Open the [FilaMan Web Installer](https://www.filaman.app/installer.html)** | ||||||
|  |    - Use a Chrome-based browser | ||||||
|  |  | ||||||
|  | 2. **Prepare ESP32** | ||||||
|  |    - Connect ESP32 via USB to your computer | ||||||
|  |    - Click "Connect" | ||||||
|  |  | ||||||
|  | 3. **Select Port** | ||||||
|  |    - Choose the appropriate USB port | ||||||
|  |    - Confirm selection | ||||||
|  |  | ||||||
|  | 4. **Start Installation** | ||||||
|  |    - Click "Install FilaMan" | ||||||
|  |    - Wait for installation to complete | ||||||
|  |  | ||||||
|  | ### Manual Compilation | ||||||
|  |  | ||||||
|  | For advanced users with PlatformIO: | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | git clone https://github.com/ManuelW77/Filaman.git | ||||||
|  | cd FilaMan/esp32 | ||||||
|  | pio lib install | ||||||
|  | pio run --target upload | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Hardware Requirements | ||||||
|  |  | ||||||
|  | ### Required Components | ||||||
|  |  | ||||||
|  | | Component | Description | Amazon Link (Affiliate) | | ||||||
|  | |-----------|-------------|-------------------------| | ||||||
|  | | ESP32 Development Board | Any ESP32 variant | [Amazon](https://amzn.to/3FHea6D) | | ||||||
|  | | HX711 + Load Cell | 5kg Load Cell Amplifier | [Amazon](https://amzn.to/4ja1KTe) | | ||||||
|  | | OLED Display | 0.96" I2C 128x64 SSD1306 | [Amazon](https://amzn.to/445aaa9) | | ||||||
|  | | PN532 NFC Module | V3 RFID Module | [Amazon](https://amzn.eu/d/gy9vaBX) | | ||||||
|  | | NFC Tags | NTAG213/NTAG215 | [Amazon](https://amzn.to/3E071xO) | | ||||||
|  | | TTP223 Touch Sensor | Optional for tare function | [Amazon](https://amzn.to/4hTChMK) | | ||||||
|  |  | ||||||
|  | ### Pin Configuration | ||||||
|  |  | ||||||
|  | | Component | ESP32 Pin | Function | | ||||||
|  | |-----------|-----------|----------| | ||||||
|  | | HX711 DOUT | 16 | Load cell data output | | ||||||
|  | | HX711 SCK | 17 | Load cell clock | | ||||||
|  | | OLED SDA | 21 | I2C data | | ||||||
|  | | OLED SCL | 22 | I2C clock | | ||||||
|  | | PN532 IRQ | 32 | Interrupt | | ||||||
|  | | PN532 RESET | 33 | Reset | | ||||||
|  | | PN532 SDA | 21 | I2C data (shared) | | ||||||
|  | | PN532 SCL | 22 | I2C clock (shared) | | ||||||
|  | | TTP223 I/O | 25 | Touch sensor (optional) | | ||||||
|  |  | ||||||
|  | ### Important Notes | ||||||
|  |  | ||||||
|  | - **PN532 DIP switches** must be set to I2C mode | ||||||
|  | - **3V pin** from ESP32 for touch sensor | ||||||
|  | - **Load cell wiring**: E+ (red), E- (black), A- (white), A+ (green) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Initial Setup | ||||||
|  |  | ||||||
|  | ### After Installation | ||||||
|  |  | ||||||
|  | 1. **ESP32 Restart** | ||||||
|  |    - System automatically creates a WiFi hotspot "FilaMan" | ||||||
|  |  | ||||||
|  | 2. **WiFi Configuration** | ||||||
|  |    - Connect to the "FilaMan" network | ||||||
|  |    - Open browser (automatic portal or <http://192.168.4.1>) | ||||||
|  |    - Configure your WiFi credentials | ||||||
|  |  | ||||||
|  | 3. **First Access** | ||||||
|  |    - After successful WiFi connection, access system at <http://filaman.local> | ||||||
|  |    - Alternative: Use IP address assigned by router | ||||||
|  |  | ||||||
|  | ### Spoolman Preparation | ||||||
|  |  | ||||||
|  | **Important Note**: Spoolman must run in debug mode: | ||||||
|  |  | ||||||
|  | ```env | ||||||
|  | # Uncomment in Spoolman's .env file: | ||||||
|  | SPOOLMAN_DEBUG_MODE=TRUE | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | This is required as Spoolman doesn't support CORS domain configuration yet. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Configuration | ||||||
|  |  | ||||||
|  | ### Scale Calibration | ||||||
|  |  | ||||||
|  | 1. **Start Calibration** | ||||||
|  |    - Go to "Scale" page | ||||||
|  |    - Prepare a 500g reference weight (e.g., water glass) | ||||||
|  |  | ||||||
|  | 2. **Calibration Steps** | ||||||
|  |    - Follow instructions on display | ||||||
|  |    - Place weight when prompted | ||||||
|  |    - Wait for calibration to complete | ||||||
|  |  | ||||||
|  | 3. **Validation** | ||||||
|  |    - Test accuracy with known weights | ||||||
|  |    - Use "Tare Scale" for zero adjustment if needed | ||||||
|  |  | ||||||
|  | ### Spoolman Connection | ||||||
|  |  | ||||||
|  | 1. **Enter Spoolman URL** | ||||||
|  |    - Go to "Spoolman/Bambu" page | ||||||
|  |    - Enter complete URL of your Spoolman instance | ||||||
|  |    - Format: `http://spoolman-server:7912` | ||||||
|  |  | ||||||
|  | 2. **Test Connection** | ||||||
|  |    - System automatically checks connection | ||||||
|  |    - Successful connection shown by green status | ||||||
|  |  | ||||||
|  | ### Bambu Lab Printer (Optional) | ||||||
|  |  | ||||||
|  | 1. **Printer Settings** | ||||||
|  |    - Open settings menu on your Bambu printer | ||||||
|  |    - Note the following data: | ||||||
|  |      - Printer IP address | ||||||
|  |      - Access Code | ||||||
|  |      - Serial Number | ||||||
|  |  | ||||||
|  | 2. **FilaMan Configuration** | ||||||
|  |    - Enter printer data on "Spoolman/Bambu" page | ||||||
|  |    - Enable "Auto Send to Bambu" for automatic AMS assignment | ||||||
|  |  | ||||||
|  | 3. **Auto-Send Timeout** | ||||||
|  |    - Configure waiting time for automatic spool detection | ||||||
|  |    - Recommended value: 10-30 seconds | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Usage | ||||||
|  |  | ||||||
|  | ### Basic Operation | ||||||
|  |  | ||||||
|  | 1. **Weigh Filament** | ||||||
|  |    - Place spool on scale | ||||||
|  |    - Weight automatically displayed on screen and web interface | ||||||
|  |  | ||||||
|  | 2. **Scan NFC Tag** | ||||||
|  |    - Hold tag near PN532 module | ||||||
|  |    - Recognized tags display spool information | ||||||
|  |    - Weight automatically updated in Spoolman | ||||||
|  |  | ||||||
|  | 3. **Status Monitoring** | ||||||
|  |    - **OLED Display** shows current weight and connection status | ||||||
|  |    - **Web Interface** provides detailed information and control | ||||||
|  |  | ||||||
|  | ### Web Interface Navigation | ||||||
|  |  | ||||||
|  | - **Home**: Main functions and current status | ||||||
|  | - **Scale**: Scale calibration and settings | ||||||
|  | - **Spoolman/Bambu**: System configuration | ||||||
|  | - **Statistics**: Usage statistics (if enabled) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## NFC Tags | ||||||
|  |  | ||||||
|  | ### Supported Tag Types | ||||||
|  |  | ||||||
|  | - **NTAG213**: 144 bytes (basic functions) | ||||||
|  | - **NTAG215**: 504 bytes (recommended) | ||||||
|  | - **NTAG216**: 888 bytes (extended functions) | ||||||
|  |  | ||||||
|  | ### Writing Tags | ||||||
|  |  | ||||||
|  | 1. **Prepare Spool in Spoolman** | ||||||
|  |    - Create new spool in Spoolman | ||||||
|  |    - Ensure all required data is entered | ||||||
|  |  | ||||||
|  | 2. **Start Tag Writing** | ||||||
|  |    - Select spool from list | ||||||
|  |    - Click "Write Tag" | ||||||
|  |    - Display shows "Waiting for Tag" | ||||||
|  |  | ||||||
|  | 3. **Place Tag** | ||||||
|  |    - Position NFC tag on PN532 module | ||||||
|  |    - Wait for confirmation | ||||||
|  |  | ||||||
|  | 4. **Success Message** | ||||||
|  |    - Successful writing shows checkmark | ||||||
|  |    - Tag is now linked to Spoolman spool | ||||||
|  |  | ||||||
|  | ### Reading Tags | ||||||
|  |  | ||||||
|  | 1. **Scan Tag** | ||||||
|  |    - Place the spool with NFC tag on the scale over the NFC reader | ||||||
|  |    - If reading fails: Reposition spool slightly (not completely at the edge) | ||||||
|  |    - Spool information automatically loaded | ||||||
|  |  | ||||||
|  | 2. **Automatic Updates** | ||||||
|  |    - Current weight transferred to Spoolman | ||||||
|  |    - Spool automatically selected in web interface | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Bambu Lab Integration | ||||||
|  |  | ||||||
|  | ### AMS (Automatic Material System) | ||||||
|  |  | ||||||
|  | 1. **Display AMS Status** | ||||||
|  |    - Web interface shows current state of all AMS slots | ||||||
|  |    - Loaded slots display filament information | ||||||
|  |  | ||||||
|  | 2. **Manual Filament Assignment** | ||||||
|  |    - Select spool from Spoolman list | ||||||
|  |    - Click corresponding AMS slot icon | ||||||
|  |    - Filament assigned to slot | ||||||
|  |  | ||||||
|  | 3. **Automatic Assignment** | ||||||
|  |    - After weighing with "Auto Send to Bambu" enabled | ||||||
|  |    - System waits for new spools in AMS | ||||||
|  |    - Calibrated filaments automatically assigned | ||||||
|  |  | ||||||
|  | ### Bambu Studio Integration | ||||||
|  |  | ||||||
|  | 1. **Sync Filament Profiles** | ||||||
|  |    - Calibrate filaments in Bambu Studio | ||||||
|  |    - Use Device → AMS → Pencil icon → Select | ||||||
|  |  | ||||||
|  | 2. **Save Setting IDs** | ||||||
|  |    - FilaMan automatically detects available setting IDs | ||||||
|  |    - Click "Save Settings to Spoolman" | ||||||
|  |    - Profiles used for future prints | ||||||
|  |  | ||||||
|  | ### Restore Connection | ||||||
|  |  | ||||||
|  | - For connection issues, click red dot in menu bar | ||||||
|  | - System automatically establishes new connection | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Spoolman Integration | ||||||
|  |  | ||||||
|  | ### Automatic Functions | ||||||
|  |  | ||||||
|  | 1. **Spool Synchronization** | ||||||
|  |    - Automatic transfer of weight changes | ||||||
|  |    - Real-time updates of spool data | ||||||
|  |  | ||||||
|  | 2. **Extra Fields** | ||||||
|  |    - FilaMan automatically creates required custom fields | ||||||
|  |    - NFC tag UID stored as reference | ||||||
|  |  | ||||||
|  | 3. **Filtering** | ||||||
|  |    - "Show only spools without NFC tag" for easy tag assignment | ||||||
|  |    - Categorization by manufacturers and material types | ||||||
|  |  | ||||||
|  | ### Spoolman Octoprint Plugin | ||||||
|  |  | ||||||
|  | For Octoprint users, automatic spool assignment is available: | ||||||
|  |  | ||||||
|  | 1. **Install Plugin** | ||||||
|  |  | ||||||
|  |    ```text | ||||||
|  |    https://github.com/ManuelW77/OctoPrint-Spoolman-Filaman/archive/refs/heads/master.zip | ||||||
|  |    ``` | ||||||
|  |  | ||||||
|  | 2. **Configure FilaMan** | ||||||
|  |    - Enable "Send to Octo-Plugin" | ||||||
|  |    - Enter Octoprint URL and API key | ||||||
|  |  | ||||||
|  | 3. **Automatic Assignment** | ||||||
|  |    - After weighing, spool automatically activated in Octoprint | ||||||
|  |    - Currently supports only Tool0 (first nozzle) | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Manufacturer Tags | ||||||
|  |  | ||||||
|  | ### Overview | ||||||
|  |  | ||||||
|  | Manufacturer tags allow filament producers to provide pre-configured NFC tags that automatically create all necessary entries in Spoolman. | ||||||
|  |  | ||||||
|  | ### Getting Started with Manufacturer Tags | ||||||
|  |  | ||||||
|  | 1. **Scan Tag** | ||||||
|  |    - Place spool with manufacturer tag on the scale over the NFC reader | ||||||
|  |    - If reading fails: Reposition spool slightly (not completely at the edge) | ||||||
|  |    - System automatically recognizes manufacturer format | ||||||
|  |  | ||||||
|  | 2. **Automatic Creation** | ||||||
|  |    - **Brand** created in Spoolman (if not present) | ||||||
|  |    - **Filament type** created with all specifications | ||||||
|  |    - **Spool** automatically registered | ||||||
|  |  | ||||||
|  | 3. **Future Scans** | ||||||
|  |    - After initial setup, tags use fast-path system | ||||||
|  |    - Immediate weight measurement without re-setup | ||||||
|  |  | ||||||
|  | ### Supported Manufacturers | ||||||
|  |  | ||||||
|  | - **RecyclingFabrik**: First official partner | ||||||
|  | - More manufacturers coming soon | ||||||
|  |  | ||||||
|  | ### Benefits | ||||||
|  |  | ||||||
|  | - ✅ **Zero manual setup** | ||||||
|  | - ✅ **Perfect data accuracy** | ||||||
|  | - ✅ **Instant integration** | ||||||
|  | - ✅ **Future-proof** | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Troubleshooting | ||||||
|  |  | ||||||
|  | ### Common Issues | ||||||
|  |  | ||||||
|  | #### WiFi Connection | ||||||
|  |  | ||||||
|  | **Issue**: Cannot connect to FilaMan hotspot | ||||||
|  |  | ||||||
|  | - Solution: Ensure ESP32 is started | ||||||
|  | - Alternative: Manually navigate to <http://192.168.4.1> | ||||||
|  |  | ||||||
|  | **Issue**: Web interface not accessible | ||||||
|  |  | ||||||
|  | - Solution: Check IP address in router | ||||||
|  | - Alternative: Use <http://filaman.local> | ||||||
|  |  | ||||||
|  | #### Scale | ||||||
|  |  | ||||||
|  | **Issue**: Inaccurate weight measurements | ||||||
|  |  | ||||||
|  | - Solution: Repeat calibration | ||||||
|  | - Tip: Use "Tare Scale" for zero adjustment | ||||||
|  |  | ||||||
|  | **Issue**: Load cell not responding | ||||||
|  |  | ||||||
|  | - Solution: Check wiring (E+, E-, A+, A-) | ||||||
|  | - Tip: Test with multimeter | ||||||
|  |  | ||||||
|  | #### NFC Tags | ||||||
|  |  | ||||||
|  | **Issue**: Tag not recognized | ||||||
|  |  | ||||||
|  | - Solution: Check PN532 DIP switches (I2C mode) | ||||||
|  | - Tip: Reposition spool slightly on scale (not completely at the edge) | ||||||
|  |  | ||||||
|  | **Issue**: Cannot write tag | ||||||
|  |  | ||||||
|  | - Solution: Use NTAG215 for better compatibility | ||||||
|  | - Tip: Ensure tag is not write-protected | ||||||
|  |  | ||||||
|  | #### Spoolman | ||||||
|  |  | ||||||
|  | **Issue**: Connection to Spoolman fails | ||||||
|  |  | ||||||
|  | - Solution: Enable SPOOLMAN_DEBUG_MODE=TRUE | ||||||
|  | - Tip: Check URL formatting | ||||||
|  |  | ||||||
|  | **Issue**: Spools not displayed | ||||||
|  |  | ||||||
|  | - Solution: Ensure Spoolman is running | ||||||
|  | - Tip: Check network firewall settings | ||||||
|  |  | ||||||
|  | #### Bambu Lab | ||||||
|  |  | ||||||
|  | **Issue**: Printer won't connect | ||||||
|  |  | ||||||
|  | - Solution: Check access code and IP address | ||||||
|  | - Tip: Ensure printer is in LAN mode | ||||||
|  |  | ||||||
|  | **Issue**: AMS status not displayed | ||||||
|  |  | ||||||
|  | - Solution: Check MQTT connection | ||||||
|  | - Note: Bambu may close API at any time | ||||||
|  |  | ||||||
|  | ### Debug Information | ||||||
|  |  | ||||||
|  | If you have problems, you can use these steps for diagnosis: | ||||||
|  |  | ||||||
|  | #### Serial Monitor (for developers) | ||||||
|  |  | ||||||
|  | - Connect the ESP32 via USB to your computer | ||||||
|  | - Open a serial monitor (e.g., Arduino IDE) with 115200 baud | ||||||
|  | - You will see detailed log messages from the system | ||||||
|  |  | ||||||
|  | #### Browser Console | ||||||
|  |  | ||||||
|  | - Open the FilaMan web interface | ||||||
|  | - Press F12 to open developer tools | ||||||
|  | - Check the console for error messages | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Maintenance and Updates | ||||||
|  |  | ||||||
|  | ### Firmware Update | ||||||
|  |  | ||||||
|  | 1. **Via Web Interface**: Access `http://filaman.local/upgrade.html` | ||||||
|  | 2. **Select firmware file** (.bin format) | ||||||
|  | 3. **Upload** - System restarts automatically | ||||||
|  | 4. **Configuration preserved** - Settings remain intact | ||||||
|  |  | ||||||
|  | ### System Reset | ||||||
|  |  | ||||||
|  | For persistent issues: | ||||||
|  |  | ||||||
|  | 1. Disconnect ESP32 from power | ||||||
|  | 2. Wait 10 seconds | ||||||
|  | 3. Reconnect | ||||||
|  | 4. Wait 30 seconds for complete startup | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Support and Information | ||||||
|  |  | ||||||
|  | **Manufacturer**: Your Company Name | ||||||
|  | **Maintainer**: Manuel W. | ||||||
|  |  | ||||||
|  | ### Scale Technology | ||||||
|  |  | ||||||
|  | #### Weight Stabilization | ||||||
|  |  | ||||||
|  | The system uses multiple filters for precise measurements: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // Moving Average Filter with 8 values | ||||||
|  | #define MOVING_AVERAGE_SIZE 8 | ||||||
|  | // Low-Pass Filter for smoothing | ||||||
|  | #define LOW_PASS_ALPHA 0.3f | ||||||
|  | // Thresholds for updates | ||||||
|  | #define DISPLAY_THRESHOLD 0.3f    // Display update | ||||||
|  | #define API_THRESHOLD 1.5f        // API actions | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Calibration Algorithm | ||||||
|  |  | ||||||
|  | 1. **System Pause**: All tasks are temporarily paused | ||||||
|  | 2. **Zero Setting**: Tare scale without weight | ||||||
|  | 3. **Reference Measurement**: 500g weight for 10 measurements | ||||||
|  | 4. **Calculation**: `newValue = rawValue / SCALE_LEVEL_WEIGHT` | ||||||
|  | 5. **NVS Storage**: Permanent value with verification | ||||||
|  | 6. **Filter Reset**: New baseline for stabilization | ||||||
|  |  | ||||||
|  | #### Auto-Tare Logic | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // Conditions for Auto-Tare | ||||||
|  | if (autoTare && (weight > 2 && weight < 7) || weight < -2) { | ||||||
|  |     scale_tare_counter++; | ||||||
|  |     if (scale_tare_counter >= 5) { | ||||||
|  |         // Automatic zero setting | ||||||
|  |         scale.tare(); | ||||||
|  |         resetWeightFilter(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### NFC Technology | ||||||
|  |  | ||||||
|  | #### PN532 Communication | ||||||
|  |  | ||||||
|  | - **Interface**: I2C at 400kHz | ||||||
|  | - **IRQ Pin**: Interrupt-based tag detection | ||||||
|  | - **Reset Handling**: Automatic recovery from communication errors | ||||||
|  | - **DIP Switches**: Must be set to I2C mode (00) | ||||||
|  |  | ||||||
|  | #### NDEF Implementation | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | // FilaMan Spoolman Format (with sm_id) | ||||||
|  | { | ||||||
|  |   "sm_id": "123", | ||||||
|  |   "color": "#FF5733", | ||||||
|  |   "type": "PLA",  | ||||||
|  |   "brand": "Example Brand" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Manufacturer Tag Schema | ||||||
|  |  | ||||||
|  | Compact JSON format for storage efficiency: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "b": "RecyclingFabrik",           // brand | ||||||
|  |   "an": "FX1_PLA-S175-1000-RED",  // article number | ||||||
|  |   "t": "PLA",                      // type | ||||||
|  |   "c": "FF0000",                   // color (hex without #) | ||||||
|  |   "cn": "Red",                     // color name | ||||||
|  |   "et": "210",                     // extruder temp | ||||||
|  |   "bt": "60",                      // bed temp | ||||||
|  |   "di": "1.75",                    // diameter | ||||||
|  |   "de": "1.24",                    // density | ||||||
|  |   "sw": "240",                      // spool weight | ||||||
|  |   "u": "https://www.yoururl.com/search?q=" // URL used vor Brand Link and Filament Link | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Display System | ||||||
|  |  | ||||||
|  | #### OLED Architecture (SSD1306) | ||||||
|  |  | ||||||
|  | - **Resolution**: 128x64 pixels monochrome | ||||||
|  | - **Areas**: | ||||||
|  |   - Status bar: 0-16 pixels (version, icons) | ||||||
|  |   - Main area: 17-64 pixels (weight, messages) | ||||||
|  | - **Update Interval**: 1 second for status line | ||||||
|  |  | ||||||
|  | #### Icon System | ||||||
|  |  | ||||||
|  | Bitmap icons for various states: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // Status Icons (16x16 pixels) | ||||||
|  | - icon_success: Checkmark for successful operations | ||||||
|  | - icon_failed: X for errors | ||||||
|  | - icon_transfer: Arrow for data transmission | ||||||
|  | - icon_loading: Loading circle for ongoing operations | ||||||
|  |  | ||||||
|  | // Connection Icons with strikethrough indicator | ||||||
|  | - wifi_on/wifi_off: WLAN status | ||||||
|  | - bambu_on: Bambu Lab connection | ||||||
|  | - spoolman_on: Spoolman API status | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### API Integration | ||||||
|  |  | ||||||
|  | #### Spoolman REST API | ||||||
|  |  | ||||||
|  | FilaMan interacts with the following endpoints: | ||||||
|  |  | ||||||
|  | ```http | ||||||
|  | GET  /api/v1/spool/          # List spools | ||||||
|  | POST /api/v1/spool/          # Create new spool | ||||||
|  | PUT  /api/v1/spool/{id}/     # Update spool | ||||||
|  |  | ||||||
|  | GET  /api/v1/vendor/         # List vendors | ||||||
|  | POST /api/v1/vendor/         # Create new vendor | ||||||
|  |  | ||||||
|  | GET  /api/v1/filament/       # List filaments | ||||||
|  | POST /api/v1/filament/       # Create new filament | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Request Handling | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // Sequential API processing | ||||||
|  | enum spoolmanApiStateType { | ||||||
|  |     API_IDLE = 0, | ||||||
|  |     API_PROCESSING = 1, | ||||||
|  |     API_ERROR = 2 | ||||||
|  | }; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Prevents simultaneous API calls and deadlocks. | ||||||
|  |  | ||||||
|  | #### Weight Update Logic | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // Conditions for Spoolman update | ||||||
|  | if (activeSpoolId != "" &&  | ||||||
|  |     weigthCouterToApi > 3 &&    // 3+ stable measurements | ||||||
|  |     weightSend == 0 &&          // Not yet sent | ||||||
|  |     weight > 5 &&               // Minimum weight 5g | ||||||
|  |     spoolmanApiState == API_IDLE) { | ||||||
|  |     updateSpoolWeight(activeSpoolId, weight); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Bambu Lab MQTT | ||||||
|  |  | ||||||
|  | #### Connection Parameters | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // SSL/TLS Configuration | ||||||
|  | #define BAMBU_PORT 8883 | ||||||
|  | #define BAMBU_USERNAME "bblp" | ||||||
|  |  | ||||||
|  | // Topic Structure | ||||||
|  | String topic = "device/" + bambu_serial + "/report"; | ||||||
|  | String request_topic = "device/" + bambu_serial + "/request"; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### AMS Data Structure | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | struct AMSData { | ||||||
|  |     String tray_id; | ||||||
|  |     String tray_type; | ||||||
|  |     String tray_color; | ||||||
|  |     String tray_material; | ||||||
|  |     String setting_id; | ||||||
|  |     String tray_info_idx; | ||||||
|  |     bool has_spool; | ||||||
|  | }; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Auto-Send Mechanism | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // After tag recognition | ||||||
|  | if (bambuCredentials.autosend_enable) { | ||||||
|  |     autoSetToBambuSpoolId = activeSpoolId.toInt(); | ||||||
|  |     // Countdown starts automatically | ||||||
|  |     // Waits for new spool in AMS | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### WebSocket Communication | ||||||
|  |  | ||||||
|  | #### Message Types | ||||||
|  |  | ||||||
|  | ```javascript | ||||||
|  | // Client → Server | ||||||
|  | { | ||||||
|  |   "type": "writeNfcTag", | ||||||
|  |   "tagType": "spool", | ||||||
|  |   "payload": { /* JSON data */ } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | { | ||||||
|  |   "type": "scale", | ||||||
|  |   "payload": "tare|calibrate|setAutoTare", | ||||||
|  |   "enabled": true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Server → Client | ||||||
|  | { | ||||||
|  |   "type": "heartbeat", | ||||||
|  |   "freeHeap": 245, | ||||||
|  |   "bambu_connected": true, | ||||||
|  |   "spoolman_connected": true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | { | ||||||
|  |   "type": "amsData", | ||||||
|  |   "data": [ /* AMS array */ ] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Connection Management | ||||||
|  |  | ||||||
|  | - **Auto-Reconnect**: Client-side reconnection | ||||||
|  | - **Heartbeat**: Every 30 seconds for connection monitoring | ||||||
|  | - **Cleanup**: Automatic removal of dead connections | ||||||
|  |  | ||||||
|  | ### Watchdog and Error Handling | ||||||
|  |  | ||||||
|  | #### System Watchdog | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | // WDT Configuration | ||||||
|  | esp_task_wdt_init(10, true);  // 10s timeout, panic on overflow | ||||||
|  | esp_task_wdt_add(NULL);       // Add current task | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Error Recovery | ||||||
|  |  | ||||||
|  | - **NFC Reset**: Automatic PN532 restart on communication errors | ||||||
|  | - **MQTT Reconnect**: Bambu Lab connection automatically restored | ||||||
|  | - **WiFi Monitoring**: Connection check every 60 seconds | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Support | ||||||
|  |  | ||||||
|  | ### Community | ||||||
|  |  | ||||||
|  | - **Discord Server**: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v) | ||||||
|  | - **GitHub Issues**: [Filaman Repository](https://github.com/ManuelW77/Filaman/issues) | ||||||
|  | - **YouTube Channel**: [German explanation video](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) | ||||||
|  |  | ||||||
|  | ### Documentation | ||||||
|  |  | ||||||
|  | - **Official Website**: [www.filaman.app](https://www.filaman.app) | ||||||
|  | - **GitHub Wiki**: [Detailed documentation](https://github.com/ManuelW77/Filaman/wiki) | ||||||
|  | - **Hardware Reference**: ESP32 pinout diagrams in `/img/` | ||||||
|  |  | ||||||
|  | ### Support Development | ||||||
|  |  | ||||||
|  | If you'd like to support the project: | ||||||
|  |  | ||||||
|  | [](https://www.buymeacoffee.com/manuelw) | ||||||
|  |  | ||||||
|  | ### License | ||||||
|  |  | ||||||
|  | This project is released under the MIT License. See [LICENSE](LICENSE.txt) for details. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | **Last Updated**: August 2025   | ||||||
|  | **Version**: 2.0   | ||||||
|  | **Maintainer**: Manuel W. | ||||||
| @@ -9,7 +9,7 @@ | |||||||
| ; https://docs.platformio.org/page/projectconf.html | ; https://docs.platformio.org/page/projectconf.html | ||||||
|  |  | ||||||
| [common] | [common] | ||||||
| version = "2.0.0-beta5" | version = "2.0.4-beta2" | ||||||
| to_old_version = "1.5.10" | to_old_version = "1.5.10" | ||||||
|  |  | ||||||
| ## | ## | ||||||
|   | |||||||
| @@ -14,17 +14,45 @@ def get_version(): | |||||||
|         return version_match.group(1) if version_match else None |         return version_match.group(1) if version_match else None | ||||||
|  |  | ||||||
| def get_last_tag(): | def get_last_tag(): | ||||||
|  |     """Get the last non-beta tag for changelog generation""" | ||||||
|     try: |     try: | ||||||
|         result = subprocess.run(['git', 'describe', '--tags', '--abbrev=0'],  |         # Get all tags sorted by version | ||||||
|  |         result = subprocess.run(['git', 'tag', '-l', '--sort=-version:refname'],  | ||||||
|                               capture_output=True, text=True) |                               capture_output=True, text=True) | ||||||
|         return result.stdout.strip() |         if result.returncode != 0: | ||||||
|  |             return None | ||||||
|  |              | ||||||
|  |         tags = result.stdout.strip().split('\n') | ||||||
|  |          | ||||||
|  |         # Find the first (newest) non-beta tag | ||||||
|  |         for tag in tags: | ||||||
|  |             if tag and not '-beta' in tag.lower(): | ||||||
|  |                 print(f"Using last stable tag for changelog: {tag}") | ||||||
|  |                 return tag | ||||||
|  |          | ||||||
|  |         # Fallback: if no non-beta tags found, use the newest tag | ||||||
|  |         print("No stable tags found, using newest tag") | ||||||
|  |         if tags and tags[0]: | ||||||
|  |             return tags[0] | ||||||
|  |         return None | ||||||
|     except subprocess.CalledProcessError: |     except subprocess.CalledProcessError: | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
| def categorize_commit(commit_msg): | def categorize_commit(commit_msg): | ||||||
|     """Categorize commit messages based on conventional commits""" |     """Categorize commit messages based on conventional commits""" | ||||||
|     lower_msg = commit_msg.lower() |     lower_msg = commit_msg.lower() | ||||||
|     if any(x in lower_msg for x in ['feat', 'add', 'new']): |      | ||||||
|  |     # Filter out automatic release documentation commits | ||||||
|  |     if ('docs:' in lower_msg and  | ||||||
|  |         ('update changelog and header for version' in lower_msg or  | ||||||
|  |          'update platformio.ini for' in lower_msg)): | ||||||
|  |         return None  # Skip these commits | ||||||
|  |      | ||||||
|  |     # Check for breaking changes first | ||||||
|  |     if ('!' in commit_msg and any(x in lower_msg for x in ['feat!', 'fix!', 'chore!', 'refactor!'])) or \ | ||||||
|  |        'breaking change' in lower_msg or 'breaking:' in lower_msg: | ||||||
|  |         return 'Breaking Changes' | ||||||
|  |     elif any(x in lower_msg for x in ['feat', 'add', 'new']): | ||||||
|         return 'Added' |         return 'Added' | ||||||
|     elif any(x in lower_msg for x in ['fix', 'bug']): |     elif any(x in lower_msg for x in ['fix', 'bug']): | ||||||
|         return 'Fixed' |         return 'Fixed' | ||||||
| @@ -34,6 +62,7 @@ def categorize_commit(commit_msg): | |||||||
| def get_changes_from_git(): | def get_changes_from_git(): | ||||||
|     """Get changes from git commits since last tag""" |     """Get changes from git commits since last tag""" | ||||||
|     changes = { |     changes = { | ||||||
|  |         'Breaking Changes': [], | ||||||
|         'Added': [], |         'Added': [], | ||||||
|         'Changed': [], |         'Changed': [], | ||||||
|         'Fixed': [] |         'Fixed': [] | ||||||
| @@ -54,9 +83,12 @@ def get_changes_from_git(): | |||||||
|         for commit in commits: |         for commit in commits: | ||||||
|             if commit: |             if commit: | ||||||
|                 category = categorize_commit(commit) |                 category = categorize_commit(commit) | ||||||
|                 # Clean up commit message |                 if category is not None:  # Skip commits that return None (filtered out) | ||||||
|                 clean_msg = re.sub(r'^(feat|fix|chore|docs|style|refactor|perf|test)(\(.*\))?:', '', commit).strip() |                     # Clean up commit message | ||||||
|                 changes[category].append(clean_msg) |                     clean_msg = re.sub(r'^(feat|fix|chore|docs|style|refactor|perf|test)(\(.*\))?!?:', '', commit).strip() | ||||||
|  |                     # Remove BREAKING CHANGE prefix if present | ||||||
|  |                     clean_msg = re.sub(r'^breaking change:\s*', '', clean_msg, flags=re.IGNORECASE).strip() | ||||||
|  |                     changes[category].append(clean_msg) | ||||||
|                  |                  | ||||||
|     except subprocess.CalledProcessError: |     except subprocess.CalledProcessError: | ||||||
|         print("Error: Failed to get git commits") |         print("Error: Failed to get git commits") | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								src/api.cpp
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/api.cpp
									
									
									
									
									
								
							| @@ -21,6 +21,7 @@ uint16_t foundVendorId = 0;    // Store ID of found vendor | |||||||
| uint16_t foundFilamentId = 0;  // Store ID of found filament | uint16_t foundFilamentId = 0;  // Store ID of found filament | ||||||
| uint16_t createdFilamentId = 0;  // Store ID of newly created filament | uint16_t createdFilamentId = 0;  // Store ID of newly created filament | ||||||
| uint16_t createdSpoolId = 0;  // Store ID of newly created spool | uint16_t createdSpoolId = 0;  // Store ID of newly created spool | ||||||
|  | uint16_t updateOctoSpoolId = 0; // Store spool ID for OctoPrint update | ||||||
| bool spoolmanConnected = false; | bool spoolmanConnected = false; | ||||||
| bool spoolmanExtraFieldsChecked = false; | bool spoolmanExtraFieldsChecked = false; | ||||||
| TaskHandle_t* apiTask; | TaskHandle_t* apiTask; | ||||||
| @@ -124,28 +125,69 @@ void sendToApi(void *parameter) { | |||||||
|     String octoToken = params->octoToken; |     String octoToken = params->octoToken; | ||||||
|     bool triggerWeightUpdate = params->triggerWeightUpdate; |     bool triggerWeightUpdate = params->triggerWeightUpdate; | ||||||
|     String spoolIdForWeight = params->spoolIdForWeight; |     String spoolIdForWeight = params->spoolIdForWeight; | ||||||
|     uint16_t weightValue = params->weightValue;     |     uint16_t weightValue = params->weightValue; | ||||||
|  |  | ||||||
|     HTTPClient http; |     // Retry mechanism with configurable parameters | ||||||
|     http.setReuse(false); |     const uint8_t MAX_RETRIES = 3; | ||||||
|  |     const uint16_t RETRY_DELAY_MS = 1000; // 1 second between retries | ||||||
|  |     const uint16_t HTTP_TIMEOUT_MS = 10000; // 10 second HTTP timeout | ||||||
|  |      | ||||||
|  |     bool success = false; | ||||||
|  |     int httpCode = -1; | ||||||
|  |     String responsePayload = ""; | ||||||
|  |      | ||||||
|  |     // Try request with retries | ||||||
|  |     for (uint8_t attempt = 1; attempt <= MAX_RETRIES && !success; attempt++) { | ||||||
|  |         Serial.printf("API Request attempt %d/%d to: %s\n", attempt, MAX_RETRIES, spoolsUrl.c_str()); | ||||||
|  |          | ||||||
|  |         HTTPClient http; | ||||||
|  |         http.setReuse(false); | ||||||
|  |         http.setTimeout(HTTP_TIMEOUT_MS); // Set HTTP timeout | ||||||
|  |          | ||||||
|  |         http.begin(spoolsUrl); | ||||||
|  |         http.addHeader("Content-Type", "application/json"); | ||||||
|  |         if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken); | ||||||
|  |  | ||||||
|     http.begin(spoolsUrl); |         // Execute HTTP request based on type | ||||||
|     http.addHeader("Content-Type", "application/json"); |         if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); | ||||||
|     if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken); |         else if (httpType == "POST") httpCode = http.POST(updatePayload); | ||||||
|  |         else if (httpType == "GET") httpCode = http.GET(); | ||||||
|  |         else httpCode = http.PUT(updatePayload); | ||||||
|  |  | ||||||
|     int httpCode; |         // Check if request was successful | ||||||
|     if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); |         if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) { | ||||||
|     else if (httpType == "POST") httpCode = http.POST(updatePayload); |             responsePayload = http.getString(); | ||||||
|     else if (httpType == "GET") httpCode = http.GET(); |             success = true; | ||||||
|     else httpCode = http.PUT(updatePayload); |             Serial.printf("API Request successful on attempt %d, HTTP Code: %d\n", attempt, httpCode); | ||||||
|  |         } else { | ||||||
|  |             Serial.printf("API Request failed on attempt %d, HTTP Code: %d (%s)\n",  | ||||||
|  |                          attempt, httpCode, http.errorToString(httpCode).c_str()); | ||||||
|  |              | ||||||
|  |             // Don't retry on certain error codes (client errors) | ||||||
|  |             if (httpCode >= 400 && httpCode < 500 && httpCode != 408 && httpCode != 429) { | ||||||
|  |                 Serial.println("Client error detected, stopping retries"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // Wait before retry (except on last attempt) | ||||||
|  |             if (attempt < MAX_RETRIES) { | ||||||
|  |                 Serial.printf("Waiting %dms before retry...\n", RETRY_DELAY_MS); | ||||||
|  |                 http.end(); | ||||||
|  |                 vTaskDelay(RETRY_DELAY_MS / portTICK_PERIOD_MS); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         http.end(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (httpCode == HTTP_CODE_OK) { |     // Process successful response | ||||||
|  |     if (success) { | ||||||
|         Serial.println("Spoolman Abfrage erfolgreich"); |         Serial.println("Spoolman Abfrage erfolgreich"); | ||||||
|  |  | ||||||
|         // Restgewicht der Spule auslesen |         // Restgewicht der Spule auslesen | ||||||
|         String payload = http.getString(); |  | ||||||
|         JsonDocument doc; |         JsonDocument doc; | ||||||
|         DeserializationError error = deserializeJson(doc, payload); |         DeserializationError error = deserializeJson(doc, responsePayload); | ||||||
|         if (error) { |         if (error) { | ||||||
|             Serial.print("Fehler beim Parsen der JSON-Antwort: "); |             Serial.print("Fehler beim Parsen der JSON-Antwort: "); | ||||||
|             Serial.println(error.c_str()); |             Serial.println(error.c_str()); | ||||||
| @@ -225,10 +267,9 @@ void sendToApi(void *parameter) { | |||||||
|     } else if (httpCode == HTTP_CODE_CREATED) { |     } else if (httpCode == HTTP_CODE_CREATED) { | ||||||
|         Serial.println("Spoolman erfolgreich erstellt"); |         Serial.println("Spoolman erfolgreich erstellt"); | ||||||
|          |          | ||||||
|         // Parse response for created resources |         // Parse response for created resources   | ||||||
|         String payload = http.getString(); |  | ||||||
|         JsonDocument doc; |         JsonDocument doc; | ||||||
|         DeserializationError error = deserializeJson(doc, payload); |         DeserializationError error = deserializeJson(doc, responsePayload); | ||||||
|         if (error) { |         if (error) { | ||||||
|             Serial.print("Fehler beim Parsen der JSON-Antwort: "); |             Serial.print("Fehler beim Parsen der JSON-Antwort: "); | ||||||
|             Serial.println(error.c_str()); |             Serial.println(error.c_str()); | ||||||
| @@ -280,14 +321,17 @@ void sendToApi(void *parameter) { | |||||||
|             Serial.println(weightPayload); |             Serial.println(weightPayload); | ||||||
|  |  | ||||||
|             // Execute weight update |             // Execute weight update | ||||||
|             http.begin(weightUrl); |             HTTPClient weightHttp; | ||||||
|             http.addHeader("Content-Type", "application/json"); |             weightHttp.setReuse(false); | ||||||
|  |             weightHttp.setTimeout(HTTP_TIMEOUT_MS); | ||||||
|  |             weightHttp.begin(weightUrl); | ||||||
|  |             weightHttp.addHeader("Content-Type", "application/json"); | ||||||
|              |              | ||||||
|             int weightHttpCode = http.PUT(weightPayload); |             int weightHttpCode = weightHttp.PUT(weightPayload); | ||||||
|              |              | ||||||
|             if (weightHttpCode == HTTP_CODE_OK) { |             if (weightHttpCode == HTTP_CODE_OK) { | ||||||
|                 Serial.println("Weight update successful"); |                 Serial.println("Weight update successful"); | ||||||
|                 String weightResponse = http.getString(); |                 String weightResponse = weightHttp.getString(); | ||||||
|                 JsonDocument weightResponseDoc; |                 JsonDocument weightResponseDoc; | ||||||
|                 DeserializationError weightError = deserializeJson(weightResponseDoc, weightResponse); |                 DeserializationError weightError = deserializeJson(weightResponseDoc, weightResponse); | ||||||
|                  |                  | ||||||
| @@ -310,6 +354,7 @@ void sendToApi(void *parameter) { | |||||||
|                 oledShowProgressBar(1, 1, "Failure!", "Weight update"); |                 oledShowProgressBar(1, 1, "Failure!", "Weight update"); | ||||||
|             } |             } | ||||||
|              |              | ||||||
|  |             weightHttp.end(); | ||||||
|             weightDoc.clear(); |             weightDoc.clear(); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
| @@ -325,14 +370,25 @@ void sendToApi(void *parameter) { | |||||||
|         case API_REQUEST_BAMBU_UPDATE: |         case API_REQUEST_BAMBU_UPDATE: | ||||||
|             oledShowProgressBar(1, 1, "Failure!", "Bambu update"); |             oledShowProgressBar(1, 1, "Failure!", "Bambu update"); | ||||||
|             break; |             break; | ||||||
|  |         case API_REQUEST_VENDOR_CHECK: | ||||||
|  |             oledShowProgressBar(1, 1, "Failure!", "Vendor check"); | ||||||
|  |             foundVendorId = 0; // Set to 0 to indicate error/not found | ||||||
|  |             break; | ||||||
|         case API_REQUEST_VENDOR_CREATE: |         case API_REQUEST_VENDOR_CREATE: | ||||||
|             oledShowProgressBar(1, 1, "Failure!", "Vendor create"); |             oledShowProgressBar(1, 1, "Failure!", "Vendor create"); | ||||||
|  |             createdVendorId = 0; // Set to 0 to indicate error | ||||||
|  |             break; | ||||||
|  |         case API_REQUEST_FILAMENT_CHECK: | ||||||
|  |             oledShowProgressBar(1, 1, "Failure!", "Filament check"); | ||||||
|  |             foundFilamentId = 0; // Set to 0 to indicate error/not found | ||||||
|             break; |             break; | ||||||
|         case API_REQUEST_FILAMENT_CREATE: |         case API_REQUEST_FILAMENT_CREATE: | ||||||
|             oledShowProgressBar(1, 1, "Failure!", "Filament create"); |             oledShowProgressBar(1, 1, "Failure!", "Filament create"); | ||||||
|  |             createdFilamentId = 0; // Set to 0 to indicate error | ||||||
|             break; |             break; | ||||||
|         case API_REQUEST_SPOOL_CREATE: |         case API_REQUEST_SPOOL_CREATE: | ||||||
|             oledShowProgressBar(1, 1, "Failure!", "Spool create"); |             oledShowProgressBar(1, 1, "Failure!", "Spool create"); | ||||||
|  |             createdSpoolId = 0; // Set to 0 to indicate error instead of hanging | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode)); |         Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode)); | ||||||
| @@ -340,7 +396,6 @@ void sendToApi(void *parameter) { | |||||||
|         nfcReaderState = NFC_IDLE; // Reset NFC state to allow retry |         nfcReaderState = NFC_IDLE; // Reset NFC state to allow retry | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     http.end(); |  | ||||||
|     vTaskDelay(50 / portTICK_PERIOD_MS); |     vTaskDelay(50 / portTICK_PERIOD_MS); | ||||||
|  |  | ||||||
|     // Speicher freigeben |     // Speicher freigeben | ||||||
| @@ -952,6 +1007,13 @@ uint16_t createSpool(uint16_t vendorId, uint16_t filamentId, JsonDocument& paylo | |||||||
|     while(createdSpoolId == 65535) { |     while(createdSpoolId == 65535) { | ||||||
|         vTaskDelay(50 / portTICK_PERIOD_MS); |         vTaskDelay(50 / portTICK_PERIOD_MS); | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     // Check if spool creation was successful | ||||||
|  |     if (createdSpoolId == 0) { | ||||||
|  |         Serial.println("ERROR: Spool creation failed"); | ||||||
|  |         nfcReaderState = NFC_IDLE; // Reset NFC state | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Write data to tag with startWriteJsonToTag |     // Write data to tag with startWriteJsonToTag | ||||||
|     // void startWriteJsonToTag(const bool isSpoolTag, const char* payload); |     // void startWriteJsonToTag(const bool isSpoolTag, const char* payload); | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ extern bool sendOctoUpdate; | |||||||
| extern String octoUrl; | extern String octoUrl; | ||||||
| extern String octoToken; | extern String octoToken; | ||||||
| extern bool spoolmanConnected; | extern bool spoolmanConnected; | ||||||
|  | extern uint16_t updateOctoSpoolId; | ||||||
|  |  | ||||||
| bool checkSpoolmanInstance(); | bool checkSpoolmanInstance(); | ||||||
| bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk); | bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk); | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ TaskHandle_t BambuMqttTask; | |||||||
| bool bambuDisabled = false; | bool bambuDisabled = false; | ||||||
|  |  | ||||||
| bool bambu_connected = false; | bool bambu_connected = false; | ||||||
| int autoSetToBambuSpoolId = 0; | uint16_t autoSetToBambuSpoolId = 0; | ||||||
|  |  | ||||||
| BambuCredentials bambuCredentials; | BambuCredentials bambuCredentials; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ extern bool bambu_connected; | |||||||
| extern int ams_count; | extern int ams_count; | ||||||
| extern AMSData ams_data[MAX_AMS]; | extern AMSData ams_data[MAX_AMS]; | ||||||
| //extern bool autoSendToBambu; | //extern bool autoSendToBambu; | ||||||
| extern int autoSetToBambuSpoolId; | extern uint16_t autoSetToBambuSpoolId; | ||||||
| extern bool bambuDisabled; | extern bool bambuDisabled; | ||||||
| extern BambuCredentials bambuCredentials; | extern BambuCredentials bambuCredentials; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -59,7 +59,7 @@ void setup() { | |||||||
|  |  | ||||||
|   // Scale |   // Scale | ||||||
|   start_scale(touchSensorConnected); |   start_scale(touchSensorConnected); | ||||||
|   scale.tare(); |   scaleTareRequest = true; | ||||||
|  |  | ||||||
|   // WDT initialisieren mit 10 Sekunden Timeout |   // WDT initialisieren mit 10 Sekunden Timeout | ||||||
|   bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus |   bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus | ||||||
| @@ -178,9 +178,11 @@ void loop() { | |||||||
|     // Ausgabe der Waage auf Display |     // Ausgabe der Waage auf Display | ||||||
|     if(pauseMainTask == 0) |     if(pauseMainTask == 0) | ||||||
|     { |     { | ||||||
|  |       // Use filtered weight for smooth display, but still check API weight for significant changes | ||||||
|  |       int16_t displayWeight = getFilteredDisplayWeight(); | ||||||
|       if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0))) |       if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0))) | ||||||
|       { |       { | ||||||
|         (weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight); |         (displayWeight < 2) ? ((displayWeight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(displayWeight); | ||||||
|       } |       } | ||||||
|       mainTaskWasPaused = false; |       mainTaskWasPaused = false; | ||||||
|     } |     } | ||||||
| @@ -195,17 +197,6 @@ void loop() { | |||||||
|     { |     { | ||||||
|       lastWeightReadTime = currentMillis; |       lastWeightReadTime = currentMillis; | ||||||
|  |  | ||||||
|       // Prüfen ob die Waage korrekt genullt ist |  | ||||||
|       // Abweichung von 2g ignorieren |  | ||||||
|       if (autoTare && (weight > 2 && weight < 7) || weight < -2) |  | ||||||
|       { |  | ||||||
|         scale_tare_counter++; |  | ||||||
|       } |  | ||||||
|       else |  | ||||||
|       { |  | ||||||
|         scale_tare_counter = 0; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       // Prüfen ob das Gewicht gleich bleibt und dann senden |       // Prüfen ob das Gewicht gleich bleibt und dann senden | ||||||
|       if (abs(weight - lastWeight) <= 2 && weight > 5) |       if (abs(weight - lastWeight) <= 2 && weight > 5) | ||||||
|       { |       { | ||||||
| @@ -219,7 +210,6 @@ void loop() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // reset weight counter after writing tag |     // reset weight counter after writing tag | ||||||
|     // TBD: what exactly is the logic behind this? |  | ||||||
|     if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS) |     if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS) | ||||||
|     { |     { | ||||||
|       weigthCouterToApi = 0; |       weigthCouterToApi = 0; | ||||||
| @@ -242,6 +232,29 @@ void loop() { | |||||||
|         { |         { | ||||||
|           autoSetToBambuSpoolId = activeSpoolId.toInt(); |           autoSetToBambuSpoolId = activeSpoolId.toInt(); | ||||||
|         } |         } | ||||||
|  |         if (octoEnabled)  | ||||||
|  |         { | ||||||
|  |           updateOctoSpoolId = activeSpoolId.toInt(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |         oledShowIcon("failed"); | ||||||
|  |         vTaskDelay(2000 / portTICK_PERIOD_MS); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Handle successful tag write: Send weight to Spoolman but NEVER auto-send to Bambu | ||||||
|  |     if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_WRITE_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE)  | ||||||
|  |     { | ||||||
|  |       // set the current tag as processed to prevent it beeing processed again | ||||||
|  |       tagProcessed = true; | ||||||
|  |  | ||||||
|  |       if (updateSpoolWeight(activeSpoolId, weight))  | ||||||
|  |       { | ||||||
|  |         weightSend = 1; | ||||||
|  |         Serial.println("Tag written: Weight sent to Spoolman, but NO auto-send to Bambu"); | ||||||
|  |         // INTENTIONALLY do NOT set autoSetToBambuSpoolId here to prevent Bambu auto-send | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
| @@ -252,7 +265,7 @@ void loop() { | |||||||
|  |  | ||||||
|     if(octoEnabled && sendOctoUpdate && spoolmanApiState == API_IDLE) |     if(octoEnabled && sendOctoUpdate && spoolmanApiState == API_IDLE) | ||||||
|     { |     { | ||||||
|       updateSpoolOcto(autoSetToBambuSpoolId); |       updateSpoolOcto(updateOctoSpoolId); | ||||||
|       sendOctoUpdate = false; |       sendOctoUpdate = false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										274
									
								
								src/nfc.cpp
									
									
									
									
									
								
							
							
						
						
									
										274
									
								
								src/nfc.cpp
									
									
									
									
									
								
							| @@ -108,6 +108,37 @@ bool formatNdefTag() { | |||||||
|   return buffer[2]*8; |   return buffer[2]*8; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Robust page reading with error recovery | ||||||
|  | bool robustPageRead(uint8_t page, uint8_t* buffer) { | ||||||
|  |     const int MAX_READ_ATTEMPTS = 3; | ||||||
|  |      | ||||||
|  |     for (int attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt++) { | ||||||
|  |         esp_task_wdt_reset(); | ||||||
|  |         yield(); | ||||||
|  |          | ||||||
|  |         if (nfc.ntag2xx_ReadPage(page, buffer)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         Serial.printf("Page %d read failed, attempt %d/%d\n", page, attempt + 1, MAX_READ_ATTEMPTS); | ||||||
|  |          | ||||||
|  |         // Try to stabilize connection between attempts | ||||||
|  |         if (attempt < MAX_READ_ATTEMPTS - 1) { | ||||||
|  |             vTaskDelay(pdMS_TO_TICKS(25)); | ||||||
|  |              | ||||||
|  |             // Re-verify tag presence with quick check | ||||||
|  |             uint8_t uid[7]; | ||||||
|  |             uint8_t uidLength; | ||||||
|  |             if (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 100)) { | ||||||
|  |                 Serial.println("Tag lost during read operation"); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| String detectNtagType() | String detectNtagType() | ||||||
| { | { | ||||||
|   // Read capability container from page 3 to determine exact NTAG type |   // Read capability container from page 3 to determine exact NTAG type | ||||||
| @@ -1268,6 +1299,61 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage, String uidString) { | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Read complete JSON data for fast-path to enable web interface display | ||||||
|  | bool readCompleteJsonForFastPath() { | ||||||
|  |     Serial.println("=== FAST-PATH: Reading complete JSON for web interface ==="); | ||||||
|  |      | ||||||
|  |     // Read tag size first | ||||||
|  |     uint16_t tagSize = readTagSize(); | ||||||
|  |     if (tagSize == 0) { | ||||||
|  |         Serial.println("FAST-PATH: Could not determine tag size"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Create buffer for complete data | ||||||
|  |     uint8_t* data = (uint8_t*)malloc(tagSize); | ||||||
|  |     if (!data) { | ||||||
|  |         Serial.println("FAST-PATH: Could not allocate memory for complete read"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     memset(data, 0, tagSize); | ||||||
|  |      | ||||||
|  |     // Read all pages | ||||||
|  |     uint8_t numPages = tagSize / 4; | ||||||
|  |     for (uint8_t i = 4; i < 4 + numPages; i++) { | ||||||
|  |         if (!robustPageRead(i, data + (i - 4) * 4)) { | ||||||
|  |             Serial.printf("FAST-PATH: Failed to read page %d\n", i); | ||||||
|  |             free(data); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Check for NDEF message end | ||||||
|  |         if (data[(i - 4) * 4] == 0xFE) { | ||||||
|  |             Serial.println("FAST-PATH: Found NDEF message end marker"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         yield(); | ||||||
|  |         esp_task_wdt_reset(); | ||||||
|  |         vTaskDelay(pdMS_TO_TICKS(2)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Decode NDEF and extract JSON | ||||||
|  |     bool success = decodeNdefAndReturnJson(data, ""); // Empty UID string for fast-path | ||||||
|  |      | ||||||
|  |     free(data); | ||||||
|  |      | ||||||
|  |     if (success) { | ||||||
|  |         Serial.println("✓ FAST-PATH: Complete JSON data successfully loaded"); | ||||||
|  |         Serial.print("nfcJsonData length: "); | ||||||
|  |         Serial.println(nfcJsonData.length()); | ||||||
|  |     } else { | ||||||
|  |         Serial.println("✗ FAST-PATH: Failed to decode complete JSON data"); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool quickSpoolIdCheck(String uidString) { | bool quickSpoolIdCheck(String uidString) { | ||||||
|     // Fast-path: Read NDEF structure to quickly locate and check JSON payload |     // Fast-path: Read NDEF structure to quickly locate and check JSON payload | ||||||
|     // This dramatically speeds up known spool recognition |     // This dramatically speeds up known spool recognition | ||||||
| @@ -1285,10 +1371,11 @@ bool quickSpoolIdCheck(String uidString) { | |||||||
|     memset(ndefData, 0, 20); |     memset(ndefData, 0, 20); | ||||||
|      |      | ||||||
|     for (uint8_t page = 4; page < 9; page++) { |     for (uint8_t page = 4; page < 9; page++) { | ||||||
|         if (!nfc.ntag2xx_ReadPage(page, ndefData + (page - 4) * 4)) { |         if (!robustPageRead(page, ndefData + (page - 4) * 4)) { | ||||||
|             Serial.print("Failed to read page "); |             Serial.print("FAST-PATH: Failed to read page "); | ||||||
|             Serial.println(page); |             Serial.print(page); | ||||||
|             return false; // Fall back to full read |             Serial.println(" - falling back to full read"); | ||||||
|  |             return false; // Fall back to full read if any page read fails | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @@ -1358,10 +1445,11 @@ bool quickSpoolIdCheck(String uidString) { | |||||||
|         memset(extraData, 0, 16); |         memset(extraData, 0, 16); | ||||||
|          |          | ||||||
|         for (uint8_t page = 9; page < 13; page++) { |         for (uint8_t page = 9; page < 13; page++) { | ||||||
|             if (!nfc.ntag2xx_ReadPage(page, extraData + (page - 9) * 4)) { |             if (!robustPageRead(page, extraData + (page - 9) * 4)) { | ||||||
|                 Serial.print("Failed to read additional page "); |                 Serial.print("FAST-PATH: Failed to read additional page "); | ||||||
|                 Serial.println(page); |                 Serial.print(page); | ||||||
|                 return false; |                 Serial.println(" - falling back to full read"); | ||||||
|  |                 return false; // Fall back to full read if extended read fails | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
| @@ -1403,6 +1491,14 @@ bool quickSpoolIdCheck(String uidString) { | |||||||
|                     activeSpoolId = quickSpoolId; |                     activeSpoolId = quickSpoolId; | ||||||
|                     lastSpoolId = activeSpoolId; |                     lastSpoolId = activeSpoolId; | ||||||
|                      |                      | ||||||
|  |                     // Read complete JSON data for web interface display | ||||||
|  |                     Serial.println("FAST-PATH: Reading complete JSON data for web interface..."); | ||||||
|  |                     if (readCompleteJsonForFastPath()) { | ||||||
|  |                         Serial.println("✓ FAST-PATH: Complete JSON data loaded for web interface"); | ||||||
|  |                     } else { | ||||||
|  |                         Serial.println("⚠ FAST-PATH: Could not read complete JSON, web interface may show limited data"); | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|                     oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); |                     oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); | ||||||
|                     Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); |                     Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); | ||||||
|                     return true; |                     return true; | ||||||
| @@ -1450,6 +1546,14 @@ bool quickSpoolIdCheck(String uidString) { | |||||||
|                 activeSpoolId = quickSpoolId; |                 activeSpoolId = quickSpoolId; | ||||||
|                 lastSpoolId = activeSpoolId; |                 lastSpoolId = activeSpoolId; | ||||||
|                  |                  | ||||||
|  |                 // Read complete JSON data for web interface display | ||||||
|  |                 Serial.println("FAST-PATH: Reading complete JSON data for web interface..."); | ||||||
|  |                 if (readCompleteJsonForFastPath()) { | ||||||
|  |                     Serial.println("✓ FAST-PATH: Complete JSON data loaded for web interface"); | ||||||
|  |                 } else { | ||||||
|  |                     Serial.println("⚠ FAST-PATH: Could not read complete JSON, web interface may show limited data"); | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|                 oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); |                 oledShowProgressBar(2, octoEnabled?5:4, "Known Spool", "Quick mode"); | ||||||
|                 Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); |                 Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly"); | ||||||
|                 return true; |                 return true; | ||||||
| @@ -1496,6 +1600,10 @@ void writeJsonToTag(void *parameter) { | |||||||
|   // aktualisieren der Website wenn sich der Status ändert |   // aktualisieren der Website wenn sich der Status ändert | ||||||
|   sendNfcData(); |   sendNfcData(); | ||||||
|   vTaskDelay(100 / portTICK_PERIOD_MS); |   vTaskDelay(100 / portTICK_PERIOD_MS); | ||||||
|  |    | ||||||
|  |   // Show waiting message for tag detection | ||||||
|  |   oledShowProgressBar(0, 1, "Write Tag", "Warte auf Tag"); | ||||||
|  |    | ||||||
|   // Wait 10sec for tag |   // Wait 10sec for tag | ||||||
|   uint8_t success = 0; |   uint8_t success = 0; | ||||||
|   String uidString = ""; |   String uidString = ""; | ||||||
| @@ -1542,7 +1650,30 @@ void writeJsonToTag(void *parameter) { | |||||||
|         if(params->tagType){ |         if(params->tagType){ | ||||||
|           // TBD: should this be simplified? |           // TBD: should this be simplified? | ||||||
|           if (updateSpoolTagId(uidString, params->payload) && params->tagType) { |           if (updateSpoolTagId(uidString, params->payload) && params->tagType) { | ||||||
|              |             // Check if weight is over 20g and send to Spoolman | ||||||
|  |             if (weight > 20) { | ||||||
|  |               Serial.println("Tag successfully written and weight > 20g - sending weight to Spoolman"); | ||||||
|  |                | ||||||
|  |               // Extract spool ID from payload for weight update | ||||||
|  |               JsonDocument payloadDoc; | ||||||
|  |               DeserializationError error = deserializeJson(payloadDoc, params->payload); | ||||||
|  |                | ||||||
|  |               if (!error && payloadDoc["sm_id"].is<String>()) { | ||||||
|  |                 String spoolId = payloadDoc["sm_id"].as<String>(); | ||||||
|  |                 if (spoolId != "") { | ||||||
|  |                   Serial.printf("Updating spool %s with weight %dg\n", spoolId.c_str(), weight); | ||||||
|  |                   updateSpoolWeight(spoolId, weight); | ||||||
|  |                 } else { | ||||||
|  |                   Serial.println("No valid spool ID found for weight update"); | ||||||
|  |                 } | ||||||
|  |               } else { | ||||||
|  |                 Serial.println("Error parsing payload for spool ID extraction"); | ||||||
|  |               } | ||||||
|  |                | ||||||
|  |               payloadDoc.clear(); | ||||||
|  |             } else { | ||||||
|  |               Serial.printf("Weight %dg is not above 20g threshold - skipping weight update\n", weight); | ||||||
|  |             } | ||||||
|           }else{ |           }else{ | ||||||
|             // Potentially handle errors |             // Potentially handle errors | ||||||
|           } |           } | ||||||
| @@ -1655,10 +1786,60 @@ void writeJsonToTag(void *parameter) { | |||||||
|   vTaskDelete(NULL); |   vTaskDelete(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Ensures sm_id is always the first key in JSON for fast-path detection | ||||||
|  | String optimizeJsonForFastPath(const char* payload) { | ||||||
|  |     JsonDocument inputDoc; | ||||||
|  |     DeserializationError error = deserializeJson(inputDoc, payload); | ||||||
|  |      | ||||||
|  |     if (error) { | ||||||
|  |         Serial.print("JSON optimization failed: "); | ||||||
|  |         Serial.println(error.c_str()); | ||||||
|  |         return String(payload); // Return original if parsing fails | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Create optimized JSON with sm_id first | ||||||
|  |     JsonDocument optimizedDoc; | ||||||
|  |      | ||||||
|  |     // Always add sm_id first (even if it's "0" for brand filaments) | ||||||
|  |     if (inputDoc["sm_id"].is<String>()) { | ||||||
|  |         optimizedDoc["sm_id"] = inputDoc["sm_id"].as<String>(); | ||||||
|  |         Serial.print("Optimizing JSON: sm_id found = "); | ||||||
|  |         Serial.println(inputDoc["sm_id"].as<String>()); | ||||||
|  |     } else { | ||||||
|  |         optimizedDoc["sm_id"] = "0"; // Default for brand filaments | ||||||
|  |         Serial.println("Optimizing JSON: No sm_id found, setting to '0'"); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Add all other keys in original order | ||||||
|  |     for (JsonPair kv : inputDoc.as<JsonObject>()) { | ||||||
|  |         String key = kv.key().c_str(); | ||||||
|  |         if (key != "sm_id") { // Skip sm_id as it's already added first | ||||||
|  |             optimizedDoc[key] = kv.value(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     String optimizedJson; | ||||||
|  |     serializeJson(optimizedDoc, optimizedJson); | ||||||
|  |      | ||||||
|  |     Serial.println("JSON optimized for fast-path detection:"); | ||||||
|  |     Serial.print("Original:  "); | ||||||
|  |     Serial.println(payload); | ||||||
|  |     Serial.print("Optimized: "); | ||||||
|  |     Serial.println(optimizedJson); | ||||||
|  |      | ||||||
|  |     inputDoc.clear(); | ||||||
|  |     optimizedDoc.clear(); | ||||||
|  |      | ||||||
|  |     return optimizedJson; | ||||||
|  | } | ||||||
|  |  | ||||||
| void startWriteJsonToTag(const bool isSpoolTag, const char* payload) { | void startWriteJsonToTag(const bool isSpoolTag, const char* payload) { | ||||||
|  |   // Optimize JSON to ensure sm_id is first key for fast-path detection | ||||||
|  |   String optimizedPayload = optimizeJsonForFastPath(payload); | ||||||
|  |    | ||||||
|   NfcWriteParameterType* parameters = new NfcWriteParameterType(); |   NfcWriteParameterType* parameters = new NfcWriteParameterType(); | ||||||
|   parameters->tagType = isSpoolTag; |   parameters->tagType = isSpoolTag; | ||||||
|   parameters->payload = strdup(payload); |   parameters->payload = strdup(optimizedPayload.c_str()); // Use optimized payload | ||||||
|    |    | ||||||
|   // Task nicht mehrfach starten |   // Task nicht mehrfach starten | ||||||
|   if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) { |   if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) { | ||||||
| @@ -1678,9 +1859,44 @@ void startWriteJsonToTag(const bool isSpoolTag, const char* payload) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Safe tag detection with manual retry logic and short timeouts | ||||||
|  | bool safeTagDetection(uint8_t* uid, uint8_t* uidLength) { | ||||||
|  |     const int MAX_ATTEMPTS = 3; | ||||||
|  |     const int SHORT_TIMEOUT = 100; // Very short timeout to prevent hanging | ||||||
|  |      | ||||||
|  |     for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { | ||||||
|  |         // Watchdog reset on each attempt | ||||||
|  |         esp_task_wdt_reset(); | ||||||
|  |         yield(); | ||||||
|  |          | ||||||
|  |         // Use short timeout to avoid blocking | ||||||
|  |         bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, uidLength, SHORT_TIMEOUT); | ||||||
|  |          | ||||||
|  |         if (success) { | ||||||
|  |             Serial.printf("✓ Tag detected on attempt %d with %dms timeout\n", attempt + 1, SHORT_TIMEOUT); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Short pause between attempts | ||||||
|  |         vTaskDelay(pdMS_TO_TICKS(25)); | ||||||
|  |          | ||||||
|  |         // Refresh RF field after failed attempt (but not on last attempt) | ||||||
|  |         if (attempt < MAX_ATTEMPTS - 1) { | ||||||
|  |             nfc.SAMConfig(); | ||||||
|  |             vTaskDelay(pdMS_TO_TICKS(10)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| void scanRfidTask(void * parameter) { | void scanRfidTask(void * parameter) { | ||||||
|   Serial.println("RFID Task gestartet"); |   Serial.println("RFID Task gestartet"); | ||||||
|   for(;;) { |   for(;;) { | ||||||
|  |     // Regular watchdog reset | ||||||
|  |     esp_task_wdt_reset(); | ||||||
|  |     yield(); | ||||||
|  |      | ||||||
|     // Skip scanning during write operations, but keep NFC interface active |     // Skip scanning during write operations, but keep NFC interface active | ||||||
|     if (nfcReaderState != NFC_WRITING && !nfcWriteInProgress && !nfcReadingTaskSuspendRequest && !booting) |     if (nfcReaderState != NFC_WRITING && !nfcWriteInProgress && !nfcReadingTaskSuspendRequest && !booting) | ||||||
|     { |     { | ||||||
| @@ -1691,10 +1907,16 @@ void scanRfidTask(void * parameter) { | |||||||
|       uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID |       uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID | ||||||
|       uint8_t uidLength; |       uint8_t uidLength; | ||||||
|  |  | ||||||
|       success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500); |       // Use safe tag detection instead of blocking readPassiveTargetID | ||||||
|  |       success = safeTagDetection(uid, &uidLength); | ||||||
|  |  | ||||||
|       foundNfcTag(nullptr, success); |       foundNfcTag(nullptr, success); | ||||||
|        |        | ||||||
|  |       // Reset activeSpoolId immediately when no tag is detected to prevent stale autoSet | ||||||
|  |       if (!success) { | ||||||
|  |         activeSpoolId = ""; | ||||||
|  |       } | ||||||
|  |        | ||||||
|       // As long as there is still a tag on the reader, do not try to read it again |       // As long as there is still a tag on the reader, do not try to read it again | ||||||
|       if (success && nfcReaderState == NFC_IDLE) |       if (success && nfcReaderState == NFC_IDLE) | ||||||
|       { |       { | ||||||
| @@ -1708,9 +1930,9 @@ void scanRfidTask(void * parameter) { | |||||||
|  |  | ||||||
|         oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag"); |         oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag"); | ||||||
|  |  | ||||||
|         // Wait 1 second after tag detection to stabilize connection |         // Reduced stabilization time for better responsiveness | ||||||
|         Serial.println("Tag detected, waiting 1 second for stabilization..."); |         Serial.println("Tag detected, minimal stabilization..."); | ||||||
|         vTaskDelay(1000 / portTICK_PERIOD_MS); |         vTaskDelay(200 / portTICK_PERIOD_MS); // Reduced from 1000ms to 200ms | ||||||
|  |  | ||||||
|         // create Tag UID string |         // create Tag UID string | ||||||
|         String uidString = ""; |         String uidString = ""; | ||||||
| @@ -1753,9 +1975,10 @@ void scanRfidTask(void * parameter) { | |||||||
|              |              | ||||||
|             for (uint8_t i = 4; i < 4+numPages; i++) { |             for (uint8_t i = 4; i < 4+numPages; i++) { | ||||||
|                |                | ||||||
|               if (!nfc.ntag2xx_ReadPage(i, data+(i-4) * 4)) |               if (!robustPageRead(i, data+(i-4) * 4)) | ||||||
|               { |               { | ||||||
|                 break; // Stop if reading fails |                 Serial.printf("Failed to read page %d after retries, stopping\n", i); | ||||||
|  |                 break; // Stop if reading fails after retries | ||||||
|               } |               } | ||||||
|               |               | ||||||
|               // Check for NDEF message end |               // Check for NDEF message end | ||||||
| @@ -1767,8 +1990,8 @@ void scanRfidTask(void * parameter) { | |||||||
|  |  | ||||||
|               yield(); |               yield(); | ||||||
|               esp_task_wdt_reset(); |               esp_task_wdt_reset(); | ||||||
|               // Increased delay to ensure stable reading |               // Reduced delay for faster reading | ||||||
|               vTaskDelay(pdMS_TO_TICKS(5)); // Increased from 1ms to 5ms |               vTaskDelay(pdMS_TO_TICKS(2)); // Reduced from 5ms to 2ms | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             Serial.println("Tag reading completed, starting NDEF decode..."); |             Serial.println("Tag reading completed, starting NDEF decode..."); | ||||||
| @@ -1789,6 +2012,9 @@ void scanRfidTask(void * parameter) { | |||||||
|           { |           { | ||||||
|             oledShowProgressBar(1, 1, "Failure", "Tag read error"); |             oledShowProgressBar(1, 1, "Failure", "Tag read error"); | ||||||
|             nfcReaderState = NFC_READ_ERROR; |             nfcReaderState = NFC_READ_ERROR; | ||||||
|  |             // Reset activeSpoolId when tag reading fails to prevent autoSet | ||||||
|  |             activeSpoolId = ""; | ||||||
|  |             Serial.println("Tag read failed - activeSpoolId reset to prevent autoSet"); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
| @@ -1796,6 +2022,9 @@ void scanRfidTask(void * parameter) { | |||||||
|           //TBD: Show error here?! |           //TBD: Show error here?! | ||||||
|           oledShowProgressBar(1, 1, "Failure", "Unkown tag type"); |           oledShowProgressBar(1, 1, "Failure", "Unkown tag type"); | ||||||
|           Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!"); |           Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!"); | ||||||
|  |           // Reset activeSpoolId when tag type is unknown to prevent autoSet | ||||||
|  |           activeSpoolId = ""; | ||||||
|  |           Serial.println("Unknown tag type - activeSpoolId reset to prevent autoSet"); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -1815,10 +2044,13 @@ void scanRfidTask(void * parameter) { | |||||||
|         Serial.println("Tag nach erfolgreichem Lesen entfernt - bereit für nächsten Tag"); |         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 |       // Add a pause after successful reading to prevent immediate re-reading | ||||||
|       if (nfcReaderState == NFC_READ_SUCCESS) { |       if (nfcReaderState == NFC_READ_SUCCESS) { | ||||||
|         Serial.println("Tag erfolgreich gelesen - warte 5 Sekunden vor nächstem Scan"); |         Serial.println("Tag erfolgreich gelesen - warte 3 Sekunden vor nächstem Scan"); | ||||||
|         vTaskDelay(5000 / portTICK_PERIOD_MS); // 5 second pause |         vTaskDelay(3000 / portTICK_PERIOD_MS); // Reduced from 5 seconds to 3 seconds | ||||||
|  |       } else { | ||||||
|  |         // Faster scanning when no tag or idle state | ||||||
|  |         vTaskDelay(150 / portTICK_PERIOD_MS); // Faster scan interval | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // aktualisieren der Website wenn sich der Status ändert |       // aktualisieren der Website wenn sich der Status ändert | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ void startNfc(); | |||||||
| void scanRfidTask(void * parameter); | void scanRfidTask(void * parameter); | ||||||
| void startWriteJsonToTag(const bool isSpoolTag, const char* payload); | void startWriteJsonToTag(const bool isSpoolTag, const char* payload); | ||||||
| bool quickSpoolIdCheck(String uidString); | bool quickSpoolIdCheck(String uidString); | ||||||
|  | bool readCompleteJsonForFastPath(); // Read complete JSON data for fast-path web interface display | ||||||
|  |  | ||||||
| extern TaskHandle_t RfidReaderTask; | extern TaskHandle_t RfidReaderTask; | ||||||
| extern String nfcJsonData; | extern String nfcJsonData; | ||||||
|   | |||||||
							
								
								
									
										185
									
								
								src/scale.cpp
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								src/scale.cpp
									
									
									
									
									
								
							| @@ -13,6 +13,21 @@ TaskHandle_t ScaleTask; | |||||||
|  |  | ||||||
| int16_t weight = 0; | int16_t weight = 0; | ||||||
|  |  | ||||||
|  | // Weight stabilization variables | ||||||
|  | #define MOVING_AVERAGE_SIZE 8           // Reduced from 20 to 8 for faster response | ||||||
|  | #define LOW_PASS_ALPHA 0.3f            // Increased from 0.15 to 0.3 for faster tracking | ||||||
|  | #define DISPLAY_THRESHOLD 0.3f         // Reduced from 0.5 to 0.3g for more responsive display | ||||||
|  | #define API_THRESHOLD 1.5f             // Reduced from 2.0 to 1.5g for faster API actions | ||||||
|  | #define MEASUREMENT_INTERVAL_MS 30     // Reduced from 50ms to 30ms for faster updates | ||||||
|  |  | ||||||
|  | float weightBuffer[MOVING_AVERAGE_SIZE]; | ||||||
|  | uint8_t bufferIndex = 0; | ||||||
|  | bool bufferFilled = false; | ||||||
|  | float filteredWeight = 0.0f; | ||||||
|  | int16_t lastDisplayedWeight = 0; | ||||||
|  | int16_t lastStableWeight = 0;        // For API/action triggering | ||||||
|  | unsigned long lastMeasurementTime = 0; | ||||||
|  |  | ||||||
| uint8_t weigthCouterToApi = 0; | uint8_t weigthCouterToApi = 0; | ||||||
| uint8_t scale_tare_counter = 0; | uint8_t scale_tare_counter = 0; | ||||||
| bool scaleTareRequest = false; | bool scaleTareRequest = false; | ||||||
| @@ -21,6 +36,93 @@ bool scaleCalibrated; | |||||||
| bool autoTare = true; | bool autoTare = true; | ||||||
| bool scaleCalibrationActive = false; | bool scaleCalibrationActive = false; | ||||||
|  |  | ||||||
|  | // ##### Weight stabilization functions ##### | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Reset weight filter buffer - call after tare or calibration | ||||||
|  |  */ | ||||||
|  | void resetWeightFilter() { | ||||||
|  |   bufferIndex = 0; | ||||||
|  |   bufferFilled = false; | ||||||
|  |   filteredWeight = 0.0f; | ||||||
|  |   lastDisplayedWeight = 0; | ||||||
|  |   lastStableWeight = 0;            // Reset stable weight for API actions | ||||||
|  |    | ||||||
|  |   // Initialize buffer with zeros | ||||||
|  |   for (int i = 0; i < MOVING_AVERAGE_SIZE; i++) { | ||||||
|  |     weightBuffer[i] = 0.0f; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Calculate moving average from weight buffer | ||||||
|  |  */ | ||||||
|  | float calculateMovingAverage() { | ||||||
|  |   float sum = 0.0f; | ||||||
|  |   int count = bufferFilled ? MOVING_AVERAGE_SIZE : bufferIndex; | ||||||
|  |    | ||||||
|  |   for (int i = 0; i < count; i++) { | ||||||
|  |     sum += weightBuffer[i]; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   return (count > 0) ? sum / count : 0.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Apply low-pass filter to smooth weight readings | ||||||
|  |  * Uses exponential smoothing: y_new = alpha * x_new + (1-alpha) * y_old | ||||||
|  |  */ | ||||||
|  | float applyLowPassFilter(float newValue) { | ||||||
|  |   filteredWeight = LOW_PASS_ALPHA * newValue + (1.0f - LOW_PASS_ALPHA) * filteredWeight; | ||||||
|  |   return filteredWeight; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Process new weight reading with stabilization | ||||||
|  |  * Returns stabilized weight value | ||||||
|  |  */ | ||||||
|  | int16_t processWeightReading(float rawWeight) { | ||||||
|  |   // Add to moving average buffer | ||||||
|  |   weightBuffer[bufferIndex] = rawWeight; | ||||||
|  |   bufferIndex = (bufferIndex + 1) % MOVING_AVERAGE_SIZE; | ||||||
|  |    | ||||||
|  |   if (bufferIndex == 0) { | ||||||
|  |     bufferFilled = true; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   // Calculate moving average | ||||||
|  |   float avgWeight = calculateMovingAverage(); | ||||||
|  |    | ||||||
|  |   // Apply low-pass filter | ||||||
|  |   float smoothedWeight = applyLowPassFilter(avgWeight); | ||||||
|  |    | ||||||
|  |   // Round to nearest gram | ||||||
|  |   int16_t newWeight = round(smoothedWeight); | ||||||
|  |    | ||||||
|  |   // Update displayed weight if display threshold is reached | ||||||
|  |   if (abs(newWeight - lastDisplayedWeight) >= DISPLAY_THRESHOLD) { | ||||||
|  |     lastDisplayedWeight = newWeight; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   // Update global weight for API actions only if stable threshold is reached | ||||||
|  |   int16_t weightToReturn = weight; // Default: keep current weight | ||||||
|  |    | ||||||
|  |   if (abs(newWeight - lastStableWeight) >= API_THRESHOLD) { | ||||||
|  |     lastStableWeight = newWeight; | ||||||
|  |     weightToReturn = newWeight; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   return weightToReturn; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Get current filtered weight for display purposes | ||||||
|  |  * This returns the smoothed weight even if it hasn't triggered API actions | ||||||
|  |  */ | ||||||
|  | int16_t getFilteredDisplayWeight() { | ||||||
|  |   return lastDisplayedWeight; | ||||||
|  | } | ||||||
|  |  | ||||||
| // ##### Funktionen für Waage ##### | // ##### Funktionen für Waage ##### | ||||||
| uint8_t setAutoTare(bool autoTareValue) { | uint8_t setAutoTare(bool autoTareValue) { | ||||||
|   Serial.print("Set AutoTare to "); |   Serial.print("Set AutoTare to "); | ||||||
| @@ -39,6 +141,7 @@ uint8_t setAutoTare(bool autoTareValue) { | |||||||
| uint8_t tareScale() { | uint8_t tareScale() { | ||||||
|   Serial.println("Tare scale"); |   Serial.println("Tare scale"); | ||||||
|   scale.tare(); |   scale.tare(); | ||||||
|  |   resetWeightFilter(); | ||||||
|    |    | ||||||
|   return 1; |   return 1; | ||||||
| } | } | ||||||
| @@ -48,37 +151,66 @@ void scale_loop(void * parameter) { | |||||||
|   Serial.println("Scale Loop started"); |   Serial.println("Scale Loop started"); | ||||||
|   Serial.println("++++++++++++++++++++++++++++++"); |   Serial.println("++++++++++++++++++++++++++++++"); | ||||||
|  |  | ||||||
|  |   //scaleTareRequest == true; | ||||||
|  |   // Initialize weight filter | ||||||
|  |   resetWeightFilter(); | ||||||
|  |   lastMeasurementTime = millis(); | ||||||
|  |  | ||||||
|   for(;;) { |   for(;;) { | ||||||
|     if (scale.is_ready())  |     unsigned long currentTime = millis(); | ||||||
|     { |      | ||||||
|       // Waage automatisch Taren, wenn zu lange Abweichung |     // Only measure at defined intervals to reduce noise | ||||||
|       if (autoTare && scale_tare_counter >= 5)  |     if (currentTime - lastMeasurementTime >= MEASUREMENT_INTERVAL_MS) { | ||||||
|  |       if (scale.is_ready())  | ||||||
|       { |       { | ||||||
|         Serial.println("Auto Tare scale"); |         // Waage manuell Taren | ||||||
|         scale.tare(); |         if (scaleTareRequest == true || (autoTare && scale_tare_counter >= 20))  | ||||||
|         scale_tare_counter = 0; |         { | ||||||
|       } |           Serial.println("Re-Tare scale"); | ||||||
|  |           oledShowMessage("TARE Scale"); | ||||||
|  |           vTaskDelay(pdMS_TO_TICKS(1000)); | ||||||
|  |           scale.tare(); | ||||||
|  |           resetWeightFilter(); // Reset filter after manual tare | ||||||
|  |           vTaskDelay(pdMS_TO_TICKS(1000)); | ||||||
|  |           oledShowWeight(0); | ||||||
|  |           scaleTareRequest = false; | ||||||
|  |           scale_tare_counter = 0; | ||||||
|  |           weight = 0; // Reset global weight variable after tare | ||||||
|  |         } | ||||||
|  |  | ||||||
|       // Waage manuell Taren |         // Get raw weight reading | ||||||
|       if (scaleTareRequest == true)  |         float rawWeight = scale.get_units(); | ||||||
|       { |          | ||||||
|         Serial.println("Re-Tare scale"); |         // Process weight with stabilization | ||||||
|         oledShowMessage("TARE Scale"); |         int16_t stabilizedWeight = processWeightReading(rawWeight); | ||||||
|         vTaskDelay(pdMS_TO_TICKS(1000)); |          | ||||||
|         scale.tare(); |         // Update global weight variable only if it changed significantly (for API actions) | ||||||
|         vTaskDelay(pdMS_TO_TICKS(1000)); |         if (stabilizedWeight != weight) { | ||||||
|         oledShowWeight(0); |           weight = stabilizedWeight; | ||||||
|         scaleTareRequest = false; |         } | ||||||
|       } |          | ||||||
|  |         // Prüfen ob die Waage korrekt genullt ist | ||||||
|  |         // Abweichung von 2g ignorieren | ||||||
|  |         if (autoTare && (rawWeight > 2 && rawWeight < 7) || rawWeight < -2) | ||||||
|  |         { | ||||||
|  |           scale_tare_counter++; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |           scale_tare_counter = 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|       // Only update weight if median changed more than 1 |         // Debug output for monitoring (can be removed in production) | ||||||
|       int16_t newWeight = round(scale.get_units()); |         static unsigned long lastDebugTime = 0; | ||||||
|       if(abs(weight-newWeight) > 1){ |         if (currentTime - lastDebugTime > 2000) { // Print every 2 seconds | ||||||
|         weight = newWeight; |           lastDebugTime = currentTime; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         lastMeasurementTime = currentTime; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     vTaskDelay(pdMS_TO_TICKS(100)); |     vTaskDelay(pdMS_TO_TICKS(10)); // Shorter delay for more responsive loop | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -123,7 +255,9 @@ void start_scale(bool touchSensorConnected) { | |||||||
|  |  | ||||||
|   scale.set_scale(calibrationValue); |   scale.set_scale(calibrationValue); | ||||||
|   //vTaskDelay(pdMS_TO_TICKS(5000)); |   //vTaskDelay(pdMS_TO_TICKS(5000)); | ||||||
|   //scale.tare(); |  | ||||||
|  |   // Initialize weight stabilization filter | ||||||
|  |   resetWeightFilter(); | ||||||
|  |  | ||||||
|   // Display Gewicht |   // Display Gewicht | ||||||
|   oledShowWeight(0); |   oledShowWeight(0); | ||||||
| @@ -209,6 +343,7 @@ uint8_t calibrate_scale() { | |||||||
|       oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight"); |       oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight"); | ||||||
|  |  | ||||||
|       scale.set_scale(newCalibrationValue); |       scale.set_scale(newCalibrationValue); | ||||||
|  |       resetWeightFilter(); // Reset filter after calibration | ||||||
|       for (uint16_t i = 0; i < 2000; i++) { |       for (uint16_t i = 0; i < 2000; i++) { | ||||||
|         yield(); |         yield(); | ||||||
|         vTaskDelay(pdMS_TO_TICKS(1)); |         vTaskDelay(pdMS_TO_TICKS(1)); | ||||||
|   | |||||||
| @@ -9,6 +9,13 @@ uint8_t start_scale(bool touchSensorConnected); | |||||||
| uint8_t calibrate_scale(); | uint8_t calibrate_scale(); | ||||||
| uint8_t tareScale(); | uint8_t tareScale(); | ||||||
|  |  | ||||||
|  | // Weight stabilization functions | ||||||
|  | void resetWeightFilter(); | ||||||
|  | float calculateMovingAverage(); | ||||||
|  | float applyLowPassFilter(float newValue); | ||||||
|  | int16_t processWeightReading(float rawWeight); | ||||||
|  | int16_t getFilteredDisplayWeight(); | ||||||
|  |  | ||||||
| extern HX711 scale; | extern HX711 scale; | ||||||
| extern int16_t weight; | extern int16_t weight; | ||||||
| extern uint8_t weigthCouterToApi; | extern uint8_t weigthCouterToApi; | ||||||
|   | |||||||
| @@ -81,7 +81,9 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp | |||||||
|         else if (doc["type"] == "scale") { |         else if (doc["type"] == "scale") { | ||||||
|             uint8_t success = 0; |             uint8_t success = 0; | ||||||
|             if (doc["payload"] == "tare") { |             if (doc["payload"] == "tare") { | ||||||
|                 success = tareScale(); |                 scaleTareRequest = true; | ||||||
|  |                 success = 1; | ||||||
|  |                 //success = tareScale(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (doc["payload"] == "calibrate") { |             if (doc["payload"] == "calibrate") { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user