Modernized the web interface with a mobile-first responsive design: - Added CSS custom properties (variables) for consistent theming - Implemented responsive breakpoints (640px, 768px) for mobile/tablet/desktop - Applied fluid typography using clamp() for automatic font scaling - Enhanced animations (fade-in, slide-in, scale effects) for smooth UX - Improved component styling with modern shadows and hover effects - Added backdrop blur effects on modals - Optimized touch targets (min 44px height) for mobile accessibility - Updated button groups and layouts to stack on mobile - Enhanced visual hierarchy with better spacing and typography - Improved recordings grid with responsive card layout Template version bumped from 1.9.2 to 2.0.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Wedding Phone - Vintage Rotary Phone Audio System
A Raspberry Pi-based rotary phone system for weddings and events. Guests can pick up the handset to hear custom greeting messages and leave voice recordings. Features a modern web interface for managing messages and recordings.
Quick Start
# 1. Clone and enter directory
git clone https://git.b4l.co.th/grabowski/wedding-phone.git
cd wedding-phone
# 2. Install UV package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
# 3. Create config from example
cp config.example.json config.json
nano config.json # Edit GPIO pins and audio device
# 4. Install dependencies
make sync
# 5. Configure HiFiBerry (if using)
./configure_hifiberry.sh
# 6. Test audio
make test
# 7. Run the system
make start
# 8. Optional: Install as service for auto-start
./install_service.sh
# 9. Optional: Setup USB backup
sudo ./setup_usb.sh
Web interface available at: http://<raspberry-pi-ip>:8080
Table of Contents
- Quick Start
- Features
- Hardware Requirements
- Software Requirements
- Installation
- Running the System
- Usage
- Configuration
- Systemd Service
- Troubleshooting
- API Endpoints
- File Structure
- Contributing
- License
Features
- Vintage Phone Integration: Uses a real rotary phone with GPIO hook detection
- Custom Greeting Messages: Upload multiple greeting messages and select which one plays
- Voice Recording: Automatically records guest messages after the greeting
- Web Interface: Beautiful, responsive web UI for managing the system
- Audio Playback: Play recordings and greetings directly in the browser
- Volume Control: Adjust playback volume with real-time slider (0-100%)
- Multiple Message Support: Upload and manage multiple greeting messages
- Active Message Selector: Choose which greeting plays when the phone is picked up
- Extra Button Support: Optional GPIO button to play custom sounds during recording
- USB Backup: Automatic backup to multiple USB drives with CRC32 verification
- Data Integrity: Every file write is verified with CRC checksums
- HiFiBerry Support: Optimized for HiFiBerry DAC+ADC Pro audio quality
- Production-Ready: Uses Waitress WSGI server for stable, production-grade web serving
- Real-time Status: Monitor phone status (on-hook/off-hook/recording)
- Auto-refresh: Status updates every 5 seconds
Hardware Requirements
- Raspberry Pi (3/4/5 or Zero 2 W)
- HiFiBerry DAC+ADC Pro (or similar audio interface)
- Vintage rotary phone with hookswitch
- Speaker (connected to HiFiBerry output)
- Microphone (connected to HiFiBerry input, or use phone handset mic)
Software Requirements
- Raspberry Pi OS (Bullseye or newer)
- Python 3.8+ (required for Flask 2.3+)
- UV package manager (recommended) or pip
- Make (for simplified commands)
- Required Python packages:
- flask>=2.3.0
- numpy>=1.21.0
- pyaudio>=0.2.13
- RPi.GPIO>=0.7.1
- waitress>=2.1.0 (production WSGI server)
- scipy>=1.7.0 (audio resampling utility)
Installation
1. Clone the Repository
git clone https://git.b4l.co.th/grabowski/wedding-phone.git
cd wedding-phone
2. Install UV (Recommended)
UV is a fast Python package installer and resolver. Install it:
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or on Raspberry Pi with pip:
pip3 install uv
3. Install Dependencies
Option A: Using UV (Recommended)
# Install system dependencies
sudo apt-get update
sudo apt-get install -y python3-pyaudio portaudio19-dev
# Install Python dependencies with UV
make sync
# Or manually:
# uv pip install flask numpy pyaudio RPi.GPIO waitress
Option B: Using pip
sudo apt-get update
sudo apt-get install -y python3-pip python3-pyaudio portaudio19-dev
pip3 install flask numpy RPi.GPIO waitress
4. Configure HiFiBerry
Run the automatic configuration script:
chmod +x configure_hifiberry.sh
./configure_hifiberry.sh
Or follow the manual instructions in AUDIO_FIX.md.
5. Create Configuration File
Copy the example configuration and customize it:
cp config.example.json config.json
nano config.json # or use your preferred editor
Important settings to configure:
{
"gpio": {
"hook_pin": 17, // GPIO pin for hookswitch
"hook_pressed_state": "LOW", // "LOW" or "HIGH"
"extra_button_enabled": true, // Enable extra button feature
"extra_button_pin": 27, // GPIO pin for extra button
"extra_button_pressed_state": "LOW" // "LOW" or "HIGH"
},
"audio": {
"device_index": 1, // HiFiBerry device index
"sample_rate": 44100
},
"paths": {
"base_dir": "./rotary_phone_data" // Relative or absolute path
},
"web": {
"port": 8080
}
}
Finding your audio device index:
python3 -c "import pyaudio; p=pyaudio.PyAudio(); [print(f'{i}: {p.get_device_info_by_index(i)[\"name\"]}') for i in range(p.get_device_count())]"
6. Test Your Audio
python3 test_complete.py
This will test:
- Speaker playback
- Dial tone generation
- Microphone recording
7. Run the System
Option A: Using Make (Recommended)
# Run the wedding phone system
make start
# Or run the audio test
make test
Option B: Using UV Directly
# Run the wedding phone system
uv run python rotary_phone_web.py
# Or run the audio test
uv run python test_complete.py
Option C: Run with Python
python3 rotary_phone_web.py
The web interface will be available at:
http://localhost:8080http://<raspberry-pi-ip>:8080
8. Install as System Service (Optional)
To run the wedding phone automatically on boot:
# Run the installer script
./install_service.sh
The installer will:
- Check dependencies
- Install the systemd service
- Configure for your user and project path
- Optionally enable and start the service
Manual service commands:
# Enable service to start on boot
sudo systemctl enable wedding-phone
# Start service now
sudo systemctl start wedding-phone
# Stop service
sudo systemctl stop wedding-phone
# Restart service
sudo systemctl restart wedding-phone
# View logs (live)
sudo journalctl -u wedding-phone -f
# View status
sudo systemctl status wedding-phone
# Disable service
sudo systemctl disable wedding-phone
Usage
Web Interface
The web interface provides four main sections:
1. Phone Status
- Shows current phone state (on-hook/off-hook/recording)
- Displays active recording filename
- Auto-refreshes every 5 seconds
2. Volume Control
- Adjust Volume: Drag slider to set playback volume (0-100%)
- Real-time visual feedback with percentage display
- Changes apply immediately to greeting playback
- Volume setting persists across restarts
3. Greeting Messages
- Upload: Click "Choose WAV File(s)" to upload one or multiple greeting messages
- Play: Click "▶️ Play" to preview any greeting in your browser
- Set Active: Click "⭐ Set Active" to select which greeting plays when the phone is picked up
- Set Button: Click "🔘 Set Button" to assign sound to extra button (if enabled)
- Delete: Remove unwanted greetings (cannot delete the active one)
- Default Tone: Generate a classic telephone dial tone
4. Recordings
- Play: Listen to recordings directly in the browser
- Download: Save individual recordings to your computer
- Download All: Download all recordings as a single ZIP file with timestamp
- Delete: Remove unwanted recordings
- Statistics: View total recordings, storage used, and total duration
Phone Operation
- Guest picks up phone: System detects via GPIO
- Greeting plays: Active greeting message plays through speaker
- Recording starts: After greeting, system records guest message
- Guest hangs up: Recording stops and saves automatically
- Ready for next call: System returns to waiting state
Extra Button Operation (Optional)
If enabled in config.json:
- Guest picks up phone: Phone goes off-hook, greeting plays
- Recording starts: After greeting finishes
- Guest presses button: System detects GPIO signal (only works during recording)
- Sound plays: Configured button sound plays through speaker
- Can be pressed multiple times: Works throughout the recording phase
- Debounced: 0.5s delay prevents accidental double-presses
Note: Button only responds during the recording phase (not during greeting or when on-hook)
Greeting Delay (Optional)
Configure a delay before the greeting plays:
- Set
greeting_delay_secondsinconfig.json(0-10 seconds) - Useful for giving guests a moment after picking up
- Example: 2 second delay gives time to position the phone
- Default: 0 (greeting plays immediately)
USB Backup (Optional)
Automatically backup all recordings and greeting files to USB drives:
Configuration (config.json):
{
"backup": {
"enabled": true,
"usb_paths": [
"/media/usb0",
"/media/usb1"
],
"verify_crc": true,
"backup_on_write": true
}
}
Features:
- Multiple USB Drives: Backup to one or more USB drives simultaneously
- CRC32 Verification: Every backup is verified with CRC checksum
- Automatic Backup: Files are backed up immediately after recording/upload
- Integrity Check: Corrupted backups are automatically deleted
- Web Monitoring: View USB drive status, free space, and test backups
Web Interface:
- Monitor USB drive status (mounted, writable, free space)
- Test backup functionality with one click
- View real-time backup results
- Green = Ready, Yellow = Not Writable, Red = Not Mounted
Backup Structure:
/media/usb0/
└── wedding-phone-backup/
├── recordings/
│ ├── recording_20250124_143022.wav
│ └── recording_20250124_143145.wav
└── sounds/
├── dialtone.wav
└── greeting.wav
How It Works:
- Recording finishes or greeting uploaded
- File saved to main storage
- CRC32 checksum calculated for source file
- File copied to each USB drive
- CRC32 checksum verified on each copy
- Corrupted copies deleted automatically
- Success/failure logged to console
Mount USB Drives with Proper Permissions:
Automated Setup (Easiest):
# Run the interactive USB setup script
sudo ./setup_usb.sh
This interactive script will:
- Scan for all connected USB drives
- Let you select which drives to auto-mount
- Choose custom mount point names (e.g., usb0, usb1, backup)
- Create systemd auto-mount units
- Handle permissions automatically (uid/gid)
- Work even when USB drives are plugged in after boot
Option 1: Mount with user permissions (Recommended)
# Find your USB device
lsblk
# Create mount point
sudo mkdir -p /media/usb0
# Mount with user ownership (replace $USER with your username if needed)
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/sda1 /media/usb0
# Verify it's writable
touch /media/usb0/test.txt && rm /media/usb0/test.txt
Option 2: Auto-mount in /etc/fstab with user permissions
# Get USB UUID
sudo blkid /dev/sda1
# Edit fstab
sudo nano /etc/fstab
# Add line (replace UUID and username):
UUID=XXXX-XXXX /media/usb0 vfat defaults,nofail,uid=1000,gid=1000 0 0
# Note: uid=1000 is usually the first user, check with: id -u
Option 3: Change ownership after mounting
# Mount normally
sudo mount /dev/sda1 /media/usb0
# Change ownership (replace with your username)
sudo chown -R $USER:$USER /media/usb0
Quick Setup Script:
#!/bin/bash
# setup_usb.sh - Mount USB drives with proper permissions
# Create mount points
sudo mkdir -p /media/usb0 /media/usb1
# Mount USB drives with user ownership
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/sda1 /media/usb0
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/sdb1 /media/usb1
echo "USB drives mounted!"
ls -la /media/usb0 /media/usb1
File Structure
wedding-phone/
├── rotary_phone_web.py # Main application (Flask + GPIO + Audio)
├── test_complete.py # Audio testing script
├── configure_hifiberry.sh # HiFiBerry DAC+ADC setup script
├── install_service.sh # Systemd service installer (interactive)
├── setup_usb.sh # Interactive USB auto-mount setup (systemd)
├── wedding-phone.service # Systemd service file template
├── Makefile # Make commands (start, test, sync, clean)
├── config.example.json # Example configuration (copy to config.json)
├── pyproject.toml # Python package configuration
├── AUDIO_FIX.md # Audio troubleshooting guide
├── CHANGELOG.md # Version history and upgrade notes
├── README.md # This file (main documentation)
├── .gitignore # Git ignore rules
└── templates/ # Auto-generated on first run
└── index.html # Web interface (auto-generated from script)
Note: The templates/index.html file is automatically generated when you first run rotary_phone_web.py. If you update the script and want to regenerate the template with new features, simply delete the templates directory and restart the script.
Runtime Data (Auto-created)
rotary_phone_data/ # Default location (configurable)
├── recordings/ # Voice recordings from guests
├── sounds/ # Greeting message WAV files
└── user_config.json # Runtime settings (volume, active greeting)
Configuration
All configuration is done via the config.json file. No need to edit Python code!
Auto-Update Feature
The system automatically adds missing configuration settings with default values when it starts:
- config.json - System configuration is auto-updated with any missing settings
- user_config.json - User settings are auto-updated with new defaults
- Console shows which settings were added:
[CONFIG] Added missing setting: system.volume_beep = 70 - Original config is preserved - only missing keys are added
- No manual editing required when upgrading to newer versions
This means you can upgrade the code and your old config files will work automatically!
Configuration File Structure
The config.json file contains all system settings:
{
"gpio": {
"hook_pin": 17, // GPIO pin number for hookswitch
"hook_pressed_state": "LOW", // "LOW" or "HIGH" depending on switch
"extra_button_enabled": true, // Enable optional extra button
"extra_button_pin": 27, // GPIO pin for extra button
"extra_button_pressed_state": "LOW" // Button pressed state
},
"audio": {
"device_index": 1, // Audio device index (run test to find)
"chunk_size": 1024, // Audio buffer size
"format": "paInt16", // Audio format (16-bit)
"channels": 1, // Mono audio
"sample_rate": 48000, // 48kHz sample rate
"max_record_seconds": 300 // Max recording time (5 minutes)
},
"paths": {
"base_dir": "./rotary_phone_data", // Data directory (relative or absolute)
"recordings_dir": "recordings", // Subdirectory for recordings
"sounds_dir": "sounds" // Subdirectory for greeting sounds
},
"backup": {
"enabled": true, // Enable USB backup
"usb_paths": ["/media/usb0", "/media/usb1"], // USB mount points
"verify_crc": true, // Verify backups with CRC32
"backup_on_write": true // Backup immediately after write
},
"web": {
"port": 8080, // Web interface port
"max_upload_size_mb": 50 // Max upload file size
},
"system": {
"active_greeting": "dialtone.wav", // Default greeting
"extra_button_sound": "button_sound.wav", // Default button sound
"beep_sound": "beep.wav", // Recording start beep
"beep_enabled": true, // Enable beep before recording
"greeting_delay_seconds": 0, // Delay before greeting plays (0-10)
"volume": 70 // Default volume (0-100)
}
}
Finding Your Audio Device
To find your HiFiBerry or other audio device index:
python3 -c "import pyaudio; p=pyaudio.PyAudio(); [print(f'{i}: {p.get_device_info_by_index(i)[\"name\"]}') for i in range(p.get_device_count())]"
Look for your HiFiBerry device and note its index number, then set it in config.json.
Troubleshooting
No Sound from Speaker
-
Check HiFiBerry configuration:
aplay -l # List audio devices amixer -c 3 sset Digital 100% # Set volume (adjust card number) -
Test speaker directly:
speaker-test -D plughw:3,0 -c 1 -t wav -
Run the complete test:
python3 test_complete.py
Microphone Not Recording
- Check microphone is connected to HiFiBerry input
- Adjust input gain:
alsamixer -c 3 - Test recording:
arecord -D plughw:3,0 -f cd test.wav -d 5 aplay test.wav
GPIO Not Detecting Hookswitch
- Verify GPIO pin number in
config.json - Check if switch is normally open or closed
- Update
hook_pressed_stateinconfig.json("LOW" or "HIGH") - Test with a simple script:
import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) print(GPIO.input(17)) # Should change when switch toggles
Web Interface Not Accessible
- Check if Flask is running:
ps aux | grep python - Verify port in
config.jsonmatches URL - Check firewall:
sudo ufw allow 8080(or your configured port) - Check IP address:
hostname -I - Try localhost:
http://127.0.0.1:8080
Audio Sample Rate Mismatch
If you see errors like Expression 'paInvalidSampleRate' failed or warnings about sample rate mismatches:
-
Check your config.json sample rate (default is 48000Hz):
"audio": { "sample_rate": 48000 } -
Resample your audio files to match the configured rate:
# Using the provided resampling script python3 resample_audio.py # Or manually with ffmpeg ffmpeg -i input.wav -ar 48000 output.wav -
Delete default sounds to regenerate at correct rate:
rm rotary_phone_data/sounds/dialtone.wav rm rotary_phone_data/sounds/beep.wav # These will be regenerated at startup
The resample_audio.py utility will:
- Automatically detect the target sample rate from
config.json - Create
.backupfiles before modifying originals - Resample all WAV files in the sounds directory
- Preserve stereo/mono and bit depth
Configuration Errors
If the script won't start:
- Ensure
config.jsonexists (copy fromconfig.example.json) - Validate JSON syntax:
python3 -m json.tool config.json - Check all paths exist or can be created
- Verify audio device index is correct
API Endpoints
The system provides REST API endpoints:
GET /- Web interfaceGET /api/status- Phone status JSONGET /api/recordings- List all recordingsGET /api/greetings- List all greeting messagesGET /api/volume- Get current volume settingPOST /api/volume- Set volume level (0-100)POST /upload_greeting- Upload new greetingPOST /set_active_greeting- Set active greetingPOST /delete_greeting/<filename>- Delete greetingGET /play_audio/<type>/<filename>- Stream audio fileGET /download/<filename>- Download recordingGET /download_all- Download all recordings as ZIPPOST /delete/<filename>- Delete recordingPOST /restore_default_sound- Generate default dial toneGET /api/backup/status- Get USB backup drive statusPOST /api/backup/test- Test backup to all USB drivesGET /api/greeting_delay- Get current greeting delayPOST /api/greeting_delay- Set greeting delay (0-10 seconds)
Contributing
Contributions are welcome! Here's how you can help:
- Report Bugs: Open an issue describing the bug and how to reproduce it
- Suggest Features: Open an issue describing your feature idea
- Submit Pull Requests: Fork the repo, make changes, and submit a PR
- Improve Documentation: Help make the docs clearer and more complete
- Share Your Experience: Post photos/videos of your wedding phone setup!
Development Guidelines
- Follow existing code style
- Test your changes thoroughly
- Update documentation for new features
- Add entries to CHANGELOG.md
- Use descriptive commit messages
Version History
See CHANGELOG.md for detailed version history and upgrade notes.
Current Version: 1.2.0
Major Updates:
- ✅ USB backup with CRC verification
- ✅ Systemd service support
- ✅ UV package manager integration
- ✅ Greeting delay control
- ✅ Extra button support
- ✅ Immediate hook detection
- ✅ Configurable everything (no hardcoded paths)
License
This project is open source and available for personal and commercial use.
License: MIT License
Feel free to:
- ✅ Use for personal projects
- ✅ Use for commercial events
- ✅ Modify and customize
- ✅ Redistribute with attribution
Credits
Created by: grabowski Purpose: Capture guest messages at weddings and events in a unique, nostalgic way Inspired by: Vintage telephone systems and the desire to preserve wedding memories
Technologies Used
- Python 3.8+
- Flask web framework
- PyAudio for audio handling
- RPi.GPIO for hardware control
- UV package manager
- HiFiBerry DAC+ADC Pro
- Systemd for service management
Support & Community
- Issues: GitHub Issues
- Documentation: This README and CHANGELOG.md
- Repository: https://git.b4l.co.th/grabowski/wedding-phone
Acknowledgments
Thanks to everyone who has contributed ideas, bug reports, and improvements to make this project better!
Made with ❤️ for preserving wedding memories 🎉📞💍