Skip to content

ESP32_C3_BARE Board Module

The ESP32_C3_BARE board support module provides build scripts, BSP definitions, and hardware configuration for a minimal ESP32-C3-based WiFi-to-serial bridge. Unlike the display-equipped boards in this repository (see pibot_pendant_v1_0_bsp.md or esp32_3248s035r.md), this board is headless: it carries no TFT, no touch controller, and no LVGL UI. Its sole purpose is to bridge a CNC controller (Grbl / grblHAL / FluidNC) over UART to a remote Web UI client over WiFi.


  1. Purpose and Scope
  2. Hardware Profile
  3. Module Architecture
  4. BSP Component
  5. Build System
  6. Build Variants
  7. Flash Layout
  8. FreeRTOS Task Map
  9. Feature Constraints
  10. Build Workflow
  11. Capability Comparison with Other Boards
  12. Related Documentation

The ESP32-C3-BARE acts as a headless WiFi bridge between a host PC running the Web UI and a CNC machine connected via UART serial. The architecture is deliberately minimal:

  • WiFi → serves the embedded Web UI, handles HTTP + WebSocket, mDNS, SSDP, and OTA updates.
  • Serial (UART0) → forwards GCode commands to the CNC controller and relays responses back.

There is no local user interface of any kind. All user interaction happens remotely through a browser.

HTTP / WebSocket

(WiFi 802.11)

UART0 (Serial)

TX=GPIO21 RX=GPIO20

115200 baud

PC / Browser

(Web UI)

ESP32-C3-BARE

(WiFi Bridge)

CNC Controller

(Grbl / grblHAL / FluidNC)


PropertyValue
SoCESP32-C3 (single-core RISC-V, 160 MHz)
Flash4 MB (DIO, 80 MHz)
PSRAMNone
DisplayNone (headless)
TouchNone
BluetoothNot used (WiFi-only variants)
USBUSB-JTAG/CDC only (no USB OTG)
UART (CNC)UART0 — TX: GPIO21, RX: GPIO20
UART baud115200, 8N1, no flow control
SD cardSPI via multiplexer — pins TBD, implementation deferred
Board hostnameESP32-C3-BARE
WiFi fallbackMode 1 (stay in STA on connection failure)
Board name str"ESP32-C3-BARE"
Board version"v1.0"

boards/ESP32_C3_BARE/
├── board_config.cmake # IDF_TARGET, sdkconfig selection, feature overrides
├── flash_params.json # esptool flash parameters (chip, mode, freq, reset)
├── partitions_4mb.csv # Single-OTA partition table for 4MB flash
├── sdkconfig.4mb.wifi # Default sdkconfig for 4MB WiFi build
├── build_scripts/
│ ├── build_one.py # CLI entry-point: build or check a single variant
│ ├── common.py # Shared build helpers: build_variant, check_variant
│ └── variants.py # Variant definitions and make_variant_args()
└── components/
└── bsp/
├── CMakeLists.txt # BSP IDF component registration
├── board_config.h # Pin assignments and hardware constants
├── board_init.h / .c # board_init(), board_get_name(), board_get_version()
├── serial_def.h # Extern declaration of esp3dSerialConfig
├── sd_def.h # SD card config (SPI via multiplexer, pins NC)
└── tasks_def.h # FreeRTOS task sizes, priorities, and buffer sizes

invokes idf.py with

CMake -D flags

loads BSP include paths

consumed by

Board Configuration

board_config.cmake

IDF_TARGET, sdkconfig,

forced-OFF features

sdkconfig.4mb.wifi

CPU, WiFi, HTTP, RTOS

defaults

partitions_4mb.csv

Flash layout

flash_params.json

DIO 80MHz esp32c3

BSP Component (components/bsp/)

board_config.h

Pin defs, UART params

board_init.c

board_init()

serial_def.h

esp3dSerialConfig

tasks_def.h

Stack sizes, priorities

sd_def.h

esp3dSdConfig

Build Scripts

build_one.py

main()

common.py

build_variant()

check_variant()

variants.py

make_variant_args()

VARIANTS{}

Core Platform

(esp3d_core, serial_client,

network, http_service)


The BSP for this board is intentionally minimal — there is no display, no touch controller, no encoder, and no factory recovery application.

esp_err_t board_init(void) // logs board name and returns ESP_OK
const char* board_get_name() // returns "ESP32-C3-BARE"
const char* board_get_version() // returns "v1.0"

Compared to display-equipped boards (which call init_lvgl(), init_touch_controller(), lvgl_flush_cb(), etc.), this BSP does no peripheral initialization at start-up. All hardware the firmware needs (UART, WiFi, HTTP stack) is initialized by the respective modules in the core platform.

ConstantValueDescription
UART_TX_PINGPIO_NUM_21UART0 TX → CNC RX
UART_RX_PINGPIO_NUM_20UART0 RX ← CNC TX
UART_BAUD_RATE_BPS115200Default CNC baud rate
UART_PORT_IDXUART_NUM_0Hardware UART port
UART_RX_BUFFER_SIZE512UART RX ring buffer
UART_RX_FLUSH_TIMEOUT1500 msIdle flush timeout
SD_MISO/MOSI/CLK/CSGPIO_NUM_NCNot connected — SD deferred
SD_SPI_FREQ_KHZ4000SPI frequency when SD is implemented

Because the ESP32-C3 is single-core, all tasks use Core 0. There is no Core 1 / Core 0 separation that exists on the dual-core boards (Xtensa / ESP32-S3).

See §8 FreeRTOS Task Map for the full table.

The SD card uses SPI via a multiplexer. All four SPI pins are currently GPIO_NUM_NC. The configuration struct esp3dSdConfig is present and correctly typed, but the hardware is not yet wired. Do not enable SD_CARD_SERVICE until the multiplexer design is finalized and pins are assigned.

The BSP component declares the following IDF component requirements:

esp_timer | esp3d_log | nvs_flash | driver | esp3d_activity_manager

No LVGL, no display driver, and no touch driver dependencies exist.


ScriptRole
build_one.pyCLI entry point: parses variant name and flags, dispatches to build_variant() or check_variant()
common.pyLow-level build engine: invokes idf.py, enforces IDF_TARGET=esp32c3, manages build directories, generates flash map
variants.pyData layer: defines VARIANTS dict and make_variant_args() factory

All CMake options listed in ROOT_CMAKE_OPTIONS are set to OFF first, then the flags specific to each variant are switched ON. This explicit whitelist-then-enable pattern prevents stale or inherited CMake cache values from activating unintended features.

DEFAULT_OFF_CMAKE_ARGS = ["-D", "OPTION_A=OFF", "-D", "OPTION_B=OFF", ...]
def make_variant_args(*on_flags):
args = DEFAULT_OFF_CMAKE_ARGS.copy()
for flag in on_flags: # e.g. "WIFI_SERVICE=ON"
args.extend(["-D", flag])
return args

Yes

No

Yes

No

rc == 0

rc != 0

rc != 0

rc == 0

python build_one.py

[--clean] [--check]

Parse variant name

from VARIANTS + FACTORY_VARIANTS

--check flag?

--clean flag?

Remove build dir

and installer dir

idf.py reconfigure

(CMake config only)

idf.py build

(IDF_TARGET=esp32c3 enforced)

idf.py size

(print size report)

generate_flash_map()

→ installer//.json

Exit 0 - OK

Exit non-zero - FAILED

common.py writes a .idf_target marker file in the build directory. On the next invocation, if the cached target differs from "esp32c3", the entire build directory is deleted before reconfiguring. This prevents subtle cross-target CMake cache corruption.

EXPECTED_IDF_TARGET = "esp32c3"
def _ensure_clean_build_dir(build_dir):
marker = os.path.join(build_dir, ".idf_target")
if os.path.isdir(build_dir):
cached = open(marker).read().strip() if os.path.isfile(marker) else None
if cached != EXPECTED_IDF_TARGET:
shutil.rmtree(build_dir) # wipe stale build
FlagEffect
(default)PROD_BUILD=ON injected automatically
--dev passed to the scriptPROD_BUILD not added — debug logging and asserts are active

The --jobs=N argument is forwarded via CMAKE_BUILD_PARALLEL_LEVEL (not via idf.py -j, which is unsupported). This is passed down by build_mgr.py when orchestrating multi-board parallel builds (see tools_build_scripts.md).

<repo_root>/
├── build/
│ ├── c3_4mb_wifi_grblhal/ ← idf.py build output
│ ├── c3_4mb_wifi_grbl/
│ └── c3_4mb_wifi_fluidnc/
└── installer/
└── ESP32_C3_BARE_4MB_wifi_<firmware>/
└── ESP32_C3_BARE_4MB_wifi_<firmware>.json ← flash map

This board has no factory variants (FACTORY_VARIANTS = {}). There is no custom bootloader, no factory recovery application, and no OTA-data backup/restore mechanism at boot — all of which exist in full-pendant boards like pibot_pendant_v1_0.

Variant nameCNC firmwareWiFiSerialmDNSSSDPWebUITimeUpdateBuild dir
4mb_wifi_grblhalgrblHAL✓✓✓✓✓✓✓build/c3_4mb_wifi_grblhal
4mb_wifi_grblGrbl✓✓✓✓✓✓✓build/c3_4mb_wifi_grbl
4mb_wifi_fluidncFluidNC✓✓✓✓✓✓✓build/c3_4mb_wifi_fluidnc
4mb_wifi_marlinMarlin (a14 groundwork)✓✓✓✓✓✓✓build/c3_4mb_wifi_marlin

All four variants share an identical feature set — the only difference is the TARGET_FW_* flag, which determines which CNC response parser and GCode flow-control logic is compiled in. Refer to cnc_grbl.md, cnc_grblhal.md, and cnc_fluidnc.md for the parser-level differences.

Always OFF (board constraints)

HTTPS_SERVICE

(no PSRAM)

USB_SERIAL_SERVICE

(no OTG)

TFT_UI_SERVICE

(no display)

BT_SERVICE

SD_CARD_SERVICE

(pins TBD)

Always ON

WIFI_SERVICE

SERIAL_SERVICE

WEB_SERVICES

+ WEBUI_SERVER

MDNS_SERVICE

SSDP_SERVICE

TIME_SERVICE

UPDATE_SERVICE


a14: the installer directory name comes from CMake (installer_config_name(cmake_args, build_dir) reads <build_dir>/build_config_name.txt, which carries the _LOG_<LEVEL> suffix; falls back to build_config_name()). This board has no screen, so no ui_resources and no --build-name.

The partition scheme uses a single OTA slot with no ota_1 partition, trading redundant-update safety for a larger application region and a large SPIFFS volume for WebUI assets and user files.

4MB Flash (0x000000 – 0x3FFFFF)
┌─────────────────────────────────────────────────────────────────┐
│ 0x0000 – 0x8FFF │ Bootloader + Partition Table (36 KB) │
├─────────────────────────────────────────────────────────────────┤
│ 0x9000 – 0xBFFF │ nvs (12 KB) │
├─────────────────────────────────────────────────────────────────┤
│ 0xC000 – 0xDFFF │ otadata ( 8 KB) │
├─────────────────────────────────────────────────────────────────┤
│ 0x10000 – 0x1CFFF │ app0 / ota_0 (1.75 MB) ← firmware image │
├─────────────────────────────────────────────────────────────────┤
│ 0x1D0000– 0x3FFFFF│ flashfs (SPIFFS) (2.1875 MB) ← WebUI │
└─────────────────────────────────────────────────────────────────┘

Note: There is no ota_1 partition. OTA updates overwrite ota_0 directly. If an update is interrupted, the device may not boot until re-flashed via USB.

ParameterValue
chipesp32c3
flash_modedio
flash_freq80m
beforedefault_reset
afterhard_reset

The ESP32-C3 is single-core — all tasks are pinned to Core 0. There is no LVGL task (Core 1), no UI rendering task, and no display flush interrupt. The scheduling model is simpler than dual-core boards.

Core 0 - All Tasks

Network Task (tftNetwork)

Stack: 4096 B | Priority: 0

Stream Task (tftStream)

Stack: 8192 B | Priority: 0

UART RX (esp3d_serial_rx_task)

Stack: 4096 B | Priority: 10

GCode Host (esp3d_gcode_host_task)

Stack: 4096 B | Priority: 14

Socket RX (esp3d_socket_rx_task)

Stack: 4096 B | Priority: 5

WS RX / WS Client

Stack: 4096-8192 B | Priority: 5

Rendering RX

Stack: 4096 B | Priority: 5

HTTP daemon (httpd)

Stack: 6144 B

Ephemeral: wifi_scan, server_scan

Stack: 4096 B | Priority: 1

TaskStackPriorityCore
Network (tftNetwork)4096 B00
Stream (tftStream)8192 B00
UART RX4096 B100
GCode host4096 B140
Socket RX4096 B50
WS RX4096 B50
WS client8192 B50
Rendering RX4096 B50
HTTP daemon6144 B—0
wifi_scan / server_scan4096 B10

Chunk size: STREAM_CHUNK_SIZE = 1024 B (no PSRAM; scales up on PSRAM-equipped boards).

HTTP limits (no PSRAM):

ConstantValue
ESP3D_HTTP_MAX_OPEN_SOCKETS4
ESP3D_HTTP_BACKLOG4
ESP3D_HTTP_STACK_SIZE6144 B
ESP3D_HTTP_UPLOAD_READ_SIZE256 B
ESP3D_HTTP_UPLOAD_WRITE_SIZE256 B
ESP3D_HTTP_HEADER_MAX_LEN300 B

The following features are permanently disabled by board_config.cmake regardless of the CMake flags passed by the caller:

Feature flagReason forced OFF
HTTPS_SERVICETLS buffers (~50 KB/connection) require PSRAM — C3 has none
USB_SERIAL_SERVICEESP32-C3 has USB-JTAG/CDC only; no USB OTG peripheral
TFT_UI_SERVICENo display hardware on this board

The following features are absent from all variant definitions (OFF by omission):

FeatureNote
BT_SERVICENot used; WiFi and BT are mutually exclusive on the C3 under RAM constraints
SD_CARD_SERVICESD hardware not yet wired (see §4.4)
LUA_INTERPRETER_SERVICENot required for a headless bridge
SOCKET_CLIENT_SERVICENot applicable — WiFi is the remote link, not the CNC link
WS_CLIENT_SERVICENot required
NOTIFICATIONS_SERVICENot required
CAMERA_SERVICENo camera hardware

For the system-wide constraints table (WiFi vs. BT, SOCKET_CLIENT vs. WEB_SERVICES, etc.), refer to feature_resource_matrix.md.


  • ESP-IDF v5.4.3 installed and sourced (or IDF_PATH environment variable set).
  • Python 3.x available on the system path.
Terminal window
# From the board's build_scripts directory:
python build_one.py 4mb_wifi_grblhal
python build_one.py 4mb_wifi_grbl
python build_one.py 4mb_wifi_fluidnc
# Clean build (remove build dir, then exit — no compilation):
python build_one.py 4mb_wifi_fluidnc --clean
# Config check only (CMake reconfigure, no compilation):
python build_one.py 4mb_wifi_grblhal --check
# Development build (PROD_BUILD=OFF, debug logging active):
python build_one.py 4mb_wifi_grbl --dev
# With parallel jobs (passed as CMAKE_BUILD_PARALLEL_LEVEL):
python build_one.py 4mb_wifi_grblhal --jobs=8
Terminal window
# From repo root — build all variants across all boards:
python tools/build_scripts/build_mgr.py
# Target this board specifically:
python tools/build_scripts/build_mgr.py --board ESP32_C3_BARE

See tools_build_scripts.md for build_mgr.py documentation.

Terminal window
# Using idf.py directly:
idf.py -B build/c3_4mb_wifi_grblhal flash monitor
# Using flash_mgr (after build — uses generated flash map JSON):
python tools/flash_scripts/flash_mgr.py \
--variant-dir installer/ESP32_C3_BARE_4MB_wifi_grblhal \
--port /dev/ttyUSB0

The file sdkconfig.4mb.wifi contains the default CONFIG_* values for this board. It is applied only once — when the build/<variant>/sdkconfig file does not yet exist. After the first build, menuconfig changes are preserved across cmake re-runs.

To reset to board defaults:

Terminal window
rm build/c3_4mb_wifi_<firmware>/sdkconfig
python build_one.py <variant>

Do not edit build/<variant>/sdkconfig directly for permanent changes — edit sdkconfig.4mb.wifi instead.


11. Capability Comparison with Other Boards

Section titled “11. Capability Comparison with Other Boards”
CapabilityESP32_C3_BAREpibot_pendant_v1_0esp32_2432s028resp32s3_8048s043c
SoCESP32-C3 (RISC-V)ESP32-S3ESP32 (Xtensa)ESP32-S3
Cores1222
Flash4 MB16 MB4 MB8 MB
PSRAMNone8 MBNone8 MB
DisplayNone (headless)ILI9341 SPIILI9341 SPIRGB parallel
TouchNoneXPT2046XPT2046GT911
LVGL / UINoYesYesYes
WiFiYesYesYesYes
BluetoothNot usedBT SPP / BLE——
HTTPSNo (no PSRAM)YesNo (no PSRAM)Yes
USB SerialNo (no OTG)NoNoYes
Factory appNoYesYesYes
Custom bootloaderNoYesNoNo
EncoderNoYesNoNo
BuzzerNoYesNoYes

DocumentRelevance
feature_resource_matrix.mdFeature compatibility, WiFi/CNC usage model, resource “tickets”
features.mdFull hardware × SKU feature matrix
esp32_memory_constraints.mdHeap, fragmentation, WiFi vs BT RAM budget
board_build_guidelines.mdGeneral board build conventions
tools_build_scripts.mdbuild_mgr.py, generate_resources.py, flash_mgr.py
gcode_host_architecture.mdGCode host service architecture
gcode_host_streaming_flow.mdGCode streaming state machine and flow control
cnc_grbl.mdGrbl target: parser, response handling
cnc_grblhal.mdgrblHAL target: parser, flow control
cnc_fluidnc.mdFluidNC target: parser, file streaming
connection_management.mdTransport lifecycle and status model
mdns.mdmDNS registered services reference
pibot_pendant_v1_0_bsp.mdFull-pendant BSP (contrasting reference)
tools.mdDevelopment tools (serial bridge, WebSocket test, simulators)