From 13dce6512857176c1464abe046d1a84a7461eaf1 Mon Sep 17 00:00:00 2001 From: Pat Nakajima Date: Mon, 18 May 2026 06:46:40 +0000 Subject: [PATCH] Update files --- audio/build-audio.sh | 16 ++-- audio/init-audio | 0 build.sh | 43 ++++++++-- matter/toes-matter.service | 16 ++++ scripts/build-in-chroot.sh | 153 +++++++++++++++++++++++++++++++++--- scripts/check-qemu.sh | 7 ++ scripts/cleanup.sh | 20 +++++ scripts/copy-files.sh | 15 ++-- scripts/setup-mount.sh | 16 +++- scripts/update-configtxt.sh | 50 ++++++++---- 10 files changed, 290 insertions(+), 46 deletions(-) mode change 100644 => 100755 audio/init-audio mode change 100644 => 100755 build.sh create mode 100644 matter/toes-matter.service create mode 100755 scripts/cleanup.sh mode change 100644 => 100755 scripts/update-configtxt.sh diff --git a/audio/build-audio.sh b/audio/build-audio.sh index e17b35a..2e4ddc2 100755 --- a/audio/build-audio.sh +++ b/audio/build-audio.sh @@ -3,11 +3,17 @@ set -ex # builds the audio overlay _inside_ the image. -modprobe i2c-dev || true -modprobe snd-soc-simple-card || true -modprobe snd-soc-tlv320aic31xx || true -modprobe snd-soc-ics43432 || true +if [ ! -s /tmp/toesaudio.dts ]; then + echo "missing /tmp/toesaudio.dts" >&2 + exit 1 +fi +mkdir -p /boot/firmware/overlays dtc -@ -I dts -O dtb -o \ "/boot/firmware/overlays/toesaudio.dtbo" \ - "/tmp/audio/toesaudio.dts" + "/tmp/toesaudio.dts" + +if [ ! -s /boot/firmware/overlays/toesaudio.dtbo ]; then + echo "failed to build /boot/firmware/overlays/toesaudio.dtbo" >&2 + exit 1 +fi diff --git a/audio/init-audio b/audio/init-audio old mode 100644 new mode 100755 diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 8b7cf1d..d0a9265 --- a/build.sh +++ b/build.sh @@ -2,9 +2,40 @@ set -ex -scripts/check-qemu.sh -scripts/download-base.sh -scripts/setup-mount.sh -scripts/copy-files.sh -scripts/update-configtxt.sh -scripts/build-in-chroot.sh +BUILD_START=$SECONDS + +format_duration() { + local total="$1" + printf '%02d:%02d:%02d' "$((total / 3600))" "$(((total % 3600) / 60))" "$((total % 60))" +} + +run_phase() { + local name="$1" + shift + local start=$SECONDS + local status=0 + + printf '====> starting %s\n' "$name" + if "$@"; then + status=0 + else + status=$? + fi + printf '====> finished %s in %s\n' "$name" "$(format_duration "$((SECONDS - start))")" + return "$status" +} + +finish() { + local status=$? + run_phase cleanup scripts/cleanup.sh || true + printf '====> total build.sh time: %s\n' "$(format_duration "$((SECONDS - BUILD_START))")" + exit "$status" +} +trap finish EXIT + +run_phase check-qemu scripts/check-qemu.sh +run_phase download-base scripts/download-base.sh +run_phase setup-mount scripts/setup-mount.sh +run_phase copy-files scripts/copy-files.sh +run_phase update-configtxt scripts/update-configtxt.sh +run_phase build-in-chroot scripts/build-in-chroot.sh diff --git a/matter/toes-matter.service b/matter/toes-matter.service new file mode 100644 index 0000000..443311d --- /dev/null +++ b/matter/toes-matter.service @@ -0,0 +1,16 @@ +[Unit] +Description=Toes Matter service +Wants=bluetooth.service avahi-daemon.service wpa_supplicant.service +After=dbus.service bluetooth.service avahi-daemon.service wpa_supplicant.service +Before=toes.service + +[Service] +Type=simple +Environment=MATTER_CREDS_DIR=/var/lib/toes-matter +StateDirectory=toes-matter +ExecStart=/usr/local/bin/toes-matter +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/scripts/build-in-chroot.sh b/scripts/build-in-chroot.sh index fdbac72..afd0d71 100755 --- a/scripts/build-in-chroot.sh +++ b/scripts/build-in-chroot.sh @@ -1,13 +1,37 @@ #! /bin/bash set -ex -# runs _inside_ the image, under qemu arm64. +# runs setup commands inside the image, under qemu arm64. -echo "====> mounting" -sudo chroot mnt/root /bin/bash +sudo tee mnt/root/tmp/image-build.sh >/dev/null <<'EOF' +#! /bin/bash +set -ex + +export DEBIAN_FRONTEND=noninteractive + +mkdir -p /etc/apt/apt.conf.d/disabled + +for f in \ + /etc/apt/apt.conf.d/*command-not-found* \ + /etc/apt/apt.conf.d/*appstream* \ + /etc/apt/apt.conf.d/*packagekit* +do + [ -e "$f" ] && mv "$f" /etc/apt/apt.conf.d/disabled/ +done + +rm -rf /var/lib/apt/lists/* +mkdir -p /var/lib/apt/lists/partial + +# The preinstalled image may contain packages from noble-updates while only +# noble and noble-security are enabled. Enable noble-updates before installing +# -dev packages so apt can resolve exact-version dependencies. +if grep -q '^Suites: noble$' /etc/apt/sources.list.d/ubuntu.sources; then + sed -i 's/^Suites: noble$/Suites: noble noble-updates/' /etc/apt/sources.list.d/ubuntu.sources +fi echo "====> updating apt" apt-get update + echo "====> installing apt packages" apt-get install -y \ ca-certificates \ @@ -16,14 +40,16 @@ apt-get install -y \ git \ locales \ network-manager \ + wpasupplicant \ + iproute2 \ + isc-dhcp-client \ + libcap2-bin \ pkg-config \ libdbus-1-dev \ avahi-daemon \ avahi-utils \ libavahi-client-dev \ bluez \ - build-essential \ - python3-dev \ python3-pip \ sox \ alsa-utils \ @@ -32,18 +58,127 @@ apt-get install -y \ i2c-tools \ device-tree-compiler \ unzip \ - "linux-modules-$(uname -r)" + sudo # setup locale locale-gen en_US.UTF-8 update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 +echo "====> configuring Wi-Fi for direct wpa_supplicant control" +mkdir -p /etc/NetworkManager/conf.d +cat >/etc/NetworkManager/conf.d/10-unmanaged-wifi.conf <<'EONM' +[keyfile] +unmanaged-devices=type:wifi +EONM + +echo "====> creating toes user" +if id -u toes >/dev/null 2>&1; then + usermod -aG sudo toes +else + useradd -m -s /bin/bash -G sudo toes +fi + +echo 'toes:nicetrywiseguy' | chpasswd + +printf 'toes ALL=(ALL) NOPASSWD:ALL\n' >/etc/sudoers.d/90-toes +chmod 0440 /etc/sudoers.d/90-toes +visudo -cf /etc/sudoers.d/90-toes + +# enable serial over usb +echo 'g_serial' >/etc/modules-load.d/usb-serial-gadget.conf +systemctl enable serial-getty@ttyGS0.service + +echo "====> building audio overlay" +/tmp/build-audio.sh +systemctl enable init-audio.service + echo "====> installing rgbled" curl -fsSL https://git.nose.space/nakajima/rgbled/releases/download/v0.1.1/install.sh | bash +echo "====> installing matter stack" +curl -fsSL https://git.fishmt.net/nakajima/toes-matter/releases/download/v0.1.1/install.sh | sh +if [ ! -x /usr/local/bin/toes-matter ]; then + echo "missing /usr/local/bin/toes-matter after install" >&2 + exit 1 +fi +systemctl enable toes-matter.service + +TOES_HOME="$(getent passwd toes | cut -d: -f6)" +mkdir -p "$TOES_HOME" +chown toes:toes "$TOES_HOME" + echo "====> installing bun" -curl -fsSL https://bun.com/install | bash +runuser -u toes -- env HOME="$TOES_HOME" USER=toes LOGNAME=toes bash -c \ + 'curl -fsSL https://bun.sh/install | bash >/dev/null 2>&1' + +BUN="$TOES_HOME/.bun/bin/bun" +if [ ! -x "$BUN" ]; then + echo "missing bun after install: $BUN" >&2 + exit 1 +fi +ln -sf "$BUN" /usr/local/bin/bun +setcap 'cap_net_bind_service=+ep' "$BUN" + +echo "====> installing toes app as toes" +runuser -u toes -- env HOME="$TOES_HOME" USER=toes LOGNAME=toes bash <<'EOSU' +set -ex + +export PATH="$HOME/.bun/bin:/usr/local/bin:/usr/bin:/bin:$PATH" + +quiet() { + "$@" +} + +REPO="https://git.nose.space/defunkt/toes" +DEST="$HOME/toes" +APPS_DIR="$HOME/apps" +DATA_DIR="$HOME/data" echo "====> installing baudy" -cd /tmp -git clone https://git.nose.space/nakajima/baudy +if [ ! -d "$HOME/baudy" ]; then + git clone https://git.nose.space/nakajima/baudy "$HOME/baudy" +fi + +if [ -d "$DEST/.git" ]; then + echo "====> Pulling latest toes" + git -C "$DEST" fetch origin main + git -C "$DEST" reset --hard origin/main +else + echo "====> Cloning toes" + git clone "$REPO" "$DEST" +fi + +mkdir -p "$APPS_DIR" "$DATA_DIR" "$DATA_DIR/toes" + +cd "$DEST" + +echo "====> Installing dependencies" +bun install + +echo "====> Building" +bun run build + +for app in $BUNDLED_APPS; do + [ -d "$DEST/apps/$app" ] || continue + echo " $app" + cp -r "$DEST/apps/$app" "$APPS_DIR/" + if [ -f "$APPS_DIR/$app/package.json" ]; then + quiet bun install --frozen-lockfile --cwd "$APPS_DIR/$app" || quiet bun install --cwd "$APPS_DIR/$app" + fi +done +EOSU + +echo "====> Installing toes service" +install -m 644 "$TOES_HOME/toes/scripts/toes.service" /etc/systemd/system/toes.service +systemctl enable toes.service + +EOF + +sudo chmod +x mnt/root/tmp/image-build.sh +sudo chroot mnt/root /bin/bash /tmp/image-build.sh +sudo rm -f mnt/root/tmp/image-build.sh + +if [ ! -s mnt/boot/overlays/toesaudio.dtbo ]; then + echo "missing mnt/boot/overlays/toesaudio.dtbo after chroot build" >&2 + exit 1 +fi diff --git a/scripts/check-qemu.sh b/scripts/check-qemu.sh index 69898b5..2e65778 100755 --- a/scripts/check-qemu.sh +++ b/scripts/check-qemu.sh @@ -3,6 +3,13 @@ set -ex # make sure the repo is properly setup by bootstrap-repo.sh +case "$(uname -m)" in + aarch64|arm64) + echo "native arm64 host detected; qemu-aarch64 binfmt is not required" + exit 0 + ;; +esac + required_binfmt="/proc/sys/fs/binfmt_misc/qemu-aarch64" if [ ! -e "$required_binfmt" ]; then diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh new file mode 100755 index 0000000..401c516 --- /dev/null +++ b/scripts/cleanup.sh @@ -0,0 +1,20 @@ +#! /bin/bash +set -ex + +sudo umount mnt/root/run/systemd/resolve || true +sudo umount mnt/root/run || true +sudo umount mnt/root/sys || true +sudo umount mnt/root/proc || true +sudo umount mnt/root/dev || true +sudo umount mnt/boot || true +sudo umount mnt/root/boot/firmware || true +sudo umount mnt/root || true + +if [ -f build/loop-device ]; then + loop_device="$(cat build/loop-device)" + sudo losetup -d "$loop_device" || true + rm -f build/loop-device +fi + +echo "--------------------------------------------" +echo "cleanup complete. image path is build/toes-ubuntu-base.img" diff --git a/scripts/copy-files.sh b/scripts/copy-files.sh index 11227d2..df98cdc 100755 --- a/scripts/copy-files.sh +++ b/scripts/copy-files.sh @@ -2,12 +2,9 @@ set -ex # copy files from this repo to _inside_ the image -cp audio/toesaudio.dts mnt/root/tmp/toesaudio.dts -cp audio/asound.conf mnt/root/etc/asound.conf -cp audio/init-audio mnt/root/usr/local/sbin/init-audio -cp audio/init-audio.service mnt/root/etc/systemd/system/init-audio.service -cp audio/build-audio.sh mnt/root/tmp/build-audio.sh - -# make sure these are executable -chmod +x mnt/root/usr/local/sbin/init-audio -chmod +x mnt/root/tmp/build-audio.sh +sudo install -m 644 audio/toesaudio.dts mnt/root/tmp/toesaudio.dts +sudo install -m 644 audio/asound.conf mnt/root/etc/asound.conf +sudo install -m 755 audio/init-audio mnt/root/usr/local/sbin/init-audio +sudo install -m 644 audio/init-audio.service mnt/root/etc/systemd/system/init-audio.service +sudo install -m 644 matter/toes-matter.service mnt/root/etc/systemd/system/toes-matter.service +sudo install -m 755 audio/build-audio.sh mnt/root/tmp/build-audio.sh diff --git a/scripts/setup-mount.sh b/scripts/setup-mount.sh index bc24a94..0524aba 100755 --- a/scripts/setup-mount.sh +++ b/scripts/setup-mount.sh @@ -7,12 +7,22 @@ mkdir -p build xz -dkc cache/ubuntu-24.04.4-preinstalled-server-arm64+raspi.img.xz >build/toes-ubuntu-base.img loop_device="$(sudo losetup --find --show --partscan build/toes-ubuntu-base.img)" +printf '%s\n' "$loop_device" >build/loop-device echo "$loop_device" - lsblk "$loop_device" mkdir -p mnt/root mnt/boot - sudo mount "${loop_device}p2" mnt/root -sudo mount "${loop_device}p1" mnt/boot +sudo mkdir -p mnt/root/boot/firmware +sudo mount "${loop_device}p1" mnt/root/boot/firmware +sudo mount --bind mnt/root/boot/firmware mnt/boot +sudo mount --bind /dev mnt/root/dev +sudo mount -t proc proc mnt/root/proc +sudo mount -t sysfs sysfs mnt/root/sys +sudo mount -t tmpfs tmpfs mnt/root/run + +if [ -d /run/systemd/resolve ]; then + sudo mkdir -p mnt/root/run/systemd/resolve + sudo mount --bind /run/systemd/resolve mnt/root/run/systemd/resolve +fi diff --git a/scripts/update-configtxt.sh b/scripts/update-configtxt.sh old mode 100644 new mode 100755 index 62196c4..ad73195 --- a/scripts/update-configtxt.sh +++ b/scripts/update-configtxt.sh @@ -2,21 +2,43 @@ set -ex # add stuff we want to config.txt -add = <<-END - dtparam=spi=on - # we can adjust these values as needed - dtparam=cooling_fan=on,fan_temp0=62000,fan_temp0_hyst=8000,fan_temp0_speed=80,fan_temp1=70000,fan_temp1_hyst=5000,fan_temp1_speed=100,fan_temp2=76000,fan_temp2_hyst=4000,fan_temp2_speed=170,fan_temp3=80000,fan_temp3_hyst=3000,fan_temp3_speed=250 - # enable i2s for audio - dtparam=i2s=on - # use an external antenna instead of the pcb antenna on the CM - dtparam=ant2 - # load our audio driver - dtoverlay=toesaudio - # enable gpio12 which we use to reset the audio codec - gpio=12=op,dh +config="mnt/boot/config.txt" +add_file="$(mktemp)" +trap 'rm -f "$add_file"' EXIT + +cat >"$add_file" <<'END' +dtparam=spi=on +# we can adjust these values as needed +dtparam=cooling_fan=on,fan_temp0=62000,fan_temp0_hyst=8000,fan_temp0_speed=80,fan_temp1=70000,fan_temp1_hyst=5000,fan_temp1_speed=100,fan_temp2=76000,fan_temp2_hyst=4000,fan_temp2_speed=170,fan_temp3=80000,fan_temp3_hyst=3000,fan_temp3_speed=250 +# enable i2s for audio +dtparam=i2s=on +# use an external antenna instead of the pcb antenna on the CM +dtparam=ant2 +# load our audio driver +dtoverlay=toesaudio +# enable gpio12 which we use to reset the audio codec +gpio=12=op,dh +# enable serial over usb in gadget/peripheral mode +dtoverlay=dwc2,dr_mode=peripheral END -sed -i "s/dtparam=spi=on/$add" mnt/boot/config.txt +if ! sudo grep -qx 'dtparam=spi=on' "$config"; then + echo "missing dtparam=spi=on in $config" >&2 + exit 1 +fi + +sudo awk -v add_file="$add_file" ' + $0 == "dtparam=spi=on" && !done { + while ((getline line < add_file) > 0) { + print line + } + close(add_file) + done = 1 + next + } + { print } +' "$config" | sudo tee "$config.tmp" >/dev/null +sudo mv "$config.tmp" "$config" # we don't need fancy graphics. we don't have a screen. -echo "\ngpu_mem=16" >>mnt/boot/config.txt +printf '\ngpu_mem=16\n' | sudo tee -a "$config" >/dev/null