Lab: safe firmware updates for IoT devices
In short: Updates fix vulnerabilities, but a careless update can brick a device or install tampered code. In this lab you back up and verify firmware before flashing, then build a simple over-the-air update that checks a hash, writes to a temporary file and keeps the previous version.
Aim and what you need
The aim is to practise the habits behind safe updates: know what is installed, verify what you install, be able to go back, and update a few devices before all of them. OTA (over-the-air) means delivering updates over the network instead of a cable.
- The ESP32 from the ESP32 sensor lab, its USB cable, and esptool and mpremote.
- A small web server on your LAN to host update files, such as
python3 -m http.serverfor testing, then HTTPS for anything real. - Your own devices only; never push code to devices you do not own.
Steps
- Back up the current flash (4 MB on most boards) and record its hash.
- Download the new MicroPython firmware and compare its SHA-256 hash with the one published by the project before flashing.
- Flash, then restore your application files.
- For code updates over the air, publish a manifest with the version and SHA-256 of the new
main.py. - On the device, download to a temporary file, check the hash, keep the old file as a backup, then swap and reboot.
- After reboot, if the app fails a simple health check, copy the backup back.
esptool --chip esp32 --port /dev/ttyUSB0 read-flash 0 0x400000 backup-2026-10.bin
sha256sum backup-2026-10.bin ESP32_GENERIC-<version>.bin
esptool --chip esp32 --port /dev/ttyUSB0 write-flash -z 0x1000 ESP32_GENERIC-<version>.bin
# on the update server
sha256sum main.py
# manifest.json
{ "version": "1.1.0", "file": "main.py", "sha256": "<hash-from-sha256sum>" }
# ota.py on the device (MicroPython)
import os, hashlib, binascii, requests
BASE = "https://updates.lab.example/sensor1/"
def update():
m = requests.get(BASE + "manifest.json").json()
data = requests.get(BASE + m["file"]).content
digest = binascii.hexlify(hashlib.sha256(data).digest()).decode()
if digest != m["sha256"]:
print("hash mismatch, update rejected")
return False
with open("main.new", "wb") as f:
f.write(data)
try:
os.remove("main.bak")
except OSError:
pass
os.rename("main.py", "main.bak")
os.rename("main.new", "main.py")
return True
A hash only proves the file matches the manifest. If an attacker can change both, they win, so real products sign updates with a private key kept off the server and verify the signature on the device. ESP-IDF, the ESP32's main framework, offers signed app images, secure boot and A/B partitions with automatic rollback; note that enabling secure boot or flash encryption burns eFuses and cannot be undone, so practise on a spare board.
How to verify and troubleshoot
Change one character in the hosted main.py without updating the manifest: the device must print a mismatch and keep running the old version. Then publish a correct update and check the version in the device's output. To test rollback, publish a deliberately broken but correctly hashed file and confirm your health check restores main.bak.
- Out of memory: large files may not fit in RAM; download in chunks and hash as you go.
- TLS errors: the device clock may be wrong; set time with NTP (Network Time Protocol) first.
- Board will not boot after flashing: re-flash over USB or restore the backup image with
write-flash 0 backup-2026-10.bin.
Clean-up and real-world practice
Stop the test web server, delete the backup images once you no longer need them (they contain your Wi-Fi password), and keep a version log. At work, the same ideas scale up: an inventory of firmware versions, vendor advisories, staged rollouts to a pilot group, maintenance windows and a rollback plan. UK and EU product security rules increasingly expect manufacturers to state how long devices will receive updates, which is worth checking before you buy.
Practice
Why is a SHA-256 hash in a manifest not enough on its own?
Why write the download to a temporary file first?
What must you be careful of when enabling secure boot on an ESP32?
Key takeaways
- Back up and record versions before any update.
- Verify hashes, and in real products signatures, before installing.
- Install to a temporary file or second slot and keep a rollback.
- Roll out to a few devices first, then the rest.
- Secure boot and flash encryption are permanent; practise on spares.
Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.