Compare commits

..

No commits in common. "main" and "v1.3.94" have entirely different histories.

33 changed files with 602 additions and 16383 deletions

View File

@ -41,16 +41,16 @@ jobs:
run: | run: |
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2) VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
# Build firmware and LittleFS # Build firmware and SPIFFS
echo "Building firmware and LittleFS..." echo "Building firmware and SPIFFS..."
pio run -e esp32dev pio run -e esp32dev
pio run -t buildfs pio run -t buildfs
# Copy firmware binary # Copy firmware binary
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
# Create LittleFS binary - direct copy without header # Create SPIFFS binary - direct copy without header
cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
# Create full binary # Create full binary
(cd .pio/build/esp32dev && (cd .pio/build/esp32dev &&
@ -63,7 +63,7 @@ jobs:
0x1000 bootloader.bin \ 0x1000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0x10000 firmware.bin \ 0x10000 firmware.bin \
0x3D0000 littlefs.bin) 0x3D0000 spiffs.bin)
# Verify file sizes # Verify file sizes
echo "File sizes:" echo "File sizes:"

View File

@ -39,16 +39,16 @@ jobs:
run: | run: |
VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2) VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
# Always build firmware and LittleFS # Always build firmware and SPIFFS
echo "Building firmware and LittleFS..." echo "Building firmware and SPIFFS..."
pio run -e esp32dev pio run -e esp32dev
pio run -t buildfs pio run -t buildfs
# Copy firmware binary # Copy firmware binary
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/upgrade_filaman_firmware_v${VERSION}.bin
# Create LittleFS binary - direct copy without header # Create SPIFFS binary - direct copy without header
cp .pio/build/esp32dev/littlefs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin cp .pio/build/esp32dev/spiffs.bin .pio/build/esp32dev/upgrade_filaman_website_v${VERSION}.bin
# Create full binary (always) # Create full binary (always)
(cd .pio/build/esp32dev && (cd .pio/build/esp32dev &&
@ -61,7 +61,7 @@ jobs:
0x1000 bootloader.bin \ 0x1000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0x10000 firmware.bin \ 0x10000 firmware.bin \
0x3D0000 littlefs.bin) 0x3D0000 spiffs.bin)
# Verify file sizes # Verify file sizes
echo "File sizes:" echo "File sizes:"
@ -131,7 +131,7 @@ jobs:
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_firmware_v${VERSION}.bin" FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_firmware_v${VERSION}.bin"
fi fi
# Add LittleFS and full binary only if they exist # Add SPIFFS and full binary only if they exist
if [ -f "upgrade_filaman_website_v${VERSION}.bin" ]; then if [ -f "upgrade_filaman_website_v${VERSION}.bin" ]; then
FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_website_v${VERSION}.bin" FILES_TO_UPLOAD="$FILES_TO_UPLOAD upgrade_filaman_website_v${VERSION}.bin"
fi fi

54
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,54 @@
{
"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"
}
}

View File

@ -1,83 +1,5 @@
# Changelog # Changelog
## [1.4.0] - 2025-03-01
### Added
- add support for Spoolman Octoprint Plugin in README files
- add OctoPrint integration with configurable fields and update functionality
- add version comparison function and check for outdated versions before updates
- remove unused version and protocol fields from JSON output; add error message for insufficient memory
### Changed
- update NFC tag references to include NTAG213 and clarify storage capacity
- bump version to 1.4.0
- remove unused version and protocol fields from NFC data packet
- sort vendors alphabetically in the dropdown list
- Merge pull request #10 from janecker/nfc-improvements
- Improves NFC Tag handling
## [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 ## [1.3.94] - 2025-02-24
### Changed ### Changed
- update webpages for version v1.3.94 - update webpages for version v1.3.94

View File

@ -9,9 +9,6 @@ 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) oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
### ESP32 Hardware-Funktionen ### ESP32 Hardware-Funktionen
- **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung. - **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung.
@ -19,7 +16,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman). - **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman).
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration. - **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
- **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung. - **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung.
- **NFC-Tag NTAG213 NTAG215:** Verwendung von NTAG213, besser NTAG215 wegen ausreichendem Speicherplatz auf dem Tag - **NFC-Tag NTAG215:** Verwendung von NTAG215 wegen ausreichendem Speicherplatz auf dem Tag
### Weboberflächen-Funktionen ### Weboberflächen-Funktionen
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates. - **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
@ -36,7 +33,6 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- Filtern und Auswählen von Filamenten. - Filtern und Auswählen von Filamenten.
- Automatische Aktualisierung der Spulengewichte. - Automatische Aktualisierung der Spulengewichte.
- Verfolgung von NFC-Tag-Zuweisungen. - Verfolgung von NFC-Tag-Zuweisungen.
- Unterstützt das Spoolman Octoprint Plugin
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee ### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@ -63,7 +59,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
[Amazon Link](https://amzn.eu/d/0AuBp2c) [Amazon Link](https://amzn.eu/d/0AuBp2c)
- **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen. - **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen.
[Amazon Link](https://amzn.eu/d/jfIuQXb) [Amazon Link](https://amzn.eu/d/jfIuQXb)
- **NFC Tags NTAG213 NTA215:** RFID Tag - **NFC Tags Ntag215:** RFID Tag
[Amazon Link](https://amzn.eu/d/9Z6mXc1) [Amazon Link](https://amzn.eu/d/9Z6mXc1)
### Pin-Konfiguration ### Pin-Konfiguration

View File

@ -6,16 +6,12 @@ FilaMan is a filament management system for 3D printing. It uses ESP32 hardware
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface. Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format. The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format.
![Scale](./img/scale_trans.png) ![Scale](./img/scale_trans.png)
More Images can be found in the [img Folder](/img/) More Images can be found in the [img Folder](/img/)
or my website: [FilaMan Website](https://www.filaman.app) or my website:[FilaMan Website](https://www.filaman.app)
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
### ESP32 Hardware Features ### ESP32 Hardware Features
- **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking. - **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking.
@ -23,7 +19,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman). - **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
- **WiFi Connectivity:** WiFiManager for easy network configuration. - **WiFi Connectivity:** WiFiManager for easy network configuration.
- **MQTT Integration:** Connects to Bambu Lab printer for AMS control. - **MQTT Integration:** Connects to Bambu Lab printer for AMS control.
- **NFC-Tag NTAG213 NTAG215:** Use NTAG213, better NTAG215 because of enaught space on the Tag - **NFC-Tag NTAG215:** Use NTAG215 because of enaught space on the Tag
### Web Interface Features ### Web Interface Features
- **Real-time Updates:** WebSocket connection for live data updates. - **Real-time Updates:** WebSocket connection for live data updates.
@ -40,7 +36,6 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- Filter and select filaments. - Filter and select filaments.
- Update spool weights automatically. - Update spool weights automatically.
- Track NFC tag assignments. - Track NFC tag assignments.
- Supports Spoolman Octoprint Plugin
### If you want to support my work, i would be happy to get a coffe ### If you want to support my work, i would be happy to get a coffe
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@ -67,7 +62,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
[Amazon Link](https://amzn.eu/d/0AuBp2c) [Amazon Link](https://amzn.eu/d/0AuBp2c)
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations. - **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
[Amazon Link](https://amzn.eu/d/jfIuQXb) [Amazon Link](https://amzn.eu/d/jfIuQXb)
- **NFC Tags NTAG213 NTAG215:** RFID Tag - **NFC Tags Ntag215:** RFID Tag
[Amazon Link](https://amzn.eu/d/9Z6mXc1) [Amazon Link](https://amzn.eu/d/9Z6mXc1)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,31 +0,0 @@
{
"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"
}

View File

@ -150,13 +150,6 @@ function initWebSocket() {
ramStatus.textContent = `${data.freeHeap}k`; 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) { } catch (error) {
isConnected = false; isConnected = false;
@ -292,14 +285,6 @@ function displayAmsData(amsData) {
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px; transform: rotate(180deg) scaleX(-1);"> <img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px; transform: rotate(180deg) scaleX(-1);">
</button>`; </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) { if (!hasAnyContent) {
return ` return `
<div class="tray"> <div class="tray">
@ -363,7 +348,6 @@ function displayAmsData(amsData) {
${trayDetails} ${trayDetails}
${tempHTML} ${tempHTML}
${(ams.ams_id === 255 && tray.tray_type !== '') ? outButtonHtml : ''} ${(ams.ams_id === 255 && tray.tray_type !== '') ? outButtonHtml : ''}
${(tray.setting_id != "" && tray.setting_id != "null") ? spoolmanButtonHtml : ''}
</div> </div>
</div>`; </div>`;
@ -389,36 +373,6 @@ 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() { function handleSpoolOut() {
// Erstelle Payload // Erstelle Payload
const payload = { const payload = {
@ -640,6 +594,8 @@ function writeNfcTag() {
// Erstelle das NFC-Datenpaket mit korrekten Datentypen // Erstelle das NFC-Datenpaket mit korrekten Datentypen
const nfcData = { const nfcData = {
version: "2.0",
protocol: "openspool",
color_hex: selectedSpool.filament.color_hex || "FFFFFF", color_hex: selectedSpool.filament.color_hex || "FFFFFF",
type: selectedSpool.filament.material, type: selectedSpool.filament.material,
min_temp: minTemp, min_temp: minTemp,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -52,18 +52,11 @@
if (spoolmanUrl && spoolmanUrl.trim() !== "") { if (spoolmanUrl && spoolmanUrl.trim() !== "") {
document.getElementById('spoolmanUrl').value = spoolmanUrl; document.getElementById('spoolmanUrl').value = spoolmanUrl;
} }
// Initialize OctoPrint fields visibility
toggleOctoFields();
}; };
function checkSpoolmanInstance() { function checkSpoolmanInstance() {
const url = document.getElementById('spoolmanUrl').value; const url = document.getElementById('spoolmanUrl').value;
const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked; fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}`)
const spoolmanOctoUrl = document.getElementById('spoolmanOctoUrl').value;
const spoolmanOctoToken = document.getElementById('spoolmanOctoToken').value;
fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}&octoEnabled=${spoolmanOctoEnabled}&octoUrl=${spoolmanOctoUrl}&octoToken=${spoolmanOctoToken}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.healthy) { if (data.healthy) {
@ -82,9 +75,8 @@
const serial = document.getElementById('bambuSerial').value; const serial = document.getElementById('bambuSerial').value;
const code = document.getElementById('bambuCode').value; const code = document.getElementById('bambuCode').value;
const autoSend = document.getElementById('autoSend').checked; 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}&autoSendTime=${autoSendTime}`) fetch(`/api/bambu?bambu_ip=${encodeURIComponent(ip)}&bambu_serialnr=${encodeURIComponent(serial)}&bambu_accesscode=${encodeURIComponent(code)}&autoSend=${autoSend}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.healthy) { if (data.healthy) {
@ -97,15 +89,6 @@
document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message; document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message;
}); });
} }
/**
* Controls visibility of OctoPrint configuration fields based on checkbox state
* Called on page load and when checkbox changes
*/
function toggleOctoFields() {
const octoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
document.getElementById('octoFields').style.display = octoEnabled ? 'block' : 'none';
}
</script> </script>
<script> <script>
var spoolmanUrl = "{{spoolmanUrl}}"; var spoolmanUrl = "{{spoolmanUrl}}";
@ -118,17 +101,6 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5> <h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5>
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port"> <input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port">
<h5 class="card-title">If you want to enable sending Spool to Spoolman Octoprint Plugin:</h5>
<p>
<input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin
</p>
<div id="octoFields" style="display: none;">
<p>
<input type="text" id="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instanz:port" value="{{spoolmanOctoUrl}}">
<input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
</p>
</div>
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button> <button onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
<p id="statusMessage"></p> <p id="statusMessage"></p>
</div> </div>
@ -150,18 +122,13 @@
<label for="bambuCode">Access Code:</label> <label for="bambuCode">Access Code:</label>
<input type="text" id="bambuCode" placeholder="Access Code vom Drucker" value="{{bambuCode}}"> <input type="text" id="bambuCode" placeholder="Access Code vom Drucker" value="{{bambuCode}}">
</div> </div>
<hr> <div class="input-group">
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p> If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.
<div class="input-group" style="display: flex; margin-bottom: 0;"> <label for="autoSend">Auto Send to Bambu:</label>
<label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label> <input type="checkbox" id="autoSend">
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait for Spool in Sec:</label>
</div>
<div class="input-group" style="display: flex;">
<input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
<input type="number" min="60" id="autoSendTime" placeholder="Time to wait" value="{{autoSendTime}}" style="width: 100px;">
</div> </div>
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button> <button onclick="saveBambuCredentials()">Save Bambu Credentials</button>
<p id="bambuStatusMessage"></p> <p id="bambuStatusMessage"></p>
</div> </div>
</div> </div>

View File

@ -86,7 +86,7 @@ function populateVendorDropdown(data, selectedSmId = null) {
}); });
// Nach der Schleife: Formatierung der Gesamtlänge // Nach der Schleife: Formatierung der Gesamtlänge
console.log("Total Length: ", totalLength); console.log("Total Lenght: ", totalLength);
const formattedLength = totalLength > 1000 const formattedLength = totalLength > 1000
? (totalLength / 1000).toFixed(2) + " km" ? (totalLength / 1000).toFixed(2) + " km"
: totalLength.toFixed(2) + " m"; : totalLength.toFixed(2) + " m";
@ -97,15 +97,13 @@ function populateVendorDropdown(data, selectedSmId = null) {
? (weightInKg / 1000).toFixed(2) + " t" ? (weightInKg / 1000).toFixed(2) + " t"
: weightInKg.toFixed(2) + " kg"; : weightInKg.toFixed(2) + " kg";
// Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert // Dropdown mit gefilterten Herstellern befüllen
Object.entries(filteredVendors) Object.entries(filteredVendors).forEach(([id, name]) => {
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name const option = document.createElement("option");
.forEach(([id, name]) => { option.value = id;
const option = document.createElement("option"); option.textContent = name;
option.value = id; vendorSelect.appendChild(option);
option.textContent = name; });
vendorSelect.appendChild(option);
});
document.getElementById("totalSpools").textContent = totalSpools; document.getElementById("totalSpools").textContent = totalSpools;
document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag; document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag;

View File

@ -188,18 +188,14 @@ label {
font-weight: bold; font-weight: bold;
} }
input[type="text"], input[type="submit"], input[type="number"] { input[type="text"], input[type="submit"] {
padding: 10px; padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
font-size: 16px; font-size: 16px;
} }
input[type="number"] { input[type="text"]:focus {
width: 108px !important;
}
input[type="text"]:focus, input[type="number"]:focus {
border-color: #007bff; border-color: #007bff;
outline: none; outline: none;
} }
@ -765,19 +761,17 @@ a:hover {
right: 20px; right: 20px;
padding: 15px 25px; padding: 15px 25px;
border-radius: 4px; border-radius: 4px;
color: black; color: white;
z-index: 1000; z-index: 1000;
animation: slideIn 0.3s ease-out; animation: slideIn 0.3s ease-out;
} }
.notification.success { .notification.success {
background-color: #28a745; background-color: #28a745;
color: black !important;
} }
.notification.error { .notification.error {
background-color: #dc3545; background-color: #dc3545;
color: white !important;
} }
.notification.fade-out { .notification.fade-out {
@ -1019,7 +1013,6 @@ input[type="submit"]:disabled,
color: #000; color: #000;
vertical-align: middle; vertical-align: middle;
margin-left: 0.5rem; margin-left: 0.5rem;
text-shadow: none !important;
} }
.progress-container { .progress-container {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 283 KiB

View File

@ -9,9 +9,7 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common] [common]
version = "1.4.0" version = "1.3.94"
to_old_version = "1.4.0"
## ##
[env:esp32dev] [env:esp32dev]
platform = espressif32 platform = espressif32
@ -35,8 +33,7 @@ lib_deps =
digitaldragon/SSLClient @ ^1.3.2 digitaldragon/SSLClient @ ^1.3.2
; Enable SPIFFS upload ; Enable SPIFFS upload
#board_build.filesystem = spiffs board_build.filesystem = spiffs
board_build.filesystem = littlefs
; Update partition settings ; Update partition settings
board_build.partitions = partitions.csv board_build.partitions = partitions.csv
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
@ -47,14 +44,13 @@ build_flags =
-Os -Os
-ffunction-sections -ffunction-sections
-fdata-sections -fdata-sections
#-DNDEBUG -DNDEBUG
-mtext-section-literals -mtext-section-literals
-DVERSION=\"${common.version}\" -DVERSION=\"${common.version}\"
-DTOOLDVERSION=\"${common.to_old_version}\"
-DASYNCWEBSERVER_REGEX -DASYNCWEBSERVER_REGEX
#-DCORE_DEBUG_LEVEL=3 -DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS=1 -DCONFIG_ARDUHAL_LOG_COLORS=1
#-DOTA_DEBUG=1 -DOTA_DEBUG=1
-DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1 -DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1
-DBOOT_APP_PARTITION_OTA_0=1 -DBOOT_APP_PARTITION_OTA_0=1
-DCONFIG_LWIP_TCP_MSL=60000 -DCONFIG_LWIP_TCP_MSL=60000

View File

@ -5,17 +5,38 @@
bool spoolman_connected = false; bool spoolman_connected = false;
String spoolmanUrl = ""; String spoolmanUrl = "";
bool octoEnabled = false;
String octoUrl = "";
String octoToken = "";
struct SendToApiParams { struct SendToApiParams {
String httpType; String httpType;
String spoolsUrl; String spoolsUrl;
String updatePayload; String updatePayload;
String octoToken;
}; };
/*
// 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) { JsonDocument fetchSingleSpoolInfo(int spoolId) {
HTTPClient http; HTTPClient http;
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId; String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
@ -91,21 +112,19 @@ void sendToApi(void *parameter) {
String httpType = params->httpType; String httpType = params->httpType;
String spoolsUrl = params->spoolsUrl; String spoolsUrl = params->spoolsUrl;
String updatePayload = params->updatePayload; String updatePayload = params->updatePayload;
String octoToken = params->octoToken;
HTTPClient http; HTTPClient http;
http.begin(spoolsUrl); http.begin(spoolsUrl);
http.addHeader("Content-Type", "application/json"); http.addHeader("Content-Type", "application/json");
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
int httpCode = http.PUT(updatePayload); int httpCode = http.PUT(updatePayload);
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
if (httpType == "POST") httpCode = http.POST(updatePayload);
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
Serial.println("Spoolman erfolgreich aktualisiert"); Serial.println("Gewicht der Spule erfolgreich aktualisiert");
} else { } else {
Serial.println("Fehler beim Senden an Spoolman!"); Serial.println("Fehler beim Aktualisieren des Gewichts der Spule");
oledShowMessage("Spoolman update failed"); oledShowMessage("Spoolman update failed");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
@ -204,89 +223,6 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
return 1; return 1;
} }
bool updateSpoolOcto(int spoolId) {
String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool";
Serial.print("Update Spule in Octoprint mit URL: ");
Serial.println(spoolsUrl);
JsonDocument updateDoc;
updateDoc["spool_id"] = spoolId;
updateDoc["tool"] = "tool0";
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 = "POST";
params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload;
params->octoToken = octoToken;
// 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;
}
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 // #### Spoolman init
bool checkSpoolmanExtraFields() { bool checkSpoolmanExtraFields() {
HTTPClient http; HTTPClient http;
@ -428,13 +364,12 @@ bool checkSpoolmanExtraFields() {
} }
} }
} }
http.end();
} }
Serial.println("-------- ENDE Prüfe Felder --------"); Serial.println("-------- ENDE Prüfe Felder --------");
Serial.println(); Serial.println();
http.end();
return true; return true;
} }
@ -478,24 +413,17 @@ bool checkSpoolmanInstance(const String& url) {
return false; return false;
} }
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) { bool saveSpoolmanUrl(const String& url) {
if (!checkSpoolmanInstance(url)) return false; if (!checkSpoolmanInstance(url)) return false;
JsonDocument doc; JsonDocument doc;
doc["url"] = url; doc["url"] = url;
doc["octoEnabled"] = octoOn; Serial.print("Speichere URL in Datei: ");
doc["octoUrl"] = octoWh; Serial.println(url);
doc["octoToken"] = octoTk;
Serial.print("Speichere Spoolman Data in Datei: ");
Serial.println(doc.as<String>());
if (!saveJsonValue("/spoolman_url.json", doc)) { if (!saveJsonValue("/spoolman_url.json", doc)) {
Serial.println("Fehler beim Speichern der Spoolman-URL."); Serial.println("Fehler beim Speichern der Spoolman-URL.");
return false;
} }
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn;
octoUrl = octoWh;
octoToken = octoTk;
return true; return true;
} }
@ -503,13 +431,6 @@ bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
JsonDocument doc; JsonDocument doc;
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) { if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) {
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false;
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>())
{
octoUrl = doc["octoUrl"].as<String>();
octoToken = doc["octoToken"].as<String>();
}
return doc["url"].as<String>(); return doc["url"].as<String>();
} }
Serial.println("Keine gültige Spoolman-URL gefunden."); Serial.println("Keine gültige Spoolman-URL gefunden.");

View File

@ -9,19 +9,15 @@
extern bool spoolman_connected; extern bool spoolman_connected;
extern String spoolmanUrl; extern String spoolmanUrl;
extern bool octoEnabled;
extern String octoUrl;
extern String octoToken;
bool checkSpoolmanInstance(const String& url); bool checkSpoolmanInstance(const String& url);
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk); bool saveSpoolmanUrl(const String& url);
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite 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 bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion zum Aktualisieren eines Spools
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten
#endif #endif

View File

@ -23,11 +23,6 @@ const char* bambu_username = "bblp";
const char* bambu_ip = nullptr; const char* bambu_ip = nullptr;
const char* bambu_accesscode = nullptr; const char* bambu_accesscode = nullptr;
const char* bambu_serialnr = nullptr; const char* bambu_serialnr = nullptr;
String g_bambu_ip = "";
String g_bambu_accesscode = "";
String g_bambu_serialnr = "";
bool bambu_connected = false; bool bambu_connected = false;
bool autoSendToBambu = false; bool autoSendToBambu = false;
int autoSetToBambuSpoolId = 0; int autoSetToBambuSpoolId = 0;
@ -37,7 +32,7 @@ int ams_count = 0;
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
AMSData ams_data[MAX_AMS]; // Definition des Arrays; AMSData ams_data[MAX_AMS]; // Definition des Arrays;
bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend, const String& autoSendTime) { bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend) {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
} }
@ -47,7 +42,6 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
doc["bambu_accesscode"] = accesscode; doc["bambu_accesscode"] = accesscode;
doc["bambu_serialnr"] = serialnr; doc["bambu_serialnr"] = serialnr;
doc["autoSendToBambu"] = autoSend; doc["autoSendToBambu"] = autoSend;
doc["autoSendTime"] = (autoSendTime != "") ? autoSendTime.toInt() : autoSetBambuAmsCounter;
if (!saveJsonValue("/bambu_credentials.json", doc)) { if (!saveJsonValue("/bambu_credentials.json", doc)) {
Serial.println("Fehler beim Speichern der Bambu-Credentials."); Serial.println("Fehler beim Speichern der Bambu-Credentials.");
@ -59,7 +53,6 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
bambu_accesscode = accesscode.c_str(); bambu_accesscode = accesscode.c_str();
bambu_serialnr = serialnr.c_str(); bambu_serialnr = serialnr.c_str();
autoSendToBambu = autoSend; autoSendToBambu = autoSend;
autoSetBambuAmsCounter = autoSendTime.toInt();
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
if (!setupMqtt()) return false; if (!setupMqtt()) return false;
@ -74,22 +67,16 @@ bool loadBambuCredentials() {
String ip = doc["bambu_ip"].as<String>(); String ip = doc["bambu_ip"].as<String>();
String code = doc["bambu_accesscode"].as<String>(); String code = doc["bambu_accesscode"].as<String>();
String serial = doc["bambu_serialnr"].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(); ip.trim();
code.trim(); code.trim();
serial.trim(); serial.trim();
// Dynamische Speicherallokation für die globalen Pointer // Dynamische Speicherallokation für die globalen Pointer
bambu_ip = g_bambu_ip.c_str(); bambu_ip = strdup(ip.c_str());
bambu_accesscode = g_bambu_accesscode.c_str(); bambu_accesscode = strdup(code.c_str());
bambu_serialnr = g_bambu_serialnr.c_str(); bambu_serialnr = strdup(serial.c_str());
report_topic = "device/" + String(bambu_serialnr) + "/report"; report_topic = "device/" + String(bambu_serialnr) + "/report";
//request_topic = "device/" + String(bambu_serialnr) + "/request"; //request_topic = "device/" + String(bambu_serialnr) + "/request";
@ -108,38 +95,12 @@ FilamentResult findFilamentIdx(String brand, String type) {
// JSON-Dokument für die Filament-Daten erstellen // JSON-Dokument für die Filament-Daten erstellen
JsonDocument doc; 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 // 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"); Serial.println("Fehler beim Laden der Filament-Daten");
return {"GFL99", "PLA"}; // Fallback auf Generic PLA 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 // 1. Erst versuchen wir die exakte Brand + Type Kombination zu finden
String searchKey; String searchKey;
if (brand == "Bambu" || brand == "Bambulab") { if (brand == "Bambu" || brand == "Bambulab") {
@ -196,7 +157,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
return {"GFL99", "PLA"}; return {"GFL99", "PLA"};
} }
bool sendMqttMessage(const String& payload) { bool sendMqttMessage(String payload) {
Serial.println("Sending MQTT message"); Serial.println("Sending MQTT message");
Serial.println(payload); Serial.println(payload);
if (client.publish(report_topic.c_str(), payload.c_str())) if (client.publish(report_topic.c_str(), payload.c_str()))
@ -314,98 +275,12 @@ void autoSetSpool(int spoolId, uint8_t trayId) {
Serial.println(spoolInfo.as<String>()); Serial.println(spoolInfo.as<String>());
setBambuSpool(spoolInfo.as<String>()); setBambuSpool(spoolInfo.as<String>());
oledShowMessage("Spool set");
} }
// id wieder zurücksetzen damit abgeschlossen // id wieder zurücksetzen damit abgeschlossen
autoSetToBambuSpoolId = 0; 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>();
if (trayObj["tray_type"].as<String>() == "") ams_data[i].trays[j].setting_id = "";
ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
}
}
// 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 // init
void mqtt_callback(char* topic, byte* payload, unsigned int length) { void mqtt_callback(char* topic, byte* payload, unsigned int length) {
String message; String message;
@ -417,7 +292,6 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
// JSON-Dokument parsen // JSON-Dokument parsen
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, message); DeserializationError error = deserializeJson(doc, message);
message = "";
if (error) if (error)
{ {
Serial.print("Fehler beim Parsen des JSON: "); Serial.print("Fehler beim Parsen des JSON: ");
@ -425,8 +299,16 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
return; return;
} }
// Wenn bambu auto set spool aktiv und eine spule erkannt und mqtt meldung das neue spule im ams
if (autoSendToBambu && autoSetToBambuSpoolId > 0 &&
doc["print"]["command"].as<String>() == "push_status" && doc["print"]["ams"]["tray_pre"].as<uint8_t>()
&& !doc["print"]["ams"]["ams"].as<JsonArray>())
{
autoSetSpool(autoSetToBambuSpoolId, doc["print"]["ams"]["tray_pre"].as<uint8_t>());
}
// Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren // Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren
if (doc["print"]["upgrade_state"].is<JsonObject>() || (doc["print"]["command"].is<String>() && doc["print"]["command"] == "push_status")) if (doc["print"]["upgrade_state"].is<JsonObject>())
{ {
// Prüfen ob AMS-Daten vorhanden sind // Prüfen ob AMS-Daten vorhanden sind
if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>()) if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>())
@ -462,81 +344,158 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
// Vergleiche die Trays // Vergleiche die Trays
for (int j = 0; j < trayArray.size() && j < 4 && !hasChanges; j++) { for (int j = 0; j < trayArray.size() && j < 4 && !hasChanges; j++) {
JsonObject trayObj = trayArray[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 || 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_type"].as<String>() != ams_data[storedIndex].trays[j].tray_type ||
trayObj["tray_color"].as<String>() != ams_data[storedIndex].trays[j].tray_color || 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) { trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
{
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
}
break; break;
} }
} }
} }
// Prüfe die externe Spule // Prüfe die externe Spule
JsonObject vtTray = doc["print"]["vt_tray"]; if (!hasChanges && doc["print"]["vt_tray"].is<JsonObject>()) {
if (doc["print"]["vt_tray"].is<JsonObject>()) { JsonObject vtTray = doc["print"]["vt_tray"];
bool foundExternal = false;
for (int i = 0; i < ams_count; i++) { for (int i = 0; i < ams_count; i++) {
if (ams_data[i].ams_id == 255) { if (ams_data[i].ams_id == 255) {
if (vtTray["tray_type"].as<String>() == "") ams_data[i].trays[0].setting_id = ""; foundExternal = true;
if (vtTray["setting_id"].isNull()) vtTray["setting_id"] = "";
if (vtTray["tray_info_idx"].as<String>() != ams_data[i].trays[0].tray_info_idx || 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_type"].as<String>() != ams_data[i].trays[0].tray_type ||
vtTray["tray_color"].as<String>() != ams_data[i].trays[0].tray_color || vtTray["tray_color"].as<String>() != ams_data[i].trays[0].tray_color ||
(vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) || vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx) {
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
{
autoSetSpool(autoSetToBambuSpoolId, 254);
}
} }
break; break;
} }
} }
if (!foundExternal) hasChanges = true;
} }
if (!hasChanges) return; if (!hasChanges) return;
updateAmsWsData(doc, amsArray, ams_count, 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>();
}
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);
}
// Neue Bedingung für ams_filament_setting // Neue Bedingung für ams_filament_setting
if (doc["print"]["command"] == "ams_filament_setting") { else if (doc["print"]["command"] == "ams_filament_setting") {
int amsId = doc["print"]["ams_id"].as<int>(); int amsId = doc["print"]["ams_id"].as<int>();
int trayId = doc["print"]["tray_id"].as<int>(); int trayId = doc["print"]["tray_id"].as<int>();
String settingId = (doc["print"]["setting_id"].is<String>()) ? doc["print"]["setting_id"].as<String>() : ""; String settingId = doc["print"]["setting_id"].as<String>();
// Finde das entsprechende AMS und Tray // Finde das entsprechende AMS und Tray
for (int i = 0; i < ams_count; i++) { for (int i = 0; i < ams_count; i++) {
if (ams_data[i].ams_id == amsId) { if (ams_data[i].ams_id == amsId) {
if (trayId == 254) // Update setting_id im entsprechenden Tray
{ ams_data[i].trays[trayId].setting_id = settingId;
// Suche AMS mit ID 255 (externe Spule)
for (int j = 0; j < ams_count; j++) { // Erstelle neues JSON für WebSocket-Clients
if (ams_data[j].ams_id == 255) { JsonDocument wsDoc;
ams_data[j].trays[0].setting_id = settingId; JsonArray wsArray = wsDoc.to<JsonArray>();
break;
} 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;
} }
} }
else
{ // Aktualisiere das globale amsJsonData
ams_data[i].trays[trayId].setting_id = settingId; amsJsonData = "";
} serializeJson(wsArray, amsJsonData);
// Sende an WebSocket Clients // Sende an WebSocket Clients
Serial.println("Filament setting updated");
sendAmsData(nullptr); sendAmsData(nullptr);
break; break;
} }
@ -546,16 +505,15 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
void reconnect() { void reconnect() {
// Loop until we're reconnected // Loop until we're reconnected
uint8_t retries = 0;
while (!client.connected()) { while (!client.connected()) {
Serial.println("Attempting MQTT re/connection..."); Serial.println("Attempting MQTT connection...");
bambu_connected = false; bambu_connected = false;
oledShowTopRow(); oledShowTopRow();
// Attempt to connect // Attempt to connect
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) { if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
Serial.println("MQTT re/connected"); Serial.println("... re-connected");
// ... and resubscribe
client.subscribe(report_topic.c_str()); client.subscribe(report_topic.c_str());
bambu_connected = true; bambu_connected = true;
oledShowTopRow(); oledShowTopRow();
@ -565,23 +523,14 @@ void reconnect() {
Serial.println(" try again in 5 seconds"); Serial.println(" try again in 5 seconds");
bambu_connected = false; bambu_connected = false;
oledShowTopRow(); oledShowTopRow();
// Wait 5 seconds before retrying
yield(); yield();
vTaskDelay(5000 / portTICK_PERIOD_MS); 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) { void mqtt_loop(void * parameter) {
Serial.println("Bambu MQTT Task gestartet");
for(;;) { for(;;) {
if (pauseBambuMqttTask) { if (pauseBambuMqttTask) {
vTaskDelay(10000); vTaskDelay(10000);
@ -595,7 +544,6 @@ void mqtt_loop(void * parameter) {
} }
client.loop(); client.loop();
yield(); yield();
esp_task_wdt_reset();
vTaskDelay(100); vTaskDelay(100);
} }
} }
@ -603,6 +551,7 @@ void mqtt_loop(void * parameter) {
bool setupMqtt() { bool setupMqtt() {
// Wenn Bambu Daten vorhanden // Wenn Bambu Daten vorhanden
bool success = loadBambuCredentials(); bool success = loadBambuCredentials();
vTaskDelay(100 / portTICK_PERIOD_MS);
if (!success) { if (!success) {
Serial.println("Failed to load Bambu credentials"); Serial.println("Failed to load Bambu credentials");
@ -635,7 +584,7 @@ bool setupMqtt() {
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
mqtt_loop, /* Function to implement the task */ mqtt_loop, /* Function to implement the task */
"BambuMqtt", /* Name of the task */ "BambuMqtt", /* Name of the task */
8192, /* Stack size in words */ 10000, /* Stack size in words */
NULL, /* Task input parameter */ NULL, /* Task input parameter */
mqttTaskPrio, /* Priority of the task */ mqttTaskPrio, /* Priority of the task */
&BambuMqttTask, /* Task handle. */ &BambuMqttTask, /* Task handle. */
@ -666,7 +615,6 @@ bool setupMqtt() {
void bambu_restart() { void bambu_restart() {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
delay(10);
} }
setupMqtt(); setupMqtt();
} }

View File

@ -32,7 +32,7 @@ extern bool autoSendToBambu;
extern int autoSetToBambuSpoolId; extern int autoSetToBambuSpoolId;
bool loadBambuCredentials(); bool loadBambuCredentials();
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend, const String& autoSendTime); bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend);
bool setupMqtt(); bool setupMqtt();
void mqtt_loop(void * parameter); void mqtt_loop(void * parameter);
bool setBambuSpool(String payload); bool setBambuSpool(String payload);

View File

@ -1,8 +1,8 @@
#include "commonFS.h" #include "commonFS.h"
#include <LittleFS.h> #include <SPIFFS.h>
bool saveJsonValue(const char* filename, const JsonDocument& doc) { bool saveJsonValue(const char* filename, const JsonDocument& doc) {
File file = LittleFS.open(filename, "w"); File file = SPIFFS.open(filename, "w");
if (!file) { if (!file) {
Serial.print("Fehler beim Öffnen der Datei zum Schreiben: "); Serial.print("Fehler beim Öffnen der Datei zum Schreiben: ");
Serial.println(filename); Serial.println(filename);
@ -20,7 +20,7 @@ bool saveJsonValue(const char* filename, const JsonDocument& doc) {
} }
bool loadJsonValue(const char* filename, JsonDocument& doc) { bool loadJsonValue(const char* filename, JsonDocument& doc) {
File file = LittleFS.open(filename, "r"); File file = SPIFFS.open(filename, "r");
if (!file) { if (!file) {
Serial.print("Fehler beim Öffnen der Datei zum Lesen: "); Serial.print("Fehler beim Öffnen der Datei zum Lesen: ");
Serial.println(filename); Serial.println(filename);
@ -36,12 +36,12 @@ bool loadJsonValue(const char* filename, JsonDocument& doc) {
return true; return true;
} }
void initializeFileSystem() { void initializeSPIFFS() {
if (!LittleFS.begin(true)) { if (!SPIFFS.begin(true, "/spiffs", 10, "spiffs")) {
Serial.println("LittleFS Mount Failed"); Serial.println("SPIFFS Mount Failed");
return; return;
} }
Serial.printf("LittleFS Total: %u bytes\n", LittleFS.totalBytes()); Serial.printf("SPIFFS Total: %u bytes\n", SPIFFS.totalBytes());
Serial.printf("LittleFS Used: %u bytes\n", LittleFS.usedBytes()); Serial.printf("SPIFFS Used: %u bytes\n", SPIFFS.usedBytes());
Serial.printf("LittleFS Free: %u bytes\n", LittleFS.totalBytes() - LittleFS.usedBytes()); Serial.printf("SPIFFS Free: %u bytes\n", SPIFFS.totalBytes() - SPIFFS.usedBytes());
} }

View File

@ -2,11 +2,11 @@
#define COMMONFS_H #define COMMONFS_H
#include <Arduino.h> #include <Arduino.h>
#include <SPIFFS.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <LittleFS.h>
bool saveJsonValue(const char* filename, const JsonDocument& doc); bool saveJsonValue(const char* filename, const JsonDocument& doc);
bool loadJsonValue(const char* filename, JsonDocument& doc); bool loadJsonValue(const char* filename, JsonDocument& doc);
void initializeFileSystem(); void initializeSPIFFS();
#endif #endif

View File

@ -20,9 +20,9 @@ void setupDisplay() {
// the library initializes this with an Adafruit splash screen. // the library initializes this with an Adafruit splash screen.
display.setTextColor(WHITE); display.setTextColor(WHITE);
display.display(); display.display();
delay(1000); // Pause for 2 seconds
oledShowTopRow(); oledShowTopRow();
oledShowMessage("FilaMan v" + String(VERSION)); delay(2000);
vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
void oledclearline() { void oledclearline() {
@ -139,9 +139,8 @@ void oledShowMultilineMessage(String message, uint8_t size) {
int totalHeight = lines.size() * lineHeight; int totalHeight = lines.size() * lineHeight;
int startY = OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - totalHeight) / 2); 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++) { for (size_t i = 0; i < lines.size(); i++) {
display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight) + (i == 1 ? lineDistance : 0)); display.setCursor(oled_center_h(lines[i]), startY + (i * lineHeight));
display.print(lines[i]); display.print(lines[i]);
} }

View File

@ -1,4 +1,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <Wire.h> #include <Wire.h>
#include <WiFi.h> #include <WiFi.h>
@ -24,7 +26,7 @@ void setup() {
Serial.printf("%08X\n", (uint32_t)chipid); //print Low 4bytes. Serial.printf("%08X\n", (uint32_t)chipid); //print Low 4bytes.
// Initialize SPIFFS // Initialize SPIFFS
initializeFileSystem(); initializeSPIFFS();
// Start Display // Start Display
setupDisplay(); setupDisplay();
@ -33,6 +35,7 @@ void setup() {
initWiFi(); initWiFi();
// Webserver // Webserver
Serial.println("Starte Webserver");
setupWebserver(server); setupWebserver(server);
// Spoolman API // Spoolman API
@ -40,9 +43,19 @@ void setup() {
initSpoolman(); initSpoolman();
// Bambu MQTT // Bambu MQTT
// bambu.cpp
setupMqtt(); setupMqtt();
// NFC Reader // 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");
startNfc(); startNfc();
uint8_t scaleCalibrated = start_scale(); uint8_t scaleCalibrated = start_scale();
@ -74,21 +87,6 @@ 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; unsigned long lastWeightReadTime = 0;
const unsigned long weightReadInterval = 1000; // 1 second const unsigned long weightReadInterval = 1000; // 1 second
@ -96,45 +94,40 @@ unsigned long lastAutoSetBambuAmsTime = 0;
const unsigned long autoSetBambuAmsInterval = 1000; // 1 second const unsigned long autoSetBambuAmsInterval = 1000; // 1 second
uint8_t autoAmsCounter = 0; uint8_t autoAmsCounter = 0;
unsigned long lastAmsSendTime = 0;
const unsigned long amsSendInterval = 60000; // 1 minute
uint8_t weightSend = 0; uint8_t weightSend = 0;
int16_t lastWeight = 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 ##### // ##### PROGRAM START #####
void loop() { void loop() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
// Überprüfe regelmäßig die WLAN-Verbindung // Send AMS Data min every Minute
if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval)) { if (currentMillis - lastAmsSendTime >= amsSendInterval)
checkWiFiConnection(); {
lastAmsSendTime = currentMillis;
sendAmsData(nullptr);
} }
// Wenn Bambu auto set Spool aktiv // Wenn Bambu auto set Spool aktiv
if (autoSendToBambu && autoSetToBambuSpoolId > 0) { if (autoSendToBambu && autoSetToBambuSpoolId > 0 && currentMillis - lastAutoSetBambuAmsTime >= autoSetBambuAmsInterval)
if (intervalElapsed(currentMillis, lastAutoSetBambuAmsTime, autoSetBambuAmsInterval)) {
{ lastAutoSetBambuAmsTime = currentMillis;
if (hasReadRfidTag == 0) oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
{ autoAmsCounter++;
lastAutoSetBambuAmsTime = currentMillis;
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s");
autoAmsCounter++;
if (autoAmsCounter >= autoSetBambuAmsCounter) if (autoAmsCounter >= autoSetBambuAmsCounter)
{ {
autoSetToBambuSpoolId = 0; autoSetToBambuSpoolId = 0;
autoAmsCounter = 0; autoAmsCounter = 0;
oledShowWeight(weight); oledShowWeight(weight);
}
}
else
{
autoAmsCounter = 0;
}
} }
} }
// Wenn Waage nicht Kalibriert // Wenn Waage nicht Kalibriert
if (scaleCalibrated == 3) if (scaleCalibrated == 3)
{ {
@ -197,11 +190,6 @@ void loop() {
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
weightSend = 1; weightSend = 1;
autoSetToBambuSpoolId = spoolId.toInt(); autoSetToBambuSpoolId = spoolId.toInt();
if (octoEnabled)
{
updateSpoolOcto(autoSetToBambuSpoolId);
}
} }
else else
{ {

View File

@ -44,6 +44,8 @@ void payloadToJson(uint8_t *data) {
DeserializationError error = deserializeJson(doc, jsonString); DeserializationError error = deserializeJson(doc, jsonString);
if (!error) { if (!error) {
const char* version = doc["version"];
const char* protocol = doc["protocol"];
const char* color_hex = doc["color_hex"]; const char* color_hex = doc["color_hex"];
const char* type = doc["type"]; const char* type = doc["type"];
int min_temp = doc["min_temp"]; int min_temp = doc["min_temp"];
@ -53,6 +55,8 @@ void payloadToJson(uint8_t *data) {
Serial.println(); Serial.println();
Serial.println("-----------------"); Serial.println("-----------------");
Serial.println("JSON-Parsed Data:"); Serial.println("JSON-Parsed Data:");
Serial.println(version);
Serial.println(protocol);
Serial.println(color_hex); Serial.println(color_hex);
Serial.println(type); Serial.println(type);
Serial.println(min_temp); Serial.println(min_temp);
@ -89,16 +93,8 @@ bool formatNdefTag() {
return success; 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 ntag2xx_WriteNDEF(const char *payload) {
uint16_t tagSize = readTagSize(); uint8_t tagSize = 240; // 144 bytes is maximum for NTAG213
Serial.print("Tag Size: ");Serial.println(tagSize); Serial.print("Tag Size: ");Serial.println(tagSize);
uint8_t pageBuffer[4] = {0, 0, 0, 0}; uint8_t pageBuffer[4] = {0, 0, 0, 0};
@ -140,8 +136,6 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
if (combinedData == NULL) if (combinedData == NULL)
{ {
Serial.println("Fehler: Nicht genug Speicher vorhanden."); Serial.println("Fehler: Nicht genug Speicher vorhanden.");
oledShowMessage("Tag too small");
vTaskDelay(2000 / portTICK_PERIOD_MS);
return 0; return 0;
} }
@ -244,12 +238,10 @@ void writeJsonToTag(void *parameter) {
hasReadRfidTag = 3; hasReadRfidTag = 3;
vTaskSuspend(RfidReaderTask); vTaskSuspend(RfidReaderTask);
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
//pauseBambuMqttTask = true; //pauseBambuMqttTask = true;
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData(nullptr);
vTaskDelay(100 / portTICK_PERIOD_MS);
oledShowMessage("Waiting for NFC-Tag"); oledShowMessage("Waiting for NFC-Tag");
// Wait 10sec for tag // Wait 10sec for tag
@ -339,7 +331,7 @@ void startWriteJsonToTag(const char* payload) {
xTaskCreate( xTaskCreate(
writeJsonToTag, // Task-Funktion writeJsonToTag, // Task-Funktion
"WriteJsonToTagTask", // Task-Name "WriteJsonToTagTask", // Task-Name
5115, // Stackgröße in Bytes 4096, // Stackgröße in Bytes
(void*)payloadCopy, // Parameter (void*)payloadCopy, // Parameter
rfidWriteTaskPrio, // Priorität rfidWriteTaskPrio, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
@ -375,51 +367,46 @@ void scanRfidTask(void * parameter) {
if (uidLength == 7) if (uidLength == 7)
{ {
uint16_t tagSize = readTagSize(); uint8_t data[256];
if(tagSize > 0)
// 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))
{ {
// Create a buffer depending on the size of the tag oledShowMessage("NFC-Tag unknown");
uint8_t* data = (uint8_t*)malloc(tagSize); vTaskDelay(2000 / portTICK_PERIOD_MS);
memset(data, 0, tagSize); hasReadRfidTag = 2;
// 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 else
{ {
oledShowMessage("NFC-Tag read error"); hasReadRfidTag = 1;
hasReadRfidTag = 2;
} }
} }
else else
{ {
@ -469,7 +456,7 @@ void startNfc() {
BaseType_t result = xTaskCreatePinnedToCore( BaseType_t result = xTaskCreatePinnedToCore(
scanRfidTask, /* Function to implement the task */ scanRfidTask, /* Function to implement the task */
"RfidReader", /* Name of the task */ "RfidReader", /* Name of the task */
5115, /* Stack size in words */ 10000, /* Stack size in words */
NULL, /* Task input parameter */ NULL, /* Task input parameter */
rfidTaskPrio, /* Priority of the task */ rfidTaskPrio, /* Priority of the task */
&RfidReaderTask, /* Task handle. */ &RfidReaderTask, /* Task handle. */

View File

@ -1,243 +0,0 @@
#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;
/**
* Compares two version strings and determines if version1 is less than version2
*
* @param version1 First version string (format: x.y.z)
* @param version2 Second version string (format: x.y.z)
* @return true if version1 is less than version2
*/
bool isVersionLessThan(const String& version1, const String& version2) {
int major1 = 0, minor1 = 0, patch1 = 0;
int major2 = 0, minor2 = 0, patch2 = 0;
// Parse version1
sscanf(version1.c_str(), "%d.%d.%d", &major1, &minor1, &patch1);
// Parse version2
sscanf(version2.c_str(), "%d.%d.%d", &major2, &minor2, &patch2);
// Compare major version
if (major1 < major2) return true;
if (major1 > major2) return false;
// Major versions equal, compare minor
if (minor1 < minor2) return true;
if (minor1 > minor2) return false;
// Minor versions equal, compare patch
return patch1 < patch2;
}
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);
// Check if current version is less than defined TOOLVERSION before proceeding with update
if (isVersionLessThan(VERSION, TOOLDVERSION)) {
updateHandler->onRequest([](AsyncWebServerRequest *request) {
request->send(400, "application/json",
"{\"success\":false,\"message\":\"Your current version is too old. Please perform a full upgrade.\"}");
});
server.addHandler(updateHandler);
return;
}
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);
}

View File

@ -1,9 +0,0 @@
#ifndef OTA_H
#define OTA_H
#include <ArduinoOTA.h>
#include <ESPAsyncWebServer.h>
void handleUpdate(AsyncWebServer &server);
#endif

View File

@ -47,7 +47,7 @@ void scale_loop(void * parameter) {
weight = round(scale.get_units()); weight = round(scale.get_units());
} }
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100)); // Verzögerung, um die CPU nicht zu überlasten
} }
} }
@ -90,7 +90,7 @@ uint8_t start_scale() {
BaseType_t result = xTaskCreatePinnedToCore( BaseType_t result = xTaskCreatePinnedToCore(
scale_loop, /* Function to implement the task */ scale_loop, /* Function to implement the task */
"ScaleLoop", /* Name of the task */ "ScaleLoop", /* Name of the task */
2048, /* Stack size in words */ 10000, /* Stack size in words */
NULL, /* Task input parameter */ NULL, /* Task input parameter */
scaleTaskPrio, /* Priority of the task */ scaleTaskPrio, /* Priority of the task */
&ScaleTask, /* Task handle. */ &ScaleTask, /* Task handle. */
@ -110,7 +110,6 @@ uint8_t calibrate_scale() {
//vTaskSuspend(RfidReaderTask); //vTaskSuspend(RfidReaderTask);
vTaskDelete(RfidReaderTask); vTaskDelete(RfidReaderTask);
vTaskDelete(ScaleTask);
pauseBambuMqttTask = true; pauseBambuMqttTask = true;
pauseMainTask = 1; pauseMainTask = 1;
@ -178,6 +177,8 @@ uint8_t calibrate_scale() {
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
//ESP.restart();
} }
else else
{ {
@ -211,7 +212,8 @@ uint8_t calibrate_scale() {
oledShowMessage("Scale Ready"); oledShowMessage("Scale Ready");
Serial.println("restart Scale Task");
Serial.println("starte Scale Task");
start_scale(); start_scale();
pauseBambuMqttTask = false; pauseBambuMqttTask = false;

View File

@ -9,7 +9,6 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include <Update.h> #include <Update.h>
#include "display.h" #include "display.h"
#include "ota.h"
#ifndef VERSION #ifndef VERSION
#define VERSION "1.1.0" #define VERSION "1.1.0"
@ -24,6 +23,48 @@ AsyncWebSocket ws("/ws");
uint8_t lastSuccess = 0; uint8_t lastSuccess = 0;
uint8_t lastHasReadRfidTag = 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) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_CONNECT) { if (type == WS_EVT_CONNECT) {
@ -95,15 +136,6 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
setBambuSpool(doc["payload"]); 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 { else {
Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>()); Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>());
} }
@ -113,12 +145,12 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei // Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
String loadHtmlWithHeader(const char* filename) { String loadHtmlWithHeader(const char* filename) {
Serial.println("Lade HTML-Datei: " + String(filename)); Serial.println("Lade HTML-Datei: " + String(filename));
if (!LittleFS.exists(filename)) { if (!SPIFFS.exists(filename)) {
Serial.println("Fehler: Datei nicht gefunden!"); Serial.println("Fehler: Datei nicht gefunden!");
return "Fehler: Datei nicht gefunden!"; return "Fehler: Datei nicht gefunden!";
} }
File file = LittleFS.open(filename, "r"); File file = SPIFFS.open(filename, "r");
String html = file.readString(); String html = file.readString();
file.close(); file.close();
@ -175,6 +207,105 @@ 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) { void setupWebserver(AsyncWebServer &server) {
// Deaktiviere alle Debug-Ausgaben // Deaktiviere alle Debug-Ausgaben
Serial.setDebugOutput(false); Serial.setDebugOutput(false);
@ -195,7 +326,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für about // Route für about
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /about erhalten"); Serial.println("Anfrage für /about erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/index.html.gz", "text/html"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -204,7 +335,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für Waage // Route für Waage
server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/waage", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /waage erhalten"); Serial.println("Anfrage für /waage erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/waage.html.gz", "text/html"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/waage.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -213,7 +344,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für RFID // Route für RFID
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /rfid erhalten"); Serial.println("Anfrage für /rfid erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/rfid.html.gz", "text/html"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/rfid.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -229,7 +360,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für WiFi // Route für WiFi
server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /wifi erhalten"); Serial.println("Anfrage für /wifi erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/wifi.html.gz", "text/html"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/wifi.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -239,10 +370,7 @@ void setupWebserver(AsyncWebServer &server) {
server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /spoolman erhalten"); Serial.println("Anfrage für /spoolman erhalten");
String html = loadHtmlWithHeader("/spoolman.html"); String html = loadHtmlWithHeader("/spoolman.html");
html.replace("{{spoolmanUrl}}", (spoolmanUrl != "") ? spoolmanUrl : ""); html.replace("{{spoolmanUrl}}", spoolmanUrl);
html.replace("{{spoolmanOctoEnabled}}", octoEnabled ? "checked" : "");
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
JsonDocument doc; JsonDocument doc;
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>())
@ -259,7 +387,6 @@ void setupWebserver(AsyncWebServer &server) {
html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : ""); html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : "");
html.replace("{{bambuCode}}", bambuCode ? bambuCode : ""); html.replace("{{bambuCode}}", bambuCode ? bambuCode : "");
html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : ""); html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : "");
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
} }
else else
{ {
@ -267,7 +394,6 @@ void setupWebserver(AsyncWebServer &server) {
html.replace("{{bambuSerial}}", ""); html.replace("{{bambuSerial}}", "");
html.replace("{{bambuCode}}", ""); html.replace("{{bambuCode}}", "");
html.replace("{{autoSendToBambu}}", ""); html.replace("{{autoSendToBambu}}", "");
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
} }
request->send(200, "text/html", html); request->send(200, "text/html", html);
@ -280,21 +406,10 @@ void setupWebserver(AsyncWebServer &server) {
return; return;
} }
if (request->getParam("octoEnabled")->value() == "true" && (!request->hasParam("octoUrl") || !request->hasParam("octoToken"))) {
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing OctoPrint URL or Token parameter\"}");
return;
}
String url = request->getParam("url")->value(); String url = request->getParam("url")->value();
bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false;
String octoUrl = request->getParam("octoUrl")->value();
String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : "";
url.trim(); url.trim();
octoUrl.trim();
octoToken.trim();
bool healthy = saveSpoolmanUrl(url, octoEnabled, octoUrl, octoToken); bool healthy = saveSpoolmanUrl(url);
String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}"; String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}";
request->send(200, "application/json", jsonResponse); request->send(200, "application/json", jsonResponse);
@ -311,19 +426,17 @@ void setupWebserver(AsyncWebServer &server) {
String bambu_serialnr = request->getParam("bambu_serialnr")->value(); String bambu_serialnr = request->getParam("bambu_serialnr")->value();
String bambu_accesscode = request->getParam("bambu_accesscode")->value(); String bambu_accesscode = request->getParam("bambu_accesscode")->value();
bool autoSend = (request->getParam("autoSend")->value() == "true") ? true : false; bool autoSend = (request->getParam("autoSend")->value() == "true") ? true : false;
String autoSendTime = request->getParam("autoSendTime")->value(); Serial.println(autoSend);
bambu_ip.trim(); bambu_ip.trim();
bambu_serialnr.trim(); bambu_serialnr.trim();
bambu_accesscode.trim(); bambu_accesscode.trim();
autoSendTime.trim();
if (bambu_ip.length() == 0 || bambu_serialnr.length() == 0 || bambu_accesscode.length() == 0) { if (bambu_ip.length() == 0 || bambu_serialnr.length() == 0 || bambu_accesscode.length() == 0) {
request->send(400, "application/json", "{\"success\": false, \"error\": \"Empty parameter\"}"); request->send(400, "application/json", "{\"success\": false, \"error\": \"Empty parameter\"}");
return; return;
} }
bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend, autoSendTime); bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend);
request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}");
}); });
@ -336,7 +449,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für das Laden der CSS-Datei // Route für das Laden der CSS-Datei
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Lade style.css"); Serial.println("Lade style.css");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/style.css.gz", "text/css"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/style.css.gz", "text/css");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -345,7 +458,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für das Logo // Route für das Logo
server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/logo.png.gz", "image/png"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/logo.png.gz", "image/png");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -354,7 +467,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für Favicon // Route für Favicon
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/favicon.ico", "image/x-icon"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/favicon.ico", "image/x-icon");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
Serial.println("favicon.ico gesendet"); Serial.println("favicon.ico gesendet");
@ -362,26 +475,17 @@ void setupWebserver(AsyncWebServer &server) {
// Route für spool_in.png // Route für spool_in.png
server.on("/spool_in.png", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/spool_in.png", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spool_in.png.gz", "image/png"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spool_in.png.gz", "image/png");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
Serial.println("spool_in.png gesendet"); 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 // Route für JavaScript Dateien
server.on("/spoolman.js", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/spoolman.js", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /spoolman.js erhalten"); Serial.println("Anfrage für /spoolman.js erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/spoolman.js.gz", "text/javascript"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/spoolman.js.gz", "text/javascript");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -390,7 +494,7 @@ void setupWebserver(AsyncWebServer &server) {
server.on("/rfid.js", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/rfid.js", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /rfid.js erhalten"); Serial.println("Anfrage für /rfid.js erhalten");
AsyncWebServerResponse *response = request->beginResponse(LittleFS,"/rfid.js.gz", "text/javascript"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS,"/rfid.js.gz", "text/javascript");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL); response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response); request->send(response);
@ -399,7 +503,7 @@ void setupWebserver(AsyncWebServer &server) {
// Vereinfachter Update-Handler // Vereinfachter Update-Handler
server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/upgrade.html.gz", "text/html"); AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/upgrade.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", "no-store"); response->addHeader("Cache-Control", "no-store");
request->send(response); request->send(response);
@ -430,3 +534,50 @@ void setupWebserver(AsyncWebServer &server) {
server.begin(); server.begin();
Serial.println("Webserver gestartet"); 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
}
}

View File

@ -19,6 +19,7 @@ extern AsyncWebSocket ws;
// Server-Initialisierung und Handler // Server-Initialisierung und Handler
void initWebServer(); 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 handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total);
void setupWebserver(AsyncWebServer &server); void setupWebserver(AsyncWebServer &server);
@ -28,4 +29,8 @@ void sendNfcData(AsyncWebSocketClient *client);
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success); void foundNfcTag(AsyncWebSocketClient *client, uint8_t success);
void sendWriteResult(AsyncWebSocketClient *client, uint8_t success); void sendWriteResult(AsyncWebSocketClient *client, uint8_t success);
// Upgrade-Funktionen
void backupJsonConfigs();
void restoreJsonConfigs();
#endif #endif

View File

@ -3,20 +3,16 @@
#include <WiFi.h> #include <WiFi.h>
#include <esp_wifi.h> #include <esp_wifi.h>
#include <WiFiManager.h> #include <WiFiManager.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include "display.h" #include "display.h"
#include "config.h" #include "config.h"
WiFiManager wm; WiFiManager wm;
bool wm_nonblocking = false; bool wm_nonblocking = false;
uint8_t wifiErrorCounter = 0;
void wifiSettings() { void initWiFi() {
// Optimierte WiFi-Einstellungen // Optimierte WiFi-Einstellungen
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
WiFi.setSleep(false); // disable sleep mode WiFi.setSleep(false); // disable sleep mode
WiFi.setHostname("FilaMan");
esp_wifi_set_ps(WIFI_PS_NONE); esp_wifi_set_ps(WIFI_PS_NONE);
// Maximale Sendeleistung // Maximale Sendeleistung
@ -27,103 +23,33 @@ void wifiSettings() {
// Aktiviere WiFi-Roaming für bessere Stabilität // Aktiviere WiFi-Roaming für bessere Stabilität
esp_wifi_set_rssi_threshold(-80); esp_wifi_set_rssi_threshold(-80);
}
void startMDNS() { if(wm_nonblocking) wm.setConfigPortalBlocking(false);
if (!MDNS.begin("filaman")) { wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
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(); oledShowTopRow();
display.display(); oledShowMessage("WiFi Setup");
vTaskDelay(500 / portTICK_PERIOD_MS); 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
// mDNS if(!res) {
startMDNS(); Serial.println("Failed to connect or hit timeout");
} // ESP.restart();
}
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(); oledShowTopRow();
startMDNS(); 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();
} }
} }
if (wifiErrorCounter >= 5)
{
Serial.println("Too many WiFi errors. Restarting...");
ESP.restart();
}
}

View File

@ -4,6 +4,5 @@
#include <Arduino.h> #include <Arduino.h>
void initWiFi(); void initWiFi();
void checkWiFiConnection();
#endif #endif