Skip to content

OTA Phase 2: core update mechanism (single device, verified) #31

Description

@TalkingJupiter

Phase 2 of the OTA foundation (#27), building on the Phase 1 version identity and rollback anchor (#29). This phase proves the update mechanism end to end on a single device, safely, and deliberately decoupled from the control plane.

Scope boundary

  • In scope: download a binary over HTTP, verify it, flash it, reboot into it, and validate that a bad image auto-reverts. Plus the fw-server that hosts the binary and a minimal trigger.
  • Deferred to Phase 3: the full MQTT ota command and ota/status state machine, idempotency, and host-side correlation. Phase 2 is about the mechanism, not the plumbing that drives it.

Component 1: fw-server (host)

  • A separate container on the Radxa that serves .bin files over HTTP. Static file server for this phase (python -m http.server or nginx).
  • URL shape: http://<radxa>:<port>/fw/<name>.bin.
  • SHA-256 is not computed by the server in Phase 2. The operator runs sha256sum on the binary and passes the hash into the trigger. The server only serves the file.

Component 2: OtaUpdater (firmware)

New module. Responsibilities:

  1. Accept a request (URL + expected SHA-256) and store it as pending.
  2. On the next main-loop pass, run the update as one blocking sequence: open TCP to the host, send an HTTP GET, read the status and Content-Length, stream the body straight into the flash writer a chunk at a time while computing the SHA-256 of what is received.
  3. On finish, compare computed vs expected hash. Mismatch: abort, stay on current image, no reboot. Match: commit the new image and reboot.
  4. On any error (connection fail, bad HTTP status, write error, interrupted transfer): abort cleanly, log the reason, leave the running image untouched.

Runs blocking in loop() (locked decision), so the device pauses its own telemetry for the few seconds an update takes. On reboot, the Phase 1 rollback anchor confirms the image once MQTT reconnects, and the post-reboot hello reports the new version.

Trigger (Phase 2)

Serial command: the operator types a command over serial with the URL and hash. Fully decoupled from MQTT so a failure isolates to the mechanism, not the messaging. Phase 3 replaces this with the real MQTT ota command.

Locked decisions

  1. HTTP client: hand-rolled minimal GET on the existing EthernetClient (no new dependency).
  2. Hashing: mbedtls (built into the ESP32).
  3. Trigger: serial command.
  4. Module layout: a new OtaUpdater file, not piled onto TelemetrySender.
  5. fw-server: simplest static file server.
  6. During the update: let telemetry pause while flashing (no enabled:false coordination; that is Phase 5).

Acceptance criteria

  • A valid new image downloads, verifies, flashes, and the device boots into it, with the post-reboot hello showing the new version.
  • A wrong SHA-256 is rejected with no reboot; the device stays on the current image.
  • An interrupted download (cable or power pulled mid-transfer) fails cleanly and leaves the current image intact.
  • A deliberately broken new image (boots but cannot reach the broker) auto-reverts on the next reset. This is the live rollback test deferred from Phase 1.

Out of scope (later phases)

Part of #27. Builds on #29.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions