Skip to content

esp32s3_bzm_tft35_gt911_factory_app_tools

Host-side Python utilities for the ESP32-S3 BZM TFT 3.5″ GT911 factory application. These four scripts cover the complete factory workflow: flashing firmware to the device, generating bitmap fonts for the factory display, and converting on-device screen snapshots back to standard images on a PC.

All scripts run on a developer workstation (Linux, macOS, or Windows) and require Python 3 with the packages listed in each section below.


ScriptPurposeKey dependency
flash_all.pyMulti-mode flash controller (recovery / full / firmware-only)esptool, pyserial
flash_factory.pyFlash the factory partition onlyesptool, pyserial
generate_font/generate_font.pyGenerate bitmap font C sources from a TTF filePillow
raw2png/snap2png.pyConvert on-device RGB565 snapshot files to PNGPillow

These scripts are board-specific adaptations of the common factory tooling pattern shared by all boards in this repository. For the corresponding tools on other boards, see pibot_pendant_v1_0_factory_app_tools.md and esp32s3_8048s070c_factory_app_tools.md.


boards/esp32s3_bzm_tft35_gt911/Factory/tools/
├── flash_all.py
├── flash_factory.py
├── generate_font/
│ └── generate_font.py
└── raw2png/
└── snap2png.py

ESP32-S3 flash (8 MB)

esp32s3_bzm_tft35_gt911_factory_app

Host (developer workstation)

esptool write_flash

esptool write_flash

esptool write_flash

esptool write_flash

esptool write_flash

generates C source

fontWxH.c / fontWxH.h

saves snap*.raw

to SD card

snap*.raw files

copied to PC

outputs PNG

flash_all.py

flash_factory.py

generate_font.py

snap2png.py

factory_app_main

factory_app_display

gfx.c / st7796.c

factory_app_input

factory_app_storage

sdcard.c

bootloader

0x1000

partition table

0xC000

otadata

0x10000

app0 firmware

0x20000

factory partition

0x7A0000

flash filesystem

0x660000

PNG images

Parent

Sibling modules

Tools module (this module)

generates sources for

decodes output of

reads files from

flash_all.py

flash_factory.py

generate_font.py

snap2png.py

esp32s3_bzm_tft35_gt911

factory_app_display

esp32s3_bzm_tft35_gt911

factory_app_storage

esp32s3_bzm_tft35_gt911

factory_app


All flash offsets are fixed and must match the partition table CSV shipped with the board. The scripts parse the CSV automatically to determine the factory partition offset.

0x00001000 bootloader.bin (44 KB reserved)
0x0000C000 partitions.bin (partition table)
0x00010000 otadata (OTA state, 8 KB)
0x00020000 app0 ← main firmware
0x00660000 flashfs / LittleFS
0x007A0000 factory ← recovery app (default, CSV-derived)

The factory offset is not hard-coded in the scripts. find_factory_offset() walks any partitions*.csv file reachable from the working directory and extracts the offset from the factory row. The constant 0x7A0000 is a fall-back for 8 MB layouts only.


flash_all.py — multi-mode flash controller

Section titled “flash_all.py — multi-mode flash controller”

Wraps esptool to support three mutually exclusive flash modes from a single entry point.

No

Yes

--recovery

--full

--fw FILE

OK

FAIL

success

fail

python flash_all.py

Parse arguments

Port

specified?

find_esp32_port

auto-detect via pyserial

find_factory_offset

parse partitions*.csv

Mode?

Files:

bootloader @ 0x1000

partitions @ 0xC000

factory @ CSV offset

Files:

bootloader @ 0x1000

partitions @ 0xC000

factory @ CSV offset

firmware @ 0x20000

Files:

firmware @ 0x20000

check_file: verify all

binaries exist

flash(port, baud, files)

esptool write_flash

sys.exit 1

sys.exit 0

sys.exit 1

FunctionSignatureDescription
main() → NoneArgument parsing and top-level orchestration
find_esp32_port() → str|NoneIterates serial.tools.list_ports; matches CP210x, CH340, FTDI keyword patterns
find_factory_offset() → strParses partitions*.csv for factory sub-type; returns hex offset string or "0x7A0000"
check_file(path, name) → boolVerifies a binary exists, prints its size
flash(port, baud, files) → boolAssembles and runs the esptool subprocess
Terminal window
# First-time board setup — flashes bootloader + partition table + factory app
python flash_all.py --recovery
# Full production flash — all partitions plus the main firmware
python flash_all.py --full --fw-file build/firmware.bin
# Development iteration — firmware only (fastest)
python flash_all.py --fw build/firmware.bin
# Explicit port
python flash_all.py --recovery --port /dev/ttyUSB0
python flash_all.py --recovery --port COM3
ArgumentDefaultDescription
--port / -pauto-detectSerial port
--baud460800Flash baud rate
--recovery—Mode: bootloader + partitions + factory
--full—Mode: everything (requires --fw-file)
--fw FILE—Mode: firmware only at APP0_OFFSET
--fw-file FILE—Firmware binary path (used with --full)

flash_factory.py — factory partition flash

Section titled “flash_factory.py — factory partition flash”

Focused single-purpose script: flashes one binary to the factory partition. Useful when iterating on the factory application without disturbing the main firmware or bootloader.

No

Yes

No

Yes

found

not found

success

fail

python flash_factory.py

--port

specified?

find_esp32_port

--offset

specified?

find_factory_offset

parse partitions*.csv

subprocess: esptool

write_flash offset binary

sys.exit 1

hint: use --offset

Done

sys.exit returncode

FunctionSignatureDescription
main() → NoneFull script entry point
find_factory_offset() → str|NoneSearches PARTITION_CSV_PATTERNS for factory row; returns None on failure (unlike flash_all.py which returns a default)
find_esp32_port() → str|NoneSame USB-UART keyword detection as flash_all.py
Terminal window
# Auto-detect everything
python flash_factory.py
# Specify port
python flash_factory.py --port /dev/ttyUSB0
# Custom binary and offset
python flash_factory.py --bin custom_factory.bin --offset 0x7A0000
# Full explicit invocation
python flash_factory.py --port COM4 --baud 921600 --bin installer/factory.bin --offset 0x7A0000
ArgumentDefaultDescription
--port / -pauto-detectSerial port
--bin / -binstaller/factory.binFactory binary path
--baud460800Flash baud rate
--offset / -oCSV-derivedFlash address override

generate_font/generate_font.py — bitmap font generator

Section titled “generate_font/generate_font.py — bitmap font generator”

Converts a TrueType font into a pair of C source files (fontWxH.c + fontWxH.h) that can be compiled directly into the factory firmware. The factory display subsystem (see esp32s3_bzm_tft35_gt911_factory_app_display.md) uses these C arrays through gfx_draw_char / gfx_draw_string.

ParameterValue
First character0x20 (space)
Last character0x7E (~)
Total characters95 printable ASCII
Pixel format1 bit per pixel, MSB = leftmost

No

Yes

found

Input: WxH target cell size

Optional: path to TTF font

Font

specified?

find_monospace_font

system font search

Validate font path

find_best_font_size

binary search pt size

to fit WxH cell

Compute global

ascent/descent range

across all 95 chars

render_font loop

For each char 0x20-0x7E:

- New L-mode image WxH

- Center horizontally

- Align vertically by baseline

- Threshold pixel > 127

- Pack to bytes MSB-first

glyphs[]

bytes_per_row = ceil(W/8)

bytes_per_glyph = bytes_per_row x H

generate_c_files

fontWxH.h

- FONT_WIDTH / FONT_HEIGHT

- FONT_BYTES_PER_ROW

- get_glyph(char) declaration

fontWxH.c

- static font_data[95][bytes_per_glyph]

- get_glyph(char) implementation

Header (fontWxH.h)

#pragma once
#include <stdint.h>
#define FONT_WIDTH 8
#define FONT_HEIGHT 16
#define FONT_BYTES_PER_ROW 1
const uint8_t *font8x16_get_glyph(char c);

Source (fontWxH.c)

static const uint8_t font_data[][16] = {
{ 0x00, ... }, /* 0x20 ' ' */
{ 0x00, ... }, /* 0x21 '!' */
/* ... 95 entries total ... */
};
const uint8_t *font8x16_get_glyph(char c) {
if (c < 0x20 || c > 0x7E) return font_data[0];
return font_data[c - 0x20];
}
FunctionSignatureDescription
main() → NoneCLI parsing, validation, orchestration
find_monospace_font() → str|NoneSearches known system font paths (Linux / macOS / Windows)
find_best_font_size(font_path, w, h) → intIterates point sizes 6–(h+10), returns largest that fits the M glyph within the target cell
render_font(font_path, w, h) → list[list[int]]Renders all 95 characters; returns list of byte arrays
generate_c_files(glyphs, w, h, output_dir) → NoneWrites .h and .c files to output_dir
Terminal window
# Minimum: 8x16 font, output to current directory, auto-detect system font
python generate_font.py 8x16
# Specify output directory
python generate_font.py 8x16 ./main
# Use a specific TTF (recommended for CP437/DOS aesthetic)
python generate_font.py 8x16 ./main --font "Perfect DOS VGA 437.ttf"
# Smaller font for compact UI areas
python generate_font.py 6x12 ./main
# Larger font
python generate_font.py 10x20 ./main --font /path/to/CustomMono.ttf
DimensionMinMax
Width4 px32 px
Height6 px64 px

For the common 8x16 cell:

  • bytes_per_row = ⌈8/8⌉ = 1
  • bytes_per_glyph = 1 × 16 = 16 bytes
  • Total for 95 characters = 1 520 bytes (~1.5 KB)

raw2png/snap2png.py — RGB565 snapshot converter

Section titled “raw2png/snap2png.py — RGB565 snapshot converter”

Decodes snapshot files saved by the factory application’s gfx_snapshot_begin / gfx_snapshot_end / snap_write subsystem (see esp32s3_bzm_tft35_gt911_factory_app_display.md) and converts them to standard PNG images on the host.

Offset Size Content
------ ---- -----------------------------------------------
0 4 B Image width (uint32_t, little-endian)
4 4 B Image height (uint32_t, little-endian)
8 W×H×2 Raw RGB565 pixels, row-major, no padding

Each 16-bit word is split as:

Bit 15..11 → Red (5 bits) → scale to 8 bits: (r << 3) | (r >> 2)
Bit 10..5 → Green (6 bits) → scale to 8 bits: (g << 2) | (g >> 4)
Bit 4..0 → Blue (5 bits) → scale to 8 bits: (b << 3) | (b >> 2)

No

Yes

snap*.raw

(on SD card / copied to PC)

Open file

Read header:

width = uint32 LE

height = uint32 LE

Read width x height x 2 bytes

Raw RGB565 pixel data

Actual size

== expected?

Print warning

continue

Pixel loop x,y:

offset = y x W + x x 2

pixel = uint16 LE

rgb565_to_rgb888 pixel

Pillow Image RGB

W x H

img.save output_path

.png format

Done

FunctionSignatureDescription
main() → NoneCLI argument parsing; iterates input files
convert_raw_to_png(raw_path, output_path=None) → NoneReads header, decodes pixels, saves PNG
rgb565_to_rgb888(pixel: int) → tuple[int,int,int]Converts one 16-bit RGB565 value to 8-bit R/G/B tuple
Terminal window
# Single file (output name derived automatically: snap000.raw → snap000.png)
python snap2png.py snap000.raw
# Wildcard batch conversion
python snap2png.py snap*.raw
# Custom output filename (single file only)
python snap2png.py snap000.raw -o screenshot.png

ScriptPython stdlibThird-party
flash_all.pyargparse, glob, os, sys, subprocesspyserial (serial.tools.list_ports), esptool (subprocess)
flash_factory.pyargparse, csv, glob, os, sys, subprocesspyserial, esptool
generate_font.pysys, os, pathlibPillow (PIL.Image, PIL.ImageDraw, PIL.ImageFont)
snap2png.pyargparse, struct, sys, pathlibPillow (PIL.Image)

Install all at once:

Terminal window
pip install esptool pyserial Pillow

ESP32-S3 boardtools/Developer (host)ESP32-S3 boardtools/Developer (host)Step 1 - First-time board setupStep 2 - Build and flash main firmwareStep 3 - Iterate factory app onlyStep 4 - Capture and inspect display snapshotStep 5 - Regenerate display fontpython flash_all.py --recoveryesptool: bootloader + partitions + factory.binFlash OKpython flash_all.py --fw build/firmware.binesptool: firmware @ 0x20000Flash OKpython flash_factory.pyesptool: factory.bin @ CSV offsetFlash OKgfx_snapshot_begin / snap_write saves snap000.raw on SD cardpython snap2png.py snap000.rawsnap000.png (PNG image)python generate_font.py 8x16 ./mainfont8x16.c / font8x16.hRebuild firmware

Both flash scripts use the same detection heuristic:

Yes

No

Yes

No

No

List all serial ports

serial.tools.list_ports.comports

Iterate ports

description or manufacturer

contains CP210x / CH340 / CH910 /

FTDI / USB Serial?

Return this port

More ports?

Return first port

in list if any

Any port?

Return None

error: use --port


ModuleRelationship
esp32s3_bzm_tft35_gt911_factory_app.mdParent factory application
esp32s3_bzm_tft35_gt911_factory_app_display.mdConsumes font C sources; produces .raw snapshot files
esp32s3_bzm_tft35_gt911_factory_app_storage.mdSD card mount/unmount used by snapshot capture
esp32s3_bzm_tft35_gt911_bsp.mdBSP board support package for the production firmware
esp32s3_bzm_tft35_gt911_build_scripts.mdBuild variant scripts (separate from flash tools)
pibot_pendant_v1_0_factory_app_tools.mdEquivalent tools for the PiBot Pendant v1.0 board
esp32s3_8048s070c_factory_app_tools.mdEquivalent tools for the 800×480 7″ board