$REPO_NAME
NOTE: Gitea replaces
$REPO_NAMEabove when a new repo is created from this template (README.mdmust be listed in.gitea/template).
Toolchain
| Item | Required version |
|---|---|
| ESP-IDF | v6.0.2 (exact, no other version) |
| Python | 3.10 – 3.13 (3.14 is not supported) |
| Docker image (CI) | espressif/idf:v6.0.2 |
Supported targets: esp32, esp32s3, esp32c3, esp32c6
Release binaries come only from CI. Local builds are for development. If a local build and CI disagree, CI is correct and your local setup is wrong.
1. Install ESP-IDF (first time only)
macOS
-
Install Python 3.13 next to whatever you already have:
brew install [email protected] export PATH="$(brew --prefix [email protected])/libexec/bin:$PATH" python3 --version # must print 3.13.xAdd the
exportline to~/.zshrcif you want it permanent. -
Install the ESP-IDF Installation Manager (EIM) CLI:
brew tap espressif/eim brew install eim -
Install the pinned IDF version from the same terminal window:
eim install -i v6.0.2Do not launch the EIM GUI from Finder/Launchpad. GUI apps don't see the
PATHyou set in the terminal and will fail with "Python Not Configured".
Windows
-
Install the EIM CLI with WinGet (PowerShell):
winget install Espressif.EIM-CLIPrefer a GUI? Use
winget install Espressif.EIMand launch it from the Start Menu. On Windows, EIM installs missing prerequisites (including Python) itself. -
Install the pinned IDF version:
eim install -i v6.0.2 -
Keep project paths short and without spaces, e.g.
C:\dev\arcs\<repo>. Long or spaced paths are a common cause of ESP-IDF build failures. -
If the board doesn't show up as a COM port, install the USB-serial driver for its bridge chip (CP210x or CH340). Boards using the chip's native USB (ESP32-S3/C3/C6) usually need no driver.
-
Find the port in Device Manager → Ports (COM & LPT), then flash:
idf.py -p COM3 flash monitor
Linux
Same as macOS, using your distro's Python 3.10–3.13 and the EIM package from
the EIM docs.
Then eim install -i v6.0.2.
Any OS, no local install (Docker)
docker run --rm -v "$PWD":/project -w /project \
-e IDF_GIT_SAFE_DIR=/project \
espressif/idf:v6.0.2 \
idf.py set-target esp32s3 build
Docker can build but generally cannot flash on macOS/Windows (no USB passthrough). Flash from the host with a local install.
2. Activate the environment
Each new terminal needs the ESP-IDF environment for v6.0.2. EIM prints the
activation command at the end of eim install; run it, then check:
idf.py --version # must print ESP-IDF v6.0.2
If it prints anything else, stop and fix that first.
3. Build
Choose the target once per checkout (this wipes build/ and regenerates config):
idf.py set-target esp32s3
idf.py build
Switching targets later means running set-target again.
4. Flash and monitor
Find the board's serial port:
ls /dev/cu.* # macOS: usually /dev/cu.usbmodem* or /dev/cu.usbserial-*
ls /dev/ttyUSB* /dev/ttyACM* # Linux
Flash and open the serial monitor:
idf.py -p <PORT> flash monitor
Exit the monitor with Ctrl+].
5. Verify the boot log
A good boot prints this (values will differ):
I (312) main: project : <repo name>
I (312) main: version : v0.1.0-3-g1a2b3c4
I (312) main: idf : v6.0.2
I (313) main: target : esp32s3 rev v0.2, 2 core(s)
I (313) main: reset : 1
I (1313) main: alive 1
I (2313) main: alive 2
Check every line:
| Line | Pass if |
|---|---|
project |
Matches this repo |
version |
Matches git describe --tags --dirty and has no -dirty |
idf |
Exactly v6.0.2 |
target |
Matches the chip on the board |
reset |
1 (power-on) after a fresh power-up. Anything else means the previous run crashed |
alive |
Should keep counting indefintely. |
Project layout
CMakeLists.txt Project definition
main/CMakeLists.txt Registers main.c and its dependencies
main/main.c Boot banner + heartbeat
sdkconfig.defaults Shared config. The ONLY config file committed.
.gitignore
.gitea/template Files Gitea fills in when generating a new repo
.gitea/workflows/ CI: builds every supported target on each push
Config rules
- Commit
sdkconfig.defaults. Never commitsdkconfig(it's generated). - To change a setting:
idf.py menuconfig, thenidf.py save-defconfig, then copy the lines you changed intosdkconfig.defaults.
Troubleshooting
| Symptom | Fix |
|---|---|
| EIM: "Python Not Configured" | Wrong Python on PATH. See step 1; run EIM from the terminal. |
idf.py --version isn't v6.0.2 |
Wrong environment activated. Re-run the v6.0.2 activation command. |
Version ends in -dirty |
Uncommitted changes. Commit or stash, rebuild. Never flash -dirty to a vehicle. |
Permission denied on the port (Linux) |
Add yourself to the dialout group, log out and in. |
| Flash fails to connect | Hold BOOT, tap RESET, release BOOT, retry. Check the cable carries data. |
| Build works locally, fails in CI | CI is right. Check your IDF version and uncommitted files. |
Origin
Based on the ESP-IDF v6.0.2 examples/get-started/hello_world (CC0-1.0), with
these changes:
- Removed the 10-second
esp_restart()loop (it made every boot look like a software reset); replaced with a 1 s heartbeat. - Added project name, version, IDF version and reset reason to the boot log.
- Removed
pytest_hello_world.py,sdkconfig.ciand the example README. - Added
.gitignore,sdkconfig.defaults, Gitea template file and CI.
When upgrading ESP-IDF, diff the new version's hello_world against the old one
to see what Espressif changed, then update the version everywhere in this file,
in CI, and in main/idf_component.yml if present.