Install
openclaw skills install unihiker-k10-otaAdd HTTP OTA (Over-The-Air) firmware update capability to Unihiker K10 Arduino projects, including AP/STA projects and ESP-NOW projects that need a safe OTA maintenance mode. Use when you need wireless firmware updates without USB cable, when ArduinoOTA fails, or when an ESP-NOW sketch must keep an OTA recovery/update path.
openclaw skills install unihiker-k10-otaEnable wireless firmware updates for K10 Arduino projects via HTTP POST.
Core principle: K10's default partition table has no OTA partitions. You must switch to a custom partition table with ota_0 + ota_1 before Update.begin() can work.
Why not ArduinoOTA? The standard ArduinoOTA library (UDP-based) requires the ESP32 to connect back to the host computer on a random port, which is often blocked by Windows Firewall. HTTP OTA uses a simple host→device upload direction and works reliably on all networks.
WebServer runningarduino-cli installed and K10 BSP (UNIHIKER:esp32:k10) availableESP-NOW sketches can support OTA, but ordinary HTTP OTA requires temporary IP networking through WIFI_AP, WIFI_STA, or WIFI_AP_STA. ESP-NOW itself is not an IP transport, so do not claim that the standard /ota HTTP endpoint works over pure ESP-NOW packets.
When adding OTA to an ESP-NOW program, use this policy:
/ota, and services server.handleClient().0.K10-OTA-<id> available in OTA mode so updates still work when STA credentials are missing or the router changes.See references/ota-implementation.md for the ESP-NOW maintenance-mode code pattern.
Create partitions.csv in your sketch directory:
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x640000,
app1, app, ota_1, 0x650000,0x640000,
spiffs, data, spiffs, 0xc90000,0x370000,
Compile with the custom partition:
arduino-cli compile --fqbn UNIHIKER:esp32:k10 . \
--output-dir build \
--build-property "build.partitions=custom"
Optional speed-up for repeated compiles:
# Use all CPU cores and keep build artifacts in stable project-local folders.
arduino-cli compile --fqbn UNIHIKER:esp32:k10 . \
--build-path .arduino-build \
--output-dir build \
--build-property "build.partitions=custom" \
-j 0
Arduino CLI already has a built-in build_cache. To use a longer-lived cache, configure the official build_cache.* keys rather than compiler.cache.*:
arduino-cli config set build_cache.path ~/.cache/arduino-build-cache
arduino-cli config set build_cache.compilations_before_purge 0
On Windows PowerShell:
arduino-cli config set build_cache.path "$env:LOCALAPPDATA\arduino\build-cache"
arduino-cli config set build_cache.compilations_before_purge 0
Include the Update library and add a POST handler:
#include <Update.h>
void handleOta() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", Update.hasError() ? "FAIL" : "OK");
if (!Update.hasError()) {
ESP.restart(); // or schedule a delayed restart
}
}
void handleOtaUpload() {
HTTPUpload &upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) {
Serial.printf("OTA Success: %u bytes\n", upload.totalSize);
} else {
Update.printError(Serial);
}
}
}
// In setup() or startNetwork():
server.on("/ota", HTTP_POST, handleOta, handleOtaUpload);
For ESP-NOW sketches, do not leave OTA as an afterthought. Add an explicit OTA mode gate:
bool otaMode = false;
bool otaUploadActive = false;
void enterOtaMode() {
otaMode = true;
WiFi.mode(WIFI_AP_STA); // AP fallback plus optional STA
WiFi.softAP("K10-OTA", "12345678");
// Optional: WiFi.begin(savedSsid, savedPassword);
server.on("/ota", HTTP_POST, handleOta, handleOtaUpload);
server.begin();
}
void loop() {
if (otaMode) {
server.handleClient();
return; // keep ESP-NOW/control traffic paused during OTA maintenance
}
// normal ESP-NOW runtime
}
The first upload must be via USB to flash the new partition table:
arduino-cli upload -p COM4 --fqbn UNIHIKER:esp32:k10 .
After the first USB upload, use any of these methods:
curl:
curl -F "file=@build/your_sketch.ino.bin" http://192.168.9.42/ota
Python script (works on Windows, macOS, and Linux):
python scripts/ota_upload.py build/your_sketch.ino.bin --ip 192.168.9.42
PowerShell 7+ (works on Windows, macOS, and Linux):
pwsh ./scripts/ota_upload.ps1 -Bin build/your_sketch.ino.bin -Ip 192.168.9.42
partitions.csv will reformat the flash partition table. Preferences / NVS data may be lost./ota handler, you lose OTA capability and must return to USB.delay() in loop() for long periods. Use non-blocking millis() patterns so the WebServer can process the upload request.server.header("Content-Length") does not work in POST handlers. Use server.clientContentLength() instead if you need the raw body size.build_cache.* settings and --build-path for repeat builds. Do not document compiler.cache.enable, compiler.cache.path, or ccache as required OTA setup because they are not part of the current Arduino CLI configuration reference.unihiker-k10-ota/
├── SKILL.md # This file
├── references/
│ └── ota-implementation.md # Detailed implementation guide
└── scripts/
├── ota_upload.py # Python OTA uploader
└── ota_upload.ps1 # PowerShell OTA uploader
| Issue | Cause | Solution |
|---|---|---|
BEGIN_FAIL | No OTA partitions in partition table | Add partitions.csv with ota_0 + ota_1 and reflash via USB |
FAIL after upload | Update.write() failed mid-stream | Check serial log; likely flash write error or insufficient space |
NO_CONTENT | Content-Length header missing | Ensure client sends valid multipart/form-data with file data |
| Device does not restart | ESP.restart() called before response sent | Use scheduleRestart() with a small delay instead |
| Network port not found | mDNS/ArduinoOTA not running | HTTP OTA does not need network port detection; use the device's IP directly |
| ESP-NOW works until STA starts | STA changed the radio channel to the router channel | Put peers on the same channel or use peer channel 0 after STA connects |
| OTA page unreachable in ESP-NOW sketch | Sketch never entered AP/STA maintenance mode | Add a button/serial/command path that calls enterOtaMode() and starts the WebServer |
| ESP-NOW packets drop during OTA | Flashing and HTTP handling are competing with runtime traffic | Pause ESP-NOW sends/control loops while otaUploadActive or otaMode is true |