Compare commits
420 Commits
Author | SHA1 | Date | |
---|---|---|---|
818094c36e | |||
4cf3858d0a | |||
66eef2242b | |||
87288e606b | |||
9ae9e80dcd | |||
f2b38a5a99 | |||
ab005b3dd1 | |||
e537c6ec07 | |||
bec769e95a | |||
5cc58927a6 | |||
afde3f5f81 | |||
6800c88bb2 | |||
6172242f24 | |||
7f4b3b8d90 | |||
7a15424bc7 | |||
039a29fa3c | |||
6cccf3d603 | |||
693ee839e5 | |||
0bf383ecd9 | |||
6451d91c59 | |||
8d82e221b5 | |||
bf63ecd594 | |||
0daa3a148b | |||
602642c203 | |||
458bd2e67b | |||
e6a5cb29a9 | |||
6502bb7185 | |||
63fafa2463 | |||
f664e85933 | |||
7bf9868d79 | |||
b9e488d675 | |||
2e3fc19741 | |||
4d84169b29 | |||
10aeb9bc52 | |||
00b9bc08af | |||
dfe9e4dbe9 | |||
79eacae225 | |||
d5d7358f58 | |||
9b362b3c73 | |||
bc51956793 | |||
5666a58da2 | |||
a35f15eca5 | |||
f28b34e427 | |||
9215560558 | |||
7f6bce1699 | |||
2a4f8bb679 | |||
480e2da23e | |||
ba22602767 | |||
b2c68d5aac | |||
52a7f6b5b6 | |||
4cce9f8d5d | |||
f0eced8585 | |||
02e31878ee | |||
7ff499f984 | |||
fcd637cc30 | |||
587485d0de | |||
e0cc99e993 | |||
d9a8388ac7 | |||
cb77112976 | |||
1c0ddb52ba | |||
17f03e9472 | |||
213b9c099c | |||
687e57b77a | |||
aea11e0c06 | |||
bd8f4606c6 | |||
ac91e71c14 | |||
0d3503f4f1 | |||
1460c6e5f9 | |||
fef7e5aa4b | |||
bda8c3dd98 | |||
8702469020 | |||
2a0f999f3b | |||
c89adb6256 | |||
1f21954703 | |||
3e59ce1366 | |||
1f880fc8f1 | |||
69bf5f90fa | |||
382caeaced | |||
47bdf022ec | |||
02febfa943 | |||
257f4df800 | |||
bff6e72219 | |||
26e905050d | |||
046f770a52 | |||
2587227e78 | |||
0f19dc4f46 | |||
721dac1ead | |||
08abd1a37f | |||
da78861613 | |||
9231a303f3 | |||
d12e766cd7 | |||
af7bc23703 | |||
de39892f64 | |||
40cb835e51 | |||
eb9d9e74f4 | |||
d8af3f45e5 | |||
96bb8f9c7c | |||
b8b6893cd0 | |||
0a246c1fe4 | |||
965ea5da1e | |||
b8b6f637f2 | |||
12044b657b | |||
95433b4842 | |||
54275f2ac9 | |||
fbd9cb66f1 | |||
f1cdd3f41d | |||
d897817020 | |||
686eb22232 | |||
a2816da654 | |||
cc8f1cfd7b | |||
d195f76d5e | |||
6bed3b086c | |||
3dd4b82710 | |||
bc41205f15 | |||
f450d1efdf | |||
6e94092a74 | |||
ece510099e | |||
1f01af4da9 | |||
c5d24d5972 | |||
48556b9519 | |||
2ac8effe04 | |||
4e58407af8 | |||
d776956c5e | |||
25233f70d5 | |||
b4584364d6 | |||
33ea062773 | |||
771b0a4839 | |||
c48003e1b2 | |||
83dec4c876 | |||
dca9ef8d08 | |||
513e02b867 | |||
99babe2b4a | |||
c17ab2c434 | |||
ec7386922e | |||
1eb81fad5d | |||
9d406e3428 | |||
5c2db22a90 | |||
164c7b2af5 | |||
cd1c93c485 | |||
15219fa1e4 | |||
206db69e6d | |||
9e67af7343 | |||
9e58b042c8 | |||
55200d31cd | |||
65967ca047 | |||
86e5f7e48a | |||
e4d1ba6c1c | |||
7ccdde8489 | |||
88598611c5 | |||
619979ab14 | |||
377f4bc146 | |||
174c48f734 | |||
7cbd34bc91 | |||
fdeb6d5b61 | |||
f7484f635e | |||
fb7dca38f0 | |||
90ce30215f | |||
|
69ae5cab5f | ||
|
5fa93f2695 | ||
|
0e00fd8b91 | ||
|
4706152022 | ||
|
accb02ab80 | ||
|
5509d98969 | ||
|
d7ee52ba1f | ||
|
a7c99d3f26 | ||
|
0a02912e4a | ||
|
89a5728cc0 | ||
|
f133a1b321 | ||
|
b95497aec2 | ||
876e9c62d8 | |||
765cb5319d | |||
9a9ed175dd | |||
a156cac18e | |||
|
5b04c2eb80 | ||
|
09f4c43f89 | ||
|
b94db80321 | ||
|
ec0e544f30 | ||
|
d815733550 | ||
|
b6d82c8afe | ||
|
afef544c66 | ||
|
97a1368747 | ||
|
6b6aec07b3 | ||
|
85a9bcf8bd | ||
|
852a2f4c69 | ||
|
c450df59aa | ||
|
4b81703e38 | ||
|
722ef421cb | ||
7ba0c4f933 | |||
b0cd731c5a | |||
d0b793a300 | |||
f022bee578 | |||
7c320a87fe | |||
3286b64836 | |||
0777b6371d | |||
739fe7e764 | |||
fcdf91071c | |||
5f8953a19d | |||
ffb1117150 | |||
c919eeb848 | |||
c317610229 | |||
43177c670e | |||
73c3457f40 | |||
1b50694f5f | |||
cf62e12aa4 | |||
48edde8557 | |||
|
b583ef71ad | ||
|
cb5d8ac10a | ||
|
b991f2ee27 | ||
|
bf48c6d4e1 | ||
|
e2e0a23f0a | ||
|
5d2d5e9ee1 | ||
|
537f452601 | ||
|
7e76612bb4 | ||
|
faaffee391 | ||
|
f038020042 | ||
|
d536181a73 | ||
|
8343fe887b | ||
e38220739d | |||
3bb6c1caf5 | |||
fc48d6e67c | |||
37df07f102 | |||
aeb61ba462 | |||
8484c1310b | |||
|
7f25f3e14f | ||
|
fd7b4c25b3 | ||
150a178038 | |||
d490b116b9 | |||
8b43f34a86 | |||
5bc6192b6f | |||
7a85ce6a04 | |||
2202d9a1aa | |||
68fa1e77a1 | |||
7dbca0ab87 | |||
9c06fe6725 | |||
24b3521f83 | |||
1cf392c1cd | |||
6c9f290bac | |||
|
69d6ba4bcb | ||
|
eab937d6ca | ||
|
21ec4e0ff3 | ||
|
27ef8399e4 | ||
c2a09b21a0 | |||
2920159f32 | |||
0937a9e9f0 | |||
2e19bccfa9 | |||
818b8387c0 | |||
859e89431e | |||
3f2beb6f54 | |||
6dc26ca51f | |||
56248ff2cb | |||
0becae7ed6 | |||
6a4945666e | |||
3d31833f50 | |||
97d1519489 | |||
599cc47443 | |||
f608c4a19b | |||
b1f7923770 | |||
aa2eb91d64 | |||
c78c20979d | |||
35d2445c6c | |||
e79c522e46 | |||
537607ed40 | |||
cf8cce72a5 | |||
7e330dca1a | |||
0b356609d1 | |||
d943d15c0a | |||
01f1e123ac | |||
a345b76cd2 | |||
012f91851e | |||
836e48bde2 | |||
9ed3c70c01 | |||
a6a8c69aee | |||
e23f3a2151 | |||
ddb4cd8e53 | |||
f73306f0b9 | |||
d45313a3ff | |||
a450d4bd1a | |||
70350e19f8 | |||
d48d994c00 | |||
7613effccf | |||
32bb85f897 | |||
7280d5be7f | |||
e9d32ee060 | |||
ada4a84942 | |||
aba28422bd | |||
e32aa6ec51 | |||
4a55620d39 | |||
04a18469b5 | |||
7b18266534 | |||
1c4d5f3874 | |||
d81acb2b61 | |||
a2eb57cd7a | |||
8c7fc159d3 | |||
1c619c5bcb | |||
476d3e82e2 | |||
2e05651f88 | |||
3c294a135f | |||
f1b803a3c1 | |||
bb751b6289 | |||
5c4ba9f0ba | |||
7fd01bd1b9 | |||
19d70301f5 | |||
fad84e12c8 | |||
4fa21d3c0e | |||
696efc4d79 | |||
f22a01127c | |||
29868e7101 | |||
92d377713d | |||
823db6157c | |||
8732c81bb9 | |||
458cc4eaf2 | |||
e7bbf45a9f | |||
83d14b32d1 | |||
a8ce964add | |||
2bf7c9fb7d | |||
|
69f01d1e57 | ||
|
ac8adca84d | ||
|
99231786a5 | ||
|
c701149c64 | ||
8536b4f8fa | |||
c84c5fa734 | |||
2a60e149b9 | |||
7e486191b7 | |||
610479bc5a | |||
b7fa53da7e | |||
629b4276cf | |||
cb15dae87e | |||
|
abed1c9806 | ||
db1f33c2b6 | |||
174a58906c | |||
20cc9b196b | |||
ff80b05502 | |||
edfdef53f4 | |||
89a3fed7a9 | |||
f44173824f | |||
169d73bfc0 | |||
c78f36d21a | |||
054bc43f65 | |||
31c41576ee | |||
3a744bc1e6 | |||
42f76fc20a | |||
|
124f326670 | ||
af34ce45dd | |||
c0cb3ff5c9 | |||
4c754d84ff | |||
d2c85018f5 | |||
8dac49ea9e | |||
5365c0e1b9 | |||
|
4abe9d6d33 | ||
e5d0334714 | |||
16364cbd86 | |||
1b63ab668f | |||
f67ef8e905 | |||
a490b77860 | |||
52d063b619 | |||
d5c005d6f7 | |||
68866f1632 | |||
a4200e469d | |||
e5e14dfc99 | |||
863d591a17 | |||
69675f3c06 | |||
2ae3df1aab | |||
3910da9fb5 | |||
26d53929ac | |||
64e3461264 | |||
bc04db91b8 | |||
f500f8bd11 | |||
84391faffd | |||
aae93de7dd | |||
0f847a2731 | |||
aa7fc7e64b | |||
|
e0f5f48cc4 | ||
|
0b79891f83 | ||
|
11c5ca3383 | ||
|
e3c3b3f42d | ||
|
8db7765e7e | ||
|
dc97740ddc | ||
|
ababe8b842 | ||
|
62bcbb2ae8 | ||
|
62330a3fd8 | ||
|
4556730c6e | ||
|
c92a8b0957 | ||
|
b08da071c2 | ||
|
9c949e74e8 | ||
|
17fcf765fd | ||
|
95a03f92e2 | ||
|
d9e69d8c14 | ||
|
1ec09ebf3a | ||
|
7ef0cc44d5 | ||
|
fe962b2bfa | ||
|
aec07f3c6d | ||
|
b5cb5b17ea | ||
|
c3e7758920 | ||
|
66395028a6 | ||
|
64403b9599 | ||
|
ebf6688701 | ||
|
073a5f4539 | ||
|
69bd5c3eb2 | ||
a328fbc6a6 | |||
6f52cd1686 | |||
c1122ad87d | |||
|
1aeced76a2 | ||
967ec35c6a | |||
f60113aa83 | |||
63a7398979 | |||
40cb504251 | |||
41a4f8af4a | |||
e122224472 | |||
5d3a8d971f | |||
e62e5e7062 | |||
726a60882d | |||
2918b4ca77 | |||
955ba0f001 | |||
8cf7dc0b77 | |||
33e4b371ed | |||
fd832d8808 | |||
c0e213a4ac | |||
bcc00f711b | |||
78f336d5d7 | |||
ee7f8ff517 |
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
.pio
|
||||
.vscode/
|
||||
.aider*
|
||||
.DS_Store
|
||||
._*
|
||||
**/.DS_Store
|
||||
**/.Spotlight-V100
|
||||
**/.Trashes
|
||||
**/.fseventsd
|
||||
.AppleDouble
|
||||
**/.DS_Store
|
||||
**/.Spotlight-V100
|
||||
**/.Trashes
|
||||
**/.fseventsd
|
||||
.AppleDouble
|
||||
.aider.chat.history.md
|
||||
.aider.input.history
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.aider.tags.cache.v3/cache.db
|
||||
.aider.tags.cache.v3/cache.db-shm
|
||||
.aider.tags.cache.v3/cache.db-wal
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.vscode/extensions.json
|
||||
.vscode/launch.json
|
||||
include/README
|
||||
lib/README
|
||||
test/README
|
||||
.aider*
|
||||
data/*
|
||||
!data/
|
||||
!data/.gitkeep
|
||||
# important
|
||||
html/bambu_credentials.json
|
||||
html/spoolman_url.json
|
||||
_local/*
|
||||
website/*
|
||||
release.sh
|
||||
.github/copilot-instructions.md
|
||||
data
|
||||
wiki
|
1728
CHANGELOG.md
1728
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
67
README.de.md
67
README.de.md
@@ -27,6 +27,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Filamentdaten auf NFC-Tags schreiben.
|
||||
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- Ermöglicht automatische Spulenerkennung im AMS
|
||||
- **Hersteller Tag Unterstützung:** Automatische Erstellung von Spoolman-Einträgen aus Hersteller NFC-Tags ([Mehr erfahren](README_ManufacturerTags_DE.md))
|
||||
- **Bambulab AMS-Integration:**
|
||||
- Anzeige der aktuellen AMS-Fachbelegung.
|
||||
- Zuordnung von Filamenten zu AMS-Slots.
|
||||
@@ -39,8 +40,35 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Unterstützt das Spoolman Octoprint Plugin
|
||||
|
||||
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
|
||||
|
||||
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
|
||||
|
||||
## Hersteller Tags Unterstützung
|
||||
|
||||
🎉 **Aufregende Neuigkeiten!** FilaMan unterstützt jetzt **Hersteller Tags** - NFC-Tags, die direkt von Filament-Herstellern vorprogrammiert geliefert werden!
|
||||
|
||||
### Erster Hersteller-Partner: RecyclingFabrik
|
||||
|
||||
Wir freuen uns anzukündigen, dass [**RecyclingFabrik**](https://www.recyclingfabrik.de) der **erste Filament-Hersteller** sein wird, der FilaMan unterstützt, indem sie NFC-Tags im FilaMan-Format auf ihren Spulen anbieten!
|
||||
|
||||
**Demnächst verfügbar:** RecyclingFabrik-Spulen werden NFC-Tags enthalten, die sich automatisch in Ihr FilaMan-System integrieren, manuelle Einrichtung überflüssig machen und perfekte Kompatibilität gewährleisten.
|
||||
|
||||
### Wie Hersteller Tags funktionieren
|
||||
|
||||
Wenn Sie zum ersten Mal einen Hersteller NFC-Tag scannen:
|
||||
1. **Automatische Markenerkennung:** FilaMan erkennt den Hersteller und erstellt die Marke in Spoolman
|
||||
2. **Filament-Typ Erstellung:** Alle Materialspezifikationen werden automatisch hinzugefügt
|
||||
3. **Spulen-Registrierung:** Ihre spezifische Spule wird mit korrektem Gewicht und Spezifikationen registriert
|
||||
4. **Zukünftige Schnellerkennung:** Nachfolgende Scans verwenden Fast-Path-Erkennung für sofortige Gewichtsmessung
|
||||
|
||||
**Für detaillierte technische Informationen:** [Hersteller Tags Dokumentation](README_ManufacturerTags_DE.md)
|
||||
|
||||
### Vorteile für Benutzer
|
||||
- ✅ **Null manuelle Einrichtung** - Einfach scannen und wiegen
|
||||
- ✅ **Perfekte Datengenauigkeit** - Hersteller-verifizierte Spezifikationen
|
||||
- ✅ **Sofortige Integration** - Nahtlose Spoolman-Kompatibilität
|
||||
- ✅ **Zukunftssicher** - Tags funktionieren mit jedem FilaMan-kompatiblen System
|
||||
|
||||
## Detaillierte Funktionalität
|
||||
|
||||
### ESP32-Funktionalität
|
||||
@@ -54,20 +82,23 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
## Hardware-Anforderungen
|
||||
|
||||
### Komponenten
|
||||
- **ESP32 Entwicklungsboard:** Jede ESP32-Variante.
|
||||
[Amazon Link](https://amzn.eu/d/aXThslf)
|
||||
- **HX711 5kg Wägezellen-Verstärker:** Für Gewichtsmessung.
|
||||
[Amazon Link](https://amzn.eu/d/06A0DLb)
|
||||
- **OLED 0.96 Zoll I2C weiß/gelb Display:** 128x64 SSD1306.
|
||||
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
||||
- **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen.
|
||||
[Amazon Link](https://amzn.eu/d/jfIuQXb)
|
||||
- **NFC Tags NTAG213 NTA215:** RFID Tag
|
||||
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
||||
### Komponenten (Affiliate Links)
|
||||
- **ESP32 Development Board:** Any ESP32 variant.
|
||||
[Amazon Link](https://amzn.to/3FHea6D)
|
||||
- **HX711 5kg Load Cell Amplifier:** For weight measurement.
|
||||
[Amazon Link](https://amzn.to/4ja1KTe)
|
||||
- **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306.
|
||||
[Amazon Link](https://amzn.to/445aaa9)
|
||||
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
|
||||
[Amazon Link](https://amzn.eu/d/gy9vaBX)
|
||||
- **NFC Tags NTAG213 NTAG215:** RFID Tag
|
||||
[Amazon Link](https://amzn.to/3E071xO)
|
||||
- **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch
|
||||
[Amazon Link](https://amzn.to/4hTChMK)
|
||||
|
||||
### Pin-Konfiguration
|
||||
| Komponente | ESP32 Pin |
|
||||
|
||||
### Pin Konfiguration
|
||||
| Component | ESP32 Pin |
|
||||
|-------------------|-----------|
|
||||
| HX711 DOUT | 16 |
|
||||
| HX711 SCK | 17 |
|
||||
@@ -77,14 +108,22 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
| PN532 RESET | 33 |
|
||||
| PN532 SDA | 21 |
|
||||
| PN532 SCL | 22 |
|
||||
| TTP223 I/O | 25 |
|
||||
|
||||
**Achte darauf, dass am PN532 die DIP-Schalter auf I2C gestellt sind**
|
||||
**!! Achte darauf, dass am PN532 die DIP-Schalter auf I2C gestellt sind**
|
||||
**Nutze den 3V Pin vom ESP für den Touch Sensor**
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
*Die Wägezelle wird bei den meisten HX711 Modulen folgendermaßen verkabelt:
|
||||
E+ rot
|
||||
E- schwarz
|
||||
A- weiß
|
||||
A+ grün*
|
||||
|
||||
## Software-Abhängigkeiten
|
||||
|
||||
### ESP32-Bibliotheken
|
||||
|
52
README.md
52
README.md
@@ -31,6 +31,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Write filament data to NFC tags.
|
||||
- uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- so you can use it with automatic Spool detection in AMS
|
||||
- **Manufacturer Tag Support:** Automatic creation of Spoolman entries from manufacturer NFC tags ([Learn more](README_ManufacturerTags_EN.md))
|
||||
- **Bambulab AMS Integration:**
|
||||
- Display current AMS tray contents.
|
||||
- Assign filaments to AMS slots.
|
||||
@@ -43,8 +44,35 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- Supports Spoolman Octoprint Plugin
|
||||
|
||||
### If you want to support my work, i would be happy to get a coffe
|
||||
|
||||
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
|
||||
|
||||
## Manufacturer Tags Support
|
||||
|
||||
🎉 **Exciting News!** FilaMan now supports **Manufacturer Tags** - NFC tags that come pre-programmed directly from filament manufacturers!
|
||||
|
||||
### First Manufacturer Partner: RecyclingFabrik
|
||||
|
||||
We're thrilled to announce that [**RecyclingFabrik**](https://www.recyclingfabrik.de) will be the **first filament manufacturer** to support FilaMan by offering NFC tags in the FilaMan format on their spools!
|
||||
|
||||
**Coming Soon:** RecyclingFabrik spools will include NFC tags that automatically integrate with your FilaMan system, eliminating manual setup and ensuring perfect compatibility.
|
||||
|
||||
### How Manufacturer Tags Work
|
||||
|
||||
When you scan a manufacturer NFC tag for the first time:
|
||||
1. **Automatic Brand Detection:** FilaMan recognizes the manufacturer and creates the brand in Spoolman
|
||||
2. **Filament Type Creation:** All material specifications are automatically added
|
||||
3. **Spool Registration:** Your specific spool is registered with proper weight and specifications
|
||||
4. **Future Fast Recognition:** Subsequent scans use fast-path detection for instant weight measurement
|
||||
|
||||
**For detailed technical information:** [Manufacturer Tags Documentation](README_ManufacturerTags_EN.md)
|
||||
|
||||
### Benefits for Users
|
||||
- ✅ **Zero Manual Setup** - Just scan and weigh
|
||||
- ✅ **Perfect Data Accuracy** - Manufacturer-verified specifications
|
||||
- ✅ **Instant Integration** - Seamless Spoolman compatibility
|
||||
- ✅ **Future-Proof** - Tags work with any FilaMan-compatible system
|
||||
|
||||
## Detailed Functionality
|
||||
|
||||
### ESP32 Functionality
|
||||
@@ -58,17 +86,19 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
### Components
|
||||
### Components (Affiliate Links)
|
||||
- **ESP32 Development Board:** Any ESP32 variant.
|
||||
[Amazon Link](https://amzn.eu/d/aXThslf)
|
||||
[Amazon Link](https://amzn.to/3FHea6D)
|
||||
- **HX711 5kg Load Cell Amplifier:** For weight measurement.
|
||||
[Amazon Link](https://amzn.eu/d/06A0DLb)
|
||||
[Amazon Link](https://amzn.to/4ja1KTe)
|
||||
- **OLED 0.96 Zoll I2C white/yellow Display:** 128x64 SSD1306.
|
||||
[Amazon Link](https://amzn.eu/d/0AuBp2c)
|
||||
[Amazon Link](https://amzn.to/445aaa9)
|
||||
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
|
||||
[Amazon Link](https://amzn.eu/d/jfIuQXb)
|
||||
[Amazon Link](https://amzn.eu/d/gy9vaBX)
|
||||
- **NFC Tags NTAG213 NTAG215:** RFID Tag
|
||||
[Amazon Link](https://amzn.eu/d/9Z6mXc1)
|
||||
[Amazon Link](https://amzn.to/3E071xO)
|
||||
- **TTP223 Touch Sensor (optional):** For reTARE per Button/Touch
|
||||
[Amazon Link](https://amzn.to/4hTChMK)
|
||||
|
||||
|
||||
### Pin Configuration
|
||||
@@ -82,14 +112,22 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
| PN532 RESET | 33 |
|
||||
| PN532 SDA | 21 |
|
||||
| PN532 SCL | 22 |
|
||||
| TTP223 I/O | 25 |
|
||||
|
||||
**Make sure that the DIP switches on the PN532 are set to I2C**
|
||||
**!! Make sure that the DIP switches on the PN532 are set to I2C**
|
||||
**Use the 3V pin from the ESP for the touch sensor**
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
*The load cell is connected to most HX711 modules as follows:
|
||||
E+ red
|
||||
E- black
|
||||
A- white
|
||||
A+ green*
|
||||
|
||||
## Software Dependencies
|
||||
|
||||
### ESP32 Libraries
|
||||
|
159
README_ManufacturerTags_DE.md
Normal file
159
README_ManufacturerTags_DE.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Hersteller Tags - Deutsche Dokumentation
|
||||
|
||||
## Überblick
|
||||
|
||||
Das FilaMan NFC-System unterstützt **Hersteller Tags**, die es Filament-Produzenten ermöglichen, standardisierte NFC-Tags für ihre Produkte zu erstellen. Beim Scannen dieser Tags werden automatisch die notwendigen Einträge in Spoolman (Marke, Filament-Typ und Spule) erstellt, ohne dass eine manuelle Einrichtung erforderlich ist.
|
||||
|
||||
## Funktionsweise der Hersteller Tags
|
||||
|
||||
### Ablauf
|
||||
|
||||
1. **Tag-Erkennung**: Wenn ein Tag ohne `sm_id` gescannt wird, prüft das System auf Hersteller Tag Format
|
||||
2. **Marken-Erstellung/Suche**: Das System sucht die Marke in Spoolman oder erstellt sie, falls sie nicht existiert
|
||||
3. **Filament-Typ-Erstellung/Suche**: Der Filament-Typ wird basierend auf Marke, Material und Spezifikationen erstellt oder gefunden
|
||||
4. **Spulen-Erstellung**: Ein neuer Spulen-Eintrag wird automatisch mit der Tag-UID als Referenz erstellt
|
||||
5. **Tag-Update**: Der Tag wird mit der neuen Spoolman Spulen-ID (`sm_id`) aktualisiert
|
||||
|
||||
### Warum Hersteller Tags verwenden?
|
||||
|
||||
- **Automatische Integration**: Keine manuelle Dateneingabe erforderlich
|
||||
- **Standardisiertes Format**: Konsistente Produktinformationen verschiedener Hersteller
|
||||
- **Lagerverwaltung**: Automatische Erstellung vollständiger Spoolman-Einträge
|
||||
- **Rückverfolgbarkeit**: Direkte Verbindung zwischen physischem Produkt und digitalem Inventar
|
||||
|
||||
## Tag-Format Spezifikation
|
||||
|
||||
### JSON-Struktur
|
||||
|
||||
Hersteller Tags müssen eine JSON-Payload mit spezifischen Feldern enthalten, die **kurze Schlüssel** verwenden, um die Tag-Größe zu minimieren:
|
||||
|
||||
```json
|
||||
{
|
||||
"b": "Marke/Hersteller Name",
|
||||
"an": "Artikelnummer",
|
||||
"t": "Filament Typ (PLA, PETG, etc)",
|
||||
"c": "Filament Farbe ohne # (FF5733)",
|
||||
"mc": "Optional Mehrfarben-Filament Farben ohne # (FF0000,00FF00,0000FF)",
|
||||
"mcd": "Optional Mehrfarben-Richtung als Wort (coaxial, longitudinal)",
|
||||
"cn": "Farbname (rot, Blaubeere, Arktisches Blau)",
|
||||
"et": "Extruder Temp als Zahl in C° (230)",
|
||||
"bt": "Bett Temp als Zahl in C° (60)",
|
||||
"di": "Durchmesser als Float (1.75)",
|
||||
"de": "Dichte als Float (1.24)",
|
||||
"sw": "Leeres Spulengewicht als Zahl in g (180)",
|
||||
"u": "URL zum Filament mit der Artikelnummer"
|
||||
}
|
||||
```
|
||||
|
||||
### Pflichtfelder
|
||||
|
||||
- **`b`** (brand): Hersteller/Markenname
|
||||
- **`an`** (article number): Eindeutige Produktkennung
|
||||
- **`t`** (type): Materialtyp (PLA, PETG, ABS, etc.)
|
||||
- **`c`** (color): Hex-Farbcode ohne #
|
||||
- **`cn`** (color name): Lesbare Farbbezeichnung
|
||||
- **`et`** (extruder temp): Empfohlene Extruder-Temperatur in Celsius
|
||||
- **`bt`** (bed temp): Empfohlene Bett-Temperatur in Celsius
|
||||
- **`di`** (diameter): Filamentdurchmesser in mm
|
||||
- **`de`** (density): Materialdichte in g/cm³
|
||||
- **`sw`** (spool weight): Leeres Spulengewicht in Gramm
|
||||
|
||||
### Optionale Felder
|
||||
|
||||
- **`mc`** (multicolor): Komma-getrennte Hex-Farben für Mehrfarben-Filamente
|
||||
- **`mcd`** (multicolor direction): Richtung für Mehrfarben (coaxial, longitudinal)
|
||||
- **`u`** (url): Produkt-URL mit direktem Link zum Artikel zB für Nachbestellung
|
||||
|
||||
### Beispiel Tag
|
||||
|
||||
```json
|
||||
{"b":"Recycling Fabrik","an":"FX1_PETG-S175-1000-DAEM00055","t":"PETG","c":"FF5733","cn":"Lebendiges Orange","et":"230","bt":"70","di":"1.75","de":"1.24","sw":"180","u":"https://www.recyclingfabrik.com/search?q="}
|
||||
```
|
||||
|
||||
## Implementierungsrichtlinien
|
||||
|
||||
### Für Hersteller
|
||||
|
||||
1. **Tag-Kodierung**: NDEF-Format mit MIME-Typ `application/json` verwenden
|
||||
2. **Datenminimierung**: Kompaktes JSON-Format für Tag-Größenbegrenzungen nutzen
|
||||
3. **Qualitätskontrolle**: Sicherstellen, dass alle Pflichtfelder vorhanden und korrekt formatiert sind
|
||||
4. **Testen**: Tags vor der Produktion mit dem FilaMan-System verifizieren
|
||||
|
||||
### Tag-Größe Überlegungen
|
||||
|
||||
- **NTAG213**: 144 Bytes Nutzerdaten (geeignet für einfache Tags)
|
||||
- **NTAG215**: 504 Bytes Nutzerdaten (empfohlen für umfassende Daten)
|
||||
- **NTAG216**: 888 Bytes Nutzerdaten (maximale Kompatibilität)
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Markennamen über alle Produkte hinweg konsistent halten
|
||||
- Standardisierte Materialtypnamen verwenden (PLA, PETG, ABS, etc.)
|
||||
- Genaue Temperaturempfehlungen angeben
|
||||
- Aussagekräftige Farbnamen für bessere Benutzererfahrung verwenden
|
||||
- Tags vor Massenproduktion mit dem FilaMan-System testen
|
||||
|
||||
## System-Integration
|
||||
|
||||
### Spoolman Datenbankstruktur
|
||||
|
||||
Bei der Verarbeitung eines Hersteller Tags erstellt das System:
|
||||
|
||||
1. **Lieferanten-Eintrag**: Markeninformationen in der Spoolman Lieferanten-Datenbank
|
||||
2. **Filament-Eintrag**: Materialspezifikationen und Eigenschaften
|
||||
3. **Spulen-Eintrag**: Einzelne Spule mit Gewicht und NFC-Tag-Referenz
|
||||
|
||||
### Fast-Path Erkennung
|
||||
|
||||
Sobald ein Tag verarbeitet und mit `sm_id` aktualisiert wurde, nutzt er das Fast-Path-Erkennungssystem für schnelle nachfolgende Scans.
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Häufige Probleme
|
||||
|
||||
- **Tag zu klein**: NTAG215 oder NTAG216 für größere JSON-Payloads verwenden
|
||||
- **Fehlende Felder**: Sicherstellen, dass alle Pflichtfelder vorhanden sind
|
||||
- **Ungültiges Format**: JSON-Syntax und Feldtypen überprüfen
|
||||
- **Spoolman-Verbindung**: Sicherstellen, dass FilaMan mit der Spoolman API verbinden kann
|
||||
|
||||
### Validierung
|
||||
|
||||
Das System validiert:
|
||||
|
||||
- JSON-Format Korrektheit
|
||||
- Vorhandensein der Pflichtfelder
|
||||
- Datentyp-Konformität
|
||||
- Tag-Größe Kompatibilität
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Verarbeitungsalgorithmus
|
||||
|
||||
1. Tag-Scan erkennt kein `sm_id` Feld
|
||||
2. System prüft auf `b` (Marke) und `an` (Artikelnummer) Felder
|
||||
3. `checkVendor()` erstellt oder findet Marke in Spoolman
|
||||
4. `checkFilament()` erstellt oder findet Filament-Typ
|
||||
5. `createSpool()` erstellt neuen Spulen-Eintrag
|
||||
6. Tag wird mit neuer `sm_id` aktualisiert
|
||||
|
||||
### Fehlerbehandlung
|
||||
|
||||
- Graceful Fallback bei Netzwerkproblemen
|
||||
- Detaillierte Protokollierung für Debugging
|
||||
- Benutzer-Feedback bei fehlgeschlagenen Operationen
|
||||
- Wiederholungsmechanismen für temporäre Fehler
|
||||
|
||||
### Systemverhalten
|
||||
|
||||
#### Bei fehlendem sm_id:
|
||||
- System prüft auf `b` (brand) und `an` (artnr) Felder
|
||||
- Falls vorhanden → Hersteller Tag erkannt
|
||||
- Automatische Erstellung von Lieferant, Filament und Spule in Spoolman
|
||||
- Tag wird mit neuer `sm_id` beschrieben
|
||||
|
||||
#### Bei vorhandenem sm_id:
|
||||
- Fast-Path Erkennung für bekannte Spulen
|
||||
- Sofortige Gewichtsmessung ohne vollständige Tag-Analyse
|
||||
- Optimierte Performance für häufig verwendete Tags
|
||||
|
||||
Dieses System ermöglicht eine nahtlose Integration von Hersteller-Filamentprodukten in das FilaMan-Ökosystem unter Beibehaltung von Datenkonsistenz und Benutzererfahrung.
|
145
README_ManufacturerTags_EN.md
Normal file
145
README_ManufacturerTags_EN.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Manufacturer Tags - English Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The FilaMan NFC system supports **Manufacturer Tags** that allow filament producers to create standardized NFC tags for their products. When scanned, these tags automatically create the necessary entries in Spoolman (brand, filament type, and spool) without requiring manual setup.
|
||||
|
||||
## How Manufacturer Tags Work
|
||||
|
||||
### Process Flow
|
||||
|
||||
1. **Tag Detection**: When a tag without `sm_id` is scanned, the system checks for manufacturer tag format
|
||||
2. **Brand Creation/Lookup**: The system searches for the brand in Spoolman or creates it if it doesn't exist
|
||||
3. **Filament Type Creation/Lookup**: The filament type is created or found based on brand, material, and specifications
|
||||
4. **Spool Creation**: A new spool entry is automatically created with the tag's UID as reference
|
||||
5. **Tag Update**: The tag is updated with the new Spoolman spool ID (`sm_id`)
|
||||
|
||||
### Why Use Manufacturer Tags?
|
||||
|
||||
- **Automatic Integration**: No manual data entry required
|
||||
- **Standardized Format**: Consistent product information across different manufacturers
|
||||
- **Inventory Management**: Automatic creation of complete Spoolman entries
|
||||
- **Traceability**: Direct link between physical product and digital inventory
|
||||
|
||||
## Tag Format Specification
|
||||
|
||||
### JSON Structure
|
||||
|
||||
Manufacturer tags must contain a JSON payload with specific fields using **short keys** to minimize tag size:
|
||||
|
||||
```json
|
||||
{
|
||||
"b": "Brand/Vendor Name",
|
||||
"an": "Article Number",
|
||||
"t": "Filament Type (PLA, PETG, etc)",
|
||||
"c": "Filament Color without # (FF5733)",
|
||||
"mc": "Optional Multicolor Filament Colors without # (FF0000,00FF00,0000FF)",
|
||||
"mcd": "Optional Multicolor Direction as Word (coaxial, longitudinal)",
|
||||
"cn": "Color Name (red, Blueberry, Arctic Blue)",
|
||||
"et": "Extruder Temp as Number in C° (230)",
|
||||
"bt": "Bed Temp as Number in C° (60)",
|
||||
"di": "Diameter as Float (1.75)",
|
||||
"de": "Density as Float (1.24)",
|
||||
"sw": "Empty Spool Weight as Number in g (180)",
|
||||
"u": "URL to get the Filament with the Article Number"
|
||||
}
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
- **`b`** (brand): Manufacturer/brand name
|
||||
- **`an`** (article number): Unique product identifier
|
||||
- **`t`** (type): Material type (PLA, PETG, ABS, etc.)
|
||||
- **`c`** (color): Hex color code without #
|
||||
- **`cn`** (color name): Human-readable color name
|
||||
- **`et`** (extruder temp): Recommended extruder temperature in Celsius
|
||||
- **`bt`** (bed temp): Recommended bed temperature in Celsius
|
||||
- **`di`** (diameter): Filament diameter in mm
|
||||
- **`de`** (density): Material density in g/cm³
|
||||
- **`sw`** (spool weight): Empty spool weight in grams
|
||||
|
||||
### Optional Fields
|
||||
|
||||
- **`mc`** (multicolor): Comma-separated hex colors for multicolor filaments
|
||||
- **`mcd`** (multicolor direction): Direction for multicolor (coaxial, longitudinal)
|
||||
- **`u`** (url): Product URL with direct link to the article e.g. for reordering
|
||||
|
||||
### Example Tag
|
||||
|
||||
```json
|
||||
{"b":"Recycling Fabrik","an":"FX1_PETG-S175-1000-DAEM00055","t":"PETG","c":"FF5733","cn":"Vibrant Orange","et":"230","bt":"70","di":"1.75","de":"1.24","sw":"180","u":"https://www.recyclingfabrik.com/search?q="}
|
||||
```
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
### For Manufacturers
|
||||
|
||||
1. **Tag Encoding**: Use NDEF format with MIME type `application/json`
|
||||
2. **Data Minimization**: Use the compact JSON format to fit within tag size limits
|
||||
3. **Quality Control**: Ensure all required fields are present and correctly formatted
|
||||
4. **Testing**: Verify tags work with FilaMan system before production
|
||||
|
||||
### Tag Size Considerations
|
||||
|
||||
- **NTAG213**: 144 bytes user data (suitable for basic tags)
|
||||
- **NTAG215**: 504 bytes user data (recommended for comprehensive data)
|
||||
- **NTAG216**: 888 bytes user data (maximum compatibility)
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Keep brand names consistent across all products
|
||||
- Use standardized material type names (PLA, PETG, ABS, etc.)
|
||||
- Provide accurate temperature recommendations
|
||||
- Include meaningful color names for user experience
|
||||
- Test tags with the FilaMan system before mass production
|
||||
|
||||
## System Integration
|
||||
|
||||
### Spoolman Database Structure
|
||||
|
||||
When a manufacturer tag is processed, the system creates:
|
||||
|
||||
1. **Vendor Entry**: Brand information in Spoolman vendor database
|
||||
2. **Filament Entry**: Material specifications and properties
|
||||
3. **Spool Entry**: Individual spool with weight and NFC tag reference
|
||||
|
||||
### Fast-Path Recognition
|
||||
|
||||
Once a tag is processed and updated with `sm_id`, it uses the fast-path recognition system for quick subsequent scans.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
- **Tag Too Small**: Use NTAG215 or NTAG216 for larger JSON payloads
|
||||
- **Missing Fields**: Ensure all required fields are present
|
||||
- **Invalid Format**: Verify JSON syntax and field types
|
||||
- **Spoolman Connection**: Ensure FilaMan can connect to Spoolman API
|
||||
|
||||
### Validation
|
||||
|
||||
The system validates:
|
||||
- JSON format correctness
|
||||
- Required field presence
|
||||
- Data type compliance
|
||||
- Tag size compatibility
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Processing Algorithm
|
||||
|
||||
1. Tag scan detects no `sm_id` field
|
||||
2. System checks for `b` (brand) and `an` (article number) fields
|
||||
3. `checkVendor()` creates or finds brand in Spoolman
|
||||
4. `checkFilament()` creates or finds filament type
|
||||
5. `createSpool()` creates new spool entry
|
||||
6. Tag is updated with new `sm_id`
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Graceful fallback for network issues
|
||||
- Detailed logging for debugging
|
||||
- User feedback for failed operations
|
||||
- Retry mechanisms for temporary failures
|
||||
|
||||
This system enables seamless integration of manufacturer filament products into the FilaMan ecosystem while maintaining data consistency and user experience.
|
468
WIKI_DE.md
Normal file
468
WIKI_DE.md
Normal file
@@ -0,0 +1,468 @@
|
||||
# FilaMan Wiki - Deutsch
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Überblick](#überblick)
|
||||
2. [Installation](#installation)
|
||||
3. [Hardware-Anforderungen](#hardware-anforderungen)
|
||||
4. [Ersteinrichtung](#ersteinrichtung)
|
||||
5. [Konfiguration](#konfiguration)
|
||||
6. [Benutzung](#benutzung)
|
||||
7. [NFC-Tags](#nfc-tags)
|
||||
8. [Bambu Lab Integration](#bambu-lab-integration)
|
||||
9. [Spoolman Integration](#spoolman-integration)
|
||||
10. [Octoprint Integration](#octoprint-integration)
|
||||
11. [Hersteller Tags](#hersteller-tags)
|
||||
12. [Fehlerbehebung](#fehlerbehebung)
|
||||
13. [Support](#support)
|
||||
|
||||
---
|
||||
|
||||
## Überblick
|
||||
|
||||
FilaMan ist ein umfassendes Filament-Managementsystem für 3D-Drucker, das auf ESP32-Hardware basiert. Es bietet Gewichtsmessung, NFC-Tag-Management und nahtlose Integration mit Spoolman und Bambu Lab 3D-Druckern.
|
||||
|
||||
### Hauptfunktionen
|
||||
|
||||
- **Präzise Gewichtsmessung** mit HX711 Wägezellen-Verstärker
|
||||
- **NFC-Tag Lesen und Schreiben** für Filament-Identifikation
|
||||
- **OLED-Display** für Status-Anzeigen
|
||||
- **WiFi-Konnektivität** mit einfacher Konfiguration
|
||||
- **Webbasierte Benutzeroberfläche** mit Echtzeit-Updates
|
||||
- **Spoolman-Integration** für Lagerverwaltung
|
||||
- **Bambu Lab AMS-Steuerung** via MQTT
|
||||
- **Openspool NFC-Format** Kompatibilität
|
||||
- **Hersteller Tag Unterstützung** für automatische Einrichtung
|
||||
|
||||
### Systemvoraussetzungen
|
||||
|
||||
- **ESP32 Development Board**
|
||||
- **Spoolman Instanz** (erforderlich für volle Funktionalität)
|
||||
- **WiFi-Netzwerk**
|
||||
- **Webbrowser** (Chrome/Firefox/Safari)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Einfache Installation (Empfohlen)
|
||||
|
||||
1. **Öffnen Sie den [FilaMan Web-Installer](https://www.filaman.app/installer.html)**
|
||||
- Verwenden Sie einen Chrome-basierten Browser
|
||||
|
||||
2. **ESP32 vorbereiten**
|
||||
- Verbinden Sie den ESP32 über USB mit Ihrem Computer
|
||||
- Klicken Sie auf "Connect"
|
||||
|
||||
3. **Port auswählen**
|
||||
- Wählen Sie den entsprechenden USB-Port aus
|
||||
- Bestätigen Sie die Auswahl
|
||||
|
||||
4. **Installation starten**
|
||||
- Klicken Sie auf "FilaMan installieren"
|
||||
- Warten Sie, bis der Installationsvorgang abgeschlossen ist
|
||||
|
||||
### Manuelle Kompilierung
|
||||
|
||||
Für erfahrene Benutzer mit PlatformIO:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ManuelW77/Filaman.git
|
||||
cd FilaMan/esp32
|
||||
pio lib install
|
||||
pio run --target upload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardware-Anforderungen
|
||||
|
||||
### Erforderliche Komponenten
|
||||
|
||||
| Komponente | Beschreibung | Amazon Link (Affiliate) |
|
||||
|------------|--------------|-------------------------|
|
||||
| ESP32 Development Board | Jede ESP32-Variante | [Amazon](https://amzn.to/3FHea6D) |
|
||||
| HX711 + Wägezelle | 5kg Load Cell Amplifier | [Amazon](https://amzn.to/4ja1KTe) |
|
||||
| OLED Display | 0.96" I2C 128x64 SSD1306 | [Amazon](https://amzn.to/445aaa9) |
|
||||
| PN532 NFC Modul | V3 RFID-Modul | [Amazon](https://amzn.eu/d/gy9vaBX) |
|
||||
| NFC Tags | NTAG213/NTAG215 | [Amazon](https://amzn.to/3E071xO) |
|
||||
| TTP223 Touch Sensor | Optional für Tara-Funktion | [Amazon](https://amzn.to/4hTChMK) |
|
||||
|
||||
### Pin-Konfiguration
|
||||
|
||||
| Komponente | ESP32 Pin | Funktion |
|
||||
|------------|-----------|----------|
|
||||
| HX711 DOUT | 16 | Datenausgang Wägezelle |
|
||||
| HX711 SCK | 17 | Takt Wägezelle |
|
||||
| OLED SDA | 21 | I2C Daten |
|
||||
| OLED SCL | 22 | I2C Takt |
|
||||
| PN532 IRQ | 32 | Interrupt |
|
||||
| PN532 RESET | 33 | Reset |
|
||||
| PN532 SDA | 21 | I2C Daten (geteilt) |
|
||||
| PN532 SCL | 22 | I2C Takt (geteilt) |
|
||||
| TTP223 I/O | 25 | Touch-Sensor (optional) |
|
||||
|
||||
### Wichtige Hinweise
|
||||
|
||||
- **PN532 DIP-Schalter** müssen auf I2C-Modus eingestellt sein
|
||||
- **3V Pin** vom ESP32 für Touch-Sensor verwenden
|
||||
- **Wägezellen-Verkabelung**: E+ (rot), E- (schwarz), A- (weiß), A+ (grün)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Ersteinrichtung
|
||||
|
||||
### Nach der Installation
|
||||
|
||||
1. **ESP32 Neustart**
|
||||
- Das System erstellt automatisch einen WiFi-Hotspot "FilaMan"
|
||||
|
||||
2. **WiFi-Konfiguration**
|
||||
- Verbinden Sie sich mit dem "FilaMan" Netzwerk
|
||||
- Öffnen Sie einen Browser (automatisches Portal oder http://192.168.4.1)
|
||||
- Konfigurieren Sie Ihre WiFi-Zugangsdaten
|
||||
|
||||
3. **Erster Zugriff**
|
||||
- Nach erfolgreicher WiFi-Verbindung ist das System unter http://filaman.local erreichbar
|
||||
- Alternativ über die vom Router zugewiesene IP-Adresse
|
||||
|
||||
### Spoolman Vorbereitung
|
||||
|
||||
**Wichtiger Hinweis**: Spoolman muss im Debug-Modus laufen:
|
||||
|
||||
```env
|
||||
# In der .env Datei von Spoolman auskommentieren:
|
||||
SPOOLMAN_DEBUG_MODE=TRUE
|
||||
```
|
||||
|
||||
Dies ist erforderlich, da Spoolman noch keine CORS-Domain-Konfiguration unterstützt.
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Waagen-Kalibrierung
|
||||
|
||||
1. **Kalibrierung starten**
|
||||
- Gehen Sie zur "Scale" (Waage) Seite
|
||||
- Bereiten Sie ein 500g Referenzgewicht vor (z.B. Wasserglas)
|
||||
|
||||
2. **Kalibrierungsschritte**
|
||||
- Folgen Sie den Anweisungen auf dem Display
|
||||
- Legen Sie das Gewicht auf, wenn gefordert
|
||||
- Warten Sie, bis die Kalibrierung abgeschlossen ist
|
||||
|
||||
3. **Validierung**
|
||||
- Testen Sie die Genauigkeit mit bekannten Gewichten
|
||||
- Bei Bedarf "Tare Scale" für Nullstellung verwenden
|
||||
|
||||
### Spoolman-Verbindung
|
||||
|
||||
1. **Spoolman-URL eingeben**
|
||||
- Gehen Sie zur "Spoolman/Bambu" Seite
|
||||
- Geben Sie die vollständige URL Ihrer Spoolman-Instanz ein
|
||||
- Format: `http://spoolman-server:7912`
|
||||
|
||||
2. **Verbindung testen**
|
||||
- Das System prüft automatisch die Verbindung
|
||||
- Erfolgreiche Verbindung wird durch grünen Status angezeigt
|
||||
|
||||
### Bambu Lab Drucker (optional)
|
||||
|
||||
1. **Drucker-Einstellungen**
|
||||
- Öffnen Sie das Einstellungsmenü auf Ihrem Bambu-Drucker
|
||||
- Notieren Sie sich die folgenden Daten:
|
||||
- IP-Adresse des Druckers
|
||||
- Access Code
|
||||
- Serial Number
|
||||
|
||||
2. **FilaMan Konfiguration**
|
||||
- Geben Sie die Drucker-Daten in der "Spoolman/Bambu" Seite ein
|
||||
- Aktivieren Sie "Auto Send to Bambu" für automatische AMS-Zuordnung
|
||||
|
||||
3. **Auto-Send Timeout**
|
||||
- Konfigurieren Sie die Wartezeit für automatische Spulen-Erkennung
|
||||
- Empfohlener Wert: 10-30 Sekunden
|
||||
|
||||
---
|
||||
|
||||
## Benutzung
|
||||
|
||||
### Grundlegende Bedienung
|
||||
|
||||
1. **Filament wiegen**
|
||||
- Platzieren Sie die Spule auf der Waage
|
||||
- Das Gewicht wird automatisch auf dem Display und in der Weboberfläche angezeigt
|
||||
|
||||
2. **NFC-Tag scannen**
|
||||
- Halten Sie den Tag in die Nähe des PN532-Moduls
|
||||
- Bei erkannten Tags wird die Spulen-Information angezeigt
|
||||
- Das Gewicht wird automatisch in Spoolman aktualisiert
|
||||
|
||||
3. **Status-Überwachung**
|
||||
- **OLED-Display** zeigt aktuelles Gewicht und Verbindungsstatus
|
||||
- **Weboberfläche** bietet detaillierte Informationen und Steuerung
|
||||
|
||||
### Weboberfläche Navigation
|
||||
|
||||
- **Startseite**: Hauptfunktionen und aktueller Status
|
||||
- **Scale**: Waagen-Kalibrierung und -Einstellungen
|
||||
- **Spoolman/Bambu**: System-Konfiguration
|
||||
- **Statistics**: Nutzungsstatistiken (falls aktiviert)
|
||||
|
||||
---
|
||||
|
||||
## NFC-Tags
|
||||
|
||||
### Unterstützte Tag-Typen
|
||||
|
||||
- **NTAG213**: 144 Bytes (grundlegende Funktionen)
|
||||
- **NTAG215**: 504 Bytes (empfohlen)
|
||||
- **NTAG216**: 888 Bytes (erweiterte Funktionen)
|
||||
|
||||
### Tag beschreiben
|
||||
|
||||
1. **Spule in Spoolman vorbereiten**
|
||||
- Erstellen Sie eine neue Spule in Spoolman
|
||||
- Stellen Sie sicher, dass alle erforderlichen Daten eingegeben sind
|
||||
|
||||
2. **Tag-Beschreibung starten**
|
||||
- Wählen Sie die Spule aus der Liste
|
||||
- Klicken Sie auf "Write Tag"
|
||||
- Das Display zeigt "Waiting for Tag"
|
||||
|
||||
3. **Tag auflegen**
|
||||
- Platzieren Sie den NFC-Tag auf dem PN532-Modul
|
||||
- Warten Sie auf die Bestätigung
|
||||
|
||||
4. **Erfolgsmeldung**
|
||||
- Bei erfolgreichem Beschreiben wird ein Häkchen angezeigt
|
||||
- Der Tag ist nun mit der Spoolman-Spule verknüpft
|
||||
|
||||
### Tag lesen
|
||||
|
||||
1. **Tag scannen**
|
||||
- Platzieren Sie die Spule mit dem NFC-Tag auf die Waage über dem NFC-Reader
|
||||
- Bei Problemen beim Lesen: Spule etwas anders positionieren (nicht ganz an den Rand)
|
||||
- Die Spulen-Information wird automatisch geladen
|
||||
|
||||
2. **Automatische Updates**
|
||||
- Das aktuelle Gewicht wird in Spoolman übertragen
|
||||
- Die Spule wird in der Weboberfläche automatisch ausgewählt
|
||||
|
||||
---
|
||||
|
||||
## Bambu Lab Integration
|
||||
|
||||
### AMS (Automatic Material System)
|
||||
|
||||
1. **AMS-Status anzeigen**
|
||||
- Die Weboberfläche zeigt den aktuellen Zustand aller AMS-Fächer
|
||||
- Beladene Fächer werden mit Filament-Informationen angezeigt
|
||||
|
||||
2. **Filament manuell zuordnen**
|
||||
- Wählen Sie eine Spule aus der Spoolman-Liste
|
||||
- Klicken Sie auf das entsprechende AMS-Fach-Symbol
|
||||
- Das Filament wird dem Fach zugeordnet
|
||||
|
||||
3. **Automatische Zuordnung**
|
||||
- Nach dem Wiegen mit aktiviertem "Auto Send to Bambu"
|
||||
- Das System wartet auf neue Spulen im AMS
|
||||
- Kalibrierte Filamente werden automatisch zugeordnet
|
||||
|
||||
### Bambu Studio Integration
|
||||
|
||||
1. **Filament-Profile synchronisieren**
|
||||
- Kalibrieren Sie Filamente in Bambu Studio
|
||||
- Verwenden Sie Device → AMS → Bleistift-Symbol → Auswählen
|
||||
|
||||
2. **Setting-IDs speichern**
|
||||
- FilaMan erkennt verfügbare Setting-IDs automatisch
|
||||
- Klicken Sie auf "Settings in Spoolman speichern"
|
||||
- Die Profile werden für zukünftige Drucke verwendet
|
||||
|
||||
### Verbindung wiederherstellen
|
||||
|
||||
- Bei Verbindungsproblemen klicken Sie den roten Punkt in der Menüleiste
|
||||
- Das System stellt automatisch eine neue Verbindung her
|
||||
|
||||
---
|
||||
|
||||
## Spoolman Integration
|
||||
|
||||
### Automatische Funktionen
|
||||
|
||||
1. **Spulen-Synchronisation**
|
||||
- Automatische Übertragung von Gewichtsänderungen
|
||||
- Echtzeit-Updates der Spulen-Daten
|
||||
|
||||
2. **Extra-Felder**
|
||||
- FilaMan erstellt automatisch erforderliche benutzerdefinierte Felder
|
||||
- NFC-Tag-UID wird als Referenz gespeichert
|
||||
|
||||
3. **Filterung**
|
||||
- "Nur Spulen ohne NFC-Tag anzeigen" für einfache Tag-Zuordnung
|
||||
- Kategorisierung nach Herstellern und Materialtypen
|
||||
|
||||
### Spoolman Octoprint Plugin
|
||||
|
||||
Für Octoprint-Benutzer ist eine automatische Spulen-Zuordnung verfügbar:
|
||||
|
||||
1. **Plugin installieren**
|
||||
```
|
||||
https://github.com/ManuelW77/OctoPrint-Spoolman-Filaman/archive/refs/heads/master.zip
|
||||
```
|
||||
|
||||
2. **FilaMan konfigurieren**
|
||||
- Aktivieren Sie "Send to Octo-Plugin"
|
||||
- Geben Sie Octoprint-URL und API-Key ein
|
||||
|
||||
3. **Automatische Zuordnung**
|
||||
- Nach dem Wiegen wird die Spule automatisch in Octoprint aktiviert
|
||||
- Unterstützt aktuell nur Tool0 (erste Düse)
|
||||
|
||||
---
|
||||
|
||||
## Hersteller Tags
|
||||
|
||||
### Überblick
|
||||
|
||||
Hersteller Tags ermöglichen es Filament-Produzenten, vorkonfigurierte NFC-Tags zu liefern, die automatisch alle notwendigen Einträge in Spoolman erstellen.
|
||||
|
||||
### Erste Schritte mit Hersteller Tags
|
||||
|
||||
1. **Tag scannen**
|
||||
- Platzieren Sie die Spule mit dem Hersteller-Tag auf die Waage über dem NFC-Reader
|
||||
- Bei Problemen beim Lesen: Spule etwas anders positionieren (nicht ganz an den Rand)
|
||||
- Das System erkennt automatisch das Hersteller-Format
|
||||
|
||||
2. **Automatische Erstellung**
|
||||
- **Marke** wird in Spoolman angelegt (falls nicht vorhanden)
|
||||
- **Filament-Typ** wird mit allen Spezifikationen erstellt
|
||||
- **Spule** wird automatisch registriert
|
||||
|
||||
3. **Zukünftige Scans**
|
||||
- Nach der ersten Einrichtung nutzen Tags das Fast-Path-System
|
||||
- Sofortige Gewichtsmessung ohne erneute Einrichtung
|
||||
|
||||
### Unterstützte Hersteller
|
||||
|
||||
- **RecyclingFabrik**: Erster offizieller Partner
|
||||
- Weitere Hersteller folgen
|
||||
|
||||
### Vorteile
|
||||
|
||||
- ✅ **Null manuelle Einrichtung**
|
||||
- ✅ **Perfekte Datengenauigkeit**
|
||||
- ✅ **Sofortige Integration**
|
||||
- ✅ **Zukunftssicher**
|
||||
|
||||
---
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Häufige Probleme
|
||||
|
||||
#### WiFi-Verbindung
|
||||
|
||||
**Problem**: Kann nicht mit FilaMan-Hotspot verbinden
|
||||
- Lösung: Stellen Sie sicher, dass der ESP32 gestartet ist
|
||||
- Alternative: Manuell zu http://192.168.4.1 navigieren
|
||||
|
||||
**Problem**: Weboberfläche nicht erreichbar
|
||||
- Lösung: Prüfen Sie die IP-Adresse im Router
|
||||
- Alternative: Verwenden Sie http://filaman.local
|
||||
|
||||
#### Waage
|
||||
|
||||
**Problem**: Ungenaue Gewichtsmessungen
|
||||
- Lösung: Kalibrierung wiederholen
|
||||
- Tipp: Verwenden Sie "Tare Scale" für Nullstellung
|
||||
|
||||
**Problem**: Wägezelle reagiert nicht
|
||||
- Lösung: Überprüfen Sie die Verkabelung (E+, E-, A+, A-)
|
||||
- Tipp: Testen Sie mit einem Multimeter
|
||||
|
||||
#### NFC-Tags
|
||||
|
||||
**Problem**: Tag wird nicht erkannt
|
||||
- Lösung: Überprüfen Sie die PN532 DIP-Schalter (I2C-Modus)
|
||||
- Tipp: Spule etwas anders auf der Waage positionieren (nicht ganz an den Rand)
|
||||
|
||||
**Problem**: Tag kann nicht beschrieben werden
|
||||
- Lösung: Verwenden Sie NTAG215 für bessere Kompatibilität
|
||||
- Tipp: Stellen Sie sicher, dass der Tag nicht schreibgeschützt ist
|
||||
|
||||
#### Spoolman
|
||||
|
||||
**Problem**: Verbindung zu Spoolman schlägt fehl
|
||||
- Lösung: Aktivieren Sie SPOOLMAN_DEBUG_MODE=TRUE
|
||||
- Tipp: Überprüfen Sie die URL-Formatierung
|
||||
|
||||
**Problem**: Spulen werden nicht angezeigt
|
||||
- Lösung: Stellen Sie sicher, dass Spoolman läuft
|
||||
- Tipp: Prüfen Sie die Netzwerk-Firewall-Einstellungen
|
||||
|
||||
#### Bambu Lab
|
||||
|
||||
**Problem**: Drucker verbindet nicht
|
||||
- Lösung: Überprüfen Sie Access Code und IP-Adresse
|
||||
- Tipp: Stellen Sie sicher, dass der Drucker im LAN-Modus ist
|
||||
|
||||
**Problem**: AMS-Status wird nicht angezeigt
|
||||
- Lösung: Prüfen Sie die MQTT-Verbindung
|
||||
- Hinweis: Bambu kann die API jederzeit schließen
|
||||
|
||||
### Debug-Informationen
|
||||
|
||||
Falls Sie Probleme haben, können Sie diese Schritte zur Diagnose verwenden:
|
||||
|
||||
#### Serieller Monitor (für Entwickler)
|
||||
- Verbinden Sie den ESP32 über USB mit Ihrem Computer
|
||||
- Öffnen Sie einen seriellen Monitor (z.B. Arduino IDE) mit 115200 Baud
|
||||
- Sie sehen detaillierte Log-Nachrichten des Systems
|
||||
|
||||
#### Browser-Konsole
|
||||
- Öffnen Sie die Weboberfläche von FilaMan
|
||||
- Drücken Sie F12 um die Entwicklertools zu öffnen
|
||||
- Schauen Sie in der Konsole nach Fehlermeldungen
|
||||
|
||||
#### Neustart bei anhaltenden Problemen
|
||||
1. ESP32 vom Strom trennen
|
||||
2. 10 Sekunden warten
|
||||
3. Wieder anschließen
|
||||
4. 30 Sekunden für vollständigen Start warten
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
### Community
|
||||
|
||||
- **Discord Server**: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- **GitHub Issues**: [Filaman Repository](https://github.com/ManuelW77/Filaman/issues)
|
||||
- **YouTube Kanal**: [Deutsches Erklärvideo](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
|
||||
### Dokumentation
|
||||
|
||||
- **Offizielle Website**: [www.filaman.app](https://www.filaman.app)
|
||||
- **GitHub Wiki**: [Detaillierte Dokumentation](https://github.com/ManuelW77/Filaman/wiki)
|
||||
- **Hardware-Referenz**: ESP32 Pinout-Diagramme in `/img/`
|
||||
|
||||
### Entwicklung unterstützen
|
||||
|
||||
Wenn Sie das Projekt unterstützen möchten:
|
||||
|
||||
[](https://www.buymeacoffee.com/manuelw)
|
||||
|
||||
### Lizenz
|
||||
|
||||
Dieses Projekt ist unter der MIT-Lizenz veröffentlicht. Siehe [LICENSE](LICENSE.txt) für Details.
|
||||
|
||||
---
|
||||
|
||||
**Letzte Aktualisierung**: August 2025
|
||||
**Version**: 2.0
|
||||
**Maintainer**: Manuel W.
|
746
WIKI_EN.md
Normal file
746
WIKI_EN.md
Normal file
@@ -0,0 +1,746 @@
|
||||
# FilaMan Wiki - English
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Installation](#installation)
|
||||
3. [Hardware Requirements](#hardware-requirements)
|
||||
4. [Initial Setup](#initial-setup)
|
||||
5. [Configuration](#configuration)
|
||||
6. [Usage](#usage)
|
||||
7. [NFC Tags](#nfc-tags)
|
||||
8. [Bambu Lab Integration](#bambu-lab-integration)
|
||||
9. [Spoolman Integration](#spoolman-integration)
|
||||
10. [Octoprint Integration](#octoprint-integration)
|
||||
11. [Manufacturer Tags](#manufacturer-tags)
|
||||
12. [Troubleshooting](#troubleshooting)
|
||||
13. [Support](#support)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
FilaMan is a comprehensive filament management system for 3D printers based on ESP32 hardware. It provides weight measurement, NFC tag management, and seamless integration with Spoolman and Bambu Lab 3D printers.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Precise weight measurement** with HX711 load cell amplifier
|
||||
- **NFC tag reading and writing** for filament identification
|
||||
- **OLED display** for status information
|
||||
- **WiFi connectivity** with easy configuration
|
||||
- **Web-based user interface** with real-time updates
|
||||
- **Spoolman integration** for inventory management
|
||||
- **Bambu Lab AMS control** via MQTT
|
||||
- **OpenSpool NFC format** compatibility
|
||||
- **Manufacturer tag support** for automatic setup
|
||||
|
||||
### System Requirements
|
||||
|
||||
- **ESP32 Development Board**
|
||||
- **Spoolman Instance** (required for full functionality)
|
||||
- **WiFi Network**
|
||||
- **Web Browser** (Chrome/Firefox/Safari)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Easy Installation (Recommended)
|
||||
|
||||
1. **Open the [FilaMan Web Installer](https://www.filaman.app/installer.html)**
|
||||
- Use a Chrome-based browser
|
||||
|
||||
2. **Prepare ESP32**
|
||||
- Connect ESP32 via USB to your computer
|
||||
- Click "Connect"
|
||||
|
||||
3. **Select Port**
|
||||
- Choose the appropriate USB port
|
||||
- Confirm selection
|
||||
|
||||
4. **Start Installation**
|
||||
- Click "Install FilaMan"
|
||||
- Wait for installation to complete
|
||||
|
||||
### Manual Compilation
|
||||
|
||||
For advanced users with PlatformIO:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ManuelW77/Filaman.git
|
||||
cd FilaMan/esp32
|
||||
pio lib install
|
||||
pio run --target upload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
### Required Components
|
||||
|
||||
| Component | Description | Amazon Link (Affiliate) |
|
||||
|-----------|-------------|-------------------------|
|
||||
| ESP32 Development Board | Any ESP32 variant | [Amazon](https://amzn.to/3FHea6D) |
|
||||
| HX711 + Load Cell | 5kg Load Cell Amplifier | [Amazon](https://amzn.to/4ja1KTe) |
|
||||
| OLED Display | 0.96" I2C 128x64 SSD1306 | [Amazon](https://amzn.to/445aaa9) |
|
||||
| PN532 NFC Module | V3 RFID Module | [Amazon](https://amzn.eu/d/gy9vaBX) |
|
||||
| NFC Tags | NTAG213/NTAG215 | [Amazon](https://amzn.to/3E071xO) |
|
||||
| TTP223 Touch Sensor | Optional for tare function | [Amazon](https://amzn.to/4hTChMK) |
|
||||
|
||||
### Pin Configuration
|
||||
|
||||
| Component | ESP32 Pin | Function |
|
||||
|-----------|-----------|----------|
|
||||
| HX711 DOUT | 16 | Load cell data output |
|
||||
| HX711 SCK | 17 | Load cell clock |
|
||||
| OLED SDA | 21 | I2C data |
|
||||
| OLED SCL | 22 | I2C clock |
|
||||
| PN532 IRQ | 32 | Interrupt |
|
||||
| PN532 RESET | 33 | Reset |
|
||||
| PN532 SDA | 21 | I2C data (shared) |
|
||||
| PN532 SCL | 22 | I2C clock (shared) |
|
||||
| TTP223 I/O | 25 | Touch sensor (optional) |
|
||||
|
||||
### Important Notes
|
||||
|
||||
- **PN532 DIP switches** must be set to I2C mode
|
||||
- **3V pin** from ESP32 for touch sensor
|
||||
- **Load cell wiring**: E+ (red), E- (black), A- (white), A+ (green)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Initial Setup
|
||||
|
||||
### After Installation
|
||||
|
||||
1. **ESP32 Restart**
|
||||
- System automatically creates a WiFi hotspot "FilaMan"
|
||||
|
||||
2. **WiFi Configuration**
|
||||
- Connect to the "FilaMan" network
|
||||
- Open browser (automatic portal or <http://192.168.4.1>)
|
||||
- Configure your WiFi credentials
|
||||
|
||||
3. **First Access**
|
||||
- After successful WiFi connection, access system at <http://filaman.local>
|
||||
- Alternative: Use IP address assigned by router
|
||||
|
||||
### Spoolman Preparation
|
||||
|
||||
**Important Note**: Spoolman must run in debug mode:
|
||||
|
||||
```env
|
||||
# Uncomment in Spoolman's .env file:
|
||||
SPOOLMAN_DEBUG_MODE=TRUE
|
||||
```
|
||||
|
||||
This is required as Spoolman doesn't support CORS domain configuration yet.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Scale Calibration
|
||||
|
||||
1. **Start Calibration**
|
||||
- Go to "Scale" page
|
||||
- Prepare a 500g reference weight (e.g., water glass)
|
||||
|
||||
2. **Calibration Steps**
|
||||
- Follow instructions on display
|
||||
- Place weight when prompted
|
||||
- Wait for calibration to complete
|
||||
|
||||
3. **Validation**
|
||||
- Test accuracy with known weights
|
||||
- Use "Tare Scale" for zero adjustment if needed
|
||||
|
||||
### Spoolman Connection
|
||||
|
||||
1. **Enter Spoolman URL**
|
||||
- Go to "Spoolman/Bambu" page
|
||||
- Enter complete URL of your Spoolman instance
|
||||
- Format: `http://spoolman-server:7912`
|
||||
|
||||
2. **Test Connection**
|
||||
- System automatically checks connection
|
||||
- Successful connection shown by green status
|
||||
|
||||
### Bambu Lab Printer (Optional)
|
||||
|
||||
1. **Printer Settings**
|
||||
- Open settings menu on your Bambu printer
|
||||
- Note the following data:
|
||||
- Printer IP address
|
||||
- Access Code
|
||||
- Serial Number
|
||||
|
||||
2. **FilaMan Configuration**
|
||||
- Enter printer data on "Spoolman/Bambu" page
|
||||
- Enable "Auto Send to Bambu" for automatic AMS assignment
|
||||
|
||||
3. **Auto-Send Timeout**
|
||||
- Configure waiting time for automatic spool detection
|
||||
- Recommended value: 10-30 seconds
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Operation
|
||||
|
||||
1. **Weigh Filament**
|
||||
- Place spool on scale
|
||||
- Weight automatically displayed on screen and web interface
|
||||
|
||||
2. **Scan NFC Tag**
|
||||
- Hold tag near PN532 module
|
||||
- Recognized tags display spool information
|
||||
- Weight automatically updated in Spoolman
|
||||
|
||||
3. **Status Monitoring**
|
||||
- **OLED Display** shows current weight and connection status
|
||||
- **Web Interface** provides detailed information and control
|
||||
|
||||
### Web Interface Navigation
|
||||
|
||||
- **Home**: Main functions and current status
|
||||
- **Scale**: Scale calibration and settings
|
||||
- **Spoolman/Bambu**: System configuration
|
||||
- **Statistics**: Usage statistics (if enabled)
|
||||
|
||||
---
|
||||
|
||||
## NFC Tags
|
||||
|
||||
### Supported Tag Types
|
||||
|
||||
- **NTAG213**: 144 bytes (basic functions)
|
||||
- **NTAG215**: 504 bytes (recommended)
|
||||
- **NTAG216**: 888 bytes (extended functions)
|
||||
|
||||
### Writing Tags
|
||||
|
||||
1. **Prepare Spool in Spoolman**
|
||||
- Create new spool in Spoolman
|
||||
- Ensure all required data is entered
|
||||
|
||||
2. **Start Tag Writing**
|
||||
- Select spool from list
|
||||
- Click "Write Tag"
|
||||
- Display shows "Waiting for Tag"
|
||||
|
||||
3. **Place Tag**
|
||||
- Position NFC tag on PN532 module
|
||||
- Wait for confirmation
|
||||
|
||||
4. **Success Message**
|
||||
- Successful writing shows checkmark
|
||||
- Tag is now linked to Spoolman spool
|
||||
|
||||
### Reading Tags
|
||||
|
||||
1. **Scan Tag**
|
||||
- Place the spool with NFC tag on the scale over the NFC reader
|
||||
- If reading fails: Reposition spool slightly (not completely at the edge)
|
||||
- Spool information automatically loaded
|
||||
|
||||
2. **Automatic Updates**
|
||||
- Current weight transferred to Spoolman
|
||||
- Spool automatically selected in web interface
|
||||
|
||||
---
|
||||
|
||||
## Bambu Lab Integration
|
||||
|
||||
### AMS (Automatic Material System)
|
||||
|
||||
1. **Display AMS Status**
|
||||
- Web interface shows current state of all AMS slots
|
||||
- Loaded slots display filament information
|
||||
|
||||
2. **Manual Filament Assignment**
|
||||
- Select spool from Spoolman list
|
||||
- Click corresponding AMS slot icon
|
||||
- Filament assigned to slot
|
||||
|
||||
3. **Automatic Assignment**
|
||||
- After weighing with "Auto Send to Bambu" enabled
|
||||
- System waits for new spools in AMS
|
||||
- Calibrated filaments automatically assigned
|
||||
|
||||
### Bambu Studio Integration
|
||||
|
||||
1. **Sync Filament Profiles**
|
||||
- Calibrate filaments in Bambu Studio
|
||||
- Use Device → AMS → Pencil icon → Select
|
||||
|
||||
2. **Save Setting IDs**
|
||||
- FilaMan automatically detects available setting IDs
|
||||
- Click "Save Settings to Spoolman"
|
||||
- Profiles used for future prints
|
||||
|
||||
### Restore Connection
|
||||
|
||||
- For connection issues, click red dot in menu bar
|
||||
- System automatically establishes new connection
|
||||
|
||||
---
|
||||
|
||||
## Spoolman Integration
|
||||
|
||||
### Automatic Functions
|
||||
|
||||
1. **Spool Synchronization**
|
||||
- Automatic transfer of weight changes
|
||||
- Real-time updates of spool data
|
||||
|
||||
2. **Extra Fields**
|
||||
- FilaMan automatically creates required custom fields
|
||||
- NFC tag UID stored as reference
|
||||
|
||||
3. **Filtering**
|
||||
- "Show only spools without NFC tag" for easy tag assignment
|
||||
- Categorization by manufacturers and material types
|
||||
|
||||
### Spoolman Octoprint Plugin
|
||||
|
||||
For Octoprint users, automatic spool assignment is available:
|
||||
|
||||
1. **Install Plugin**
|
||||
|
||||
```text
|
||||
https://github.com/ManuelW77/OctoPrint-Spoolman-Filaman/archive/refs/heads/master.zip
|
||||
```
|
||||
|
||||
2. **Configure FilaMan**
|
||||
- Enable "Send to Octo-Plugin"
|
||||
- Enter Octoprint URL and API key
|
||||
|
||||
3. **Automatic Assignment**
|
||||
- After weighing, spool automatically activated in Octoprint
|
||||
- Currently supports only Tool0 (first nozzle)
|
||||
|
||||
---
|
||||
|
||||
## Manufacturer Tags
|
||||
|
||||
### Overview
|
||||
|
||||
Manufacturer tags allow filament producers to provide pre-configured NFC tags that automatically create all necessary entries in Spoolman.
|
||||
|
||||
### Getting Started with Manufacturer Tags
|
||||
|
||||
1. **Scan Tag**
|
||||
- Place spool with manufacturer tag on the scale over the NFC reader
|
||||
- If reading fails: Reposition spool slightly (not completely at the edge)
|
||||
- System automatically recognizes manufacturer format
|
||||
|
||||
2. **Automatic Creation**
|
||||
- **Brand** created in Spoolman (if not present)
|
||||
- **Filament type** created with all specifications
|
||||
- **Spool** automatically registered
|
||||
|
||||
3. **Future Scans**
|
||||
- After initial setup, tags use fast-path system
|
||||
- Immediate weight measurement without re-setup
|
||||
|
||||
### Supported Manufacturers
|
||||
|
||||
- **RecyclingFabrik**: First official partner
|
||||
- More manufacturers coming soon
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ **Zero manual setup**
|
||||
- ✅ **Perfect data accuracy**
|
||||
- ✅ **Instant integration**
|
||||
- ✅ **Future-proof**
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### WiFi Connection
|
||||
|
||||
**Issue**: Cannot connect to FilaMan hotspot
|
||||
|
||||
- Solution: Ensure ESP32 is started
|
||||
- Alternative: Manually navigate to <http://192.168.4.1>
|
||||
|
||||
**Issue**: Web interface not accessible
|
||||
|
||||
- Solution: Check IP address in router
|
||||
- Alternative: Use <http://filaman.local>
|
||||
|
||||
#### Scale
|
||||
|
||||
**Issue**: Inaccurate weight measurements
|
||||
|
||||
- Solution: Repeat calibration
|
||||
- Tip: Use "Tare Scale" for zero adjustment
|
||||
|
||||
**Issue**: Load cell not responding
|
||||
|
||||
- Solution: Check wiring (E+, E-, A+, A-)
|
||||
- Tip: Test with multimeter
|
||||
|
||||
#### NFC Tags
|
||||
|
||||
**Issue**: Tag not recognized
|
||||
|
||||
- Solution: Check PN532 DIP switches (I2C mode)
|
||||
- Tip: Reposition spool slightly on scale (not completely at the edge)
|
||||
|
||||
**Issue**: Cannot write tag
|
||||
|
||||
- Solution: Use NTAG215 for better compatibility
|
||||
- Tip: Ensure tag is not write-protected
|
||||
|
||||
#### Spoolman
|
||||
|
||||
**Issue**: Connection to Spoolman fails
|
||||
|
||||
- Solution: Enable SPOOLMAN_DEBUG_MODE=TRUE
|
||||
- Tip: Check URL formatting
|
||||
|
||||
**Issue**: Spools not displayed
|
||||
|
||||
- Solution: Ensure Spoolman is running
|
||||
- Tip: Check network firewall settings
|
||||
|
||||
#### Bambu Lab
|
||||
|
||||
**Issue**: Printer won't connect
|
||||
|
||||
- Solution: Check access code and IP address
|
||||
- Tip: Ensure printer is in LAN mode
|
||||
|
||||
**Issue**: AMS status not displayed
|
||||
|
||||
- Solution: Check MQTT connection
|
||||
- Note: Bambu may close API at any time
|
||||
|
||||
### Debug Information
|
||||
|
||||
If you have problems, you can use these steps for diagnosis:
|
||||
|
||||
#### Serial Monitor (for developers)
|
||||
|
||||
- Connect the ESP32 via USB to your computer
|
||||
- Open a serial monitor (e.g., Arduino IDE) with 115200 baud
|
||||
- You will see detailed log messages from the system
|
||||
|
||||
#### Browser Console
|
||||
|
||||
- Open the FilaMan web interface
|
||||
- Press F12 to open developer tools
|
||||
- Check the console for error messages
|
||||
|
||||
---
|
||||
|
||||
## Maintenance and Updates
|
||||
|
||||
### Firmware Update
|
||||
|
||||
1. **Via Web Interface**: Access `http://filaman.local/upgrade.html`
|
||||
2. **Select firmware file** (.bin format)
|
||||
3. **Upload** - System restarts automatically
|
||||
4. **Configuration preserved** - Settings remain intact
|
||||
|
||||
### System Reset
|
||||
|
||||
For persistent issues:
|
||||
|
||||
1. Disconnect ESP32 from power
|
||||
2. Wait 10 seconds
|
||||
3. Reconnect
|
||||
4. Wait 30 seconds for complete startup
|
||||
|
||||
---
|
||||
|
||||
## Support and Information
|
||||
|
||||
**Manufacturer**: Your Company Name
|
||||
**Maintainer**: Manuel W.
|
||||
|
||||
### Scale Technology
|
||||
|
||||
#### Weight Stabilization
|
||||
|
||||
The system uses multiple filters for precise measurements:
|
||||
|
||||
```cpp
|
||||
// Moving Average Filter with 8 values
|
||||
#define MOVING_AVERAGE_SIZE 8
|
||||
// Low-Pass Filter for smoothing
|
||||
#define LOW_PASS_ALPHA 0.3f
|
||||
// Thresholds for updates
|
||||
#define DISPLAY_THRESHOLD 0.3f // Display update
|
||||
#define API_THRESHOLD 1.5f // API actions
|
||||
```
|
||||
|
||||
#### Calibration Algorithm
|
||||
|
||||
1. **System Pause**: All tasks are temporarily paused
|
||||
2. **Zero Setting**: Tare scale without weight
|
||||
3. **Reference Measurement**: 500g weight for 10 measurements
|
||||
4. **Calculation**: `newValue = rawValue / SCALE_LEVEL_WEIGHT`
|
||||
5. **NVS Storage**: Permanent value with verification
|
||||
6. **Filter Reset**: New baseline for stabilization
|
||||
|
||||
#### Auto-Tare Logic
|
||||
|
||||
```cpp
|
||||
// Conditions for Auto-Tare
|
||||
if (autoTare && (weight > 2 && weight < 7) || weight < -2) {
|
||||
scale_tare_counter++;
|
||||
if (scale_tare_counter >= 5) {
|
||||
// Automatic zero setting
|
||||
scale.tare();
|
||||
resetWeightFilter();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NFC Technology
|
||||
|
||||
#### PN532 Communication
|
||||
|
||||
- **Interface**: I2C at 400kHz
|
||||
- **IRQ Pin**: Interrupt-based tag detection
|
||||
- **Reset Handling**: Automatic recovery from communication errors
|
||||
- **DIP Switches**: Must be set to I2C mode (00)
|
||||
|
||||
#### NDEF Implementation
|
||||
|
||||
```json
|
||||
// FilaMan Spoolman Format (with sm_id)
|
||||
{
|
||||
"sm_id": "123",
|
||||
"color": "#FF5733",
|
||||
"type": "PLA",
|
||||
"brand": "Example Brand"
|
||||
}
|
||||
```
|
||||
|
||||
#### Manufacturer Tag Schema
|
||||
|
||||
Compact JSON format for storage efficiency:
|
||||
|
||||
```json
|
||||
{
|
||||
"b": "RecyclingFabrik", // brand
|
||||
"an": "FX1_PLA-S175-1000-RED", // article number
|
||||
"t": "PLA", // type
|
||||
"c": "FF0000", // color (hex without #)
|
||||
"cn": "Red", // color name
|
||||
"et": "210", // extruder temp
|
||||
"bt": "60", // bed temp
|
||||
"di": "1.75", // diameter
|
||||
"de": "1.24", // density
|
||||
"sw": "240", // spool weight
|
||||
"u": "https://www.yoururl.com/search?q=" // URL used vor Brand Link and Filament Link
|
||||
}
|
||||
```
|
||||
|
||||
### Display System
|
||||
|
||||
#### OLED Architecture (SSD1306)
|
||||
|
||||
- **Resolution**: 128x64 pixels monochrome
|
||||
- **Areas**:
|
||||
- Status bar: 0-16 pixels (version, icons)
|
||||
- Main area: 17-64 pixels (weight, messages)
|
||||
- **Update Interval**: 1 second for status line
|
||||
|
||||
#### Icon System
|
||||
|
||||
Bitmap icons for various states:
|
||||
|
||||
```cpp
|
||||
// Status Icons (16x16 pixels)
|
||||
- icon_success: Checkmark for successful operations
|
||||
- icon_failed: X for errors
|
||||
- icon_transfer: Arrow for data transmission
|
||||
- icon_loading: Loading circle for ongoing operations
|
||||
|
||||
// Connection Icons with strikethrough indicator
|
||||
- wifi_on/wifi_off: WLAN status
|
||||
- bambu_on: Bambu Lab connection
|
||||
- spoolman_on: Spoolman API status
|
||||
```
|
||||
|
||||
### API Integration
|
||||
|
||||
#### Spoolman REST API
|
||||
|
||||
FilaMan interacts with the following endpoints:
|
||||
|
||||
```http
|
||||
GET /api/v1/spool/ # List spools
|
||||
POST /api/v1/spool/ # Create new spool
|
||||
PUT /api/v1/spool/{id}/ # Update spool
|
||||
|
||||
GET /api/v1/vendor/ # List vendors
|
||||
POST /api/v1/vendor/ # Create new vendor
|
||||
|
||||
GET /api/v1/filament/ # List filaments
|
||||
POST /api/v1/filament/ # Create new filament
|
||||
```
|
||||
|
||||
#### Request Handling
|
||||
|
||||
```cpp
|
||||
// Sequential API processing
|
||||
enum spoolmanApiStateType {
|
||||
API_IDLE = 0,
|
||||
API_PROCESSING = 1,
|
||||
API_ERROR = 2
|
||||
};
|
||||
```
|
||||
|
||||
Prevents simultaneous API calls and deadlocks.
|
||||
|
||||
#### Weight Update Logic
|
||||
|
||||
```cpp
|
||||
// Conditions for Spoolman update
|
||||
if (activeSpoolId != "" &&
|
||||
weigthCouterToApi > 3 && // 3+ stable measurements
|
||||
weightSend == 0 && // Not yet sent
|
||||
weight > 5 && // Minimum weight 5g
|
||||
spoolmanApiState == API_IDLE) {
|
||||
updateSpoolWeight(activeSpoolId, weight);
|
||||
}
|
||||
```
|
||||
|
||||
### Bambu Lab MQTT
|
||||
|
||||
#### Connection Parameters
|
||||
|
||||
```cpp
|
||||
// SSL/TLS Configuration
|
||||
#define BAMBU_PORT 8883
|
||||
#define BAMBU_USERNAME "bblp"
|
||||
|
||||
// Topic Structure
|
||||
String topic = "device/" + bambu_serial + "/report";
|
||||
String request_topic = "device/" + bambu_serial + "/request";
|
||||
```
|
||||
|
||||
#### AMS Data Structure
|
||||
|
||||
```cpp
|
||||
struct AMSData {
|
||||
String tray_id;
|
||||
String tray_type;
|
||||
String tray_color;
|
||||
String tray_material;
|
||||
String setting_id;
|
||||
String tray_info_idx;
|
||||
bool has_spool;
|
||||
};
|
||||
```
|
||||
|
||||
#### Auto-Send Mechanism
|
||||
|
||||
```cpp
|
||||
// After tag recognition
|
||||
if (bambuCredentials.autosend_enable) {
|
||||
autoSetToBambuSpoolId = activeSpoolId.toInt();
|
||||
// Countdown starts automatically
|
||||
// Waits for new spool in AMS
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket Communication
|
||||
|
||||
#### Message Types
|
||||
|
||||
```javascript
|
||||
// Client → Server
|
||||
{
|
||||
"type": "writeNfcTag",
|
||||
"tagType": "spool",
|
||||
"payload": { /* JSON data */ }
|
||||
}
|
||||
|
||||
{
|
||||
"type": "scale",
|
||||
"payload": "tare|calibrate|setAutoTare",
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
// Server → Client
|
||||
{
|
||||
"type": "heartbeat",
|
||||
"freeHeap": 245,
|
||||
"bambu_connected": true,
|
||||
"spoolman_connected": true
|
||||
}
|
||||
|
||||
{
|
||||
"type": "amsData",
|
||||
"data": [ /* AMS array */ ]
|
||||
}
|
||||
```
|
||||
|
||||
#### Connection Management
|
||||
|
||||
- **Auto-Reconnect**: Client-side reconnection
|
||||
- **Heartbeat**: Every 30 seconds for connection monitoring
|
||||
- **Cleanup**: Automatic removal of dead connections
|
||||
|
||||
### Watchdog and Error Handling
|
||||
|
||||
#### System Watchdog
|
||||
|
||||
```cpp
|
||||
// WDT Configuration
|
||||
esp_task_wdt_init(10, true); // 10s timeout, panic on overflow
|
||||
esp_task_wdt_add(NULL); // Add current task
|
||||
```
|
||||
|
||||
#### Error Recovery
|
||||
|
||||
- **NFC Reset**: Automatic PN532 restart on communication errors
|
||||
- **MQTT Reconnect**: Bambu Lab connection automatically restored
|
||||
- **WiFi Monitoring**: Connection check every 60 seconds
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
### Community
|
||||
|
||||
- **Discord Server**: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
- **GitHub Issues**: [Filaman Repository](https://github.com/ManuelW77/Filaman/issues)
|
||||
- **YouTube Channel**: [German explanation video](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
|
||||
### Documentation
|
||||
|
||||
- **Official Website**: [www.filaman.app](https://www.filaman.app)
|
||||
- **GitHub Wiki**: [Detailed documentation](https://github.com/ManuelW77/Filaman/wiki)
|
||||
- **Hardware Reference**: ESP32 pinout diagrams in `/img/`
|
||||
|
||||
### Support Development
|
||||
|
||||
If you'd like to support the project:
|
||||
|
||||
[](https://www.buymeacoffee.com/manuelw)
|
||||
|
||||
### License
|
||||
|
||||
This project is released under the MIT License. See [LICENSE](LICENSE.txt) for details.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: August 2025
|
||||
**Version**: 2.0
|
||||
**Maintainer**: Manuel W.
|
BIN
html/.DS_Store
vendored
Normal file
BIN
html/.DS_Store
vendored
Normal file
Binary file not shown.
1
html/bambu_credentials.json
Normal file
1
html/bambu_credentials.json
Normal file
@@ -0,0 +1 @@
|
||||
{"bambu_ip": "192.168.1.14", "bambu_accesscode": "22772584", "bambu_serialnr": "01P00C492600230","autoSendToBambu":true,"autoSendTime": 60}
|
@@ -139,6 +139,18 @@
|
||||
<p id="nfcInfo" class="nfc-status"></p>
|
||||
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
|
||||
</div>
|
||||
|
||||
<div class="feature-box">
|
||||
<h2>Spoolman Locations</h2>
|
||||
<label for="locationSelect">Location:</label>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<select id="locationSelect" class="styled-select">
|
||||
<option value="">Please choose...</option>
|
||||
</select>
|
||||
</div>
|
||||
<p id="nfcInfoLocation" class="nfc-status"></p>
|
||||
<button id="writeLocationNfcButton" class="btn btn-primary hidden" onclick="writeLocationNfcTag()">Write Location Tag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
191
html/rfid.js
191
html/rfid.js
@@ -7,6 +7,7 @@ let heartbeatTimer = null;
|
||||
let lastHeartbeatResponse = Date.now();
|
||||
const HEARTBEAT_TIMEOUT = 20000;
|
||||
let reconnectTimer = null;
|
||||
let spoolDetected = false;
|
||||
|
||||
// WebSocket Funktionen
|
||||
function startHeartbeat() {
|
||||
@@ -215,20 +216,6 @@ document.addEventListener('filamentSelected', function (event) {
|
||||
updateSpoolButtons(selectedText !== "Please choose...");
|
||||
});
|
||||
|
||||
// Hilfsfunktion für kontrastreiche Textfarbe
|
||||
function getContrastColor(hexcolor) {
|
||||
// Konvertiere Hex zu RGB
|
||||
const r = parseInt(hexcolor.substr(0,2),16);
|
||||
const g = parseInt(hexcolor.substr(2,2),16);
|
||||
const b = parseInt(hexcolor.substr(4,2),16);
|
||||
|
||||
// Berechne Helligkeit (YIQ Formel)
|
||||
const yiq = ((r*299)+(g*587)+(b*114))/1000;
|
||||
|
||||
// Return schwarz oder weiß basierend auf Helligkeit
|
||||
return (yiq >= 128) ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
|
||||
function updateNfcInfo() {
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
const nfcInfo = document.getElementById("nfcInfo");
|
||||
@@ -522,12 +509,15 @@ function updateNfcStatusIndicator(data) {
|
||||
if (data.found === 0) {
|
||||
// Kein NFC Tag gefunden
|
||||
indicator.className = 'status-circle';
|
||||
spoolDetected = false;
|
||||
} else if (data.found === 1) {
|
||||
// NFC Tag erfolgreich gelesen
|
||||
indicator.className = 'status-circle success';
|
||||
spoolDetected = true;
|
||||
} else {
|
||||
// Fehler beim Lesen
|
||||
indicator.className = 'status-circle error';
|
||||
spoolDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +559,10 @@ function updateNfcData(data) {
|
||||
}
|
||||
|
||||
// HTML für die Datenanzeige erstellen
|
||||
let html = `
|
||||
let html = "";
|
||||
|
||||
if(data.sm_id){
|
||||
html = `
|
||||
<div class="nfc-card-data" style="margin-top: 10px;">
|
||||
<p><strong>Brand:</strong> ${data.brand || 'N/A'}</p>
|
||||
<p><strong>Type:</strong> ${data.type || 'N/A'} ${data.color_hex ? `<span style="
|
||||
@@ -582,10 +575,27 @@ function updateNfcData(data) {
|
||||
border-radius: 3px;
|
||||
margin-left: 5px;
|
||||
"></span>` : ''}</p>
|
||||
`;
|
||||
`;
|
||||
|
||||
// Spoolman ID anzeigen
|
||||
html += `<p><strong>Spoolman ID:</strong> ${data.sm_id || 'No Spoolman ID'}</p>`;
|
||||
// Spoolman ID anzeigen
|
||||
html += `<p><strong>Spoolman ID:</strong> ${data.sm_id} (<a href="${spoolmanUrl}/spool/show/${data.sm_id}">Open in Spoolman</a>)</p>`;
|
||||
}
|
||||
else if(data.location)
|
||||
{
|
||||
html = `
|
||||
<div class="nfc-card-data" style="margin-top: 10px;">
|
||||
<p><strong>Location:</strong> ${data.location || 'N/A'}</p>
|
||||
`;
|
||||
}
|
||||
else
|
||||
{
|
||||
html = `
|
||||
<div class="nfc-card-data" style="margin-top: 10px;">
|
||||
<p><strong>Unknown tag</strong></p>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Nur wenn eine sm_id vorhanden ist, aktualisiere die Dropdowns
|
||||
if (data.sm_id) {
|
||||
@@ -612,65 +622,112 @@ function updateNfcData(data) {
|
||||
}
|
||||
|
||||
function writeNfcTag() {
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
if (selectedText === "Please choose...") {
|
||||
alert('Please select a Spool first.');
|
||||
return;
|
||||
if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
if (selectedText === "Please choose...") {
|
||||
alert('Please select a Spool first.');
|
||||
return;
|
||||
}
|
||||
|
||||
const spoolsData = window.getSpoolData();
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
|
||||
if (!selectedSpool) {
|
||||
alert('Ausgewählte Spule konnte nicht gefunden werden.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Temperaturwerte korrekt extrahieren
|
||||
let minTemp = "175";
|
||||
let maxTemp = "275";
|
||||
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
selectedSpool.filament.nozzle_temperature.length >= 2) {
|
||||
minTemp = String(selectedSpool.filament.nozzle_temperature[0]);
|
||||
maxTemp = String(selectedSpool.filament.nozzle_temperature[1]);
|
||||
}
|
||||
|
||||
// Erstelle das NFC-Datenpaket mit korrekten Datentypen
|
||||
const nfcData = {
|
||||
color_hex: selectedSpool.filament.color_hex || "FFFFFF",
|
||||
type: selectedSpool.filament.material,
|
||||
min_temp: minTemp,
|
||||
max_temp: maxTemp,
|
||||
brand: selectedSpool.filament.vendor.name,
|
||||
sm_id: String(selectedSpool.id) // Konvertiere zu String
|
||||
};
|
||||
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
const writeButton = document.getElementById("writeNfcButton");
|
||||
writeButton.classList.add("writing");
|
||||
writeButton.textContent = "Writing";
|
||||
socket.send(JSON.stringify({
|
||||
type: 'writeNfcTag',
|
||||
tagType: 'spool',
|
||||
payload: nfcData
|
||||
}));
|
||||
} else {
|
||||
alert('Not connected to Server. Please check connection.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const spoolsData = window.getSpoolData();
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
function writeLocationNfcTag() {
|
||||
if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
|
||||
const selectedText = document.getElementById("locationSelect").value;
|
||||
if (selectedText === "Please choose...") {
|
||||
alert('Please select a location first.');
|
||||
return;
|
||||
}
|
||||
// Erstelle das NFC-Datenpaket mit korrekten Datentypen
|
||||
const nfcData = {
|
||||
location: String(selectedText)
|
||||
};
|
||||
|
||||
if (!selectedSpool) {
|
||||
alert('Ausgewählte Spule konnte nicht gefunden werden.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Temperaturwerte korrekt extrahieren
|
||||
let minTemp = "175";
|
||||
let maxTemp = "275";
|
||||
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
selectedSpool.filament.nozzle_temperature.length >= 2) {
|
||||
minTemp = String(selectedSpool.filament.nozzle_temperature[0]);
|
||||
maxTemp = String(selectedSpool.filament.nozzle_temperature[1]);
|
||||
}
|
||||
|
||||
// Erstelle das NFC-Datenpaket mit korrekten Datentypen
|
||||
const nfcData = {
|
||||
color_hex: selectedSpool.filament.color_hex || "FFFFFF",
|
||||
type: selectedSpool.filament.material,
|
||||
min_temp: minTemp,
|
||||
max_temp: maxTemp,
|
||||
brand: selectedSpool.filament.vendor.name,
|
||||
sm_id: String(selectedSpool.id) // Konvertiere zu String
|
||||
};
|
||||
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
const writeButton = document.getElementById("writeNfcButton");
|
||||
writeButton.classList.add("writing");
|
||||
writeButton.textContent = "Writing";
|
||||
socket.send(JSON.stringify({
|
||||
type: 'writeNfcTag',
|
||||
payload: nfcData
|
||||
}));
|
||||
} else {
|
||||
alert('Not connected to Server. Please check connection.');
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
const writeButton = document.getElementById("writeLocationNfcButton");
|
||||
writeButton.classList.add("writing");
|
||||
writeButton.textContent = "Writing";
|
||||
socket.send(JSON.stringify({
|
||||
type: 'writeNfcTag',
|
||||
tagType: 'location',
|
||||
payload: nfcData
|
||||
}));
|
||||
} else {
|
||||
alert('Not connected to Server. Please check connection.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWriteNfcTagResponse(success) {
|
||||
const writeButton = document.getElementById("writeNfcButton");
|
||||
writeButton.classList.remove("writing");
|
||||
writeButton.classList.add(success ? "success" : "error");
|
||||
writeButton.textContent = success ? "Write success" : "Write failed";
|
||||
const writeLocationButton = document.getElementById("writeLocationNfcButton");
|
||||
if(writeButton.classList.contains("writing")){
|
||||
writeButton.classList.remove("writing");
|
||||
writeButton.classList.add(success ? "success" : "error");
|
||||
writeButton.textContent = success ? "Write success" : "Write failed";
|
||||
|
||||
setTimeout(() => {
|
||||
writeButton.classList.remove("success", "error");
|
||||
writeButton.textContent = "Write Tag";
|
||||
}, 5000);
|
||||
setTimeout(() => {
|
||||
writeButton.classList.remove("success", "error");
|
||||
writeButton.textContent = "Write Tag";
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if(writeLocationButton.classList.contains("writing")){
|
||||
writeLocationButton.classList.remove("writing");
|
||||
writeLocationButton.classList.add(success ? "success" : "error");
|
||||
writeLocationButton.textContent = success ? "Write success" : "Write failed";
|
||||
|
||||
setTimeout(() => {
|
||||
writeLocationButton.classList.remove("success", "error");
|
||||
writeLocationButton.textContent = "Write Location Tag";
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function showNotification(message, isSuccess) {
|
||||
|
@@ -139,6 +139,18 @@
|
||||
<p id="nfcInfo" class="nfc-status"></p>
|
||||
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
|
||||
</div>
|
||||
|
||||
<div class="feature-box">
|
||||
<h2>Spoolman Locations</h2>
|
||||
<label for="locationSelect">Location:</label>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<select id="locationSelect" class="styled-select">
|
||||
<option value="">Please choose...</option>
|
||||
</select>
|
||||
</div>
|
||||
<p id="nfcInfoLocation" class="nfc-status"></p>
|
||||
<button id="writeLocationNfcButton" class="btn btn-primary hidden" onclick="writeLocationNfcTag()">Write Location Tag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rechte Spalte -->
|
||||
|
@@ -146,20 +146,20 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<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">
|
||||
<h5 class="card-title">If you want to enable sending Spool to Spoolman Octoprint Plugin:</h5>
|
||||
<h5 class="card-title">Set URL/IP to your Spoolman instance</h5>
|
||||
<input type="text" id="spoolmanUrl" onkeydown="if(event.keyCode == 13) document.getElementById('btnSaveSpoolmanUrl').click()" placeholder="http://ip-or-url-of-your-spoolman-instance:port">
|
||||
<h5 class="card-title">If you want to enable sending the spool to the 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="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instance:port" value="{{spoolmanOctoUrl}}">
|
||||
<input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
|
||||
<button id="btnSaveSpoolmanUrl" onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
|
||||
<p id="statusMessage"></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,16 +169,16 @@
|
||||
<h5 class="card-title">Bambu Lab Printer Credentials</h5>
|
||||
<div class="bambu-settings">
|
||||
<div class="input-group">
|
||||
<label for="bambuIp">Bambu Drucker IP-Adresse:</label>
|
||||
<label for="bambuIp">Bambu Printer IP Address:</label>
|
||||
<input type="text" id="bambuIp" placeholder="192.168.1.xxx" value="{{bambuIp}}">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="bambuSerial">Drucker Seriennummer:</label>
|
||||
<label for="bambuSerial">Printer Serial Number:</label>
|
||||
<input type="text" id="bambuSerial" placeholder="BBLXXXXXXXX" value="{{bambuSerial}}">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="bambuCode">Access Code:</label>
|
||||
<input type="text" id="bambuCode" placeholder="Access Code vom Drucker" value="{{bambuCode}}">
|
||||
<input type="text" id="bambuCode" placeholder="Access Code of the printer" value="{{bambuCode}}">
|
||||
</div>
|
||||
<hr>
|
||||
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>
|
||||
|
118
html/spoolman.js
118
html/spoolman.js
@@ -1,6 +1,7 @@
|
||||
// Globale Variablen
|
||||
let spoolmanUrl = '';
|
||||
let spoolsData = [];
|
||||
let locationData = [];
|
||||
|
||||
// Hilfsfunktionen für Datenmanipulation
|
||||
function processSpoolData(data) {
|
||||
@@ -133,6 +134,26 @@ function populateVendorDropdown(data, selectedSmId = null) {
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown-Funktionen
|
||||
function populateLocationDropdown(data) {
|
||||
const locationSelect = document.getElementById("locationSelect");
|
||||
if (!locationSelect) {
|
||||
console.error('locationSelect Element nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
locationSelect.innerHTML = '<option value="">Bitte wählen...</option>';
|
||||
// Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert
|
||||
Object.entries(data)
|
||||
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name
|
||||
.forEach(([id, name]) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = name;
|
||||
option.textContent = name;
|
||||
locationSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function updateFilamentDropdown(selectedSmId = null) {
|
||||
const vendorId = document.getElementById("vendorSelect").value;
|
||||
const dropdownContentInner = document.getElementById("filament-dropdown-content");
|
||||
@@ -169,9 +190,32 @@ function updateFilamentDropdown(selectedSmId = null) {
|
||||
option.setAttribute("data-value", spool.filament.id);
|
||||
option.setAttribute("data-nfc-id", spool.extra.nfc_id || "");
|
||||
|
||||
const colorHex = spool.filament.color_hex || 'FFFFFF';
|
||||
|
||||
// Generate color representation based on filament type (single or multi color)
|
||||
let colorHTML = '';
|
||||
|
||||
// Check if this is a multicolor filament
|
||||
if (spool.filament.multi_color_hexes) {
|
||||
// Parse multi color hexes from comma-separated string
|
||||
const colors = spool.filament.multi_color_hexes.replace(/#/g, '').split(',');
|
||||
|
||||
// Determine the display style based on direction
|
||||
const direction = spool.filament.multi_color_direction || 'coaxial';
|
||||
|
||||
// Generate color circles for each color
|
||||
colorHTML = '<div class="option-colors">';
|
||||
colors.forEach(color => {
|
||||
colorHTML += `<div class="option-color multi-color ${direction}" style="background-color: #${color}"></div>`;
|
||||
});
|
||||
colorHTML += '</div>';
|
||||
} else {
|
||||
// Single color filament
|
||||
const colorHex = spool.filament.color_hex || 'FFFFFF';
|
||||
colorHTML = `<div class="option-color" style="background-color: #${colorHex}"></div>`;
|
||||
}
|
||||
|
||||
option.innerHTML = `
|
||||
<div class="option-color" style="background-color: #${colorHex}"></div>
|
||||
${colorHTML}
|
||||
<span>${spool.id} | ${spool.filament.name} (${spool.filament.material})</span>
|
||||
`;
|
||||
|
||||
@@ -185,12 +229,41 @@ function updateFilamentDropdown(selectedSmId = null) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateLocationSelect(){
|
||||
const writeLocationNfcButton = document.getElementById('writeLocationNfcButton');
|
||||
if(writeLocationNfcButton){
|
||||
writeLocationNfcButton.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function selectFilament(spool) {
|
||||
const selectedColor = document.getElementById("selected-color");
|
||||
const selectedText = document.getElementById("selected-filament");
|
||||
const dropdownContent = document.getElementById("filament-dropdown-content");
|
||||
|
||||
selectedColor.style.backgroundColor = `#${spool.filament.color_hex || 'FFFFFF'}`;
|
||||
// Update the selected color display
|
||||
if (spool.filament.multi_color_hexes) {
|
||||
// Handle multicolor filament display in the selection header
|
||||
const colors = spool.filament.multi_color_hexes.replace(/#/g, '').split(',');
|
||||
const direction = spool.filament.multi_color_direction || 'coaxial';
|
||||
|
||||
// Replace the single color div with multiple color divs
|
||||
selectedColor.innerHTML = '';
|
||||
colors.forEach(color => {
|
||||
const colorDiv = document.createElement('div');
|
||||
colorDiv.className = `color-segment multi-color ${direction}`;
|
||||
colorDiv.style.backgroundColor = `#${color}`;
|
||||
selectedColor.appendChild(colorDiv);
|
||||
});
|
||||
// Add multiple color class to the container
|
||||
selectedColor.classList.add('multi-color-container');
|
||||
} else {
|
||||
// Single color filament - reset to default display
|
||||
selectedColor.innerHTML = '';
|
||||
selectedColor.classList.remove('multi-color-container');
|
||||
selectedColor.style.backgroundColor = `#${spool.filament.color_hex || 'FFFFFF'}`;
|
||||
}
|
||||
|
||||
selectedText.textContent = `${spool.id} | ${spool.filament.name} (${spool.filament.material})`;
|
||||
dropdownContent.classList.remove("show");
|
||||
|
||||
@@ -216,10 +289,18 @@ async function initSpoolman() {
|
||||
|
||||
const fetchedData = await fetchSpoolData();
|
||||
spoolsData = processSpoolData(fetchedData);
|
||||
|
||||
|
||||
document.dispatchEvent(new CustomEvent('spoolDataLoaded', {
|
||||
detail: spoolsData
|
||||
}));
|
||||
|
||||
locationData = await fetchLocationData();
|
||||
|
||||
document.dispatchEvent(new CustomEvent('locationDataLoaded', {
|
||||
detail: locationData
|
||||
}));
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Initialisieren von Spoolman:', error);
|
||||
document.dispatchEvent(new CustomEvent('spoolmanError', {
|
||||
@@ -247,6 +328,25 @@ async function fetchSpoolData() {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchLocationData() {
|
||||
try {
|
||||
if (!spoolmanUrl) {
|
||||
throw new Error('Spoolman URL ist nicht initialisiert');
|
||||
}
|
||||
|
||||
const response = await fetch(`${spoolmanUrl}/api/v1/location`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Location-Daten:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listener
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initSpoolman();
|
||||
@@ -255,6 +355,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (vendorSelect) {
|
||||
vendorSelect.addEventListener('change', () => updateFilamentDropdown());
|
||||
}
|
||||
|
||||
const locationSelect = document.getElementById('locationSelect');
|
||||
if (locationSelect) {
|
||||
locationSelect.addEventListener('change', () => updateLocationSelect());
|
||||
}
|
||||
|
||||
const onlyWithoutSmId = document.getElementById('onlyWithoutSmId');
|
||||
if (onlyWithoutSmId) {
|
||||
@@ -267,6 +372,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
document.addEventListener('spoolDataLoaded', (event) => {
|
||||
populateVendorDropdown(event.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('locationDataLoaded', (event) => {
|
||||
populateLocationDropdown(event.detail);
|
||||
});
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (!event.target.closest('.custom-dropdown')) {
|
||||
@@ -297,6 +406,7 @@ window.getSpoolData = () => spoolsData;
|
||||
window.setSpoolData = (data) => { spoolsData = data; };
|
||||
window.reloadSpoolData = initSpoolman;
|
||||
window.populateVendorDropdown = populateVendorDropdown;
|
||||
window.populateLocationDropdown = populateLocationDropdown;
|
||||
window.updateFilamentDropdown = updateFilamentDropdown;
|
||||
window.toggleFilamentDropdown = () => {
|
||||
const content = document.getElementById("filament-dropdown-content");
|
||||
|
1
html/spoolman_url.json
Normal file
1
html/spoolman_url.json
Normal file
@@ -0,0 +1 @@
|
||||
{"url": "http://192.168.1.5:7912", "octoEnabled": true, "octoUrl": "http://192.168.1.17:5001", "octoToken": "O5zZ58mXRAyeGpVEj2ZZj-UPAPqJ2N7JgtD36mw1M4g"}
|
@@ -759,6 +759,50 @@ a:hover {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Multi-color filament styles */
|
||||
.option-colors {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.multi-color {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
/* Coaxial pattern (horizontal stripes) */
|
||||
.multi-color.coaxial {
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Longitudinal pattern (vertical stripes) */
|
||||
.multi-color.longitudinal {
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Container for multiple colors in selected display */
|
||||
.multi-color-container {
|
||||
display: flex !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
gap: 2px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.color-segment {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
@@ -927,31 +971,35 @@ input[type="submit"]:disabled,
|
||||
}
|
||||
|
||||
/* Schreib-Button */
|
||||
#writeNfcButton {
|
||||
#writeNfcButton, #writeLocationNfcButton {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#writeNfcButton.writing {
|
||||
#writeNfcButton.writing, #writeLocationNfcButton.writing {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#writeNfcButton.success {
|
||||
#writeNfcButton.success, #writeLocationNfcButton.success {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#writeNfcButton.error {
|
||||
#writeNfcButton.error, #writeLocationNfcButton.error {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#writeLocationNfcButton{
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
@keyframes dots {
|
||||
0% { content: ""; }
|
||||
33% { content: "."; }
|
||||
@@ -959,7 +1007,7 @@ input[type="submit"]:disabled,
|
||||
100% { content: "..."; }
|
||||
}
|
||||
|
||||
#writeNfcButton.writing::after {
|
||||
#writeNfcButton.writing::after, #writeLocationNfcButton.writing::after {
|
||||
content: "...";
|
||||
animation: dots 1s steps(3, end) infinite;
|
||||
}
|
||||
|
@@ -56,8 +56,8 @@
|
||||
|
||||
<div class="update-options">
|
||||
<div class="update-section">
|
||||
<h2>Firmware Update</h2>
|
||||
<p>Upload a new firmware file (filaman_*.bin)</p>
|
||||
<h2>1) Firmware Update</h2>
|
||||
<p>Upload a new firmware file (upgrade_filaman_firmware_*.bin)</p>
|
||||
<div class="update-form">
|
||||
<form id="firmwareForm" enctype='multipart/form-data' data-type="firmware">
|
||||
<input type='file' name='update' accept='.bin' required>
|
||||
@@ -67,8 +67,8 @@
|
||||
</div>
|
||||
|
||||
<div class="update-section">
|
||||
<h2>Webpage Update</h2>
|
||||
<p>Upload a new webpage file (webpage_*.bin)</p>
|
||||
<h2>2) Webpage Update</h2>
|
||||
<p>Upload a new webpage file (upgrade_filaman_website_*.bin)</p>
|
||||
<div class="update-form">
|
||||
<form id="webpageForm" enctype='multipart/form-data' data-type="webpage">
|
||||
<input type='file' name='update' accept='.bin' required>
|
||||
|
@@ -55,6 +55,7 @@
|
||||
<h5 class="card-title">Sacle Calibration</h5>
|
||||
<button id="calibrateBtn" class="btn btn-primary">Calibrate 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>
|
||||
</div>
|
||||
@@ -140,6 +141,15 @@
|
||||
}));
|
||||
});
|
||||
|
||||
// Add auto-tare function
|
||||
function setAutoTare(enabled) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'scale',
|
||||
payload: 'setAutoTare',
|
||||
enabled: enabled
|
||||
}));
|
||||
}
|
||||
|
||||
// WebSocket-Verbindung beim Laden der Seite initiieren
|
||||
connectWebSocket();
|
||||
</script>
|
||||
|
@@ -9,8 +9,8 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[common]
|
||||
version = "1.4.9"
|
||||
to_old_version = "1.4.0"
|
||||
version = "2.0.2"
|
||||
to_old_version = "1.5.10"
|
||||
|
||||
##
|
||||
[env:esp32dev]
|
||||
@@ -18,14 +18,12 @@ platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
#monitor_port = /dev/cu.usbmodem01
|
||||
|
||||
lib_deps =
|
||||
tzapu/WiFiManager @ ^2.0.17
|
||||
https://github.com/me-no-dev/ESPAsyncWebServer.git#master
|
||||
#me-no-dev/AsyncTCP @ ^1.1.1
|
||||
https://github.com/esphome/AsyncTCP.git
|
||||
#mathieucarbou/ESPAsyncWebServer @ ^3.6.0
|
||||
#esp32async/AsyncTCP @ ^3.3.5
|
||||
bogde/HX711 @ ^0.7.5
|
||||
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||
adafruit/Adafruit GFX Library @ ^1.11.11
|
||||
@@ -35,7 +33,6 @@ lib_deps =
|
||||
digitaldragon/SSLClient @ ^1.3.2
|
||||
|
||||
; Enable SPIFFS upload
|
||||
#board_build.filesystem = spiffs
|
||||
board_build.filesystem = littlefs
|
||||
; Update partition settings
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -51,6 +48,7 @@ build_flags =
|
||||
-mtext-section-literals
|
||||
-DVERSION=\"${common.version}\"
|
||||
-DTOOLDVERSION=\"${common.to_old_version}\"
|
||||
#-DENABLE_HEAP_DEBUGGING
|
||||
-DASYNCWEBSERVER_REGEX
|
||||
#-DCORE_DEBUG_LEVEL=3
|
||||
-DCONFIG_ARDUHAL_LOG_COLORS=1
|
||||
|
@@ -14,7 +14,7 @@ def copy_file(input_file, output_file):
|
||||
|
||||
def should_compress(file):
|
||||
# Skip compression for spoolman.html
|
||||
if file == 'spoolman.html':
|
||||
if file == 'spoolman.html' or file == 'waage.html':
|
||||
return False
|
||||
# Komprimiere nur bestimmte Dateitypen
|
||||
return file.endswith(('.js', '.png', '.css', '.html'))
|
||||
|
@@ -14,17 +14,39 @@ def get_version():
|
||||
return version_match.group(1) if version_match else None
|
||||
|
||||
def get_last_tag():
|
||||
"""Get the last non-beta tag for changelog generation"""
|
||||
try:
|
||||
result = subprocess.run(['git', 'describe', '--tags', '--abbrev=0'],
|
||||
# Get all tags sorted by version
|
||||
result = subprocess.run(['git', 'tag', '-l', '--sort=-version:refname'],
|
||||
capture_output=True, text=True)
|
||||
return result.stdout.strip()
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
|
||||
tags = result.stdout.strip().split('\n')
|
||||
|
||||
# Find the first (newest) non-beta tag
|
||||
for tag in tags:
|
||||
if tag and not '-beta' in tag.lower():
|
||||
print(f"Using last stable tag for changelog: {tag}")
|
||||
return tag
|
||||
|
||||
# Fallback: if no non-beta tags found, use the newest tag
|
||||
print("No stable tags found, using newest tag")
|
||||
if tags and tags[0]:
|
||||
return tags[0]
|
||||
return None
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
def categorize_commit(commit_msg):
|
||||
"""Categorize commit messages based on conventional commits"""
|
||||
lower_msg = commit_msg.lower()
|
||||
if any(x in lower_msg for x in ['feat', 'add', 'new']):
|
||||
|
||||
# Check for breaking changes first
|
||||
if ('!' in commit_msg and any(x in lower_msg for x in ['feat!', 'fix!', 'chore!', 'refactor!'])) or \
|
||||
'breaking change' in lower_msg or 'breaking:' in lower_msg:
|
||||
return 'Breaking Changes'
|
||||
elif any(x in lower_msg for x in ['feat', 'add', 'new']):
|
||||
return 'Added'
|
||||
elif any(x in lower_msg for x in ['fix', 'bug']):
|
||||
return 'Fixed'
|
||||
@@ -34,6 +56,7 @@ def categorize_commit(commit_msg):
|
||||
def get_changes_from_git():
|
||||
"""Get changes from git commits since last tag"""
|
||||
changes = {
|
||||
'Breaking Changes': [],
|
||||
'Added': [],
|
||||
'Changed': [],
|
||||
'Fixed': []
|
||||
@@ -55,7 +78,9 @@ def get_changes_from_git():
|
||||
if commit:
|
||||
category = categorize_commit(commit)
|
||||
# Clean up commit message
|
||||
clean_msg = re.sub(r'^(feat|fix|chore|docs|style|refactor|perf|test)(\(.*\))?:', '', commit).strip()
|
||||
clean_msg = re.sub(r'^(feat|fix|chore|docs|style|refactor|perf|test)(\(.*\))?!?:', '', commit).strip()
|
||||
# Remove BREAKING CHANGE prefix if present
|
||||
clean_msg = re.sub(r'^breaking change:\s*', '', clean_msg, flags=re.IGNORECASE).strip()
|
||||
changes[category].append(clean_msg)
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
|
1272
src/api.cpp
1272
src/api.cpp
File diff suppressed because it is too large
Load Diff
25
src/api.h
25
src/api.h
@@ -6,22 +6,45 @@
|
||||
#include "website.h"
|
||||
#include "display.h"
|
||||
#include <ArduinoJson.h>
|
||||
typedef enum {
|
||||
API_INIT,
|
||||
API_IDLE,
|
||||
API_TRANSMITTING
|
||||
} spoolmanApiStateType;
|
||||
|
||||
typedef enum {
|
||||
API_REQUEST_OCTO_SPOOL_UPDATE,
|
||||
API_REQUEST_BAMBU_UPDATE,
|
||||
API_REQUEST_SPOOL_TAG_ID_UPDATE,
|
||||
API_REQUEST_SPOOL_WEIGHT_UPDATE,
|
||||
API_REQUEST_SPOOL_LOCATION_UPDATE,
|
||||
API_REQUEST_VENDOR_CREATE,
|
||||
API_REQUEST_VENDOR_CHECK,
|
||||
API_REQUEST_FILAMENT_CHECK,
|
||||
API_REQUEST_FILAMENT_CREATE,
|
||||
API_REQUEST_SPOOL_CREATE
|
||||
} SpoolmanApiRequestType;
|
||||
|
||||
extern volatile spoolmanApiStateType spoolmanApiState;
|
||||
extern bool spoolman_connected;
|
||||
extern String spoolmanUrl;
|
||||
extern bool octoEnabled;
|
||||
extern bool sendOctoUpdate;
|
||||
extern String octoUrl;
|
||||
extern String octoToken;
|
||||
extern bool spoolmanConnected;
|
||||
|
||||
bool checkSpoolmanInstance(const String& url);
|
||||
bool checkSpoolmanInstance();
|
||||
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk);
|
||||
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
|
||||
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder
|
||||
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
|
||||
bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion zum Aktualisieren eines Spools
|
||||
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
|
||||
uint8_t updateSpoolLocation(String spoolId, String location);
|
||||
bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
|
||||
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
|
||||
bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten
|
||||
bool createBrandFilament(JsonDocument& payload, String uidString);
|
||||
|
||||
#endif
|
||||
|
158
src/bambu.cpp
158
src/bambu.cpp
@@ -10,6 +10,7 @@
|
||||
#include "esp_task_wdt.h"
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include <Preferences.h>
|
||||
|
||||
WiFiClient espClient;
|
||||
SSLClient sslClient(&espClient);
|
||||
@@ -17,22 +18,13 @@ PubSubClient client(sslClient);
|
||||
|
||||
TaskHandle_t BambuMqttTask;
|
||||
|
||||
String topic = "";
|
||||
//String request_topic = "";
|
||||
const char* bambu_username = "bblp";
|
||||
const char* bambu_ip = nullptr;
|
||||
const char* bambu_accesscode = 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 autoSendToBambu = false;
|
||||
int autoSetToBambuSpoolId = 0;
|
||||
|
||||
BambuCredentials bambuCredentials;
|
||||
|
||||
// Globale Variablen für AMS-Daten
|
||||
int ams_count = 0;
|
||||
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
|
||||
@@ -41,20 +33,25 @@ AMSData ams_data[MAX_AMS]; // Definition des Arrays;
|
||||
bool removeBambuCredentials() {
|
||||
if (BambuMqttTask) {
|
||||
vTaskDelete(BambuMqttTask);
|
||||
BambuMqttTask = NULL;
|
||||
}
|
||||
|
||||
if (!removeJsonValue("/bambu_credentials.json")) {
|
||||
Serial.println("Fehler beim Löschen der Bambu-Credentials.");
|
||||
return false;
|
||||
}
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
|
||||
preferences.remove(NVS_KEY_BAMBU_IP);
|
||||
preferences.remove(NVS_KEY_BAMBU_SERIAL);
|
||||
preferences.remove(NVS_KEY_BAMBU_ACCESSCODE);
|
||||
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_ENABLE);
|
||||
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_TIME);
|
||||
preferences.end();
|
||||
|
||||
// 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;
|
||||
bambuCredentials.ip = "";
|
||||
bambuCredentials.serial = "";
|
||||
bambuCredentials.accesscode = "";
|
||||
bambuCredentials.autosend_enable = false;
|
||||
bambuCredentials.autosend_time = BAMBU_DEFAULT_AUTOSEND_TIME;
|
||||
|
||||
autoSetToBambuSpoolId = 0;
|
||||
ams_count = 0;
|
||||
amsJsonData = "";
|
||||
@@ -67,26 +64,23 @@ bool removeBambuCredentials() {
|
||||
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
|
||||
if (BambuMqttTask) {
|
||||
vTaskDelete(BambuMqttTask);
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
doc["bambu_ip"] = ip;
|
||||
doc["bambu_accesscode"] = accesscode;
|
||||
doc["bambu_serialnr"] = serialnr;
|
||||
doc["autoSendToBambu"] = autoSend;
|
||||
doc["autoSendTime"] = (autoSendTime != "") ? autoSendTime.toInt() : autoSetBambuAmsCounter;
|
||||
|
||||
if (!saveJsonValue("/bambu_credentials.json", doc)) {
|
||||
Serial.println("Fehler beim Speichern der Bambu-Credentials.");
|
||||
return false;
|
||||
BambuMqttTask = NULL;
|
||||
}
|
||||
|
||||
// Dynamische Speicherallokation für die globalen Pointer
|
||||
bambu_ip = ip.c_str();
|
||||
bambu_accesscode = accesscode.c_str();
|
||||
bambu_serialnr = serialnr.c_str();
|
||||
autoSendToBambu = autoSend;
|
||||
autoSetBambuAmsCounter = autoSendTime.toInt();
|
||||
bambuCredentials.ip = ip.c_str();
|
||||
bambuCredentials.serial = serialnr.c_str();
|
||||
bambuCredentials.accesscode = accesscode.c_str();
|
||||
bambuCredentials.autosend_enable = autoSend;
|
||||
bambuCredentials.autosend_time = autoSendTime.toInt();
|
||||
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
|
||||
preferences.putString(NVS_KEY_BAMBU_IP, bambuCredentials.ip);
|
||||
preferences.putString(NVS_KEY_BAMBU_SERIAL, bambuCredentials.serial);
|
||||
preferences.putString(NVS_KEY_BAMBU_ACCESSCODE, bambuCredentials.accesscode);
|
||||
preferences.putBool(NVS_KEY_BAMBU_AUTOSEND_ENABLE, bambuCredentials.autosend_enable);
|
||||
preferences.putInt(NVS_KEY_BAMBU_AUTOSEND_TIME, bambuCredentials.autosend_time);
|
||||
preferences.end();
|
||||
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
if (!setupMqtt()) return false;
|
||||
@@ -95,35 +89,36 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
|
||||
}
|
||||
|
||||
bool loadBambuCredentials() {
|
||||
JsonDocument doc;
|
||||
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) {
|
||||
// Temporäre Strings für die Werte
|
||||
String ip = doc["bambu_ip"].as<String>();
|
||||
String code = doc["bambu_accesscode"].as<String>();
|
||||
String serial = doc["bambu_serialnr"].as<String>();
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_BAMBU, true);
|
||||
String ip = preferences.getString(NVS_KEY_BAMBU_IP, "");
|
||||
String serial = preferences.getString(NVS_KEY_BAMBU_SERIAL, "");
|
||||
String code = preferences.getString(NVS_KEY_BAMBU_ACCESSCODE, "");
|
||||
bool autosendEnable = preferences.getBool(NVS_KEY_BAMBU_AUTOSEND_ENABLE, false);
|
||||
int autosendTime = preferences.getInt(NVS_KEY_BAMBU_AUTOSEND_TIME, BAMBU_DEFAULT_AUTOSEND_TIME);
|
||||
preferences.end();
|
||||
|
||||
g_bambu_ip = ip;
|
||||
g_bambu_accesscode = code;
|
||||
g_bambu_serialnr = serial;
|
||||
if(ip != ""){
|
||||
bambuCredentials.ip = ip.c_str();
|
||||
bambuCredentials.serial = serial.c_str();
|
||||
bambuCredentials.accesscode = code.c_str();
|
||||
bambuCredentials.autosend_enable = autosendEnable;
|
||||
bambuCredentials.autosend_time = autosendTime;
|
||||
|
||||
if (doc["autoSendToBambu"].is<bool>()) autoSendToBambu = doc["autoSendToBambu"].as<bool>();
|
||||
if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>();
|
||||
Serial.println("credentials loaded loadCredentials!");
|
||||
Serial.println(bambuCredentials.ip);
|
||||
Serial.println(bambuCredentials.serial);
|
||||
Serial.println(bambuCredentials.accesscode);
|
||||
Serial.println(String(bambuCredentials.autosend_enable));
|
||||
Serial.println(String(bambuCredentials.autosend_time));
|
||||
|
||||
ip.trim();
|
||||
code.trim();
|
||||
serial.trim();
|
||||
|
||||
// Dynamische Speicherallokation für die globalen Pointer
|
||||
bambu_ip = g_bambu_ip.c_str();
|
||||
bambu_accesscode = g_bambu_accesscode.c_str();
|
||||
bambu_serialnr = g_bambu_serialnr.c_str();
|
||||
|
||||
topic = "device/" + String(bambu_serialnr);
|
||||
//request_topic = "device/" + String(bambu_serialnr) + "/request";
|
||||
return true;
|
||||
}
|
||||
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
|
||||
return false;
|
||||
else
|
||||
{
|
||||
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct FilamentResult {
|
||||
@@ -226,7 +221,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
|
||||
bool sendMqttMessage(const String& payload) {
|
||||
Serial.println("Sending MQTT message");
|
||||
Serial.println(payload);
|
||||
if (client.publish((String(topic) + "/request").c_str(), payload.c_str()))
|
||||
if (client.publish(("device/"+bambuCredentials.serial+"/request").c_str(), payload.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -499,7 +494,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
|
||||
hasChanges = true;
|
||||
|
||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
{
|
||||
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
|
||||
}
|
||||
@@ -523,7 +518,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
|
||||
hasChanges = true;
|
||||
|
||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
{
|
||||
autoSetSpool(autoSetToBambuSpoolId, 254);
|
||||
}
|
||||
@@ -580,11 +575,11 @@ void reconnect() {
|
||||
oledShowTopRow();
|
||||
|
||||
// Attempt to connect
|
||||
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100));
|
||||
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode)) {
|
||||
String clientId = bambuCredentials.serial + "_" + String(random(0, 100));
|
||||
if (client.connect(clientId.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str())) {
|
||||
Serial.println("MQTT re/connected");
|
||||
|
||||
client.subscribe((String(topic) + "/report").c_str());
|
||||
client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
|
||||
bambu_connected = true;
|
||||
oledShowTopRow();
|
||||
} else {
|
||||
@@ -600,6 +595,7 @@ void reconnect() {
|
||||
Serial.println("Disable Bambu MQTT Task after 5 retries");
|
||||
//vTaskSuspend(BambuMqttTask);
|
||||
vTaskDelete(BambuMqttTask);
|
||||
BambuMqttTask = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -630,28 +626,24 @@ void mqtt_loop(void * parameter) {
|
||||
|
||||
bool setupMqtt() {
|
||||
// Wenn Bambu Daten vorhanden
|
||||
bool success = loadBambuCredentials();
|
||||
//bool success = loadBambuCredentials();
|
||||
|
||||
if (!success) {
|
||||
bambuDisabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
|
||||
if (bambuCredentials.ip != "" && bambuCredentials.accesscode != "" && bambuCredentials.serial != "")
|
||||
{
|
||||
oledShowProgressBar(4, 7, DISPLAY_BOOT_TEXT, "Bambu init");
|
||||
bambuDisabled = false;
|
||||
sslClient.setCACert(root_ca);
|
||||
sslClient.setInsecure();
|
||||
client.setServer(bambu_ip, 8883);
|
||||
client.setServer(bambuCredentials.ip.c_str(), 8883);
|
||||
|
||||
// Verbinden mit dem MQTT-Server
|
||||
bool connected = true;
|
||||
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100));
|
||||
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode))
|
||||
String clientId = String(bambuCredentials.serial) + "_" + String(random(0, 100));
|
||||
if (client.connect(bambuCredentials.ip.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str()))
|
||||
{
|
||||
client.setCallback(mqtt_callback);
|
||||
client.setBufferSize(15488);
|
||||
client.subscribe((String(topic) + "/report").c_str());
|
||||
client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
|
||||
Serial.println("MQTT-Client initialisiert");
|
||||
|
||||
oledShowMessage("Bambu Connected");
|
||||
@@ -674,6 +666,7 @@ bool setupMqtt() {
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
connected = false;
|
||||
oledShowTopRow();
|
||||
autoSetToBambuSpoolId = 0;
|
||||
}
|
||||
|
||||
if (!connected) return false;
|
||||
@@ -687,8 +680,11 @@ bool setupMqtt() {
|
||||
}
|
||||
|
||||
void bambu_restart() {
|
||||
Serial.println("Bambu restart");
|
||||
|
||||
if (BambuMqttTask) {
|
||||
vTaskDelete(BambuMqttTask);
|
||||
BambuMqttTask = NULL;
|
||||
delay(10);
|
||||
}
|
||||
setupMqtt();
|
||||
|
11
src/bambu.h
11
src/bambu.h
@@ -16,6 +16,14 @@ struct TrayData {
|
||||
String cali_idx;
|
||||
};
|
||||
|
||||
struct BambuCredentials {
|
||||
String ip;
|
||||
String serial;
|
||||
String accesscode;
|
||||
bool autosend_enable;
|
||||
int autosend_time;
|
||||
};
|
||||
|
||||
#define MAX_AMS 17 // 16 normale AMS + 1 externe Spule
|
||||
extern String amsJsonData; // Für die vorbereiteten JSON-Daten
|
||||
|
||||
@@ -28,9 +36,10 @@ extern bool bambu_connected;
|
||||
|
||||
extern int ams_count;
|
||||
extern AMSData ams_data[MAX_AMS];
|
||||
extern bool autoSendToBambu;
|
||||
//extern bool autoSendToBambu;
|
||||
extern int autoSetToBambuSpoolId;
|
||||
extern bool bambuDisabled;
|
||||
extern BambuCredentials bambuCredentials;
|
||||
|
||||
bool removeBambuCredentials();
|
||||
bool loadBambuCredentials();
|
||||
|
@@ -16,20 +16,20 @@ const uint8_t LOADCELL_DOUT_PIN = 16; //16;
|
||||
const uint8_t LOADCELL_SCK_PIN = 17; //17;
|
||||
const uint8_t calVal_eepromAdress = 0;
|
||||
const uint16_t SCALE_LEVEL_WEIGHT = 500;
|
||||
uint16_t defaultScaleCalibrationValue = 430;
|
||||
// ***** HX711
|
||||
|
||||
// ***** TTP223 (Touch Sensor)
|
||||
// TTP223 circuit wiring
|
||||
const uint8_t TTP223_PIN = 25;
|
||||
// ***** TTP223
|
||||
|
||||
|
||||
// ***** Display
|
||||
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
|
||||
// On an ESP32: 21(SDA), 22(SCL)
|
||||
const int8_t OLED_RESET = -1; // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||
const uint8_t SCREEN_ADDRESS = 0x3C; ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
||||
const uint8_t SCREEN_WIDTH = 128; // OLED display width, in pixels
|
||||
const uint8_t SCREEN_HEIGHT = 64; // OLED display height, in pixels
|
||||
const uint8_t OLED_TOP_START = 0;
|
||||
const uint8_t OLED_TOP_END = 16;
|
||||
const uint8_t OLED_DATA_START = 17;
|
||||
const uint8_t OLED_DATA_END = SCREEN_HEIGHT;
|
||||
|
||||
// ***** Display
|
||||
|
||||
// ***** Webserver
|
||||
@@ -40,8 +40,6 @@ const uint8_t webserverPort = 80;
|
||||
const char* apiUrl = "/api/v1";
|
||||
// ***** API
|
||||
|
||||
// ***** Bambu Auto Set Spool
|
||||
uint8_t autoSetBambuAmsCounter = 60;
|
||||
// ***** Bambu Auto Set Spool
|
||||
|
||||
// ***** Task Prios
|
||||
|
42
src/config.h
42
src/config.h
@@ -3,6 +3,40 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define BAMBU_DEFAULT_AUTOSEND_TIME 60
|
||||
|
||||
#define NVS_NAMESPACE_API "api"
|
||||
#define NVS_KEY_SPOOLMAN_URL "spoolmanUrl"
|
||||
#define NVS_KEY_OCTOPRINT_ENABLED "octoEnabled"
|
||||
#define NVS_KEY_OCTOPRINT_URL "octoUrl"
|
||||
#define NVS_KEY_OCTOPRINT_TOKEN "octoToken"
|
||||
|
||||
#define NVS_NAMESPACE_BAMBU "bambu"
|
||||
#define NVS_KEY_BAMBU_IP "bambuIp"
|
||||
#define NVS_KEY_BAMBU_ACCESSCODE "bambuCode"
|
||||
#define NVS_KEY_BAMBU_SERIAL "bambuSerial"
|
||||
#define NVS_KEY_BAMBU_AUTOSEND_ENABLE "autosendEnable"
|
||||
#define NVS_KEY_BAMBU_AUTOSEND_TIME "autosendTime"
|
||||
|
||||
#define NVS_NAMESPACE_SCALE "scale"
|
||||
#define NVS_KEY_CALIBRATION "cal_value"
|
||||
#define NVS_KEY_AUTOTARE "auto_tare"
|
||||
#define SCALE_DEFAULT_CALIBRATION_VALUE 430.0f;
|
||||
|
||||
#define BAMBU_USERNAME "bblp"
|
||||
|
||||
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||
#define SCREEN_ADDRESS 0x3CU // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
||||
#define SCREEN_WIDTH 128U
|
||||
#define SCREEN_HEIGHT 64U
|
||||
#define SCREEN_TOP_BAR_HEIGHT 16U
|
||||
#define SCREEN_PROGRESS_BAR_HEIGHT 12U
|
||||
#define DISPLAY_BOOT_TEXT "FilaMan"
|
||||
|
||||
#define WIFI_CHECK_INTERVAL 60000U
|
||||
#define DISPLAY_UPDATE_INTERVAL 1000U
|
||||
#define SPOOLMAN_HEALTHCHECK_INTERVAL 60000U
|
||||
|
||||
extern const uint8_t PN532_IRQ;
|
||||
extern const uint8_t PN532_RESET;
|
||||
|
||||
@@ -11,10 +45,8 @@ extern const uint8_t LOADCELL_SCK_PIN;
|
||||
extern const uint8_t calVal_eepromAdress;
|
||||
extern const uint16_t SCALE_LEVEL_WEIGHT;
|
||||
|
||||
extern const int8_t OLED_RESET;
|
||||
extern const uint8_t SCREEN_ADDRESS;
|
||||
extern const uint8_t SCREEN_WIDTH;
|
||||
extern const uint8_t SCREEN_HEIGHT;
|
||||
extern const uint8_t TTP223_PIN;
|
||||
|
||||
extern const uint8_t OLED_TOP_START;
|
||||
extern const uint8_t OLED_TOP_END;
|
||||
extern const uint8_t OLED_DATA_START;
|
||||
@@ -23,7 +55,7 @@ extern const uint8_t OLED_DATA_END;
|
||||
extern const char* apiUrl;
|
||||
extern const uint8_t webserverPort;
|
||||
|
||||
extern uint8_t autoSetBambuAmsCounter;
|
||||
|
||||
|
||||
extern const unsigned char wifi_on[];
|
||||
extern const unsigned char wifi_off[];
|
||||
|
12
src/debug.h
Normal file
12
src/debug.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_HEAP_DEBUGGING
|
||||
#define HEAP_DEBUG_MESSAGE(location) printHeapDebugData(location);
|
||||
#else
|
||||
#define HEAP_DEBUG_MESSAGE(location)
|
||||
#endif
|
||||
|
||||
inline void printHeapDebugData(const char *location){
|
||||
Serial.println("Heap: " + String(ESP.getMinFreeHeap()/1024) + "\t" + String(ESP.getFreeHeap()/1024) + "\t" + String(ESP.getMaxAllocHeap()/1024) + "\t" + location);
|
||||
}
|
@@ -2,10 +2,12 @@
|
||||
#include "api.h"
|
||||
#include <vector>
|
||||
#include "icons.h"
|
||||
#include "main.h"
|
||||
|
||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
|
||||
bool wifiOn = false;
|
||||
bool iconToggle = false;
|
||||
|
||||
void setupDisplay() {
|
||||
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||
@@ -14,15 +16,10 @@ void setupDisplay() {
|
||||
}
|
||||
display.setTextColor(WHITE);
|
||||
display.clearDisplay();
|
||||
display.display();
|
||||
|
||||
// Show initial display buffer contents on the screen --
|
||||
// the library initializes this with an Adafruit splash screen.
|
||||
display.setTextColor(WHITE);
|
||||
display.display();
|
||||
|
||||
oledShowTopRow();
|
||||
oledShowMessage("FilaMan v" + String(VERSION));
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
oledShowProgressBar(0, 7, DISPLAY_BOOT_TEXT, "Display init");
|
||||
}
|
||||
|
||||
void oledclearline() {
|
||||
@@ -45,14 +42,14 @@ void oledcleardata() {
|
||||
//display.display();
|
||||
}
|
||||
|
||||
int oled_center_h(String text) {
|
||||
int oled_center_h(const String &text) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
|
||||
return (SCREEN_WIDTH - w) / 2;
|
||||
}
|
||||
|
||||
int oled_center_v(String text) {
|
||||
int oled_center_v(const String &text) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(text, 0, OLED_DATA_START, &x1, &y1, &w, &h);
|
||||
@@ -60,7 +57,7 @@ int oled_center_v(String text) {
|
||||
return OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - h) / 2);
|
||||
}
|
||||
|
||||
std::vector<String> splitTextIntoLines(String text, uint8_t textSize) {
|
||||
std::vector<String> splitTextIntoLines(const String &text, uint8_t textSize) {
|
||||
std::vector<String> lines;
|
||||
display.setTextSize(textSize);
|
||||
|
||||
@@ -120,7 +117,7 @@ std::vector<String> splitTextIntoLines(String text, uint8_t textSize) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
void oledShowMultilineMessage(String message, uint8_t size) {
|
||||
void oledShowMultilineMessage(const String &message, uint8_t size) {
|
||||
std::vector<String> lines;
|
||||
int maxLines = 3; // Maximale Anzahl Zeilen für size 2
|
||||
|
||||
@@ -148,7 +145,7 @@ void oledShowMultilineMessage(String message, uint8_t size) {
|
||||
display.display();
|
||||
}
|
||||
|
||||
void oledShowMessage(String message, uint8_t size) {
|
||||
void oledShowMessage(const String &message, uint8_t size) {
|
||||
oledcleardata();
|
||||
display.setTextSize(size);
|
||||
display.setTextWrap(false);
|
||||
@@ -171,22 +168,46 @@ void oledShowMessage(String message, uint8_t size) {
|
||||
void oledShowTopRow() {
|
||||
oledclearline();
|
||||
|
||||
if (bambu_connected == 1) {
|
||||
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE);
|
||||
} else {
|
||||
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE);
|
||||
}
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 4);
|
||||
display.print("v");
|
||||
display.print(VERSION);
|
||||
|
||||
if (spoolman_connected == 1) {
|
||||
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
|
||||
} else {
|
||||
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);
|
||||
}
|
||||
iconToggle = !iconToggle;
|
||||
|
||||
if (wifiOn == 1) {
|
||||
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE);
|
||||
} else {
|
||||
display.drawBitmap(107, 0, wifi_off , 16, 16, WHITE);
|
||||
// Do not show status indicators during boot
|
||||
if(!booting){
|
||||
if(bambuDisabled == false) {
|
||||
if (bambu_connected == 1) {
|
||||
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE);
|
||||
} else {
|
||||
if(iconToggle){
|
||||
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE);
|
||||
display.drawLine(50, 15, 66, 0, WHITE);
|
||||
display.drawLine(51, 15, 67, 0, WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spoolmanConnected) {
|
||||
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
|
||||
} else {
|
||||
if(iconToggle){
|
||||
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
|
||||
display.drawLine(80, 15, 96, 0, WHITE);
|
||||
display.drawLine(81, 15, 97, 0, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
if (wifiOn == 1) {
|
||||
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE);
|
||||
} else {
|
||||
if(iconToggle){
|
||||
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE);
|
||||
display.drawLine(107, 15, 123, 0, WHITE);
|
||||
display.drawLine(108, 15, 124, 0, WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display.display();
|
||||
@@ -214,6 +235,27 @@ void oledShowIcon(const char* icon) {
|
||||
display.display();
|
||||
}
|
||||
|
||||
void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage) {
|
||||
assert(step <= numSteps);
|
||||
|
||||
// clear data and bar area
|
||||
display.fillRect(0, OLED_DATA_START, SCREEN_WIDTH, SCREEN_HEIGHT-16, BLACK);
|
||||
|
||||
|
||||
display.setTextWrap(false);
|
||||
display.setTextSize(2);
|
||||
display.setCursor(0, OLED_DATA_START+4);
|
||||
display.print(largeText);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, OLED_DATA_END-SCREEN_PROGRESS_BAR_HEIGHT-10);
|
||||
display.print(statusMessage);
|
||||
|
||||
const int barLength = ((SCREEN_WIDTH-2)*step)/numSteps;
|
||||
display.drawRoundRect(0, SCREEN_HEIGHT-SCREEN_PROGRESS_BAR_HEIGHT, SCREEN_WIDTH, 12, 6, WHITE);
|
||||
display.fillRoundRect(1, SCREEN_HEIGHT-SCREEN_PROGRESS_BAR_HEIGHT+1, barLength, 10, 6, WHITE);
|
||||
display.display();
|
||||
}
|
||||
|
||||
void oledShowWeight(uint16_t weight) {
|
||||
// Display Gewicht
|
||||
oledcleardata();
|
||||
|
@@ -13,11 +13,13 @@ extern bool wifiOn;
|
||||
void setupDisplay();
|
||||
void oledclearline();
|
||||
void oledcleardata();
|
||||
int oled_center_h(String text);
|
||||
int oled_center_v(String text);
|
||||
int oled_center_h(const String &text);
|
||||
int oled_center_v(const String &text);
|
||||
|
||||
void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage);
|
||||
|
||||
void oledShowWeight(uint16_t weight);
|
||||
void oledShowMessage(String message, uint8_t size = 2);
|
||||
void oledShowMessage(const String &message, uint8_t size = 2);
|
||||
void oledShowTopRow();
|
||||
void oledShowIcon(const char* icon);
|
||||
|
||||
|
210
src/main.cpp
210
src/main.cpp
@@ -15,6 +15,8 @@
|
||||
|
||||
bool mainTaskWasPaused = 0;
|
||||
uint8_t scaleTareCounter = 0;
|
||||
bool touchSensorConnected = false;
|
||||
bool booting = true;
|
||||
|
||||
// ##### SETUP #####
|
||||
void setup() {
|
||||
@@ -39,7 +41,6 @@ void setup() {
|
||||
setupWebserver(server);
|
||||
|
||||
// Spoolman API
|
||||
// api.cpp
|
||||
initSpoolman();
|
||||
|
||||
// Bambu MQTT
|
||||
@@ -48,12 +49,22 @@ void setup() {
|
||||
// NFC Reader
|
||||
startNfc();
|
||||
|
||||
start_scale();
|
||||
// Touch Sensor
|
||||
pinMode(TTP223_PIN, INPUT_PULLUP);
|
||||
if (digitalRead(TTP223_PIN) == LOW)
|
||||
{
|
||||
Serial.println("Touch Sensor is connected");
|
||||
touchSensorConnected = true;
|
||||
}
|
||||
|
||||
// Scale
|
||||
start_scale(touchSensorConnected);
|
||||
|
||||
// WDT initialisieren mit 10 Sekunden Timeout
|
||||
bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus
|
||||
esp_task_wdt_init(10, panic);
|
||||
|
||||
booting = false;
|
||||
// Aktuellen Task (loopTask) zum Watchdog hinzufügen
|
||||
esp_task_wdt_add(NULL);
|
||||
}
|
||||
@@ -84,21 +95,46 @@ uint8_t autoAmsCounter = 0;
|
||||
uint8_t weightSend = 0;
|
||||
int16_t lastWeight = 0;
|
||||
|
||||
// WIFI check variables
|
||||
unsigned long lastWifiCheckTime = 0;
|
||||
const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms)
|
||||
unsigned long lastTopRowUpdateTime = 0;
|
||||
unsigned long lastSpoolmanHealcheckTime = 0;
|
||||
|
||||
// Button debounce variables
|
||||
unsigned long lastButtonPress = 0;
|
||||
const unsigned long debounceDelay = 500; // 500 ms debounce delay
|
||||
|
||||
// ##### PROGRAM START #####
|
||||
void loop() {
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
// Überprüfe den Status des Touch Sensors
|
||||
if (touchSensorConnected && digitalRead(TTP223_PIN) == HIGH && currentMillis - lastButtonPress > debounceDelay)
|
||||
{
|
||||
lastButtonPress = currentMillis;
|
||||
scaleTareRequest = true;
|
||||
}
|
||||
|
||||
// Überprüfe regelmäßig die WLAN-Verbindung
|
||||
if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval))
|
||||
if (intervalElapsed(currentMillis, lastWifiCheckTime, WIFI_CHECK_INTERVAL))
|
||||
{
|
||||
checkWiFiConnection();
|
||||
}
|
||||
|
||||
// Periodic display update
|
||||
if (intervalElapsed(currentMillis, lastTopRowUpdateTime, DISPLAY_UPDATE_INTERVAL))
|
||||
{
|
||||
oledShowTopRow();
|
||||
}
|
||||
|
||||
// Periodic spoolman health check
|
||||
if (intervalElapsed(currentMillis, lastSpoolmanHealcheckTime, SPOOLMAN_HEALTHCHECK_INTERVAL))
|
||||
{
|
||||
checkSpoolmanInstance();
|
||||
}
|
||||
|
||||
// Wenn Bambu auto set Spool aktiv
|
||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0)
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
|
||||
{
|
||||
if (!bambuDisabled && !bambu_connected)
|
||||
{
|
||||
@@ -107,13 +143,13 @@ void loop() {
|
||||
|
||||
if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval))
|
||||
{
|
||||
if (hasReadRfidTag == 0)
|
||||
if (nfcReaderState == NFC_IDLE)
|
||||
{
|
||||
lastAutoSetBambuAmsTime = currentMillis;
|
||||
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
|
||||
oledShowMessage("Auto Set " + String(bambuCredentials.autosend_time - autoAmsCounter) + "s");
|
||||
autoAmsCounter++;
|
||||
|
||||
if (autoAmsCounter >= autoSetBambuAmsCounter)
|
||||
if (autoAmsCounter >= bambuCredentials.autosend_time)
|
||||
{
|
||||
autoSetToBambuSpoolId = 0;
|
||||
autoAmsCounter = 0;
|
||||
@@ -127,95 +163,117 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn Waage nicht Kalibriert
|
||||
if (scaleCalibrated == 3)
|
||||
// If scale is not calibrated, only show a warning
|
||||
if (!scaleCalibrated)
|
||||
{
|
||||
oledShowMessage("Scale not calibrated!");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ausgabe der Waage auf Display
|
||||
if(pauseMainTask == 0)
|
||||
{
|
||||
if (mainTaskWasPaused || (weight != lastWeight && hasReadRfidTag == 0 && (!autoSendToBambu || autoSetToBambuSpoolId == 0)))
|
||||
{
|
||||
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
||||
// Do not show the warning if the calibratin process is onging
|
||||
if(!scaleCalibrationActive){
|
||||
oledShowMessage("Scale not calibrated");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
mainTaskWasPaused = false;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
mainTaskWasPaused = true;
|
||||
}
|
||||
|
||||
|
||||
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag < 3)
|
||||
{
|
||||
lastWeightReadTime = currentMillis;
|
||||
|
||||
// Prüfen ob die Waage korrekt genullt ist
|
||||
if ((weight > 0 && weight < 5) || weight < -1)
|
||||
// Ausgabe der Waage auf Display
|
||||
if(pauseMainTask == 0)
|
||||
{
|
||||
if(scaleTareCounter < 5)
|
||||
// Use filtered weight for smooth display, but still check API weight for significant changes
|
||||
int16_t displayWeight = getFilteredDisplayWeight();
|
||||
if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0)))
|
||||
{
|
||||
scaleTareCounter++;
|
||||
(displayWeight < 2) ? ((displayWeight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(displayWeight);
|
||||
}
|
||||
mainTaskWasPaused = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainTaskWasPaused = true;
|
||||
}
|
||||
|
||||
|
||||
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
|
||||
{
|
||||
lastWeightReadTime = currentMillis;
|
||||
|
||||
// Prüfen ob die Waage korrekt genullt ist
|
||||
// Abweichung von 2g ignorieren
|
||||
if (autoTare && (weight > 2 && weight < 7) || weight < -2)
|
||||
{
|
||||
scale_tare_counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleTareRequest = true;
|
||||
scaleTareCounter = 0;
|
||||
scale_tare_counter = 0;
|
||||
}
|
||||
|
||||
// Prüfen ob das Gewicht gleich bleibt und dann senden
|
||||
if (abs(weight - lastWeight) <= 2 && weight > 5)
|
||||
{
|
||||
weigthCouterToApi++;
|
||||
}
|
||||
else
|
||||
{
|
||||
weigthCouterToApi = 0;
|
||||
weightSend = 0;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleTareCounter = 0;
|
||||
}
|
||||
|
||||
// Prüfen ob das Gewicht gleich bleibt und dann senden
|
||||
if (weight == lastWeight && weight > 5)
|
||||
{
|
||||
weigthCouterToApi++;
|
||||
}
|
||||
else
|
||||
// reset weight counter after writing tag
|
||||
// TBD: what exactly is the logic behind this?
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
|
||||
{
|
||||
weigthCouterToApi = 0;
|
||||
weightSend = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lastWeight = weight;
|
||||
|
||||
// reset weight counter after writing tag
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag > 1)
|
||||
{
|
||||
weigthCouterToApi = 0;
|
||||
}
|
||||
|
||||
lastWeight = weight;
|
||||
|
||||
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
|
||||
if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && hasReadRfidTag == 1) {
|
||||
oledShowIcon("loading");
|
||||
if (updateSpoolWeight(spoolId, weight))
|
||||
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
|
||||
if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE)
|
||||
{
|
||||
oledShowIcon("success");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
weightSend = 1;
|
||||
autoSetToBambuSpoolId = spoolId.toInt();
|
||||
// set the current tag as processed to prevent it beeing processed again
|
||||
tagProcessed = true;
|
||||
|
||||
if (octoEnabled)
|
||||
if (updateSpoolWeight(activeSpoolId, weight))
|
||||
{
|
||||
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||
weightSend = 1;
|
||||
|
||||
// Set Bambu spool ID for auto-send if enabled
|
||||
if (bambuCredentials.autosend_enable)
|
||||
{
|
||||
autoSetToBambuSpoolId = activeSpoolId.toInt();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
oledShowIcon("failed");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Handle successful tag write: Send weight to Spoolman but NEVER auto-send to Bambu
|
||||
if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_WRITE_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE)
|
||||
{
|
||||
oledShowIcon("failed");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
// set the current tag as processed to prevent it beeing processed again
|
||||
tagProcessed = true;
|
||||
|
||||
if (updateSpoolWeight(activeSpoolId, weight))
|
||||
{
|
||||
weightSend = 1;
|
||||
Serial.println("Tag written: Weight sent to Spoolman, but NO auto-send to Bambu");
|
||||
// INTENTIONALLY do NOT set autoSetToBambuSpoolId here to prevent Bambu auto-send
|
||||
}
|
||||
else
|
||||
{
|
||||
oledShowIcon("failed");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
if(octoEnabled && sendOctoUpdate && spoolmanApiState == API_IDLE)
|
||||
{
|
||||
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||
sendOctoUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
9
src/main.h
Normal file
9
src/main.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
extern bool booting;
|
||||
|
||||
#endif
|
1913
src/nfc.cpp
1913
src/nfc.cpp
File diff suppressed because it is too large
Load Diff
23
src/nfc.h
23
src/nfc.h
@@ -3,14 +3,31 @@
|
||||
|
||||
#include <Arduino.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 scanRfidTask(void * parameter);
|
||||
void startWriteJsonToTag(const char* payload);
|
||||
void startWriteJsonToTag(const bool isSpoolTag, const char* payload);
|
||||
bool quickSpoolIdCheck(String uidString);
|
||||
bool readCompleteJsonForFastPath(); // Read complete JSON data for fast-path web interface display
|
||||
|
||||
extern TaskHandle_t RfidReaderTask;
|
||||
extern String nfcJsonData;
|
||||
extern String spoolId;
|
||||
extern volatile uint8_t hasReadRfidTag;
|
||||
extern String activeSpoolId;
|
||||
extern String lastSpoolId;
|
||||
extern volatile nfcReaderStateType nfcReaderState;
|
||||
extern volatile bool pauseBambuMqttTask;
|
||||
extern volatile bool nfcWriteInProgress;
|
||||
extern bool tagProcessed;
|
||||
|
||||
|
||||
|
||||
#endif
|
35
src/ota.cpp
35
src/ota.cpp
@@ -1,6 +1,10 @@
|
||||
#include <Arduino.h>
|
||||
#include <website.h>
|
||||
#include <commonFS.h>
|
||||
#include "scale.h"
|
||||
#include "bambu.h"
|
||||
#include "nfc.h"
|
||||
|
||||
|
||||
// Globale Variablen für Config Backups hinzufügen
|
||||
String bambuCredentialsBackup;
|
||||
@@ -151,6 +155,25 @@ void handleUpdate(AsyncWebServer &server) {
|
||||
|
||||
updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
|
||||
size_t index, uint8_t *data, size_t len, bool final) {
|
||||
|
||||
// Disable all Tasks
|
||||
if (BambuMqttTask != NULL)
|
||||
{
|
||||
Serial.println("Delete BambuMqttTask");
|
||||
vTaskDelete(BambuMqttTask);
|
||||
BambuMqttTask = NULL;
|
||||
}
|
||||
if (ScaleTask) {
|
||||
Serial.println("Delete ScaleTask");
|
||||
vTaskDelete(ScaleTask);
|
||||
ScaleTask = NULL;
|
||||
}
|
||||
if (RfidReaderTask) {
|
||||
Serial.println("Delete RfidReaderTask");
|
||||
vTaskDelete(RfidReaderTask);
|
||||
RfidReaderTask = NULL;
|
||||
}
|
||||
|
||||
if (!index) {
|
||||
updateTotalSize = request->contentLength();
|
||||
updateWritten = 0;
|
||||
@@ -159,9 +182,9 @@ void handleUpdate(AsyncWebServer &server) {
|
||||
if (isSpiffsUpdate) {
|
||||
// Backup vor dem Update
|
||||
sendUpdateProgress(0, "backup", "Backing up configurations...");
|
||||
delay(200);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
backupJsonConfigs();
|
||||
delay(200);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
|
||||
if (!partition || !Update.begin(partition->size, U_SPIFFS)) {
|
||||
@@ -169,14 +192,14 @@ void handleUpdate(AsyncWebServer &server) {
|
||||
return;
|
||||
}
|
||||
sendUpdateProgress(5, "starting", "Starting SPIFFS update...");
|
||||
delay(200);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
if (!Update.begin(updateTotalSize)) {
|
||||
request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
|
||||
return;
|
||||
}
|
||||
sendUpdateProgress(0, "starting", "Starting firmware update...");
|
||||
delay(200);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,8 +224,8 @@ void handleUpdate(AsyncWebServer &server) {
|
||||
static int lastProgress = -1;
|
||||
if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
|
||||
sendUpdateProgress(currentProgress, "uploading");
|
||||
oledShowMessage("Update: " + String(currentProgress) + "%");
|
||||
delay(50);
|
||||
oledShowProgressBar(currentProgress, 100, "Update", "Download");
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
lastProgress = currentProgress;
|
||||
}
|
||||
}
|
||||
|
276
src/scale.cpp
276
src/scale.cpp
@@ -13,19 +13,135 @@ TaskHandle_t ScaleTask;
|
||||
|
||||
int16_t weight = 0;
|
||||
|
||||
// Weight stabilization variables
|
||||
#define MOVING_AVERAGE_SIZE 8 // Reduced from 20 to 8 for faster response
|
||||
#define LOW_PASS_ALPHA 0.3f // Increased from 0.15 to 0.3 for faster tracking
|
||||
#define DISPLAY_THRESHOLD 0.3f // Reduced from 0.5 to 0.3g for more responsive display
|
||||
#define API_THRESHOLD 1.5f // Reduced from 2.0 to 1.5g for faster API actions
|
||||
#define MEASUREMENT_INTERVAL_MS 30 // Reduced from 50ms to 30ms for faster updates
|
||||
|
||||
float weightBuffer[MOVING_AVERAGE_SIZE];
|
||||
uint8_t bufferIndex = 0;
|
||||
bool bufferFilled = false;
|
||||
float filteredWeight = 0.0f;
|
||||
int16_t lastDisplayedWeight = 0;
|
||||
int16_t lastStableWeight = 0; // For API/action triggering
|
||||
unsigned long lastMeasurementTime = 0;
|
||||
|
||||
uint8_t weigthCouterToApi = 0;
|
||||
uint8_t scale_tare_counter = 0;
|
||||
bool scaleTareRequest = false;
|
||||
uint8_t pauseMainTask = 0;
|
||||
uint8_t scaleCalibrated = 1;
|
||||
bool scaleCalibrated;
|
||||
bool autoTare = true;
|
||||
bool scaleCalibrationActive = false;
|
||||
|
||||
Preferences preferences;
|
||||
const char* NVS_NAMESPACE = "scale";
|
||||
const char* NVS_KEY_CALIBRATION = "cal_value";
|
||||
// ##### Weight stabilization functions #####
|
||||
|
||||
/**
|
||||
* Reset weight filter buffer - call after tare or calibration
|
||||
*/
|
||||
void resetWeightFilter() {
|
||||
bufferIndex = 0;
|
||||
bufferFilled = false;
|
||||
filteredWeight = 0.0f;
|
||||
lastDisplayedWeight = 0;
|
||||
lastStableWeight = 0; // Reset stable weight for API actions
|
||||
|
||||
// Initialize buffer with zeros
|
||||
for (int i = 0; i < MOVING_AVERAGE_SIZE; i++) {
|
||||
weightBuffer[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate moving average from weight buffer
|
||||
*/
|
||||
float calculateMovingAverage() {
|
||||
float sum = 0.0f;
|
||||
int count = bufferFilled ? MOVING_AVERAGE_SIZE : bufferIndex;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
sum += weightBuffer[i];
|
||||
}
|
||||
|
||||
return (count > 0) ? sum / count : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply low-pass filter to smooth weight readings
|
||||
* Uses exponential smoothing: y_new = alpha * x_new + (1-alpha) * y_old
|
||||
*/
|
||||
float applyLowPassFilter(float newValue) {
|
||||
filteredWeight = LOW_PASS_ALPHA * newValue + (1.0f - LOW_PASS_ALPHA) * filteredWeight;
|
||||
return filteredWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process new weight reading with stabilization
|
||||
* Returns stabilized weight value
|
||||
*/
|
||||
int16_t processWeightReading(float rawWeight) {
|
||||
// Add to moving average buffer
|
||||
weightBuffer[bufferIndex] = rawWeight;
|
||||
bufferIndex = (bufferIndex + 1) % MOVING_AVERAGE_SIZE;
|
||||
|
||||
if (bufferIndex == 0) {
|
||||
bufferFilled = true;
|
||||
}
|
||||
|
||||
// Calculate moving average
|
||||
float avgWeight = calculateMovingAverage();
|
||||
|
||||
// Apply low-pass filter
|
||||
float smoothedWeight = applyLowPassFilter(avgWeight);
|
||||
|
||||
// Round to nearest gram
|
||||
int16_t newWeight = round(smoothedWeight);
|
||||
|
||||
// Update displayed weight if display threshold is reached
|
||||
if (abs(newWeight - lastDisplayedWeight) >= DISPLAY_THRESHOLD) {
|
||||
lastDisplayedWeight = newWeight;
|
||||
}
|
||||
|
||||
// Update global weight for API actions only if stable threshold is reached
|
||||
int16_t weightToReturn = weight; // Default: keep current weight
|
||||
|
||||
if (abs(newWeight - lastStableWeight) >= API_THRESHOLD) {
|
||||
lastStableWeight = newWeight;
|
||||
weightToReturn = newWeight;
|
||||
}
|
||||
|
||||
return weightToReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current filtered weight for display purposes
|
||||
* This returns the smoothed weight even if it hasn't triggered API actions
|
||||
*/
|
||||
int16_t getFilteredDisplayWeight() {
|
||||
return lastDisplayedWeight;
|
||||
}
|
||||
|
||||
// ##### Funktionen für Waage #####
|
||||
uint8_t setAutoTare(bool autoTareValue) {
|
||||
Serial.print("Set AutoTare to ");
|
||||
Serial.println(autoTareValue);
|
||||
autoTare = autoTareValue;
|
||||
|
||||
// Speichern mit NVS
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
|
||||
preferences.putBool(NVS_KEY_AUTOTARE, autoTare);
|
||||
preferences.end();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t tareScale() {
|
||||
Serial.println("Tare scale");
|
||||
scale.tare();
|
||||
resetWeightFilter(); // Reset stabilization filter after tare
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -35,31 +151,85 @@ void scale_loop(void * parameter) {
|
||||
Serial.println("Scale Loop started");
|
||||
Serial.println("++++++++++++++++++++++++++++++");
|
||||
|
||||
for(;;) {
|
||||
if (scale.is_ready())
|
||||
{
|
||||
// Waage nochmal Taren, wenn zu lange Abweichung
|
||||
if (scaleTareRequest == true)
|
||||
{
|
||||
Serial.println("Re-Tare scale");
|
||||
scale.tare();
|
||||
scaleTareRequest = false;
|
||||
}
|
||||
// Initialize weight filter
|
||||
resetWeightFilter();
|
||||
lastMeasurementTime = millis();
|
||||
|
||||
weight = round(scale.get_units());
|
||||
for(;;) {
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Only measure at defined intervals to reduce noise
|
||||
if (currentTime - lastMeasurementTime >= MEASUREMENT_INTERVAL_MS) {
|
||||
if (scale.is_ready())
|
||||
{
|
||||
// Waage automatisch Taren, wenn zu lange Abweichung
|
||||
if (autoTare && scale_tare_counter >= 5)
|
||||
{
|
||||
Serial.println("Auto Tare scale");
|
||||
scale.tare();
|
||||
resetWeightFilter(); // Reset filter after auto tare
|
||||
scale_tare_counter = 0;
|
||||
}
|
||||
|
||||
// Waage manuell Taren
|
||||
if (scaleTareRequest == true)
|
||||
{
|
||||
Serial.println("Re-Tare scale");
|
||||
oledShowMessage("TARE Scale");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
scale.tare();
|
||||
resetWeightFilter(); // Reset filter after manual tare
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
oledShowWeight(0);
|
||||
scaleTareRequest = false;
|
||||
}
|
||||
|
||||
// Get raw weight reading
|
||||
float rawWeight = scale.get_units();
|
||||
|
||||
// Process weight with stabilization
|
||||
int16_t stabilizedWeight = processWeightReading(rawWeight);
|
||||
|
||||
// Update global weight variable only if it changed significantly (for API actions)
|
||||
if (stabilizedWeight != weight) {
|
||||
weight = stabilizedWeight;
|
||||
}
|
||||
|
||||
// Debug output for monitoring (can be removed in production)
|
||||
static unsigned long lastDebugTime = 0;
|
||||
if (currentTime - lastDebugTime > 2000) { // Print every 2 seconds
|
||||
lastDebugTime = currentTime;
|
||||
}
|
||||
|
||||
lastMeasurementTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
vTaskDelay(pdMS_TO_TICKS(10)); // Shorter delay for more responsive loop
|
||||
}
|
||||
}
|
||||
|
||||
void start_scale() {
|
||||
void start_scale(bool touchSensorConnected) {
|
||||
Serial.println("Prüfe Calibration Value");
|
||||
float calibrationValue;
|
||||
|
||||
// NVS lesen
|
||||
preferences.begin(NVS_NAMESPACE, true); // true = readonly
|
||||
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
|
||||
if(preferences.isKey(NVS_KEY_CALIBRATION)){
|
||||
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION);
|
||||
scaleCalibrated = true;
|
||||
}else{
|
||||
calibrationValue = SCALE_DEFAULT_CALIBRATION_VALUE;
|
||||
scaleCalibrated = false;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
Serial.print("Read Scale Calibration Value ");
|
||||
@@ -67,34 +237,29 @@ void start_scale() {
|
||||
|
||||
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
|
||||
|
||||
if (isnan(calibrationValue) || calibrationValue < 1) {
|
||||
calibrationValue = defaultScaleCalibrationValue;
|
||||
scaleCalibrated = 0;
|
||||
|
||||
oledShowMessage("Scale not calibrated!");
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
}
|
||||
|
||||
oledShowMessage("Scale Tare Please remove all");
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
|
||||
for (uint16_t i = 0; i < 3000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
if (scale.wait_ready_timeout(1000))
|
||||
{
|
||||
scale.set_scale(calibrationValue); // this value is obtained by calibrating the scale with known weights; see the README for details
|
||||
scale.tare();
|
||||
while(!scale.is_ready()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
}
|
||||
|
||||
scale.set_scale(calibrationValue);
|
||||
//vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
|
||||
// Initialize weight stabilization filter
|
||||
resetWeightFilter();
|
||||
|
||||
// Display Gewicht
|
||||
oledShowWeight(0);
|
||||
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
scale.tare();
|
||||
|
||||
Serial.println("starte Scale Task");
|
||||
BaseType_t result = xTaskCreatePinnedToCore(
|
||||
scale_loop, /* Function to implement the task */
|
||||
@@ -116,6 +281,8 @@ uint8_t calibrate_scale() {
|
||||
uint8_t returnState = 0;
|
||||
float newCalibrationValue;
|
||||
|
||||
scaleCalibrationActive = true;
|
||||
|
||||
vTaskSuspend(RfidReaderTask);
|
||||
vTaskSuspend(ScaleTask);
|
||||
|
||||
@@ -126,7 +293,7 @@ uint8_t calibrate_scale() {
|
||||
{
|
||||
|
||||
scale.set_scale();
|
||||
oledShowMessage("Step 1 empty Scale");
|
||||
oledShowProgressBar(0, 3, "Scale Cal.", "Empty Scale");
|
||||
|
||||
for (uint16_t i = 0; i < 5000; i++) {
|
||||
yield();
|
||||
@@ -138,7 +305,7 @@ uint8_t calibrate_scale() {
|
||||
Serial.println("Tare done...");
|
||||
Serial.print("Place a known weight on the scale...");
|
||||
|
||||
oledShowMessage("Step 2 Place the weight");
|
||||
oledShowProgressBar(1, 3, "Scale Cal.", "Place the weight");
|
||||
|
||||
for (uint16_t i = 0; i < 5000; i++) {
|
||||
yield();
|
||||
@@ -158,30 +325,30 @@ uint8_t calibrate_scale() {
|
||||
Serial.println(newCalibrationValue);
|
||||
|
||||
// Speichern mit NVS
|
||||
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
|
||||
preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue);
|
||||
preferences.end();
|
||||
|
||||
// Verifizieren
|
||||
preferences.begin(NVS_NAMESPACE, true);
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, true);
|
||||
float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0);
|
||||
preferences.end();
|
||||
|
||||
Serial.print("Verified stored value: ");
|
||||
Serial.println(verifyValue);
|
||||
|
||||
Serial.println("End calibration, remove weight");
|
||||
|
||||
oledShowMessage("Remove weight");
|
||||
oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight");
|
||||
|
||||
scale.set_scale(newCalibrationValue);
|
||||
resetWeightFilter(); // Reset filter after calibration
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
oledShowMessage("Scale calibrated");
|
||||
oledShowProgressBar(3, 3, "Scale Cal.", "Completed");
|
||||
|
||||
// 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();
|
||||
@@ -193,23 +360,21 @@ uint8_t calibrate_scale() {
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
scaleCalibrated = true;
|
||||
returnState = 1;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
{
|
||||
Serial.println("Calibration value is invalid. Please recalibrate.");
|
||||
Serial.println("Calibration value is invalid. Please recalibrate.");
|
||||
|
||||
oledShowMessage("Calibration ERROR Try again");
|
||||
oledShowProgressBar(3, 3, "Failure", "Calibration error");
|
||||
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
returnState = 0;
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
returnState = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -230,6 +395,7 @@ uint8_t calibrate_scale() {
|
||||
vTaskResume(ScaleTask);
|
||||
pauseBambuMqttTask = false;
|
||||
pauseMainTask = 0;
|
||||
scaleCalibrationActive = false;
|
||||
|
||||
return returnState;
|
||||
}
|
||||
|
16
src/scale.h
16
src/scale.h
@@ -4,17 +4,27 @@
|
||||
#include <Arduino.h>
|
||||
#include "HX711.h"
|
||||
|
||||
|
||||
uint8_t start_scale();
|
||||
uint8_t setAutoTare(bool autoTareValue);
|
||||
uint8_t start_scale(bool touchSensorConnected);
|
||||
uint8_t calibrate_scale();
|
||||
uint8_t tareScale();
|
||||
|
||||
// Weight stabilization functions
|
||||
void resetWeightFilter();
|
||||
float calculateMovingAverage();
|
||||
float applyLowPassFilter(float newValue);
|
||||
int16_t processWeightReading(float rawWeight);
|
||||
int16_t getFilteredDisplayWeight();
|
||||
|
||||
extern HX711 scale;
|
||||
extern int16_t weight;
|
||||
extern uint8_t weigthCouterToApi;
|
||||
extern uint8_t scale_tare_counter;
|
||||
extern uint8_t scaleTareRequest;
|
||||
extern uint8_t pauseMainTask;
|
||||
extern uint8_t scaleCalibrated;
|
||||
extern bool scaleCalibrated;
|
||||
extern bool autoTare;
|
||||
extern bool scaleCalibrationActive;
|
||||
|
||||
extern TaskHandle_t ScaleTask;
|
||||
|
||||
|
134
src/website.cpp
134
src/website.cpp
@@ -10,6 +10,9 @@
|
||||
#include <Update.h>
|
||||
#include "display.h"
|
||||
#include "ota.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#ifndef VERSION
|
||||
#define VERSION "1.1.0"
|
||||
@@ -22,17 +25,22 @@ AsyncWebServer server(webserverPort);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
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) {
|
||||
HEAP_DEBUG_MESSAGE("onWsEvent begin");
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
Serial.println("Neuer Client verbunden!");
|
||||
// Sende die AMS-Daten an den neuen Client
|
||||
if (!bambuDisabled) sendAmsData(client);
|
||||
sendNfcData(client);
|
||||
sendNfcData();
|
||||
foundNfcTag(client, 0);
|
||||
sendWriteResult(client, 3);
|
||||
|
||||
// Clean up dead connections
|
||||
(*server).cleanupClients();
|
||||
Serial.println("Currently connected number of clients: " + String((*server).getClients().size()));
|
||||
} else if (type == WS_EVT_DISCONNECT) {
|
||||
Serial.println("Client getrennt.");
|
||||
} else if (type == WS_EVT_ERROR) {
|
||||
@@ -40,9 +48,15 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
||||
} else if (type == WS_EVT_PONG) {
|
||||
Serial.printf("WebSocket Client #%u pong\n", client->id());
|
||||
} else if (type == WS_EVT_DATA) {
|
||||
String message = String((char*)data);
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, message);
|
||||
DeserializationError error = deserializeJson(doc, (char*)data, len);
|
||||
//String message = String((char*)data);
|
||||
//deserializeJson(doc, message);
|
||||
|
||||
if (error) {
|
||||
Serial.println("JSON deserialization failed: " + String(error.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc["type"] == "heartbeat") {
|
||||
// Sende Heartbeat-Antwort
|
||||
@@ -50,7 +64,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
||||
"\"type\":\"heartbeat\","
|
||||
"\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + ","
|
||||
"\"bambu_connected\":" + String(bambu_connected) + ","
|
||||
"\"spoolman_connected\":" + String(spoolman_connected) + ""
|
||||
"\"spoolman_connected\":" + String(spoolmanConnected) + ""
|
||||
"}");
|
||||
}
|
||||
|
||||
@@ -59,7 +73,8 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
||||
// Versuche NFC-Daten zu schreiben
|
||||
String payloadString;
|
||||
serializeJson(doc["payload"], payloadString);
|
||||
startWriteJsonToTag(payloadString.c_str());
|
||||
|
||||
startWriteJsonToTag((doc["tagType"] == "spool") ? true : false, payloadString.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +88,10 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
||||
success = calibrate_scale();
|
||||
}
|
||||
|
||||
if (doc["payload"] == "setAutoTare") {
|
||||
success = setAutoTare(doc["enabled"].as<bool>());
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}");
|
||||
} else {
|
||||
@@ -107,7 +126,9 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
|
||||
else {
|
||||
Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>());
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
HEAP_DEBUG_MESSAGE("onWsEvent end");
|
||||
}
|
||||
|
||||
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
|
||||
@@ -134,39 +155,36 @@ void sendWriteResult(AsyncWebSocketClient *client, uint8_t success) {
|
||||
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) {
|
||||
if (success == lastSuccess) return;
|
||||
ws.textAll("{\"type\":\"nfcTag\", \"payload\":{\"found\": " + String(success) + "}}");
|
||||
sendNfcData(nullptr);
|
||||
sendNfcData();
|
||||
lastSuccess = success;
|
||||
}
|
||||
|
||||
void sendNfcData(AsyncWebSocketClient *client) {
|
||||
if (lastHasReadRfidTag == hasReadRfidTag) return;
|
||||
if (hasReadRfidTag == 0) {
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}");
|
||||
void sendNfcData() {
|
||||
if (lastnfcReaderState == nfcReaderState) return;
|
||||
// TBD: Why is there no status for reading the tag?
|
||||
switch(nfcReaderState){
|
||||
case NFC_IDLE:
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}");
|
||||
break;
|
||||
case NFC_READ_SUCCESS:
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}");
|
||||
break;
|
||||
case NFC_READ_ERROR:
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}");
|
||||
break;
|
||||
case NFC_WRITING:
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}");
|
||||
break;
|
||||
case NFC_WRITE_SUCCESS:
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}");
|
||||
break;
|
||||
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\"}}");
|
||||
}
|
||||
else if (hasReadRfidTag == 1) {
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}");
|
||||
}
|
||||
else if (hasReadRfidTag == 2)
|
||||
{
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}");
|
||||
}
|
||||
else if (hasReadRfidTag == 3)
|
||||
{
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}");
|
||||
}
|
||||
else if (hasReadRfidTag == 4)
|
||||
{
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Error writing to Tag\"}}");
|
||||
}
|
||||
else if (hasReadRfidTag == 5)
|
||||
{
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Something went wrong\"}}");
|
||||
}
|
||||
lastHasReadRfidTag = hasReadRfidTag;
|
||||
lastnfcReaderState = nfcReaderState;
|
||||
}
|
||||
|
||||
void sendAmsData(AsyncWebSocketClient *client) {
|
||||
@@ -176,6 +194,7 @@ void sendAmsData(AsyncWebSocketClient *client) {
|
||||
}
|
||||
|
||||
void setupWebserver(AsyncWebServer &server) {
|
||||
oledShowProgressBar(2, 7, DISPLAY_BOOT_TEXT, "Webserver init");
|
||||
// Deaktiviere alle Debug-Ausgaben
|
||||
Serial.setDebugOutput(false);
|
||||
|
||||
@@ -192,6 +211,9 @@ void setupWebserver(AsyncWebServer &server) {
|
||||
Serial.print("Geladene Spoolman-URL: ");
|
||||
Serial.println(spoolmanUrl);
|
||||
|
||||
// Load Bamb credentials:
|
||||
loadBambuCredentials();
|
||||
|
||||
// Route für about
|
||||
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
Serial.println("Anfrage für /about erhalten");
|
||||
@@ -204,10 +226,14 @@ void setupWebserver(AsyncWebServer &server) {
|
||||
// Route für Waage
|
||||
server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
Serial.println("Anfrage für /waage erhalten");
|
||||
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html");
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||
request->send(response);
|
||||
//AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html");
|
||||
//response->addHeader("Content-Encoding", "gzip");
|
||||
//response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||
|
||||
String html = loadHtmlWithHeader("/waage.html");
|
||||
html.replace("{{autoTare}}", (autoTare) ? "checked" : "");
|
||||
|
||||
request->send(200, "text/html", html);
|
||||
});
|
||||
|
||||
// Route für RFID
|
||||
@@ -247,31 +273,11 @@ void setupWebserver(AsyncWebServer &server) {
|
||||
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
|
||||
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
|
||||
|
||||
JsonDocument doc;
|
||||
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>())
|
||||
{
|
||||
String bambuIp = doc["bambu_ip"].as<String>();
|
||||
String bambuSerial = doc["bambu_serialnr"].as<String>();
|
||||
String bambuCode = doc["bambu_accesscode"].as<String>();
|
||||
autoSendToBambu = doc["autoSendToBambu"].as<bool>();
|
||||
bambuIp.trim();
|
||||
bambuSerial.trim();
|
||||
bambuCode.trim();
|
||||
|
||||
html.replace("{{bambuIp}}", bambuIp ? bambuIp : "");
|
||||
html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : "");
|
||||
html.replace("{{bambuCode}}", bambuCode ? bambuCode : "");
|
||||
html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : "");
|
||||
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
|
||||
}
|
||||
else
|
||||
{
|
||||
html.replace("{{bambuIp}}", "");
|
||||
html.replace("{{bambuSerial}}", "");
|
||||
html.replace("{{bambuCode}}", "");
|
||||
html.replace("{{autoSendToBambu}}", "");
|
||||
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
|
||||
}
|
||||
html.replace("{{bambuIp}}", bambuCredentials.ip);
|
||||
html.replace("{{bambuSerial}}", bambuCredentials.serial);
|
||||
html.replace("{{bambuCode}}", bambuCredentials.accesscode ? bambuCredentials.accesscode : "");
|
||||
html.replace("{{autoSendToBambu}}", bambuCredentials.autosend_enable ? "checked" : "");
|
||||
html.replace("{{autoSendTime}}", (bambuCredentials.autosend_time != 0) ? String(bambuCredentials.autosend_time) : String(BAMBU_DEFAULT_AUTOSEND_TIME));
|
||||
|
||||
request->send(200, "text/html", html);
|
||||
});
|
||||
|
@@ -24,7 +24,7 @@ void setupWebserver(AsyncWebServer &server);
|
||||
|
||||
// WebSocket-Funktionen
|
||||
void sendAmsData(AsyncWebSocketClient *client);
|
||||
void sendNfcData(AsyncWebSocketClient *client);
|
||||
void sendNfcData();
|
||||
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success);
|
||||
void sendWriteResult(AsyncWebSocketClient *client, uint8_t success);
|
||||
|
||||
|
@@ -61,8 +61,7 @@ void initWiFi() {
|
||||
wm.setWiFiAutoReconnect(true);
|
||||
wm.setConnectTimeout(10);
|
||||
|
||||
oledShowTopRow();
|
||||
oledShowMessage("WiFi Setup");
|
||||
oledShowProgressBar(1, 7, DISPLAY_BOOT_TEXT, "WiFi init");
|
||||
|
||||
//bool res = wm.autoConnect("FilaMan"); // anonymous ap
|
||||
if(!wm.autoConnect("FilaMan")) {
|
||||
@@ -80,9 +79,6 @@ void initWiFi() {
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
oledShowTopRow();
|
||||
display.display();
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
|
||||
// mDNS
|
||||
startMDNS();
|
||||
|
Reference in New Issue
Block a user