Compare commits

..

No commits in common. "4d8a6fb943ef18532d144d0d01c2324142f2de5d" and "8b246e180b281c5901c817df4c41e7a393be61a1" have entirely different histories.

13 changed files with 239 additions and 103 deletions

View File

@ -1,10 +1,5 @@
# Changelog
## [1.2.3] - 2025-02-18
### Added
- update version to 1.2.3; modify HTML files to reflect new version; enhance firmware update process and UI improvements
## [1.2.2] - 2025-02-18
### Added
- update version to 1.2.2; change OTA upgrade link in HTML files; enhance OTA upload handling with progress updates and JSON responses

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -1056,7 +1056,6 @@ input[type="submit"]:disabled,
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin: 0 auto;
width: 400px;
text-align: center;
}
.update-form input[type="file"] {
margin-bottom: 15px;
@ -1064,7 +1063,6 @@ input[type="submit"]:disabled,
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.update-form input[type="submit"] {
background-color: #4CAF50;
@ -1074,7 +1072,6 @@ input[type="submit"]:disabled,
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
.update-form input[type="submit"]:hover {
background-color: #45a049;
@ -1086,10 +1083,8 @@ input[type="submit"]:disabled,
.warning {
background-color: var(--primary-color);
border: 1px solid #ffe0b2;
color: white;
color: #e65100;
padding: 15px;
margin: 20px auto;
margin: 20px 0;
border-radius: 4px;
max-width: 600px;
text-align: center;
}

View File

@ -1,3 +1,4 @@
<!-- head --><!DOCTYPE html>
<html lang="en">
<head>
@ -12,7 +13,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>
@ -41,13 +42,14 @@
<div class="warning">
<strong>Warning:</strong> Please do not turn off or restart the device during the update.
The device will restart automatically after the update.
Configuration files will be automatically backed up and restored after the update.
</div>
<div class="update-form">
<form id="updateForm" enctype='multipart/form-data'>
<div class="update-form" style="align-items: center;">
<form id="updateForm" enctype='multipart/form-data' style="text-align: center;"></form>
<input type='file' name='update' accept='.bin' required>
<input type='submit' value='Start Firmware Update'>
<input type='submit' value='Firmware Update starten'>
</form>
</div>
@ -58,21 +60,10 @@
</div>
<script>
// Hide status indicators during update
const statusContainer = document.querySelector('.status-container');
if (statusContainer) {
statusContainer.style.display = 'none';
}
document.getElementById('updateForm').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const file = form.update.files[0];
if (!file) {
alert('Please select a firmware file.');
return;
}
const formData = new FormData();
formData.append('update', file);
@ -100,24 +91,21 @@
try {
let response = this.responseText;
try {
// Versuche als JSON zu parsen
const jsonResponse = JSON.parse(response);
response = jsonResponse.message;
if (jsonResponse.restart) {
status.textContent = response + " Redirecting in 20 seconds...";
let countdown = 20;
const timer = setInterval(() => {
countdown--;
if (countdown <= 0) {
clearInterval(timer);
window.location.href = '/';
} else {
status.textContent = response + ` Redirecting in ${countdown} seconds...`;
}
}, 1000);
// Wenn Gerät neustartet, warte und lade dann neu
status.textContent = response + " Weiterleitung in 5 Sekunden...";
setTimeout(() => {
window.location.href = '/';
}, 5000);
}
} catch (e) {
// Wenn kein JSON, nutze response als Text
if (!isNaN(response)) {
// Wenn es eine Zahl ist, update den Fortschritt
const percent = parseInt(response);
progress.style.width = percent + '%';
progress.textContent = percent + '%';
@ -133,7 +121,7 @@
form.querySelector('input[type=submit]').disabled = false;
}
} catch (error) {
status.textContent = 'Error: ' + error.message;
status.textContent = 'Fehler: ' + error.message;
status.classList.add('error');
status.style.display = 'block';
form.querySelector('input[type=submit]').disabled = false;
@ -141,7 +129,7 @@
};
xhr.onerror = function() {
status.textContent = 'Update failed: Network error';
status.textContent = 'Update fehlgeschlagen: Netzwerkfehler';
status.classList.add('error');
status.style.display = 'block';
form.querySelector('input[type=submit]').disabled = false;

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div style="display: flex; align-items: center; gap: 2rem;">
<img src="/logo.png" alt="FilaMan Logo" class="logo">
<div class="logo-text">
<h1>FilaMan<span class="version">v1.2.2</span></h1>
<h1>FilaMan<span class="version">v1.2.1</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -1,6 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 0x1F0000, 0x1E0000,
spiffs, data, spiffs, 0x3D0000, 0x30000,
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, factory, 0x10000, 0x1E0000,
app1, app, ota_0, 0x1F0000, 0x1E0000,
spiffs, data, spiffs, 0x3D0000, 0x30000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 factory 0x10000 0x1E0000
5 app1 app ota_1 ota_0 0x1F0000 0x1E0000
6 spiffs data spiffs 0x3D0000 0x30000

View File

@ -9,7 +9,7 @@
; https://docs.platformio.org/page/projectconf.html
[common]
version = "1.2.3"
version = "1.2.2"
[env:esp32dev]
platform = espressif32
@ -52,13 +52,6 @@ build_flags =
-DARDUINO_EVENT_RUNNING_CORE=1
-DCONFIG_OPTIMIZATION_LEVEL_DEBUG=1
-DCONFIG_ESP32_PANIC_PRINT_REBOOT
-DCONFIG_ARDUINO_OTA_READSIZE=1024
-DCONFIG_ASYNC_TCP_RUNNING_CORE=1
-DCONFIG_ASYNC_TCP_USE_WDT=0
-DCONFIG_LWIP_TCP_MSS=1460
-DOTA_PARTITION_SUBTYPE=0x10
-DPARTITION_TABLE_OFFSET=0x8000
-DPARTITION_TABLE_SIZE=0x1000
extra_scripts =
scripts/extra_script.py

View File

@ -3,44 +3,219 @@
#include <Update.h>
#include <SPIFFS.h>
#include "commonFS.h"
#include <esp_task_wdt.h>
#include <esp_int_wdt.h>
#include <esp_pthread.h>
#include <esp_ota_ops.h>
void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
static size_t contentLength = 0;
if (!index) {
contentLength = request->contentLength();
Serial.printf("Update size: %u bytes\n", contentLength);
if (contentLength == 0) {
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Invalid file size\"}");
return;
}
// Reduzierter Puffer für Dateioperationen
const size_t BUFFER_SIZE = 128;
const size_t MIN_FREE_HEAP = 32768; // 32KB minimum free heap
if (!Update.begin(contentLength)) {
Serial.printf("Not enough space: %u required\n", contentLength);
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Not enough space available\"}");
return;
}
Serial.println("Update started");
// Files to backup before update
const char* CONFIG_FILES[] = {
"/bambu_credentials.json",
"/spoolman_url.json"
};
const int CONFIG_FILES_COUNT = sizeof(CONFIG_FILES) / sizeof(CONFIG_FILES[0]);
bool backupConfigs() {
if (!SPIFFS.begin(true)) {
Serial.println("Failed to mount SPIFFS");
return false;
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Error writing update\"}");
for (int i = 0; i < CONFIG_FILES_COUNT; i++) {
if (SPIFFS.exists(CONFIG_FILES[i])) {
String backupFile = String(CONFIG_FILES[i]) + ".bak";
if (SPIFFS.exists(backupFile)) {
SPIFFS.remove(backupFile);
}
File sourceFile = SPIFFS.open(CONFIG_FILES[i], "r");
File destFile = SPIFFS.open(backupFile, "w");
if (!sourceFile || !destFile) {
Serial.printf("Failed to open files for backup: %s\n", CONFIG_FILES[i]);
return false;
}
// Verwenden Sie einen kleineren Puffer
uint8_t* buf = (uint8_t*)malloc(BUFFER_SIZE);
if (!buf) {
Serial.println("Failed to allocate buffer");
sourceFile.close();
destFile.close();
return false;
}
size_t len = 0;
bool success = true;
while ((len = sourceFile.read(buf, BUFFER_SIZE)) > 0) {
if (destFile.write(buf, len) != len) {
Serial.println("Write failed");
success = false;
break;
}
}
free(buf);
sourceFile.close();
destFile.close();
if (!success) {
return false;
}
}
}
return true;
}
bool restoreConfigs() {
if (!SPIFFS.begin(true)) {
Serial.println("Failed to mount SPIFFS");
return false;
}
bool success = true;
for (int i = 0; i < CONFIG_FILES_COUNT; i++) {
String backupFile = String(CONFIG_FILES[i]) + ".bak";
if (SPIFFS.exists(backupFile)) {
if (SPIFFS.exists(CONFIG_FILES[i])) {
SPIFFS.remove(CONFIG_FILES[i]);
}
File sourceFile = SPIFFS.open(backupFile, "r");
File destFile = SPIFFS.open(CONFIG_FILES[i], "w");
if (!sourceFile || !destFile) {
Serial.printf("Failed to open files for restore: %s\n", CONFIG_FILES[i]);
success = false;
continue;
}
// Verwenden Sie einen kleineren Puffer
uint8_t* buf = (uint8_t*)malloc(BUFFER_SIZE);
if (!buf) {
Serial.println("Failed to allocate buffer");
sourceFile.close();
destFile.close();
success = false;
continue;
}
size_t len = 0;
while ((len = sourceFile.read(buf, BUFFER_SIZE)) > 0) {
if (destFile.write(buf, len) != len) {
Serial.println("Write failed");
success = false;
break;
}
}
free(buf);
sourceFile.close();
destFile.close();
SPIFFS.remove(backupFile);
}
}
return success;
}
void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
static bool updateStarted = false;
static size_t totalBytes = 0;
static size_t totalSize = 0;
if (!index) {
updateStarted = false;
totalBytes = 0;
totalSize = request->contentLength();
// Check minimum heap size
if (ESP.getFreeHeap() < MIN_FREE_HEAP) {
request->send(500, "text/plain", "Not enough memory available");
return;
}
if (totalSize == 0) {
request->send(400, "text/plain", "Invalid file size");
return;
}
Serial.printf("Update size: %u bytes\n", totalSize);
Serial.printf("Free space: %u bytes\n", ESP.getFreeSketchSpace());
Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
// Backup configs first
if (!backupConfigs()) {
request->send(500, "text/plain", "Failed to backup configuration");
return;
}
// Close all files and unmount SPIFFS
SPIFFS.end();
// Begin update with minimal configuration
if (!Update.begin(totalSize)) {
Serial.printf("Update.begin failed: %s\n", Update.errorString());
request->send(500, "text/plain", "Failed to start update");
return;
}
updateStarted = true;
Serial.println("Update process started");
// Send initial progress
request->send(200, "text/plain", "0");
}
if (!updateStarted) {
request->send(500, "text/plain", "Update not properly started");
return;
}
// Write update data
if (Update.write(data, len) != len) {
Serial.printf("Update.write failed: %s\n", Update.errorString());
Update.abort();
request->send(500, "text/plain", "Error during update");
return;
}
totalBytes += len;
// Send progress update
if (!final) {
int progress = (totalBytes * 100) / totalSize;
request->send(200, "text/plain", String(progress));
}
if (final) {
if (Update.end(true)) {
Serial.println("Update complete");
request->send(200, "application/json", "{\"status\":\"success\",\"message\":\"Update successful! Device will restart...\",\"restart\":true}");
delay(1000);
ESP.restart();
} else {
Update.printError(Serial);
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Update failed\"}");
if (!Update.end(true)) {
Serial.printf("Update.end failed: %s\n", Update.errorString());
request->send(500, "text/plain", "Update failed");
return;
}
// Try to restore configs
if (!SPIFFS.begin(true)) {
Serial.println("Failed to mount SPIFFS for restore");
request->send(200, "application/json", "{\"status\": \"success\", \"message\": \"Update successful but config restore failed. Device will restart...\", \"restart\": true}");
delay(2000);
ESP.restart();
return;
}
if (!restoreConfigs()) {
Serial.println("Failed to restore configs");
}
request->send(200, "application/json", "{\"status\": \"success\", \"message\": \"Update successful! Device will restart...\", \"restart\": true}");
delay(2000);
ESP.restart();
}
}

View File

@ -164,7 +164,7 @@ void setupWebserver(AsyncWebServer &server) {
// Route für about
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /about erhalten");
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html.gz", "text/html");
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/about.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response);
@ -338,13 +338,8 @@ void setupWebserver(AsyncWebServer &server) {
Serial.println("RFID.js gesendet");
});
// Route for Firmware Update
// Route für Firmware Update
server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest *request) {
// During OTA, reduce memory usage
ws.enable(false); // Temporarily disable WebSocket
ws.cleanupClients();
Serial.println("Request for /upgrade received");
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/upgrade.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL);
@ -355,12 +350,7 @@ void setupWebserver(AsyncWebServer &server) {
[](AsyncWebServerRequest *request) {
// The response will be sent from handleOTAUpload when the upload is complete
},
[](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
// Free memory before handling update
ws.enable(false);
ws.cleanupClients();
handleOTAUpload(request, filename, index, data, len, final);
}
handleOTAUpload
);
// Fehlerbehandlung für nicht gefundene Seiten