Pi IoT Academy
Phase 2 · Install the Platform

Phase 02

Install the platform.

Last phase you built the Pi. This phase you’ll fill it with software — five small programs that work together to make the whole IoT lab. The clever bit: one command starts all five.

~60–90 min Difficulty: 2 / 3 Lots of typing Software lesson

What you’re about to install

Read this first — it’s the map.

The platform is made of five services. A service is just a program that does one job and runs in the background. They talk to each other over the network.

Mosquitto
The MQTT broker — the message post office. Sensors send messages here; everyone who’s interested picks them up.
Node-RED
A visual programming tool. We’ll use it to build small automations and to peek at MQTT messages while we’re developing.
FastAPI
The main API. It saves sensor readings to the database and feeds the dashboard.
AI service
A separate small API that talks to Claude. We’ll wire it up properly in Phase 6.
Vue dashboard
The webpage that runs on your touchscreen. You’ll design it in Phase 5.

All five run inside Docker. Docker is a tool that puts each service in its own “box” (called a container) so they can’t mess with each other and they all start the same way on every Pi. One file, docker-compose.yml, tells Docker how to run them together.

Why containers?

Without Docker you’d install five programs by hand, each with its own version of Python or Node, each with its own config files. Containers bundle the program and everything it needs into one box. Reset broken? Throw the box away and start a new one.

Install Docker

~10 minutes · on the Pi

SSH into your Pi (you set this up in Phase 1) and run the official install script:

terminal · on the Pi
curl -fsSL https://get.docker.com | sudo sh

This downloads a script from Docker and runs it. It takes 3–5 minutes. When it finishes, add yourself to the docker group so you don’t have to type sudo every time:

terminal · on the Pi
sudo usermod -aG docker $USER

For that change to take effect, log out and log back in. (If you’re on SSH, just exit and SSH back in.) Then check Docker is alive:

terminal · on the Pi
docker version
docker compose version

You should see version numbers, no errors. If you see “permission denied”, you’ve forgotten the log-out / log-in.

What does curl … | sudo sh do?

curl downloads a file from a URL. The pipe | hands it straight to sh, which runs it as a shell script with administrator privileges. You should only ever do this with scripts from trusted sources — get.docker.com is one of them.

Checkpoint

Get the project files

~5 minutes · on the Pi

The platform code lives in a Git repository. Git is a tool for tracking and sharing files. We’ll clone the repo — copy it from the internet to your Pi.

Move into the folder you set up in Phase 1, then clone:

terminal · on the Pi
cd /opt/iot-platform
git clone https://github.com/Computing-Hub/IoT_Master.git .
ls
Using a fork?

If your class has its own fork (so each year-group can save changes), replace the URL above with your teacher’s fork URL. The structure is identical.

The dot at the end of git clone means “clone into the current folder”. The ls at the end shows you what arrived. You should see at least:

expected files
docker-compose.yml
.env.example
infra/
services/
frontend/

If git isn’t installed, install it:

terminal · on the Pi
sudo apt install -y git
Checkpoint

Configure your .env

~5 minutes · on the Pi

Some settings change between Pis — passwords, secret keys, your specific Wi-Fi name. They live in a file called .env, which is never committed to Git. There’s a template called .env.example that we copy and edit.

terminal · on the Pi
cd /opt/iot-platform
cp .env.example .env
nano .env

nano is a simple text editor. You should see something like this:

.env.example · what you need to change
# MQTT credentials — change the password
MQTT_USER=platform
MQTT_PASSWORD=change-me-please

# Anthropic API key — leave blank for now (we'll add it in Phase 6)
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=claude-haiku-4-5-20251001

# Database file
DB_PATH=/data/sqlite/platform.db

For now, the only thing you must change is MQTT_PASSWORD. Pick something memorable but not obvious — not password, not your name, not 1234. Save with Ctrl+O, Enter, then exit with Ctrl+X.

Never share your .env

This file holds passwords. It’s already in .gitignore so Git ignores it — but don’t copy-paste it into Slack, email, or a screenshot. Treat it like the password to your school account.

Now create the MQTT password file Mosquitto needs:

terminal · on the Pi
./scripts/bootstrap-mqtt-passwd.sh

That script reads MQTT_USER and MQTT_PASSWORD from .env and writes a hashed copy to infra/mosquitto/passwd. Mosquitto will check the hash, not the plain password, when clients connect.

Checkpoint

Start the platform

~10 minutes · mostly waiting

Here’s the magic command:

terminal · on the Pi
docker compose up -d

Reading that out loud: “Docker, compose up the services in detached mode.” -d means “run in the background” — without it, your terminal would be filled with logs from all five services at once.

The first time you run this, Docker has to download (“pull”) the images for each service. That can take 5–10 minutes on school Wi-Fi. You’ll see lines like:

example output
[+] Pulling 47/47
 ✔ mosquitto Pulled    32.1s
 ✔ node-red  Pulled    58.4s
 ✔ api       Built      2.7s
 ✔ ai        Built      2.4s
 ✔ frontend  Built      4.1s
[+] Running 5/5
 ✔ Container iot-mosquitto  Started
 ✔ Container iot-node-red   Started
 ✔ Container iot-api        Started
 ✔ Container iot-ai         Started
 ✔ Container iot-frontend   Started

Once it’s done, check what’s running:

terminal · on the Pi
docker compose ps

You should see five rows, each with Up in the STATUS column. If any say Restarting or Exited, check that service’s logs:

terminal · on the Pi
docker compose logs api          # one service
docker compose logs -f           # all services, live
docker compose logs --tail=50 api # last 50 lines

Press Ctrl+C to stop following logs. (That doesn’t stop the service — just stops showing you the logs.)

Useful Docker commands

A small toolkit you’ll use throughout the course:

  • docker compose ps — what’s running.
  • docker compose logs <service> — show logs.
  • docker compose restart <service> — turn it off and on again.
  • docker compose down — stop everything.
  • docker compose up -d — start everything.
Checkpoint

Verify each service

~10 minutes

“Up” in Docker just means the container started. It doesn’t mean the service inside actually works. Let’s prove each one.

Find your Pi’s hostname (it should be pi-iot-XX from Phase 1):

terminal · on the Pi
hostname

From now on, replace pi-iot-XX in every command with the hostname your Pi reports.

5a. The Vue dashboard

On the Pi’s touchscreen, open Chromium and go to:

browser
http://localhost/

Or from your laptop on the same Wi-Fi:

browser
http://pi-iot-XX.local/

You should see a placeholder page that says “Pi IoT Platform”. It’s empty for now — you’ll build the real dashboard in Phase 5.

5b. Node-RED

browser
http://pi-iot-XX.local:1880/

Node-RED’s flow editor should load — a blank canvas with a sidebar of “nodes”. We’ll use this in Phase 3.

5c. The API

terminal · on the Pi
curl http://localhost:8000/health

Should print:

expected
{"status":"ok"}

5d. The AI service

terminal · on the Pi
curl http://localhost:8001/health

Same response: {"status":"ok"}. (No API key yet, that’s fine — the service is alive.)

What is a /health endpoint?

A tiny URL that every service offers, just to say “I’m alive”. We’ll use these in monitoring and in Docker’s health checks. They’re a habit you should pick up — every service you ever build should have one.

Checkpoint

Round-trip an MQTT message

~10 minutes · works best with a partner

This is the showpiece of Phase 2. You’ll send a message from one laptop, through the Pi’s MQTT broker, to a different laptop — without writing any code.

On both laptops, install the MQTT command-line tools (one-off, ~30 seconds each):

terminal · on each laptop (Linux/Mac)
sudo apt install -y mosquitto-clients   # Debian/Ubuntu/Raspberry Pi OS
brew install mosquitto                  # macOS

On laptop A, listen for messages on a test topic:

terminal · laptop A (the listener)
mosquitto_sub -h pi-iot-XX.local -p 1883 \
  -u platform -P "<your MQTT password>" \
  -t "dev/hello"

The \ at the end of a line means “the command continues on the next line”. After running it, the terminal will sit there and wait. That’s correct — it’s listening.

On laptop B, send a message to the same topic:

terminal · laptop B (the sender)
mosquitto_pub -h pi-iot-XX.local -p 1883 \
  -u platform -P "<your MQTT password>" \
  -t "dev/hello" \
  -m "Hello from laptop B!"

Watch laptop A. The message should appear instantly:

laptop A output
Hello from laptop B!

Press Ctrl+C on laptop A to stop listening. Now swap roles — laptop A sends, laptop B listens. Try a different topic, like dev/temperature, and a different message.

What just happened?

Laptop B sent a message to the broker on the Pi. The broker saw that laptop A had subscribed to that topic, so it forwarded the message. Neither laptop knew about the other — they only knew about the broker. That’s how every IoT device on the platform will work.

Connection refused?

If mosquitto_sub says “Connection Refused: not authorised”, the password is wrong. Check the password is the one in .env on the Pi. If it says “No route to host”, your laptop can’t reach the Pi — check it’s on the same Wi-Fi.

Checkpoint

Final verification

5 minutes

Tick everything below before declaring Phase 2 finished. Phase 3 assumes the bus is working and the API is reachable.

Phase 2 sign-off

Test that last one

Reboot the Pi (sudo reboot). After it comes back, SSH in and run docker compose ps — everything should be Up again. If not, something’s missing in the compose file’s restart policy.

Phase 2 complete?

You’ve installed a five-service IoT platform with one command and proved it works. Real software engineers do this for a living. Next phase you’ll send your first sensor reading through the bus and see it on the Pi’s screen.

Phase 3: Talk over MQTT  →

(The button unlocks when every box above is ticked.)