Skip to content

esp32_3248s035c_build_scripts

Build automation scripts for the ESP32-3248S035C board (ESP32, 4 MB flash, 480 × 320 ST7796 SPI display with capacitive GT911 touch). These three Python scripts define every named firmware variant for this board and orchestrate the complete build pipeline: UI-resource generation, ESP-IDF compilation, factory-artifact injection, installer assembly, and flash-map generation.


  1. Module Position
  2. File Overview
  3. Variant System
  4. Build Pipeline
  5. Directory & Path Conventions
  6. Component Reference
  7. Environment & Prerequisites
  8. Usage
  9. Cross-Module Dependencies

This module is one of three sibling sub-modules that together form the complete esp32_3248s035c board package. The other two siblings are documented separately:

  • esp32_3248s035c_factory — the Factory app sources whose build output this module copies into every regular variant’s installer directory.
  • esp32_3248s035c_bsp — the Board Support Package compiled into every firmware variant.

The build scripts also invoke utilities from tools_build_scripts for UI resource generation and installer kit packaging.

cmake -D flags

calls

bootloader + partitions

+ factory.bin

compiled into firmware

firmware.bin + ota data

ui_resources_*.bin

flash_mgr.py --generate

esp32_3248s035c_bsp

(board_init, LVGL, touch)

esp32_3248s035c_factory

(factory app, bootloader)

esp32_3248s035c_build_scripts

build_one.py · common.py · variants.py

tools_build_scripts

generate_resources.py

package_user_resources_kit.py

ESP-IDF v5.4.3

(idf.py)

installer/

ESP32_3248S035C_4MB_…/


FileRole
variants.pyDeclares every named build variant as a Python dict with cmake flags, working directory, and build directory. Single source of truth for what can be built.
common.pyImplements the full build pipeline — cleaning, cmake invocation, resource generation, factory artifact injection, installer assembly, and flash-map generation.
build_one.pyThin CLI entry-point. Parses the variant name from sys.argv, then delegates to check_variant() or build_variant() in common.py.

imports VARIANTS, FACTORY_VARIANTS

calls build_variant() / check_variant()

reads cmake / cwd / build_dir

build_one.py

(CLI entry)

common.py

(pipeline logic)

variants.py

(variant registry)


Every variant is a plain Python dict with four keys:

KeyTypeDescription
namestrHuman-readable identifier, also used as the build-directory suffix
cmakelist[str]Ordered list of -D KEY=VALUE arguments passed verbatim to idf.py
cwdstrWorking directory for idf.py (REPO_ROOT for regular variants, FACTORY_DIR for the factory variant)
build_dirstrAbsolute path to the build artifact directory

3.2 Variant Registry (VARIANTS + FACTORY_VARIANTS)

Section titled “3.2 Variant Registry (VARIANTS + FACTORY_VARIANTS)”

VARIANTS (regular firmware)

FACTORY_VARIANTS

artifacts required by

artifacts required by

artifacts required by

artifacts required by

factory_4mb

cwd: Factory/

MEMORY_4_MB=ON

4mb_wifi_fluidnc

WIFI · SOCKET_CLIENT · FluidNC

4mb_serial_fluidnc

SERIAL · FluidNC

4mb_serial_grbl

SERIAL · GRBL

4mb_wifi_grblhal

WIFI · SOCKET_CLIENT · grblHAL

VariantMemoryTransportFirmwareMDNSSocket ClientWebUI
4mb_wifi_fluidnc4 MBWiFi + SerialFluidNC✓✓—
4mb_serial_fluidnc4 MBSerialFluidNC———
4mb_serial_grbl4 MBSerialGRBL———
4mb_wifi_grblhal4 MBWiFi + SerialgrblHAL✓✓—
4mb_serial_marlin4 MBSerialMarlin (a14 groundwork)———
factory_4mb (special)4 MB—Factory App———

Note: SOCKET_CLIENT_SERVICE=ON is used for WiFi CNC control (TCP socket to the machine). WEBUI_SERVER and WS_SERVER_SERVICE are intentionally OFF — the WiFi radio is dedicated to the CNC link. See the feature resource matrix for the rationale.

3.3 make_variant_args() — Flag Generation

Section titled “3.3 make_variant_args() — Flag Generation”

make_variant_args() starts by setting every known cmake option to OFF (DEFAULT_OFF_CMAKE_ARGS), then selectively overrides specific flags to ON. This prevents stale cmake cache values from a previous build with a different profile from leaking into the current one.

# Example output fragment:
make_variant_args("ESP32_3248S035C=ON", "MEMORY_4_MB=ON", "WIFI_SERVICE=ON")
# → [ '-D', 'ESP32_PIBOT_CNC_PENDANT_V1=OFF', …, '-D', 'ESP32_3248S035C=OFF', …,
# '-D', 'ESP32_3248S035C=ON', '-D', 'MEMORY_4_MB=ON', '-D', 'WIFI_SERVICE=ON' ]

ROOT_CMAKE_OPTIONS list

(all board / memory / transport /

firmware / feature flags)

DEFAULT_OFF_CMAKE_ARGS

[ '-D', 'FLAG=OFF', … ]

make_variant_args('ESP32_3248S035C=ON', 'MEMORY_4_MB=ON', …)

cmake args list

[ '-D', 'FLAG=OFF', …, '-D', 'ESP32_3248S035C=ON', … ]

The final list is appended to the idf.py command line so later -D entries override earlier ones for the flags being enabled.


a14 build changes:

  • The installer directory name comes from CMake (cmake/build_config_name.cmake, which appends the log level _LOG_<LEVEL>): installer_config_name(cmake_args, build_dir) reads <build_dir>/build_config_name.txt and falls back to build_config_name() before any configure. copy_factory_artifacts(cmake_args, build_dir) uses it too.
  • build_config_name() (no log suffix) is passed to generate_resources.py as --build-name, so a resources binary fits the variant with or without logs (build trailer, see tools_build_scripts_ui_resources.md).
  • New 4mb_serial_marlin variant (Marlin groundwork, TARGET_FW_MARLIN=ON).

yes

no

yes

no

yes

no

yes

yes

no

no

build_variant(config)

--clean flag?

clean_build_dir(build_dir)

clean installer_dir

return 0

prepare_build_dir(build_dir)

• _ensure_clean_build_dir → wipe if IDF_TARGET mismatch

• write .idf_target marker = 'esp32'

generate_resources(cmake_args, build_dir)

• derive variant string (e.g. '4mb_wifi_fluidnc')

• call generate_resources.py --variant … --resolution res_480_320

• output: ui_resources_*.bin + esp3d_ui_offsets.h

rc != 0?

FAILED: ui_resources generation

run_cmake_build(cmake_args, cwd, build_dir)

• idf.py -B build_dir flags -D PROD_BUILD=ON build

• IDF_TARGET=esp32 forced in env

• CMAKE_BUILD_PARALLEL_LEVEL from --jobs=N

rc != 0?

FAILED: cmake build

_show_size_report(build_dir, cwd)

• idf.py size

cwd == REPO_ROOT?

(regular variant)

copy_factory_artifacts(cmake_args)

• source: Factory/installer/ESP3D-FACTORY_4MB/

• auto-build factory if artifacts missing

• copy bootloader_4MB.bin, partitions_4mb.bin, factory_4MB.bin

• log to installer_history.log

rc != 0?

FAILED: factory artifacts

_copy_ui_resources_bin(build_dir, installer_dir)

• copy ui_resources_*.bin to installer//

_package_user_resources_kit(cmake_args, build_dir, installer_dir)

• call package_user_resources_kit.py

• output: installer//ui_resources_kit/

_log_firmware_artifacts(name, build_dir, installer_dir, cmake_args)

• append FIRMWARE section to installer_history.log

generate_flash_map(config)

• call flash_mgr.py --variant-dir … --generate

• output: installer//.json

return 0 - OK

4.2 check_variant() — Config Validation Only

Section titled “4.2 check_variant() — Config Validation Only”

A lightweight path that runs idf.py reconfigure (cmake configuration pass only, no compilation) to validate the cmake flags without producing any binaries.

yes

no

check_variant(config)

prepare_build_dir(build_dir)

run_cmake_check(cmake_args, cwd, build_dir)

• idf.py -B build_dir flags reconfigure

rc != 0?

FAILED: print error, return rc

OK: return 0

Regular variants depend on bootloader and partition-table binaries compiled by the factory variant. copy_factory_artifacts() implements a self-healing dependency:

yes

no

yes

no

_factory_artifacts_complete(Factory/installer/ESP3D-FACTORY_4MB/)

bootloader_*.bin

found?

_build_missing_factory(source_dir)

→ build_variant(factory_4mb config)

artifacts

complete now?

ERROR: cannot produce factory artifacts

copy all files except size_report.txt

→ installer//

append to installer_history.log

return 0

To prevent stale build directories from a different chip target (e.g., an ESP32-S3 build) causing silent miscompilation, prepare_build_dir() writes a .idf_target marker file and wipes the directory if the cached target does not match "esp32".

yes

no

yes

no

build_dir exists?

read .idf_target

cached == 'esp32'?

shutil.rmtree(build_dir)

(mismatch: wipe)

os.makedirs(build_dir)

write .idf_target = 'esp32'


<REPO_ROOT>/
├── build/
│ └── esp32_3248s035c_<variant>/ ← idf.py build artifacts (.elf, .bin, .map, …)
│ ├── .idf_target ← IDF_TARGET guard marker ("esp32")
│ ├── ui_resources_<variant>.bin ← generated by generate_resources.py
│ ├── ui_resources_manifest_<v>.json ← manifest used by kit packager
│ └── esp3d_ui_offsets.h ← generated offsets header
│
├── installer/
│ └── ESP32_3248S035C_4MB_<radio>_<fw>/ ← assembled installer directory
│ ├── bootloader_4MB.bin ← from Factory (factory_4mb variant)
│ ├── partitions_4mb.bin ← from Factory
│ ├── factory_4MB.bin ← from Factory
│ ├── firmware.bin ← main pendant firmware
│ ├── ota_data_initial.bin ← OTA partition initial state
│ ├── ui_resources_<variant>.bin ← UI resources partition
│ ├── ui_resources_kit/ ← standalone customization kit
│ │ └── … ← see tools_build_scripts
│ ├── ESP32_3248S035C_4MB_….json ← flash map (flash_mgr output)
│ └── installer_history.log ← append-only audit log
│
└── boards/esp32_3248s035c/
├── build_scripts/
│ ├── build_one.py
│ ├── common.py
│ └── variants.py
├── Factory/
│ ├── build/factory_4mb/ ← factory app build artifacts
│ └── installer/
│ └── ESP3D-FACTORY_4MB/ ← factory installer output (source for copy)
├── resources/ ← (optional) board-specific UI overrides
├── partitions_4mb.csv
└── flash_params.json ← (optional) board flash parameters for flash_mgr

build_config_name() derives the installer subdirectory name from the cmake flags:

ESP32_3248S035C_<memory>_<radio>_<firmware>
Examples:
ESP32_3248S035C_4MB_wifi_fluidnc
ESP32_3248S035C_4MB_serial_grbl
ESP32_3248S035C_4MB_wifi_grblhal

For the factory variant (cwd == FACTORY_DIR), the fixed name ESP3D-FACTORY_4MB is used instead.


make_variant_args(*on_flags) → list[str]

Section titled “make_variant_args(*on_flags) → list[str]”

Generates the cmake argument list for a variant. Starts from DEFAULT_OFF_CMAKE_ARGS (all flags in ROOT_CMAKE_OPTIONS set to OFF) and appends each item in on_flags as -D <flag> to override it to ON.

Master list of every toggleable cmake option across the whole project, grouped by category:

CategoryExamples
Board selectionESP32_3248S035C, ESP32_3248S035R, ESP32S3_4827S043C, …
MemoryMEMORY_4_MB, MEMORY_8_MB, MEMORY_16_MB
PSRAMPSRAM_NONE, PSRAM_2_MB, PSRAM_4_MB, PSRAM_8_MB
Firmware targetTARGET_FW_FLUIDNC, TARGET_FW_GRBLHAL, TARGET_FW_GRBL, …
TransportsSERIAL_SERVICE, WIFI_SERVICE, BT_SERVICE, BT_SERIAL_SERVICE, …
FeaturesTFT_UI_SERVICE, SD_CARD_SERVICE, MDNS_SERVICE, SOCKET_CLIENT_SERVICE, …
Keycwdbuild_dir
4mb_wifi_fluidncREPO_ROOTbuild/esp32_3248s035c_4mb_wifi_fluidnc
4mb_serial_fluidncREPO_ROOTbuild/esp32_3248s035c_4mb_serial_fluidnc
4mb_serial_grblREPO_ROOTbuild/esp32_3248s035c_4mb_serial_grbl
4mb_wifi_grblhalREPO_ROOTbuild/esp32_3248s035c_4mb_wifi_grblhal
4mb_serial_marlinREPO_ROOTbuild/esp32_3248s035c_4mb_serial_marlin
Keycwdbuild_dir
factory_4mbboards/esp32_3248s035c/FactoryFactory/build/factory_4mb

NameValue / Source
BOARD_ROOTboards/esp32_3248s035c/
REPO_ROOTTwo levels above build_scripts/
IDF_PATH$IDF_PATH env var, default C:\Users\luc\esp\v5.4.3\esp-idf
IDF_PY$IDF_PATH/tools/idf.py
RESOLUTION"res_480_320" (hardcoded for this board)
EXPECTED_IDF_TARGET"esp32"
GENERATE_RESOURCEStools/build_scripts/generate_resources.py
PACKAGE_USER_KITtools/build_scripts/package_user_resources_kit.py
FunctionSignaturePurpose
build_variant(config: dict) → intExecute the full build pipeline for one variant
check_variant(config: dict) → intRun cmake reconfigure only (no compilation)
installer_dir_for(config: dict) → str | NoneCompute the installer output directory path
generate_flash_map(config: dict)Invoke flash_mgr.py --generate after a successful build
FunctionPurpose
parse_cmake_flags(cmake_args)Parse -D KEY=VALUE pairs from the cmake args list into a dict
build_config_name(cmake_args)Derive the human-readable config name (used as installer dir name)
resource_variant_string(cmake_args)Derive the generate_resources.py --variant string (e.g. "4mb_wifi_fluidnc"); returns None for factory variants
generate_resources(cmake_args, build_dir)Call generate_resources.py; skip silently if no resource variant
copy_factory_artifacts(cmake_args)Copy factory binaries into the installer dir; auto-build factory if missing
_factory_artifacts_complete(source_dir)Returns True only if source_dir contains at least one bootloader_*.bin
_build_missing_factory(source_dir)Identify and build the matching factory variant by comparing installer dirs
prepare_build_dir(build_dir)Create build dir and write .idf_target; wipe first if target mismatch
_ensure_clean_build_dir(build_dir)Wipe build dir if .idf_target marker does not match EXPECTED_IDF_TARGET
run_cmake_build(cmake_args, cwd, build_dir)Execute idf.py … build with IDF_TARGET=esp32 forced in env
run_cmake_check(cmake_args, cwd, build_dir)Execute idf.py … reconfigure
clean_build_dir(build_dir)shutil.rmtree with existence check
_show_size_report(build_dir, cwd)Run idf.py size (informational, non-fatal)
_copy_ui_resources_bin(build_dir, installer_dir)Copy ui_resources_*.bin from build dir to installer dir
_package_user_resources_kit(cmake_args, build_dir, installer_dir)Call package_user_resources_kit.py; failures are non-fatal (WARN)
_log_firmware_artifacts(variant_name, build_dir, installer_dir, cmake_args)Append a FIRMWARE section to installer_history.log
ensure_idf_py()Abort with a clear error if IDF_PY path does not exist
_get_jobs_env_value()Extract --jobs=N from sys.argv for use as CMAKE_BUILD_PARALLEL_LEVEL

Usage: python build_one.py <variant_name> [--clean] [--check]
Flags:
--clean Wipe build_dir and installer_dir, then exit (no compilation)
--check Run cmake reconfigure only (no compilation)
(none) Full build pipeline
Exit codes:
0 Success (or --clean completed)
1 Unknown variant or missing argument
N Propagated subprocess exit code from idf.py / cmake

The merged registry ({**FACTORY_VARIANTS, **VARIANTS}) means factory_4mb and all regular variants are accessible from the same CLI without distinguishing between the two dicts.


RequirementDetails
ESP-IDFv5.4.3, activated in the current shell (idf.py on PATH or IDF_PATH set)
Python3.x (scripts re-invoke themselves via sys.executable)
IDF_PATHMust point to the ESP-IDF root; fallback default is C:\Users\luc\esp\v5.4.3\esp-idf
Board IDF targetesp32 (enforced via IDF_TARGET env var and .idf_target marker)
tools/build_scripts/generate_resources.pyRequired for UI resource partition generation
tools/build_scripts/package_user_resources_kit.pyRequired for kit packaging (non-fatal if it fails)
tools/flash_scripts/flash_mgr.pyRequired for flash map JSON generation (called after build)

Parallel builds: Pass --jobs=N through build_mgr.py. The value is forwarded as CMAKE_BUILD_PARALLEL_LEVEL in the environment; idf.py does not accept -j directly.

Production vs. development builds: By default (when --dev is absent) the cmake flag -D PROD_BUILD=ON is injected automatically. Pass --dev on the command line to suppress it.


Terminal window
# From the repo root, with ESP-IDF activated:
python boards/esp32_3248s035c/build_scripts/build_one.py 4mb_wifi_fluidnc
python boards/esp32_3248s035c/build_scripts/build_one.py 4mb_serial_grbl
python boards/esp32_3248s035c/build_scripts/build_one.py factory_4mb

Clean a variant’s build and installer directories

Section titled “Clean a variant’s build and installer directories”
Terminal window
python boards/esp32_3248s035c/build_scripts/build_one.py 4mb_wifi_fluidnc --clean

Validate cmake configuration without compiling

Section titled “Validate cmake configuration without compiling”
Terminal window
python boards/esp32_3248s035c/build_scripts/build_one.py 4mb_serial_grbl --check
Terminal window
python boards/esp32_3248s035c/build_scripts/build_one.py
# Prints: Available variants: factory_4mb, 4mb_wifi_fluidnc, …

The top-level tools/build_scripts/build_mgr.py orchestrates multi-board, multi-variant builds and forwards --jobs=N for parallelism. See tools_build_scripts for details.


Installer Output

Sibling Modules

External Tools

subprocess

subprocess

subprocess

subprocess

firmware.bin

ui_resources_*.bin

ui_resources_kit/

*.json flash map

shutil.copy2

bootloader · partitions · factory.bin

cmake component

esp32_3248s035c_build_scripts

(build_one.py · common.py · variants.py)

tools/build_scripts/generate_resources.py

UI resources partition binary

tools/build_scripts/package_user_resources_kit.py

standalone ui_resources_kit/

tools/flash_scripts/flash_mgr.py

flash map JSON

idf.py (ESP-IDF v5.4.3)

cmake + ninja build

esp32_3248s035c_bsp

compiled into firmware

esp32_3248s035c_factory

(source of factory artifacts)

installer/

ESP32_3248S035C_4MB_…/

DependencyDirectionNature
esp32_3248s035c_factoryConsumes build outputshutil.copy2 of bootloader_4MB.bin, partitions_4mb.bin, factory_4MB.bin from Factory/installer/ESP3D-FACTORY_4MB/
esp32_3248s035c_bspCompiled into firmwareESP-IDF cmake component — no direct Python dependency
tools_build_scripts — generate_resources.pySubprocess callProduces ui_resources_*.bin and esp3d_ui_offsets.h for the display partition
tools_build_scripts — package_user_resources_kit.pySubprocess call (non-fatal)Assembles the standalone user customisation kit
tools/flash_scripts/flash_mgr.pySubprocess callGenerates the per-variant flash map JSON consumed by the web installer
pibot_pendant_v1_0_build_scriptsStructural peerIdentical three-file pattern; useful reference for contrast on a different board