167 lines
8.3 KiB
Markdown
167 lines
8.3 KiB
Markdown
# eMMC provisioning for reTerminal DM4 (CM4)
|
||
|
||
This guide covers:
|
||
|
||
1. **USB boot mode**: When the reTerminal is in boot mode (eMMC disable jumper) and connected via USB, the host runs **rpiboot** to expose the eMMC, then the **dashboard** shows "Device connected (USB)". You choose **Backup** or **Deploy** in the portal — there is no auto-flash; the action runs only after your choice.
|
||
2. **Network boot**: 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.
|
||
3. **Cloud-init**: The golden image can include cloud-init so each device configures itself on first boot (hostname, network, packages, kiosk setup).
|
||
|
||
---
|
||
|
||
## Part 1: USB boot mode — detect device, choose Backup or Deploy in portal
|
||
|
||
### 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 the **provisioning script** that:
|
||
1. Runs **rpiboot** (from the `usbboot` project). The CM4 then exposes its eMMC as a USB mass-storage device.
|
||
2. Finds the new block device (eMMC) and writes status so the **dashboard** shows "Device connected (USB boot mode). Choose Backup or Deploy in the dashboard."
|
||
3. **Waits for your choice in the portal** — no automatic flash. When you click **Backup** or **Deploy** in the dashboard, the script runs that action (dd backup or dd deploy).
|
||
- You remove the jumper and power cycle; the reTerminal boots from eMMC and can run **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 provisioning 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 provisioning (no device detection), remove that file: `sudo rm /etc/cm4-provisioning/enabled`.
|
||
|
||
#### 6. Optional: pass environment into the provisioning 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.
|