4.1 KiB
Frontend OTA Strategy for ESP32-S3 Provider
Authored by Antigravity Date: 2026-03-03
1. Goal
Implement a robust Over-The-Air (OTA) update mechanism specifically for the Svelte frontend assets served by the ESP32-S3. The update must:
- Update the frontend code without requiring a full firmware re-flash.
- Provide a reliable fallback if an update fails (Rollback capability).
- Handle updates gracefully within the ESP32's available RAM limitations.
- Provide a dedicated UI for the user to upload new frontend binaries.
2. Chosen Approach
We have opted for a Dual-Partition Image Flash (A/B slots) strategy using LittleFS.
Instead of updating individual files (HTML, JS, CSS) over HTTP, the build process will generate a single, pre-packaged .bin image of the entire www directory. This image will be streamed directly to an inactive flash partition, mimicking the safety of standard firmware OTA.
3. Why Dual-Partition Image Flash?
Image Flash vs. Individual File Uploads
| Image Flash (LittleFS .bin) | Individual File Uploads | |
|---|---|---|
| Integrity | High (Flash whole partition, verify, switch) | Low (A failure mid-upload leaves a broken site) |
| Simplicity (Backend) | Easy: Stream bytes to raw flash partition | Hard: Manage file creation, deletion, truncation |
| Speed | Faster (One contiguous flash write) | Slower (Multiple HTTP requests, VFS overhead) |
Dual-Partition (A/B) vs. Single Partition
| Dual-Partition (A/B) | Single Partition | |
|---|---|---|
| Rollback | ✅ Yes: Revert to previous slot if new one fails | ❌ No: Broken update bricks the UI |
| Flash Usage | Higher (Requires 2x space) | Lower |
Decision: Because we have a 16MB flash chip, allocating two 1MB partitions for the frontend (www_0 and www_1) is trivial and provides crucial safety guarantees.
4. Architecture & Workflow
4.1. The Partition Table
The partitions.csv will be modified to include two 1MB data partitions for LittleFS:
www_0www_1
4.2. State Management (NVS)
The active partition index (0 or 1) will be stored in Non-Volatile Storage (NVS).
- On factory flash via serial,
www_0is populated. - During boot (
app_main), the ESP32 reads the NVS key. If the key is empty, it defaults to0and mountswww_0to the/wwwVFS path.
4.3. The Update Process (Backend)
- Identify Slot: The ESP32 determines which slot is currently inactive.
- Stream Upload: The new LittleFS image (.bin) is
POSTed to/api/ota/frontend. - Write to Flash: The HTTP handler streams the payload directly to the raw, unmounted inactive partition using
esp_partition_erase_rangeandesp_partition_write, bypassing LittleFS entirely to save RAM and CPU. - Switch: Once the upload completes successfully, the NVS pointer is updated to point to the newly flashed partition.
- Reboot: The ESP32 reboots. The bootloader reads the new NVS value, mounts the updated partition, and the new frontend is served.
Design Note: We chose an explicit reboot over a hot-swap (unmounting and remounting at runtime) because a reboot is very fast (~2-3 seconds) and guarantees a clean state, closing any open file handles.
4.4. Security Decisions
Authentication and security for the /api/ota/frontend endpoint are deferred. The device operates exclusively on a local, trusted network, making immediate authentication overhead unnecessary for this iteration.
5. Implementation Steps
- Partition Table: Update
partitions.csvwithwww_0andwww_1(1MB each). - Boot Logic: Update
main.cppandhttp_server.cppto read the active partition from NVS and mount the correct label. - API Endpoints:
- Add
GET /api/ota/statusto report the current active slot. - Add
POST /api/ota/frontendto handle the binary stream.
- Add
- Frontend UI: Create a standalone "Update" page in the Svelte app that fetches the status and provides a file picker and progress bar for the upload.
- Build Automation: Add
mklittlefsto the Node.js build pipeline to generatewww.binalongside the standarddistoutput.