09 Software — Pi Setup

09 Software — Pi Setup

📸 Pi Imager screenshots coming. The terminal commands below are the source of truth; the screenshots are visual reinforcement.

The hardware is wired and the relays click on cue. Now get an OS onto the Pi. This page walks Pi Imager (one screen — set the hostname, WiFi, and SSH key, then write the SD card), the first SSH-in over the LAN, and the one-shot setup-pi.sh script that installs every Python dependency, enables I2C, sets up a venv under ~/irrigation/, and wires up the cron schedule. Reboot when prompted, verify with i2cdetect -y 1, and you’re ready for page 10.

Get an OS on the SD card

Download Raspberry Pi Imager from raspberrypi.com and install it on your laptop. Insert a microSD card (8 GB minimum, class 10 or better) into your laptop’s card reader.

In the Imager, pick Raspberry Pi OS Lite (64-bit) under “Choose OS → Raspberry Pi OS (other)”. Lite means no desktop — this Pi is a headless server, so the desktop would just waste SD-card space and boot time.

Before you click “Write”, click the gear icon (advanced settings) and configure:

Save the settings, then click “Write” and confirm. The Imager flashes Pi OS Lite onto the SD card in about 3 minutes. Eject the card, slide it into the Pi’s SD slot (underside of the board), and power it on. First boot takes ~60 seconds — the Pi expands the filesystem, joins WiFi, and starts the SSH daemon.

First boot and SSH in

From your laptop, on the same WiFi network as the Pi:

The first connection asks you to confirm the host key — type yes and press Enter. After that you’re at a Pi shell prompt: pi@irrigator:~ $. Everything you type from here runs on the Pi.

What is SSH? Secure Shell — a way to type commands into a remote computer (the Pi) from your laptop. The terminal you’re typing in is on your laptop; everything after ssh [email protected] is running on the Pi. When you exit the SSH session, you’re back at your laptop’s shell.

What if .local doesn’t resolve? The .local suffix relies on mDNS, and mDNS doesn’t work on every router or every OS combination. If ssh [email protected] hangs for 30 seconds and then says “host not found”, look at your router’s admin page (usually 192.168.1.1 or 192.168.0.1) for the DHCP lease list and find the Pi by hostname irrigator. Then SSH by IP: ssh [email protected] (or whatever IP the router shows). On a Mac, dns-sd -B _ssh._tcp lists every mDNS-advertised SSH server on your LAN — handy for debugging.

The one-shot setup script

The script below installs every Python dependency, enables I2C, creates ~/irrigation/ with a logs/ subdirectory, sets up a Python virtual environment, copies the four irrigation scripts into place, and installs a cron schedule for 7am + 6pm watering checks. Run it once on a fresh Pi OS Lite image; reboot when it tells you to.

#!/bin/bash
# Raspberry Pi Irrigation Controller — Full Setup Script
# Run this on the Pi via SSH or directly
# Usage: bash setup-pi.sh

set -e

echo "============================================"
echo "Irrigation Controller Setup"
echo "============================================"

# 1. Update system
echo "[1/6] Updating system packages..."
sudo apt update && sudo apt upgrade -y

# 2. Install system dependencies
echo "[2/6] Installing system dependencies..."
sudo apt install -y python3-pip python3-smbus i2c-tools python3-venv

# 3. Enable I2C
echo "[3/6] Enabling I2C..."
sudo raspi-config nonint do_i2c 0

# 4. Create project structure
echo "[4/6] Creating project structure..."
mkdir -p ~/irrigation/logs
cd ~/irrigation

# 5. Create virtual environment and install Python packages
echo "[5/6] Setting up Python environment..."
python3 -m venv venv
source venv/bin/activate
pip install adafruit-circuitpython-ads1x15 RPi.GPIO

# 6. Copy scripts (assumes they're in the same directory as this setup script)
echo "[6/6] Installing scripts..."
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

for script in irrigate.py calibrate.py status.py manual_water.py; do
    if [ -f "$SCRIPT_DIR/$script" ]; then
        cp "$SCRIPT_DIR/$script" ~/irrigation/
        chmod +x ~/irrigation/$script
        echo "  Installed $script"
    else
        echo "  WARNING: $script not found in $SCRIPT_DIR"
    fi
done

# Create cron wrapper
cat > ~/irrigation/run.sh << 'EOF'
#!/bin/bash
cd ~/irrigation
source venv/bin/activate
python3 irrigate.py
EOF
chmod +x ~/irrigation/run.sh

# Set up cron schedule
echo "Setting up cron schedule..."
(crontab -l 2>/dev/null | grep -v irrigation; echo "0 7 * * * ~/irrigation/run.sh >> ~/irrigation/logs/cron.log 2>&1"; echo "0 18 * * * ~/irrigation/run.sh >> ~/irrigation/logs/cron.log 2>&1") | crontab -

echo ""
echo "============================================"
echo "Setup complete!"
echo "============================================"
echo ""
echo "Next steps:"
echo "  1. Reboot: sudo reboot"
echo "  2. After reboot, verify I2C: sudo i2cdetect -y 1"
echo "  3. Calibrate sensors: cd ~/irrigation && source venv/bin/activate && python3 calibrate.py"
echo "  4. Update thresholds: nano ~/irrigation/irrigate.py"
echo "  5. Test run: python3 ~/irrigation/irrigate.py"

Run it

You need the script and the four irrigation Python files (irrigate.py, calibrate.py, status.py, manual_water.py) on the Pi together in one directory. Either git clone from your fork of the repo, or scp them from your laptop (scp -r irrigation/ [email protected]:~/). Then from the Pi’s SSH session:

cd ~/irrigation
bash setup-pi.sh

The script logs each step ([1/6] through [6/6]); apt update + the Python deps takes 3-5 minutes on a fresh image. The first time it runs raspi-config nonint do_i2c 0 you may see a brief flash about reconfiguring dphys-swapfile — that’s normal.

What the script does, step-by-step

After the script finishes you’ll have this layout under ~/irrigation/:

~/irrigation/
├── venv/                       # Python virtual environment (5)
├── logs/
│   └── cron.log                # appended by cron at 7am + 6pm
├── irrigate.py                 # main loop — page 10
├── calibrate.py                # interactive sensor reader — page 11
├── status.py                   # one-shot status print
├── manual_water.py             # manual override CLI
├── run.sh                      # cron wrapper (sources venv, runs irrigate.py)
└── setup-pi.sh                 # this script (kept around for re-runs)

What is a venv? A Python virtual environment — a self-contained venv/ directory under ~/irrigation/ with its own Python interpreter and pip-installed packages. The source venv/bin/activate line tells your shell to use the venv’s Python instead of the system Python; which python3 after activating points into ~/irrigation/venv/bin/. It keeps the irrigation libraries from polluting the rest of the Pi’s Python install (and vice-versa — Pi OS updates won’t break your pinned package versions). Activate with source ~/irrigation/venv/bin/activate; deactivate with the bare command deactivate.

Reboot and verify

The I2C kernel module needs a reboot to load:

sudo reboot

Wait ~30 seconds; SSH back in once the LED activity settles.

ssh [email protected]

Verify I2C is working — you should see 48 in the address grid (the ADS1115’s default I2C address):

sudo i2cdetect -y 1

If 48 shows up, your sensors are reachable from the Pi and you’re ready for page 10. If the grid is empty, one of three things is wrong: the ADS1115 isn’t powered (check 3.3V on its VDD pin), SDA and SCL are swapped at the Pi header (re-check the page-08 wiring table), or the ADDR pin is floating instead of tied to GND. Page 13’s troubleshooting section walks the full I2C debug path.

With the Pi software installed and I2C verified, page 10 walks the irrigation script itself — what each config block in irrigate.py means, how the watering loop decides to fire a zone, and the finally: all_off() fail-safe that guarantees no relay stays latched if the script crashes.