Unihiker K10 OTA
/install unihiker-k10-ota
Unihiker K10 - HTTP OTA
Overview
Enable 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.
When to Use
- Your K10 is installed in a location difficult to reach with USB
- You want to update firmware without opening the enclosure
- You need a scriptable/automated deployment pipeline
- ArduinoOTA network port upload fails with "No response from device"
Prerequisites
- Existing K10 Arduino project with
WebServerrunning arduino-cliinstalled and K10 BSP (UNIHIKER:esp32:k10) available- Device and computer on the same network (or connected to K10's AP)
ESP-NOW Compatibility Rule
ESP-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:
- Prefer a maintenance OTA mode: normal runtime uses ESP-NOW; a button, serial command, saved flag, or received command enters OTA mode, starts AP or STA networking, registers
/ota, and servicesserver.handleClient(). - Keep ESP-NOW and WiFi on the same channel if they run together. If STA connects to a router, the router determines the channel; ESP-NOW peers must use that channel or peer channel
0. - For reliability, pause ESP-NOW sends and time-critical control loops while an OTA upload is active.
- Keep an AP fallback such as
K10-OTA-\x3Cid>available in OTA mode so updates still work when STA credentials are missing or the router changes. - Treat pure ESP-NOW firmware transfer as an advanced separate design. It needs packet chunking, acknowledgements, image validation, and writes to OTA partitions; do not replace HTTP OTA with it unless the user explicitly asks for ESP-NOW-only OTA.
See references/ota-implementation.md for the ESP-NOW maintenance-mode code pattern.
Quick Start
Step 1: Add Custom Partition Table
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
Step 2: Add OTA Endpoint to Firmware
Include the Update library and add a POST handler:
#include \x3CUpdate.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\
", 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
}
Step 3: First USB Upload (Required Once)
The first upload must be via USB to flash the new partition table:
arduino-cli upload -p COM4 --fqbn UNIHIKER:esp32:k10 .
Step 4: Update Over WiFi
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
Important Notes
- Partition change erases flash layout. The first USB upload after adding
partitions.csvwill reformat the flash partition table.Preferences/ NVS data may be lost. - Every OTA-enabled sketch must include the OTA code. If you upload a sketch without
/otahandler, you lose OTA capability and must return to USB. - Do not use
delay()inloop()for long periods. Use non-blockingmillis()patterns so the WebServer can process the upload request. - Content-Length: Arduino WebServer's
server.header("Content-Length")does not work in POST handlers. Useserver.clientContentLength()instead if you need the raw body size. - Compile cache: Use Arduino CLI's official
build_cache.*settings and--build-pathfor repeat builds. Do not documentcompiler.cache.enable,compiler.cache.path, orccacheas required OTA setup because they are not part of the current Arduino CLI configuration reference. - ESP-NOW: HTTP OTA needs AP/STA networking. If the program uses ESP-NOW, add an OTA maintenance mode, manage WiFi channel alignment, and pause ESP-NOW traffic while flashing.
Files
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
Troubleshooting
| 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 |
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install unihiker-k10-ota - 安装完成后,直接呼叫该 Skill 的名称或使用
/unihiker-k10-ota触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
Unihiker K10 OTA 是什么?
Add 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... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 35 次。
如何安装 Unihiker K10 OTA?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install unihiker-k10-ota」即可一键安装,无需额外配置。
Unihiker K10 OTA 是免费的吗?
是的,Unihiker K10 OTA 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。
Unihiker K10 OTA 支持哪些平台?
Unihiker K10 OTA 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 Unihiker K10 OTA?
由 Rockets_cn(@rockets-cn)开发并维护,当前版本 v1.0.0。