neiam
/lambent-flex-c6
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Show how to drive OTA from a terminal
d76b734 · Gabriel Morell · 2026-09-16 19:25
Message
{commit_body(@commit)}
Files changed
modified
README.md
+74
−4
@@ -147,14 +147,84 @@ https://gg.neiam.co/pkg/raw/neiam/lambent-flex-c6/manifest.json
| 147 | 147 | |
| 148 | 148 | Every 6 hours for a CI build; **a locally flashed one never polls on its own**, |
| 149 | 149 | so `cargo run --release` is not quietly undone a minute later by the forge's |
| 150 | −build. `POST /ota` forces a check either way. `GET /` | |
| 151 | −reports `version`, `ota_slot`, `ota_state`, and whatever the last check found. | |
| 150 | +build. | |
| 151 | + | |
| 152 | +### Driving it by hand | |
| 153 | + | |
| 154 | +`GET /` says what a box is running — note it answers **218**, not 200, which | |
| 155 | +the packet sender matches on — and `POST /ota` makes it go and look now rather | |
| 156 | +than at the next interval: | |
| 157 | + | |
| 158 | +```console | |
| 159 | +$ curl -s http://172.16.15.139/ | jq '{version, ota_slot, ota_state, ota_available}' | |
| 160 | +{ | |
| 161 | + "version": "0.1.0", | |
| 162 | + "ota_slot": "ota_0", | |
| 163 | + "ota_state": "unknown", | |
| 164 | + "ota_available": null | |
| 165 | +} | |
| 166 | + | |
| 167 | +$ curl -s -i -X POST http://172.16.15.139/ota | head -1 | |
| 168 | +HTTP/1.1 202 Accepted | |
| 169 | + | |
| 170 | +$ curl -s -X POST http://172.16.15.139/ota | jq | |
| 171 | +{ | |
| 172 | + "queued": true, | |
| 173 | + "running": "0.1.0", | |
| 174 | + "available": null, | |
| 175 | + "last_error": null, | |
| 176 | + "checked": false | |
| 177 | +} | |
| 178 | +``` | |
| 179 | + | |
| 180 | +**202 means queued, not done.** The check and the download run on the OTA | |
| 181 | +thread, so the response says nothing about the outcome — and a successful | |
| 182 | +update ends in a reboot, so the device stops answering for a few seconds. | |
| 183 | +Ask again once it is back: | |
| 184 | + | |
| 185 | +```console | |
| 186 | +$ curl -s http://172.16.15.139/ | jq '{version, ota_slot, ota_state}' | |
| 187 | +{ | |
| 188 | + "version": "0.1.0+ci20260916.g2131e3c", | |
| 189 | + "ota_slot": "ota_1", | |
| 190 | + "ota_state": "valid" | |
| 191 | +} | |
| 192 | +``` | |
| 193 | + | |
| 194 | +It moved slots, and `valid` means this image reached the point of serving HTTP | |
| 195 | +and cancelled its own rollback. A failed check leaves the device exactly where | |
| 196 | +it was and says why: | |
| 152 | 197 | |
| 153 | 198 | ```console |
| 154 | −$ curl -X POST http://<device-ip>/ota | |
| 155 | −{"queued":true,"running":"0.1.0+ci20260916.g1a2b3c4","available":null,…} | |
| 199 | +$ curl -s http://172.16.15.139/ | jq '{ota_available, ota_error}' | |
| 200 | +{ | |
| 201 | + "ota_available": null, | |
| 202 | + "ota_error": "fetching https://gg.neiam.co/pkg/raw/neiam/lambent-flex-c6/manifest.json: HTTP 503" | |
| 203 | +} | |
| 204 | +``` | |
| 205 | + | |
| 206 | +`ota_state` reads `unknown` on a board flashed over USB, which is not a fault: | |
| 207 | +`--erase-parts otadata` leaves no state entry to read. It becomes `valid` after | |
| 208 | +the first over-the-air install. | |
| 209 | + | |
| 210 | +### A fleet | |
| 211 | + | |
| 212 | +```sh | |
| 213 | +# what is everyone running? | |
| 214 | +for ip in 172.16.15.{139,140,141}; do | |
| 215 | + printf '%-16s %s\n' "$ip" "$(curl -s --max-time 5 "http://$ip/" | jq -r '.version // "unreachable"')" | |
| 216 | +done | |
| 217 | + | |
| 218 | +# everyone, go and look now | |
| 219 | +for ip in 172.16.15.{139,140,141}; do | |
| 220 | + curl -s --max-time 5 -X POST "http://$ip/ota" > /dev/null & | |
| 221 | +done; wait | |
| 156 | 222 | ``` |
| 157 | 223 | |
| 224 | +Nothing needs coordinating: each device downloads for itself, verifies the | |
| 225 | +SHA-256 the manifest gave it, and reboots when it is ready. One that is offline | |
| 226 | +or mid-frame simply picks the update up at its next check. | |
| 227 | + | |
| 158 | 228 | **A device flashed before OTA existed needs one USB flash to migrate** onto the |
| 159 | 229 | two-slot partition table — the app slot moved and there is no otadata. NVS |
| 160 | 230 | keeps its offset and size, so credentials and pins survive; do *not* erase it. |
Parents: b76c527