Skip to main content

OTA firmware updates: complete guide

OTA (over the air) means updating a device's firmware without touching it: no USB cable, no visit. You upload the new firmware in the Organization Portal, choose the devices, and start a shipment. Each device downloads the new firmware, installs it and restarts into it.

This guide covers both sides: what the firmware needs (developers) and how to ship an update (organizations).

How it works​

ThingsLinker does not make thousands of devices ask "is there an update?" every few minutes. Instead it rings a doorbell: the moment you start a shipment, each of its devices gets a small message over its existing MQTT connection. Only then does the device ask the server what to install.

Why it is built this way:

  • Instant and light. Devices hear about an update at once, without polling the server. That keeps working with thousands of devices.
  • Offline devices are not missed. The doorbell is kept on the broker, so a device that is off when you start hears it as soon as it reconnects.
  • Safe. The doorbell carries no firmware and no instructions. The device always asks the server over HTTPS, and the server alone decides what it gets. A fake message can only make a device ask; it cannot make it install anything.
  • A safety net. The device also checks once a few minutes after it starts and then every 12 hours, in case it ever missed a doorbell.

1. Prepare the firmware​

Turn on automatic updates​

Add one line after iot.begin():

#include <ThingsLinker.h>

ThingsLinker iot("YOUR_DEVICE_AUTH_TOKEN", "YOUR_BLUEPRINT_KEY");

// Printed at start-up, so you can see which version is running after an update
static const char* FIRMWARE_VERSION = "1.0";

void setup() {
Serial.begin(115200);
Serial.printf("My product v%s\n", FIRMWARE_VERSION);

iot.begin();
iot.enableOTA(); // listen for the doorbell, plus a safety-net check every 12 hours
}

void loop() {
iot.run(); // installs an update when one is waiting
// your code
}

That is all. The complete sketch is the library example 19_OTA_Auto.

4G (GSM) boards

ThingsLinkerGSM does not have enableOTA(). Call iot.checkOTA() yourself on a schedule, for example every few hours, to save mobile data. See Check for updates yourself.

Choose the partition scheme (important)​

To update itself, the ESP32 keeps two copies of the firmware: the one running and the one being downloaded. The flash must be divided to make room for both.

In Arduino IDE:

  1. Tools → Flash Size: your chip's real size, for example 4MB for most ESP32 DevKits or 16MB for an ESP32-S3 N16R8.
  2. Tools → Partition Scheme → Minimal SPIFFS (1.9MB APP with OTA).
Use the same settings for every version

Upload the first firmware over USB with these settings, and build every later .bin with exactly the same flash size and partition scheme. A firmware built differently can fail to install, or install and not start. "Default with OTA" (1.3 MB per slot) is usually too small for a sketch with WiFi and OTA.

Build the .bin file​

  1. Raise the version in your code, for example "1.0" → "1.1".
  2. Choose Sketch → Export Compiled Binary.
  3. Arduino IDE saves the file in the sketch's build folder. Use the one named after your sketch, ending in .ino.bin, not the bootloader or partitions files.

2. Test on one device first​

Always ship a new version to one device on your desk before shipping to everyone:

  1. Flash version 1.0 over USB and add the device to an account so it is online.
  2. Ship version 1.1 to that device only (next section).
  3. Watch the Serial Monitor: you see the download progress, then the device restart and print v1.1.

If anything goes wrong, you can always reflash over USB.

3. How versions are chosen​

  • A device installs the newest live shipment it is part of. It is never offered an older one, so there are no accidental downgrades.
  • When a device installs an update it reports completed, and the portal shows its new firmware version.
  • A device that is inactive (not using a plan slot) gets no updates.

4. Ship the update​

In the Organization Portal, go to OTA / Firmware and choose New Shipment. The full walkthrough with pictures is in Firmware updates. In short:

  1. Shipment Details: a name and the blueprint.
  2. Devices: choose them, or Select all.
  3. Firmware: the version and the .bin file (drag it onto the upload area).
  4. Retry Settings: how many tries each device gets.
  5. Start Shipping, then Start the shipment.

5. When an update fails​

Downloads can fail: weak WiFi, a power cut, a restart in the middle. ThingsLinker handles this for you.

SettingMeaningExample
Attempts LimitHow many tries each device gets.3: up to three tries, about 5 minutes apart
Attempts Reset PeriodAfter using all its tries, how long a device waits before getting a fresh set.24 hours: if all three fail, try again tomorrow

A device that restarts in the middle of an update counts as a failed attempt, so it is retried instead of being marked as updated. A failed update never breaks the device: it keeps running the firmware it had.

Check for updates yourself​

If you prefer to decide when to check (for example on 4G), call checkOTA() instead of enableOTA():

void loop() {
iot.run();

static unsigned long lastCheck = 0;
if (millis() - lastCheck >= 6UL * 60 * 60 * 1000) { // every 6 hours
lastCheck = millis();
OTAResult r = iot.checkOTA();
if (r == OTA_NO_UPDATE) Serial.println("Firmware up to date");
if (r == OTA_FAILED) Serial.println("Update failed; the server will retry");
if (r == OTA_ERROR) Serial.println("Could not reach the server");
// On success the device restarts into the new firmware, so OTA_SUCCESS is not returned here
}
}

Troubleshooting​

What you seeCause and fix
The serial log mentions a partition or "not enough space"The .bin was built with a different flash size or partition scheme than the device. Rebuild it with the same settings as the firmware on the device, or reflash the device over USB with the right settings once.
The device downloads part of the file, then stopsWeak WiFi or power. The server retries automatically; check the device's signal and power supply.
Nothing happens after StartThe firmware must call iot.enableOTA() (or checkOTA()), the device must be online, and it must be active on your plan.
The update installs but the device keeps the old versionYou uploaded the wrong .bin (for example the bootloader). Use <sketch>.ino.bin.
The device shows Offline for a moment during an updateNormal: it closes its MQTT connection while downloading, then restarts.
A device used all its attemptsWait for the Attempts reset period, or fix the cause and start a new shipment.

Good practice​

  • Version every release and print it at start-up.
  • Test on one device, then a few, then all.
  • Keep the same partition scheme for the life of the product.
  • Do not change the credentials in a new version: the update must keep the same auth token and blueprint key.
  • Keep the .bin files of released versions, so you can ship a fix quickly.