Skip to content

Buildroot: Custom Image with SDL2 and SPI

Time estimate: ~90+ minutes (build time: 30--60 minutes) Prerequisites: Buildroot: Minimal Linux

Learning Objectives

By the end of this tutorial you will be able to:

  • Extend a Buildroot configuration for a specific application
  • Add SDL2 with KMSDRM backend support
  • Enable SPI kernel support in Buildroot
  • Measure the boot time impact of added packages
Understanding Buildroot Package Selection

Buildroot is a tool that cross-compiles a complete Linux system — kernel, libraries, and applications — for an embedded target. Its menuconfig interface lets you select exactly which packages to include in the final image. Each selected package is downloaded, cross-compiled for the target architecture, and installed into the root filesystem. For example, enabling SDL2 with the KMSDRM backend adds the SDL2 library and its DRM/KMS rendering support so that graphical applications can run without a desktop environment. Because only the packages you select are included, the resulting image stays small — typically tens to hundreds of megabytes instead of the gigabytes required by a full desktop distribution.

For a deeper look at why you would build a custom Linux image and how Buildroot, Yocto, and other build systems compare, see the Building Custom Linux Reference.


1. Start from Existing Config

Navigate to your Buildroot directory from the previous tutorial and load your saved configuration:

cd ~/buildroot
make raspberrypi4_64_defconfig
# Or load your saved config from the previous tutorial:
# cp ../my_saved_defconfig .config

Open the configuration menu:

make menuconfig
Tip

If you saved a defconfig in the previous tutorial with make savedefconfig, you can restore it by copying it to configs/ and running make <your_defconfig>. This ensures you start from a known working state.


2. Enable SPI Support

SPI kernel support is needed for communicating with hardware like the BMI160 accelerometer in later tutorials.

In make menuconfig, navigate to:

Kernel → Linux Kernel → Kernel configuration

Select Use the architecture default configuration or if you are using a custom fragment, ensure the following options are enabled:

Then run the kernel configuration tool:

make linux-menuconfig

Navigate to:

Device Drivers → SPI support →
    [*] BCM2835 SPI controller
    [*] User mode SPI device driver support (spidev)

These two options do the following:

Option Purpose
BCM2835 SPI controller Hardware SPI driver for the Raspberry Pi's Broadcom SoC
User mode SPI device driver support Exposes SPI devices as /dev/spidevX.Y for userspace access

Save and exit the kernel configuration.

Checkpoint

After saving the kernel config, verify your selections are stored. Run make linux-menuconfig again and confirm both SPI options show [*].

Stuck?

If you cannot find BCM2835 SPI controller, make sure you are building for the correct platform (raspberrypi4_64_defconfig). The BCM2835 driver also covers BCM2836/2837/2711 used in Pi 3 and Pi 4.


3. Add SDL2

SDL2 (Simple DirectMedia Layer) provides cross-platform graphics, audio, and input handling. For embedded Linux without a window manager, we use the DRM/KMS backend which renders directly to the display via the kernel's DRM/KMS subsystem.

In make menuconfig, navigate to:

Target packages → Graphic libraries and applications →
    [*] SDL2

After enabling SDL2, enter its submenu and configure:

SDL2 →
    [*] kmsdrm video driver

This tells SDL2 to use the DRM/KMS interface instead of X11 or Wayland. On an embedded system without a desktop environment, this is the correct backend.

SDL2 will automatically pull in its dependencies:

Dependency Purpose
libdrm Kernel DRM/KMS interface library
mesa3d (optional) GPU acceleration (may not be selected by default)
libgbm Generic buffer management for DRM
Tip

Do not enable the X11 or Wayland video drivers unless you specifically need them. Each adds significant build time and image size.

SDL2's KMSDRM backend also requires the VC4 DRM driver in the kernel. Verify it is enabled in the kernel config:

make linux-menuconfig

Navigate to:

Device Drivers → Graphics support → Direct Rendering Manager →
    [*] VC4 (Broadcom VideoCore GPU)

This is typically enabled by default in the Raspberry Pi defconfig, but verify it is present.


4. Add Python3 Packages

For prototyping and testing, add Python3 with SPI and numerical libraries:

In make menuconfig, navigate to:

Target packages → Interpreter languages and scripting →
    [*] python3

Then add the additional Python packages:

Target packages → Interpreter languages and scripting → External python modules →
    [*] python-spidev
    [*] python-numpy
Package Purpose
python3 Python interpreter for prototyping
python-spidev Python bindings for /dev/spidevX.Y
python-numpy Numerical arrays — used for sensor data processing
Tip

python-numpy significantly increases build time (10--15 minutes). If you are short on time, you can skip it and add it later. python-spidev is small and builds quickly.


5. Enable Device Tree Overlay Support

Device tree overlays allow you to describe additional hardware (like SPI sensors) without modifying the base device tree. Ensure overlay support is included in the boot partition.

In make menuconfig, check:

System configuration → Root filesystem overlay directories

If you have a custom overlay directory (e.g., board/raspberrypi4-64/rootfs-overlay/), ensure it includes the /boot/overlays/ directory structure.

Also verify in the kernel configuration (make linux-menuconfig):

Device Drivers → Device Tree and Open Firmware support →
    [*] Device Tree overlays
Tip

On Raspberry Pi, overlays are loaded by the firmware before the kernel starts. The config.txt file on the boot partition controls which overlays are applied. Ensure your Buildroot post-build scripts copy config.txt to the boot partition.


6. Build

Save your configuration and start the build:

make savedefconfig  # Save your config for reproducibility
make -j$(nproc)

This will take 30--60 minutes depending on your host machine. What happens during the build:

Phase Duration What Happens
Toolchain 5--10 min Cross-compiler is built (if not cached)
Kernel 5--15 min Linux kernel is compiled with your SPI options
SDL2 + deps 5--10 min SDL2 and its dependencies are compiled
Python3 + numpy 10--15 min Python interpreter and numerical library
Root filesystem 1--2 min All packages are assembled into a filesystem image
Image generation <1 min SD card image is created

The final image will be at output/images/sdcard.img.

ls -lh output/images/sdcard.img
Checkpoint

The build should complete without errors. The image file should exist and be larger than your previous minimal image (typically 150--300 MB vs 50--80 MB for minimal).

Stuck?

Common build failures:

  • Missing host dependencies: Run sudo apt install build-essential libncurses-dev and any packages Buildroot reports missing.
  • Network errors: Buildroot downloads source tarballs during build. Ensure internet access.
  • Disk space: A full build can use 5--10 GB. Check with df -h.
  • Partial rebuild: If a single package fails, fix the issue and run make again. Buildroot will resume from where it stopped.

7. Flash and Boot

Write the image to an SD card (replace sdX with your SD card device):

# Identify your SD card
lsblk

# Write the image (WARNING: this erases the SD card)
sudo dd if=output/images/sdcard.img of=/dev/sdX bs=4M status=progress
sudo sync

Insert the SD card into the Raspberry Pi, boot, and connect via SSH or serial console.

Verify the new components:

# Check SPI devices
ls /dev/spidev*

Expected output:

/dev/spidev0.0  /dev/spidev0.1
# Check SDL2
sdl2-config --version

Expected output:

2.28.x
# Check Python and spidev
python3 -c "import spidev; print('spidev OK')"
python3 -c "import numpy; print('numpy', numpy.__version__)"
Checkpoint

All three checks should pass:

  1. /dev/spidev* devices exist
  2. sdl2-config --version prints a version number
  3. Python can import spidev and numpy without errors
Stuck?

SPI devices not appearing:

  • Check that dtparam=spi=on is in /boot/config.txt (or equivalent on your image)
  • Verify kernel config: zcat /proc/config.gz | grep SPI should show CONFIG_SPI_BCM2835=y

SDL2 missing KMSDRM backend:

  • Run sdl2-config --libs and check if kmsdrm appears in the video drivers
  • If not, re-run make menuconfig and verify the kmsdrm option is selected in SDL2's submenu

Python import errors:

  • Ensure the packages were selected in Buildroot menuconfig
  • Run make python-spidev-rebuild and make to rebuild just that package

8. Measure Boot Time

Compare the boot time of this image to your previous minimal image.

If your image includes systemd:

systemd-analyze time

If your image uses BusyBox init (no systemd), measure manually:

# Check kernel boot time from dmesg
dmesg | tail -5

# Check total uptime
cat /proc/uptime

The first number from /proc/uptime is seconds since boot.

Fill in the comparison table:

Metric Minimal Image SDL2 + SPI Image Difference
Image size (MB)
Kernel boot (s)
Total boot to login (s)
Number of packages

Check image size:

ls -lh output/images/sdcard.img

Count installed packages:

# On the Raspberry Pi
ls /usr/lib/ | wc -l
Tip

Each added package increases image size and potentially boot time. In production embedded systems, every package must justify its inclusion. This trade-off analysis is a core skill in embedded Linux engineering.


What Just Happened?

You extended a minimal Buildroot Linux image with specific application requirements:

  1. SPI kernel support — enabled at the kernel level so hardware SPI peripherals appear as /dev/spidevX.Y
  2. SDL2 with KMSDRM — a graphics library that renders directly to the display without a window manager
  3. Python prototyping toolsspidev and numpy for rapid hardware testing
  4. Device tree overlay support — so you can add hardware descriptions without rebuilding the kernel

This image will serve as the base for the BMI160 SPI driver tutorial and the display application tutorials. The key engineering decision was choosing the KMSDRM backend for SDL2, which avoids the overhead of X11/Wayland while still providing a standard graphics API.


Challenges

Challenge 1: Add OpenCV

Enable OpenCV in Buildroot (Target packages → Libraries → Graphics → opencv4). Measure the build time increase and final image size. How much larger is the image compared to SDL2 alone?

Challenge 2: Minimize Image Size

Starting from your current config, try to reduce the image size by removing unnecessary features (e.g., disable Python readline, remove unused SDL2 backends). Document each change and its size impact. What is the smallest image that still has working SDL2 + SPI + Python?

Challenge 3: Boot Time Budget

Create a boot time budget document that allocates time to each boot phase. If the requirement is "application ready in under 5 seconds," which components would you need to optimize or remove?


Deliverable

Submit the following:

Item Description
Buildroot defconfig Your saved defconfig file (from make savedefconfig)
Verification screenshot Terminal output showing SPI, SDL2, and Python checks from Step 7
Boot time comparison table Completed table from Step 8 comparing minimal vs. SDL2+SPI image
Image size comparison File sizes of both images

Course Overview | Next: BMI160 SPI Driver →