25 KiB
Yocto Image for Raspberry Pi — ECU Test Framework as a Bench Appliance
This guide explains how to build a custom Linux distribution with the Yocto Project so a Raspberry Pi is the test bench: power it on, it boots, the test framework runs the configured suites against a connected MUM and ECU, and reports land in a known location (or get pushed to a server). No PC in the loop.
If you only want to run the framework on a stock Raspberry Pi OS
install, you're looking at
docs/09_raspberry_pi_deployment.md.
If you want a pre-baked Pi OS image (still Debian, just snapshotted
with the framework already installed), see
docs/10_build_custom_image.md. This
document is about Yocto specifically — building a minimal, hardened,
reproducible OS from sources around the framework.
1. Why Yocto vs. Raspberry Pi OS?
| Concern | Raspberry Pi OS (Debian) | Yocto |
|---|---|---|
| First image up | Hours | First build: a day. Subsequent builds: hours. |
| Image size | ~2–4 GB minimum (Lite) | ~150–500 MB realistic |
| Reproducibility | Snapshot of apt state at image time |
Full source pinning via layer revisions |
| Auditing "what's installed" | dpkg -l of a moving target |
Single manifest, version-pinned |
| Hardening / removing surface | Have to disable / uninstall | Just don't include the recipe |
| Boot time to test | 30–60 s | 5–15 s with a tuned image |
| Building a fleet | Re-snapshot per change | Rebuild image, push artifact |
| Build host requirements | Pi + SD card | Linux build host with ~100 GB free and ~16 GB RAM ideally |
Pick Yocto when the Pi is a deployed appliance, not a workstation — a permanent bench, a HIL rack, a customer-shipped test fixture. For day-to-day developer work the Pi OS path is fine.
2. Architecture
┌────────────────────────────────┐
│ Raspberry Pi (Yocto image) │
│ │
┌── Ethernet ───┤ 192.168.7.1 (host on RNDIS) │
│ │ │
┌───────────────┴──────────┐ │ ecu-test-framework systemd │
│ MUM @ 192.168.7.2 │ │ service: │
│ USB-RNDIS (or wired) │ │ pytest -m "hardware and │
└──────────────────────────┘ │ mum and not slow" │
│ │
│ /opt/ecu-tests/ (the repo) │
│ /opt/ecu-tests/.venv/ │
│ │
┌── USB-serial ─┤ /dev/ttyUSB0 (Owon PSU) │
│ │ │
┌───────────────┴──────────┐ │ /var/log/ecu-tests/ │
│ Owon PSU │ │ report.html, junit.xml, │
└──────────────────────────┘ │ summary.md │
│ │
│ rsync/scp/HTTP push of /var/ │
│ log/ecu-tests/ to a server │
└────────────────────────────────┘
Key choices made by this document:
meta-raspberrypias the BSP forraspberrypi4-64(or-3,-cm4, depending on your hardware).meta-openembeddedfor Python + general userspace.- A new layer
meta-ecu-testsholds: the framework recipe, recipes for the non-PyPI Python deps, the image recipe, and the systemd unit. systemdinit system (Yocto'score-image-minimaldefaults to sysvinit; we override tosystemd).- Pinned Yocto release:
scarthgap(LTS, May 2024). Pick a current LTS at build time; this doc shows scarthgap.
3. Build-host prerequisites
A Linux machine (Ubuntu 22.04 LTS or Debian 12 are the smoothest; WSL2 works but is slower and consumes a lot of disk).
Resources:
- 100 GB free disk (the first
bitbakerun downloads sources and builds toolchains) - 16 GB RAM ideal, 8 GB workable
- Multi-core CPU; expect 1–4 h for the first image build
Packages (Ubuntu 22.04):
sudo apt update
sudo apt install -y \
gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat \
cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping \
python3-git python3-jinja2 python3-subunit zstd liblz4-tool file locales \
libacl1
sudo locale-gen en_US.UTF-8
Make sure your user can run docker if you plan to use the
kas-container shortcut; not required for the manual bitbake
flow shown here.
4. Layer layout
~/yocto/
├── poky/ # Yocto core, ~3 GB
├── meta-openembedded/ # community python/network/etc. layers
├── meta-raspberrypi/ # Raspberry Pi BSP
├── meta-ecu-tests/ # ← we create this
│ ├── conf/
│ │ └── layer.conf
│ ├── recipes-ecu-tests/
│ │ ├── ecu-test-framework_git.bb
│ │ ├── ecu-test-framework/
│ │ │ ├── ecu-test-framework.service
│ │ │ ├── ecu-test-runner.sh
│ │ │ └── push-reports.sh
│ │ └── python3-melexis/
│ │ ├── python3-pylin_1.2.0.bb
│ │ ├── python3-pymumclient_1.2.0.bb
│ │ └── python3-pylinframe_1.2.0.bb
│ ├── recipes-python/
│ │ └── python3-ldfparser_<ver>.bb
│ └── recipes-images/
│ └── ecu-tests-image.bb
└── build/ # bitbake's TMPDIR (huge)
5. Setting up the build environment
5.1 Clone Yocto + BSP + needed layers
mkdir -p ~/yocto && cd ~/yocto
BRANCH=scarthgap
git clone -b $BRANCH https://git.yoctoproject.org/git/poky
git clone -b $BRANCH https://git.openembedded.org/meta-openembedded
git clone -b $BRANCH https://git.yoctoproject.org/git/meta-raspberrypi
5.2 Bootstrap the build directory
source poky/oe-init-build-env build
# you are now in ~/yocto/build/
5.3 Tell bitbake which layers exist
conf/bblayers.conf should look like:
BBLAYERS ?= " \
${TOPDIR}/../poky/meta \
${TOPDIR}/../poky/meta-poky \
${TOPDIR}/../poky/meta-yocto-bsp \
${TOPDIR}/../meta-openembedded/meta-oe \
${TOPDIR}/../meta-openembedded/meta-python \
${TOPDIR}/../meta-openembedded/meta-networking \
${TOPDIR}/../meta-raspberrypi \
${TOPDIR}/../meta-ecu-tests \
"
(meta-ecu-tests will be created in §6 — bitbake will warn until
it exists, that's fine.)
5.4 Configure the build target
conf/local.conf — append/edit:
MACHINE = "raspberrypi4-64"
DISTRO = "poky"
# Init manager
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME:init_manager = "systemd"
VIRTUAL-RUNTIME:initscripts = ""
DISTRO_FEATURES_BACKFILL_CONSIDERED += "sysvinit"
# We want SSH for first-boot diagnosis
EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"
# Make sure Python 3 ends up in the image
IMAGE_INSTALL:append = " python3 python3-modules"
# Speed up downloads and rebuilds by sharing caches
DL_DIR ?= "${TOPDIR}/downloads"
SSTATE_DIR ?= "${TOPDIR}/sstate-cache"
BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}"
PARALLEL_MAKE = "-j${@oe.utils.cpu_count()}"
# Raspberry Pi specifics
ENABLE_UART = "1" # serial console on UART
RPI_USE_U_BOOT = "0"
DISABLE_RPI_BOOT_LOGO = "1"
For a Raspberry Pi 3, change MACHINE = "raspberrypi3-64". For
Compute Module 4: MACHINE = "raspberrypi-cm4". Each lists in
meta-raspberrypi/conf/machine/.
6. Create meta-ecu-tests
6.1 Skeleton
cd ~/yocto
mkdir -p meta-ecu-tests/{conf,recipes-ecu-tests,recipes-python,recipes-images}
mkdir -p meta-ecu-tests/recipes-ecu-tests/ecu-test-framework
mkdir -p meta-ecu-tests/recipes-ecu-tests/python3-melexis
meta-ecu-tests/conf/layer.conf:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "ecu-tests"
BBFILE_PATTERN_ecu-tests = "^${LAYERDIR}/"
BBFILE_PRIORITY_ecu-tests = "10"
LAYERSERIES_COMPAT_ecu-tests = "scarthgap"
LAYERDEPENDS_ecu-tests = "core meta-python openembedded-layer raspberrypi"
6.2 Recipe — the framework itself
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework_git.bb:
SUMMARY = "ECU Test Framework (pytest-based MUM/PSU bench runner)"
DESCRIPTION = "Hardware-in-the-loop test suite for the 4SEVEN ALM ECU."
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""
SRC_URI = " \
git://your-git-host/ecu-tests.git;branch=main;protocol=https \
file://ecu-test-framework.service \
file://ecu-test-runner.sh \
file://push-reports.sh \
"
SRCREV = "${AUTOREV}"
PV = "0.1+git${SRCPV}"
S = "${WORKDIR}/git"
inherit systemd
SYSTEMD_SERVICE:${PN} = "ecu-test-framework.service"
SYSTEMD_AUTO_ENABLE = "enable"
RDEPENDS:${PN} = " \
python3-pytest \
python3-pytest-html \
python3-pytest-cov \
python3-pytest-xdist \
python3-pyserial \
python3-pyyaml \
python3-ldfparser \
python3-pylin \
python3-pymumclient \
python3-pylinframe \
bash \
"
do_install() {
install -d ${D}/opt/ecu-tests
cp -a ${S}/* ${D}/opt/ecu-tests/
# Strip any committed venv / cache / reports
rm -rf ${D}/opt/ecu-tests/.venv ${D}/opt/ecu-tests/.pytest_cache \
${D}/opt/ecu-tests/reports/*
install -d ${D}/var/log/ecu-tests
install -d ${D}/etc/ecu-tests
install -m 0755 ${WORKDIR}/ecu-test-runner.sh ${D}/opt/ecu-tests/
install -m 0755 ${WORKDIR}/push-reports.sh ${D}/opt/ecu-tests/
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/ecu-test-framework.service \
${D}${systemd_system_unitdir}/
}
FILES:${PN} = " \
/opt/ecu-tests \
/var/log/ecu-tests \
/etc/ecu-tests \
${systemd_system_unitdir}/ecu-test-framework.service \
"
Replace git://your-git-host/ecu-tests.git;branch=main;protocol=https
with your actual remote. For an air-gapped build, ship the repo as
a tarball: SRC_URI = "file://ecu-tests.tar.gz" and place it next
to the recipe.
6.3 Recipe — runner script
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework/ecu-test-runner.sh:
#!/bin/sh
set -eu
REPO=/opt/ecu-tests
LOG=/var/log/ecu-tests
RUN_TS=$(date -u +%Y%m%dT%H%M%SZ)
OUT="$LOG/$RUN_TS"
mkdir -p "$OUT"
cd "$REPO"
# Marker selection lives in /etc/ecu-tests/marker (a single line, e.g.:
# hardware and mum and not slow
# Defaults to a safe non-slow MUM run.
MARKER=$(cat /etc/ecu-tests/marker 2>/dev/null || echo "hardware and mum and not slow")
ECU_TESTS_CONFIG=/etc/ecu-tests/test_config.yaml \
python3 -m pytest -m "$MARKER" -v \
--junitxml="$OUT/junit.xml" \
--html="$OUT/report.html" --self-contained-html \
--tb=short 2>&1 | tee "$OUT/run.log" || true
# Symlink "latest" for convenience
ln -sfn "$RUN_TS" "$LOG/latest"
# Optional: rsync to a server. Reads RSYNC_DEST from
# /etc/ecu-tests/push.env. Silently no-ops if unset.
. /etc/ecu-tests/push.env 2>/dev/null || true
[ -n "${RSYNC_DEST:-}" ] && /opt/ecu-tests/push-reports.sh "$OUT" "$RSYNC_DEST" || true
push-reports.sh is a thin rsync wrapper — left as an exercise
for your network setup (or replace with curl to an HTTP collector,
or mosquitto_pub to MQTT — whatever your infra prefers).
6.4 Recipe — systemd unit
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework/ecu-test-framework.service:
[Unit]
Description=ECU Test Framework one-shot run
After=network-online.target dev-ttyUSB0.device
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/ecu-tests/ecu-test-runner.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Pair this with a .timer if you want periodic runs, or leave as a
one-shot triggered by reboot or systemctl start ecu-test-framework.service over SSH.
For continuous runs (every N minutes), add
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework/ecu-test-framework.timer:
[Unit]
Description=Run ECU tests every 30 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=30min
Unit=ecu-test-framework.service
[Install]
WantedBy=timers.target
…and add it to SYSTEMD_SERVICE:${PN} in the recipe.
6.5 Recipe — python3-ldfparser
ldfparser is on PyPI but isn't in stock OpenEmbedded. Add a
minimal recipe at
meta-ecu-tests/recipes-python/python3-ldfparser_0.27.0.bb
(update the version):
SUMMARY = "Pure-Python LDF parser (LIN Description File)"
HOMEPAGE = "https://github.com/c4deszes/ldfparser"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=<fill-in-md5>"
SRC_URI = "https://files.pythonhosted.org/packages/source/l/ldfparser/ldfparser-${PV}.tar.gz"
SRC_URI[sha256sum] = "<fill-in-sha256>"
S = "${WORKDIR}/ldfparser-${PV}"
inherit setuptools3 pypi
RDEPENDS:${PN} = "python3-lark python3-bitstruct"
Run bitbake -c devshell python3-ldfparser and use
devtool to compute the hashes if you don't already have them, or
fetch them with:
pip download ldfparser==0.27.0 --no-deps -d /tmp/ldfparser
sha256sum /tmp/ldfparser/ldfparser-0.27.0.tar.gz
6.6 Recipes — Melexis non-PyPI packages
pylin, pymumclient, and pylinframe ship inside the Melexis IDE
installer; they're not on PyPI and you must source them from a
legally-licensed Melexis install. Each gets a recipe that consumes a
pre-staged tarball under ${BSPDIR}/downloads/.
Stage once on the build host:
# On a machine that has Melexis IDE installed
MELEXIS_SITE="/mnt/c/Program Files/Melexis/Melexis IDE/plugins/com.melexis.mlxide.python_1.2.0.202408130945/python/Lib/site-packages"
mkdir -p ~/yocto/downloads
for pkg in pylin pymumclient pylinframe; do
tar -czf ~/yocto/downloads/${pkg}-1.2.0.tar.gz -C "$MELEXIS_SITE" $pkg
done
meta-ecu-tests/recipes-ecu-tests/python3-melexis/python3-pylin_1.2.0.bb:
SUMMARY = "Melexis pylin — proprietary, not redistributable"
DESCRIPTION = "Vendored copy of the pylin package shipped with Melexis IDE."
LICENSE = "Proprietary"
LIC_FILES_CHKSUM = ""
# License Mode:
# This recipe ships proprietary code. Yocto will refuse to build unless
# you whitelist it. In your conf/local.conf:
# LICENSE_FLAGS_ACCEPTED += "commercial_pylin commercial_pymumclient commercial_pylinframe"
LICENSE_FLAGS = "commercial_pylin"
# The tarball must be pre-staged at DL_DIR/pylin-${PV}.tar.gz
SRC_URI = "file://pylin-${PV}.tar.gz"
SRC_URI[sha256sum] = "<fill-in>"
S = "${WORKDIR}"
RDEPENDS:${PN} = "python3 python3-modules"
do_install() {
install -d ${D}${PYTHON_SITEPACKAGES_DIR}
cp -a ${S}/pylin ${D}${PYTHON_SITEPACKAGES_DIR}/
}
FILES:${PN} = "${PYTHON_SITEPACKAGES_DIR}/pylin"
python3-pymumclient_1.2.0.bb and python3-pylinframe_1.2.0.bb
are the same shape with the package name and LICENSE_FLAGS
swapped.
Add to conf/local.conf:
LICENSE_FLAGS_ACCEPTED += "commercial_pylin commercial_pymumclient commercial_pylinframe"
License hygiene: the resulting image embeds proprietary packages. Treat the image artifact as proprietary — same access controls as the Melexis IDE installer.
6.7 Image recipe
meta-ecu-tests/recipes-images/ecu-tests-image.bb:
SUMMARY = "ECU bench image — Raspberry Pi as a test runner"
DESCRIPTION = "Minimal Linux image that boots, configures network, \
and runs the ECU test framework on a schedule."
LICENSE = "MIT"
IMAGE_FEATURES += "ssh-server-openssh"
IMAGE_INSTALL = " \
packagegroup-core-boot \
packagegroup-core-ssh-openssh \
${CORE_IMAGE_EXTRA_INSTALL} \
\
python3 \
python3-pip \
python3-pytest \
python3-pytest-html \
python3-pytest-cov \
python3-pytest-xdist \
python3-pyserial \
python3-pyyaml \
\
python3-ldfparser \
python3-pylin \
python3-pymumclient \
python3-pylinframe \
\
ecu-test-framework \
\
rsync openssh-sftp-server curl \
htop nano vim-tiny \
kernel-modules \
chrony \
"
# Be explicit about init system in the image
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME:init_manager = "systemd"
inherit core-image
# Size constraint (raise if you add a lot of debug tools)
IMAGE_OVERHEAD_FACTOR = "1.3"
IMAGE_ROOTFS_EXTRA_SPACE = "524288"
7. Network configuration
The bench MUM exposes itself as a USB-RNDIS Ethernet device at
192.168.7.2/24 with the host expected at 192.168.7.1. Bake the
host-side address into the image so the Pi takes it automatically.
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework/files/20-mum.network
(append to the recipe's SRC_URI and do_install):
[Match]
# usbX is what the Pi's kernel names the USB-RNDIS device. Verify
# with `ip link` on a running image and adjust if needed (it may be
# enxXXXXXXXXXXXX based on MAC address).
Name=usb0 enx*
[Network]
Address=192.168.7.1/24
LinkLocalAddressing=no
IPMasquerade=no
ConfigureWithoutCarrier=yes
The recipe installs this to /etc/systemd/network/20-mum.network.
systemd-networkd is already enabled when systemd is the init
manager.
For a wired connection to the lab network as well, add a second profile:
[Match]
Name=eth0
[Network]
DHCP=yes
8. USB / serial configuration
The Owon PSU is a USB-serial device, typically /dev/ttyUSB0. To
keep the path stable across reboots when the host has other USB
adapters, add a udev rule.
meta-ecu-tests/recipes-ecu-tests/ecu-test-framework/files/99-owon-psu.rules:
# Adjust idVendor/idProduct for your specific adapter (check `lsusb` on
# a booted image). The symlink lets config use /dev/owon_psu instead of
# /dev/ttyUSBn, which can shift if multiple adapters are present.
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", \
MODE="0660", GROUP="dialout", SYMLINK+="owon_psu"
Install to /etc/udev/rules.d/99-owon-psu.rules. The framework's
config/test_config.yaml then carries port: /dev/owon_psu
regardless of enumeration order.
9. The configuration file shipped in the image
/etc/ecu-tests/test_config.yaml (installed by the recipe):
interface:
type: mum
host: 192.168.7.2
lin_device: lin0
power_device: power_out0
bitrate: 19200
boot_settle_seconds: 0.5
ldf_path: /opt/ecu-tests/vendor/4SEVEN_color_lib_test.ldf
flash:
enabled: false
power_supply:
enabled: true
port: /dev/owon_psu # from the udev rule
baudrate: 115200
timeout: 2.0
parity: N
stopbits: 1
idn_substr: OWON
do_set: true
set_voltage: 13.0
set_current: 1.0
And /etc/ecu-tests/marker (single line):
hardware and mum and not slow
Operators can edit either over SSH without rebuilding the image.
10. Build, flash, boot
10.1 Build
From ~/yocto/build/:
bitbake ecu-tests-image
First run: 1–4 h depending on your machine. Subsequent rebuilds
(with sstate-cache intact): minutes.
Output ends up at
~/yocto/build/tmp/deploy/images/raspberrypi4-64/ecu-tests-image-raspberrypi4-64.wic.bz2.
10.2 Flash
# Find the SD card
lsblk
# Assume /dev/sdX is the SD card; double-check before running!
bzcat ~/yocto/build/tmp/deploy/images/raspberrypi4-64/ecu-tests-image-raspberrypi4-64.wic.bz2 \
| sudo dd of=/dev/sdX bs=4M conv=fsync status=progress
sync
Or use bmaptool from Yocto for faster flashing of sparse images.
10.3 First boot
- Insert the SD card into the Pi.
- Connect: power, USB-Ethernet (MUM), USB-serial (Owon PSU), and either Ethernet or HDMI+keyboard for diagnosis.
- Boot.
- SSH in:
ssh root@<ip>(no password by default thanks todebug-tweaks— disable that for production builds, see §13).
journalctl -u ecu-test-framework.service -e
ls /var/log/ecu-tests/latest
cat /var/log/ecu-tests/latest/junit.xml | head
11. Updating the image
There are three ways to push updates without a full re-flash:
| Approach | When | How |
|---|---|---|
| Re-flash | Major changes, package adds | bitbake ecu-tests-image → flash |
| In-place git pull | Test-code-only changes | git -C /opt/ecu-tests pull && systemctl restart ecu-test-framework |
| RAUC / Mender A/B | Production fleets | Adds an A/B partition layout and an update agent; out of scope for this doc |
For developer iteration, the git-pull path is fastest. The image
should ship with the framework's git remote so git pull works
out of the box.
12. Air-gapped or no-network builds
Yocto can fetch everything locally if you stage:
downloads/populated by a one-timebitbake -c fetchall ecu-tests-imageon a connected machine.sstate-cache/similarly.
Then on the air-gapped builder set:
BB_NO_NETWORK = "1"
BB_FETCH_PREMIRRORONLY = "1"
And copy downloads/ and sstate-cache/ from the staging machine.
13. Hardening for production
Before shipping the image to a customer or a permanent installation:
- Disable
debug-tweaksinEXTRA_IMAGE_FEATURES. This reinstates root password requirement, removes the empty-password bypass, and hardens the SSH config. - Set a real
ROOT_HOMEpassword in a.bbappendfor the base recipe, OR provision SSH keys at first boot, OR enforce password-only logins. - Read-only rootfs — Yocto supports
IMAGE_FEATURES += "read-only-rootfs". Anything mutable (configs, logs) needs to move to/varon tmpfs or a persistent partition. - Watchdog — enable the hardware watchdog so the Pi reboots if
it locks up.
meta-raspberrypiexposes the BCM watchdog; pair withsystemd'sWatchdogSec=. - Boot-time integrity —
securebootis not viable on Raspberry Pi to the degree it is on automotive ECUs, but you can checksum the rootfs and refuse to run tests if it's been tampered with. - TLS for report uploads — if the push step talks HTTP/MQTT to a collector, pin the server certificate.
14. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
do_compile fails for python3-pylin with "license not accepted" |
Missing LICENSE_FLAGS_ACCEPTED entry |
Add the three commercial_* flags to conf/local.conf |
pylin not importable in the running image |
Recipe installed to the wrong site-packages path | Confirm PYTHON_SITEPACKAGES_DIR in the recipe matches the image's actual path (python3 -c "import site; print(site.getsitepackages())" on a booted image) |
| MUM unreachable at boot | systemd-networkd profile didn't match the USB iface |
ip link to find the real name; widen the Name= glob |
| Tests fail with "ECU not responding" | Same as above, or the MUM hasn't come up by the time the timer fires | Add After=network-online.target (already done) and a startup delay in the runner |
| PSU port not found | udev rule didn't match the adapter; check lsusb for VID/PID |
Adjust the rule and rebuild, or fall back to /dev/ttyUSB0 and hope nothing else enumerates first |
bitbake runs out of disk |
TMPDIR fills up | Mount ~/yocto/build on its own disk, or change TMPDIR in local.conf to a bigger volume |
| First build runs forever | All-from-source compile | This is normal; subsequent builds use the populated sstate-cache |
| Image too big for the SD card | Too many extras in IMAGE_INSTALL |
Trim htop nano vim-tiny chrony etc. if you don't need them |
15. What this gives you vs. running on the Pi directly
Pi OS + pi_install.sh |
Yocto image | |
|---|---|---|
| Reproducible | ad-hoc | yes |
| Image footprint | ~2 GB | ~400 MB realistic |
| Boot to first test | ~45 s | ~12 s with a tuned image |
| Updates over the air | manual | feasible with RAUC/Mender |
| Day-to-day dev | comfortable | painful — every change rebuilds |
| Auditing the OS | dpkg snapshot | full source manifest |
Use the Yocto path when the Pi is part of a deliverable, when you need to ship N benches identical, or when an auditor needs a list of every byte on the device.
16. Related docs
docs/09_raspberry_pi_deployment.md— run the framework on stock Raspberry Pi OS (the lighter path).docs/10_build_custom_image.md— a preseeded Pi OS image, the middle ground between vanilla Pi OS and a Yocto build.docs/20_docker_image.md— if you'd rather the framework run from a container on a regular Linux host rather than as the host itself.docs/14_power_supply.md— PSU port resolution; on the image the udev rule makes/dev/owon_psustable, so the resolver's job is trivial.docs/02_configuration_resolution.md— howECU_TESTS_CONFIGselects/etc/ecu-tests/test_config.yamlat runtime.vendor/automated_lin_test/install_packages.sh— the equivalent of the Melexis-recipe step for a developer venv on a workstation.