Author: Mario Klingemann
This guide configures a Raspberry Pi Zero W to stream WAV files (8kHz, mono PCM) to a phone via Bluetooth Hands-Free Profile (HFP), simulating a microphone for calls. Tested on Raspbian Bullseye with a Pixel 8 Pro and Pixel 6 Pro, it should work with similar setups.
Note: due to restrictions of Android it seems that headset microphones are not allowed to use certain features like voice assistant or voice typing on the keyboard. This seems to be an ongoing nuisance and I have not found a solution for it.
Note: this might not work on a Raspberry Zero 2 W, since it is using a different bluetooth chip which seems to have certain quirks.
- Hardware: Raspberry Pi Zero W (Cypress CYW43438 Bluetooth)
- OS: Raspbian Bullseye (kernel 5.10+ recommended)
- Software: bluez, pulseaudio, pulseaudio-module-bluetooth, alsa-utils, mpg123 (for MP3, optional), sox (for file conversion)
- WAV File: 8kHz, mono PCM (S16LE or U8), non-empty (check with
ls -l
) - Phone: Bluetooth-capable (e.g., Android/iOS with HFP support)
Update and install Bluetooth, audio, and conversion tools:
sudo apt update
sudo apt install bluez pulseaudio pulseaudio-module-bluetooth alsa-utils mpg123 sox
Edit main.conf to enable HFP:
sudo nano /etc/bluetooth/main.conf
Set:
[General]
Name = playpi # Your device name
Class = 0x200404
Enable = Source,Sink,Headset,Gateway,Control
[Policy]
AutoEnable = true
Save and restart Bluetooth:
sudo systemctl restart bluetooth
Create a system service:
sudo nano /etc/systemd/system/pulseaudio.service
Add:
[Unit]
Description=PulseAudio system server
After=sound.target
[Service]
Type=simple
ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disable-shm
Restart=on-failure
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable pulseaudio
sudo systemctl start pulseaudio
Edit system.pa:
sudo nano /etc/pulse/system.pa
Replace with:
### Core defaults
load-module module-device-restore
load-module module-stream-restore
load-module module-card-restore
.ifexists module-udev-detect.so
load-module module-udev-detect
.else
load-module module-detect
.endif
load-module module-native-protocol-unix
load-module module-default-device-restore
load-module module-always-sink
load-module module-suspend-on-idle
load-module module-position-event-sounds
### Virtual sink for WAV playback
load-module module-null-sink sink_name=VirtualMic sink_properties=device.description=VirtualMic
### Bluetooth support
load-module module-bluetooth-policy
load-module module-bluetooth-discover headset=auto
Restart:
sudo systemctl restart pulseaudio
Start bluetoothctl:
bluetoothctl
Run:
power on
discoverable on
pairable on
scan on
On your phone, find and pair with playpi
. Note your phone's MAC address (e.g., run scan on
to find it—example: AA:BB:CC:DD:EE:FF
).
In bluetoothctl:
pair AA:BB:CC:DD:EE:FF
trust AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF
exit
On phone: Enable "Phone Calls" in Bluetooth settings for playpi.
Check profile:
pactl list cards
If Active Profile: off:
pactl set-card-profile bluez_card.AA_BB_CC_DD_EE_FF headset_audio_gateway
Verify:
pactl list sinks
Look for bluez_sink.AA_BB_CC_DD_EE_FF.headset_audio_gateway
.
Load loopback (after pairing):
pactl load-module module-loopback source=VirtualMic.monitor sink=bluez_sink.AA_BB_CC_DD_EE_FF.headset_audio_gateway
HFP requires 8kHz, mono PCM (S16LE or U8). Convert any file using sox:
Example (MP3 to WAV):
sox input.mp3 -t wav -r 8000 -c 1 -e signed-integer -b 16 output.wav
-r 8000
: Sets 8kHz sample rate-c 1
: Mono-e signed-integer -b 16
: S16LE PCM (common for HFP)
Check:
file output.wav
Should show: WAV ... 8000 Hz, mono, 16 bit
.
Use a non-empty WAV (e.g., from Step 7):
ls -l output.wav # Ensure size > 0 (e.g., ~16KB/sec)
Example (create test tone):
sox -n -t wav test.wav synth 5 sine 440 rate 8000 channels 1
Make a call:
- Call a second phone, ensuring playpi is the audio source (tap Bluetooth icon in call if needed)
- Play:
aplay -D pulse:VirtualMic test.wav
- Ask: "Do you hear a tone?" (Other party should hear it)
Check profile:
pactl list cards
If off, repeat Step 5.
Verify playback:
pactl list sink-inputs
During aplay, ensure it targets VirtualMic.
Logs:
sudo journalctl -u pulseaudio
Reconnect:
bluetoothctl
disconnect AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF
Test file content:
aplay test.wav # Needs local audio output
- MAC Address: Replace
AA:BB:CC:DD:EE:FF
with your phone's actual MAC - WAV Format: 8kHz, mono PCM (S16LE/U8) is critical—use sox to convert
- No Local Sound: Normal—audio goes to the call, not the phone's speaker
Credit: Developed with Grok (xAI) on Feb 21, 2025—a rare solution for this use case!