BackOpen source
Open source2025Author

HttpsOta

Over-the-air updates for ESP32, over TLS, with rollback

An OTA update library written for a device that could not be reached physically — so every failure mode that would brick it had to be handled before the first update was ever sent.

Problem

An update to a device you can physically reach is an inconvenience when it fails. An update to a device on another continent is the end of the device.

The naive OTA path has several ways to end that way: a truncated download that verifies as complete, a stalled connection that never times out, a valid image that does not boot, a TLS check skipped because the device's clock is wrong.

What I built

A library whose feature list is a list of the ways this can go wrong.

  • SHA-256 verification of the downloaded image
  • Download stall detection and total timeouts
  • Progress callbacks
  • Dual OTA partition support with automatic rollback
  • TLS validation, with the certificate time dependency handled explicitly
  • Heap guards, and handling for partition table failures
  • Chunked transfer considerations
  • An HMAC signing example

The failure analysis is the documentation

The README is written as an account of what breaks rather than a feature list — the certificate time dependency, the partition table failure mode, what a stalled chunked transfer looks like from the device's side. That is the part worth reading.

An update, and every way it can fail

Each branch here is a way a naive OTA bricks a device you cannot reach. The library exists because this specific device was on another continent.

  1. Poll the update endpoint

    The server returns version, sha256 and size.

  2. Is there enough heap for a TLS handshake?

    No — refuse to start. LowMemory
    Yes — open the connection
  3. Download into the inactive OTA slot

    Hashed while downloading, and sized from Content-Length.

    Connection dies mid-download — Stalled, retry later
    Total timeout exceeded — give up rather than hang
  4. Verify SHA-256

    Mismatch — truncated or tampered. Nothing is installed
    Match — mark the new slot bootable
  5. Reboot into the new image

    It comes up and reports its version — done
    It does not — automatic rollback to the running slot

Failure modes, and what the device does

A truncated image and a tampered one look identical from the device, so both are rejected the same way — before install, never after.

ResultCauseConsequence
StalledNetwork died mid-downloadNothing installed; retry later
HashMismatchTruncated or tampered imageNothing installed
LowMemoryNot enough contiguous heap for TLSHandshake never started
RollbackNew image did not bootDevice returns to the running slot

Things that bite you once

Documented because each cost real debugging time, and the symptom points somewhere other than the cause.

SymptomActual cause
`No bootable app partitions in the partition table`Looks like corrupt flash; is really an off-by-one partition table
Update reinstalls on every pollThe device is not reporting the version it is running
Write fails part-way`Content-Length` missing — the device sizes its partition write from it
TLS validation fails on a fresh deviceThe certificate check is time-dependent and the clock is not set

Limitations and failure modes

  • Rollback protects against an image that does not boot. It cannot protect against an image that boots and then loses its network configuration.

Attribution

Built by me
The library, extracted from the PrabalOS deployment it was written for.

Related

  • PrabalOS

    A connected ESP32 device for staying in touch