SkillAgentSearch skills...

Esp32FOTA

Experiments in firmware OTA updates for ESP32 dev boards

Install / Use

/learn @chrisjoyce911/Esp32FOTA
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

PlatformIO

arduino-library-badge PlatformIO Registry

esp32FOTA library for Arduino

Purpose

A simple library to add support for Over-The-Air (OTA) updates to your project.

Features

  • [x] Zlib or gzip compressed firmware support
  • [x] SPIFFS/LittleFS partition Update [#25], [#47], [#60], [#92] (thanks to all participants)
  • [x] Any fs::FS support (SPIFFS/LITTLEFS/SD) for cert/signature storage [#79], [#74], [#91], [#92] (thanks to all participants)
  • [x] Seamless http/https
  • [x] Web update (requires web server)
  • [x] Batch firmware sync
  • [x] Force firmware update [#8]
  • [x] https support [#26] ( Thanks to @fbambusi )
  • [x] Signature check of downloaded firmware-image [#65]
  • [x] Signature verification
  • [x] Semantic versioning support
  • [ ] Checking for update via bin headers [#15]

How it works

This library tries to access a JSON file hosted on a webserver, and reviews it to decide if a newer firmware has been published, if so it will download it and install it.

There are a few things that need to be in place for an update to work.

  • A webserver with the firmware information in a JSON file
  • Firmware version
  • Firmware type
  • Firmware bin (can optionnally be compressed with zlib or gzip)
  • For https or signature check: SPIFFS with root_ca.pem (https) and rsa_key.pem (signature check)

You can supply http or https URLs. If you are using https, you need the root_ca.pem in your SPIFFS partition. For the actual firmware it will use https when you define port 443 or 4433. Otherwise it will use plain http.

Usage

Hosted JSON

This is hosted by a webserver and contains information about the latest firmware:

{
    "type": "esp32-fota-http",
    "version": 2,
    "host": "192.168.0.100",
    "port": 80,
    "bin": "/fota/esp32-fota-http-2.bin"
}

Version information can be either a single number or a semantic version string. Alternatively, a full URL path can be provided:

{
    "type": "esp32-fota-http",
    "version": "2.5.1",
    "url": "http://192.168.0.100/fota/esp32-fota-http-2.bin"
}

⚠️ Important: URL Field Behavior

When using the url field in your manifest, all other URL-related fields will be ignored. This includes:

  • host, port, bin fields
  • Filesystem fields: spiffs, littlefs, fatfs
// ❌ This will NOT work - littlefs will be ignored because 'url' is present
{
  "type": "esp32-fota-http",
  "version": "1.0.0",
  "url": "https://example.com/firmware.bin",
  "littlefs": "https://example.com/filesystem.bin"  // IGNORED!
}

// ✅ Use this format for firmware + filesystem updates
{
  "type": "esp32-fota-http",
  "version": "1.0.0",
  "host": "example.com",
  "port": 443,
  "bin": "/firmware.bin",
  "littlefs": "/filesystem.bin"
}

// ✅ Or this format for firmware-only updates
{
  "type": "esp32-fota-http",
  "version": "1.0.0",
  "url": "https://example.com/firmware.bin"
}

Manifest Format Options:

  1. Complete URL (firmware only): Use url field - filesystem updates not supported
  2. Component-based URLs (firmware + filesystem): Use host, port, bin, and optional spiffs/littlefs/fatfs fields

You cannot mix these approaches in a single manifest.

A single JSON file can provide information on multiple firmware types by combining them together into an array. When this is loaded, the firmware manifest with a type matching the one passed to the esp32FOTA constructor will be selected:

[
   {
      "type":"esp32-fota-http",
      "version":"0.0.2",
      "url":"http://192.168.0.100/fota/esp32-fota-http-2.bin"
   },
   {
      "type":"esp32-other-hardware",
      "version":"0.0.3",
      "url":"http://192.168.0.100/fota/esp32-other-hardware.bin"
   }
]

A single JSON file can also contain several versions of a single firmware type:

[
   {
      "type":"esp32-fota-http",
      "version":"0.0.2",
      "url":"http://192.168.0.100/fota/esp32-fota-0.0.2.bin"
   },
   {
      "type":"esp32-fota-http",
      "version":"0.0.3",
      "url":"http://192.168.0.100/fota/esp32-fota-0.0.3.bin",
      "spiffs":"http://192.168.0.100/fota/esp32-fota-0.0.3.spiffs.bin"
   }
]

Filesystem image (spiffs/littlefs)

Adding spiffs key to the JSON entry will end up with the filesystem being updated first, then the firmware.

Obviously don't use the filesystem you're updating to store certificates needed by the update, spiffs partition doesn't have redundancy like OTA0/OTA1 and won't recover from a failed update without a restart and format.

{
    "type": "esp32-fota-http",
    "version": 2,
    "host": "192.168.0.100",
    "port": 80,
    "bin": "/fota/esp32-fota-http-2.bin",
    "spiffs": "/fota/default_spiffs.bin"
}

Other accepted keys for filesystems are spiffs, littlefs and fatfs. Picking one or another doesn't make any difference yet.

Firmware types

Types are used to compare with the current loaded firmware, this is used to make sure that when loaded, the device will still do the intended job.

As an example, a device used as a data logger should ony be updated with new versions of the data logger.

examples
  • TTGO-T8-ESP32-Logger
  • TTGO-T8-ESP32-Temp
  • TTGO-T8-ESP32-Relay

Debug

Messages depends of build level. If you pass -D CORE_DEBUG_LEVEL=3 to build flags, it enable the messages

Sketch

In this early init example, a version 1 of 'esp32-fota-http' is in use, it would be updated when using the JSON example.

#include <esp32FOTA.hpp>

const char *ssid = "";
const char *password = "";

esp32FOTA esp32FOTA("esp32-fota-http", "1.0.0");

const char* manifest_url = "http://server/fota/fota.json";

void setup()
{
  Serial.begin(115200);
  setup_wifi();
  esp32FOTA.setManifestURL( manifest_url );
  // esp32FOTA.useDeviceId( true ); // optionally append the device ID to the HTTP query
}

void setup_wifi()
{
  delay(10);
  Serial.print("Connecting to ");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
}

void loop()
{
  esp32FOTA.handle();
  // or ...
  // bool updatedNeeded = esp32FOTA.execHTTPcheck();
  // if (updatedNeeded) {
  //   esp32FOTA.execOTA();
  // }
  delay(2000);
}

Late init is possible using FOTAConfig_t, allowing more complex configurations:

#include <SPIFFS.h> // include filesystem *before* esp32FOTA librart
#include <esp32FOTA.hpp>

esp32FOTA FOTA;

const char* manifest_url = "http://server/fota/fota.json";
const char* fota_name = "esp32-fota-http";

// CryptoFileAsset *MyRootCA = new CryptoFileAsset( "/root_ca.pem", &SPIFFS );
// CryptoFileAsset *MyRSAKey = new CryptoFileAsset( "/rsa_key.pub", &SD );

void setup()
{
  Serial.begin( 115200 );
  setup_wifi();

  {
    auto cfg = FOTA.getConfig();
    cfg.name          = fota_name;
    cfg.manifest_url  = manifest_url;
    cfg.sem           = SemverClass( 1, 0, 0 ); // major, minor, patch
    cfg.check_sig     = false; // verify signed firmware with rsa public key
    cfg.unsafe        = true; // disable certificate check when using TLS
    //cfg.root_ca       = MyRootCA;
    //cfg.pub_key       = MyRSAKey;
    //cfg.use_device_id = false;
    FOTA.setConfig( cfg );
  }
}

void loop()
{
  esp32FOTA.handle();
  // or ...
  // bool updatedNeeded = esp32FOTA.execHTTPcheck();
  // if (updatedNeeded) {
  //   esp32FOTA.execOTA();
  // }
  delay(2000);
}

Zlib/gzip support

⚠️ This feature cannot be used with signature check.

For firmwares compressed with pigz utility (see , file extension must be .zz:

#include <flashz.hpp> // http://github.com/vortigont/esp32-flashz
#include <esp32FOTA.hpp>
$ pigz -9kzc esp32-fota-http-2.bin > esp32-fota-http-2.bin.zz
{
    "type": "esp32-fota-http",
    "version": "2.5.1",
    "url": "http://192.168.0.100/fota/esp32-fota-http-2.bin.zz"
}

For firmwares compressed with gzip utility, file extension must be .gz

#include <ESP32-targz.h> // http://github.com/tobozo/ESP32-targz
#include <esp32FOTA.hpp>
$ gzip -c esp32-fota-http-2.bin > esp32-fota-http-2.bin.gz
{
    "type": "esp32-fota-http",
    "version": "2.5.1",
    "url": "http://192.168.0.100/fota/esp32-fota-http-2.bin.gz"
}

Root Certificates

Certificate Bundles

Arduino 3.x now supports bundled root certificates, which means 99% of sites (including github.com) will work over https and you don't need to maintain a custom certificate on your firmware.

To enable this functionality, simply call this during your setup:

esp32FOTA.useBundledCerts();

If you are using Platformio / PIOArduino, the certificates are not automatically bundled and you will need to download them from CURL.

Save that file to your project root directory and then add this line to your platformio.ini:

board_build.embed_txtfiles=ca_cert_bundle

Make sure it is named exactly ca_cert_bundle with no extension and located in the top level of your project.

Custom Certificates

Certificates and signatures can be stored in different places: any fs::FS filesystem or progmem as const char*.

Filesystems:

CryptoFileAsset *MyRootCA = new CryptoFileAsset( "/root_ca.pem", &SPIFFS );
CryptoFileAsset *MyRootCA = new CryptoFileAsset( "/root_ca.pem", &LittleFS );
CryptoFileAsset *MyRootCA = new CryptoFileAsset( "/root_ca.pem", &SD );

Progmem:

const char* root_ca = R"ROOT_CA(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQs

Related Skills

View on GitHub
GitHub Stars409
CategoryDevelopment
Updated1d ago
Forks100

Languages

C++

Security Score

100/100

Audited on Apr 4, 2026

No findings