Table of Contents

Ghost Tree BeagleBones (unit1–unit5)

Summary of how the tree BeagleBone Blacks are set up (October 2026), with the low-level details needed to change or undo things in the field.

Overview

Each unit is a BeagleBone Black (Debian 10 Buster, BeagleBoard.org IoT image 2020-04-06, kernel 4.19) with a USB C-Media sound card and one LED strip.

Signal chain:

mic -> USB card (hw:1,0) -> jackd -> Pd 0.55 (effectTry4.pd) -> speaker
                                       |
                                       +-- [netreceive] TCP 9990 --> ww-led --> OPC over UDP 7891 --> ledscape (opc-server) --> LEDs
Unit IP Notes
unit1 10.42.0.61 flasher image made from this unit
unit2 (to do) spare card ready (has rec/)
unit3 (to do) spare card ready (has rec/)
unit4 10.42.0.64 was “unit2” on .62; reference unit; has ShellHub (chameleons-bb)
unit5 (to do) spare card ready (has rec/)

Login: debian with an SSH key. sudo asks for the debian password. Gateway 10.42.0.1, DNS 8.8.8.8 and 1.1.1.1.

Setting up the remaining units (unit2, unit3, unit5)

Everything is done from a PC by ~/treeSetup/setup-tree.sh.

What the PC needs

Steps per unit

  1. Test the microSD card on any running unit before formatting it (this erases the card):
    sudo bash ~/test-sdcard.sh

    The result must be OK; fake cards fail here. On a PC with a card reader, use sudo bash test-sdcard.sh /dev/sdX from the treeSetup folder (find the device with lsblk). It refuses non-removable disks and asks you to type yes.

  2. Format it:
    sudo bash ~/setup-sdcard.sh format
  3. Cards formatted before 2026-10-24 have no rec/ folder. setup-tree.sh creates it on the inserted card; for a spare card, reformat it or run sudo bash ~/recordings-subfolder.sh with it inserted.
  4. Put the card in the new unit, connect it to the network and power it up. Find its DHCP address (DHCP leases, ip neigh, or connmanctl services on its console).
  5. On the PC:
    cd treeSetup && ./setup-tree.sh

    It asks for:

    • the current address
    • the unit name (unit1 to unit5)
    • the new static IP
    • netmask, gateway and DNS (Enter accepts the defaults)
  6. Enter the debian password (first time, for ssh-copy-id) and the sudo password (once).
  7. The script reboots the unit onto its new IP. It waits up to about 6.5 min for jack, Pd and ww-led, then prints a report. Check the report for:
    • all services active
    • ww-led connected to Pd
    • recordings: microSD card /dev/mmcblk0p1[/rec]
    • fstrim.timer: enabled
    • rsyslog inactive, /tmp tmpfs, commit=60
    • RemoveIPC=no ok and fsck.repair=yes ok
    • the crontab, and reboot rule ok

The script stops if ~/dev/ww-led on the unit is not at git commit 400f44de..., so it never overwrites an unknown version. The ww-led files it replaces are first saved as *.orig.

What setup-tree.sh does

As debian:

As root (~/setup-tree/root-setup.sh):

Alternative: start from the unit1 image

~/chameleon-ghostTree-test-2026-10-23.img.xz (914 MB) is an eMMC flasher made with BeagleBoard's /opt/scripts/tools/eMMC/beaglebone-black-make-microSD-flasher-from-eMMC.sh. It fits any card of 8 GB or more.

  1. Write it to a card, replacing sdX with the card's device (check with lsblk):
    xzcat chameleon-ghostTree-test-2026-10-23.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
  2. Insert the card in the unit and power on. It rewrites the eMMC (10–20 min); the LEDs stop chasing when it's done. Remove the card before the next boot, or it flashes again.
  3. Always run setup-tree.sh on a flashed unit. The flasher writes a brand-new minimal /etc/fstab onto the eMMC (_generate_fstab in /opt/scripts/tools/eMMC/functions.sh). A freshly flashed unit therefore has no card mount (recordings go to the eMMC), no commit=60 and no /tmp in RAM. The image also predates the 2026-10-24 changes: 80/60 pruning, weekly TRIM and 100 LEDs. setup-tree.sh puts all of that back, so the image itself doesn't need updating for those.
  4. The clone is a copy of unit1 on DHCP. Give it its own identity:
    sudo rm /etc/ssh/ssh_host_* && sudo dpkg-reconfigure openssh-server
    sudo rm /etc/machine-id && sudo systemd-machine-id-setup
  5. Run setup-tree.sh to set its name and IP.

How the image was made from the 116 GB card it was written to (shrunk so it fits small cards):

sudo umount /dev/sda1
sudo e2fsck -f /dev/sda1
sudo resize2fs /dev/sda1 4G
echo ',4G' | sudo sfdisk -N 1 /dev/sda
sudo dd if=/dev/sda bs=1M count=4100 status=progress | xz -T0 > chameleon-ghostTree-test-2026-10-23.img.xz

Field reference

Services

systemctl status jackdaudio jackPlumbing doTheTree ww-led ledscape
journalctl -u doTheTree -b          # Pd output for this boot
sudo systemctl restart doTheTree    # restart Pd only
sudo systemctl restart jackdaudio jackPlumbing doTheTree   # restart the audio chain
JACK_NO_START_SERVER=1 jack_lsp -c  # jack ports and connections
ss -tn | grep 9990                  # is ww-led connected to Pd?

Always set JACK_NO_START_SERVER=1 when running jack tools by hand. Without it, a jack client can start its own temporary server when jackd isn't reachable.

Pd

Start command (in ~/bin/doTheTree.sh):

/home/debian/installs/pd-0.55-0/bin/pd -jack -nogui -send "sensitivity 35" -send "wet 0.6" -send "windowSize 2048" -send "micNoisegate 60" -send "exponent 2" -path /home/debian/ghostTree -path /home/debian/ghostTree/iemlib -stderr effectTry4.pd

To run Pd by hand to see its messages:

sudo systemctl stop doTheTree
cd ~/ghostTree && /home/debian/installs/pd-0.55-0/bin/pd -jack -nogui ...same args... effectTry4.pd
sudo systemctl start doTheTree      # when done

LED count and frame rate

Number of LEDs (two places, must match)

Show the current settings:

grep '^trunk' ~/dev/ww-led/python/config.py
grep -E 'ledsPerStrip|usedStripCount' /etc/ledscape-config.json

Set N LEDs (example: 100):

N=100
sed -i -E "s/^trunk = \[0, [0-9]+\]/trunk = [0, $((N-1))]/" ~/dev/ww-led/python/config.py
sudo sed -i -E "s/(\"ledsPerStrip\":\s*)[0-9]+/\1$N/" /etc/ledscape-config.json
sudo systemctl restart ledscape ww-led

setup-tree.sh sets 100 on new units: LEDS=100 in its root part and trunk in payload/ww-led/python/config.py. The original ledscape config is kept as /etc/ledscape-config.json.orig.

Frame rate

The rate the LEDs change is set by Pd, with a cap in ww-led. ledscape has no frame-rate setting; it smooths between frames itself (enableInterpolation).

Change Pd's rate (edit -send "windowSize 2048" in the Pd start line):

sed -i 's/-send "windowSize [0-9]*"/-send "windowSize 1024"/' ~/bin/doTheTree.sh
sudo systemctl restart doTheTree

Change ww-led's cap:

sed -i -E 's/^TARGET_FPS = [0-9]+/TARGET_FPS = 30/' ~/dev/ww-led/python/config.py
sudo systemctl restart ww-led

Show the current settings:

grep -o 'windowSize [0-9]*' ~/bin/doTheTree.sh
grep '^TARGET_FPS' ~/dev/ww-led/python/config.py

To change these on future units too, make the same edits in treeSetup/payload/bin/doTheTree.sh and treeSetup/payload/ww-led/python/config.py.

Hostname

sudo sed -i "s/\bunit2\b/unit4/g" /etc/hosts     # old name -> new name in the 127.0.1.1 line
sudo hostnamectl set-hostname unit4

Change /etc/hosts first. Otherwise sudo prints “unable to resolve host” (harmless).

Static IP (connman)

connmanctl services          # shows e.g. ethernet_647060b02dc3_cable (the MAC without colons)
sudo connmanctl config ethernet_647060b02dc3_cable --ipv4 manual 10.42.0.64 255.255.255.0 10.42.0.1 --nameservers 8.8.8.8 1.1.1.1
sudo connmanctl config ethernet_647060b02dc3_cable --ipv4 dhcp     # back to DHCP

The change takes effect immediately, so an SSH session drops. Close it with Enter, then ~.. The setting is stored per MAC address, so a cloned eMMC on another board starts on DHCP.

Logging

Logs are kept in RAM only and are lost at reboot:

Read the logs with journalctl (-b for this boot, -u SERVICE, -f to follow).

Persistent logging, for debugging something that happens across reboots:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journalctl --list-boots          # after a reboot: previous boots are now there
journalctl -b -1                 # previous boot

Undo it (back to RAM only):

sudo rm -rf /var/log/journal && sudo systemctl restart systemd-journald

To bring back rsyslog and text files in /var/log, use reduce-writes.sh undo (next section) or only:

sudo rm /etc/systemd/journald.conf.d/no-syslog.conf
sudo systemctl restart systemd-journald
sudo systemctl enable --now rsyslog

Files that survive reboots anyway:

eMMC write reduction (reduce-writes.sh)

sudo bash ~/reduce-writes.sh        # apply (setup-tree does this)
sudo bash ~/reduce-writes.sh undo   # put everything back

What it changes:

After undo, /tmp returns to disk at the next reboot.

Possible later step: overlayroot (read-only root with a RAM overlay). The rcn-ee package exists for Buster, and the kernel and initramfs support it. It's not installed.

Power loss

jack: RemoveIPC fix

/etc/systemd/logind.conf.d/keep-ipc.conf:

[Login]
RemoveIPC=no

By default, when the last login session of user debian ends (for example an SSH logout), systemd-logind deletes all shared memory and semaphores owned by debian. jackd also runs as debian, so this broke jack: “JACK semaphore error”, “jack_shm_lock_registry fails”, and no sound. Never remove this file. It needs a reboot to take effect.

Jack watchdog

~/bin/restartIfJackIsJacked.sh runs from debian's crontab every 5 min. It reboots the unit if:

It logs the reason to ~/jack-reboots.log. To reboot without a password it relies on /etc/sudoers.d/reboot-debian:

debian ALL=(root) NOPASSWD: /sbin/reboot

To pause it while debugging (otherwise stopping jackd by hand can get the unit rebooted):

crontab -e       # put # in front of the restartIfJackIsJacked.sh line; remove it when done

Recordings and microSD cards

Dataplicity

Removed from all units (supervisor purged, /opt/dataplicity and the dataplicity user deleted). Nothing to undo.

ShellHub (unit4 only)

Other gotchas