After creating the SD card for this project, I next wanted to automate the initial Pi setup on the first login – further configuring system settings and installing applications. While it might have been possible to do some of that with pi-gen or Yocto layers, I just wanted a simple script to execute initially. In the previous post, sd-card-write.sh copies this script to the SD card as /boot/setup.sh.

System Configuration and Password

It doesn’t seem to be well documented but raspi-config can be run non-interactively with a nonint option. Browsing the source, various methods like do_camera are available to be called. Later I stumbled upon this raspi-config Gist that provides a nice template for easily scripting various configuration tasks.

#!/bin/bash

echo "Running automated raspi-config tasks"

# Via https://gist.github.com/damoclark/ab3d700aafa140efb97e510650d9b1be
# Execute the config options starting with 'do_' below
grep -E -v -e '^\s*#' -e '^\s*$' <<END | \
sed -e 's/$//' -e 's/^\s*/\/usr\/bin\/raspi-config nonint /' | bash -x -
#

# Drop this file in SD card root. After booting run: sudo /boot/setup.sh

# --- Begin raspi-config non-interactive config option specification ---

# Hardware Configuration
do_boot_wait 0            # Turn on waiting for network before booting
do_boot_splash 1          # Disable the splash screen
do_overscan 1             # Enable overscan
do_camera 1               # Enable the camera
do_ssh 0                  # Enable remote ssh login

# System Configuration
do_configure_keyboard us
do_hostname ${host}
do_change_timezone America/New_York
do_change_locale LANG=en_US.UTF-8

# Don't add any raspi-config configuration options after 'END' line below & don't remove 'END' line
END

The ${host} value is a placeholder set when running a script to create the SD card.

It can be important to set the locale details (especially keyboard layout); by default it’s UK English and the password I entered on my keyboard contained an “@” character. I spent a few minutes scratching my head after failed ssh login attempts once.

After the raspi-config steps quickly execute the script first starts the password change process with sudo passwd pi. Using /usr/bin/raspi-config do_change_pass is another option but it’s slower going through that UI. This is the only part of the process requiring user input; use of pi-gen could avoid this by changing the default user credentials.

System Updates

Next package lists are updated and packages are upgraded.

echo "Updating packages"
sudo apt-get update && sudo apt-get -y upgrade
Get:1 http://archive.raspberrypi.org/debian buster InRelease [25.1 kB]
Get:2 http://raspbian.raspberrypi.org/raspbian buster InRelease [15.0 kB]
Get:3 http://raspbian.raspberrypi.org/raspbian buster/main armhf Packages [13.0 MB]
Get:4 http://archive.raspberrypi.org/debian buster/main armhf Packages [280 kB]
Fetched 13.3 MB in 7s (1,865 kB/s)
Reading package lists... Done
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages have been kept back:
    binutils binutils-arm-linux-gnueabihf binutils-common libbinutils
The following packages will be upgraded:
    bluez curl firmware-atheros firmware-brcm80211 firmware-libertas firmware-misc-nonfree
    firmware-realtek libcurl4 libgnutls30 libicu63 libpam-systemd libsystemd0 libudev1 raspi-config
    rpi-eeprom rpi-eeprom-images systemd systemd-sysv udev
19 upgraded, 0 newly installed, 0 to remove and 4 not upgraded.
Need to get 31.2 MB of archives
...

Enabling the Camera

I didn’t spend much time trying to figure out why, but do_camera via non-interactive raspi-config didn’t seem to work; there were no errors but the camera wasn’t enabled afterwards. Various posts indicated this could be done via directly modifying /boot/config.txt so that’s the route I went:

echo "Enabling camera"
sed -i "s/start_x=0/start_x=1/g" /boot/config.txt

Docker and Reboot

A helper script to install and run the application (described in upcoming post) is next moved to the home directory for convenient execution. I considered having the setup script run the app install script but found it more flexible keeping them separated.

echo "Moving pull script from boot to home"
mv /boot/pull.sh /home/pi

This script works by pulling the application image from a Docker registry so Docker first needs to be installed.

echo "Installing Docker"
# Installing docker will disconnect ssh
curl -sSL https://get.docker.com | sh

echo "Finishing docker setup"
sudo usermod -aG docker pi

Afterwards, all these script changes are finalized with a restart.

echo "Restarting to apply changes. After run ssh pi@${host}.local"
# Reboot after all changes above complete
/sbin/shutdown -r now

The full script is available here: setup.sh.

Going Further

Later I ended up using pi-gen here to move some of this setup to a pre-built custom Pi image. See Using Pi-Gen to Build a Custom Raspbian Lite Image for details.

Up Next

Deploying, Running, Debugging .NET Code on Raspberry Pi – The next post in this series covers a few common ways to get .NET Code on the Pi and run and debug it.

One thought on “Automating Raspberry Pi Setup

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.