Raspberry Pi Setup

In order to standardize between units, much of the Pi setup is automated or semi-automated. This guide will walk you through the steps of setting up your Pi the way we do. Along the way, there are also links for more information on how to customize this setup. This is an area where you will almost certainly need to customize some aspects of the setup.

Initial setup with cloud-init

Setup of the Raspberry Pi is semi-automated using cloud-init.

Cloud-init customization

The cloud-init setup is controlled by two files: user-data and network-config. (You’ll use these files a couple of steps down.)

Examples of each are shown below, but you will likely need to modify these to suit your purpose. We have pages on how to customize user-data and network-config.

user-data Example

#cloud-config

# This is the user-data configuration file for cloud-init.
# The cloud-init documentation has more details:
#
# https://cloudinit.readthedocs.io/

system_info:
  default_user:
    name: ubuntu # Allow the default user to shutdown or reboot the system without entering a password (used by our automated scripts)
    sudo: "ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown"

# On first boot, set the (default) ubuntu user's password to "cryosphere"
chpasswd:
  expire: false
  list:
  - ubuntu:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1

# Enable password authentication with the SSH daemon
ssh_pwauth: true

# Set a default timezone
timezone: Etc/UTC

## On first boot, use ssh-import-id to give the specific users SSH access to
## the default user
ssh_import_id:
- gh:thomasteisberg
- gh:albroome
- gh:dfxmay

## Update apt database and upgrade packages on first boot
package_update: true
package_upgrade: true

## Install additional packages on first boot
packages:
- net-tools
- git
- cmake
- g++
- mosh
- exfat-fuse
- i2c-tools
- rpi.gpio-common
- util-linux-extra

## Write arbitrary files to the file-system
write_files:
- path: /home/ubuntu/initial_setup.sh
  content: |
    #!/bin/bash
    exec > >(tee -a "initial_setup_output.log") 2>&1
    # Miniconda Setup
    wget --progress=bar:force:noscroll "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh" -O $HOME/miniconda.sh
    bash $HOME/miniconda.sh -b -p $HOME/miniconda
    cd $HOME
    source .profile
    source miniconda/etc/profile.d/conda.sh
    conda init bash
    # Setup logger environment
    git clone git@github.com:thomasteisberg/uav_radar_logger.git
    # Clone uhd_radar repo
    git clone git@github.com:radioglaciology/uhd_radar.git
    cd uhd_radar
    #git checkout thomas/dask # Uncomment if you want to check out a specific branch other than main
    conda env create -n uhd -f environment-rpi.yaml
    conda activate uhd
    python /home/ubuntu/miniconda/envs/uhd/lib/uhd/utils/uhd_images_downloader.py
    systemctl --user enable radar.service
    systemctl --user enable logger.service
    ifconfig
    sudo reboot    
  append: true
- path: /home/ubuntu/.profile
  content: |
    PATH=/home/ubuntu/miniconda/bin:$PATH
    source /home/ubuntu/.bashrc    
  append: true
- path: /home/ubuntu/.ssh/known_hosts
  content: |
    github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
    github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
    github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=    
- path: /etc/security/limits.conf # Recommended by Ettus https://kb.ettus.com/USRP_Host_Performance_Tuning_Tips_and_Tricks
  content: |
    ubuntu    - rtprio    99    
  append: true
- path: /etc/systemd/user/radar.service
  content: |
    [Unit]
    Description=Service to run the radar code on startup

    [Service]
    Type=simple
    WorkingDirectory=/home/ubuntu/uhd_radar/
    ExecStart=/home/ubuntu/uhd_radar/manager/radar_service.sh

    Restart=always
    RestartSec=10

    KillSignal=SIGINT

    [Install]
    WantedBy=default.target    
- path: /etc/systemd/user/logger.service
  content: |
    [Unit]
    Description=Service to log data from I2C sensors and automatically shutdown below a voltage threshold

    [Service]
    Type=simple
    WorkingDirectory=/home/ubuntu/uav_radar_logger/
    ExecStart=/home/ubuntu/uav_radar_logger/logger_service.sh

    Restart=always
    RestartSec=60

    KillSignal=SIGINT

    [Install]
    WantedBy=default.target    

# Run arbitrary commands at rc.local like time
# These commands are run with root permissions
# If you want commands run as a normal user, put them in initial_setup.sh (see above)
# which is run as the "ubuntu" user (see below)
runcmd:
- chown -R ubuntu:ubuntu /home/ubuntu
- chmod +x /home/ubuntu/initial_setup.sh
- wget -O /etc/udev/rules.d/uhd-usrp.rules https://raw.githubusercontent.com/EttusResearch/uhd/master/host/utils/uhd-usrp.rules
- usermod -a -G i2c ubuntu
- usermod -a -G dialout ubuntu
- usermod -a -G tty ubuntu
- apt remove -y modemmanager
- systemctl stop serial-getty@ttyS0.service && systemctl disable serial-getty@ttyS0.service
- i2cdetect -y 1
- echo "dtoverlay=i2c-rtc,pcf8523" >> /boot/firmware/config.txt
- loginctl enable-linger ubuntu
- mkdir /media/ssd
- chown ubuntu /media/ssd
- chgrp ubuntu /media/ssd
- echo "/dev/sda2  /media/ssd  exfat  defaults,nofail,uid=1000,gid=1000  0  2" | tee -a /etc/fstab

network-config Example

# This file contains a netplan-compatible configuration which cloud-init will
# apply on first-boot (note: it will *not* update the config after the first
# boot). Please refer to the cloud-init documentation and the netplan reference
# for full details:
#
# https://cloudinit.readthedocs.io/en/latest/topics/network-config.html
# https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v2.html
# https://netplan.io/reference

version: 2
ethernets:
  eth0:  # Your ethernet name.
    dhcp4: no
    addresses: [192.168.11.137/24]
    gateway4: 192.168.11.1
    nameservers:
      addresses: [8.8.8.8,8.8.4.4]
wifis:
  renderer: networkd
  wlan0:
    dhcp4: true
    optional: true
    access-points:
      "<YOUR WIFI SSID HERE>":
        password: "<YOUR WIFI PASSWORD HERE>"

Imaging your Pi

To start, download the Raspberry Pi Imager tool (or use your preferred software for imaging SD cards). On Ubuntu, you can install it like this:

sudo apt install rpi-imager

For other operating systems, see the website.

Launch Imager. After clicking on “Choose OS,” navigate through the general purpose category to find Ubuntu Server 22.04.xx LTS 64-bit. 64-bit is important – 32-bit will not work.

You want Ubuntu Server 22.04 LTS 64-bit.

Insert your MicroSD card and select it as the location to write to.

After imaging is complete, you will see two drives mounted: writable and system-boot.

Copying cloud-init config files

After customizing the user-data and network-config files (see above), copy user-data and network-config to the system-boot volume, replacing the existing files.

Eject the microSD and put it back in the Pi.

Running cloud-init

Power up the Pi and wait for cloud-init to run.

Within about a minute, your Pi should connect to whatever network interface(s) are described in network-config and you should be able to find it on the network. If you setup some sort of key-based authentication (such as by importing a key from GitHub), it may take an extra couple of minutes for this to be ready.

After the network setup is complete, you should be able to login over SSH.

For instructions on SSH-ing into your Pi, see here. In particular, please note that you need to have SSH agent forwarding working. Instructions for this are on that page.

When you first login, cloud-init may not have finished running. To check the status, run:

cloud-init status --long

There are also logs in /var/log/cloud-init-output.log.

To keep an eye on the entire process, you can run:

watch "cloud-init status --long && tail -n 10 /var/log/cloud-init-output.log"

Expect this process to take a few minutes to complete.

Running initial_setup.sh

After the cloud-init process is complete, you’ll also need to run initial_setup.sh:

./initial_setup.sh

This will log to /home/ubuntu/initial_setup_output.log. It may take around 10 minutes to complete. It will automatically reboot your Pi at the end. If you don’t want this, feel free to comment out the last line.

Setting up the logger service

More details on the logging service will eventually be here.

For now, if you’re building a Peregrine system, the default setup should be fine.

If you’re building Eyas, first run this command to create a place for logs:

mkdir /media/ssd/logger

And then modify the last line of /home/ubuntu/uav_radar_logger/logger_service.sh to look like this:

python -u logger.py --shutdown-voltage 11.8 --log-dir /media/ssd/logger/

Replacing 11.8 with an appropriate threshold (in volts) at which to shutdown.

Setting up the radar service

More details on the radar service will eventually be here.

For now, if you’re building a Peregrine system, the default setup should be fine.

If you’re building Eyas, run these commands to create a location for logging data and update the default configuration:

mkdir /media/ssd/radar
cd /home/ubuntu/uhd_radar/config
mv default.yaml default-old.yaml
cp default-eyas.yaml default.yaml

Using overlayroot to protect the file system

Let’s take a quick step back and talk about the problem before we talk about (perhaps drastic) solutions:

MicroSD card corruption is an ongoing challenge for any device (such as the Pi) that boots from a MicroSD card. Most cases can be traced to either (a) junk MicroSD cards or (b) sudden power loss during a write operation.

You can mostly avoid problem (a) by carefully sourcing your MicroSD cards. If you’re considering saving $20 by buying an off-brand MicroSD card or, worse, a possible fake, don’t do it.

Sudden power loss is harder to completely guard against. Ideally, you want to shutdown your Pi before removing power. This can be done by SSH-ing into it and running sudo shutdown -h now and waiting about a minute. If you use our radar systemd service, you can also perform a safe shutdown by pressing and holding the button for 5 seconds, waiting for the light to turn off, and then giving it about a minute to fully shut down.

For Peregrine, this should all be good enough.

For Eyas and other longer-term installations, the risk of a battery discharging faster than expected is relatively high.

The first line of defense is the automatic shutdown provided by the logger service that will safely shut everything down when the battery voltage gets too low.

If you have a separate data storage device, such as an external USB-connected SSD, you can go a step further and make your Pi’s MicroSD card read-only.

A utility called overlayroot (which uses OverlayFS) can be used to make the root filesystem read-only and create an “overlay filesystem” stored only in RAM. This means that all changes to the MicroSD card file system are temporary and are lost upon reboot.

This is great for keeping a standardized configuration, but you need to be aware that this means most log files are lost on reboot, any config files on the SD card are lost on reboot, and all radar data you want to keep around must be stored to your external storage device.

You can read a more detailed tutorial about Overlayroot here.

If you’re going to do this, be sure that you’ve tested your setup in advance so you know if the data you want to keep is being stored.

Enabling Overlayroot

If you still want to set this up, it’s quite easy to enable.

Simply edit the last line of /etc/overlayroot.conf to this:

overlayroot="tmpfs:recurse=0"

tmpfs specifies that we want to use a RAM-based filesystem to store the “overlay” part of the filesystem. recurse=0 says that we want to apply this only to the / mount and not to other mounts below that.

(You need to edit this file as root. For example: sudo vim /etc/overlayroot.conf)

Disabling Overlayroot

You can temporarily re-mount the root filesystem to edit things using the built-in utility:

sudo overlayroot-chroot

To exit, just type exit.

If you want to permenantly disable it, just edit the last line of /etc/overlayroot.conf back to:

overlayroot=""

and reboot.

Last modified October 25, 2023: Address Anna's comments on Pi setup (f782dd4)