72 lines
4.1 KiB
Markdown
72 lines
4.1 KiB
Markdown
# 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_0`
|
|
- `www_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_0` is populated.
|
|
- During boot (`app_main`), the ESP32 reads the NVS key. If the key is empty, it defaults to `0` and mounts `www_0` to the `/www` VFS path.
|
|
|
|
### 4.3. The Update Process (Backend)
|
|
1. **Identify Slot**: The ESP32 determines which slot is currently *inactive*.
|
|
2. **Stream Upload**: The new LittleFS image (.bin) is `POST`ed to `/api/ota/frontend`.
|
|
3. **Write to Flash**: The HTTP handler streams the payload directly to the raw, unmounted inactive partition using `esp_partition_erase_range` and `esp_partition_write`, bypassing LittleFS entirely to save RAM and CPU.
|
|
4. **Switch**: Once the upload completes successfully, the NVS pointer is updated to point to the newly flashed partition.
|
|
5. **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
|
|
|
|
1. **Partition Table**: Update `partitions.csv` with `www_0` and `www_1` (1MB each).
|
|
2. **Boot Logic**: Update `main.cpp` and `http_server.cpp` to read the active partition from NVS and mount the correct label.
|
|
3. **API Endpoints**:
|
|
- Add `GET /api/ota/status` to report the current active slot.
|
|
- Add `POST /api/ota/frontend` to handle the binary stream.
|
|
4. **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.
|
|
5. **Build Automation**: Add `mklittlefs` to the Node.js build pipeline to generate `www.bin` alongside the standard `dist` output.
|