Skip to content

Over-the-air (OTA) firmware update over W5500 Ethernet, MQTT-triggered #27

Description

@TalkingJupiter

Context

Firmware is flashed over USB today (platformio.ini sets upload_port per controller). With
controllers mounted in racks, physical access for reflashing doesn't scale, and #26 unifies the
image so every device runs the same binary — making a fleet-wide OTA path both feasible and
necessary. The MQTT contract already anticipates this: enabled:false is documented as "remote
off, e.g. OTA," and #19 tracks quieting a device during an update.

Problem

  • No remote update mechanism exists — every firmware change requires USB access to each ESP32.
  • Transport is wired W5500 Ethernet, not WiFi, so the standard mDNS-based ArduinoOTA /
    espota flow does not apply. OTA must work over the Ethernet client.
  • The two controllers form an A/B failover pair; updating the active one must not blind the rack.

Proposal

Pull-based HTTP OTA using the Arduino Update.h API over the existing W5500 EthernetClient.
The host (Radxa) serves the firmware binary; an MQTT command tells a target device to fetch and
apply it, then reboot. This reuses the broker + provisioning channel we already have rather than
introducing a new push protocol.

Decisions needed

  1. Trigger & command topic — e.g. repacss/devices/<mac>/ota carrying firmware URL + version
    • expected checksum. Per-device vs rack-wide vs broadcast.
  2. Binary source — Radxa HTTP endpoint serving the .bin; where versions live and how a
    device knows its current vs available version.
  3. Integrity & safety — checksum/signature verification before Update.end(); refuse on
    mismatch. Consider signed images given the control-channel spoofing concern raised in Prod network design: isolate rack telemetry segment + broker bootstrap (DHCP) #24.
  4. Failover coordination — update standby first, verify healthy, then active; or set the
    target enabled:false (Firmware: accept enabled:false to support remote disable #19) so its peer takes over before it reboots. Never update both at once.
  5. Rollback — behavior on failed boot after flash (bad image, power loss mid-write). ESP32
    rollback / OTA-data partition strategy.
  6. Partition scheme — confirm the build uses a dual-app OTA partition table (default single-
    app layout has no OTA slot); update platformio.ini board_build.partitions if needed.
  7. Ack/status — device reports OTA progress/result (downloading → verifying → applied → booted,
    or failed+reason) back over MQTT so the host knows the outcome (relates to the Publish config_ack after applying config #13 config_ack pattern).

Considerations

Acceptance criteria

  • A device fetches and applies a new image over W5500 Ethernet, triggered by an MQTT command, and
    reboots into it.
  • Image integrity is verified before commit; a corrupt/mismatched image is rejected without bricking.
  • A failed flash rolls back to the previous working image.
  • Rolling A/B update keeps the rack reporting throughout (peer covers the device being updated).
  • OTA outcome (success/failure + reason) is reported back to the host.

Files affected

  • ESP32-Firmware/platformio.ini — OTA partition table (board_build.partitions), build/version metadata
  • ESP32-Firmware/src/TelemetrySender.cpp — MQTT command handling for the OTA topic
  • new ESP32-Firmware/src/ OTA module (fetch + Update.h apply + verify + status)
  • ESP32-Firmware/src/main.cpp — coordinate OTA with failover / enabled gating
  • docs/mqtt-contract.md — define the OTA command/status topics + payload schema
  • host side — firmware-serving endpoint + OTA trigger (provisioning service)

Relates to #19 (remote disable during OTA), #24 (control-channel auth/signing), #13 (ack pattern),
#26 (unified image).

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