Compare commits

...

63 Commits

Author SHA1 Message Date
739fe7e764 docs: update changelog and header for version v1.5.6
Some checks failed
Release Workflow / detect-provider (push) Successful in 4s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Failing after 2m22s
2025-07-28 09:32:35 +02:00
5f8953a19d docs: update webpages for version v1.5.6 2025-07-28 09:32:35 +02:00
c919eeb848 docs: update platformio.ini for version v1.5.6 2025-07-28 09:32:30 +02:00
43177c670e Merge pull request #42 from janecker/configuration_nvs_storage
Changes configuration storage of spoolman and bambu values
2025-07-28 09:31:33 +02:00
1b50694f5f Merge branch 'main' into configuration_nvs_storage 2025-07-28 09:31:23 +02:00
48edde8557 Merge pull request #41 from janecker/memory_leak_fixes
Memory leak fixes
2025-07-28 09:26:30 +02:00
Jan Philipp Ecker
cb5d8ac10a Changes configuration storage of spoolman and bambu values
Change that moves configuration values of spoolman and bambu credentials to use NVS storage. Also fixes some typos and missing translation.
2025-07-27 17:33:09 +02:00
Jan Philipp Ecker
bf48c6d4e1 Fixes compiler warnings in nfc
Replaces the depricated function call containsKey() with is<T>() of JsonDocument.
2025-07-26 22:52:10 +02:00
Jan Philipp Ecker
5d2d5e9ee1 Adds ENABLE_HEAP_DEBUGGING define as comment to the build flags
Adds the new ENABLE_HEAP_DEBUGGING define to the build falgs. The option is commented out, but it makes it easier to quickly enable it
2025-07-26 22:50:08 +02:00
Jan Philipp Ecker
7e76612bb4 Adds data directory and further .vscode files to to .gitignore
Adds the whole data folder that is created during build of the project and further visual studio code related files to the .gitignore file.
2025-07-26 22:39:37 +02:00
Jan Philipp Ecker
f038020042 Memory leak fixes in api and nfc, location tag fix
Fixes multiple potential memory leaks in API and NFC. Also fixes an issue in the new locaiton tag feature that could lead to multiple parallel API requests. This could cause memory leak issues but also result in wrong weights being registered for a spool.
2025-07-26 22:36:04 +02:00
Jan Philipp Ecker
8343fe887b Introduces new heap debugging feature and fixes some memory leaks in website feature
Introduces a new define HEAP_DEBUG_MESSAGE(location) that can be used to instrument the code to get heap information output on the Serial output. It can be enabled via the define ENABLE_HEAP_DEBUGGING. Also fixes some memory leaks in the website part of the project.
2025-07-26 22:14:58 +02:00
3bb6c1caf5 docs: update changelog and header for version v1.5.5
All checks were successful
Release Workflow / detect-provider (push) Successful in 1m6s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 2m52s
2025-07-22 17:36:45 +02:00
37df07f102 docs: update platformio.ini for version v1.5.5 2025-07-22 17:36:45 +02:00
8484c1310b Merge pull request #40 from janecker/location_bambu_fix
Fixes some issues with the new location tags
2025-07-22 17:35:02 +02:00
Jan Philipp Ecker
fd7b4c25b3 Fixes some issues with the new location tags
Fixes an issue where the location dropdown is not visible if the Bambu integration is active. Adds support for the "NFC-Tag" view on the webpage, it now also shows info about the location tags. Revers a change that was not supposed to go into main where the amount of data written to the spool tag is reduced to only the sm_id.
2025-07-22 10:47:47 +02:00
d490b116b9 docs: update changelog and header for version v1.5.4
All checks were successful
Release Workflow / detect-provider (push) Successful in 1m10s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m52s
2025-07-22 06:36:16 +02:00
5bc6192b6f docs: update platformio.ini for version v1.5.4 2025-07-22 06:36:16 +02:00
2202d9a1aa Merge branch 'main' of github.com:ManuelW77/Filaman 2025-07-22 06:35:13 +02:00
7dbca0ab87 Merge pull request #39 from janecker/location_tags
Adds new feature to write and read location tags
2025-07-22 06:32:44 +02:00
24b3521f83 Merge pull request #38 from janecker/scale_debouncing
Adds slight debouncing to the scale loop weight logic
2025-07-22 06:32:31 +02:00
6c9f290bac fix: uncomment monitor_port configuration in platformio.ini 2025-07-22 06:31:51 +02:00
Jan Philipp Ecker
eab937d6ca Adds new feature to write and read location tags
Location tags can be written via the website. If a location tag is read after reading a spool tag, the location of the spool will be updated in spoolman to the location from the tag.
2025-07-21 21:03:55 +02:00
Jan Philipp Ecker
27ef8399e4 Adds slight debouncing to the scale loop weight logic
Adds slight debouncing to the scale loop to prevent jitter of the
weight displayed on the screen.
2025-06-19 10:08:15 +02:00
2920159f32 add loadcell desc. 2025-05-02 16:44:57 +02:00
2e19bccfa9 docs: update changelog and header for version v1.5.3
All checks were successful
Release Workflow / detect-provider (push) Successful in 1m10s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m25s
2025-04-25 15:52:56 +02:00
859e89431e docs: update platformio.ini for version v1.5.3 2025-04-25 15:52:56 +02:00
6dc26ca51f fix: update spool weight conditionally based on NFC ID 2025-04-25 15:52:38 +02:00
0becae7ed6 Affiliate Links 2025-04-25 09:41:02 +02:00
3d31833f50 docs: update changelog and header for version v1.5.2
All checks were successful
Release Workflow / detect-provider (push) Successful in 1m21s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m9s
2025-04-23 17:47:40 +02:00
599cc47443 docs: update platformio.ini for version v1.5.2 2025-04-23 17:47:40 +02:00
b1f7923770 feat: implement multi-color filament display and styles for dropdown options 2025-04-23 17:47:02 +02:00
c78c20979d fix: add remaining weight logging for PUT requests and improve error reporting in sendToApi function 2025-04-23 14:51:31 +02:00
e79c522e46 fix: add remaining weight logging and display after successful spool update 2025-04-23 11:33:07 +02:00
cf8cce72a5 fix: update weight field in update payload to only include values greater than 10 2025-04-23 11:08:32 +02:00
0b356609d1 fix: add weight field to update payload in updateSpoolTagId function 2025-04-23 11:07:30 +02:00
01f1e123ac fix: increase stack size for sendToApi task to improve stability 2025-04-15 16:38:16 +02:00
012f91851e fix: adjust tare weight tolerance to ignore deviations of 2g 2025-03-31 10:59:54 +02:00
9ed3c70c01 fix: improve weight stability check before sending to API 2025-03-31 10:08:26 +02:00
e23f3a2151 docs: update changelog and header for version v1.5.1
All checks were successful
Release Workflow / detect-provider (push) Successful in 3s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 2m52s
2025-03-30 16:38:38 +02:00
f73306f0b9 chore: update version to 1.5.1 and improve OTA update handling with task management 2025-03-30 16:38:23 +02:00
a450d4bd1a docs: update changelog and header for version v1.4.14
All checks were successful
Release Workflow / detect-provider (push) Successful in 3s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 2m42s
2025-03-30 16:01:45 +02:00
d48d994c00 docs: update platformio.ini for version v1.4.14 2025-03-30 16:01:45 +02:00
32bb85f897 feat: add auto-tare functionality and update scale handling based on touch sensor connection 2025-03-30 16:01:17 +02:00
e9d32ee060 docs: update changelog and header for version v1.4.13
All checks were successful
Release Workflow / detect-provider (push) Successful in 5s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 2m44s
2025-03-30 12:59:57 +02:00
aba28422bd docs: update platformio.ini for version v1.4.13 2025-03-30 12:59:57 +02:00
4a55620d39 fix: update touch sensor connection logic to correctly identify connection status 2025-03-30 12:59:51 +02:00
7b18266534 docs: update changelog and header for version v1.4.12
Some checks failed
Release Workflow / detect-provider (push) Successful in 3s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Failing after 2m16s
2025-03-30 12:55:33 +02:00
d81acb2b61 docs: update platformio.ini for version v1.4.12 2025-03-30 12:55:33 +02:00
8c7fc159d3 fix: add touch sensor connection check and update logic 2025-03-30 12:55:26 +02:00
476d3e82e2 docs: update README files to clarify PN532 DIP switch settings 2025-03-30 12:35:50 +02:00
3c294a135f docs: update changelog and header for version v1.4.11
All checks were successful
Release Workflow / detect-provider (push) Successful in 2s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 2m56s
2025-03-30 12:21:57 +02:00
bb751b6289 docs: update platformio.ini for version v1.4.11 2025-03-30 12:21:57 +02:00
7fd01bd1b9 Merge branch 'main' of github.com:ManuelW77/Filaman 2025-03-30 12:21:04 +02:00
fad84e12c8 docs: update changelog and header for version v1.4.10
All checks were successful
Release Workflow / detect-provider (push) Successful in 3s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m6s
2025-03-30 12:19:25 +02:00
696efc4d79 docs: update platformio.ini for version v1.4.10 2025-03-30 12:19:25 +02:00
29868e7101 fix: update TTP223 pin configuration and adjust touch sensor logic 2025-03-30 12:19:07 +02:00
823db6157c fix: add manual tare functionality for scale 2025-03-29 14:44:33 +01:00
458cc4eaf2 Merge pull request #31 from janecker/nfc_rework
Introducing enum for handling the NFC state to improve code readability
2025-03-29 14:25:20 +01:00
83d14b32d1 fix: add debounce handling for TTP223 touch sensor 2025-03-29 14:23:55 +01:00
2bf7c9fb7d feat: add TTP223 touch sensor support and wiring configuration 2025-03-29 14:18:58 +01:00
Jan Philipp Ecker
ac8adca84d Renamed states of NFC state machine and introduced new state machine for spoolman API 2025-03-29 13:21:47 +01:00
Jan Philipp Ecker
c701149c64 Introducing enum for handling the NFC state to improve code readability 2025-03-29 11:45:38 +01:00
31 changed files with 963 additions and 305 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/extensions.json
.vscode/settings.json
data

View File

@@ -1,5 +1,131 @@
# Changelog # Changelog
## [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 ## [1.4.9] - 2025-03-29
### Changed ### Changed
- update platformio.ini for version v1.4.9 - update platformio.ini for version v1.4.9

View File

@@ -54,20 +54,23 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
## Hardware-Anforderungen ## Hardware-Anforderungen
### Komponenten ### Komponenten (Affiliate Links)
- **ESP32 Entwicklungsboard:** Jede ESP32-Variante. - **ESP32 Development Board:** Any ESP32 variant.
[Amazon Link](https://amzn.eu/d/aXThslf) [Amazon Link](https://amzn.to/3FHea6D)
- **HX711 5kg Wägezellen-Verstärker:** Für Gewichtsmessung. - **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 weiß/gelb Display:** 128x64 SSD1306. - **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:** Für NFC-Tag-Operationen. - **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 NTA215:** RFID Tag - **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-Konfiguration
| Komponente | ESP32 Pin | ### Pin Konfiguration
| Component | ESP32 Pin |
|-------------------|-----------| |-------------------|-----------|
| HX711 DOUT | 16 | | HX711 DOUT | 16 |
| HX711 SCK | 17 | | HX711 SCK | 17 |
@@ -77,14 +80,22 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
| PN532 RESET | 33 | | PN532 RESET | 33 |
| PN532 SDA | 21 | | PN532 SDA | 21 |
| PN532 SCL | 22 | | 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**
![Wiring](./img/Schaltplan.png) ![Wiring](./img/Schaltplan.png)
![myWiring](./img/IMG_2589.jpeg) ![myWiring](./img/IMG_2589.jpeg)
![myWiring](./img/IMG_2590.jpeg) ![myWiring](./img/IMG_2590.jpeg)
*Die Wägezelle wird bei den meisten HX711 Modulen folgendermaßen verkabelt:
E+ rot
E- schwarz
A- weiß
A+ grün*
## Software-Abhängigkeiten ## Software-Abhängigkeiten
### ESP32-Bibliotheken ### ESP32-Bibliotheken

View File

@@ -58,17 +58,19 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
## Hardware Requirements ## Hardware Requirements
### Components ### Components (Affiliate Links)
- **ESP32 Development Board:** Any ESP32 variant. - **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. - **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. - **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. - **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 - **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 ### Pin Configuration
@@ -82,14 +84,22 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
| PN532 RESET | 33 | | PN532 RESET | 33 |
| PN532 SDA | 21 | | PN532 SDA | 21 |
| PN532 SCL | 22 | | 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**
![Wiring](./img/Schaltplan.png) ![Wiring](./img/Schaltplan.png)
![myWiring](./img/IMG_2589.jpeg) ![myWiring](./img/IMG_2589.jpeg)
![myWiring](./img/IMG_2590.jpeg) ![myWiring](./img/IMG_2590.jpeg)
*The load cell is connected to most HX711 modules as follows:
E+ red
E- black
A- white
A+ green*
## Software Dependencies ## Software Dependencies
### ESP32 Libraries ### ESP32 Libraries

BIN
html/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"bambu_ip": "192.168.1.14", "bambu_accesscode": "22772584", "bambu_serialnr": "01P00C492600230","autoSendToBambu":true,"autoSendTime": 60}

View File

@@ -139,6 +139,18 @@
<p id="nfcInfo" class="nfc-status"></p> <p id="nfcInfo" class="nfc-status"></p>
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button> <button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
</div> </div>
<div class="feature-box">
<h2>Spoolman Locations</h2>
<label for="locationSelect">Location:</label>
<div style="display: flex; justify-content: space-between; align-items: center;">
<select id="locationSelect" class="styled-select">
<option value="">Please choose...</option>
</select>
</div>
<p id="nfcInfoLocation" class="nfc-status"></p>
<button id="writeLocationNfcButton" class="btn btn-primary hidden" onclick="writeLocationNfcTag()">Write Location Tag</button>
</div>
</div> </div>
</div> </div>

View File

@@ -215,20 +215,6 @@ document.addEventListener('filamentSelected', function (event) {
updateSpoolButtons(selectedText !== "Please choose..."); 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() { function updateNfcInfo() {
const selectedText = document.getElementById("selected-filament").textContent; const selectedText = document.getElementById("selected-filament").textContent;
const nfcInfo = document.getElementById("nfcInfo"); const nfcInfo = document.getElementById("nfcInfo");
@@ -569,7 +555,10 @@ function updateNfcData(data) {
} }
// HTML für die Datenanzeige erstellen // HTML für die Datenanzeige erstellen
let html = ` let html = "";
if(data.sm_id){
html = `
<div class="nfc-card-data" style="margin-top: 10px;"> <div class="nfc-card-data" style="margin-top: 10px;">
<p><strong>Brand:</strong> ${data.brand || 'N/A'}</p> <p><strong>Brand:</strong> ${data.brand || 'N/A'}</p>
<p><strong>Type:</strong> ${data.type || 'N/A'} ${data.color_hex ? `<span style=" <p><strong>Type:</strong> ${data.type || 'N/A'} ${data.color_hex ? `<span style="
@@ -582,10 +571,27 @@ function updateNfcData(data) {
border-radius: 3px; border-radius: 3px;
margin-left: 5px; margin-left: 5px;
"></span>` : ''}</p> "></span>` : ''}</p>
`; `;
// Spoolman ID anzeigen // Spoolman ID anzeigen
html += `<p><strong>Spoolman ID:</strong> ${data.sm_id || 'No Spoolman ID'}</p>`; 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 // Nur wenn eine sm_id vorhanden ist, aktualisiere die Dropdowns
if (data.sm_id) { if (data.sm_id) {
@@ -661,16 +667,56 @@ function writeNfcTag() {
} }
} }
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',
payload: nfcData
}));
} else {
alert('Not connected to Server. Please check connection.');
}
}
function handleWriteNfcTagResponse(success) { function handleWriteNfcTagResponse(success) {
const writeButton = document.getElementById("writeNfcButton"); const writeButton = document.getElementById("writeNfcButton");
writeButton.classList.remove("writing"); const writeLocationButton = document.getElementById("writeLocationNfcButton");
writeButton.classList.add(success ? "success" : "error"); if(writeButton.classList.contains("writing")){
writeButton.textContent = success ? "Write success" : "Write failed"; writeButton.classList.remove("writing");
writeButton.classList.add(success ? "success" : "error");
writeButton.textContent = success ? "Write success" : "Write failed";
setTimeout(() => { setTimeout(() => {
writeButton.classList.remove("success", "error"); writeButton.classList.remove("success", "error");
writeButton.textContent = "Write Tag"; writeButton.textContent = "Write Tag";
}, 5000); }, 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) { function showNotification(message, isSuccess) {

View File

@@ -139,6 +139,18 @@
<p id="nfcInfo" class="nfc-status"></p> <p id="nfcInfo" class="nfc-status"></p>
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button> <button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write Tag</button>
</div> </div>
<div class="feature-box">
<h2>Spoolman Locations</h2>
<label for="locationSelect">Location:</label>
<div style="display: flex; justify-content: space-between; align-items: center;">
<select id="locationSelect" class="styled-select">
<option value="">Please choose...</option>
</select>
</div>
<p id="nfcInfoLocation" class="nfc-status"></p>
<button id="writeLocationNfcButton" class="btn btn-primary hidden" onclick="writeLocationNfcTag()">Write Location Tag</button>
</div>
</div> </div>
<!-- Rechte Spalte --> <!-- Rechte Spalte -->

View File

@@ -146,20 +146,20 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5> <h5 class="card-title">Set URL/IP to your Spoolman instance</h5>
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port"> <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 Spool to Spoolman Octoprint Plugin:</h5> <h5 class="card-title">If you want to enable sending the spool to the Spoolman Octoprint plugin:</h5>
<p> <p>
<input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin <input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin
</p> </p>
<div id="octoFields" style="display: none;"> <div id="octoFields" style="display: none;">
<p> <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}}"> <input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
</p> </p>
</div> </div>
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button> <button id="btnSaveSpoolmanUrl" onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
<p id="statusMessage"></p> <p id="statusMessage"></p>
</div> </div>
</div> </div>
@@ -169,16 +169,16 @@
<h5 class="card-title">Bambu Lab Printer Credentials</h5> <h5 class="card-title">Bambu Lab Printer Credentials</h5>
<div class="bambu-settings"> <div class="bambu-settings">
<div class="input-group"> <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}}"> <input type="text" id="bambuIp" placeholder="192.168.1.xxx" value="{{bambuIp}}">
</div> </div>
<div class="input-group"> <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}}"> <input type="text" id="bambuSerial" placeholder="BBLXXXXXXXX" value="{{bambuSerial}}">
</div> </div>
<div class="input-group"> <div class="input-group">
<label for="bambuCode">Access Code:</label> <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> </div>
<hr> <hr>
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p> <p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>

View File

@@ -1,6 +1,7 @@
// Globale Variablen // Globale Variablen
let spoolmanUrl = ''; let spoolmanUrl = '';
let spoolsData = []; let spoolsData = [];
let locationData = [];
// Hilfsfunktionen für Datenmanipulation // Hilfsfunktionen für Datenmanipulation
function processSpoolData(data) { function processSpoolData(data) {
@@ -133,6 +134,26 @@ function populateVendorDropdown(data, selectedSmId = null) {
} }
} }
// Dropdown-Funktionen
function populateLocationDropdown(data) {
const locationSelect = document.getElementById("locationSelect");
if (!locationSelect) {
console.error('locationSelect Element nicht gefunden');
return;
}
locationSelect.innerHTML = '<option value="">Bitte wählen...</option>';
// Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert
Object.entries(data)
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name
.forEach(([id, name]) => {
const option = document.createElement("option");
option.value = name;
option.textContent = name;
locationSelect.appendChild(option);
});
}
function updateFilamentDropdown(selectedSmId = null) { function updateFilamentDropdown(selectedSmId = null) {
const vendorId = document.getElementById("vendorSelect").value; const vendorId = document.getElementById("vendorSelect").value;
const dropdownContentInner = document.getElementById("filament-dropdown-content"); const dropdownContentInner = document.getElementById("filament-dropdown-content");
@@ -169,9 +190,32 @@ function updateFilamentDropdown(selectedSmId = null) {
option.setAttribute("data-value", spool.filament.id); option.setAttribute("data-value", spool.filament.id);
option.setAttribute("data-nfc-id", spool.extra.nfc_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 = ` option.innerHTML = `
<div class="option-color" style="background-color: #${colorHex}"></div> ${colorHTML}
<span>${spool.id} | ${spool.filament.name} (${spool.filament.material})</span> <span>${spool.id} | ${spool.filament.name} (${spool.filament.material})</span>
`; `;
@@ -185,12 +229,41 @@ function updateFilamentDropdown(selectedSmId = null) {
} }
} }
function updateLocationSelect(){
const writeLocationNfcButton = document.getElementById('writeLocationNfcButton');
if(writeLocationNfcButton){
writeLocationNfcButton.classList.remove("hidden");
}
}
function selectFilament(spool) { function selectFilament(spool) {
const selectedColor = document.getElementById("selected-color"); const selectedColor = document.getElementById("selected-color");
const selectedText = document.getElementById("selected-filament"); const selectedText = document.getElementById("selected-filament");
const dropdownContent = document.getElementById("filament-dropdown-content"); 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})`; selectedText.textContent = `${spool.id} | ${spool.filament.name} (${spool.filament.material})`;
dropdownContent.classList.remove("show"); dropdownContent.classList.remove("show");
@@ -216,10 +289,18 @@ async function initSpoolman() {
const fetchedData = await fetchSpoolData(); const fetchedData = await fetchSpoolData();
spoolsData = processSpoolData(fetchedData); spoolsData = processSpoolData(fetchedData);
document.dispatchEvent(new CustomEvent('spoolDataLoaded', { document.dispatchEvent(new CustomEvent('spoolDataLoaded', {
detail: spoolsData detail: spoolsData
})); }));
locationData = await fetchLocationData();
document.dispatchEvent(new CustomEvent('locationDataLoaded', {
detail: locationData
}));
} catch (error) { } catch (error) {
console.error('Fehler beim Initialisieren von Spoolman:', error); console.error('Fehler beim Initialisieren von Spoolman:', error);
document.dispatchEvent(new CustomEvent('spoolmanError', { document.dispatchEvent(new CustomEvent('spoolmanError', {
@@ -247,6 +328,25 @@ async function fetchSpoolData() {
} }
} }
async function fetchLocationData() {
try {
if (!spoolmanUrl) {
throw new Error('Spoolman URL ist nicht initialisiert');
}
const response = await fetch(`${spoolmanUrl}/api/v1/location`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fehler beim Abrufen der Location-Daten:', error);
return [];
}
}
// Event Listener // Event Listener
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initSpoolman(); initSpoolman();
@@ -255,6 +355,11 @@ document.addEventListener('DOMContentLoaded', () => {
if (vendorSelect) { if (vendorSelect) {
vendorSelect.addEventListener('change', () => updateFilamentDropdown()); vendorSelect.addEventListener('change', () => updateFilamentDropdown());
} }
const locationSelect = document.getElementById('locationSelect');
if (locationSelect) {
locationSelect.addEventListener('change', () => updateLocationSelect());
}
const onlyWithoutSmId = document.getElementById('onlyWithoutSmId'); const onlyWithoutSmId = document.getElementById('onlyWithoutSmId');
if (onlyWithoutSmId) { if (onlyWithoutSmId) {
@@ -267,6 +372,10 @@ document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('spoolDataLoaded', (event) => { document.addEventListener('spoolDataLoaded', (event) => {
populateVendorDropdown(event.detail); populateVendorDropdown(event.detail);
}); });
document.addEventListener('locationDataLoaded', (event) => {
populateLocationDropdown(event.detail);
});
window.onclick = function(event) { window.onclick = function(event) {
if (!event.target.closest('.custom-dropdown')) { if (!event.target.closest('.custom-dropdown')) {
@@ -297,6 +406,7 @@ window.getSpoolData = () => spoolsData;
window.setSpoolData = (data) => { spoolsData = data; }; window.setSpoolData = (data) => { spoolsData = data; };
window.reloadSpoolData = initSpoolman; window.reloadSpoolData = initSpoolman;
window.populateVendorDropdown = populateVendorDropdown; window.populateVendorDropdown = populateVendorDropdown;
window.populateLocationDropdown = populateLocationDropdown;
window.updateFilamentDropdown = updateFilamentDropdown; window.updateFilamentDropdown = updateFilamentDropdown;
window.toggleFilamentDropdown = () => { window.toggleFilamentDropdown = () => {
const content = document.getElementById("filament-dropdown-content"); const content = document.getElementById("filament-dropdown-content");

1
html/spoolman_url.json Normal file
View File

@@ -0,0 +1 @@
{"url": "http://192.168.1.5:7912", "octoEnabled": true, "octoUrl": "http://192.168.1.17:5001", "octoToken": "O5zZ58mXRAyeGpVEj2ZZj-UPAPqJ2N7JgtD36mw1M4g"}

View File

@@ -759,6 +759,50 @@ a:hover {
flex-shrink: 0; 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 { .notification {
position: fixed; position: fixed;
top: 20px; top: 20px;
@@ -927,31 +971,35 @@ input[type="submit"]:disabled,
} }
/* Schreib-Button */ /* Schreib-Button */
#writeNfcButton { #writeNfcButton, #writeLocationNfcButton {
background-color: #007bff; background-color: #007bff;
color: white; color: white;
transition: background-color 0.3s, color 0.3s; transition: background-color 0.3s, color 0.3s;
width: 160px; width: 160px;
} }
#writeNfcButton.writing { #writeNfcButton.writing, #writeLocationNfcButton.writing {
background-color: #ffc107; background-color: #ffc107;
color: black; color: black;
width: 160px; width: 160px;
} }
#writeNfcButton.success { #writeNfcButton.success, #writeLocationNfcButton.success {
background-color: #28a745; background-color: #28a745;
color: white; color: white;
width: 160px; width: 160px;
} }
#writeNfcButton.error { #writeNfcButton.error, #writeLocationNfcButton.error {
background-color: #dc3545; background-color: #dc3545;
color: white; color: white;
width: 160px; width: 160px;
} }
#writeLocationNfcButton{
width: 250px;
}
@keyframes dots { @keyframes dots {
0% { content: ""; } 0% { content: ""; }
33% { content: "."; } 33% { content: "."; }
@@ -959,7 +1007,7 @@ input[type="submit"]:disabled,
100% { content: "..."; } 100% { content: "..."; }
} }
#writeNfcButton.writing::after { #writeNfcButton.writing::after, #writeLocationNfcButton.writing::after {
content: "..."; content: "...";
animation: dots 1s steps(3, end) infinite; animation: dots 1s steps(3, end) infinite;
} }

View File

@@ -55,6 +55,7 @@
<h5 class="card-title">Sacle Calibration</h5> <h5 class="card-title">Sacle Calibration</h5>
<button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button> <button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button>
<button id="tareBtn" class="btn btn-secondary">Tare Scale</button> <button id="tareBtn" class="btn btn-secondary">Tare Scale</button>
&nbsp;&nbsp;&nbsp;Enable Auto-TARE <input type="checkbox" id="autoTareCheckbox" onchange="setAutoTare(this.checked);" {{autoTare}}>
<div id="statusMessage" class="mt-3"></div> <div id="statusMessage" class="mt-3"></div>
</div> </div>
</div> </div>
@@ -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 // WebSocket-Verbindung beim Laden der Seite initiieren
connectWebSocket(); connectWebSocket();
</script> </script>

View File

@@ -9,8 +9,8 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common] [common]
version = "1.4.9" version = "1.5.6"
to_old_version = "1.4.0" to_old_version = "1.5.0"
## ##
[env:esp32dev] [env:esp32dev]
@@ -18,6 +18,7 @@ platform = espressif32
board = esp32dev board = esp32dev
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
#monitor_port = /dev/cu.usbmodem01
lib_deps = lib_deps =
tzapu/WiFiManager @ ^2.0.17 tzapu/WiFiManager @ ^2.0.17
@@ -51,6 +52,7 @@ build_flags =
-mtext-section-literals -mtext-section-literals
-DVERSION=\"${common.version}\" -DVERSION=\"${common.version}\"
-DTOOLDVERSION=\"${common.to_old_version}\" -DTOOLDVERSION=\"${common.to_old_version}\"
#-DENABLE_HEAP_DEBUGGING
-DASYNCWEBSERVER_REGEX -DASYNCWEBSERVER_REGEX
#-DCORE_DEBUG_LEVEL=3 #-DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS=1 -DCONFIG_ARDUHAL_LOG_COLORS=1

View File

@@ -14,7 +14,7 @@ def copy_file(input_file, output_file):
def should_compress(file): def should_compress(file):
# Skip compression for spoolman.html # Skip compression for spoolman.html
if file == 'spoolman.html': if file == 'spoolman.html' or file == 'waage.html':
return False return False
# Komprimiere nur bestimmte Dateitypen # Komprimiere nur bestimmte Dateitypen
return file.endswith(('.js', '.png', '.css', '.html')) return file.endswith(('.js', '.png', '.css', '.html'))

View File

@@ -2,14 +2,18 @@
#include <HTTPClient.h> #include <HTTPClient.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "commonFS.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 = ""; String spoolmanUrl = "";
bool octoEnabled = false; bool octoEnabled = false;
String octoUrl = ""; String octoUrl = "";
String octoToken = ""; String octoToken = "";
struct SendToApiParams { struct SendToApiParams {
SpoolmanApiRequestType requestType;
String httpType; String httpType;
String spoolsUrl; String spoolsUrl;
String updatePayload; String updatePayload;
@@ -85,9 +89,13 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
} }
void sendToApi(void *parameter) { void sendToApi(void *parameter) {
HEAP_DEBUG_MESSAGE("sendToApi begin");
spoolmanApiState = API_TRANSMITTING;
SendToApiParams* params = (SendToApiParams*)parameter; SendToApiParams* params = (SendToApiParams*)parameter;
// Extrahiere die Werte // Extrahiere die Werte
SpoolmanApiRequestType requestType = params->requestType;
String httpType = params->httpType; String httpType = params->httpType;
String spoolsUrl = params->spoolsUrl; String spoolsUrl = params->spoolsUrl;
String updatePayload = params->updatePayload; String updatePayload = params->updatePayload;
@@ -107,8 +115,31 @@ void sendToApi(void *parameter) {
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
Serial.println("Spoolman erfolgreich aktualisiert"); 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 {
if (requestType == API_REQUEST_SPOOL_WEIGHT_UPDATE) {
uint16_t remaining_weight = doc["remaining_weight"].as<float>();
Serial.print("Aktuelles Gewicht: ");
Serial.println(remaining_weight);
oledShowMessage("Remaining: " + String(remaining_weight) + "g");
}
else if ( requestType == API_REQUEST_SPOOL_LOCATION_UPDATE) {
oledShowMessage("Location updated!");
}
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
doc.clear();
} else { } else {
Serial.println("Fehler beim Senden an Spoolman!"); Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode));
oledShowMessage("Spoolman update failed"); oledShowMessage("Spoolman update failed");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
@@ -118,6 +149,8 @@ void sendToApi(void *parameter) {
// Speicher freigeben // Speicher freigeben
delete params; delete params;
HEAP_DEBUG_MESSAGE("sendToApi end");
spoolmanApiState = API_IDLE;
vTaskDelete(NULL); vTaskDelete(NULL);
} }
@@ -141,6 +174,8 @@ bool updateSpoolTagId(String uidString, const char* payload) {
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
doc.clear();
// Update Payload erstellen // Update Payload erstellen
JsonDocument updateDoc; JsonDocument updateDoc;
updateDoc["extra"]["nfc_id"] = "\""+uidString+"\""; updateDoc["extra"]["nfc_id"] = "\""+uidString+"\"";
@@ -155,6 +190,7 @@ bool updateSpoolTagId(String uidString, const char* payload) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren."); Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return false; return false;
} }
params->requestType = API_REQUEST_SPOOL_TAG_ID_UPDATE;
params->httpType = "PATCH"; params->httpType = "PATCH";
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
@@ -163,7 +199,7 @@ bool updateSpoolTagId(String uidString, const char* payload) {
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name "SendToApiTask", // Task-Name
4096, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
@@ -171,10 +207,14 @@ bool updateSpoolTagId(String uidString, const char* payload) {
updateDoc.clear(); updateDoc.clear();
// Update Spool weight
if (weight > 10) updateSpoolWeight(doc["sm_id"].as<String>(), weight);
return true; return true;
} }
uint8_t updateSpoolWeight(String spoolId, uint16_t weight) { uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
HEAP_DEBUG_MESSAGE("updateSpoolWeight begin");
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId + "/measure"; String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId + "/measure";
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -193,6 +233,7 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren."); Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return 0; return 0;
} }
params->requestType = API_REQUEST_SPOOL_WEIGHT_UPDATE;
params->httpType = "PUT"; params->httpType = "PUT";
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
@@ -201,7 +242,49 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name "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");
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;
// Erstelle die Task
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
@@ -209,6 +292,7 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
updateDoc.clear(); updateDoc.clear();
HEAP_DEBUG_MESSAGE("updateSpoolLocation end");
return 1; return 1;
} }
@@ -231,6 +315,7 @@ bool updateSpoolOcto(int spoolId) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren."); Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return false; return false;
} }
params->requestType = API_REQUEST_OCTO_SPOOL_UPDATE;
params->httpType = "POST"; params->httpType = "POST";
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
@@ -240,7 +325,7 @@ bool updateSpoolOcto(int spoolId) {
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name "SendToApiTask", // Task-Name
4096, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
@@ -272,6 +357,10 @@ bool updateSpoolBambuData(String payload) {
String updatePayload; String updatePayload;
serializeJson(updateDoc, updatePayload); serializeJson(updateDoc, updatePayload);
doc.clear();
updateDoc.clear();
Serial.print("Update Payload: "); Serial.print("Update Payload: ");
Serial.println(updatePayload); Serial.println(updatePayload);
@@ -280,6 +369,7 @@ bool updateSpoolBambuData(String payload) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren."); Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return false; return false;
} }
params->requestType = API_REQUEST_BAMBU_UPDATE;
params->httpType = "PATCH"; params->httpType = "PATCH";
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
@@ -288,7 +378,7 @@ bool updateSpoolBambuData(String payload) {
BaseType_t result = xTaskCreate( BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name "SendToApiTask", // Task-Name
4096, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
@@ -437,6 +527,7 @@ bool checkSpoolmanExtraFields() {
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
} }
} }
doc.clear();
} }
} }
@@ -479,51 +570,52 @@ bool checkSpoolmanInstance(const String& url) {
return false; return false;
} }
spoolman_connected = true; spoolmanApiState = API_IDLE;
oledShowTopRow();
return strcmp(status, "healthy") == 0; return strcmp(status, "healthy") == 0;
} }
doc.clear();
} }
} else {
Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode));
} }
http.end(); http.end();
return false; return false;
} }
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) { bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) {
if (!checkSpoolmanInstance(url)) return false; 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; //TBD: This could be handled nicer in the future
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;
}
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn; octoEnabled = octoOn;
octoUrl = octoWh; octoUrl = octo_url;
octoToken = octoTk; octoToken = octoTk;
doc.clear();
return true; return true;
} }
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
JsonDocument doc; Preferences preferences;
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) { preferences.begin(NVS_NAMESPACE_API, true);
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false; String spoolmanUrl = preferences.getString(NVS_KEY_SPOOLMAN_URL, "");
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>()) octoEnabled = preferences.getBool(NVS_KEY_OCTOPRINT_ENABLED, false);
{ if(octoEnabled)
octoUrl = doc["octoUrl"].as<String>(); {
octoToken = doc["octoToken"].as<String>(); octoUrl = preferences.getString(NVS_KEY_OCTOPRINT_URL, "");
} octoToken = preferences.getString(NVS_KEY_OCTOPRINT_TOKEN, "");
return doc["url"].as<String>();
} }
Serial.println("Keine gültige Spoolman-URL gefunden."); preferences.end();
return ""; return spoolmanUrl;
} }
bool initSpoolman() { bool initSpoolman() {

View File

@@ -6,7 +6,21 @@
#include "website.h" #include "website.h"
#include "display.h" #include "display.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
typedef enum {
API_INIT,
API_IDLE,
API_TRANSMITTING
} spoolmanApiStateType;
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
} SpoolmanApiRequestType;
extern volatile spoolmanApiStateType spoolmanApiState;
extern bool spoolman_connected; extern bool spoolman_connected;
extern String spoolmanUrl; extern String spoolmanUrl;
extern bool octoEnabled; extern bool octoEnabled;
@@ -20,6 +34,7 @@ bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafeld
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion zum Aktualisieren eines Spools 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 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 initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten

View File

@@ -10,6 +10,7 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "config.h" #include "config.h"
#include "display.h" #include "display.h"
#include <Preferences.h>
WiFiClient espClient; WiFiClient espClient;
SSLClient sslClient(&espClient); SSLClient sslClient(&espClient);
@@ -17,22 +18,13 @@ PubSubClient client(sslClient);
TaskHandle_t BambuMqttTask; TaskHandle_t BambuMqttTask;
String topic = "";
//String request_topic = "";
const char* bambu_username = "bblp";
const char* bambu_ip = nullptr;
const char* bambu_accesscode = nullptr;
const char* bambu_serialnr = nullptr;
String g_bambu_ip = "";
String g_bambu_accesscode = "";
String g_bambu_serialnr = "";
bool bambuDisabled = false; bool bambuDisabled = false;
bool bambu_connected = false; bool bambu_connected = false;
bool autoSendToBambu = false;
int autoSetToBambuSpoolId = 0; int autoSetToBambuSpoolId = 0;
BambuCredentials bambuCredentials;
// Globale Variablen für AMS-Daten // Globale Variablen für AMS-Daten
int ams_count = 0; int ams_count = 0;
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
@@ -43,18 +35,22 @@ bool removeBambuCredentials() {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
} }
if (!removeJsonValue("/bambu_credentials.json")) { Preferences preferences;
Serial.println("Fehler beim Löschen der Bambu-Credentials."); preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
return false; preferences.remove(NVS_KEY_BAMBU_IP);
} preferences.remove(NVS_KEY_BAMBU_SERIAL);
preferences.remove(NVS_KEY_BAMBU_ACCESSCODE);
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_ENABLE);
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_TIME);
preferences.end();
// Löschen der globalen Variablen // Löschen der globalen Variablen
g_bambu_ip = ""; bambuCredentials.ip = "";
g_bambu_accesscode = ""; bambuCredentials.serial = "";
g_bambu_serialnr = ""; bambuCredentials.accesscode = "";
bambu_ip = nullptr; bambuCredentials.autosend_enable = false;
bambu_accesscode = nullptr; bambuCredentials.autosend_time = BAMBU_DEFAULT_AUTOSEND_TIME;
bambu_serialnr = nullptr;
autoSendToBambu = false;
autoSetToBambuSpoolId = 0; autoSetToBambuSpoolId = 0;
ams_count = 0; ams_count = 0;
amsJsonData = ""; amsJsonData = "";
@@ -68,25 +64,21 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
} }
JsonDocument doc;
doc["bambu_ip"] = ip;
doc["bambu_accesscode"] = accesscode;
doc["bambu_serialnr"] = serialnr;
doc["autoSendToBambu"] = autoSend;
doc["autoSendTime"] = (autoSendTime != "") ? autoSendTime.toInt() : autoSetBambuAmsCounter;
if (!saveJsonValue("/bambu_credentials.json", doc)) { bambuCredentials.ip = ip.c_str();
Serial.println("Fehler beim Speichern der Bambu-Credentials."); bambuCredentials.serial = serialnr.c_str();
return false; bambuCredentials.accesscode = accesscode.c_str();
} bambuCredentials.autosend_enable = autoSend;
bambuCredentials.autosend_time = autoSendTime.toInt();
// Dynamische Speicherallokation für die globalen Pointer Preferences preferences;
bambu_ip = ip.c_str(); preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
bambu_accesscode = accesscode.c_str(); preferences.putString(NVS_KEY_BAMBU_IP, bambuCredentials.ip);
bambu_serialnr = serialnr.c_str(); preferences.putString(NVS_KEY_BAMBU_SERIAL, bambuCredentials.serial);
autoSendToBambu = autoSend; preferences.putString(NVS_KEY_BAMBU_ACCESSCODE, bambuCredentials.accesscode);
autoSetBambuAmsCounter = autoSendTime.toInt(); 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); vTaskDelay(100 / portTICK_PERIOD_MS);
if (!setupMqtt()) return false; if (!setupMqtt()) return false;
@@ -95,35 +87,36 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
} }
bool loadBambuCredentials() { bool loadBambuCredentials() {
JsonDocument doc; Preferences preferences;
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) { preferences.begin(NVS_NAMESPACE_BAMBU, true);
// Temporäre Strings für die Werte String ip = preferences.getString(NVS_KEY_BAMBU_IP, "");
String ip = doc["bambu_ip"].as<String>(); String serial = preferences.getString(NVS_KEY_BAMBU_SERIAL, "");
String code = doc["bambu_accesscode"].as<String>(); String code = preferences.getString(NVS_KEY_BAMBU_ACCESSCODE, "");
String serial = doc["bambu_serialnr"].as<String>(); 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; if(ip != ""){
g_bambu_accesscode = code; bambuCredentials.ip = ip.c_str();
g_bambu_serialnr = serial; 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>(); Serial.println("credentials loaded loadCredentials!");
if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>(); Serial.println(bambuCredentials.ip);
Serial.println(bambuCredentials.serial);
Serial.println(bambuCredentials.accesscode);
Serial.println(String(bambuCredentials.autosend_enable));
Serial.println(String(bambuCredentials.autosend_time));
ip.trim();
code.trim();
serial.trim();
// Dynamische Speicherallokation für die globalen Pointer
bambu_ip = g_bambu_ip.c_str();
bambu_accesscode = g_bambu_accesscode.c_str();
bambu_serialnr = g_bambu_serialnr.c_str();
topic = "device/" + String(bambu_serialnr);
//request_topic = "device/" + String(bambu_serialnr) + "/request";
return true; return true;
} }
Serial.println("Keine gültigen Bambu-Credentials gefunden."); else
return false; {
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
return false;
}
} }
struct FilamentResult { struct FilamentResult {
@@ -226,7 +219,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
bool sendMqttMessage(const String& payload) { bool sendMqttMessage(const String& payload) {
Serial.println("Sending MQTT message"); Serial.println("Sending MQTT message");
Serial.println(payload); Serial.println(payload);
if (client.publish((String(topic) + "/request").c_str(), payload.c_str())) if (client.publish(("device/"+bambuCredentials.serial+"/request").c_str(), payload.c_str()))
{ {
return true; return true;
} }
@@ -499,7 +492,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) { trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
{ {
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id); autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
} }
@@ -523,7 +516,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) { (vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
{ {
autoSetSpool(autoSetToBambuSpoolId, 254); autoSetSpool(autoSetToBambuSpoolId, 254);
} }
@@ -580,11 +573,11 @@ void reconnect() {
oledShowTopRow(); oledShowTopRow();
// Attempt to connect // Attempt to connect
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100)); String clientId = bambuCredentials.serial + "_" + String(random(0, 100));
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode)) { if (client.connect(clientId.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str())) {
Serial.println("MQTT re/connected"); Serial.println("MQTT re/connected");
client.subscribe((String(topic) + "/report").c_str()); client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
bambu_connected = true; bambu_connected = true;
oledShowTopRow(); oledShowTopRow();
} else { } else {
@@ -630,28 +623,23 @@ void mqtt_loop(void * parameter) {
bool setupMqtt() { bool setupMqtt() {
// Wenn Bambu Daten vorhanden // Wenn Bambu Daten vorhanden
bool success = loadBambuCredentials(); //bool success = loadBambuCredentials();
if (!success) { if (bambuCredentials.ip != "" && bambuCredentials.accesscode != "" && bambuCredentials.serial != "")
bambuDisabled = true;
return false;
}
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
{ {
bambuDisabled = false; bambuDisabled = false;
sslClient.setCACert(root_ca); sslClient.setCACert(root_ca);
sslClient.setInsecure(); sslClient.setInsecure();
client.setServer(bambu_ip, 8883); client.setServer(bambuCredentials.ip.c_str(), 8883);
// Verbinden mit dem MQTT-Server // Verbinden mit dem MQTT-Server
bool connected = true; bool connected = true;
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100)); String clientId = String(bambuCredentials.serial) + "_" + String(random(0, 100));
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode)) if (client.connect(bambuCredentials.ip.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str()))
{ {
client.setCallback(mqtt_callback); client.setCallback(mqtt_callback);
client.setBufferSize(15488); client.setBufferSize(15488);
client.subscribe((String(topic) + "/report").c_str()); client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
Serial.println("MQTT-Client initialisiert"); Serial.println("MQTT-Client initialisiert");
oledShowMessage("Bambu Connected"); oledShowMessage("Bambu Connected");
@@ -674,6 +662,7 @@ bool setupMqtt() {
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
connected = false; connected = false;
oledShowTopRow(); oledShowTopRow();
autoSetToBambuSpoolId = 0;
} }
if (!connected) return false; if (!connected) return false;
@@ -687,6 +676,8 @@ bool setupMqtt() {
} }
void bambu_restart() { void bambu_restart() {
Serial.println("Bambu restart");
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
delay(10); delay(10);

View File

@@ -16,6 +16,14 @@ struct TrayData {
String cali_idx; 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 #define MAX_AMS 17 // 16 normale AMS + 1 externe Spule
extern String amsJsonData; // Für die vorbereiteten JSON-Daten extern String amsJsonData; // Für die vorbereiteten JSON-Daten
@@ -28,9 +36,10 @@ extern bool bambu_connected;
extern int ams_count; extern int ams_count;
extern AMSData ams_data[MAX_AMS]; extern AMSData ams_data[MAX_AMS];
extern bool autoSendToBambu; //extern bool autoSendToBambu;
extern int autoSetToBambuSpoolId; extern int autoSetToBambuSpoolId;
extern bool bambuDisabled; extern bool bambuDisabled;
extern BambuCredentials bambuCredentials;
bool removeBambuCredentials(); bool removeBambuCredentials();
bool loadBambuCredentials(); bool loadBambuCredentials();

View File

@@ -19,6 +19,12 @@ const uint16_t SCALE_LEVEL_WEIGHT = 500;
uint16_t defaultScaleCalibrationValue = 430; uint16_t defaultScaleCalibrationValue = 430;
// ***** HX711 // ***** HX711
// ***** TTP223 (Touch Sensor)
// TTP223 circuit wiring
const uint8_t TTP223_PIN = 25;
// ***** TTP223
// ***** Display // ***** Display
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// On an ESP32: 21(SDA), 22(SCL) // On an ESP32: 21(SDA), 22(SCL)
@@ -40,8 +46,6 @@ const uint8_t webserverPort = 80;
const char* apiUrl = "/api/v1"; const char* apiUrl = "/api/v1";
// ***** API // ***** API
// ***** Bambu Auto Set Spool
uint8_t autoSetBambuAmsCounter = 60;
// ***** Bambu Auto Set Spool // ***** Bambu Auto Set Spool
// ***** Task Prios // ***** Task Prios

View File

@@ -3,6 +3,29 @@
#include <Arduino.h> #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"
extern const uint8_t PN532_IRQ; extern const uint8_t PN532_IRQ;
extern const uint8_t PN532_RESET; extern const uint8_t PN532_RESET;
@@ -11,6 +34,8 @@ extern const uint8_t LOADCELL_SCK_PIN;
extern const uint8_t calVal_eepromAdress; extern const uint8_t calVal_eepromAdress;
extern const uint16_t SCALE_LEVEL_WEIGHT; extern const uint16_t SCALE_LEVEL_WEIGHT;
extern const uint8_t TTP223_PIN;
extern const int8_t OLED_RESET; extern const int8_t OLED_RESET;
extern const uint8_t SCREEN_ADDRESS; extern const uint8_t SCREEN_ADDRESS;
extern const uint8_t SCREEN_WIDTH; extern const uint8_t SCREEN_WIDTH;
@@ -23,7 +48,7 @@ extern const uint8_t OLED_DATA_END;
extern const char* apiUrl; extern const char* apiUrl;
extern const uint8_t webserverPort; extern const uint8_t webserverPort;
extern uint8_t autoSetBambuAmsCounter;
extern const unsigned char wifi_on[]; extern const unsigned char wifi_on[];
extern const unsigned char wifi_off[]; extern const unsigned char wifi_off[];

12
src/debug.h Normal file
View 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(String location){
Serial.println("Heap: " + String(ESP.getMinFreeHeap()/1024) + "\t" + String(ESP.getFreeHeap()/1024) + "\t" + String(ESP.getMaxAllocHeap()/1024) + "\t" + location);
}

View File

@@ -177,7 +177,7 @@ void oledShowTopRow() {
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE); display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE);
} }
if (spoolman_connected == 1) { if (spoolmanApiState != API_INIT) {
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE); display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
} else { } else {
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE); display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);

View File

@@ -15,6 +15,7 @@
bool mainTaskWasPaused = 0; bool mainTaskWasPaused = 0;
uint8_t scaleTareCounter = 0; uint8_t scaleTareCounter = 0;
bool touchSensorConnected = false;
// ##### SETUP ##### // ##### SETUP #####
void setup() { void setup() {
@@ -39,7 +40,6 @@ void setup() {
setupWebserver(server); setupWebserver(server);
// Spoolman API // Spoolman API
// api.cpp
initSpoolman(); initSpoolman();
// Bambu MQTT // Bambu MQTT
@@ -48,7 +48,16 @@ void setup() {
// NFC Reader // NFC Reader
startNfc(); startNfc();
start_scale(); // Touch Sensor
pinMode(TTP223_PIN, INPUT_PULLUP);
if (digitalRead(TTP223_PIN) == LOW)
{
Serial.println("Touch Sensor is connected");
touchSensorConnected = true;
}
// Scale
start_scale(touchSensorConnected);
// WDT initialisieren mit 10 Sekunden Timeout // WDT initialisieren mit 10 Sekunden Timeout
bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus
@@ -84,13 +93,25 @@ uint8_t autoAmsCounter = 0;
uint8_t weightSend = 0; uint8_t weightSend = 0;
int16_t lastWeight = 0; int16_t lastWeight = 0;
// WIFI check variables
unsigned long lastWifiCheckTime = 0; unsigned long lastWifiCheckTime = 0;
const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms) const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms)
// Button debounce variables
unsigned long lastButtonPress = 0;
const unsigned long debounceDelay = 500; // 500 ms debounce delay
// ##### PROGRAM START ##### // ##### PROGRAM START #####
void loop() { void loop() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
// Ü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 // Überprüfe regelmäßig die WLAN-Verbindung
if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval)) if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval))
{ {
@@ -98,7 +119,7 @@ void loop() {
} }
// Wenn Bambu auto set Spool aktiv // Wenn Bambu auto set Spool aktiv
if (autoSendToBambu && autoSetToBambuSpoolId > 0) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
{ {
if (!bambuDisabled && !bambu_connected) if (!bambuDisabled && !bambu_connected)
{ {
@@ -107,13 +128,13 @@ void loop() {
if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval)) if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval))
{ {
if (hasReadRfidTag == 0) if (nfcReaderState == NFC_IDLE)
{ {
lastAutoSetBambuAmsTime = currentMillis; lastAutoSetBambuAmsTime = currentMillis;
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s"); oledShowMessage("Auto Set " + String(bambuCredentials.autosend_time - autoAmsCounter) + "s");
autoAmsCounter++; autoAmsCounter++;
if (autoAmsCounter >= autoSetBambuAmsCounter) if (autoAmsCounter >= bambuCredentials.autosend_time)
{ {
autoSetToBambuSpoolId = 0; autoSetToBambuSpoolId = 0;
autoAmsCounter = 0; autoAmsCounter = 0;
@@ -141,7 +162,7 @@ void loop() {
// Ausgabe der Waage auf Display // Ausgabe der Waage auf Display
if(pauseMainTask == 0) if(pauseMainTask == 0)
{ {
if (mainTaskWasPaused || (weight != lastWeight && hasReadRfidTag == 0 && (!autoSendToBambu || autoSetToBambuSpoolId == 0))) if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0)))
{ {
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight); (weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
} }
@@ -154,31 +175,23 @@ void loop() {
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird // Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag < 3) if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
{ {
lastWeightReadTime = currentMillis; lastWeightReadTime = currentMillis;
// Prüfen ob die Waage korrekt genullt ist // Prüfen ob die Waage korrekt genullt ist
if ((weight > 0 && weight < 5) || weight < -1) // Abweichung von 2g ignorieren
if (autoTare && (weight > 2 && weight < 7) || weight < -2)
{ {
if(scaleTareCounter < 5) scale_tare_counter++;
{
scaleTareCounter++;
}
else
{
scaleTareRequest = true;
scaleTareCounter = 0;
}
} }
else else
{ {
scaleTareCounter = 0; scale_tare_counter = 0;
} }
// Prüfen ob das Gewicht gleich bleibt und dann senden // Prüfen ob das Gewicht gleich bleibt und dann senden
if (weight == lastWeight && weight > 5) if (abs(weight - lastWeight) <= 2 && weight > 5)
{ {
weigthCouterToApi++; weigthCouterToApi++;
} }
@@ -190,7 +203,8 @@ void loop() {
} }
// reset weight counter after writing tag // reset weight counter after writing tag
if (currentMillis - lastWeightReadTime >= weightReadInterval && hasReadRfidTag > 1) // TBD: what exactly is the logic behind this?
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
{ {
weigthCouterToApi = 0; weigthCouterToApi = 0;
} }
@@ -198,14 +212,14 @@ void loop() {
lastWeight = weight; lastWeight = weight;
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden // Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && hasReadRfidTag == 1) { if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS) {
oledShowIcon("loading"); oledShowIcon("loading");
if (updateSpoolWeight(spoolId, weight)) if (updateSpoolWeight(activeSpoolId, weight))
{ {
oledShowIcon("success"); oledShowIcon("success");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
weightSend = 1; weightSend = 1;
autoSetToBambuSpoolId = spoolId.toInt(); autoSetToBambuSpoolId = activeSpoolId.toInt();
if (octoEnabled) if (octoEnabled)
{ {

View File

@@ -7,6 +7,7 @@
#include "api.h" #include "api.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "scale.h" #include "scale.h"
#include "bambu.h"
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS); //Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET); Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
@@ -14,11 +15,12 @@ Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
TaskHandle_t RfidReaderTask; TaskHandle_t RfidReaderTask;
JsonDocument rfidData; JsonDocument rfidData;
String spoolId = ""; String activeSpoolId = "";
String lastSpoolId = "";
String nfcJsonData = ""; String nfcJsonData = "";
volatile bool pauseBambuMqttTask = false; volatile bool pauseBambuMqttTask = false;
volatile uint8_t hasReadRfidTag = 0; volatile nfcReaderStateType nfcReaderState = NFC_IDLE;
// 0 = nicht gelesen // 0 = nicht gelesen
// 1 = erfolgreich gelesen // 1 = erfolgreich gelesen
// 2 = fehler beim Lesen // 2 = fehler beim Lesen
@@ -64,6 +66,8 @@ void payloadToJson(uint8_t *data) {
Serial.print("deserializeJson() failed: "); Serial.print("deserializeJson() failed: ");
Serial.println(error.f_str()); Serial.println(error.f_str());
} }
doc.clear();
} else { } else {
Serial.println("Kein gültiger JSON-Inhalt gefunden oder fehlerhafte Formatierung."); 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\"}"); //writeJsonToTag("{\"version\":\"1.0\",\"protocol\":\"NFC\",\"color_hex\":\"#FFFFFF\",\"type\":\"Example\",\"min_temp\":10,\"max_temp\":30,\"brand\":\"BrandName\"}");
@@ -218,20 +222,37 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
// Sende die aktualisierten AMS-Daten an alle WebSocket-Clients // Sende die aktualisierten AMS-Daten an alle WebSocket-Clients
Serial.println("JSON-Dokument erfolgreich verarbeitet"); Serial.println("JSON-Dokument erfolgreich verarbeitet");
Serial.println(doc.as<String>()); Serial.println(doc.as<String>());
if (doc["sm_id"] != "") if (doc["sm_id"].is<String>() && doc["sm_id"] != "")
{ {
Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>()); Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>());
spoolId = doc["sm_id"].as<String>(); activeSpoolId = doc["sm_id"].as<String>();
} lastSpoolId = activeSpoolId;
}
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!");
oledShowMessage("No spool scanned before!");
}
}
else else
{ {
Serial.println("Keine SPOOL-ID gefunden."); Serial.println("Keine SPOOL-ID gefunden.");
spoolId = ""; activeSpoolId = "";
oledShowMessage("Unknown Spool"); oledShowMessage("Unknown Spool");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
} }
doc.clear();
return true; return true;
} }
@@ -242,7 +263,7 @@ void writeJsonToTag(void *parameter) {
Serial.println("Erstelle NDEF-Message..."); Serial.println("Erstelle NDEF-Message...");
Serial.println(payload); Serial.println(payload);
hasReadRfidTag = 3; nfcReaderState = NFC_WRITING;
vTaskSuspend(RfidReaderTask); vTaskSuspend(RfidReaderTask);
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(50 / portTICK_PERIOD_MS);
@@ -288,7 +309,7 @@ void writeJsonToTag(void *parameter) {
//oledShowMessage("NFC-Tag written"); //oledShowMessage("NFC-Tag written");
oledShowIcon("success"); oledShowIcon("success");
vTaskDelay(1000 / portTICK_PERIOD_MS); vTaskDelay(1000 / portTICK_PERIOD_MS);
hasReadRfidTag = 5; nfcReaderState = NFC_WRITE_SUCCESS;
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData(nullptr);
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
@@ -310,7 +331,7 @@ void writeJsonToTag(void *parameter) {
Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag"); Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag");
oledShowIcon("failed"); oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 4; nfcReaderState = NFC_WRITE_ERROR;
} }
} }
else else
@@ -318,7 +339,7 @@ void writeJsonToTag(void *parameter) {
Serial.println("Fehler: Kein Tag zu schreiben gefunden."); Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
oledShowMessage("No NFC-Tag found"); oledShowMessage("No NFC-Tag found");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 0; nfcReaderState = NFC_IDLE;
} }
sendWriteResult(nullptr, success); sendWriteResult(nullptr, success);
@@ -334,7 +355,7 @@ void startWriteJsonToTag(const char* payload) {
char* payloadCopy = strdup(payload); char* payloadCopy = strdup(payload);
// Task nicht mehrfach starten // Task nicht mehrfach starten
if (hasReadRfidTag != 3) { if (nfcReaderState != NFC_WRITING) {
// Erstelle die Task // Erstelle die Task
xTaskCreate( xTaskCreate(
writeJsonToTag, // Task-Funktion writeJsonToTag, // Task-Funktion
@@ -351,7 +372,7 @@ void scanRfidTask(void * parameter) {
Serial.println("RFID Task gestartet"); Serial.println("RFID Task gestartet");
for(;;) { for(;;) {
// Wenn geschrieben wird Schleife aussetzen // Wenn geschrieben wird Schleife aussetzen
if (hasReadRfidTag != 3) if (nfcReaderState != NFC_WRITING)
{ {
yield(); yield();
@@ -363,12 +384,12 @@ void scanRfidTask(void * parameter) {
foundNfcTag(nullptr, success); foundNfcTag(nullptr, success);
if (success && hasReadRfidTag != 1) if (success && nfcReaderState != NFC_READ_SUCCESS)
{ {
// Display some basic information about the card // Display some basic information about the card
Serial.println("Found an ISO14443A card"); Serial.println("Found an ISO14443A card");
hasReadRfidTag = 6; nfcReaderState = NFC_READING;
oledShowIcon("transfer"); oledShowIcon("transfer");
vTaskDelay(500 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
@@ -406,11 +427,11 @@ void scanRfidTask(void * parameter) {
{ {
oledShowMessage("NFC-Tag unknown"); oledShowMessage("NFC-Tag unknown");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 2; nfcReaderState = NFC_READ_ERROR;
} }
else else
{ {
hasReadRfidTag = 1; nfcReaderState = NFC_READ_SUCCESS;
} }
free(data); free(data);
@@ -418,7 +439,7 @@ void scanRfidTask(void * parameter) {
else else
{ {
oledShowMessage("NFC-Tag read error"); oledShowMessage("NFC-Tag read error");
hasReadRfidTag = 2; nfcReaderState = NFC_READ_ERROR;
} }
} }
else else
@@ -427,13 +448,14 @@ void scanRfidTask(void * parameter) {
} }
} }
if (!success && hasReadRfidTag > 0) if (!success && nfcReaderState != NFC_IDLE)
{ {
hasReadRfidTag = 0; nfcReaderState = NFC_IDLE;
//uidString = ""; //uidString = "";
nfcJsonData = ""; nfcJsonData = "";
activeSpoolId = "";
Serial.println("Tag entfernt"); Serial.println("Tag entfernt");
if (!autoSendToBambu) oledShowWeight(weight); if (!bambuCredentials.autosend_enable) oledShowWeight(weight);
} }
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert

View File

@@ -3,14 +3,27 @@
#include <Arduino.h> #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 startNfc();
void scanRfidTask(void * parameter); void scanRfidTask(void * parameter);
void startWriteJsonToTag(const char* payload); void startWriteJsonToTag(const char* payload);
extern TaskHandle_t RfidReaderTask; extern TaskHandle_t RfidReaderTask;
extern String nfcJsonData; extern String nfcJsonData;
extern String spoolId; extern String activeSpoolId;
extern volatile uint8_t hasReadRfidTag; extern String lastSpoolId;
extern volatile nfcReaderStateType nfcReaderState;
extern volatile bool pauseBambuMqttTask; extern volatile bool pauseBambuMqttTask;
#endif #endif

View File

@@ -1,6 +1,10 @@
#include <Arduino.h> #include <Arduino.h>
#include <website.h> #include <website.h>
#include <commonFS.h> #include <commonFS.h>
#include "scale.h"
#include "bambu.h"
#include "nfc.h"
// Globale Variablen für Config Backups hinzufügen // Globale Variablen für Config Backups hinzufügen
String bambuCredentialsBackup; String bambuCredentialsBackup;
@@ -151,6 +155,25 @@ void handleUpdate(AsyncWebServer &server) {
updateHandler->onUpload([](AsyncWebServerRequest *request, String filename, updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
size_t index, uint8_t *data, size_t len, bool final) { size_t index, uint8_t *data, size_t len, bool final) {
// 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) { if (!index) {
updateTotalSize = request->contentLength(); updateTotalSize = request->contentLength();
updateWritten = 0; updateWritten = 0;
@@ -159,9 +182,9 @@ void handleUpdate(AsyncWebServer &server) {
if (isSpiffsUpdate) { if (isSpiffsUpdate) {
// Backup vor dem Update // Backup vor dem Update
sendUpdateProgress(0, "backup", "Backing up configurations..."); sendUpdateProgress(0, "backup", "Backing up configurations...");
delay(200); vTaskDelay(200 / portTICK_PERIOD_MS);
backupJsonConfigs(); 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); 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)) { if (!partition || !Update.begin(partition->size, U_SPIFFS)) {
@@ -169,14 +192,14 @@ void handleUpdate(AsyncWebServer &server) {
return; return;
} }
sendUpdateProgress(5, "starting", "Starting SPIFFS update..."); sendUpdateProgress(5, "starting", "Starting SPIFFS update...");
delay(200); vTaskDelay(200 / portTICK_PERIOD_MS);
} else { } else {
if (!Update.begin(updateTotalSize)) { if (!Update.begin(updateTotalSize)) {
request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}"); request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
return; return;
} }
sendUpdateProgress(0, "starting", "Starting firmware update..."); sendUpdateProgress(0, "starting", "Starting firmware update...");
delay(200); vTaskDelay(200 / portTICK_PERIOD_MS);
} }
} }
@@ -202,7 +225,7 @@ void handleUpdate(AsyncWebServer &server) {
if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) { if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
sendUpdateProgress(currentProgress, "uploading"); sendUpdateProgress(currentProgress, "uploading");
oledShowMessage("Update: " + String(currentProgress) + "%"); oledShowMessage("Update: " + String(currentProgress) + "%");
delay(50); vTaskDelay(50 / portTICK_PERIOD_MS);
lastProgress = currentProgress; lastProgress = currentProgress;
} }
} }

View File

@@ -14,15 +14,27 @@ TaskHandle_t ScaleTask;
int16_t weight = 0; int16_t weight = 0;
uint8_t weigthCouterToApi = 0; uint8_t weigthCouterToApi = 0;
uint8_t scale_tare_counter = 0;
bool scaleTareRequest = false; bool scaleTareRequest = false;
uint8_t pauseMainTask = 0; uint8_t pauseMainTask = 0;
uint8_t scaleCalibrated = 1; uint8_t scaleCalibrated = 1;
bool autoTare = true;
Preferences preferences;
const char* NVS_NAMESPACE = "scale";
const char* NVS_KEY_CALIBRATION = "cal_value";
// ##### Funktionen für Waage ##### // ##### 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() { uint8_t tareScale() {
Serial.println("Tare scale"); Serial.println("Tare scale");
scale.tare(); scale.tare();
@@ -38,28 +50,52 @@ void scale_loop(void * parameter) {
for(;;) { for(;;) {
if (scale.is_ready()) if (scale.is_ready())
{ {
// Waage nochmal Taren, wenn zu lange Abweichung // Waage automatisch Taren, wenn zu lange Abweichung
if (autoTare && scale_tare_counter >= 5)
{
Serial.println("Auto Tare scale");
scale.tare();
scale_tare_counter = 0;
}
// Waage manuell Taren
if (scaleTareRequest == true) if (scaleTareRequest == true)
{ {
Serial.println("Re-Tare scale"); Serial.println("Re-Tare scale");
oledShowMessage("TARE Scale");
vTaskDelay(pdMS_TO_TICKS(1000));
scale.tare(); scale.tare();
vTaskDelay(pdMS_TO_TICKS(1000));
oledShowWeight(0);
scaleTareRequest = false; scaleTareRequest = false;
} }
weight = round(scale.get_units()); // 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)); vTaskDelay(pdMS_TO_TICKS(100));
} }
} }
void start_scale() { void start_scale(bool touchSensorConnected) {
Serial.println("Prüfe Calibration Value"); Serial.println("Prüfe Calibration Value");
float calibrationValue; float calibrationValue;
// NVS lesen // NVS lesen
preferences.begin(NVS_NAMESPACE, true); // true = readonly Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue); calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue);
// auto Tare
// Wenn Touch Sensor verbunden, dann autoTare auf false setzen
// Danach prüfen was in NVS gespeichert ist
autoTare = (touchSensorConnected) ? false : true;
autoTare = preferences.getBool(NVS_KEY_AUTOTARE, autoTare);
preferences.end(); preferences.end();
Serial.print("Read Scale Calibration Value "); Serial.print("Read Scale Calibration Value ");
@@ -158,12 +194,13 @@ uint8_t calibrate_scale() {
Serial.println(newCalibrationValue); Serial.println(newCalibrationValue);
// Speichern mit NVS // Speichern mit NVS
preferences.begin(NVS_NAMESPACE, false); // false = readwrite Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue); preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue);
preferences.end(); preferences.end();
// Verifizieren // Verifizieren
preferences.begin(NVS_NAMESPACE, true); preferences.begin(NVS_NAMESPACE_SCALE, true);
float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0); float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0);
preferences.end(); preferences.end();

View File

@@ -4,17 +4,19 @@
#include <Arduino.h> #include <Arduino.h>
#include "HX711.h" #include "HX711.h"
uint8_t setAutoTare(bool autoTareValue);
uint8_t start_scale(); uint8_t start_scale(bool touchSensorConnected);
uint8_t calibrate_scale(); uint8_t calibrate_scale();
uint8_t tareScale(); uint8_t tareScale();
extern HX711 scale; extern HX711 scale;
extern int16_t weight; extern int16_t weight;
extern uint8_t weigthCouterToApi; extern uint8_t weigthCouterToApi;
extern uint8_t scale_tare_counter;
extern uint8_t scaleTareRequest; extern uint8_t scaleTareRequest;
extern uint8_t pauseMainTask; extern uint8_t pauseMainTask;
extern uint8_t scaleCalibrated; extern uint8_t scaleCalibrated;
extern bool autoTare;
extern TaskHandle_t ScaleTask; extern TaskHandle_t ScaleTask;

View File

@@ -10,6 +10,9 @@
#include <Update.h> #include <Update.h>
#include "display.h" #include "display.h"
#include "ota.h" #include "ota.h"
#include "config.h"
#include "debug.h"
#ifndef VERSION #ifndef VERSION
#define VERSION "1.1.0" #define VERSION "1.1.0"
@@ -22,10 +25,11 @@ AsyncWebServer server(webserverPort);
AsyncWebSocket ws("/ws"); AsyncWebSocket ws("/ws");
uint8_t lastSuccess = 0; uint8_t lastSuccess = 0;
uint8_t lastHasReadRfidTag = 0; nfcReaderStateType lastnfcReaderState = NFC_IDLE;
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
HEAP_DEBUG_MESSAGE("onWsEvent begin");
if (type == WS_EVT_CONNECT) { if (type == WS_EVT_CONNECT) {
Serial.println("Neuer Client verbunden!"); Serial.println("Neuer Client verbunden!");
// Sende die AMS-Daten an den neuen Client // Sende die AMS-Daten an den neuen Client
@@ -33,6 +37,10 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
sendNfcData(client); sendNfcData(client);
foundNfcTag(client, 0); foundNfcTag(client, 0);
sendWriteResult(client, 3); 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) { } else if (type == WS_EVT_DISCONNECT) {
Serial.println("Client getrennt."); Serial.println("Client getrennt.");
} else if (type == WS_EVT_ERROR) { } else if (type == WS_EVT_ERROR) {
@@ -44,13 +52,15 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, message); deserializeJson(doc, message);
bool spoolmanConnected = (spoolmanApiState != API_INIT);
if (doc["type"] == "heartbeat") { if (doc["type"] == "heartbeat") {
// Sende Heartbeat-Antwort // Sende Heartbeat-Antwort
ws.text(client->id(), "{" ws.text(client->id(), "{"
"\"type\":\"heartbeat\"," "\"type\":\"heartbeat\","
"\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + "," "\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + ","
"\"bambu_connected\":" + String(bambu_connected) + "," "\"bambu_connected\":" + String(bambu_connected) + ","
"\"spoolman_connected\":" + String(spoolman_connected) + "" "\"spoolman_connected\":" + String(spoolmanConnected) + ""
"}"); "}");
} }
@@ -73,6 +83,10 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
success = calibrate_scale(); success = calibrate_scale();
} }
if (doc["payload"] == "setAutoTare") {
success = setAutoTare(doc["enabled"].as<bool>());
}
if (success) { if (success) {
ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}"); ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}");
} else { } else {
@@ -107,7 +121,9 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
else { else {
Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>()); 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 // Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
@@ -139,34 +155,31 @@ void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) {
} }
void sendNfcData(AsyncWebSocketClient *client) { void sendNfcData(AsyncWebSocketClient *client) {
if (lastHasReadRfidTag == hasReadRfidTag) return; if (lastnfcReaderState == nfcReaderState) return;
if (hasReadRfidTag == 0) { // TBD: Why is there no status for reading the tag?
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}"); 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) { lastnfcReaderState = nfcReaderState;
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;
} }
void sendAmsData(AsyncWebSocketClient *client) { void sendAmsData(AsyncWebSocketClient *client) {
@@ -192,6 +205,9 @@ void setupWebserver(AsyncWebServer &server) {
Serial.print("Geladene Spoolman-URL: "); Serial.print("Geladene Spoolman-URL: ");
Serial.println(spoolmanUrl); Serial.println(spoolmanUrl);
// Load Bamb credentials:
loadBambuCredentials();
// Route für about // Route für about
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /about erhalten"); Serial.println("Anfrage für /about erhalten");
@@ -204,10 +220,14 @@ void setupWebserver(AsyncWebServer &server) {
// Route für Waage // Route für Waage
server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /waage erhalten"); Serial.println("Anfrage für /waage erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html"); //AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); //response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); //response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response);
String html = loadHtmlWithHeader("/waage.html");
html.replace("{{autoTare}}", (autoTare) ? "checked" : "");
request->send(200, "text/html", html);
}); });
// Route für RFID // Route für RFID
@@ -247,31 +267,13 @@ void setupWebserver(AsyncWebServer &server) {
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : ""); html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : ""); html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
JsonDocument doc; html.replace("{{bambuIp}}", bambuCredentials.ip);
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) html.replace("{{bambuSerial}}", bambuCredentials.serial);
{ html.replace("{{bambuCode}}", bambuCredentials.accesscode ? bambuCredentials.accesscode : "");
String bambuIp = doc["bambu_ip"].as<String>(); html.replace("{{autoSendToBambu}}", bambuCredentials.autosend_enable ? "checked" : "");
String bambuSerial = doc["bambu_serialnr"].as<String>(); html.replace("{{autoSendTime}}", (bambuCredentials.autosend_time != 0) ? String(bambuCredentials.autosend_time) : String(BAMBU_DEFAULT_AUTOSEND_TIME));
String bambuCode = doc["bambu_accesscode"].as<String>();
autoSendToBambu = doc["autoSendToBambu"].as<bool>();
bambuIp.trim();
bambuSerial.trim();
bambuCode.trim();
html.replace("{{bambuIp}}", bambuIp ? bambuIp : ""); doc.clear();
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));
}
request->send(200, "text/html", html); request->send(200, "text/html", html);
}); });