Progress Log

Pi Software Bootstrap (Build Guide 2 Steps 1-3)

April 11, 202621:20Build Log

Pi Software Bootstrap (Build Guide 2 Steps 1-3)

Date: 2026-04-11 Type: Build Log

Context

Continuing the same evening that ended with 2026-04-11_2032--rpi-headless-first-boot.md. The Pi was headless, on WiFi, SSH-able, but the OS was bare. Todd wasn't in "wire stuff up" mode and didn't have all the hardware on hand yet, so we focused on the parts of Build Guide 2 that needed zero new hardware: the apt updates, the Python virtual environment, the hardware interfaces, and the project skeleton. Goal: leave the Pi in a "ready for components, just plug things in next session" state.

What Changed

The Pi (tickslayer) is fully software-provisioned per Build Guide 2 Steps 1, 2, 3, and 8. The build guide itself was rewritten to match reality, including several non-obvious gotchas that the original guide got wrong.

What we did, in order

  1. sudo apt update && sudo apt upgrade -y — 92 packages upgraded. ~5 minutes. Notable: this is Debian 13 Trixie, not Bookworm (the Pi OS image is pi-gen Dec 4 2025 build, kernel 6.12.47+rpt-rpi-v8). Initramfs was regenerated.
  2. System packages installed: python3-pip, python3-venv, i2c-tools, git, rpicam-apps (was libcamera-apps — now a transitional package on Trixie), gpsd, gpsd-clients, plus python3-picamera2 and libcap-dev (added after the next failure).
  3. First pip install attempt failed. pip install picamera2 tried to build python-prctl from source, which requires libcap development headers, which weren't installed. Even fixing that gives a worse install than the apt path. Pi OS-blessed approach: install picamera2 from apt, then use a venv with --system-site-packages so the venv inherits it.
  4. Recreated the venv with --system-site-packages:
    rm -rf ~/rover/venv
    python3 -m venv --system-site-packages ~/rover/venv
    
    Verified picamera2 visible from inside: /usr/lib/python3/dist-packages/picamera2/__init__.py
  5. Pip-installed the libraries that do belong in pip (everything except picamera2): adafruit-circuitpython-pca9685, adafruit-circuitpython-ads1x15, adafruit-circuitpython-motor, pynmea2, pyserial, requests. Pulls in Adafruit-Blinka 9.0.4 plus a long tail of CircuitPython dependencies (Adafruit-PlatformDetect, Adafruit-PureIO, RPi.GPIO, rpi_ws281x, sysv_ipc, etc).
  6. Verified imports from inside the venv:
    import board, busio
    import adafruit_pca9685
    import adafruit_ads1x15.ads1115 as ADS
    from adafruit_motor import servo
    import picamera2
    import pynmea2
    import serial
    import requests
    
    All clean.
  7. Wired venv into .bashrc: echo 'source ~/rover/venv/bin/activate' >> ~/.bashrc. This works for interactive SSH sessions only — non-interactive ssh tickslayer 'cmd' doesn't source .bashrc, so my own scripts have to source the activate explicitly.
  8. Enabled hardware interfaces via raspi-config nonint:
    • do_i2c 0 — I2C on
    • do_serial_hw 0 — UART hardware on (for GPS)
    • do_serial_cons 1 — login shell over serial off (so we don't fight with GPS over the wire)
    • No do_camera: that function is gone in current raspi-config. Camera is auto-detected via libcamera at boot, no explicit enable needed.
    • No get_serial: also gone, only the setters exist.
  9. Created project structure: ~/rover/{config,logs,captures,routes} and staged ~/rover/config/rover_config.py with TODO-marked calibration values for steering, throttle, camera, GPS, and battery.
  10. Rebooted the Pi to make the I2C kernel module load and the new initramfs from the apt upgrade take effect. Came back in ~50 sec, auto-rejoined WiFi via the NetworkManager profile from the previous session.
  11. Verified post-reboot state: i2cdetect -y 1 returns an empty 16x16 grid (all --). That's exactly what we want — bus is alive, no chips wired yet, but ready to talk to the ADS1115 and PCA9685 when they get wired up. Discovered i2cdetect lives in /usr/sbin/, which is in PATH for interactive shells but not non-interactive.

Files Modified

  • On the Pi (tickslayer):

    • ~/rover/venv/ — Python virtualenv with --system-site-packages
    • ~/rover/config/rover_config.py — calibration constants, all marked TODO for later guides
    • ~/rover/{captures,logs,routes}/ — empty data directories
    • ~/.bashrc — appended source ~/rover/venv/bin/activate
    • /boot/firmware/config.txtdtparam=i2c_arm=on, UART enabled (via raspi-config)
    • /boot/cmdline.txtconsole=serial0 removed (via do_serial_cons 1)
    • All upgraded apt packages from apt upgrade (initramfs regenerated)
  • In the repo:

    • docs/articles/build-02-pi-setup.md — Steps 1, 2, and 3 rewritten with reality (rpicam-apps name change, picamera2-via-apt, --system-site-packages venv requirement, raspi-config function changes, the non-interactive SSH .bashrc gotcha, the i2cdetect PATH gotcha, and an agent-tip callout for the bootstrap phase)

Key Takeaways

  • Raspberry Pi OS Lite is now Debian 13 Trixie, not Bookworm. The guide language should reflect this.
  • libcamera-apps is a transitional package on Trixie. Real package is rpicam-apps. Use the new name in fresh installs.
  • NEVER pip install picamera2. It fails on python-prctllibcap headers, and even when patched gives a worse install. Always apt install python3-picamera2 and create the venv with --system-site-packages so it inherits picamera2 from the system.
  • raspi-config no longer has do_camera or get_serial on current Pi OS. Camera is auto-detected via libcamera. Use do_serial_hw and do_serial_cons instead of get_serial.
  • .bashrc venv activation only works for interactive SSH sessions. Non-interactive ssh tickslayer 'cmd' runs in a non-interactive shell that doesn't source .bashrc. For scripts: source the activate explicitly or use ~/rover/venv/bin/python directly.
  • i2cdetect lives at /usr/sbin/i2cdetect. In PATH for interactive shells (via /etc/profile), not for non-interactive ones. Use sudo /usr/sbin/i2cdetect -y 1 in scripts, or just i2cdetect -y 1 interactively.
  • The user is right not to wire one chip in isolation. Setting up + tearing down a breadboard for a single i2cdetect test isn't worth it. Wait until enough parts are present to do all the I2C wiring at once (ADS1115 + PCA9685 + voltage divider into A0).
  • Background tasks for slow apt/pip operations are great. Saved real time tonight by kicking off apt upgrade in the background and checking other state in the meantime. The harness's task notification system makes this clean.

What's still gated on parts

  • Build Guide 1 Step 2 — Voltage divider passive test (need voltage sensor)
  • Build Guide 1 Step 4 — Camera bench test (need camera + CSI cable)
  • Build Guide 1 Step 5 — GPS NMEA test (need GPS module)
  • Build Guide 1 Step 6 — PCA9685 I2C detect (need PWM driver)
  • Build Guide 1 Step 7 — ADS1115 read through divider (have ADS1115 + multimeter, need voltage divider for the actual read; bare i2cdetect is doable now but low-value alone)
  • Build Guide 1 Step 8 — USB battery pack power test (have everything; we deferred only because Todd was tired)
  • Build Guide 2 Steps 4–7 — Camera deep test, GPS service setup, I2C verification with both chips, PWM servo sweep
  • Build Guide 3+ — Vehicle prep and beyond

Next session

When more parts land, the Pi is ready. Just SSH in and start wiring. The first satisfying milestone will be i2cdetect -y 1 showing both 0x40 (PCA9685) and 0x48 (ADS1115) at the same time — that's Build Guide 2 Step 6 fully passing.