A Raspberry Pi 5 face recognition greeter. A PIR motion sensor wakes the system, the camera captures frames, and a personalized spoken greeting plays through a USB speaker. A touchscreen display will show status and admin info in a future release.
Version 1 goal: Validate all hardware — PIR, Camera Module 3, USB audio, and the motion-triggered main loop with a placeholder greeting.
- Project Overview
- Architecture
- Hardware List
- Physical Assembly Order
- GPIO Wiring
- Raspberry Pi OS Setup
- Enable Camera
- USB Audio Setup
- Develop on Mac
- Deploy to Raspberry Pi
- Step 1 Validation (Camera + TTS)
- Step 2 Face Enrollment
- Final Step: Motion Sensor (PIR)
- Hardware Test Order
- Run the Main App
- Project Structure
- Troubleshooting
- Next Milestones
Pi Face Greeter sits by your door and:
- Waits for motion (PIR sensor)
- Wakes the camera and captures frames
- Identifies known faces with
face_recognition(dlib) - Plays a personalized greeting through speakers
- Shows status on a touchscreen (future)
Milestone order:
| Step | What | Command |
|---|---|---|
| 1 | Kiosk app (animated face, camera preview, settings, recognition) | pi-face-greeter-app |
| 2 | Face enrollment (CLI or from settings UI) | pi-face-greeter-enroll |
| — | Hardware validators (camera, TTS, enroll CLI) | pi-face-greeter-validate-step1, etc. |
| Last | PIR motion loop (optional) | pi-face-greeter-validate-motion then pi-face-greeter |
PIR stays disabled (pir.enabled: false) until the final optional step.
Kiosk app: Swipeable touchscreen UI with an animated face, live camera preview (yellow box on detected faces), spoken "Hi <name>" / "Hi friend" on presence, and a settings screen to enroll and manage faces from the live camera.
Source: architecture/architecture.drawio. Solid = active milestones. Dashed = final PIR step / future.
Regenerate after diagram edits:
drawio -x -f svg -o architecture/architecture.svg architecture/architecture.drawio
drawio -x -f png -o architecture/architecture.png architecture/architecture.drawio| Module | Path | Role |
|---|---|---|
| Kiosk app | src/pi_face_greeter/app/ |
Kivy UI: animated face, camera preview, settings |
| Main loop | src/pi_face_greeter/main.py |
Motion → capture → TTS → cooldown (PIR, optional) |
| Step 1 validator | src/pi_face_greeter/validate_step1.py |
Camera + TTS |
| Step 2 enrollment | src/pi_face_greeter/enroll.py |
Capture known-face photos + embeddings (CLI) |
| Recognition | src/pi_face_greeter/app/recognizer.py |
Load encodings, identify faces |
| Motion validator | src/pi_face_greeter/validate_motion.py |
PIR + one greet (final step) |
| PIR | src/pi_face_greeter/pir_sensor.py |
gpiozero wrapper for AM312 |
| Camera | src/pi_face_greeter/camera.py |
Picamera2 (CSI) backend |
| TTS | src/pi_face_greeter/tts.py |
Piper neural TTS (espeak-ng fallback) |
| Conversation | src/pi_face_greeter/app/conversation.py |
Ollama SLM greetings (fallback to canned phrases) |
| Ollama client | src/pi_face_greeter/ollama_client.py |
HTTP client for local Ollama |
| Config | config/config.yaml |
Runtime settings |
Camera path for this build: You have a Raspberry Pi Camera Module 3 (CSI). Use Picamera2 (camera.backend: picamera2 in config). JPEG saving uses Pillow (installed via pip).
USB webcam alternative: Set camera.backend: opencv and camera.device_index: 0 — requires python3-opencv from apt.
See docs/wiring.md for physical connections and docs/roadmap.md for future work.
| Component | Notes |
|---|---|
| Raspberry Pi 5 | 64-bit Raspberry Pi OS Bookworm |
| Official 27W USB-C power supply | Required for stable camera + CPU load |
| Active cooler | Recommended for sustained use |
| Raspberry Pi Camera Module 3 | CSI ribbon cable |
| AM312 / HC-SR312 mini PIR | 3.3V power only |
| USB sound card + 8Ω 5W speaker | Driver-free, plug and play |
| Hosyond 5" MIPI DSI touchscreen | 800×480, capacitive — future UI |
| Female-to-female jumper wires | For PIR wiring |
Follow this order on the bench before installing software.
- Attach the active cooler to the Pi 5.
- Insert a microSD card flashed with Raspberry Pi OS (64-bit) Bookworm.
- Connect HDMI (or DSI display later), keyboard, and the 27W USB-C power supply.
- Boot and complete initial setup (user, Wi‑Fi, updates).
- Verify architecture:
uname -m
# Expected: aarch64- Power off the Pi.
- Connect the CSI ribbon to the camera connector (see docs/wiring.md).
- Power on and enable the camera (see Enable Camera).
- Power off the Pi.
- Connect the Hosyond 5" DSI ribbon per the display manual.
- Power on — you should see the desktop at 800×480.
- Run the kiosk app:
pi-face-greeter-app(see Run the Kiosk App).
Wire the PIR after camera and audio are tested — see GPIO Wiring.
- Connect the speaker to the sound card header.
- Plug the USB sound card into the Pi.
- Complete USB Audio Setup.
- Pi boots reliably with PSU + cooler
- Camera detected (
rpicam-hello --list-cameras) - USB audio works (
speaker-test) - PIR wired to 3.3V, GND, GPIO17
- Project cloned and dependencies installed
| PIR pin | Pi physical pin | Pi function |
|---|---|---|
| VCC (+) | Pin 1 | 3.3V |
| GND (-) | Pin 6 | GND |
| OUT (S) | Pin 11 | GPIO17 |
Do not power the PIR from 5V. The AM312 / HC-SR312 expects 3.3V.
Full diagrams: docs/wiring.md
- Flash Raspberry Pi OS (64-bit) Bookworm using Raspberry Pi Imager.
- Enable SSH and set hostname/user in Imager if headless.
- Boot the Pi and update:
sudo apt update
sudo apt full-upgrade -y
sudo reboot- Clone this project:
cd ~
git clone <your-repo-url> pi_face_greeter
cd pi_face_greeter- Enable the camera interface:
sudo raspi-config
# Interface Options → Camera → Enable → Finish → Reboot- Verify Camera Module 3:
rpicam-hello --list-cameras
rpicam-hello -t 5000You should see a live preview for 5 seconds. Pi 5 exposes cam0 and cam1; either port works.
Do not use legacy raspicam or picamera — they are unsupported on Bookworm.
- List playback devices:
aplay -lExample output:
card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]
- Test the USB card (adjust card/device numbers):
speaker-test -D plughw:1,0 -c 2 -t wavPress Ctrl+C after confirming audio.
- Set the device in
config/config.yaml:
tts:
alsa_device: "plughw:1,0"Leave as null to use the system default if HDMI/audio jack is preferred.
git clone <your-repo-url> pi_face_greeter
cd pi_face_greeter
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytestHardware tests (camera, TTS, PIR) run on the Pi only. Push after pytest passes.
Quick setup from the project root on the Pi:
cd ~/pi_face_greeter
git pull
./scripts/setup_system.sh
# log out and back in after system setup (group membership + Ollama)
./scripts/setup_venv.sh
source .venv/bin/activate
pi-face-greeter-appsetup_system.sh installs apt packages and Ollama. setup_venv.sh installs the optional [recognition] extra (face_recognition + dlib), [voice] extra (Piper TTS), downloads the Piper voice model, and pulls the Ollama SLM. The dlib compile can take 30+ minutes on a Pi — run it once and leave the terminal open.
Or update an existing install:
cd ~/pi_face_greeter
git pull
source .venv/bin/activate
pip install -e ".[recognition]"
pip install -e ".[voice]"
./scripts/setup_venv.shInstall system packages manually (once on the Pi):
sudo apt update
sudo apt install -y \
python3-picamera2 python3-libcamera rpicam-apps \
python3-gpiozero python3-lgpio \
python3-opencv \
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
pkg-config libmtdev-dev xinput xfonts-base xfonts-scalable \
espeak-ng alsa-utils v4l-utils \
cmake build-essential libopenblas-dev liblapack-dev libjpeg-dev libsndfile1
sudo usermod -aG video,gpio $USEROr run ./scripts/setup_system.sh to install the packages and Ollama automatically.
Create the venv on the Pi with system site packages so apt libraries are visible:
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install -e ".[recognition]"
pip install -e ".[voice]"
./scripts/setup_venv.shOr run ./scripts/setup_venv.sh to create the venv and install the package.
Log out and back in for group membership. Do not pip install picamera2, opencv-python, or RPi.GPIO.
Primary experience on the Hosyond 5" DSI touchscreen:
pi-face-greeter-app- Face screen (default): Animated face with random blinking eyes and moving mouth during speech. Live camera preview in the upper-left corner; yellow box on detected faces. Greets with varied conversational phrases ("Hey Todd, good to see you. How are you doing today?") when recognized, or a friendly unknown greeting.
- Settings screen: Swipe left. Add, list, edit, and delete faces. Add Face captures photos from the live camera (same
CameraSourceas the face screen), computes face embeddings, and reloads recognition without restarting the app.
On Mac for UI development, set camera.backend: opencv in config/config.yaml and install dev deps: pip install -e ".[dev]".
UI settings in config/config.yaml under ui::
presence_frames_required— consecutive face-detection frames before starting recognitionrecognition_frames_required— same identity must confirm over N frames before greeting (reduces misfires)greet_cooldown_seconds— default per-person cooldown between greetings
Optional per-person overrides in config/people.yaml:
people:
- name: Todd
face_dir: data/known_faces/todd
greeting: "Welcome home, Todd!"
cooldown_seconds: 120The kiosk uses Piper neural TTS by default (tts.engine: piper) for a natural US female voice (en_US-amy-medium). espeak-ng is kept as an automatic fallback if Piper or the voice model is missing.
One-time setup on the Pi (included in ./scripts/setup_venv.sh):
pip install -e ".[voice]"
./scripts/setup_venv.sh # downloads ~63MB Piper model to data/voices/The voice model is downloaded once per Pi, not per face or greeting. To swap voices, change tts.piper.model and download a different .onnx from rhasspy/piper-voices.
Use tts.engine: espeak to force the old robotic voice. Toggle tts.ask_how_are_you: false to skip the follow-up question.
When enabled, the greeter asks a local Ollama small language model for a short, context-aware spoken greeting (name + time of day) instead of the canned phrases. Output is still spoken through Piper TTS. If Ollama is disabled or unreachable, behavior falls back to build_greeting automatically.
One-time setup (included in ./scripts/setup_system.sh and ./scripts/setup_venv.sh):
./scripts/setup_system.sh # installs Ollama (log out/in after)
./scripts/setup_venv.sh # pulls llama3.2:1b (or OLLAMA_MODEL=...)Enable in config/config.yaml:
ollama:
enabled: true
model: llama3.2:1b
timeout_seconds: 30 # increase if cold starts still time out on Pi
warmup_on_startup: true # loads model when kiosk starts
keep_alive: 10m # keeps model in RAM while app runsSmoke test:
pi-face-greeter-test-ollamaWhen face detection is not working, enable diagnostics to capture detailed logs and annotated camera snapshots:
PI_FACE_GREETER_DEBUG=1 pi-face-greeter-appOr set diagnostics.debug: true in config/config.yaml.
On startup the app prints the absolute paths for:
- Log file:
data/logs/greeter.log(frame stats, cascade path, detection params, face counts) - Debug snapshots:
data/debug/(JPEG every 2s with yellow boxes drawn on detected faces)
Share data/logs/greeter.log and the latest images from data/debug/ to diagnose detection issues. Adjust snapshot frequency via diagnostics.snapshot_interval_seconds.
Logs rotate automatically via logging.max_bytes and logging.backup_count in config/config.yaml. Total on-disk size is roughly max_bytes × (backup_count + 1) (default ~4 MB). Lower max_bytes if debug mode fills logs quickly — debug logs per-frame detection stats.
After camera and USB audio are wired, set tts.alsa_device if needed (see USB Audio Setup), then:
pi-face-greeter-validate-step1Output: one success line, or a failure summary with log path and tail. Full detail is in data/logs/greeter.log.
Success example: Step 1 passed. Frame: data/captured/step1_frame.jpg
Manual single greet (no PIR, no loop):
pi-face-greeter-greet-onceAfter Step 1 passes, enroll known people by capturing reference photos:
pi-face-greeter-enroll ToddOptions: --count 5 to override enrollment.capture_count in config.
Output: one success line or failure summary with log tail (same quiet pattern as Step 1).
Success example: Step 2 passed. Enrolled Todd: 5 photos in data/known_faces/todd
Photos are saved as 001.jpg, 002.jpg, … under data/known_faces/<slug>/, with stacked 128-d embeddings in encodings.npy. The person is registered in config/people.yaml.
Optional per-person greeting in config/people.yaml:
people:
- name: Todd
face_dir: data/known_faces/todd
greeting: "Welcome home, Todd!"Recognition tolerance (lower = stricter matching) in config/config.yaml:
recognition:
tolerance: 0.6If python3-opencv is installed on the Pi, each frame is checked for exactly one face during enrollment.
Do this last, after enrollment and recognition are working.
Wire the PIR, then set in config/config.yaml:
pir:
enabled: true
gpio_pin: 17Validate motion triggers a full greet cycle:
pi-face-greeter-validate-motionThen run the production loop:
pi-face-greeterLegacy note: pip install -r requirements.txt still works for runtime deps only. Prefer pip install -e ".[dev]" on Mac or pip install -e . on Pi.
Pi venv must use --system-site-packages so Picamera2, gpiozero, and OpenCV from apt are available.
See Deploy to Raspberry Pi for the apt install block.
From project root with venv activated on the Pi:
cd ~/pi_face_greeter
source .venv/bin/activatepi-face-greeter-apppi-face-greeter-validate-step1Or run components individually:
pi-face-greeter-test-camera
pi-face-greeter-test-ttspi-face-greeter-enroll ToddSet pir.enabled: true, then:
pi-face-greeter-validate-motion
pi-face-greeterpi-face-greeter-test-camera
pi-face-greeter-test-tts
pi-face-greeter-test-pirRequires pir.enabled: true in config.
pi-face-greeterConfiguration: config/config.yaml. Logs: data/logs/greeter.log.
A service template is in systemd/pi-face-greeter.service. After hardware validation:
# Edit paths in the service file first, then:
sudo cp systemd/pi-face-greeter.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable pi-face-greeter
sudo systemctl start pi-face-greeter
sudo systemctl status pi-face-greeterpi_face_greeter/
├── pyproject.toml
├── README.md
├── architecture/
│ ├── architecture.drawio
│ ├── architecture.svg
│ └── architecture.png
├── config/
│ ├── config.yaml
│ └── people.yaml
├── src/pi_face_greeter/
│ ├── app/ # Kivy kiosk UI
│ ├── main.py
│ ├── validate_step1.py
│ ├── greet_once.py
│ ├── cli.py
│ ├── camera.py
│ ├── tts.py
│ ├── pir_sensor.py
│ └── ...
├── scripts/ # thin wrappers (backward compatible)
├── tests/
├── data/
├── docs/
└── systemd/
- Confirm 3.3V on pin 1 (not 5V).
- Confirm OUT → GPIO17 (physical pin 11).
- Wait 15–30 s after power-on for sensor stabilization.
- Keep sensor away from heat sources.
- Repoint away from vents, windows, or moving curtains.
- Increase distance from the Pi board (PIR can be sensitive to warm electronics).
rpicam-hello --list-cameras- Reseat CSI ribbon with Pi powered off; contacts face the board.
- Enable camera in
raspi-config. - Try the other CSI port (
cam0vscam1).
aplay -l
speaker-test -D plughw:1,0 -c 2 -t wav- Set
tts.alsa_deviceinconfig/config.yaml. - Confirm speaker wired to sound card header with correct polarity.
- Check volume:
alsamixer(select USB card with F6).
- Use gpiozero with lgpio (installed via apt above).
- Do not use
RPi.GPIO— it does not work on Pi 5. - Ensure user is in the
gpiogroup:groupsshould listgpio.
- Recreate venv with system site packages:
python3 -m venv --system-site-packages .venv- Confirm apt package:
dpkg -l python3-picamera2
sudo usermod -aG video $USER
# log out and back inSee docs/roadmap.md for the full roadmap:
- FastAPI admin portal
- Ollama SLM — light local conversation
- systemd auto-start
- PIR motion loop (optional)
- Add touchscreen kiosk UI (Hosyond 5" DSI)
- Animated face with blinking eyes and talking mouth
- Live camera preview with face detection boxes
- Settings screen for face CRUD + live enrollment
- Face recognition via
face_recognition(dlib) - Generate face embeddings (
encodings.npy) - Confidence threshold (
recognition.tolerance) - Require multiple matching frames before greeting
- Per-person greeting messages (
greeting:in people.yaml) - Per-person cooldown (
cooldown_seconds:in people.yaml) - Enrollment photo capture from settings UI
- Piper TTS for natural voice (espeak fallback)
- Ollama SLM for light local conversation (opt-in; see docs/roadmap.md)
- Add local FastAPI admin portal
- Add systemd service for boot startup
- Add privacy mode / mute button
- Add optional logging to SQLite
- Add optional AWS sync later
MIT (or your chosen license)