# Automatic eMMC provisioning for reTerminal DM4 (CM4) This guide covers: 1. **Auto-flash**: When the reTerminal is switched to boot mode (eMMC disable jumper) and connected via USB to a provisioning host, the host automatically deploys a golden image to the CM4 eMMC. 2. **Backup**: When a device is detected (USB or network), the dashboard asks you to choose **Backup** or **Deploy**. Backup saves the device eMMC to a timestamped file in `backups/`. 3. **Network**: If the device boots over the network and runs the **provisioning client** (see `network-client/`), it registers with the dashboard and appears as "Device (Network)"; you then choose Backup or Deploy. Deploy streams the golden image to the device; Backup uploads the device eMMC to the server. 4. **Cloud-init**: The golden image includes cloud-init so each device configures itself on first boot (hostname, network, packages, kiosk setup). --- ## Part 1: Auto-flash when reTerminal is in boot mode ### How it works - reTerminal has an **eMMC disable** jumper (see reTerminal docs; often “J2” or “nRPIBOOT”). When the jumper is fitted, the CM4 boots in **USB device mode** and waits for `rpiboot` from the host. - You connect the reTerminal’s **USB slave** port to a **provisioning PC** (Linux). - **udev** detects the Raspberry Pi Foundation USB device (vendor `2b8e`) and runs a trigger script. - The trigger starts a **flash job** that: 1. Runs **rpiboot** (from the `usbboot` project). The CM4 then exposes its eMMC as a USB mass-storage device. 2. After `rpiboot` exits, finds the new block device (eMMC) and writes your **golden image** to it with `dd`. - You remove the jumper and power cycle; the reTerminal boots from eMMC and runs **cloud-init** on first boot. ### Provisioning host setup (Linux) #### 1. Build and install usbboot (rpiboot) ```bash sudo apt-get install -y libusb-1.0-0-dev git clone --depth=1 https://github.com/raspberrypi/usbboot cd usbboot make sudo mkdir -p /opt/usbboot sudo cp rpiboot /opt/usbboot/ ``` #### 2. Create golden image and config directory - Build your golden image (see Part 2) and place it where the script will find it, e.g.: ```bash sudo mkdir -p /var/lib/cm4-provisioning sudo cp /path/to/your/golden-reterminal.img /var/lib/cm4-provisioning/golden.img ``` - Or use a different path and set `GOLDEN_IMAGE` when installing the script (see below). #### 3. Install the auto-flash script and trigger ```bash # From this repo (chromium-setup/emmc-provisioning/host/) cd chromium-setup/emmc-provisioning/host sudo mkdir -p /opt/cm4-provisioning sudo cp flash-emmc-on-connect.sh /opt/cm4-provisioning/ sudo chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh # Optional: override paths via environment (create env file) echo 'GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img' | sudo tee /opt/cm4-provisioning/env echo 'RPIBOOT_DIR=/opt/usbboot' | sudo tee -a /opt/cm4-provisioning/env echo 'EMMC_SIZE_BYTES=8589934592' | sudo tee -a /opt/cm4-provisioning/env # 8GB; use 17179869184 for 16GB sudo cp cm4-flash-trigger.sh /usr/local/bin/ sudo chmod +x /usr/local/bin/cm4-flash-trigger.sh ``` If your golden image path or rpiboot path is different, set `GOLDEN_IMAGE`, `RPIBOOT_DIR`, and optionally `EMMC_SIZE_BYTES` in `/opt/cm4-provisioning/env` and source it from the script, or pass them into the systemd-run call in the trigger (e.g. by making the trigger source the env file and export variables before `systemd-run`). #### 4. Install udev rule ```bash # From emmc-provisioning/host/ sudo cp 90-cm4-boot-mode.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger ``` #### 5. Enable provisioning (safety) Provisioning runs only if the “enabled” file exists: ```bash sudo mkdir -p /etc/cm4-provisioning sudo touch /etc/cm4-provisioning/enabled ``` To disable auto-flash, remove that file: `sudo rm /etc/cm4-provisioning/enabled`. #### 6. Optional: pass environment into the flash job If you use `/opt/cm4-provisioning/env`, update the trigger so the flash script sees those variables. For example change `/usr/local/bin/cm4-flash-trigger.sh` to: ```bash #!/usr/bin/env bash set -a [[ -f /opt/cm4-provisioning/env ]] && source /opt/cm4-provisioning/env set +a export GOLDEN_IMAGE RPIBOOT_DIR EMMC_SIZE_BYTES FLASH_SCRIPT="${CM4_FLASH_SCRIPT:-/opt/cm4-provisioning/flash-emmc-on-connect.sh}" exec systemd-run --no-block --unit=cm4-flash-once --property=Environment="GOLDEN_IMAGE=$GOLDEN_IMAGE" ... ``` Or keep it simple and edit the defaults inside `flash-emmc-on-connect.sh` (e.g. `GOLDEN_IMAGE`, `RPIBOOT_DIR`, `EMMC_SIZE_BYTES`). ### Usage 1. Fit the **eMMC disable** jumper on the reTerminal. 2. Connect the reTerminal **USB slave** port to the provisioning PC. 3. Power the reTerminal (or apply power after USB). 4. On the host, `rpiboot` will run automatically; when it exits, the script will `dd` the golden image to the eMMC. Watch logs: `journalctl -u cm4-flash-once -f` or `journalctl -t cm4-flash -f`. 5. When done, remove the jumper and power cycle the reTerminal. It will boot from eMMC; cloud-init will run on first boot. --- ## Part 2: Golden image with cloud-init Raspberry Pi OS (recent versions) supports **cloud-init** using the **NoCloud** datasource: it reads `user-data`, `meta-data`, and optionally `network-config` from the **boot** (FAT32) partition. ### Creating the golden image 1. **Flash Raspberry Pi OS** (or your base image) to a spare SD card or a loop file. 2. **Mount the boot partition** (first partition, FAT32). On the image file it might be at an offset; use `losetup -P` or mount the SD’s partition. 3. **Add cloud-init NoCloud files** on the boot partition (same level as `config.txt`, not in a subfolder for default NoCloud): - `user-data` – main config (packages, runcmd, etc.) - `meta-data` – optional (instance-id, local-hostname) - `network-config` – optional (network config in netplan format) You can use the examples in this repo: ```bash # After mounting boot partition at e.g. /mnt/boot # (On Raspberry Pi OS, boot is often /boot/firmware on the running system, or the first FAT partition of the image) cp emmc-provisioning/cloud-init/user-data /mnt/boot/ cp emmc-provisioning/cloud-init/meta-data /mnt/boot/ cp emmc-provisioning/cloud-init/network-config /mnt/boot/ ``` 4. **Customise** `user-data` and `network-config` (hostname, WiFi, packages, Chromium kiosk, etc.). 5. **Copy your kiosk/Chromium scripts** into the image rootfs if needed (e.g. under `/home/pi/` or `/opt/`) and reference them from `user-data` `runcmd` or a systemd unit. 6. **Unmount**, then create a **golden image** from the SD or loop device (e.g. `dd` or `dd` of the whole block device). Use that as `golden.img` on the provisioning host. ### Cloud-init file locations on the Pi - **NoCloud**: Boot partition root – `user-data`, `meta-data`, `network-config`. - Some images expect them in a subfolder `cloud-init/` or on a separate vfat partition labeled `cidata`; check your OS docs. Standard Raspberry Pi OS NoCloud uses the boot partition root. ### Per-device config (optional) NoCloud can also use a **seed** partition or **config drive**. For per-device hostname/settings you can: - Use **meta-data** `instance-id` and `local-hostname` and generate different `meta-data` per device when imaging (e.g. script that writes `meta-data` before flashing), or - Use a first-boot script that calls a provisioning server (e.g. by serial number) and applies device-specific config; cloud-init can launch that script from `runcmd`. --- ## Summary | Step | Action | |------|--------| | 1 | Build `usbboot`, install `rpiboot` on provisioning host. | | 2 | Create golden image with cloud-init `user-data`, `meta-data`, `network-config` on boot partition. | | 3 | Install `flash-emmc-on-connect.sh`, `cm4-flash-trigger.sh`, and udev rule; set `GOLDEN_IMAGE` and enable file. | | 4 | Put reTerminal in boot mode (jumper), connect USB to host; image is written automatically. | | 5 | Remove jumper, power cycle; device boots from eMMC and cloud-init runs on first boot. | This gives you automatic deployment of the golden image to eMMC when the reTerminal is in boot mode, plus first-boot configuration via cloud-init.