Compare commits

..

2 Commits

22 changed files with 703 additions and 4625 deletions

1
.gitignore vendored
View File

@@ -32,7 +32,6 @@ test/README
data/* data/*
!data/ !data/
!data/.gitkeep !data/.gitkeep
# important
html/bambu_credentials.json html/bambu_credentials.json
html/spoolman_url.json html/spoolman_url.json
_local/* _local/*

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,6 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Filamentdaten auf NFC-Tags schreiben. - Filamentdaten auf NFC-Tags schreiben.
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool) - Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool)
- Ermöglicht automatische Spulenerkennung im AMS - Ermöglicht automatische Spulenerkennung im AMS
- **Hersteller Tag Unterstützung:** Automatische Erstellung von Spoolman-Einträgen aus Hersteller NFC-Tags ([Mehr erfahren](README_ManufacturerTags_DE.md))
- **Bambulab AMS-Integration:** - **Bambulab AMS-Integration:**
- Anzeige der aktuellen AMS-Fachbelegung. - Anzeige der aktuellen AMS-Fachbelegung.
- Zuordnung von Filamenten zu AMS-Slots. - Zuordnung von Filamenten zu AMS-Slots.
@@ -40,35 +39,8 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Unterstützt das Spoolman Octoprint Plugin - Unterstützt das Spoolman Octoprint Plugin
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee ### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
## Hersteller Tags Unterstützung
🎉 **Aufregende Neuigkeiten!** FilaMan unterstützt jetzt **Hersteller Tags** - NFC-Tags, die direkt von Filament-Herstellern vorprogrammiert geliefert werden!
### Erster Hersteller-Partner: RecyclingFabrik
Wir freuen uns anzukündigen, dass [**RecyclingFabrik**](https://www.recyclingfabrik.de) der **erste Filament-Hersteller** sein wird, der FilaMan unterstützt, indem sie NFC-Tags im FilaMan-Format auf ihren Spulen anbieten!
**Demnächst verfügbar:** RecyclingFabrik-Spulen werden NFC-Tags enthalten, die sich automatisch in Ihr FilaMan-System integrieren, manuelle Einrichtung überflüssig machen und perfekte Kompatibilität gewährleisten.
### Wie Hersteller Tags funktionieren
Wenn Sie zum ersten Mal einen Hersteller NFC-Tag scannen:
1. **Automatische Markenerkennung:** FilaMan erkennt den Hersteller und erstellt die Marke in Spoolman
2. **Filament-Typ Erstellung:** Alle Materialspezifikationen werden automatisch hinzugefügt
3. **Spulen-Registrierung:** Ihre spezifische Spule wird mit korrektem Gewicht und Spezifikationen registriert
4. **Zukünftige Schnellerkennung:** Nachfolgende Scans verwenden Fast-Path-Erkennung für sofortige Gewichtsmessung
**Für detaillierte technische Informationen:** [Hersteller Tags Dokumentation](README_ManufacturerTags_DE.md)
### Vorteile für Benutzer
-**Null manuelle Einrichtung** - Einfach scannen und wiegen
-**Perfekte Datengenauigkeit** - Hersteller-verifizierte Spezifikationen
-**Sofortige Integration** - Nahtlose Spoolman-Kompatibilität
-**Zukunftssicher** - Tags funktionieren mit jedem FilaMan-kompatiblen System
## Detaillierte Funktionalität ## Detaillierte Funktionalität
### ESP32-Funktionalität ### ESP32-Funktionalität
@@ -90,7 +62,7 @@ Wenn Sie zum ersten Mal einen Hersteller NFC-Tag scannen:
- **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306. - **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306.
[Amazon Link](https://amzn.to/445aaa9) [Amazon Link](https://amzn.to/445aaa9)
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations. - **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
[Amazon Link](https://amzn.eu/d/gy9vaBX) [Amazon Link](https://amzn.to/4iO6CO4)
- **NFC Tags NTAG213 NTAG215:** RFID Tag - **NFC Tags NTAG213 NTAG215:** RFID Tag
[Amazon Link](https://amzn.to/3E071xO) [Amazon Link](https://amzn.to/3E071xO)
- **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch - **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch

View File

@@ -31,7 +31,6 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Write filament data to NFC tags. - Write filament data to NFC tags.
- uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool) - uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool)
- so you can use it with automatic Spool detection in AMS - so you can use it with automatic Spool detection in AMS
- **Manufacturer Tag Support:** Automatic creation of Spoolman entries from manufacturer NFC tags ([Learn more](README_ManufacturerTags_EN.md))
- **Bambulab AMS Integration:** - **Bambulab AMS Integration:**
- Display current AMS tray contents. - Display current AMS tray contents.
- Assign filaments to AMS slots. - Assign filaments to AMS slots.
@@ -44,35 +43,8 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Supports Spoolman Octoprint Plugin - Supports Spoolman Octoprint Plugin
### If you want to support my work, i would be happy to get a coffe ### If you want to support my work, i would be happy to get a coffe
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
## Manufacturer Tags Support
🎉 **Exciting News!** FilaMan now supports **Manufacturer Tags** - NFC tags that come pre-programmed directly from filament manufacturers!
### First Manufacturer Partner: RecyclingFabrik
We're thrilled to announce that [**RecyclingFabrik**](https://www.recyclingfabrik.de) will be the **first filament manufacturer** to support FilaMan by offering NFC tags in the FilaMan format on their spools!
**Coming Soon:** RecyclingFabrik spools will include NFC tags that automatically integrate with your FilaMan system, eliminating manual setup and ensuring perfect compatibility.
### How Manufacturer Tags Work
When you scan a manufacturer NFC tag for the first time:
1. **Automatic Brand Detection:** FilaMan recognizes the manufacturer and creates the brand in Spoolman
2. **Filament Type Creation:** All material specifications are automatically added
3. **Spool Registration:** Your specific spool is registered with proper weight and specifications
4. **Future Fast Recognition:** Subsequent scans use fast-path detection for instant weight measurement
**For detailed technical information:** [Manufacturer Tags Documentation](README_ManufacturerTags_EN.md)
### Benefits for Users
-**Zero Manual Setup** - Just scan and weigh
-**Perfect Data Accuracy** - Manufacturer-verified specifications
-**Instant Integration** - Seamless Spoolman compatibility
-**Future-Proof** - Tags work with any FilaMan-compatible system
## Detailed Functionality ## Detailed Functionality
### ESP32 Functionality ### ESP32 Functionality
@@ -94,7 +66,7 @@ When you scan a manufacturer NFC tag for the first time:
- **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306. - **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306.
[Amazon Link](https://amzn.to/445aaa9) [Amazon Link](https://amzn.to/445aaa9)
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations. - **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
[Amazon Link](https://amzn.eu/d/gy9vaBX) [Amazon Link](https://amzn.to/4iO6CO4)
- **NFC Tags NTAG213 NTAG215:** RFID Tag - **NFC Tags NTAG213 NTAG215:** RFID Tag
[Amazon Link](https://amzn.to/3E071xO) [Amazon Link](https://amzn.to/3E071xO)
- **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch - **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch

View File

@@ -1,159 +0,0 @@
# Hersteller Tags - Deutsche Dokumentation
## Überblick
Das FilaMan NFC-System unterstützt **Hersteller Tags**, die es Filament-Produzenten ermöglichen, standardisierte NFC-Tags für ihre Produkte zu erstellen. Beim Scannen dieser Tags werden automatisch die notwendigen Einträge in Spoolman (Marke, Filament-Typ und Spule) erstellt, ohne dass eine manuelle Einrichtung erforderlich ist.
## Funktionsweise der Hersteller Tags
### Ablauf
1. **Tag-Erkennung**: Wenn ein Tag ohne `sm_id` gescannt wird, prüft das System auf Hersteller Tag Format
2. **Marken-Erstellung/Suche**: Das System sucht die Marke in Spoolman oder erstellt sie, falls sie nicht existiert
3. **Filament-Typ-Erstellung/Suche**: Der Filament-Typ wird basierend auf Marke, Material und Spezifikationen erstellt oder gefunden
4. **Spulen-Erstellung**: Ein neuer Spulen-Eintrag wird automatisch mit der Tag-UID als Referenz erstellt
5. **Tag-Update**: Der Tag wird mit der neuen Spoolman Spulen-ID (`sm_id`) aktualisiert
### Warum Hersteller Tags verwenden?
- **Automatische Integration**: Keine manuelle Dateneingabe erforderlich
- **Standardisiertes Format**: Konsistente Produktinformationen verschiedener Hersteller
- **Lagerverwaltung**: Automatische Erstellung vollständiger Spoolman-Einträge
- **Rückverfolgbarkeit**: Direkte Verbindung zwischen physischem Produkt und digitalem Inventar
## Tag-Format Spezifikation
### JSON-Struktur
Hersteller Tags müssen eine JSON-Payload mit spezifischen Feldern enthalten, die **kurze Schlüssel** verwenden, um die Tag-Größe zu minimieren:
```json
{
"b": "Marke/Hersteller Name",
"an": "Artikelnummer",
"t": "Filament Typ (PLA, PETG, etc)",
"c": "Filament Farbe ohne # (FF5733)",
"mc": "Optional Mehrfarben-Filament Farben ohne # (FF0000,00FF00,0000FF)",
"mcd": "Optional Mehrfarben-Richtung als Wort (coaxial, longitudinal)",
"cn": "Farbname (rot, Blaubeere, Arktisches Blau)",
"et": "Extruder Temp als Zahl in C° (230)",
"bt": "Bett Temp als Zahl in C° (60)",
"di": "Durchmesser als Float (1.75)",
"de": "Dichte als Float (1.24)",
"sw": "Leeres Spulengewicht als Zahl in g (180)",
"u": "URL zum Filament mit der Artikelnummer"
}
```
### Pflichtfelder
- **`b`** (brand): Hersteller/Markenname
- **`an`** (article number): Eindeutige Produktkennung
- **`t`** (type): Materialtyp (PLA, PETG, ABS, etc.)
- **`c`** (color): Hex-Farbcode ohne #
- **`cn`** (color name): Lesbare Farbbezeichnung
- **`et`** (extruder temp): Empfohlene Extruder-Temperatur in Celsius
- **`bt`** (bed temp): Empfohlene Bett-Temperatur in Celsius
- **`di`** (diameter): Filamentdurchmesser in mm
- **`de`** (density): Materialdichte in g/cm³
- **`sw`** (spool weight): Leeres Spulengewicht in Gramm
### Optionale Felder
- **`mc`** (multicolor): Komma-getrennte Hex-Farben für Mehrfarben-Filamente
- **`mcd`** (multicolor direction): Richtung für Mehrfarben (coaxial, longitudinal)
- **`u`** (url): Produkt-URL mit direktem Link zum Artikel zB für Nachbestellung
### Beispiel Tag
```json
{"b":"Recycling Fabrik","an":"FX1_PETG-S175-1000-DAEM00055","t":"PETG","c":"FF5733","cn":"Lebendiges Orange","et":"230","bt":"70","di":"1.75","de":"1.24","sw":"180","u":"https://www.recyclingfabrik.com/search?q="}
```
## Implementierungsrichtlinien
### Für Hersteller
1. **Tag-Kodierung**: NDEF-Format mit MIME-Typ `application/json` verwenden
2. **Datenminimierung**: Kompaktes JSON-Format für Tag-Größenbegrenzungen nutzen
3. **Qualitätskontrolle**: Sicherstellen, dass alle Pflichtfelder vorhanden und korrekt formatiert sind
4. **Testen**: Tags vor der Produktion mit dem FilaMan-System verifizieren
### Tag-Größe Überlegungen
- **NTAG213**: 144 Bytes Nutzerdaten (geeignet für einfache Tags)
- **NTAG215**: 504 Bytes Nutzerdaten (empfohlen für umfassende Daten)
- **NTAG216**: 888 Bytes Nutzerdaten (maximale Kompatibilität)
### Best Practices
- Markennamen über alle Produkte hinweg konsistent halten
- Standardisierte Materialtypnamen verwenden (PLA, PETG, ABS, etc.)
- Genaue Temperaturempfehlungen angeben
- Aussagekräftige Farbnamen für bessere Benutzererfahrung verwenden
- Tags vor Massenproduktion mit dem FilaMan-System testen
## System-Integration
### Spoolman Datenbankstruktur
Bei der Verarbeitung eines Hersteller Tags erstellt das System:
1. **Lieferanten-Eintrag**: Markeninformationen in der Spoolman Lieferanten-Datenbank
2. **Filament-Eintrag**: Materialspezifikationen und Eigenschaften
3. **Spulen-Eintrag**: Einzelne Spule mit Gewicht und NFC-Tag-Referenz
### Fast-Path Erkennung
Sobald ein Tag verarbeitet und mit `sm_id` aktualisiert wurde, nutzt er das Fast-Path-Erkennungssystem für schnelle nachfolgende Scans.
## Fehlerbehebung
### Häufige Probleme
- **Tag zu klein**: NTAG215 oder NTAG216 für größere JSON-Payloads verwenden
- **Fehlende Felder**: Sicherstellen, dass alle Pflichtfelder vorhanden sind
- **Ungültiges Format**: JSON-Syntax und Feldtypen überprüfen
- **Spoolman-Verbindung**: Sicherstellen, dass FilaMan mit der Spoolman API verbinden kann
### Validierung
Das System validiert:
- JSON-Format Korrektheit
- Vorhandensein der Pflichtfelder
- Datentyp-Konformität
- Tag-Größe Kompatibilität
## Technische Details
### Verarbeitungsalgorithmus
1. Tag-Scan erkennt kein `sm_id` Feld
2. System prüft auf `b` (Marke) und `an` (Artikelnummer) Felder
3. `checkVendor()` erstellt oder findet Marke in Spoolman
4. `checkFilament()` erstellt oder findet Filament-Typ
5. `createSpool()` erstellt neuen Spulen-Eintrag
6. Tag wird mit neuer `sm_id` aktualisiert
### Fehlerbehandlung
- Graceful Fallback bei Netzwerkproblemen
- Detaillierte Protokollierung für Debugging
- Benutzer-Feedback bei fehlgeschlagenen Operationen
- Wiederholungsmechanismen für temporäre Fehler
### Systemverhalten
#### Bei fehlendem sm_id:
- System prüft auf `b` (brand) und `an` (artnr) Felder
- Falls vorhanden → Hersteller Tag erkannt
- Automatische Erstellung von Lieferant, Filament und Spule in Spoolman
- Tag wird mit neuer `sm_id` beschrieben
#### Bei vorhandenem sm_id:
- Fast-Path Erkennung für bekannte Spulen
- Sofortige Gewichtsmessung ohne vollständige Tag-Analyse
- Optimierte Performance für häufig verwendete Tags
Dieses System ermöglicht eine nahtlose Integration von Hersteller-Filamentprodukten in das FilaMan-Ökosystem unter Beibehaltung von Datenkonsistenz und Benutzererfahrung.

View File

@@ -1,145 +0,0 @@
# Manufacturer Tags - English Documentation
## Overview
The FilaMan NFC system supports **Manufacturer Tags** that allow filament producers to create standardized NFC tags for their products. When scanned, these tags automatically create the necessary entries in Spoolman (brand, filament type, and spool) without requiring manual setup.
## How Manufacturer Tags Work
### Process Flow
1. **Tag Detection**: When a tag without `sm_id` is scanned, the system checks for manufacturer tag format
2. **Brand Creation/Lookup**: The system searches for the brand in Spoolman or creates it if it doesn't exist
3. **Filament Type Creation/Lookup**: The filament type is created or found based on brand, material, and specifications
4. **Spool Creation**: A new spool entry is automatically created with the tag's UID as reference
5. **Tag Update**: The tag is updated with the new Spoolman spool ID (`sm_id`)
### Why Use Manufacturer Tags?
- **Automatic Integration**: No manual data entry required
- **Standardized Format**: Consistent product information across different manufacturers
- **Inventory Management**: Automatic creation of complete Spoolman entries
- **Traceability**: Direct link between physical product and digital inventory
## Tag Format Specification
### JSON Structure
Manufacturer tags must contain a JSON payload with specific fields using **short keys** to minimize tag size:
```json
{
"b": "Brand/Vendor Name",
"an": "Article Number",
"t": "Filament Type (PLA, PETG, etc)",
"c": "Filament Color without # (FF5733)",
"mc": "Optional Multicolor Filament Colors without # (FF0000,00FF00,0000FF)",
"mcd": "Optional Multicolor Direction as Word (coaxial, longitudinal)",
"cn": "Color Name (red, Blueberry, Arctic Blue)",
"et": "Extruder Temp as Number in C° (230)",
"bt": "Bed Temp as Number in C° (60)",
"di": "Diameter as Float (1.75)",
"de": "Density as Float (1.24)",
"sw": "Empty Spool Weight as Number in g (180)",
"u": "URL to get the Filament with the Article Number"
}
```
### Required Fields
- **`b`** (brand): Manufacturer/brand name
- **`an`** (article number): Unique product identifier
- **`t`** (type): Material type (PLA, PETG, ABS, etc.)
- **`c`** (color): Hex color code without #
- **`cn`** (color name): Human-readable color name
- **`et`** (extruder temp): Recommended extruder temperature in Celsius
- **`bt`** (bed temp): Recommended bed temperature in Celsius
- **`di`** (diameter): Filament diameter in mm
- **`de`** (density): Material density in g/cm³
- **`sw`** (spool weight): Empty spool weight in grams
### Optional Fields
- **`mc`** (multicolor): Comma-separated hex colors for multicolor filaments
- **`mcd`** (multicolor direction): Direction for multicolor (coaxial, longitudinal)
- **`u`** (url): Product URL with direct link to the article e.g. for reordering
### Example Tag
```json
{"b":"Recycling Fabrik","an":"FX1_PETG-S175-1000-DAEM00055","t":"PETG","c":"FF5733","cn":"Vibrant Orange","et":"230","bt":"70","di":"1.75","de":"1.24","sw":"180","u":"https://www.recyclingfabrik.com/search?q="}
```
## Implementation Guidelines
### For Manufacturers
1. **Tag Encoding**: Use NDEF format with MIME type `application/json`
2. **Data Minimization**: Use the compact JSON format to fit within tag size limits
3. **Quality Control**: Ensure all required fields are present and correctly formatted
4. **Testing**: Verify tags work with FilaMan system before production
### Tag Size Considerations
- **NTAG213**: 144 bytes user data (suitable for basic tags)
- **NTAG215**: 504 bytes user data (recommended for comprehensive data)
- **NTAG216**: 888 bytes user data (maximum compatibility)
### Best Practices
- Keep brand names consistent across all products
- Use standardized material type names (PLA, PETG, ABS, etc.)
- Provide accurate temperature recommendations
- Include meaningful color names for user experience
- Test tags with the FilaMan system before mass production
## System Integration
### Spoolman Database Structure
When a manufacturer tag is processed, the system creates:
1. **Vendor Entry**: Brand information in Spoolman vendor database
2. **Filament Entry**: Material specifications and properties
3. **Spool Entry**: Individual spool with weight and NFC tag reference
### Fast-Path Recognition
Once a tag is processed and updated with `sm_id`, it uses the fast-path recognition system for quick subsequent scans.
## Troubleshooting
### Common Issues
- **Tag Too Small**: Use NTAG215 or NTAG216 for larger JSON payloads
- **Missing Fields**: Ensure all required fields are present
- **Invalid Format**: Verify JSON syntax and field types
- **Spoolman Connection**: Ensure FilaMan can connect to Spoolman API
### Validation
The system validates:
- JSON format correctness
- Required field presence
- Data type compliance
- Tag size compatibility
## Technical Details
### Processing Algorithm
1. Tag scan detects no `sm_id` field
2. System checks for `b` (brand) and `an` (article number) fields
3. `checkVendor()` creates or finds brand in Spoolman
4. `checkFilament()` creates or finds filament type
5. `createSpool()` creates new spool entry
6. Tag is updated with new `sm_id`
### Error Handling
- Graceful fallback for network issues
- Detailed logging for debugging
- User feedback for failed operations
- Retry mechanisms for temporary failures
This system enables seamless integration of manufacturer filament products into the FilaMan ecosystem while maintaining data consistency and user experience.

View File

@@ -7,7 +7,6 @@ let heartbeatTimer = null;
let lastHeartbeatResponse = Date.now(); let lastHeartbeatResponse = Date.now();
const HEARTBEAT_TIMEOUT = 20000; const HEARTBEAT_TIMEOUT = 20000;
let reconnectTimer = null; let reconnectTimer = null;
let spoolDetected = false;
// WebSocket Funktionen // WebSocket Funktionen
function startHeartbeat() { function startHeartbeat() {
@@ -509,15 +508,12 @@ function updateNfcStatusIndicator(data) {
if (data.found === 0) { if (data.found === 0) {
// Kein NFC Tag gefunden // Kein NFC Tag gefunden
indicator.className = 'status-circle'; indicator.className = 'status-circle';
spoolDetected = false;
} else if (data.found === 1) { } else if (data.found === 1) {
// NFC Tag erfolgreich gelesen // NFC Tag erfolgreich gelesen
indicator.className = 'status-circle success'; indicator.className = 'status-circle success';
spoolDetected = true;
} else { } else {
// Fehler beim Lesen // Fehler beim Lesen
indicator.className = 'status-circle error'; indicator.className = 'status-circle error';
spoolDetected = true;
} }
} }
@@ -578,7 +574,7 @@ function updateNfcData(data) {
`; `;
// Spoolman ID anzeigen // Spoolman ID anzeigen
html += `<p><strong>Spoolman ID:</strong> ${data.sm_id} (<a href="${spoolmanUrl}/spool/show/${data.sm_id}">Open in Spoolman</a>)</p>`; html += `<p><strong>Spoolman ID:</strong> ${data.sm_id || 'No Spoolman ID'}</p>`;
} }
else if(data.location) else if(data.location)
{ {
@@ -622,7 +618,6 @@ function updateNfcData(data) {
} }
function writeNfcTag() { function writeNfcTag() {
if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
const selectedText = document.getElementById("selected-filament").textContent; const selectedText = document.getElementById("selected-filament").textContent;
if (selectedText === "Please choose...") { if (selectedText === "Please choose...") {
alert('Please select a Spool first.'); alert('Please select a Spool first.');
@@ -671,11 +666,9 @@ function writeNfcTag() {
} else { } else {
alert('Not connected to Server. Please check connection.'); alert('Not connected to Server. Please check connection.');
} }
}
} }
function writeLocationNfcTag() { function writeLocationNfcTag() {
if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
const selectedText = document.getElementById("locationSelect").value; const selectedText = document.getElementById("locationSelect").value;
if (selectedText === "Please choose...") { if (selectedText === "Please choose...") {
alert('Please select a location first.'); alert('Please select a location first.');
@@ -686,7 +679,6 @@ function writeLocationNfcTag() {
location: String(selectedText) location: String(selectedText)
}; };
if (socket?.readyState === WebSocket.OPEN) { if (socket?.readyState === WebSocket.OPEN) {
const writeButton = document.getElementById("writeLocationNfcButton"); const writeButton = document.getElementById("writeLocationNfcButton");
writeButton.classList.add("writing"); writeButton.classList.add("writing");
@@ -699,7 +691,6 @@ function writeLocationNfcTag() {
} else { } else {
alert('Not connected to Server. Please check connection.'); alert('Not connected to Server. Please check connection.');
} }
}
} }
function handleWriteNfcTagResponse(success) { function handleWriteNfcTagResponse(success) {

View File

@@ -56,7 +56,7 @@
<div class="update-options"> <div class="update-options">
<div class="update-section"> <div class="update-section">
<h2>1) Firmware Update</h2> <h2>Firmware Update</h2>
<p>Upload a new firmware file (upgrade_filaman_firmware_*.bin)</p> <p>Upload a new firmware file (upgrade_filaman_firmware_*.bin)</p>
<div class="update-form"> <div class="update-form">
<form id="firmwareForm" enctype='multipart/form-data' data-type="firmware"> <form id="firmwareForm" enctype='multipart/form-data' data-type="firmware">
@@ -67,7 +67,7 @@
</div> </div>
<div class="update-section"> <div class="update-section">
<h2>2) Webpage Update</h2> <h2>Webpage Update</h2>
<p>Upload a new webpage file (upgrade_filaman_website_*.bin)</p> <p>Upload a new webpage file (upgrade_filaman_website_*.bin)</p>
<div class="update-form"> <div class="update-form">
<form id="webpageForm" enctype='multipart/form-data' data-type="webpage"> <form id="webpageForm" enctype='multipart/form-data' data-type="webpage">

View File

@@ -9,8 +9,8 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common] [common]
version = "2.0.0-beta14" version = "1.5.7"
to_old_version = "1.5.10" to_old_version = "1.5.0"
## ##
[env:esp32dev] [env:esp32dev]
@@ -23,7 +23,10 @@ monitor_speed = 115200
lib_deps = lib_deps =
tzapu/WiFiManager @ ^2.0.17 tzapu/WiFiManager @ ^2.0.17
https://github.com/me-no-dev/ESPAsyncWebServer.git#master https://github.com/me-no-dev/ESPAsyncWebServer.git#master
#me-no-dev/AsyncTCP @ ^1.1.1
https://github.com/esphome/AsyncTCP.git https://github.com/esphome/AsyncTCP.git
#mathieucarbou/ESPAsyncWebServer @ ^3.6.0
#esp32async/AsyncTCP @ ^3.3.5
bogde/HX711 @ ^0.7.5 bogde/HX711 @ ^0.7.5
adafruit/Adafruit SSD1306 @ ^2.5.13 adafruit/Adafruit SSD1306 @ ^2.5.13
adafruit/Adafruit GFX Library @ ^1.11.11 adafruit/Adafruit GFX Library @ ^1.11.11
@@ -33,6 +36,7 @@ lib_deps =
digitaldragon/SSLClient @ ^1.3.2 digitaldragon/SSLClient @ ^1.3.2
; Enable SPIFFS upload ; Enable SPIFFS upload
#board_build.filesystem = spiffs
board_build.filesystem = littlefs board_build.filesystem = littlefs
; Update partition settings ; Update partition settings
board_build.partitions = partitions.csv board_build.partitions = partitions.csv

View File

@@ -14,39 +14,17 @@ 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:
# Get all tags sorted by version result = subprocess.run(['git', 'describe', '--tags', '--abbrev=0'],
result = subprocess.run(['git', 'tag', '-l', '--sort=-version:refname'],
capture_output=True, text=True) capture_output=True, text=True)
if result.returncode != 0: return result.stdout.strip()
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']):
# 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'
@@ -56,7 +34,6 @@ 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': []
@@ -78,9 +55,7 @@ def get_changes_from_git():
if commit: if commit:
category = categorize_commit(commit) category = categorize_commit(commit)
# Clean up commit message # Clean up commit message
clean_msg = re.sub(r'^(feat|fix|chore|docs|style|refactor|perf|test)(\(.*\))?!?:', '', commit).strip() 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) changes[category].append(clean_msg)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:

View File

@@ -4,11 +4,8 @@
#include "commonFS.h" #include "commonFS.h"
#include <Preferences.h> #include <Preferences.h>
#include "debug.h" #include "debug.h"
#include "scale.h"
#include "nfc.h"
#include <time.h>
volatile spoolmanApiStateType spoolmanApiState = API_IDLE;
volatile spoolmanApiStateType spoolmanApiState = API_INIT;
//bool spoolman_connected = false; //bool spoolman_connected = false;
String spoolmanUrl = ""; String spoolmanUrl = "";
bool octoEnabled = false; bool octoEnabled = false;
@@ -19,11 +16,7 @@ uint16_t remainingWeight = 0;
uint16_t createdVendorId = 0; // Store ID of newly created vendor uint16_t createdVendorId = 0; // Store ID of newly created vendor
uint16_t foundVendorId = 0; // Store ID of found vendor 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 createdSpoolId = 0; // Store ID of newly created spool
bool spoolmanConnected = false; bool spoolmanConnected = false;
bool spoolmanExtraFieldsChecked = false;
TaskHandle_t* apiTask;
struct SendToApiParams { struct SendToApiParams {
SpoolmanApiRequestType requestType; SpoolmanApiRequestType requestType;
@@ -31,10 +24,6 @@ struct SendToApiParams {
String spoolsUrl; String spoolsUrl;
String updatePayload; String updatePayload;
String octoToken; String octoToken;
// Weight update parameters for sequential execution
bool triggerWeightUpdate;
String spoolIdForWeight;
uint16_t weightValue;
}; };
JsonDocument fetchSingleSpoolInfo(int spoolId) { JsonDocument fetchSingleSpoolInfo(int spoolId) {
@@ -108,85 +97,36 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
void sendToApi(void *parameter) { void sendToApi(void *parameter) {
HEAP_DEBUG_MESSAGE("sendToApi begin"); HEAP_DEBUG_MESSAGE("sendToApi begin");
// Wait until API is IDLE
while(spoolmanApiState != API_IDLE){
vTaskDelay(100 / portTICK_PERIOD_MS);
yield();
}
spoolmanApiState = API_TRANSMITTING; spoolmanApiState = API_TRANSMITTING;
SendToApiParams* params = (SendToApiParams*)parameter; SendToApiParams* params = (SendToApiParams*)parameter;
// Extract values including weight update parameters // Extrahiere die Werte
SpoolmanApiRequestType requestType = params->requestType; SpoolmanApiRequestType requestType = params->requestType;
String httpType = params->httpType; String httpType = params->httpType;
String spoolsUrl = params->spoolsUrl; String spoolsUrl = params->spoolsUrl;
String updatePayload = params->updatePayload; String updatePayload = params->updatePayload;
String octoToken = params->octoToken; String octoToken = params->octoToken;
bool triggerWeightUpdate = params->triggerWeightUpdate;
String spoolIdForWeight = params->spoolIdForWeight;
uint16_t weightValue = params->weightValue;
// Retry mechanism with configurable parameters
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; HTTPClient http;
http.setReuse(false); http.setReuse(false);
http.setTimeout(HTTP_TIMEOUT_MS); // Set HTTP timeout
http.begin(spoolsUrl); http.begin(spoolsUrl);
http.addHeader("Content-Type", "application/json"); http.addHeader("Content-Type", "application/json");
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken); if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
// Execute HTTP request based on type int httpCode;
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
else if (httpType == "POST") httpCode = http.POST(updatePayload); else if (httpType == "POST") httpCode = http.POST(updatePayload);
else if (httpType == "GET") httpCode = http.GET(); else if (httpType == "GET") httpCode = http.GET();
else httpCode = http.PUT(updatePayload); else httpCode = http.PUT(updatePayload);
// Check if request was successful if (httpCode == HTTP_CODE_OK) {
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) { Serial.println("Spoolman erfolgreich aktualisiert");
responsePayload = http.getString();
success = true;
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();
}
// Process successful response
if (success) {
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, responsePayload); DeserializationError error = deserializeJson(doc, payload);
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());
@@ -199,8 +139,7 @@ void sendToApi(void *parameter) {
//oledShowMessage("Remaining: " + String(remaining_weight) + "g"); //oledShowMessage("Remaining: " + String(remaining_weight) + "g");
if(!octoEnabled){ if(!octoEnabled){
// TBD: Do not use Strings... // TBD: Do not use Strings...
//oledShowProgressBar(1, 1, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str()); oledShowProgressBar(1, 1, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
oledShowMessage("Remaining: " + String(remainingWeight) + "g");
remainingWeight = 0; remainingWeight = 0;
}else{ }else{
// ocoto is enabled, trigger octo update // ocoto is enabled, trigger octo update
@@ -215,8 +154,7 @@ void sendToApi(void *parameter) {
break; break;
case API_REQUEST_OCTO_SPOOL_UPDATE: case API_REQUEST_OCTO_SPOOL_UPDATE:
// TBD: Do not use Strings... // TBD: Do not use Strings...
//oledShowProgressBar(5, 5, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str()); oledShowProgressBar(5, 5, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
oledShowMessage("Remaining: " + String(remainingWeight) + "g");
remainingWeight = 0; remainingWeight = 0;
break; break;
case API_REQUEST_VENDOR_CREATE: case API_REQUEST_VENDOR_CREATE:
@@ -246,20 +184,6 @@ void sendToApi(void *parameter) {
Serial.println(foundFilamentId); Serial.println(foundFilamentId);
} }
break; break;
case API_REQUEST_FILAMENT_CREATE:
Serial.println("Filament successfully created!");
createdFilamentId = doc["id"].as<uint16_t>();
Serial.print("Created Filament ID: ");
Serial.println(createdFilamentId);
oledShowProgressBar(1, 1, "Filament", "Created!");
break;
case API_REQUEST_SPOOL_CREATE:
Serial.println("Spool successfully created!");
createdSpoolId = doc["id"].as<uint16_t>();
Serial.print("Created Spool ID: ");
Serial.println(createdSpoolId);
oledShowProgressBar(1, 1, "Spool", "Created!");
break;
} }
} }
doc.clear(); doc.clear();
@@ -267,8 +191,9 @@ void sendToApi(void *parameter) {
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, responsePayload); DeserializationError error = deserializeJson(doc, payload);
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());
@@ -281,81 +206,12 @@ void sendToApi(void *parameter) {
Serial.println(createdVendorId); Serial.println(createdVendorId);
oledShowProgressBar(1, 1, "Vendor", "Created!"); oledShowProgressBar(1, 1, "Vendor", "Created!");
break; break;
case API_REQUEST_FILAMENT_CREATE:
Serial.println("Filament successfully created!");
createdFilamentId = doc["id"].as<uint16_t>();
Serial.print("Created Filament ID: ");
Serial.println(createdFilamentId);
oledShowProgressBar(1, 1, "Filament", "Created!");
break;
case API_REQUEST_SPOOL_CREATE:
Serial.println("Spool successfully created!");
createdSpoolId = doc["id"].as<uint16_t>();
Serial.print("Created Spool ID: ");
Serial.println(createdSpoolId);
oledShowProgressBar(1, 1, "Spool", "Created!");
break;
default: default:
// Handle other create operations if needed // Handle other create operations if needed
break; break;
} }
} }
doc.clear(); doc.clear();
// Execute weight update if requested and tag update was successful
if (triggerWeightUpdate && requestType == API_REQUEST_SPOOL_TAG_ID_UPDATE && weightValue > 10) {
Serial.println("Executing weight update after successful tag update");
// Prepare weight update request
String weightUrl = spoolmanUrl + apiUrl + "/spool/" + spoolIdForWeight + "/measure";
JsonDocument weightDoc;
weightDoc["weight"] = weightValue;
String weightPayload;
serializeJson(weightDoc, weightPayload);
Serial.print("Weight update URL: ");
Serial.println(weightUrl);
Serial.print("Weight update payload: ");
Serial.println(weightPayload);
// Execute weight update
HTTPClient weightHttp;
weightHttp.setReuse(false);
weightHttp.setTimeout(HTTP_TIMEOUT_MS);
weightHttp.begin(weightUrl);
weightHttp.addHeader("Content-Type", "application/json");
int weightHttpCode = weightHttp.PUT(weightPayload);
if (weightHttpCode == HTTP_CODE_OK) {
Serial.println("Weight update successful");
String weightResponse = weightHttp.getString();
JsonDocument weightResponseDoc;
DeserializationError weightError = deserializeJson(weightResponseDoc, weightResponse);
if (!weightError) {
remainingWeight = weightResponseDoc["remaining_weight"].as<uint16_t>();
Serial.print("Updated weight: ");
Serial.println(remainingWeight);
if (!octoEnabled) {
oledShowProgressBar(1, 1, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
remainingWeight = 0;
} else {
sendOctoUpdate = true;
}
}
weightResponseDoc.clear();
} else {
Serial.print("Weight update failed with HTTP code: ");
Serial.println(weightHttpCode);
oledShowProgressBar(1, 1, "Failure!", "Weight update");
}
weightHttp.end();
weightDoc.clear();
}
} else { } else {
switch(requestType){ switch(requestType){
case API_REQUEST_SPOOL_WEIGHT_UPDATE: case API_REQUEST_SPOOL_WEIGHT_UPDATE:
@@ -369,32 +225,17 @@ 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;
case API_REQUEST_FILAMENT_CREATE:
oledShowProgressBar(1, 1, "Failure!", "Filament create");
createdFilamentId = 0; // Set to 0 to indicate error
break;
case API_REQUEST_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));
// TBD: really required?
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
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
@@ -422,8 +263,7 @@ bool updateSpoolTagId(String uidString, const char* payload) {
return false; return false;
} }
String spoolId = doc["sm_id"].as<String>(); String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + doc["sm_id"].as<String>();
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -448,25 +288,21 @@ bool updateSpoolTagId(String uidString, const char* payload) {
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
// Add weight update parameters for sequential execution // Erstelle die Task
params->triggerWeightUpdate = (weight > 10);
params->spoolIdForWeight = spoolId;
params->weightValue = weight;
// Erstelle die Task mit erhöhter Stackgröße für zusätzliche HTTP-Anfrage
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name "SendToApiTask", // Task-Name
8192, // Erhöhte Stackgröße für zusätzliche HTTP-Anfrage 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
apiTask // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
// Update Spool weight now handled sequentially in sendToApi task // Update Spool weight
// to prevent parallel API access issues //TBD: how to handle this with spool and locatin tags? Also potential parallel access again
//if (weight > 10) updateSpoolWeight(doc["sm_id"].as<String>(), weight);
return true; return true;
} }
@@ -505,7 +341,7 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
apiTask // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
@@ -542,8 +378,7 @@ uint8_t updateSpoolLocation(String spoolId, String location){
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
if(spoolmanApiState == API_IDLE){
if(apiTask == nullptr){
// Erstelle die Task // Erstelle die Task
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
@@ -551,8 +386,9 @@ uint8_t updateSpoolLocation(String spoolId, String location){
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
apiTask // Task-Handle NULL // Task-Handle (nicht benötigt)
); );
}else{ }else{
Serial.println("Not spawning new task, API still active!"); Serial.println("Not spawning new task, API still active!");
} }
@@ -597,7 +433,7 @@ bool updateSpoolOcto(int spoolId) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
apiTask // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
@@ -650,20 +486,77 @@ bool updateSpoolBambuData(String payload) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
apiTask // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
return true; return true;
} }
// #### Brand Filament // #### Filament Fabrik
uint16_t createVendor(const JsonDocument& payload) { uint16_t checkVendor(String vendor) {
oledShowProgressBar(2, 5, "New Brand", "Create new Vendor"); // Check if vendor exists using task system
foundVendorId = 0; // Reset previous value
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor?name=" + vendor;
Serial.print("Check vendor with URL: ");
Serial.println(spoolsUrl);
SendToApiParams* params = new SendToApiParams();
if (params == nullptr) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return 0;
}
params->requestType = API_REQUEST_VENDOR_CHECK;
params->httpType = "GET";
params->spoolsUrl = spoolsUrl;
params->updatePayload = ""; // Empty for GET request
// Check if API is idle before creating task
if(spoolmanApiState == API_IDLE){
// Erstelle die Task
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
} else {
Serial.println("Not spawning new task, API still active!");
delete params;
return 0;
}
// Wait for task completion
while(spoolmanApiState != API_IDLE) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// Check if vendor was found
if (foundVendorId == 0) {
Serial.println("Vendor not found, creating new vendor...");
uint16_t vendorId = createVendor(vendor);
if (vendorId == 0) {
Serial.println("Failed to create vendor, returning 0.");
return 0; // Failed to create vendor
} else {
Serial.println("Vendor created with ID: " + String(vendorId));
checkFilament(vendorId);
return vendorId;
}
} else {
Serial.println("Vendor found: " + vendor);
Serial.print("Vendor ID: ");
Serial.println(foundVendorId);
return foundVendorId;
}
}
uint16_t createVendor(String vendor) {
// Create new vendor in Spoolman database using task system // Create new vendor in Spoolman database using task system
// Note: Due to async nature, the ID will be stored in createdVendorId global variable // Note: Due to async nature, the ID will be stored in createdVendorId global variable
// Note: This function assumes that the caller has already ensured API is IDLE createdVendorId = 0; // Reset previous value
createdVendorId = 65535; // Reset previous value
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor"; String spoolsUrl = spoolmanUrl + apiUrl + "/vendor";
Serial.print("Create vendor with URL: "); Serial.print("Create vendor with URL: ");
@@ -671,24 +564,10 @@ uint16_t createVendor(const JsonDocument& payload) {
// Create JSON payload for vendor creation // Create JSON payload for vendor creation
JsonDocument vendorDoc; JsonDocument vendorDoc;
vendorDoc["name"] = payload["b"].as<String>(); vendorDoc["name"] = vendor;
vendorDoc["comment"] = "automatically generated";
// Extract domain from URL if present, otherwise use brand name vendorDoc["empty_spool_weight"] = 180;
String externalId = ""; vendorDoc["external_id"] = vendor;
if (payload["u"].is<String>()) {
String url = payload["u"].as<String>();
// Extract domain from URL (e.g., "https://www.blubb.de/f1234/?suche=irgendwas" -> "https://www.blubb.de")
int protocolEnd = url.indexOf("://");
if (protocolEnd != -1) {
int pathStart = url.indexOf("/", protocolEnd + 3);
externalId = (pathStart != -1) ? url.substring(0, pathStart) : url;
} else {
externalId = url; // No protocol found, use as is
}
} else {
externalId = payload["b"].as<String>();
}
vendorDoc["comment"] = externalId;
String vendorPayload; String vendorPayload;
serializeJson(vendorDoc, vendorPayload); serializeJson(vendorDoc, vendorPayload);
@@ -706,66 +585,8 @@ uint16_t createVendor(const JsonDocument& payload) {
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = vendorPayload; params->updatePayload = vendorPayload;
// Create task without additional API state check since caller ensures synchronization
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
if (result != pdPASS) {
Serial.println("Failed to create vendor task!");
delete params;
vendorDoc.clear();
return 0;
}
vendorDoc.clear();
// Delay for Display Bar
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Wait for task completion and return the created vendor ID
// Note: createdVendorId will be set by sendToApi when response is received
while(createdVendorId == 65535) {
vTaskDelay(50 / portTICK_PERIOD_MS);
}
return createdVendorId;
}
uint16_t checkVendor(const JsonDocument& payload) {
oledShowProgressBar(1, 5, "New Brand", "Check Vendor");
// Check if vendor exists using task system
foundVendorId = 65535; // Reset to invalid value to detect when API response is received
String vendorName = payload["b"].as<String>();
vendorName.trim();
vendorName.replace(" ", "+");
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor?name=" + vendorName;
Serial.print("Check vendor with URL: ");
Serial.println(spoolsUrl);
SendToApiParams* params = new SendToApiParams();
if (params == nullptr) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return 0;
}
params->requestType = API_REQUEST_VENDOR_CHECK;
params->httpType = "GET";
params->spoolsUrl = spoolsUrl;
params->updatePayload = ""; // Empty for GET request
// Check if API is idle before creating task // Check if API is idle before creating task
while (spoolmanApiState != API_IDLE) if(spoolmanApiState == API_IDLE){
{
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// Erstelle die Task // Erstelle die Task
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
@@ -775,130 +596,29 @@ uint16_t checkVendor(const JsonDocument& payload) {
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
// Wait until foundVendorId is updated by the API response (not 65535 anymore)
while (foundVendorId == 65535)
{
vTaskDelay(50 / portTICK_PERIOD_MS);
}
// Check if vendor was found
if (foundVendorId == 0) {
Serial.println("Vendor not found, creating new vendor...");
uint16_t vendorId = createVendor(payload);
if (vendorId == 0) {
Serial.println("Failed to create vendor, returning 0.");
return 0; // Failed to create vendor
} else { } else {
Serial.println("Vendor created with ID: " + String(vendorId)); Serial.println("Not spawning new task, API still active!");
return vendorId;
}
} else {
Serial.println("Vendor found: " + payload["b"].as<String>());
Serial.print("Vendor ID: ");
Serial.println(foundVendorId);
return foundVendorId;
}
}
uint16_t createFilament(uint16_t vendorId, const JsonDocument& payload) {
oledShowProgressBar(4, 5, "New Brand", "Create Filament");
// Create new filament in Spoolman database using task system
// Note: Due to async nature, the ID will be stored in createdFilamentId global variable
// Note: This function assumes that the caller has already ensured API is IDLE
createdFilamentId = 65535; // Reset previous value
String spoolsUrl = spoolmanUrl + apiUrl + "/filament";
Serial.print("Create filament with URL: ");
Serial.println(spoolsUrl);
// Create JSON payload for filament creation
JsonDocument filamentDoc;
filamentDoc["name"] = payload["cn"].as<String>();
filamentDoc["vendor_id"] = String(vendorId);
filamentDoc["material"] = payload["t"].as<String>();
filamentDoc["density"] = (payload["de"].is<String>() && payload["de"].as<String>().length() > 0) ? payload["de"].as<String>() : "1.24";
filamentDoc["diameter"] = (payload["di"].is<String>() && payload["di"].as<String>().length() > 0) ? payload["di"].as<String>() : "1.75";
filamentDoc["weight"] = String(weight);
filamentDoc["spool_weight"] = payload["sw"].as<String>();
filamentDoc["article_number"] = payload["an"].as<String>();
filamentDoc["settings_extruder_temp"] = payload["et"].is<String>() ? payload["et"].as<String>() : "";
filamentDoc["settings_bed_temp"] = payload["bt"].is<String>() ? payload["bt"].as<String>() : "";
if (payload["an"].is<String>())
{
filamentDoc["external_id"] = payload["an"].as<String>();
filamentDoc["comment"] = payload["u"].is<String>() ? payload["u"].as<String>() + payload["an"].as<String>() : "automatically generated";
}
else
{
filamentDoc["comment"] = payload["u"].is<String>() ? payload["u"].as<String>() : "automatically generated";
}
if (payload["mc"].is<String>()) {
filamentDoc["multi_color_hexes"] = payload["mc"].as<String>();
filamentDoc["multi_color_direction"] = payload["mcd"].is<String>() ? payload["mcd"].as<String>() : "";
}
else
{
filamentDoc["color_hex"] = (payload["c"].is<String>() && payload["c"].as<String>().length() >= 6) ? payload["c"].as<String>() : "FFFFFF";
}
String filamentPayload;
serializeJson(filamentDoc, filamentPayload);
Serial.print("Filament Payload: ");
Serial.println(filamentPayload);
SendToApiParams* params = new SendToApiParams();
if (params == nullptr) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
filamentDoc.clear();
return 0;
}
params->requestType = API_REQUEST_FILAMENT_CREATE;
params->httpType = "POST";
params->spoolsUrl = spoolsUrl;
params->updatePayload = filamentPayload;
// Create task without additional API state check since caller ensures synchronization
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
if (result != pdPASS) {
Serial.println("Failed to create filament task!");
delete params; delete params;
filamentDoc.clear(); vendorDoc.clear();
return 0; return 0;
} }
filamentDoc.clear(); vendorDoc.clear();
// Delay for Display Bar // Wait for task completion and return the created vendor ID
vTaskDelay(1000 / portTICK_PERIOD_MS); // Note: createdVendorId will be set by sendToApi when response is received
while(spoolmanApiState != API_IDLE) {
// Wait for task completion and return the created filament ID vTaskDelay(100 / portTICK_PERIOD_MS);
// Note: createdFilamentId will be set by sendToApi when response is received
while(createdFilamentId == 65535) {
vTaskDelay(50 / portTICK_PERIOD_MS);
} }
return createdFilamentId; return createdVendorId;
} }
uint16_t checkFilament(uint16_t vendorId, const JsonDocument& payload) { uint16_t checkFilament(uint16_t vendorId) {
oledShowProgressBar(3, 5, "New Brand", "Check Filament");
// Check if filament exists using task system // Check if filament exists using task system
foundFilamentId = 65535; // Reset to invalid value to detect when API response is received foundFilamentId = 0; // Reset previous value
String spoolsUrl = spoolmanUrl + apiUrl + "/filament?vendor.id=" + String(vendorId) + "&external_id=" + String(payload["artnr"].as<String>()); String spoolsUrl = spoolmanUrl + apiUrl + "/filament?vendor.id=" + String(vendorId);
Serial.print("Check filament with URL: "); Serial.print("Check filament with URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -912,6 +632,8 @@ uint16_t checkFilament(uint16_t vendorId, const JsonDocument& payload) {
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = ""; // Empty for GET request params->updatePayload = ""; // Empty for GET request
// Check if API is idle before creating task
if(spoolmanApiState == API_IDLE){
// Erstelle die Task // Erstelle die Task
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
@@ -921,21 +643,27 @@ uint16_t checkFilament(uint16_t vendorId, const JsonDocument& payload) {
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
} else {
Serial.println("Not spawning new task, API still active!");
delete params;
return 0;
}
// Wait until foundFilamentId is updated by the API response (not 65535 anymore) // Wait for task completion
while (foundFilamentId == 65535) { while(spoolmanApiState != API_IDLE) {
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
} }
// Check if filament was found // Check if filament was found
if (foundFilamentId == 0) { if (foundFilamentId == 0) {
Serial.println("Filament not found, creating new filament..."); Serial.println("Filament not found, creating new filament...");
uint16_t filamentId = createFilament(vendorId, payload); uint16_t filamentId = createFilament();
if (filamentId == 0) { if (filamentId == 0) {
Serial.println("Failed to create filament, returning 0."); Serial.println("Failed to create filament, returning 0.");
return 0; // Failed to create filament return 0; // Failed to create filament
} else { } else {
Serial.println("Filament created with ID: " + String(filamentId)); Serial.println("Filament created with ID: " + String(filamentId));
checkSpool();
return filamentId; return filamentId;
} }
} else { } else {
@@ -946,128 +674,58 @@ uint16_t checkFilament(uint16_t vendorId, const JsonDocument& payload) {
} }
} }
uint16_t createSpool(uint16_t vendorId, uint16_t filamentId, JsonDocument& payload, String uidString) { bool createFilament() {
oledShowProgressBar(5, 5, "New Brand", "Create new Spool"); // {
// "name": "PolyTerra Charcoal Black",
// "vendor_id": 0,
// "material": "PLA",
// "price": 20,
// "density": 1.24,
// "diameter": 1.75,
// "weight": 1000,
// "spool_weight": 140,
// "article_number": "PM70820",
// "comment": "automatically generated",
// "settings_extruder_temp": 210,
// "settings_bed_temp": 60,
// "color_hex": "FF0000",
// "multi_color_hexes": "FF0000,00FF00,0000FF",
// "multi_color_direction": "coaxial",
// "external_id": "polymaker_pla_polysonicblack_1000_175",
// "extra": {
// "nozzle_temperature": "string"
// }
// }
// Create new spool in Spoolman database using task system
// Note: Due to async nature, the ID will be stored in createdSpoolId global variable
// Note: This function assumes that the caller has already ensured API is IDLE
createdSpoolId = 65535; // Reset to invalid value to detect when API response is received
String spoolsUrl = spoolmanUrl + apiUrl + "/spool";
Serial.print("Create spool with URL: ");
Serial.println(spoolsUrl);
// Create JSON payload for spool creation
JsonDocument spoolDoc;
spoolDoc["filament_id"] = String(filamentId);
spoolDoc["initial_weight"] = weight > 10 ? String(weight - payload["sw"].as<int>()) : "1000";
spoolDoc["spool_weight"] = (payload["sw"].is<String>() && payload["sw"].as<String>().length() > 0) ? payload["sw"].as<String>() : "180";
spoolDoc["remaining_weight"] = spoolDoc["initial_weight"];
spoolDoc["lot_nr"] = (payload["an"].is<String>() && payload["an"].as<String>().length() > 0) ? payload["an"].as<String>() : "";
spoolDoc["comment"] = "automatically generated";
spoolDoc["extra"]["nfc_id"] = "\"" + uidString + "\"";
String spoolPayload;
serializeJson(spoolDoc, spoolPayload);
Serial.print("Spool Payload: ");
Serial.println(spoolPayload);
spoolDoc.clear();
SendToApiParams* params = new SendToApiParams();
if (params == nullptr) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
spoolDoc.clear();
return 0;
}
params->requestType = API_REQUEST_SPOOL_CREATE;
params->httpType = "POST";
params->spoolsUrl = spoolsUrl;
params->updatePayload = spoolPayload;
// Create task without additional API state check since caller ensures synchronization
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
if (result != pdPASS) {
Serial.println("Failed to create spool task!");
delete params;
return 0;
}
// Wait for task completion and return the created spool ID
// Note: createdSpoolId will be set by sendToApi when response is received
while(createdSpoolId == 65535) {
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
// void startWriteJsonToTag(const bool isSpoolTag, const char* payload);
// Create optimized JSON structure with sm_id at the beginning for fast-path detection
JsonDocument optimizedPayload;
optimizedPayload["sm_id"] = String(createdSpoolId); // Place sm_id first for fast scanning
optimizedPayload["b"] = payload["b"].as<String>();
optimizedPayload["cn"] = payload["an"].as<String>();
String payloadString;
serializeJson(optimizedPayload, payloadString);
Serial.println("Optimized JSON with sm_id first:");
Serial.println(payloadString);
optimizedPayload.clear();
nfcReaderState = NFC_IDLE;
// Delay for Display Bar
vTaskDelay(1000 / portTICK_PERIOD_MS);
startWriteJsonToTag(true, payloadString.c_str());
return createdSpoolId;
} }
bool createBrandFilament(JsonDocument& payload, String uidString) { uint16_t checkSpool() {
uint16_t vendorId = checkVendor(payload);
if (vendorId == 0) {
Serial.println("ERROR: Failed to create/find vendor");
return false;
}
uint16_t filamentId = checkFilament(vendorId, payload); }
if (filamentId == 0) {
Serial.println("ERROR: Failed to create/find filament");
return false;
}
uint16_t spoolId = createSpool(vendorId, filamentId, payload, uidString); bool createSpool() {
if (spoolId == 0) { // Implement specific handling for Spool creation
Serial.println("ERROR: Failed to create spool"); // {
return false; // "first_used": "2019-08-24T14:15:22Z",
} // "last_used": "2019-08-24T14:15:22Z",
// "filament_id": 0,
Serial.println("SUCCESS: Brand filament created with Spool ID: " + String(spoolId)); // "price": 20,
return true; // "initial_weight": 200,
// "spool_weight": 200,
// "remaining_weight": 800,
// "used_weight": 200,
// "location": "Shelf A",
// "lot_nr": "52342",
// "comment": "",
// "archived": false,
// "extra": {
// "nfc_id": "string"
// }
// }
} }
// #### Spoolman init // #### Spoolman init
bool checkSpoolmanExtraFields() { bool checkSpoolmanExtraFields() {
// Only check extra fields if they have not been checked before
if(!spoolmanExtraFieldsChecked){
HTTPClient http; HTTPClient http;
String checkUrls[] = { String checkUrls[] = {
spoolmanUrl + apiUrl + "/field/spool", spoolmanUrl + apiUrl + "/field/spool",
@@ -1215,23 +873,14 @@ bool checkSpoolmanExtraFields() {
http.end(); http.end();
spoolmanExtraFieldsChecked = true;
return true; return true;
}else{
return true;
}
} }
bool checkSpoolmanInstance() { bool checkSpoolmanInstance(const String& url) {
HTTPClient http; HTTPClient http;
bool returnValue = false; String healthUrl = url + apiUrl + "/health";
// Only do the spoolman instance check if there is no active API request going on Serial.print("Überprüfe Spoolman-Instanz unter: ");
if(spoolmanApiState == API_IDLE){
spoolmanApiState = API_TRANSMITTING;
String healthUrl = spoolmanUrl + apiUrl + "/health";
Serial.print("Checking spoolman instance: ");
Serial.println(healthUrl); Serial.println(healthUrl);
http.begin(healthUrl); http.begin(healthUrl);
@@ -1259,30 +908,16 @@ bool checkSpoolmanInstance() {
spoolmanApiState = API_IDLE; spoolmanApiState = API_IDLE;
oledShowTopRow(); oledShowTopRow();
spoolmanConnected = true; spoolmanConnected = true;
returnValue = strcmp(status, "healthy") == 0; return strcmp(status, "healthy") == 0;
}else{
spoolmanConnected = false;
} }
doc.clear(); doc.clear();
}else{
spoolmanConnected = false;
} }
} else { } else {
spoolmanConnected = false;
Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode)); Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode));
} }
http.end(); http.end();
spoolmanApiState = API_IDLE; return false;
}
else
{
// If the check is skipped, return the previous status
Serial.println("Skipping spoolman healthcheck, API is active.");
returnValue = spoolmanConnected;
}
Serial.println("Healthcheck completed!");
return returnValue;
} }
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) { bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) {
@@ -1295,13 +930,12 @@ bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, con
preferences.end(); preferences.end();
//TBD: This could be handled nicer in the future //TBD: This could be handled nicer in the future
spoolmanExtraFieldsChecked = false;
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn; octoEnabled = octoOn;
octoUrl = octo_url; octoUrl = octo_url;
octoToken = octoTk; octoToken = octoTk;
return checkSpoolmanInstance(); return true;
} }
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
@@ -1321,10 +955,15 @@ String loadSpoolmanUrl() {
bool initSpoolman() { bool initSpoolman() {
oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init"); oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init");
spoolmanUrl = loadSpoolmanUrl(); spoolmanUrl = loadSpoolmanUrl();
spoolmanUrl.trim();
if (spoolmanUrl == "") {
Serial.println("Keine Spoolman-URL gefunden.");
return false;
}
bool success = checkSpoolmanInstance(); bool success = checkSpoolmanInstance(spoolmanUrl);
if (!success) { if (!success) {
Serial.println("Spoolman not available"); Serial.println("Spoolman nicht erreichbar.");
return false; return false;
} }

View File

@@ -20,9 +20,7 @@ typedef enum {
API_REQUEST_SPOOL_LOCATION_UPDATE, API_REQUEST_SPOOL_LOCATION_UPDATE,
API_REQUEST_VENDOR_CREATE, API_REQUEST_VENDOR_CREATE,
API_REQUEST_VENDOR_CHECK, API_REQUEST_VENDOR_CHECK,
API_REQUEST_FILAMENT_CHECK, API_REQUEST_FILAMENT_CHECK
API_REQUEST_FILAMENT_CREATE,
API_REQUEST_SPOOL_CREATE
} SpoolmanApiRequestType; } SpoolmanApiRequestType;
extern volatile spoolmanApiStateType spoolmanApiState; extern volatile spoolmanApiStateType spoolmanApiState;
@@ -32,9 +30,12 @@ extern bool octoEnabled;
extern bool sendOctoUpdate; extern bool sendOctoUpdate;
extern String octoUrl; extern String octoUrl;
extern String octoToken; extern String octoToken;
extern uint16_t createdVendorId; // ID of newly created vendor
extern uint16_t foundVendorId; // ID of found vendor
extern uint16_t foundFilamentId; // ID of found filament
extern bool spoolmanConnected; extern bool spoolmanConnected;
bool checkSpoolmanInstance(); bool checkSpoolmanInstance(const String& url);
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);
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder
@@ -45,6 +46,12 @@ uint8_t updateSpoolLocation(String spoolId, String location);
bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten
bool createBrandFilament(JsonDocument& payload, String uidString); uint16_t checkVendor(String vendor); // Check if vendor exists, return ID
uint16_t createVendor(String vendor); // Create vendor, return ID
uint16_t checkFilament(); // Check if filament exists, return ID
bool createFilament(); // Create filament
uint16_t checkSpool(); // Check if spool exists, return ID
bool createSpool(); // Create spool
void createFilamentFabrik(JsonDocument payload);
#endif #endif

View File

@@ -33,7 +33,6 @@ AMSData ams_data[MAX_AMS]; // Definition des Arrays;
bool removeBambuCredentials() { bool removeBambuCredentials() {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
BambuMqttTask = NULL;
} }
Preferences preferences; Preferences preferences;
@@ -64,7 +63,6 @@ bool removeBambuCredentials() {
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) { bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
BambuMqttTask = NULL;
} }
bambuCredentials.ip = ip.c_str(); bambuCredentials.ip = ip.c_str();
@@ -595,7 +593,6 @@ void reconnect() {
Serial.println("Disable Bambu MQTT Task after 5 retries"); Serial.println("Disable Bambu MQTT Task after 5 retries");
//vTaskSuspend(BambuMqttTask); //vTaskSuspend(BambuMqttTask);
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
BambuMqttTask = NULL;
break; break;
} }
@@ -684,7 +681,6 @@ void bambu_restart() {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
BambuMqttTask = NULL;
delay(10); delay(10);
} }
setupMqtt(); setupMqtt();

View File

@@ -16,6 +16,7 @@ const uint8_t LOADCELL_DOUT_PIN = 16; //16;
const uint8_t LOADCELL_SCK_PIN = 17; //17; const uint8_t LOADCELL_SCK_PIN = 17; //17;
const uint8_t calVal_eepromAdress = 0; const uint8_t calVal_eepromAdress = 0;
const uint16_t SCALE_LEVEL_WEIGHT = 500; const uint16_t SCALE_LEVEL_WEIGHT = 500;
uint16_t defaultScaleCalibrationValue = 430;
// ***** HX711 // ***** HX711
// ***** TTP223 (Touch Sensor) // ***** TTP223 (Touch Sensor)

View File

@@ -5,6 +5,7 @@
#define BAMBU_DEFAULT_AUTOSEND_TIME 60 #define BAMBU_DEFAULT_AUTOSEND_TIME 60
#define NVS_NAMESPACE_API "api" #define NVS_NAMESPACE_API "api"
#define NVS_KEY_SPOOLMAN_URL "spoolmanUrl" #define NVS_KEY_SPOOLMAN_URL "spoolmanUrl"
#define NVS_KEY_OCTOPRINT_ENABLED "octoEnabled" #define NVS_KEY_OCTOPRINT_ENABLED "octoEnabled"
@@ -21,7 +22,6 @@
#define NVS_NAMESPACE_SCALE "scale" #define NVS_NAMESPACE_SCALE "scale"
#define NVS_KEY_CALIBRATION "cal_value" #define NVS_KEY_CALIBRATION "cal_value"
#define NVS_KEY_AUTOTARE "auto_tare" #define NVS_KEY_AUTOTARE "auto_tare"
#define SCALE_DEFAULT_CALIBRATION_VALUE 430.0f;
#define BAMBU_USERNAME "bblp" #define BAMBU_USERNAME "bblp"
@@ -33,9 +33,6 @@
#define SCREEN_PROGRESS_BAR_HEIGHT 12U #define SCREEN_PROGRESS_BAR_HEIGHT 12U
#define DISPLAY_BOOT_TEXT "FilaMan" #define DISPLAY_BOOT_TEXT "FilaMan"
#define WIFI_CHECK_INTERVAL 60000U
#define DISPLAY_UPDATE_INTERVAL 1000U
#define SPOOLMAN_HEALTHCHECK_INTERVAL 60000U
extern const uint8_t PN532_IRQ; extern const uint8_t PN532_IRQ;
extern const uint8_t PN532_RESET; extern const uint8_t PN532_RESET;

View File

@@ -235,7 +235,7 @@ void oledShowIcon(const char* icon) {
display.display(); display.display();
} }
void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage) { void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage){
assert(step <= numSteps); assert(step <= numSteps);
// clear data and bar area // clear data and bar area

View File

@@ -59,7 +59,6 @@ void setup() {
// Scale // Scale
start_scale(touchSensorConnected); start_scale(touchSensorConnected);
scale.tare();
// 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
@@ -98,8 +97,7 @@ int16_t lastWeight = 0;
// WIFI check variables // WIFI check variables
unsigned long lastWifiCheckTime = 0; unsigned long lastWifiCheckTime = 0;
unsigned long lastTopRowUpdateTime = 0; const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms)
unsigned long lastSpoolmanHealcheckTime = 0;
// Button debounce variables // Button debounce variables
unsigned long lastButtonPress = 0; unsigned long lastButtonPress = 0;
@@ -117,23 +115,17 @@ void loop() {
} }
// Überprüfe regelmäßig die WLAN-Verbindung // Überprüfe regelmäßig die WLAN-Verbindung
if (intervalElapsed(currentMillis, lastWifiCheckTime, WIFI_CHECK_INTERVAL)) if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval))
{ {
checkWiFiConnection(); checkWiFiConnection();
} }
// Periodic display update // Periodic display update
if (intervalElapsed(currentMillis, lastTopRowUpdateTime, DISPLAY_UPDATE_INTERVAL)) if (intervalElapsed(currentMillis, lastWifiCheckTime, 1000))
{ {
oledShowTopRow(); oledShowTopRow();
} }
// Periodic spoolman health check
if (intervalElapsed(currentMillis, lastSpoolmanHealcheckTime, SPOOLMAN_HEALTHCHECK_INTERVAL))
{
checkSpoolmanInstance();
}
// Wenn Bambu auto set Spool aktiv // Wenn Bambu auto set Spool aktiv
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
{ {
@@ -164,25 +156,23 @@ void loop() {
} }
} }
// If scale is not calibrated, only show a warning // Wenn Waage nicht Kalibriert
if (!scaleCalibrated) if (scaleCalibrated == 3)
{ {
// Do not show the warning if the calibratin process is onging oledShowMessage("Scale not calibrated!");
if(!scaleCalibrationActive){ vTaskDelay(5000 / portTICK_PERIOD_MS);
oledShowMessage("Scale not calibrated"); yield();
vTaskDelay(1000 / portTICK_PERIOD_MS); esp_task_wdt_reset();
return;
} }
}
else
{
// 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)))
{ {
(displayWeight < 2) ? ((displayWeight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(displayWeight); (weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
} }
mainTaskWasPaused = false; mainTaskWasPaused = false;
} }
@@ -230,8 +220,7 @@ void loop() {
lastWeight = weight; lastWeight = weight;
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden // Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE) if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE) {
{
// set the current tag as processed to prevent it beeing processed again // set the current tag as processed to prevent it beeing processed again
tagProcessed = true; tagProcessed = true;
@@ -239,43 +228,22 @@ void loop() {
{ {
weightSend = 1; weightSend = 1;
// Set Bambu spool ID for auto-send if enabled }
if (bambuCredentials.autosend_enable) else
{ {
oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
if(sendOctoUpdate && spoolmanApiState == API_IDLE){
autoSetToBambuSpoolId = activeSpoolId.toInt(); autoSetToBambuSpoolId = 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(octoEnabled)
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
{
oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
if(octoEnabled && sendOctoUpdate && spoolmanApiState == API_IDLE)
{ {
updateSpoolOcto(autoSetToBambuSpoolId); updateSpoolOcto(autoSetToBambuSpoolId);
sendOctoUpdate = false;
} }
sendOctoUpdate = false;
} }
esp_task_wdt_reset(); esp_task_wdt_reset();

File diff suppressed because it is too large Load Diff

View File

@@ -16,8 +16,6 @@ typedef enum{
void startNfc(); 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 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;
@@ -25,7 +23,6 @@ extern String activeSpoolId;
extern String lastSpoolId; extern String lastSpoolId;
extern volatile nfcReaderStateType nfcReaderState; extern volatile nfcReaderStateType nfcReaderState;
extern volatile bool pauseBambuMqttTask; extern volatile bool pauseBambuMqttTask;
extern volatile bool nfcWriteInProgress;
extern bool tagProcessed; extern bool tagProcessed;

View File

@@ -13,115 +13,12 @@ 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;
uint8_t pauseMainTask = 0; uint8_t pauseMainTask = 0;
bool scaleCalibrated; uint8_t scaleCalibrated = 1;
bool autoTare = true; bool autoTare = true;
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) {
@@ -141,7 +38,6 @@ uint8_t setAutoTare(bool autoTareValue) {
uint8_t tareScale() { uint8_t tareScale() {
Serial.println("Tare scale"); Serial.println("Tare scale");
scale.tare(); scale.tare();
resetWeightFilter(); // Reset stabilization filter after tare
return 1; return 1;
} }
@@ -151,15 +47,7 @@ void scale_loop(void * parameter) {
Serial.println("Scale Loop started"); Serial.println("Scale Loop started");
Serial.println("++++++++++++++++++++++++++++++"); Serial.println("++++++++++++++++++++++++++++++");
// Initialize weight filter
resetWeightFilter();
lastMeasurementTime = millis();
for(;;) { for(;;) {
unsigned long currentTime = millis();
// Only measure at defined intervals to reduce noise
if (currentTime - lastMeasurementTime >= MEASUREMENT_INTERVAL_MS) {
if (scale.is_ready()) if (scale.is_ready())
{ {
// Waage automatisch Taren, wenn zu lange Abweichung // Waage automatisch Taren, wenn zu lange Abweichung
@@ -167,7 +55,6 @@ void scale_loop(void * parameter) {
{ {
Serial.println("Auto Tare scale"); Serial.println("Auto Tare scale");
scale.tare(); scale.tare();
resetWeightFilter(); // Reset filter after auto tare
scale_tare_counter = 0; scale_tare_counter = 0;
} }
@@ -178,34 +65,19 @@ void scale_loop(void * parameter) {
oledShowMessage("TARE Scale"); oledShowMessage("TARE Scale");
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
scale.tare(); scale.tare();
resetWeightFilter(); // Reset filter after manual tare
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
oledShowWeight(0); oledShowWeight(0);
scaleTareRequest = false; scaleTareRequest = false;
} }
// Get raw weight reading // Only update weight if median changed more than 1
float rawWeight = scale.get_units(); int16_t newWeight = round(scale.get_units());
if(abs(weight-newWeight) > 1){
// Process weight with stabilization weight = newWeight;
int16_t stabilizedWeight = processWeightReading(rawWeight);
// Update global weight variable only if it changed significantly (for API actions)
if (stabilizedWeight != weight) {
weight = stabilizedWeight;
}
// Debug output for monitoring (can be removed in production)
static unsigned long lastDebugTime = 0;
if (currentTime - lastDebugTime > 2000) { // Print every 2 seconds
lastDebugTime = currentTime;
}
lastMeasurementTime = currentTime;
} }
} }
vTaskDelay(pdMS_TO_TICKS(10)); // Shorter delay for more responsive loop vTaskDelay(pdMS_TO_TICKS(100));
} }
} }
@@ -216,13 +88,7 @@ void start_scale(bool touchSensorConnected) {
// NVS lesen // NVS lesen
Preferences preferences; Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
if(preferences.isKey(NVS_KEY_CALIBRATION)){ calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION);
scaleCalibrated = true;
}else{
calibrationValue = SCALE_DEFAULT_CALIBRATION_VALUE;
scaleCalibrated = false;
}
// auto Tare // auto Tare
// Wenn Touch Sensor verbunden, dann autoTare auf false setzen // Wenn Touch Sensor verbunden, dann autoTare auf false setzen
@@ -237,24 +103,31 @@ void start_scale(bool touchSensorConnected) {
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
if (isnan(calibrationValue) || calibrationValue < 1) {
calibrationValue = defaultScaleCalibrationValue;
scaleCalibrated = 0;
oledShowMessage("Scale not calibrated!");
for (uint16_t i = 0; i < 50000; i++) {
yield();
vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset();
}
}
oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale"); oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
for (uint16_t i = 0; i < 3000; i++) { for (uint16_t i = 0; i < 2000; i++) {
yield(); yield();
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
while(!scale.is_ready()) { if (scale.wait_ready_timeout(1000))
vTaskDelay(pdMS_TO_TICKS(5000)); {
scale.set_scale(calibrationValue); // this value is obtained by calibrating the scale with known weights; see the README for details
scale.tare();
} }
scale.set_scale(calibrationValue);
//vTaskDelay(pdMS_TO_TICKS(5000));
//scale.tare();
// Initialize weight stabilization filter
resetWeightFilter();
// Display Gewicht // Display Gewicht
oledShowWeight(0); oledShowWeight(0);
@@ -279,8 +152,6 @@ uint8_t calibrate_scale() {
uint8_t returnState = 0; uint8_t returnState = 0;
float newCalibrationValue; float newCalibrationValue;
scaleCalibrationActive = true;
vTaskSuspend(RfidReaderTask); vTaskSuspend(RfidReaderTask);
vTaskSuspend(ScaleTask); vTaskSuspend(ScaleTask);
@@ -339,7 +210,6 @@ 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));
@@ -358,7 +228,6 @@ uint8_t calibrate_scale() {
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
scaleCalibrated = true;
returnState = 1; returnState = 1;
} }
else else
@@ -393,7 +262,6 @@ uint8_t calibrate_scale() {
vTaskResume(ScaleTask); vTaskResume(ScaleTask);
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
pauseMainTask = 0; pauseMainTask = 0;
scaleCalibrationActive = false;
return returnState; return returnState;
} }

View File

@@ -9,22 +9,14 @@ 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;
extern uint8_t scale_tare_counter; extern uint8_t scale_tare_counter;
extern uint8_t scaleTareRequest; extern uint8_t scaleTareRequest;
extern uint8_t pauseMainTask; extern uint8_t pauseMainTask;
extern bool scaleCalibrated; extern uint8_t scaleCalibrated;
extern bool autoTare; extern bool autoTare;
extern bool scaleCalibrationActive;
extern TaskHandle_t ScaleTask; extern TaskHandle_t ScaleTask;

View File

@@ -48,15 +48,9 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
} else if (type == WS_EVT_PONG) { } else if (type == WS_EVT_PONG) {
Serial.printf("WebSocket Client #%u pong\n", client->id()); Serial.printf("WebSocket Client #%u pong\n", client->id());
} else if (type == WS_EVT_DATA) { } else if (type == WS_EVT_DATA) {
String message = String((char*)data);
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, (char*)data, len); deserializeJson(doc, message);
//String message = String((char*)data);
//deserializeJson(doc, message);
if (error) {
Serial.println("JSON deserialization failed: " + String(error.c_str()));
return;
}
if (doc["type"] == "heartbeat") { if (doc["type"] == "heartbeat") {
// Sende Heartbeat-Antwort // Sende Heartbeat-Antwort