Adding a userspace tool

Use the Buildroot external tree to include an application in the image. Follow Writing applications for a first C program and a build-to-board example. The commands here run from the parent project root unless marked as board commands.

Add a shell script

Place a user command under buildroot-external/board/esp32-s31/overlay/usr/bin/; use usr/sbin/ for an administrative command. Start the script with its interpreter, such as #!/bin/sh, and make it executable. Buildroot copies this filesystem overlay into the image during the rootfs build.

Add a Buildroot package

This example adds a small s31-hello program using the same local-source package mechanism as s31-tools.

1. Add the source

Create rootfs/s31-hello/hello.c:

#include <stdio.h>

int main(void)
{
    puts("Hello from the s31-hello package!");
    return 0;
}

2. Add the package definition

Create buildroot-external/package/s31-hello/Config.in:

config BR2_PACKAGE_S31_HELLO
    bool "s31-hello"
    help
      Small example application for ESP32-S31 Linux.

Create buildroot-external/package/s31-hello/s31-hello.mk. The command lines inside the define blocks start with a tab:

S31_HELLO_VERSION = 1.0
S31_HELLO_SITE = $(BR2_EXTERNAL_ESP32_S31_PATH)/../rootfs/s31-hello
S31_HELLO_SITE_METHOD = local

define S31_HELLO_BUILD_CMDS
	$(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) \
		$(@D)/hello.c -o $(@D)/s31-hello
endef

define S31_HELLO_INSTALL_TARGET_CMDS
	$(INSTALL) -D -m 0755 $(@D)/s31-hello \
		$(TARGET_DIR)/usr/bin/s31-hello
endef

$(eval $(generic-package))

Add this line inside the existing menu in buildroot-external/Config.in:

source "$BR2_EXTERNAL_ESP32_S31_PATH/package/s31-hello/Config.in"

buildroot-external/external.mk already includes package/*/*.mk, so the new Makefile is found automatically. For a larger application, add its build dependencies and corresponding Kconfig dependencies or selections. Record its actual license and license files in the package metadata when preparing it for distribution. s31-tools and esp-simd are local examples.

3. Select and save the package

After make fetch has prepared the Buildroot output, open its menu:

make buildroot-menuconfig

Find s31-hello under the external ESP32-S31 packages menu and enable it. Exit with the configuration saved, then use Buildroot’s savedefconfig target to export a review copy:

make -C buildroot O="$PWD/out/buildroot" \
  BR2_EXTERNAL="$PWD/buildroot-external" \
  savedefconfig DEFCONFIG="$PWD/out/generated/buildroot-menu.defconfig"

Merge the intended package selections into buildroot-external/configs/esp32s31_rootfs_defconfig; this example adds BR2_PACKAGE_S31_HELLO=y. Preserve the source expressions for BR2_TOOLCHAIN_EXTERNAL_PATH and BR2_ROOTFS_OVERLAY: the review copy contains resolved host/toolchain and staging paths injected by the parent, so do not replace the portable source defconfig with that whole file.

Review the source diff before reconfiguring. A populated Buildroot output rejects manually changed output configuration and changed package/toolchain inputs. Reconfiguration discards that output while retaining download caches.

4. Build, flash, and run

The application is deployed as part of a verified matched image set. With the ESP-IDF environment active, run on the host:

make buildroot-reconfigure
make fetch-rootfs
make image
ls -l out/buildroot/target/usr/bin/s31-hello
make flash-existing-all PORT=/dev/ttyUSB0

Replace the port and close its serial monitor before flashing. This updates kernel, rootfs/module and radio together; partial flash targets are disabled. After restart, log in through the serial console and run on the board:

s31-hello

It prints Hello from the s31-hello package!. This process does not require an SSH server on the board.

5. Rebuild after changing local source

The parent tracks changed inputs for its existing local utility packages; your new package is not in that list. After editing rootfs/s31-hello/hello.c, remove this package’s build directory before rebuilding the image:

make -C buildroot O="$PWD/out/buildroot" \
  BR2_EXTERNAL="$PWD/buildroot-external" s31-hello-dirclean
make image
make flash-existing-all PORT=/dev/ttyUSB0

This makes Buildroot copy and compile the updated local source. Repeat the board run command after flashing.

Keep the application and its dependencies in the image

The post-build script prunes selected programs and shared libraries after package installation. Check out/buildroot/target after make rootfs, including the libraries your executable needs. A successful package build alone does not show that its runtime files remain in the image.

For example, the script removes libstdc++, libatomic, BlueZ tools and daemon, and D-Bus/GLib/BlueALSA files. Adding a C++ application or a BlueZ-based system therefore needs a corresponding review of those removal rules. The current script checks essential runtime executables, including s31-btstack-a2dp, s31-ext-test, s31-gpio, s31-config-archive, esp32-config, and the BTstack startup script /etc/init.d/S40btstack. Replacing a required file needs a corresponding update to that check and the services or commands that use it. The parent build checks that the finished rootfs fits its flash partition.

Deploy files that must survive reboot

Ordinary persistence is covered by Configuration. Two startup cleanup rules matter when uploading replacements for packaged files. Before mounting the writable root layer, the init script removes writable copies of:

/usr/sbin/s31-btstack-a2dp
/etc/init.d/S40btstack
/etc/init.d/S40bluetoothd
/etc/init.d/S42s31-a2dp
/usr/sbin/bluetoothd
/usr/bin/bluealsa
/usr/sbin/s31-bt-agent
/etc/bluetooth/main.conf

It also removes writable copies of .dtbo filenames shipped in /usr/lib/s31-overlays by the current rootfs. A replacement uploaded to one of these paths can work during the current session and disappear at the next boot. To retain changes to these packaged files, rebuild and flash the rootfs. For a temporary experiment, use a separate filename and invoke it explicitly. Pairing keys and /etc/esp32-conf are outside these cleanup rules.