feat: Add Raspberry Pi webcam capture and upload demo with install/uninstall scripts, config, and README.
This commit is contained in:
131
demo/README.md
Normal file
131
demo/README.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Raspberry Pi Webcam Capture Demo
|
||||||
|
|
||||||
|
Automated picture capture and upload for Raspberry Pi 3 with a USB webcam (Logitech 1080p).
|
||||||
|
|
||||||
|
## Quick Setup (5 minutes)
|
||||||
|
|
||||||
|
### 1. Install Required Packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y fswebcam curl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure the Script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy the config template
|
||||||
|
cp picupper.conf.example picupper.conf
|
||||||
|
|
||||||
|
# Edit with your settings
|
||||||
|
nano picupper.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Update `picupper.conf` with your API key and camera settings.
|
||||||
|
|
||||||
|
### 3. Install & Enable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Make scripts executable
|
||||||
|
chmod +x capture-upload.sh install.sh
|
||||||
|
|
||||||
|
# Run installer (sets up cron job)
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Your Pi will now capture and upload one picture per minute.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Included
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `capture-upload.sh` | Main script - captures photo and uploads |
|
||||||
|
| `picupper.conf.example` | Configuration template |
|
||||||
|
| `install.sh` | Installer script (sets up cron) |
|
||||||
|
| `uninstall.sh` | Removes cron job |
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
Edit `picupper.conf`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PicUpper server URL
|
||||||
|
API_URL="https://dev.seedheads.de/picUploadApi/upload"
|
||||||
|
|
||||||
|
# Your API key (from server admin)
|
||||||
|
API_KEY="your-api-key-here"
|
||||||
|
|
||||||
|
# Camera identifier (unique name for this Pi)
|
||||||
|
CAMERA_ID="rpi-webcam-1"
|
||||||
|
|
||||||
|
# Resolution (1920x1080 for full HD)
|
||||||
|
RESOLUTION="1920x1080"
|
||||||
|
|
||||||
|
# Video device (usually /dev/video0)
|
||||||
|
VIDEO_DEVICE="/dev/video0"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test capture only (saves to /tmp)
|
||||||
|
./capture-upload.sh --test
|
||||||
|
|
||||||
|
# Run full capture and upload once
|
||||||
|
./capture-upload.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check if webcam is detected
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls /dev/video*
|
||||||
|
v4l2-ctl --list-devices
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test capture manually
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fswebcam -d /dev/video0 -r 1920x1080 --no-banner test.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View recent capture logs
|
||||||
|
tail -f /var/log/picupper.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
| Problem | Solution |
|
||||||
|
|---------|----------|
|
||||||
|
| "No video device" | Replug USB webcam, check `dmesg` |
|
||||||
|
| "Permission denied" | Add user to video group: `sudo usermod -aG video $USER` |
|
||||||
|
| "Upload failed" | Check API key and network connection |
|
||||||
|
|
||||||
|
## Changing Capture Frequency
|
||||||
|
|
||||||
|
Default is 1 picture per minute. To change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit cron job
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- Every 5 minutes: `*/5 * * * * /home/pi/picupper-demo/capture-upload.sh`
|
||||||
|
- Every 30 seconds: Use the included systemd timer (see advanced setup)
|
||||||
|
|
||||||
|
## Hardware Requirements
|
||||||
|
|
||||||
|
- Raspberry Pi 3 (any model)
|
||||||
|
- USB webcam (Logitech C920/C922/C270 recommended)
|
||||||
|
- SD card with Raspberry Pi OS
|
||||||
|
- Network connection (WiFi or Ethernet)
|
||||||
|
|
||||||
|
## Network Notes
|
||||||
|
|
||||||
|
The script retries failed uploads and logs all attempts. If your Pi loses network, captures continue and uploads resume when connectivity returns.
|
||||||
168
demo/capture-upload.sh
Normal file
168
demo/capture-upload.sh
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# capture-upload.sh - Capture from USB webcam and upload to PicUpper
|
||||||
|
# For Raspberry Pi 3 with Logitech USB webcam
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CONFIG_FILE="${SCRIPT_DIR}/picupper.conf"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Load Configuration
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo "ERROR: Configuration file not found: $CONFIG_FILE"
|
||||||
|
echo "Please copy picupper.conf.example to picupper.conf and configure it."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
|
# Validate required settings
|
||||||
|
if [[ -z "${API_KEY:-}" || "$API_KEY" == "your-api-key-here" ]]; then
|
||||||
|
echo "ERROR: API_KEY not configured in $CONFIG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set defaults for optional settings
|
||||||
|
API_URL="${API_URL:-https://dev.seedheads.de/picUploadApi/upload}"
|
||||||
|
CAMERA_ID="${CAMERA_ID:-rpi-webcam}"
|
||||||
|
VIDEO_DEVICE="${VIDEO_DEVICE:-/dev/video0}"
|
||||||
|
RESOLUTION="${RESOLUTION:-1920x1080}"
|
||||||
|
SKIP_FRAMES="${SKIP_FRAMES:-5}"
|
||||||
|
TEMP_DIR="${TEMP_DIR:-/tmp}"
|
||||||
|
LOG_FILE="${LOG_FILE:-/var/log/picupper.log}"
|
||||||
|
TIMEOUT="${TIMEOUT:-30}"
|
||||||
|
MAX_RETRIES="${MAX_RETRIES:-3}"
|
||||||
|
RETRY_DELAY="${RETRY_DELAY:-5}"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Helper Functions
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
log() {
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
echo "[$timestamp] $*" | tee -a "$LOG_FILE" 2>/dev/null || echo "[$timestamp] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
log "ERROR: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "${TEMP_FILE:-}" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Test Mode
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
if [[ "${1:-}" == "--test" ]]; then
|
||||||
|
echo "=== PicUpper Test Mode ==="
|
||||||
|
echo "Config file: $CONFIG_FILE"
|
||||||
|
echo "API URL: $API_URL"
|
||||||
|
echo "Camera ID: $CAMERA_ID"
|
||||||
|
echo "Video device: $VIDEO_DEVICE"
|
||||||
|
echo "Resolution: $RESOLUTION"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check video device
|
||||||
|
if [[ ! -e "$VIDEO_DEVICE" ]]; then
|
||||||
|
echo "❌ Video device not found: $VIDEO_DEVICE"
|
||||||
|
echo " Available devices:"
|
||||||
|
ls -la /dev/video* 2>/dev/null || echo " No video devices found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Video device exists: $VIDEO_DEVICE"
|
||||||
|
|
||||||
|
# Check fswebcam
|
||||||
|
if ! command -v fswebcam &>/dev/null; then
|
||||||
|
echo "❌ fswebcam not installed. Run: sudo apt install fswebcam"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ fswebcam installed"
|
||||||
|
|
||||||
|
# Try capture
|
||||||
|
TEST_FILE="${TEMP_DIR}/picupper_test_$(date +%s).jpg"
|
||||||
|
echo "Capturing test image to: $TEST_FILE"
|
||||||
|
|
||||||
|
if fswebcam -d "$VIDEO_DEVICE" -r "$RESOLUTION" -S "$SKIP_FRAMES" --no-banner "$TEST_FILE" 2>/dev/null; then
|
||||||
|
FILE_SIZE=$(stat -f%z "$TEST_FILE" 2>/dev/null || stat -c%s "$TEST_FILE" 2>/dev/null)
|
||||||
|
echo "✓ Capture successful: $TEST_FILE (${FILE_SIZE} bytes)"
|
||||||
|
echo ""
|
||||||
|
echo "To view the image: gpicview $TEST_FILE"
|
||||||
|
else
|
||||||
|
echo "❌ Capture failed. Check webcam connection and permissions."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Main Capture & Upload
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
TEMP_FILE="${TEMP_DIR}/picupper_${CAMERA_ID}_$(date +%s).jpg"
|
||||||
|
|
||||||
|
# Check video device
|
||||||
|
if [[ ! -e "$VIDEO_DEVICE" ]]; then
|
||||||
|
log_error "Video device not found: $VIDEO_DEVICE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Capture image
|
||||||
|
log "Capturing from $VIDEO_DEVICE ($RESOLUTION)"
|
||||||
|
if ! fswebcam -d "$VIDEO_DEVICE" -r "$RESOLUTION" -S "$SKIP_FRAMES" --no-banner "$TEMP_FILE" 2>/dev/null; then
|
||||||
|
log_error "Capture failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILE_SIZE=$(stat -c%s "$TEMP_FILE" 2>/dev/null || echo "unknown")
|
||||||
|
log "Captured: $TEMP_FILE ($FILE_SIZE bytes)"
|
||||||
|
|
||||||
|
# Upload with retry
|
||||||
|
upload_success=false
|
||||||
|
attempt=1
|
||||||
|
|
||||||
|
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||||||
|
log "Upload attempt $attempt/$MAX_RETRIES"
|
||||||
|
|
||||||
|
HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||||
|
--max-time "$TIMEOUT" \
|
||||||
|
-X POST \
|
||||||
|
-H "X-API-Key: $API_KEY" \
|
||||||
|
-F "image=@$TEMP_FILE" \
|
||||||
|
-F "cameraId=$CAMERA_ID" \
|
||||||
|
"$API_URL" 2>/dev/null) || true
|
||||||
|
|
||||||
|
HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -n1)
|
||||||
|
RESPONSE_BODY=$(echo "$HTTP_RESPONSE" | sed '$d')
|
||||||
|
|
||||||
|
if [[ "$HTTP_CODE" == "200" ]]; then
|
||||||
|
log "Upload successful (HTTP $HTTP_CODE)"
|
||||||
|
upload_success=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log_error "Upload failed (HTTP $HTTP_CODE): $RESPONSE_BODY"
|
||||||
|
|
||||||
|
if [[ $attempt -lt $MAX_RETRIES ]]; then
|
||||||
|
log "Retrying in $RETRY_DELAY seconds..."
|
||||||
|
sleep "$RETRY_DELAY"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$upload_success" != "true" ]]; then
|
||||||
|
log_error "All upload attempts failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Done"
|
||||||
94
demo/install.sh
Normal file
94
demo/install.sh
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# install.sh - Set up PicUpper webcam capture on Raspberry Pi
|
||||||
|
# Creates cron job for 1 picture per minute
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CAPTURE_SCRIPT="${SCRIPT_DIR}/capture-upload.sh"
|
||||||
|
CONFIG_FILE="${SCRIPT_DIR}/picupper.conf"
|
||||||
|
LOG_FILE="/var/log/picupper.log"
|
||||||
|
|
||||||
|
echo "=== PicUpper Installer ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
echo "Checking dependencies..."
|
||||||
|
|
||||||
|
if ! command -v fswebcam &>/dev/null; then
|
||||||
|
echo "❌ fswebcam not installed"
|
||||||
|
echo " Run: sudo apt update && sudo apt install -y fswebcam"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ fswebcam"
|
||||||
|
|
||||||
|
if ! command -v curl &>/dev/null; then
|
||||||
|
echo "❌ curl not installed"
|
||||||
|
echo " Run: sudo apt update && sudo apt install -y curl"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ curl"
|
||||||
|
|
||||||
|
# Check config
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "❌ Configuration file not found!"
|
||||||
|
echo " Please create it first:"
|
||||||
|
echo " cp picupper.conf.example picupper.conf"
|
||||||
|
echo " nano picupper.conf"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Config file exists"
|
||||||
|
|
||||||
|
# Check API key is configured
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
if [[ -z "${API_KEY:-}" || "$API_KEY" == "your-api-key-here" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "❌ API key not configured in picupper.conf"
|
||||||
|
echo " Edit the file and set your API key"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ API key configured"
|
||||||
|
|
||||||
|
# Make scripts executable
|
||||||
|
chmod +x "$CAPTURE_SCRIPT"
|
||||||
|
chmod +x "${SCRIPT_DIR}/uninstall.sh" 2>/dev/null || true
|
||||||
|
echo "✓ Scripts are executable"
|
||||||
|
|
||||||
|
# Create log file if needed
|
||||||
|
if [[ ! -f "$LOG_FILE" ]]; then
|
||||||
|
sudo touch "$LOG_FILE"
|
||||||
|
sudo chown "$USER:$USER" "$LOG_FILE"
|
||||||
|
echo "✓ Log file created: $LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "✓ Log file exists: $LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up cron job
|
||||||
|
CRON_JOB="* * * * * $CAPTURE_SCRIPT >> $LOG_FILE 2>&1"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Setting up cron job for 1 picture per minute..."
|
||||||
|
|
||||||
|
# Check if cron job already exists
|
||||||
|
if crontab -l 2>/dev/null | grep -q "$CAPTURE_SCRIPT"; then
|
||||||
|
echo "⚠ Cron job already exists, skipping"
|
||||||
|
else
|
||||||
|
# Add to crontab
|
||||||
|
(crontab -l 2>/dev/null || true; echo "$CRON_JOB") | crontab -
|
||||||
|
echo "✓ Cron job added"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Installation Complete ==="
|
||||||
|
echo ""
|
||||||
|
echo "Your Raspberry Pi will now capture a picture every minute."
|
||||||
|
echo ""
|
||||||
|
echo "Useful commands:"
|
||||||
|
echo " View logs: tail -f $LOG_FILE"
|
||||||
|
echo " Test capture: $CAPTURE_SCRIPT --test"
|
||||||
|
echo " Run manually: $CAPTURE_SCRIPT"
|
||||||
|
echo " Uninstall: ${SCRIPT_DIR}/uninstall.sh"
|
||||||
|
echo ""
|
||||||
|
echo "To change frequency, edit crontab: crontab -e"
|
||||||
47
demo/picupper.conf.example
Normal file
47
demo/picupper.conf.example
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# PicUpper Configuration
|
||||||
|
# Copy this file to picupper.conf and update with your settings
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SERVER SETTINGS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# API endpoint URL
|
||||||
|
API_URL="https://dev.seedheads.de/picUploadApi/upload"
|
||||||
|
|
||||||
|
# Your API key (get this from the PicUpper server admin)
|
||||||
|
API_KEY="your-api-key-here"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# CAMERA SETTINGS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Unique identifier for this camera (no spaces, use hyphens)
|
||||||
|
CAMERA_ID="rpi-webcam-1"
|
||||||
|
|
||||||
|
# Video device path (run 'ls /dev/video*' to find yours)
|
||||||
|
VIDEO_DEVICE="/dev/video0"
|
||||||
|
|
||||||
|
# Capture resolution (common options: 1920x1080, 1280x720, 640x480)
|
||||||
|
RESOLUTION="1920x1080"
|
||||||
|
|
||||||
|
# Skip first N frames (helps camera adjust exposure, default 5)
|
||||||
|
SKIP_FRAMES=5
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ADVANCED SETTINGS (usually don't need to change)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Temporary file location
|
||||||
|
TEMP_DIR="/tmp"
|
||||||
|
|
||||||
|
# Log file location
|
||||||
|
LOG_FILE="/var/log/picupper.log"
|
||||||
|
|
||||||
|
# Connection timeout in seconds
|
||||||
|
TIMEOUT=30
|
||||||
|
|
||||||
|
# Retry failed uploads (0 = no retry)
|
||||||
|
MAX_RETRIES=3
|
||||||
|
|
||||||
|
# Delay between retries in seconds
|
||||||
|
RETRY_DELAY=5
|
||||||
29
demo/uninstall.sh
Normal file
29
demo/uninstall.sh
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# uninstall.sh - Remove PicUpper cron job
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CAPTURE_SCRIPT="${SCRIPT_DIR}/capture-upload.sh"
|
||||||
|
|
||||||
|
echo "=== PicUpper Uninstaller ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Remove cron job
|
||||||
|
if crontab -l 2>/dev/null | grep -q "$CAPTURE_SCRIPT"; then
|
||||||
|
crontab -l | grep -v "$CAPTURE_SCRIPT" | crontab -
|
||||||
|
echo "✓ Cron job removed"
|
||||||
|
else
|
||||||
|
echo "⚠ No cron job found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Uninstall complete."
|
||||||
|
echo ""
|
||||||
|
echo "Note: Log file and configuration are preserved."
|
||||||
|
echo " Config: ${SCRIPT_DIR}/picupper.conf"
|
||||||
|
echo " Logs: /var/log/picupper.log"
|
||||||
|
echo ""
|
||||||
|
echo "To completely remove, also delete:"
|
||||||
|
echo " rm -rf ${SCRIPT_DIR}"
|
||||||
|
echo " sudo rm /var/log/picupper.log"
|
||||||
Reference in New Issue
Block a user