> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thistle.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Thistle OTA Update on Orange Pi Zero 3

> Root Filesystem A/B OTA Update using Thistle on Orange Pi Zero 3 (Allwinner H618)

<img src="https://mintcdn.com/thistletechnologies/MrBm0BC7xpW_ySdM/images/orangepiz3.png?fit=max&auto=format&n=MrBm0BC7xpW_ySdM&q=85&s=0bf51c20a39e2b0c629499c3eb75540d" alt="Orange Pi Zero 3" width="956" height="798" data-path="images/orangepiz3.png" />

This guide shows how to integrate Thistle OTA on Orange Pi Zero 3. Start with a quick file‑update path (no A/B). Optionally, prepare your image for rootfs A/B, and pre‑enable SSH/Wi‑Fi before flashing.

<Note>
  Orange Pi Zero 3 uses Allwinner H618. Community images vary. Ensure you use a Linux image with U‑Boot and device tree overlays for I²C/SPI when needed. See community pinouts and I²C overlay guidance for related boards for enabling buses when integrating peripherals.
</Note>

## Board setup (vendor/community docs)

* General Orange Pi OS preparation and flashing microSD: [Orange Pi guide – prepare microSD](https://sbc-community.org/docs/general_guides/prepare_sd_card/)

Flash the appropriate image for Zero 3 (Orange Pi OS or Armbian‑based), boot, and ensure SSH access.

## Default Credentials

Login to your Orange Pi over serial or SSH using these defaults:

* **Orange Pi OS (official):** `oem` / `oem` (default hostname: `orange-os.local`)

## Prerequisites

* Orange Pi Zero 3 with power supply and microSD card
* Linux image (Ubuntu/Debian/Armbian‑based)
* Network access
* Linux/macOS workstation

## Quick path: File updates (no A/B)

Use Thistle’s file‑update mode to ship signed files and app bundles without changing partitions. On Orange Pi OS the boot partition is mounted at `/boot`; use that as the persistent directory.

1. Download tools

* Workstation (TRH):

```bash theme={"dark"}
VER=1.5.0
curl -LO https://downloads.thistle.tech/embedded-client/$VER/trh-$VER-x86_64-unknown-linux-musl.gz
gunzip trh-$VER-x86_64-unknown-linux-musl.gz
chmod +x trh-$VER-x86_64-unknown-linux-musl && ln -sf trh-$VER-x86_64-unknown-linux-musl trh
```

* Device (TUC on Zero 3, aarch64):

```bash theme={"dark"}
# On the device shell (SSH or serial)
VER=1.5.0
curl -LO https://downloads.thistle.tech/embedded-client/$VER/tuc-$VER-aarch64-unknown-linux-musl.gz
gunzip tuc-$VER-aarch64-unknown-linux-musl.gz
chmod +x tuc-$VER-aarch64-unknown-linux-musl
sudo mv tuc-$VER-aarch64-unknown-linux-musl /usr/local/bin/tuc
tuc --help | head -n 3
```

2. Initialize and release a simple file update on the workstation

```bash theme={"dark"}
export THISTLE_TOKEN=$(cat)   # paste token, Enter, then Ctrl-D
./trh init --persist="/boot"

mkdir -p example && echo "hello from thistle" > example/app
# Install to /opt/example on device
./trh prepare --target=./example --file-base-path=/opt/example
./trh release
```

3. Copy config to device and run client

```bash theme={"dark"}
# On workstation
scp config.json oem@orange-os.local:/tmp/tuc-config.json

# On device (SSH or serial)
sudo mv /tmp/tuc-config.json /boot/tuc-config.json
sudo tuc -c /boot/tuc-config.json
```

That's it! File updates are fetched, verified, and installed atomically. Continue below only if you need rootfs A/B for boot‑time rollback.

## Optional: Verify A/B partitions (if present)

Most Orange Pi OS images do not provision A/B partitions by default. If you've already created `rootfsB` (e.g., via the headless prep below) or your image includes it, SSH in and verify partitions:

```bash theme={"dark"}
ssh oem@orange-os.local
lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT
```

Expected layout (names may vary by image):

* p1 (vfat) mounted at `/boot`
* p2 (ext4) mounted at `/`
* p3 (ext4, label often `rootfsB`) present for A/B updates

<Note>
  If `rootfsB` is missing, follow the headless image prep section below to create it (resize rootfs and add a new `rootfsB` partition), then return here to verify.
</Note>

## Optional: Headless image prep (Orange Pi OS, aarch64)

Prepare an Orange Pi OS (aarch64) image before flashing: create `rootfsB`, enable SSH, preconfigure Wi‑Fi via NetworkManager, and set hostname. Docker is optional; any live Linux works.

### 1. Download the image

```bash theme={"dark"}
mkdir -p ~/opi-img && cd ~/opi-img
# Download an Orange Pi OS aarch64 image from the vendor and name it OrangePiOS_Arch64.img
# For example, if you have OrangePiOS_Arch64-<date>.img(.xz), place/decompress it here as OrangePiOS_Arch64.img
```

### 2. Start a Linux environment in Docker

```bash theme={"dark"}
docker run --rm -it --privileged -v "$PWD":/work ubuntu:24.04 bash
```

Inside the container:

```bash theme={"dark"}
apt-get update
apt-get install -y parted e2fsprogs kpartx util-linux
cd /work
```

### 3. Expand image size & map partitions

```bash theme={"dark"}
truncate -s +8G OrangePiOS_Arch64.img
losetup -Pf --show OrangePiOS_Arch64.img   # e.g. /dev/loop0
kpartx -av /dev/loop0                      # maps /dev/mapper/loop0p1, p2
```

### 4. Resize rootfs (p2) to 6 GiB

```bash theme={"dark"}
e2fsck -f /dev/mapper/loop0p2
resize2fs /dev/mapper/loop0p2 5900M
parted -s /dev/loop0 unit GiB resizepart 2 6
e2fsck -f /dev/mapper/loop0p2
resize2fs /dev/mapper/loop0p2
```

### 5. Create & format rootfsB

```bash theme={"dark"}
parted -s /dev/loop0 -a optimal mkpart primary ext4 6GiB 100%
kpartx -a /dev/loop0
mkfs.ext4 -F -L rootfsB /dev/mapper/loop0p3
```

### 6. Enable headless SSH + Wi‑Fi

Mount rootfs:

```bash theme={"dark"}
mkdir -p /mnt/root
mount /dev/mapper/loop0p2 /mnt/root
```

#### a) Enable SSH at boot

```bash theme={"dark"}
ln -sf /usr/lib/systemd/system/sshd.service \
  /mnt/root/etc/systemd/system/multi-user.target.wants/sshd.service
```

#### b) Add your SSH key

```bash theme={"dark"}
mkdir -p /mnt/root/home/oem/.ssh
chmod 700 /mnt/root/home/oem/.ssh
echo 'ssh-ed25519 AAAA...you@host' >> /mnt/root/home/oem/.ssh/authorized_keys
chmod 600 /mnt/root/home/oem/.ssh/authorized_keys
chroot /mnt/root chown -R oem:oem /home/oem/.ssh || true
```

#### c) Configure Wi‑Fi (NetworkManager profile)

```bash theme={"dark"}
mkdir -p /mnt/root/etc/NetworkManager/system-connections
cat > /mnt/root/etc/NetworkManager/system-connections/headless.nmconnection <<'EOF'
[connection]
id=headless
type=wifi
autoconnect=true

[wifi]
ssid=YOUR_SSID
mode=infrastructure

[wifi-security]
key-mgmt=wpa-psk
psk=YOUR_PASSWORD

[ipv4]
method=auto

[ipv6]
method=auto
EOF
chmod 600 /mnt/root/etc/NetworkManager/system-connections/headless.nmconnection

# Ensure NetworkManager is enabled
ln -sf /usr/lib/systemd/system/NetworkManager.service \
  /mnt/root/etc/systemd/system/multi-user.target.wants/NetworkManager.service
```

#### d) Set hostname (optional)

```bash theme={"dark"}
echo "orangepi" > /mnt/root/etc/hostname
```

Unmount:

```bash theme={"dark"}
umount /mnt/root
```

### 7. Cleanup

```bash theme={"dark"}
kpartx -dv /dev/loop0
losetup -d /dev/loop0
exit
```

### 8. Flash to SD (on macOS host)

```bash theme={"dark"}
diskutil list                         # locate your SD (e.g., /dev/disk3)
diskutil unmountDisk /dev/diskN       # replace diskN with your SD identifier
sudo dd if=OrangePiOS_Arch64.img of=/dev/rdiskN bs=4m status=progress
sync
```

### 9. First boot

* Partitions:
  * p1 → boot (FAT)
  * p2 → rootfs (6 GiB ext4)
  * p3 → rootfsB (ext4, label `rootfsB`)
* SSH enabled automatically
* Wi‑Fi auto‑connects to your SSID
* Hostname set to `orangepi` (or your choice)

Login:

```bash theme={"dark"}
ssh oem@orangepi.local
# or: ssh oem@<board-ip>
```

## Thistle Tools

On the workstation and the device, download TRH/TUC as appropriate, then export your Project Access Token on the workstation.

```bash theme={"dark"}
# Workstation: TRH (prepare/release)
VER=1.5.0
curl -LO https://downloads.thistle.tech/embedded-client/$VER/trh-$VER-x86_64-unknown-linux-musl.gz
gunzip trh-$VER-x86_64-unknown-linux-musl.gz
chmod +x trh-$VER-x86_64-unknown-linux-musl && ln -sf trh-$VER-x86_64-unknown-linux-musl trh

# Device: TUC (install)
ssh oem@orange-os.local 'VER=1.5.0; curl -LO https://downloads.thistle.tech/embedded-client/$VER/tuc-$VER-aarch64-unknown-linux-musl.gz && gunzip tuc-$VER-aarch64-unknown-linux-musl.gz && chmod +x tuc-$VER-aarch64-unknown-linux-musl && sudo mv tuc-$VER-aarch64-unknown-linux-musl /usr/local/bin/tuc && tuc --help | head -n 3'

# Workstation: Project token for TRH
export THISTLE_TOKEN=$(cat)
```

## Initialize and Prepare Rootfs Release

```bash theme={"dark"}
./trh init --persist="/boot"
./trh prepare --target=myrootfs.img
./trh release
```

## Device Configuration

Create `tuc-config.json`:

```json theme={"dark"}
{
  "name": "opi_zero3",
  "persistent_directory": "/boot",
  "public_keys": ["<YOUR_PUBLIC_KEY>"],
  "bootloader": "Thistle-U-Boot",
  "part_a": "/dev/mmcblk0p2",
  "part_b": "/dev/mmcblk0p3"
}
```

<Note>
  On Orange Pi OS, the boot chain typically uses U‑Boot; set `bootloader` to `Thistle-U-Boot`. Verify device nodes with `lsblk` and adjust `part_a`/`part_b` if yours differ.
</Note>

Copy configuration to the board and persist under `/boot`:

```bash theme={"dark"}
scp tuc-config.json oem@orange-os.local:/tmp/
ssh oem@orange-os.local 'sudo cp /tmp/tuc-config.json /boot/tuc-config.json'
```

## Run Update

```bash theme={"dark"}
ssh oem@orange-os.local
sudo tuc -c /boot/tuc-config.json
```

The device installs to the inactive slot and reboots. After reboot, run the client again to latch‑in the update.

## Notes and Tips

* Verify which storage node your rootfs partitions use (`/dev/mmcblk0p2`, `/dev/mmcblk0p3`).

## References

* Orange Pi Zero pin reference and I²C notes (community): [Armbian forum – pinout](https://forum.armbian.com/topic/28674-text-version-of-orange-pi-zero-pinout/), [I²C overlay discussion for Zero 2W/Zero 3](https://forum.armbian.com/topic/41365-orange-pi-zero-2w-overlay-i2c-spi-help/)
* General OS flashing: [Prepare microSD](https://sbc-community.org/docs/general_guides/prepare_sd_card/)
