ESP32-3248S035R Board Support Package
Section titled “ESP32-3248S035R Board Support Package”Overview
Section titled “Overview”The ESP32-3248S035R module is the Board Support Package (BSP) for the ESP32-3248S035R development board — a 3.5-inch TFT touchscreen panel built around the original ESP32 SoC. The “R” suffix denotes a Resistive touch panel (XPT2046, shared SPI bus), distinguishing it from the otherwise similar esp32_3248s035c variant which uses a Capacitive touch controller (GT911, I2C).
This BSP provides:
- A standalone Factory/recovery application flashed to the factory partition, used for OTA firmware and UI-resource updates from an SD card.
- A BSP component used by the main pendant firmware, wiring the ST7796 display and XPT2046 resistive touch controller into LVGL.
- Build scripts that define and compile the supported firmware variants for this board.
The module lives entirely under boards/esp32_3248s035r/ and is one board in the wider Board Support Packages family.
Hardware Summary
Section titled “Hardware Summary”| Feature | Detail |
|---|---|
| MCU | ESP32 (Xtensa dual-core LX6, 240 MHz) |
| Flash | 4 MB |
| PSRAM | None |
| Display | ST7796 3.5″ TFT, 320 × 480 px, SPI |
| Touch | XPT2046 resistive touch, shared SPI bus (own CS pin) |
| Physical buttons | Not populated (GPIO_NUM_NC) |
| Rotary encoder | Not populated (GPIO_NUM_NC) |
| Buzzer | Not populated (GPIO_NUM_NC) |
| SD card | SPI interface |
| Recovery entry | Software trigger only ([ESP444]FACTORY from main firmware) |
| Custom bootloader | None (no physical boot button to hold at reset) |
Touch-only navigation. This board has no encoder, buttons, or buzzer wired. All navigation in the factory app is via virtual on-screen buttons. Driver code for buttons, encoder, and buzzer is compiled in but safely no-ops at runtime through
GPIO_IS_VALID_GPIO()guards.
Resistive touch on the display SPI bus. Unlike the esp32_2432s028r which bit-bangs a dedicated SPI bus for the XPT2046, this board routes its XPT2046 on the same hardware SPI bus as the ST7796 display, with a separate CS pin. The touch controller is added as a second SPI device via
spi_bus_add_device()after the display driver initializes the bus.
Architecture Overview
Section titled “Architecture Overview”The module is organized into three distinct layers, each documented in its own sub-module file:
Flash Partition Layout
Section titled “Flash Partition Layout”0x0000 Bootloader (standard ESP-IDF — no custom hooks on this board)0xC000 Partition table0xD000 NVS0xF000 otadata ← saved to 0xB000 by main firmware before entering recovery0x10000 app0 ← main pendant firmware factory ← this module's Factory app ui_resources ← LVGL images, fonts, theme paletteThe main firmware (in
esp444.cpp) writes a magic-tagged backup ofotadatato offset0xB000before switching to the factory partition. The factory app’srestore_otadata_from_backup()restores it on startup so the board correctly returns toapp0after a power cycle during recovery.
Sub-Modules
Section titled “Sub-Modules”1. Factory Application — esp32_3248s035r_factory.md
Section titled “1. Factory Application — esp32_3248s035r_factory.md”The recovery application running from the factory flash partition. It presents a touch-navigable menu to:
- Boot a selected OTA partition (
app0/app1). - Flash a new firmware image from SD card (
esp3dfw.bin→ OTA viaesp_ota_write). - Flash new UI resources from SD card (
ui_resources.bin→ raw partition write).
Key components: main.c (menu loop, OTA actions), gfx.c (2D graphics / snapshot), st7796.c (standalone SPI display driver), touch.c (XPT2046 polling on shared SPI bus, with read_reg12() + calibrate()), sdcard.c (SPI SD mount), buttons.c, buzzer.c, encoder.c (no-op on this board), factory_log.h.
The hardware initialization layer consumed by the main pendant firmware. It:
- Initializes the ST7796 SPI display and applies a board-specific mirror-Y orientation correction.
- Attaches the XPT2046 resistive touch controller as a second device on the already-initialized display SPI bus (via
spi_bus_add_device()), with per-board ADC calibration constants applied intouch_calibrate(). - Configures LVGL: display, DMA-capable draw buffers, flush callback, tick timer, and touch input device (
touch_read_cbat 10 ms). - Registers custom LVGL control events for switch and potentiometer inputs.
Key components: board_init.c, control_event.h, control_types.c, touch_xpt2046_def.h.
3. Build Scripts — esp32_3248s035r_build_scripts.md
Section titled “3. Build Scripts — esp32_3248s035r_build_scripts.md”Python build system for the firmware variant matrix. It handles:
- Variant definitions (4 MB flash × transport × CNC firmware target).
- CMake invocation through ESP-IDF
idf.pywith forcedIDF_TARGET=esp32. - UI resource binary (
ui_resources_*.bin) generation viagenerate_resources.py. - Factory artifact copying (bootloader, partition table) from the factory build into the installer directory.
- Flash map JSON generation.
Key components: build_one.py, common.py, variants.py.
Component Interaction — BSP Layer
Section titled “Component Interaction — BSP Layer”Component Interaction — Factory App
Section titled “Component Interaction — Factory App”Firmware Variants
Section titled “Firmware Variants”The build system defines four production firmware variants and one factory variant:
| Variant | Memory | Transport | CNC Target |
|---|---|---|---|
4mb_wifi_fluidnc | 4 MB | WiFi + Serial | FluidNC |
4mb_serial_fluidnc | 4 MB | Serial UART | FluidNC |
4mb_serial_grbl | 4 MB | Serial UART | GRBL |
4mb_wifi_grblhal | 4 MB | WiFi + Serial | grblHAL |
factory_4mb (factory) | 4 MB | — | Recovery |
All production variants enable TFT_UI_SERVICE, TFT_TOUCH_SERVICE, UPDATE_SERVICE, and SERIAL_SERVICE. WiFi variants additionally enable WIFI_SERVICE, MDNS_SERVICE, and SOCKET_CLIENT_SERVICE.
Transport policy: Per
cmake/sanity_check.cmake,WEBUI_SERVER,SSDP_SERVICE, andWS_SERVER_SERVICEare disabled whenSOCKET_CLIENT_SERVICEis ON. See feature_resource_matrix.md for the full compatibility matrix.
Comparison with Sibling Board (esp32_3248s035c)
Section titled “Comparison with Sibling Board (esp32_3248s035c)”| Aspect | esp32_3248s035r | esp32_3248s035c |
|---|---|---|
| Touch controller | XPT2046 (resistive, SPI) | GT911 (capacitive, I2C) |
| Touch bus | Shared SPI bus with display (own CS) | Dedicated I2C bus |
| Touch calibration | Manual per-board TOUCH_CALIBRATION_* constants + calibrate() | Auto-detected by GT911 |
| BSP touch init | spi_bus_add_device() + touch_xpt2046_configure() | bus_i2c_init() + touch_gt911_configure() |
| Factory touch driver | Standalone read_reg12() + calibrate() in touch.c | Thin polling wrapper over shared touch_gt911 |
touch_xpt2046_def.h | ✓ (SPI device config + inline SPI read function) | — |
| Custom bootloader | None | None |
| Physical controls | None (GPIO_NUM_NC) | None (GPIO_NUM_NC) |
| Display driver | ST7796 SPI (same) | ST7796 SPI (same) |
| Orientation fix | mirror-Y via esp_lcd_panel_mirror() | Same panel quirk, same fix |
Key Design Decisions
Section titled “Key Design Decisions”XPT2046 on Shared SPI Bus
Section titled “XPT2046 on Shared SPI Bus”The XPT2046 touch controller is wired to the same hardware SPI bus (TFT_HOST) as the ST7796 display, differentiated only by a dedicated CS pin (TOUCH_SPI_CS_PIN). In the BSP, spi_bus_add_device() is called inside init_touch_controller() after st7796_spi_configure() has already initialized the bus. In the factory app, touch_init() does the same. This contrasts with esp32_2432s028r which uses a separate bit-banged SPI bus for its XPT2046.
Resistive Touch Calibration
Section titled “Resistive Touch Calibration”TOUCH_CALIBRATION_X_MIN / X_MAX / Y_MIN / Y_MAX constants in board_config.h define the raw 12-bit ADC range that maps to the 0–480 / 0–320 pixel space. Both the BSP (touch_calibrate() in board_init.c) and the factory app (calibrate() in touch.c) implement the same linear rescale formula:
pixel = (raw - cal_min) * range / (cal_max - cal_min)Values below cal_min are clamped to zero. These constants are the primary touch-quality tuning knob for this board and may need adjustment per panel batch.
Touch-Only Navigation (Factory App)
Section titled “Touch-Only Navigation (Factory App)”Three virtual buttons are rendered at the bottom of the 320×480 screen. touch_hint_hit_test() divides the screen width into thirds: left third = Up (BTN_1), center third = Down (BTN_2), right third = OK (BTN_3). This gives large tap targets (≈107 px wide) independent of the drawn circle radius (27 px). Visual press feedback (draw_button_hint_pressed()) is drawn before dispatch_button() to give tactile-like confirmation. The encoder and hardware button loops are still polled but return immediately with no-ops on this board.
Standalone Factory Display Driver
Section titled “Standalone Factory Display Driver”Factory/main/st7796.c reimplements the display driver over the raw ESP-IDF SPI master driver without depending on esp_lcd, esp3d_log, or LVGL. Init sequence, MADCTL rotation map, and gamma tuning mirror the shared hardware driver. This keeps the factory partition self-contained and minimal, matching the approach used in pibot_pendant_v1_0_factory_app.md and esp32_3248s035c_factory.md.
Mirror-Y Orientation Quirk (BSP)
Section titled “Mirror-Y Orientation Quirk (BSP)”The ST7796 panel requires a mirror-Y correction not expressible via the shared driver’s DISPLAY_ORIENTATION enum. board_init() calls esp_lcd_panel_mirror(handle, false, true) after st7796_spi_configure() as a board-specific one-off, leaving the shared driver unchanged for all other boards. The factory standalone driver encodes the equivalent MADCTL value 0x88 directly in its rotation map.
OTA Data Backup / Restore
Section titled “OTA Data Backup / Restore”The backup offset 0xB000 and magic word 0xAA55AA55 at offset 0xB040 must match the values in the main firmware’s esp444.cpp exactly. Both files contain warnings about this coupling. CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED=y is required in the factory sdkconfig because esp_flash_erase_region() targets 0xB000, which is below the first partition and is normally write-protected by ESP-IDF.
Snapshot Feature (Optional)
Section titled “Snapshot Feature (Optional)”When ENABLE_SNAPSHOT is defined at build time, pressing GPIO0 (the BOOT button on this board) triggers snapshot_take(): the SD card is mounted, a .raw file is opened, and a full-screen redraw sends pixel data simultaneously to the SPI display and the file. The raw format is 8 bytes header (width + height as little-endian uint32_t) followed by native-endian RGB565 pixels. The tools/images_converter/raw2png.py script converts these snapshots to PNG.
Related Documentation
Section titled “Related Documentation”| Topic | File |
|---|---|
| Hardware schematics | pibot-cnc-pendant-hardware-documentation.md |
| Display driver architecture | display_drivers.md |
| ST7796 SPI driver | bsp_display_drivers_spi.md |
| XPT2046 touch driver | bsp_touch_controllers.md |
| UI resources partition | development.md |
| Theme palette tokens | theme_palette.md |
| Build guidelines | board_build_guidelines.md |
| Memory constraints | esp32_memory_constraints.md |
| Feature / transport matrix | feature_resource_matrix.md |
| Sibling (capacitive touch) | esp32_3248s035c.md |
| Logging guide | esp3d_log_guide.md |