Real-time Canon EOS and Sony Alpha photo sync to cloud storage, running on Raspberry Pi 4.
Captures photos from your Canon or Sony camera (USB or WiFi) and uploads them to Google Photos, Google Drive, S3, or other cloud targets — configured remotely from the dslr2cloud-server dashboard.
- The client runs on a Raspberry Pi 4 connected to your Canon or Sony camera
- It detects new photos as you shoot (via gphoto2 over USB, or Canon CCAPI over WiFi)
- Photos are queued locally in SQLite and uploaded to your configured cloud connectors
- The server dashboard manages which cloud targets to use, folder paths, and project settings
- Everything works offline — photos queue locally and upload when connectivity returns
- Raspberry Pi 4 (2GB+ RAM recommended) or any Linux machine
- Python 3.11+
- Canon EOS camera (USB cable or WiFi-enabled model for CCAPI), or Sony Alpha camera (USB cable)
- Network connection (WiFi, Ethernet, or phone hotspot) for cloud uploads
- A running dslr2cloud-server instance
sudo apt-get update && sudo apt-get upgrade -ysudo apt-get install -y git python3 python3-pip python3-venv gphoto2 libgphoto2-devcd ~
git clone https://github.com/yourusername/dslr2cloud-client.git
cd dslr2cloud-clientpython3 -m venv .venv
source .venv/bin/activatepip install -e ".[dev]"cp .env.example .env
nano .envUpdate these values:
# Point to your dslr2cloud server
SERVER_URL=https://your-server.example.com
# Camera interface: gphoto2 (USB) or ccapi (WiFi)
CAMERA_INTERFACE=gphoto2
# For WiFi cameras using CCAPI, uncomment and set:
# CAMERA_IP=192.168.1.100USB connection (Canon & Sony):
- Plug your camera into any USB port on the Pi
- Turn the camera on
- Canon: Set USB mode to PTP/PC Connection in the camera menu
- Sony: Set USB Connection to PC Remote or Mass Storage (Settings → USB → USB Connection)
- Verify the camera is detected:
gphoto2 --auto-detectYou should see your camera model listed (e.g., Canon EOS R6 or Sony Alpha-A7 IV (ILCE-7M4)). If not, check the USB cable and camera USB settings.
WiFi connection (Canon CCAPI only):
- Enable CCAPI on your camera (requires Canon's CCAPI activation tool — see Canon developer docs)
- Connect the Pi and camera to the same WiFi network
- Set
CAMERA_INTERFACE=ccapiandCAMERA_IP=<camera IP>in.env
./scripts/run.shThe script activates the venv automatically and starts the web dashboard and photo sync pipeline. Open http://<pi-ip>:8080 in any browser on the same network.
To also launch a Tkinter desktop window (if a display is attached to the Pi):
./scripts/run.sh --desktopFirst time only: make the script executable with
chmod +x scripts/run.sh
On the server dashboard, register a new device and copy the pairing code (e.g. ABCD-1234).
Then open the web dashboard at http://<pi-ip>:8080 — the pairing card is shown at the top when the device is unpaired. Enter the code and click Pair Device.
This reads the Pi's MAC address, sends it to the server, and stores the API key locally. You only need to do this once — the pairing persists in ~/.dslr2cloud/device.db.
Create the service file:
sudo nano /etc/systemd/system/dslr2cloud.servicePaste this content:
[Unit]
Description=DSLR2Cloud Client — Real-time photo sync
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/dslr2cloud-client
ExecStart=/home/pi/dslr2cloud-client/.venv/bin/python src/main.py
Restart=always
RestartSec=10
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.targetNote: If your username is not
pi, replace/home/piwith your actual home directory.
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable dslr2cloud
sudo systemctl start dslr2cloudsudo systemctl status dslr2cloudYou should see active (running). On the server dashboard, your device should show as online. The web dashboard is accessible at http://<pi-ip>:8080.
The client includes a built-in web dashboard accessible from any device on the same network at http://<pi-ip>:8080.
Features:
- Live status — service state, camera connection, server reachability, queue depth (polls every 2s)
- Device pairing — enter pairing code directly in the browser
- Service controls — start/stop the photo sync pipeline
- Upload statistics — today's uploads, success rate, recent upload history
- Queue monitor — pending jobs per connector, recent failures
- Configuration — view server-synced config, connectors, and local settings
Configuration:
| Variable | Default | Description |
|---|---|---|
GUI_PORT |
8080 |
Web dashboard port |
GUI_HOST |
0.0.0.0 |
Bind address (0.0.0.0 = all interfaces, 127.0.0.1 = local only) |
For Raspberry Pi units with a touchscreen or monitor attached, a Tkinter desktop window is available:
python src/main.py --desktop
# or set GUI_DESKTOP=true in .envThe desktop GUI shows a compact status display with service controls and an "Open Web Dashboard" button. It polls the same local API as the web dashboard.
# Check status
sudo systemctl status dslr2cloud
# View live logs
journalctl -u dslr2cloud -f
# Restart (e.g., after a config change)
sudo systemctl restart dslr2cloud
# Stop
sudo systemctl stop dslr2cloud
# Disable auto-start
sudo systemctl disable dslr2cloudsource .venv/bin/activate
python -m pytest tests/ -vblack src/ tests/
isort src/ tests/
ruff check src/ tests/python scripts/test_camera.pyAll configuration is in .env. The server can override most settings remotely — local .env values are used as defaults and for bootstrap (server URL, camera interface).
| Variable | Default | Description |
|---|---|---|
SERVER_URL |
http://localhost:8000 |
URL of the dslr2cloud server |
LOG_LEVEL |
INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
STAGING_DIR |
/tmp/dslr2cloud/staging |
Temp directory for photos before upload |
DB_PATH |
~/.dslr2cloud/device.db |
SQLite database path |
PHOTO_CHECK_INTERVAL |
5 |
Seconds between camera polls |
CONFIG_SYNC_INTERVAL |
300 |
Seconds between server config syncs |
HEARTBEAT_INTERVAL |
60 |
Seconds between heartbeats to server |
CAMERA_INTERFACE |
gphoto2 |
Camera interface: gphoto2 or ccapi |
CAMERA_IP |
(empty) | IP address for CCAPI WiFi cameras |
MAX_CONCURRENT_UPLOADS |
2 |
Parallel uploads per connector |
UPLOAD_TIMEOUT |
300 |
Upload timeout in seconds |
MAX_RETRIES |
3 |
Max upload retry attempts |
RETRY_DELAY |
30 |
Initial retry delay (exponential backoff) |
GUI_PORT |
8080 |
Web dashboard port |
GUI_HOST |
0.0.0.0 |
Dashboard bind address |
- Powered USB hub: The Pi 4 provides 1.2A total across USB ports. Canon and Sony cameras can draw 500mA+. Use a powered hub for long shoots to avoid disconnects.
- Heat management: The Pi can throttle under sustained load. A heatsink or fan case helps during continuous uploads.
- SD card: Use a high-endurance microSD (32GB+). The SQLite database and staging directory write frequently.
- WiFi vs Ethernet: Ethernet is more reliable for uploads. WiFi works but may drop during large RAW file transfers (50MB+ per file).
- Battery: For outdoor shoots, a USB-C power bank (10000mAh+, 5V/3A) can run the Pi for several hours.
Camera not detected (gphoto2)
# Kill any process holding the camera
sudo killall gvfs-gphoto2-volume-monitor 2>/dev/null
gphoto2 --auto-detectSony camera not detected
- Set USB Connection to PC Remote (Settings → USB → USB Connection)
- Some Sony models need USB LUN Setting set to Multi
- Try a different USB cable — some cables are charge-only
Permission denied on USB
# Add your user to the plugdev group
sudo usermod -aG plugdev $USER
# Then log out and back inPairing fails
- Check that the server is reachable:
curl https://your-server.com/api/v1/pair - Verify the pairing code hasn't expired (15-minute TTL)
- Check if the MAC address is already paired to another device on the server
Uploads stuck in queue
# Check queue status in the database
sqlite3 ~/.dslr2cloud/device.db "SELECT connector_id, status, COUNT(*) FROM upload_queue GROUP BY connector_id, status;"Service won't start
# Check for errors
journalctl -u dslr2cloud --no-pager -n 50dslr2cloud-client/
├── README.md # This file
├── CLAUDE.md # Agent context
├── pyproject.toml # Project config + dependencies
├── .env.example # Configuration template
├── src/
│ ├── main.py # Entry point
│ ├── config.py # Config loading
│ ├── database.py # SQLite setup + helpers
│ ├── identity.py # MAC address + device identity
│ ├── service_state.py # Shared runtime state
│ ├── camera/ # Camera adapters (gphoto2 for Canon/Sony/Nikon, CCAPI for Canon WiFi)
│ ├── pipeline/ # Photo watcher, staging, upload queue
│ ├── connectors/ # Cloud upload targets
│ ├── server/ # Server communication (config, heartbeat, pairing)
│ └── gui/ # Web dashboard (FastAPI) + desktop GUI (Tkinter)
│ ├── app.py # FastAPI app factory
│ ├── desktop.py # Tkinter desktop window
│ ├── routes/ # API endpoints + dashboard route
│ ├── static/ # CSS + JS
│ └── templates/ # Jinja2 HTML templates
├── tests/ # pytest test suite
└── scripts/
├── run.sh # Start the client (auto-detects venv)
├── install.sh # Automated Pi setup
└── test_camera.py # Manual camera test
MIT