How the SD card image is built
Since 2025 the ethoscope image is based on Raspberry Pi OS, which supports the Raspberry Pi hardware better and ships native libcamera packages. Images made before 2025 were based on Arch Linux ARM.
Building the image
Section titled “Building the image”The image is Raspberry Pi OS Lite (64-bit), based on Debian Trixie.
-
Write Raspberry Pi OS Lite (64-bit) to an empty microSD card or SSD with Raspberry Pi Imager. You can set a user name and a host name (for example
ethoscope000) when writing. -
Start a Raspberry Pi from it, connected by Ethernet (a screen and keyboard are optional). The first boot takes a while: the system generates its SSH keys, expands to fill the card and reboots.
-
Log in (for example
ssh [email protected]), download the installation script and run it:Terminal window wget https://raw.githubusercontent.com/gilestrolab/ethoscope/refs/heads/dev/accessories/install_ethoscope_debian.shsudo bash install_ethoscope_debian.sh
The script does everything: it creates the ethoscope user (password ethoscope), clones the software into /opt/ethoscope (with origin pointing to the node’s git mirror at git://node.local and a github remote for fetching directly from GitHub), installs the device software and its systemd services, and configures MariaDB, the network and the Pi hardware. It reboots once after installing the system packages and then carries on by itself. When it has finished, the new ethoscope appears on the node as ETHOSCOPE_000.
One image for Pi 3 and Pi 4
Section titled “One image for Pi 3 and Pi 4”The camera stack differs between models: the legacy start_x.elf and bcm2835-v4l2 on the Pi 2 and 3, KMS/libcamera (vc4-kms-v3d, imx219) on the Pi 4 and 5. Images used to be built per model for this reason. Now configure_pi_camera.sh manages a single block in config.txt, and ethoscope_camera_firstboot.service runs it at every boot. It does nothing when the block already matches the Pi, and rewrites it and reboots once when a card is moved to a different model, so one image configures its own camera. The image is fully supported on the Pi 3 and Pi 4 and is named with a _pi3_pi4 tag. The 64-bit image does not boot the common ARMv7 Pi 2, and the Pi 5 has no pigpio, so its light can only be switched on and off, although its camera is configured automatically.
Light dimming: pigpio
Section titled “Light dimming: pigpio”Debian Trixie has no pigpio package, and pigpio is the only backend that dims the LED on GPIO17 without flicker: it times the PWM with the DMA engine, while the lgpio fallback is timed by the CPU and is clean only on the hardware PWM pins (12, 13, 18 and 19). The installer therefore builds pigpio v79 from source, after installing python3-setuptools and python3-full (Python 3.12 and later need them, or make install fails with No module named 'distutils'). pigpio is installed in /usr/local/bin without a service, so the installer writes pigpiod.service and runs it on port 8889, because 8888 is taken by ethoscope_update. After a boot, journalctl -u ethoscope_light -b | grep backend should report backend = pigpio.
Capturing the image
Section titled “Capturing the image”1. Sanitise the card
Section titled “1. Sanitise the card”A public image must not carry the unique details of the device it was made on. On the configured device, run:
sudo /opt/ethoscope/accessories/install_ethoscope_debian.sh --prepare-imagesudo poweroff # shut down cleanly; never pull the power--prepare-image deletes the SSH host keys (each ethoscope generates its own at first boot, with sshd-keygen.service) and clears the installation logs, shell history, package and pip caches and the journal. It does not blank /etc/machine-id: systemd reads an empty machine-id as a first boot (ConditionFirstBoot=yes), which can hang a headless start. A shared machine-id in the image does no harm, because each ethoscope generates its own when it is renamed from ETHOSCOPE_000 to its real number.
2. Capture and finish the image
Section titled “2. Capture and finish the image”Put the card in a Linux computer, unmount its partitions, then:
export DEVICE=sdc # check with lsblk: the partitions are labelled bootfs and rootfssudo umount /dev/${DEVICE}?* 2>/dev/nullsudo dd if=/dev/${DEVICE} of=$(date +%Y%m%d)_ethoscope000_pi3.img bs=4M status=progress conv=fsync
# write today's date into /etc/sdimagename and shrink the image to fit a 32 GB cardsudo /opt/ethoscope/accessories/ethoscope-image.sh --rename --shrink $(date +%Y%m%d)_ethoscope000_pi3.imgmd5sum *_ethoscope000_pi3.img > $(date +%Y%m%d)_ethoscope000_pi3.img.md5ethoscope-image.sh also has --info (inspect an image), --fsck, and --update (bring /opt/ethoscope inside an image up to date without writing it to a card).
SSD images
Section titled “SSD images”The same procedure works on an SSD connected to a Pi 4. An image captured from a small SSD (40 GB, for example) can be written to a larger one and expanded afterwards, with the Expand root filesystem setting of the ethoscope or from its command line:
sudo raspi-config --expand-rootfsConverting an older per-model image
Section titled “Converting an older per-model image”An image built for one model can be converted to serve both the Pi 3 and the Pi 4, without rebuilding:
sudo ./ethoscope-image.sh --universal-camera OLD_ethoscope000_pi3.imgThis mounts the image, brings /opt/ethoscope up to date, enables ethoscope_camera_firstboot.service, moves config.txt to the managed camera block (removing the old start_x.elf lines) and renames the image’s /etc/sdimagename tag from _piN to _pi3_pi4. Compress and upload the image again afterwards.