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.
To make this repository easier to navigate:
- End users / installers / support: see
enduser.mdfor onboarding, OTA operation, reset procedures, and troubleshooting. - Developers / integrators: continue with this
README.mdfor build, signing, and flashing workflows. - Security and operations detail: see
docs/securityanddocs/operations.
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=yand provide your ownLCM_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).
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.
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.
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.binandmain.bin.sig). - Blink the status LED for identification so you can confirm you are working with the correct device during installation or servicing.
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.
You can initiate an over-the-air update through an OTA trigger (for example via an HTTP request) whenever a new release becomes available.
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.
- 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.
During installation or update LCM writes the value of
LIFECYCLE_DEFAULT_FW_VERSION so you can verify remotely which firmware version
is running.
- First boot – LCM starts in AP mode and exposes the captive portal.
- Network provisioning – The installer links the device to a Wi-Fi network and stores the GitHub repository that hosts signed firmware releases.
- Firmware acquisition – LCM downloads
main.binandmain.bin.sig, validates both files, and activates the firmware upon success. - Normal operation – Your application firmware now runs while LCM remains on standby to handle OTA updates and recovery tasks.
-
Install ESP-IDF by following the official Espressif documentation.
-
Select the correct target for your board:
idf.py set-target esp32 # or esp32s2, esp32s3, esp32c3, ... -
Build the lifecycle manager firmware:
idf.py build
The resulting
main.binwill be placed inside thebuild/directory.
- Primary validated line: ESP-IDF
v5.4.2(full build + tests in CI). - ESP-IDF
v6.0status: 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.
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).
After cloning this repository, you can sign immediately with the included demo key:
./generate_sig.sh build/main.binYou can also point directly to any firmware image path, for example:
./generate_sig.sh examples/led/build/main.binUse your own signing key by passing it as the second argument:
./generate_sig.sh build/main.bin /path/to/ota_signing_private.pemTo 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.
Use esptool.py (installable via pip install esptool) and replace the
filenames with the artifacts produced by your build.
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.binpython -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.binpython -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.binpython -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.binpython -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.binThese 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.
- 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.
