Compare commits
50 Commits
v1.5.12-be
...
scale
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
bc51956793 | |||
5666a58da2 | |||
a35f15eca5 | |||
f28b34e427 | |||
9215560558 | |||
7f6bce1699 | |||
2a4f8bb679 | |||
480e2da23e | |||
ba22602767 | |||
b2c68d5aac | |||
52a7f6b5b6 | |||
4cce9f8d5d | |||
f0eced8585 | |||
02e31878ee | |||
7ff499f984 | |||
fcd637cc30 | |||
587485d0de | |||
e0cc99e993 |
1034
CHANGELOG.md
1034
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
28
README.de.md
28
README.de.md
@@ -27,6 +27,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Filamentdaten auf NFC-Tags schreiben.
|
||||
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- 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:**
|
||||
- Anzeige der aktuellen AMS-Fachbelegung.
|
||||
- Zuordnung von Filamenten zu AMS-Slots.
|
||||
@@ -39,8 +40,35 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Unterstützt das Spoolman Octoprint Plugin
|
||||
|
||||
### 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>
|
||||
|
||||
## 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
|
||||
|
||||
### ESP32-Funktionalität
|
||||
|
28
README.md
28
README.md
@@ -31,6 +31,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Write filament data to NFC tags.
|
||||
- uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- 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:**
|
||||
- Display current AMS tray contents.
|
||||
- Assign filaments to AMS slots.
|
||||
@@ -43,8 +44,35 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Supports Spoolman Octoprint Plugin
|
||||
|
||||
### 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>
|
||||
|
||||
## 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
|
||||
|
||||
### ESP32 Functionality
|
||||
|
159
README_ManufacturerTags_DE.md
Normal file
159
README_ManufacturerTags_DE.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# 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.
|
145
README_ManufacturerTags_EN.md
Normal file
145
README_ManufacturerTags_EN.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# 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.
|
@@ -9,8 +9,8 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[common]
|
||||
version = "1.5.12-beta18"
|
||||
to_old_version = "1.5.0"
|
||||
version = "2.0.0-beta14"
|
||||
to_old_version = "1.5.10"
|
||||
|
||||
##
|
||||
[env:esp32dev]
|
||||
|
@@ -14,17 +14,39 @@ def get_version():
|
||||
return version_match.group(1) if version_match else None
|
||||
|
||||
def get_last_tag():
|
||||
"""Get the last non-beta tag for changelog generation"""
|
||||
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)
|
||||
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:
|
||||
return None
|
||||
|
||||
def categorize_commit(commit_msg):
|
||||
"""Categorize commit messages based on conventional commits"""
|
||||
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'
|
||||
elif any(x in lower_msg for x in ['fix', 'bug']):
|
||||
return 'Fixed'
|
||||
@@ -34,6 +56,7 @@ def categorize_commit(commit_msg):
|
||||
def get_changes_from_git():
|
||||
"""Get changes from git commits since last tag"""
|
||||
changes = {
|
||||
'Breaking Changes': [],
|
||||
'Added': [],
|
||||
'Changed': [],
|
||||
'Fixed': []
|
||||
@@ -55,7 +78,9 @@ def get_changes_from_git():
|
||||
if commit:
|
||||
category = categorize_commit(commit)
|
||||
# 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)
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
|
138
src/api.cpp
138
src/api.cpp
@@ -124,28 +124,69 @@ void sendToApi(void *parameter) {
|
||||
String octoToken = params->octoToken;
|
||||
bool triggerWeightUpdate = params->triggerWeightUpdate;
|
||||
String spoolIdForWeight = params->spoolIdForWeight;
|
||||
uint16_t weightValue = params->weightValue;
|
||||
uint16_t weightValue = params->weightValue;
|
||||
|
||||
HTTPClient http;
|
||||
http.setReuse(false);
|
||||
// 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;
|
||||
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);
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
|
||||
// Execute HTTP request based on type
|
||||
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
|
||||
else if (httpType == "POST") httpCode = http.POST(updatePayload);
|
||||
else if (httpType == "GET") httpCode = http.GET();
|
||||
else httpCode = http.PUT(updatePayload);
|
||||
|
||||
int httpCode;
|
||||
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
|
||||
else if (httpType == "POST") httpCode = http.POST(updatePayload);
|
||||
else if (httpType == "GET") httpCode = http.GET();
|
||||
else httpCode = http.PUT(updatePayload);
|
||||
// Check if request was successful
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) {
|
||||
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();
|
||||
}
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
// Process successful response
|
||||
if (success) {
|
||||
Serial.println("Spoolman Abfrage erfolgreich");
|
||||
|
||||
// Restgewicht der Spule auslesen
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
DeserializationError error = deserializeJson(doc, responsePayload);
|
||||
if (error) {
|
||||
Serial.print("Fehler beim Parsen der JSON-Antwort: ");
|
||||
Serial.println(error.c_str());
|
||||
@@ -225,10 +266,9 @@ void sendToApi(void *parameter) {
|
||||
} else if (httpCode == HTTP_CODE_CREATED) {
|
||||
Serial.println("Spoolman erfolgreich erstellt");
|
||||
|
||||
// Parse response for created resources
|
||||
String payload = http.getString();
|
||||
// Parse response for created resources
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
DeserializationError error = deserializeJson(doc, responsePayload);
|
||||
if (error) {
|
||||
Serial.print("Fehler beim Parsen der JSON-Antwort: ");
|
||||
Serial.println(error.c_str());
|
||||
@@ -280,14 +320,17 @@ void sendToApi(void *parameter) {
|
||||
Serial.println(weightPayload);
|
||||
|
||||
// Execute weight update
|
||||
http.begin(weightUrl);
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
HTTPClient weightHttp;
|
||||
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) {
|
||||
Serial.println("Weight update successful");
|
||||
String weightResponse = http.getString();
|
||||
String weightResponse = weightHttp.getString();
|
||||
JsonDocument weightResponseDoc;
|
||||
DeserializationError weightError = deserializeJson(weightResponseDoc, weightResponse);
|
||||
|
||||
@@ -310,6 +353,7 @@ void sendToApi(void *parameter) {
|
||||
oledShowProgressBar(1, 1, "Failure!", "Weight update");
|
||||
}
|
||||
|
||||
weightHttp.end();
|
||||
weightDoc.clear();
|
||||
}
|
||||
} else {
|
||||
@@ -325,14 +369,25 @@ void sendToApi(void *parameter) {
|
||||
case API_REQUEST_BAMBU_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Failure!", "Bambu update");
|
||||
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:
|
||||
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;
|
||||
}
|
||||
Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode));
|
||||
@@ -340,7 +395,6 @@ void sendToApi(void *parameter) {
|
||||
nfcReaderState = NFC_IDLE; // Reset NFC state to allow retry
|
||||
}
|
||||
|
||||
http.end();
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// Speicher freigeben
|
||||
@@ -603,7 +657,7 @@ bool updateSpoolBambuData(String payload) {
|
||||
}
|
||||
|
||||
// #### Brand Filament
|
||||
uint16_t createVendor(String vendor) {
|
||||
uint16_t createVendor(const JsonDocument& payload) {
|
||||
oledShowProgressBar(2, 5, "New Brand", "Create new Vendor");
|
||||
|
||||
// Create new vendor in Spoolman database using task system
|
||||
@@ -617,9 +671,24 @@ uint16_t createVendor(String vendor) {
|
||||
|
||||
// Create JSON payload for vendor creation
|
||||
JsonDocument vendorDoc;
|
||||
vendorDoc["name"] = vendor;
|
||||
vendorDoc["comment"] = "automatically generated";
|
||||
vendorDoc["external_id"] = vendor;
|
||||
vendorDoc["name"] = payload["b"].as<String>();
|
||||
|
||||
// Extract domain from URL if present, otherwise use brand name
|
||||
String externalId = "";
|
||||
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;
|
||||
serializeJson(vendorDoc, vendorPayload);
|
||||
@@ -668,13 +737,13 @@ uint16_t createVendor(String vendor) {
|
||||
return createdVendorId;
|
||||
}
|
||||
|
||||
uint16_t checkVendor(String vendor) {
|
||||
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 = vendor;
|
||||
String vendorName = payload["b"].as<String>();
|
||||
vendorName.trim();
|
||||
vendorName.replace(" ", "+");
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor?name=" + vendorName;
|
||||
@@ -716,7 +785,7 @@ uint16_t checkVendor(String vendor) {
|
||||
// Check if vendor was found
|
||||
if (foundVendorId == 0) {
|
||||
Serial.println("Vendor not found, creating new vendor...");
|
||||
uint16_t vendorId = createVendor(vendor);
|
||||
uint16_t vendorId = createVendor(payload);
|
||||
if (vendorId == 0) {
|
||||
Serial.println("Failed to create vendor, returning 0.");
|
||||
return 0; // Failed to create vendor
|
||||
@@ -725,7 +794,7 @@ uint16_t checkVendor(String vendor) {
|
||||
return vendorId;
|
||||
}
|
||||
} else {
|
||||
Serial.println("Vendor found: " + vendor);
|
||||
Serial.println("Vendor found: " + payload["b"].as<String>());
|
||||
Serial.print("Vendor ID: ");
|
||||
Serial.println(foundVendorId);
|
||||
return foundVendorId;
|
||||
@@ -937,6 +1006,13 @@ uint16_t createSpool(uint16_t vendorId, uint16_t filamentId, JsonDocument& paylo
|
||||
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);
|
||||
@@ -966,7 +1042,7 @@ uint16_t createSpool(uint16_t vendorId, uint16_t filamentId, JsonDocument& paylo
|
||||
}
|
||||
|
||||
bool createBrandFilament(JsonDocument& payload, String uidString) {
|
||||
uint16_t vendorId = checkVendor(payload["b"].as<String>());
|
||||
uint16_t vendorId = checkVendor(payload);
|
||||
if (vendorId == 0) {
|
||||
Serial.println("ERROR: Failed to create/find vendor");
|
||||
return false;
|
||||
|
24
src/main.cpp
24
src/main.cpp
@@ -59,6 +59,7 @@ void setup() {
|
||||
|
||||
// Scale
|
||||
start_scale(touchSensorConnected);
|
||||
scale.tare();
|
||||
|
||||
// WDT initialisieren mit 10 Sekunden Timeout
|
||||
bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus
|
||||
@@ -177,9 +178,11 @@ void loop() {
|
||||
// Ausgabe der Waage auf Display
|
||||
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)))
|
||||
{
|
||||
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
||||
(displayWeight < 2) ? ((displayWeight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(displayWeight);
|
||||
}
|
||||
mainTaskWasPaused = false;
|
||||
}
|
||||
@@ -249,6 +252,25 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
oledShowIcon("failed");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
if(octoEnabled && sendOctoUpdate && spoolmanApiState == API_IDLE)
|
||||
{
|
||||
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||
|
249
src/nfc.cpp
249
src/nfc.cpp
@@ -108,6 +108,37 @@ bool formatNdefTag() {
|
||||
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()
|
||||
{
|
||||
// Read capability container from page 3 to determine exact NTAG type
|
||||
@@ -1268,6 +1299,61 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage, String uidString) {
|
||||
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) {
|
||||
// Fast-path: Read NDEF structure to quickly locate and check JSON payload
|
||||
// This dramatically speeds up known spool recognition
|
||||
@@ -1285,10 +1371,11 @@ bool quickSpoolIdCheck(String uidString) {
|
||||
memset(ndefData, 0, 20);
|
||||
|
||||
for (uint8_t page = 4; page < 9; page++) {
|
||||
if (!nfc.ntag2xx_ReadPage(page, ndefData + (page - 4) * 4)) {
|
||||
Serial.print("Failed to read page ");
|
||||
Serial.println(page);
|
||||
return false; // Fall back to full read
|
||||
if (!robustPageRead(page, ndefData + (page - 4) * 4)) {
|
||||
Serial.print("FAST-PATH: Failed to read page ");
|
||||
Serial.print(page);
|
||||
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);
|
||||
|
||||
for (uint8_t page = 9; page < 13; page++) {
|
||||
if (!nfc.ntag2xx_ReadPage(page, extraData + (page - 9) * 4)) {
|
||||
Serial.print("Failed to read additional page ");
|
||||
Serial.println(page);
|
||||
return false;
|
||||
if (!robustPageRead(page, extraData + (page - 9) * 4)) {
|
||||
Serial.print("FAST-PATH: Failed to read additional page ");
|
||||
Serial.print(page);
|
||||
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;
|
||||
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");
|
||||
Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly");
|
||||
return true;
|
||||
@@ -1450,6 +1546,14 @@ bool quickSpoolIdCheck(String uidString) {
|
||||
activeSpoolId = quickSpoolId;
|
||||
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");
|
||||
Serial.println("✓ FAST-PATH SUCCESS: Known spool processed quickly");
|
||||
return true;
|
||||
@@ -1496,6 +1600,10 @@ void writeJsonToTag(void *parameter) {
|
||||
// aktualisieren der Website wenn sich der Status ändert
|
||||
sendNfcData();
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
// Show waiting message for tag detection
|
||||
oledShowProgressBar(0, 1, "Write Tag", "Warte auf Tag");
|
||||
|
||||
// Wait 10sec for tag
|
||||
uint8_t success = 0;
|
||||
String uidString = "";
|
||||
@@ -1655,10 +1763,60 @@ void writeJsonToTag(void *parameter) {
|
||||
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) {
|
||||
// Optimize JSON to ensure sm_id is first key for fast-path detection
|
||||
String optimizedPayload = optimizeJsonForFastPath(payload);
|
||||
|
||||
NfcWriteParameterType* parameters = new NfcWriteParameterType();
|
||||
parameters->tagType = isSpoolTag;
|
||||
parameters->payload = strdup(payload);
|
||||
parameters->payload = strdup(optimizedPayload.c_str()); // Use optimized payload
|
||||
|
||||
// Task nicht mehrfach starten
|
||||
if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) {
|
||||
@@ -1678,9 +1836,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) {
|
||||
Serial.println("RFID Task gestartet");
|
||||
for(;;) {
|
||||
// Regular watchdog reset
|
||||
esp_task_wdt_reset();
|
||||
yield();
|
||||
|
||||
// Skip scanning during write operations, but keep NFC interface active
|
||||
if (nfcReaderState != NFC_WRITING && !nfcWriteInProgress && !nfcReadingTaskSuspendRequest && !booting)
|
||||
{
|
||||
@@ -1691,10 +1884,16 @@ void scanRfidTask(void * parameter) {
|
||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
||||
uint8_t uidLength;
|
||||
|
||||
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500);
|
||||
// Use safe tag detection instead of blocking readPassiveTargetID
|
||||
success = safeTagDetection(uid, &uidLength);
|
||||
|
||||
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
|
||||
if (success && nfcReaderState == NFC_IDLE)
|
||||
{
|
||||
@@ -1708,9 +1907,9 @@ void scanRfidTask(void * parameter) {
|
||||
|
||||
oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag");
|
||||
|
||||
// Wait 1 second after tag detection to stabilize connection
|
||||
Serial.println("Tag detected, waiting 1 second for stabilization...");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
// Reduced stabilization time for better responsiveness
|
||||
Serial.println("Tag detected, minimal stabilization...");
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS); // Reduced from 1000ms to 200ms
|
||||
|
||||
// create Tag UID string
|
||||
String uidString = "";
|
||||
@@ -1753,9 +1952,10 @@ void scanRfidTask(void * parameter) {
|
||||
|
||||
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
|
||||
@@ -1767,8 +1967,8 @@ void scanRfidTask(void * parameter) {
|
||||
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
// Increased delay to ensure stable reading
|
||||
vTaskDelay(pdMS_TO_TICKS(5)); // Increased from 1ms to 5ms
|
||||
// Reduced delay for faster reading
|
||||
vTaskDelay(pdMS_TO_TICKS(2)); // Reduced from 5ms to 2ms
|
||||
}
|
||||
|
||||
Serial.println("Tag reading completed, starting NDEF decode...");
|
||||
@@ -1789,6 +1989,9 @@ void scanRfidTask(void * parameter) {
|
||||
{
|
||||
oledShowProgressBar(1, 1, "Failure", "Tag 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
|
||||
@@ -1796,6 +1999,9 @@ void scanRfidTask(void * parameter) {
|
||||
//TBD: Show error here?!
|
||||
oledShowProgressBar(1, 1, "Failure", "Unkown tag type");
|
||||
Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!");
|
||||
// Reset activeSpoolId when tag type is unknown to prevent autoSet
|
||||
activeSpoolId = "";
|
||||
Serial.println("Unknown tag type - activeSpoolId reset to prevent autoSet");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1815,10 +2021,13 @@ void scanRfidTask(void * parameter) {
|
||||
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) {
|
||||
Serial.println("Tag erfolgreich gelesen - warte 5 Sekunden vor nächstem Scan");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS); // 5 second pause
|
||||
Serial.println("Tag erfolgreich gelesen - warte 3 Sekunden vor nächstem Scan");
|
||||
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
|
||||
|
@@ -17,6 +17,7 @@ void startNfc();
|
||||
void scanRfidTask(void * parameter);
|
||||
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 String nfcJsonData;
|
||||
|
186
src/scale.cpp
186
src/scale.cpp
@@ -13,6 +13,21 @@ TaskHandle_t ScaleTask;
|
||||
|
||||
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 scale_tare_counter = 0;
|
||||
bool scaleTareRequest = false;
|
||||
@@ -21,6 +36,93 @@ bool scaleCalibrated;
|
||||
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 #####
|
||||
uint8_t setAutoTare(bool autoTareValue) {
|
||||
Serial.print("Set AutoTare to ");
|
||||
@@ -39,6 +141,7 @@ uint8_t setAutoTare(bool autoTareValue) {
|
||||
uint8_t tareScale() {
|
||||
Serial.println("Tare scale");
|
||||
scale.tare();
|
||||
resetWeightFilter(); // Reset stabilization filter after tare
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -48,37 +151,61 @@ void scale_loop(void * parameter) {
|
||||
Serial.println("Scale Loop started");
|
||||
Serial.println("++++++++++++++++++++++++++++++");
|
||||
|
||||
// Initialize weight filter
|
||||
resetWeightFilter();
|
||||
lastMeasurementTime = millis();
|
||||
|
||||
for(;;) {
|
||||
if (scale.is_ready())
|
||||
{
|
||||
// Waage automatisch Taren, wenn zu lange Abweichung
|
||||
if (autoTare && scale_tare_counter >= 5)
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Only measure at defined intervals to reduce noise
|
||||
if (currentTime - lastMeasurementTime >= MEASUREMENT_INTERVAL_MS) {
|
||||
if (scale.is_ready())
|
||||
{
|
||||
Serial.println("Auto Tare scale");
|
||||
scale.tare();
|
||||
scale_tare_counter = 0;
|
||||
}
|
||||
// Waage automatisch Taren, wenn zu lange Abweichung
|
||||
if (autoTare && scale_tare_counter >= 5)
|
||||
{
|
||||
Serial.println("Auto Tare scale");
|
||||
scale.tare();
|
||||
resetWeightFilter(); // Reset filter after auto tare
|
||||
scale_tare_counter = 0;
|
||||
}
|
||||
|
||||
// Waage manuell Taren
|
||||
if (scaleTareRequest == true)
|
||||
{
|
||||
Serial.println("Re-Tare scale");
|
||||
oledShowMessage("TARE Scale");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
scale.tare();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
oledShowWeight(0);
|
||||
scaleTareRequest = false;
|
||||
}
|
||||
// Waage manuell Taren
|
||||
if (scaleTareRequest == true)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Only update weight if median changed more than 1
|
||||
int16_t newWeight = round(scale.get_units());
|
||||
if(abs(weight-newWeight) > 1){
|
||||
weight = newWeight;
|
||||
// Get raw weight reading
|
||||
float rawWeight = scale.get_units();
|
||||
|
||||
// Process weight with stabilization
|
||||
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(100));
|
||||
vTaskDelay(pdMS_TO_TICKS(10)); // Shorter delay for more responsive loop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +238,7 @@ void start_scale(bool touchSensorConnected) {
|
||||
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
|
||||
|
||||
oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
for (uint16_t i = 0; i < 3000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
@@ -121,8 +248,12 @@ void start_scale(bool touchSensorConnected) {
|
||||
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
|
||||
oledShowWeight(0);
|
||||
@@ -208,6 +339,7 @@ uint8_t calibrate_scale() {
|
||||
oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight");
|
||||
|
||||
scale.set_scale(newCalibrationValue);
|
||||
resetWeightFilter(); // Reset filter after calibration
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
|
@@ -9,6 +9,13 @@ uint8_t start_scale(bool touchSensorConnected);
|
||||
uint8_t calibrate_scale();
|
||||
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 int16_t weight;
|
||||
extern uint8_t weigthCouterToApi;
|
||||
|
Reference in New Issue
Block a user