Update Service
Section titled “Update Service”Overview
Section titled “Overview”The Update Service (ESP3DUpdateService) is the central orchestrator for all SD-card-based update operations on the pendant firmware. It handles six distinct update types — firmware (OTA), configuration settings (INI), UI resources (images + fonts partition), individual resource patches, theme colour palettes, and language packs — all sourced from the SD card.
The service runs as a synchronous, blocking operation during boot (before the LVGL UI task starts), and exposes a progress callback so the Update Screen can render live feedback via the ESP3DValues observable system.
A global singleton esp3dUpdateService is declared in the header and is the only instance used throughout the firmware.
Architecture
Section titled “Architecture”Module Location
Section titled “Module Location”| File | Role |
|---|---|
main/modules/update/esp3d_update_service.h | Class declaration, enums, file path macros |
main/modules/update/esp3d_update_service.cpp | All update logic implementation |
Update Types
Section titled “Update Types”The ESP3DUpdateType enum defines the priority-ordered set of updates the service can detect and apply:
Only one update type is executed per boot cycle. Once a higher-priority update file is found, lower-priority files are ignored until the next boot.
| Enum Value | Priority | Source File(s) | Flash Target |
|---|---|---|---|
firmware | 1 (highest) | /esp3dfw.bin | OTA partition (ota_1) |
config | 2 | /esp3dcnf.ini | NVS (all settings) |
resources | 3 | /ui_resources.bin | ui_resources data partition |
theme_colors | 4 | /esp3dtheme.ini | ui_resources partition (palette slots) |
language_packs | 5 | /ui_*.lng (any matching file) | ui_resources partition (language slots) |
sounds (a14) | 6 | /esp3dsound.ini (partial: events it does not mention keep their sound) | ui_resources partition (sounds slot), via esp3d_sound_update_from_sd() |
resource_patches | 7 (lowest) | /esp3dres/*.bin, /esp3dres/*.fnt | ui_resources partition (individual entries) |
none | — | — | — |
Note:
firmwareupdates require a valid second OTA slot (ota_1). Boards with a single-app-slot layout cannot self-update firmware — the service detects this viacanUpdate()and skips the firmware check entirely, but still applies resource/config/theme/language updates.
File Naming Convention
Section titled “File Naming Convention”After an update, the service renames the source file to signal the outcome. If a .ok or .bad file already exists from a previous run, the old file is first renamed with a numeric suffix (*.ok1, *.ok2, …) to preserve history.
| Source | On Success | On Failure |
|---|---|---|
/esp3dfw.bin | /esp3dfw.ok | /esp3dfw.bad |
/esp3dcnf.ini | /esp3dcnf.ok (secrets scrambled) | /esp3dcnf.bad |
/ui_resources.bin | /ui_resources.ok | /ui_resources.bad |
/esp3dtheme.ini | /esp3dtheme.ok | /esp3dtheme.bad |
/ui_*.lng | /ui_*.ok (renamed by translation service) | /ui_*.bad |
/esp3dsound.ini | /esp3dsound.ok | /esp3dsound.bad |
Dependency Graph
Section titled “Dependency Graph”- Storage & Configuration — Filesystem — SD card access is gated through
sd.accessFS()/sd.releaseFS(). All file I/O goes via theESP3DSDabstraction. - Storage & Configuration — Config File — INI parsing and
.okfile generation (with secret scrambling) are delegated entirely toESP3DConfigFile. - Core Platform — Settings — Setting validation and NVS writes go through
ESP3DSettings. - UI Framework — Screens — The update screen subscribes to
ESP3DValuesIndex::update_progressandupdate_statusto render the progress bar and status text in real time. - UI Framework — Resources —
esp3d_resources_patch_sd()applies individual binary/font patches to theui_resourcespartition. See also UI Resources — Development Reference.
Detection Flow
Section titled “Detection Flow”Firmware Update Flow
Section titled “Firmware Update Flow”Firmware updates use the ESP-IDF OTA API. The service validates that a genuine second OTA slot exists (single-slot boards are rejected by canUpdate()) and that the binary fits within the partition before touching flash.
Memory note: The 1 KB chunk buffer is heap-allocated (
malloc(CHUNK_BUFFER_SIZE)) rather than stack-allocated, to avoid stack overflow. It is freed immediately after the write loop.
UI Resources Update Flow
Section titled “UI Resources Update Flow”The ui_resources partition holds all images, fonts, theme data, and language slots used by LVGL. Before erasing the partition the binary is validated against an ESP3 magic header and the board-specific RESOURCES_VARIANT key to prevent flashing a wrong-board image onto a healthy partition.
Build trailer (a14): the variant key does not tell boards, resolutions or releases apart, so the last 96 bytes of the file carry the exact build (installer variant name and firmware version). A file without trailer (“kit from an older firmware?”) fails with Resources file not built for this firmware; a mismatch with Resources file built for another firmware. A kit therefore only works for one variant and one firmware version; see tools_build_scripts_ui_resources.md.
For details on the binary format validated by this header check, see UI Resources — Development Reference.
Configuration Update Flow
Section titled “Configuration Update Flow”Config updates parse an INI-formatted file (/esp3dcnf.ini) and write each key/value pair into NVS via ESP3DSettings. After a successful parse, revokeFile() renames the INI to .ok and scrambles the values of protected security keys so credentials are not left in plain text on the SD card.
Protected Keys (Scrambled in .ok File)
Section titled “Protected Keys (Scrambled in .ok File)”The following settings are automatically obfuscated when the .ini is renamed to .ok, so sensitive credentials are never stored in clear text on the SD card after a successful config update:
| Key | Setting |
|---|---|
NOTIF_TOKEN1 | Notification service token 1 |
NOTIF_TOKEN2 | Notification service token 2 |
AP_Password | Wi-Fi access-point password |
STA_Password | Wi-Fi station password |
ADMIN_PASSWORD | HTTP admin password |
USER_PASSWORD | HTTP user password |
BT_Pin | Bluetooth PIN |
BLE_Passkey | BLE passkey |
INI Special Value Mapping
Section titled “INI Special Value Mapping”Certain settings require string-to-enum translation before being written to NVS. processSpecialValue() handles these before the generic findSettingByIni() lookup. Values equal to the scramble sentinel (HIDDEN_SETTING_VALUE) are silently skipped.
| Section | Key | Accepted String Values |
|---|---|---|
network | radio_mode | BT, STA, AP, SETUP, OFF |
network | sta_fallback | BT, WIFI-SETUP, OFF |
network | STA_IP_mode | DHCP, STATIC |
system | TargetFW | GRBL, GRBLHAL, MARLIN, SMOOTHIEWARE, REPETIER, HP_GL, FLUIDNC, None |
system | output | USB-OTG (USB kept as alias for INI files written before 2026-10-30), USB-CDC (USB CDC device builds, a14), SERIAL, UARTEXT / UART_EXT / EXTERNAL_MODULE |
services | NOTIF_TYPE | None, PushOver, Email, Telegram, IFTTT |
Boolean strings (yes/no, on/off, true/false, 1/0) are accepted for all byte_t settings. Unknown section/key pairs are logged and silently skipped — they are not treated as errors.
Progress Feedback and UI Integration
Section titled “Progress Feedback and UI Integration”The service communicates update progress to the LVGL Update Screen through the ESP3DValues observable layer, keeping the update logic fully decoupled from the display stack.
Font safety during resources update: The update screen uses only the built-in
lv_font_montserrat_14(embedded in the firmware binary) for all labels. It never uses XIP fonts from theui_resourcespartition because that partition may be partially erased during a resources update, making its flash-mapped data unsafe to read.
The ESP3DUpdateWaitBeforeRebootFn hook lets the update screen gate the reboot on its final render cycle completing, instead of the service performing a blind esp3d_hal::wait() sleep of fixed duration.
Status State Machine
Section titled “Status State Machine”Public API Reference
Section titled “Public API Reference”ESP3DUpdateService
Section titled “ESP3DUpdateService”// Lifecyclebool begin(ESP3DUpdateProgressCallback callback = nullptr, bool detectType = true, bool auto_reboot_on_fail = true, uint16_t delay = 1000, ESP3DUpdateWaitBeforeRebootFn waitBeforeReboot = nullptr);void handle(); // reserved for periodic tasks, currently no-opvoid end(); // resets status and pendingUpdate to idle/none
// Detection (does NOT execute the update)ESP3DUpdateType detect();
// Individual executors (called internally by begin())bool executeFirmwareUpdate(ESP3DUpdateProgressCallback callback = nullptr);bool executeConfigUpdate(ESP3DUpdateProgressCallback callback = nullptr);bool executeResourcesUpdate(ESP3DUpdateProgressCallback callback = nullptr);bool executeResourcePatches(ESP3DUpdateProgressCallback callback = nullptr);bool executeThemeColorsUpdate(ESP3DUpdateProgressCallback callback = nullptr);bool executeLangPacksUpdate(ESP3DUpdateProgressCallback callback = nullptr);
// Status queriesESP3DUpdateType getPendingUpdate() const; // set after detect()ESP3DUpdateStatus getStatus() const;size_t getFirmwareSize() const; // valid after detect() if firmware pendingsize_t getResourcesSize() const; // valid after detect() if resources pendingconst char* getErrorMessage() const; // last error string
// Partition capability checksbool canUpdate(); // safe OTA slot available and distinct from runningbool canUpdateResources(); // ui_resources partition present in partition tablesize_t maxUpdateSize(); // OTA partition size in bytes (0 if none)size_t maxResourcesUpdateSize(); // ui_resources partition size in bytes (0 if absent)
// INI file processing callback (static; passed to ESP3DConfigFile)static bool processingFileFunction(const char* section, const char* key, const char* value);Callback Types
Section titled “Callback Types”// Progress notification: percent 0–100, human-readable status stringusing ESP3DUpdateProgressCallback = std::function<void(uint8_t percent, const char* status)>;
// Optional hook to delay/gate reboot after a successful update// (e.g. wait for LVGL to render the final completion screen)using ESP3DUpdateWaitBeforeRebootFn = std::function<void(uint16_t delay_ms)>;File Path Macros
Section titled “File Path Macros”| Macro | Value |
|---|---|
ESP3D_FW_FILE | /esp3dfw.bin |
ESP3D_FW_FILE_OK | /esp3dfw.ok |
ESP3D_FW_FILE_BAD | /esp3dfw.bad |
ESP3D_CONFIG_FILE | /esp3dcnf.ini |
ESP3D_CONFIG_FILE_OK | /esp3dcnf.ok |
ESP3D_CONFIG_FILE_BAD | /esp3dcnf.bad |
ESP3D_RESOURCES_FILE | /ui_resources.bin |
ESP3D_RESOURCES_FILE_OK | /ui_resources.ok |
ESP3D_RESOURCES_FILE_BAD | /ui_resources.bad |
ESP3D_THEME_FILE | /esp3dtheme.ini |
ESP3D_THEME_FILE_OK | /esp3dtheme.ok |
ESP3D_SOUND_FILE / _OK / _BAD (a14) | /esp3dsound.ini / .ok / .bad |
ESP3D_THEME_FILE_BAD | /esp3dtheme.bad |
Build-Time Feature Guards
Section titled “Build-Time Feature Guards”The update service is conditionally compiled based on CMakeLists.txt feature options:
| Guard | Controlled Behaviour |
|---|---|
ESP3D_SD_CARD_FEATURE | All SD-sourced update paths (firmware, config, resources, theme, language, patches) |
ESP3D_DISPLAY_FEATURE | Resource patches, theme colours, language packs (require esp3d_resources and UIManager) |
ESP3D_WIFI_FEATURE | Wi-Fi-specific INI keys (STA_IP_mode, sta_fallback, STA/AP/SETUP radio modes) |
ESP3D_NOTIFICATIONS_FEATURE | NOTIF_TYPE INI key mapping |
ESP3D_USB_SERIAL_FEATURE | USB output client INI value |
ESP3D_UART_EXT_FEATURE | UARTEXT / UART_EXT / EXTERNAL_MODULE output client INI values |
Memory Constraints
Section titled “Memory Constraints”- The chunk buffer (
CHUNK_BUFFER_SIZE= 1024 bytes) is heap-allocated and freed immediately after the write loop. - No large stack allocations exist in any update path.
std::stringis used only for file-suffix collision avoidance inrenameToOk()/renameToBad(), which run after the main flash write — not in the hot path.- All
malloc()calls are guarded; anullptrresult produces a logged error and returnsfalseimmediately.
For general memory budget guidance see ESP32 Memory Constraints.