From 1d1e432d265df8c169d28af751c2b12b5975efdc Mon Sep 17 00:00:00 2001 From: Arnav Wadhwa <40817363+ArnavVWadhwa@users.noreply.github.com> Date: Tue, 1 Sep 2026 13:34:13 -0700 Subject: [PATCH] fix: turn off SPI and the serial login console for the DPi bus The DPi computer board uses GPIO7 (SPI0 CE1) as a digital input, so the enabled SPI overlay claimed the pin and GPIO.setup raised "GPIO busy". The board also talks over RS485 on /dev/serial0, which the default serial getty holds open, giving "device reports readiness to read but returned no data (multiple access on port?)". Drop dtparam=spi=on, strip the console= token from cmdline.txt, and mask serial-getty@ttyAMA0 (enable_uart stays, so the UART hardware is still up). Update the smoke test, CI assertions, and README to match. --- .github/workflows/validate.yml | 6 +++++- README.md | 26 +++++++++++++++----------- customize.sh | 16 +++++++++++++++- hardware-smoke-test.sh | 3 ++- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f26ad2d..b3a8a1a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -53,9 +53,13 @@ jobs: dpkg -S /usr/sbin/avahi-daemon 2>/dev/null | grep -q '^avahi-dpea:' \ && echo "OK /usr/sbin/avahi-daemon owned by avahi-dpea" \ || { echo "FAIL avahi-daemon binary not replaced by avahi-dpea"; fail=1; } - for line in "dtparam=i2c_arm=on" "dtparam=spi=on" "enable_uart=1" "dtoverlay=disable-bt"; do + for line in "dtparam=i2c_arm=on" "enable_uart=1" "dtoverlay=disable-bt"; do grep -qx "$line" /boot/firmware/config.txt && echo "OK config: $line" || { echo "FAIL config: $line"; fail=1; } done + # SPI must NOT be enabled: it would claim GPIO7 (SPI0 CE1), a DPi computer input. + grep -qx "dtparam=spi=on" /boot/firmware/config.txt && { echo "FAIL config: spi should be off"; fail=1; } || echo "OK config: spi not enabled" + # serial login console masked so the DPi RS485 bus owns /dev/serial0. + test -L /etc/systemd/system/serial-getty@ttyAMA0.service && echo "OK serial getty masked" || { echo "FAIL serial getty not masked"; fail=1; } test -f /etc/NetworkManager/system-connections/dpea-eth0.nmconnection \ && echo "OK eth0 profile present" || { echo "FAIL eth0 profile"; fail=1; } # best-effort: mDNS binds 5358 (a container may not let the daemon start; verify on hardware) diff --git a/README.md b/README.md index 94ce724..c89e137 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,11 @@ dependencies and installs them with `uv`. ## What the image bakes in - **OS**: Raspberry Pi OS Trixie 64-bit (arm64), Desktop -- **Hardware**: I2C, SPI, and UART enabled. `dtoverlay=disable-bt` so - `/dev/serial0` is the PL011 UART for the SlushEngine / DPi bus. `i2c-dev` loaded. +- **Hardware**: I2C and UART enabled, SPI deliberately OFF. `dtoverlay=disable-bt` + so `/dev/serial0` is the PL011 UART for the SlushEngine / DPi bus, and the serial + login console is disabled so a getty does not fight that bus on the same port. + `i2c-dev` loaded. SPI stays off because the DPi computer uses GPIO7 (SPI0 CE1) as + a digital input, which an SPI overlay would claim ("GPIO busy"). - **Networking (`eth0`)**: DHCP for internet, plus an always-on static `172.17.21.2/22` for a direct laptop-to-Pi cable. - **mDNS**: the patched avahi that serves mDNS on port **5358**. @@ -21,7 +24,7 @@ dependencies and installs them with `uv`. Current contents: - core: `git`, `curl`, `ca-certificates` -- I2C/SPI: `i2c-tools` +- I2C: `i2c-tools` - kivy runtime: `libsdl2-2.0-0`, `libsdl2-image-2.0-0`, `libsdl2-mixer-2.0-0`, `libsdl2-ttf-2.0-0`, `libmtdev1`, `libgl1-mesa-dri` - mDNS resolution: `libnss-mdns` @@ -66,20 +69,21 @@ repo is public, so no GitHub account is needed to download the image. Runs `shellcheck`, then runs `customize.sh` inside an arm64 Trixie container and asserts what can be checked without hardware: `uv` present, the 5358 avahi -`.deb` installed, the I2C/SPI/UART lines written, and the eth0 NetworkManager -profile present. This gates merges to `main`. +`.deb` installed, the I2C/UART config lines written (and SPI *not* enabled), the +serial getty masked, and the eth0 NetworkManager profile present. This gates +merges to `main`. Container checks cannot exercise real hardware or multicast, so they do not cover -actual I2C/SPI/UART function, live `.local` resolution, or the eth0 address -coming up. Those need a flashed Pi. +actual I2C/UART function, live `.local` resolution, or the eth0 address coming up. +Those need a flashed Pi. ### On real hardware (`hardware-smoke-test.sh`) After flashing a Pi, run `sudo ./hardware-smoke-test.sh` on it. It checks: `uv` -present, `/dev/i2c-*` and `/dev/spidev*` nodes, `/dev/serial0 -> ttyAMA0`, avahi -listening on 5358 (and not 5353), and eth0 holding `172.17.21.2`. For the -direct-cable path, set another machine's ethernet to `172.17.21.1` and confirm -you can reach `172.17.21.2`. +present, `/dev/i2c-*` present and no `/dev/spidev*` (SPI off), `/dev/serial0 -> +ttyAMA0` with the serial login console off, avahi listening on 5358 (and not +5353), and eth0 holding `172.17.21.2`. For the direct-cable path, set another +machine's ethernet to `172.17.21.1` and confirm you can reach `172.17.21.2`. ## Files diff --git a/customize.sh b/customize.sh index 30d8799..8c5496b 100755 --- a/customize.sh +++ b/customize.sh @@ -16,14 +16,28 @@ apt-get install -y --no-install-recommends \ $(sed -e 's/#.*//' -e '/^[[:space:]]*$/d' /tmp/packages.txt) # --- hardware interfaces (edit config.txt directly) --- +# SPI is deliberately NOT enabled: the DPi computer board uses GPIO7 (SPI0 CE1) +# as a digital input (_IN_3_PIN), so an enabled SPI overlay claims that pin and +# the board's GPIO.setup fails with "GPIO busy". CONFIG=/boot/firmware/config.txt [ -f "$CONFIG" ] || CONFIG=/boot/config.txt grep -q '^dtparam=i2c_arm=on' "$CONFIG" || echo 'dtparam=i2c_arm=on' >> "$CONFIG" -grep -q '^dtparam=spi=on' "$CONFIG" || echo 'dtparam=spi=on' >> "$CONFIG" grep -q '^enable_uart=1' "$CONFIG" || echo 'enable_uart=1' >> "$CONFIG" grep -q '^dtoverlay=disable-bt' "$CONFIG" || echo 'dtoverlay=disable-bt' >> "$CONFIG" grep -q '^i2c-dev' /etc/modules || echo 'i2c-dev' >> /etc/modules +# --- serial login console OFF (the UART hardware stays on via enable_uart above) --- +# Raspberry Pi OS runs a getty on the serial console by default; it holds +# /dev/serial0 open and fights the DPi RS485 bus (serial.Serial on /dev/serial0), +# giving "device reports readiness to read but returned no data (multiple access +# on port?)". Disable it two ways: drop the console= token from cmdline.txt, and +# mask the serial getty. enable_uart=1 above keeps the UART itself available. +CMDLINE=/boot/firmware/cmdline.txt +[ -f "$CMDLINE" ] || CMDLINE=/boot/cmdline.txt +[ -f "$CMDLINE" ] && sed -i -E 's/ ?console=(serial0|ttyAMA0|ttyS0),[0-9]+//g' "$CMDLINE" +install -d /etc/systemd/system +ln -sf /dev/null /etc/systemd/system/serial-getty@ttyAMA0.service + # --- patched avahi (mDNS on 5358) from the prebuilt arm64 .deb (avahi_0.8 CI) --- AVAHI_DEB_URL="${AVAHI_DEB_URL:-https://github.com/dpengineering/avahi_0.8/releases/latest/download/avahi-dpea_0.8_arm64.deb}" if curl -fLsS "$AVAHI_DEB_URL" -o /tmp/avahi.deb; then diff --git a/hardware-smoke-test.sh b/hardware-smoke-test.sh index e25bb3d..a50c9e2 100755 --- a/hardware-smoke-test.sh +++ b/hardware-smoke-test.sh @@ -11,8 +11,9 @@ skip(){ echo "SKIP $1 ($2)"; skipped=$((skipped+1)); } chk "uv installed" "command -v uv" chk "I2C device node present" "ls /dev/i2c-*" -chk "SPI device node present" "ls /dev/spidev*" +chk "SPI off (no spidev; GPIO7 free for DPi)" "! ls /dev/spidev*" chk "/dev/serial0 -> ttyAMA0" "[ \"\$(readlink -f /dev/serial0)\" = /dev/ttyAMA0 ]" +chk "serial login console off" "! systemctl is-active --quiet serial-getty@ttyAMA0.service" chk "avahi listening on 5358" "ss -lun | grep -q ':5358'" chk "avahi NOT on 5353" "! ss -lun | grep -q ':5353'" # The eth0 static (172.17.21.2) only activates when the link has carrier. Running