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 soxEdit main.conf to enable HFP:
sudo nano /etc/bluetooth/main.confSet:
[General]
Name = playpi # Your device name
Class = 0x200404
Enable = Source,Sink,Headset,Gateway,Control
[Policy]
AutoEnable = trueSave and restart Bluetooth:
sudo systemctl restart bluetoothCreate a system service:
sudo nano /etc/systemd/system/pulseaudio.serviceAdd:
[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.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable pulseaudio
sudo systemctl start pulseaudioEdit system.pa:
sudo nano /etc/pulse/system.paReplace 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 pulseaudioStart bluetoothctl:
bluetoothctlRun:
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 cardsIf Active Profile: off:
pactl set-card-profile bluez_card.AA_BB_CC_DD_EE_FF headset_audio_gatewayVerify:
pactl list sinksLook 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_gatewayHFP 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.wavShould 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 1Make 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 cardsIf off, repeat Step 5.
Verify playback:
pactl list sink-inputsDuring aplay, ensure it targets VirtualMic.
Logs:
sudo journalctl -u pulseaudioReconnect:
bluetoothctl
disconnect AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FFTest file content:
aplay test.wav # Needs local audio output- MAC Address: Replace
AA:BB:CC:DD:EE:FFwith 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!