Build from source

Ubuntu 24.04 LTS on x86-64 Linux is the reference build platform. The pinned prebuilt Linux toolchain is an x86-64 Linux executable. Other Linux distributions need compatible development tools and libraries; another host architecture requires a suitable source-built toolchain. Native Windows and macOS source builds are not covered here.

Before you start building for the ESP32-S31 Linux port, make sure you have the following prerequisites.

  • A relatively recent Linux distribution.

  • Sufficient free disk for the source history, toolchain, ESP-IDF tools/download cache, and build outputs. Do not treat 10 GiB as a verified full-build minimum; usage depends on clone depth and retained toolchain/package caches. Check available space with df -h before building and leave room for temporary downloads and rebuilds.

  • RAM usage depends on the configured packages and build parallelism; no measured minimum is specified here. Reduce JOBS if the host runs out of RAM.

1. Clone the Repository

Clone the ESP32-S31 Linux port repository and initialize its pinned submodules:

git clone --recurse-submodules https://github.com/GrieferPig/esp32-s31-linux.git
cd esp32-s31-linux
git submodule update --init --recursive

2. Install Development Tools and Libraries

The ESP-IDF environment installed below supplies esptool for image generation and flashing. A separate global installation is not needed for a source build. For flashing only, use the isolated installation in Flash and first boot.

Build Essentials

These provide the essential development tools and libraries required to build and work with the ESP32-S31 Linux port. Install with

sudo apt-get update
sudo apt-get install -y \
  git curl wget file python3 python3-pip python3-venv cmake \
  bison bc build-essential ccache cpio device-tree-compiler flex gperf \
  libffi-dev libssl-dev libncurses-dev ninja-build python3-pkg-resources python3-pyelftools \
  python3-dev swig rsync unzip xz-utils mtd-utils squashfs-tools

libncurses-dev supports the Buildroot configuration menu, and mtd-utils supplies mkfs.jffs2 for the optional make persist target.

Select the interpreter that can import the installed U-Boot host dependencies:

export HOST_PYTHON=/usr/bin/python3
"$HOST_PYTHON" -c 'import pkg_resources, elftools'

Keep this setting when activating ESP-IDF below. Its Python environment may not include Ubuntu’s python3-pkg-resources and python3-pyelftools packages. The build uses HOST_PYTHON for U-Boot’s Python tools, including binman scripts with an env python3 shebang; ESP-IDF retains its own environment. If using a different host interpreter, install those dependencies there and run the same import check. Save the interpreter path in local.mk to reuse it in new shells.


ESP-IDF

ESP-IDF is the official development framework for the ESP32 series of chips. LP and Radio firmwares in esp32-s31-linux depend on ESP-IDF’s proprietary libraries and toolchain.

Use the exact ESP-IDF revision in configs/build-versions.mk, not a moving master branch or a version number alone. The current pin is a602e67b0bf9ee0806dc4e1df7afc9affedf5c33. From the project root, install it into a new directory:

export IDF_PATH="$HOME/esp-idf"
ESP_IDF_REF=$(sed -n 's/^ESP_IDF_REF := //p' configs/build-versions.mk)
git clone --filter=blob:none https://github.com/espressif/esp-idf.git "$IDF_PATH"
git -C "$IDF_PATH" checkout "$ESP_IDF_REF"
git -C "$IDF_PATH" submodule update --init --recursive
"$IDF_PATH/install.sh" esp32s31

If you already have an ESP-IDF installation, use a separate checkout or save its local changes before switching revisions. make idf-check rejects a checkout that differs from the pin. S31_ALLOW_UNPINNED=1 is an explicit experimental override, not the reproducible build path.

Espressif’s ESP32-S31 installation guide provides platform setup help; retain this project’s pinned revision when following it.

Custom Toolchains

A fork of espressif’s crosstool-ng is used to build custom toolchains for the ESP32-S31 Linux port. These custom toolchains contain optimizations specific to this port and the S31 chip.

Sources can be found on GitHub. The release binaries are built via GitHub Actions. Download them to cache/toolchains/riscv32-esp-linux-musl by

make toolchain-fetch

The default release is pinned in configs/build-versions.mk (esp32s31-linux-gcc-15.2.0-5 at this revision). The target checks the downloaded archive against the release’s SHA-256 file. Use TOOLCHAIN_RELEASE_TAG=local only when deliberately testing an already installed custom toolchain.

3. Build the Project

Activate ESP-IDF in your terminal:

export IDF_PATH="$HOME/esp-idf" # Replace with the path to your ESP-IDF installation if different
. "$IDF_PATH/export.sh"

Build the complete image on the host:

make doctor
make fetch
make image

image builds the boot firmware, kernel, rootfs, radio XIP image, and combined flash image, verifies the paired artifacts, and publishes them atomically to dist/. all is a compatibility alias for image. It does not access a serial port or flash a board. The default is the full board configuration with native controller drivers. Drivers for external devices, such as a particular codec or USB device class, may require additional kernel selections. Set JOBS, for example make JOBS=4 all, to limit parallel compilation. See Build configuration.

To build a particular component, use

make uboot
make linux
make rootfs

To update a connected board already using the compact layout while preserving its persist partition, select the port explicitly:

make PORT=/dev/ttyUSB0 BAUD=2000000 flash-existing-all

This target verifies and writes the immutable dist/current set without rebuilding; flash-all is its alias. Complete make image first, before flashing. Partial update targets are disabled because the installed companion identities are unknown. make build-flash explicitly combines build and flash. For first installation, data backup, and release-image updates, see Flash and first boot.

4. Build output

Path under out/images/

Content

u-boot-spl-dtb.bin

SPL with its DTB

spl_app.bin

ESP ROM image wrapper around SPL

u-boot.itb

FIT containing OpenSBI, U-Boot proper and associated data

esp32s31_generic.dtb

Linux device tree

xipImage

Flash-XIP Linux kernel

rootfs.sqfs

SquashFS root filesystem

radio.bin

Prelinked flash-XIP radio payload; paired with this build’s kernel

s31_full_flash.bin

Combined installation image; overwrites persist

radio.json

Build-time kernel/module/payload/import binding and radio hash

build-manifest.json

Source, configuration, toolchain, artifact hashes and build provenance

SHA256SUMS

Checksums for the release artifacts

persist.jffs2

Empty JFFS2 image, produced only by make persist; destructive flashing requires a separate maintenance procedure

For more build targets, see the Make reference.

Native build trees live beside images/: linux/, opensbi/, u-boot/, idf-radio/, radio/, lp/, and buildroot/. Shared downloads and toolchains are under cache/; make clean only removes the build output.

Host-specific settings belong in the ignored local.mk, for example:

IDF_EXPORT := /opt/esp-idf/export.sh
HOST_PYTHON := /usr/bin/python3
JOBS := 4

An explicitly requested ROOTFS_BASELINE=/absolute/path/to/rootfs.sqfs build repackages a verified existing rootfs with the current radio module. Its manifest records incremental provenance, validates the full runtime inventory and module list, and rejects incomplete userspace. With explicit ROOTFS_BUSYBOX_BUILD evidence it can restore only missing stock logging/cron init scripts from pinned sources. Inherited binaries retain their original optimization. This is not a clean Buildroot rebuild and does not validate unrelated rootfs source changes.