Compare commits
167 Commits
v1.4.0
...
recyclingf
Author | SHA1 | Date | |
---|---|---|---|
3e04f385cb | |||
2eac7bbd1d | |||
a156cac18e | |||
|
09f4c43f89 | ||
|
ec0e544f30 | ||
|
b6d82c8afe | ||
|
97a1368747 | ||
|
85a9bcf8bd | ||
|
c450df59aa | ||
|
722ef421cb | ||
b0cd731c5a | |||
f022bee578 | |||
3286b64836 | |||
739fe7e764 | |||
5f8953a19d | |||
c919eeb848 | |||
43177c670e | |||
1b50694f5f | |||
48edde8557 | |||
|
cb5d8ac10a | ||
|
bf48c6d4e1 | ||
|
5d2d5e9ee1 | ||
|
7e76612bb4 | ||
|
f038020042 | ||
|
8343fe887b | ||
3bb6c1caf5 | |||
37df07f102 | |||
8484c1310b | |||
|
fd7b4c25b3 | ||
d490b116b9 | |||
5bc6192b6f | |||
2202d9a1aa | |||
7dbca0ab87 | |||
24b3521f83 | |||
6c9f290bac | |||
|
eab937d6ca | ||
|
27ef8399e4 | ||
2920159f32 | |||
2e19bccfa9 | |||
859e89431e | |||
6dc26ca51f | |||
0becae7ed6 | |||
3d31833f50 | |||
599cc47443 | |||
b1f7923770 | |||
c78c20979d | |||
e79c522e46 | |||
cf8cce72a5 | |||
0b356609d1 | |||
01f1e123ac | |||
012f91851e | |||
9ed3c70c01 | |||
e23f3a2151 | |||
f73306f0b9 | |||
a450d4bd1a | |||
d48d994c00 | |||
32bb85f897 | |||
e9d32ee060 | |||
aba28422bd | |||
4a55620d39 | |||
7b18266534 | |||
d81acb2b61 | |||
8c7fc159d3 | |||
476d3e82e2 | |||
3c294a135f | |||
bb751b6289 | |||
7fd01bd1b9 | |||
fad84e12c8 | |||
696efc4d79 | |||
29868e7101 | |||
823db6157c | |||
458cc4eaf2 | |||
83d14b32d1 | |||
2bf7c9fb7d | |||
|
ac8adca84d | ||
|
c701149c64 | ||
07a919b6ba | |||
8618b90e33 | |||
57723b5354 | |||
d2be752175 | |||
97a050ace8 | |||
367e692c74 | |||
926a21249b | |||
2635c19667 | |||
|
6cc4efca0a | ||
1484a6b0da | |||
b5f0472af4 | |||
95c1bc823c | |||
491ba7f526 | |||
56d7d8596c | |||
1044e91a0a | |||
e459b53472 | |||
024056cb7d | |||
e040a736b0 | |||
72b6b349c6 | |||
190e952ec4 | |||
89620a7f00 | |||
536950eeb3 | |||
|
fe4d2d7479 | ||
43719aac41 | |||
16d0079f7a | |||
48b9bf7076 | |||
b6bd4cb9ad | |||
e89bb1d547 | |||
f25789d703 | |||
|
65d8cd675f | ||
9dfe75ffa2 | |||
68cdd8ab40 | |||
1069781931 | |||
eada54eff2 | |||
48301ade36 | |||
76e0b20393 | |||
a765b39896 | |||
d68f6c4a89 | |||
1702e2396e | |||
af23b07df1 | |||
dd7ba3bf5d | |||
a818dcd3c0 | |||
b5279b167a | |||
a09fd4fda4 | |||
e4fe08f54c | |||
3eac0e5ac4 | |||
24d91693d9 | |||
94c26590c8 | |||
4559bae066 | |||
cdb2d16cf9 | |||
cd71949c82 | |||
6cd280389d | |||
|
daf27820b1 | ||
|
dd7fbe1119 | ||
|
dc2ddb47eb | ||
|
6bb8f565e6 | ||
|
ec60ca88f1 | ||
|
17664acf9e | ||
|
18f7454a76 | ||
|
e7b5917888 | ||
|
5c57968ba9 | ||
|
795c926c1f | ||
|
8735a9740c | ||
|
02d0adc6bf | ||
|
24067666ed | ||
|
9264333eda | ||
|
66216d57ae | ||
|
5100a669b0 | ||
|
4ad89b68a7 | ||
|
758acaff9f | ||
|
fed96b9c58 | ||
|
2d072ee09a | ||
|
b55b6e3fd5 | ||
|
238b928236 | ||
|
24ce0ca6df | ||
|
3cf934b920 | ||
|
f68ea3edb0 | ||
|
16321c9461 | ||
|
f9530f6d9a | ||
83f2f0834d | |||
6632aa8f95 | |||
8a558c3121 | |||
|
d434fde92e | ||
5afb60df32 | |||
3394e6eb01 | |||
3818c2c059 | |||
0afc543b5f | |||
adee46e3fc | |||
1db74867e6 | |||
0f24a63d32 | |||
3640809502 |
41
.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
.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
|
||||
html/bambu_credentials.json
|
||||
html/spoolman_url.json
|
||||
_local/*
|
||||
website/*
|
||||
release.sh
|
||||
.github/copilot-instructions.md
|
||||
data
|
274
CHANGELOG.md
@@ -1,5 +1,279 @@
|
||||
# Changelog
|
||||
|
||||
## [1.5.7] - 2025-07-28
|
||||
### Changed
|
||||
- update platformio.ini for version v1.5.7
|
||||
- clean up unused variables and improve .gitignore entries
|
||||
|
||||
|
||||
## [1.5.6] - 2025-07-28
|
||||
### Added
|
||||
- Adds ENABLE_HEAP_DEBUGGING define as comment to the build flags
|
||||
- Adds data directory and further .vscode files to to .gitignore
|
||||
- Introduces new heap debugging feature and fixes some memory leaks in website feature
|
||||
|
||||
### Changed
|
||||
- update webpages for version v1.5.6
|
||||
- update platformio.ini for version v1.5.6
|
||||
- Merge pull request #42 from janecker/configuration_nvs_storage
|
||||
- Merge branch 'main' into configuration_nvs_storage
|
||||
- Changes configuration storage of spoolman and bambu values
|
||||
|
||||
### Fixed
|
||||
- Merge pull request #41 from janecker/memory_leak_fixes
|
||||
- Fixes compiler warnings in nfc
|
||||
- Memory leak fixes in api and nfc, location tag fix
|
||||
|
||||
|
||||
## [1.5.5] - 2025-07-22
|
||||
### Added
|
||||
- Fixes some issues with the new location tags
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.5.5
|
||||
|
||||
### Fixed
|
||||
- Merge pull request #40 from janecker/location_bambu_fix
|
||||
|
||||
|
||||
## [1.5.4] - 2025-07-22
|
||||
### Added
|
||||
- Adds new feature to write and read location tags
|
||||
- Adds slight debouncing to the scale loop weight logic
|
||||
- add loadcell desc.
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.5.4
|
||||
- Merge branch 'main' of github.com:ManuelW77/Filaman
|
||||
- Merge pull request #39 from janecker/location_tags
|
||||
- Merge pull request #38 from janecker/scale_debouncing
|
||||
|
||||
### Fixed
|
||||
- uncomment monitor_port configuration in platformio.ini
|
||||
|
||||
|
||||
## [1.5.3] - 2025-04-25
|
||||
### Changed
|
||||
- update platformio.ini for version v1.5.3
|
||||
- Affiliate Links
|
||||
|
||||
### Fixed
|
||||
- update spool weight conditionally based on NFC ID
|
||||
|
||||
|
||||
## [1.5.2] - 2025-04-23
|
||||
### Added
|
||||
- implement multi-color filament display and styles for dropdown options
|
||||
- add remaining weight logging for PUT requests and improve error reporting in sendToApi function
|
||||
- add remaining weight logging and display after successful spool update
|
||||
- add weight field to update payload in updateSpoolTagId function
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.5.2
|
||||
|
||||
### Fixed
|
||||
- update weight field in update payload to only include values greater than 10
|
||||
- increase stack size for sendToApi task to improve stability
|
||||
- adjust tare weight tolerance to ignore deviations of 2g
|
||||
- improve weight stability check before sending to API
|
||||
|
||||
|
||||
## [1.5.1] - 2025-03-30
|
||||
### Changed
|
||||
- update version to 1.5.1 and improve OTA update handling with task management
|
||||
|
||||
|
||||
## [1.4.14] - 2025-03-30
|
||||
### Added
|
||||
- add auto-tare functionality and update scale handling based on touch sensor connection
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.14
|
||||
|
||||
|
||||
## [1.4.13] - 2025-03-30
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.13
|
||||
|
||||
### Fixed
|
||||
- update touch sensor connection logic to correctly identify connection status
|
||||
|
||||
|
||||
## [1.4.12] - 2025-03-30
|
||||
### Added
|
||||
- add touch sensor connection check and update logic
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.12
|
||||
- update README files to clarify PN532 DIP switch settings
|
||||
|
||||
|
||||
## [1.4.11] - 2025-03-30
|
||||
### Added
|
||||
- Renamed states of NFC state machine and introduced new state machine for spoolman API
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.11
|
||||
- Merge branch 'main' of github.com:ManuelW77/Filaman
|
||||
- Merge pull request #31 from janecker/nfc_rework
|
||||
- Introducing enum for handling the NFC state to improve code readability
|
||||
|
||||
|
||||
## [1.4.10] - 2025-03-30
|
||||
### Added
|
||||
- add manual tare functionality for scale
|
||||
- add debounce handling for TTP223 touch sensor
|
||||
- add TTP223 touch sensor support and wiring configuration
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.10
|
||||
|
||||
### Fixed
|
||||
- update TTP223 pin configuration and adjust touch sensor logic
|
||||
|
||||
|
||||
## [1.4.9] - 2025-03-29
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.9
|
||||
|
||||
### Fixed
|
||||
- enhance HTTP method handling in sendToApi function
|
||||
|
||||
|
||||
## [1.4.8] - 2025-03-29
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.8
|
||||
- Merge pull request #30 from janecker/main
|
||||
- Merge branch 'testing' into main
|
||||
|
||||
### Fixed
|
||||
- improve HTTP client configuration and clear update documents after API calls
|
||||
- Fixes memory leak in HTTPClient by disabling connection reuse
|
||||
- update reload logic after removing and saving Bambu credentials for better cache handling
|
||||
|
||||
|
||||
## [1.4.7] - 2025-03-27
|
||||
### Added
|
||||
- add forced cache refresh after removing and saving Bambu credentials
|
||||
- add functionality to remove Bambu credentials and update API handling
|
||||
- add rfid_bambu.html and update bambu connection handling
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.7
|
||||
- Merge branch 'testing'
|
||||
- update remove button for Bambu credentials with red background
|
||||
- Merge pull request #28 from tugsi/main
|
||||
|
||||
### Fixed
|
||||
- handle Bambu connection state by introducing bambuDisabled flag
|
||||
- Fix rfid.js-Failure with X1-Series, if you wanna send a Spool to AMS: - Uncaught TypeError: Cannot read properties of undefined (reading 'replace') at handleSpoolIn (rfid.js:493:67) at HTMLButtonElement.onclick ((Index):1:1) handleSpoolIn @ rfid.js:493 onclick @ (Index):1
|
||||
|
||||
|
||||
## [1.4.6] - 2025-03-26
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.6
|
||||
|
||||
### Fixed
|
||||
- handle potential undefined value for tray_info_idx in handleSpoolIn function, by @tugsi
|
||||
|
||||
|
||||
## [1.4.5] - 2025-03-25
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.5
|
||||
- Merge branch 'testing'
|
||||
- remove unused request_topic subscription and reduce MQTT task stack size
|
||||
- Merge pull request #26 from tugsi/main
|
||||
- rename report_topic to topic and update MQTT subscription logic, switched publish topic to request
|
||||
|
||||
### Fixed
|
||||
- increase MQTT buffer size and adjust task stack size
|
||||
- Fix BufferSize for larger JSONs from X-Series
|
||||
|
||||
|
||||
## [1.4.4] - 2025-03-23
|
||||
### Added
|
||||
- add error handling for missing vendor IDs in filament data
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.4
|
||||
|
||||
### Fixed
|
||||
- adjust weight threshold for tare check to allow negative values
|
||||
|
||||
|
||||
## [1.4.3] - 2025-03-23
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.3
|
||||
|
||||
|
||||
## [1.4.2] - 2025-03-23
|
||||
### Added
|
||||
- add WiFi connection check and restart Bambu if not connected
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.2
|
||||
- increase stack size for BambuMqtt task
|
||||
- update Discord Link
|
||||
- update Discord Link
|
||||
- remove commented-out subscription topic in MQTT setup
|
||||
|
||||
### Fixed
|
||||
- use unique client ID for MQTT connection to avoid conflicts
|
||||
- reload page after firmware update completion
|
||||
- increase WiFi connection timeout from 5 to 10 seconds
|
||||
- ensure valid URL format and remove trailing slash in setupWebserver
|
||||
|
||||
|
||||
## [1.4.1] - 2025-03-10
|
||||
### Added
|
||||
- added new .step, now with correct individual parts
|
||||
- added changelog
|
||||
- Add files via upload
|
||||
- added .stp files of modifications
|
||||
- added merged picture
|
||||
- added pictures of components bought from AliE
|
||||
- Add files via upload
|
||||
- added pictures for heat insert location
|
||||
- added pictures showing heat insert location
|
||||
- remove unnecessary delay in MQTT setup and add delay before restart
|
||||
- add new 3D print file for Filaman scale
|
||||
- added Discord Server
|
||||
|
||||
### Changed
|
||||
- update platformio.ini for version v1.4.1
|
||||
- refactor length calculation to convert total length to meters before formatting
|
||||
- Merge pull request #16 from spitzbirne32/main
|
||||
- improved housing to show display better
|
||||
- removed CAD, as they were all duplicates
|
||||
- typo in AliE link
|
||||
- Delete usermod/spitzbirne32/STL/README.md
|
||||
- Update README.md
|
||||
- moved pictures of parts into dedicated folders
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Delete usermod/spitzbirne32/STL/ScaleTop_Heatinsert_Location_usermod_spitzbirne32_.png
|
||||
- Delete usermod/spitzbirne32/STL/Housing_Heatinsert_Location_usermod_spitzbirne32_.png
|
||||
- created folders
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Create README.md
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Create README.md
|
||||
- Merge pull request #15 from ManuelW77/main
|
||||
- Merge pull request #14 from janecker/scale-calibration-rework
|
||||
- Reworks the scale calibration handling
|
||||
- remove redundant scale calibration checks and enhance task management
|
||||
- enhance AMS data handling and streamline spool auto-setting logic
|
||||
- adjust stack size and improve scale calibration logic
|
||||
- update labels and input types for better clarity and functionality
|
||||
- update documentation for clarity and accuracy
|
||||
|
||||
### Fixed
|
||||
- correct typo in console log for total length
|
||||
|
||||
|
||||
## [1.4.0] - 2025-03-01
|
||||
### Added
|
||||
- add support for Spoolman Octoprint Plugin in README files
|
||||
|
42
README.de.md
@@ -8,7 +8,8 @@ Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/
|
||||
|
||||
Weitere Bilder finden Sie im [img Ordner](/img/)
|
||||
oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
|
||||
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||
|
||||
@@ -53,20 +54,23 @@ Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaO
|
||||
|
||||
## 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.to/4iO6CO4)
|
||||
- **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 |
|
||||
@@ -76,14 +80,22 @@ Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaO
|
||||
| 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
|
||||
|
30
README.md
@@ -6,12 +6,14 @@ FilaMan is a filament management system for 3D printing. It uses ESP32 hardware
|
||||
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
|
||||
The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
More Images can be found in the [img Folder](/img/)
|
||||
or my website:[FilaMan Website](https://www.filaman.app)
|
||||
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
or my website: [FilaMan Website](https://www.filaman.app)
|
||||
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||
|
||||
@@ -56,17 +58,19 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
|
||||
|
||||
## 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.to/4iO6CO4)
|
||||
- **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
|
||||
@@ -80,14 +84,22 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
|
||||
| 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
|
||||
|
15297
_3D Print Files/FilaMan-Waage.step
Normal file
BIN
_3D Print Files/Filaman-Waage.f3z
Normal file
BIN
html/.DS_Store
vendored
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,17 +139,20 @@
|
||||
<p id="nfcInfo" class="nfc-status"></p>
|
||||
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rechte Spalte -->
|
||||
<div class="column">
|
||||
<div class="feature-box">
|
||||
<h2>Bambu AMS</h2>
|
||||
<div id="amsDataContainer">
|
||||
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
112
html/rfid.js
@@ -11,7 +11,7 @@ let reconnectTimer = null;
|
||||
// WebSocket Funktionen
|
||||
function startHeartbeat() {
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
|
||||
|
||||
heartbeatTimer = setInterval(() => {
|
||||
// Prüfe ob zu lange keine Antwort kam
|
||||
if (Date.now() - lastHeartbeatResponse > HEARTBEAT_TIMEOUT) {
|
||||
@@ -29,7 +29,7 @@ function startHeartbeat() {
|
||||
updateConnectionStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Sende Heartbeat
|
||||
socket.send(JSON.stringify({ type: 'heartbeat' }));
|
||||
@@ -83,7 +83,7 @@ function initWebSocket() {
|
||||
isConnected = false;
|
||||
updateConnectionStatus();
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
|
||||
|
||||
// Bei Fehler Verbindung schließen und neu aufbauen
|
||||
if (socket) {
|
||||
socket.close();
|
||||
@@ -109,7 +109,7 @@ function initWebSocket() {
|
||||
const bambuDot = document.getElementById('bambuDot');
|
||||
const spoolmanDot = document.getElementById('spoolmanDot');
|
||||
const ramStatus = document.getElementById('ramStatus');
|
||||
|
||||
|
||||
if (bambuDot) {
|
||||
bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline');
|
||||
// Add click handler only when offline
|
||||
@@ -208,27 +208,13 @@ document.addEventListener('spoolmanError', function(event) {
|
||||
showNotification(`Spoolman Error: ${event.detail.message}`, false);
|
||||
});
|
||||
|
||||
document.addEventListener('filamentSelected', function(event) {
|
||||
document.addEventListener('filamentSelected', function (event) {
|
||||
updateNfcInfo();
|
||||
// Zeige Spool-Buttons wenn ein Filament ausgewählt wurde
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
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");
|
||||
@@ -490,7 +476,7 @@ function handleSpoolIn(amsId, trayId) {
|
||||
nozzle_temp_max: parseInt(maxTemp),
|
||||
type: selectedSpool.filament.material,
|
||||
brand: selectedSpool.filament.vendor.name,
|
||||
tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(),
|
||||
tray_info_idx: selectedSpool.filament.extra.bambu_idx?.replace(/['"]+/g, '').trim() || '',
|
||||
cali_idx: "-1" // Default-Wert setzen
|
||||
}
|
||||
};
|
||||
@@ -569,7 +555,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 +571,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 || 'No Spoolman ID'}</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) {
|
||||
@@ -654,6 +660,32 @@ function writeNfcTag() {
|
||||
writeButton.textContent = "Writing";
|
||||
socket.send(JSON.stringify({
|
||||
type: 'writeNfcTag',
|
||||
tagType: 'spool',
|
||||
payload: nfcData
|
||||
}));
|
||||
} else {
|
||||
alert('Not connected to Server. Please check connection.');
|
||||
}
|
||||
}
|
||||
|
||||
function writeLocationNfcTag() {
|
||||
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 (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 {
|
||||
@@ -663,14 +695,30 @@ function writeNfcTag() {
|
||||
|
||||
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) {
|
||||
@@ -686,4 +734,4 @@ function showNotification(message, isSuccess) {
|
||||
notification.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
172
html/rfid_bambu.html
Normal file
@@ -0,0 +1,172 @@
|
||||
<!-- head --><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FilaMan - Filament Management Tool</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script>
|
||||
fetch('/api/version')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const versionSpan = document.querySelector('.version');
|
||||
if (versionSpan) {
|
||||
versionSpan.textContent = 'v' + data.version;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching version:', error));
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div style="display: flex; align-items: center; gap: 2rem;">
|
||||
<img src="/logo.png" alt="FilaMan Logo" class="logo">
|
||||
<div class="logo-text">
|
||||
<h1>FilaMan<span class="version"></span></h1>
|
||||
<h4>Filament Management Tool</h4>
|
||||
</div>
|
||||
</div>
|
||||
<nav style="display: flex; gap: 1rem;">
|
||||
<a href="/">Start</a>
|
||||
<a href="/waage">Scale</a>
|
||||
<a href="/spoolman">Spoolman/Bambu</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/upgrade">Upgrade</a>
|
||||
</nav>
|
||||
<div class="status-container">
|
||||
<div class="status-item">
|
||||
<span class="status-dot" id="bambuDot"></span>B
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot" id="spoolmanDot"></span>S
|
||||
</div>
|
||||
<div class="ram-status" id="ramStatus"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- head -->
|
||||
|
||||
<div class="connection-status hidden">
|
||||
<div class="spinner"></div>
|
||||
<span>Connection lost. Trying to reconnect...</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="three-column-layout">
|
||||
<!-- Linke Spalte -->
|
||||
<div class="column">
|
||||
<div class="feature-box">
|
||||
<div class="statistics-header">
|
||||
<h2>Statistics</h2>
|
||||
<button id="refreshSpoolman" class="refresh-button">
|
||||
<span>Refresh Spoolman</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="statistics-column">
|
||||
<h3>Spools</h3>
|
||||
<div class="spool-stat" style="display: flex; justify-content: center; align-items: center;">
|
||||
<span class="stat-label">total:</span>
|
||||
<span class="stat-value" id="totalSpools"></span>
|
||||
<div style="width: auto;"></div>
|
||||
<span class="stat-label">without Tag:</span>
|
||||
<span class="stat-value" id="spoolsWithoutTag"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="statistics-grid">
|
||||
<div class="statistics-column">
|
||||
<h3>Overview</h3>
|
||||
<ul class="statistics-list">
|
||||
<li>
|
||||
<span class="stat-label">Manufacturer:</span>
|
||||
<span class="stat-value" id="totalVendors"></span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="stat-label">Weight:</span>
|
||||
<span class="stat-value"><span id="totalWeight"></span></span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="stat-label">Length:</span>
|
||||
<span class="stat-value"><span id="totalLength"></span></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="statistics-column">
|
||||
<h3>Materials</h3>
|
||||
<ul class="statistics-list" id="materialsList">
|
||||
<!-- Wird dynamisch befüllt -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-box">
|
||||
<div class="nfc-header">
|
||||
<h2>NFC-Tag</h2>
|
||||
<span id="nfcStatusIndicator" class="status-circle"></span>
|
||||
</div>
|
||||
<div class="nfc-status-display"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mittlere Spalte -->
|
||||
<div class="column">
|
||||
<div class="feature-box">
|
||||
<h2>Spoolman Spools</h2>
|
||||
<label for="vendorSelect">Manufacturer:</label>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<select id="vendorSelect" class="styled-select">
|
||||
<option value="">Please choose...</option>
|
||||
</select>
|
||||
<label style="margin-left: 10px;">
|
||||
<input type="checkbox" id="onlyWithoutSmId" checked onchange="updateFilamentDropdown()">
|
||||
Only Spools without SM ID
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="filamentSection" class="feature-box hidden">
|
||||
<label>Spool / Filament:</label>
|
||||
<div class="custom-dropdown">
|
||||
<div class="dropdown-button" onclick="toggleFilamentDropdown()">
|
||||
<div class="selected-color" id="selected-color"></div>
|
||||
<span id="selected-filament">Please choose...</span>
|
||||
<span class="dropdown-arrow">▼</span>
|
||||
</div>
|
||||
<div class="dropdown-content" id="filament-dropdown-content">
|
||||
<!-- Optionen werden dynamisch hinzugefügt -->
|
||||
</div>
|
||||
</div>
|
||||
<p id="nfcInfo" class="nfc-status"></p>
|
||||
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
|
||||
</div>
|
||||
|
||||
<div 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 -->
|
||||
<div class="column">
|
||||
<div class="feature-box">
|
||||
<h2>Bambu AMS</h2>
|
||||
<div id="amsDataContainer">
|
||||
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="spoolman.js"></script>
|
||||
<script src="rfid.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -57,6 +57,31 @@
|
||||
toggleOctoFields();
|
||||
};
|
||||
|
||||
function removeBambuCredentials() {
|
||||
fetch('/api/bambu?remove=true')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('bambuIp').value = '';
|
||||
document.getElementById('bambuSerial').value = '';
|
||||
document.getElementById('bambuCode').value = '';
|
||||
document.getElementById('autoSend').checked = false;
|
||||
document.getElementById('autoSendTime').value = '';
|
||||
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials removed!';
|
||||
// Reload with forced cache refresh after short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload(true);
|
||||
window.location.href = '/';
|
||||
}, 1500);
|
||||
} else {
|
||||
document.getElementById('bambuStatusMessage').innerText = 'Error while removing Bambu Credentials.';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('bambuStatusMessage').innerText = 'Error while removing: ' + error.message;
|
||||
});
|
||||
}
|
||||
|
||||
function checkSpoolmanInstance() {
|
||||
const url = document.getElementById('spoolmanUrl').value;
|
||||
const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
|
||||
@@ -89,6 +114,11 @@
|
||||
.then(data => {
|
||||
if (data.healthy) {
|
||||
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!';
|
||||
// Reload with forced cache refresh after short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload(true);
|
||||
window.location.href = '/';
|
||||
}, 1500);
|
||||
} else {
|
||||
document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.';
|
||||
}
|
||||
@@ -116,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>
|
||||
@@ -139,29 +169,30 @@
|
||||
<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>
|
||||
<div class="input-group" style="display: flex; margin-bottom: 0;">
|
||||
<label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label>
|
||||
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait time in Seconds:</label>
|
||||
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait for Spool in Sec:</label>
|
||||
</div>
|
||||
<div class="input-group" style="display: flex;">
|
||||
<input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
|
||||
<input type="text" id="autoSendTime" placeholder="Time to wait for new Spool" value="{{autoSendTime}}" style="width: 100px;">
|
||||
<input type="number" min="60" id="autoSendTime" placeholder="Time to wait" value="{{autoSendTime}}" style="width: 100px;">
|
||||
</div>
|
||||
|
||||
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>
|
||||
<button style="margin: 0; background-color: red;" onclick="removeBambuCredentials()">Remove Credentials</button>
|
||||
<p id="bambuStatusMessage"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
143
html/spoolman.js
@@ -1,6 +1,7 @@
|
||||
// Globale Variablen
|
||||
let spoolmanUrl = '';
|
||||
let spoolsData = [];
|
||||
let locationData = [];
|
||||
|
||||
// Hilfsfunktionen für Datenmanipulation
|
||||
function processSpoolData(data) {
|
||||
@@ -86,10 +87,10 @@ function populateVendorDropdown(data, selectedSmId = null) {
|
||||
});
|
||||
|
||||
// Nach der Schleife: Formatierung der Gesamtlänge
|
||||
console.log("Total Lenght: ", totalLength);
|
||||
const formattedLength = totalLength > 1000
|
||||
? (totalLength / 1000).toFixed(2) + " km"
|
||||
: totalLength.toFixed(2) + " m";
|
||||
const lengthInM = totalLength / 1000; // erst in m umrechnen
|
||||
const formattedLength = lengthInM > 1000
|
||||
? (lengthInM / 1000).toFixed(2) + " km"
|
||||
: lengthInM.toFixed(2) + " m";
|
||||
|
||||
// Formatierung des Gesamtgewichts (von g zu kg zu t)
|
||||
const weightInKg = totalWeight / 1000; // erst in kg umrechnen
|
||||
@@ -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");
|
||||
@@ -147,6 +168,13 @@ function updateFilamentDropdown(selectedSmId = null) {
|
||||
|
||||
if (vendorId) {
|
||||
const filteredFilaments = spoolsData.filter(spool => {
|
||||
if (!spool?.filament?.vendor?.id) {
|
||||
console.log('Problem aufgetreten bei: ', spool?.filament?.vendor);
|
||||
console.log('Problematische Spulen:',
|
||||
spoolsData.filter(spool => !spool?.filament?.vendor?.id));
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasValidNfcId = spool.extra &&
|
||||
spool.extra.nfc_id &&
|
||||
spool.extra.nfc_id !== '""' &&
|
||||
@@ -162,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>
|
||||
`;
|
||||
|
||||
@@ -178,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");
|
||||
|
||||
@@ -209,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', {
|
||||
@@ -240,17 +328,24 @@ async function fetchSpoolData() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Exportiere Funktionen
|
||||
window.getSpoolData = () => spoolsData;
|
||||
window.reloadSpoolData = initSpoolman;
|
||||
window.populateVendorDropdown = populateVendorDropdown;
|
||||
window.updateFilamentDropdown = updateFilamentDropdown;
|
||||
window.toggleFilamentDropdown = () => {
|
||||
const content = document.getElementById("filament-dropdown-content");
|
||||
content.classList.toggle("show");
|
||||
};
|
||||
*/
|
||||
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', () => {
|
||||
@@ -260,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) {
|
||||
@@ -272,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')) {
|
||||
@@ -302,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
@@ -0,0 +1 @@
|
||||
{"url": "http://192.168.1.5:7912", "octoEnabled": true, "octoUrl": "http://192.168.1.17:5001", "octoToken": "O5zZ58mXRAyeGpVEj2ZZj-UPAPqJ2N7JgtD36mw1M4g"}
|
@@ -188,14 +188,18 @@ label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="submit"] {
|
||||
input[type="text"], input[type="submit"], input[type="number"] {
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
input[type="number"] {
|
||||
width: 108px !important;
|
||||
}
|
||||
|
||||
input[type="text"]:focus, input[type="number"]:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
@@ -755,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;
|
||||
@@ -923,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: "."; }
|
||||
@@ -955,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;
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@
|
||||
<div class="update-options">
|
||||
<div class="update-section">
|
||||
<h2>Firmware Update</h2>
|
||||
<p>Upload a new firmware file (filaman_*.bin)</p>
|
||||
<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>
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<div class="update-section">
|
||||
<h2>Webpage Update</h2>
|
||||
<p>Upload a new webpage file (webpage_*.bin)</p>
|
||||
<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>
|
||||
@@ -129,6 +129,7 @@
|
||||
if (data.status === 'success' || lastReceivedProgress >= 98) {
|
||||
clearTimeout(wsReconnectTimer);
|
||||
setTimeout(() => {
|
||||
window.location.reload(true);
|
||||
window.location.href = '/';
|
||||
}, 30000);
|
||||
}
|
||||
@@ -148,6 +149,7 @@
|
||||
status.style.display = 'block';
|
||||
clearTimeout(wsReconnectTimer);
|
||||
setTimeout(() => {
|
||||
window.location.reload(true);
|
||||
window.location.href = '/';
|
||||
}, 30000);
|
||||
} else {
|
||||
|
@@ -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>
|
||||
|
BIN
img/7-enable.png
Normal file
After Width: | Height: | Size: 52 KiB |
@@ -9,8 +9,8 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[common]
|
||||
version = "1.4.0"
|
||||
to_old_version = "1.4.0"
|
||||
version = "1.5.7"
|
||||
to_old_version = "1.5.0"
|
||||
|
||||
##
|
||||
[env:esp32dev]
|
||||
@@ -18,6 +18,7 @@ platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
#monitor_port = /dev/cu.usbmodem01
|
||||
|
||||
lib_deps =
|
||||
tzapu/WiFiManager @ ^2.0.17
|
||||
@@ -51,6 +52,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'))
|
||||
|
517
src/api.cpp
@@ -2,14 +2,24 @@
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "commonFS.h"
|
||||
#include <Preferences.h>
|
||||
#include "debug.h"
|
||||
|
||||
bool spoolman_connected = false;
|
||||
volatile spoolmanApiStateType spoolmanApiState = API_INIT;
|
||||
//bool spoolman_connected = false;
|
||||
String spoolmanUrl = "";
|
||||
bool octoEnabled = false;
|
||||
bool sendOctoUpdate = false;
|
||||
String octoUrl = "";
|
||||
String octoToken = "";
|
||||
uint16_t remainingWeight = 0;
|
||||
uint16_t createdVendorId = 0; // Store ID of newly created vendor
|
||||
uint16_t foundVendorId = 0; // Store ID of found vendor
|
||||
uint16_t foundFilamentId = 0; // Store ID of found filament
|
||||
bool spoolmanConnected = false;
|
||||
|
||||
struct SendToApiParams {
|
||||
SpoolmanApiRequestType requestType;
|
||||
String httpType;
|
||||
String spoolsUrl;
|
||||
String updatePayload;
|
||||
@@ -85,39 +95,159 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
|
||||
}
|
||||
|
||||
void sendToApi(void *parameter) {
|
||||
HEAP_DEBUG_MESSAGE("sendToApi begin");
|
||||
|
||||
spoolmanApiState = API_TRANSMITTING;
|
||||
SendToApiParams* params = (SendToApiParams*)parameter;
|
||||
|
||||
// Extrahiere die Werte
|
||||
SpoolmanApiRequestType requestType = params->requestType;
|
||||
String httpType = params->httpType;
|
||||
String spoolsUrl = params->spoolsUrl;
|
||||
String updatePayload = params->updatePayload;
|
||||
String octoToken = params->octoToken;
|
||||
|
||||
HTTPClient http;
|
||||
http.setReuse(false);
|
||||
|
||||
http.begin(spoolsUrl);
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
|
||||
|
||||
int httpCode = http.PUT(updatePayload);
|
||||
int httpCode;
|
||||
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
|
||||
if (httpType == "POST") httpCode = http.POST(updatePayload);
|
||||
else if (httpType == "POST") httpCode = http.POST(updatePayload);
|
||||
else if (httpType == "GET") httpCode = http.GET();
|
||||
else httpCode = http.PUT(updatePayload);
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
Serial.println("Spoolman erfolgreich aktualisiert");
|
||||
|
||||
// Restgewicht der Spule auslesen
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
if (error) {
|
||||
Serial.print("Fehler beim Parsen der JSON-Antwort: ");
|
||||
Serial.println(error.c_str());
|
||||
} else {
|
||||
switch(requestType){
|
||||
case API_REQUEST_SPOOL_WEIGHT_UPDATE:
|
||||
remainingWeight = doc["remaining_weight"].as<uint16_t>();
|
||||
Serial.print("Aktuelles Gewicht: ");
|
||||
Serial.println(remainingWeight);
|
||||
//oledShowMessage("Remaining: " + String(remaining_weight) + "g");
|
||||
if(!octoEnabled){
|
||||
// TBD: Do not use Strings...
|
||||
oledShowProgressBar(1, 1, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
|
||||
remainingWeight = 0;
|
||||
}else{
|
||||
// ocoto is enabled, trigger octo update
|
||||
sendOctoUpdate = true;
|
||||
}
|
||||
break;
|
||||
case API_REQUEST_SPOOL_LOCATION_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Loc. Tag", "Done!");
|
||||
break;
|
||||
case API_REQUEST_SPOOL_TAG_ID_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Write Tag", "Done!");
|
||||
break;
|
||||
case API_REQUEST_OCTO_SPOOL_UPDATE:
|
||||
// TBD: Do not use Strings...
|
||||
oledShowProgressBar(5, 5, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
|
||||
remainingWeight = 0;
|
||||
break;
|
||||
case API_REQUEST_VENDOR_CREATE:
|
||||
Serial.println("Vendor successfully created!");
|
||||
createdVendorId = doc["id"].as<uint16_t>();
|
||||
Serial.print("Created Vendor ID: ");
|
||||
Serial.println(createdVendorId);
|
||||
oledShowProgressBar(1, 1, "Vendor", "Created!");
|
||||
break;
|
||||
case API_REQUEST_VENDOR_CHECK:
|
||||
if (doc.isNull() || doc.size() == 0) {
|
||||
Serial.println("Vendor not found in response");
|
||||
foundVendorId = 0;
|
||||
} else {
|
||||
foundVendorId = doc[0]["id"].as<uint16_t>();
|
||||
Serial.print("Found Vendor ID: ");
|
||||
Serial.println(foundVendorId);
|
||||
}
|
||||
break;
|
||||
case API_REQUEST_FILAMENT_CHECK:
|
||||
if (doc.isNull() || doc.size() == 0) {
|
||||
Serial.println("Filament not found in response");
|
||||
foundFilamentId = 0;
|
||||
} else {
|
||||
foundFilamentId = doc[0]["id"].as<uint16_t>();
|
||||
Serial.print("Found Filament ID: ");
|
||||
Serial.println(foundFilamentId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
doc.clear();
|
||||
} else if (httpCode == HTTP_CODE_CREATED) {
|
||||
Serial.println("Spoolman erfolgreich erstellt");
|
||||
|
||||
// Parse response for created resources
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
if (error) {
|
||||
Serial.print("Fehler beim Parsen der JSON-Antwort: ");
|
||||
Serial.println(error.c_str());
|
||||
} else {
|
||||
switch(requestType){
|
||||
case API_REQUEST_VENDOR_CREATE:
|
||||
Serial.println("Vendor successfully created!");
|
||||
createdVendorId = doc["id"].as<uint16_t>();
|
||||
Serial.print("Created Vendor ID: ");
|
||||
Serial.println(createdVendorId);
|
||||
oledShowProgressBar(1, 1, "Vendor", "Created!");
|
||||
break;
|
||||
default:
|
||||
// Handle other create operations if needed
|
||||
break;
|
||||
}
|
||||
}
|
||||
doc.clear();
|
||||
} else {
|
||||
Serial.println("Fehler beim Senden an Spoolman!");
|
||||
oledShowMessage("Spoolman update failed");
|
||||
switch(requestType){
|
||||
case API_REQUEST_SPOOL_WEIGHT_UPDATE:
|
||||
case API_REQUEST_SPOOL_LOCATION_UPDATE:
|
||||
case API_REQUEST_SPOOL_TAG_ID_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Failure!", "Spoolman update");
|
||||
break;
|
||||
case API_REQUEST_OCTO_SPOOL_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Failure!", "Octoprint update");
|
||||
break;
|
||||
case API_REQUEST_BAMBU_UPDATE:
|
||||
oledShowProgressBar(1, 1, "Failure!", "Bambu update");
|
||||
break;
|
||||
case API_REQUEST_VENDOR_CREATE:
|
||||
oledShowProgressBar(1, 1, "Failure!", "Vendor create");
|
||||
break;
|
||||
}
|
||||
Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode));
|
||||
|
||||
// TBD: really required?
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
http.end();
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// Speicher freigeben
|
||||
delete params;
|
||||
HEAP_DEBUG_MESSAGE("sendToApi end");
|
||||
spoolmanApiState = API_IDLE;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
bool updateSpoolTagId(String uidString, const char* payload) {
|
||||
oledShowProgressBar(2, 3, "Write Tag", "Update Spoolman");
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
@@ -137,6 +267,8 @@ bool updateSpoolTagId(String uidString, const char* payload) {
|
||||
Serial.print("Update Spule mit URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
|
||||
doc.clear();
|
||||
|
||||
// Update Payload erstellen
|
||||
JsonDocument updateDoc;
|
||||
updateDoc["extra"]["nfc_id"] = "\""+uidString+"\"";
|
||||
@@ -146,11 +278,12 @@ bool updateSpoolTagId(String uidString, const char* payload) {
|
||||
Serial.print("Update Payload: ");
|
||||
Serial.println(updatePayload);
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return false;
|
||||
}
|
||||
params->requestType = API_REQUEST_SPOOL_TAG_ID_UPDATE;
|
||||
params->httpType = "PATCH";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = updatePayload;
|
||||
@@ -159,16 +292,24 @@ bool updateSpoolTagId(String uidString, const char* payload) {
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
4096, // Stackgröße in Bytes
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
|
||||
updateDoc.clear();
|
||||
|
||||
// Update Spool weight
|
||||
//TBD: how to handle this with spool and locatin tags? Also potential parallel access again
|
||||
//if (weight > 10) updateSpoolWeight(doc["sm_id"].as<String>(), weight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
|
||||
HEAP_DEBUG_MESSAGE("updateSpoolWeight begin");
|
||||
oledShowProgressBar(3, octoEnabled?5:4, "Spool Tag", "Spoolman update");
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId + "/measure";
|
||||
Serial.print("Update Spule mit URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
@@ -184,9 +325,11 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
// TBD: reset ESP instead of showing a message
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return 0;
|
||||
}
|
||||
params->requestType = API_REQUEST_SPOOL_WEIGHT_UPDATE;
|
||||
params->httpType = "PUT";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = updatePayload;
|
||||
@@ -195,16 +338,70 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
4096, // Stackgröße in Bytes
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
|
||||
updateDoc.clear();
|
||||
HEAP_DEBUG_MESSAGE("updateSpoolWeight end");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t updateSpoolLocation(String spoolId, String location){
|
||||
HEAP_DEBUG_MESSAGE("updateSpoolLocation begin");
|
||||
|
||||
oledShowProgressBar(3, octoEnabled?5:4, "Loc. Tag", "Spoolman update");
|
||||
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
|
||||
Serial.print("Update Spule mit URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
|
||||
// Update Payload erstellen
|
||||
JsonDocument updateDoc;
|
||||
updateDoc["location"] = location;
|
||||
|
||||
String updatePayload;
|
||||
serializeJson(updateDoc, updatePayload);
|
||||
Serial.print("Update Payload: ");
|
||||
Serial.println(updatePayload);
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return 0;
|
||||
}
|
||||
params->requestType = API_REQUEST_SPOOL_LOCATION_UPDATE;
|
||||
params->httpType = "PATCH";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = updatePayload;
|
||||
|
||||
if(spoolmanApiState == API_IDLE){
|
||||
// Erstelle die Task
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
|
||||
}else{
|
||||
Serial.println("Not spawning new task, API still active!");
|
||||
}
|
||||
|
||||
updateDoc.clear();
|
||||
|
||||
HEAP_DEBUG_MESSAGE("updateSpoolLocation end");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool updateSpoolOcto(int spoolId) {
|
||||
oledShowProgressBar(4, octoEnabled?5:4, "Spool Tag", "Octoprint update");
|
||||
|
||||
String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool";
|
||||
Serial.print("Update Spule in Octoprint mit URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
@@ -223,6 +420,7 @@ bool updateSpoolOcto(int spoolId) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return false;
|
||||
}
|
||||
params->requestType = API_REQUEST_OCTO_SPOOL_UPDATE;
|
||||
params->httpType = "POST";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = updatePayload;
|
||||
@@ -232,12 +430,14 @@ bool updateSpoolOcto(int spoolId) {
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
4096, // Stackgröße in Bytes
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
|
||||
updateDoc.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,6 +462,10 @@ bool updateSpoolBambuData(String payload) {
|
||||
|
||||
String updatePayload;
|
||||
serializeJson(updateDoc, updatePayload);
|
||||
|
||||
doc.clear();
|
||||
updateDoc.clear();
|
||||
|
||||
Serial.print("Update Payload: ");
|
||||
Serial.println(updatePayload);
|
||||
|
||||
@@ -270,6 +474,7 @@ bool updateSpoolBambuData(String payload) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return false;
|
||||
}
|
||||
params->requestType = API_REQUEST_BAMBU_UPDATE;
|
||||
params->httpType = "PATCH";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = updatePayload;
|
||||
@@ -278,7 +483,7 @@ bool updateSpoolBambuData(String payload) {
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
4096, // Stackgröße in Bytes
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
@@ -287,6 +492,238 @@ bool updateSpoolBambuData(String payload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// #### Filament Fabrik
|
||||
uint16_t checkVendor(String vendor) {
|
||||
// Check if vendor exists using task system
|
||||
foundVendorId = 0; // Reset previous value
|
||||
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor?name=" + vendor;
|
||||
Serial.print("Check vendor with URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return 0;
|
||||
}
|
||||
params->requestType = API_REQUEST_VENDOR_CHECK;
|
||||
params->httpType = "GET";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = ""; // Empty for GET request
|
||||
|
||||
// Check if API is idle before creating task
|
||||
if(spoolmanApiState == API_IDLE){
|
||||
// Erstelle die Task
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
} else {
|
||||
Serial.println("Not spawning new task, API still active!");
|
||||
delete params;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Wait for task completion
|
||||
while(spoolmanApiState != API_IDLE) {
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// Check if vendor was found
|
||||
if (foundVendorId == 0) {
|
||||
Serial.println("Vendor not found, creating new vendor...");
|
||||
uint16_t vendorId = createVendor(vendor);
|
||||
if (vendorId == 0) {
|
||||
Serial.println("Failed to create vendor, returning 0.");
|
||||
return 0; // Failed to create vendor
|
||||
} else {
|
||||
Serial.println("Vendor created with ID: " + String(vendorId));
|
||||
checkFilament(vendorId);
|
||||
return vendorId;
|
||||
}
|
||||
} else {
|
||||
Serial.println("Vendor found: " + vendor);
|
||||
Serial.print("Vendor ID: ");
|
||||
Serial.println(foundVendorId);
|
||||
return foundVendorId;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t createVendor(String vendor) {
|
||||
// Create new vendor in Spoolman database using task system
|
||||
// Note: Due to async nature, the ID will be stored in createdVendorId global variable
|
||||
createdVendorId = 0; // Reset previous value
|
||||
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/vendor";
|
||||
Serial.print("Create vendor with URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
|
||||
// Create JSON payload for vendor creation
|
||||
JsonDocument vendorDoc;
|
||||
vendorDoc["name"] = vendor;
|
||||
vendorDoc["comment"] = "automatically generated";
|
||||
vendorDoc["empty_spool_weight"] = 180;
|
||||
vendorDoc["external_id"] = vendor;
|
||||
|
||||
String vendorPayload;
|
||||
serializeJson(vendorDoc, vendorPayload);
|
||||
Serial.print("Vendor Payload: ");
|
||||
Serial.println(vendorPayload);
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
vendorDoc.clear();
|
||||
return 0;
|
||||
}
|
||||
params->requestType = API_REQUEST_VENDOR_CREATE;
|
||||
params->httpType = "POST";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = vendorPayload;
|
||||
|
||||
// Check if API is idle before creating task
|
||||
if(spoolmanApiState == API_IDLE){
|
||||
// Erstelle die Task
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
} else {
|
||||
Serial.println("Not spawning new task, API still active!");
|
||||
delete params;
|
||||
vendorDoc.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
vendorDoc.clear();
|
||||
|
||||
// Wait for task completion and return the created vendor ID
|
||||
// Note: createdVendorId will be set by sendToApi when response is received
|
||||
while(spoolmanApiState != API_IDLE) {
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
return createdVendorId;
|
||||
}
|
||||
|
||||
uint16_t checkFilament(uint16_t vendorId) {
|
||||
// Check if filament exists using task system
|
||||
foundFilamentId = 0; // Reset previous value
|
||||
|
||||
String spoolsUrl = spoolmanUrl + apiUrl + "/filament?vendor.id=" + String(vendorId);
|
||||
Serial.print("Check filament with URL: ");
|
||||
Serial.println(spoolsUrl);
|
||||
|
||||
SendToApiParams* params = new SendToApiParams();
|
||||
if (params == nullptr) {
|
||||
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
|
||||
return 0;
|
||||
}
|
||||
params->requestType = API_REQUEST_FILAMENT_CHECK;
|
||||
params->httpType = "GET";
|
||||
params->spoolsUrl = spoolsUrl;
|
||||
params->updatePayload = ""; // Empty for GET request
|
||||
|
||||
// Check if API is idle before creating task
|
||||
if(spoolmanApiState == API_IDLE){
|
||||
// Erstelle die Task
|
||||
BaseType_t result = xTaskCreate(
|
||||
sendToApi, // Task-Funktion
|
||||
"SendToApiTask", // Task-Name
|
||||
6144, // Stackgröße in Bytes
|
||||
(void*)params, // Parameter
|
||||
0, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
} else {
|
||||
Serial.println("Not spawning new task, API still active!");
|
||||
delete params;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Wait for task completion
|
||||
while(spoolmanApiState != API_IDLE) {
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// Check if filament was found
|
||||
if (foundFilamentId == 0) {
|
||||
Serial.println("Filament not found, creating new filament...");
|
||||
uint16_t filamentId = createFilament();
|
||||
if (filamentId == 0) {
|
||||
Serial.println("Failed to create filament, returning 0.");
|
||||
return 0; // Failed to create filament
|
||||
} else {
|
||||
Serial.println("Filament created with ID: " + String(filamentId));
|
||||
checkSpool();
|
||||
return filamentId;
|
||||
}
|
||||
} else {
|
||||
Serial.println("Filament found for vendor ID: " + String(vendorId));
|
||||
Serial.print("Filament ID: ");
|
||||
Serial.println(foundFilamentId);
|
||||
return foundFilamentId;
|
||||
}
|
||||
}
|
||||
|
||||
bool createFilament() {
|
||||
// {
|
||||
// "name": "PolyTerra Charcoal Black",
|
||||
// "vendor_id": 0,
|
||||
// "material": "PLA",
|
||||
// "price": 20,
|
||||
// "density": 1.24,
|
||||
// "diameter": 1.75,
|
||||
// "weight": 1000,
|
||||
// "spool_weight": 140,
|
||||
// "article_number": "PM70820",
|
||||
// "comment": "automatically generated",
|
||||
// "settings_extruder_temp": 210,
|
||||
// "settings_bed_temp": 60,
|
||||
// "color_hex": "FF0000",
|
||||
// "multi_color_hexes": "FF0000,00FF00,0000FF",
|
||||
// "multi_color_direction": "coaxial",
|
||||
// "external_id": "polymaker_pla_polysonicblack_1000_175",
|
||||
// "extra": {
|
||||
// "nozzle_temperature": "string"
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
uint16_t checkSpool() {
|
||||
|
||||
}
|
||||
|
||||
bool createSpool() {
|
||||
// Implement specific handling for Spool creation
|
||||
// {
|
||||
// "first_used": "2019-08-24T14:15:22Z",
|
||||
// "last_used": "2019-08-24T14:15:22Z",
|
||||
// "filament_id": 0,
|
||||
// "price": 20,
|
||||
// "initial_weight": 200,
|
||||
// "spool_weight": 200,
|
||||
// "remaining_weight": 800,
|
||||
// "used_weight": 200,
|
||||
// "location": "Shelf A",
|
||||
// "lot_nr": "52342",
|
||||
// "comment": "",
|
||||
// "archived": false,
|
||||
// "extra": {
|
||||
// "nfc_id": "string"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// #### Spoolman init
|
||||
bool checkSpoolmanExtraFields() {
|
||||
HTTPClient http;
|
||||
@@ -427,6 +864,7 @@ bool checkSpoolmanExtraFields() {
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,9 +888,6 @@ bool checkSpoolmanInstance(const String& url) {
|
||||
|
||||
if (httpCode > 0) {
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
oledShowMessage("Spoolman available");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
@@ -463,60 +898,62 @@ bool checkSpoolmanInstance(const String& url) {
|
||||
if (!checkSpoolmanExtraFields()) {
|
||||
Serial.println("Fehler beim Überprüfen der Extrafelder.");
|
||||
|
||||
// TBD
|
||||
oledShowMessage("Spoolman Error creating Extrafields");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
spoolman_connected = true;
|
||||
spoolmanApiState = API_IDLE;
|
||||
oledShowTopRow();
|
||||
spoolmanConnected = true;
|
||||
return strcmp(status, "healthy") == 0;
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
}
|
||||
} else {
|
||||
Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode));
|
||||
}
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) {
|
||||
if (!checkSpoolmanInstance(url)) return false;
|
||||
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) {
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_API, false); // false = readwrite
|
||||
preferences.putString(NVS_KEY_SPOOLMAN_URL, url);
|
||||
preferences.putBool(NVS_KEY_OCTOPRINT_ENABLED, octoOn);
|
||||
preferences.putString(NVS_KEY_OCTOPRINT_URL, octo_url);
|
||||
preferences.putString(NVS_KEY_OCTOPRINT_TOKEN, octoTk);
|
||||
preferences.end();
|
||||
|
||||
JsonDocument doc;
|
||||
doc["url"] = url;
|
||||
doc["octoEnabled"] = octoOn;
|
||||
doc["octoUrl"] = octoWh;
|
||||
doc["octoToken"] = octoTk;
|
||||
Serial.print("Speichere Spoolman Data in Datei: ");
|
||||
Serial.println(doc.as<String>());
|
||||
if (!saveJsonValue("/spoolman_url.json", doc)) {
|
||||
Serial.println("Fehler beim Speichern der Spoolman-URL.");
|
||||
return false;
|
||||
}
|
||||
//TBD: This could be handled nicer in the future
|
||||
spoolmanUrl = url;
|
||||
octoEnabled = octoOn;
|
||||
octoUrl = octoWh;
|
||||
octoUrl = octo_url;
|
||||
octoToken = octoTk;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String loadSpoolmanUrl() {
|
||||
JsonDocument doc;
|
||||
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) {
|
||||
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false;
|
||||
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>())
|
||||
{
|
||||
octoUrl = doc["octoUrl"].as<String>();
|
||||
octoToken = doc["octoToken"].as<String>();
|
||||
}
|
||||
|
||||
return doc["url"].as<String>();
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_API, true);
|
||||
String spoolmanUrl = preferences.getString(NVS_KEY_SPOOLMAN_URL, "");
|
||||
octoEnabled = preferences.getBool(NVS_KEY_OCTOPRINT_ENABLED, false);
|
||||
if(octoEnabled)
|
||||
{
|
||||
octoUrl = preferences.getString(NVS_KEY_OCTOPRINT_URL, "");
|
||||
octoToken = preferences.getString(NVS_KEY_OCTOPRINT_TOKEN, "");
|
||||
}
|
||||
Serial.println("Keine gültige Spoolman-URL gefunden.");
|
||||
return "";
|
||||
preferences.end();
|
||||
return spoolmanUrl;
|
||||
}
|
||||
|
||||
bool initSpoolman() {
|
||||
oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init");
|
||||
spoolmanUrl = loadSpoolmanUrl();
|
||||
spoolmanUrl.trim();
|
||||
if (spoolmanUrl == "") {
|
||||
|
30
src/api.h
@@ -6,12 +6,34 @@
|
||||
#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
|
||||
} 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 uint16_t createdVendorId; // ID of newly created vendor
|
||||
extern uint16_t foundVendorId; // ID of found vendor
|
||||
extern uint16_t foundFilamentId; // ID of found filament
|
||||
extern bool spoolmanConnected;
|
||||
|
||||
bool checkSpoolmanInstance(const String& url);
|
||||
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk);
|
||||
@@ -20,8 +42,16 @@ bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafeld
|
||||
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
|
||||
uint16_t checkVendor(String vendor); // Check if vendor exists, return ID
|
||||
uint16_t createVendor(String vendor); // Create vendor, return ID
|
||||
uint16_t checkFilament(); // Check if filament exists, return ID
|
||||
bool createFilament(); // Create filament
|
||||
uint16_t checkSpool(); // Check if spool exists, return ID
|
||||
bool createSpool(); // Create spool
|
||||
void createFilamentFabrik(JsonDocument payload);
|
||||
|
||||
#endif
|
||||
|
188
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,49 +18,67 @@ PubSubClient client(sslClient);
|
||||
|
||||
TaskHandle_t BambuMqttTask;
|
||||
|
||||
String report_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
|
||||
AMSData ams_data[MAX_AMS]; // Definition des Arrays;
|
||||
|
||||
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
|
||||
bool removeBambuCredentials() {
|
||||
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;
|
||||
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();
|
||||
|
||||
if (!saveJsonValue("/bambu_credentials.json", doc)) {
|
||||
Serial.println("Fehler beim Speichern der Bambu-Credentials.");
|
||||
return false;
|
||||
// Löschen der globalen Variablen
|
||||
bambuCredentials.ip = "";
|
||||
bambuCredentials.serial = "";
|
||||
bambuCredentials.accesscode = "";
|
||||
bambuCredentials.autosend_enable = false;
|
||||
bambuCredentials.autosend_time = BAMBU_DEFAULT_AUTOSEND_TIME;
|
||||
|
||||
autoSetToBambuSpoolId = 0;
|
||||
ams_count = 0;
|
||||
amsJsonData = "";
|
||||
|
||||
bambuDisabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
|
||||
if (BambuMqttTask) {
|
||||
vTaskDelete(BambuMqttTask);
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -68,35 +87,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();
|
||||
|
||||
report_topic = "device/" + String(bambu_serialnr) + "/report";
|
||||
//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 {
|
||||
@@ -199,7 +219,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
|
||||
bool sendMqttMessage(const String& payload) {
|
||||
Serial.println("Sending MQTT message");
|
||||
Serial.println(payload);
|
||||
if (client.publish(report_topic.c_str(), payload.c_str()))
|
||||
if (client.publish(("device/"+bambuCredentials.serial+"/request").c_str(), payload.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -341,7 +361,7 @@ void updateAmsWsData(JsonDocument& doc, JsonArray& amsArray, int& ams_count, Jso
|
||||
ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
|
||||
ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
|
||||
ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
|
||||
//ams_data[i].trays[j].setting_id = trayObj["setting_id"].as<String>();
|
||||
if (trayObj["tray_type"].as<String>() == "") ams_data[i].trays[j].setting_id = "";
|
||||
ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
|
||||
}
|
||||
}
|
||||
@@ -425,16 +445,8 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wenn bambu auto set spool aktiv und eine spule erkannt und mqtt meldung das neue spule im ams
|
||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0 &&
|
||||
doc["print"]["command"].as<String>() == "push_status" && doc["print"]["ams"]["tray_pre"].as<uint8_t>()
|
||||
&& !doc["print"]["ams"]["ams"].as<JsonArray>())
|
||||
{
|
||||
autoSetSpool(autoSetToBambuSpoolId, doc["print"]["ams"]["tray_pre"].as<uint8_t>());
|
||||
}
|
||||
|
||||
// Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren
|
||||
if (doc["print"]["upgrade_state"].is<JsonObject>())
|
||||
if (doc["print"]["upgrade_state"].is<JsonObject>() || (doc["print"]["command"].is<String>() && doc["print"]["command"] == "push_status"))
|
||||
{
|
||||
// Prüfen ob AMS-Daten vorhanden sind
|
||||
if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>())
|
||||
@@ -443,7 +455,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
}
|
||||
|
||||
JsonArray amsArray = doc["print"]["ams"]["ams"].as<JsonArray>();
|
||||
|
||||
|
||||
// Prüfe ob sich die AMS-Daten geändert haben
|
||||
bool hasChanges = false;
|
||||
|
||||
@@ -479,6 +491,12 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
(trayObj["setting_id"].as<String>() != "" && trayObj["setting_id"].as<String>() != ams_data[storedIndex].trays[j].setting_id) ||
|
||||
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
|
||||
hasChanges = true;
|
||||
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
{
|
||||
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -497,6 +515,11 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
|
||||
(vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) ||
|
||||
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
|
||||
hasChanges = true;
|
||||
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
|
||||
{
|
||||
autoSetSpool(autoSetToBambuSpoolId, 254);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -550,10 +573,11 @@ void reconnect() {
|
||||
oledShowTopRow();
|
||||
|
||||
// Attempt to connect
|
||||
if (client.connect(bambu_serialnr, 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(report_topic.c_str());
|
||||
client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
|
||||
bambu_connected = true;
|
||||
oledShowTopRow();
|
||||
} else {
|
||||
@@ -599,31 +623,24 @@ void mqtt_loop(void * parameter) {
|
||||
|
||||
bool setupMqtt() {
|
||||
// Wenn Bambu Daten vorhanden
|
||||
bool success = loadBambuCredentials();
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
//bool success = loadBambuCredentials();
|
||||
|
||||
if (!success) {
|
||||
Serial.println("Failed to load Bambu credentials");
|
||||
oledShowMessage("Bambu Credentials Missing");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
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;
|
||||
if (client.connect(bambu_serialnr, 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(5120);
|
||||
// Optional: Topic abonnieren
|
||||
client.subscribe(report_topic.c_str());
|
||||
//client.subscribe(request_topic.c_str());
|
||||
client.setBufferSize(15488);
|
||||
client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
|
||||
Serial.println("MQTT-Client initialisiert");
|
||||
|
||||
oledShowMessage("Bambu Connected");
|
||||
@@ -646,24 +663,25 @@ bool setupMqtt() {
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
connected = false;
|
||||
oledShowTopRow();
|
||||
autoSetToBambuSpoolId = 0;
|
||||
}
|
||||
|
||||
if (!connected) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Fehler: Keine MQTT-Daten vorhanden");
|
||||
oledShowMessage("Bambu Credentials Missing");
|
||||
oledShowTopRow();
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
bambuDisabled = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void bambu_restart() {
|
||||
Serial.println("Bambu restart");
|
||||
|
||||
if (BambuMqttTask) {
|
||||
vTaskDelete(BambuMqttTask);
|
||||
delay(10);
|
||||
}
|
||||
setupMqtt();
|
||||
}
|
13
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,12 @@ 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();
|
||||
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime);
|
||||
bool setupMqtt();
|
||||
|
@@ -1,6 +1,20 @@
|
||||
#include "commonFS.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
bool removeJsonValue(const char* filename) {
|
||||
File file = LittleFS.open(filename, "r");
|
||||
if (!file) {
|
||||
return true;
|
||||
}
|
||||
file.close();
|
||||
if (!LittleFS.remove(filename)) {
|
||||
Serial.print("Fehler beim Löschen der Datei: ");
|
||||
Serial.println(filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveJsonValue(const char* filename, const JsonDocument& doc) {
|
||||
File file = LittleFS.open(filename, "w");
|
||||
if (!file) {
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
bool removeJsonValue(const char* filename);
|
||||
bool saveJsonValue(const char* filename, const JsonDocument& doc);
|
||||
bool loadJsonValue(const char* filename, JsonDocument& doc);
|
||||
void initializeFileSystem();
|
||||
|
@@ -19,17 +19,18 @@ 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 +41,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
|
||||
|
39
src/config.h
@@ -3,6 +3,37 @@
|
||||
|
||||
#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 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"
|
||||
|
||||
|
||||
extern const uint8_t PN532_IRQ;
|
||||
extern const uint8_t PN532_RESET;
|
||||
|
||||
@@ -11,10 +42,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 +52,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
@@ -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);
|
||||
|
||||
|
125
src/main.cpp
@@ -13,6 +13,11 @@
|
||||
#include "esp_task_wdt.h"
|
||||
#include "commonFS.h"
|
||||
|
||||
bool mainTaskWasPaused = 0;
|
||||
uint8_t scaleTareCounter = 0;
|
||||
bool touchSensorConnected = false;
|
||||
bool booting = true;
|
||||
|
||||
// ##### SETUP #####
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@@ -36,7 +41,6 @@ void setup() {
|
||||
setupWebserver(server);
|
||||
|
||||
// Spoolman API
|
||||
// api.cpp
|
||||
initSpoolman();
|
||||
|
||||
// Bambu MQTT
|
||||
@@ -45,32 +49,24 @@ void setup() {
|
||||
// NFC Reader
|
||||
startNfc();
|
||||
|
||||
uint8_t scaleCalibrated = start_scale();
|
||||
if (scaleCalibrated == 3) {
|
||||
oledShowMessage("Scale not calibrated!");
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
} else if (scaleCalibrated == 0) {
|
||||
oledShowMessage("HX711 not found");
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
// 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);
|
||||
esp_task_wdt_init(10, panic);
|
||||
|
||||
booting = false;
|
||||
// Aktuellen Task (loopTask) zum Watchdog hinzufügen
|
||||
esp_task_wdt_add(NULL);
|
||||
|
||||
// Optional: Andere Tasks zum Watchdog hinzufügen, falls nötig
|
||||
// esp_task_wdt_add(task_handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,29 +95,54 @@ 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)
|
||||
|
||||
// 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, wifiCheckInterval))
|
||||
{
|
||||
checkWiFiConnection();
|
||||
}
|
||||
|
||||
// Periodic display update
|
||||
if (intervalElapsed(currentMillis, lastWifiCheckTime, 1000))
|
||||
{
|
||||
oledShowTopRow();
|
||||
}
|
||||
|
||||
// Wenn Bambu auto set Spool aktiv
|
||||
if (autoSendToBambu && autoSetToBambuSpoolId > 0) {
|
||||
if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
|
||||
{
|
||||
if (!bambuDisabled && !bambu_connected)
|
||||
{
|
||||
bambu_restart();
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -147,19 +168,28 @@ void loop() {
|
||||
}
|
||||
|
||||
// Ausgabe der Waage auf Display
|
||||
if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0 && (!autoSendToBambu || autoSetToBambuSpoolId == 0))
|
||||
if(pauseMainTask == 0)
|
||||
{
|
||||
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
||||
if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0)))
|
||||
{
|
||||
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
|
||||
}
|
||||
mainTaskWasPaused = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainTaskWasPaused = true;
|
||||
}
|
||||
|
||||
|
||||
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag < 3)
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
|
||||
{
|
||||
lastWeightReadTime = currentMillis;
|
||||
|
||||
// Prüfen ob die Waage korrekt genullt ist
|
||||
if ((weight > 0 && weight < 5) || weight < 0)
|
||||
// Abweichung von 2g ignorieren
|
||||
if (autoTare && (weight > 2 && weight < 7) || weight < -2)
|
||||
{
|
||||
scale_tare_counter++;
|
||||
}
|
||||
@@ -169,7 +199,7 @@ void loop() {
|
||||
}
|
||||
|
||||
// Prüfen ob das Gewicht gleich bleibt und dann senden
|
||||
if (weight == lastWeight && weight > 5)
|
||||
if (abs(weight - lastWeight) <= 2 && weight > 5)
|
||||
{
|
||||
weigthCouterToApi++;
|
||||
}
|
||||
@@ -181,7 +211,8 @@ void loop() {
|
||||
}
|
||||
|
||||
// reset weight counter after writing tag
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag > 1)
|
||||
// TBD: what exactly is the logic behind this?
|
||||
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
|
||||
{
|
||||
weigthCouterToApi = 0;
|
||||
}
|
||||
@@ -189,19 +220,14 @@ void loop() {
|
||||
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))
|
||||
{
|
||||
oledShowIcon("success");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
weightSend = 1;
|
||||
autoSetToBambuSpoolId = spoolId.toInt();
|
||||
if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE) {
|
||||
// set the current tag as processed to prevent it beeing processed again
|
||||
tagProcessed = true;
|
||||
|
||||
if (octoEnabled)
|
||||
{
|
||||
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||
}
|
||||
if (updateSpoolWeight(activeSpoolId, weight))
|
||||
{
|
||||
weightSend = 1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -210,6 +236,15 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
yield();
|
||||
if(sendOctoUpdate && spoolmanApiState == API_IDLE){
|
||||
autoSetToBambuSpoolId = activeSpoolId.toInt();
|
||||
|
||||
if(octoEnabled)
|
||||
{
|
||||
updateSpoolOcto(autoSetToBambuSpoolId);
|
||||
}
|
||||
sendOctoUpdate = false;
|
||||
}
|
||||
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
9
src/main.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
extern bool booting;
|
||||
|
||||
#endif
|
208
src/nfc.cpp
@@ -7,6 +7,7 @@
|
||||
#include "api.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "scale.h"
|
||||
#include "bambu.h"
|
||||
|
||||
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
|
||||
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
|
||||
@@ -14,11 +15,18 @@ Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
|
||||
TaskHandle_t RfidReaderTask;
|
||||
|
||||
JsonDocument rfidData;
|
||||
String spoolId = "";
|
||||
String activeSpoolId = "";
|
||||
String lastSpoolId = "";
|
||||
String nfcJsonData = "";
|
||||
bool tagProcessed = false;
|
||||
volatile bool pauseBambuMqttTask = false;
|
||||
|
||||
volatile uint8_t hasReadRfidTag = 0;
|
||||
struct NfcWriteParameterType {
|
||||
bool tagType;
|
||||
char* payload;
|
||||
};
|
||||
|
||||
volatile nfcReaderStateType nfcReaderState = NFC_IDLE;
|
||||
// 0 = nicht gelesen
|
||||
// 1 = erfolgreich gelesen
|
||||
// 2 = fehler beim Lesen
|
||||
@@ -29,6 +37,11 @@ volatile uint8_t hasReadRfidTag = 0;
|
||||
// ***** PN532
|
||||
|
||||
|
||||
// ##### Recycling Fabrik #####
|
||||
bool isRecyclingFabrik(const char* brand) {
|
||||
return strcmp(brand, "Recycling Fabrik") == 0;
|
||||
}
|
||||
|
||||
// ##### Funktionen für RFID #####
|
||||
void payloadToJson(uint8_t *data) {
|
||||
const char* startJson = strchr((char*)data, '{');
|
||||
@@ -49,7 +62,13 @@ void payloadToJson(uint8_t *data) {
|
||||
int min_temp = doc["min_temp"];
|
||||
int max_temp = doc["max_temp"];
|
||||
const char* brand = doc["brand"];
|
||||
|
||||
|
||||
// Recycling Fabrik
|
||||
if (isRecyclingFabrik(brand)) {
|
||||
// TODO: Implement specific handling for Recycling Fabrik
|
||||
Serial.println("Recycling Fabrik erkannt.");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("-----------------");
|
||||
Serial.println("JSON-Parsed Data:");
|
||||
@@ -64,6 +83,8 @@ void payloadToJson(uint8_t *data) {
|
||||
Serial.print("deserializeJson() failed: ");
|
||||
Serial.println(error.f_str());
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
} else {
|
||||
Serial.println("Kein gültiger JSON-Inhalt gefunden oder fehlerhafte Formatierung.");
|
||||
//writeJsonToTag("{\"version\":\"1.0\",\"protocol\":\"NFC\",\"color_hex\":\"#FFFFFF\",\"type\":\"Example\",\"min_temp\":10,\"max_temp\":30,\"brand\":\"BrandName\"}");
|
||||
@@ -192,6 +213,8 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
|
||||
}
|
||||
|
||||
bool decodeNdefAndReturnJson(const byte* encodedMessage) {
|
||||
oledShowProgressBar(1, octoEnabled?5:4, "Reading", "Decoding data");
|
||||
|
||||
byte typeLength = encodedMessage[3];
|
||||
byte payloadLength = encodedMessage[4];
|
||||
|
||||
@@ -203,7 +226,7 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
|
||||
}
|
||||
|
||||
// JSON-Dokument verarbeiten
|
||||
JsonDocument doc; // Passen Sie die Größe an den JSON-Inhalt an
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, nfcJsonData);
|
||||
if (error)
|
||||
{
|
||||
@@ -215,52 +238,87 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sende die aktualisierten AMS-Daten an alle WebSocket-Clients
|
||||
Serial.println("JSON-Dokument erfolgreich verarbeitet");
|
||||
Serial.println(doc.as<String>());
|
||||
if (doc["sm_id"] != "")
|
||||
{
|
||||
Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>());
|
||||
spoolId = doc["sm_id"].as<String>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Keine SPOOL-ID gefunden.");
|
||||
spoolId = "";
|
||||
oledShowMessage("Unknown Spool");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
// If spoolman is unavailable, there is no point in continuing
|
||||
if(spoolmanConnected){
|
||||
// Sende die aktualisierten AMS-Daten an alle WebSocket-Clients
|
||||
Serial.println("JSON-Dokument erfolgreich verarbeitet");
|
||||
Serial.println(doc.as<String>());
|
||||
if (doc["sm_id"].is<String>() && doc["sm_id"] != "" && doc["sm_id"] != "0")
|
||||
{
|
||||
oledShowProgressBar(2, octoEnabled?5:4, "Spool Tag", "Weighing");
|
||||
Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>());
|
||||
activeSpoolId = doc["sm_id"].as<String>();
|
||||
lastSpoolId = activeSpoolId;
|
||||
|
||||
Serial.println("Api state: " + String(spoolmanApiState));
|
||||
}
|
||||
else if(doc["location"].is<String>() && doc["location"] != "")
|
||||
{
|
||||
Serial.println("Location Tag found!");
|
||||
String location = doc["location"].as<String>();
|
||||
if(lastSpoolId != ""){
|
||||
updateSpoolLocation(lastSpoolId, location);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Location update tag scanned without scanning spool before!");
|
||||
oledShowProgressBar(1, 1, "Failure", "Scan spool first");
|
||||
}
|
||||
}
|
||||
// Recycling Fabrik
|
||||
else if (isRecyclingFabrik(doc["type"].as<String>().c_str())) {
|
||||
// If no sm_id is present but the brand is Recycling Fabrik then
|
||||
// create a new spool, maybe brand too, in Spoolman
|
||||
Serial.println("Recycling Fabrik Tag found!");
|
||||
createFilamentFabrik(doc);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Keine SPOOL-ID gefunden.");
|
||||
activeSpoolId = "";
|
||||
oledShowProgressBar(1, 1, "Failure", "Unkown tag");
|
||||
}
|
||||
}else{
|
||||
oledShowProgressBar(octoEnabled?5:4, octoEnabled?5:4, "Failure!", "Spoolman unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void writeJsonToTag(void *parameter) {
|
||||
const char* payload = (const char*)parameter;
|
||||
NfcWriteParameterType* params = (NfcWriteParameterType*)parameter;
|
||||
|
||||
// Gib die erstellte NDEF-Message aus
|
||||
Serial.println("Erstelle NDEF-Message...");
|
||||
Serial.println(payload);
|
||||
Serial.println(params->payload);
|
||||
|
||||
hasReadRfidTag = 3;
|
||||
nfcReaderState = NFC_WRITING;
|
||||
vTaskSuspend(RfidReaderTask);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
//pauseBambuMqttTask = true;
|
||||
// aktualisieren der Website wenn sich der Status ändert
|
||||
sendNfcData(nullptr);
|
||||
sendNfcData();
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
oledShowMessage("Waiting for NFC-Tag");
|
||||
|
||||
Serial.println("CP 1");
|
||||
// Wait 10sec for tag
|
||||
uint8_t success = 0;
|
||||
String uidString = "";
|
||||
for (uint16_t i = 0; i < 20; i++) {
|
||||
Serial.println("CP 2");
|
||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
||||
uint8_t uidLength;
|
||||
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500);
|
||||
// yield before potentially waiting for 400ms
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400);
|
||||
if (success) {
|
||||
Serial.println("CP 3.1");
|
||||
for (uint8_t i = 0; i < uidLength; i++) {
|
||||
//TBD: Rework to remove all the string operations
|
||||
uidString += String(uid[i], HEX);
|
||||
if (i < uidLength - 1) {
|
||||
uidString += ":"; // Optional: Trennzeichen hinzufügen
|
||||
@@ -268,10 +326,10 @@ void writeJsonToTag(void *parameter) {
|
||||
}
|
||||
foundNfcTag(nullptr, success);
|
||||
break;
|
||||
}else{
|
||||
Serial.println("CP 3.2");
|
||||
}
|
||||
|
||||
if (i == 0) oledShowMessage("Waiting for NFC-Tag");
|
||||
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
@@ -279,29 +337,37 @@ void writeJsonToTag(void *parameter) {
|
||||
|
||||
if (success)
|
||||
{
|
||||
oledShowIcon("transfer");
|
||||
oledShowProgressBar(1, 3, "Write Tag", "Writing");
|
||||
|
||||
// Schreibe die NDEF-Message auf den Tag
|
||||
success = ntag2xx_WriteNDEF(payload);
|
||||
success = ntag2xx_WriteNDEF(params->payload);
|
||||
if (success)
|
||||
{
|
||||
Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben");
|
||||
//oledShowMessage("NFC-Tag written");
|
||||
oledShowIcon("success");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
hasReadRfidTag = 5;
|
||||
//vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
nfcReaderState = NFC_WRITE_SUCCESS;
|
||||
// aktualisieren der Website wenn sich der Status ändert
|
||||
sendNfcData(nullptr);
|
||||
sendNfcData();
|
||||
pauseBambuMqttTask = false;
|
||||
|
||||
if (updateSpoolTagId(uidString, payload)) {
|
||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
||||
uint8_t uidLength;
|
||||
oledShowIcon("success");
|
||||
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
|
||||
yield();
|
||||
if(params->tagType){
|
||||
// TBD: should this be simplified?
|
||||
if (updateSpoolTagId(uidString, params->payload) && params->tagType) {
|
||||
|
||||
}else{
|
||||
// Potentially handle errors
|
||||
}
|
||||
}else{
|
||||
oledShowProgressBar(1, 1, "Write Tag", "Done!");
|
||||
}
|
||||
|
||||
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
||||
uint8_t uidLength;
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400)) {
|
||||
yield();
|
||||
}
|
||||
vTaskResume(RfidReaderTask);
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
}
|
||||
@@ -310,19 +376,19 @@ void writeJsonToTag(void *parameter) {
|
||||
Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag");
|
||||
oledShowIcon("failed");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
hasReadRfidTag = 4;
|
||||
nfcReaderState = NFC_WRITE_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
|
||||
oledShowMessage("No NFC-Tag found");
|
||||
oledShowProgressBar(1, 1, "Failure!", "No tag found");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
hasReadRfidTag = 0;
|
||||
nfcReaderState = NFC_IDLE;
|
||||
}
|
||||
|
||||
sendWriteResult(nullptr, success);
|
||||
sendNfcData(nullptr);
|
||||
sendNfcData();
|
||||
|
||||
vTaskResume(RfidReaderTask);
|
||||
pauseBambuMqttTask = false;
|
||||
@@ -330,20 +396,26 @@ void writeJsonToTag(void *parameter) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void startWriteJsonToTag(const char* payload) {
|
||||
char* payloadCopy = strdup(payload);
|
||||
void startWriteJsonToTag(const bool isSpoolTag, const char* payload) {
|
||||
NfcWriteParameterType* parameters = new NfcWriteParameterType();
|
||||
parameters->tagType = isSpoolTag;
|
||||
parameters->payload = strdup(payload);
|
||||
|
||||
// Task nicht mehrfach starten
|
||||
if (hasReadRfidTag != 3) {
|
||||
if (nfcReaderState == NFC_IDLE) {
|
||||
oledShowProgressBar(0, 1, "Write Tag", "Place tag now");
|
||||
// Erstelle die Task
|
||||
xTaskCreate(
|
||||
writeJsonToTag, // Task-Funktion
|
||||
"WriteJsonToTagTask", // Task-Name
|
||||
5115, // Stackgröße in Bytes
|
||||
(void*)payloadCopy, // Parameter
|
||||
(void*)parameters, // Parameter
|
||||
rfidWriteTaskPrio, // Priorität
|
||||
NULL // Task-Handle (nicht benötigt)
|
||||
);
|
||||
}else{
|
||||
oledShowProgressBar(0, 1, "FAILURE", "NFC busy!");
|
||||
// TBD: Add proper error handling (website)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +423,7 @@ void scanRfidTask(void * parameter) {
|
||||
Serial.println("RFID Task gestartet");
|
||||
for(;;) {
|
||||
// Wenn geschrieben wird Schleife aussetzen
|
||||
if (hasReadRfidTag != 3)
|
||||
if (nfcReaderState != NFC_WRITING)
|
||||
{
|
||||
yield();
|
||||
|
||||
@@ -363,14 +435,19 @@ void scanRfidTask(void * parameter) {
|
||||
|
||||
foundNfcTag(nullptr, success);
|
||||
|
||||
if (success && hasReadRfidTag != 1)
|
||||
// As long as there is still a tag on the reader, do not try to read it again
|
||||
if (success && nfcReaderState == NFC_IDLE)
|
||||
{
|
||||
// Set the current tag as not processed
|
||||
tagProcessed = false;
|
||||
|
||||
// Display some basic information about the card
|
||||
Serial.println("Found an ISO14443A card");
|
||||
|
||||
hasReadRfidTag = 6;
|
||||
nfcReaderState = NFC_READING;
|
||||
|
||||
oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag");
|
||||
|
||||
oledShowIcon("transfer");
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
|
||||
if (uidLength == 7)
|
||||
@@ -404,55 +481,56 @@ void scanRfidTask(void * parameter) {
|
||||
|
||||
if (!decodeNdefAndReturnJson(data))
|
||||
{
|
||||
oledShowMessage("NFC-Tag unknown");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
hasReadRfidTag = 2;
|
||||
oledShowProgressBar(1, 1, "Failure", "Unknown tag");
|
||||
nfcReaderState = NFC_READ_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasReadRfidTag = 1;
|
||||
nfcReaderState = NFC_READ_SUCCESS;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
oledShowMessage("NFC-Tag read error");
|
||||
hasReadRfidTag = 2;
|
||||
oledShowProgressBar(1, 1, "Failure", "Tag read error");
|
||||
nfcReaderState = NFC_READ_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TBD: Show error here?!
|
||||
oledShowProgressBar(1, 1, "Failure", "Unkown tag type");
|
||||
Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!");
|
||||
}
|
||||
}
|
||||
|
||||
if (!success && hasReadRfidTag > 0)
|
||||
if (!success && nfcReaderState != NFC_IDLE)
|
||||
{
|
||||
hasReadRfidTag = 0;
|
||||
nfcReaderState = NFC_IDLE;
|
||||
//uidString = "";
|
||||
nfcJsonData = "";
|
||||
activeSpoolId = "";
|
||||
Serial.println("Tag entfernt");
|
||||
if (!autoSendToBambu) oledShowWeight(weight);
|
||||
if (!bambuCredentials.autosend_enable) oledShowWeight(weight);
|
||||
}
|
||||
|
||||
// aktualisieren der Website wenn sich der Status ändert
|
||||
sendNfcData(nullptr);
|
||||
sendNfcData();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void startNfc() {
|
||||
oledShowProgressBar(5, 7, DISPLAY_BOOT_TEXT, "NFC init");
|
||||
nfc.begin(); // Beginne Kommunikation mit RFID Leser
|
||||
delay(1000);
|
||||
unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus
|
||||
if (! versiondata) { // Wenn keine Antwort kommt
|
||||
Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor
|
||||
//delay(5000);
|
||||
//ESP.restart();
|
||||
oledShowMessage("No RFID Board found");
|
||||
delay(2000);
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
else {
|
||||
Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen
|
||||
|
20
src/nfc.h
@@ -3,14 +3,28 @@
|
||||
|
||||
#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);
|
||||
|
||||
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 bool tagProcessed;
|
||||
|
||||
|
||||
|
||||
#endif
|
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;
|
||||
}
|
||||
}
|
||||
|
158
src/scale.cpp
@@ -15,14 +15,26 @@ int16_t weight = 0;
|
||||
|
||||
uint8_t weigthCouterToApi = 0;
|
||||
uint8_t scale_tare_counter = 0;
|
||||
bool scaleTareRequest = false;
|
||||
uint8_t pauseMainTask = 0;
|
||||
uint8_t scaleCalibrated = 1;
|
||||
|
||||
Preferences preferences;
|
||||
const char* NVS_NAMESPACE = "scale";
|
||||
const char* NVS_KEY_CALIBRATION = "cal_value";
|
||||
bool autoTare = true;
|
||||
|
||||
// ##### 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();
|
||||
@@ -34,30 +46,56 @@ void scale_loop(void * parameter) {
|
||||
Serial.println("++++++++++++++++++++++++++++++");
|
||||
Serial.println("Scale Loop started");
|
||||
Serial.println("++++++++++++++++++++++++++++++");
|
||||
|
||||
for(;;) {
|
||||
if (scale.is_ready())
|
||||
{
|
||||
// Waage nochmal Taren, wenn zu lange Abweichung
|
||||
if (scale_tare_counter >= 5)
|
||||
// Waage automatisch Taren, wenn zu lange Abweichung
|
||||
if (autoTare && scale_tare_counter >= 5)
|
||||
{
|
||||
Serial.println("Auto Tare scale");
|
||||
scale.tare();
|
||||
scale_tare_counter = 0;
|
||||
}
|
||||
|
||||
weight = round(scale.get_units());
|
||||
// Waage manuell Taren
|
||||
if (scaleTareRequest == true)
|
||||
{
|
||||
Serial.println("Re-Tare scale");
|
||||
oledShowMessage("TARE Scale");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
scale.tare();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
oledShowWeight(0);
|
||||
scaleTareRequest = false;
|
||||
}
|
||||
|
||||
// Only update weight if median changed more than 1
|
||||
int16_t newWeight = round(scale.get_units());
|
||||
if(abs(weight-newWeight) > 1){
|
||||
weight = newWeight;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // Verzögerung, um die CPU nicht zu überlasten
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t start_scale() {
|
||||
void start_scale(bool touchSensorConnected) {
|
||||
Serial.println("Prüfe Calibration Value");
|
||||
long calibrationValue;
|
||||
float calibrationValue;
|
||||
|
||||
// NVS lesen
|
||||
preferences.begin(NVS_NAMESPACE, true); // true = readonly
|
||||
calibrationValue = preferences.getLong(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
|
||||
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
|
||||
|
||||
// auto Tare
|
||||
// Wenn Touch Sensor verbunden, dann autoTare auf false setzen
|
||||
// Danach prüfen was in NVS gespeichert ist
|
||||
autoTare = (touchSensorConnected) ? false : true;
|
||||
autoTare = preferences.getBool(NVS_KEY_AUTOTARE, autoTare);
|
||||
|
||||
preferences.end();
|
||||
|
||||
Serial.print("Read Scale Calibration Value ");
|
||||
@@ -68,9 +106,16 @@ uint8_t start_scale() {
|
||||
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");
|
||||
oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
@@ -90,7 +135,7 @@ uint8_t start_scale() {
|
||||
BaseType_t result = xTaskCreatePinnedToCore(
|
||||
scale_loop, /* Function to implement the task */
|
||||
"ScaleLoop", /* Name of the task */
|
||||
10000, /* Stack size in words */
|
||||
2048, /* Stack size in words */
|
||||
NULL, /* Task input parameter */
|
||||
scaleTaskPrio, /* Priority of the task */
|
||||
&ScaleTask, /* Task handle. */
|
||||
@@ -101,22 +146,23 @@ uint8_t start_scale() {
|
||||
} else {
|
||||
Serial.println("ScaleLoop-Task erfolgreich erstellt");
|
||||
}
|
||||
|
||||
return (scaleCalibrated == 1) ? 1 : 3;
|
||||
}
|
||||
|
||||
uint8_t calibrate_scale() {
|
||||
long newCalibrationValue;
|
||||
uint8_t returnState = 0;
|
||||
float newCalibrationValue;
|
||||
|
||||
vTaskSuspend(RfidReaderTask);
|
||||
vTaskSuspend(ScaleTask);
|
||||
|
||||
//vTaskSuspend(RfidReaderTask);
|
||||
vTaskDelete(RfidReaderTask);
|
||||
pauseBambuMqttTask = true;
|
||||
pauseMainTask = 1;
|
||||
|
||||
|
||||
if (scale.wait_ready_timeout(1000))
|
||||
{
|
||||
|
||||
scale.set_scale();
|
||||
oledShowMessage("Step 1 empty Scale");
|
||||
oledShowProgressBar(0, 3, "Scale Cal.", "Empty Scale");
|
||||
|
||||
for (uint16_t i = 0; i < 5000; i++) {
|
||||
yield();
|
||||
@@ -128,7 +174,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();
|
||||
@@ -136,7 +182,7 @@ uint8_t calibrate_scale() {
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
long newCalibrationValue = scale.get_units(10);
|
||||
float newCalibrationValue = scale.get_units(10);
|
||||
Serial.print("Result: ");
|
||||
Serial.println(newCalibrationValue);
|
||||
|
||||
@@ -148,21 +194,33 @@ uint8_t calibrate_scale() {
|
||||
Serial.println(newCalibrationValue);
|
||||
|
||||
// Speichern mit NVS
|
||||
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
|
||||
preferences.putLong(NVS_KEY_CALIBRATION, newCalibrationValue);
|
||||
Preferences preferences;
|
||||
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
|
||||
preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue);
|
||||
preferences.end();
|
||||
|
||||
// Verifizieren
|
||||
preferences.begin(NVS_NAMESPACE, true);
|
||||
long verifyValue = preferences.getLong(NVS_KEY_CALIBRATION, 0);
|
||||
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, revome weight");
|
||||
oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight");
|
||||
|
||||
oledShowMessage("Remove weight");
|
||||
scale.set_scale(newCalibrationValue);
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
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();
|
||||
scaleTareRequest = true;
|
||||
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
@@ -170,32 +228,22 @@ uint8_t calibrate_scale() {
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
oledShowMessage("Calibration done");
|
||||
|
||||
for (uint16_t i = 0; i < 2000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
//ESP.restart();
|
||||
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();
|
||||
}
|
||||
return 0;
|
||||
for (uint16_t i = 0; i < 50000; i++) {
|
||||
yield();
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
returnState = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("HX711 not found.");
|
||||
@@ -207,17 +255,13 @@ uint8_t calibrate_scale() {
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
return 0;
|
||||
returnState = 0;
|
||||
}
|
||||
|
||||
oledShowMessage("Scale Ready");
|
||||
|
||||
|
||||
Serial.println("starte Scale Task");
|
||||
start_scale();
|
||||
|
||||
vTaskResume(RfidReaderTask);
|
||||
vTaskResume(ScaleTask);
|
||||
pauseBambuMqttTask = false;
|
||||
pauseMainTask = 0;
|
||||
|
||||
return 1;
|
||||
return returnState;
|
||||
}
|
||||
|
@@ -4,8 +4,8 @@
|
||||
#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();
|
||||
|
||||
@@ -13,8 +13,10 @@ 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 autoTare;
|
||||
|
||||
extern TaskHandle_t ScaleTask;
|
||||
|
||||
|
150
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
|
||||
sendAmsData(client);
|
||||
sendNfcData(client);
|
||||
if (!bambuDisabled) sendAmsData(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) {
|
||||
@@ -50,7 +58,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 +67,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 +82,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 +120,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 +149,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 +188,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 +205,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,16 +220,23 @@ 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
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
Serial.println("Anfrage für /rfid erhalten");
|
||||
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/rfid.html.gz", "text/html");
|
||||
|
||||
String page = (bambuDisabled) ? "/rfid.html.gz" : "/rfid_bambu.html.gz";
|
||||
AsyncWebServerResponse *response = request->beginResponse(LittleFS, page, "text/html");
|
||||
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
response->addHeader("Cache-Control", CACHE_CONTROL);
|
||||
request->send(response);
|
||||
@@ -244,31 +267,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);
|
||||
});
|
||||
@@ -286,6 +289,14 @@ void setupWebserver(AsyncWebServer &server) {
|
||||
}
|
||||
|
||||
String url = request->getParam("url")->value();
|
||||
if (url.indexOf("http://") == -1 && url.indexOf("https://") == -1) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
// Remove trailing slash if exists
|
||||
if (url.length() > 0 && url.charAt(url.length()-1) == '/') {
|
||||
url = url.substring(0, url.length()-1);
|
||||
}
|
||||
|
||||
bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false;
|
||||
String octoUrl = request->getParam("octoUrl")->value();
|
||||
String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : "";
|
||||
@@ -300,8 +311,17 @@ void setupWebserver(AsyncWebServer &server) {
|
||||
request->send(200, "application/json", jsonResponse);
|
||||
});
|
||||
|
||||
// Route für das Überprüfen der Spoolman-Instanz
|
||||
// Route für das Überprüfen der Bambu-Instanz
|
||||
server.on("/api/bambu", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
if (request->hasParam("remove")) {
|
||||
if (removeBambuCredentials()) {
|
||||
request->send(200, "application/json", "{\"success\": true}");
|
||||
} else {
|
||||
request->send(500, "application/json", "{\"success\": false, \"error\": \"Fehler beim Löschen der Bambu-Credentials\"}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request->hasParam("bambu_ip") || !request->hasParam("bambu_serialnr") || !request->hasParam("bambu_accesscode")) {
|
||||
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing parameter\"}");
|
||||
return;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -59,10 +59,9 @@ void initWiFi() {
|
||||
if(wm_nonblocking) wm.setConfigPortalBlocking(false);
|
||||
//wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
|
||||
wm.setWiFiAutoReconnect(true);
|
||||
wm.setConnectTimeout(5);
|
||||
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();
|
||||
|
6432
usermod/spitzbirne32/CAD/Base_usermod_spitzbirne32.stp
Normal file
16385
usermod/spitzbirne32/CAD/FilaMan-Scale_usermod_spitzbirne32.stp
Normal file
5278
usermod/spitzbirne32/CAD/Housing_usermod_spitzbirne32.stp
Normal file
0
usermod/spitzbirne32/CAD/README.md
Normal file
4888
usermod/spitzbirne32/CAD/ScaleTop_usermod_spitzbirne32.stp
Normal file
After Width: | Height: | Size: 540 KiB |
After Width: | Height: | Size: 525 KiB |
After Width: | Height: | Size: 7.9 MiB |
After Width: | Height: | Size: 183 KiB |
12
usermod/spitzbirne32/Images/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## **Heat insert location**
|
||||
|
||||
Housing:
|
||||
- every hole is made to fit a heat insert
|
||||

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

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

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