Skip to content

esp32s3_zx3d50ce02s_usrc_4832_factory_app_main

esp32s3_zx3d50ce02s_usrc_4832_factory_app_main

Section titled “esp32s3_zx3d50ce02s_usrc_4832_factory_app_main”

Factory recovery application entry point and orchestration logic for the ESP32S3-ZX3D50CE02S-USRC-4832 board (480×320, ST7796 via Intel 8080). This module implements the complete recovery menu: hardware bring-up, OTA backup restore, touch-navigated menu, SD-card firmware flashing, UI resources flashing, and optional screen snapshot capture.


  1. Module Role and Scope
  2. Board Context
  3. Architecture Overview
  4. Module Decomposition
  5. Hardware Initialization Sequence
  6. OTA Backup Restore Mechanism
  7. Menu System
  8. Flash Operations
  9. Display Layout
  10. Virtual Button and Touch System
  11. Snapshot System
  12. Logging System
  13. Key Constants and Configuration
  14. Data Flow
  15. Related Modules

This module is the top-level orchestrator of the factory recovery application for the ZX3D50CE02S-USRC-4832 board. It runs from the ESP32-S3’s factory OTA partition and is entered exclusively via the software [ESP444]FACTORY command issued by the main pendant firmware — not through a physical button held at boot (the board has no physical buttons).

What this module owns:

ResponsibilityFunction
Application entry pointapp_main()
Hardware init orchestrationOrdered init of display → backlight → gfx → buttons → encoder → touch → buzzer
OTA backup restorerestore_otadata_from_backup()
Menu rendering and navigationdraw_menu(), menu_move(), menu_select()
Partition boot switchingboot_partition(), action_boot_partition()
Firmware OTA flash from SDaction_sd_update()
UI resources flash from SDaction_sd_update_res()
Input dispatch (touch + physical)dispatch_button(), touch_hint_hit_test()
Progress and result renderingdraw_progress(), draw_result()
Screen snapshot (optional)snapshot_take(), snapshot_check()

What this module does NOT own (delegated to sibling modules):


PropertyValue
SoCESP32-S3
Board IDESP32S3-ZX3D50CE02S-USRC-4832
Display480×320, ST7796 controller via Intel 8080 (I80) parallel bus
Display driverst7796_i80 (see esp32s3_zx3d50ce02s_usrc_4832_factory_app_display.md)
Touch controllerCapacitive (shares RESET GPIO with display)
Physical buttonsNone — all GPIO_NUM_NC
Physical encoderNone — GPIO_NUM_NC
BuzzerNone — GPIO_NUM_NC
IO expanderNone (unlike esp32s3_hmi43v3 which uses TCA9554)
BacklightPlain active-high GPIO (TFT_LED)
Custom bootloaderNone — no physical trigger mechanism exists
Factory entry pathSoftware only: main firmware [ESP444]FACTORY command

The absence of physical inputs is the primary design difference from boards like pibot_pendant_v1_0 or esp32s3_hmi43v3. All user interaction occurs through the virtual button hints rendered at the bottom of the touchscreen.


Sibling Modules

Actions

User Input Sources

Recovery Logic (main.c)

Hardware Bring-up

Factory Recovery Partition

Main Firmware (pendant app)

sets boot partition to factory

backs up otadata

touch_hint_hit_test()

dispatch_button()

dispatch_button()

dispatch_button()

gfx_*() calls

buttons_*, touch_*, encoder_*

sdcard_mount/unmount

[ESP444]FACTORY command

app_main()

main.c

st7796_i80_init()

(resets touch too)

gpio_config / gpio_set_level

(TFT_LED, active-high)

gfx_init()

buttons_init() / encoder_init()

touch_init() / buzzer_init()

restore_otadata_from_backup()

Build menu_items[]

probe_sd_files()

draw_menu()

Main Event Loop

touch_read()

button_wait_press()

encoder_read()

action_boot_partition()

boot_partition()

action_sd_update()

OTA API flash

action_sd_update_res()

Partition direct write

factory_app_display

gfx + st7796_i80

factory_app_input

buttons/encoder/touch/buzzer

factory_app_storage

sdcard_mount/unmount


The esp32s3_zx3d50ce02s_usrc_4832_factory_app_main module consists of exactly two source files:

esp32s3_zx3d50ce02s_usrc_4832_factory_app_main

#include

main.c

All recovery logic

UI draw, OTA, SD flash

factory_log.h

Conditional debug logging

SD stack silencing

main.c functional groups

OTA Backup/Restore

restore_otadata_from_backup()

Partition Helpers

boot_partition()

get_active_ota_label()

SD Probe

probe_sd_files()

Menu System

menu_item_t, menu_move()

menu_select(), draw_menu_item()

UI Drawing

draw_header(), draw_menu()

draw_footer_zone(), draw_progress()

draw_flashing_screen(), draw_result()

draw_sd_indicators()

Button Hint Icons

draw_circle(), draw_up_arrow()

draw_down_arrow(), draw_check_mark()

draw_button_hint_at(), draw_button_hints()

draw_button_hint_pressed()

Touch Navigation

touch_hint_hit_test()

Actions

action_boot_partition()

action_sd_update()

action_sd_update_res()

Input Dispatch

dispatch_button()

execute_selected_action()

Snapshot (optional)

snap_find_next_number()

snapshot_take()

snapshot_check()

Entry Point

app_main()

Status Bar

show_status(), clear_status()

SymbolBehaviour
FACTORY_LOGD(tag, fmt, ...)Expands to ESP_LOGI if FACTORY_LOG_LEVEL != 0, otherwise to do {} while (0)
FACTORY_LOG_LEVELSet by ENABLE_FACTORY_DEBUG_LOG in Factory/CMakeLists.txt; default 0 (silent)
factory_log_silence_sd_stack()Calls esp_log_level_set() on 8 SD/FAT driver tags to suppress runtime chatter

ESP_LOGW and ESP_LOGE always remain active regardless of FACTORY_LOG_LEVEL.


The init order in app_main() is strictly constrained by shared hardware lines:

buzzer_init()touch_init()encoder_init()buttons_init()gfx_init()Backlight GPIOst7796_i80_init()app_main()buzzer_init()touch_init()encoder_init()buttons_init()gfx_init()Backlight GPIOst7796_i80_init()app_main()CRITICAL: Display RESET GPIO is physicallyshared with touch controller RESET.st7796_i80_init() issues the panel resetpulse that also brings touch out of reset.Active-high, no IO expander,no PWM on this boardFrame buffer and font setupNo-op - BUTTON_n_PIN all NCNo-op - ENCODER_n_PIN all NCMust run AFTER st7796_i80_init().touch.c rst_pin is NC.No-op - BUZZER_PIN is NCst7796_i80_init()ESP_OK or abort()gpio_config(TFT_LED, OUTPUT)gpio_set_level(TFT_LED, 1)gfx_init()buttons_init()encoder_init()touch_init()buzzer_init()

Critical constraint: On this board, touch_init() must always be called after st7796_i80_init(). The display and touch controller share the same TFT_RST_PIN GPIO. If touch_init() tried to toggle reset independently before the display init, it could corrupt the panel’s state. This is unlike boards with an IO expander (e.g., esp32s3_hmi43v3 using TCA9554) where the lines can be controlled independently.


The main pendant firmware writes a backup of the ESP32’s OTA metadata (otadata partition) to a reserved flash sector before switching the boot partition to factory. This ensures that if the device powers off during recovery, a subsequent boot still targets the correct main firmware partition (app0) rather than looping back to factory indefinitely.

Flash address space
┌────────────────────┬──────────┐
│ 0x0000 Bootloader │ │
├────────────────────┤ │
│ 0x8000 ... (gap) │ │
├────────────────────┤ │
│ 0xB000 BACKUP │ ← OTADATA_BACKUP_OFFSET (4 KB sector)
│ sector │ Written by main firmware (esp444.cpp)
│ +0x00 entry1[32] │ Magic at +0x40 = 0xAA55AA55
│ +0x20 entry2[32] │
│ +0x40 MAGIC[4] │
├────────────────────┤ │
│ 0xC000 Partition │ │
│ table │ │
├────────────────────┤ │
│ 0xD000 NVS part. │ │
├────────────────────┤ │
│ 0x10000 otadata │ ← OTADATA_OFFSET (2×4 KB)
│ entry1[32] │ Restored to here from backup
│ entry2[32] │
├────────────────────┤ │
│ ... app0 │ │
│ ... app1 │ │
│ ... factory │ ← We are here
│ ... ui_res │ │
└────────────────────┘

No

Yes

Yes

No

Yes

No

Yes

No

restore_otadata_from_backup()

esp_flash_read(NULL, &magic,

OTADATA_BACKUP_OFFSET + 0x40, 4)

magic == 0xAA55AA55?

FACTORY_LOGD: No backup found

return false

Read entry1[32] and entry2[32]

from OTADATA_BACKUP_OFFSET

Both entries

all 0xFF?

ESP_LOGW: empty backup

goto clear_backup

esp_flash_erase_region(NULL,

0x10000, 0x1000)

esp_flash_erase_region(NULL,

0x11000, 0x1000)

entry1 not empty?

esp_flash_write(NULL, entry1, 0x10000, 32)

entry2 not empty?

esp_flash_write(NULL, entry2, 0x11000, 32)

esp_flash_erase_region(NULL,

0xB000, 0x1000)

(prevents re-restore on next boot)

return true

ConstraintDetail
OTADATA_BACKUP_OFFSET alignmentMust be 4 KB-aligned
OTADATA_BACKUP_OFFSET rangeAfter bootloader end, before partition table at 0xC000
No partition overlapFirst partition (NVS) starts at 0xD000
CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED=yRequired in factory sdkconfig — addresses below the first partition are written directly. The factory app is a privileged recovery tool; this is intentional.
Must match main firmwareOTADATA_BACKUP_OFFSET and BACKUP_MAGIC values must be identical in both main.c and the main firmware’s esp444.cpp.

Menu items are built dynamically at startup based on detected partitions and the static menu_item_t struct:

typedef struct {
const char *label; // Display label
menu_action_t action; // Action enum
uint16_t color; // Text color when not selected
} menu_item_t;
ConditionItems Added
Always"Boot app0" (MENU_ACTION_BOOT_APP0)
has_app1 == true"Boot app1" (MENU_ACTION_BOOT_APP1)
Always"SD -> app0" (MENU_ACTION_SD_UPDATE_APP0)
has_app1 == true"SD -> app1" (MENU_ACTION_SD_UPDATE_APP1)
Always"SD -> resources" (MENU_ACTION_SD_UPDATE_RES)

has_app1 is detected with esp_partition_find_first(APP, ANY, "app1") at startup. On this board variant, only app0 is shipped by default.

Static state:
menu_items[] — array of up to MENU_MAX_ITEMS (8) items
menu_count — actual item count (3 without app1, 5 with app1)
menu_selected — index of currently highlighted item
last_status_msg — footer status text (or "" for default message)
last_status_color — footer text color
sd_has_fw — true if /sdcard/esp3dfw.bin exists
sd_has_res — true if /sdcard/ui_resources.bin exists
has_app1 — true if app1 partition found

Navigation (menu_move)

Yes

No

Yes

No

menu_move(-1)

direction = -1

target = selected + direction

menu_move(+1)

direction = +1

target < 0?

target = count - 1

target >= count?

target = 0

menu_select(target)

menu_select() additionally calls clear_status() if a status message is showing, then redraws only the two affected menu items (old and new selection) rather than the full screen.


Flashes esp3dfw.bin from the SD card to a target OTA partition using the ESP-IDF OTA API.

draw_flashing_screen() + draw_progress()esp_ota_begin/write/endfopen(FW_FILENAME)sdcard_mount()esp_partition_find_first()action_sd_update()draw_flashing_screen() + draw_progress()esp_ota_begin/write/endfopen(FW_FILENAME)sdcard_mount()esp_partition_find_first()action_sd_update()loop[1 KB chunks until fw_size written]Find target partition by labelupdate_part or NULL → show_status errorsdcard_mount()ESP_OK or → show_status errorfopen("/sdcard/esp3dfw.bin", "rb")fw_file or NULL → show_status errorValidate fw_size (>0, ≤partition size)draw_flashing_screen()esp_ota_begin(update_part, fw_size, &handle)fread(buf, 1, 1024, fw_file)read_len bytesesp_ota_write(handle, buf, read_len)draw_progress(percent) on changeesp_ota_end(handle)esp_ota_set_boot_partition(update_part)rename esp3dfw.bin → esp3dfw.ok (or .bad)sdcard_unmount()draw_result(ok, ...) then esp_restart() or draw_menu()

Error handling: If any step fails, esp_ota_abort() is called, the file is renamed to .bad, and the recovery menu is redrawn. Heap size is logged before and after SD mount to aid diagnosing allocation failures on constrained builds.

8.2 Resources Flash (action_sd_update_res)

Section titled “8.2 Resources Flash (action_sd_update_res)”

Flashes ui_resources.bin from the SD card to the ui_resources partition using direct partition write (not OTA API, since it is a data partition).

draw_flashing_screen() + draw_progress()esp_partition_erase_range + writefopen(RES_FILENAME)sdcard_mount()esp_partition_find_first(DATA, ANY, "ui_resources")action_sd_update_res()draw_flashing_screen() + draw_progress()esp_partition_erase_range + writefopen(RES_FILENAME)sdcard_mount()esp_partition_find_first(DATA, ANY, "ui_resources")action_sd_update_res()Reads first 16 bytes to check build header:'ESP3' magic + 12-char variant string.Logs variant for mismatch detection.loop[1 KB chunks until res_size written]Find "ui_resources" data partitionsdcard_mount()fopen("/sdcard/ui_resources.bin", "rb")Validate res_size (>0, ≤partition size)draw_flashing_screen()esp_partition_erase_range(res_part, 0, res_part->size)fread(buf, 1, 1024, res_file)esp_partition_write(res_part, offset, buf, read_len)draw_progress(percent) on changerename ui_resources.bin → .ok or .badsdcard_unmount()esp_restart() on success, or draw_menu() on failure

Build header check: The first 16 bytes of ui_resources.bin contain "ESP3" + a 12-character variant string (generated by generate_resources.py). This is logged for early detection of board/transport mismatches — the flash is not blocked by a mismatch, only a warning is logged.

StageFirmware filenameResources filename
Before flashesp3dfw.binui_resources.bin
After successesp3dfw.okui_resources.ok
After failureesp3dfw.badui_resources.bad

This prevents re-flashing a broken binary on subsequent entries if the rename succeeds before the reboot.


The recovery UI uses font8×16 (not font12×24) because the 480 px canvas is narrower than boards that use the wider font (e.g., esp32s3_hmi43v3 at 800 px). The 320 px height provides more vertical room than some other boards (e.g., esp32s3_4827s043c at 272 px), leaving extra margin with the same layout constants.

┌──────────────────────────────────────────────────┐ y=0
│ Recovery v1.x.x (COLOR_CYAN) │ y=13
│──────────────────────────────────────── y=40 ────│
│ Active: app0 (default) (YELLOW) │ y=45
│ SD: FW RES (right-aligned, GREEN/CYAN) │ y=68
│──────────────────────────────────────── y=88 ────│
│ ┌────────────────────────────────────┐ │ y=94 MENU_START_Y
│ │ Boot app0 ← highlight box │ item 0 │ MENU_ITEM_H=34
│ └────────────────────────────────────┘ │
│ Boot app1 │ item 1
│ SD -> app0 │ item 2
│ SD -> app1 │ item 3
│ SD -> resources │ item 4
│ │
│─────────────────────────────── STATUS_Y-7 ───────│
│ Power off to cancel (or status message) │ STATUS_Y
│─────────────────────────────── BTN_HINT_BASE_Y ──│
│ ↑ ↓ ✓ │ BTN_HINT_CY
│ (BTN_1/blue) (BTN_2/blue) (BTN_3/green) │
└──────────────────────────────────────────────────┘ y=319
x=0 x=479
ConstantValueDescription
MENU_START_Y94Y of first menu item
MENU_ITEM_H34Height per menu item (including gap)
MENU_PAD_X20Left/right padding for menu items
FONT_WIDTH8Pixel width of one character
FONT_HEIGHT16Pixel height of one character
BTN_CIRCLE_R22Radius of virtual button circles
BTN_HINT_H53Total height of button hint bar (2×22+9)
STATUS_YSCREEN_HEIGHT - 28 - BTN_HINT_HY of status/footer line
BTN_HINT_BASE_YSCREEN_HEIGHT - BTN_HINT_HY of hint bar top separator
BTN_HINT_CYBTN_HINT_BASE_Y + 5 + BTN_CIRCLE_RCentre Y of all hint circles
BTN_HINT_CX1SCREEN_WIDTH/2 - 100 = 140Centre X: up button
BTN_HINT_CX2SCREEN_WIDTH/2 = 240Centre X: down button
BTN_HINT_CX3SCREEN_WIDTH/2 + 100 = 340Centre X: OK button
ConstantRGB valuesUsage
MENU_HIGHLIGHTGFX_RGB565(0,80,160)Selection box fill
MENU_HIGHLIGHT_TXTGFX_RGB565(80,160,255)Selected item text
BTN_NAV_COLORGFX_RGB565(100,160,255)Up/down button icons
BTN_OK_COLORGFX_RGB565(100,220,100)OK/select button icon
BTN_PRESSED_COLORGFX_RGB565(180,80,220)Touch press feedback flash

Because the board has no physical buttons, the only real input is the capacitive touchscreen. The recovery app renders three virtual button hints at the bottom of the screen and maps touch coordinates to them.

Virtual ButtonIconActionCircle Centre X
BTN_1↑ Up arrowmenu_move(-1)140 (SCREEN_WIDTH/2 - 100)
BTN_2↓ Down arrowmenu_move(+1)240 (SCREEN_WIDTH/2)
BTN_3✓ Check markexecute_selected_action()340 (SCREEN_WIDTH/2 + 100)

The hit test uses column midpoints between button centres, not screen thirds, because the icons are clustered around screen centre. Using screen thirds would map BTN_1 and BTN_3 to the wrong columns.

Yes

No

Yes

No

Yes

No

touch_hint_hit_test(x, y)

y < BTN_HINT_BASE_Y?

return BTN_NONE

boundary_1_2 = (CX1 + CX2) / 2

= (140 + 240) / 2 = 190

boundary_2_3 = (CX2 + CX3) / 2

= (240 + 340) / 2 = 290

x < 190?

x < 290?

return BTN_1 (up)

return BTN_2 (down)

return BTN_3 (OK)

Yes

No

Yes

No

touch = touch_read()

touch.pressed &&

!touch_was_pressed?

vbtn = touch_hint_hit_test(touch.x, touch.y)

vbtn != BTN_NONE?

draw_button_hint_pressed(vbtn)

buzzer_beep_short()

vTaskDelay(80ms)

draw_button_hints()

dispatch_button(vbtn)

touch_was_pressed = touch.pressed

The touch_was_pressed edge-detection (pressed && !was_pressed) prevents repeated triggers from a held finger. The violet flash (BTN_PRESSED_COLOR) and 80 ms delay provide haptic-equivalent visual feedback before the action executes.

All icon primitives are built from gfx_hline() spans only (no diagonal line support required):

FunctionIconGeometry
draw_circle(cx,cy,r,color)Circle outlineMidpoint algorithm, 8-way symmetry
draw_up_arrow(cx,cy,color)↑ Arrow7-row head + 10-row stem; fits inside r=22
draw_down_arrow(cx,cy,color)↓ Arrow10-row stem + 7-row head; fits inside r=22
draw_check_mark(cx,cy,color)✓ Checkmark4 px thick, two arms with junction row

Each button circle is drawn twice (radii r and r-1) for a bolder outline without a fill.


The snapshot system is conditionally compiled via #ifdef ENABLE_SNAPSHOT. When enabled, pressing GPIO0 (the ESP32-S3 BOOT button on the devkit footprint) captures the current screen state to a .raw file on the SD card. This is primarily a development and documentation aid.

No

Yes

No

Yes

snapshot_check()

called each main loop iteration

and at key flash milestones

gpio_get_level(GPIO_NUM_0) == 0?

vTaskDelay(50ms)

still pressed?

snapshot_take()

Wait for GPIO0 release

+ 50ms debounce

return (no snapshot)

Full screen redrawgfx_snapshot_begin/end()sdcard_mount()snapshot_take()Full screen redrawgfx_snapshot_begin/end()sdcard_mount()snapshot_take()All subsequent gfx_* callswrite pixels to both display and filesdcard_mount()snap_find_next_number() on first call\n(scans /sdcard/snap000.raw .. snap999.raw)gfx_snapshot_begin("/sdcard/snapdraw_menu() - or - draw_flashing_screen() + draw_progress()gfx_snapshot_end()s_snapshot_count++
VariablePurpose
s_snap_in_flashtrue during OTA/resource flash; snapshot redraws the flash progress screen instead of the menu
s_flash_last_percentLast progress percentage; passed to draw_progress() when redrawing during a flash snapshot
s_snapshot_countMonotonically incremented after each capture; initialized on first snapshot by scanning existing files

snapshot_check() is also called at these strategic flash checkpoints: after draw_flashing_screen(), at each progress percentage update, and after draw_result().


ON → =1

OFF → =0

ENABLE_FACTORY_DEBUG_LOG

(Factory/CMakeLists.txt option)

FACTORY_LOG_LEVEL

(compile-time define:

0=silent, 1=active)

FACTORY_LOGD(tag, fmt, ...)

→ ESP_LOGI if level≠0

→ do {} while(0) if level=0

factory_log_silence_sd_stack()

→ esp_log_level_set() × 8 tags

→ ESP_LOG_NONE at runtime

factory_log_silence_sd_stack() (first call in app_main) sets these tags to ESP_LOG_NONE:

"sdmmc" "vfs_fat_sdmmc" "sdmmc_periph" "sdmmc_req"
"sdmmc_common" "fatfs" "sdspi" "sd_diskio"

These are silenced because:

  • Production builds (level 0): sdkconfig.prod_log already suppresses them, but runtime silencing ensures correctness if the sdkconfig baseline is ever changed.
  • Debug builds (level 1): sdkconfig ceiling is raised to INFO for everything, which makes the SD stack verbose and obscures the factory app’s own logic.

ESP-IDF internal startup logs emitted before app_main() are handled separately via sdkconfig.prod_log, not this runtime mechanism.


ConstantValueNotes
OTADATA_OFFSET0x10000Standard ESP32-S3 otadata address
OTADATA_SECTOR_SIZE0x10004 KB
OTADATA_ENTRY_SIZE32Bytes per OTA entry
OTADATA_BACKUP_OFFSET0xB000Must match esp444.cpp in main firmware
BACKUP_MAGIC_OFFSET0x40Byte offset within backup sector for magic word
BACKUP_MAGIC0xAA55AA55Sentinel; must match main firmware
ConstantPathDescription
FW_FILENAME/sdcard/esp3dfw.binMain firmware binary
FW_OK_FILENAME/sdcard/esp3dfw.okRename target on success
FW_BAD_FILENAME/sdcard/esp3dfw.badRename target on failure
RES_FILENAME/sdcard/ui_resources.binUI resources binary
RES_OK_FILENAME/sdcard/ui_resources.okRename target on success
RES_BAD_FILENAME/sdcard/ui_resources.badRename target on failure
uint8_t buf[1024]; // 1 KB static read buffer — shared by both flash operations

A 1 KB static buffer avoids heap fragmentation during recovery. Both action_sd_update() and action_sd_update_res() reuse this pattern with separate local declarations.

ConstantValueDescription
MENU_MAX_ITEMS8Maximum total menu entries (array size)
Typical count (no app1)3Boot app0, SD→app0, SD→resources
Typical count (with app1)5+ Boot app1, SD→app1

Yes

No

Yes

No

No

Yes

Yes, none

No, found

app_main() - init complete

menu drawn

Main loop (infinite)

snapshot_check()

if ENABLE_SNAPSHOT

encoder_read()

enc > 0?

enc < 0?

menu_move(-1)

CW = up

menu_move(+1)

CCW = down

button_wait_press(100ms timeout)

dispatch_button(btn)

touch_read()

pressed &&

!was_pressed?

touch_hint_hit_test(x,y)

BTN_NONE?

draw_button_hint_pressed

buzzer_beep_short

vTaskDelay(80ms)

draw_button_hints

dispatch_button(vbtn)

touch_was_pressed = touch.pressed

Yes

No

Yes

No

Yes

dispatch_button(btn)

BTN_1?

BTN_2?

BTN_3?

menu_move(-1)

menu_move(+1)

execute_selected_action()

switch(menu_items[selected].action)

action_boot_partition('app0')

→ esp_ota_set_boot_partition

→ esp_restart()

action_boot_partition('app1')

→ esp_ota_set_boot_partition

→ esp_restart()

action_sd_update('app0')

→ OTA API write

→ esp_restart() on OK

action_sd_update('app1')

→ OTA API write

→ esp_restart() on OK

action_sd_update_res()

→ direct partition write

→ esp_restart() on OK


This module orchestrates the recovery application by calling into all sibling modules of esp32s3_zx3d50ce02s_usrc_4832_factory_app. Documentation for each subsystem:

ModuleDocumentationResponsibility
esp32s3_zx3d50ce02s_usrc_4832_factory_app_displayesp32s3_zx3d50ce02s_usrc_4832_factory_app_display.mdGFX abstraction layer (gfx_*) and ST7796 I80 driver (st7796_i80_init, st7796_i80_flush_ready_cb)
esp32s3_zx3d50ce02s_usrc_4832_factory_app_inputesp32s3_zx3d50ce02s_usrc_4832_factory_app_input.mdPhysical input drivers: buttons, encoder, touch (touch_read, touch_point_t), buzzer — all are no-ops on this board
esp32s3_zx3d50ce02s_usrc_4832_factory_app_storageesp32s3_zx3d50ce02s_usrc_4832_factory_app_storage.mdSD card mount/unmount (sdcard_mount, sdcard_unmount)
esp32s3_zx3d50ce02s_usrc_4832_factory_app_toolsesp32s3_zx3d50ce02s_usrc_4832_factory_app_tools.mdHost-side tooling: flash_all.py, flash_factory.py, font generator, raw-to-PNG converter
esp32s3_zx3d50ce02s_usrc_4832_bspesp32s3_zx3d50ce02s_usrc_4832_bsp.mdBoard support package for the main pendant firmware (LVGL integration, board_init, I80 vsync callback) — separate from the factory app

For analogous factory apps on other boards:

For project-level reference documentation:

  • docs/Factory/ — factory app and bootloader documentation
  • docs/guides/esp32_memory_constraints.md — heap and fragmentation reference
  • docs/guides/board_build_guidelines.md — per-board build configuration
  • docs/ui_resources/development.md — ui_resources partition binary format and update mechanisms