Compare commits

...

28 Commits

Author SHA1 Message Date
tugsi 4dee298b73 Added
add auto-tare functionality and update scale handling based on touch sensor connection
2025-03-30 16:29:20 +02:00
tugsi c430c989ef Fixed
update touch sensor connection logic to correctly identify connection status
2025-03-30 15:17:47 +02:00
tugsi b40a2166f2 Fixed
enhance HTTP method handling in sendToApi function
2025-03-30 14:34:54 +02:00
tugsi 3590187cbe Added
Fixed
improve HTTP client configuration and clear update documents after API calls (Manuel Weiser)
6cc4efc - Fixes memory leak in HTTPClient by disabling connection reuse (Jan Philipp Ecker)
update reload logic after removing and saving Bambu credentials for better cache handling (Manuel Weiser)
Changed
docs: update changelog and header for version v1.4.8 (Manuel Weiser)
docs: update platformio.ini for version v1.4.8 (Manuel Weiser)
Merge pull request #30 from janecker/main (ManuelW)
Merge branch 'testing' into main (ManuelW)
2025-03-30 14:32:11 +02:00
tugsi 0302725bc7 Added
add forced cache refresh after removing and saving Bambu credentials (Manuel Weiser)
add functionality to remove Bambu credentials and update API handling (Manuel Weiser)
add rfid_bambu.html and update bambu connection handling (Manuel Weiser)
Fixed
handle Bambu connection state by introducing bambuDisabled flag (Manuel Weiser)
fe4d2d7 - Fix rfid.js-Failure with X1-Series, if you wanna send a Spool to AMS: - Uncaught TypeError: Cannot read properties of undefined (reading 'replace') at handleSpoolIn (rfid.js:493:67) at HTMLButtonElement.onclick ((Index):1:1) handleSpoolIn @ rfid.js:493 onclick @ (Index):1 (tugsi)
Changed
docs: update changelog and header for version v1.4.7 (Manuel Weiser)
docs: update platformio.ini for version v1.4.7 (Manuel Weiser)
Merge branch 'testing' (Manuel Weiser)
style: update remove button for Bambu credentials with red background (Manuel Weiser)
Merge pull request #28 from tugsi/main (ManuelW)
2025-03-30 14:28:17 +02:00
tugsi 75fbd5ebc3 Version-Fix - get it´s own Version 2025-03-24 18:21:41 +01:00
tugsi 53894a3746 Changed RFID to SPI and for using 2 PN532 2025-03-24 17:32:57 +01:00
tugsi 360c925cfc More Diag-Infos Heap-memory 2025-03-24 17:09:19 +01:00
tugsi fc5d77c97b fix: adjust weight threshold for tare check to allow negative values 2025-03-24 16:54:36 +01:00
tugsi f869bcc0c1 Origin Rfid.js 2025-03-23 18:58:33 +01:00
tugsi b75be0ac06 fix 2025-03-23 18:53:57 +01:00
tugsi 1c01bfb2da - X1C MQTT Fix
- Spoolman Settings Fix
- Spoolman Out Fix
- Spoolman In Fix
2025-03-23 18:09:25 +01:00
tugsi 15d0bfd8e9 gitgnore 2025-03-23 17:43:10 +01:00
tugsi d363a7b905 fix: add error handling for missing vendor IDs in filament data 2025-03-23 17:14:53 +01:00
tugsi 9d7accc746 fix: use unique client ID for MQTT connection to avoid conflicts 2025-03-23 16:42:52 +01:00
tugsi c2e3dc1d46 fix: reload page after firmware update completion 2025-03-23 16:35:03 +01:00
tugsi d81cf5d4e4 fix: ensure valid URL format and remove trailing slash in setupWebserver 2025-03-23 16:33:02 +01:00
tugsi 75e13387c8 fix: add WiFi connection check and restart Bambu if not connected 2025-03-23 16:31:12 +01:00
tugsi 6534419381 increase stack size for BambuMqtt task 2025-03-23 16:27:20 +01:00
tugsi e55b2d7fdf Merge pull request '- WifiTimeout changed from 5s to 10s #Issue22' (#1) from WLAN-Timeout into main
Reviewed-on: http://10.10.10.101:3000/tugsi/Filaman_V2/pulls/1
2025-03-23 16:00:37 +01:00
tugsi 4aa81c7842 - WifiTimeout changed from 5s to 10s #Issue22 2025-03-21 08:00:59 +01:00
tugsi 8ff4e81826 Merge branch 'ManuelW77:main' into main 2025-03-16 19:50:13 +01:00
ManuelW 659dfb8627 Update README.de.md
Removed OpenSpool
2025-03-12 10:02:30 +01:00
ManuelW b4dd0d357a Update README.md 2025-03-12 10:01:40 +01:00
ManuelW 8e38a68191 Merge pull request #19 from tugsi/patch-1
Update README.de.md
2025-03-12 09:59:54 +01:00
ManuelW c3ec7edf6a Merge pull request #20 from tugsi/patch-2
Update README.md
2025-03-12 09:58:52 +01:00
tugsi a35e13e015 Update README.md
See German-Commit Readme :-)
2025-03-11 14:54:43 +01:00
tugsi e0df5d33f2 Update README.de.md
Einige Kleinigkeiten die mir aufgefallen sind.
- einheitlich Bambu Lab
- generell Namensgebung korrigiert
- die meisten Auflistungen haben ein . am Ende, eigentlich könnte man die weglassen, aber ich habe die, die keine hatten angepasst
- Installation Google Chrome oder Microsoft Edge hinzugefügt
2025-03-11 14:49:28 +01:00
28 changed files with 1012 additions and 350 deletions
+3
View File
@@ -0,0 +1,3 @@
.pio
.vscode
data
+9 -10
View File
@@ -1,8 +1,8 @@
# FilaMan - Filament Management System # FilaMan - Filament Management System
FilaMan ist ein Filament-Managementsystem für den 3D-Druck. Es verwendet ESP32-Hardware für Gewichtsmessungen und NFC-Tag-Management. FilaMan ist ein Filament-Managementsystem für den 3D-Druck. Es verwendet ESP32-Hardware für Gewichtsmessungen und NFC-Tag-Management.
Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bablulab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen. Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bambu Lab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen.
Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambulab](https://bambulab.com/en-us) 3D-Druckern und sowie dem [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG Format. Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambu Lab](https://bambulab.com/en-us) 3D-Druckern.
![Scale](./img/scale_trans.png) ![Scale](./img/scale_trans.png)
@@ -16,18 +16,17 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
### ESP32 Hardware-Funktionen ### ESP32 Hardware-Funktionen
- **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung. - **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung.
- **NFC-Tag Lesen/Schreiben:** PN532-Modul zum Lesen und Schreiben von Filamentdaten auf NFC-Tags. - **NFC-Tag Lesen/Schreiben:** PN532-Modul zum Lesen und Schreiben von Filamentdaten auf NFC-Tags.
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman). - **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, BambuLab, Spoolman).
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration. - **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
- **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung. - **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung.
- **NFC-Tag NTAG213 NTAG215:** Verwendung von NTAG213, besser NTAG215 wegen ausreichendem Speicherplatz auf dem Tag - **NFC-Tag NTAG213 NTAG215:** Verwendung von NTAG213 (144 Bytes - Kapazität), besser NTAG215 (540 Bytes - Kapazität) wegen ausreichendem Speicherplatz auf dem Tag
### Weboberflächen-Funktionen ### Weboberflächen-Funktionen
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates. - **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
- **NFC-Tag-Verwaltung:** - **NFC-Tag-Verwaltung:**
- Filamentdaten auf NFC-Tags schreiben. - Filamentdaten auf NFC-Tags schreiben.
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool) - Ermöglicht automatische Spulenerkennung im AMS.
- Ermöglicht automatische Spulenerkennung im AMS - **Bambu Lab AMS-Integration:**
- **Bambulab AMS-Integration:**
- Anzeige der aktuellen AMS-Fachbelegung. - Anzeige der aktuellen AMS-Fachbelegung.
- Zuordnung von Filamenten zu AMS-Slots. - Zuordnung von Filamenten zu AMS-Slots.
- Unterstützung für externe Spulenhalter. - Unterstützung für externe Spulenhalter.
@@ -36,7 +35,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Filtern und Auswählen von Filamenten. - Filtern und Auswählen von Filamenten.
- Automatische Aktualisierung der Spulengewichte. - Automatische Aktualisierung der Spulengewichte.
- Verfolgung von NFC-Tag-Zuweisungen. - Verfolgung von NFC-Tag-Zuweisungen.
- Unterstützt das Spoolman Octoprint Plugin - Unterstützt das Spoolman Octoprint Plugin.
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee ### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@@ -122,7 +121,7 @@ Du musst Spoolman auf DEBUG Modus setzten, da man bisher in Spoolman keine CORS
``` ```
## Schritt-für-Schritt Installation ## Schritt-für-Schritt Installation
### Einfache Installation ### Einfache Installation (Google Chrome oder Microsoft Edge)
1. **Gehe auf [FilaMan Installer](https://www.filaman.app/installer.html)** 1. **Gehe auf [FilaMan Installer](https://www.filaman.app/installer.html)**
2. **Stecke dein ESP an den Rechner und klicke Connect** 2. **Stecke dein ESP an den Rechner und klicke Connect**
@@ -185,4 +184,4 @@ Dieses Projekt ist unter der MIT-Lizenz lizenziert. Siehe [LICENSE](LICENSE) Dat
Der Code kann getestet und die Anwendung kann vom [GitHub Repository](https://github.com/ManuelW77/Filaman) heruntergeladen werden. Der Code kann getestet und die Anwendung kann vom [GitHub Repository](https://github.com/ManuelW77/Filaman) heruntergeladen werden.
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee ### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
+5 -6
View File
@@ -4,7 +4,7 @@
FilaMan is a filament management system for 3D printing. It uses ESP32 hardware for weight measurement and NFC tag management. FilaMan is a filament management system for 3D printing. It uses ESP32 hardware for weight measurement and NFC tag management.
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface. Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format. The system integrates seamlessly with [Bambu Lab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management.
![Scale](./img/scale_trans.png) ![Scale](./img/scale_trans.png)
@@ -23,13 +23,12 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman). - **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
- **WiFi Connectivity:** WiFiManager for easy network configuration. - **WiFi Connectivity:** WiFiManager for easy network configuration.
- **MQTT Integration:** Connects to Bambu Lab printer for AMS control. - **MQTT Integration:** Connects to Bambu Lab printer for AMS control.
- **NFC-Tag NTAG213 NTAG215:** Use NTAG213, better NTAG215 because of enaught space on the Tag - **NFC-Tag NTAG213 NTAG215:** Use NTAG213 (Capacity of 144 bytes), better NTAG215 (Capacity of 540 bytes) because of enaught space on the Tag.
### Web Interface Features ### Web Interface Features
- **Real-time Updates:** WebSocket connection for live data updates. - **Real-time Updates:** WebSocket connection for live data updates.
- **NFC Tag Management:** - **NFC Tag Management:**
- Write filament data to NFC tags. - Write filament data to NFC tags.
- uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool)
- so you can use it with automatic Spool detection in AMS - so you can use it with automatic Spool detection in AMS
- **Bambulab AMS Integration:** - **Bambulab AMS Integration:**
- Display current AMS tray contents. - Display current AMS tray contents.
@@ -40,7 +39,7 @@ Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
- Filter and select filaments. - Filter and select filaments.
- Update spool weights automatically. - Update spool weights automatically.
- Track NFC tag assignments. - Track NFC tag assignments.
- Supports Spoolman Octoprint Plugin - Supports Spoolman Octoprint Plugin.
### If you want to support my work, i would be happy to get a coffe ### If you want to support my work, i would be happy to get a coffe
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@@ -128,7 +127,7 @@ You have to activate Spoolman in debug mode, because you are not able to set COR
## Step-by-Step Installation ## Step-by-Step Installation
### Easy Installation ### Easy Installation (Google Chrome or Microsoft Edge)
1. **Go to [FilaMan Installer](https://www.filaman.app/installer.html)** 1. **Go to [FilaMan Installer](https://www.filaman.app/installer.html)**
2. **Plug you device in and push Connect button** 2. **Plug you device in and push Connect button**
@@ -191,4 +190,4 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
The code can be tested and the application can be downloaded from the [GitHub repository](https://github.com/ManuelW77/Filaman). The code can be tested and the application can be downloaded from the [GitHub repository](https://github.com/ManuelW77/Filaman).
### If you want to support my work, i would be happy to get a coffe ### If you want to support my work, i would be happy to get a coffe
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
-9
View File
@@ -141,15 +141,6 @@
</div> </div>
</div> </div>
<!-- Rechte Spalte -->
<div class="column">
<div class="feature-box">
<h2>Bambu AMS</h2>
<div id="amsDataContainer">
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
+55 -56
View File
@@ -11,7 +11,7 @@ let reconnectTimer = null;
// WebSocket Funktionen // WebSocket Funktionen
function startHeartbeat() { function startHeartbeat() {
if (heartbeatTimer) clearInterval(heartbeatTimer); if (heartbeatTimer) clearInterval(heartbeatTimer);
heartbeatTimer = setInterval(() => { heartbeatTimer = setInterval(() => {
// Prüfe ob zu lange keine Antwort kam // Prüfe ob zu lange keine Antwort kam
if (Date.now() - lastHeartbeatResponse > HEARTBEAT_TIMEOUT) { if (Date.now() - lastHeartbeatResponse > HEARTBEAT_TIMEOUT) {
@@ -29,7 +29,7 @@ function startHeartbeat() {
updateConnectionStatus(); updateConnectionStatus();
return; return;
} }
try { try {
// Sende Heartbeat // Sende Heartbeat
socket.send(JSON.stringify({ type: 'heartbeat' })); socket.send(JSON.stringify({ type: 'heartbeat' }));
@@ -59,18 +59,18 @@ function initWebSocket() {
try { try {
socket = new WebSocket('ws://' + window.location.host + '/ws'); socket = new WebSocket('ws://' + window.location.host + '/ws');
socket.onopen = function() { socket.onopen = function () {
isConnected = true; isConnected = true;
updateConnectionStatus(); updateConnectionStatus();
startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung
}; };
socket.onclose = function() { socket.onclose = function () {
isConnected = false; isConnected = false;
updateConnectionStatus(); updateConnectionStatus();
if (heartbeatTimer) clearInterval(heartbeatTimer); if (heartbeatTimer) clearInterval(heartbeatTimer);
// Nur neue Verbindung versuchen, wenn kein Timer läuft // Nur neue Verbindung versuchen, wenn kein Timer läuft
if (!reconnectTimer) { if (!reconnectTimer) {
reconnectTimer = setTimeout(() => { reconnectTimer = setTimeout(() => {
@@ -78,22 +78,22 @@ function initWebSocket() {
}, RECONNECT_INTERVAL); }, RECONNECT_INTERVAL);
} }
}; };
socket.onerror = function(error) { socket.onerror = function (error) {
isConnected = false; isConnected = false;
updateConnectionStatus(); updateConnectionStatus();
if (heartbeatTimer) clearInterval(heartbeatTimer); if (heartbeatTimer) clearInterval(heartbeatTimer);
// Bei Fehler Verbindung schließen und neu aufbauen // Bei Fehler Verbindung schließen und neu aufbauen
if (socket) { if (socket) {
socket.close(); socket.close();
socket = null; socket = null;
} }
}; };
socket.onmessage = function(event) { socket.onmessage = function (event) {
lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.type === 'amsData') { if (data.type === 'amsData') {
displayAmsData(data.payload); displayAmsData(data.payload);
@@ -109,13 +109,13 @@ function initWebSocket() {
const bambuDot = document.getElementById('bambuDot'); const bambuDot = document.getElementById('bambuDot');
const spoolmanDot = document.getElementById('spoolmanDot'); const spoolmanDot = document.getElementById('spoolmanDot');
const ramStatus = document.getElementById('ramStatus'); const ramStatus = document.getElementById('ramStatus');
if (bambuDot) { if (bambuDot) {
bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline'); bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline');
// Add click handler only when offline // Add click handler only when offline
if (!data.bambu_connected) { if (!data.bambu_connected) {
bambuDot.style.cursor = 'pointer'; bambuDot.style.cursor = 'pointer';
bambuDot.onclick = function() { bambuDot.onclick = function () {
if (socket && socket.readyState === WebSocket.OPEN) { if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: 'reconnect', type: 'reconnect',
@@ -133,7 +133,7 @@ function initWebSocket() {
// Add click handler only when offline // Add click handler only when offline
if (!data.spoolman_connected) { if (!data.spoolman_connected) {
spoolmanDot.style.cursor = 'pointer'; spoolmanDot.style.cursor = 'pointer';
spoolmanDot.onclick = function() { spoolmanDot.onclick = function () {
if (socket && socket.readyState === WebSocket.OPEN) { if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: 'reconnect', type: 'reconnect',
@@ -161,7 +161,7 @@ function initWebSocket() {
} catch (error) { } catch (error) {
isConnected = false; isConnected = false;
updateConnectionStatus(); updateConnectionStatus();
// Nur neue Verbindung versuchen, wenn kein Timer läuft // Nur neue Verbindung versuchen, wenn kein Timer läuft
if (!reconnectTimer) { if (!reconnectTimer) {
reconnectTimer = setTimeout(() => { reconnectTimer = setTimeout(() => {
@@ -189,26 +189,26 @@ function updateConnectionStatus() {
} }
// Event Listeners // Event Listeners
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function () {
initWebSocket(); initWebSocket();
// Event Listener für Checkbox // Event Listener für Checkbox
document.getElementById("onlyWithoutSmId").addEventListener("change", function() { document.getElementById("onlyWithoutSmId").addEventListener("change", function () {
const spoolsData = window.getSpoolData(); const spoolsData = window.getSpoolData();
window.populateVendorDropdown(spoolsData); window.populateVendorDropdown(spoolsData);
}); });
}); });
// Event Listener für Spoolman Events // Event Listener für Spoolman Events
document.addEventListener('spoolDataLoaded', function(event) { document.addEventListener('spoolDataLoaded', function (event) {
window.populateVendorDropdown(event.detail); window.populateVendorDropdown(event.detail);
}); });
document.addEventListener('spoolmanError', function(event) { document.addEventListener('spoolmanError', function (event) {
showNotification(`Spoolman Error: ${event.detail.message}`, false); showNotification(`Spoolman Error: ${event.detail.message}`, false);
}); });
document.addEventListener('filamentSelected', function(event) { document.addEventListener('filamentSelected', function (event) {
updateNfcInfo(); updateNfcInfo();
// Zeige Spool-Buttons wenn ein Filament ausgewählt wurde // Zeige Spool-Buttons wenn ein Filament ausgewählt wurde
const selectedText = document.getElementById("selected-filament").textContent; const selectedText = document.getElementById("selected-filament").textContent;
@@ -218,13 +218,13 @@ document.addEventListener('filamentSelected', function(event) {
// Hilfsfunktion für kontrastreiche Textfarbe // Hilfsfunktion für kontrastreiche Textfarbe
function getContrastColor(hexcolor) { function getContrastColor(hexcolor) {
// Konvertiere Hex zu RGB // Konvertiere Hex zu RGB
const r = parseInt(hexcolor.substr(0,2),16); const r = parseInt(hexcolor.substr(0, 2), 16);
const g = parseInt(hexcolor.substr(2,2),16); const g = parseInt(hexcolor.substr(2, 2), 16);
const b = parseInt(hexcolor.substr(4,2),16); const b = parseInt(hexcolor.substr(4, 2), 16);
// Berechne Helligkeit (YIQ Formel) // Berechne Helligkeit (YIQ Formel)
const yiq = ((r*299)+(g*587)+(b*114))/1000; const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
// Return schwarz oder weiß basierend auf Helligkeit // Return schwarz oder weiß basierend auf Helligkeit
return (yiq >= 128) ? '#000000' : '#FFFFFF'; return (yiq >= 128) ? '#000000' : '#FFFFFF';
} }
@@ -242,7 +242,7 @@ function updateNfcInfo() {
} }
// Finde die ausgewählte Spule in den Daten // Finde die ausgewählte Spule in den Daten
const selectedSpool = spoolsData.find(spool => const selectedSpool = spoolsData.find(spool =>
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
); );
@@ -255,18 +255,18 @@ function updateNfcInfo() {
function displayAmsData(amsData) { function displayAmsData(amsData) {
const amsDataContainer = document.getElementById('amsData'); const amsDataContainer = document.getElementById('amsData');
amsDataContainer.innerHTML = ''; amsDataContainer.innerHTML = '';
amsData.forEach((ams) => { amsData.forEach((ams) => {
// Bestimme den Anzeigenamen für das AMS // Bestimme den Anzeigenamen für das AMS
const amsDisplayName = ams.ams_id === 255 ? 'External Spool' : `AMS ${ams.ams_id}`; const amsDisplayName = ams.ams_id === 255 ? 'External Spool' : `AMS ${ams.ams_id}`;
const trayHTML = ams.tray.map(tray => { const trayHTML = ams.tray.map(tray => {
// Prüfe ob überhaupt Daten vorhanden sind // Prüfe ob überhaupt Daten vorhanden sind
const relevantFields = ['tray_type', 'tray_sub_brands', 'tray_info_idx', 'setting_id', 'cali_idx']; const relevantFields = ['tray_type', 'tray_sub_brands', 'tray_info_idx', 'setting_id', 'cali_idx'];
const hasAnyContent = relevantFields.some(field => const hasAnyContent = relevantFields.some(field =>
tray[field] !== null && tray[field] !== null &&
tray[field] !== undefined && tray[field] !== undefined &&
tray[field] !== '' && tray[field] !== '' &&
tray[field] !== 'null' tray[field] !== 'null'
); );
@@ -282,8 +282,8 @@ function displayAmsData(amsData) {
cursor: pointer; display: none;"> cursor: pointer; display: none;">
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;"> <img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;">
</button>`; </button>`;
// Nur für nicht-leere Trays den Button-HTML erstellen // Nur für nicht-leere Trays den Button-HTML erstellen
const outButtonHtml = ` const outButtonHtml = `
<button class="spool-button" onclick="handleSpoolOut()" <button class="spool-button" onclick="handleSpoolOut()"
style="position: absolute; top: -35px; right: -15px; style="position: absolute; top: -35px; right: -15px;
@@ -313,7 +313,7 @@ function displayAmsData(amsData) {
} }
// Generiere den Type mit Color-Box zusammen // Generiere den Type mit Color-Box zusammen
const typeWithColor = tray.tray_type ? const typeWithColor = tray.tray_type ?
`<p>Typ: ${tray.tray_type} ${tray.tray_color ? `<span style=" `<p>Typ: ${tray.tray_type} ${tray.tray_color ? `<span style="
background-color: #${tray.tray_color}; background-color: #${tray.tray_color};
width: 20px; width: 20px;
@@ -334,9 +334,9 @@ function displayAmsData(amsData) {
// Nur gültige Felder anzeigen // Nur gültige Felder anzeigen
const trayDetails = trayProperties const trayDetails = trayProperties
.filter(prop => .filter(prop =>
tray[prop.key] !== null && tray[prop.key] !== null &&
tray[prop.key] !== undefined && tray[prop.key] !== undefined &&
tray[prop.key] !== '' && tray[prop.key] !== '' &&
tray[prop.key] !== 'null' tray[prop.key] !== 'null'
) )
@@ -350,7 +350,7 @@ function displayAmsData(amsData) {
.join(''); .join('');
// Temperaturen nur anzeigen, wenn beide nicht 0 sind // Temperaturen nur anzeigen, wenn beide nicht 0 sind
const tempHTML = (tray.nozzle_temp_min > 0 && tray.nozzle_temp_max > 0) const tempHTML = (tray.nozzle_temp_min > 0 && tray.nozzle_temp_max > 0)
? `<p>Nozzle Temp: ${tray.nozzle_temp_min}°C - ${tray.nozzle_temp_max}°C</p>` ? `<p>Nozzle Temp: ${tray.nozzle_temp_min}°C - ${tray.nozzle_temp_max}°C</p>`
: ''; : '';
@@ -376,7 +376,7 @@ function displayAmsData(amsData) {
${trayHTML} ${trayHTML}
</div> </div>
</div>`; </div>`;
amsDataContainer.innerHTML += amsInfo; amsDataContainer.innerHTML += amsInfo;
}); });
} }
@@ -394,7 +394,7 @@ function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp
const selectedText = document.getElementById("selected-filament").textContent; const selectedText = document.getElementById("selected-filament").textContent;
// Finde die ausgewählte Spule in den Daten // Finde die ausgewählte Spule in den Daten
const selectedSpool = spoolsData.find(spool => const selectedSpool = spoolsData.find(spool =>
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
); );
@@ -460,7 +460,7 @@ function handleSpoolIn(amsId, trayId) {
} }
// Finde die ausgewählte Spule in den Daten // Finde die ausgewählte Spule in den Daten
const selectedSpool = spoolsData.find(spool => const selectedSpool = spoolsData.find(spool =>
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
); );
@@ -473,7 +473,7 @@ function handleSpoolIn(amsId, trayId) {
let minTemp = "175"; let minTemp = "175";
let maxTemp = "275"; let maxTemp = "275";
if (Array.isArray(selectedSpool.filament.nozzle_temperature) && if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
selectedSpool.filament.nozzle_temperature.length >= 2) { selectedSpool.filament.nozzle_temperature.length >= 2) {
minTemp = selectedSpool.filament.nozzle_temperature[0]; minTemp = selectedSpool.filament.nozzle_temperature[0];
maxTemp = selectedSpool.filament.nozzle_temperature[1]; maxTemp = selectedSpool.filament.nozzle_temperature[1];
@@ -490,7 +490,7 @@ function handleSpoolIn(amsId, trayId) {
nozzle_temp_max: parseInt(maxTemp), nozzle_temp_max: parseInt(maxTemp),
type: selectedSpool.filament.material, type: selectedSpool.filament.material,
brand: selectedSpool.filament.vendor.name, brand: selectedSpool.filament.vendor.name,
tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(), tray_info_idx: selectedSpool.filament.extra.bambu_idx?.replace(/['"]+/g, '').trim() || '',
cali_idx: "-1" // Default-Wert setzen cali_idx: "-1" // Default-Wert setzen
} }
}; };
@@ -518,7 +518,7 @@ function handleSpoolIn(amsId, trayId) {
function updateNfcStatusIndicator(data) { function updateNfcStatusIndicator(data) {
const indicator = document.getElementById('nfcStatusIndicator'); const indicator = document.getElementById('nfcStatusIndicator');
if (data.found === 0) { if (data.found === 0) {
// Kein NFC Tag gefunden // Kein NFC Tag gefunden
indicator.className = 'status-circle'; indicator.className = 'status-circle';
@@ -534,7 +534,7 @@ function updateNfcStatusIndicator(data) {
function updateNfcData(data) { function updateNfcData(data) {
// Den Container für den NFC Status finden // Den Container für den NFC Status finden
const nfcStatusContainer = document.querySelector('.nfc-status-display'); const nfcStatusContainer = document.querySelector('.nfc-status-display');
// Bestehende Daten-Anzeige entfernen falls vorhanden // Bestehende Daten-Anzeige entfernen falls vorhanden
const existingData = nfcStatusContainer.querySelector('.nfc-data'); const existingData = nfcStatusContainer.querySelector('.nfc-data');
if (existingData) { if (existingData) {
@@ -593,7 +593,7 @@ function updateNfcData(data) {
if (matchingSpool) { if (matchingSpool) {
// Zuerst Hersteller-Dropdown aktualisieren // Zuerst Hersteller-Dropdown aktualisieren
document.getElementById("vendorSelect").value = matchingSpool.filament.vendor.id; document.getElementById("vendorSelect").value = matchingSpool.filament.vendor.id;
// Dann Filament-Dropdown aktualisieren und Spule auswählen // Dann Filament-Dropdown aktualisieren und Spule auswählen
updateFilamentDropdown(); updateFilamentDropdown();
setTimeout(() => { setTimeout(() => {
@@ -606,7 +606,7 @@ function updateNfcData(data) {
html += '</div>'; html += '</div>';
nfcDataDiv.innerHTML = html; nfcDataDiv.innerHTML = html;
// Neues div zum Container hinzufügen // Neues div zum Container hinzufügen
nfcStatusContainer.appendChild(nfcDataDiv); nfcStatusContainer.appendChild(nfcDataDiv);
} }
@@ -619,7 +619,7 @@ function writeNfcTag() {
} }
const spoolsData = window.getSpoolData(); const spoolsData = window.getSpoolData();
const selectedSpool = spoolsData.find(spool => const selectedSpool = spoolsData.find(spool =>
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
); );
@@ -631,8 +631,8 @@ function writeNfcTag() {
// Temperaturwerte korrekt extrahieren // Temperaturwerte korrekt extrahieren
let minTemp = "175"; let minTemp = "175";
let maxTemp = "275"; let maxTemp = "275";
if (Array.isArray(selectedSpool.filament.nozzle_temperature) && if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
selectedSpool.filament.nozzle_temperature.length >= 2) { selectedSpool.filament.nozzle_temperature.length >= 2) {
minTemp = String(selectedSpool.filament.nozzle_temperature[0]); minTemp = String(selectedSpool.filament.nozzle_temperature[0]);
maxTemp = String(selectedSpool.filament.nozzle_temperature[1]); maxTemp = String(selectedSpool.filament.nozzle_temperature[1]);
@@ -685,5 +685,4 @@ function showNotification(message, isSuccess) {
setTimeout(() => { setTimeout(() => {
notification.remove(); notification.remove();
}, 300); }, 300);
}, 3000); }, 3000);}
}
+165
View File
@@ -0,0 +1,165 @@
<!-- head -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FilaMan - Filament Management Tool</title>
<link rel="icon" type="image/png" href="/favicon.ico">
<link rel="stylesheet" href="style.css">
<script>
fetch('/api/version')
.then(response => response.json())
.then(data => {
const versionSpan = document.querySelector('.version');
if (versionSpan) {
versionSpan.textContent = 'v' + data.version;
}
})
.catch(error => console.error('Error fetching version:', error));
</script>
</head>
<body>
<div class="navbar">
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version"></span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>
<nav style="display: flex; gap: 1rem;">
<a href="/">Start</a>
<a href="/waage">Scale</a>
<a href="/spoolman">Spoolman/Bambu</a>
<a href="/about">About</a>
<a href="/upgrade">Upgrade</a>
</nav>
<div class="status-container">
<div class="status-item">
<span class="status-dot" id="bambuDot"></span>B
</div>
<div class="status-item">
<span class="status-dot" id="spoolmanDot"></span>S
</div>
<div class="ram-status" id="ramStatus"></div>
</div>
</div>
<!-- head -->
<div class="connection-status hidden">
<div class="spinner"></div>
<span>Connection lost. Trying to reconnect...</span>
</div>
<div class="content">
<div class="three-column-layout">
<!-- Linke Spalte -->
<div class="column">
<div class="feature-box">
<div class="statistics-header">
<h2>Statistics</h2>
<button id="refreshSpoolman" class="refresh-button">
<span>Refresh Spoolman</span>
</button>
</div>
<div class="statistics-column">
<h3>Spools</h3>
<div class="spool-stat" style="display: flex; justify-content: center; align-items: center;">
<span class="stat-label">total:</span>
<span class="stat-value" id="totalSpools"></span>
<div style="width: auto;"></div>
<span class="stat-label">without Tag:</span>
<span class="stat-value" id="spoolsWithoutTag"></span>
</div>
</div>
<div class="statistics-grid">
<div class="statistics-column">
<h3>Overview</h3>
<ul class="statistics-list">
<li>
<span class="stat-label">Manufacturer:</span>
<span class="stat-value" id="totalVendors"></span>
</li>
<li>
<span class="stat-label">Weight:</span>
<span class="stat-value"><span id="totalWeight"></span></span>
</li>
<li>
<span class="stat-label">Length:</span>
<span class="stat-value"><span id="totalLength"></span></span>
</li>
</ul>
</div>
<div class="statistics-column">
<h3>Materials</h3>
<ul class="statistics-list" id="materialsList">
<!-- Wird dynamisch befüllt -->
</ul>
</div>
</div>
</div>
<div class="feature-box">
<div class="nfc-header">
<h2>NFC-Tag</h2>
<span id="nfcStatusIndicator" class="status-circle"></span>
</div>
<div class="nfc-status-display"></div>
</div>
</div>
<!-- Mittlere Spalte -->
<div class="column">
<div class="feature-box">
<h2>Spoolman Spools</h2>
<label for="vendorSelect">Manufacturer:</label>
<div style="display: flex; justify-content: space-between; align-items: center;">
<select id="vendorSelect" class="styled-select">
<option value="">Please choose...</option>
</select>
<label style="margin-left: 10px;">
<input type="checkbox" id="onlyWithoutSmId" checked onchange="updateFilamentDropdown()">
Only Spools without SM ID
</label>
</div>
</div>
<div id="filamentSection" class="feature-box hidden">
<label>Spool / Filament:</label>
<div class="custom-dropdown">
<div class="dropdown-button" onclick="toggleFilamentDropdown()">
<div class="selected-color" id="selected-color"></div>
<span id="selected-filament">Please choose...</span>
<span class="dropdown-arrow"></span>
</div>
<div class="dropdown-content" id="filament-dropdown-content">
<!-- Optionen werden dynamisch hinzugefügt -->
</div>
</div>
<p id="nfcInfo" class="nfc-status"></p>
<button id="writeNfcButton" class="btn btn-primary hidden" onclick="writeNfcTag()">Write
Tag</button>
</div>
</div>
<!-- Rechte Spalte -->
<div class="column">
<div class="feature-box">
<h2>Bambu AMS</h2>
<div id="amsDataContainer">
<div class="amsData" id="amsData">Wait for AMS-Data...</div>
</div>
</div>
</div>
</div>
</div>
<script src="spoolman.js"></script>
<script src="rfid.js"></script>
</body>
</html>
+33 -2
View File
@@ -57,6 +57,31 @@
toggleOctoFields(); toggleOctoFields();
}; };
function removeBambuCredentials() {
fetch('/api/bambu?remove=true')
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('bambuIp').value = '';
document.getElementById('bambuSerial').value = '';
document.getElementById('bambuCode').value = '';
document.getElementById('autoSend').checked = false;
document.getElementById('autoSendTime').value = '';
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials removed!';
// Reload with forced cache refresh after short delay
setTimeout(() => {
window.location.reload(true);
window.location.href = '/';
}, 1500);
} else {
document.getElementById('bambuStatusMessage').innerText = 'Error while removing Bambu Credentials.';
}
})
.catch(error => {
document.getElementById('bambuStatusMessage').innerText = 'Error while removing: ' + error.message;
});
}
function checkSpoolmanInstance() { function checkSpoolmanInstance() {
const url = document.getElementById('spoolmanUrl').value; const url = document.getElementById('spoolmanUrl').value;
const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked; const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
@@ -89,7 +114,12 @@
.then(data => { .then(data => {
if (data.healthy) { if (data.healthy) {
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!'; document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!';
} else { // Reload with forced cache refresh after short delay
setTimeout(() => {
window.location.reload(true);
window.location.href = '/';
}, 1500);
} else {
document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.'; document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.';
} }
}) })
@@ -162,7 +192,8 @@
</div> </div>
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button> <button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>
<p id="bambuStatusMessage"></p> <button style="margin: 0; background-color: red;" onclick="removeBambuCredentials()">Remove Credentials</button>
<p id="bambuStatusMessage"></p>
</div> </div>
</div> </div>
</div> </div>
+7 -12
View File
@@ -147,6 +147,13 @@ function updateFilamentDropdown(selectedSmId = null) {
if (vendorId) { if (vendorId) {
const filteredFilaments = spoolsData.filter(spool => { const filteredFilaments = spoolsData.filter(spool => {
if (!spool?.filament?.vendor?.id) {
console.log('Problem aufgetreten bei: ', spool?.filament?.vendor);
console.log('Problematische Spulen:',
spoolsData.filter(spool => !spool?.filament?.vendor?.id));
return false;
}
const hasValidNfcId = spool.extra && const hasValidNfcId = spool.extra &&
spool.extra.nfc_id && spool.extra.nfc_id &&
spool.extra.nfc_id !== '""' && spool.extra.nfc_id !== '""' &&
@@ -240,18 +247,6 @@ async function fetchSpoolData() {
} }
} }
/*
// Exportiere Funktionen
window.getSpoolData = () => spoolsData;
window.reloadSpoolData = initSpoolman;
window.populateVendorDropdown = populateVendorDropdown;
window.updateFilamentDropdown = updateFilamentDropdown;
window.toggleFilamentDropdown = () => {
const content = document.getElementById("filament-dropdown-content");
content.classList.toggle("show");
};
*/
// Event Listener // Event Listener
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initSpoolman(); initSpoolman();
+2
View File
@@ -129,6 +129,7 @@
if (data.status === 'success' || lastReceivedProgress >= 98) { if (data.status === 'success' || lastReceivedProgress >= 98) {
clearTimeout(wsReconnectTimer); clearTimeout(wsReconnectTimer);
setTimeout(() => { setTimeout(() => {
window.location.reload(true);
window.location.href = '/'; window.location.href = '/';
}, 30000); }, 30000);
} }
@@ -164,6 +165,7 @@
status.className = 'status success'; status.className = 'status success';
status.style.display = 'block'; status.style.display = 'block';
setTimeout(() => { setTimeout(() => {
window.location.reload(true);
window.location.href = '/'; window.location.href = '/';
}, 30000); }, 30000);
} }
+10
View File
@@ -55,6 +55,8 @@
<h5 class="card-title">Sacle Calibration</h5> <h5 class="card-title">Sacle Calibration</h5>
<button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button> <button id="calibrateBtn" class="btn btn-primary">Calibrate Scale</button>
<button id="tareBtn" class="btn btn-secondary">Tare Scale</button> <button id="tareBtn" class="btn btn-secondary">Tare Scale</button>
&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>
@@ -139,6 +141,14 @@
payload: 'tare' payload: 'tare'
})); }));
}); });
// Add auto-tare function
function setAutoTare(enabled) {
ws.send(JSON.stringify({
type: 'scale',
payload: 'setAutoTare',
enabled: enabled
}));
}
// WebSocket-Verbindung beim Laden der Seite initiieren // WebSocket-Verbindung beim Laden der Seite initiieren
connectWebSocket(); connectWebSocket();
+2 -2
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.1" version = "0.2.1"
to_old_version = "1.4.0" to_old_version = "0.2.0"
## ##
[env:esp32dev] [env:esp32dev]
+1 -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'))
+16 -5
View File
@@ -3,7 +3,7 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "commonFS.h" #include "commonFS.h"
bool spoolman_connected = false; volatile spoolmanApiStateType spoolmanApiState = API_INIT;
String spoolmanUrl = ""; String spoolmanUrl = "";
bool octoEnabled = false; bool octoEnabled = false;
String octoUrl = ""; String octoUrl = "";
@@ -85,7 +85,8 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
} }
void sendToApi(void *parameter) { void sendToApi(void *parameter) {
SendToApiParams* params = (SendToApiParams*)parameter; spoolmanApiState = API_TRANSMITTING;
SendToApiParams *params = (SendToApiParams *)parameter;
// Extrahiere die Werte // Extrahiere die Werte
String httpType = params->httpType; String httpType = params->httpType;
@@ -94,13 +95,17 @@ void sendToApi(void *parameter) {
String octoToken = params->octoToken; String octoToken = params->octoToken;
HTTPClient http; HTTPClient http;
http.setReuse(false);
http.begin(spoolsUrl); http.begin(spoolsUrl);
http.addHeader("Content-Type", "application/json"); http.addHeader("Content-Type", "application/json");
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken); if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
int httpCode = http.PUT(updatePayload); int httpCode;
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
if (httpType == "POST") httpCode = http.POST(updatePayload); else if (httpType == "POST")
httpCode = http.POST(updatePayload);
else
httpCode = http.PUT(updatePayload);
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
Serial.println("Spoolman erfolgreich aktualisiert"); Serial.println("Spoolman erfolgreich aktualisiert");
@@ -111,10 +116,12 @@ void sendToApi(void *parameter) {
} }
http.end(); http.end();
vTaskDelay(50 / portTICK_PERIOD_MS);
// Speicher freigeben // Speicher freigeben
delete params; delete params;
vTaskDelete(NULL); vTaskDelete(NULL);
spoolmanApiState = API_IDLE;
} }
bool updateSpoolTagId(String uidString, const char* payload) { bool updateSpoolTagId(String uidString, const char* payload) {
@@ -164,6 +171,7 @@ bool updateSpoolTagId(String uidString, const char* payload) {
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear();
return true; return true;
} }
@@ -200,6 +208,7 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear();
return 1; return 1;
} }
@@ -237,6 +246,7 @@ bool updateSpoolOcto(int spoolId) {
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
updateDoc.clear();
return true; return true;
} }
@@ -469,7 +479,8 @@ bool checkSpoolmanInstance(const String& url) {
return false; return false;
} }
spoolman_connected = true; spoolmanApiState = API_IDLE;
oledShowTopRow();
return strcmp(status, "healthy") == 0; return strcmp(status, "healthy") == 0;
} }
} }
+7
View File
@@ -6,7 +6,14 @@
#include "website.h" #include "website.h"
#include "display.h" #include "display.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
typedef enum
{
API_INIT,
API_IDLE,
API_TRANSMITTING
} spoolmanApiStateType;
extern volatile spoolmanApiStateType spoolmanApiState;
extern bool spoolman_connected; extern bool spoolman_connected;
extern String spoolmanUrl; extern String spoolmanUrl;
extern bool octoEnabled; extern bool octoEnabled;
+196 -53
View File
@@ -18,7 +18,7 @@ PubSubClient client(sslClient);
TaskHandle_t BambuMqttTask; TaskHandle_t BambuMqttTask;
String report_topic = ""; String report_topic = "";
//String request_topic = ""; String request_topic = "";
const char* bambu_username = "bblp"; const char* bambu_username = "bblp";
const char* bambu_ip = nullptr; const char* bambu_ip = nullptr;
const char* bambu_accesscode = nullptr; const char* bambu_accesscode = nullptr;
@@ -27,6 +27,7 @@ const char* bambu_serialnr = nullptr;
String g_bambu_ip = ""; String g_bambu_ip = "";
String g_bambu_accesscode = ""; String g_bambu_accesscode = "";
String g_bambu_serialnr = ""; String g_bambu_serialnr = "";
bool bambuDisabled = false;
bool bambu_connected = false; bool bambu_connected = false;
bool autoSendToBambu = false; bool autoSendToBambu = false;
@@ -37,6 +38,35 @@ int ams_count = 0;
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
AMSData ams_data[MAX_AMS]; // Definition des Arrays; AMSData ams_data[MAX_AMS]; // Definition des Arrays;
bool removeBambuCredentials()
{
if (BambuMqttTask)
{
vTaskDelete(BambuMqttTask);
}
if (!removeJsonValue("/bambu_credentials.json"))
{
Serial.println("Fehler beim Löschen der Bambu-Credentials.");
return false;
}
// Löschen der globalen Variablen
g_bambu_ip = "";
g_bambu_accesscode = "";
g_bambu_serialnr = "";
bambu_ip = nullptr;
bambu_accesscode = nullptr;
bambu_serialnr = nullptr;
autoSendToBambu = false;
autoSetToBambuSpoolId = 0;
ams_count = 0;
amsJsonData = "";
bambuDisabled = true;
return true;
}
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) { bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
@@ -92,7 +122,7 @@ bool loadBambuCredentials() {
bambu_serialnr = g_bambu_serialnr.c_str(); bambu_serialnr = g_bambu_serialnr.c_str();
report_topic = "device/" + String(bambu_serialnr) + "/report"; report_topic = "device/" + String(bambu_serialnr) + "/report";
//request_topic = "device/" + String(bambu_serialnr) + "/request"; request_topic = "device/" + String(bambu_serialnr) + "/request";
return true; return true;
} }
Serial.println("Keine gültigen Bambu-Credentials gefunden."); Serial.println("Keine gültigen Bambu-Credentials gefunden.");
@@ -197,44 +227,104 @@ FilamentResult findFilamentIdx(String brand, String type) {
} }
bool sendMqttMessage(const String& payload) { bool sendMqttMessage(const String& payload) {
Serial.println("Sending MQTT message"); // Check MQTT client state first
Serial.println(payload); if (!client.connected()) {
if (client.publish(report_topic.c_str(), payload.c_str())) Serial.println("Error: MQTT client not connected when trying to send message");
{ Serial.print("MQTT client state: ");
return true; Serial.println(client.state());
return false;
} }
// Check if request_topic is empty
if (request_topic.isEmpty()) {
Serial.println("Error: request_topic is empty!");
return false;
}
// Check payload
if (payload.isEmpty()) {
Serial.println("Error: Empty payload!");
return false;
}
Serial.println("Attempting to send MQTT message:");
Serial.println("Topic: " + request_topic);
Serial.println("Payload: " + payload);
Serial.println("Payload length: " + String(payload.length()));
return false; // Try to publish with QoS 1 and retain flag false
bool published = client.publish(request_topic.c_str(), payload.c_str(), false);
if (published) {
Serial.println("MQTT message published successfully");
return true;
} else {
Serial.println("Failed to publish MQTT message");
Serial.print("MQTT client state after publish attempt: ");
Serial.println(client.state());
// Additional error information
switch(client.state()) {
case -4:
Serial.println("Reason: Connection timeout");
break;
case -3:
Serial.println("Reason: Connection lost");
break;
case -2:
Serial.println("Reason: Connect failed");
break;
case -1:
Serial.println("Reason: Disconnected");
break;
case 0:
Serial.println("Reason: Connected but publish failed");
break;
default:
Serial.println("Reason: Unknown error");
break;
}
return false;
}
} }
bool setBambuSpool(String payload) { bool setBambuSpool(String payload) {
Serial.println("Spool settings in"); Serial.println("Spool settings received from WebSocket");
Serial.println(payload); Serial.println(payload);
// Parse the JSON // Parse the JSON
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
if (error) { if (error) {
Serial.print("Error parsing JSON: "); Serial.print("Error parsing setBambuSpool-JSON: ");
Serial.println(error.c_str()); Serial.println(error.c_str());
Serial.print("Free Heap-Memory: ");
Serial.println(ESP.getFreeHeap());
return false; return false;
} }
int amsId = doc["amsId"]; // Check if we have all required fields
int trayId = doc["trayId"]; if (!doc["amsId"].is<int>() || !doc["trayId"].is<int>()) {
Serial.println("Error: Missing or invalid required fields in payload");
return false;
}
int amsId = doc["amsId"].as<int>();
int trayId = doc["trayId"].as<int>();
String color = doc["color"].as<String>(); String color = doc["color"].as<String>();
color.toUpperCase(); color.toUpperCase();
int minTemp = doc["nozzle_temp_min"]; int minTemp = doc["nozzle_temp_min"] | 0; // Default to 0 if not present
int maxTemp = doc["nozzle_temp_max"]; int maxTemp = doc["nozzle_temp_max"] | 0; // Default to 0 if not present
String type = doc["type"].as<String>(); String type = doc["type"] | ""; // Default to empty string if not present
(type == "PLA+") ? type = "PLA" : type; (type == "PLA+") ? type = "PLA" : type;
String brand = doc["brand"].as<String>(); String brand = doc["brand"] | ""; // Default to empty string if not present
String tray_info_idx = (doc["tray_info_idx"].as<String>() != "-1") ? doc["tray_info_idx"].as<String>() : ""; String tray_info_idx = (doc["tray_info_idx"].as<String>() != "-1") ? doc["tray_info_idx"].as<String>() : "";
if (tray_info_idx == "") { if (tray_info_idx == "") {
if (brand != "" && type != "") { if (brand != "" && type != "") {
FilamentResult result = findFilamentIdx(brand, type); FilamentResult result = findFilamentIdx(brand, type);
tray_info_idx = result.key; tray_info_idx = result.key;
type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp
Serial.println("Found filament idx: " + tray_info_idx + " for type: " + type);
} }
} }
String setting_id = doc["bambu_setting_id"].as<String>(); String setting_id = doc["bambu_setting_id"].as<String>();
@@ -242,6 +332,7 @@ bool setBambuSpool(String payload) {
doc.clear(); doc.clear();
// Create MQTT message
doc["print"]["sequence_id"] = "0"; doc["print"]["sequence_id"] = "0";
doc["print"]["command"] = "ams_filament_setting"; doc["print"]["command"] = "ams_filament_setting";
doc["print"]["ams_id"] = amsId < 200 ? amsId : 255; doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
@@ -250,26 +341,25 @@ bool setBambuSpool(String payload) {
doc["print"]["nozzle_temp_min"] = minTemp; doc["print"]["nozzle_temp_min"] = minTemp;
doc["print"]["nozzle_temp_max"] = maxTemp; doc["print"]["nozzle_temp_max"] = maxTemp;
doc["print"]["tray_type"] = type; doc["print"]["tray_type"] = type;
//doc["print"]["cali_idx"] = (cali_idx != "") ? cali_idx : "";
doc["print"]["tray_info_idx"] = tray_info_idx; doc["print"]["tray_info_idx"] = tray_info_idx;
doc["print"]["setting_id"] = setting_id; doc["print"]["setting_id"] = setting_id;
// Serialize the JSON // Serialize and send MQTT message
String output; String output;
serializeJson(doc, output); serializeJson(doc, output);
Serial.println("Sending to Bambu printer:");
Serial.println(output);
if (sendMqttMessage(output)) { if (!sendMqttMessage(output)) {
Serial.println("Spool successfully set"); Serial.println("Failed to send filament settings to printer");
}
else
{
Serial.println("Failed to set spool");
return false; return false;
} }
Serial.println("Filament settings sent successfully");
doc.clear(); doc.clear();
yield(); yield();
// Send calibration if available
if (cali_idx != "") { if (cali_idx != "") {
yield(); yield();
doc["print"]["sequence_id"] = "0"; doc["print"]["sequence_id"] = "0";
@@ -278,21 +368,18 @@ bool setBambuSpool(String payload) {
doc["print"]["nozzle_diameter"] = "0.4"; doc["print"]["nozzle_diameter"] = "0.4";
doc["print"]["cali_idx"] = cali_idx.toInt(); doc["print"]["cali_idx"] = cali_idx.toInt();
doc["print"]["tray_id"] = trayId < 200 ? trayId : 254; doc["print"]["tray_id"] = trayId < 200 ? trayId : 254;
//doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
// Serialize the JSON
String output; String output;
serializeJson(doc, output); serializeJson(doc, output);
Serial.println("Sending calibration to printer:");
Serial.println(output);
if (sendMqttMessage(output)) { if (!sendMqttMessage(output)) {
Serial.println("Extrusion calibration successfully set"); Serial.println("Failed to send calibration settings to printer");
}
else
{
Serial.println("Failed to set extrusion calibration");
return false; return false;
} }
Serial.println("Calibration settings sent successfully");
doc.clear(); doc.clear();
yield(); yield();
} }
@@ -420,7 +507,9 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
message = ""; message = "";
if (error) if (error)
{ {
Serial.print("Fehler beim Parsen des JSON: "); Serial.print("Free Heap-Memory: ");
Serial.println(ESP.getFreeHeap());
Serial.print("Error parsing MQTT-JSON: ");
Serial.println(error.c_str()); Serial.println(error.c_str());
return; return;
} }
@@ -549,20 +638,47 @@ void reconnect() {
uint8_t retries = 0; uint8_t retries = 0;
while (!client.connected()) { while (!client.connected()) {
Serial.println("Attempting MQTT re/connection..."); Serial.println("Attempting MQTT re/connection...");
Serial.print("State before connect: ");
Serial.println(client.state());
bambu_connected = false; bambu_connected = false;
oledShowTopRow(); oledShowTopRow();
// Attempt to connect // Generate a random client ID suffix
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) { String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
Serial.println("MQTT re/connected"); Serial.print("Reconnecting with client ID: ");
Serial.println(clientId);
client.subscribe(report_topic.c_str()); // Attempt to connect with clean session and will message
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr)) {
Serial.println("MQTT re/connected");
// Subscribe with QoS 1
if (client.subscribe(report_topic.c_str(), 1)) {
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
} else {
Serial.println("Failed to subscribe to topic: " + report_topic);
}
bambu_connected = true; bambu_connected = true;
oledShowTopRow(); oledShowTopRow();
} else { } else {
int state = client.state();
Serial.print("failed, rc="); Serial.print("failed, rc=");
Serial.print(client.state()); Serial.print(state);
Serial.println(" try again in 5 seconds"); Serial.print(" (");
// Print detailed error message
switch(state) {
case -4: Serial.print("MQTT_CONNECTION_TIMEOUT"); break;
case -3: Serial.print("MQTT_CONNECTION_LOST"); break;
case -2: Serial.print("MQTT_CONNECT_FAILED"); break;
case -1: Serial.print("MQTT_DISCONNECTED"); break;
case 1: Serial.print("MQTT_CONNECT_BAD_PROTOCOL"); break;
case 2: Serial.print("MQTT_CONNECT_BAD_CLIENT_ID"); break;
case 3: Serial.print("MQTT_CONNECT_UNAVAILABLE"); break;
case 4: Serial.print("MQTT_CONNECT_BAD_CREDENTIALS"); break;
case 5: Serial.print("MQTT_CONNECT_UNAUTHORIZED"); break;
default: Serial.print("UNKNOWN"); break;
}
Serial.println(") try again in 5 seconds");
bambu_connected = false; bambu_connected = false;
oledShowTopRow(); oledShowTopRow();
@@ -570,11 +686,9 @@ void reconnect() {
vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS);
if (retries > 5) { if (retries > 5) {
Serial.println("Disable Bambu MQTT Task after 5 retries"); Serial.println("Disable Bambu MQTT Task after 5 retries");
//vTaskSuspend(BambuMqttTask);
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
break; break;
} }
retries++; retries++;
} }
} }
@@ -582,17 +696,34 @@ void reconnect() {
void mqtt_loop(void * parameter) { void mqtt_loop(void * parameter) {
Serial.println("Bambu MQTT Task gestartet"); Serial.println("Bambu MQTT Task gestartet");
unsigned long lastCheck = 0;
for(;;) { for(;;) {
if (pauseBambuMqttTask) { if (pauseBambuMqttTask) {
vTaskDelay(10000); vTaskDelay(10000);
continue;
} }
unsigned long now = millis();
if (!client.connected()) { if (!client.connected()) {
Serial.println("Connection lost, attempting reconnect...");
reconnect(); reconnect();
yield(); yield();
esp_task_wdt_reset(); esp_task_wdt_reset();
vTaskDelay(100); vTaskDelay(100);
continue;
} }
// Periodically check connection status
if (now - lastCheck > 60000) { // Check every 60 seconds
Serial.print("MQTT Status Check - Connected: ");
Serial.println(client.connected() ? "Yes" : "No");
lastCheck = now;
Serial.print("Free Heap-Memory: ");
Serial.println(ESP.getFreeHeap());
}
client.loop(); client.loop();
yield(); yield();
esp_task_wdt_reset(); esp_task_wdt_reset();
@@ -605,26 +736,41 @@ bool setupMqtt() {
bool success = loadBambuCredentials(); bool success = loadBambuCredentials();
if (!success) { if (!success) {
Serial.println("Failed to load Bambu credentials"); bambuDisabled = true;
oledShowMessage("Bambu Credentials Missing");
vTaskDelay(2000 / portTICK_PERIOD_MS);
return false; return false;
} }
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "") if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
{ {
bambuDisabled = false;
sslClient.setCACert(root_ca); sslClient.setCACert(root_ca);
sslClient.setInsecure(); sslClient.setInsecure();
client.setServer(bambu_ip, 8883); client.setServer(bambu_ip, 8883);
// Generate a random client ID suffix
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
// MQTT Connection Options
client.setKeepAlive(60);
client.setSocketTimeout(60); // Increase socket timeout
Serial.print("Connecting with client ID: ");
Serial.println(clientId);
// Verbinden mit dem MQTT-Server // Verbinden mit dem MQTT-Server
bool connected = true; bool connected = true;
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr))
{ {
client.setCallback(mqtt_callback); client.setCallback(mqtt_callback);
client.setBufferSize(5120); client.setBufferSize(16384); // Increased to 16KB to handle larger JSON
client.subscribe(report_topic.c_str());
//client.subscribe(request_topic.c_str()); // Subscribe with QoS 1
if (client.subscribe(report_topic.c_str(), 1)) {
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
} else {
Serial.println("Failed to subscribe to topic: " + report_topic);
}
Serial.println("MQTT-Client initialisiert"); Serial.println("MQTT-Client initialisiert");
oledShowMessage("Bambu Connected"); oledShowMessage("Bambu Connected");
@@ -634,7 +780,7 @@ bool setupMqtt() {
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
mqtt_loop, /* Function to implement the task */ mqtt_loop, /* Function to implement the task */
"BambuMqtt", /* Name of the task */ "BambuMqtt", /* Name of the task */
8192, /* Stack size in words */ 16384, /* Stack size in words */
NULL, /* Task input parameter */ NULL, /* Task input parameter */
mqttTaskPrio, /* Priority of the task */ mqttTaskPrio, /* Priority of the task */
&BambuMqttTask, /* Task handle. */ &BambuMqttTask, /* Task handle. */
@@ -653,10 +799,7 @@ bool setupMqtt() {
} }
else else
{ {
Serial.println("Fehler: Keine MQTT-Daten vorhanden"); bambuDisabled = true;
oledShowMessage("Bambu Credentials Missing");
oledShowTopRow();
vTaskDelay(2000 / portTICK_PERIOD_MS);
return false; return false;
} }
return true; return true;
+2
View File
@@ -30,7 +30,9 @@ extern int ams_count;
extern AMSData ams_data[MAX_AMS]; extern AMSData ams_data[MAX_AMS];
extern bool autoSendToBambu; extern bool autoSendToBambu;
extern int autoSetToBambuSpoolId; extern int autoSetToBambuSpoolId;
extern bool bambuDisabled;
bool removeBambuCredentials();
bool loadBambuCredentials(); bool loadBambuCredentials();
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime); bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime);
bool setupMqtt(); bool setupMqtt();
+17
View File
@@ -1,6 +1,23 @@
#include "commonFS.h" #include "commonFS.h"
#include <LittleFS.h> #include <LittleFS.h>
bool removeJsonValue(const char *filename)
{
File file = LittleFS.open(filename, "r");
if (!file)
{
return true;
}
file.close();
if (!LittleFS.remove(filename))
{
Serial.print("Fehler beim Löschen der Datei: ");
Serial.println(filename);
return false;
}
return true;
}
bool saveJsonValue(const char* filename, const JsonDocument& doc) { bool saveJsonValue(const char* filename, const JsonDocument& doc) {
File file = LittleFS.open(filename, "w"); File file = LittleFS.open(filename, "w");
if (!file) { if (!file) {
+2 -1
View File
@@ -5,7 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <LittleFS.h> #include <LittleFS.h>
bool saveJsonValue(const char* filename, const JsonDocument& doc); bool removeJsonValue(const char *filename);
bool saveJsonValue(const char *filename, const JsonDocument &doc);
bool loadJsonValue(const char* filename, JsonDocument& doc); bool loadJsonValue(const char* filename, JsonDocument& doc);
void initializeFileSystem(); void initializeFileSystem();
+5
View File
@@ -19,6 +19,11 @@ const uint16_t SCALE_LEVEL_WEIGHT = 500;
uint16_t defaultScaleCalibrationValue = 430; uint16_t defaultScaleCalibrationValue = 430;
// ***** HX711 // ***** HX711
// ***** TTP223 (Touch Sensor)
// TTP223 circuit wiring
const uint8_t TTP223_PIN = 25;
// ***** TTP223
// ***** Display // ***** Display
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// On an ESP32: 21(SDA), 22(SCL) // On an ESP32: 21(SDA), 22(SCL)
+1
View File
@@ -10,6 +10,7 @@ extern const uint8_t LOADCELL_DOUT_PIN;
extern const uint8_t LOADCELL_SCK_PIN; extern const uint8_t LOADCELL_SCK_PIN;
extern const uint8_t calVal_eepromAdress; extern const uint8_t calVal_eepromAdress;
extern const uint16_t SCALE_LEVEL_WEIGHT; extern const uint16_t SCALE_LEVEL_WEIGHT;
extern const uint8_t TTP223_PIN;
extern const int8_t OLED_RESET; extern const int8_t OLED_RESET;
extern const uint8_t SCREEN_ADDRESS; extern const uint8_t SCREEN_ADDRESS;
+6 -3
View File
@@ -177,9 +177,12 @@ void oledShowTopRow() {
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE); display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE);
} }
if (spoolman_connected == 1) { if (spoolmanApiState != API_INIT)
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE); {
} else { display.drawBitmap(80, 0, bitmap_spoolman_on, 16, 16, WHITE);
}
else
{
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE); display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);
} }
+40 -21
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)) {
checkWiFiConnection(); checkWiFiConnection();
@@ -98,9 +119,14 @@ void loop() {
// Wenn Bambu auto set Spool aktiv // Wenn Bambu auto set Spool aktiv
if (autoSendToBambu && autoSetToBambuSpoolId > 0) { if (autoSendToBambu && autoSetToBambuSpoolId > 0) {
if (!bambuDisabled && !bambu_connected)
{
bambu_restart();
}
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(autoSetBambuAmsCounter - autoAmsCounter) + "s");
@@ -134,7 +160,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 && (!autoSendToBambu || autoSetToBambuSpoolId == 0)))
{ {
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight); (weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
} }
@@ -147,27 +173,18 @@ 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 < 0) if (autoTare && (weight > 0 && weight < 5) || weight < 0)
{ {
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
@@ -183,15 +200,17 @@ 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;
} }
lastWeight = weight; lastWeight = weight;
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden // Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && hasReadRfidTag == 1) { if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS)
{
oledShowIcon("loading"); oledShowIcon("loading");
if (updateSpoolWeight(spoolId, weight)) if (updateSpoolWeight(spoolId, weight))
{ {
@@ -211,6 +230,6 @@ void loop() {
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
} }
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
+294 -120
View File
@@ -7,9 +7,23 @@
#include "api.h" #include "api.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "scale.h" #include "scale.h"
#include <SPI.h>
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS); // Pin definitions for both PN532 chips
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET); #define PN532_SCK (18) // SPI SCK
#define PN532_MISO (19) // SPI MISO
#define PN532_MOSI (23) // SPI MOSI
// CS pins for each PN532
#define PN532_CS1 (5) // CS for first PN532
#define PN532_CS2 (4) // CS for second PN532
// Mifare authentication key
uint8_t keyA[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// Create two PN532 instances
Adafruit_PN532 nfc1(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_CS1);
Adafruit_PN532 nfc2(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_CS2);
TaskHandle_t RfidReaderTask; TaskHandle_t RfidReaderTask;
@@ -18,7 +32,7 @@ String spoolId = "";
String nfcJsonData = ""; String nfcJsonData = "";
volatile bool pauseBambuMqttTask = false; volatile bool pauseBambuMqttTask = false;
volatile uint8_t hasReadRfidTag = 0; volatile nfcReaderStateType nfcReaderState = NFC_IDLE;
// 0 = nicht gelesen // 0 = nicht gelesen
// 1 = erfolgreich gelesen // 1 = erfolgreich gelesen
// 2 = fehler beim Lesen // 2 = fehler beim Lesen
@@ -28,6 +42,165 @@ volatile uint8_t hasReadRfidTag = 0;
// 6 = reading // 6 = reading
// ***** PN532 // ***** PN532
// Buffer for reading data
uint8_t data[32];
// Function to initialize a specific PN532
bool initPN532(Adafruit_PN532 &pn532) {
pn532.begin();
uint32_t versiondata = pn532.getFirmwareVersion();
if (!versiondata) {
Serial.println("Didn't find PN532 board");
return false;
}
// Got valid data, print it out!
Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC);
// Configure board to read RFID tags
pn532.SAMConfig();
Serial.println("Waiting for an ISO14443A Card ...");
return true;
}
void initNfc() {
// Configure CS pins as outputs
pinMode(PN532_CS1, OUTPUT);
pinMode(PN532_CS2, OUTPUT);
digitalWrite(PN532_CS1, HIGH); // Deselect both chips initially
digitalWrite(PN532_CS2, HIGH);
// Initialize SPI
SPI.begin(PN532_SCK, PN532_MISO, PN532_MOSI);
SPI.setFrequency(1000000); // 1MHz SPI clock
// Initialize both PN532 chips
if (!initPN532(nfc1)) {
Serial.println("Failed to initialize PN532 #1");
return;
}
if (!initPN532(nfc2)) {
Serial.println("Failed to initialize PN532 #2");
return;
}
Serial.println("Both PN532 chips initialized successfully");
}
// Function to read a specific PN532
bool readPN532(Adafruit_PN532 &pn532, uint8_t *uid, uint8_t *uidLength) {
uint8_t success;
success = pn532.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, uidLength);
return success;
}
// Function to write to a specific PN532
bool writePN532(Adafruit_PN532 &pn532, uint8_t *uid, uint8_t uidLength, uint8_t *data, uint8_t dataLen) {
if (!pn532.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 1, keyA)) {
Serial.println("Failed to authenticate block");
return false;
}
if (!pn532.mifareclassic_WriteDataBlock(4, data)) {
Serial.println("Failed to write block");
return false;
}
return true;
}
void loopNfc() {
uint8_t uid1[] = {0, 0, 0, 0, 0, 0, 0};
uint8_t uid2[] = {0, 0, 0, 0, 0, 0, 0};
uint8_t uidLength1 = 0;
uint8_t uidLength2 = 0;
// Try to read from both PN532 chips
bool success1 = readPN532(nfc1, uid1, &uidLength1);
bool success2 = readPN532(nfc2, uid2, &uidLength2);
if (success1 || success2) {
// Process the first tag if found
if (success1) {
processTag(uid1, uidLength1, 1);
}
// Process the second tag if found
if (success2) {
processTag(uid2, uidLength2, 2);
}
}
}
void processTag(uint8_t *uid, uint8_t uidLength, uint8_t readerNumber) {
Adafruit_PN532 &pn532 = (readerNumber == 1) ? nfc1 : nfc2;
Serial.print("Reader "); Serial.print(readerNumber); Serial.println(" found tag:");
Serial.print("UID Length: "); Serial.println(uidLength);
Serial.print("UID Value: ");
for (uint8_t i = 0; i < uidLength; i++) {
Serial.print(" 0x"); Serial.print(uid[i], HEX);
}
Serial.println("");
if (uidLength == 7) {
uint16_t tagSize = readTagSize(pn532);
if(tagSize > 0) {
Serial.print("Tag size: "); Serial.println(tagSize);
uint8_t* data = (uint8_t*)malloc(tagSize);
memset(data, 0, tagSize);
// Mehrere Leseversuche
bool readSuccess = false;
for(int attempt = 0; attempt < 3 && !readSuccess; attempt++) {
readSuccess = true;
uint8_t numPages = tagSize / 4;
for (uint8_t i = 4; i < 4 + numPages; i++) {
if (!pn532.ntag2xx_ReadPage(i, data + (i - 4) * 4)) {
Serial.print("Failed to read page "); Serial.println(i);
readSuccess = false;
break;
}
delay(5); // Kleine Pause zwischen den Seiten
}
if (!readSuccess) {
delay(50); // Pause vor erneutem Versuch
}
}
if (readSuccess) {
Serial.println("Successfully read tag data.");
processNfcData(data, createTagId(uid, uidLength));
} else {
Serial.println("Failed to read tag data after 3 attempts");
oledShowMessage("Read Error");
}
free(data);
}
}
}
void processNfcData(uint8_t *data, String tagId) {
// Process the data and send it via WebSocket
if (decodeNdefAndReturnJson(data)) {
nfcReaderState = NFC_READ_SUCCESS;
sendNfcData(nullptr);
} else {
nfcReaderState = NFC_READ_ERROR;
oledShowMessage("NFC-Tag unknown");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
// Function to write to a specific tag
bool writeNfcTag(uint8_t *uid, uint8_t uidLength, uint8_t *data, uint8_t dataLen, uint8_t readerNumber) {
// Select the appropriate PN532 based on reader number
Adafruit_PN532 &pn532 = (readerNumber == 1) ? nfc1 : nfc2;
return writePN532(pn532, uid, uidLength, data, dataLen);
}
// ##### Funktionen für RFID ##### // ##### Funktionen für RFID #####
void payloadToJson(uint8_t *data) { void payloadToJson(uint8_t *data) {
@@ -80,7 +253,7 @@ bool formatNdefTag() {
// Schreibe die Initialisierungsnachricht auf die ersten Seiten // Schreibe die Initialisierungsnachricht auf die ersten Seiten
for (int i = 0; i < sizeof(ndefInit); i += 4) { for (int i = 0; i < sizeof(ndefInit); i += 4) {
if (!nfc.ntag2xx_WritePage(pageOffset + (i / 4), &ndefInit[i])) { if (!nfc1.ntag2xx_WritePage(pageOffset + (i / 4), &ndefInit[i])) {
success = false; success = false;
break; break;
} }
@@ -89,16 +262,15 @@ bool formatNdefTag() {
return success; return success;
} }
uint16_t readTagSize() uint16_t readTagSize(Adafruit_PN532 &pn532) {
{ uint8_t buffer[4];
uint8_t buffer[4]; memset(buffer, 0, 4);
memset(buffer, 0, 4); pn532.ntag2xx_ReadPage(3, buffer);
nfc.ntag2xx_ReadPage(3, buffer); return buffer[2] * 8;
return buffer[2]*8;
} }
uint8_t ntag2xx_WriteNDEF(const char *payload) { uint8_t ntag2xx_WriteNDEF(const char *payload, Adafruit_PN532 &pn532) {
uint16_t tagSize = readTagSize(); uint16_t tagSize = readTagSize(pn532);
Serial.print("Tag Size: ");Serial.println(tagSize); Serial.print("Tag Size: ");Serial.println(tagSize);
uint8_t pageBuffer[4] = {0, 0, 0, 0}; uint8_t pageBuffer[4] = {0, 0, 0, 0};
@@ -157,11 +329,7 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
int bytesToWrite = (totalSize < 4) ? totalSize : 4; int bytesToWrite = (totalSize < 4) ? totalSize : 4;
memcpy(pageBuffer, combinedData + a, bytesToWrite); memcpy(pageBuffer, combinedData + a, bytesToWrite);
//uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID if (!(pn532.ntag2xx_WritePage(4+i, pageBuffer)))
//uint8_t uidLength;
//nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 100);
if (!(nfc.ntag2xx_WritePage(4+i, pageBuffer)))
{ {
Serial.println("Fehler beim Schreiben der Seite."); Serial.println("Fehler beim Schreiben der Seite.");
free(combinedData); free(combinedData);
@@ -169,8 +337,6 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
} }
yield(); yield();
//esp_task_wdt_reset();
i++; i++;
a += 4; a += 4;
totalSize -= bytesToWrite; totalSize -= bytesToWrite;
@@ -179,7 +345,7 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
// Ensure the NDEF message is properly terminated // Ensure the NDEF message is properly terminated
memset(pageBuffer, 0, 4); memset(pageBuffer, 0, 4);
pageBuffer[0] = 0xFE; // NDEF record footer pageBuffer[0] = 0xFE; // NDEF record footer
if (!(nfc.ntag2xx_WritePage(4+i, pageBuffer))) if (!(pn532.ntag2xx_WritePage(4+i, pageBuffer)))
{ {
Serial.println("Fehler beim Schreiben des End-Bits."); Serial.println("Fehler beim Schreiben des End-Bits.");
free(combinedData); free(combinedData);
@@ -238,32 +404,43 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
void writeJsonToTag(void *parameter) { void writeJsonToTag(void *parameter) {
const char* payload = (const char*)parameter; const char* payload = (const char*)parameter;
// Gib die erstellte NDEF-Message aus
Serial.println("Erstelle NDEF-Message..."); Serial.println("Erstelle NDEF-Message...");
Serial.println(payload); Serial.println(payload);
hasReadRfidTag = 3; nfcReaderState = NFC_WRITING;
vTaskSuspend(RfidReaderTask); vTaskSuspend(RfidReaderTask);
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(50 / portTICK_PERIOD_MS);
//pauseBambuMqttTask = true;
// aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData(nullptr);
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
oledShowMessage("Waiting for NFC-Tag"); oledShowMessage("Waiting for NFC-Tag");
// Wait 10sec for tag // Try both readers
uint8_t success = 0; uint8_t success = 0;
String uidString = ""; String uidString = "";
Adafruit_PN532* activeReader = nullptr;
for (uint16_t i = 0; i < 20; i++) { for (uint16_t i = 0; i < 20; i++) {
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID // Try first reader
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength; uint8_t uidLength;
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500); success = nfc1.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 250);
if (!success) {
// Try second reader
success = nfc2.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 250);
if (success) {
activeReader = &nfc2;
}
} else {
activeReader = &nfc1;
}
if (success) { if (success) {
for (uint8_t i = 0; i < uidLength; i++) { for (uint8_t i = 0; i < uidLength; i++) {
uidString += String(uid[i], HEX); uidString += String(uid[i], HEX);
if (i < uidLength - 1) { if (i < uidLength - 1) {
uidString += ":"; // Optional: Trennzeichen hinzufügen uidString += ":";
} }
} }
foundNfcTag(nullptr, success); foundNfcTag(nullptr, success);
@@ -277,50 +454,42 @@ void writeJsonToTag(void *parameter) {
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
} }
if (success) if (success && activeReader != nullptr) {
{
oledShowIcon("transfer"); oledShowIcon("transfer");
// Schreibe die NDEF-Message auf den Tag // Schreibe die NDEF-Message auf den Tag
success = ntag2xx_WriteNDEF(payload); success = ntag2xx_WriteNDEF(payload, *activeReader);
if (success) if (success) {
{
Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben"); Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben");
//oledShowMessage("NFC-Tag written");
oledShowIcon("success"); oledShowIcon("success");
vTaskDelay(1000 / portTICK_PERIOD_MS); vTaskDelay(1000 / portTICK_PERIOD_MS);
hasReadRfidTag = 5; nfcReaderState = NFC_WRITE_SUCCESS;
// aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData(nullptr);
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
if (updateSpoolTagId(uidString, payload)) { if (updateSpoolTagId(uidString, payload)) {
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength; uint8_t uidLength;
oledShowIcon("success"); oledShowIcon("success");
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) { while (activeReader->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
yield(); yield();
} }
} }
vTaskResume(RfidReaderTask); vTaskResume(RfidReaderTask);
vTaskDelay(500 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
} } else {
else
{
Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag"); Serial.println("Fehler beim Schreiben der NDEF-Message auf den Tag");
oledShowIcon("failed"); oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 4; nfcReaderState = NFC_WRITE_ERROR;
} }
} } else {
else
{
Serial.println("Fehler: Kein Tag zu schreiben gefunden."); Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
oledShowMessage("No NFC-Tag found"); oledShowMessage("No NFC-Tag found");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 0; nfcReaderState = NFC_IDLE;
} }
sendWriteResult(nullptr, success); sendWriteResult(nullptr, success);
sendNfcData(nullptr); sendNfcData(nullptr);
@@ -334,7 +503,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
@@ -348,69 +517,79 @@ void startWriteJsonToTag(const char* payload) {
} }
void scanRfidTask(void * parameter) { void scanRfidTask(void * parameter) {
Serial.println("RFID Task gestartet"); Serial.println("RFID Task gestartet");
for(;;) { if (nfcReaderState != NFC_WRITING)
// Wenn geschrieben wird Schleife aussetzen
if (hasReadRfidTag != 3)
{ {
yield(); yield();
uint8_t success; uint8_t success = 0;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0};
uint8_t uidLength; uint8_t uidLength;
Adafruit_PN532 *activeReader = nullptr;
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000); // Try first reader with increased timeout
success = nfc1.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 150);
if (success)
{
activeReader = &nfc1;
}
else
{
delay(50); // Small delay between readers
// Try second reader with increased timeout
success = nfc2.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 150);
if (success)
{
activeReader = &nfc2;
}
}
foundNfcTag(nullptr, success); foundNfcTag(nullptr, success);
if (success && hasReadRfidTag != 1) if (success && nfcReaderState != NFC_READ_SUCCESS && activeReader != nullptr)
{ {
// Display some basic information about the card
Serial.println("Found an ISO14443A card"); Serial.println("Found an ISO14443A card");
hasReadRfidTag = 6; nfcReaderState = NFC_READING;
oledShowIcon("transfer"); oledShowIcon("transfer");
vTaskDelay(500 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
if (uidLength == 7) if (uidLength == 7)
{ {
uint16_t tagSize = readTagSize(); uint16_t tagSize = readTagSize(*activeReader);
if(tagSize > 0) if (tagSize > 0)
{ {
// Create a buffer depending on the size of the tag uint8_t *data = (uint8_t *)malloc(tagSize);
uint8_t* data = (uint8_t*)malloc(tagSize);
memset(data, 0, tagSize); memset(data, 0, tagSize);
// We probably have an NTAG2xx card (though it could be Ultralight as well)
Serial.println("Seems to be an NTAG2xx tag (7 byte UID)"); Serial.println("Seems to be an NTAG2xx tag (7 byte UID)");
uint8_t numPages = readTagSize()/4; uint8_t numPages = readTagSize(*activeReader) / 4;
for (uint8_t i = 4; i < 4+numPages; i++) { for (uint8_t i = 4; i < 4 + numPages; i++)
if (!nfc.ntag2xx_ReadPage(i, data+(i-4) * 4)) {
if (!activeReader->ntag2xx_ReadPage(i, data + (i - 4) * 4))
{ {
break; // Stop if reading fails break;
} }
// Check for NDEF message end if (data[(i - 4) * 4] == 0xFE)
if (data[(i - 4) * 4] == 0xFE)
{ {
break; // End of NDEF message break;
} }
yield(); yield();
esp_task_wdt_reset(); esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(5)); // Increased delay between page reads
} }
if (!decodeNdefAndReturnJson(data)) if (!decodeNdefAndReturnJson(data))
{ {
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 +597,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,58 +606,53 @@ void scanRfidTask(void * parameter) {
} }
} }
if (!success && hasReadRfidTag > 0) if (!success && nfcReaderState != NFC_IDLE)
{ {
hasReadRfidTag = 0; nfcReaderState = NFC_IDLE;
//uidString = "";
nfcJsonData = ""; nfcJsonData = "";
Serial.println("Tag entfernt"); Serial.println("Tag entfernt");
if (!autoSendToBambu) oledShowWeight(weight); if (!autoSendToBambu)
oledShowWeight(weight);
} }
// aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData(nullptr);
delay(100); // Add small delay at end of loop
} }
yield(); yield();
}
void startNfc() {
initNfc();
BaseType_t result = xTaskCreatePinnedToCore(
scanRfidTask, /* Function to implement the task */
"RfidReader", /* Name of the task */
5115, /* Stack size in words */
NULL, /* Task input parameter */
rfidTaskPrio, /* Priority of the task */
&RfidReaderTask, /* Task handle. */
rfidTaskCore); /* Core where the task should run */
if (result != pdPASS) {
Serial.println("Fehler beim Erstellen des RFID Tasks");
} else {
Serial.println("RFID Task erfolgreich erstellt");
} }
} }
void startNfc() { String createTagId(uint8_t *uid, uint8_t uidLength)
nfc.begin(); // Beginne Kommunikation mit RFID Leser {
delay(1000); String tagId = ""; // Initialisieren Sie einen leeren String
unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus
if (! versiondata) { // Wenn keine Antwort kommt
Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor
//delay(5000);
//ESP.restart();
oledShowMessage("No RFID Board found");
delay(2000);
}
else {
Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen
Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); // Monitor, wenn Antwort vom Board kommt
Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); //
nfc.SAMConfig(); // Erstellen Sie die Tag-ID basierend auf dem UID
// Set the max number of retry attempts to read from a card for (uint8_t i = 0; i < uidLength; i++)
// This prevents us from waiting forever for a card, which is {
// the default behaviour of the PN532. tagId += String(uid[i], HEX); // Konvertieren Sie die UID in einen String
//nfc.setPassiveActivationRetries(0x7F); if (i < uidLength - 1)
//nfc.setPassiveActivationRetries(0xFF); {
tagId += ":"; // Fügen Sie einen Trennstrich hinzu, wenn es nicht das letzte Element ist
BaseType_t result = xTaskCreatePinnedToCore(
scanRfidTask, /* Function to implement the task */
"RfidReader", /* Name of the task */
5115, /* Stack size in words */
NULL, /* Task input parameter */
rfidTaskPrio, /* Priority of the task */
&RfidReaderTask, /* Task handle. */
rfidTaskCore); /* Core where the task should run */
if (result != pdPASS) {
Serial.println("Fehler beim Erstellen des RFID Tasks");
} else {
Serial.println("RFID Task erfolgreich erstellt");
} }
} }
return tagId; // Geben Sie die erstellte Tag-ID zurück
Serial.println("tagID:" + tagId);
} }
+21 -5
View File
@@ -1,16 +1,32 @@
#ifndef NFC_H #pragma once
#define NFC_H
#include <Arduino.h> #include <Arduino.h>
#include <Adafruit_PN532.h>
typedef enum
{
NFC_IDLE,
NFC_READING,
NFC_READ_SUCCESS,
NFC_READ_ERROR,
NFC_WRITING,
NFC_WRITE_SUCCESS,
NFC_WRITE_ERROR
} nfcReaderStateType;
void startNfc(); void startNfc();
void scanRfidTask(void * parameter);
void startWriteJsonToTag(const char* payload); void startWriteJsonToTag(const char* payload);
void writeJsonToTag(void *parameter);
void scanRfidTask(void * parameter);
void processTag(uint8_t *uid, uint8_t uidLength, uint8_t readerNumber);
void processNfcData(uint8_t *data, String tagId);
bool decodeNdefAndReturnJson(const byte* encodedMessage);
extern TaskHandle_t RfidReaderTask; extern TaskHandle_t RfidReaderTask;
extern String nfcJsonData; extern String nfcJsonData;
extern String spoolId; extern String spoolId;
extern volatile uint8_t hasReadRfidTag; extern volatile nfcReaderStateType nfcReaderState;
extern volatile bool pauseBambuMqttTask; extern volatile bool pauseBambuMqttTask;
#endif // Function declarations
uint16_t readTagSize(Adafruit_PN532 &pn532);
String createTagId(uint8_t *uid, uint8_t uidLength);
+42 -4
View File
@@ -14,6 +14,7 @@ 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;
@@ -21,9 +22,26 @@ uint8_t scaleCalibrated = 1;
Preferences preferences; Preferences preferences;
const char* NVS_NAMESPACE = "scale"; const char* NVS_NAMESPACE = "scale";
const char* NVS_KEY_CALIBRATION = "cal_value"; const char* NVS_KEY_CALIBRATION = "cal_value";
const char *NVS_KEY_AUTOTARE = "auto_tare";
bool autoTare = true;
// ##### Funktionen für Waage ##### // ##### Funktionen für Waage #####
uint8_t tareScale() { uint8_t setAutoTare(bool autoTareValue)
{
Serial.print("Set AutoTare to ");
Serial.println(autoTareValue);
autoTare = autoTareValue;
// Speichern mit NVS
preferences.begin(NVS_NAMESPACE, false); // false = readwrite
preferences.putBool(NVS_KEY_AUTOTARE, autoTare);
preferences.end();
return 1;
}
uint8_t tareScale()
{
Serial.println("Tare scale"); Serial.println("Tare scale");
scale.tare(); scale.tare();
@@ -38,11 +56,23 @@ 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 (scaleTareRequest == true) if (autoTare && scale_tare_counter >= 5)
{
Serial.println("Auto Tare scale");
scale.tare();
scale_tare_counter = 0;
}
// Waage manuell Taren
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;
} }
@@ -53,13 +83,21 @@ void scale_loop(void * parameter) {
} }
} }
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.begin(NVS_NAMESPACE, 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 ");
+4 -2
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;
+66 -37
View File
@@ -22,14 +22,13 @@ AsyncWebServer server(webserverPort);
AsyncWebSocket ws("/ws"); AsyncWebSocket ws("/ws");
uint8_t lastSuccess = 0; uint8_t lastSuccess = 0;
uint8_t lastHasReadRfidTag = 0; nfcReaderStateType lastnfcReaderState = NFC_IDLE;
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_CONNECT) { if (type == WS_EVT_CONNECT) {
Serial.println("Neuer Client verbunden!"); Serial.println("Neuer Client verbunden!");
// Sende die AMS-Daten an den neuen Client // Sende die AMS-Daten an den neuen Client
sendAmsData(client); if (!bambuDisabled) sendAmsData(client);
sendNfcData(client); sendNfcData(client);
foundNfcTag(client, 0); foundNfcTag(client, 0);
sendWriteResult(client, 3); sendWriteResult(client, 3);
@@ -43,6 +42,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
String message = String((char*)data); String message = String((char*)data);
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, message); deserializeJson(doc, message);
bool spoolmanConnected = (spoolmanApiState != API_INIT);
if (doc["type"] == "heartbeat") { if (doc["type"] == "heartbeat") {
// Sende Heartbeat-Antwort // Sende Heartbeat-Antwort
@@ -50,7 +50,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
"\"type\":\"heartbeat\"," "\"type\":\"heartbeat\","
"\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + "," "\"freeHeap\":" + String(ESP.getFreeHeap()/1024) + ","
"\"bambu_connected\":" + String(bambu_connected) + "," "\"bambu_connected\":" + String(bambu_connected) + ","
"\"spoolman_connected\":" + String(spoolman_connected) + "" "\"spoolman_connected\":" + String(spoolmanConnected) + ""
"}"); "}");
} }
@@ -73,6 +73,11 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
success = calibrate_scale(); success = calibrate_scale();
} }
if (doc["payload"] == "setAutoTare")
{
success = setAutoTare(doc["enabled"].as<bool>());
}
if (success) { if (success) {
ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}"); ws.textAll("{\"type\":\"scale\",\"payload\":\"success\"}");
} else { } else {
@@ -139,34 +144,33 @@ void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) {
} }
void sendNfcData(AsyncWebSocketClient *client) { void sendNfcData(AsyncWebSocketClient *client) {
if (lastHasReadRfidTag == hasReadRfidTag) return; if (lastnfcReaderState == nfcReaderState)
if (hasReadRfidTag == 0) { return;
// TBD: Why is there no status for reading the tag?
switch (nfcReaderState)
{
case NFC_IDLE:
ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":{}}");
} break;
else if (hasReadRfidTag == 1) { case NFC_READ_SUCCESS:
ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":" + nfcJsonData + "}");
} break;
else if (hasReadRfidTag == 2) case NFC_READ_ERROR:
{
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Empty Tag or Data not readable\"}}");
} break;
else if (hasReadRfidTag == 3) case NFC_WRITING:
{
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Schreibe Tag...\"}}");
} break;
else if (hasReadRfidTag == 4) case NFC_WRITE_SUCCESS:
{
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Error writing to Tag\"}}");
}
else if (hasReadRfidTag == 5)
{
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"info\":\"Tag erfolgreich geschrieben\"}}");
} break;
else case NFC_WRITE_ERROR:
{ ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Error writing to Tag\"}}");
break;
case DEFAULT:
ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Something went wrong\"}}"); ws.textAll("{\"type\":\"nfcData\", \"payload\":{\"error\":\"Something went wrong\"}}");
} }
lastHasReadRfidTag = hasReadRfidTag; lastnfcReaderState = nfcReaderState;
} }
void sendAmsData(AsyncWebSocketClient *client) { void sendAmsData(AsyncWebSocketClient *client) {
@@ -202,18 +206,24 @@ 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
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /rfid erhalten"); Serial.println("Anfrage für /rfid erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/rfid.html.gz", "text/html");
String page = (bambuDisabled) ? "/rfid.html.gz" : "/rfid_bambu.html.gz";
AsyncWebServerResponse *response = request->beginResponse(LittleFS, page, "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@@ -286,6 +296,16 @@ void setupWebserver(AsyncWebServer &server) {
} }
String url = request->getParam("url")->value(); String url = request->getParam("url")->value();
if (url.indexOf("http://") == -1 && url.indexOf("https://") == -1)
{
url = "http://" + url;
}
// Remove trailing slash if exists
if (url.length() > 0 && url.charAt(url.length() - 1) == '/')
{
url = url.substring(0, url.length() - 1);
}
bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false; bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false;
String octoUrl = request->getParam("octoUrl")->value(); String octoUrl = request->getParam("octoUrl")->value();
String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : ""; String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : "";
@@ -300,8 +320,18 @@ void setupWebserver(AsyncWebServer &server) {
request->send(200, "application/json", jsonResponse); request->send(200, "application/json", jsonResponse);
}); });
// Route für das Überprüfen der Spoolman-Instanz // Route für das Überprüfen der Bambu-Instanz
server.on("/api/bambu", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/api/bambu", HTTP_GET, [](AsyncWebServerRequest *request)
{
if (request->hasParam("remove")) {
if (removeBambuCredentials()) {
request->send(200, "application/json", "{\"success\": true}");
} else {
request->send(500, "application/json", "{\"success\": false, \"error\": \"Fehler beim Löschen der Bambu-Credentials\"}");
}
return;
}
if (!request->hasParam("bambu_ip") || !request->hasParam("bambu_serialnr") || !request->hasParam("bambu_accesscode")) { if (!request->hasParam("bambu_ip") || !request->hasParam("bambu_serialnr") || !request->hasParam("bambu_accesscode")) {
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing parameter\"}"); request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing parameter\"}");
return; return;
@@ -325,8 +355,7 @@ void setupWebserver(AsyncWebServer &server) {
bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime); bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime);
request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); });
});
// Route für das Überprüfen der Spoolman-Instanz // Route für das Überprüfen der Spoolman-Instanz
server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request){
+1 -1
View File
@@ -59,7 +59,7 @@ void initWiFi() {
if(wm_nonblocking) wm.setConfigPortalBlocking(false); if(wm_nonblocking) wm.setConfigPortalBlocking(false);
//wm.setConfigPortalTimeout(320); // Portal nach 5min schließen //wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
wm.setWiFiAutoReconnect(true); wm.setWiFiAutoReconnect(true);
wm.setConnectTimeout(5); wm.setConnectTimeout(10);
oledShowTopRow(); oledShowTopRow();
oledShowMessage("WiFi Setup"); oledShowMessage("WiFi Setup");