Compare commits
	
		
			45 Commits
		
	
	
		
			v1.3.93
			...
			bec2c91331
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bec2c91331 | |||
| c6e727de06 | |||
| 3253e7d407 | |||
| bce2ad2ed8 | |||
| 
						 | 
					0eff29ef4a | ||
| 492bf6cdb8 | |||
| b0317f4001 | |||
| 58ff6458b0 | |||
| d9c40f5124 | |||
| 68bc31e29a | |||
| 9b23ac5fd2 | |||
| d31bff14c3 | |||
| 150f92484a | |||
| fa74832fb9 | |||
| 2eab3db77d | |||
| 0a1bf22f7e | |||
| d58244c1f8 | |||
| db626ea516 | |||
| fd8f7685a1 | |||
| 944b156528 | |||
| 76100593cc | |||
| 732d590344 | |||
| 46cd953b80 | |||
| c645035bbe | |||
| 9e76620cd3 | |||
| faddda6201 | |||
| de9c1706c0 | |||
| 9f7ee13e78 | |||
| cf3f6f6741 | |||
| b87d43c64e | |||
| 3d0411e3c1 | |||
| 9c61b708aa | |||
| 90f800d042 | |||
| a7b1721e1d | |||
| e4825d2905 | |||
| c1733848d3 | |||
| 484c95523d | |||
| 8499613215 | |||
| 08f37186b4 | |||
| 2948a35fa8 | |||
| 730724fe58 | |||
| 714b7065e7 | |||
| 2d8aec515d | |||
| b245a206ce | |||
| f1489e75cc | 
							
								
								
									
										10
									
								
								.github/workflows/gitea-release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/gitea-release.yml
									
									
									
									
										vendored
									
									
								
							@@ -41,16 +41,16 @@ jobs:
 | 
			
		||||
      run: |
 | 
			
		||||
        VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
 | 
			
		||||
        
 | 
			
		||||
        # Build firmware and SPIFFS
 | 
			
		||||
        echo "Building firmware and SPIFFS..."
 | 
			
		||||
        # Build firmware and LittleFS
 | 
			
		||||
        echo "Building firmware and LittleFS..."
 | 
			
		||||
        pio run -e esp32dev
 | 
			
		||||
        pio run -t buildfs
 | 
			
		||||
        
 | 
			
		||||
        # Copy firmware binary
 | 
			
		||||
        cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
 | 
			
		||||
        
 | 
			
		||||
        # Create SPIFFS binary - direct copy without header
 | 
			
		||||
        cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
 | 
			
		||||
        # Create LittleFS binary - direct copy without header
 | 
			
		||||
        cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
 | 
			
		||||
        
 | 
			
		||||
        # Create full binary
 | 
			
		||||
        (cd .pio/build/esp32dev && 
 | 
			
		||||
@@ -63,7 +63,7 @@ jobs:
 | 
			
		||||
          0x1000 bootloader.bin \
 | 
			
		||||
          0x8000 partitions.bin \
 | 
			
		||||
          0x10000 firmware.bin \
 | 
			
		||||
          0x3D0000 spiffs.bin)
 | 
			
		||||
          0x3D0000 littlefs.bin)
 | 
			
		||||
        
 | 
			
		||||
        # Verify file sizes
 | 
			
		||||
        echo "File sizes:"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/workflows/github-release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/github-release.yml
									
									
									
									
										vendored
									
									
								
							@@ -39,16 +39,16 @@ jobs:
 | 
			
		||||
      run: |
 | 
			
		||||
        VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
 | 
			
		||||
        
 | 
			
		||||
        # Always build firmware and SPIFFS
 | 
			
		||||
        echo "Building firmware and SPIFFS..."
 | 
			
		||||
        # Always build firmware and LittleFS
 | 
			
		||||
        echo "Building firmware and LittleFS..."
 | 
			
		||||
        pio run -e esp32dev
 | 
			
		||||
        pio run -t buildfs
 | 
			
		||||
        
 | 
			
		||||
        # Copy firmware binary
 | 
			
		||||
        cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
 | 
			
		||||
        
 | 
			
		||||
        # Create SPIFFS binary - direct copy without header
 | 
			
		||||
        cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
 | 
			
		||||
        # Create LittleFS binary - direct copy without header
 | 
			
		||||
        cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
 | 
			
		||||
        
 | 
			
		||||
        # Create full binary (always)
 | 
			
		||||
        (cd .pio/build/esp32dev && 
 | 
			
		||||
@@ -61,7 +61,7 @@ jobs:
 | 
			
		||||
          0x1000 bootloader.bin \
 | 
			
		||||
          0x8000 partitions.bin \
 | 
			
		||||
          0x10000 firmware.bin \
 | 
			
		||||
          0x3D0000 spiffs.bin)
 | 
			
		||||
          0x3D0000 littlefs.bin)
 | 
			
		||||
        
 | 
			
		||||
        # Verify file sizes
 | 
			
		||||
        echo "File sizes:"
 | 
			
		||||
@@ -131,7 +131,7 @@ jobs:
 | 
			
		||||
          FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_firmware_v${VERSION}.bin"
 | 
			
		||||
        fi
 | 
			
		||||
        
 | 
			
		||||
        # Add SPIFFS and full binary only if they exist
 | 
			
		||||
        # Add LittleFS and full binary only if they exist
 | 
			
		||||
        if [ -f "upgrade_filaman_website_v${VERSION}.bin" ]; then
 | 
			
		||||
          FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_website_v${VERSION}.bin"
 | 
			
		||||
        fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -1,54 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "files.associations": {
 | 
			
		||||
        "algorithm": "cpp",
 | 
			
		||||
        "vector": "cpp",
 | 
			
		||||
        "cmath": "cpp",
 | 
			
		||||
        "array": "cpp",
 | 
			
		||||
        "atomic": "cpp",
 | 
			
		||||
        "*.tcc": "cpp",
 | 
			
		||||
        "bitset": "cpp",
 | 
			
		||||
        "cctype": "cpp",
 | 
			
		||||
        "clocale": "cpp",
 | 
			
		||||
        "cstdarg": "cpp",
 | 
			
		||||
        "cstddef": "cpp",
 | 
			
		||||
        "cstdint": "cpp",
 | 
			
		||||
        "cstdio": "cpp",
 | 
			
		||||
        "cstdlib": "cpp",
 | 
			
		||||
        "cstring": "cpp",
 | 
			
		||||
        "ctime": "cpp",
 | 
			
		||||
        "cwchar": "cpp",
 | 
			
		||||
        "cwctype": "cpp",
 | 
			
		||||
        "deque": "cpp",
 | 
			
		||||
        "unordered_map": "cpp",
 | 
			
		||||
        "unordered_set": "cpp",
 | 
			
		||||
        "exception": "cpp",
 | 
			
		||||
        "functional": "cpp",
 | 
			
		||||
        "iterator": "cpp",
 | 
			
		||||
        "map": "cpp",
 | 
			
		||||
        "memory": "cpp",
 | 
			
		||||
        "memory_resource": "cpp",
 | 
			
		||||
        "numeric": "cpp",
 | 
			
		||||
        "optional": "cpp",
 | 
			
		||||
        "random": "cpp",
 | 
			
		||||
        "regex": "cpp",
 | 
			
		||||
        "string": "cpp",
 | 
			
		||||
        "string_view": "cpp",
 | 
			
		||||
        "system_error": "cpp",
 | 
			
		||||
        "tuple": "cpp",
 | 
			
		||||
        "type_traits": "cpp",
 | 
			
		||||
        "utility": "cpp",
 | 
			
		||||
        "fstream": "cpp",
 | 
			
		||||
        "initializer_list": "cpp",
 | 
			
		||||
        "iomanip": "cpp",
 | 
			
		||||
        "iosfwd": "cpp",
 | 
			
		||||
        "istream": "cpp",
 | 
			
		||||
        "limits": "cpp",
 | 
			
		||||
        "new": "cpp",
 | 
			
		||||
        "ostream": "cpp",
 | 
			
		||||
        "sstream": "cpp",
 | 
			
		||||
        "stdexcept": "cpp",
 | 
			
		||||
        "streambuf": "cpp",
 | 
			
		||||
        "cinttypes": "cpp",
 | 
			
		||||
        "typeinfo": "cpp"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,5 +1,75 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## [1.3.99] - 2025-02-28
 | 
			
		||||
### Changed
 | 
			
		||||
- update platformio.ini for version v1.3.99
 | 
			
		||||
- update workflows to build firmware with LittleFS instead of SPIFFS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.98] - 2025-02-28
 | 
			
		||||
### Changed
 | 
			
		||||
- update platformio.ini for version v1.3.98
 | 
			
		||||
- migrate from SPIFFS to LittleFS for file handling
 | 
			
		||||
- remove unused VSCode settings file
 | 
			
		||||
- remove commented-out spoolman and filaman data from api.cpp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.97] - 2025-02-28
 | 
			
		||||
### Added
 | 
			
		||||
- füge Bestätigungsmeldung für Spool-Einstellung hinzu
 | 
			
		||||
- verbessere WLAN-Konfiguration und füge mDNS-Unterstützung hinzu
 | 
			
		||||
- aktualisiere OLED-Anzeige mit Versionsnummer und verbessere Textausrichtung
 | 
			
		||||
- füge regelmäßige WLAN-Verbindungsüberprüfung hinzu
 | 
			
		||||
- aktualisiere Schaltplan-Bild
 | 
			
		||||
- zeige Versionsnummer im OLED-Display an
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
- update platformio.ini for version v1.3.97
 | 
			
		||||
- entferne text-shadow von deaktivierten Schaltflächen
 | 
			
		||||
- füge Link zum Wiki für detaillierte Informationen über die Nutzung hinzu
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- Speichernutzung optimiert
 | 
			
		||||
- behebe doppelte http.end() Aufrufe in checkSpoolmanExtraFields
 | 
			
		||||
- optimiere Verzögerungen und Stackgrößen in NFC-Task-Funktionen
 | 
			
		||||
- entferne ungenutzte Bibliotheken und Debug-Ausgaben aus main.cpp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.96] - 2025-02-25
 | 
			
		||||
### Added
 | 
			
		||||
- füge Unterstützung für Spoolman-Einstellungen hinzu und aktualisiere die Benutzeroberfläche
 | 
			
		||||
- entferne die sendAmsData-Funktion aus der API-Schnittstelle
 | 
			
		||||
- erweitere Bambu-Credentials um AutoSend-Zeit und aktualisiere die Benutzeroberfläche
 | 
			
		||||
- erweitere Bambu-Credentials mit AutoSend-Wartezeit und aktualisiere die Benutzeroberfläche
 | 
			
		||||
- add espRestart function and replace delay with vTaskDelay for OTA update process
 | 
			
		||||
- implement OTA update functionality with backup and restore for configurations
 | 
			
		||||
- add own_filaments.json and integrate custom filament loading in bambu.cpp
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
- update platformio.ini for version v1.3.96
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- aktualisiere Bedingungen für die AMS-Datenaktualisierung und entferne unnötige Aufrufe
 | 
			
		||||
- aktualisiere Bedingung für den Fortschritt der OTA-Update-Nachricht
 | 
			
		||||
- update auto set logic to check RFID tag before setting Bambu spool
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.95] - 2025-02-24
 | 
			
		||||
### Changed
 | 
			
		||||
- update webpages for version v1.3.95
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- bind autoSendToBambu variable to checkbox in spoolman.html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.94] - 2025-02-24
 | 
			
		||||
### Changed
 | 
			
		||||
- update webpages for version v1.3.94
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- correct payload type check in NFC write event handling
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [1.3.93] - 2025-02-24
 | 
			
		||||
### Added
 | 
			
		||||
- implement auto send feature for Bambu spool management and update related configurations
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,12 @@ Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Weitere Bilder finden Sie im [img Ordner](/img/)  
 | 
			
		||||
Weitere Bilder finden Sie im [img Ordner](/img/)
 | 
			
		||||
oder auf meiner Website: [FilaMan Website](https://www.filaman.app)  
 | 
			
		||||
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
 | 
			
		||||
 | 
			
		||||
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
 | 
			
		||||
 | 
			
		||||
### ESP32 Hardware-Funktionen
 | 
			
		||||
- **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.
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ More Images can be found in the [img Folder](/img/)
 | 
			
		||||
or my website:[FilaMan Website](https://www.filaman.app)  
 | 
			
		||||
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
 | 
			
		||||
 | 
			
		||||
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
 | 
			
		||||
 | 
			
		||||
### ESP32 Hardware Features
 | 
			
		||||
- **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking.
 | 
			
		||||
- **NFC Tag Reading/Writing:** PN532 module for reading and writing filament data to NFC tags.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								html/own_filaments.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								html/own_filaments.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
{
 | 
			
		||||
    "TPU": "GFU99",
 | 
			
		||||
    "PA": "GFN99",
 | 
			
		||||
    "PA-CF": "GFN98",
 | 
			
		||||
    "PLA": "GFL99",
 | 
			
		||||
    "PLA Silk": "GFL96",
 | 
			
		||||
    "PLA-CF": "GFL98",
 | 
			
		||||
    "PLA High Speed": "GFL95",
 | 
			
		||||
    "PETG": "GFG99",
 | 
			
		||||
    "PETG-CF": "GFG98",
 | 
			
		||||
    "PCTG": "GFG97",
 | 
			
		||||
    "ABS": "GFB99",
 | 
			
		||||
    "ABS+HS": "GFB99",
 | 
			
		||||
    "PC": "GFC99",
 | 
			
		||||
    "PC/ABS": "GFC99",
 | 
			
		||||
    "ASA": "GFB98",
 | 
			
		||||
    "PVA": "GFS99",
 | 
			
		||||
    "HIPS": "GFS98",
 | 
			
		||||
    "PPS-CF": "GFT98",
 | 
			
		||||
    "PPS": "GFT97",
 | 
			
		||||
    "PPA-CF": "GFN97",
 | 
			
		||||
    "PPA-GF": "GFN96",
 | 
			
		||||
    "PE": "GFP99",
 | 
			
		||||
    "PE-CF": "GFP98",
 | 
			
		||||
    "PP": "GFP97",
 | 
			
		||||
    "PP-CF": "GFP96",
 | 
			
		||||
    "PP-GF": "GFP95",
 | 
			
		||||
    "EVA": "GFR99",
 | 
			
		||||
    "PHA": "GFR98",
 | 
			
		||||
    "BVOH": "GFS97"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								html/rfid.js
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								html/rfid.js
									
									
									
									
									
								
							@@ -150,6 +150,13 @@ function initWebSocket() {
 | 
			
		||||
                    ramStatus.textContent = `${data.freeHeap}k`;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (data.type === 'setSpoolmanSettings') {
 | 
			
		||||
                if (data.payload == 'success') {
 | 
			
		||||
                    showNotification(`Spoolman Settings set successfully`, true);
 | 
			
		||||
                } else {
 | 
			
		||||
                    showNotification(`Error setting Spoolman Settings`, false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        isConnected = false;
 | 
			
		||||
@@ -285,6 +292,14 @@ function displayAmsData(amsData) {
 | 
			
		||||
                    <img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px; transform: rotate(180deg) scaleX(-1);">
 | 
			
		||||
                </button>`;
 | 
			
		||||
 | 
			
		||||
            const spoolmanButtonHtml = `
 | 
			
		||||
                <button class="spool-button" onclick="handleSpoolmanSettings('${tray.tray_info_idx}', '${tray.setting_id}', '${tray.cali_idx}', '${tray.nozzle_temp_min}', '${tray.nozzle_temp_max}')" 
 | 
			
		||||
                        style="position: absolute; bottom: 0px; right: 0px; 
 | 
			
		||||
                               background: none; border: none; padding: 0; 
 | 
			
		||||
                               cursor: pointer; display: none;">
 | 
			
		||||
                    <img src="set_spoolman.png" alt="Spool In" style="width: 38px; height: 38px;">
 | 
			
		||||
                </button>`;
 | 
			
		||||
 | 
			
		||||
            if (!hasAnyContent) {
 | 
			
		||||
                return `
 | 
			
		||||
                    <div class="tray">
 | 
			
		||||
@@ -348,6 +363,7 @@ function displayAmsData(amsData) {
 | 
			
		||||
                        ${trayDetails}
 | 
			
		||||
                        ${tempHTML}
 | 
			
		||||
                        ${(ams.ams_id === 255 && tray.tray_type !== '') ? outButtonHtml : ''}
 | 
			
		||||
                        ${(tray.setting_id != "" && tray.setting_id != "null") ? spoolmanButtonHtml : ''}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    
 | 
			
		||||
                </div>`;
 | 
			
		||||
@@ -373,6 +389,36 @@ function updateSpoolButtons(show) {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp_min, nozzle_temp_max) {
 | 
			
		||||
    // Hole das ausgewählte Filament
 | 
			
		||||
    const selectedText = document.getElementById("selected-filament").textContent;
 | 
			
		||||
 | 
			
		||||
    // Finde die ausgewählte Spule in den Daten
 | 
			
		||||
    const selectedSpool = spoolsData.find(spool => 
 | 
			
		||||
        `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const payload = {
 | 
			
		||||
        type: 'setSpoolmanSettings',
 | 
			
		||||
        payload: {
 | 
			
		||||
            filament_id: selectedSpool.filament.id,
 | 
			
		||||
            tray_info_idx: tray_info_idx,
 | 
			
		||||
            setting_id: setting_id,
 | 
			
		||||
            cali_idx: cali_idx,
 | 
			
		||||
            temp_min: nozzle_temp_min,
 | 
			
		||||
            temp_max: nozzle_temp_max
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        socket.send(JSON.stringify(payload));
 | 
			
		||||
        showNotification(`Setting send to Spoolman`, true);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error("Error while sending settings to Spoolman:", error);
 | 
			
		||||
        showNotification("Error while sending!", false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSpoolOut() {
 | 
			
		||||
    // Erstelle Payload
 | 
			
		||||
    const payload = {
 | 
			
		||||
@@ -594,8 +640,6 @@ function writeNfcTag() {
 | 
			
		||||
 | 
			
		||||
    // Erstelle das NFC-Datenpaket mit korrekten Datentypen
 | 
			
		||||
    const nfcData = {
 | 
			
		||||
        version: "2.0",
 | 
			
		||||
        protocol: "openspool",
 | 
			
		||||
        color_hex: selectedSpool.filament.color_hex || "FFFFFF",
 | 
			
		||||
        type: selectedSpool.filament.material,
 | 
			
		||||
        min_temp: minTemp,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								html/set_spoolman.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								html/set_spoolman.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 9.2 KiB  | 
@@ -75,8 +75,9 @@
 | 
			
		||||
            const serial = document.getElementById('bambuSerial').value;
 | 
			
		||||
            const code = document.getElementById('bambuCode').value;
 | 
			
		||||
            const autoSend = document.getElementById('autoSend').checked;
 | 
			
		||||
            const autoSendTime = document.getElementById('autoSendTime').value;
 | 
			
		||||
 | 
			
		||||
            fetch(`/api/bambu?bambu_ip=${encodeURIComponent(ip)}&bambu_serialnr=${encodeURIComponent(serial)}&bambu_accesscode=${encodeURIComponent(code)}&autoSend=${autoSend}`)
 | 
			
		||||
            fetch(`/api/bambu?bambu_ip=${encodeURIComponent(ip)}&bambu_serialnr=${encodeURIComponent(serial)}&bambu_accesscode=${encodeURIComponent(code)}&autoSend=${autoSend}&autoSendTime=${autoSendTime}`)
 | 
			
		||||
                .then(response => response.json())
 | 
			
		||||
                .then(data => {
 | 
			
		||||
                    if (data.healthy) {
 | 
			
		||||
@@ -122,13 +123,18 @@
 | 
			
		||||
                        <label for="bambuCode">Access Code:</label>
 | 
			
		||||
                        <input type="text" id="bambuCode" placeholder="Access Code vom Drucker" value="{{bambuCode}}">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="input-group">
 | 
			
		||||
                        If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.
 | 
			
		||||
                        <label for="autoSend">Auto Send to Bambu:</label>
 | 
			
		||||
                        <input type="checkbox" id="autoSend">
 | 
			
		||||
                    <hr>
 | 
			
		||||
                    <p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>
 | 
			
		||||
                    <div class="input-group" style="display: flex; margin-bottom: 0;">
 | 
			
		||||
                        <label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label>
 | 
			
		||||
                        <label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait time in Seconds:</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="input-group" style="display: flex;">
 | 
			
		||||
                        <input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
 | 
			
		||||
                        <input type="text" id="autoSendTime" placeholder="Time to wait for new Spool" value="{{autoSendTime}}" style="width: 100px;">
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <button onclick="saveBambuCredentials()">Save Bambu Credentials</button>
 | 
			
		||||
                    <button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>
 | 
			
		||||
                    <p id="bambuStatusMessage"></p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -97,13 +97,15 @@ function populateVendorDropdown(data, selectedSmId = null) {
 | 
			
		||||
        ? (weightInKg / 1000).toFixed(2) + " t" 
 | 
			
		||||
        : weightInKg.toFixed(2) + " kg";
 | 
			
		||||
 | 
			
		||||
    // Dropdown mit gefilterten Herstellern befüllen
 | 
			
		||||
    Object.entries(filteredVendors).forEach(([id, name]) => {
 | 
			
		||||
        const option = document.createElement("option");
 | 
			
		||||
        option.value = id;
 | 
			
		||||
        option.textContent = name;
 | 
			
		||||
        vendorSelect.appendChild(option);
 | 
			
		||||
    });
 | 
			
		||||
    // Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert
 | 
			
		||||
    Object.entries(filteredVendors)
 | 
			
		||||
        .sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name
 | 
			
		||||
        .forEach(([id, name]) => {
 | 
			
		||||
            const option = document.createElement("option");
 | 
			
		||||
            option.value = id;
 | 
			
		||||
            option.textContent = name;
 | 
			
		||||
            vendorSelect.appendChild(option);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    document.getElementById("totalSpools").textContent = totalSpools;
 | 
			
		||||
    document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag;
 | 
			
		||||
 
 | 
			
		||||
@@ -761,17 +761,19 @@ a:hover {
 | 
			
		||||
    right: 20px;
 | 
			
		||||
    padding: 15px 25px;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    color: white;
 | 
			
		||||
    color: black;
 | 
			
		||||
    z-index: 1000;
 | 
			
		||||
    animation: slideIn 0.3s ease-out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification.success {
 | 
			
		||||
    background-color: #28a745;
 | 
			
		||||
    color: black !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification.error {
 | 
			
		||||
    background-color: #dc3545;
 | 
			
		||||
    color: white !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification.fade-out {
 | 
			
		||||
@@ -1013,6 +1015,7 @@ input[type="submit"]:disabled,
 | 
			
		||||
    color: #000;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    margin-left: 0.5rem;
 | 
			
		||||
    text-shadow: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.progress-container {
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 283 KiB  | 
@@ -9,7 +9,7 @@
 | 
			
		||||
; https://docs.platformio.org/page/projectconf.html
 | 
			
		||||
 | 
			
		||||
[common]
 | 
			
		||||
version = "1.3.93"
 | 
			
		||||
version = "1.3.99"
 | 
			
		||||
##
 | 
			
		||||
[env:esp32dev]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
@@ -33,7 +33,8 @@ lib_deps =
 | 
			
		||||
    digitaldragon/SSLClient @ ^1.3.2
 | 
			
		||||
    
 | 
			
		||||
; Enable SPIFFS upload
 | 
			
		||||
board_build.filesystem = spiffs
 | 
			
		||||
#board_build.filesystem = spiffs
 | 
			
		||||
board_build.filesystem = littlefs
 | 
			
		||||
; Update partition settings
 | 
			
		||||
board_build.partitions = partitions.csv
 | 
			
		||||
board_upload.flash_size = 4MB
 | 
			
		||||
@@ -44,13 +45,13 @@ build_flags =
 | 
			
		||||
    -Os
 | 
			
		||||
    -ffunction-sections
 | 
			
		||||
    -fdata-sections
 | 
			
		||||
    -DNDEBUG
 | 
			
		||||
    #-DNDEBUG
 | 
			
		||||
    -mtext-section-literals
 | 
			
		||||
    -DVERSION=\"${common.version}\"
 | 
			
		||||
    -DASYNCWEBSERVER_REGEX
 | 
			
		||||
    -DCORE_DEBUG_LEVEL=3
 | 
			
		||||
    #-DCORE_DEBUG_LEVEL=3
 | 
			
		||||
    -DCONFIG_ARDUHAL_LOG_COLORS=1
 | 
			
		||||
    -DOTA_DEBUG=1
 | 
			
		||||
    #-DOTA_DEBUG=1
 | 
			
		||||
    -DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1
 | 
			
		||||
    -DBOOT_APP_PARTITION_OTA_0=1
 | 
			
		||||
    -DCONFIG_LWIP_TCP_MSL=60000
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								src/api.cpp
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								src/api.cpp
									
									
									
									
									
								
							@@ -12,31 +12,6 @@ struct SendToApiParams {
 | 
			
		||||
    String updatePayload;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    // Spoolman Data
 | 
			
		||||
    {
 | 
			
		||||
        "version":"1.0",
 | 
			
		||||
        "protocol":"openspool",
 | 
			
		||||
        "color_hex":"AF7933",
 | 
			
		||||
        "type":"ABS",
 | 
			
		||||
        "min_temp":175,
 | 
			
		||||
        "max_temp":275,
 | 
			
		||||
        "brand":"Overture"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // FilaMan Data
 | 
			
		||||
    {
 | 
			
		||||
        "version":"1.0",
 | 
			
		||||
        "protocol":"openspool",
 | 
			
		||||
        "color_hex":"AF7933",
 | 
			
		||||
        "type":"ABS",
 | 
			
		||||
        "min_temp":175,
 | 
			
		||||
        "max_temp":275,
 | 
			
		||||
        "brand":"Overture",
 | 
			
		||||
        "sm_id": 
 | 
			
		||||
    }
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
JsonDocument fetchSingleSpoolInfo(int spoolId) {
 | 
			
		||||
    HTTPClient http;
 | 
			
		||||
    String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
 | 
			
		||||
@@ -122,9 +97,9 @@ void sendToApi(void *parameter) {
 | 
			
		||||
    if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
 | 
			
		||||
 | 
			
		||||
    if (httpCode == HTTP_CODE_OK) {
 | 
			
		||||
        Serial.println("Gewicht der Spule erfolgreich aktualisiert");
 | 
			
		||||
        Serial.println("Spoolman erfolgreich aktualisiert");
 | 
			
		||||
    } else {
 | 
			
		||||
        Serial.println("Fehler beim Aktualisieren des Gewichts der Spule");
 | 
			
		||||
        Serial.println("Fehler beim Senden an Spoolman!");
 | 
			
		||||
        oledShowMessage("Spoolman update failed");
 | 
			
		||||
        vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
    }
 | 
			
		||||
@@ -223,6 +198,52 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool updateSpoolBambuData(String payload) {
 | 
			
		||||
    JsonDocument doc;
 | 
			
		||||
    DeserializationError error = deserializeJson(doc, payload);
 | 
			
		||||
    if (error) {
 | 
			
		||||
        Serial.print("Fehler beim JSON-Parsing: ");
 | 
			
		||||
        Serial.println(error.c_str());
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String spoolsUrl = spoolmanUrl + apiUrl + "/filament/" + doc["filament_id"].as<String>();
 | 
			
		||||
    Serial.print("Update Spule mit URL: ");
 | 
			
		||||
    Serial.println(spoolsUrl);
 | 
			
		||||
 | 
			
		||||
    JsonDocument updateDoc;
 | 
			
		||||
    updateDoc["extra"]["bambu_setting_id"] = "\"" + doc["setting_id"].as<String>() + "\"";
 | 
			
		||||
    updateDoc["extra"]["bambu_cali_id"] = "\"" + doc["cali_idx"].as<String>() + "\"";
 | 
			
		||||
    updateDoc["extra"]["bambu_idx"] = "\"" + doc["tray_info_idx"].as<String>() + "\"";
 | 
			
		||||
    updateDoc["extra"]["nozzle_temperature"] = "[" + doc["temp_min"].as<String>() + "," + doc["temp_max"].as<String>() + "]";
 | 
			
		||||
 | 
			
		||||
    String updatePayload;
 | 
			
		||||
    serializeJson(updateDoc, updatePayload);
 | 
			
		||||
    Serial.print("Update Payload: ");
 | 
			
		||||
    Serial.println(updatePayload);
 | 
			
		||||
 | 
			
		||||
    SendToApiParams* params = new SendToApiParams();
 | 
			
		||||
    if (params == nullptr) {
 | 
			
		||||
        Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    params->httpType = "PATCH";
 | 
			
		||||
    params->spoolsUrl = spoolsUrl;
 | 
			
		||||
    params->updatePayload = updatePayload;
 | 
			
		||||
 | 
			
		||||
    // Erstelle die Task
 | 
			
		||||
    BaseType_t result = xTaskCreate(
 | 
			
		||||
        sendToApi,                // Task-Funktion
 | 
			
		||||
        "SendToApiTask",          // Task-Name
 | 
			
		||||
        4096,                     // Stackgröße in Bytes
 | 
			
		||||
        (void*)params,            // Parameter
 | 
			
		||||
        0,                        // Priorität
 | 
			
		||||
        NULL                      // Task-Handle (nicht benötigt)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #### Spoolman init
 | 
			
		||||
bool checkSpoolmanExtraFields() {
 | 
			
		||||
    HTTPClient http;
 | 
			
		||||
@@ -364,12 +385,13 @@ bool checkSpoolmanExtraFields() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        http.end();  
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Serial.println("-------- ENDE Prüfe Felder --------");
 | 
			
		||||
    Serial.println();
 | 
			
		||||
 | 
			
		||||
    http.end();
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,9 @@ bool saveSpoolmanUrl(const String& url);
 | 
			
		||||
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
 | 
			
		||||
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder
 | 
			
		||||
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
 | 
			
		||||
void sendAmsData(AsyncWebSocketClient *client); // Neue Funktion zum Senden von AMS-Daten
 | 
			
		||||
bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion zum Aktualisieren eines Spools
 | 
			
		||||
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
 | 
			
		||||
bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
 | 
			
		||||
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										307
									
								
								src/bambu.cpp
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								src/bambu.cpp
									
									
									
									
									
								
							@@ -23,6 +23,11 @@ const char* bambu_username = "bblp";
 | 
			
		||||
const char* bambu_ip = nullptr;
 | 
			
		||||
const char* bambu_accesscode = nullptr;
 | 
			
		||||
const char* bambu_serialnr = nullptr;
 | 
			
		||||
 | 
			
		||||
String g_bambu_ip = "";
 | 
			
		||||
String g_bambu_accesscode = "";
 | 
			
		||||
String g_bambu_serialnr = "";
 | 
			
		||||
 | 
			
		||||
bool bambu_connected = false;
 | 
			
		||||
bool autoSendToBambu = false;
 | 
			
		||||
int autoSetToBambuSpoolId = 0;
 | 
			
		||||
@@ -32,7 +37,7 @@ int ams_count = 0;
 | 
			
		||||
String amsJsonData;  // Speichert das fertige JSON für WebSocket-Clients
 | 
			
		||||
AMSData ams_data[MAX_AMS];  // Definition des Arrays;
 | 
			
		||||
 | 
			
		||||
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend) {
 | 
			
		||||
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) {
 | 
			
		||||
    if (BambuMqttTask) {
 | 
			
		||||
        vTaskDelete(BambuMqttTask);
 | 
			
		||||
    }
 | 
			
		||||
@@ -42,6 +47,7 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
 | 
			
		||||
    doc["bambu_accesscode"] = accesscode;
 | 
			
		||||
    doc["bambu_serialnr"] = serialnr;
 | 
			
		||||
    doc["autoSendToBambu"] = autoSend;
 | 
			
		||||
    doc["autoSendTime"] = (autoSendTime != "") ? autoSendTime.toInt() : autoSetBambuAmsCounter;
 | 
			
		||||
 | 
			
		||||
    if (!saveJsonValue("/bambu_credentials.json", doc)) {
 | 
			
		||||
        Serial.println("Fehler beim Speichern der Bambu-Credentials.");
 | 
			
		||||
@@ -53,6 +59,7 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
 | 
			
		||||
    bambu_accesscode = accesscode.c_str();
 | 
			
		||||
    bambu_serialnr = serialnr.c_str();
 | 
			
		||||
    autoSendToBambu = autoSend;
 | 
			
		||||
    autoSetBambuAmsCounter = autoSendTime.toInt();
 | 
			
		||||
 | 
			
		||||
    vTaskDelay(100 / portTICK_PERIOD_MS);
 | 
			
		||||
    if (!setupMqtt()) return false;
 | 
			
		||||
@@ -67,16 +74,22 @@ bool loadBambuCredentials() {
 | 
			
		||||
        String ip = doc["bambu_ip"].as<String>();
 | 
			
		||||
        String code = doc["bambu_accesscode"].as<String>();
 | 
			
		||||
        String serial = doc["bambu_serialnr"].as<String>();
 | 
			
		||||
        autoSendToBambu = doc["autoSendToBambu"].as<bool>();
 | 
			
		||||
 | 
			
		||||
        g_bambu_ip = ip;
 | 
			
		||||
        g_bambu_accesscode = code;
 | 
			
		||||
        g_bambu_serialnr = serial;
 | 
			
		||||
 | 
			
		||||
        if (doc["autoSendToBambu"].is<bool>()) autoSendToBambu = doc["autoSendToBambu"].as<bool>();
 | 
			
		||||
        if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>();
 | 
			
		||||
 | 
			
		||||
        ip.trim();
 | 
			
		||||
        code.trim();
 | 
			
		||||
        serial.trim();
 | 
			
		||||
 | 
			
		||||
        // Dynamische Speicherallokation für die globalen Pointer
 | 
			
		||||
        bambu_ip = strdup(ip.c_str());
 | 
			
		||||
        bambu_accesscode = strdup(code.c_str());
 | 
			
		||||
        bambu_serialnr = strdup(serial.c_str());
 | 
			
		||||
        bambu_ip = g_bambu_ip.c_str();
 | 
			
		||||
        bambu_accesscode = g_bambu_accesscode.c_str();
 | 
			
		||||
        bambu_serialnr = g_bambu_serialnr.c_str();
 | 
			
		||||
 | 
			
		||||
        report_topic = "device/" + String(bambu_serialnr) + "/report";
 | 
			
		||||
        //request_topic = "device/" + String(bambu_serialnr) + "/request";
 | 
			
		||||
@@ -95,12 +108,38 @@ FilamentResult findFilamentIdx(String brand, String type) {
 | 
			
		||||
    // JSON-Dokument für die Filament-Daten erstellen
 | 
			
		||||
    JsonDocument doc;
 | 
			
		||||
    
 | 
			
		||||
    // Laden der own_filaments.json
 | 
			
		||||
    String ownFilament = "";
 | 
			
		||||
    if (!loadJsonValue("/own_filaments.json", doc)) 
 | 
			
		||||
    {
 | 
			
		||||
        Serial.println("Fehler beim Laden der eigenen Filament-Daten");
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // Durchsuche direkt nach dem Type als Schlüssel
 | 
			
		||||
        if (doc[type].is<String>()) {
 | 
			
		||||
            ownFilament = doc[type].as<String>();
 | 
			
		||||
        }
 | 
			
		||||
        doc.clear();
 | 
			
		||||
    }
 | 
			
		||||
    doc.clear();
 | 
			
		||||
 | 
			
		||||
    // Laden der bambu_filaments.json
 | 
			
		||||
    if (!loadJsonValue("/bambu_filaments.json", doc)) {
 | 
			
		||||
    if (!loadJsonValue("/bambu_filaments.json", doc)) 
 | 
			
		||||
    {
 | 
			
		||||
        Serial.println("Fehler beim Laden der Filament-Daten");
 | 
			
		||||
        return {"GFL99", "PLA"}; // Fallback auf Generic PLA
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wenn eigener Typ
 | 
			
		||||
    if (ownFilament != "")
 | 
			
		||||
    {
 | 
			
		||||
        if (doc[ownFilament].is<String>()) 
 | 
			
		||||
        {
 | 
			
		||||
            return {ownFilament, doc[ownFilament].as<String>()};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 1. Erst versuchen wir die exakte Brand + Type Kombination zu finden
 | 
			
		||||
    String searchKey;
 | 
			
		||||
    if (brand == "Bambu" || brand == "Bambulab") {
 | 
			
		||||
@@ -157,7 +196,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
 | 
			
		||||
    return {"GFL99", "PLA"};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool sendMqttMessage(String payload) {
 | 
			
		||||
bool sendMqttMessage(const String& payload) {
 | 
			
		||||
    Serial.println("Sending MQTT message");
 | 
			
		||||
    Serial.println(payload);
 | 
			
		||||
    if (client.publish(report_topic.c_str(), payload.c_str())) 
 | 
			
		||||
@@ -275,16 +314,102 @@ void autoSetSpool(int spoolId, uint8_t trayId) {
 | 
			
		||||
        Serial.println(spoolInfo.as<String>());
 | 
			
		||||
 | 
			
		||||
        setBambuSpool(spoolInfo.as<String>());
 | 
			
		||||
 | 
			
		||||
        oledShowMessage("Spool set");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // id wieder zurücksetzen damit abgeschlossen
 | 
			
		||||
    autoSetToBambuSpoolId = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void updateAmsWsData(JsonDocument& doc, JsonArray& amsArray, int& ams_count, JsonObject& vtTray) {
 | 
			
		||||
    // Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden
 | 
			
		||||
    ams_count = amsArray.size();
 | 
			
		||||
        
 | 
			
		||||
    for (int i = 0; i < ams_count && i < 16; i++) {
 | 
			
		||||
        JsonObject amsObj = amsArray[i];
 | 
			
		||||
        JsonArray trayArray = amsObj["tray"].as<JsonArray>();
 | 
			
		||||
 | 
			
		||||
        ams_data[i].ams_id = i; // Setze die AMS-ID
 | 
			
		||||
        for (int j = 0; j < trayArray.size() && j < 4; j++) { // Annahme: Maximal 4 Trays pro AMS
 | 
			
		||||
            JsonObject trayObj = trayArray[j];
 | 
			
		||||
 | 
			
		||||
            ams_data[i].trays[j].id = trayObj["id"].as<uint8_t>();
 | 
			
		||||
            ams_data[i].trays[j].tray_info_idx = trayObj["tray_info_idx"].as<String>();
 | 
			
		||||
            ams_data[i].trays[j].tray_type = trayObj["tray_type"].as<String>();
 | 
			
		||||
            ams_data[i].trays[j].tray_sub_brands = trayObj["tray_sub_brands"].as<String>();
 | 
			
		||||
            ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
 | 
			
		||||
            ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
 | 
			
		||||
            ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
 | 
			
		||||
            //ams_data[i].trays[j].setting_id = trayObj["setting_id"].as<String>();
 | 
			
		||||
            ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Setze ams_count auf die Anzahl der normalen AMS
 | 
			
		||||
    ams_count = amsArray.size();
 | 
			
		||||
 | 
			
		||||
    // Wenn externe Spule vorhanden, füge sie hinzu
 | 
			
		||||
    if (doc["print"]["vt_tray"].is<JsonObject>()) {
 | 
			
		||||
        //JsonObject vtTray = doc["print"]["vt_tray"];
 | 
			
		||||
        int extIdx = ams_count;  // Index für externe Spule
 | 
			
		||||
        ams_data[extIdx].ams_id = 255;  // Spezielle ID für externe Spule
 | 
			
		||||
        ams_data[extIdx].trays[0].id = 254;  // Spezielle ID für externes Tray
 | 
			
		||||
        ams_data[extIdx].trays[0].tray_info_idx = vtTray["tray_info_idx"].as<String>();
 | 
			
		||||
        ams_data[extIdx].trays[0].tray_type = vtTray["tray_type"].as<String>();
 | 
			
		||||
        ams_data[extIdx].trays[0].tray_sub_brands = vtTray["tray_sub_brands"].as<String>();
 | 
			
		||||
        ams_data[extIdx].trays[0].tray_color = vtTray["tray_color"].as<String>();
 | 
			
		||||
        ams_data[extIdx].trays[0].nozzle_temp_min = vtTray["nozzle_temp_min"].as<int>();
 | 
			
		||||
        ams_data[extIdx].trays[0].nozzle_temp_max = vtTray["nozzle_temp_max"].as<int>();
 | 
			
		||||
 | 
			
		||||
        if (doc["print"]["vt_tray"]["tray_type"].as<String>() != "")
 | 
			
		||||
        {
 | 
			
		||||
            //ams_data[extIdx].trays[0].setting_id = vtTray["setting_id"].as<String>();
 | 
			
		||||
            ams_data[extIdx].trays[0].cali_idx = vtTray["cali_idx"].as<String>();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            ams_data[extIdx].trays[0].setting_id = "";
 | 
			
		||||
            ams_data[extIdx].trays[0].cali_idx = "";
 | 
			
		||||
        }
 | 
			
		||||
        ams_count++;  // Erhöhe ams_count für die externe Spule
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Erstelle JSON für WebSocket-Clients
 | 
			
		||||
    JsonDocument wsDoc;
 | 
			
		||||
    JsonArray wsArray = wsDoc.to<JsonArray>();
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < ams_count; i++) {
 | 
			
		||||
        JsonObject amsObj = wsArray.add<JsonObject>();
 | 
			
		||||
        amsObj["ams_id"] = ams_data[i].ams_id;
 | 
			
		||||
 | 
			
		||||
        JsonArray trays = amsObj["tray"].to<JsonArray>();
 | 
			
		||||
        int maxTrays = (ams_data[i].ams_id == 255) ? 1 : 4;
 | 
			
		||||
        
 | 
			
		||||
        for (int j = 0; j < maxTrays; j++) {
 | 
			
		||||
            JsonObject trayObj = trays.add<JsonObject>();
 | 
			
		||||
            trayObj["id"] = ams_data[i].trays[j].id;
 | 
			
		||||
            trayObj["tray_info_idx"] = ams_data[i].trays[j].tray_info_idx;
 | 
			
		||||
            trayObj["tray_type"] = ams_data[i].trays[j].tray_type;
 | 
			
		||||
            trayObj["tray_sub_brands"] = ams_data[i].trays[j].tray_sub_brands;
 | 
			
		||||
            trayObj["tray_color"] = ams_data[i].trays[j].tray_color;
 | 
			
		||||
            trayObj["nozzle_temp_min"] = ams_data[i].trays[j].nozzle_temp_min;
 | 
			
		||||
            trayObj["nozzle_temp_max"] = ams_data[i].trays[j].nozzle_temp_max;
 | 
			
		||||
            trayObj["setting_id"] = ams_data[i].trays[j].setting_id;
 | 
			
		||||
            trayObj["cali_idx"] = ams_data[i].trays[j].cali_idx;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    serializeJson(wsArray, amsJsonData);
 | 
			
		||||
    wsDoc.clear();
 | 
			
		||||
    Serial.println("AMS data updated");
 | 
			
		||||
    sendAmsData(nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// init
 | 
			
		||||
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
 | 
			
		||||
    String message;
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < length; i++) {
 | 
			
		||||
        message += (char)payload[i];
 | 
			
		||||
    }
 | 
			
		||||
@@ -292,6 +417,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
 | 
			
		||||
    // JSON-Dokument parsen
 | 
			
		||||
    JsonDocument doc;
 | 
			
		||||
    DeserializationError error = deserializeJson(doc, message);
 | 
			
		||||
    message = "";
 | 
			
		||||
    if (error) 
 | 
			
		||||
    {
 | 
			
		||||
        Serial.print("Fehler beim Parsen des JSON: ");
 | 
			
		||||
@@ -344,9 +470,13 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
 | 
			
		||||
            // Vergleiche die Trays
 | 
			
		||||
            for (int j = 0; j < trayArray.size() && j < 4 && !hasChanges; j++) {
 | 
			
		||||
                JsonObject trayObj = trayArray[j];
 | 
			
		||||
 | 
			
		||||
                if (trayObj["tray_type"].as<String>() == "") ams_data[storedIndex].trays[j].setting_id = "";
 | 
			
		||||
                if (trayObj["setting_id"].isNull()) trayObj["setting_id"] = "";
 | 
			
		||||
                if (trayObj["tray_info_idx"].as<String>() != ams_data[storedIndex].trays[j].tray_info_idx ||
 | 
			
		||||
                    trayObj["tray_type"].as<String>() != ams_data[storedIndex].trays[j].tray_type ||
 | 
			
		||||
                    trayObj["tray_color"].as<String>() != ams_data[storedIndex].trays[j].tray_color ||
 | 
			
		||||
                    (trayObj["setting_id"].as<String>() != "" && trayObj["setting_id"].as<String>() != ams_data[storedIndex].trays[j].setting_id) ||
 | 
			
		||||
                    trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
 | 
			
		||||
                    hasChanges = true;
 | 
			
		||||
                    break;
 | 
			
		||||
@@ -355,147 +485,55 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Prüfe die externe Spule
 | 
			
		||||
        if (!hasChanges && doc["print"]["vt_tray"].is<JsonObject>()) {
 | 
			
		||||
            JsonObject vtTray = doc["print"]["vt_tray"];
 | 
			
		||||
            bool foundExternal = false;
 | 
			
		||||
            
 | 
			
		||||
        JsonObject vtTray = doc["print"]["vt_tray"];
 | 
			
		||||
        if (doc["print"]["vt_tray"].is<JsonObject>()) {
 | 
			
		||||
            for (int i = 0; i < ams_count; i++) {
 | 
			
		||||
                if (ams_data[i].ams_id == 255) {
 | 
			
		||||
                    foundExternal = true;
 | 
			
		||||
                    if (vtTray["tray_type"].as<String>() == "") ams_data[i].trays[0].setting_id = "";
 | 
			
		||||
                    if (vtTray["setting_id"].isNull()) vtTray["setting_id"] = "";
 | 
			
		||||
                    if (vtTray["tray_info_idx"].as<String>() != ams_data[i].trays[0].tray_info_idx ||
 | 
			
		||||
                        vtTray["tray_type"].as<String>() != ams_data[i].trays[0].tray_type ||
 | 
			
		||||
                        vtTray["tray_color"].as<String>() != ams_data[i].trays[0].tray_color ||
 | 
			
		||||
                        vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx) {
 | 
			
		||||
                        (vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) ||
 | 
			
		||||
                        (vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
 | 
			
		||||
                        hasChanges = true;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!foundExternal) hasChanges = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!hasChanges) return;
 | 
			
		||||
 | 
			
		||||
        // Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden
 | 
			
		||||
        ams_count = amsArray.size();
 | 
			
		||||
        
 | 
			
		||||
        for (int i = 0; i < ams_count && i < 16; i++) {
 | 
			
		||||
            JsonObject amsObj = amsArray[i];
 | 
			
		||||
            JsonArray trayArray = amsObj["tray"].as<JsonArray>();
 | 
			
		||||
 | 
			
		||||
            ams_data[i].ams_id = i; // Setze die AMS-ID
 | 
			
		||||
            for (int j = 0; j < trayArray.size() && j < 4; j++) { // Annahme: Maximal 4 Trays pro AMS
 | 
			
		||||
                JsonObject trayObj = trayArray[j];
 | 
			
		||||
 | 
			
		||||
                ams_data[i].trays[j].id = trayObj["id"].as<uint8_t>();
 | 
			
		||||
                ams_data[i].trays[j].tray_info_idx = trayObj["tray_info_idx"].as<String>();
 | 
			
		||||
                ams_data[i].trays[j].tray_type = trayObj["tray_type"].as<String>();
 | 
			
		||||
                ams_data[i].trays[j].tray_sub_brands = trayObj["tray_sub_brands"].as<String>();
 | 
			
		||||
                ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
 | 
			
		||||
                ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
 | 
			
		||||
                ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
 | 
			
		||||
                ams_data[i].trays[j].setting_id = trayObj["setting_id"].as<String>();
 | 
			
		||||
                ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Setze ams_count auf die Anzahl der normalen AMS
 | 
			
		||||
        ams_count = amsArray.size();
 | 
			
		||||
 | 
			
		||||
        // Wenn externe Spule vorhanden, füge sie hinzu
 | 
			
		||||
        if (doc["print"]["vt_tray"].is<JsonObject>()) {
 | 
			
		||||
            JsonObject vtTray = doc["print"]["vt_tray"];
 | 
			
		||||
            int extIdx = ams_count;  // Index für externe Spule
 | 
			
		||||
            ams_data[extIdx].ams_id = 255;  // Spezielle ID für externe Spule
 | 
			
		||||
            ams_data[extIdx].trays[0].id = 254;  // Spezielle ID für externes Tray
 | 
			
		||||
            ams_data[extIdx].trays[0].tray_info_idx = vtTray["tray_info_idx"].as<String>();
 | 
			
		||||
            ams_data[extIdx].trays[0].tray_type = vtTray["tray_type"].as<String>();
 | 
			
		||||
            ams_data[extIdx].trays[0].tray_sub_brands = vtTray["tray_sub_brands"].as<String>();
 | 
			
		||||
            ams_data[extIdx].trays[0].tray_color = vtTray["tray_color"].as<String>();
 | 
			
		||||
            ams_data[extIdx].trays[0].nozzle_temp_min = vtTray["nozzle_temp_min"].as<int>();
 | 
			
		||||
            ams_data[extIdx].trays[0].nozzle_temp_max = vtTray["nozzle_temp_max"].as<int>();
 | 
			
		||||
 | 
			
		||||
            if (doc["print"]["vt_tray"]["tray_type"].as<String>() != "")
 | 
			
		||||
            {
 | 
			
		||||
                ams_data[extIdx].trays[0].setting_id = vtTray["setting_id"].as<String>();
 | 
			
		||||
                ams_data[extIdx].trays[0].cali_idx = vtTray["cali_idx"].as<String>();
 | 
			
		||||
            }
 | 
			
		||||
            ams_count++;  // Erhöhe ams_count für die externe Spule
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sende die aktualisierten AMS-Daten
 | 
			
		||||
        //sendAmsData(nullptr);
 | 
			
		||||
 | 
			
		||||
        // Erstelle JSON für WebSocket-Clients
 | 
			
		||||
        JsonDocument wsDoc;
 | 
			
		||||
        JsonArray wsArray = wsDoc.to<JsonArray>();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < ams_count; i++) {
 | 
			
		||||
            JsonObject amsObj = wsArray.add<JsonObject>();
 | 
			
		||||
            amsObj["ams_id"] = ams_data[i].ams_id;
 | 
			
		||||
 | 
			
		||||
            JsonArray trays = amsObj["tray"].to<JsonArray>();
 | 
			
		||||
            int maxTrays = (ams_data[i].ams_id == 255) ? 1 : 4;
 | 
			
		||||
            
 | 
			
		||||
            for (int j = 0; j < maxTrays; j++) {
 | 
			
		||||
                JsonObject trayObj = trays.add<JsonObject>();
 | 
			
		||||
                trayObj["id"] = ams_data[i].trays[j].id;
 | 
			
		||||
                trayObj["tray_info_idx"] = ams_data[i].trays[j].tray_info_idx;
 | 
			
		||||
                trayObj["tray_type"] = ams_data[i].trays[j].tray_type;
 | 
			
		||||
                trayObj["tray_sub_brands"] = ams_data[i].trays[j].tray_sub_brands;
 | 
			
		||||
                trayObj["tray_color"] = ams_data[i].trays[j].tray_color;
 | 
			
		||||
                trayObj["nozzle_temp_min"] = ams_data[i].trays[j].nozzle_temp_min;
 | 
			
		||||
                trayObj["nozzle_temp_max"] = ams_data[i].trays[j].nozzle_temp_max;
 | 
			
		||||
                trayObj["setting_id"] = ams_data[i].trays[j].setting_id;
 | 
			
		||||
                trayObj["cali_idx"] = ams_data[i].trays[j].cali_idx;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        serializeJson(wsArray, amsJsonData);
 | 
			
		||||
        sendAmsData(nullptr);
 | 
			
		||||
        updateAmsWsData(doc, amsArray, ams_count, vtTray);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Neue Bedingung für ams_filament_setting
 | 
			
		||||
    else if (doc["print"]["command"] == "ams_filament_setting") {
 | 
			
		||||
    if (doc["print"]["command"] == "ams_filament_setting") {
 | 
			
		||||
        int amsId = doc["print"]["ams_id"].as<int>();
 | 
			
		||||
        int trayId = doc["print"]["tray_id"].as<int>();
 | 
			
		||||
        String settingId = doc["print"]["setting_id"].as<String>();
 | 
			
		||||
        String settingId = (doc["print"]["setting_id"].is<String>()) ? doc["print"]["setting_id"].as<String>() : "";
 | 
			
		||||
 | 
			
		||||
        // Finde das entsprechende AMS und Tray
 | 
			
		||||
        for (int i = 0; i < ams_count; i++) {
 | 
			
		||||
            if (ams_data[i].ams_id == amsId) {
 | 
			
		||||
                // Update setting_id im entsprechenden Tray
 | 
			
		||||
                ams_data[i].trays[trayId].setting_id = settingId;
 | 
			
		||||
                
 | 
			
		||||
                // Erstelle neues JSON für WebSocket-Clients
 | 
			
		||||
                JsonDocument wsDoc;
 | 
			
		||||
                JsonArray wsArray = wsDoc.to<JsonArray>();
 | 
			
		||||
 | 
			
		||||
                for (int j = 0; j < ams_count; j++) {
 | 
			
		||||
                    JsonObject amsObj = wsArray.add<JsonObject>();
 | 
			
		||||
                    amsObj["ams_id"] = ams_data[j].ams_id;
 | 
			
		||||
 | 
			
		||||
                    JsonArray trays = amsObj["tray"].to<JsonArray>();
 | 
			
		||||
                    int maxTrays = (ams_data[j].ams_id == 255) ? 1 : 4;
 | 
			
		||||
                    
 | 
			
		||||
                    for (int k = 0; k < maxTrays; k++) {
 | 
			
		||||
                        JsonObject trayObj = trays.add<JsonObject>();
 | 
			
		||||
                        trayObj["id"] = ams_data[j].trays[k].id;
 | 
			
		||||
                        trayObj["tray_info_idx"] = ams_data[j].trays[k].tray_info_idx;
 | 
			
		||||
                        trayObj["tray_type"] = ams_data[j].trays[k].tray_type;
 | 
			
		||||
                        trayObj["tray_sub_brands"] = ams_data[j].trays[k].tray_sub_brands;
 | 
			
		||||
                        trayObj["tray_color"] = ams_data[j].trays[k].tray_color;
 | 
			
		||||
                        trayObj["nozzle_temp_min"] = ams_data[j].trays[k].nozzle_temp_min;
 | 
			
		||||
                        trayObj["nozzle_temp_max"] = ams_data[j].trays[k].nozzle_temp_max;
 | 
			
		||||
                        trayObj["setting_id"] = ams_data[j].trays[k].setting_id;
 | 
			
		||||
                        trayObj["cali_idx"] = ams_data[j].trays[k].cali_idx;
 | 
			
		||||
                if (trayId == 254)
 | 
			
		||||
                {
 | 
			
		||||
                    // Suche AMS mit ID 255 (externe Spule)
 | 
			
		||||
                    for (int j = 0; j < ams_count; j++) {
 | 
			
		||||
                        if (ams_data[j].ams_id == 255) {
 | 
			
		||||
                            ams_data[j].trays[0].setting_id = settingId;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Aktualisiere das globale amsJsonData
 | 
			
		||||
                amsJsonData = "";
 | 
			
		||||
                serializeJson(wsArray, amsJsonData);
 | 
			
		||||
                
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ams_data[i].trays[trayId].setting_id = settingId;
 | 
			
		||||
                }
 | 
			
		||||
               
 | 
			
		||||
                // Sende an WebSocket Clients
 | 
			
		||||
                Serial.println("Filament setting updated");
 | 
			
		||||
                sendAmsData(nullptr);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -505,15 +543,16 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
 | 
			
		||||
 | 
			
		||||
void reconnect() {
 | 
			
		||||
    // Loop until we're reconnected
 | 
			
		||||
    uint8_t retries = 0;
 | 
			
		||||
    while (!client.connected()) {
 | 
			
		||||
        Serial.println("Attempting MQTT connection...");
 | 
			
		||||
        Serial.println("Attempting MQTT re/connection...");
 | 
			
		||||
        bambu_connected = false;
 | 
			
		||||
        oledShowTopRow();
 | 
			
		||||
 | 
			
		||||
        // Attempt to connect
 | 
			
		||||
        if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
 | 
			
		||||
            Serial.println("... re-connected");
 | 
			
		||||
            // ... and resubscribe
 | 
			
		||||
            Serial.println("MQTT re/connected");
 | 
			
		||||
 | 
			
		||||
            client.subscribe(report_topic.c_str());
 | 
			
		||||
            bambu_connected = true;
 | 
			
		||||
            oledShowTopRow();
 | 
			
		||||
@@ -523,14 +562,23 @@ void reconnect() {
 | 
			
		||||
            Serial.println(" try again in 5 seconds");
 | 
			
		||||
            bambu_connected = false;
 | 
			
		||||
            oledShowTopRow();
 | 
			
		||||
            // Wait 5 seconds before retrying
 | 
			
		||||
            
 | 
			
		||||
            yield();
 | 
			
		||||
            vTaskDelay(5000 / portTICK_PERIOD_MS);
 | 
			
		||||
            if (retries > 5) {
 | 
			
		||||
                Serial.println("Disable Bambu MQTT Task after 5 retries");
 | 
			
		||||
                //vTaskSuspend(BambuMqttTask);
 | 
			
		||||
                vTaskDelete(BambuMqttTask);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            retries++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mqtt_loop(void * parameter) {
 | 
			
		||||
    Serial.println("Bambu MQTT Task gestartet");
 | 
			
		||||
    for(;;) {
 | 
			
		||||
        if (pauseBambuMqttTask) {
 | 
			
		||||
            vTaskDelay(10000);
 | 
			
		||||
@@ -544,6 +592,7 @@ void mqtt_loop(void * parameter) {
 | 
			
		||||
        }
 | 
			
		||||
        client.loop();
 | 
			
		||||
        yield();
 | 
			
		||||
        esp_task_wdt_reset();
 | 
			
		||||
        vTaskDelay(100);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -584,7 +633,7 @@ bool setupMqtt() {
 | 
			
		||||
            xTaskCreatePinnedToCore(
 | 
			
		||||
                mqtt_loop, /* Function to implement the task */
 | 
			
		||||
                "BambuMqtt", /* Name of the task */
 | 
			
		||||
                10000,  /* Stack size in words */
 | 
			
		||||
                8192,  /* Stack size in words */
 | 
			
		||||
                NULL,  /* Task input parameter */
 | 
			
		||||
                mqttTaskPrio,  /* Priority of the task */
 | 
			
		||||
                &BambuMqttTask,  /* Task handle. */
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ extern bool autoSendToBambu;
 | 
			
		||||
extern int autoSetToBambuSpoolId;
 | 
			
		||||
 | 
			
		||||
bool loadBambuCredentials();
 | 
			
		||||
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend);
 | 
			
		||||
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime);
 | 
			
		||||
bool setupMqtt();
 | 
			
		||||
void mqtt_loop(void * parameter);
 | 
			
		||||
bool setBambuSpool(String payload);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#include "commonFS.h"
 | 
			
		||||
#include <SPIFFS.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
 | 
			
		||||
bool saveJsonValue(const char* filename, const JsonDocument& doc) {
 | 
			
		||||
    File file = SPIFFS.open(filename, "w");
 | 
			
		||||
    File file = LittleFS.open(filename, "w");
 | 
			
		||||
    if (!file) {
 | 
			
		||||
        Serial.print("Fehler beim Öffnen der Datei zum Schreiben: ");
 | 
			
		||||
        Serial.println(filename);
 | 
			
		||||
@@ -20,7 +20,7 @@ bool saveJsonValue(const char* filename, const JsonDocument& doc) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool loadJsonValue(const char* filename, JsonDocument& doc) {
 | 
			
		||||
    File file = SPIFFS.open(filename, "r");
 | 
			
		||||
    File file = LittleFS.open(filename, "r");
 | 
			
		||||
    if (!file) {
 | 
			
		||||
        Serial.print("Fehler beim Öffnen der Datei zum Lesen: ");
 | 
			
		||||
        Serial.println(filename);
 | 
			
		||||
@@ -36,12 +36,12 @@ bool loadJsonValue(const char* filename, JsonDocument& doc) {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void initializeSPIFFS() {
 | 
			
		||||
    if (!SPIFFS.begin(true, "/spiffs", 10, "spiffs")) {
 | 
			
		||||
        Serial.println("SPIFFS Mount Failed");
 | 
			
		||||
void initializeFileSystem() {
 | 
			
		||||
    if (!LittleFS.begin(true)) {
 | 
			
		||||
        Serial.println("LittleFS Mount Failed");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    Serial.printf("SPIFFS Total: %u bytes\n", SPIFFS.totalBytes());
 | 
			
		||||
    Serial.printf("SPIFFS Used: %u bytes\n", SPIFFS.usedBytes());
 | 
			
		||||
    Serial.printf("SPIFFS Free: %u bytes\n", SPIFFS.totalBytes() - SPIFFS.usedBytes());
 | 
			
		||||
    Serial.printf("LittleFS Total: %u bytes\n", LittleFS.totalBytes());
 | 
			
		||||
    Serial.printf("LittleFS Used: %u bytes\n", LittleFS.usedBytes());
 | 
			
		||||
    Serial.printf("LittleFS Free: %u bytes\n", LittleFS.totalBytes() - LittleFS.usedBytes());
 | 
			
		||||
}
 | 
			
		||||
@@ -2,11 +2,11 @@
 | 
			
		||||
#define COMMONFS_H
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <SPIFFS.h>
 | 
			
		||||
#include <ArduinoJson.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
 | 
			
		||||
bool saveJsonValue(const char* filename, const JsonDocument& doc);
 | 
			
		||||
bool loadJsonValue(const char* filename, JsonDocument& doc);
 | 
			
		||||
void initializeSPIFFS();
 | 
			
		||||
void initializeFileSystem();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,9 @@ void setupDisplay() {
 | 
			
		||||
    // the library initializes this with an Adafruit splash screen.
 | 
			
		||||
    display.setTextColor(WHITE);
 | 
			
		||||
    display.display();
 | 
			
		||||
    delay(1000); // Pause for 2 seconds
 | 
			
		||||
    oledShowTopRow();
 | 
			
		||||
    delay(2000);
 | 
			
		||||
    oledShowMessage("FilaMan v" + String(VERSION));
 | 
			
		||||
    vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void oledclearline() {
 | 
			
		||||
@@ -139,8 +139,9 @@ void oledShowMultilineMessage(String message, uint8_t size) {
 | 
			
		||||
    int totalHeight = lines.size() * lineHeight;
 | 
			
		||||
    int startY = OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - totalHeight) / 2);
 | 
			
		||||
    
 | 
			
		||||
    uint8_t lineDistance = (lines.size() == 2) ? 5 : 0;
 | 
			
		||||
    for (size_t i = 0; i < lines.size(); i++) {
 | 
			
		||||
        display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight));
 | 
			
		||||
        display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight) + (i == 1 ? lineDistance : 0));
 | 
			
		||||
        display.print(lines[i]);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								src/main.cpp
									
									
									
									
									
								
							@@ -1,6 +1,4 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <ESPmDNS.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +24,7 @@ void setup() {
 | 
			
		||||
  Serial.printf("%08X\n", (uint32_t)chipid); //print Low 4bytes.
 | 
			
		||||
 | 
			
		||||
  // Initialize SPIFFS
 | 
			
		||||
  initializeSPIFFS();
 | 
			
		||||
  initializeFileSystem();
 | 
			
		||||
 | 
			
		||||
  // Start Display
 | 
			
		||||
  setupDisplay();
 | 
			
		||||
@@ -35,7 +33,6 @@ void setup() {
 | 
			
		||||
  initWiFi();
 | 
			
		||||
 | 
			
		||||
  // Webserver
 | 
			
		||||
  Serial.println("Starte Webserver");
 | 
			
		||||
  setupWebserver(server);
 | 
			
		||||
 | 
			
		||||
  // Spoolman API
 | 
			
		||||
@@ -43,19 +40,9 @@ void setup() {
 | 
			
		||||
  initSpoolman();
 | 
			
		||||
 | 
			
		||||
  // Bambu MQTT
 | 
			
		||||
  // bambu.cpp
 | 
			
		||||
  setupMqtt();
 | 
			
		||||
 | 
			
		||||
  // mDNS
 | 
			
		||||
  Serial.println("Starte MDNS");
 | 
			
		||||
  if (!MDNS.begin("filaman")) {   // Set the hostname to "esp32.local"
 | 
			
		||||
    Serial.println("Error setting up MDNS responder!");
 | 
			
		||||
    while(1) {
 | 
			
		||||
      delay(1000);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  Serial.println("mDNS responder started");
 | 
			
		||||
  
 | 
			
		||||
  // NFC Reader
 | 
			
		||||
  startNfc();
 | 
			
		||||
 | 
			
		||||
  uint8_t scaleCalibrated = start_scale();
 | 
			
		||||
@@ -87,6 +74,21 @@ void setup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Safe interval check that handles millis() overflow
 | 
			
		||||
 * @param currentTime Current millis() value
 | 
			
		||||
 * @param lastTime Last recorded time
 | 
			
		||||
 * @param interval Desired interval in milliseconds
 | 
			
		||||
 * @return True if interval has elapsed
 | 
			
		||||
 */
 | 
			
		||||
bool intervalElapsed(unsigned long currentTime, unsigned long &lastTime, unsigned long interval) {
 | 
			
		||||
  if (currentTime - lastTime >= interval || currentTime < lastTime) {
 | 
			
		||||
    lastTime = currentTime;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long lastWeightReadTime = 0;
 | 
			
		||||
const unsigned long weightReadInterval = 1000; // 1 second
 | 
			
		||||
 | 
			
		||||
@@ -94,39 +96,44 @@ unsigned long lastAutoSetBambuAmsTime = 0;
 | 
			
		||||
const unsigned long autoSetBambuAmsInterval = 1000; // 1 second
 | 
			
		||||
uint8_t autoAmsCounter = 0;
 | 
			
		||||
 | 
			
		||||
unsigned long lastAmsSendTime = 0;
 | 
			
		||||
const unsigned long amsSendInterval = 60000; // 1 minute
 | 
			
		||||
 | 
			
		||||
uint8_t weightSend = 0;
 | 
			
		||||
int16_t lastWeight = 0;
 | 
			
		||||
uint8_t wifiErrorCounter = 0;
 | 
			
		||||
 | 
			
		||||
unsigned long lastWifiCheckTime = 0;
 | 
			
		||||
const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms)
 | 
			
		||||
 | 
			
		||||
// ##### PROGRAM START #####
 | 
			
		||||
void loop() {
 | 
			
		||||
  unsigned long currentMillis = millis();
 | 
			
		||||
 | 
			
		||||
  // Send AMS Data min every Minute
 | 
			
		||||
  if (currentMillis - lastAmsSendTime >= amsSendInterval) 
 | 
			
		||||
  {
 | 
			
		||||
    lastAmsSendTime = currentMillis;
 | 
			
		||||
    sendAmsData(nullptr);
 | 
			
		||||
  // Überprüfe regelmäßig die WLAN-Verbindung
 | 
			
		||||
  if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval)) {
 | 
			
		||||
    checkWiFiConnection();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Wenn Bambu auto set Spool aktiv
 | 
			
		||||
  if (autoSendToBambu && autoSetToBambuSpoolId > 0 && currentMillis - lastAutoSetBambuAmsTime >= autoSetBambuAmsInterval) 
 | 
			
		||||
  {
 | 
			
		||||
    lastAutoSetBambuAmsTime = currentMillis;
 | 
			
		||||
    oledShowMessage("Auto Set         " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
 | 
			
		||||
    autoAmsCounter++;
 | 
			
		||||
 | 
			
		||||
    if (autoAmsCounter >= autoSetBambuAmsCounter) 
 | 
			
		||||
  if (autoSendToBambu && autoSetToBambuSpoolId > 0) {
 | 
			
		||||
    if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval)) 
 | 
			
		||||
    {
 | 
			
		||||
      autoSetToBambuSpoolId = 0;
 | 
			
		||||
      autoAmsCounter = 0;
 | 
			
		||||
      oledShowWeight(weight);
 | 
			
		||||
      if (hasReadRfidTag == 0)
 | 
			
		||||
      {
 | 
			
		||||
        lastAutoSetBambuAmsTime = currentMillis;
 | 
			
		||||
        oledShowMessage("Auto Set         " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
 | 
			
		||||
        autoAmsCounter++;
 | 
			
		||||
 | 
			
		||||
        if (autoAmsCounter >= autoSetBambuAmsCounter) 
 | 
			
		||||
        {
 | 
			
		||||
          autoSetToBambuSpoolId = 0;
 | 
			
		||||
          autoAmsCounter = 0;
 | 
			
		||||
          oledShowWeight(weight);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        autoAmsCounter = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // Wenn Waage nicht Kalibriert
 | 
			
		||||
  if (scaleCalibrated == 3) 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								src/nfc.cpp
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								src/nfc.cpp
									
									
									
									
									
								
							@@ -44,8 +44,6 @@ void payloadToJson(uint8_t *data) {
 | 
			
		||||
      DeserializationError error = deserializeJson(doc, jsonString);
 | 
			
		||||
  
 | 
			
		||||
      if (!error) {
 | 
			
		||||
        const char* version = doc["version"];
 | 
			
		||||
        const char* protocol = doc["protocol"];
 | 
			
		||||
        const char* color_hex = doc["color_hex"];
 | 
			
		||||
        const char* type = doc["type"];
 | 
			
		||||
        int min_temp = doc["min_temp"];
 | 
			
		||||
@@ -55,8 +53,6 @@ void payloadToJson(uint8_t *data) {
 | 
			
		||||
        Serial.println();
 | 
			
		||||
        Serial.println("-----------------");
 | 
			
		||||
        Serial.println("JSON-Parsed Data:");
 | 
			
		||||
        Serial.println(version);
 | 
			
		||||
        Serial.println(protocol);
 | 
			
		||||
        Serial.println(color_hex);
 | 
			
		||||
        Serial.println(type);
 | 
			
		||||
        Serial.println(min_temp);
 | 
			
		||||
@@ -93,8 +89,16 @@ bool formatNdefTag() {
 | 
			
		||||
    return success;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
uint16_t readTagSize()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t buffer[4];
 | 
			
		||||
  memset(buffer, 0, 4);
 | 
			
		||||
  nfc.ntag2xx_ReadPage(3, buffer);
 | 
			
		||||
  return buffer[2]*8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ntag2xx_WriteNDEF(const char *payload) {
 | 
			
		||||
  uint8_t tagSize = 240; // 144 bytes is maximum for NTAG213
 | 
			
		||||
  uint16_t tagSize = readTagSize();
 | 
			
		||||
  Serial.print("Tag Size: ");Serial.println(tagSize);
 | 
			
		||||
 | 
			
		||||
  uint8_t pageBuffer[4] = {0, 0, 0, 0};
 | 
			
		||||
@@ -136,6 +140,8 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
 | 
			
		||||
  if (combinedData == NULL) 
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("Fehler: Nicht genug Speicher vorhanden.");
 | 
			
		||||
    oledShowMessage("Tag too small");
 | 
			
		||||
    vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -238,12 +244,14 @@ void writeJsonToTag(void *parameter) {
 | 
			
		||||
 | 
			
		||||
  hasReadRfidTag = 3;
 | 
			
		||||
  vTaskSuspend(RfidReaderTask);
 | 
			
		||||
  vTaskDelay(500 / portTICK_PERIOD_MS);
 | 
			
		||||
  vTaskDelay(50 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
  //pauseBambuMqttTask = true;
 | 
			
		||||
  // aktualisieren der Website wenn sich der Status ändert
 | 
			
		||||
  sendNfcData(nullptr);
 | 
			
		||||
  vTaskDelay(100 / portTICK_PERIOD_MS);
 | 
			
		||||
  oledShowMessage("Waiting for NFC-Tag");
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
  // Wait 10sec for tag
 | 
			
		||||
  uint8_t success = 0;
 | 
			
		||||
  String uidString = "";
 | 
			
		||||
@@ -331,7 +339,7 @@ void startWriteJsonToTag(const char* payload) {
 | 
			
		||||
    xTaskCreate(
 | 
			
		||||
        writeJsonToTag,        // Task-Funktion
 | 
			
		||||
        "WriteJsonToTagTask",       // Task-Name
 | 
			
		||||
        4096,                        // Stackgröße in Bytes
 | 
			
		||||
        5115,                        // Stackgröße in Bytes
 | 
			
		||||
        (void*)payloadCopy,         // Parameter
 | 
			
		||||
        rfidWriteTaskPrio,           // Priorität
 | 
			
		||||
        NULL                         // Task-Handle (nicht benötigt)
 | 
			
		||||
@@ -367,46 +375,51 @@ void scanRfidTask(void * parameter) {
 | 
			
		||||
 | 
			
		||||
        if (uidLength == 7)
 | 
			
		||||
        {
 | 
			
		||||
          uint8_t data[256];
 | 
			
		||||
 | 
			
		||||
          // We probably have an NTAG2xx card (though it could be Ultralight as well)
 | 
			
		||||
          Serial.println("Seems to be an NTAG2xx tag (7 byte UID)");
 | 
			
		||||
          
 | 
			
		||||
          for (uint8_t i = 0; i < 45; i++) {
 | 
			
		||||
            /*
 | 
			
		||||
            if (i < uidLength) {
 | 
			
		||||
              uidString += String(uid[i], HEX);
 | 
			
		||||
              if (i < uidLength - 1) {
 | 
			
		||||
                  uidString += ":"; // Optional: Trennzeichen hinzufügen
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
            if (!nfc.mifareclassic_ReadDataBlock(i, data + (i - 4) * 4)) 
 | 
			
		||||
            {
 | 
			
		||||
              break; // Stop if reading fails
 | 
			
		||||
            }
 | 
			
		||||
            // Check for NDEF message end
 | 
			
		||||
            if (data[(i - 4) * 4] == 0xFE) 
 | 
			
		||||
            {
 | 
			
		||||
              break; // End of NDEF message
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            yield();
 | 
			
		||||
            esp_task_wdt_reset();
 | 
			
		||||
            vTaskDelay(pdMS_TO_TICKS(1));
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (!decodeNdefAndReturnJson(data)) 
 | 
			
		||||
          uint16_t tagSize = readTagSize();
 | 
			
		||||
          if(tagSize > 0)
 | 
			
		||||
          {
 | 
			
		||||
            oledShowMessage("NFC-Tag unknown");
 | 
			
		||||
            vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
            // Create a buffer depending on the size of the tag
 | 
			
		||||
            uint8_t* data = (uint8_t*)malloc(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)");
 | 
			
		||||
            
 | 
			
		||||
            uint8_t numPages = readTagSize()/4;
 | 
			
		||||
            for (uint8_t i = 4; i < 4+numPages; i++) {
 | 
			
		||||
              if (!nfc.ntag2xx_ReadPage(i, data+(i-4) * 4))
 | 
			
		||||
              {
 | 
			
		||||
                break; // Stop if reading fails
 | 
			
		||||
              }
 | 
			
		||||
              // Check for NDEF message end
 | 
			
		||||
              if (data[(i - 4) * 4] == 0xFE) 
 | 
			
		||||
              {
 | 
			
		||||
                break; // End of NDEF message
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              yield();
 | 
			
		||||
              esp_task_wdt_reset();
 | 
			
		||||
              vTaskDelay(pdMS_TO_TICKS(1));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!decodeNdefAndReturnJson(data)) 
 | 
			
		||||
            {
 | 
			
		||||
              oledShowMessage("NFC-Tag unknown");
 | 
			
		||||
              vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
              hasReadRfidTag = 2;
 | 
			
		||||
            }
 | 
			
		||||
            else 
 | 
			
		||||
            {
 | 
			
		||||
              hasReadRfidTag = 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            free(data);
 | 
			
		||||
          }
 | 
			
		||||
          else
 | 
			
		||||
          {
 | 
			
		||||
            oledShowMessage("NFC-Tag read error");
 | 
			
		||||
            hasReadRfidTag = 2;
 | 
			
		||||
          }
 | 
			
		||||
          else 
 | 
			
		||||
          {
 | 
			
		||||
            hasReadRfidTag = 1;
 | 
			
		||||
          }
 | 
			
		||||
         
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -456,7 +469,7 @@ void startNfc() {
 | 
			
		||||
    BaseType_t result = xTaskCreatePinnedToCore(
 | 
			
		||||
      scanRfidTask, /* Function to implement the task */
 | 
			
		||||
      "RfidReader", /* Name of the task */
 | 
			
		||||
      10000,  /* Stack size in words */
 | 
			
		||||
      5115,  /* Stack size in words */
 | 
			
		||||
      NULL,  /* Task input parameter */
 | 
			
		||||
      rfidTaskPrio,  /* Priority of the task */
 | 
			
		||||
      &RfidReaderTask,  /* Task handle. */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										205
									
								
								src/ota.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/ota.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <website.h>
 | 
			
		||||
#include <commonFS.h>
 | 
			
		||||
 | 
			
		||||
// Globale Variablen für Config Backups hinzufügen
 | 
			
		||||
String bambuCredentialsBackup;
 | 
			
		||||
String spoolmanUrlBackup;
 | 
			
		||||
 | 
			
		||||
// Globale Variable für den Update-Typ
 | 
			
		||||
static int currentUpdateCommand = 0;
 | 
			
		||||
 | 
			
		||||
// Globale Update-Variablen
 | 
			
		||||
static size_t updateTotalSize = 0;
 | 
			
		||||
static size_t updateWritten = 0;
 | 
			
		||||
static bool isSpiffsUpdate = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void backupJsonConfigs() {
 | 
			
		||||
    // Bambu Credentials backup
 | 
			
		||||
    if (LittleFS.exists("/bambu_credentials.json")) {
 | 
			
		||||
        File file = LittleFS.open("/bambu_credentials.json", "r");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            bambuCredentialsBackup = file.readString();
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Bambu credentials backed up");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Spoolman URL backup
 | 
			
		||||
    if (LittleFS.exists("/spoolman_url.json")) {
 | 
			
		||||
        File file = LittleFS.open("/spoolman_url.json", "r");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            spoolmanUrlBackup = file.readString();
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Spoolman URL backed up");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void restoreJsonConfigs() {
 | 
			
		||||
    // Restore Bambu credentials
 | 
			
		||||
    if (bambuCredentialsBackup.length() > 0) {
 | 
			
		||||
        File file = LittleFS.open("/bambu_credentials.json", "w");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            file.print(bambuCredentialsBackup);
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Bambu credentials restored");
 | 
			
		||||
        }
 | 
			
		||||
        bambuCredentialsBackup = ""; // Clear backup
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Restore Spoolman URL
 | 
			
		||||
    if (spoolmanUrlBackup.length() > 0) {
 | 
			
		||||
        File file = LittleFS.open("/spoolman_url.json", "w");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            file.print(spoolmanUrlBackup);
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Spoolman URL restored");
 | 
			
		||||
        }
 | 
			
		||||
        spoolmanUrlBackup = ""; // Clear backup
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void espRestart() {
 | 
			
		||||
    yield();
 | 
			
		||||
    vTaskDelay(5000 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
    ESP.restart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void sendUpdateProgress(int progress, const char* status = nullptr, const char* message = nullptr) {
 | 
			
		||||
    static int lastSentProgress = -1;
 | 
			
		||||
    
 | 
			
		||||
    // Verhindere zu häufige Updates
 | 
			
		||||
    if (progress == lastSentProgress && !status && !message) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    String progressMsg = "{\"type\":\"updateProgress\",\"progress\":" + String(progress);
 | 
			
		||||
    if (status) {
 | 
			
		||||
        progressMsg += ",\"status\":\"" + String(status) + "\"";
 | 
			
		||||
    }
 | 
			
		||||
    if (message) {
 | 
			
		||||
        progressMsg += ",\"message\":\"" + String(message) + "\"";
 | 
			
		||||
    }
 | 
			
		||||
    progressMsg += "}";
 | 
			
		||||
    
 | 
			
		||||
    if (progress >= 100) {
 | 
			
		||||
        // Sende die Nachricht nur einmal für den Abschluss
 | 
			
		||||
        ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        delay(50);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sende die Nachricht mehrmals mit Verzögerung für wichtige Updates
 | 
			
		||||
    if (status || abs(progress - lastSentProgress) >= 10 || progress == 100) {
 | 
			
		||||
        for (int i = 0; i < 2; i++) {
 | 
			
		||||
            ws.textAll(progressMsg);
 | 
			
		||||
            delay(100);  // Längerer Delay zwischen Nachrichten
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        ws.textAll(progressMsg);
 | 
			
		||||
        delay(50);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    lastSentProgress = progress;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleUpdate(AsyncWebServer &server) {
 | 
			
		||||
    AsyncCallbackWebHandler* updateHandler = new AsyncCallbackWebHandler();
 | 
			
		||||
    updateHandler->setUri("/update");
 | 
			
		||||
    updateHandler->setMethod(HTTP_POST);
 | 
			
		||||
    
 | 
			
		||||
    updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
 | 
			
		||||
                             size_t index, uint8_t *data, size_t len, bool final) {
 | 
			
		||||
        if (!index) {
 | 
			
		||||
            updateTotalSize = request->contentLength();
 | 
			
		||||
            updateWritten = 0;
 | 
			
		||||
            isSpiffsUpdate = (filename.indexOf("website") > -1);
 | 
			
		||||
            
 | 
			
		||||
            if (isSpiffsUpdate) {
 | 
			
		||||
                // Backup vor dem Update
 | 
			
		||||
                sendUpdateProgress(0, "backup", "Backing up configurations...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
                backupJsonConfigs();
 | 
			
		||||
                delay(200);
 | 
			
		||||
                
 | 
			
		||||
                const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
 | 
			
		||||
                if (!partition || !Update.begin(partition->size, U_SPIFFS)) {
 | 
			
		||||
                    request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                sendUpdateProgress(5, "starting", "Starting SPIFFS update...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!Update.begin(updateTotalSize)) {
 | 
			
		||||
                    request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                sendUpdateProgress(0, "starting", "Starting firmware update...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (len) {
 | 
			
		||||
            if (Update.write(data, len) != len) {
 | 
			
		||||
                request->send(400, "application/json", "{\"success\":false,\"message\":\"Write failed\"}");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            updateWritten += len;
 | 
			
		||||
            int currentProgress;
 | 
			
		||||
            
 | 
			
		||||
            // Berechne den Fortschritt basierend auf dem Update-Typ
 | 
			
		||||
            if (isSpiffsUpdate) {
 | 
			
		||||
                // SPIFFS: 5-75% für Upload
 | 
			
		||||
                currentProgress = 6 + (updateWritten * 100) / updateTotalSize;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Firmware: 0-100% für Upload
 | 
			
		||||
                currentProgress = 1 + (updateWritten * 100) / updateTotalSize;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            static int lastProgress = -1;
 | 
			
		||||
            if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
 | 
			
		||||
                sendUpdateProgress(currentProgress, "uploading");
 | 
			
		||||
                oledShowMessage("Update: " + String(currentProgress) + "%");
 | 
			
		||||
                delay(50);
 | 
			
		||||
                lastProgress = currentProgress;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (final) {
 | 
			
		||||
            if (Update.end(true)) {
 | 
			
		||||
                if (isSpiffsUpdate) {
 | 
			
		||||
                    restoreJsonConfigs();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                request->send(400, "application/json", "{\"success\":false,\"message\":\"Update finalization failed\"}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    updateHandler->onRequest([](AsyncWebServerRequest *request) {
 | 
			
		||||
        if (Update.hasError()) {
 | 
			
		||||
            request->send(400, "application/json", "{\"success\":false,\"message\":\"Update failed\"}");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Erste 100% Nachricht
 | 
			
		||||
        ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        vTaskDelay(2000 / portTICK_PERIOD_MS);
 | 
			
		||||
        
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(200, "application/json", 
 | 
			
		||||
            "{\"success\":true,\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        response->addHeader("Connection", "close");
 | 
			
		||||
        request->send(response);
 | 
			
		||||
        
 | 
			
		||||
        // Zweite 100% Nachricht zur Sicherheit
 | 
			
		||||
        ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        
 | 
			
		||||
        espRestart();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    server.addHandler(updateHandler);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/ota.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/ota.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#ifndef OTA_H
 | 
			
		||||
#define OTA_H
 | 
			
		||||
 | 
			
		||||
#include <ArduinoOTA.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
void handleUpdate(AsyncWebServer &server);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										245
									
								
								src/website.cpp
									
									
									
									
									
								
							
							
						
						
									
										245
									
								
								src/website.cpp
									
									
									
									
									
								
							@@ -9,6 +9,7 @@
 | 
			
		||||
#include "esp_task_wdt.h"
 | 
			
		||||
#include <Update.h>
 | 
			
		||||
#include "display.h"
 | 
			
		||||
#include "ota.h"
 | 
			
		||||
 | 
			
		||||
#ifndef VERSION
 | 
			
		||||
  #define VERSION "1.1.0"
 | 
			
		||||
@@ -23,48 +24,6 @@ AsyncWebSocket ws("/ws");
 | 
			
		||||
uint8_t lastSuccess = 0;
 | 
			
		||||
uint8_t lastHasReadRfidTag = 0;
 | 
			
		||||
 | 
			
		||||
// Globale Variablen für Config Backups hinzufügen
 | 
			
		||||
String bambuCredentialsBackup;
 | 
			
		||||
String spoolmanUrlBackup;
 | 
			
		||||
 | 
			
		||||
// Globale Variable für den Update-Typ
 | 
			
		||||
static int currentUpdateCommand = 0;
 | 
			
		||||
 | 
			
		||||
// Globale Update-Variablen
 | 
			
		||||
static size_t updateTotalSize = 0;
 | 
			
		||||
static size_t updateWritten = 0;
 | 
			
		||||
static bool isSpiffsUpdate = false;
 | 
			
		||||
 | 
			
		||||
void sendUpdateProgress(int progress, const char* status = nullptr, const char* message = nullptr) {
 | 
			
		||||
    static int lastSentProgress = -1;
 | 
			
		||||
    
 | 
			
		||||
    // Verhindere zu häufige Updates
 | 
			
		||||
    if (progress == lastSentProgress && !status && !message) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    String progressMsg = "{\"type\":\"updateProgress\",\"progress\":" + String(progress);
 | 
			
		||||
    if (status) {
 | 
			
		||||
        progressMsg += ",\"status\":\"" + String(status) + "\"";
 | 
			
		||||
    }
 | 
			
		||||
    if (message) {
 | 
			
		||||
        progressMsg += ",\"message\":\"" + String(message) + "\"";
 | 
			
		||||
    }
 | 
			
		||||
    progressMsg += "}";
 | 
			
		||||
    
 | 
			
		||||
    // Sende die Nachricht mehrmals mit Verzögerung für wichtige Updates
 | 
			
		||||
    if (status || abs(progress - lastSentProgress) >= 10 || progress == 100) {
 | 
			
		||||
        for (int i = 0; i < 2; i++) {
 | 
			
		||||
            ws.textAll(progressMsg);
 | 
			
		||||
            delay(100);  // Längerer Delay zwischen Nachrichten
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        ws.textAll(progressMsg);
 | 
			
		||||
        delay(50);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    lastSentProgress = progress;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
 | 
			
		||||
    if (type == WS_EVT_CONNECT) {
 | 
			
		||||
@@ -84,7 +43,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
 | 
			
		||||
        String message = String((char*)data);
 | 
			
		||||
        JsonDocument doc;
 | 
			
		||||
        deserializeJson(doc, message);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (doc["type"] == "heartbeat") {
 | 
			
		||||
            // Sende Heartbeat-Antwort
 | 
			
		||||
            ws.text(client->id(), "{"
 | 
			
		||||
@@ -96,7 +55,7 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else if (doc["type"] == "writeNfcTag") {
 | 
			
		||||
            if (doc["payload"].is<String>()) {
 | 
			
		||||
            if (doc["payload"].is<JsonObject>()) {
 | 
			
		||||
                // Versuche NFC-Daten zu schreiben
 | 
			
		||||
                String payloadString;
 | 
			
		||||
                serializeJson(doc["payload"], payloadString);
 | 
			
		||||
@@ -136,6 +95,15 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
 | 
			
		||||
            setBambuSpool(doc["payload"]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else if (doc["type"] == "setSpoolmanSettings") {
 | 
			
		||||
            Serial.println(doc["payload"].as<String>());
 | 
			
		||||
            if (updateSpoolBambuData(doc["payload"].as<String>())) {
 | 
			
		||||
                ws.textAll("{\"type\":\"setSpoolmanSettings\",\"payload\":\"success\"}");
 | 
			
		||||
            } else {
 | 
			
		||||
                ws.textAll("{\"type\":\"setSpoolmanSettings\",\"payload\":\"error\"}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else {
 | 
			
		||||
            Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>());
 | 
			
		||||
        }
 | 
			
		||||
@@ -145,12 +113,12 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
 | 
			
		||||
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
 | 
			
		||||
String loadHtmlWithHeader(const char* filename) {
 | 
			
		||||
    Serial.println("Lade HTML-Datei: " + String(filename));
 | 
			
		||||
    if (!SPIFFS.exists(filename)) {
 | 
			
		||||
    if (!LittleFS.exists(filename)) {
 | 
			
		||||
        Serial.println("Fehler: Datei nicht gefunden!");
 | 
			
		||||
        return "Fehler: Datei nicht gefunden!";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    File file = SPIFFS.open(filename, "r");
 | 
			
		||||
    File file = LittleFS.open(filename, "r");
 | 
			
		||||
    String html = file.readString();
 | 
			
		||||
    file.close();
 | 
			
		||||
 | 
			
		||||
@@ -207,105 +175,6 @@ void sendAmsData(AsyncWebSocketClient *client) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleUpdate(AsyncWebServer &server) {
 | 
			
		||||
    AsyncCallbackWebHandler* updateHandler = new AsyncCallbackWebHandler();
 | 
			
		||||
    updateHandler->setUri("/update");
 | 
			
		||||
    updateHandler->setMethod(HTTP_POST);
 | 
			
		||||
    
 | 
			
		||||
    updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
 | 
			
		||||
                             size_t index, uint8_t *data, size_t len, bool final) {
 | 
			
		||||
        if (!index) {
 | 
			
		||||
            updateTotalSize = request->contentLength();
 | 
			
		||||
            updateWritten = 0;
 | 
			
		||||
            isSpiffsUpdate = (filename.indexOf("website") > -1);
 | 
			
		||||
            
 | 
			
		||||
            if (isSpiffsUpdate) {
 | 
			
		||||
                // Backup vor dem Update
 | 
			
		||||
                sendUpdateProgress(0, "backup", "Backing up configurations...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
                backupJsonConfigs();
 | 
			
		||||
                delay(200);
 | 
			
		||||
                
 | 
			
		||||
                const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
 | 
			
		||||
                if (!partition || !Update.begin(partition->size, U_SPIFFS)) {
 | 
			
		||||
                    request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                sendUpdateProgress(5, "starting", "Starting SPIFFS update...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!Update.begin(updateTotalSize)) {
 | 
			
		||||
                    request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                sendUpdateProgress(0, "starting", "Starting firmware update...");
 | 
			
		||||
                delay(200);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (len) {
 | 
			
		||||
            if (Update.write(data, len) != len) {
 | 
			
		||||
                request->send(400, "application/json", "{\"success\":false,\"message\":\"Write failed\"}");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            updateWritten += len;
 | 
			
		||||
            int currentProgress;
 | 
			
		||||
            
 | 
			
		||||
            // Berechne den Fortschritt basierend auf dem Update-Typ
 | 
			
		||||
            if (isSpiffsUpdate) {
 | 
			
		||||
                // SPIFFS: 5-75% für Upload
 | 
			
		||||
                currentProgress = 5 + (updateWritten * 100) / updateTotalSize;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Firmware: 0-100% für Upload
 | 
			
		||||
                currentProgress = 1 + (updateWritten * 100) / updateTotalSize;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            static int lastProgress = -1;
 | 
			
		||||
            if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
 | 
			
		||||
                sendUpdateProgress(currentProgress, "uploading");
 | 
			
		||||
                oledShowMessage("Update: " + String(currentProgress) + "%");
 | 
			
		||||
                delay(50);
 | 
			
		||||
                lastProgress = currentProgress;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (final) {
 | 
			
		||||
            if (Update.end(true)) {
 | 
			
		||||
                if (isSpiffsUpdate) {
 | 
			
		||||
                    restoreJsonConfigs();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                request->send(400, "application/json", "{\"success\":false,\"message\":\"Update finalization failed\"}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    updateHandler->onRequest([](AsyncWebServerRequest *request) {
 | 
			
		||||
        if (Update.hasError()) {
 | 
			
		||||
            request->send(400, "application/json", "{\"success\":false,\"message\":\"Update failed\"}");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Erste 100% Nachricht
 | 
			
		||||
        ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        delay(2000);  // Längerer Delay für die erste Nachricht
 | 
			
		||||
        
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(200, "application/json", 
 | 
			
		||||
            "{\"success\":true,\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        response->addHeader("Connection", "close");
 | 
			
		||||
        request->send(response);
 | 
			
		||||
        
 | 
			
		||||
        // Zweite 100% Nachricht zur Sicherheit
 | 
			
		||||
        ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
 | 
			
		||||
        delay(3000);  // Noch längerer Delay vor dem Neustart
 | 
			
		||||
        
 | 
			
		||||
        ESP.restart();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    server.addHandler(updateHandler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Deaktiviere alle Debug-Ausgaben
 | 
			
		||||
    Serial.setDebugOutput(false);
 | 
			
		||||
@@ -326,7 +195,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Route für about
 | 
			
		||||
    server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /about erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html.gz", "text/html");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/index.html.gz", "text/html");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -335,7 +204,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Route für Waage
 | 
			
		||||
    server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /waage erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/waage.html.gz", "text/html");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -344,7 +213,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Route für RFID
 | 
			
		||||
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /rfid erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/rfid.html.gz", "text/html");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/rfid.html.gz", "text/html");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -360,7 +229,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Route für WiFi
 | 
			
		||||
    server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /wifi erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/wifi.html.gz", "text/html");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/wifi.html.gz", "text/html");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -387,6 +256,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
            html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : "");
 | 
			
		||||
            html.replace("{{bambuCode}}", bambuCode ? bambuCode : "");
 | 
			
		||||
            html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : "");
 | 
			
		||||
            html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -394,6 +264,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
            html.replace("{{bambuSerial}}", "");
 | 
			
		||||
            html.replace("{{bambuCode}}", "");
 | 
			
		||||
            html.replace("{{autoSendToBambu}}", "");
 | 
			
		||||
            html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        request->send(200, "text/html", html);
 | 
			
		||||
@@ -426,17 +297,19 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
        String bambu_serialnr = request->getParam("bambu_serialnr")->value();
 | 
			
		||||
        String bambu_accesscode = request->getParam("bambu_accesscode")->value();
 | 
			
		||||
        bool autoSend = (request->getParam("autoSend")->value() == "true") ? true : false;
 | 
			
		||||
        Serial.println(autoSend);
 | 
			
		||||
        String autoSendTime = request->getParam("autoSendTime")->value();
 | 
			
		||||
        
 | 
			
		||||
        bambu_ip.trim();
 | 
			
		||||
        bambu_serialnr.trim();
 | 
			
		||||
        bambu_accesscode.trim();
 | 
			
		||||
        autoSendTime.trim();
 | 
			
		||||
 | 
			
		||||
        if (bambu_ip.length() == 0 || bambu_serialnr.length() == 0 || bambu_accesscode.length() == 0) {
 | 
			
		||||
            request->send(400, "application/json", "{\"success\": false, \"error\": \"Empty parameter\"}");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend);
 | 
			
		||||
        bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime);
 | 
			
		||||
 | 
			
		||||
        request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}");
 | 
			
		||||
    });
 | 
			
		||||
@@ -449,7 +322,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    // Route für das Laden der CSS-Datei
 | 
			
		||||
    server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Lade style.css");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/style.css.gz", "text/css");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/style.css.gz", "text/css");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -458,7 +331,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
 | 
			
		||||
    // Route für das Logo
 | 
			
		||||
    server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/logo.png.gz", "image/png");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/logo.png.gz", "image/png");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -467,7 +340,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
 | 
			
		||||
    // Route für Favicon
 | 
			
		||||
    server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/favicon.ico", "image/x-icon");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/favicon.ico", "image/x-icon");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
        Serial.println("favicon.ico gesendet");
 | 
			
		||||
@@ -475,17 +348,26 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
 | 
			
		||||
    // Route für spool_in.png
 | 
			
		||||
    server.on("/spool_in.png", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spool_in.png.gz", "image/png");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spool_in.png.gz", "image/png");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
        Serial.println("spool_in.png gesendet");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Route für set_spoolman.png
 | 
			
		||||
    server.on("/set_spoolman.png", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/set_spoolman.png.gz", "image/png");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
        Serial.println("set_spoolman.png gesendet");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Route für JavaScript Dateien
 | 
			
		||||
    server.on("/spoolman.js", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /spoolman.js erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spoolman.js.gz", "text/javascript");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spoolman.js.gz", "text/javascript");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -494,7 +376,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
 | 
			
		||||
    server.on("/rfid.js", HTTP_GET, [](AsyncWebServerRequest *request){
 | 
			
		||||
        Serial.println("Anfrage für /rfid.js erhalten");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS,"/rfid.js.gz", "text/javascript");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS,"/rfid.js.gz", "text/javascript");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", CACHE_CONTROL);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -503,7 +385,7 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
 | 
			
		||||
    // Vereinfachter Update-Handler
 | 
			
		||||
    server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) {
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/upgrade.html.gz", "text/html");
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/upgrade.html.gz", "text/html");
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Cache-Control", "no-store");
 | 
			
		||||
        request->send(response);
 | 
			
		||||
@@ -534,50 +416,3 @@ void setupWebserver(AsyncWebServer &server) {
 | 
			
		||||
    server.begin();
 | 
			
		||||
    Serial.println("Webserver gestartet");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void backupJsonConfigs() {
 | 
			
		||||
    // Bambu Credentials backup
 | 
			
		||||
    if (SPIFFS.exists("/bambu_credentials.json")) {
 | 
			
		||||
        File file = SPIFFS.open("/bambu_credentials.json", "r");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            bambuCredentialsBackup = file.readString();
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Bambu credentials backed up");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Spoolman URL backup
 | 
			
		||||
    if (SPIFFS.exists("/spoolman_url.json")) {
 | 
			
		||||
        File file = SPIFFS.open("/spoolman_url.json", "r");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            spoolmanUrlBackup = file.readString();
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Spoolman URL backed up");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void restoreJsonConfigs() {
 | 
			
		||||
    // Restore Bambu credentials
 | 
			
		||||
    if (bambuCredentialsBackup.length() > 0) {
 | 
			
		||||
        File file = SPIFFS.open("/bambu_credentials.json", "w");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            file.print(bambuCredentialsBackup);
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Bambu credentials restored");
 | 
			
		||||
        }
 | 
			
		||||
        bambuCredentialsBackup = ""; // Clear backup
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Restore Spoolman URL
 | 
			
		||||
    if (spoolmanUrlBackup.length() > 0) {
 | 
			
		||||
        File file = SPIFFS.open("/spoolman_url.json", "w");
 | 
			
		||||
        if (file) {
 | 
			
		||||
            file.print(spoolmanUrlBackup);
 | 
			
		||||
            file.close();
 | 
			
		||||
            Serial.println("Spoolman URL restored");
 | 
			
		||||
        }
 | 
			
		||||
        spoolmanUrlBackup = ""; // Clear backup
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ extern AsyncWebSocket ws;
 | 
			
		||||
 | 
			
		||||
// Server-Initialisierung und Handler
 | 
			
		||||
void initWebServer();
 | 
			
		||||
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
 | 
			
		||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total);
 | 
			
		||||
void setupWebserver(AsyncWebServer &server);
 | 
			
		||||
 | 
			
		||||
@@ -29,8 +28,4 @@ void sendNfcData(AsyncWebSocketClient *client);
 | 
			
		||||
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success);
 | 
			
		||||
void sendWriteResult(AsyncWebSocketClient *client, uint8_t success);
 | 
			
		||||
 | 
			
		||||
// Upgrade-Funktionen
 | 
			
		||||
void backupJsonConfigs();
 | 
			
		||||
void restoreJsonConfigs();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								src/wlan.cpp
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/wlan.cpp
									
									
									
									
									
								
							@@ -3,16 +3,20 @@
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <esp_wifi.h>
 | 
			
		||||
#include <WiFiManager.h>
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <ESPmDNS.h>
 | 
			
		||||
#include "display.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
WiFiManager wm;
 | 
			
		||||
bool wm_nonblocking = false;
 | 
			
		||||
uint8_t wifiErrorCounter = 0;
 | 
			
		||||
 | 
			
		||||
void initWiFi() {
 | 
			
		||||
void wifiSettings() {
 | 
			
		||||
    // Optimierte WiFi-Einstellungen
 | 
			
		||||
    WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
 | 
			
		||||
    WiFi.setSleep(false); // disable sleep mode
 | 
			
		||||
    WiFi.setHostname("FilaMan");
 | 
			
		||||
    esp_wifi_set_ps(WIFI_PS_NONE);
 | 
			
		||||
    
 | 
			
		||||
    // Maximale Sendeleistung
 | 
			
		||||
@@ -23,33 +27,103 @@ void initWiFi() {
 | 
			
		||||
    
 | 
			
		||||
    // Aktiviere WiFi-Roaming für bessere Stabilität
 | 
			
		||||
    esp_wifi_set_rssi_threshold(-80);
 | 
			
		||||
  
 | 
			
		||||
    if(wm_nonblocking) wm.setConfigPortalBlocking(false);
 | 
			
		||||
    wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
 | 
			
		||||
  
 | 
			
		||||
    oledShowTopRow();
 | 
			
		||||
    oledShowMessage("WiFi Setup");
 | 
			
		||||
    
 | 
			
		||||
    bool res;
 | 
			
		||||
    // res = wm.autoConnect(); // auto generated AP name from chipid
 | 
			
		||||
    res = wm.autoConnect("FilaMan"); // anonymous ap
 | 
			
		||||
    // res = wm.autoConnect("spoolman","password"); // password protected ap
 | 
			
		||||
  
 | 
			
		||||
    if(!res) {
 | 
			
		||||
      Serial.println("Failed to connect or hit timeout");
 | 
			
		||||
      // ESP.restart();
 | 
			
		||||
      oledShowTopRow();
 | 
			
		||||
      oledShowMessage("WiFi not connected Check Portal");
 | 
			
		||||
    } 
 | 
			
		||||
    else {
 | 
			
		||||
      wifiOn = true;
 | 
			
		||||
  
 | 
			
		||||
      //if you get here you have connected to the WiFi    
 | 
			
		||||
      Serial.println("connected...yeey :)");
 | 
			
		||||
      Serial.print("IP address: ");
 | 
			
		||||
      Serial.println(WiFi.localIP());
 | 
			
		||||
  
 | 
			
		||||
      oledShowTopRow();
 | 
			
		||||
      display.display();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void startMDNS() {
 | 
			
		||||
  if (!MDNS.begin("filaman")) {
 | 
			
		||||
    Serial.println("Error setting up MDNS responder!");
 | 
			
		||||
    while(1) {
 | 
			
		||||
      vTaskDelay(1000 / portTICK_PERIOD_MS);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
  Serial.println("mDNS responder started");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void configModeCallback (WiFiManager *myWiFiManager) {
 | 
			
		||||
  Serial.println("Entered config mode");
 | 
			
		||||
  oledShowTopRow();
 | 
			
		||||
  oledShowMessage("WiFi Config Mode");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void initWiFi() {
 | 
			
		||||
  // load Wifi settings
 | 
			
		||||
  wifiSettings();
 | 
			
		||||
 | 
			
		||||
  wm.setAPCallback(configModeCallback);
 | 
			
		||||
 | 
			
		||||
  wm.setSaveConfigCallback([]() {
 | 
			
		||||
    Serial.println("Configurations updated");
 | 
			
		||||
    ESP.restart();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if(wm_nonblocking) wm.setConfigPortalBlocking(false);
 | 
			
		||||
  //wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
 | 
			
		||||
  wm.setWiFiAutoReconnect(true);
 | 
			
		||||
  wm.setConnectTimeout(5);
 | 
			
		||||
 | 
			
		||||
  oledShowTopRow();
 | 
			
		||||
  oledShowMessage("WiFi Setup");
 | 
			
		||||
  
 | 
			
		||||
  //bool res = wm.autoConnect("FilaMan"); // anonymous ap
 | 
			
		||||
  if(!wm.autoConnect("FilaMan")) {
 | 
			
		||||
    Serial.println("Failed to connect or hit timeout");
 | 
			
		||||
    // ESP.restart();
 | 
			
		||||
    oledShowTopRow();
 | 
			
		||||
    oledShowMessage("WiFi not connected Check Portal");
 | 
			
		||||
  } 
 | 
			
		||||
  else {
 | 
			
		||||
    wifiOn = true;
 | 
			
		||||
 | 
			
		||||
    //if you get here you have connected to the WiFi    
 | 
			
		||||
    Serial.println("connected...yeey :)");
 | 
			
		||||
    Serial.print("IP address: ");
 | 
			
		||||
    Serial.println(WiFi.localIP());
 | 
			
		||||
 | 
			
		||||
    oledShowTopRow();
 | 
			
		||||
    display.display();
 | 
			
		||||
 | 
			
		||||
    vTaskDelay(500 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
    // mDNS
 | 
			
		||||
    startMDNS();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void checkWiFiConnection() {
 | 
			
		||||
  if (WiFi.status() != WL_CONNECTED) 
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("WiFi connection lost. Reconnecting...");
 | 
			
		||||
    wifiOn = false;
 | 
			
		||||
    oledShowTopRow();
 | 
			
		||||
    oledShowMessage("WiFi reconnecting");
 | 
			
		||||
    WiFi.reconnect(); // Versuche, die Verbindung wiederherzustellen
 | 
			
		||||
    vTaskDelay(5000 / portTICK_PERIOD_MS); // Warte 5 Sekunden, bevor erneut geprüft wird
 | 
			
		||||
    if (WiFi.status() != WL_CONNECTED) 
 | 
			
		||||
    {
 | 
			
		||||
      Serial.println("Failed to reconnect. Restarting WiFi...");
 | 
			
		||||
      WiFi.disconnect();
 | 
			
		||||
      Serial.println("WiFi disconnected.");
 | 
			
		||||
      vTaskDelay(1000 / portTICK_PERIOD_MS);
 | 
			
		||||
      wifiErrorCounter++;
 | 
			
		||||
 | 
			
		||||
      //wifiSettings();
 | 
			
		||||
      WiFi.reconnect();
 | 
			
		||||
      Serial.println("WiFi reconnecting...");
 | 
			
		||||
      WiFi.waitForConnectResult();
 | 
			
		||||
    } 
 | 
			
		||||
    else 
 | 
			
		||||
    {
 | 
			
		||||
      Serial.println("WiFi reconnected.");
 | 
			
		||||
      wifiErrorCounter = 0;
 | 
			
		||||
      wifiOn = true;
 | 
			
		||||
      oledShowTopRow();
 | 
			
		||||
      startMDNS();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (wifiErrorCounter >= 5) 
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("Too many WiFi errors. Restarting...");
 | 
			
		||||
    ESP.restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,5 +4,6 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
 | 
			
		||||
void initWiFi();
 | 
			
		||||
void checkWiFiConnection();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Reference in New Issue
	
	Block a user