Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4dee298b73 | |||
| c430c989ef | |||
| b40a2166f2 | |||
| 3590187cbe | |||
| 0302725bc7 | |||
| 75fbd5ebc3 | |||
| 53894a3746 | |||
| 360c925cfc | |||
| fc5d77c97b | |||
| f869bcc0c1 | |||
| b75be0ac06 | |||
| 1c01bfb2da | |||
| 15d0bfd8e9 | |||
| d363a7b905 | |||
| 9d7accc746 | |||
| c2e3dc1d46 | |||
| d81cf5d4e4 | |||
| 75e13387c8 | |||
| 6534419381 | |||
| e55b2d7fdf | |||
| 4aa81c7842 | |||
| 8ff4e81826 | |||
| 3eac0e5ac4 | |||
| 24d91693d9 | |||
| 659dfb8627 | |||
| b4dd0d357a | |||
| 8e38a68191 | |||
| c3ec7edf6a | |||
| a35e13e015 | |||
| e0df5d33f2 | |||
| 94c26590c8 | |||
| 4559bae066 | |||
| cdb2d16cf9 | |||
| cd71949c82 | |||
| 6cd280389d | |||
| daf27820b1 | |||
| dd7fbe1119 | |||
| dc2ddb47eb | |||
| 6bb8f565e6 | |||
| ec60ca88f1 | |||
| 17664acf9e | |||
| 18f7454a76 | |||
| e7b5917888 | |||
| 5c57968ba9 | |||
| 795c926c1f | |||
| 8735a9740c | |||
| 02d0adc6bf | |||
| 24067666ed | |||
| 9264333eda | |||
| 66216d57ae | |||
| 5100a669b0 | |||
| 4ad89b68a7 | |||
| 758acaff9f | |||
| fed96b9c58 | |||
| 2d072ee09a | |||
| b55b6e3fd5 | |||
| 238b928236 | |||
| 24ce0ca6df | |||
| 3cf934b920 | |||
| f68ea3edb0 | |||
| 16321c9461 | |||
| f9530f6d9a | |||
| 83f2f0834d | |||
| 6632aa8f95 | |||
| 8a558c3121 | |||
| d434fde92e | |||
| 5afb60df32 | |||
| 3394e6eb01 | |||
| 3818c2c059 | |||
| 0afc543b5f | |||
| adee46e3fc | |||
| 1db74867e6 | |||
| 0f24a63d32 | |||
| 3640809502 | |||
| 289d5357be | |||
| 315530d1ea | |||
| f36773a4c4 | |||
| b35163936f | |||
| 7a2c9d6d17 | |||
| eb2a8dc128 | |||
| bec2c91331 | |||
| c6e727de06 | |||
| 3253e7d407 | |||
| bce2ad2ed8 | |||
| 0eff29ef4a | |||
| 492bf6cdb8 | |||
| b0317f4001 | |||
| 58ff6458b0 | |||
| d9c40f5124 | |||
| 68bc31e29a | |||
| 9b23ac5fd2 | |||
| d31bff14c3 | |||
| 150f92484a | |||
| fa74832fb9 | |||
| 2eab3db77d | |||
| 0a1bf22f7e | |||
| d58244c1f8 | |||
| db626ea516 | |||
| fd8f7685a1 | |||
| 944b156528 | |||
| 76100593cc | |||
| 732d590344 | |||
| 46cd953b80 | |||
| c645035bbe | |||
| 9e76620cd3 | |||
| faddda6201 | |||
| de9c1706c0 |
@@ -41,16 +41,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
|
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
|
||||||
|
|
||||||
# Build firmware and SPIFFS
|
# Build firmware and LittleFS
|
||||||
echo "Building firmware and SPIFFS..."
|
echo "Building firmware and LittleFS..."
|
||||||
pio run -e esp32dev
|
pio run -e esp32dev
|
||||||
pio run -t buildfs
|
pio run -t buildfs
|
||||||
|
|
||||||
# Copy firmware binary
|
# Copy firmware binary
|
||||||
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
|
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
|
||||||
|
|
||||||
# Create SPIFFS binary - direct copy without header
|
# Create LittleFS binary - direct copy without header
|
||||||
cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
|
cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
|
||||||
|
|
||||||
# Create full binary
|
# Create full binary
|
||||||
(cd .pio/build/esp32dev &&
|
(cd .pio/build/esp32dev &&
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
0x1000 bootloader.bin \
|
0x1000 bootloader.bin \
|
||||||
0x8000 partitions.bin \
|
0x8000 partitions.bin \
|
||||||
0x10000 firmware.bin \
|
0x10000 firmware.bin \
|
||||||
0x3D0000 spiffs.bin)
|
0x3D0000 littlefs.bin)
|
||||||
|
|
||||||
# Verify file sizes
|
# Verify file sizes
|
||||||
echo "File sizes:"
|
echo "File sizes:"
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
|
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
|
||||||
|
|
||||||
# Always build firmware and SPIFFS
|
# Always build firmware and LittleFS
|
||||||
echo "Building firmware and SPIFFS..."
|
echo "Building firmware and LittleFS..."
|
||||||
pio run -e esp32dev
|
pio run -e esp32dev
|
||||||
pio run -t buildfs
|
pio run -t buildfs
|
||||||
|
|
||||||
# Copy firmware binary
|
# Copy firmware binary
|
||||||
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
|
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
|
||||||
|
|
||||||
# Create SPIFFS binary - direct copy without header
|
# Create LittleFS binary - direct copy without header
|
||||||
cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
|
cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
|
||||||
|
|
||||||
# Create full binary (always)
|
# Create full binary (always)
|
||||||
(cd .pio/build/esp32dev &&
|
(cd .pio/build/esp32dev &&
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
0x1000 bootloader.bin \
|
0x1000 bootloader.bin \
|
||||||
0x8000 partitions.bin \
|
0x8000 partitions.bin \
|
||||||
0x10000 firmware.bin \
|
0x10000 firmware.bin \
|
||||||
0x3D0000 spiffs.bin)
|
0x3D0000 littlefs.bin)
|
||||||
|
|
||||||
# Verify file sizes
|
# Verify file sizes
|
||||||
echo "File sizes:"
|
echo "File sizes:"
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_firmware_v${VERSION}.bin"
|
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_firmware_v${VERSION}.bin"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add SPIFFS and full binary only if they exist
|
# Add LittleFS and full binary only if they exist
|
||||||
if [ -f "upgrade_filaman_website_v${VERSION}.bin" ]; then
|
if [ -f "upgrade_filaman_website_v${VERSION}.bin" ]; then
|
||||||
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_website_v${VERSION}.bin"
|
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_website_v${VERSION}.bin"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.pio
|
||||||
|
.vscode
|
||||||
|
data
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"regex": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iomanip": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,106 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.4.1] - 2025-03-10
|
||||||
|
### Added
|
||||||
|
- added new .step, now with correct individual parts
|
||||||
|
- added changelog
|
||||||
|
- Add files via upload
|
||||||
|
- added .stp files of modifications
|
||||||
|
- added merged picture
|
||||||
|
- added pictures of components bought from AliE
|
||||||
|
- Add files via upload
|
||||||
|
- added pictures for heat insert location
|
||||||
|
- added pictures showing heat insert location
|
||||||
|
- remove unnecessary delay in MQTT setup and add delay before restart
|
||||||
|
- add new 3D print file for Filaman scale
|
||||||
|
- added Discord Server
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- update platformio.ini for version v1.4.1
|
||||||
|
- refactor length calculation to convert total length to meters before formatting
|
||||||
|
- Merge pull request #16 from spitzbirne32/main
|
||||||
|
- improved housing to show display better
|
||||||
|
- removed CAD, as they were all duplicates
|
||||||
|
- typo in AliE link
|
||||||
|
- Delete usermod/spitzbirne32/STL/README.md
|
||||||
|
- Update README.md
|
||||||
|
- moved pictures of parts into dedicated folders
|
||||||
|
- Update README.md
|
||||||
|
- Update README.md
|
||||||
|
- Update README.md
|
||||||
|
- Delete usermod/spitzbirne32/STL/ScaleTop_Heatinsert_Location_usermod_spitzbirne32_.png
|
||||||
|
- Delete usermod/spitzbirne32/STL/Housing_Heatinsert_Location_usermod_spitzbirne32_.png
|
||||||
|
- created folders
|
||||||
|
- Update README.md
|
||||||
|
- Update README.md
|
||||||
|
- Create README.md
|
||||||
|
- Update README.md
|
||||||
|
- Update README.md
|
||||||
|
- Create README.md
|
||||||
|
- Merge pull request #15 from ManuelW77/main
|
||||||
|
- Merge pull request #14 from janecker/scale-calibration-rework
|
||||||
|
- Reworks the scale calibration handling
|
||||||
|
- remove redundant scale calibration checks and enhance task management
|
||||||
|
- enhance AMS data handling and streamline spool auto-setting logic
|
||||||
|
- adjust stack size and improve scale calibration logic
|
||||||
|
- update labels and input types for better clarity and functionality
|
||||||
|
- update documentation for clarity and accuracy
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- correct typo in console log for total length
|
||||||
|
|
||||||
|
|
||||||
|
## [1.4.0] - 2025-03-01
|
||||||
|
### Added
|
||||||
|
- add support for Spoolman Octoprint Plugin in README files
|
||||||
|
- add OctoPrint integration with configurable fields and update functionality
|
||||||
|
- add version comparison function and check for outdated versions before updates
|
||||||
|
- remove unused version and protocol fields from JSON output; add error message for insufficient memory
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- update NFC tag references to include NTAG213 and clarify storage capacity
|
||||||
|
- bump version to 1.4.0
|
||||||
|
- remove unused version and protocol fields from NFC data packet
|
||||||
|
- sort vendors alphabetically in the dropdown list
|
||||||
|
- Merge pull request #10 from janecker/nfc-improvements
|
||||||
|
- Improves NFC Tag handling
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.99] - 2025-02-28
|
||||||
|
### Changed
|
||||||
|
- update platformio.ini for version v1.3.99
|
||||||
|
- update workflows to build firmware with LittleFS instead of SPIFFS
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.98] - 2025-02-28
|
||||||
|
### Changed
|
||||||
|
- update platformio.ini for version v1.3.98
|
||||||
|
- migrate from SPIFFS to LittleFS for file handling
|
||||||
|
- remove unused VSCode settings file
|
||||||
|
- remove commented-out spoolman and filaman data from api.cpp
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.97] - 2025-02-28
|
||||||
|
### Added
|
||||||
|
- füge Bestätigungsmeldung für Spool-Einstellung hinzu
|
||||||
|
- verbessere WLAN-Konfiguration und füge mDNS-Unterstützung hinzu
|
||||||
|
- aktualisiere OLED-Anzeige mit Versionsnummer und verbessere Textausrichtung
|
||||||
|
- füge regelmäßige WLAN-Verbindungsüberprüfung hinzu
|
||||||
|
- aktualisiere Schaltplan-Bild
|
||||||
|
- zeige Versionsnummer im OLED-Display an
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- update platformio.ini for version v1.3.97
|
||||||
|
- entferne text-shadow von deaktivierten Schaltflächen
|
||||||
|
- füge Link zum Wiki für detaillierte Informationen über die Nutzung hinzu
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Speichernutzung optimiert
|
||||||
|
- behebe doppelte http.end() Aufrufe in checkSpoolmanExtraFields
|
||||||
|
- optimiere Verzögerungen und Stackgrößen in NFC-Task-Funktionen
|
||||||
|
- entferne ungenutzte Bibliotheken und Debug-Ausgaben aus main.cpp
|
||||||
|
|
||||||
|
|
||||||
## [1.3.96] - 2025-02-25
|
## [1.3.96] - 2025-02-25
|
||||||
### Added
|
### Added
|
||||||
- füge Unterstützung für Spoolman-Einstellungen hinzu und aktualisiere die Benutzeroberfläche
|
- füge Unterstützung für Spoolman-Einstellungen hinzu und aktualisiere die Benutzeroberfläche
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
# FilaMan - Filament Management System
|
# FilaMan - Filament Management System
|
||||||
|
|
||||||
FilaMan ist ein Filament-Managementsystem für den 3D-Druck. Es verwendet ESP32-Hardware für Gewichtsmessungen und NFC-Tag-Management.
|
FilaMan ist ein Filament-Managementsystem für den 3D-Druck. Es verwendet ESP32-Hardware für Gewichtsmessungen und NFC-Tag-Management.
|
||||||
Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bablulab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen.
|
Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bambu Lab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen.
|
||||||
Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambulab](https://bambulab.com/en-us) 3D-Druckern und sowie dem [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG Format.
|
Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambu Lab](https://bambulab.com/en-us) 3D-Druckern.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Weitere Bilder finden Sie im [img Ordner](/img/)
|
Weitere Bilder finden Sie im [img Ordner](/img/)
|
||||||
oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
|
oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
|
||||||
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||||
|
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||||
|
|
||||||
|
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||||
|
|
||||||
### ESP32 Hardware-Funktionen
|
### ESP32 Hardware-Funktionen
|
||||||
- **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung.
|
- **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung.
|
||||||
- **NFC-Tag Lesen/Schreiben:** PN532-Modul zum Lesen und Schreiben von Filamentdaten auf NFC-Tags.
|
- **NFC-Tag Lesen/Schreiben:** PN532-Modul zum Lesen und Schreiben von Filamentdaten auf NFC-Tags.
|
||||||
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman).
|
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, BambuLab, Spoolman).
|
||||||
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
|
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
|
||||||
- **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung.
|
- **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung.
|
||||||
- **NFC-Tag NTAG215:** Verwendung von NTAG215 wegen ausreichendem Speicherplatz auf dem Tag
|
- **NFC-Tag NTAG213 NTAG215:** Verwendung von NTAG213 (144 Bytes - Kapazität), besser NTAG215 (540 Bytes - Kapazität) wegen ausreichendem Speicherplatz auf dem Tag
|
||||||
|
|
||||||
### Weboberflächen-Funktionen
|
### Weboberflächen-Funktionen
|
||||||
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
|
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
|
||||||
- **NFC-Tag-Verwaltung:**
|
- **NFC-Tag-Verwaltung:**
|
||||||
- Filamentdaten auf NFC-Tags schreiben.
|
- Filamentdaten auf NFC-Tags schreiben.
|
||||||
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool)
|
- Ermöglicht automatische Spulenerkennung im AMS.
|
||||||
- Ermöglicht automatische Spulenerkennung im AMS
|
- **Bambu Lab 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.
|
||||||
- Unterstützung für externe Spulenhalter.
|
- Unterstützung für externe Spulenhalter.
|
||||||
@@ -33,6 +35,7 @@ Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaO
|
|||||||
- Filtern und Auswählen von Filamenten.
|
- Filtern und Auswählen von Filamenten.
|
||||||
- Automatische Aktualisierung der Spulengewichte.
|
- Automatische Aktualisierung der Spulengewichte.
|
||||||
- Verfolgung von NFC-Tag-Zuweisungen.
|
- Verfolgung von NFC-Tag-Zuweisungen.
|
||||||
|
- 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>
|
||||||
@@ -59,7 +62,7 @@ Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaO
|
|||||||
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
||||||
- **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen.
|
- **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen.
|
||||||
[Amazon Link](https://amzn.eu/d/jfIuQXb)
|
[Amazon Link](https://amzn.eu/d/jfIuQXb)
|
||||||
- **NFC Tags Ntag215:** RFID Tag
|
- **NFC Tags NTAG213 NTA215:** RFID Tag
|
||||||
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
||||||
|
|
||||||
### Pin-Konfiguration
|
### Pin-Konfiguration
|
||||||
@@ -118,7 +121,7 @@ Du musst Spoolman auf DEBUG Modus setzten, da man bisher in Spoolman keine CORS
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Schritt-für-Schritt Installation
|
## Schritt-für-Schritt Installation
|
||||||
### Einfache Installation
|
### Einfache Installation (Google Chrome oder Microsoft Edge)
|
||||||
1. **Gehe auf [FilaMan Installer](https://www.filaman.app/installer.html)**
|
1. **Gehe auf [FilaMan Installer](https://www.filaman.app/installer.html)**
|
||||||
|
|
||||||
2. **Stecke dein ESP an den Rechner und klicke Connect**
|
2. **Stecke dein ESP an den Rechner und klicke Connect**
|
||||||
|
|||||||
@@ -4,14 +4,18 @@
|
|||||||
|
|
||||||
FilaMan is a filament management system for 3D printing. It uses ESP32 hardware for weight measurement and NFC tag management.
|
FilaMan is a filament management system for 3D printing. It uses ESP32 hardware for weight measurement and NFC tag management.
|
||||||
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
|
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
|
||||||
The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format.
|
The system integrates seamlessly with [Bambu Lab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management.
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
More Images can be found in the [img Folder](/img/)
|
More Images can be found in the [img Folder](/img/)
|
||||||
or my website:[FilaMan Website](https://www.filaman.app)
|
or my website: [FilaMan Website](https://www.filaman.app)
|
||||||
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||||
|
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||||
|
|
||||||
|
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||||
|
|
||||||
### ESP32 Hardware Features
|
### ESP32 Hardware Features
|
||||||
- **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking.
|
- **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking.
|
||||||
@@ -19,13 +23,12 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
|
|||||||
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
|
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
|
||||||
- **WiFi Connectivity:** WiFiManager for easy network configuration.
|
- **WiFi Connectivity:** WiFiManager for easy network configuration.
|
||||||
- **MQTT Integration:** Connects to Bambu Lab printer for AMS control.
|
- **MQTT Integration:** Connects to Bambu Lab printer for AMS control.
|
||||||
- **NFC-Tag NTAG215:** Use NTAG215 because of enaught space on the Tag
|
- **NFC-Tag NTAG213 NTAG215:** Use NTAG213 (Capacity of 144 bytes), better NTAG215 (Capacity of 540 bytes) because of enaught space on the Tag.
|
||||||
|
|
||||||
### Web Interface Features
|
### Web Interface Features
|
||||||
- **Real-time Updates:** WebSocket connection for live data updates.
|
- **Real-time Updates:** WebSocket connection for live data updates.
|
||||||
- **NFC Tag Management:**
|
- **NFC Tag Management:**
|
||||||
- Write filament data to NFC tags.
|
- 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
|
- so you can use it with automatic Spool detection in AMS
|
||||||
- **Bambulab AMS Integration:**
|
- **Bambulab AMS Integration:**
|
||||||
- Display current AMS tray contents.
|
- Display current AMS tray contents.
|
||||||
@@ -36,6 +39,7 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
|
|||||||
- Filter and select filaments.
|
- Filter and select filaments.
|
||||||
- Update spool weights automatically.
|
- Update spool weights automatically.
|
||||||
- Track NFC tag assignments.
|
- Track NFC tag assignments.
|
||||||
|
- 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>
|
||||||
@@ -62,7 +66,7 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
|
|||||||
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
||||||
- **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/jfIuQXb)
|
[Amazon Link](https://amzn.eu/d/jfIuQXb)
|
||||||
- **NFC Tags Ntag215:** RFID Tag
|
- **NFC Tags NTAG213 NTAG215:** RFID Tag
|
||||||
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +127,7 @@ You have to activate Spoolman in debug mode, because you are not able to set COR
|
|||||||
|
|
||||||
|
|
||||||
## Step-by-Step Installation
|
## Step-by-Step Installation
|
||||||
### Easy Installation
|
### Easy Installation (Google Chrome or Microsoft Edge)
|
||||||
1. **Go to [FilaMan Installer](https://www.filaman.app/installer.html)**
|
1. **Go to [FilaMan Installer](https://www.filaman.app/installer.html)**
|
||||||
|
|
||||||
2. **Plug you device in and push Connect button**
|
2. **Plug you device in and push Connect button**
|
||||||
|
|||||||
@@ -141,15 +141,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Rechte Spalte -->
|
|
||||||
<div class="column">
|
|
||||||
<div class="feature-box">
|
|
||||||
<h2>Bambu AMS</h2>
|
|
||||||
<div id="amsDataContainer">
|
|
||||||
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -60,13 +60,13 @@ function initWebSocket() {
|
|||||||
try {
|
try {
|
||||||
socket = new WebSocket('ws://' + window.location.host + '/ws');
|
socket = new WebSocket('ws://' + window.location.host + '/ws');
|
||||||
|
|
||||||
socket.onopen = function() {
|
socket.onopen = function () {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung
|
startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onclose = function() {
|
socket.onclose = function () {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||||
@@ -79,7 +79,7 @@ function initWebSocket() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onerror = function(error) {
|
socket.onerror = function (error) {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||||
@@ -91,7 +91,7 @@ function initWebSocket() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onmessage = function(event) {
|
socket.onmessage = function (event) {
|
||||||
lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort
|
lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort
|
||||||
|
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
@@ -115,7 +115,7 @@ function initWebSocket() {
|
|||||||
// Add click handler only when offline
|
// Add click handler only when offline
|
||||||
if (!data.bambu_connected) {
|
if (!data.bambu_connected) {
|
||||||
bambuDot.style.cursor = 'pointer';
|
bambuDot.style.cursor = 'pointer';
|
||||||
bambuDot.onclick = function() {
|
bambuDot.onclick = function () {
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: 'reconnect',
|
type: 'reconnect',
|
||||||
@@ -133,7 +133,7 @@ function initWebSocket() {
|
|||||||
// Add click handler only when offline
|
// Add click handler only when offline
|
||||||
if (!data.spoolman_connected) {
|
if (!data.spoolman_connected) {
|
||||||
spoolmanDot.style.cursor = 'pointer';
|
spoolmanDot.style.cursor = 'pointer';
|
||||||
spoolmanDot.onclick = function() {
|
spoolmanDot.onclick = function () {
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: 'reconnect',
|
type: 'reconnect',
|
||||||
@@ -189,26 +189,26 @@ function updateConnectionStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Event Listeners
|
// Event Listeners
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
initWebSocket();
|
initWebSocket();
|
||||||
|
|
||||||
// Event Listener für Checkbox
|
// Event Listener für Checkbox
|
||||||
document.getElementById("onlyWithoutSmId").addEventListener("change", function() {
|
document.getElementById("onlyWithoutSmId").addEventListener("change", function () {
|
||||||
const spoolsData = window.getSpoolData();
|
const spoolsData = window.getSpoolData();
|
||||||
window.populateVendorDropdown(spoolsData);
|
window.populateVendorDropdown(spoolsData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event Listener für Spoolman Events
|
// Event Listener für Spoolman Events
|
||||||
document.addEventListener('spoolDataLoaded', function(event) {
|
document.addEventListener('spoolDataLoaded', function (event) {
|
||||||
window.populateVendorDropdown(event.detail);
|
window.populateVendorDropdown(event.detail);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('spoolmanError', function(event) {
|
document.addEventListener('spoolmanError', function (event) {
|
||||||
showNotification(`Spoolman Error: ${event.detail.message}`, false);
|
showNotification(`Spoolman Error: ${event.detail.message}`, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('filamentSelected', function(event) {
|
document.addEventListener('filamentSelected', function (event) {
|
||||||
updateNfcInfo();
|
updateNfcInfo();
|
||||||
// Zeige Spool-Buttons wenn ein Filament ausgewählt wurde
|
// Zeige Spool-Buttons wenn ein Filament ausgewählt wurde
|
||||||
const selectedText = document.getElementById("selected-filament").textContent;
|
const selectedText = document.getElementById("selected-filament").textContent;
|
||||||
@@ -218,12 +218,12 @@ document.addEventListener('filamentSelected', function(event) {
|
|||||||
// Hilfsfunktion für kontrastreiche Textfarbe
|
// Hilfsfunktion für kontrastreiche Textfarbe
|
||||||
function getContrastColor(hexcolor) {
|
function getContrastColor(hexcolor) {
|
||||||
// Konvertiere Hex zu RGB
|
// Konvertiere Hex zu RGB
|
||||||
const r = parseInt(hexcolor.substr(0,2),16);
|
const r = parseInt(hexcolor.substr(0, 2), 16);
|
||||||
const g = parseInt(hexcolor.substr(2,2),16);
|
const g = parseInt(hexcolor.substr(2, 2), 16);
|
||||||
const b = parseInt(hexcolor.substr(4,2),16);
|
const b = parseInt(hexcolor.substr(4, 2), 16);
|
||||||
|
|
||||||
// Berechne Helligkeit (YIQ Formel)
|
// Berechne Helligkeit (YIQ Formel)
|
||||||
const yiq = ((r*299)+(g*587)+(b*114))/1000;
|
const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
|
||||||
|
|
||||||
// Return schwarz oder weiß basierend auf Helligkeit
|
// Return schwarz oder weiß basierend auf Helligkeit
|
||||||
return (yiq >= 128) ? '#000000' : '#FFFFFF';
|
return (yiq >= 128) ? '#000000' : '#FFFFFF';
|
||||||
@@ -283,7 +283,7 @@ function displayAmsData(amsData) {
|
|||||||
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;">
|
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;">
|
||||||
</button>`;
|
</button>`;
|
||||||
|
|
||||||
// Nur für nicht-leere Trays den Button-HTML erstellen
|
// Nur für nicht-leere Trays den Button-HTML erstellen
|
||||||
const outButtonHtml = `
|
const outButtonHtml = `
|
||||||
<button class="spool-button" onclick="handleSpoolOut()"
|
<button class="spool-button" onclick="handleSpoolOut()"
|
||||||
style="position: absolute; top: -35px; right: -15px;
|
style="position: absolute; top: -35px; right: -15px;
|
||||||
@@ -490,7 +490,7 @@ function handleSpoolIn(amsId, trayId) {
|
|||||||
nozzle_temp_max: parseInt(maxTemp),
|
nozzle_temp_max: parseInt(maxTemp),
|
||||||
type: selectedSpool.filament.material,
|
type: selectedSpool.filament.material,
|
||||||
brand: selectedSpool.filament.vendor.name,
|
brand: selectedSpool.filament.vendor.name,
|
||||||
tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(),
|
tray_info_idx: selectedSpool.filament.extra.bambu_idx?.replace(/['"]+/g, '').trim() || '',
|
||||||
cali_idx: "-1" // Default-Wert setzen
|
cali_idx: "-1" // Default-Wert setzen
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -640,8 +640,6 @@ function writeNfcTag() {
|
|||||||
|
|
||||||
// Erstelle das NFC-Datenpaket mit korrekten Datentypen
|
// Erstelle das NFC-Datenpaket mit korrekten Datentypen
|
||||||
const nfcData = {
|
const nfcData = {
|
||||||
version: "2.0",
|
|
||||||
protocol: "openspool",
|
|
||||||
color_hex: selectedSpool.filament.color_hex || "FFFFFF",
|
color_hex: selectedSpool.filament.color_hex || "FFFFFF",
|
||||||
type: selectedSpool.filament.material,
|
type: selectedSpool.filament.material,
|
||||||
min_temp: minTemp,
|
min_temp: minTemp,
|
||||||
@@ -687,5 +685,4 @@ function showNotification(message, isSuccess) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
notification.remove();
|
notification.remove();
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 3000);
|
}, 3000);}
|
||||||
}
|
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
<!-- head -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FilaMan - Filament Management Tool</title>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
fetch('/api/version')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const versionSpan = document.querySelector('.version');
|
||||||
|
if (versionSpan) {
|
||||||
|
versionSpan.textContent = 'v' + data.version;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching version:', error));
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="navbar">
|
||||||
|
<div style="display: flex; align-items: center; gap: 2rem;">
|
||||||
|
<img src="/logo.png" alt="FilaMan Logo" class="logo">
|
||||||
|
<div class="logo-text">
|
||||||
|
<h1>FilaMan<span class="version"></span></h1>
|
||||||
|
<h4>Filament Management Tool</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<nav style="display: flex; gap: 1rem;">
|
||||||
|
<a href="/">Start</a>
|
||||||
|
<a href="/waage">Scale</a>
|
||||||
|
<a href="/spoolman">Spoolman/Bambu</a>
|
||||||
|
<a href="/about">About</a>
|
||||||
|
<a href="/upgrade">Upgrade</a>
|
||||||
|
</nav>
|
||||||
|
<div class="status-container">
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-dot" id="bambuDot"></span>B
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-dot" id="spoolmanDot"></span>S
|
||||||
|
</div>
|
||||||
|
<div class="ram-status" id="ramStatus"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- head -->
|
||||||
|
|
||||||
|
<div class="connection-status hidden">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<span>Connection lost. Trying to reconnect...</span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="three-column-layout">
|
||||||
|
<!-- Linke Spalte -->
|
||||||
|
<div class="column">
|
||||||
|
<div class="feature-box">
|
||||||
|
<div class="statistics-header">
|
||||||
|
<h2>Statistics</h2>
|
||||||
|
<button id="refreshSpoolman" class="refresh-button">
|
||||||
|
<span>Refresh Spoolman</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="statistics-column">
|
||||||
|
<h3>Spools</h3>
|
||||||
|
<div class="spool-stat" style="display: flex; justify-content: center; align-items: center;">
|
||||||
|
<span class="stat-label">total:</span>
|
||||||
|
<span class="stat-value" id="totalSpools"></span>
|
||||||
|
<div style="width: auto;"></div>
|
||||||
|
<span class="stat-label">without Tag:</span>
|
||||||
|
<span class="stat-value" id="spoolsWithoutTag"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="statistics-grid">
|
||||||
|
<div class="statistics-column">
|
||||||
|
<h3>Overview</h3>
|
||||||
|
<ul class="statistics-list">
|
||||||
|
<li>
|
||||||
|
<span class="stat-label">Manufacturer:</span>
|
||||||
|
<span class="stat-value" id="totalVendors"></span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span class="stat-label">Weight:</span>
|
||||||
|
<span class="stat-value"><span id="totalWeight"></span></span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span class="stat-label">Length:</span>
|
||||||
|
<span class="stat-value"><span id="totalLength"></span></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="statistics-column">
|
||||||
|
<h3>Materials</h3>
|
||||||
|
<ul class="statistics-list" id="materialsList">
|
||||||
|
<!-- Wird dynamisch befüllt -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="feature-box">
|
||||||
|
<div class="nfc-header">
|
||||||
|
<h2>NFC-Tag</h2>
|
||||||
|
<span id="nfcStatusIndicator" class="status-circle"></span>
|
||||||
|
</div>
|
||||||
|
<div class="nfc-status-display"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mittlere Spalte -->
|
||||||
|
<div class="column">
|
||||||
|
<div class="feature-box">
|
||||||
|
<h2>Spoolman Spools</h2>
|
||||||
|
<label for="vendorSelect">Manufacturer:</label>
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<select id="vendorSelect" class="styled-select">
|
||||||
|
<option value="">Please choose...</option>
|
||||||
|
</select>
|
||||||
|
<label style="margin-left: 10px;">
|
||||||
|
<input type="checkbox" id="onlyWithoutSmId" checked onchange="updateFilamentDropdown()">
|
||||||
|
Only Spools without SM ID
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="filamentSection" class="feature-box hidden">
|
||||||
|
<label>Spool / Filament:</label>
|
||||||
|
<div class="custom-dropdown">
|
||||||
|
<div class="dropdown-button" onclick="toggleFilamentDropdown()">
|
||||||
|
<div class="selected-color" id="selected-color"></div>
|
||||||
|
<span id="selected-filament">Please choose...</span>
|
||||||
|
<span class="dropdown-arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-content" id="filament-dropdown-content">
|
||||||
|
<!-- Optionen werden dynamisch hinzugefügt -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p id="nfcInfo" class="nfc-status"></p>
|
||||||
|
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write
|
||||||
|
Tag</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Rechte Spalte -->
|
||||||
|
<div class="column">
|
||||||
|
<div class="feature-box">
|
||||||
|
<h2>Bambu AMS</h2>
|
||||||
|
<div id="amsDataContainer">
|
||||||
|
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="spoolman.js"></script>
|
||||||
|
<script src="rfid.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -52,11 +52,43 @@
|
|||||||
if (spoolmanUrl && spoolmanUrl.trim() !== "") {
|
if (spoolmanUrl && spoolmanUrl.trim() !== "") {
|
||||||
document.getElementById('spoolmanUrl').value = spoolmanUrl;
|
document.getElementById('spoolmanUrl').value = spoolmanUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize OctoPrint fields visibility
|
||||||
|
toggleOctoFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function removeBambuCredentials() {
|
||||||
|
fetch('/api/bambu?remove=true')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
document.getElementById('bambuIp').value = '';
|
||||||
|
document.getElementById('bambuSerial').value = '';
|
||||||
|
document.getElementById('bambuCode').value = '';
|
||||||
|
document.getElementById('autoSend').checked = false;
|
||||||
|
document.getElementById('autoSendTime').value = '';
|
||||||
|
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials removed!';
|
||||||
|
// Reload with forced cache refresh after short delay
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload(true);
|
||||||
|
window.location.href = '/';
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
document.getElementById('bambuStatusMessage').innerText = 'Error while removing Bambu Credentials.';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
document.getElementById('bambuStatusMessage').innerText = 'Error while removing: ' + error.message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function checkSpoolmanInstance() {
|
function checkSpoolmanInstance() {
|
||||||
const url = document.getElementById('spoolmanUrl').value;
|
const url = document.getElementById('spoolmanUrl').value;
|
||||||
fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}`)
|
const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
|
||||||
|
const spoolmanOctoUrl = document.getElementById('spoolmanOctoUrl').value;
|
||||||
|
const spoolmanOctoToken = document.getElementById('spoolmanOctoToken').value;
|
||||||
|
|
||||||
|
fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}&octoEnabled=${spoolmanOctoEnabled}&octoUrl=${spoolmanOctoUrl}&octoToken=${spoolmanOctoToken}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.healthy) {
|
if (data.healthy) {
|
||||||
@@ -82,7 +114,12 @@
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.healthy) {
|
if (data.healthy) {
|
||||||
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!';
|
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!';
|
||||||
} else {
|
// Reload with forced cache refresh after short delay
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload(true);
|
||||||
|
window.location.href = '/';
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.';
|
document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -90,6 +127,15 @@
|
|||||||
document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message;
|
document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls visibility of OctoPrint configuration fields based on checkbox state
|
||||||
|
* Called on page load and when checkbox changes
|
||||||
|
*/
|
||||||
|
function toggleOctoFields() {
|
||||||
|
const octoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
|
||||||
|
document.getElementById('octoFields').style.display = octoEnabled ? 'block' : 'none';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var spoolmanUrl = "{{spoolmanUrl}}";
|
var spoolmanUrl = "{{spoolmanUrl}}";
|
||||||
@@ -102,6 +148,17 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5>
|
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5>
|
||||||
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port">
|
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port">
|
||||||
|
<h5 class="card-title">If you want to enable sending Spool to Spoolman Octoprint Plugin:</h5>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin
|
||||||
|
</p>
|
||||||
|
<div id="octoFields" style="display: none;">
|
||||||
|
<p>
|
||||||
|
<input type="text" id="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instanz:port" value="{{spoolmanOctoUrl}}">
|
||||||
|
<input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
|
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
|
||||||
<p id="statusMessage"></p>
|
<p id="statusMessage"></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,15 +184,16 @@
|
|||||||
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>
|
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>
|
||||||
<div class="input-group" style="display: flex; margin-bottom: 0;">
|
<div class="input-group" style="display: flex; margin-bottom: 0;">
|
||||||
<label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label>
|
<label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label>
|
||||||
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait time in Seconds:</label>
|
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait for Spool in Sec:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group" style="display: flex;">
|
<div class="input-group" style="display: flex;">
|
||||||
<input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
|
<input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
|
||||||
<input type="text" id="autoSendTime" placeholder="Time to wait for new Spool" value="{{autoSendTime}}" style="width: 100px;">
|
<input type="number" min="60" id="autoSendTime" placeholder="Time to wait" value="{{autoSendTime}}" style="width: 100px;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>
|
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>
|
||||||
<p id="bambuStatusMessage"></p>
|
<button style="margin: 0; background-color: red;" onclick="removeBambuCredentials()">Remove Credentials</button>
|
||||||
|
<p id="bambuStatusMessage"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -86,10 +86,10 @@ function populateVendorDropdown(data, selectedSmId = null) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Nach der Schleife: Formatierung der Gesamtlänge
|
// Nach der Schleife: Formatierung der Gesamtlänge
|
||||||
console.log("Total Lenght: ", totalLength);
|
const lengthInM = totalLength / 1000; // erst in m umrechnen
|
||||||
const formattedLength = totalLength > 1000
|
const formattedLength = lengthInM > 1000
|
||||||
? (totalLength / 1000).toFixed(2) + " km"
|
? (lengthInM / 1000).toFixed(2) + " km"
|
||||||
: totalLength.toFixed(2) + " m";
|
: lengthInM.toFixed(2) + " m";
|
||||||
|
|
||||||
// Formatierung des Gesamtgewichts (von g zu kg zu t)
|
// Formatierung des Gesamtgewichts (von g zu kg zu t)
|
||||||
const weightInKg = totalWeight / 1000; // erst in kg umrechnen
|
const weightInKg = totalWeight / 1000; // erst in kg umrechnen
|
||||||
@@ -97,13 +97,15 @@ function populateVendorDropdown(data, selectedSmId = null) {
|
|||||||
? (weightInKg / 1000).toFixed(2) + " t"
|
? (weightInKg / 1000).toFixed(2) + " t"
|
||||||
: weightInKg.toFixed(2) + " kg";
|
: weightInKg.toFixed(2) + " kg";
|
||||||
|
|
||||||
// Dropdown mit gefilterten Herstellern befüllen
|
// Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert
|
||||||
Object.entries(filteredVendors).forEach(([id, name]) => {
|
Object.entries(filteredVendors)
|
||||||
const option = document.createElement("option");
|
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name
|
||||||
option.value = id;
|
.forEach(([id, name]) => {
|
||||||
option.textContent = name;
|
const option = document.createElement("option");
|
||||||
vendorSelect.appendChild(option);
|
option.value = id;
|
||||||
});
|
option.textContent = name;
|
||||||
|
vendorSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById("totalSpools").textContent = totalSpools;
|
document.getElementById("totalSpools").textContent = totalSpools;
|
||||||
document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag;
|
document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag;
|
||||||
@@ -145,6 +147,13 @@ function updateFilamentDropdown(selectedSmId = null) {
|
|||||||
|
|
||||||
if (vendorId) {
|
if (vendorId) {
|
||||||
const filteredFilaments = spoolsData.filter(spool => {
|
const filteredFilaments = spoolsData.filter(spool => {
|
||||||
|
if (!spool?.filament?.vendor?.id) {
|
||||||
|
console.log('Problem aufgetreten bei: ', spool?.filament?.vendor);
|
||||||
|
console.log('Problematische Spulen:',
|
||||||
|
spoolsData.filter(spool => !spool?.filament?.vendor?.id));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const hasValidNfcId = spool.extra &&
|
const hasValidNfcId = spool.extra &&
|
||||||
spool.extra.nfc_id &&
|
spool.extra.nfc_id &&
|
||||||
spool.extra.nfc_id !== '""' &&
|
spool.extra.nfc_id !== '""' &&
|
||||||
@@ -238,18 +247,6 @@ async function fetchSpoolData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Exportiere Funktionen
|
|
||||||
window.getSpoolData = () => spoolsData;
|
|
||||||
window.reloadSpoolData = initSpoolman;
|
|
||||||
window.populateVendorDropdown = populateVendorDropdown;
|
|
||||||
window.updateFilamentDropdown = updateFilamentDropdown;
|
|
||||||
window.toggleFilamentDropdown = () => {
|
|
||||||
const content = document.getElementById("filament-dropdown-content");
|
|
||||||
content.classList.toggle("show");
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Event Listener
|
// Event Listener
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initSpoolman();
|
initSpoolman();
|
||||||
|
|||||||
@@ -188,14 +188,18 @@ label {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"], input[type="submit"] {
|
input[type="text"], input[type="submit"], input[type="number"] {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"]:focus {
|
input[type="number"] {
|
||||||
|
width: 108px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:focus, input[type="number"]:focus {
|
||||||
border-color: #007bff;
|
border-color: #007bff;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
@@ -1015,7 +1019,7 @@ input[type="submit"]:disabled,
|
|||||||
color: #000;
|
color: #000;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
text-shadow: 0 !important;
|
text-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-container {
|
.progress-container {
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
if (data.status === 'success' || lastReceivedProgress >= 98) {
|
if (data.status === 'success' || lastReceivedProgress >= 98) {
|
||||||
clearTimeout(wsReconnectTimer);
|
clearTimeout(wsReconnectTimer);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
window.location.reload(true);
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
@@ -164,6 +165,7 @@
|
|||||||
status.className = 'status success';
|
status.className = 'status success';
|
||||||
status.style.display = 'block';
|
status.style.display = 'block';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
window.location.reload(true);
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
<h5 class="card-title">Sacle Calibration</h5>
|
<h5 class="card-title">Sacle Calibration</h5>
|
||||||
<button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button>
|
<button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button>
|
||||||
<button id="tareBtn" class="btn btn-secondary">Tare Scale</button>
|
<button id="tareBtn" class="btn btn-secondary">Tare Scale</button>
|
||||||
|
Enable Auto-TARE <input type="checkbox" id="autoTareCheckbox" onchange="setAutoTare(this.checked);"
|
||||||
|
{{autoTare}}>
|
||||||
<div id="statusMessage" class="mt-3"></div>
|
<div id="statusMessage" class="mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,6 +141,14 @@
|
|||||||
payload: 'tare'
|
payload: 'tare'
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
// Add auto-tare function
|
||||||
|
function setAutoTare(enabled) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'scale',
|
||||||
|
payload: 'setAutoTare',
|
||||||
|
enabled: enabled
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// WebSocket-Verbindung beim Laden der Seite initiieren
|
// WebSocket-Verbindung beim Laden der Seite initiieren
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|||||||
|
After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 283 KiB |
@@ -9,7 +9,9 @@
|
|||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
version = "1.3.96"
|
version = "0.2.1"
|
||||||
|
to_old_version = "0.2.0"
|
||||||
|
|
||||||
##
|
##
|
||||||
[env:esp32dev]
|
[env:esp32dev]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
@@ -33,7 +35,8 @@ 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 = spiffs
|
||||||
|
board_build.filesystem = littlefs
|
||||||
; Update partition settings
|
; Update partition settings
|
||||||
board_build.partitions = partitions.csv
|
board_build.partitions = partitions.csv
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
@@ -44,13 +47,14 @@ build_flags =
|
|||||||
-Os
|
-Os
|
||||||
-ffunction-sections
|
-ffunction-sections
|
||||||
-fdata-sections
|
-fdata-sections
|
||||||
-DNDEBUG
|
#-DNDEBUG
|
||||||
-mtext-section-literals
|
-mtext-section-literals
|
||||||
-DVERSION=\"${common.version}\"
|
-DVERSION=\"${common.version}\"
|
||||||
|
-DTOOLDVERSION=\"${common.to_old_version}\"
|
||||||
-DASYNCWEBSERVER_REGEX
|
-DASYNCWEBSERVER_REGEX
|
||||||
-DCORE_DEBUG_LEVEL=3
|
#-DCORE_DEBUG_LEVEL=3
|
||||||
-DCONFIG_ARDUHAL_LOG_COLORS=1
|
-DCONFIG_ARDUHAL_LOG_COLORS=1
|
||||||
-DOTA_DEBUG=1
|
#-DOTA_DEBUG=1
|
||||||
-DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1
|
-DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1
|
||||||
-DBOOT_APP_PARTITION_OTA_0=1
|
-DBOOT_APP_PARTITION_OTA_0=1
|
||||||
-DCONFIG_LWIP_TCP_MSL=60000
|
-DCONFIG_LWIP_TCP_MSL=60000
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def copy_file(input_file, output_file):
|
|||||||
|
|
||||||
def should_compress(file):
|
def should_compress(file):
|
||||||
# Skip compression for spoolman.html
|
# Skip compression for spoolman.html
|
||||||
if file == 'spoolman.html':
|
if file == 'spoolman.html' or file == 'waage.html':
|
||||||
return False
|
return False
|
||||||
# Komprimiere nur bestimmte Dateitypen
|
# Komprimiere nur bestimmte Dateitypen
|
||||||
return file.endswith(('.js', '.png', '.css', '.html'))
|
return file.endswith(('.js', '.png', '.css', '.html'))
|
||||||
|
|||||||
@@ -3,40 +3,19 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include "commonFS.h"
|
#include "commonFS.h"
|
||||||
|
|
||||||
bool spoolman_connected = false;
|
volatile spoolmanApiStateType spoolmanApiState = API_INIT;
|
||||||
String spoolmanUrl = "";
|
String spoolmanUrl = "";
|
||||||
|
bool octoEnabled = false;
|
||||||
|
String octoUrl = "";
|
||||||
|
String octoToken = "";
|
||||||
|
|
||||||
struct SendToApiParams {
|
struct SendToApiParams {
|
||||||
String httpType;
|
String httpType;
|
||||||
String spoolsUrl;
|
String spoolsUrl;
|
||||||
String updatePayload;
|
String updatePayload;
|
||||||
|
String octoToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
// Spoolman Data
|
|
||||||
{
|
|
||||||
"version":"1.0",
|
|
||||||
"protocol":"openspool",
|
|
||||||
"color_hex":"AF7933",
|
|
||||||
"type":"ABS",
|
|
||||||
"min_temp":175,
|
|
||||||
"max_temp":275,
|
|
||||||
"brand":"Overture"
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilaMan Data
|
|
||||||
{
|
|
||||||
"version":"1.0",
|
|
||||||
"protocol":"openspool",
|
|
||||||
"color_hex":"AF7933",
|
|
||||||
"type":"ABS",
|
|
||||||
"min_temp":175,
|
|
||||||
"max_temp":275,
|
|
||||||
"brand":"Overture",
|
|
||||||
"sm_id":
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
JsonDocument fetchSingleSpoolInfo(int spoolId) {
|
JsonDocument fetchSingleSpoolInfo(int spoolId) {
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
|
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
|
||||||
@@ -106,20 +85,27 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sendToApi(void *parameter) {
|
void sendToApi(void *parameter) {
|
||||||
SendToApiParams* params = (SendToApiParams*)parameter;
|
spoolmanApiState = API_TRANSMITTING;
|
||||||
|
SendToApiParams *params = (SendToApiParams *)parameter;
|
||||||
|
|
||||||
// Extrahiere die Werte
|
// Extrahiere die Werte
|
||||||
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;
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
http.setReuse(false);
|
||||||
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);
|
||||||
|
|
||||||
int httpCode = http.PUT(updatePayload);
|
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
|
||||||
|
httpCode = http.PUT(updatePayload);
|
||||||
|
|
||||||
if (httpCode == HTTP_CODE_OK) {
|
if (httpCode == HTTP_CODE_OK) {
|
||||||
Serial.println("Spoolman erfolgreich aktualisiert");
|
Serial.println("Spoolman erfolgreich aktualisiert");
|
||||||
@@ -130,10 +116,12 @@ void sendToApi(void *parameter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
// Speicher freigeben
|
// Speicher freigeben
|
||||||
delete params;
|
delete params;
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
|
spoolmanApiState = API_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updateSpoolTagId(String uidString, const char* payload) {
|
bool updateSpoolTagId(String uidString, const char* payload) {
|
||||||
@@ -183,6 +171,7 @@ bool updateSpoolTagId(String uidString, const char* payload) {
|
|||||||
0, // Priorität
|
0, // Priorität
|
||||||
NULL // Task-Handle (nicht benötigt)
|
NULL // Task-Handle (nicht benötigt)
|
||||||
);
|
);
|
||||||
|
updateDoc.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -219,10 +208,49 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
|
|||||||
0, // Priorität
|
0, // Priorität
|
||||||
NULL // Task-Handle (nicht benötigt)
|
NULL // Task-Handle (nicht benötigt)
|
||||||
);
|
);
|
||||||
|
updateDoc.clear();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool updateSpoolOcto(int spoolId) {
|
||||||
|
String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool";
|
||||||
|
Serial.print("Update Spule in Octoprint mit URL: ");
|
||||||
|
Serial.println(spoolsUrl);
|
||||||
|
|
||||||
|
JsonDocument updateDoc;
|
||||||
|
updateDoc["spool_id"] = spoolId;
|
||||||
|
updateDoc["tool"] = "tool0";
|
||||||
|
|
||||||
|
String updatePayload;
|
||||||
|
serializeJson(updateDoc, updatePayload);
|
||||||
|
Serial.print("Update Payload: ");
|
||||||
|
Serial.println(updatePayload);
|
||||||
|
|
||||||
|
SendToApiParams* params = new SendToApiParams();
|
||||||
|
if (params == nullptr) {
|
||||||
|
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
params->httpType = "POST";
|
||||||
|
params->spoolsUrl = spoolsUrl;
|
||||||
|
params->updatePayload = updatePayload;
|
||||||
|
params->octoToken = octoToken;
|
||||||
|
|
||||||
|
// Erstelle die Task
|
||||||
|
BaseType_t result = xTaskCreate(
|
||||||
|
sendToApi, // Task-Funktion
|
||||||
|
"SendToApiTask", // Task-Name
|
||||||
|
4096, // Stackgröße in Bytes
|
||||||
|
(void*)params, // Parameter
|
||||||
|
0, // Priorität
|
||||||
|
NULL // Task-Handle (nicht benötigt)
|
||||||
|
);
|
||||||
|
updateDoc.clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool updateSpoolBambuData(String payload) {
|
bool updateSpoolBambuData(String payload) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError error = deserializeJson(doc, payload);
|
DeserializationError error = deserializeJson(doc, payload);
|
||||||
@@ -410,12 +438,13 @@ bool checkSpoolmanExtraFields() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("-------- ENDE Prüfe Felder --------");
|
Serial.println("-------- ENDE Prüfe Felder --------");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +479,8 @@ bool checkSpoolmanInstance(const String& url) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
spoolman_connected = true;
|
spoolmanApiState = API_IDLE;
|
||||||
|
oledShowTopRow();
|
||||||
return strcmp(status, "healthy") == 0;
|
return strcmp(status, "healthy") == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,17 +489,24 @@ bool checkSpoolmanInstance(const String& url) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool saveSpoolmanUrl(const String& url) {
|
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) {
|
||||||
if (!checkSpoolmanInstance(url)) return false;
|
if (!checkSpoolmanInstance(url)) return false;
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc["url"] = url;
|
doc["url"] = url;
|
||||||
Serial.print("Speichere URL in Datei: ");
|
doc["octoEnabled"] = octoOn;
|
||||||
Serial.println(url);
|
doc["octoUrl"] = octoWh;
|
||||||
|
doc["octoToken"] = octoTk;
|
||||||
|
Serial.print("Speichere Spoolman Data in Datei: ");
|
||||||
|
Serial.println(doc.as<String>());
|
||||||
if (!saveJsonValue("/spoolman_url.json", doc)) {
|
if (!saveJsonValue("/spoolman_url.json", doc)) {
|
||||||
Serial.println("Fehler beim Speichern der Spoolman-URL.");
|
Serial.println("Fehler beim Speichern der Spoolman-URL.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
spoolmanUrl = url;
|
spoolmanUrl = url;
|
||||||
|
octoEnabled = octoOn;
|
||||||
|
octoUrl = octoWh;
|
||||||
|
octoToken = octoTk;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -477,6 +514,13 @@ bool saveSpoolmanUrl(const String& url) {
|
|||||||
String loadSpoolmanUrl() {
|
String loadSpoolmanUrl() {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) {
|
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) {
|
||||||
|
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false;
|
||||||
|
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>())
|
||||||
|
{
|
||||||
|
octoUrl = doc["octoUrl"].as<String>();
|
||||||
|
octoToken = doc["octoToken"].as<String>();
|
||||||
|
}
|
||||||
|
|
||||||
return doc["url"].as<String>();
|
return doc["url"].as<String>();
|
||||||
}
|
}
|
||||||
Serial.println("Keine gültige Spoolman-URL gefunden.");
|
Serial.println("Keine gültige Spoolman-URL gefunden.");
|
||||||
|
|||||||
@@ -6,12 +6,22 @@
|
|||||||
#include "website.h"
|
#include "website.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
API_INIT,
|
||||||
|
API_IDLE,
|
||||||
|
API_TRANSMITTING
|
||||||
|
} spoolmanApiStateType;
|
||||||
|
|
||||||
|
extern volatile spoolmanApiStateType spoolmanApiState;
|
||||||
extern bool spoolman_connected;
|
extern bool spoolman_connected;
|
||||||
extern String spoolmanUrl;
|
extern String spoolmanUrl;
|
||||||
|
extern bool octoEnabled;
|
||||||
|
extern String octoUrl;
|
||||||
|
extern String octoToken;
|
||||||
|
|
||||||
bool checkSpoolmanInstance(const String& url);
|
bool checkSpoolmanInstance(const String& url);
|
||||||
bool saveSpoolmanUrl(const String& url);
|
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
|
||||||
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
|
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
|
||||||
@@ -19,5 +29,6 @@ bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion z
|
|||||||
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
|
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
|
||||||
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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,11 +18,17 @@ PubSubClient client(sslClient);
|
|||||||
TaskHandle_t BambuMqttTask;
|
TaskHandle_t BambuMqttTask;
|
||||||
|
|
||||||
String report_topic = "";
|
String report_topic = "";
|
||||||
//String request_topic = "";
|
String request_topic = "";
|
||||||
const char* bambu_username = "bblp";
|
const char* bambu_username = "bblp";
|
||||||
const char* bambu_ip = nullptr;
|
const char* bambu_ip = nullptr;
|
||||||
const char* bambu_accesscode = nullptr;
|
const char* bambu_accesscode = nullptr;
|
||||||
const char* bambu_serialnr = nullptr;
|
const char* bambu_serialnr = nullptr;
|
||||||
|
|
||||||
|
String g_bambu_ip = "";
|
||||||
|
String g_bambu_accesscode = "";
|
||||||
|
String g_bambu_serialnr = "";
|
||||||
|
bool bambuDisabled = false;
|
||||||
|
|
||||||
bool bambu_connected = false;
|
bool bambu_connected = false;
|
||||||
bool autoSendToBambu = false;
|
bool autoSendToBambu = false;
|
||||||
int autoSetToBambuSpoolId = 0;
|
int autoSetToBambuSpoolId = 0;
|
||||||
@@ -32,6 +38,35 @@ int ams_count = 0;
|
|||||||
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
|
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
|
||||||
AMSData ams_data[MAX_AMS]; // Definition des Arrays;
|
AMSData ams_data[MAX_AMS]; // Definition des Arrays;
|
||||||
|
|
||||||
|
bool removeBambuCredentials()
|
||||||
|
{
|
||||||
|
if (BambuMqttTask)
|
||||||
|
{
|
||||||
|
vTaskDelete(BambuMqttTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!removeJsonValue("/bambu_credentials.json"))
|
||||||
|
{
|
||||||
|
Serial.println("Fehler beim Löschen der Bambu-Credentials.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Löschen der globalen Variablen
|
||||||
|
g_bambu_ip = "";
|
||||||
|
g_bambu_accesscode = "";
|
||||||
|
g_bambu_serialnr = "";
|
||||||
|
bambu_ip = nullptr;
|
||||||
|
bambu_accesscode = nullptr;
|
||||||
|
bambu_serialnr = nullptr;
|
||||||
|
autoSendToBambu = false;
|
||||||
|
autoSetToBambuSpoolId = 0;
|
||||||
|
ams_count = 0;
|
||||||
|
amsJsonData = "";
|
||||||
|
|
||||||
|
bambuDisabled = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -69,6 +104,11 @@ bool loadBambuCredentials() {
|
|||||||
String ip = doc["bambu_ip"].as<String>();
|
String ip = doc["bambu_ip"].as<String>();
|
||||||
String code = doc["bambu_accesscode"].as<String>();
|
String code = doc["bambu_accesscode"].as<String>();
|
||||||
String serial = doc["bambu_serialnr"].as<String>();
|
String serial = doc["bambu_serialnr"].as<String>();
|
||||||
|
|
||||||
|
g_bambu_ip = ip;
|
||||||
|
g_bambu_accesscode = code;
|
||||||
|
g_bambu_serialnr = serial;
|
||||||
|
|
||||||
if (doc["autoSendToBambu"].is<bool>()) autoSendToBambu = doc["autoSendToBambu"].as<bool>();
|
if (doc["autoSendToBambu"].is<bool>()) autoSendToBambu = doc["autoSendToBambu"].as<bool>();
|
||||||
if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>();
|
if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>();
|
||||||
|
|
||||||
@@ -77,12 +117,12 @@ bool loadBambuCredentials() {
|
|||||||
serial.trim();
|
serial.trim();
|
||||||
|
|
||||||
// Dynamische Speicherallokation für die globalen Pointer
|
// Dynamische Speicherallokation für die globalen Pointer
|
||||||
bambu_ip = strdup(ip.c_str());
|
bambu_ip = g_bambu_ip.c_str();
|
||||||
bambu_accesscode = strdup(code.c_str());
|
bambu_accesscode = g_bambu_accesscode.c_str();
|
||||||
bambu_serialnr = strdup(serial.c_str());
|
bambu_serialnr = g_bambu_serialnr.c_str();
|
||||||
|
|
||||||
report_topic = "device/" + String(bambu_serialnr) + "/report";
|
report_topic = "device/" + String(bambu_serialnr) + "/report";
|
||||||
//request_topic = "device/" + String(bambu_serialnr) + "/request";
|
request_topic = "device/" + String(bambu_serialnr) + "/request";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
|
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
|
||||||
@@ -112,6 +152,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
|
|||||||
}
|
}
|
||||||
doc.clear();
|
doc.clear();
|
||||||
}
|
}
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
// Laden der bambu_filaments.json
|
// Laden der bambu_filaments.json
|
||||||
if (!loadJsonValue("/bambu_filaments.json", doc))
|
if (!loadJsonValue("/bambu_filaments.json", doc))
|
||||||
@@ -185,45 +226,105 @@ FilamentResult findFilamentIdx(String brand, String type) {
|
|||||||
return {"GFL99", "PLA"};
|
return {"GFL99", "PLA"};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sendMqttMessage(String payload) {
|
bool sendMqttMessage(const String& payload) {
|
||||||
Serial.println("Sending MQTT message");
|
// Check MQTT client state first
|
||||||
Serial.println(payload);
|
if (!client.connected()) {
|
||||||
if (client.publish(report_topic.c_str(), payload.c_str()))
|
Serial.println("Error: MQTT client not connected when trying to send message");
|
||||||
{
|
Serial.print("MQTT client state: ");
|
||||||
return true;
|
Serial.println(client.state());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// Check if request_topic is empty
|
||||||
|
if (request_topic.isEmpty()) {
|
||||||
|
Serial.println("Error: request_topic is empty!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check payload
|
||||||
|
if (payload.isEmpty()) {
|
||||||
|
Serial.println("Error: Empty payload!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Attempting to send MQTT message:");
|
||||||
|
Serial.println("Topic: " + request_topic);
|
||||||
|
Serial.println("Payload: " + payload);
|
||||||
|
Serial.println("Payload length: " + String(payload.length()));
|
||||||
|
|
||||||
|
// Try to publish with QoS 1 and retain flag false
|
||||||
|
bool published = client.publish(request_topic.c_str(), payload.c_str(), false);
|
||||||
|
|
||||||
|
if (published) {
|
||||||
|
Serial.println("MQTT message published successfully");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to publish MQTT message");
|
||||||
|
Serial.print("MQTT client state after publish attempt: ");
|
||||||
|
Serial.println(client.state());
|
||||||
|
|
||||||
|
// Additional error information
|
||||||
|
switch(client.state()) {
|
||||||
|
case -4:
|
||||||
|
Serial.println("Reason: Connection timeout");
|
||||||
|
break;
|
||||||
|
case -3:
|
||||||
|
Serial.println("Reason: Connection lost");
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
Serial.println("Reason: Connect failed");
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
Serial.println("Reason: Disconnected");
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
Serial.println("Reason: Connected but publish failed");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.println("Reason: Unknown error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setBambuSpool(String payload) {
|
bool setBambuSpool(String payload) {
|
||||||
Serial.println("Spool settings in");
|
Serial.println("Spool settings received from WebSocket");
|
||||||
Serial.println(payload);
|
Serial.println(payload);
|
||||||
|
|
||||||
// Parse the JSON
|
// Parse the JSON
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError error = deserializeJson(doc, payload);
|
DeserializationError error = deserializeJson(doc, payload);
|
||||||
if (error) {
|
if (error) {
|
||||||
Serial.print("Error parsing JSON: ");
|
Serial.print("Error parsing setBambuSpool-JSON: ");
|
||||||
Serial.println(error.c_str());
|
Serial.println(error.c_str());
|
||||||
|
Serial.print("Free Heap-Memory: ");
|
||||||
|
Serial.println(ESP.getFreeHeap());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int amsId = doc["amsId"];
|
// Check if we have all required fields
|
||||||
int trayId = doc["trayId"];
|
if (!doc["amsId"].is<int>() || !doc["trayId"].is<int>()) {
|
||||||
|
Serial.println("Error: Missing or invalid required fields in payload");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int amsId = doc["amsId"].as<int>();
|
||||||
|
int trayId = doc["trayId"].as<int>();
|
||||||
String color = doc["color"].as<String>();
|
String color = doc["color"].as<String>();
|
||||||
color.toUpperCase();
|
color.toUpperCase();
|
||||||
int minTemp = doc["nozzle_temp_min"];
|
int minTemp = doc["nozzle_temp_min"] | 0; // Default to 0 if not present
|
||||||
int maxTemp = doc["nozzle_temp_max"];
|
int maxTemp = doc["nozzle_temp_max"] | 0; // Default to 0 if not present
|
||||||
String type = doc["type"].as<String>();
|
String type = doc["type"] | ""; // Default to empty string if not present
|
||||||
(type == "PLA+") ? type = "PLA" : type;
|
(type == "PLA+") ? type = "PLA" : type;
|
||||||
String brand = doc["brand"].as<String>();
|
String brand = doc["brand"] | ""; // Default to empty string if not present
|
||||||
String tray_info_idx = (doc["tray_info_idx"].as<String>() != "-1") ? doc["tray_info_idx"].as<String>() : "";
|
String tray_info_idx = (doc["tray_info_idx"].as<String>() != "-1") ? doc["tray_info_idx"].as<String>() : "";
|
||||||
if (tray_info_idx == "") {
|
if (tray_info_idx == "") {
|
||||||
if (brand != "" && type != "") {
|
if (brand != "" && type != "") {
|
||||||
FilamentResult result = findFilamentIdx(brand, type);
|
FilamentResult result = findFilamentIdx(brand, type);
|
||||||
tray_info_idx = result.key;
|
tray_info_idx = result.key;
|
||||||
type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp
|
type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp
|
||||||
|
Serial.println("Found filament idx: " + tray_info_idx + " for type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String setting_id = doc["bambu_setting_id"].as<String>();
|
String setting_id = doc["bambu_setting_id"].as<String>();
|
||||||
@@ -231,6 +332,7 @@ bool setBambuSpool(String payload) {
|
|||||||
|
|
||||||
doc.clear();
|
doc.clear();
|
||||||
|
|
||||||
|
// Create MQTT message
|
||||||
doc["print"]["sequence_id"] = "0";
|
doc["print"]["sequence_id"] = "0";
|
||||||
doc["print"]["command"] = "ams_filament_setting";
|
doc["print"]["command"] = "ams_filament_setting";
|
||||||
doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
|
doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
|
||||||
@@ -239,26 +341,25 @@ bool setBambuSpool(String payload) {
|
|||||||
doc["print"]["nozzle_temp_min"] = minTemp;
|
doc["print"]["nozzle_temp_min"] = minTemp;
|
||||||
doc["print"]["nozzle_temp_max"] = maxTemp;
|
doc["print"]["nozzle_temp_max"] = maxTemp;
|
||||||
doc["print"]["tray_type"] = type;
|
doc["print"]["tray_type"] = type;
|
||||||
//doc["print"]["cali_idx"] = (cali_idx != "") ? cali_idx : "";
|
|
||||||
doc["print"]["tray_info_idx"] = tray_info_idx;
|
doc["print"]["tray_info_idx"] = tray_info_idx;
|
||||||
doc["print"]["setting_id"] = setting_id;
|
doc["print"]["setting_id"] = setting_id;
|
||||||
|
|
||||||
// Serialize the JSON
|
// Serialize and send MQTT message
|
||||||
String output;
|
String output;
|
||||||
serializeJson(doc, output);
|
serializeJson(doc, output);
|
||||||
|
Serial.println("Sending to Bambu printer:");
|
||||||
|
Serial.println(output);
|
||||||
|
|
||||||
if (sendMqttMessage(output)) {
|
if (!sendMqttMessage(output)) {
|
||||||
Serial.println("Spool successfully set");
|
Serial.println("Failed to send filament settings to printer");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Serial.println("Failed to set spool");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Serial.println("Filament settings sent successfully");
|
||||||
doc.clear();
|
doc.clear();
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
|
// Send calibration if available
|
||||||
if (cali_idx != "") {
|
if (cali_idx != "") {
|
||||||
yield();
|
yield();
|
||||||
doc["print"]["sequence_id"] = "0";
|
doc["print"]["sequence_id"] = "0";
|
||||||
@@ -267,21 +368,18 @@ bool setBambuSpool(String payload) {
|
|||||||
doc["print"]["nozzle_diameter"] = "0.4";
|
doc["print"]["nozzle_diameter"] = "0.4";
|
||||||
doc["print"]["cali_idx"] = cali_idx.toInt();
|
doc["print"]["cali_idx"] = cali_idx.toInt();
|
||||||
doc["print"]["tray_id"] = trayId < 200 ? trayId : 254;
|
doc["print"]["tray_id"] = trayId < 200 ? trayId : 254;
|
||||||
//doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
|
|
||||||
|
|
||||||
// Serialize the JSON
|
|
||||||
String output;
|
String output;
|
||||||
serializeJson(doc, output);
|
serializeJson(doc, output);
|
||||||
|
Serial.println("Sending calibration to printer:");
|
||||||
|
Serial.println(output);
|
||||||
|
|
||||||
if (sendMqttMessage(output)) {
|
if (!sendMqttMessage(output)) {
|
||||||
Serial.println("Extrusion calibration successfully set");
|
Serial.println("Failed to send calibration settings to printer");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Serial.println("Failed to set extrusion calibration");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Serial.println("Calibration settings sent successfully");
|
||||||
doc.clear();
|
doc.clear();
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
@@ -303,12 +401,98 @@ void autoSetSpool(int spoolId, uint8_t trayId) {
|
|||||||
Serial.println(spoolInfo.as<String>());
|
Serial.println(spoolInfo.as<String>());
|
||||||
|
|
||||||
setBambuSpool(spoolInfo.as<String>());
|
setBambuSpool(spoolInfo.as<String>());
|
||||||
|
|
||||||
|
oledShowMessage("Spool set");
|
||||||
}
|
}
|
||||||
|
|
||||||
// id wieder zurücksetzen damit abgeschlossen
|
// id wieder zurücksetzen damit abgeschlossen
|
||||||
autoSetToBambuSpoolId = 0;
|
autoSetToBambuSpoolId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateAmsWsData(JsonDocument& doc, JsonArray& amsArray, int& ams_count, JsonObject& vtTray) {
|
||||||
|
// Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden
|
||||||
|
ams_count = amsArray.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < ams_count && i < 16; i++) {
|
||||||
|
JsonObject amsObj = amsArray[i];
|
||||||
|
JsonArray trayArray = amsObj["tray"].as<JsonArray>();
|
||||||
|
|
||||||
|
ams_data[i].ams_id = i; // Setze die AMS-ID
|
||||||
|
for (int j = 0; j < trayArray.size() && j < 4; j++) { // Annahme: Maximal 4 Trays pro AMS
|
||||||
|
JsonObject trayObj = trayArray[j];
|
||||||
|
|
||||||
|
ams_data[i].trays[j].id = trayObj["id"].as<uint8_t>();
|
||||||
|
ams_data[i].trays[j].tray_info_idx = trayObj["tray_info_idx"].as<String>();
|
||||||
|
ams_data[i].trays[j].tray_type = trayObj["tray_type"].as<String>();
|
||||||
|
ams_data[i].trays[j].tray_sub_brands = trayObj["tray_sub_brands"].as<String>();
|
||||||
|
ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
|
||||||
|
ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
|
||||||
|
ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
|
||||||
|
if (trayObj["tray_type"].as<String>() == "") ams_data[i].trays[j].setting_id = "";
|
||||||
|
ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setze ams_count auf die Anzahl der normalen AMS
|
||||||
|
ams_count = amsArray.size();
|
||||||
|
|
||||||
|
// Wenn externe Spule vorhanden, füge sie hinzu
|
||||||
|
if (doc["print"]["vt_tray"].is<JsonObject>()) {
|
||||||
|
//JsonObject vtTray = doc["print"]["vt_tray"];
|
||||||
|
int extIdx = ams_count; // Index für externe Spule
|
||||||
|
ams_data[extIdx].ams_id = 255; // Spezielle ID für externe Spule
|
||||||
|
ams_data[extIdx].trays[0].id = 254; // Spezielle ID für externes Tray
|
||||||
|
ams_data[extIdx].trays[0].tray_info_idx = vtTray["tray_info_idx"].as<String>();
|
||||||
|
ams_data[extIdx].trays[0].tray_type = vtTray["tray_type"].as<String>();
|
||||||
|
ams_data[extIdx].trays[0].tray_sub_brands = vtTray["tray_sub_brands"].as<String>();
|
||||||
|
ams_data[extIdx].trays[0].tray_color = vtTray["tray_color"].as<String>();
|
||||||
|
ams_data[extIdx].trays[0].nozzle_temp_min = vtTray["nozzle_temp_min"].as<int>();
|
||||||
|
ams_data[extIdx].trays[0].nozzle_temp_max = vtTray["nozzle_temp_max"].as<int>();
|
||||||
|
|
||||||
|
if (doc["print"]["vt_tray"]["tray_type"].as<String>() != "")
|
||||||
|
{
|
||||||
|
//ams_data[extIdx].trays[0].setting_id = vtTray["setting_id"].as<String>();
|
||||||
|
ams_data[extIdx].trays[0].cali_idx = vtTray["cali_idx"].as<String>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ams_data[extIdx].trays[0].setting_id = "";
|
||||||
|
ams_data[extIdx].trays[0].cali_idx = "";
|
||||||
|
}
|
||||||
|
ams_count++; // Erhöhe ams_count für die externe Spule
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle JSON für WebSocket-Clients
|
||||||
|
JsonDocument wsDoc;
|
||||||
|
JsonArray wsArray = wsDoc.to<JsonArray>();
|
||||||
|
|
||||||
|
for (int i = 0; i < ams_count; i++) {
|
||||||
|
JsonObject amsObj = wsArray.add<JsonObject>();
|
||||||
|
amsObj["ams_id"] = ams_data[i].ams_id;
|
||||||
|
|
||||||
|
JsonArray trays = amsObj["tray"].to<JsonArray>();
|
||||||
|
int maxTrays = (ams_data[i].ams_id == 255) ? 1 : 4;
|
||||||
|
|
||||||
|
for (int j = 0; j < maxTrays; j++) {
|
||||||
|
JsonObject trayObj = trays.add<JsonObject>();
|
||||||
|
trayObj["id"] = ams_data[i].trays[j].id;
|
||||||
|
trayObj["tray_info_idx"] = ams_data[i].trays[j].tray_info_idx;
|
||||||
|
trayObj["tray_type"] = ams_data[i].trays[j].tray_type;
|
||||||
|
trayObj["tray_sub_brands"] = ams_data[i].trays[j].tray_sub_brands;
|
||||||
|
trayObj["tray_color"] = ams_data[i].trays[j].tray_color;
|
||||||
|
trayObj["nozzle_temp_min"] = ams_data[i].trays[j].nozzle_temp_min;
|
||||||
|
trayObj["nozzle_temp_max"] = ams_data[i].trays[j].nozzle_temp_max;
|
||||||
|
trayObj["setting_id"] = ams_data[i].trays[j].setting_id;
|
||||||
|
trayObj["cali_idx"] = ams_data[i].trays[j].cali_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(wsArray, amsJsonData);
|
||||||
|
wsDoc.clear();
|
||||||
|
Serial.println("AMS data updated");
|
||||||
|
sendAmsData(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||||
String message;
|
String message;
|
||||||
@@ -320,23 +504,18 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
|||||||
// JSON-Dokument parsen
|
// JSON-Dokument parsen
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError error = deserializeJson(doc, message);
|
DeserializationError error = deserializeJson(doc, message);
|
||||||
|
message = "";
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
Serial.print("Fehler beim Parsen des JSON: ");
|
Serial.print("Free Heap-Memory: ");
|
||||||
|
Serial.println(ESP.getFreeHeap());
|
||||||
|
Serial.print("Error parsing MQTT-JSON: ");
|
||||||
Serial.println(error.c_str());
|
Serial.println(error.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn bambu auto set spool aktiv und eine spule erkannt und mqtt meldung das neue spule im ams
|
|
||||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0 &&
|
|
||||||
doc["print"]["command"].as<String>() == "push_status" && doc["print"]["ams"]["tray_pre"].as<uint8_t>()
|
|
||||||
&& !doc["print"]["ams"]["ams"].as<JsonArray>())
|
|
||||||
{
|
|
||||||
autoSetSpool(autoSetToBambuSpoolId, doc["print"]["ams"]["tray_pre"].as<uint8_t>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren
|
// Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren
|
||||||
if (doc["print"]["upgrade_state"].is<JsonObject>())
|
if (doc["print"]["upgrade_state"].is<JsonObject>() || (doc["print"]["command"].is<String>() && doc["print"]["command"] == "push_status"))
|
||||||
{
|
{
|
||||||
// Prüfen ob AMS-Daten vorhanden sind
|
// Prüfen ob AMS-Daten vorhanden sind
|
||||||
if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>())
|
if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>())
|
||||||
@@ -372,160 +551,78 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
|||||||
// Vergleiche die Trays
|
// Vergleiche die Trays
|
||||||
for (int j = 0; j < trayArray.size() && j < 4 && !hasChanges; j++) {
|
for (int j = 0; j < trayArray.size() && j < 4 && !hasChanges; j++) {
|
||||||
JsonObject trayObj = trayArray[j];
|
JsonObject trayObj = trayArray[j];
|
||||||
|
|
||||||
|
if (trayObj["tray_type"].as<String>() == "") ams_data[storedIndex].trays[j].setting_id = "";
|
||||||
|
if (trayObj["setting_id"].isNull()) trayObj["setting_id"] = "";
|
||||||
if (trayObj["tray_info_idx"].as<String>() != ams_data[storedIndex].trays[j].tray_info_idx ||
|
if (trayObj["tray_info_idx"].as<String>() != ams_data[storedIndex].trays[j].tray_info_idx ||
|
||||||
trayObj["tray_type"].as<String>() != ams_data[storedIndex].trays[j].tray_type ||
|
trayObj["tray_type"].as<String>() != ams_data[storedIndex].trays[j].tray_type ||
|
||||||
trayObj["tray_color"].as<String>() != ams_data[storedIndex].trays[j].tray_color ||
|
trayObj["tray_color"].as<String>() != ams_data[storedIndex].trays[j].tray_color ||
|
||||||
|
(trayObj["setting_id"].as<String>() != "" && trayObj["setting_id"].as<String>() != ams_data[storedIndex].trays[j].setting_id) ||
|
||||||
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
|
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
|
|
||||||
|
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||||
|
{
|
||||||
|
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe die externe Spule
|
// Prüfe die externe Spule
|
||||||
if (!hasChanges && doc["print"]["vt_tray"].is<JsonObject>()) {
|
JsonObject vtTray = doc["print"]["vt_tray"];
|
||||||
JsonObject vtTray = doc["print"]["vt_tray"];
|
if (doc["print"]["vt_tray"].is<JsonObject>()) {
|
||||||
bool foundExternal = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < ams_count; i++) {
|
for (int i = 0; i < ams_count; i++) {
|
||||||
if (ams_data[i].ams_id == 255) {
|
if (ams_data[i].ams_id == 255) {
|
||||||
foundExternal = true;
|
if (vtTray["tray_type"].as<String>() == "") ams_data[i].trays[0].setting_id = "";
|
||||||
|
if (vtTray["setting_id"].isNull()) vtTray["setting_id"] = "";
|
||||||
if (vtTray["tray_info_idx"].as<String>() != ams_data[i].trays[0].tray_info_idx ||
|
if (vtTray["tray_info_idx"].as<String>() != ams_data[i].trays[0].tray_info_idx ||
|
||||||
vtTray["tray_type"].as<String>() != ams_data[i].trays[0].tray_type ||
|
vtTray["tray_type"].as<String>() != ams_data[i].trays[0].tray_type ||
|
||||||
vtTray["tray_color"].as<String>() != ams_data[i].trays[0].tray_color ||
|
vtTray["tray_color"].as<String>() != ams_data[i].trays[0].tray_color ||
|
||||||
|
(vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) ||
|
||||||
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
|
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
|
|
||||||
|
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||||
|
{
|
||||||
|
autoSetSpool(autoSetToBambuSpoolId, 254);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if (!foundExternal) hasChanges = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasChanges) return;
|
if (!hasChanges) return;
|
||||||
|
|
||||||
// Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden
|
updateAmsWsData(doc, amsArray, ams_count, vtTray);
|
||||||
ams_count = amsArray.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < ams_count && i < 16; i++) {
|
|
||||||
JsonObject amsObj = amsArray[i];
|
|
||||||
JsonArray trayArray = amsObj["tray"].as<JsonArray>();
|
|
||||||
|
|
||||||
ams_data[i].ams_id = i; // Setze die AMS-ID
|
|
||||||
for (int j = 0; j < trayArray.size() && j < 4; j++) { // Annahme: Maximal 4 Trays pro AMS
|
|
||||||
JsonObject trayObj = trayArray[j];
|
|
||||||
|
|
||||||
ams_data[i].trays[j].id = trayObj["id"].as<uint8_t>();
|
|
||||||
ams_data[i].trays[j].tray_info_idx = trayObj["tray_info_idx"].as<String>();
|
|
||||||
ams_data[i].trays[j].tray_type = trayObj["tray_type"].as<String>();
|
|
||||||
ams_data[i].trays[j].tray_sub_brands = trayObj["tray_sub_brands"].as<String>();
|
|
||||||
ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
|
|
||||||
ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
|
|
||||||
ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
|
|
||||||
ams_data[i].trays[j].setting_id = trayObj["setting_id"].as<String>();
|
|
||||||
ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setze ams_count auf die Anzahl der normalen AMS
|
|
||||||
ams_count = amsArray.size();
|
|
||||||
|
|
||||||
// Wenn externe Spule vorhanden, füge sie hinzu
|
|
||||||
if (doc["print"]["vt_tray"].is<JsonObject>()) {
|
|
||||||
JsonObject vtTray = doc["print"]["vt_tray"];
|
|
||||||
int extIdx = ams_count; // Index für externe Spule
|
|
||||||
ams_data[extIdx].ams_id = 255; // Spezielle ID für externe Spule
|
|
||||||
ams_data[extIdx].trays[0].id = 254; // Spezielle ID für externes Tray
|
|
||||||
ams_data[extIdx].trays[0].tray_info_idx = vtTray["tray_info_idx"].as<String>();
|
|
||||||
ams_data[extIdx].trays[0].tray_type = vtTray["tray_type"].as<String>();
|
|
||||||
ams_data[extIdx].trays[0].tray_sub_brands = vtTray["tray_sub_brands"].as<String>();
|
|
||||||
ams_data[extIdx].trays[0].tray_color = vtTray["tray_color"].as<String>();
|
|
||||||
ams_data[extIdx].trays[0].nozzle_temp_min = vtTray["nozzle_temp_min"].as<int>();
|
|
||||||
ams_data[extIdx].trays[0].nozzle_temp_max = vtTray["nozzle_temp_max"].as<int>();
|
|
||||||
|
|
||||||
if (doc["print"]["vt_tray"]["tray_type"].as<String>() != "")
|
|
||||||
{
|
|
||||||
ams_data[extIdx].trays[0].setting_id = vtTray["setting_id"].as<String>();
|
|
||||||
ams_data[extIdx].trays[0].cali_idx = vtTray["cali_idx"].as<String>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ams_data[extIdx].trays[0].setting_id = "";
|
|
||||||
ams_data[extIdx].trays[0].cali_idx = "";
|
|
||||||
}
|
|
||||||
ams_count++; // Erhöhe ams_count für die externe Spule
|
|
||||||
}
|
|
||||||
|
|
||||||
// Erstelle JSON für WebSocket-Clients
|
|
||||||
JsonDocument wsDoc;
|
|
||||||
JsonArray wsArray = wsDoc.to<JsonArray>();
|
|
||||||
|
|
||||||
for (int i = 0; i < ams_count; i++) {
|
|
||||||
JsonObject amsObj = wsArray.add<JsonObject>();
|
|
||||||
amsObj["ams_id"] = ams_data[i].ams_id;
|
|
||||||
|
|
||||||
JsonArray trays = amsObj["tray"].to<JsonArray>();
|
|
||||||
int maxTrays = (ams_data[i].ams_id == 255) ? 1 : 4;
|
|
||||||
|
|
||||||
for (int j = 0; j < maxTrays; j++) {
|
|
||||||
JsonObject trayObj = trays.add<JsonObject>();
|
|
||||||
trayObj["id"] = ams_data[i].trays[j].id;
|
|
||||||
trayObj["tray_info_idx"] = ams_data[i].trays[j].tray_info_idx;
|
|
||||||
trayObj["tray_type"] = ams_data[i].trays[j].tray_type;
|
|
||||||
trayObj["tray_sub_brands"] = ams_data[i].trays[j].tray_sub_brands;
|
|
||||||
trayObj["tray_color"] = ams_data[i].trays[j].tray_color;
|
|
||||||
trayObj["nozzle_temp_min"] = ams_data[i].trays[j].nozzle_temp_min;
|
|
||||||
trayObj["nozzle_temp_max"] = ams_data[i].trays[j].nozzle_temp_max;
|
|
||||||
trayObj["setting_id"] = ams_data[i].trays[j].setting_id;
|
|
||||||
trayObj["cali_idx"] = ams_data[i].trays[j].cali_idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serializeJson(wsArray, amsJsonData);
|
|
||||||
Serial.println("AMS data updated");
|
|
||||||
sendAmsData(nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neue Bedingung für ams_filament_setting
|
// Neue Bedingung für ams_filament_setting
|
||||||
if (doc["print"]["command"] == "ams_filament_setting") {
|
if (doc["print"]["command"] == "ams_filament_setting") {
|
||||||
int amsId = doc["print"]["ams_id"].as<int>();
|
int amsId = doc["print"]["ams_id"].as<int>();
|
||||||
int trayId = doc["print"]["tray_id"].as<int>();
|
int trayId = doc["print"]["tray_id"].as<int>();
|
||||||
String settingId = doc["print"]["setting_id"].as<String>();
|
String settingId = (doc["print"]["setting_id"].is<String>()) ? doc["print"]["setting_id"].as<String>() : "";
|
||||||
|
|
||||||
// Finde das entsprechende AMS und Tray
|
// Finde das entsprechende AMS und Tray
|
||||||
for (int i = 0; i < ams_count; i++) {
|
for (int i = 0; i < ams_count; i++) {
|
||||||
if (ams_data[i].ams_id == amsId) {
|
if (ams_data[i].ams_id == amsId) {
|
||||||
// Update setting_id im entsprechenden Tray
|
if (trayId == 254)
|
||||||
ams_data[i].trays[trayId].setting_id = settingId;
|
{
|
||||||
|
// Suche AMS mit ID 255 (externe Spule)
|
||||||
// Erstelle neues JSON für WebSocket-Clients
|
for (int j = 0; j < ams_count; j++) {
|
||||||
JsonDocument wsDoc;
|
if (ams_data[j].ams_id == 255) {
|
||||||
JsonArray wsArray = wsDoc.to<JsonArray>();
|
ams_data[j].trays[0].setting_id = settingId;
|
||||||
|
break;
|
||||||
for (int j = 0; j < ams_count; j++) {
|
}
|
||||||
JsonObject amsObj = wsArray.add<JsonObject>();
|
|
||||||
amsObj["ams_id"] = ams_data[j].ams_id;
|
|
||||||
|
|
||||||
JsonArray trays = amsObj["tray"].to<JsonArray>();
|
|
||||||
int maxTrays = (ams_data[j].ams_id == 255) ? 1 : 4;
|
|
||||||
|
|
||||||
for (int k = 0; k < maxTrays; k++) {
|
|
||||||
JsonObject trayObj = trays.add<JsonObject>();
|
|
||||||
trayObj["id"] = ams_data[j].trays[k].id;
|
|
||||||
trayObj["tray_info_idx"] = ams_data[j].trays[k].tray_info_idx;
|
|
||||||
trayObj["tray_type"] = ams_data[j].trays[k].tray_type;
|
|
||||||
trayObj["tray_sub_brands"] = ams_data[j].trays[k].tray_sub_brands;
|
|
||||||
trayObj["tray_color"] = ams_data[j].trays[k].tray_color;
|
|
||||||
trayObj["nozzle_temp_min"] = ams_data[j].trays[k].nozzle_temp_min;
|
|
||||||
trayObj["nozzle_temp_max"] = ams_data[j].trays[k].nozzle_temp_max;
|
|
||||||
trayObj["setting_id"] = ams_data[j].trays[k].setting_id;
|
|
||||||
trayObj["cali_idx"] = ams_data[j].trays[k].cali_idx;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Aktualisiere das globale amsJsonData
|
{
|
||||||
amsJsonData = "";
|
ams_data[i].trays[trayId].setting_id = settingId;
|
||||||
serializeJson(wsArray, amsJsonData);
|
}
|
||||||
|
|
||||||
// Sende an WebSocket Clients
|
// Sende an WebSocket Clients
|
||||||
Serial.println("Filament setting updated");
|
Serial.println("Filament setting updated");
|
||||||
@@ -538,45 +635,98 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
|||||||
|
|
||||||
void reconnect() {
|
void reconnect() {
|
||||||
// Loop until we're reconnected
|
// Loop until we're reconnected
|
||||||
|
uint8_t retries = 0;
|
||||||
while (!client.connected()) {
|
while (!client.connected()) {
|
||||||
Serial.println("Attempting MQTT connection...");
|
Serial.println("Attempting MQTT re/connection...");
|
||||||
|
Serial.print("State before connect: ");
|
||||||
|
Serial.println(client.state());
|
||||||
bambu_connected = false;
|
bambu_connected = false;
|
||||||
oledShowTopRow();
|
oledShowTopRow();
|
||||||
|
|
||||||
// Attempt to connect
|
// Generate a random client ID suffix
|
||||||
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
|
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
|
||||||
Serial.println("... re-connected");
|
Serial.print("Reconnecting with client ID: ");
|
||||||
// ... and resubscribe
|
Serial.println(clientId);
|
||||||
client.subscribe(report_topic.c_str());
|
|
||||||
|
// Attempt to connect with clean session and will message
|
||||||
|
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr)) {
|
||||||
|
Serial.println("MQTT re/connected");
|
||||||
|
|
||||||
|
// Subscribe with QoS 1
|
||||||
|
if (client.subscribe(report_topic.c_str(), 1)) {
|
||||||
|
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to subscribe to topic: " + report_topic);
|
||||||
|
}
|
||||||
bambu_connected = true;
|
bambu_connected = true;
|
||||||
oledShowTopRow();
|
oledShowTopRow();
|
||||||
} else {
|
} else {
|
||||||
|
int state = client.state();
|
||||||
Serial.print("failed, rc=");
|
Serial.print("failed, rc=");
|
||||||
Serial.print(client.state());
|
Serial.print(state);
|
||||||
Serial.println(" try again in 5 seconds");
|
Serial.print(" (");
|
||||||
|
// Print detailed error message
|
||||||
|
switch(state) {
|
||||||
|
case -4: Serial.print("MQTT_CONNECTION_TIMEOUT"); break;
|
||||||
|
case -3: Serial.print("MQTT_CONNECTION_LOST"); break;
|
||||||
|
case -2: Serial.print("MQTT_CONNECT_FAILED"); break;
|
||||||
|
case -1: Serial.print("MQTT_DISCONNECTED"); break;
|
||||||
|
case 1: Serial.print("MQTT_CONNECT_BAD_PROTOCOL"); break;
|
||||||
|
case 2: Serial.print("MQTT_CONNECT_BAD_CLIENT_ID"); break;
|
||||||
|
case 3: Serial.print("MQTT_CONNECT_UNAVAILABLE"); break;
|
||||||
|
case 4: Serial.print("MQTT_CONNECT_BAD_CREDENTIALS"); break;
|
||||||
|
case 5: Serial.print("MQTT_CONNECT_UNAUTHORIZED"); break;
|
||||||
|
default: Serial.print("UNKNOWN"); break;
|
||||||
|
}
|
||||||
|
Serial.println(") try again in 5 seconds");
|
||||||
bambu_connected = false;
|
bambu_connected = false;
|
||||||
oledShowTopRow();
|
oledShowTopRow();
|
||||||
// Wait 5 seconds before retrying
|
|
||||||
yield();
|
yield();
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
if (retries > 5) {
|
||||||
|
Serial.println("Disable Bambu MQTT Task after 5 retries");
|
||||||
|
vTaskDelete(BambuMqttTask);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retries++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mqtt_loop(void * parameter) {
|
void mqtt_loop(void * parameter) {
|
||||||
|
Serial.println("Bambu MQTT Task gestartet");
|
||||||
|
unsigned long lastCheck = 0;
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if (pauseBambuMqttTask) {
|
if (pauseBambuMqttTask) {
|
||||||
vTaskDelay(10000);
|
vTaskDelay(10000);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
if (!client.connected()) {
|
if (!client.connected()) {
|
||||||
|
Serial.println("Connection lost, attempting reconnect...");
|
||||||
reconnect();
|
reconnect();
|
||||||
yield();
|
yield();
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Periodically check connection status
|
||||||
|
if (now - lastCheck > 60000) { // Check every 60 seconds
|
||||||
|
Serial.print("MQTT Status Check - Connected: ");
|
||||||
|
Serial.println(client.connected() ? "Yes" : "No");
|
||||||
|
lastCheck = now;
|
||||||
|
Serial.print("Free Heap-Memory: ");
|
||||||
|
Serial.println(ESP.getFreeHeap());
|
||||||
|
}
|
||||||
|
|
||||||
client.loop();
|
client.loop();
|
||||||
yield();
|
yield();
|
||||||
|
esp_task_wdt_reset();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,30 +734,43 @@ void mqtt_loop(void * parameter) {
|
|||||||
bool setupMqtt() {
|
bool setupMqtt() {
|
||||||
// Wenn Bambu Daten vorhanden
|
// Wenn Bambu Daten vorhanden
|
||||||
bool success = loadBambuCredentials();
|
bool success = loadBambuCredentials();
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Serial.println("Failed to load Bambu credentials");
|
bambuDisabled = true;
|
||||||
oledShowMessage("Bambu Credentials Missing");
|
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
|
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
|
||||||
{
|
{
|
||||||
|
bambuDisabled = false;
|
||||||
sslClient.setCACert(root_ca);
|
sslClient.setCACert(root_ca);
|
||||||
sslClient.setInsecure();
|
sslClient.setInsecure();
|
||||||
client.setServer(bambu_ip, 8883);
|
client.setServer(bambu_ip, 8883);
|
||||||
|
|
||||||
|
// Generate a random client ID suffix
|
||||||
|
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
|
||||||
|
|
||||||
|
// MQTT Connection Options
|
||||||
|
client.setKeepAlive(60);
|
||||||
|
client.setSocketTimeout(60); // Increase socket timeout
|
||||||
|
|
||||||
|
Serial.print("Connecting with client ID: ");
|
||||||
|
Serial.println(clientId);
|
||||||
|
|
||||||
// Verbinden mit dem MQTT-Server
|
// Verbinden mit dem MQTT-Server
|
||||||
bool connected = true;
|
bool connected = true;
|
||||||
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode))
|
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr))
|
||||||
{
|
{
|
||||||
client.setCallback(mqtt_callback);
|
client.setCallback(mqtt_callback);
|
||||||
client.setBufferSize(5120);
|
client.setBufferSize(16384); // Increased to 16KB to handle larger JSON
|
||||||
// Optional: Topic abonnieren
|
|
||||||
client.subscribe(report_topic.c_str());
|
// Subscribe with QoS 1
|
||||||
//client.subscribe(request_topic.c_str());
|
if (client.subscribe(report_topic.c_str(), 1)) {
|
||||||
|
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to subscribe to topic: " + report_topic);
|
||||||
|
}
|
||||||
|
|
||||||
Serial.println("MQTT-Client initialisiert");
|
Serial.println("MQTT-Client initialisiert");
|
||||||
|
|
||||||
oledShowMessage("Bambu Connected");
|
oledShowMessage("Bambu Connected");
|
||||||
@@ -617,7 +780,7 @@ bool setupMqtt() {
|
|||||||
xTaskCreatePinnedToCore(
|
xTaskCreatePinnedToCore(
|
||||||
mqtt_loop, /* Function to implement the task */
|
mqtt_loop, /* Function to implement the task */
|
||||||
"BambuMqtt", /* Name of the task */
|
"BambuMqtt", /* Name of the task */
|
||||||
10000, /* Stack size in words */
|
16384, /* Stack size in words */
|
||||||
NULL, /* Task input parameter */
|
NULL, /* Task input parameter */
|
||||||
mqttTaskPrio, /* Priority of the task */
|
mqttTaskPrio, /* Priority of the task */
|
||||||
&BambuMqttTask, /* Task handle. */
|
&BambuMqttTask, /* Task handle. */
|
||||||
@@ -636,10 +799,7 @@ bool setupMqtt() {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Fehler: Keine MQTT-Daten vorhanden");
|
bambuDisabled = true;
|
||||||
oledShowMessage("Bambu Credentials Missing");
|
|
||||||
oledShowTopRow();
|
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -648,6 +808,7 @@ bool setupMqtt() {
|
|||||||
void bambu_restart() {
|
void bambu_restart() {
|
||||||
if (BambuMqttTask) {
|
if (BambuMqttTask) {
|
||||||
vTaskDelete(BambuMqttTask);
|
vTaskDelete(BambuMqttTask);
|
||||||
|
delay(10);
|
||||||
}
|
}
|
||||||
setupMqtt();
|
setupMqtt();
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,9 @@ extern int ams_count;
|
|||||||
extern AMSData ams_data[MAX_AMS];
|
extern AMSData ams_data[MAX_AMS];
|
||||||
extern bool autoSendToBambu;
|
extern bool autoSendToBambu;
|
||||||
extern int autoSetToBambuSpoolId;
|
extern int autoSetToBambuSpoolId;
|
||||||
|
extern bool bambuDisabled;
|
||||||
|
|
||||||
|
bool removeBambuCredentials();
|
||||||
bool loadBambuCredentials();
|
bool loadBambuCredentials();
|
||||||
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime);
|
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime);
|
||||||
bool setupMqtt();
|
bool setupMqtt();
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
#include "commonFS.h"
|
#include "commonFS.h"
|
||||||
#include <SPIFFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
bool removeJsonValue(const char *filename)
|
||||||
|
{
|
||||||
|
File file = LittleFS.open(filename, "r");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
if (!LittleFS.remove(filename))
|
||||||
|
{
|
||||||
|
Serial.print("Fehler beim Löschen der Datei: ");
|
||||||
|
Serial.println(filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool saveJsonValue(const char* filename, const JsonDocument& doc) {
|
bool saveJsonValue(const char* filename, const JsonDocument& doc) {
|
||||||
File file = SPIFFS.open(filename, "w");
|
File file = LittleFS.open(filename, "w");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.print("Fehler beim Öffnen der Datei zum Schreiben: ");
|
Serial.print("Fehler beim Öffnen der Datei zum Schreiben: ");
|
||||||
Serial.println(filename);
|
Serial.println(filename);
|
||||||
@@ -20,7 +37,7 @@ bool saveJsonValue(const char* filename, const JsonDocument& doc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool loadJsonValue(const char* filename, JsonDocument& doc) {
|
bool loadJsonValue(const char* filename, JsonDocument& doc) {
|
||||||
File file = SPIFFS.open(filename, "r");
|
File file = LittleFS.open(filename, "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.print("Fehler beim Öffnen der Datei zum Lesen: ");
|
Serial.print("Fehler beim Öffnen der Datei zum Lesen: ");
|
||||||
Serial.println(filename);
|
Serial.println(filename);
|
||||||
@@ -36,12 +53,12 @@ bool loadJsonValue(const char* filename, JsonDocument& doc) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeSPIFFS() {
|
void initializeFileSystem() {
|
||||||
if (!SPIFFS.begin(true, "/spiffs", 10, "spiffs")) {
|
if (!LittleFS.begin(true)) {
|
||||||
Serial.println("SPIFFS Mount Failed");
|
Serial.println("LittleFS Mount Failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Serial.printf("SPIFFS Total: %u bytes\n", SPIFFS.totalBytes());
|
Serial.printf("LittleFS Total: %u bytes\n", LittleFS.totalBytes());
|
||||||
Serial.printf("SPIFFS Used: %u bytes\n", SPIFFS.usedBytes());
|
Serial.printf("LittleFS Used: %u bytes\n", LittleFS.usedBytes());
|
||||||
Serial.printf("SPIFFS Free: %u bytes\n", SPIFFS.totalBytes() - SPIFFS.usedBytes());
|
Serial.printf("LittleFS Free: %u bytes\n", LittleFS.totalBytes() - LittleFS.usedBytes());
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
#define COMMONFS_H
|
#define COMMONFS_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <SPIFFS.h>
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
|
||||||
bool saveJsonValue(const char* filename, const JsonDocument& doc);
|
bool removeJsonValue(const char *filename);
|
||||||
|
bool saveJsonValue(const char *filename, const JsonDocument &doc);
|
||||||
bool loadJsonValue(const char* filename, JsonDocument& doc);
|
bool loadJsonValue(const char* filename, JsonDocument& doc);
|
||||||
void initializeSPIFFS();
|
void initializeFileSystem();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ const uint16_t SCALE_LEVEL_WEIGHT = 500;
|
|||||||
uint16_t defaultScaleCalibrationValue = 430;
|
uint16_t defaultScaleCalibrationValue = 430;
|
||||||
// ***** HX711
|
// ***** HX711
|
||||||
|
|
||||||
|
// ***** TTP223 (Touch Sensor)
|
||||||
|
// TTP223 circuit wiring
|
||||||
|
const uint8_t TTP223_PIN = 25;
|
||||||
|
// ***** TTP223
|
||||||
|
|
||||||
// ***** Display
|
// ***** Display
|
||||||
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
|
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
|
||||||
// On an ESP32: 21(SDA), 22(SCL)
|
// On an ESP32: 21(SDA), 22(SCL)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ extern const uint8_t LOADCELL_DOUT_PIN;
|
|||||||
extern const uint8_t LOADCELL_SCK_PIN;
|
extern const uint8_t LOADCELL_SCK_PIN;
|
||||||
extern const uint8_t calVal_eepromAdress;
|
extern const uint8_t calVal_eepromAdress;
|
||||||
extern const uint16_t SCALE_LEVEL_WEIGHT;
|
extern const uint16_t SCALE_LEVEL_WEIGHT;
|
||||||
|
extern const uint8_t TTP223_PIN;
|
||||||
|
|
||||||
extern const int8_t OLED_RESET;
|
extern const int8_t OLED_RESET;
|
||||||
extern const uint8_t SCREEN_ADDRESS;
|
extern const uint8_t SCREEN_ADDRESS;
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ void setupDisplay() {
|
|||||||
// the library initializes this with an Adafruit splash screen.
|
// the library initializes this with an Adafruit splash screen.
|
||||||
display.setTextColor(WHITE);
|
display.setTextColor(WHITE);
|
||||||
display.display();
|
display.display();
|
||||||
delay(1000); // Pause for 2 seconds
|
|
||||||
oledShowTopRow();
|
oledShowTopRow();
|
||||||
delay(2000);
|
oledShowMessage("FilaMan v" + String(VERSION));
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void oledclearline() {
|
void oledclearline() {
|
||||||
@@ -139,8 +139,9 @@ void oledShowMultilineMessage(String message, uint8_t size) {
|
|||||||
int totalHeight = lines.size() * lineHeight;
|
int totalHeight = lines.size() * lineHeight;
|
||||||
int startY = OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - totalHeight) / 2);
|
int startY = OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - totalHeight) / 2);
|
||||||
|
|
||||||
|
uint8_t lineDistance = (lines.size() == 2) ? 5 : 0;
|
||||||
for (size_t i = 0; i < lines.size(); i++) {
|
for (size_t i = 0; i < lines.size(); i++) {
|
||||||
display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight));
|
display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight) + (i == 1 ? lineDistance : 0));
|
||||||
display.print(lines[i]);
|
display.print(lines[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +177,12 @@ void oledShowTopRow() {
|
|||||||
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE);
|
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spoolman_connected == 1) {
|
if (spoolmanApiState != API_INIT)
|
||||||
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
|
{
|
||||||
} else {
|
display.drawBitmap(80, 0, bitmap_spoolman_on, 16, 16, WHITE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);
|
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <DNSServer.h>
|
|
||||||
#include <ESPmDNS.h>
|
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
@@ -15,6 +13,10 @@
|
|||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
#include "commonFS.h"
|
#include "commonFS.h"
|
||||||
|
|
||||||
|
bool mainTaskWasPaused = 0;
|
||||||
|
uint8_t scaleTareCounter = 0;
|
||||||
|
bool touchSensorConnected = false;
|
||||||
|
|
||||||
// ##### SETUP #####
|
// ##### SETUP #####
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
@@ -26,7 +28,7 @@ void setup() {
|
|||||||
Serial.printf("%08X\n", (uint32_t)chipid); //print Low 4bytes.
|
Serial.printf("%08X\n", (uint32_t)chipid); //print Low 4bytes.
|
||||||
|
|
||||||
// Initialize SPIFFS
|
// Initialize SPIFFS
|
||||||
initializeSPIFFS();
|
initializeFileSystem();
|
||||||
|
|
||||||
// Start Display
|
// Start Display
|
||||||
setupDisplay();
|
setupDisplay();
|
||||||
@@ -35,58 +37,52 @@ void setup() {
|
|||||||
initWiFi();
|
initWiFi();
|
||||||
|
|
||||||
// Webserver
|
// Webserver
|
||||||
Serial.println("Starte Webserver");
|
|
||||||
setupWebserver(server);
|
setupWebserver(server);
|
||||||
|
|
||||||
// Spoolman API
|
// Spoolman API
|
||||||
// api.cpp
|
|
||||||
initSpoolman();
|
initSpoolman();
|
||||||
|
|
||||||
// Bambu MQTT
|
// Bambu MQTT
|
||||||
// bambu.cpp
|
|
||||||
setupMqtt();
|
setupMqtt();
|
||||||
|
|
||||||
// mDNS
|
// NFC Reader
|
||||||
Serial.println("Starte MDNS");
|
|
||||||
if (!MDNS.begin("filaman")) { // Set the hostname to "esp32.local"
|
|
||||||
Serial.println("Error setting up MDNS responder!");
|
|
||||||
while(1) {
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.println("mDNS responder started");
|
|
||||||
|
|
||||||
startNfc();
|
startNfc();
|
||||||
|
|
||||||
uint8_t scaleCalibrated = start_scale();
|
// Touch Sensor
|
||||||
if (scaleCalibrated == 3) {
|
pinMode(TTP223_PIN, INPUT_PULLUP);
|
||||||
oledShowMessage("Scale not calibrated!");
|
if (digitalRead(TTP223_PIN) == LOW)
|
||||||
for (uint16_t i = 0; i < 50000; i++) {
|
{
|
||||||
yield();
|
Serial.println("Touch Sensor is connected");
|
||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
touchSensorConnected = true;
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
} else if (scaleCalibrated == 0) {
|
|
||||||
oledShowMessage("HX711 not found");
|
|
||||||
for (uint16_t i = 0; i < 50000; i++) {
|
|
||||||
yield();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
start_scale(touchSensorConnected);
|
||||||
|
|
||||||
// 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
|
||||||
esp_task_wdt_init(10, panic);
|
esp_task_wdt_init(10, panic);
|
||||||
|
|
||||||
// Aktuellen Task (loopTask) zum Watchdog hinzufügen
|
// Aktuellen Task (loopTask) zum Watchdog hinzufügen
|
||||||
esp_task_wdt_add(NULL);
|
esp_task_wdt_add(NULL);
|
||||||
|
|
||||||
// Optional: Andere Tasks zum Watchdog hinzufügen, falls nötig
|
|
||||||
// esp_task_wdt_add(task_handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe interval check that handles millis() overflow
|
||||||
|
* @param currentTime Current millis() value
|
||||||
|
* @param lastTime Last recorded time
|
||||||
|
* @param interval Desired interval in milliseconds
|
||||||
|
* @return True if interval has elapsed
|
||||||
|
*/
|
||||||
|
bool intervalElapsed(unsigned long currentTime, unsigned long &lastTime, unsigned long interval) {
|
||||||
|
if (currentTime - lastTime >= interval || currentTime < lastTime) {
|
||||||
|
lastTime = currentTime;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long lastWeightReadTime = 0;
|
unsigned long lastWeightReadTime = 0;
|
||||||
const unsigned long weightReadInterval = 1000; // 1 second
|
const unsigned long weightReadInterval = 1000; // 1 second
|
||||||
|
|
||||||
@@ -94,47 +90,62 @@ unsigned long lastAutoSetBambuAmsTime = 0;
|
|||||||
const unsigned long autoSetBambuAmsInterval = 1000; // 1 second
|
const unsigned long autoSetBambuAmsInterval = 1000; // 1 second
|
||||||
uint8_t autoAmsCounter = 0;
|
uint8_t autoAmsCounter = 0;
|
||||||
|
|
||||||
unsigned long lastAmsSendTime = 0;
|
|
||||||
const unsigned long amsSendInterval = 60000; // 1 minute
|
|
||||||
|
|
||||||
uint8_t weightSend = 0;
|
uint8_t weightSend = 0;
|
||||||
int16_t lastWeight = 0;
|
int16_t lastWeight = 0;
|
||||||
uint8_t wifiErrorCounter = 0;
|
|
||||||
|
// WIFI check variables
|
||||||
|
unsigned long lastWifiCheckTime = 0;
|
||||||
|
const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms)
|
||||||
|
|
||||||
|
// Button debounce variables
|
||||||
|
unsigned long lastButtonPress = 0;
|
||||||
|
const unsigned long debounceDelay = 500; // 500 ms debounce delay
|
||||||
|
|
||||||
// ##### PROGRAM START #####
|
// ##### PROGRAM START #####
|
||||||
void loop() {
|
void loop() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
// Send AMS Data min every Minute
|
// Überprüfe den Status des Touch Sensors
|
||||||
if (currentMillis - lastAmsSendTime >= amsSendInterval)
|
if (touchSensorConnected && digitalRead(TTP223_PIN) == HIGH && currentMillis - lastButtonPress > debounceDelay)
|
||||||
{
|
{
|
||||||
lastAmsSendTime = currentMillis;
|
lastButtonPress = currentMillis;
|
||||||
//sendAmsData(nullptr);
|
scaleTareRequest = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Überprüfe regelmäßig die WLAN-Verbindung
|
||||||
|
if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval)) {
|
||||||
|
checkWiFiConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn Bambu auto set Spool aktiv
|
// Wenn Bambu auto set Spool aktiv
|
||||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && currentMillis - lastAutoSetBambuAmsTime >= autoSetBambuAmsInterval)
|
if (autoSendToBambu && autoSetToBambuSpoolId > 0) {
|
||||||
{
|
if (!bambuDisabled && !bambu_connected)
|
||||||
if (hasReadRfidTag == 0)
|
|
||||||
{
|
|
||||||
lastAutoSetBambuAmsTime = currentMillis;
|
|
||||||
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
|
|
||||||
autoAmsCounter++;
|
|
||||||
|
|
||||||
if (autoAmsCounter >= autoSetBambuAmsCounter)
|
|
||||||
{
|
{
|
||||||
autoSetToBambuSpoolId = 0;
|
bambu_restart();
|
||||||
|
}
|
||||||
|
if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval))
|
||||||
|
{
|
||||||
|
if (nfcReaderState == NFC_IDLE)
|
||||||
|
{
|
||||||
|
lastAutoSetBambuAmsTime = currentMillis;
|
||||||
|
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
|
||||||
|
autoAmsCounter++;
|
||||||
|
|
||||||
|
if (autoAmsCounter >= autoSetBambuAmsCounter)
|
||||||
|
{
|
||||||
|
autoSetToBambuSpoolId = 0;
|
||||||
|
autoAmsCounter = 0;
|
||||||
|
oledShowWeight(weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
autoAmsCounter = 0;
|
autoAmsCounter = 0;
|
||||||
oledShowWeight(weight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
autoAmsCounter = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wenn Waage nicht Kalibriert
|
// Wenn Waage nicht Kalibriert
|
||||||
if (scaleCalibrated == 3)
|
if (scaleCalibrated == 3)
|
||||||
{
|
{
|
||||||
@@ -147,19 +158,27 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ausgabe der Waage auf Display
|
// Ausgabe der Waage auf Display
|
||||||
if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0 && (!autoSendToBambu || autoSetToBambuSpoolId == 0))
|
if(pauseMainTask == 0)
|
||||||
{
|
{
|
||||||
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!autoSendToBambu || autoSetToBambuSpoolId == 0)))
|
||||||
|
{
|
||||||
|
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
||||||
|
}
|
||||||
|
mainTaskWasPaused = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainTaskWasPaused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
|
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
|
||||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag < 3)
|
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
|
||||||
{
|
{
|
||||||
lastWeightReadTime = currentMillis;
|
lastWeightReadTime = currentMillis;
|
||||||
|
|
||||||
// Prüfen ob die Waage korrekt genullt ist
|
// Prüfen ob die Waage korrekt genullt ist
|
||||||
if ((weight > 0 && weight < 5) || weight < 0)
|
if (autoTare && (weight > 0 && weight < 5) || weight < 0)
|
||||||
{
|
{
|
||||||
scale_tare_counter++;
|
scale_tare_counter++;
|
||||||
}
|
}
|
||||||
@@ -181,7 +200,8 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reset weight counter after writing tag
|
// reset weight counter after writing tag
|
||||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag > 1)
|
// TBD: what exactly is the logic behind this?
|
||||||
|
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
|
||||||
{
|
{
|
||||||
weigthCouterToApi = 0;
|
weigthCouterToApi = 0;
|
||||||
}
|
}
|
||||||
@@ -189,7 +209,8 @@ 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 (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && hasReadRfidTag == 1) {
|
if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS)
|
||||||
|
{
|
||||||
oledShowIcon("loading");
|
oledShowIcon("loading");
|
||||||
if (updateSpoolWeight(spoolId, weight))
|
if (updateSpoolWeight(spoolId, weight))
|
||||||
{
|
{
|
||||||
@@ -197,6 +218,11 @@ void loop() {
|
|||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
weightSend = 1;
|
weightSend = 1;
|
||||||
autoSetToBambuSpoolId = spoolId.toInt();
|
autoSetToBambuSpoolId = spoolId.toInt();
|
||||||
|
|
||||||
|
if (octoEnabled)
|
||||||
|
{
|
||||||
|
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -205,6 +231,5 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield();
|
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,23 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
#include "scale.h"
|
#include "scale.h"
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
|
// Pin definitions for both PN532 chips
|
||||||
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
|
#define PN532_SCK (18) // SPI SCK
|
||||||
|
#define PN532_MISO (19) // SPI MISO
|
||||||
|
#define PN532_MOSI (23) // SPI MOSI
|
||||||
|
|
||||||
|
// CS pins for each PN532
|
||||||
|
#define PN532_CS1 (5) // CS for first PN532
|
||||||
|
#define PN532_CS2 (4) // CS for second PN532
|
||||||
|
|
||||||
|
// Mifare authentication key
|
||||||
|
uint8_t keyA[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||||
|
|
||||||
|
// Create two PN532 instances
|
||||||
|
Adafruit_PN532 nfc1(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_CS1);
|
||||||
|
Adafruit_PN532 nfc2(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_CS2);
|
||||||
|
|
||||||
TaskHandle_t RfidReaderTask;
|
TaskHandle_t RfidReaderTask;
|
||||||
|
|
||||||
@@ -18,7 +32,7 @@ String spoolId = "";
|
|||||||
String nfcJsonData = "";
|
String nfcJsonData = "";
|
||||||
volatile bool pauseBambuMqttTask = false;
|
volatile bool pauseBambuMqttTask = false;
|
||||||
|
|
||||||
volatile uint8_t hasReadRfidTag = 0;
|
volatile nfcReaderStateType nfcReaderState = NFC_IDLE;
|
||||||
// 0 = nicht gelesen
|
// 0 = nicht gelesen
|
||||||
// 1 = erfolgreich gelesen
|
// 1 = erfolgreich gelesen
|
||||||
// 2 = fehler beim Lesen
|
// 2 = fehler beim Lesen
|
||||||
@@ -28,6 +42,165 @@ volatile uint8_t hasReadRfidTag = 0;
|
|||||||
// 6 = reading
|
// 6 = reading
|
||||||
// ***** PN532
|
// ***** PN532
|
||||||
|
|
||||||
|
// Buffer for reading data
|
||||||
|
uint8_t data[32];
|
||||||
|
|
||||||
|
// Function to initialize a specific PN532
|
||||||
|
bool initPN532(Adafruit_PN532 &pn532) {
|
||||||
|
pn532.begin();
|
||||||
|
uint32_t versiondata = pn532.getFirmwareVersion();
|
||||||
|
if (!versiondata) {
|
||||||
|
Serial.println("Didn't find PN532 board");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Got valid data, print it out!
|
||||||
|
Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX);
|
||||||
|
Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC);
|
||||||
|
Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC);
|
||||||
|
|
||||||
|
// Configure board to read RFID tags
|
||||||
|
pn532.SAMConfig();
|
||||||
|
Serial.println("Waiting for an ISO14443A Card ...");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initNfc() {
|
||||||
|
// Configure CS pins as outputs
|
||||||
|
pinMode(PN532_CS1, OUTPUT);
|
||||||
|
pinMode(PN532_CS2, OUTPUT);
|
||||||
|
digitalWrite(PN532_CS1, HIGH); // Deselect both chips initially
|
||||||
|
digitalWrite(PN532_CS2, HIGH);
|
||||||
|
|
||||||
|
// Initialize SPI
|
||||||
|
SPI.begin(PN532_SCK, PN532_MISO, PN532_MOSI);
|
||||||
|
SPI.setFrequency(1000000); // 1MHz SPI clock
|
||||||
|
|
||||||
|
// Initialize both PN532 chips
|
||||||
|
if (!initPN532(nfc1)) {
|
||||||
|
Serial.println("Failed to initialize PN532 #1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!initPN532(nfc2)) {
|
||||||
|
Serial.println("Failed to initialize PN532 #2");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Both PN532 chips initialized successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to read a specific PN532
|
||||||
|
bool readPN532(Adafruit_PN532 &pn532, uint8_t *uid, uint8_t *uidLength) {
|
||||||
|
uint8_t success;
|
||||||
|
success = pn532.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, uidLength);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to write to a specific PN532
|
||||||
|
bool writePN532(Adafruit_PN532 &pn532, uint8_t *uid, uint8_t uidLength, uint8_t *data, uint8_t dataLen) {
|
||||||
|
if (!pn532.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 1, keyA)) {
|
||||||
|
Serial.println("Failed to authenticate block");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pn532.mifareclassic_WriteDataBlock(4, data)) {
|
||||||
|
Serial.println("Failed to write block");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopNfc() {
|
||||||
|
uint8_t uid1[] = {0, 0, 0, 0, 0, 0, 0};
|
||||||
|
uint8_t uid2[] = {0, 0, 0, 0, 0, 0, 0};
|
||||||
|
uint8_t uidLength1 = 0;
|
||||||
|
uint8_t uidLength2 = 0;
|
||||||
|
|
||||||
|
// Try to read from both PN532 chips
|
||||||
|
bool success1 = readPN532(nfc1, uid1, &uidLength1);
|
||||||
|
bool success2 = readPN532(nfc2, uid2, &uidLength2);
|
||||||
|
|
||||||
|
if (success1 || success2) {
|
||||||
|
// Process the first tag if found
|
||||||
|
if (success1) {
|
||||||
|
processTag(uid1, uidLength1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the second tag if found
|
||||||
|
if (success2) {
|
||||||
|
processTag(uid2, uidLength2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processTag(uint8_t *uid, uint8_t uidLength, uint8_t readerNumber) {
|
||||||
|
Adafruit_PN532 &pn532 = (readerNumber == 1) ? nfc1 : nfc2;
|
||||||
|
|
||||||
|
Serial.print("Reader "); Serial.print(readerNumber); Serial.println(" found tag:");
|
||||||
|
Serial.print("UID Length: "); Serial.println(uidLength);
|
||||||
|
Serial.print("UID Value: ");
|
||||||
|
for (uint8_t i = 0; i < uidLength; i++) {
|
||||||
|
Serial.print(" 0x"); Serial.print(uid[i], HEX);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
|
||||||
|
if (uidLength == 7) {
|
||||||
|
uint16_t tagSize = readTagSize(pn532);
|
||||||
|
if(tagSize > 0) {
|
||||||
|
Serial.print("Tag size: "); Serial.println(tagSize);
|
||||||
|
uint8_t* data = (uint8_t*)malloc(tagSize);
|
||||||
|
memset(data, 0, tagSize);
|
||||||
|
|
||||||
|
// Mehrere Leseversuche
|
||||||
|
bool readSuccess = false;
|
||||||
|
for(int attempt = 0; attempt < 3 && !readSuccess; attempt++) {
|
||||||
|
readSuccess = true;
|
||||||
|
uint8_t numPages = tagSize / 4;
|
||||||
|
for (uint8_t i = 4; i < 4 + numPages; i++) {
|
||||||
|
if (!pn532.ntag2xx_ReadPage(i, data + (i - 4) * 4)) {
|
||||||
|
Serial.print("Failed to read page "); Serial.println(i);
|
||||||
|
readSuccess = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(5); // Kleine Pause zwischen den Seiten
|
||||||
|
}
|
||||||
|
if (!readSuccess) {
|
||||||
|
delay(50); // Pause vor erneutem Versuch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readSuccess) {
|
||||||
|
Serial.println("Successfully read tag data.");
|
||||||
|
processNfcData(data, createTagId(uid, uidLength));
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to read tag data after 3 attempts");
|
||||||
|
oledShowMessage("Read Error");
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processNfcData(uint8_t *data, String tagId) {
|
||||||
|
// Process the data and send it via WebSocket
|
||||||
|
if (decodeNdefAndReturnJson(data)) {
|
||||||
|
nfcReaderState = NFC_READ_SUCCESS;
|
||||||
|
sendNfcData(nullptr);
|
||||||
|
} else {
|
||||||
|
nfcReaderState = NFC_READ_ERROR;
|
||||||
|
oledShowMessage("NFC-Tag unknown");
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to write to a specific tag
|
||||||
|
bool writeNfcTag(uint8_t *uid, uint8_t uidLength, uint8_t *data, uint8_t dataLen, uint8_t readerNumber) {
|
||||||
|
// Select the appropriate PN532 based on reader number
|
||||||
|
Adafruit_PN532 &pn532 = (readerNumber == 1) ? nfc1 : nfc2;
|
||||||
|
|
||||||
|
return writePN532(pn532, uid, uidLength, data, dataLen);
|
||||||
|
}
|
||||||
|
|
||||||
// ##### Funktionen für RFID #####
|
// ##### Funktionen für RFID #####
|
||||||
void payloadToJson(uint8_t *data) {
|
void payloadToJson(uint8_t *data) {
|
||||||
@@ -44,8 +217,6 @@ void payloadToJson(uint8_t *data) {
|
|||||||
DeserializationError error = deserializeJson(doc, jsonString);
|
DeserializationError error = deserializeJson(doc, jsonString);
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
const char* version = doc["version"];
|
|
||||||
const char* protocol = doc["protocol"];
|
|
||||||
const char* color_hex = doc["color_hex"];
|
const char* color_hex = doc["color_hex"];
|
||||||
const char* type = doc["type"];
|
const char* type = doc["type"];
|
||||||
int min_temp = doc["min_temp"];
|
int min_temp = doc["min_temp"];
|
||||||
@@ -55,8 +226,6 @@ void payloadToJson(uint8_t *data) {
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println("-----------------");
|
Serial.println("-----------------");
|
||||||
Serial.println("JSON-Parsed Data:");
|
Serial.println("JSON-Parsed Data:");
|
||||||
Serial.println(version);
|
|
||||||
Serial.println(protocol);
|
|
||||||
Serial.println(color_hex);
|
Serial.println(color_hex);
|
||||||
Serial.println(type);
|
Serial.println(type);
|
||||||
Serial.println(min_temp);
|
Serial.println(min_temp);
|
||||||
@@ -84,7 +253,7 @@ bool formatNdefTag() {
|
|||||||
|
|
||||||
// Schreibe die Initialisierungsnachricht auf die ersten Seiten
|
// Schreibe die Initialisierungsnachricht auf die ersten Seiten
|
||||||
for (int i = 0; i < sizeof(ndefInit); i += 4) {
|
for (int i = 0; i < sizeof(ndefInit); i += 4) {
|
||||||
if (!nfc.ntag2xx_WritePage(pageOffset + (i / 4), &ndefInit[i])) {
|
if (!nfc1.ntag2xx_WritePage(pageOffset + (i / 4), &ndefInit[i])) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -93,8 +262,15 @@ bool formatNdefTag() {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
uint16_t readTagSize(Adafruit_PN532 &pn532) {
|
||||||
uint8_t tagSize = 240; // 144 bytes is maximum for NTAG213
|
uint8_t buffer[4];
|
||||||
|
memset(buffer, 0, 4);
|
||||||
|
pn532.ntag2xx_ReadPage(3, buffer);
|
||||||
|
return buffer[2] * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ntag2xx_WriteNDEF(const char *payload, Adafruit_PN532 &pn532) {
|
||||||
|
uint16_t tagSize = readTagSize(pn532);
|
||||||
Serial.print("Tag Size: ");Serial.println(tagSize);
|
Serial.print("Tag Size: ");Serial.println(tagSize);
|
||||||
|
|
||||||
uint8_t pageBuffer[4] = {0, 0, 0, 0};
|
uint8_t pageBuffer[4] = {0, 0, 0, 0};
|
||||||
@@ -136,6 +312,8 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
|||||||
if (combinedData == NULL)
|
if (combinedData == NULL)
|
||||||
{
|
{
|
||||||
Serial.println("Fehler: Nicht genug Speicher vorhanden.");
|
Serial.println("Fehler: Nicht genug Speicher vorhanden.");
|
||||||
|
oledShowMessage("Tag too small");
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,11 +329,7 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
|||||||
int bytesToWrite = (totalSize < 4) ? totalSize : 4;
|
int bytesToWrite = (totalSize < 4) ? totalSize : 4;
|
||||||
memcpy(pageBuffer, combinedData + a, bytesToWrite);
|
memcpy(pageBuffer, combinedData + a, bytesToWrite);
|
||||||
|
|
||||||
//uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
if (!(pn532.ntag2xx_WritePage(4+i, pageBuffer)))
|
||||||
//uint8_t uidLength;
|
|
||||||
//nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 100);
|
|
||||||
|
|
||||||
if (!(nfc.ntag2xx_WritePage(4+i, pageBuffer)))
|
|
||||||
{
|
{
|
||||||
Serial.println("Fehler beim Schreiben der Seite.");
|
Serial.println("Fehler beim Schreiben der Seite.");
|
||||||
free(combinedData);
|
free(combinedData);
|
||||||
@@ -163,8 +337,6 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
yield();
|
yield();
|
||||||
//esp_task_wdt_reset();
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
a += 4;
|
a += 4;
|
||||||
totalSize -= bytesToWrite;
|
totalSize -= bytesToWrite;
|
||||||
@@ -173,7 +345,7 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
|||||||
// Ensure the NDEF message is properly terminated
|
// Ensure the NDEF message is properly terminated
|
||||||
memset(pageBuffer, 0, 4);
|
memset(pageBuffer, 0, 4);
|
||||||
pageBuffer[0] = 0xFE; // NDEF record footer
|
pageBuffer[0] = 0xFE; // NDEF record footer
|
||||||
if (!(nfc.ntag2xx_WritePage(4+i, pageBuffer)))
|
if (!(pn532.ntag2xx_WritePage(4+i, pageBuffer)))
|
||||||
{
|
{
|
||||||
Serial.println("Fehler beim Schreiben des End-Bits.");
|
Serial.println("Fehler beim Schreiben des End-Bits.");
|
||||||
free(combinedData);
|
free(combinedData);
|
||||||
@@ -232,30 +404,43 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
|
|||||||
void writeJsonToTag(void *parameter) {
|
void writeJsonToTag(void *parameter) {
|
||||||
const char* payload = (const char*)parameter;
|
const char* payload = (const char*)parameter;
|
||||||
|
|
||||||
// Gib die erstellte NDEF-Message aus
|
|
||||||
Serial.println("Erstelle NDEF-Message...");
|
Serial.println("Erstelle NDEF-Message...");
|
||||||
Serial.println(payload);
|
Serial.println(payload);
|
||||||
|
|
||||||
hasReadRfidTag = 3;
|
nfcReaderState = NFC_WRITING;
|
||||||
vTaskSuspend(RfidReaderTask);
|
vTaskSuspend(RfidReaderTask);
|
||||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
//pauseBambuMqttTask = true;
|
|
||||||
// aktualisieren der Website wenn sich der Status ändert
|
|
||||||
sendNfcData(nullptr);
|
sendNfcData(nullptr);
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
oledShowMessage("Waiting for NFC-Tag");
|
oledShowMessage("Waiting for NFC-Tag");
|
||||||
|
|
||||||
// Wait 10sec for tag
|
// Try both readers
|
||||||
uint8_t success = 0;
|
uint8_t success = 0;
|
||||||
String uidString = "";
|
String uidString = "";
|
||||||
|
Adafruit_PN532* activeReader = nullptr;
|
||||||
|
|
||||||
for (uint16_t i = 0; i < 20; i++) {
|
for (uint16_t i = 0; i < 20; i++) {
|
||||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
// Try first reader
|
||||||
|
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
|
||||||
uint8_t uidLength;
|
uint8_t uidLength;
|
||||||
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500);
|
success = nfc1.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 250);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
// Try second reader
|
||||||
|
success = nfc2.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 250);
|
||||||
|
if (success) {
|
||||||
|
activeReader = &nfc2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activeReader = &nfc1;
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
for (uint8_t i = 0; i < uidLength; i++) {
|
for (uint8_t i = 0; i < uidLength; i++) {
|
||||||
uidString += String(uid[i], HEX);
|
uidString += String(uid[i], HEX);
|
||||||
if (i < uidLength - 1) {
|
if (i < uidLength - 1) {
|
||||||
uidString += ":"; // Optional: Trennzeichen hinzufügen
|
uidString += ":";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foundNfcTag(nullptr, success);
|
foundNfcTag(nullptr, success);
|
||||||
@@ -269,48 +454,40 @@ void writeJsonToTag(void *parameter) {
|
|||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success)
|
if (success && activeReader != nullptr) {
|
||||||
{
|
|
||||||
oledShowIcon("transfer");
|
oledShowIcon("transfer");
|
||||||
// Schreibe die NDEF-Message auf den Tag
|
// Schreibe die NDEF-Message auf den Tag
|
||||||
success = ntag2xx_WriteNDEF(payload);
|
success = ntag2xx_WriteNDEF(payload, *activeReader);
|
||||||
if (success)
|
if (success) {
|
||||||
{
|
|
||||||
Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben");
|
Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben");
|
||||||
//oledShowMessage("NFC-Tag written");
|
|
||||||
oledShowIcon("success");
|
oledShowIcon("success");
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
hasReadRfidTag = 5;
|
nfcReaderState = NFC_WRITE_SUCCESS;
|
||||||
// aktualisieren der Website wenn sich der Status ändert
|
|
||||||
sendNfcData(nullptr);
|
sendNfcData(nullptr);
|
||||||
pauseBambuMqttTask = false;
|
pauseBambuMqttTask = false;
|
||||||
|
|
||||||
if (updateSpoolTagId(uidString, payload)) {
|
if (updateSpoolTagId(uidString, payload)) {
|
||||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
|
||||||
uint8_t uidLength;
|
uint8_t uidLength;
|
||||||
oledShowIcon("success");
|
oledShowIcon("success");
|
||||||
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
|
while (activeReader->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskResume(RfidReaderTask);
|
vTaskResume(RfidReaderTask);
|
||||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag");
|
Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag");
|
||||||
oledShowIcon("failed");
|
oledShowIcon("failed");
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
hasReadRfidTag = 4;
|
nfcReaderState = NFC_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
|
Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
|
||||||
oledShowMessage("No NFC-Tag found");
|
oledShowMessage("No NFC-Tag found");
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
hasReadRfidTag = 0;
|
nfcReaderState = NFC_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendWriteResult(nullptr, success);
|
sendWriteResult(nullptr, success);
|
||||||
@@ -326,12 +503,12 @@ void startWriteJsonToTag(const char* payload) {
|
|||||||
char* payloadCopy = strdup(payload);
|
char* payloadCopy = strdup(payload);
|
||||||
|
|
||||||
// Task nicht mehrfach starten
|
// Task nicht mehrfach starten
|
||||||
if (hasReadRfidTag != 3) {
|
if (nfcReaderState != NFC_WRITING) {
|
||||||
// Erstelle die Task
|
// Erstelle die Task
|
||||||
xTaskCreate(
|
xTaskCreate(
|
||||||
writeJsonToTag, // Task-Funktion
|
writeJsonToTag, // Task-Funktion
|
||||||
"WriteJsonToTagTask", // Task-Name
|
"WriteJsonToTagTask", // Task-Name
|
||||||
4096, // Stackgröße in Bytes
|
5115, // Stackgröße in Bytes
|
||||||
(void*)payloadCopy, // Parameter
|
(void*)payloadCopy, // Parameter
|
||||||
rfidWriteTaskPrio, // Priorität
|
rfidWriteTaskPrio, // Priorität
|
||||||
NULL // Task-Handle (nicht benötigt)
|
NULL // Task-Handle (nicht benötigt)
|
||||||
@@ -340,73 +517,88 @@ void startWriteJsonToTag(const char* payload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void scanRfidTask(void * parameter) {
|
void scanRfidTask(void * parameter) {
|
||||||
Serial.println("RFID Task gestartet");
|
Serial.println("RFID Task gestartet");
|
||||||
for(;;) {
|
if (nfcReaderState != NFC_WRITING)
|
||||||
// Wenn geschrieben wird Schleife aussetzen
|
|
||||||
if (hasReadRfidTag != 3)
|
|
||||||
{
|
{
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
uint8_t success;
|
uint8_t success = 0;
|
||||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0};
|
||||||
uint8_t uidLength;
|
uint8_t uidLength;
|
||||||
|
Adafruit_PN532 *activeReader = nullptr;
|
||||||
|
|
||||||
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000);
|
// Try first reader with increased timeout
|
||||||
|
success = nfc1.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 150);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
activeReader = &nfc1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delay(50); // Small delay between readers
|
||||||
|
// Try second reader with increased timeout
|
||||||
|
success = nfc2.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 150);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
activeReader = &nfc2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foundNfcTag(nullptr, success);
|
foundNfcTag(nullptr, success);
|
||||||
|
|
||||||
if (success && hasReadRfidTag != 1)
|
if (success && nfcReaderState != NFC_READ_SUCCESS && activeReader != nullptr)
|
||||||
{
|
{
|
||||||
// Display some basic information about the card
|
|
||||||
Serial.println("Found an ISO14443A card");
|
Serial.println("Found an ISO14443A card");
|
||||||
|
|
||||||
hasReadRfidTag = 6;
|
nfcReaderState = NFC_READING;
|
||||||
|
|
||||||
oledShowIcon("transfer");
|
oledShowIcon("transfer");
|
||||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
if (uidLength == 7)
|
if (uidLength == 7)
|
||||||
{
|
{
|
||||||
uint8_t data[256];
|
uint16_t tagSize = readTagSize(*activeReader);
|
||||||
|
if (tagSize > 0)
|
||||||
// We probably have an NTAG2xx card (though it could be Ultralight as well)
|
|
||||||
Serial.println("Seems to be an NTAG2xx tag (7 byte UID)");
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 45; i++) {
|
|
||||||
/*
|
|
||||||
if (i < uidLength) {
|
|
||||||
uidString += String(uid[i], HEX);
|
|
||||||
if (i < uidLength - 1) {
|
|
||||||
uidString += ":"; // Optional: Trennzeichen hinzufügen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (!nfc.mifareclassic_ReadDataBlock(i, data + (i - 4) * 4))
|
|
||||||
{
|
|
||||||
break; // Stop if reading fails
|
|
||||||
}
|
|
||||||
// Check for NDEF message end
|
|
||||||
if (data[(i - 4) * 4] == 0xFE)
|
|
||||||
{
|
|
||||||
break; // End of NDEF message
|
|
||||||
}
|
|
||||||
|
|
||||||
yield();
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!decodeNdefAndReturnJson(data))
|
|
||||||
{
|
{
|
||||||
oledShowMessage("NFC-Tag unknown");
|
uint8_t *data = (uint8_t *)malloc(tagSize);
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
memset(data, 0, tagSize);
|
||||||
hasReadRfidTag = 2;
|
|
||||||
|
Serial.println("Seems to be an NTAG2xx tag (7 byte UID)");
|
||||||
|
|
||||||
|
uint8_t numPages = readTagSize(*activeReader) / 4;
|
||||||
|
for (uint8_t i = 4; i < 4 + numPages; i++)
|
||||||
|
{
|
||||||
|
if (!activeReader->ntag2xx_ReadPage(i, data + (i - 4) * 4))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data[(i - 4) * 4] == 0xFE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield();
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5)); // Increased delay between page reads
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decodeNdefAndReturnJson(data))
|
||||||
|
{
|
||||||
|
oledShowMessage("NFC-Tag unknown");
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
nfcReaderState = NFC_READ_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nfcReaderState = NFC_READ_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hasReadRfidTag = 1;
|
oledShowMessage("NFC-Tag read error");
|
||||||
|
nfcReaderState = NFC_READ_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -414,58 +606,53 @@ void scanRfidTask(void * parameter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success && hasReadRfidTag > 0)
|
if (!success && nfcReaderState != NFC_IDLE)
|
||||||
{
|
{
|
||||||
hasReadRfidTag = 0;
|
nfcReaderState = NFC_IDLE;
|
||||||
//uidString = "";
|
|
||||||
nfcJsonData = "";
|
nfcJsonData = "";
|
||||||
Serial.println("Tag entfernt");
|
Serial.println("Tag entfernt");
|
||||||
if (!autoSendToBambu) oledShowWeight(weight);
|
if (!autoSendToBambu)
|
||||||
|
oledShowWeight(weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// aktualisieren der Website wenn sich der Status ändert
|
|
||||||
sendNfcData(nullptr);
|
sendNfcData(nullptr);
|
||||||
|
delay(100); // Add small delay at end of loop
|
||||||
|
}
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startNfc() {
|
void startNfc() {
|
||||||
nfc.begin(); // Beginne Kommunikation mit RFID Leser
|
initNfc();
|
||||||
delay(1000);
|
BaseType_t result = xTaskCreatePinnedToCore(
|
||||||
unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus
|
scanRfidTask, /* Function to implement the task */
|
||||||
if (! versiondata) { // Wenn keine Antwort kommt
|
"RfidReader", /* Name of the task */
|
||||||
Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor
|
5115, /* Stack size in words */
|
||||||
//delay(5000);
|
NULL, /* Task input parameter */
|
||||||
//ESP.restart();
|
rfidTaskPrio, /* Priority of the task */
|
||||||
oledShowMessage("No RFID Board found");
|
&RfidReaderTask, /* Task handle. */
|
||||||
delay(2000);
|
rfidTaskCore); /* Core where the task should run */
|
||||||
}
|
|
||||||
else {
|
|
||||||
Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen
|
|
||||||
Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); // Monitor, wenn Antwort vom Board kommt
|
|
||||||
Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); //
|
|
||||||
|
|
||||||
nfc.SAMConfig();
|
if (result != pdPASS) {
|
||||||
// Set the max number of retry attempts to read from a card
|
Serial.println("Fehler beim Erstellen des RFID Tasks");
|
||||||
// This prevents us from waiting forever for a card, which is
|
} else {
|
||||||
// the default behaviour of the PN532.
|
Serial.println("RFID Task erfolgreich erstellt");
|
||||||
//nfc.setPassiveActivationRetries(0x7F);
|
|
||||||
//nfc.setPassiveActivationRetries(0xFF);
|
|
||||||
|
|
||||||
BaseType_t result = xTaskCreatePinnedToCore(
|
|
||||||
scanRfidTask, /* Function to implement the task */
|
|
||||||
"RfidReader", /* Name of the task */
|
|
||||||
10000, /* Stack size in words */
|
|
||||||
NULL, /* Task input parameter */
|
|
||||||
rfidTaskPrio, /* Priority of the task */
|
|
||||||
&RfidReaderTask, /* Task handle. */
|
|
||||||
rfidTaskCore); /* Core where the task should run */
|
|
||||||
|
|
||||||
if (result != pdPASS) {
|
|
||||||
Serial.println("Fehler beim Erstellen des RFID Tasks");
|
|
||||||
} else {
|
|
||||||
Serial.println("RFID Task erfolgreich erstellt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String createTagId(uint8_t *uid, uint8_t uidLength)
|
||||||
|
{
|
||||||
|
String tagId = ""; // Initialisieren Sie einen leeren String
|
||||||
|
|
||||||
|
// Erstellen Sie die Tag-ID basierend auf dem UID
|
||||||
|
for (uint8_t i = 0; i < uidLength; i++)
|
||||||
|
{
|
||||||
|
tagId += String(uid[i], HEX); // Konvertieren Sie die UID in einen String
|
||||||
|
if (i < uidLength - 1)
|
||||||
|
{
|
||||||
|
tagId += ":"; // Fügen Sie einen Trennstrich hinzu, wenn es nicht das letzte Element ist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagId; // Geben Sie die erstellte Tag-ID zurück
|
||||||
|
Serial.println("tagID:" + tagId);
|
||||||
|
}
|
||||||
@@ -1,16 +1,32 @@
|
|||||||
#ifndef NFC_H
|
#pragma once
|
||||||
#define NFC_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <Adafruit_PN532.h>
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NFC_IDLE,
|
||||||
|
NFC_READING,
|
||||||
|
NFC_READ_SUCCESS,
|
||||||
|
NFC_READ_ERROR,
|
||||||
|
NFC_WRITING,
|
||||||
|
NFC_WRITE_SUCCESS,
|
||||||
|
NFC_WRITE_ERROR
|
||||||
|
} nfcReaderStateType;
|
||||||
|
|
||||||
void startNfc();
|
void startNfc();
|
||||||
void scanRfidTask(void * parameter);
|
|
||||||
void startWriteJsonToTag(const char* payload);
|
void startWriteJsonToTag(const char* payload);
|
||||||
|
void writeJsonToTag(void *parameter);
|
||||||
|
void scanRfidTask(void * parameter);
|
||||||
|
void processTag(uint8_t *uid, uint8_t uidLength, uint8_t readerNumber);
|
||||||
|
void processNfcData(uint8_t *data, String tagId);
|
||||||
|
bool decodeNdefAndReturnJson(const byte* encodedMessage);
|
||||||
|
|
||||||
extern TaskHandle_t RfidReaderTask;
|
extern TaskHandle_t RfidReaderTask;
|
||||||
extern String nfcJsonData;
|
extern String nfcJsonData;
|
||||||
extern String spoolId;
|
extern String spoolId;
|
||||||
extern volatile uint8_t hasReadRfidTag;
|
extern volatile nfcReaderStateType nfcReaderState;
|
||||||
extern volatile bool pauseBambuMqttTask;
|
extern volatile bool pauseBambuMqttTask;
|
||||||
|
|
||||||
#endif
|
// Function declarations
|
||||||
|
uint16_t readTagSize(Adafruit_PN532 &pn532);
|
||||||
|
String createTagId(uint8_t *uid, uint8_t uidLength);
|
||||||
@@ -14,11 +14,39 @@ static size_t updateTotalSize = 0;
|
|||||||
static size_t updateWritten = 0;
|
static size_t updateWritten = 0;
|
||||||
static bool isSpiffsUpdate = false;
|
static bool isSpiffsUpdate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two version strings and determines if version1 is less than version2
|
||||||
|
*
|
||||||
|
* @param version1 First version string (format: x.y.z)
|
||||||
|
* @param version2 Second version string (format: x.y.z)
|
||||||
|
* @return true if version1 is less than version2
|
||||||
|
*/
|
||||||
|
bool isVersionLessThan(const String& version1, const String& version2) {
|
||||||
|
int major1 = 0, minor1 = 0, patch1 = 0;
|
||||||
|
int major2 = 0, minor2 = 0, patch2 = 0;
|
||||||
|
|
||||||
|
// Parse version1
|
||||||
|
sscanf(version1.c_str(), "%d.%d.%d", &major1, &minor1, &patch1);
|
||||||
|
|
||||||
|
// Parse version2
|
||||||
|
sscanf(version2.c_str(), "%d.%d.%d", &major2, &minor2, &patch2);
|
||||||
|
|
||||||
|
// Compare major version
|
||||||
|
if (major1 < major2) return true;
|
||||||
|
if (major1 > major2) return false;
|
||||||
|
|
||||||
|
// Major versions equal, compare minor
|
||||||
|
if (minor1 < minor2) return true;
|
||||||
|
if (minor1 > minor2) return false;
|
||||||
|
|
||||||
|
// Minor versions equal, compare patch
|
||||||
|
return patch1 < patch2;
|
||||||
|
}
|
||||||
|
|
||||||
void backupJsonConfigs() {
|
void backupJsonConfigs() {
|
||||||
// Bambu Credentials backup
|
// Bambu Credentials backup
|
||||||
if (SPIFFS.exists("/bambu_credentials.json")) {
|
if (LittleFS.exists("/bambu_credentials.json")) {
|
||||||
File file = SPIFFS.open("/bambu_credentials.json", "r");
|
File file = LittleFS.open("/bambu_credentials.json", "r");
|
||||||
if (file) {
|
if (file) {
|
||||||
bambuCredentialsBackup = file.readString();
|
bambuCredentialsBackup = file.readString();
|
||||||
file.close();
|
file.close();
|
||||||
@@ -27,8 +55,8 @@ void backupJsonConfigs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Spoolman URL backup
|
// Spoolman URL backup
|
||||||
if (SPIFFS.exists("/spoolman_url.json")) {
|
if (LittleFS.exists("/spoolman_url.json")) {
|
||||||
File file = SPIFFS.open("/spoolman_url.json", "r");
|
File file = LittleFS.open("/spoolman_url.json", "r");
|
||||||
if (file) {
|
if (file) {
|
||||||
spoolmanUrlBackup = file.readString();
|
spoolmanUrlBackup = file.readString();
|
||||||
file.close();
|
file.close();
|
||||||
@@ -40,7 +68,7 @@ void backupJsonConfigs() {
|
|||||||
void restoreJsonConfigs() {
|
void restoreJsonConfigs() {
|
||||||
// Restore Bambu credentials
|
// Restore Bambu credentials
|
||||||
if (bambuCredentialsBackup.length() > 0) {
|
if (bambuCredentialsBackup.length() > 0) {
|
||||||
File file = SPIFFS.open("/bambu_credentials.json", "w");
|
File file = LittleFS.open("/bambu_credentials.json", "w");
|
||||||
if (file) {
|
if (file) {
|
||||||
file.print(bambuCredentialsBackup);
|
file.print(bambuCredentialsBackup);
|
||||||
file.close();
|
file.close();
|
||||||
@@ -51,7 +79,7 @@ void restoreJsonConfigs() {
|
|||||||
|
|
||||||
// Restore Spoolman URL
|
// Restore Spoolman URL
|
||||||
if (spoolmanUrlBackup.length() > 0) {
|
if (spoolmanUrlBackup.length() > 0) {
|
||||||
File file = SPIFFS.open("/spoolman_url.json", "w");
|
File file = LittleFS.open("/spoolman_url.json", "w");
|
||||||
if (file) {
|
if (file) {
|
||||||
file.print(spoolmanUrlBackup);
|
file.print(spoolmanUrlBackup);
|
||||||
file.close();
|
file.close();
|
||||||
@@ -111,6 +139,16 @@ void handleUpdate(AsyncWebServer &server) {
|
|||||||
updateHandler->setUri("/update");
|
updateHandler->setUri("/update");
|
||||||
updateHandler->setMethod(HTTP_POST);
|
updateHandler->setMethod(HTTP_POST);
|
||||||
|
|
||||||
|
// Check if current version is less than defined TOOLVERSION before proceeding with update
|
||||||
|
if (isVersionLessThan(VERSION, TOOLDVERSION)) {
|
||||||
|
updateHandler->onRequest([](AsyncWebServerRequest *request) {
|
||||||
|
request->send(400, "application/json",
|
||||||
|
"{\"success\":false,\"message\":\"Your current version is too old. Please perform a full upgrade.\"}");
|
||||||
|
});
|
||||||
|
server.addHandler(updateHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
|
updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
|
||||||
size_t index, uint8_t *data, size_t len, bool final) {
|
size_t index, uint8_t *data, size_t len, bool final) {
|
||||||
if (!index) {
|
if (!index) {
|
||||||
|
|||||||
@@ -15,15 +15,33 @@ int16_t weight = 0;
|
|||||||
|
|
||||||
uint8_t weigthCouterToApi = 0;
|
uint8_t weigthCouterToApi = 0;
|
||||||
uint8_t scale_tare_counter = 0;
|
uint8_t scale_tare_counter = 0;
|
||||||
|
bool scaleTareRequest = false;
|
||||||
uint8_t pauseMainTask = 0;
|
uint8_t pauseMainTask = 0;
|
||||||
uint8_t scaleCalibrated = 1;
|
uint8_t scaleCalibrated = 1;
|
||||||
|
|
||||||
Preferences preferences;
|
Preferences preferences;
|
||||||
const char* NVS_NAMESPACE = "scale";
|
const char* NVS_NAMESPACE = "scale";
|
||||||
const char* NVS_KEY_CALIBRATION = "cal_value";
|
const char* NVS_KEY_CALIBRATION = "cal_value";
|
||||||
|
const char *NVS_KEY_AUTOTARE = "auto_tare";
|
||||||
|
bool autoTare = true;
|
||||||
|
|
||||||
// ##### Funktionen für Waage #####
|
// ##### Funktionen für Waage #####
|
||||||
uint8_t tareScale() {
|
uint8_t setAutoTare(bool autoTareValue)
|
||||||
|
{
|
||||||
|
Serial.print("Set AutoTare to ");
|
||||||
|
Serial.println(autoTareValue);
|
||||||
|
autoTare = autoTareValue;
|
||||||
|
|
||||||
|
// Speichern mit NVS
|
||||||
|
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
|
||||||
|
preferences.putBool(NVS_KEY_AUTOTARE, autoTare);
|
||||||
|
preferences.end();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tareScale()
|
||||||
|
{
|
||||||
Serial.println("Tare scale");
|
Serial.println("Tare scale");
|
||||||
scale.tare();
|
scale.tare();
|
||||||
|
|
||||||
@@ -34,30 +52,52 @@ void scale_loop(void * parameter) {
|
|||||||
Serial.println("++++++++++++++++++++++++++++++");
|
Serial.println("++++++++++++++++++++++++++++++");
|
||||||
Serial.println("Scale Loop started");
|
Serial.println("Scale Loop started");
|
||||||
Serial.println("++++++++++++++++++++++++++++++");
|
Serial.println("++++++++++++++++++++++++++++++");
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if (scale.is_ready())
|
if (scale.is_ready())
|
||||||
{
|
{
|
||||||
// Waage nochmal Taren, wenn zu lange Abweichung
|
// Waage automatisch Taren, wenn zu lange Abweichung
|
||||||
if (scale_tare_counter >= 5)
|
if (autoTare && scale_tare_counter >= 5)
|
||||||
{
|
{
|
||||||
|
Serial.println("Auto Tare scale");
|
||||||
scale.tare();
|
scale.tare();
|
||||||
scale_tare_counter = 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
weight = round(scale.get_units());
|
weight = round(scale.get_units());
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100)); // Verzögerung, um die CPU nicht zu überlasten
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t start_scale() {
|
void start_scale(bool touchSensorConnected)
|
||||||
|
{
|
||||||
Serial.println("Prüfe Calibration Value");
|
Serial.println("Prüfe Calibration Value");
|
||||||
long calibrationValue;
|
float calibrationValue;
|
||||||
|
|
||||||
// NVS lesen
|
// NVS lesen
|
||||||
preferences.begin(NVS_NAMESPACE, true); // true = readonly
|
preferences.begin(NVS_NAMESPACE, true); // true = readonly
|
||||||
calibrationValue = preferences.getLong(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
|
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
|
||||||
|
|
||||||
|
// auto Tare
|
||||||
|
// Wenn Touch Sensor verbunden, dann autoTare auf false setzen
|
||||||
|
// Danach prüfen was in NVS gespeichert ist
|
||||||
|
autoTare = (touchSensorConnected) ? false : true;
|
||||||
|
autoTare = preferences.getBool(NVS_KEY_AUTOTARE, autoTare);
|
||||||
|
|
||||||
preferences.end();
|
preferences.end();
|
||||||
|
|
||||||
Serial.print("Read Scale Calibration Value ");
|
Serial.print("Read Scale Calibration Value ");
|
||||||
@@ -68,6 +108,13 @@ uint8_t start_scale() {
|
|||||||
if (isnan(calibrationValue) || calibrationValue < 1) {
|
if (isnan(calibrationValue) || calibrationValue < 1) {
|
||||||
calibrationValue = defaultScaleCalibrationValue;
|
calibrationValue = defaultScaleCalibrationValue;
|
||||||
scaleCalibrated = 0;
|
scaleCalibrated = 0;
|
||||||
|
|
||||||
|
oledShowMessage("Scale not calibrated!");
|
||||||
|
for (uint16_t i = 0; i < 50000; i++) {
|
||||||
|
yield();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oledShowMessage("Scale Tare Please remove all");
|
oledShowMessage("Scale Tare Please remove all");
|
||||||
@@ -90,7 +137,7 @@ uint8_t start_scale() {
|
|||||||
BaseType_t result = xTaskCreatePinnedToCore(
|
BaseType_t result = xTaskCreatePinnedToCore(
|
||||||
scale_loop, /* Function to implement the task */
|
scale_loop, /* Function to implement the task */
|
||||||
"ScaleLoop", /* Name of the task */
|
"ScaleLoop", /* Name of the task */
|
||||||
10000, /* Stack size in words */
|
2048, /* Stack size in words */
|
||||||
NULL, /* Task input parameter */
|
NULL, /* Task input parameter */
|
||||||
scaleTaskPrio, /* Priority of the task */
|
scaleTaskPrio, /* Priority of the task */
|
||||||
&ScaleTask, /* Task handle. */
|
&ScaleTask, /* Task handle. */
|
||||||
@@ -101,20 +148,21 @@ uint8_t start_scale() {
|
|||||||
} else {
|
} else {
|
||||||
Serial.println("ScaleLoop-Task erfolgreich erstellt");
|
Serial.println("ScaleLoop-Task erfolgreich erstellt");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (scaleCalibrated == 1) ? 1 : 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t calibrate_scale() {
|
uint8_t calibrate_scale() {
|
||||||
long newCalibrationValue;
|
uint8_t returnState = 0;
|
||||||
|
float newCalibrationValue;
|
||||||
|
|
||||||
|
vTaskSuspend(RfidReaderTask);
|
||||||
|
vTaskSuspend(ScaleTask);
|
||||||
|
|
||||||
//vTaskSuspend(RfidReaderTask);
|
|
||||||
vTaskDelete(RfidReaderTask);
|
|
||||||
pauseBambuMqttTask = true;
|
pauseBambuMqttTask = true;
|
||||||
pauseMainTask = 1;
|
pauseMainTask = 1;
|
||||||
|
|
||||||
if (scale.wait_ready_timeout(1000))
|
if (scale.wait_ready_timeout(1000))
|
||||||
{
|
{
|
||||||
|
|
||||||
scale.set_scale();
|
scale.set_scale();
|
||||||
oledShowMessage("Step 1 empty Scale");
|
oledShowMessage("Step 1 empty Scale");
|
||||||
|
|
||||||
@@ -136,7 +184,7 @@ uint8_t calibrate_scale() {
|
|||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
long newCalibrationValue = scale.get_units(10);
|
float newCalibrationValue = scale.get_units(10);
|
||||||
Serial.print("Result: ");
|
Serial.print("Result: ");
|
||||||
Serial.println(newCalibrationValue);
|
Serial.println(newCalibrationValue);
|
||||||
|
|
||||||
@@ -149,28 +197,33 @@ uint8_t calibrate_scale() {
|
|||||||
|
|
||||||
// Speichern mit NVS
|
// Speichern mit NVS
|
||||||
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
|
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
|
||||||
preferences.putLong(NVS_KEY_CALIBRATION, newCalibrationValue);
|
preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue);
|
||||||
preferences.end();
|
preferences.end();
|
||||||
|
|
||||||
// Verifizieren
|
// Verifizieren
|
||||||
preferences.begin(NVS_NAMESPACE, true);
|
preferences.begin(NVS_NAMESPACE, true);
|
||||||
long verifyValue = preferences.getLong(NVS_KEY_CALIBRATION, 0);
|
float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0);
|
||||||
preferences.end();
|
preferences.end();
|
||||||
|
|
||||||
Serial.print("Verified stored value: ");
|
Serial.print("Verified stored value: ");
|
||||||
Serial.println(verifyValue);
|
Serial.println(verifyValue);
|
||||||
|
|
||||||
Serial.println("End calibration, revome weight");
|
Serial.println("End calibration, remove weight");
|
||||||
|
|
||||||
oledShowMessage("Remove weight");
|
oledShowMessage("Remove weight");
|
||||||
|
|
||||||
|
scale.set_scale(newCalibrationValue);
|
||||||
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));
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
oledShowMessage("Calibration done");
|
oledShowMessage("Scale calibrated");
|
||||||
|
|
||||||
|
// For some reason it is not possible to re-tare the scale here, it will result in a wdt timeout. Instead let the scale loop do the taring
|
||||||
|
//scale.tare();
|
||||||
|
scaleTareRequest = true;
|
||||||
|
|
||||||
for (uint16_t i = 0; i < 2000; i++) {
|
for (uint16_t i = 0; i < 2000; i++) {
|
||||||
yield();
|
yield();
|
||||||
@@ -178,8 +231,9 @@ uint8_t calibrate_scale() {
|
|||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
//ESP.restart();
|
returnState = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -192,7 +246,7 @@ uint8_t calibrate_scale() {
|
|||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
return 0;
|
returnState = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,17 +261,13 @@ uint8_t calibrate_scale() {
|
|||||||
vTaskDelay(pdMS_TO_TICKS(1));
|
vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
return 0;
|
returnState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
oledShowMessage("Scale Ready");
|
vTaskResume(RfidReaderTask);
|
||||||
|
vTaskResume(ScaleTask);
|
||||||
|
|
||||||
Serial.println("starte Scale Task");
|
|
||||||
start_scale();
|
|
||||||
|
|
||||||
pauseBambuMqttTask = false;
|
pauseBambuMqttTask = false;
|
||||||
pauseMainTask = 0;
|
pauseMainTask = 0;
|
||||||
|
|
||||||
return 1;
|
return returnState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "HX711.h"
|
#include "HX711.h"
|
||||||
|
|
||||||
|
uint8_t setAutoTare(bool autoTareValue);
|
||||||
uint8_t start_scale();
|
uint8_t start_scale(bool touchSensorConnected);
|
||||||
uint8_t calibrate_scale();
|
uint8_t calibrate_scale();
|
||||||
uint8_t tareScale();
|
uint8_t tareScale();
|
||||||
|
|
||||||
@@ -13,8 +13,10 @@ 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 pauseMainTask;
|
extern uint8_t pauseMainTask;
|
||||||
extern uint8_t scaleCalibrated;
|
extern uint8_t scaleCalibrated;
|
||||||
|
extern bool autoTare;
|
||||||
|
|
||||||
extern TaskHandle_t ScaleTask;
|
extern TaskHandle_t ScaleTask;
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ AsyncWebServer server(webserverPort);
|
|||||||
AsyncWebSocket ws("/ws");
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
uint8_t lastSuccess = 0;
|
uint8_t lastSuccess = 0;
|
||||||
uint8_t lastHasReadRfidTag = 0;
|
nfcReaderStateType lastnfcReaderState = NFC_IDLE;
|
||||||
|
|
||||||
|
|
||||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||||
if (type == WS_EVT_CONNECT) {
|
if (type == WS_EVT_CONNECT) {
|
||||||
Serial.println("Neuer Client verbunden!");
|
Serial.println("Neuer Client verbunden!");
|
||||||
// Sende die AMS-Daten an den neuen Client
|
// Sende die AMS-Daten an den neuen Client
|
||||||
sendAmsData(client);
|
if (!bambuDisabled) sendAmsData(client);
|
||||||
sendNfcData(client);
|
sendNfcData(client);
|
||||||
foundNfcTag(client, 0);
|
foundNfcTag(client, 0);
|
||||||
sendWriteResult(client, 3);
|
sendWriteResult(client, 3);
|
||||||
@@ -43,6 +42,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
|||||||
String message = String((char*)data);
|
String message = String((char*)data);
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
deserializeJson(doc, message);
|
deserializeJson(doc, message);
|
||||||
|
bool spoolmanConnected = (spoolmanApiState != API_INIT);
|
||||||
|
|
||||||
if (doc["type"] == "heartbeat") {
|
if (doc["type"] == "heartbeat") {
|
||||||
// Sende Heartbeat-Antwort
|
// Sende Heartbeat-Antwort
|
||||||
@@ -50,7 +50,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
|||||||
"\"type\":\"heartbeat\","
|
"\"type\":\"heartbeat\","
|
||||||
"\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + ","
|
"\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + ","
|
||||||
"\"bambu_connected\":" + String(bambu_connected) + ","
|
"\"bambu_connected\":" + String(bambu_connected) + ","
|
||||||
"\"spoolman_connected\":" + String(spoolman_connected) + ""
|
"\"spoolman_connected\":" + String(spoolmanConnected) + ""
|
||||||
"}");
|
"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +73,11 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
|||||||
success = calibrate_scale();
|
success = calibrate_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (doc["payload"] == "setAutoTare")
|
||||||
|
{
|
||||||
|
success = setAutoTare(doc["enabled"].as<bool>());
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}");
|
ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}");
|
||||||
} else {
|
} else {
|
||||||
@@ -113,12 +118,12 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
|||||||
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
|
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
|
||||||
String loadHtmlWithHeader(const char* filename) {
|
String loadHtmlWithHeader(const char* filename) {
|
||||||
Serial.println("Lade HTML-Datei: " + String(filename));
|
Serial.println("Lade HTML-Datei: " + String(filename));
|
||||||
if (!SPIFFS.exists(filename)) {
|
if (!LittleFS.exists(filename)) {
|
||||||
Serial.println("Fehler: Datei nicht gefunden!");
|
Serial.println("Fehler: Datei nicht gefunden!");
|
||||||
return "Fehler: Datei nicht gefunden!";
|
return "Fehler: Datei nicht gefunden!";
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = SPIFFS.open(filename, "r");
|
File file = LittleFS.open(filename, "r");
|
||||||
String html = file.readString();
|
String html = file.readString();
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
@@ -139,34 +144,33 @@ void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sendNfcData(AsyncWebSocketClient *client) {
|
void sendNfcData(AsyncWebSocketClient *client) {
|
||||||
if (lastHasReadRfidTag == hasReadRfidTag) return;
|
if (lastnfcReaderState == nfcReaderState)
|
||||||
if (hasReadRfidTag == 0) {
|
return;
|
||||||
|
// TBD: Why is there no status for reading the tag?
|
||||||
|
switch (nfcReaderState)
|
||||||
|
{
|
||||||
|
case NFC_IDLE:
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}");
|
||||||
}
|
break;
|
||||||
else if (hasReadRfidTag == 1) {
|
case NFC_READ_SUCCESS:
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}");
|
||||||
}
|
break;
|
||||||
else if (hasReadRfidTag == 2)
|
case NFC_READ_ERROR:
|
||||||
{
|
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}");
|
||||||
}
|
break;
|
||||||
else if (hasReadRfidTag == 3)
|
case NFC_WRITING:
|
||||||
{
|
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}");
|
||||||
}
|
break;
|
||||||
else if (hasReadRfidTag == 4)
|
case NFC_WRITE_SUCCESS:
|
||||||
{
|
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Error writing to Tag\"}}");
|
|
||||||
}
|
|
||||||
else if (hasReadRfidTag == 5)
|
|
||||||
{
|
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}");
|
||||||
}
|
break;
|
||||||
else
|
case NFC_WRITE_ERROR:
|
||||||
{
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Error writing to Tag\"}}");
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Something went wrong\"}}");
|
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Something went wrong\"}}");
|
||||||
}
|
}
|
||||||
lastHasReadRfidTag = hasReadRfidTag;
|
lastnfcReaderState = nfcReaderState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendAmsData(AsyncWebSocketClient *client) {
|
void sendAmsData(AsyncWebSocketClient *client) {
|
||||||
@@ -195,25 +199,31 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
// Route für about
|
// Route für about
|
||||||
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /about erhalten");
|
Serial.println("Anfrage für /about erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html.gz", "text/html");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/index.html.gz", "text/html");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route für Waage
|
// Route für Waage
|
||||||
server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
Serial.println("Anfrage für /waage erhalten");
|
Serial.println("Anfrage für /waage erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/waage.html.gz", "text/html");
|
//AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
//response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
//response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
|
||||||
});
|
String html = loadHtmlWithHeader("/waage.html");
|
||||||
|
html.replace("{{autoTare}}", (autoTare) ? "checked" : "");
|
||||||
|
|
||||||
|
request->send(200, "text/html", html); });
|
||||||
|
|
||||||
// Route für RFID
|
// Route für RFID
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /rfid erhalten");
|
Serial.println("Anfrage für /rfid erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/rfid.html.gz", "text/html");
|
|
||||||
|
String page = (bambuDisabled) ? "/rfid.html.gz" : "/rfid_bambu.html.gz";
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, page, "text/html");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -229,7 +239,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
// Route für WiFi
|
// Route für WiFi
|
||||||
server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /wifi erhalten");
|
Serial.println("Anfrage für /wifi erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/wifi.html.gz", "text/html");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/wifi.html.gz", "text/html");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -239,7 +249,10 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /spoolman erhalten");
|
Serial.println("Anfrage für /spoolman erhalten");
|
||||||
String html = loadHtmlWithHeader("/spoolman.html");
|
String html = loadHtmlWithHeader("/spoolman.html");
|
||||||
html.replace("{{spoolmanUrl}}", spoolmanUrl);
|
html.replace("{{spoolmanUrl}}", (spoolmanUrl != "") ? spoolmanUrl : "");
|
||||||
|
html.replace("{{spoolmanOctoEnabled}}", octoEnabled ? "checked" : "");
|
||||||
|
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
|
||||||
|
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>())
|
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>())
|
||||||
@@ -277,17 +290,48 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = request->getParam("url")->value();
|
if (request->getParam("octoEnabled")->value() == "true" && (!request->hasParam("octoUrl") || !request->hasParam("octoToken"))) {
|
||||||
url.trim();
|
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing OctoPrint URL or Token parameter\"}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool healthy = saveSpoolmanUrl(url);
|
String url = request->getParam("url")->value();
|
||||||
|
if (url.indexOf("http://") == -1 && url.indexOf("https://") == -1)
|
||||||
|
{
|
||||||
|
url = "http://" + url;
|
||||||
|
}
|
||||||
|
// Remove trailing slash if exists
|
||||||
|
if (url.length() > 0 && url.charAt(url.length() - 1) == '/')
|
||||||
|
{
|
||||||
|
url = url.substring(0, url.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false;
|
||||||
|
String octoUrl = request->getParam("octoUrl")->value();
|
||||||
|
String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : "";
|
||||||
|
|
||||||
|
url.trim();
|
||||||
|
octoUrl.trim();
|
||||||
|
octoToken.trim();
|
||||||
|
|
||||||
|
bool healthy = saveSpoolmanUrl(url, octoEnabled, octoUrl, octoToken);
|
||||||
String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}";
|
String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}";
|
||||||
|
|
||||||
request->send(200, "application/json", jsonResponse);
|
request->send(200, "application/json", jsonResponse);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route für das Überprüfen der Spoolman-Instanz
|
// Route für das Überprüfen der Bambu-Instanz
|
||||||
server.on("/api/bambu", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/api/bambu", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
if (request->hasParam("remove")) {
|
||||||
|
if (removeBambuCredentials()) {
|
||||||
|
request->send(200, "application/json", "{\"success\": true}");
|
||||||
|
} else {
|
||||||
|
request->send(500, "application/json", "{\"success\": false, \"error\": \"Fehler beim Löschen der Bambu-Credentials\"}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!request->hasParam("bambu_ip") || !request->hasParam("bambu_serialnr") || !request->hasParam("bambu_accesscode")) {
|
if (!request->hasParam("bambu_ip") || !request->hasParam("bambu_serialnr") || !request->hasParam("bambu_accesscode")) {
|
||||||
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing parameter\"}");
|
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing parameter\"}");
|
||||||
return;
|
return;
|
||||||
@@ -311,8 +355,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime);
|
bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime);
|
||||||
|
|
||||||
request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}");
|
request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); });
|
||||||
});
|
|
||||||
|
|
||||||
// Route für das Überprüfen der Spoolman-Instanz
|
// Route für das Überprüfen der Spoolman-Instanz
|
||||||
server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
@@ -322,7 +365,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
// Route für das Laden der CSS-Datei
|
// Route für das Laden der CSS-Datei
|
||||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Lade style.css");
|
Serial.println("Lade style.css");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/style.css.gz", "text/css");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/style.css.gz", "text/css");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -331,7 +374,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
// Route für das Logo
|
// Route für das Logo
|
||||||
server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/logo.png.gz", "image/png");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/logo.png.gz", "image/png");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -340,7 +383,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
// Route für Favicon
|
// Route für Favicon
|
||||||
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/favicon.ico", "image/x-icon");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/favicon.ico", "image/x-icon");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
Serial.println("favicon.ico gesendet");
|
Serial.println("favicon.ico gesendet");
|
||||||
@@ -348,7 +391,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
// Route für spool_in.png
|
// Route für spool_in.png
|
||||||
server.on("/spool_in.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/spool_in.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spool_in.png.gz", "image/png");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spool_in.png.gz", "image/png");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -357,7 +400,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
// Route für set_spoolman.png
|
// Route für set_spoolman.png
|
||||||
server.on("/set_spoolman.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/set_spoolman.png", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/set_spoolman.png.gz", "image/png");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/set_spoolman.png.gz", "image/png");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -367,7 +410,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
// Route für JavaScript Dateien
|
// Route für JavaScript Dateien
|
||||||
server.on("/spoolman.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/spoolman.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /spoolman.js erhalten");
|
Serial.println("Anfrage für /spoolman.js erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spoolman.js.gz", "text/javascript");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spoolman.js.gz", "text/javascript");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -376,7 +419,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
server.on("/rfid.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/rfid.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
Serial.println("Anfrage für /rfid.js erhalten");
|
Serial.println("Anfrage für /rfid.js erhalten");
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS,"/rfid.js.gz", "text/javascript");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS,"/rfid.js.gz", "text/javascript");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
@@ -385,7 +428,7 @@ void setupWebserver(AsyncWebServer &server) {
|
|||||||
|
|
||||||
// Vereinfachter Update-Handler
|
// Vereinfachter Update-Handler
|
||||||
server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/upgrade.html.gz", "text/html");
|
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/upgrade.html.gz", "text/html");
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
response->addHeader("Cache-Control", "no-store");
|
response->addHeader("Cache-Control", "no-store");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|||||||
@@ -3,16 +3,20 @@
|
|||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
WiFiManager wm;
|
WiFiManager wm;
|
||||||
bool wm_nonblocking = false;
|
bool wm_nonblocking = false;
|
||||||
|
uint8_t wifiErrorCounter = 0;
|
||||||
|
|
||||||
void initWiFi() {
|
void wifiSettings() {
|
||||||
// Optimierte WiFi-Einstellungen
|
// Optimierte WiFi-Einstellungen
|
||||||
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
|
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
|
||||||
WiFi.setSleep(false); // disable sleep mode
|
WiFi.setSleep(false); // disable sleep mode
|
||||||
|
WiFi.setHostname("FilaMan");
|
||||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||||
|
|
||||||
// Maximale Sendeleistung
|
// Maximale Sendeleistung
|
||||||
@@ -23,33 +27,103 @@ void initWiFi() {
|
|||||||
|
|
||||||
// Aktiviere WiFi-Roaming für bessere Stabilität
|
// Aktiviere WiFi-Roaming für bessere Stabilität
|
||||||
esp_wifi_set_rssi_threshold(-80);
|
esp_wifi_set_rssi_threshold(-80);
|
||||||
|
}
|
||||||
|
|
||||||
if(wm_nonblocking) wm.setConfigPortalBlocking(false);
|
void startMDNS() {
|
||||||
wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
|
if (!MDNS.begin("filaman")) {
|
||||||
|
Serial.println("Error setting up MDNS responder!");
|
||||||
oledShowTopRow();
|
while(1) {
|
||||||
oledShowMessage("WiFi Setup");
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
bool res;
|
|
||||||
// res = wm.autoConnect(); // auto generated AP name from chipid
|
|
||||||
res = wm.autoConnect("FilaMan"); // anonymous ap
|
|
||||||
// res = wm.autoConnect("spoolman","password"); // password protected ap
|
|
||||||
|
|
||||||
if(!res) {
|
|
||||||
Serial.println("Failed to connect or hit timeout");
|
|
||||||
// ESP.restart();
|
|
||||||
oledShowTopRow();
|
|
||||||
oledShowMessage("WiFi not connected Check Portal");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
wifiOn = true;
|
|
||||||
|
|
||||||
//if you get here you have connected to the WiFi
|
|
||||||
Serial.println("connected...yeey :)");
|
|
||||||
Serial.print("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
|
|
||||||
oledShowTopRow();
|
|
||||||
display.display();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Serial.println("mDNS responder started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void configModeCallback (WiFiManager *myWiFiManager) {
|
||||||
|
Serial.println("Entered config mode");
|
||||||
|
oledShowTopRow();
|
||||||
|
oledShowMessage("WiFi Config Mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
void initWiFi() {
|
||||||
|
// load Wifi settings
|
||||||
|
wifiSettings();
|
||||||
|
|
||||||
|
wm.setAPCallback(configModeCallback);
|
||||||
|
|
||||||
|
wm.setSaveConfigCallback([]() {
|
||||||
|
Serial.println("Configurations updated");
|
||||||
|
ESP.restart();
|
||||||
|
});
|
||||||
|
|
||||||
|
if(wm_nonblocking) wm.setConfigPortalBlocking(false);
|
||||||
|
//wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
|
||||||
|
wm.setWiFiAutoReconnect(true);
|
||||||
|
wm.setConnectTimeout(10);
|
||||||
|
|
||||||
|
oledShowTopRow();
|
||||||
|
oledShowMessage("WiFi Setup");
|
||||||
|
|
||||||
|
//bool res = wm.autoConnect("FilaMan"); // anonymous ap
|
||||||
|
if(!wm.autoConnect("FilaMan")) {
|
||||||
|
Serial.println("Failed to connect or hit timeout");
|
||||||
|
// ESP.restart();
|
||||||
|
oledShowTopRow();
|
||||||
|
oledShowMessage("WiFi not connected Check Portal");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wifiOn = true;
|
||||||
|
|
||||||
|
//if you get here you have connected to the WiFi
|
||||||
|
Serial.println("connected...yeey :)");
|
||||||
|
Serial.print("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
oledShowTopRow();
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
// mDNS
|
||||||
|
startMDNS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWiFiConnection() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.println("WiFi connection lost. Reconnecting...");
|
||||||
|
wifiOn = false;
|
||||||
|
oledShowTopRow();
|
||||||
|
oledShowMessage("WiFi reconnecting");
|
||||||
|
WiFi.reconnect(); // Versuche, die Verbindung wiederherzustellen
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS); // Warte 5 Sekunden, bevor erneut geprüft wird
|
||||||
|
if (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.println("Failed to reconnect. Restarting WiFi...");
|
||||||
|
WiFi.disconnect();
|
||||||
|
Serial.println("WiFi disconnected.");
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
wifiErrorCounter++;
|
||||||
|
|
||||||
|
//wifiSettings();
|
||||||
|
WiFi.reconnect();
|
||||||
|
Serial.println("WiFi reconnecting...");
|
||||||
|
WiFi.waitForConnectResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("WiFi reconnected.");
|
||||||
|
wifiErrorCounter = 0;
|
||||||
|
wifiOn = true;
|
||||||
|
oledShowTopRow();
|
||||||
|
startMDNS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wifiErrorCounter >= 5)
|
||||||
|
{
|
||||||
|
Serial.println("Too many WiFi errors. Restarting...");
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,5 +4,6 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
void initWiFi();
|
void initWiFi();
|
||||||
|
void checkWiFiConnection();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 525 KiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 183 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
## **Heat insert location**
|
||||||
|
|
||||||
|
Housing:
|
||||||
|
- every hole is made to fit a heat insert
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
Scale top:
|
||||||
|
- two heat inserts for the NFC Reader
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 491 KiB |
|
After Width: | Height: | Size: 834 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,69 @@
|
|||||||
|
## Modifications
|
||||||
|
|
||||||
|
To reduce costs, components were sourced from AliExpress instead of Amazon. However, differences in dimensions and mounting hole spacing necessitated adjustments to the 3D-printed parts. Additionally M3 heat inserts were used to limit M4 screws to a minimum.
|
||||||
|

|
||||||
|
---
|
||||||
|
|
||||||
|
List of parts that were used:
|
||||||
|
- Display: https://aliexpress.com/item/1005007389730469.html
|
||||||
|
- Scale(5KG with HX711): https://aliexpress.com/item/1005006827930173.html
|
||||||
|
- NFC Reader: https://aliexpress.com/item/1005005973913526.html
|
||||||
|
- NFC Chips: https://aliexpress.com/item/1005006332360160.html
|
||||||
|
- [VORON](https://vorondesign.com/) Heat Inserts M3 OD5mm L4mm: https://aliexpress.com/item/1005003582355741.html - make sure to select the correct size
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **Parts are designed to be printed in ABS/ASA.** Shrinking compensation not needed.
|
||||||
|
|
||||||
|
- **Display and Scale Adjustments:** The AliExpress-sourced display and scale had different dimensions and hole spacings compared to the Amazon versions. The 3D models were modified to accommodate these differences, ensuring proper fit and functionality.
|
||||||
|
- measurement of my Display & Scale to check if your parts will fit can be found in the images folder
|
||||||
|
|
||||||
|
- **Screw Size and Heat Inserts:** All holes originally designed for M4 screws were resized to fit M3 screws. Standard VORON heat inserts were incorporated to provide durable threading. This change standardizes the hardware and simplifies assembly.
|
||||||
|
|
||||||
|
- **Display Mounting:** The display is now mounted using M3 screws with VORON heat inserts. The display's mounting holes need to be drilled to 3mm to accommodate the M3 screws.
|
||||||
|
|
||||||
|
- **Scale Top Surface:** The top surface of the scale was modified to allow M3 socket head cap screws to sit flush with the 3D-printed part. This design ensures that the filament spool rests flat without interference.
|
||||||
|
|
||||||
|
- **NFC Reader Mounting:** The NFC reader is also secured using M3 screws and VORON heat inserts, maintaining consistency across all components.
|
||||||
|
|
||||||
|
- **Scale Base Mounting:** The only M4 screws required are for attaching the metal part of the scale to its base.
|
||||||
|
|
||||||
|
## Benefits of Modifications
|
||||||
|
|
||||||
|
- **Cost Reduction:** Sourcing components from AliExpress offers a more affordable alternative to Amazon, making the project more accessible.
|
||||||
|
|
||||||
|
- **Standardized Hardware:** Using M3 screws and [VORON](https://vorondesign.com/) heat inserts throughout the assembly simplifies the build process and reduces the variety of required hardware.
|
||||||
|
|
||||||
|
- **Enhanced Compatibility:** Adjustments to the 3D models ensure compatibility with readily available components, accommodating variations in part dimensions.
|
||||||
|
|
||||||
|
## Assembly Instructions
|
||||||
|
|
||||||
|
1. **Component Preparation:**
|
||||||
|
- Carefully drill the display's mounting holes to 3mm to fit M3 screws.
|
||||||
|
|
||||||
|
2. **Heat Insert Installation:**
|
||||||
|
- install VORON M3 heat inserts into the designated holes in the 3D-printed housing/case for the ESP32 and Scale top → [heat insert location pictures](./Images/README.md)
|
||||||
|
|
||||||
|
3. **Component Mounting:**
|
||||||
|
- Attach the display, scale, and NFC reader to their respective mounts using M3 screws.
|
||||||
|
- Secure the metal part of the scale to its base using M4 screws.
|
||||||
|
|
||||||
|
4. **Final Assembly:**
|
||||||
|
- Assemble all components according to the original FilaMan instructions, ensuring that all modified parts fit correctly and function as intended.
|
||||||
|
|
||||||
|
For detailed assembly guides and additional resources, refer to the [original FilaMan documentation](https://github.com/ManuelW77/Filaman).
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
These modifications to the FilaMan project provide a cost-effective and standardized approach to building a filament management system. By sourcing components from AliExpress and adjusting the 3D models accordingly, users can achieve the same functionality at a reduced cost, with the added benefit of using uniform hardware throughout the assembly.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### Version 1.0 - 2025-03-04
|
||||||
|
- Initial release of modifications for AliExpress-sourced components.
|
||||||
|
- Adjusted 3D models to fit different display and scale dimensions.
|
||||||
|
- Replaced M4 screws with M3 screws and integrated VORON heat inserts.
|
||||||
|
- Modified display mounting, requiring drilling to 3mm for M3 screws.
|
||||||
|
- Adjusted scale top surface for flush screw placement.
|
||||||
|
- Standardized NFC reader mounting with M3 screws and VORON heat inserts.
|
||||||
|
- Retained M4 screws only for metal scale attachment.
|
||||||