Skip to content

Repository files navigation

ESP32 Lifecycle Manager (LCM)

ESP32 Lifecycle Manager (LCM) is a lightweight helper firmware that shepherds an ESP32-based device through its entire lifecycle. You can think of it as the "operations center" that boots first, prepares network access, installs your application firmware, keeps it up to date, and offers recovery tools when something goes wrong. Even if you are new to ESP-IDF, LCM guides you from an unconfigured module to a fully managed device in a few straightforward steps.

Documentation map

To make this repository easier to navigate:

  • End users / installers / support: see enduser.md for onboarding, OTA operation, reset procedures, and troubleshooting.
  • Developers / integrators: continue with this README.md for build, signing, and flashing workflows.
  • Security and operations detail: see docs/security and docs/operations.

Important security warning

This repository contains example key files (ota_signing_private.pem and ota_signing_public.pem) for demonstration purposes. Because the matching private key is committed here, it is public knowledge: anyone can sign firmware that a device built with the demo key would accept. The demo key therefore provides no authenticity.

So the project works out of the box, the firmware builds with this demo key by default. The firmware logs a warning at runtime whenever the demo key is in use. This is intended for evaluation and development only.

For any real device, switch to your own key:

  • set LCM_USE_CUSTOM_OTA_PUBLIC_KEY=y and provide your own LCM_CUSTOM_OTA_PUBLIC_KEY_PEM (recommended), and sign firmware with the matching private key.

Always generate and protect your own signing keys before shipping devices. If you ever placed a real key in this repo, treat it as compromised, rotate it, and scrub it from git history (e.g. git filter-repo).

How LCM operates

At every boot LCM starts before your application. It evaluates whether the system can continue normal operation or whether maintenance is required. The firmware stores Wi-Fi credentials, checks for new releases, validates firmware signatures, and triggers recovery flows when necessary. This means you do not need to build these management utilities yourself—LCM bundles the essentials for remote lifecycle control of ESP32 devices.

Core capabilities

Access Point (AP) mode

LCM can launch a temporary access point so the device remains reachable even before any Wi-Fi network has been configured. Connect to this hotspot to perform initial setup.

Captive portal (CNA)

When you join the LCM access point a captive portal automatically opens. From this portal you can:

  • Select a visible Wi-Fi network and provide its password.
  • Add a hidden Wi-Fi network manually by entering the SSID and password.
  • Configure the GitHub repository URL where LCM should look for new firmware releases (main.bin and main.bin.sig).
  • Blink the status LED for identification so you can confirm you are working with the correct device during installation or servicing.

Downloading and installing main.bin

After provisioning, LCM downloads main.bin and the accompanying main.bin.sig. It validates the ECDSA P-256 / SHA-256 signature as well as the reported image length and only activates the firmware (switches the boot partition) when the verification succeeds. The download is staged to the inactive OTA partition; if signature verification fails the boot partition is left untouched, so an unverified image is never booted.

Software update (ota_trigger)

You can initiate an over-the-air update through an OTA trigger (for example via an HTTP request) whenever a new release becomes available.

Hardware-assisted update

LCM can listen to hardware controls such as a physical button to start or confirm the update flow. This is useful for devices that must update without a connected computer.

Reset and recovery options

  • Software factory reset – Use the LCM API to erase the stored configuration and reboot the device.
  • Hardware factory reset – Quickly power-cycle or reset the device 10 times in succession. Once LCM detects the pattern it starts an ~11 second countdown and performs the factory reset automatically.

Automatic firmware version stamping

During installation or update LCM writes the value of LIFECYCLE_DEFAULT_FW_VERSION so you can verify remotely which firmware version is running.

Typical installation flow

  1. First boot – LCM starts in AP mode and exposes the captive portal.
  2. Network provisioning – The installer links the device to a Wi-Fi network and stores the GitHub repository that hosts signed firmware releases.
  3. Firmware acquisition – LCM downloads main.bin and main.bin.sig, validates both files, and activates the firmware upon success.
  4. Normal operation – Your application firmware now runs while LCM remains on standby to handle OTA updates and recovery tasks.

Building LCM with ESP-IDF

  1. Install ESP-IDF by following the official Espressif documentation.

  2. Select the correct target for your board:

    idf.py set-target esp32        # or esp32s2, esp32s3, esp32c3, ...
  3. Build the lifecycle manager firmware:

    idf.py build

    The resulting main.bin will be placed inside the build/ directory.

ESP-IDF compatibility status

  • Primary validated line: ESP-IDF v5.4.2 (full build + tests in CI).
  • ESP-IDF v6.0 status: pending full CI validation; use staging before production rollout.
  • Officially targeted by this repository: esp32, esp32s2, esp32s3, esp32c2, esp32c3.
  • Not currently targeted: esp32c5, esp32c6, esp32c61.

Signing main.bin

Every firmware release must be accompanied by a signature file generated by generate_sig.sh (ECDSA P-256 metadata/signature blob expected by the OTA verifier in this project).

Standard flow (out-of-the-box demo keys)

After cloning this repository, you can sign immediately with the included demo key:

./generate_sig.sh build/main.bin

You can also point directly to any firmware image path, for example:

./generate_sig.sh examples/led/build/main.bin

Advanced flow (custom keys)

Use your own signing key by passing it as the second argument:

./generate_sig.sh build/main.bin /path/to/ota_signing_private.pem

To make LCM verify your custom key, set LCM_USE_CUSTOM_OTA_PUBLIC_KEY=y and provide LCM_CUSTOM_OTA_PUBLIC_KEY_PEM (single-line PEM with \n escapes) in menuconfig or sdkconfig.defaults.

Upload both files to a GitHub release (for example version 0.0.1). Follow Semantic Versioning when deciding on the version number:

  • Increase MAJOR for incompatible API changes.
  • Increase MINOR when you add backward-compatible functionality.
  • Increase PATCH for backward-compatible bug fixes.

Flash instructions by chipset

Use esptool.py (installable via pip install esptool) and replace the filenames with the artifacts produced by your build.

ESP32

python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 4MB --flash_freq 40m \
  0x1000 bootloader.bin \
  0x8000 partition-table.bin \
  0xe000 ota_data_initial.bin \
  0x20000 esp32-lifecycle-manager.bin

ESP32-S2

python -m esptool --chip esp32s2 -b 460800 --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 4MB --flash_freq 80m \
  0x1000 bootloader.bin \
  0x8000 partition-table.bin \
  0xe000 ota_data_initial.bin \
  0x20000 esp32-lifecycle-manager.bin

ESP32-S3

python -m esptool --chip esp32s3 -b 460800 --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 4MB --flash_freq 80m \
  0x1000 bootloader.bin \
  0x8000 partition-table.bin \
  0xe000 ota_data_initial.bin \
  0x20000 esp32-lifecycle-manager.bin

ESP32-C2

python -m esptool --chip esp32c2 -b 460800 --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 4MB --flash_freq 60m \
  0x1000 bootloader.bin \
  0x8000 partition-table.bin \
  0xe000 ota_data_initial.bin \
  0x20000 esp32-lifecycle-manager.bin

ESP32-C3

python -m esptool --chip esp32c3 -b 460800 --before default_reset --after hard_reset \
  write_flash --flash_mode dio --flash_size 4MB --flash_freq 80m \
  0x1000 bootloader.bin \
  0x8000 partition-table.bin \
  0xe000 ota_data_initial.bin \
  0x20000 esp32-lifecycle-manager.bin

ESP32-C5 / ESP32-C6 / ESP32-C61

These targets are currently not part of the official targeted set in this repository. Bring-up can be done experimentally, but production support is only claimed for the targets listed above.

Reset and recovery recap

  • Software factory reset – Triggered through the LCM API to wipe stored configuration and reboot.
  • Hardware factory reset – Power-cycle or reset the device 10 consecutive times. LCM detects the pattern, waits roughly 11 seconds, and then executes the factory reset.

Armed with these steps you can quickly provision an ESP32 device, deploy your own firmware, and keep it managed remotely through LCM.

Releases

Packages

Contributors

Languages