PiWave is a Python module designed to manage and control your Raspberry Pi radio using multiple FM transmission backends. It provides a unified interface for broadcasting audio files with multiple backends support and RDS (Radio Data System) support.
- Multi-Backend Architecture: Supports multiple backends for different actions
- Wide Frequency Support: 1-250 MHz coverage through different backends
- RDS Support: Program Service, Radio Text, and Program Identifier broadcasting
- Live Stream Support: Broadcast live from a stream source.
- Smart Backend Selection: Automatically chooses the best backend to suit your needs
- Audio Format Support: Converts most audio formats (MP3, FLAC, M4A, etc.) to WAV
- Real-time Settings Updates: Change frequency, RDS data, and settings without restart
- Advanced Playback Control: Play, pause, resume, stop, and loop functionality
- CLI Interface: Command-line tools for backend management and broadcasting
- Detailed Logging: Debug mode with comprehensive error handling
- Event Callbacks: Custom handlers for track changes and errors
- Non-blocking Operation: Threading-based playback with status monitoring
- Frequency Range: 80.0 - 108.0 MHz (Standard FM band)
- RDS Support: âś… Full support (PS, RT, PI)
- Live Support: ❌ No live support
- Repository: ChristopheJacquet/PiFmRds
- Best For: Standard FM broadcasting with RDS features
- Frequency Range: 1.0 - 250.0 MHz (Extended range)
- RDS Support: ❌ No RDS support
- Live Support: âś… Experimental support
- Repository: markondej/fm_transmitter
- Best For: Non-standard frequencies and experimental broadcasting
To use PiWave for broadcasting, you need to set up the hardware correctly:
-
Connect the Antenna:
- Attach a cable or antenna to GPIO 4 (Pin 7) on the Raspberry Pi
- Ensure secure connection for optimal signal quality
- Use appropriate antenna length for your target frequency
-
GPIO Configuration:
- GPIO 4 (Pin 7) is used for FM signal output
- No additional hardware modifications required
Warning
Legal Disclaimer: Broadcasting radio signals may be subject to local regulations and laws. It is your responsibility to ensure compliance with all applicable legal requirements in your area. Unauthorized broadcasting may result in legal consequences, including fines or penalties.
Liability: The author is not responsible for any damage, loss, or legal issues arising from the use of this software. Users accept all risks and liabilities associated with operation and broadcasting capabilities.
Use the automated installer script:
curl -sL https://setup.piwave.xyz/ | sudo bashThis installs PiWave dependencies and the PiFmRds backend automatically.
# Install with fm_transmitter backend (may affect system stability)
curl -sL https://setup.piwave.xyz/ | sudo bash -s -- --install_fmt
# Skip confirmation prompts
curl -sL https://setup.piwave.xyz/ | sudo bash -s -- --install_fmt --no-waitcurl -sL https://setup.piwave.xyz/uninstall | sudo bash-
Install Dependencies:
sudo apt update sudo apt install -y python3 python3-pip ffmpeg git make libsndfile1-dev
-
Install PiWave:
# Create virtual environment (recommended) python3 -m venv ~/piwave-env source ~/piwave-env/bin/activate # Install PiWave pip install git+https://github.com/douxxtech/piwave.git
-
Install Backends:
PiFmRds (Recommended):
git clone https://github.com/ChristopheJacquet/PiFmRds /opt/PiWave/PiFmRds cd /opt/PiWave/PiFmRds/src makeFmTransmitter (Optional):
sudo apt install -y libraspberrypi-dev git clone https://github.com/markondej/fm_transmitter /opt/PiWave/fm_transmitter cd /opt/PiWave/fm_transmitter make
from piwave import PiWave
# Initialize with automatic backend selection
pw = PiWave(
frequency=90.0,
ps="MyRadio",
rt="Playing great music",
pi="ABCD",
debug=True
)
# Play a single audio file
pw.play("song.mp3")
# Stop playback
pw.stop()from piwave import PiWave
# Automatic selection (recommended)
pw = PiWave(frequency=95.0, backend="auto")
# Force specific backend
pw = PiWave(frequency=95.0, backend="pi_fm_rds")
# Extended frequency range with fm_transmitter
pw = PiWave(frequency=150.0, backend="fm_transmitter")from piwave import PiWave
pw = PiWave()
# Update multiple settings at once
pw.update(
frequency=101.5,
ps="NewName",
rt="Updated radio text",
debug=True
)
# Individual setting updates
pw.update(frequency=102.1)
pw.update(ps="Radio2024")from piwave import PiWave
pw = PiWave(frequency=95.0, loop=True)
# Playback control
pw.play("music.mp3")
pw.pause()
pw.resume()
pw.stop()
# Status monitoring
status = pw.get_status()
print(f"Playing: {status['is_playing']}")
print(f"Current backend: {status['current_backend']}")
print(f"Backend supports RDS: {status['backend_supports_rds']}")
print(f"Frequency range: {status['backend_frequency_range']}")# Search for available backends on system
python3 -m piwave search
# List cached backends
python3 -m piwave list
# Manually add backend executable path
python3 -m piwave add pi_fm_rds /path/to/pi_fm_rds
# Show package information
python3 -m piwave info
# Broadcast a file directly
python3 -m piwave broadcast song.mp3 --frequency 101.5 --ps "MyRadio"from piwave.backends import discover_backends, list_backends, search_backends
# Load cached backends
discover_backends()
# Search for new backends (ignores cache)
search_backends()
# List available backends with details
backends_info = list_backends()Click to expand examples
from piwave import PiWave
import os
import time
def smart_radio_station():
"""Automatically selects best backend for each frequency"""
stations = [
{"freq": 88.5, "name": "Jazz FM", "file": "jazz.mp3"},
{"freq": 101.5, "name": "Rock Radio", "file": "rock.mp3"},
{"freq": 150.0, "name": "Experimental", "file": "experimental.wav"}
]
for station in stations:
try:
# PiWave automatically selects the best backend
pw = PiWave(
frequency=station["freq"],
ps=station["name"][:8], # Max 8 chars
rt=f"Broadcasting {station['name']}",
backend="auto" # Let PiWave choose
)
print(f"Starting {station['name']} on {station['freq']}MHz")
print(f"Using backend: {pw.get_status()['current_backend']}")
if os.path.exists(station["file"]):
pw.play(station["file"])
# Wait for completion or user interrupt
while pw.get_status()['is_playing']:
time.sleep(1)
print(f"{station['name']} completed")
else:
print(f"File {station['file']} not found")
pw.cleanup()
except Exception as e:
print(f"Error with {station['name']}: {e}")
if __name__ == "__main__":
smart_radio_station()from gtts import gTTS
from piwave import PiWave
from pydub import AudioSegment
import os
import sys
import time
def tts_radio():
"""Text-to-speech radio with automatic backend selection using update() method"""
print("=" * 50)
print("TTS Radio, original: https://git.new/SEdemCA")
print("=" * 50)
pw = None
wav_file = "tts_radio.wav"
try:
# Initialize PiWave once with default settings
print("Initializing PiWave...")
pw = PiWave(
frequency=90.0, # Default frequency
ps="TTS-FM",
rt="Text-to-Speech Radio",
backend="auto",
silent=False
)
print("PiWave initialized successfully!")
print(f"Initial backend: {pw.get_status()['current_backend']}")
print("=" * 50)
while True:
print("\n" + "=" * 30)
text = input("Text to broadcast: ").strip()
if not text:
print("No text entered, skipping...\n")
continue
try:
freq = float(input("Frequency (MHz): "))
except ValueError:
print("Invalid frequency, please enter a number.\n")
continue
# Let user choose backend or use auto
backend_choice = input("Backend (auto/pi_fm_rds/fm_transmitter) [auto]: ").strip()
if not backend_choice:
backend_choice = "auto"
# Generate TTS
print("Generating speech...")
mp3_file = "temp_tts.mp3"
tts = gTTS(text=text, lang="en", slow=False)
tts.save(mp3_file)
# Convert to WAV
sound = AudioSegment.from_mp3(mp3_file)
sound.export(wav_file, format="wav")
os.remove(mp3_file)
try:
# update pw settings
update_params = {
'frequency': freq,
'rt': text[:64]
}
update_params['backend'] = backend_choice
print("Updating broadcast settings...")
pw.update(**update_params)
status = pw.get_status()
print(f"\nBroadcast Configuration:")
print(f"Frequency: {freq}MHz")
print(f"Backend: {status['current_backend']}")
print(f"RDS Support: {'Yes' if status['backend_supports_rds'] else 'No'}")
print(f"Text: {text}")
print("=" * 50)
pw.play(wav_file)
print("Broadcasting! Press Ctrl+C to stop...\n")
# Wait for completion
while pw.get_status()['is_playing']:
time.sleep(0.5)
print("Broadcast completed!\n")
except Exception as e:
print(f"Update/Broadcast error: {e}")
print("Continuing with current settings...\n")
continue
except KeyboardInterrupt:
print("\nStopped by user.")
except Exception as e:
print(f"Initialization error: {e}")
print("Make sure you're running on a Raspberry Pi as root with PiWave dependencies installed.")
finally:
if pw:
pw.cleanup()
# Cleanup temp files
for temp_file in [wav_file, "temp_tts.mp3"]:
if os.path.exists(temp_file):
os.remove(temp_file)
print("Cleanup completed.")
if __name__ == "__main__":
tts_radio()Click to expand API reference
PiWave(
frequency=90.0, # Broadcast frequency (1.0-250.0 MHz)
ps="PiWave", # Program Service name (max 8 chars)
rt="PiWave: ...", # Radio Text (max 64 chars)
pi="FFFF", # Program Identifier (4 hex digits)
debug=False, # Enable debug logging
silent=False, # Disable all logging
loop=False, # Loop current track continuously
backend="auto", # Backend selection ("auto", "pi_fm_rds", "fm_transmitter")
used_for="file_broadcast", # Backend main purpose, used if backend = auto ("file_broadcast", "live_broadcast")
on_track_change=None, # Callback for track changes
on_error=None # Callback for errors
)Start playing an audio file with automatic format conversion.
pw.play("song.mp3") # Returns True if started successfullyStop all playback and clean up processes.
pw.stop()Pause and resume playback control.
pw.pause()
pw.resume()Update settings in real-time. Accepts any initialization parameter.
pw.update(frequency=101.5, ps="NewName", backend="fm_transmitter")Get comprehensive status information.
status = pw.get_status()
# Returns:
{
'is_playing': bool,
'is_live_streaming': bool,
'frequency': float,
'current_file': str|None,
'current_backend': str,
'backend_frequency_range': str,
'backend_supports_rds': bool,
'available_backends': list,
'ps': str,
'rt': str,
'pi': str,
'loop': bool
}Clean up resources and temporary files.
[!WARNING] Always clean up behind you! Dropped support of auto-cleanup on version > 2.1.2
pw.cleanup()from piwave.backends import discover_backends, search_backends, list_backends
# Load cached backend availability
discover_backends()
# Perform fresh search (updates cache)
search_backends()
# List available backends with details
backend_info = list_backends()from piwave.backends import get_best_backend
# Get best backend for specific frequency
backend_name = get_best_backend("file_broadcast", 95.0)# Backend management
python3 -m piwave search # Search for backends
python3 -m piwave list # List cached backends
python3 -m piwave add pi_fm_rds /path/exe # Add backend path
python3 -m piwave info # Package information
# Direct broadcasting
python3 -m piwave broadcast file.mp3 \
--frequency 101.5 \
--ps "MyRadio" \
--rt "Great Music" \
--pi "ABCD" \
--backend auto \
--loop \
--debugPiWave includes comprehensive error handling:
- Environment Validation: Raspberry Pi and root access checks
- Backend Validation: Automatic detection and compatibility verification
- Frequency Validation: Ensures frequency is within backend's supported range
- File Validation: Checks file existence and format compatibility
- Process Management: Clean process termination and resource cleanup
- Exception Callbacks: Custom error handlers for applications
from piwave import PiWave, PiWaveError
try:
pw = PiWave(frequency=50.0, backend="pi_fm_rds") # Outside range
except PiWaveError as e:
print(f"Configuration error: {e}")
# Try with auto backend selection
pw = PiWave(frequency=50.0, backend="auto")from piwave.backends.base import Backend
class CustomBackend(Backend):
@property
def name(self):
return "custom_backend"
@property
def frequency_range(self):
return (50.0, 200.0) # MHz range
@property
def supports_rds(self):
return True # RDS capability
@property
def supports_loop(self):
return True # loop support capability
def _get_executable_name(self):
return "my_transmitter"
def _get_search_paths(self):
return ["/opt", "/usr/local/bin", "/usr/bin"]
def build_command(self, wav_file: str, loop: bool):
cmd = ['sudo', self.required_executable, '-f', str(self.frequency)]
if self.supports_rds and self.ps:
cmd.extend(['-ps', self.ps])
if self.supports_rds and loop:
cmd.extend(['-loop'])
cmd.append(wav_file)
return cmdfrom piwave.backends import backend_classes
# Register custom backend
backend_classes["custom_backend"] = CustomBackend
# Re-discover backends
from piwave.backends import discover_backends
discover_backends()-
"No suitable backend found"
python3 -m piwave search # Refresh backend cache -
"Backend doesn't support frequency"
# Check supported ranges python3 -m piwave list # Use auto selection pw = PiWave(frequency=your_freq, backend="auto")
-
"Process failed to start"
- Ensure running as root:
sudo python3 your_script.py - Verify backend installation:
python3 -m piwave list - Check executable permissions
- Ensure running as root:
-
Audio conversion issues
- Install FFmpeg:
sudo apt install ffmpeg - Check file format support
- Verify file permissions
- Install FFmpeg:
Enable comprehensive logging:
pw = PiWave(debug=True)
# or
pw.update(debug=True)Manually specify backend paths:
# Find backend executable
sudo find /opt -name "pi_fm_rds" -type f
# Add to PiWave
python3 -m piwave add pi_fm_rds /opt/PiWave/PiFmRds/src/pi_fm_rds- Backend Selection:
pi_fm_rdsgenerally provides better stability for standard FM frequencies - Audio Conversion: WAV files play immediately; other formats require conversion time
- Memory Usage: Large audio files are streamed, not loaded entirely into memory
- CPU Impact: FM transmission is CPU-intensive; avoid other heavy processes during broadcast
- Raspberry Pi (any model with GPIO)
- Root access (
sudo) - Python 3.7+
- FFmpeg for audio conversion
- At least one backend installed (PiFmRds or fm_transmitter)
sudo apt install -y python3 python3-pip ffmpeg git make libsndfile1-dev# For extended frequency range
sudo apt install -y libraspberrypi-dev
# For Python virtual environment (recommended)
sudo apt install -y python3-venvPiWave is licensed under the GNU General Public License (GPL) v3.0. See the LICENSE file for details.
Contributions are welcome! Areas for contribution:
- Additional backend implementations
- Improved frequency range detection
- Enhanced RDS functionality
- Performance optimizations
- Documentation improvements
Please submit pull requests or open issues on GitHub.
- ChristopheJacquet/PiFmRds - Primary FM/RDS backend
- markondej/fm_transmitter - Extended frequency backend
PiWave - FM Broadcasting module for Raspberry Pi
