Raspberry Pi Zero capture frontend for vidaugment (20sVA).
The Pi acts as an autonomous recording device that replaces the phone/browser capture flow:
┌─────────────────────────────────────────────────────┐
│ IDLE (low-power QR scan) │
│ Camera at 480×480 @ 15 fps, scanning for QR code │
│ from viewer.html │
└──────────────────┬──────────────────────────────────┘
│ QR detected → masterSessionId
▼
┌─────────────────────────────────────────────────────┐
│ CAPTURE (high-performance) │
│ 720×1280 @ 30 fps video + ALSA audio, 20 seconds │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ UPLOAD → POST /api/uploadVideo │
│ FINISH → POST /api/masterSession/<id>/finish │
└──────────────────┬──────────────────────────────────┘
│
▼ back to IDLE
| Component | Notes |
|---|---|
| Raspberry Pi Zero 2 W | Also works on Pi 3/4/5 |
| Pi Camera Module 3 | Connected via ribbon cable |
| I2S microphone (INMP441) | Default: via Google voiceHAT overlay on hw:0,0 |
| USB microphone (alternative) | Plugs in as hw:1,0 |
| Status LED (optional) | GPIO 17, simple on/off feedback |
| Piezo buzzer or vibration motor (optional) | GPIO 23, mutually exclusive – chosen at setup |
Pi GPIO 17 ──┤330Ω├── LED (+) ── GND
Pi GPIO 23 ────────── Buzzer (+) ── GND # if FEEDBACK_DEVICE=buzzer
Pi GPIO 23 ── NPN base (1kΩ) ── motor ── GND # if FEEDBACK_DEVICE=vibration
│
└─ flyback diode (1N4148) across motor leads
I2S INMP441:
BCLK/SCK → GPIO 18 (pin 12)
WS/LRCLK → GPIO 19 (pin 35)
SD/DOUT → GPIO 20 (pin 38)
VDD → 3.3 V
GND → GND
L/R → GND (left channel, mono)
- LED: Any standard 3mm/5mm LED with a 330Ω resistor in series. Long leg (anode) toward the resistor, short leg (cathode) to GND.
- Buzzer: Passive piezo buzzer (PWM-driven for tonal feedback).
+to GPIO,−to GND. No resistor needed. - Vibration motor: Small DC vibration motor (e.g. 3 V coin/disc). GPIO pins cannot source the motor current directly – drive it via an NPN transistor (e.g. 2N2222 / BC547) or N-channel MOSFET with a flyback diode across the motor to absorb back-EMF.
- I2S mic: INMP441 or SPH0645. Directly soldered, no external board needed. Exposed via the
googlevoicehat-soundcardoverlay.
LED and GPIO23 feedback device are optional — the software no-ops gracefully without them. Only one device may be wired to GPIO23 at a time; the choice is persisted in capture/config.env as FEEDBACK_DEVICE.
# 1. Clone onto the Pi
git clone https://github.com/helloworld-germany/PiZero.git
cd PiZero
# 2. Run setup (will prompt for sudo where needed)
./setup.sh
# setup includes hardware + chain validation prompts:
# LED/buzzer/button checks, backend reachability, camera capture test, mic test
# 3. Reboot if prompted (needed for I2S audio / tmpfs)
# After reboot the system is ready to run.
# 4. Run
source ~/.venvs/picapture/bin/activate
python -m captureAll settings live in capture/config.env (or as environment variables):
| Variable | Default | Description |
|---|---|---|
VIDAUGMENT_API_BASE_URL |
— | Backend URL (required) |
RECORD_DURATION_S |
20 |
Capture length in seconds |
VIDEO_WIDTH / VIDEO_HEIGHT |
720 / 1280 |
Capture resolution |
VIDEO_FPS |
30 |
Capture frame rate |
VIDEO_BITRATE |
2000000 |
H.264 target bitrate in bits per second |
QR_SCAN_WIDTH / QR_SCAN_HEIGHT |
480 / 480 |
QR scanner resolution |
QR_SCAN_FPS |
15 |
Idle scanner frame rate |
MIC_TYPE |
i2s |
Microphone: i2s, usb, or none |
AUDIO_UPLOAD_LEFT_ONLY |
true |
Upload only left channel for WAV audio |
AUDIO_UPLOAD_GAIN_DB |
0 |
Software gain in dB applied before upload |
CAPTURE_DIR |
/run/picapture |
Capture directory (tmpfs RAM disk) |
PAUSE_IDLE_TIMEOUT_S |
60 |
Auto-end session after this many seconds paused |
LED_PIN |
17 |
BCM GPIO pin for status LED |
FEEDBACK_DEVICE |
buzzer |
buzzer, vibration, or none (chosen at setup) |
BUZZER_PIN |
23 |
BCM GPIO pin for piezo buzzer |
VIBRATION_PIN |
23 |
BCM GPIO pin for vibration motor (mutually exclusive with buzzer) |
Recording uses rpicam-vid (Bookworm rpicam-apps) with native hardware
muxing: H.264 encoding + ALSA audio capture + MKV container — all in one
binary with near-zero CPU usage.
The default capture profile is portrait (720x1280). This is valid for
rpicam-vid; width does not need to be greater than height. It only affects
the encoded frame dimensions and resulting aspect ratio, not capture stability.
rpicam-vid -t 10 --audio-source alsa --audio-device plughw:0,0 \
--audio-channels 1 --audio-samplerate 48000 -o output.mkv
The microphone is selected by MIC_TYPE in capture/config.env:
MIC_TYPE |
ALSA device | Description |
|---|---|---|
i2s |
plughw:0,0 |
I2S mic via googlevoicehat overlay (default) |
usb |
plughw:<N>,0 |
USB microphone (auto-detected card number) |
none |
— | No audio |
Audio normalization is handled in the cloud after upload.
Run arecord -l to list available capture devices.
Enable the Google voiceHAT overlay in /boot/firmware/config.txt
(or /boot/config.txt on older images):
dtparam=i2s=on
dtoverlay=googlevoicehat-soundcard
Reboot, then verify:
arecord -l
# card 0: sndrpigooglevoi [...], device 0: ...Test recording:
arecord -D plughw:0,0 -f S32_LE -r 48000 -c 2 -d 3 test.wav
aplay test.wavIf backend QA reports low level audio, boost upload gain in capture/config.env:
AUDIO_UPLOAD_LEFT_ONLY=true
AUDIO_UPLOAD_GAIN_DB=28This boost is applied in the uploader thread right before HTTP upload and does
not affect real-time camera capture performance. For INMP441 wired with L/R
to GND, AUDIO_UPLOAD_LEFT_ONLY=true ensures only the active left channel is
uploaded.
Plug in a USB mic and check:
arecord -l
# card 1: ..., device 0: ...Then configure:
MIC_TYPE=usbBy default, recordings are written to /run/picapture, a tmpfs RAM disk.
This avoids SD card I/O during capture, resulting in significantly faster
writes and reduced SD card wear. The setup.sh script adds the mount
automatically via /etc/fstab:
tmpfs /run/picapture tmpfs nodev,nosuid,size=200M 0 0
The 200 MB size is sufficient for a single 20-second recording cycle. Files are uploaded and cleaned up before the next capture, so usage stays low. Since tmpfs lives in RAM, data is lost on reboot — this is fine because recordings are uploaded immediately after capture.
Configure WiFi via raspi-config or by editing
/etc/wpa_supplicant/wpa_supplicant.conf:
network={
ssid="YourNetwork"
psk="YourPassword"
}
The setup script can install a systemd service:
sudo systemctl enable picapture
sudo systemctl start picapture
sudo journalctl -u picapture -f # view logscapture/
__init__.py
__main__.py # python -m capture entry point
main.py # state machine orchestrator
config.py # settings from config.env / env vars
config.env # editable configuration
camera.py # picamera2 QR-scan mode
qr_scanner.py # low-power QR detection (pyzbar)
mic.py # audio device selection (I2S / ALSA / auto)
recorder.py # rpicam-vid native H.264+audio capture
uploader.py # HTTP upload & session finish
led.py # GPIO LED feedback
buzzer.py # GPIO piezo buzzer (PWM tonal feedback)
vibration.py # GPIO vibration motor (on/off pulse feedback, GPIO23 alt.)
feedback.py # dispatch façade: routes to buzzer OR vibration
setup.sh # one-shot Pi setup (sudo required)
requirements.txt # Python dependencies