Wifi auto connect
This commit is contained in:
parent
a12a49192f
commit
69fe3c8f77
@ -12,6 +12,7 @@ BBLAYERS ?= " \
|
||||
/workspace/yocto/meta-openembedded/meta-oe \
|
||||
/workspace/yocto/meta-openembedded/meta-python \
|
||||
/workspace/yocto/meta-openembedded/meta-multimedia \
|
||||
/workspace/yocto/meta-openembedded/meta-networking \
|
||||
/workspace/yocto/meta-rust-bin \
|
||||
/workspace/meta-layers/meta-fcb-looper \
|
||||
"
|
||||
|
||||
@ -35,4 +35,12 @@ VIRTUAL-RUNTIME_initscripts = ""
|
||||
CORE_IMAGE_EXTRA_INSTALL += "gui-app weston liberation-fonts"
|
||||
|
||||
# Needed to enable terminal on tty2
|
||||
EXTRA_IMAGE_FEATURES += "debug-tweaks"
|
||||
EXTRA_IMAGE_FEATURES += "debug-tweaks"
|
||||
|
||||
# WiFi support - Intel Centrino Advanced-N 6235 specific
|
||||
DISTRO_FEATURES += "wifi opengl"
|
||||
IMAGE_INSTALL:append = " wpa-supplicant iw linux-firmware packagegroup-base-wifi pciutils usbutils"
|
||||
IMAGE_INSTALL:append = " linux-firmware-iwlwifi-6000g2b-6"
|
||||
|
||||
# Enable iwlwifi driver in kernel
|
||||
KERNEL_FEATURES:append = " features/iwlwifi/iwlwifi.scc"
|
||||
@ -6,7 +6,7 @@ Build a minimal, real-time Linux system for an audio looper pedal using Yocto Pr
|
||||
|
||||
**Key Requirements:**
|
||||
- x64 hardware (NUC-class)
|
||||
- Static IP: 192.168.0.50
|
||||
- Static IP: 192.168.0.25
|
||||
- SSH access with public key authentication
|
||||
- USB live installer that can dd copy to disk
|
||||
- A/B partition scheme for reliable updates
|
||||
@ -67,843 +67,4 @@ The run script wraps docker compose run and adds some environment variables.
|
||||
|
||||
**Installation Testing:** Boot USB installer in QEMU, SSH in, run installation script on virtual disk, then boot installed system to verify complete workflow.
|
||||
|
||||
The implementation should focus on creating minimal, working recipes that can be tested incrementally in QEMU before hardware deployment. Each component (kernel, network, SSH, audio, updates) should be testable independently.
|
||||
|
||||
## Core Architecture and Directory Structure
|
||||
|
||||
The system integrates **Yocto Project 5.0 Scarthgap LTS** with Docker containerization, organizing all components within a Rust workspace directory structure. This approach minimizes host dependencies while providing a robust embedded Linux development environment with reliable over-the-air updates.
|
||||
|
||||
### Project Organization Within Rust Workspace
|
||||
|
||||
```
|
||||
image/
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── run
|
||||
│
|
||||
├── yocto/
|
||||
│ ├── poky/
|
||||
│ ├── meta-openembedded/
|
||||
│ ├── meta-rust-bin/ # Pre-built Rust toolchain
|
||||
│ └── meta-swupdate/ # SWUpdate integration
|
||||
├── meta-layers/
|
||||
│ ├── meta-custom/ # Custom project layer
|
||||
│ │ ├── conf/
|
||||
│ │ │ ├── distro/
|
||||
│ │ │ ├── machine/
|
||||
│ │ │ └── layer.conf
|
||||
│ │ ├── recipes-apps/
|
||||
│ │ │ └── egui-kiosk/
|
||||
│ │ ├── recipes-core/
|
||||
│ │ │ └── images/
|
||||
│ │ └── recipes-support/
|
||||
│ │ └── swupdate/
|
||||
│ └── meta-custom-bsp/ # BSP-specific configurations
|
||||
├── scripts/
|
||||
│ ├── setup-environment.sh
|
||||
│ ├── build-images.sh
|
||||
│ ├── deploy-qemu.sh
|
||||
│ └── create-update.sh
|
||||
└── conf/
|
||||
├── local.conf
|
||||
└── bblayers.conf
|
||||
```
|
||||
|
||||
The **Rust workspace configuration** unifies all application components:
|
||||
|
||||
```toml
|
||||
# rust-workspace/Cargo.toml
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"apps/*",
|
||||
"libs/*"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[workspace.dependencies]
|
||||
egui = "0.24"
|
||||
eframe = { version = "0.24", features = ["wgpu"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.0", features = ["rt-multi-thread"] }
|
||||
```
|
||||
|
||||
## Yocto Layer Creation and Rust Integration
|
||||
|
||||
The custom layer provides comprehensive Rust application support with modern toolchain integration using **meta-rust-bin** for faster builds and easier maintenance.
|
||||
|
||||
### Custom Layer Configuration
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/conf/layer.conf
|
||||
BBPATH .= ":${LAYERDIR}"
|
||||
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend"
|
||||
|
||||
BBFILE_COLLECTIONS += "custom-layer"
|
||||
BBFILE_PATTERN_custom-layer = "^${LAYERDIR}/"
|
||||
BBFILE_PRIORITY_custom-layer = "10"
|
||||
|
||||
LAYERVERSION_custom-layer = "1"
|
||||
LAYERSERIES_COMPAT_custom-layer = "scarthgap"
|
||||
|
||||
LAYERDEPENDS_custom-layer = "core openembedded-layer meta-rust-bin"
|
||||
```
|
||||
|
||||
### Rust egui Application Recipe
|
||||
|
||||
The recipe integrates seamlessly with the Rust workspace, handling cross-compilation and dependencies automatically:
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/recipes-apps/egui-kiosk/egui-kiosk_1.0.bb
|
||||
SUMMARY = "Rust egui Fullscreen Kiosk Application"
|
||||
DESCRIPTION = "Cross-platform GUI kiosk application built with egui framework"
|
||||
LICENSE = "MIT"
|
||||
LIC_FILES_CHKSUM = "file://LICENSE;md5=935a9b2a57ae70704d8125b9c0e39059"
|
||||
|
||||
inherit cargo_bin systemd
|
||||
|
||||
# Enable network access for cargo dependencies
|
||||
do_compile[network] = "1"
|
||||
|
||||
SRC_URI = "\
|
||||
git://localhost/rust-workspace.git;protocol=file;branch=main \
|
||||
file://egui-kiosk.service \
|
||||
file://egui-kiosk-init.sh \
|
||||
file://weston.ini \
|
||||
"
|
||||
|
||||
SRCREV = "${AUTOREV}"
|
||||
S = "${WORKDIR}/git"
|
||||
|
||||
# Point to specific app in workspace
|
||||
CARGO_SRC_DIR = "apps/egui-kiosk"
|
||||
|
||||
# Cargo features for kiosk mode
|
||||
CARGO_FEATURES = "kiosk-mode fullscreen wayland"
|
||||
|
||||
# Runtime dependencies for GUI applications
|
||||
RDEPENDS_${PN} += "\
|
||||
wayland \
|
||||
weston \
|
||||
wayland-protocols \
|
||||
libxkbcommon \
|
||||
fontconfig \
|
||||
liberation-fonts \
|
||||
systemd \
|
||||
"
|
||||
|
||||
# Build dependencies
|
||||
DEPENDS += "\
|
||||
wayland-native \
|
||||
wayland-protocols-native \
|
||||
libxkbcommon \
|
||||
fontconfig \
|
||||
"
|
||||
|
||||
# Install systemd service and configuration
|
||||
do_install_append() {
|
||||
install -d ${D}${systemd_system_unitdir}
|
||||
install -m 0644 ${WORKDIR}/egui-kiosk.service ${D}${systemd_system_unitdir}/
|
||||
|
||||
install -d ${D}${bindir}
|
||||
install -m 0755 ${WORKDIR}/egui-kiosk-init.sh ${D}${bindir}/
|
||||
|
||||
install -d ${D}${sysconfdir}/xdg/weston
|
||||
install -m 0644 ${WORKDIR}/weston.ini ${D}${sysconfdir}/xdg/weston/
|
||||
}
|
||||
|
||||
SYSTEMD_SERVICE_${PN} = "egui-kiosk.service"
|
||||
SYSTEMD_AUTO_ENABLE_${PN} = "enable"
|
||||
|
||||
FILES_${PN} += "\
|
||||
${systemd_system_unitdir}/egui-kiosk.service \
|
||||
${bindir}/egui-kiosk-init.sh \
|
||||
${sysconfdir}/xdg/weston/weston.ini \
|
||||
"
|
||||
```
|
||||
|
||||
## Rust egui Application Implementation
|
||||
|
||||
The application leverages **egui 0.24** with fullscreen kiosk capabilities and system integration:
|
||||
|
||||
```rust
|
||||
// rust-workspace/apps/egui-kiosk/src/main.rs
|
||||
use eframe::{egui, App, Frame, NativeOptions};
|
||||
use std::env;
|
||||
|
||||
struct KioskApp {
|
||||
counter: i32,
|
||||
show_confirmation_dialog: bool,
|
||||
}
|
||||
|
||||
impl KioskApp {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
show_confirmation_dialog: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for KioskApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Production Kiosk System");
|
||||
|
||||
ui.add_space(20.0);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Increment").clicked() {
|
||||
self.counter += 1;
|
||||
}
|
||||
ui.label(format!("Counter: {}", self.counter));
|
||||
});
|
||||
|
||||
ui.add_space(20.0);
|
||||
|
||||
if ui.button("System Settings").clicked() {
|
||||
self.show_confirmation_dialog = true;
|
||||
}
|
||||
});
|
||||
|
||||
if self.show_confirmation_dialog {
|
||||
egui::Window::new("Confirm Action")
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
ui.label("Are you sure you want to access system settings?");
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Yes").clicked() {
|
||||
// Handle system settings access
|
||||
self.show_confirmation_dialog = false;
|
||||
}
|
||||
if ui.button("No").clicked() {
|
||||
self.show_confirmation_dialog = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
let options = NativeOptions {
|
||||
fullscreen: true,
|
||||
decorated: false,
|
||||
resizable: false,
|
||||
always_on_top: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"Kiosk Application",
|
||||
options,
|
||||
Box::new(|_cc| Box::new(KioskApp::new())),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Systemd Service Configuration for Auto-Start
|
||||
|
||||
The systemd service ensures reliable application startup with proper dependencies and restart capabilities:
|
||||
|
||||
```ini
|
||||
# files/egui-kiosk.service
|
||||
[Unit]
|
||||
Description=Rust egui Kiosk Application
|
||||
Documentation=https://github.com/company/egui-kiosk
|
||||
After=graphical.target weston.service
|
||||
Wants=weston.service
|
||||
Requires=graphical.target
|
||||
StartLimitIntervalSec=30
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kiosk
|
||||
Group=kiosk
|
||||
Environment="DISPLAY=:0"
|
||||
Environment="WAYLAND_DISPLAY=wayland-0"
|
||||
Environment="XDG_RUNTIME_DIR=/run/user/1000"
|
||||
Environment="RUST_LOG=info"
|
||||
|
||||
ExecStartPre=/bin/sleep 3
|
||||
ExecStart=/usr/bin/egui-kiosk
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
WatchdogSec=30
|
||||
|
||||
# Resource limits
|
||||
MemoryMax=256M
|
||||
CPUQuota=80%
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/tmp /run/user/1000
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
```
|
||||
|
||||
## initramfs Integration Strategy
|
||||
|
||||
The initramfs approach embeds the Rust application directly in the boot image for fastest startup times and minimal attack surface.
|
||||
|
||||
### initramfs Image Recipe
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/recipes-core/images/initramfs-egui-image.bb
|
||||
DESCRIPTION = "Minimal initramfs image with Rust GUI application"
|
||||
|
||||
PACKAGE_INSTALL = "\
|
||||
initramfs-framework-base \
|
||||
busybox \
|
||||
base-files \
|
||||
egui-kiosk \
|
||||
weston \
|
||||
liberation-fonts \
|
||||
${VIRTUAL-RUNTIME_base-utils} \
|
||||
"
|
||||
|
||||
# Minimize image size
|
||||
IMAGE_LINGUAS = ""
|
||||
LICENSE = "MIT"
|
||||
|
||||
inherit core-image
|
||||
|
||||
# Optimize for minimal size
|
||||
IMAGE_ROOTFS_SIZE ?= "32768"
|
||||
IMAGE_ROOTFS_EXTRA_SPACE = "0"
|
||||
IMAGE_OVERHEAD_FACTOR = "1.0"
|
||||
|
||||
# Bundle with kernel
|
||||
INITRAMFS_IMAGE_BUNDLE = "1"
|
||||
```
|
||||
|
||||
### initramfs Boot Module
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/recipes-core/initramfs-egui-boot/files/egui-boot
|
||||
#!/bin/sh
|
||||
|
||||
egui_boot_enabled() {
|
||||
return 0
|
||||
}
|
||||
|
||||
egui_boot_run() {
|
||||
msg "Starting GUI boot sequence..."
|
||||
|
||||
# Mount essential filesystems
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sysfs /sys
|
||||
mount -t devtmpfs devtmpfs /dev
|
||||
|
||||
# Setup runtime directories
|
||||
mkdir -p /run/user/1000
|
||||
chown 1000:1000 /run/user/1000
|
||||
|
||||
# Start Wayland compositor
|
||||
export XDG_RUNTIME_DIR="/run/user/1000"
|
||||
export WAYLAND_DISPLAY="wayland-0"
|
||||
|
||||
weston --backend=drm-backend.so --idle-time=0 &
|
||||
sleep 2
|
||||
|
||||
# Start Rust GUI application
|
||||
egui-kiosk &
|
||||
|
||||
msg "GUI application started successfully"
|
||||
}
|
||||
```
|
||||
|
||||
## SWUpdate A/B Partitioning System
|
||||
|
||||
The A/B update system provides **atomic updates with automatic rollback**, ensuring system reliability even during power failures or failed updates.
|
||||
|
||||
### Partition Layout Configuration
|
||||
|
||||
```bash
|
||||
# Recommended eMMC/SSD partition layout
|
||||
/dev/sda
|
||||
├── sda1 # Boot partition (EFI/BIOS, 64MB)
|
||||
├── sda2 # RootFS A (ext4, 2GB)
|
||||
├── sda3 # RootFS B (ext4, 2GB)
|
||||
├── sda4 # Data partition (ext4, remaining)
|
||||
└── sda5 # U-Boot environment (1MB)
|
||||
```
|
||||
|
||||
### WIC Image Configuration for A/B Layout
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/wic/dual-rootfs.wks
|
||||
# Boot partition with bootloader
|
||||
part /boot --source bootimg-partition --sourceparams="loader=u-boot" \
|
||||
--ondisk sda --fstype=vfat --label boot --size 64M --align 1024
|
||||
|
||||
# Root filesystem A (active partition)
|
||||
part / --source rootfs --rootfs-dir=${IMAGE_ROOTFS} \
|
||||
--ondisk sda --fstype=ext4 --label rootfs_a --size 2G
|
||||
|
||||
# Root filesystem B (update target, initially empty)
|
||||
part --ondisk sda --fstype=ext4 --label rootfs_b --size 2G
|
||||
|
||||
# Persistent data partition
|
||||
part /data --ondisk sda --fstype=ext4 --label data --size 1G \
|
||||
--fsoptions="defaults"
|
||||
|
||||
# U-Boot environment for A/B switching
|
||||
part --ondisk sda --size 1M --label uboot_env
|
||||
|
||||
bootloader u-boot
|
||||
```
|
||||
|
||||
### SWUpdate Configuration
|
||||
|
||||
```libconfig
|
||||
# files/sw-description
|
||||
software =
|
||||
{
|
||||
version = "1.0.0";
|
||||
description = "Dual-copy Rust GUI system update";
|
||||
|
||||
hardware-compatibility: [ "1.0", "2.0" ];
|
||||
|
||||
stable = {
|
||||
copy1 = {
|
||||
images: (
|
||||
{
|
||||
filename = "core-image-egui-minimal.ext4.gz";
|
||||
type = "raw";
|
||||
compressed = true;
|
||||
device = "/dev/sda2";
|
||||
sha256 = "$swupdate_get_sha256(core-image-egui-minimal.ext4.gz)";
|
||||
}
|
||||
);
|
||||
|
||||
scripts: (
|
||||
{
|
||||
filename = "post-update.sh";
|
||||
type = "shellscript";
|
||||
}
|
||||
);
|
||||
|
||||
bootenv: (
|
||||
{
|
||||
name = "rootfs_part";
|
||||
value = "2";
|
||||
},
|
||||
{
|
||||
name = "upgrade_available";
|
||||
value = "1";
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
copy2 = {
|
||||
images: (
|
||||
{
|
||||
filename = "core-image-egui-minimal.ext4.gz";
|
||||
type = "raw";
|
||||
compressed = true;
|
||||
device = "/dev/sda3";
|
||||
sha256 = "$swupdate_get_sha256(core-image-egui-minimal.ext4.gz)";
|
||||
}
|
||||
);
|
||||
|
||||
scripts: (
|
||||
{
|
||||
filename = "post-update.sh";
|
||||
type = "shellscript";
|
||||
}
|
||||
);
|
||||
|
||||
bootenv: (
|
||||
{
|
||||
name = "rootfs_part";
|
||||
value = "3";
|
||||
},
|
||||
{
|
||||
name = "upgrade_available";
|
||||
value = "1";
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
scripts: (
|
||||
{
|
||||
filename = "partition-select.lua";
|
||||
type = "lua";
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Automatic Partition Selection Logic
|
||||
|
||||
```lua
|
||||
-- files/partition-select.lua
|
||||
function preinst()
|
||||
-- Determine current root partition
|
||||
local f = io.open("/proc/cmdline", "r")
|
||||
local cmdline = f:read("*all")
|
||||
f:close()
|
||||
|
||||
local current_part = cmdline:match("root=/dev/sda(%d)")
|
||||
|
||||
if current_part == "2" then
|
||||
-- Currently on partition 2, update partition 3
|
||||
swupdate.notify(RECOVERY, "Updating partition 3 (copy2)")
|
||||
return 0, "copy2"
|
||||
else
|
||||
-- Currently on partition 3 or default, update partition 2
|
||||
swupdate.notify(RECOVERY, "Updating partition 2 (copy1)")
|
||||
return 0, "copy1"
|
||||
end
|
||||
end
|
||||
|
||||
function postinst()
|
||||
swupdate.notify(RECOVERY, "Update installation completed")
|
||||
return 0
|
||||
end
|
||||
```
|
||||
|
||||
## QEMU Testing Configuration
|
||||
|
||||
QEMU provides comprehensive testing capabilities with **KVM acceleration** and proper graphics support for egui application validation.
|
||||
|
||||
### QEMU Launch Configuration
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/deploy-qemu.sh
|
||||
|
||||
set -e
|
||||
|
||||
IMAGE_PATH="yocto/build/tmp/deploy/images/qemux86-64"
|
||||
KERNEL="${IMAGE_PATH}/bzImage"
|
||||
ROOTFS="${IMAGE_PATH}/core-image-egui-minimal-qemux86-64.ext4"
|
||||
|
||||
# Launch QEMU with graphics support
|
||||
qemu-system-x86_64 \
|
||||
-enable-kvm \
|
||||
-m 2048 \
|
||||
-smp 4 \
|
||||
-cpu host \
|
||||
-kernel "${KERNEL}" \
|
||||
-drive file="${ROOTFS}",if=virtio,format=raw \
|
||||
-append "root=/dev/vda rw console=ttyS0 console=tty0 quiet" \
|
||||
-netdev user,id=net0,hostfwd=tcp::8080-:8080,hostfwd=tcp::2222-:22 \
|
||||
-device virtio-net-pci,netdev=net0 \
|
||||
-device virtio-vga-gl \
|
||||
-display gtk,gl=on \
|
||||
-audiodev pulse,id=audio0 \
|
||||
-device intel-hda \
|
||||
-device hda-duplex,audiodev=audio0 \
|
||||
-serial stdio \
|
||||
-show-cursor
|
||||
```
|
||||
|
||||
### Automated Testing Framework
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/test-system.sh
|
||||
|
||||
test_boot_time() {
|
||||
echo "Testing boot time..."
|
||||
start_time=$(date +%s.%N)
|
||||
|
||||
timeout 60 qemu-system-x86_64 \
|
||||
-enable-kvm -m 1024 -nographic \
|
||||
-kernel "${KERNEL}" \
|
||||
-drive file="${ROOTFS}",if=virtio,format=raw \
|
||||
-append "root=/dev/vda rw console=ttyS0 init=/bin/sh" &
|
||||
|
||||
QEMU_PID=$!
|
||||
wait $QEMU_PID
|
||||
|
||||
end_time=$(date +%s.%N)
|
||||
boot_time=$(echo "$end_time - $start_time" | bc)
|
||||
|
||||
echo "Boot time: ${boot_time} seconds"
|
||||
if (( $(echo "$boot_time < 15" | bc -l) )); then
|
||||
echo "PASS: Boot time acceptable"
|
||||
else
|
||||
echo "FAIL: Boot time too slow"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_application_startup() {
|
||||
echo "Testing application startup..."
|
||||
# Launch system and verify egui application starts
|
||||
timeout 120 expect -c '
|
||||
spawn qemu-system-x86_64 -enable-kvm -m 1024 -nographic \
|
||||
-kernel '"${KERNEL}"' \
|
||||
-drive file='"${ROOTFS}"',if=virtio,format=raw \
|
||||
-append "root=/dev/vda rw console=ttyS0"
|
||||
|
||||
expect "login:" { send "root\r" }
|
||||
expect "# " { send "systemctl status egui-kiosk\r" }
|
||||
expect "active (running)" {
|
||||
puts "SUCCESS: egui application running"
|
||||
exit 0
|
||||
}
|
||||
timeout {
|
||||
puts "FAIL: Application not started in time"
|
||||
exit 1
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
test_boot_time
|
||||
test_application_startup
|
||||
echo "All tests passed!"
|
||||
```
|
||||
|
||||
## Minimal Linux System Configuration
|
||||
|
||||
The system optimizes for **minimal resource usage** while maintaining essential functionality for the Rust GUI application.
|
||||
|
||||
### Minimal Image Recipe
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/recipes-core/images/core-image-egui-minimal.bb
|
||||
SUMMARY = "Minimal Linux system with Rust egui application"
|
||||
|
||||
IMAGE_INSTALL = "\
|
||||
packagegroup-core-boot \
|
||||
egui-kiosk \
|
||||
weston \
|
||||
liberation-fonts \
|
||||
systemd \
|
||||
networkmanager \
|
||||
swupdate \
|
||||
${CORE_IMAGE_EXTRA_INSTALL} \
|
||||
"
|
||||
|
||||
# Remove unnecessary features
|
||||
DISTRO_FEATURES_remove = "alsa bluetooth wifi nfs zeroconf pci 3g nfc x11"
|
||||
IMAGE_FEATURES = "read-only-rootfs"
|
||||
|
||||
# Package optimization
|
||||
PACKAGE_CLASSES = "package_ipk"
|
||||
IMAGE_FSTYPES = "ext4 ext4.gz wic"
|
||||
|
||||
# Size constraints
|
||||
IMAGE_ROOTFS_SIZE ?= "512000" # 512MB
|
||||
IMAGE_OVERHEAD_FACTOR = "1.1"
|
||||
|
||||
LICENSE = "MIT"
|
||||
|
||||
inherit core-image
|
||||
|
||||
# Remove debug packages
|
||||
PACKAGE_EXCLUDE = "\
|
||||
gdb \
|
||||
gdbserver \
|
||||
strace \
|
||||
${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'x11-common', '', d)} \
|
||||
"
|
||||
|
||||
# Optimize systemd
|
||||
IMAGE_INSTALL_append = " systemd-analyze"
|
||||
SYSTEMD_DEFAULT_TARGET = "graphical.target"
|
||||
```
|
||||
|
||||
### System Optimization Configuration
|
||||
|
||||
```bash
|
||||
# meta-layers/meta-custom/recipes-core/systemd/systemd/system.conf
|
||||
[Manager]
|
||||
LogLevel=warning
|
||||
LogTarget=kmsg
|
||||
SystemMaxUse=50M
|
||||
RuntimeMaxUse=50M
|
||||
DefaultTimeoutStartSec=10s
|
||||
DefaultTimeoutStopSec=5s
|
||||
DefaultRestartSec=5s
|
||||
```
|
||||
|
||||
## Build and Deployment Workflows
|
||||
|
||||
The complete workflow integrates Docker containerization with automated builds, testing, and deployment.
|
||||
|
||||
### Environment Setup Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/setup-environment.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "Setting up Yocto development environment..."
|
||||
|
||||
# Export user ID for Docker
|
||||
export USER_ID=$(id -u)
|
||||
export GROUP_ID=$(id -g)
|
||||
|
||||
# Initialize Git repositories if not present
|
||||
if [ ! -d "yocto/sources/poky" ]; then
|
||||
echo "Cloning Yocto repositories..."
|
||||
mkdir -p yocto/sources
|
||||
cd yocto/sources
|
||||
|
||||
git clone git://git.yoctoproject.org/poky -b scarthgap
|
||||
git clone git://git.openembedded.org/meta-openembedded -b scarthgap
|
||||
git clone https://github.com/rust-embedded/meta-rust-bin.git -b master
|
||||
git clone https://github.com/sbabic/meta-swupdate.git -b scarthgap
|
||||
|
||||
cd ../..
|
||||
fi
|
||||
|
||||
# Start Docker environment
|
||||
echo "Starting containerized build environment..."
|
||||
docker-compose up -d yocto-builder
|
||||
|
||||
# Wait for container to be ready
|
||||
sleep 5
|
||||
|
||||
# Initialize build environment inside container
|
||||
docker-compose exec yocto-builder bash -c "
|
||||
cd /workspace/yocto &&
|
||||
source sources/poky/oe-init-build-env build &&
|
||||
|
||||
# Configure layers
|
||||
bitbake-layers add-layer ../sources/meta-openembedded/meta-oe
|
||||
bitbake-layers add-layer ../sources/meta-openembedded/meta-python
|
||||
bitbake-layers add-layer ../sources/meta-openembedded/meta-networking
|
||||
bitbake-layers add-layer ../sources/meta-rust-bin
|
||||
bitbake-layers add-layer ../sources/meta-swupdate
|
||||
bitbake-layers add-layer ../../meta-layers/meta-custom
|
||||
|
||||
# Configure build
|
||||
cat >> conf/local.conf << 'EOF'
|
||||
MACHINE = \"qemux86-64\"
|
||||
DISTRO = \"poky\"
|
||||
|
||||
# Rust support
|
||||
ENABLE_RUST = \"1\"
|
||||
|
||||
# Performance optimizations
|
||||
BB_NUMBER_THREADS = \"8\"
|
||||
PARALLEL_MAKE = \"-j8\"
|
||||
|
||||
# Package management
|
||||
PACKAGE_CLASSES = \"package_ipk\"
|
||||
|
||||
# SWUpdate integration
|
||||
PREFERRED_PROVIDER_u-boot-fw-utils = \"libubootenv\"
|
||||
IMAGE_FSTYPES += \"ext4.gz wic\"
|
||||
|
||||
# Add applications to rootfs
|
||||
CORE_IMAGE_EXTRA_INSTALL += \"egui-kiosk swupdate swupdate-www\"
|
||||
|
||||
# Enable systemd
|
||||
DISTRO_FEATURES_append = \" systemd\"
|
||||
VIRTUAL-RUNTIME_init_manager = \"systemd\"
|
||||
DISTRO_FEATURES_BACKFILL_CONSIDERED = \"sysvinit\"
|
||||
VIRTUAL-RUNTIME_initscripts = \"\"
|
||||
EOF
|
||||
"
|
||||
|
||||
echo "Environment setup complete!"
|
||||
echo "To enter the build environment, run: docker-compose exec yocto-builder bash"
|
||||
```
|
||||
|
||||
### Complete Build Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/build-images.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "Building Yocto images..."
|
||||
|
||||
# Build in container
|
||||
docker-compose exec yocto-builder bash -c "
|
||||
cd /workspace/yocto &&
|
||||
source sources/poky/oe-init-build-env build &&
|
||||
|
||||
# Build core system
|
||||
echo 'Building minimal egui system...'
|
||||
bitbake core-image-egui-minimal
|
||||
|
||||
# Build update image
|
||||
echo 'Building SWUpdate package...'
|
||||
bitbake update-image
|
||||
|
||||
# Build SDK for development
|
||||
echo 'Building extensible SDK...'
|
||||
bitbake core-image-egui-minimal -c populate_sdk_ext
|
||||
|
||||
echo 'Build completed successfully!'
|
||||
echo 'Images available in: build/tmp/deploy/images/qemux86-64/'
|
||||
"
|
||||
|
||||
# Test the built image
|
||||
echo "Testing built image..."
|
||||
./scripts/test-system.sh
|
||||
|
||||
echo "Build and test completed successfully!"
|
||||
```
|
||||
|
||||
### Update Package Creation
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/create-update.sh
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=${1:-$(date +%Y%m%d-%H%M%S)}
|
||||
OUTPUT_DIR="updates"
|
||||
|
||||
echo "Creating SWUpdate package version: $VERSION"
|
||||
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
# Build update in container
|
||||
docker-compose exec yocto-builder bash -c "
|
||||
cd /workspace/yocto &&
|
||||
source sources/poky/oe-init-build-env build &&
|
||||
|
||||
# Update version in sw-description
|
||||
sed -i 's/version = \".*\"/version = \"$VERSION\"/' \
|
||||
/workspace/meta-layers/meta-custom/recipes-support/update-image/files/sw-description
|
||||
|
||||
# Build update package
|
||||
bitbake update-image
|
||||
|
||||
# Copy to output directory
|
||||
cp tmp/deploy/images/qemux86-64/update-image-qemux86-64.swu \
|
||||
/workspace/$OUTPUT_DIR/system-update-$VERSION.swu
|
||||
"
|
||||
|
||||
echo "Update package created: $OUTPUT_DIR/system-update-$VERSION.swu"
|
||||
echo "Deploy with: swupdate -i $OUTPUT_DIR/system-update-$VERSION.swu"
|
||||
```
|
||||
|
||||
## Production Deployment and Validation
|
||||
|
||||
This comprehensive system provides a **production-ready embedded Linux platform** with automatic updates, security hardening, and reliable operation. The containerized build approach ensures consistent results across development teams, while the A/B update system provides robust field update capabilities with automatic rollback protection.
|
||||
|
||||
**Key advantages of this architecture:**
|
||||
- **Minimal host dependencies** through complete Docker containerization
|
||||
- **Fast development cycles** with shared state caching and incremental builds
|
||||
- **Reliable updates** with atomic A/B partition switching and automatic rollback
|
||||
- **Optimized performance** with minimal Linux configuration and Rust applications
|
||||
- **Comprehensive testing** with QEMU emulation and automated validation
|
||||
|
||||
The system scales from single-developer projects to large embedded teams, providing a solid foundation for Rust-based embedded GUI applications with modern DevOps practices and reliable over-the-air update capabilities.
|
||||
The implementation should focus on creating minimal, working recipes that can be tested incrementally in QEMU before hardware deployment. Each component (kernel, network, SSH, audio, updates) should be testable independently.
|
||||
@ -0,0 +1,12 @@
|
||||
[Match]
|
||||
Name=wlp2s0
|
||||
|
||||
[Network]
|
||||
DHCP=no
|
||||
Address=192.168.0.25/24
|
||||
Gateway=192.168.0.1
|
||||
DNS=192.168.0.1
|
||||
FallbackDNS=8.8.8.8 1.1.1.1
|
||||
|
||||
[DHCP]
|
||||
UseDNS=yes
|
||||
@ -0,0 +1,29 @@
|
||||
SUMMARY = "WiFi Auto-Connect Configuration"
|
||||
DESCRIPTION = "systemd-networkd and wpa_supplicant configuration for automatic WiFi connection"
|
||||
LICENSE = "MIT"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
|
||||
|
||||
SRC_URI = "file://25-wireless.network \
|
||||
file://wpa_supplicant-wlp2s0.conf"
|
||||
|
||||
S = "${WORKDIR}"
|
||||
|
||||
do_install() {
|
||||
# Install systemd-networkd configuration
|
||||
install -d ${D}${sysconfdir}/systemd/network
|
||||
install -m 0644 ${WORKDIR}/25-wireless.network ${D}${sysconfdir}/systemd/network/
|
||||
|
||||
# Install wpa_supplicant configuration
|
||||
install -d ${D}${sysconfdir}/wpa_supplicant
|
||||
install -m 0600 ${WORKDIR}/wpa_supplicant-wlp2s0.conf ${D}${sysconfdir}/wpa_supplicant/
|
||||
|
||||
# Create systemd service symlinks for auto-start
|
||||
install -d ${D}${sysconfdir}/systemd/system/multi-user.target.wants
|
||||
ln -sf ${systemd_system_unitdir}/wpa_supplicant@.service ${D}${sysconfdir}/systemd/system/multi-user.target.wants/wpa_supplicant@wlp2s0.service
|
||||
}
|
||||
|
||||
FILES:${PN} = "${sysconfdir}/systemd/network/* \
|
||||
${sysconfdir}/wpa_supplicant/* \
|
||||
${sysconfdir}/systemd/system/multi-user.target.wants/*"
|
||||
|
||||
RDEPENDS:${PN} = "systemd wpa-supplicant"
|
||||
@ -12,6 +12,7 @@ IMAGE_INSTALL += "\
|
||||
systemd \
|
||||
openssh \
|
||||
ssh-keys \
|
||||
wifi-config \
|
||||
${CORE_IMAGE_EXTRA_INSTALL} \
|
||||
"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user