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.
Imaging your Pi
To start, download the Raspberry Pi Imager tool (or use your preferred software
for imaging SD cards). Imaging is basically giving the Raspberry Pi an operating system. On Ubuntu, you can install it like this:
sudo apt install rpi-imager
For other operating systems, download the tool from here.
Remember to choose your microSD card carefully as mentioned in hardware options
Launcher the imager
Select what version of Raspberry Pi you are using
Under “OS” select “Other general-purpose OS”
Select Ubuntu
Scroll until you find Ubuntu Server 24.04.xx LTS (64-bit). Make sure you get 64-bit, 32-bit will not work. Also make sure that the Raspberry Pi you are using is supported.
You want Ubuntu Server 24.04 LTS 64-bit and make sure your Pi is supported as shown in the highlight
Insert your SD card if you haven’t already done so, and select it
Skip customization, we will create our own user-data file to insert
Image the SD card
After imaging is complete, you should see a system-boot drive and maybe a writable drive. system-boot is more important.
Can't find system-boot?
If you see a different drive from the SD card that tells you to format it in order to read it, do not format it. The following information is likely Windows specific. If you can’t see the system-boot drive, it is likely because it wasn’t assigned a letter. You need admin privliages to fix this. Hit WIN + X and select “Disk Manager”. You will see the system-boot drive there, it just doesn’t have a letter. Right click on it and assign a letter. Click “add” on the pop up and assign it a letter. The drive should now be visible in your file explorer.
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
network-config and user-data.
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:falseusers:- name:ubuntupassword:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1type:hash# Enable password authentication with the SSH daemonssh_pwauth:true# Set a default timezonetimezone:Etc/UTC## Update apt database and upgrade packages on first bootpackage_update:truepackage_upgrade:true## Install additional packages on first bootpackages:- net-tools- git- cmake- g++- mosh- exfat-fuse- i2c-tools- rpi.gpio-common- util-linux-extra- gpsd- gpsd-clients## Write arbitrary files to the file-systemwrite_files:- path:/home/ubuntu/initial_setup.shcontent:| #!/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 rebootappend:true- path:/home/ubuntu/.profilecontent:| PATH=/home/ubuntu/miniconda/bin:$PATH
source /home/ubuntu/.bashrcappend:true- path:/home/ubuntu/.ssh/known_hostscontent:| 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_Trickscontent:| ubuntu - rtprio 99append:true- path:/etc/systemd/user/radar.servicecontent:| [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.servicecontent:| [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/referenceversion:2ethernets:eth0:# Your ethernet name.dhcp4:noaddresses:[192.168.11.137/24]gateway4:192.168.11.1nameservers:addresses:[8.8.8.8,8.8.4.4]wifis:renderer:networkdwlan0:dhcp4:trueoptional:trueaccess-points:"<YOUR WIFI SSID HERE>":password:"<YOUR WIFI PASSWORD HERE>"
network-config Example for Laptop Mobile Hotspot and Possibly Normal Wifi
# 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/referencenetwork:version:2renderer:networkdwifis:wlan0:dhcp4:yesaccess-points:"<WIFI SSID HERE>":password:"<PASSWORD HERE>"
If your wifi SSID or password has slashes, it might freak the Raspberry Pi out and the network-config won’t run properly.
After you edit the user-data and network-config files and add them to your SD card. You can now put the card back into the Pi.
# 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/referenceversion:2ethernets:eth0:# Your ethernet name.dhcp4:noaddresses:[192.168.11.137/24]gateway4:192.168.11.1nameservers:addresses:[8.8.8.8,8.8.4.4]wifis:renderer:networkdwlan0:dhcp4:trueoptional:trueaccess-points:"<YOUR WIFI SSID HERE>":password:"<YOUR WIFI PASSWORD HERE>"
The above configuration sets up a static IP over the ethernet interface. It
also configures 192.168.11.1 as the default gateway. This allows for sharing
an internet connection from a computer over this interface if desired.
The configuration also provides an SSID and password for a WiFi network. In practice,
we configure this to the settings for a phone hotspot that can be used to get
internet when WiFi is not otherwise available. This is also a simpler setup for
getting the Pi on the internet when needed.
Reconfiguring with netplan
By default, network interfaces are configured with netplan. See
the netplan documentation
for more details.
By default, the cloud-init script sets up a static IP of 192.168.11.137, but
you could choose to configure this to something different for each payload box.
Our usual way of connecting is by plugging an ethernet cable into the Pi and
connecting it to a laptop. You can read about
all the networking options here.
Understanding the user-data file and any edits you may want to make
Use this when setting up your Raspberry Pi on initial boot-up.
The default user-data we start from is as shown below. You will likely need to
tweak many of these settings. Descriptions and tips for the most important
sections are below.
Note that this is one of two key configuration files. You can read about
network-config here.
Starting point user-data file
#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:falseusers:- name:ubuntupassword:$6$rounds=4096$aQ7tu0.beL3WAL32$fKxKYvZpY7EMCoxAU1heRomA3v8WvgbqBhhz08QwOtQdlP/DJOP2BThqZFoRW8d2a9PaIKK9BC9NHs1qNnkya1type:hash# Enable password authentication with the SSH daemonssh_pwauth:true# Set a default timezonetimezone:Etc/UTC## Update apt database and upgrade packages on first bootpackage_update:truepackage_upgrade:true## Install additional packages on first bootpackages:- net-tools- git- cmake- g++- mosh- exfat-fuse- i2c-tools- rpi.gpio-common- util-linux-extra- gpsd- gpsd-clients## Write arbitrary files to the file-systemwrite_files:- path:/home/ubuntu/initial_setup.shcontent:| #!/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 rebootappend:true- path:/home/ubuntu/.profilecontent:| PATH=/home/ubuntu/miniconda/bin:$PATH
source /home/ubuntu/.bashrcappend:true- path:/home/ubuntu/.ssh/known_hostscontent:| 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_Trickscontent:| ubuntu - rtprio 99append:true- path:/etc/systemd/user/radar.servicecontent:| [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.servicecontent:| [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
Password-less shutdown
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"
One of the features supported by the uav_radar_logger utility is to automatically
cleanly shutdown the system if the measured battery voltage drops below a
threshold. To facilitate this, the default user must be able to shutdown the
system without needing additional authentication. This gives permission for the
ubuntu user to call sudo shutdown or sudo reboot without entering a password.
Password authentication
# 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
This sets up a default password for the ubuntu user. You should change this to
something else (or disable password authentication completely, if you prefer).
Passwords are stored in a hashed format. You can generate password hashes using
this utility:
mkpasswd --method=SHA-512 --rounds=4096
Timezone
# Set a default timezone
timezone: Etc/UTC
You could set this to other time zones (i.e. `America/Los_Angeles"), but really
it would make everyone’s life easier if you just set your clock to UTC.
Add SSH keys through GitHub
## 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
You can very conveniently enable key-based authentication for specific GitHub
user names. If your username is in here and you have a public key setup with GitHub,
this public key will be imported and you will be able to SSH into your Pi
with no additional setup. You might want to remove us from your list, though. :)
Files
Arbitrary files can be written to the system with cloud-init. Some of these are
important.
initial_setup.sh
- 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.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
The initial_setup.sh script grabs copies of our code and sets up the radar and
logging services. This script is intended to be manually run the first time you
SSH into the system. This enables you to use
SSH agent forwarding
to provide any needed GitHub authentication to get the code.
This is also where you would customize the repositories to check out (if, for
example, you’ve forked our code) and where you can pick a branch to automatically
checkout.
Radar and Logger services
- 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
Two systemd services are used to manage everything. One run the radar code in
its default button-controlled setup. The other runs basic logging of I2C-connected
sensors and handles automatic low-battery shutdown.
Some final setup is done by running arbitrary commands. These are run as the root
user.
One aspect of this you may wish to customize are the last two lines, which add
settings to automatically mount an ExFAT-formatted SSD plugged into the Pi. This
can be (optionally) used as a storage location for radar data.
Testing changes
You may want to test your changes before using them on your Pi. Options for doing
that are described here.
Note that the initial_setup.sh script downloads miniconda for the aarch64
architecture, which probably won’t work on your computer. If you want to test that
part, you’ll need to change this.