Custom Yocto Linux image for the PageSpeak portable OCR-to-speech device. Supports Raspberry Pi 3, 4, and 5.
Related repos:
| Target | Machine Name | Architecture | Kernel | Status |
|---|---|---|---|---|
| Raspberry Pi 3B/3B+ | raspberrypi3 |
ARM32 (armv7) | 5.15 | Tested |
| Raspberry Pi 4B | raspberrypi4 |
ARM32 (armv7) | 5.15 | Supported |
| Raspberry Pi 4B | raspberrypi4-64 |
ARM64 (aarch64) | 5.15 | Supported |
| Raspberry Pi 5 | raspberrypi5 |
ARM64 (aarch64) | 6.1 | Tested |
The build.sh script handles all configuration and supports multi-target builds:
# Clone and setup
git clone https://github.com/cu-ecen-aeld/final-project-mu2d2.git
cd final-project-mu2d2
./setup-build.sh
# Build for a specific target
./build.sh rpi3 # Raspberry Pi 3
./build.sh rpi4 # Raspberry Pi 4 (32-bit)
./build.sh rpi5 # Raspberry Pi 5
# Build for all targets
./build.sh all
# Build only a specific recipe
./build.sh rpi3 pagespeak-cam-driver
./build.sh rpi5 pagespeak-btn# Setup
./setup-build.sh
cd build
# Build for RPi 3 (default)
bitbake pagespeak-image
# Build for a different target
MACHINE=raspberrypi5 bitbake pagespeak-image
MACHINE=raspberrypi4 bitbake pagespeak-imageAfter a successful build, SD card images are at:
build/tmp/deploy/images/<machine>/pagespeak-image-<machine>.rpi-sdimg
Examples:
build/tmp/deploy/images/raspberrypi3/pagespeak-image-raspberrypi3.rpi-sdimgbuild/tmp/deploy/images/raspberrypi5/pagespeak-image-raspberrypi5.rpi-sdimg
Yocto cannot build natively on macOS. Use Docker:
-
Install Docker Desktop with at least 8 GB RAM and 80 GB disk.
-
Build the ARM64 Docker image (one-time setup):
docker build -t yocto-arm64 -f Dockerfile.arm64 . -
Use
./build.shwhich automatically runs inside Docker, or run manually:docker run --rm -it \ -v $(pwd):/workdir \ -v yocto-downloads:/yocto-cache/downloads \ -v yocto-sstate:/yocto-cache/sstate-cache \ -v yocto-tmp:/yocto-cache/tmp \ yocto-arm64:latest bash
Note: Docker volumes provide a case-sensitive ext4 filesystem (required by Yocto) and persist the build cache across runs.
Install required packages (Ubuntu/Debian):
sudo apt-get install 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-8To build natively instead of in Docker:
DOCKER=0 ./build.sh rpi3Requirements: 80 GB free disk space, 8 GB RAM (16 GB recommended).
PageSpeak includes two custom kernel modules:
Character device driver for USB webcam frame capture via /dev/pagespeak-cam.
| Feature | Description |
|---|---|
| Device node | /dev/pagespeak-cam (auto-created) |
| Frame capture | read() returns JPEG frames via V4L2 |
| Configuration | ioctl() for resolution, pixel format |
| Concurrency | Mutex-protected, single-opener (returns -EBUSY) |
Build only the camera driver:
./build.sh rpi3 pagespeak-cam-driver
./build.sh rpi5 pagespeak-cam-driverGPIO interrupt driver for the capture button.
Build only the button driver:
./build.sh rpi3 pagespeak-btn
./build.sh rpi5 pagespeak-btnWARNING: Double-check the device name — dd will overwrite whatever you point it at.
# Find your SD card device
lsblk # Linux: /dev/sdX
diskutil list # macOS: /dev/diskN
# Unmount
sudo umount /dev/sdX* # Linux
diskutil unmountDisk /dev/diskN # macOS
# Flash (example for RPi 3)
sudo dd if=build/tmp/deploy/images/raspberrypi3/pagespeak-image-raspberrypi3.rpi-sdimg \
of=/dev/sdX bs=4M status=progress
sync- Open Raspberry Pi Imager
- Choose OS → Use custom → select the
.rpi-sdimgfile - Choose your SD card
- Write
Connect via serial console (115200 baud) or SSH over Ethernet. Login as root (no password).
# Verify kernel modules loaded
lsmod | grep pagespeak
# Expected: pagespeak_cam, pagespeak_btn
# Verify device nodes
ls -la /dev/pagespeak-cam
# Expected: character device
# Test camera module (with USB webcam connected)
dmesg | grep pagespeak_cam
v4l2-ctl --list-devices
# Test GPIO
gpiodetect
gpioinfo gpiochip0 | head -10
# Test audio
aplay -l
speaker-test -t wav -c 2final-project-mu2d2/
├── meta-pagespeak/ # Custom Yocto layer
│ ├── conf/layer.conf # Layer configuration (Kirkstone)
│ ├── recipes-core/images/
│ │ └── pagespeak-image.bb # Main image recipe
│ └── recipes-kernel/
│ ├── linux/
│ │ ├── linux-raspberrypi_%.bbappend
│ │ └── files/pagespeak.cfg # Kernel config fragment
│ ├── pagespeak-cam-driver/
│ │ ├── pagespeak-cam-driver_0.1.bb
│ │ └── files/
│ │ ├── pagespeak_cam.c # Camera capture module
│ │ ├── pagespeak_cam.h # ioctl definitions
│ │ ├── Makefile
│ │ └── COPYING
│ └── pagespeak-btn/
│ ├── pagespeak-btn.bb
│ └── files/
│ ├── pagespeak-btn.c # GPIO button module
│ └── Makefile
├── tests/
│ ├── pagespeak_cam_test.c # Userspace test for camera driver
│ └── pagespeak_cam.h
├── conf/
│ ├── local.conf # Build configuration
│ └── bblayers.conf.sample # Layer paths template
├── build.sh # Multi-target build script
├── setup-build.sh # Initial setup script
├── Dockerfile.arm64 # Docker image for Apple Silicon
└── README.md
Edit conf/local.conf:
# Default machine (can be overridden by MACHINE env var or build.sh)
MACHINE ?= "raspberrypi3"Or override at build time:
MACHINE=raspberrypi5 bitbake pagespeak-imageIn conf/local.conf or via environment:
BB_NUMBER_THREADS=16 PARALLEL_MAKE="-j 16" ./build.sh rpi5Docker builds use named volumes for persistent caching:
yocto-downloads— Downloaded source tarballsyocto-sstate— Shared state cache (dramatically speeds up rebuilds)yocto-tmp— Build artifacts
To clear the cache:
docker volume rm yocto-downloads yocto-sstate yocto-tmpOn macOS, Yocto requires a case-sensitive filesystem. The Docker volumes handle this automatically. If building natively on Linux, ensure your filesystem is case-sensitive (ext4, xfs).
The kernel module was built for a different kernel version. Rebuild the module:
./build.sh rpi3 pagespeak-cam-driver -c cleansstate
./build.sh rpi3 pagespeak-cam-driverThis is expected — Rosetta x86 emulation adds overhead. Use the native ARM64 Docker image (Dockerfile.arm64) for better performance. First builds take 2-4 hours; subsequent builds use sstate cache and are much faster.
- Kernel modules: GPL-2.0
- Build scripts and configuration: MIT