OTA Configuration

Uploading Firmware

Uploading Firmware via Platform IO (VSCode)

You can find an example project here: https://github.com/TechdroidInc

To upload directly from platform IO we use a pre- and post-script in the platform.ini file. The two scripts can be found here (post_extra_script.py, pre_extra_script.py) and should be places in your root folder. Configuration is done via specific variables in the platform.ini file.

There following variables are required:

  • api_token - an api token with access to the project on otabin
  • upload_server_url - otabin upload server URL without trailing slash (should almost always be ‘https://app.otabin.com ’)
  • ota_url - otabin server URL without trailing slash (should almost always be ‘https://app.otabin.com ’)
  • custom_prog_version - firmware version
  • custom_prog_board - hardware name
  • custom_hw_uuid - hardware unqiueID from the otabin platform

Step-by-step setup:

  1. Download the two scripts and place them in your project root.
  2. Create a private_config.ini in the root folder. Add a ‘otabin’ section
    api_token = otabin_api_key
    upload_server_url = https://app.otabin.com
    Also add [otabin] section and pre- and post-scripts to your env section in your platform.ini
    ota_url = https://app.otabin.com
    custom_prog_version = 1.0.2
    custom_prog_board = esp32-s3-rev-a
    custom_hw_uuid = 7d2e5970-a8de-4c69-bd3d-6c8eb78fd3f9
    upload_protocol = custom
    extra_scripts =
  3. In platform IO the firmware will now be uploaded upon running this particular env. We recommend setting separate envs for local development and release. There is a complete project example available here:

Configure Hardware for OTA

Arduino - esp32FOTA

Similar to the upload configuration in order to have your hardware do OTAs you need to additionally add the following to your env in platform.ini

build_flags =
  -D VERSION=\"${this.custom_prog_version}\"
  -D BOARD=\"${this.custom_prog_board}\"
  -D OTA_HW_UUID=\"${this.custom_hw_uuid}\"
  -D OTA_URL=\"${otabin.server_url}/fw/l\"

This will inject 4 constants into your project which esp32FOTA will use to perform the OTA.

In your main.cpp do something similar to this:

#include <esp32FOTA.hpp>

esp32FOTA FOTA(BOARD, VERSION, false, true); // init esp32FOTA

int32_t updateCounter = 86401;  // Do update on boot
int32_t updateInterval = 86400; // Check for update every 24 hours if loop delay is 1 second

void setup()

  FOTA.setManifestURL(OTA_URL); // OTA without trailing slash
  FOTA.useDeviceId(true); // Unique HW identifier
  FOTA.setExtraHTTPHeader("x-hardware", BOARD); // env name of the build, will be sent as 'type' in json response
  FOTA.setExtraHTTPHeader("x-current-version", VERSION); // this is the current version
  FOTA.setExtraHTTPHeader("x-hardware-uuid", OTA_HW_UUID); // the hardware Unique ID

  // Other options such as reboot after update
  FOTA.setUpdateFinishedCb([](int partition, bool restart_after) {
    if (restart_after)



void loop()

  if (updateCounter > updateInterval)
    updateCounter = 0;
    bool updatedNeeded = FOTA.execHTTPcheck();
    if (updatedNeeded)


Example pre_extra_script.py


    import configparser
except ImportError:
    import ConfigParser as configparser
project_config = configparser.ConfigParser()

env.Replace(PROGNAME=env.GetProjectOption("custom_prog_board") + "_" + env.GetProjectOption("custom_prog_version")) 

Example post_extra_script.py

import requests
import sys
from os.path import basename


    import configparser
except ImportError:
    import ConfigParser as configparser
project_config = configparser.ConfigParser(inline_comment_prefixes="#")
private_config = configparser.ConfigParser(inline_comment_prefixes="#")

ota_config = {k: v for k, v in project_config.items("otabin")}
otaupload_config = {k: v for k, v in private_config.items("otabin")}

def publish_firmware(source, target, env):

    hardware = env.GetProjectOption("custom_prog_board")
    uuid = env.GetProjectOption("custom_hw_uuid")
    version = env.GetProjectOption("custom_prog_version")
    firmware_path = str(source[0])
    firmware_name = basename(firmware_path)

    print("Uploading {0} to otabin.com. Version: {1}".format(firmware_path, version))

    url = "/".join([
        otaupload_config.get("upload_server_url"), "fw/upload"

    headers = {
        "X-Firmware-Version": version,
        "X-Hardware": hardware,
        "X-Hardware-Uuid": uuid,
        "Authorization": "Bearer " + otaupload_config['api_token']

    r = None
        files = {'firmware': open(firmware_path, "rb")}
        r = requests.post(url,
                         files = files,
                         headers = headers)
    except requests.exceptions.RequestException as e:
        if r.status_code >= 400:
            sys.stderr.write("Failed to submit package: %s\n" % r.text)           
            sys.stderr.write("Failed to submit package: %s\n" %
                            ("%s\n%s" % (r.status_code, r.text) if r else str(e)))

    print("The firmware has been successfuly uploaded to otabin.com")