Skip to main content
NotesFirmwareNew

Over-The-Air (OTA) Firmware Updates: Design & Implementation

A practical guide to designing safe OTA firmware updates for ESP32 and STM32, covering partitions, signed images, rollback, rollout control, and delta updates.

Share

LinkedInFacebookX
ESP32 development board beside a laptop showing a firmware deployment dashboard on an electronics workbench

An OTA update is easy to demo and surprisingly easy to get wrong. The happy path is simple: download a new binary, reboot, and see the new version. The real design has to answer harder questions: what if power disappears halfway through the download, the new image boots but cannot connect, or the device is offline for six months?

In this guide, I will build a practical mental model for ESP32 and STM32. The goal is not to prescribe one cloud service. It is to show the pieces that make an update recoverable: a flash layout, an image manifest, signature verification, a first-boot health check, rollback, and a rollout policy.

OTA is a boot process, not just an HTTP download

Think of an update as a handoff between three actors:

  1. The running application downloads an update package into a safe location.
  2. A bootloader verifies the image and decides which slot to boot.
  3. The new application proves that it is healthy before the bootloader trusts it permanently.

If the application writes over the only copy of itself, a power cut can turn a recoverable update into a field return. That is why the storage layout should be decided before the network code.

ESP32 development board beside a notebook with a hand-drawn flash partition plan

Before writing OTA code, draw the flash layout and decide where the device can safely keep the previous working image.

Start with a partition strategy

For an ESP32 application, the common safe layout has a bootloader, partition table, OTA metadata, and at least two application slots:

flash
├── bootloader
├── partition table
├── otadata          # which OTA slot should boot
├── ota_0            # current or previous application
├── ota_1            # download target / candidate application
└── nvs, littlefs…   # device data, kept separate from the app image

The running firmware writes the new image to the inactive slot. After the image is complete and verified, it changes the boot selection. The current image remains available until the candidate has passed its first boot checks.

STM32 designs use different names, but the same idea applies. A bootloader can keep a download slot and an execution slot, or use two application slots with a swap/copy policy. ST's SBSFU examples show both single-slot and dual-slot arrangements; the dual-slot approach gives you much more room to recover from an interrupted install.

The trade-off is flash. Two full application slots cost more than one slot, and a delta package still needs temporary space or a streaming patch strategy. Decide the maximum firmware size early. A binary that fits today may not fit in either OTA slot after the next display driver is added.

Define an image manifest before defining the endpoint

Do not make the device infer everything from a file name such as firmware-v12.bin. Put the important facts in a signed manifest or signed image header:

{
  "product": "sensor-node",
  "hardware": ["rev-a", "rev-b"],
  "version": "1.8.0",
  "security_version": 4,
  "size": 786432,
  "sha256": "…",
  "image_url": "https://updates.example.com/node-1.8.0.bin",
  "minimum_bootloader": "2.1.0"
}

The hash catches corruption. The signature answers a different question: did an authorised release process create this image? Always verify both the image signature and the device/product compatibility before marking a slot bootable.

On ESP32, ESP-IDF's OTA APIs and secure boot flow can verify signed application images. On STM32, the equivalent can be implemented with a vendor secure firmware update solution such as SBSFU or with a bootloader such as MCUboot. The exact crypto primitive is less important than the trust boundary: the private signing key must stay in the release system, never inside the device firmware or the update server's public directory.

The update state machine

I like to make the state transitions explicit because it exposes most OTA bugs:

IDLE
  → CHECKING
  → DOWNLOADING
  → DOWNLOADED
  → VERIFIED
  → PENDING_BOOT
  → FIRST_BOOT_TEST
       ├── HEALTHY  → CONFIRMED
       └── FAILED   → ROLLBACK

The device should persist enough state to resume or safely restart after a reset. A download can be retried; a boot decision must be atomic. Use a monotonic counter or redundant metadata records so a power cut cannot leave the bootloader with half-written selection data.

For ESP-IDF, the otadata partition is designed for this job. For MCUboot, the image trailer and swap state record whether a test image was confirmed or must be reverted. For a custom STM32 bootloader, this is the part that deserves the most failure-injection testing.

Rollback needs a health check

“The device rebooted” is not the same as “the update worked.” On first boot, run a short diagnostic window:

  • Can the application initialise its required peripherals?
  • Can it load configuration without a migration error?
  • Can it connect to the control plane within a timeout?
  • Does the watchdog remain quiet while normal work starts?
  • Does a basic sensor or actuator self-test pass?

Only then should the new image be marked valid. If the check fails, the bootloader should select the last known-good slot on the next restart. Test the negative path by deliberately making a candidate image fail its health check.

Microcontroller development board beside a bench power supply and laptop during firmware update testing

A safe OTA design is tested with power loss, failed boot, and interrupted downloads—not only with a successful update.

With ESP-IDF, application rollback is coordinated by the bootloader and the application. A pending image can be confirmed with esp_ota_mark_app_valid_cancel_rollback(), or rejected with esp_ota_mark_app_invalid_rollback_and_reboot(). With MCUboot, the new image is confirmed only after it marks itself OK; otherwise the bootloader reverts the test swap.

One detail matters in production: the bootloader already installed on devices must support the rollback policy. Enabling a setting in a new application does not magically upgrade an old bootloader that cannot track pending images.

Secure transport, signed images, and anti-rollback

Use TLS for the transport, but do not confuse TLS with firmware authenticity. TLS protects the connection; image signatures protect the update when it is stored, mirrored, cached, or served by the wrong endpoint.

For a serious product, use all of these layers:

  • HTTPS or another authenticated transport.
  • A signed manifest and signed firmware image.
  • Secure boot or a bootloader root of trust.
  • Device identity and authorisation at the update service.
  • Anti-rollback for security fixes that must never be replaced by an older vulnerable image.
  • Encrypted or access-controlled device data, especially credentials in NVS or external storage.

Anti-rollback should be applied deliberately. A normal bug fix may need a safe rollback to a previous image. A firmware version that fixes a critical vulnerability may need a higher security counter so the device cannot return to the vulnerable build. Keep “application version” and “security version” as separate concepts.

Delta updates: useful, but not free

A delta update sends the difference between an old and a new firmware image. This can save bandwidth, which matters for cellular devices or a fleet with expensive connectivity. It also adds constraints:

  • The device must know exactly which base image it has.
  • The patch must be authenticated just like a full image.
  • The device needs enough temporary storage and RAM for the patch algorithm, or a carefully designed streaming implementation.
  • You need a full-image fallback for devices that are several versions behind or have corrupted storage.
  • Testing must cover interrupted patching and power loss, not only a clean patch.

I would start with full-image A/B updates unless bandwidth is already a measurable problem. Once the stable path is boring, add delta packages at the distribution layer and keep the bootloader's final contract the same: it receives a complete, verified bootable image.

Roll out to a fleet gradually

An OTA backend should not broadcast every release to every device at once. A small rollout policy is enough to start:

  1. Release to internal test devices.
  2. Release to a random canary group.
  3. Watch boot success, rollback rate, connectivity, battery impact, and crash reports.
  4. Expand in stages only when the metrics stay within limits.
  5. Pause or cancel the rollout automatically when a threshold is crossed.

The device should report more than “download complete.” Send the installed version, boot result, rollback reason, update duration, and a coarse error code. Avoid putting secrets or full customer data in telemetry. A fleet update is an operational system, not only a firmware feature.

A compact implementation checklist

Before calling an OTA design production-ready, I would verify:

  • The inactive slot is large enough for the maximum signed image.
  • A power cut during download leaves the current image bootable.
  • A power cut during boot metadata writing does not corrupt slot selection.
  • A bad signature, wrong hardware revision, and expired package are rejected.
  • The first-boot health check can trigger rollback.
  • A device offline for a long time can update from an appropriate full image.
  • The bootloader and rollback policy are already deployed to the fleet.
  • Staged rollout and a stop button exist on the server side.
  • Recovery by USB/UART or a service tool exists for the genuinely unrecoverable cases.

Wrap-up

The most useful OTA design is intentionally conservative: keep the last working image, verify before booting, confirm only after health checks, and make rollout observable. ESP32 gives you a well-documented OTA path through ESP-IDF. STM32 gives you several secure firmware update patterns depending on the family and bootloader you choose. The architecture is portable even when the APIs are not.

My recommendation is to implement the full-image dual-slot path first, test it with forced power loss, then add anti-rollback and staged rollout. Treat delta updates as an optimisation after the reliable path is already working.

References

Share

LinkedInFacebookX

Keep exploring

Read next

Related articles

View more in Notes

Nastrotek uses cookies for analytics and ad personalization to help us understand how the site is used. You can accept or decline non-essential cookies. Privacy Policy