Installation on RaspberryPi Trixie (64)

This documents steps to running a prebuilt Exercise 306 binary on a Raspberry Pi 4B or Pi Zero 2W. You will need a second instance to hand shake with, which can be another ARM64 Pi, an AMD64 Linux host, or a T-Beam (ESP32-S).

New SD Card (Optional)

This section is optional if you already have Trixie running on a Raspberry Pi 4B or Raspberry Pi Zero 2W. This install guide has not been tested on other model.

This assume you know where your SD Card is exposed as a device and in this example, the card is available as /dev/sdf.

 sudo dd if=/home/jlpoole/Downloads/RaspberryPi/2026-04-21-raspios-trixie-arm64-lite.img of=/dev/sdf bs=4M status=progress conv=fsync

Example:

jlpoole@jp ~ $ date; time sudo dd if=/home/jlpoole/Downloads/RaspberryPi/2026-04-21-raspios-trixie-arm64-lite.img of=/dev/sdf bs=4M status=progress conv=fsync
Fri May 22 09:32:34 PDT 2026
2738880512 bytes (2.7 GB, 2.6 GiB) copied, 105 s, 26.1 MB/s3229614080 bytes (3.2 GB, 3.0 GiB) copied, 105.792 s, 30.5 MB/s
 
770+0 records in
770+0 records out
3229614080 bytes (3.2 GB, 3.0 GiB) copied, 140.443 s, 23.0 MB/s
 
real    2m20.497s
user    0m0.008s
sys     0m0.009s
jlpoole@jp ~ $ 

1. Confirm the target image is 64-bit ARM

Your binary is:

ELF 64-bit LSB pie executable, ARM aarch64
interpreter /lib/ld-linux-aarch64.so.1

So your receiving Pi image must be 64-bit Raspberry Pi OS / Debian Trixie, not 32-bit.

uname -m
getconf LONG_BIT

Expected:

aarch64
64

2. Install runtime packages

BlueZ is the official Linux Bluetooth protocol stack and supplies Bluetooth daemons/tools on Debian Trixie. The rfkill package is useful because blocked Bluetooth is a common fresh-image problem. For Raspberry Pi Bluetooth firmware, Debian’s bluez-firmware package specifically notes Broadcom BCM203x and Raspberry Pi chipset support.

sudo apt update

sudo apt install -y \
  bluez \
  bluez-firmware \
  rfkill \
  bluetooth \
  libglib2.0-0t64 \
  libstdc++6 \
  libc6 \
  zlib1g \
  libmount1 \
  libselinux1 \
  libffi8 \
  libatomic1 \
  libpcre2-8-0 \
  util-linux

3. Enable and start Bluetooth

Raspberry Pi OS uses systemd.

sudo systemctl enable bluetooth
sudo systemctl start bluetooth
sudo systemctl status bluetooth --no-pager

Then unblock the radio:

sudo rfkill unblock bluetooth
rfkill list

You want Bluetooth to show:

Soft blocked: no
Hard blocked: no

4. Verify BlueZ sees the adapter

bluetoothctl list
bluetoothctl show

A quick noninteractive check:

bluetoothctl show | egrep 'Controller|Powered|Discoverable|Discovering'

If Powered: no, run:

bluetoothctl power on

The bluetoothctl tool works with both classic Bluetooth and BLE controllers, and is the standard CLI check for BlueZ state.

5. Copy the binary

For Pi-to-Pi exchange, prefer the dual-role binaries. The filename should identify the platform, role mode, and poem compiled into the executable:

microreticulum_306_rpi_arm64_dual_little_boy_blue
microreticulum_306_rpi_arm64_dual_children

Dual binaries default to --ble-dual-policy=first-path-wins: both BlueZ roles start, but the first working peer path stops the opposite role. Normal runs leave frame tracing off. To reproduce the verbose diagnostic trace, add --ble-frame-log. To reproduce the older experimental dual-path behavior, run with --ble-dual-policy=both.

Example scp command to a new server named "trixie1":

scp microreticulum_306_rpi_arm64_dual_children trixie1:~

6. Verify shared libraries before first run

ldd ./microreticulum_306_rpi_arm64_dual_children | tee ldd_$(date +%Y%m%d_%H%M).txt

Example:

jlpoole@trixie1:~ $ ldd ./microreticulum_306_rpi_arm64_dual_children | tee ldd_$(date +%Y%m%d_%H%M).txt
        linux-vdso.so.1 (0x0000007f9cb5c000)
        libgio-2.0.so.0 => /lib/aarch64-linux-gnu/libgio-2.0.so.0 (0x0000007f9c830000)
        libgobject-2.0.so.0 => /lib/aarch64-linux-gnu/libgobject-2.0.so.0 (0x0000007f9c7a0000)
        libglib-2.0.so.0 => /lib/aarch64-linux-gnu/libglib-2.0.so.0 (0x0000007f9c610000)
        libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000007f9c3a0000)
        libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000007f9c2f0000)
        libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000007f9c2b0000)
        libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000007f9c0f0000)
        libgmodule-2.0.so.0 => /lib/aarch64-linux-gnu/libgmodule-2.0.so.0 (0x0000007f9c0c0000)
        libz.so.1 => /lib/aarch64-linux-gnu/libz.so.1 (0x0000007f9c080000)
        libmount.so.1 => /lib/aarch64-linux-gnu/libmount.so.1 (0x0000007f9bfe0000)
        libselinux.so.1 => /lib/aarch64-linux-gnu/libselinux.so.1 (0x0000007f9bf80000)
        /lib/ld-linux-aarch64.so.1 (0x0000007f9cb20000)
        libffi.so.8 => /lib/aarch64-linux-gnu/libffi.so.8 (0x0000007f9bf50000)
        libatomic.so.1 => /lib/aarch64-linux-gnu/libatomic.so.1 (0x0000007f9bf20000)
        libpcre2-8.so.0 => /lib/aarch64-linux-gnu/libpcre2-8.so.0 (0x0000007f9be60000)
        libblkid.so.1 => /lib/aarch64-linux-gnu/libblkid.so.1 (0x0000007f9bde0000)
jlpoole@trixie1:~ $ 

The pass/fail test is simple:

ldd ./microreticulum_306_rpi_arm64_dual_children | grep 'not found'

No output is good.

Also document:

file ./microreticulum_306_rpi_arm64_dual_children

Expected:

ELF 64-bit LSB pie executable, ARM aarch64, dynamically linked ... stripped

7. Run a smoke test

First run in foreground:

Expected healthy startup:

jlpoole@trixie1:~ $ ./microreticulum_306_rpi_arm64_dual_children
Exercise 306 native BLE file transfer console
Node=Node-PIZERO2-DUAL
BLE dual policy=first-path-wins
BLE frame log=off
Selected file=children.txt bytes=1422 chunk=32 interval_ms=500 repeat_rest_ms=10000
[ustore] Initializing PosixFileSystem
[ustore] WARNING: FlashFSFileSystem check failed, reformatting!
BLE linux-central: adapter=/org/bluez/hci0 label=Node-PIZERO2-DUAL
BLE linux-central: scanning for Reticulum service
BLE linux-peripheral: adapter=/org/bluez/hci0 label=Node-PIZERO2-DUAL service=37145b00-442d-4a94-917f-8f42c5da28e3
BLE linux-peripheral: advertising Reticulum service; waiting for central
Local SINGLE destination: dcf5338564d2cf21bfa86f1f2a9a89a3
microReticulum ready; OLED skipped on host native build
BLE linux-dual waiting for peer
BLE linux-dual waiting for peer

When one role wins, expect a line like:

BLE dual policy first-path-wins: peripheral path won peer=:1.42; stopping central role

or:

BLE dual policy first-path-wins: central path won peer=/org/bluez/hci0/dev_D8_3A_DD_1D_CF_B5; stopping peripheral role

With --ble-frame-log, the native BLE interfaces emit BLE-FRAME lines for Reticulum frame tracing. Those lines include timestamp, node label, BLE role, peer path/address, BlueZ object path, interface pointer, packet length, and the first packet byte. When you have a second unit running, your console will show something like:

[Startup stuff]
...
BLE linux-dual waiting for peer
BLE linux-dual waiting for peer
BLE linux-central: peer candidate path=/org/bluez/hci0/dev_B8_27_EB_82_B2_4C name=RNS-Node-PIZERO1-DUAL
BLE linux-central: connected to /org/bluez/hci0/dev_B8_27_EB_82_B2_4C
BLE linux-central: notifications active; identity handshake sent
2026-05-22 18:24:37 [ERR] Failed to add destination 5aec1dc0939292aa8113cb8661bf06c3 to path table!
RX ANNOUNCE: label=Node-PIZERO1-DUAL hash=5aec1dc0939292aa8113cb8661bf06c3
TX ANNOUNCE: Node-PIZERO2-DUAL
LINK ACTIVE: inbound peer=Node-PIZERO1-DUAL link_id=7993e8fb37845c2f9d1e54ad3d5e2457

RX FILE BEGIN: from=Node-PIZERO1-DUAL file=little_boy_blue.txt bytes=942 chunks=30 crc=8FFFB95D
TX FILE BEGIN: round=1 file=children.txt bytes=1422 chunks=45 crc=C9E80D8B
The little toy dog is covered with dust,
   But sturdy and staunch he stands;
 And the little toy soldier is red with rust,
   And his musket molds in his hands.
Time was when the little toy dog was new,
   And the soldier was passing fair;
And that was the time when our Little Boy Blue
   Kissed them and put them there.

...
What has become of our Little Boy Blue,
   Since he kissed them and put them there. 
RX FILE END: from=Node-PIZERO1-DUAL file=little_boy_blue.txt received=942/942 chunks=30/30 crc=8FFFB95D status=OK
TX FILE END: round=1 file=children.txt bytes=1422 chunks=45 crc=C9E80D8B next_round_in_ms=10000

RX FILE BEGIN: from=Node-PIZERO1-DUAL file=little_boy_blue.txt bytes=942 chunks=30 crc=8FFFB95D
The little toy dog is covered with dust,
   But sturdy and staunch he stands;
 And the little toy soldier is red with rust,
   And his musket molds in his hands.
Time was when the little toy dog was new,
   And the soldier w^CBLE linux-central: disconnected; local stop

Stopped
jlpoole@trixie1:~ $ # 

The [ustore] WARNING: FlashFSFileSystem check failed, reformatting! line is not necessarily fatal in the native build, but the program must be run from a writable directory, because it appears to initialize local storage.

9. Permission troubleshooting

If the program cannot talk to BlueZ over D-Bus or cannot power/scan the adapter, test once with sudo:

sudo ./microreticulum_306_rpi_arm64_dual_children

If sudo works but the normal user fails, document that this is a BlueZ/D-Bus permission issue, not a missing-library issue. The clean fix may be a D-Bus policy rule for your program’s BlueZ access, but for the first howto I would keep the instruction conservative:

sudo usermod -aG bluetooth,netdev "$USER"
newgrp bluetooth

Then log out and back in.

10. Common failure checks

Is the adapter present?

hciconfig -a 2>/dev/null || bluetoothctl list

Is Bluetooth blocked?

rfkill list

Is the daemon running?

systemctl status bluetooth --no-pager

Does BlueZ respond?

bluetoothctl show

Are libraries missing?

ldd ./microreticulum_306_rpi_arm64_dual_children | grep 'not found'

Is the binary executable?

ls -l ./microreticulum_306_rpi_arm64_dual_children

Note: on an initial run, the binary will create a local Reticulum credential file transport_identity in the directory where you run the binary from. Example:

jlpoole@trixie1:~ $ ls -la transport_identity 
-rw-r--r-- 1 jlpoole jlpoole 64 May 22 18:21 transport_identity
jlpoole@trixie1:~ $