Compare commits

..

19 Commits

Author SHA1 Message Date
8c88827a2d docs: update changelog for version 1.2.11
Some checks failed
Release / prepare (push) Successful in 8s
Release / github-release (push) Has been skipped
Release / gitea-release (push) Failing after 3s
2025-02-18 23:50:00 +01:00
6128bc2827 feat: update version to 1.2.11 and enhance GitHub and Gitea release workflows with input validation and improved error handling 2025-02-18 23:49:57 +01:00
6d21a8ec52 docs: update changelog for version 1.2.10 2025-02-18 23:31:16 +01:00
2ee4eca7dd feat: enhance Gitea release workflow with API connection verification and URL validation; update version to 1.2.10 2025-02-18 23:31:00 +01:00
9eb64e06f0 docs: update changelog for version 1.2.9
Some checks failed
Release / release (push) Failing after 2m37s
2025-02-18 23:20:06 +01:00
693ef4e26c feat: update version to 1.2.9 in platformio.ini 2025-02-18 23:20:03 +01:00
11c099eba2 feat: refactor GitHub and Gitea release workflows for improved version handling and firmware uploads 2025-02-18 23:14:52 +01:00
c7c122a505 docs: update changelog for version 1.2.8
Some checks failed
GitHub Release / build (push) Has been cancelled
Release / detect (push) Successful in 7s
Release / gitea-release (push) Failing after 2m22s
Release / github-release (push) Has been skipped
2025-02-18 22:32:11 +01:00
d1bb4b7c83 feat: update version to 1.2.8 in platformio.ini; refactor Gitea and GitHub release workflows 2025-02-18 22:32:07 +01:00
dc5118587c feat: update version to 1.2.7 in platformio.ini; adjust Gitea release workflow 2025-02-18 22:28:31 +01:00
b13f2c4eee docs: update changelog for version 1.2.6
Some checks failed
Gitea Release / build (push) Failing after 0s
GitHub Release / build (push) Failing after 2m41s
Release / detect-and-run (push) Failing after 17s
2025-02-18 22:23:50 +01:00
6d16c35e62 feat: update version to 1.2.6 in platformio.ini 2025-02-18 22:23:45 +01:00
c94ebf5e33 docs: update changelog for version 1.2.5 2025-02-18 22:18:51 +01:00
9203706a20 feat: update version to 1.2.5; enhance Gitea release workflow and streamline release process 2025-02-18 22:18:49 +01:00
c252f48a49 docs: update changelog for version 1.2.4
Some checks failed
GitHub Release / build (push) Has been cancelled
Release / detect-and-run (push) Failing after 0s
Gitea Release / build (push) Failing after 2m48s
2025-02-18 21:56:09 +01:00
228dcacb1e feat: update version to 1.2.4 in HTML files and platformio.ini 2025-02-18 21:56:05 +01:00
9de4ed9ee9 feat: add GitHub and Gitea release workflows; streamline firmware release process 2025-02-18 21:53:23 +01:00
4d8a6fb943 docs: update changelog for version 1.2.3
Some checks failed
Create Release / build (push) Failing after 5m9s
2025-02-18 14:40:02 +01:00
2cab24403e feat: update version to 1.2.3; modify HTML files to reflect new version; enhance firmware update process and UI improvements 2025-02-18 14:18:14 +01:00
17 changed files with 400 additions and 315 deletions

View File

@ -0,0 +1,114 @@
name: Gitea Release
on:
workflow_call:
inputs:
version:
required: true
type: string
changelog:
required: true
type: string
permissions:
contents: write # Required for creating releases
issues: read # Required for reading changelog
pull-requests: read # Required for reading changelog
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio esptool
sudo apt-get update
sudo apt-get install -y jq
- name: Build Firmware
run: |
pio run -t buildfs
pio run
- name: Merge firmware and SPIFFS
run: |
esptool.py --chip esp32 merge_bin \
--flash_mode dio \
--flash_freq 40m \
--flash_size 4MB \
-o .pio/build/esp32dev/filaman_full.bin \
0x1000 .pio/build/esp32dev/bootloader.bin \
0x8000 .pio/build/esp32dev/partitions.bin \
0x10000 .pio/build/esp32dev/firmware.bin \
0x290000 .pio/build/esp32dev/spiffs.bin
- name: Prepare OTA firmware
run: cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/filaman_ota.bin
- name: Create Release
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_API_URL: ${{ secrets.GITEA_API_URL }}
GITEA_REPOSITORY: ${{ secrets.GITEA_REPOSITORY }}
run: |
# Validate and sanitize API URL
if [[ ! "$GITEA_API_URL" =~ ^https?:// ]]; then
echo "Error: GITEA_API_URL must start with http:// or https://"
exit 1
fi
# Remove trailing slash and ensure /api/v1
GITEA_API_URL="${GITEA_API_URL%/}"
if [[ ! "$GITEA_API_URL" =~ /api/v1$ ]]; then
GITEA_API_URL="${GITEA_API_URL}/api/v1"
fi
echo "Creating Gitea release..."
RESPONSE=$(curl -sS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
"${GITEA_API_URL}/repos/${GITEA_REPOSITORY}/releases" \
-d @- << EOF
{
"tag_name": "${{ github.ref_name }}",
"name": "Release ${{ inputs.version }}",
"body": ${{ toJSON(inputs.changelog) }},
"draft": false,
"prerelease": false
}
EOF
)
# Extract and validate release ID
RELEASE_ID=$(echo "$RESPONSE" | jq -r .id)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
echo "Error: Failed to get release ID"
echo "API Response:"
echo "$RESPONSE" | jq .
exit 1
fi
# Upload binary files
for file in "filaman_full.bin" "filaman_ota.bin"; do
echo "Uploading $file..."
if ! curl -sS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
"${GITEA_API_URL}/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${file}" \
--data-binary "@.pio/build/esp32dev/${file}"; then
echo "Error: Failed to upload $file"
exit 1
fi
echo "Successfully uploaded $file"
done

View File

@ -0,0 +1,64 @@
name: GitHub Release
on:
workflow_call:
inputs:
version:
required: true
type: string
changelog:
required: true
type: string
permissions:
contents: write # Required for creating releases
issues: read # Required for reading changelog
pull-requests: read # Required for reading changelog
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write # Required for creating releases at job level
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio esptool
- name: Build Firmware
run: |
pio run -t buildfs # Build SPIFFS
pio run # Build firmware
- name: Merge firmware and SPIFFS
run: |
esptool.py --chip esp32 merge_bin \
--flash_mode dio \
--flash_freq 40m \
--flash_size 4MB \
-o .pio/build/esp32dev/filaman_full.bin \
0x1000 .pio/build/esp32dev/bootloader.bin \
0x8000 .pio/build/esp32dev/partitions.bin \
0x10000 .pio/build/esp32dev/firmware.bin \
0x290000 .pio/build/esp32dev/spiffs.bin
- name: Prepare OTA firmware
run: cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/filaman_ota.bin
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ github.ref_name }}" \
--title "Release ${{ inputs.version }}" \
--notes "${{ inputs.changelog }}" \
".pio/build/esp32dev/filaman_full.bin#filaman_full.bin" \
".pio/build/esp32dev/filaman_ota.bin#filaman_ota.bin"

View File

@ -1,85 +1,68 @@
name: Create Release
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write # Required for creating releases
issues: read # Required for reading changelog
pull-requests: read # Required for reading changelog
jobs:
build:
prepare:
runs-on: ubuntu-latest
permissions:
contents: write # Required for creating releases at job level
outputs:
version: ${{ steps.get_version.outputs.VERSION }}
changelog: ${{ steps.changelog.outputs.CHANGES }}
provider: ${{ steps.detect-provider.outputs.provider }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Build Firmware
run: |
pio run -t buildfs # Build SPIFFS
pio run # Build firmware
- name: Install esptool
run: |
pip install esptool
- name: Merge firmware and SPIFFS
run: |
esptool.py --chip esp32 merge_bin \
--flash_mode dio \
--flash_freq 40m \
--flash_size 4MB \
-o .pio/build/esp32dev/filaman_full.bin \
0x1000 .pio/build/esp32dev/bootloader.bin \
0x8000 .pio/build/esp32dev/partitions.bin \
0x10000 .pio/build/esp32dev/firmware.bin \
0x290000 .pio/build/esp32dev/spiffs.bin
- name: Prepare OTA firmware
run: |
# Use PlatformIO to create a proper OTA image
cp .pio/build/esp32dev/firmware.bin .pio/build/esp32dev/filaman_ota.bin
- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
- name: Read CHANGELOG.md
id: changelog
run: |
CHANGELOG=$(awk "/## \\[${{ steps.get_version.outputs.VERSION }}\\]/{p=1;print;next} /## \\[/{p=0} p" CHANGELOG.md)
echo "CHANGES<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Install and Configure GitHub CLI
run: |
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
- name: Create Release with GitHub CLI
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ github.ref_name }}" \
--title "Release ${{ steps.get_version.outputs.VERSION }}" \
--notes "${{ steps.changelog.outputs.CHANGES }}" \
".pio/build/esp32dev/filaman_full.bin#filaman_full.bin" \
".pio/build/esp32dev/filaman_ota.bin#filaman_ota.bin"
- name: Detect provider
id: detect-provider
run: |
if [[ "$GITHUB_SERVER_URL" == "https://github.com" ]]; then
echo "provider=github" >> $GITHUB_OUTPUT
else
echo "provider=gitea" >> $GITHUB_OUTPUT
fi
- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Verify version match
run: |
PIO_VERSION=$(grep '^version = ' platformio.ini | cut -d'"' -f2)
TAG_VERSION=${{ steps.get_version.outputs.VERSION }}
echo "PlatformIO version: $PIO_VERSION"
echo "Tag version: $TAG_VERSION"
if [ "$PIO_VERSION" != "$TAG_VERSION" ]; then
echo "Error: Version mismatch between tag ($TAG_VERSION) and platformio.ini ($PIO_VERSION)"
exit 1
fi
- name: Read CHANGELOG.md
id: changelog
run: |
CHANGELOG=$(awk "/## \\[${{ steps.get_version.outputs.VERSION }}\\]/{p=1;print;next} /## \\[/{p=0} p" CHANGELOG.md)
echo "CHANGES<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
github-release:
needs: prepare
if: needs.prepare.outputs.provider == 'github'
uses: ./.github/workflows/providers/github-release.yml
with:
version: ${{ needs.prepare.outputs.version }}
changelog: ${{ needs.prepare.outputs.changelog }}
secrets: inherit
gitea-release:
needs: prepare
if: needs.prepare.outputs.provider == 'gitea'
uses: ./.github/workflows/providers/gitea-release.yml
with:
version: ${{ needs.prepare.outputs.version }}
changelog: ${{ needs.prepare.outputs.changelog }}
secrets: inherit

View File

@ -1,5 +1,52 @@
# Changelog
## [1.2.11] - 2025-02-18
### Added
- update version to 1.2.11 and enhance GitHub and Gitea release workflows with input validation and improved error handling
## [1.2.10] - 2025-02-18
### Added
- enhance Gitea release workflow with API connection verification and URL validation; update version to 1.2.10
## [1.2.9] - 2025-02-18
### Added
- update version to 1.2.9 in platformio.ini
- refactor GitHub and Gitea release workflows for improved version handling and firmware uploads
## [1.2.8] - 2025-02-18
### Added
- update version to 1.2.8 in platformio.ini; refactor Gitea and GitHub release workflows
- update version to 1.2.7 in platformio.ini; adjust Gitea release workflow
## [1.2.6] - 2025-02-18
### Added
- update version to 1.2.6 in platformio.ini
- update version to 1.2.5; enhance Gitea release workflow and streamline release process
### Changed
- update changelog for version 1.2.5
## [1.2.5] - 2025-02-18
### Added
- update version to 1.2.5; enhance Gitea release workflow and streamline release process
## [1.2.4] - 2025-02-18
### Added
- update version to 1.2.4 in HTML files and platformio.ini
- add GitHub and Gitea release workflows; streamline firmware release process
## [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

@ -124,6 +124,24 @@ german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62z
- Configure WiFi settings through the captive portal.
- Access the web interface at `http://filaman.local` or the IP address.
## GitHub Actions Configuration
### Required Secrets for Gitea Releases
When using Gitea as your repository host, you need to configure the following secrets in your repository:
- `GITEA_API_URL`: The base URL of your Gitea instance, including protocol (e.g., `https://git.example.com`)
- `GITEA_TOKEN`: Your Gitea access token with permissions to create releases
- `GITEA_REPOSITORY`: The repository name in format `owner/repo` (e.g., `username/filaman`)
Example values:
```
GITEA_API_URL=https://git.example.com
GITEA_TOKEN=abcdef1234567890
GITEA_REPOSITORY=username/filaman
```
Make sure to set these secrets in your repository settings under Settings > Secrets and Variables > Actions.
## Documentation

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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>

View File

@ -1056,6 +1056,7 @@ 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;
@ -1063,6 +1064,7 @@ input[type="submit"]:disabled,
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.update-form input[type="submit"] {
background-color: #4CAF50;
@ -1072,6 +1074,7 @@ 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;
@ -1083,8 +1086,10 @@ input[type="submit"]:disabled,
.warning {
background-color: var(--primary-color);
border: 1px solid #ffe0b2;
color: #e65100;
color: white;
padding: 15px;
margin: 20px 0;
margin: 20px auto;
border-radius: 4px;
max-width: 600px;
text-align: center;
}

View File

@ -1,4 +1,3 @@
<!-- head --><!DOCTYPE html>
<html lang="en">
<head>
@ -13,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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</span></h1>
<h4>Filament Management Tool</h4>
</div>
</div>
@ -42,14 +41,13 @@
<div class="warning">
<strong>Warning:</strong> Please do not turn off or restart the device during the update.
Configuration files will be automatically backed up and restored after the update.
The device will restart automatically after the update.
</div>
<div class="update-form" style="align-items: center;">
<form id="updateForm" enctype='multipart/form-data' style="text-align: center;"></form>
<div class="update-form">
<form id="updateForm" enctype='multipart/form-data'>
<input type='file' name='update' accept='.bin' required>
<input type='submit' value='Firmware Update starten'>
<input type='submit' value='Start Firmware Update'>
</form>
</div>
@ -60,10 +58,21 @@
</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);
@ -91,21 +100,24 @@
try {
let response = this.responseText;
try {
// Versuche als JSON zu parsen
const jsonResponse = JSON.parse(response);
response = jsonResponse.message;
if (jsonResponse.restart) {
// Wenn Gerät neustartet, warte und lade dann neu
status.textContent = response + " Weiterleitung in 5 Sekunden...";
setTimeout(() => {
window.location.href = '/';
}, 5000);
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);
}
} 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 + '%';
@ -121,7 +133,7 @@
form.querySelector('input[type=submit]').disabled = false;
}
} catch (error) {
status.textContent = 'Fehler: ' + error.message;
status.textContent = 'Error: ' + error.message;
status.classList.add('error');
status.style.display = 'block';
form.querySelector('input[type=submit]').disabled = false;
@ -129,7 +141,7 @@
};
xhr.onerror = function() {
status.textContent = 'Update fehlgeschlagen: Netzwerkfehler';
status.textContent = 'Update failed: Network error';
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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</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.1</span></h1>
<h1>FilaMan<span class="version">v1.2.4</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, factory, 0x10000, 0x1E0000,
app1, app, ota_0, 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, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 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 factory ota_0 0x10000 0x1E0000
5 app1 app ota_0 ota_1 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.2"
version = "1.2.11"
[env:esp32dev]
platform = espressif32
@ -52,6 +52,13 @@ 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,219 +3,44 @@
#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>
// Reduzierter Puffer für Dateioperationen
const size_t BUFFER_SIZE = 128;
const size_t MIN_FREE_HEAP = 32768; // 32KB minimum free heap
// 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;
}
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;
static size_t contentLength = 0;
// Send progress update
if (!final) {
int progress = (totalBytes * 100) / totalSize;
request->send(200, "text/plain", String(progress));
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;
}
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");
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Error writing update\"}");
return;
}
if (final) {
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);
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();
return;
} else {
Update.printError(Serial);
request->send(400, "application/json", "{\"status\":\"error\",\"message\":\"Update failed\"}");
}
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, "/about.html.gz", "text/html");
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.html.gz", "text/html");
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", CACHE_CONTROL);
request->send(response);
@ -338,8 +338,13 @@ void setupWebserver(AsyncWebServer &server) {
Serial.println("RFID.js gesendet");
});
// Route für Firmware Update
// Route for 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);
@ -350,7 +355,12 @@ void setupWebserver(AsyncWebServer &server) {
[](AsyncWebServerRequest *request) {
// The response will be sent from handleOTAUpload when the upload is complete
},
handleOTAUpload
[](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);
}
);
// Fehlerbehandlung für nicht gefundene Seiten