|
| 1 | +# How This Project Works and How to Customize It |
| 2 | + |
| 3 | +This project builds a Raspberry Pi OS image that already has the Pinewood Robotics software stack installed (B.L.I.T.Z + Autobahn), plus some device-specific system setup. |
| 4 | + |
| 5 | +The important idea is: **do all the image editing inside Docker**, using QEMU so we can chroot into an ARM64 Raspberry Pi OS image from an x86_64 machine. |
| 6 | + |
| 7 | +## What this project produces |
| 8 | + |
| 9 | +- A flashable Raspberry Pi OS `.img` file (optionally compressed to `.img.xz`) |
| 10 | +- The image is preconfigured so the device boots with: |
| 11 | + - system dependencies installed |
| 12 | + - SSH + mDNS enabled (so it is reachable on the network) |
| 13 | + - B.L.I.T.Z installed (from a chosen branch) |
| 14 | + - Autobahn installed |
| 15 | + - a USB udev naming rule applied (so ports map to stable names) |
| 16 | + - a first-boot flow that asks you to set the device name (hostname) |
| 17 | + |
| 18 | +Outputs are written to `outputs/`. |
| 19 | + |
| 20 | +## Big picture flow (methodology) |
| 21 | + |
| 22 | +The pipeline is a sequence of small steps. Each step has one job and is easy to swap out. |
| 23 | + |
| 24 | +### 1) Build container environment |
| 25 | + |
| 26 | +- `Dockerfile` installs the tools needed to manipulate disk images: |
| 27 | + - loop devices, partition tools, filesystem tools |
| 28 | + - `qemu-user-static` so the host can execute ARM64 binaries via emulation |
| 29 | +- `compose.yml` runs the container in **privileged** mode so loop devices and `kpartx` work. |
| 30 | + |
| 31 | +### 2) Pick a target device and base OS image |
| 32 | + |
| 33 | +- Device definitions live in `installation/devices_and_distros/`. |
| 34 | +- `devices.conf` maps device keys to a Raspberry Pi OS download URL and filename. |
| 35 | +- The entrypoint (`installation/devices_and_distros/build.bash`) chooses: |
| 36 | + - build all device scripts, or |
| 37 | + - build one device script (example: `pi5`) |
| 38 | + |
| 39 | +### 3) Download and unpack the base image (with caching) |
| 40 | + |
| 41 | +- `installation/util/install_distro.bash` downloads the `.img.xz` into: |
| 42 | + - `installation/cached_images/` (on the host via the Docker volume) |
| 43 | +- It then decompresses into the container workspace so we can edit it. |
| 44 | + |
| 45 | +Why this is structured this way: |
| 46 | + |
| 47 | +- **Host cache** avoids re-downloading the large base image every run. |
| 48 | +- **Workspace copy** avoids mutating the cached file and keeps each run reproducible. |
| 49 | + |
| 50 | +### 4) Expand partitions and mount the image |
| 51 | + |
| 52 | +- `installation/devices_and_distros/build.bash` expands the image file size using `truncate`. |
| 53 | +- `setup_image.bash` does the low-level disk work: |
| 54 | + - attaches the image to a loop device |
| 55 | + - repairs/rescans the partition table (handles “expanded image” correctly) |
| 56 | + - resizes partition 2 to fill the available space |
| 57 | + - runs `e2fsck` and `resize2fs` |
| 58 | + - mounts root and boot partitions under `/mnt/raspios` |
| 59 | + - bind-mounts `/dev`, `/proc`, `/sys`, and the repo workspace into the chroot |
| 60 | + - copies `qemu-aarch64-static` into the image so `chroot` can run ARM64 binaries |
| 61 | + |
| 62 | +This step is separated because it is “image plumbing” and is the same no matter which device or software stack you install. |
| 63 | + |
| 64 | +### 5) Chroot into the image and install everything |
| 65 | + |
| 66 | +Once mounted, `setup_image.bash` runs a device script inside the image: |
| 67 | + |
| 68 | +- The device script (example: `installation/devices_and_distros/pi5.bash`) runs: |
| 69 | + - `installation/modules/main_startup.bash` to install the software stacks |
| 70 | + - device-specific patches (example: install a udev rules file) |
| 71 | + |
| 72 | +Inside `installation/modules/main_startup.bash` the work is split into modules: |
| 73 | + |
| 74 | +- `installation_common.bash` |
| 75 | + - installs OS packages and tools (git, python, build tools, OpenCV, etc.) |
| 76 | + - enables services like SSH and Avahi |
| 77 | + - installs Rust (via rustup) and ensures `python` points to `python3` |
| 78 | + - prepares `/opt/blitz` |
| 79 | +- `installation_blitz.bash` |
| 80 | + - clones `B.L.I.T.Z` into `/opt/blitz/B.L.I.T.Z` |
| 81 | + - checks out a specific branch (`merge-backend` in the current script) |
| 82 | + - runs the project installer with a default name |
| 83 | +- `installation_autobahn.bash` |
| 84 | + - clones Autobahn into `/opt/blitz/autobahn` |
| 85 | + - runs its installer script |
| 86 | + |
| 87 | +Why the modules folder exists: |
| 88 | + |
| 89 | +- **Common setup** is shared across devices. |
| 90 | +- **Per-project installs** are separate so you can add/remove components cleanly. |
| 91 | +- **Per-device scripts** stay small and focus on hardware/OS tweaks. |
| 92 | + |
| 93 | +### 6) Apply device-specific system patches |
| 94 | + |
| 95 | +For Pi 5, `installation/devices_and_distros/pi5.bash` installs: |
| 96 | + |
| 97 | +- `installation/system-patch/90-usb-port-names.rules` into `/etc/udev/rules.d/` |
| 98 | + |
| 99 | +This creates stable symlinks like `usb_cam1`, `usb_cam2`, etc., based on physical USB port topology. |
| 100 | + |
| 101 | +Keeping this in `installation/system-patch/` makes it obvious that this is “OS configuration”, not “application code”. |
| 102 | + |
| 103 | +### 7) Export (and optionally compress) the final image |
| 104 | + |
| 105 | +- `export_image_and_compress.bash`: |
| 106 | + - unmounts everything mounted by `setup_image.bash` |
| 107 | + - detaches `kpartx` mappings and loop devices |
| 108 | + - copies the final `.img` into `/host/outputs/` (mapped to repo `outputs/`) |
| 109 | + - optionally compresses it using `xz` |
| 110 | + |
| 111 | +This is separated so cleanup and export logic is consistent and easy to debug. |
| 112 | + |
| 113 | +## First boot naming behavior (hostname) |
| 114 | + |
| 115 | +The system is intended to ship with a placeholder name first, then ask for a real name on first boot. |
| 116 | + |
| 117 | +- `installation/system-patch/blitzprojstartup.bash` checks this file: |
| 118 | + - `/opt/blitz/B.L.I.T.Z/system_data/name.txt` |
| 119 | +- If it is missing, it creates it with the default placeholder name. |
| 120 | +- If it still equals the placeholder, it prompts on the console for a new name and then: |
| 121 | + - writes it to `name.txt` |
| 122 | + - sets the system hostname (`hostnamectl`) |
| 123 | + - ensures `/etc/hosts` has `127.0.1.1 <name>` |
| 124 | + - restarts Avahi and SSH |
| 125 | + - reboots |
| 126 | + |
| 127 | +`post_install.bash` is a helper script to place `installation/system-patch/blitzprojstartup.bash` into `/usr/local/bin/` and make it runnable. |
| 128 | + |
| 129 | +## Why the file structure looks like this |
| 130 | + |
| 131 | +This repo is split into a few “layers” on purpose: |
| 132 | + |
| 133 | +- **Top-level Docker + entry scripts** |
| 134 | + - `Dockerfile`, `compose.yml`, `Makefile` |
| 135 | + - These define how to run the build in a controlled environment. |
| 136 | +- **Image plumbing** |
| 137 | + - `setup_image.bash`, `export_image_and_compress.bash` |
| 138 | + - These deal with loop devices, partitions, mounts, and cleanup. |
| 139 | +- **Installation logic inside the image** |
| 140 | + - `installation/modules/` |
| 141 | + - These are the steps that run _inside_ the chroot and install software. |
| 142 | +- **Per-device logic** |
| 143 | + - `installation/devices_and_distros/` |
| 144 | + - One small script per target device, plus a `devices.conf` that defines which OS image to use. |
| 145 | +- **System patches** |
| 146 | + - `installation/system-patch/` |
| 147 | + - Files that should be copied into the image as-is (udev rules, etc.). |
| 148 | +- **Docs** |
| 149 | + - `docs/` holds usage and “how it works” explanations. |
| 150 | + |
| 151 | +This separation keeps changes safe: |
| 152 | + |
| 153 | +- If you change device details, you usually only touch a device script or patch file. |
| 154 | +- If you change what software gets installed, you usually only touch `installation/modules/`. |
| 155 | +- If you change how images are mounted/exported, you usually only touch the image plumbing scripts. |
| 156 | + |
| 157 | +## How to customize (common changes) |
| 158 | + |
| 159 | +### Change the base OS image (or update versions) |
| 160 | + |
| 161 | +Edit `installation/devices_and_distros/devices.conf`: |
| 162 | + |
| 163 | +- Update `PI5_IMAGE_URL` to a newer Raspberry Pi OS image. |
| 164 | +- Update `PI5_IMAGE_FILE` to match the downloaded filename. |
| 165 | + |
| 166 | +### Change how much extra space is added |
| 167 | + |
| 168 | +Edit `installation/devices_and_distros/build.bash`: |
| 169 | + |
| 170 | +- `EXPAND_IMAGE_SIZE` controls how much space is appended to the image file before resizing partitions. |
| 171 | + |
| 172 | +### Add a new device target |
| 173 | + |
| 174 | +1. Add a new `*.bash` script in `installation/devices_and_distros/` (copy `pi5.bash` as a starting point). |
| 175 | +2. Add `YOURDEVICE_IMAGE_FILE` and `YOURDEVICE_IMAGE_URL` to `devices.conf`. |
| 176 | +3. Build it with: |
| 177 | + - `make build-for ARGS=yourdevice` |
| 178 | + |
| 179 | +### Change what gets installed inside the image |
| 180 | + |
| 181 | +Edit modules in `installation/modules/`: |
| 182 | + |
| 183 | +- Add packages in `installation_common.bash` |
| 184 | +- Change the B.L.I.T.Z branch or name defaults in `installation_blitz.bash` |
| 185 | +- Change Autobahn install behavior in `installation_autobahn.bash` |
| 186 | + |
| 187 | +### Change USB port naming rules |
| 188 | + |
| 189 | +Edit `installation/system-patch/90-usb-port-names.rules`. |
| 190 | + |
| 191 | +If you are targeting different hardware, keep the patch file but adjust the `KERNELS==` matches and symlink names. |
| 192 | + |
| 193 | +### Change first-boot name behavior |
| 194 | + |
| 195 | +Edit `installation/system-patch/blitzprojstartup.bash`: |
| 196 | + |
| 197 | +- Change the placeholder default name (currently `blitz-pi-random-name-1234`) |
| 198 | +- Change allowed characters (currently letters, numbers, `_`, `-`) |
| 199 | +- Change what services restart on rename |
| 200 | + |
| 201 | +If you do not want an interactive prompt, you can pre-create: |
| 202 | + |
| 203 | +- `/opt/blitz/B.L.I.T.Z/system_data/name.txt` |
| 204 | + with the final name inside the image during the build. |
| 205 | + |
| 206 | +## Where to start reading code |
| 207 | + |
| 208 | +If you want to understand the build end-to-end, read in this order: |
| 209 | + |
| 210 | +1. `installation/devices_and_distros/build.bash` |
| 211 | +2. `installation/util/install_distro.bash` |
| 212 | +3. `setup_image.bash` |
| 213 | +4. `installation/devices_and_distros/pi5.bash` |
| 214 | +5. `installation/modules/main_startup.bash` and the module scripts it calls |
| 215 | +6. `export_image_and_compress.bash` |
| 216 | +7. `installation/system-patch/blitzprojstartup.bash` |
0 commit comments