Bluetooth on Raspberry Pi

树莓派3 as 蓝牙音响

$ sudo apt install -y pulseaudio-module-bluetooth
$ sudo vi /etc/pulse/system.pa

- load-module module-native-protocol-unix
+ load-module module-native-protocol-unix auth-anonymous=1
+ load-module module-native-protocol-tcp auth-anonymous=1

+ ### Automatically load driver modules for Bluetooth hardware
+ .ifexists module-bluetooth-policy.so
+ load-module module-bluetooth-policy
+ .endif
+
+ .ifexists module-bluetooth-discover.so
+ load-module module-bluetooth-discover
+ .endif

$ sudo pulseaudio --system --disallow-exit
W: [pulseaudio] main.c: Running in system mode, but --disallow-module-loading not set!  
N: [pulseaudio] main.c: Running in system mode, forcibly disabling SHM mode!  
N: [pulseaudio] main.c: Running in system mode, forcibly disabling exit idle time!  
W: [pulseaudio] main.c: OK, so you are running PA in system mode. Please note that you most likely shouldn't be doing that.  
W: [pulseaudio] main.c: If you do it nonetheless then it's your own fault if things don't work as expected.  
W: [pulseaudio] main.c: Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an explanation why system mode is usually a bad idea.  
E: [pulseaudio] bluez4-util.c: org.bluez.Manager.GetProperties() failed: org.freedesktop.DBus.Error.UnknownMethod: Method "GetProperties" with signature "" on interface "org.bluez.Manager" doesn't exist

$ speaker-test -t wav

$ paplay test.wav

module-native-protocol-tcp is for docker container remote access.

# /etc/systemd/system/pulseaudio.service

[Unit]
Description=PulseAudio Sound System  
Before=sound.target

[Service]
BusName=org.pulseaudio.Server  
ExecStart=/usr/bin/pulseaudio --system --disallow-exit  
Restart=always

[Install]
WantedBy=multi-user.target  
$ bluetoothctl
>>> list
Controller B8:27:EB:31:F6:8A raspberrypi [default]  
>>> power on
Changing power on succeeded  
[CHG] Controller B8:27:EB:31:F6:8A Powered: yes
>>> scan on
Discovery started  
[CHG] Controller B8:27:EB:31:F6:8A Discovering: yes
[CHG] Device 9C:99:A0:CA:63:22 RSSI: -70
>>> devices
Device 9C:99:A0:CA:63:22 Redmi  
>>> info 9C:99:A0:CA:63:22
Device 9C:99:A0:CA:63:22  
        Name: Redmi
        Alias: Redmi
        Class: 0x5a020c
        Icon: phone
        Paired: no
        Trusted: no
        Blocked: no
        Connected: no
        LegacyPairing: no
>>> pair 9C:99:A0:CA:63:22
Attempting to pair with 9C:99:A0:CA:63:22  
>>> paired-devices
Device 9C:99:A0:CA:63:22 Redmi  
>>> trust 9C:99:A0:CA:63:22
Changing 9C:99:A0:CA:63:22 trust succeeded  
>>> connect 9C:99:A0:CA:63:22
Attempting to connect to 9C:99:A0:CA:63:22  
[CHG] Device 9C:99:A0:CA:63:22 Connected: yes
Connection successful  
>>> info 9C:99:A0:CA:63:22
Device 9C:99:A0:CA:63:22  
        Name: Redmi
        Alias: Redmi
        Class: 0x5a020c
        Icon: phone
        Paired: yes
        Trusted: yes
        Blocked: no
        Connected: yes
        LegacyPairing: no
>>> disconnect 9C:99:A0:CA:63:22
>>> untrust 9C:99:A0:CA:63:22
>>> remove 9C:99:A0:CA:63:22
>>> exit

树莓派3 to 蓝牙音箱

$ $ sudo pulseaudio --system --disallow-exit
W: [pulseaudio] main.c: Running in system mode, but --disallow-module-loading not set!  
N: [pulseaudio] main.c: Running in system mode, forcibly disabling SHM mode!  
N: [pulseaudio] main.c: Running in system mode, forcibly disabling exit idle time!  
W: [pulseaudio] main.c: OK, so you are running PA in system mode. Please note that you most likely shouldn't be doing that.  
W: [pulseaudio] main.c: If you do it nonetheless then it's your own fault if things don't work as expected.  
W: [pulseaudio] main.c: Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an explanation why system mode is usually a bad idea.  
E: [pulseaudio] bluez4-util.c: org.bluez.Manager.GetProperties() failed: org.freedesktop.DBus.Error.UnknownMethod: Method "GetProperties" with signature "" on interface "org.bluez.Manager" doesn't exist  
$ bluetoothctl
>>> scan on
>>> devices
Device E8:07:BF:46:F4:B1 MI Portable Bluetooth Speaker  
Device 9C:99:A0:CA:63:22 Redmi  
>>> pair E8:07:BF:46:F4:B1
Attempting to pair with E8:07:BF:46:F4:B1  
>>> trust E8:07:BF:46:F4:B1
Changing E8:07:BF:46:F4:B1 trust succeeded  
>>> connect E8:07:BF:46:F4:B1
Attempting to connect to E8:07:BF:46:F4:B1  
Connection successful  
>>> exit
[DEL] Controller B8:27:EB:31:F6:8A raspberrypi [default]
$ pactl list sinks short
0       alsa_output.0.analog-stereo     module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED  
1       bluez_sink.E8_07_BF_46_F4_B1    module-bluez5-device.c  s16le 2ch 44100Hz       SUSPENDED  
$ pactl set-default-sink bluez_sink.E8_07_BF_46_F4_B1
$ pactl stat short
Currently in use: 1 blocks containing 64.0 KiB bytes total.  
Allocated during whole lifetime: 2593 blocks containing 22.9 MiB bytes total.  
Sample cache size: 0 B  
$ paplay test.wav

树莓派3 as 音乐盒子

$ bluetoothctl
$ connect E8:07:BF:46:F4:B1

$ pactl list sinks shorts
$ pactl set-default-sink bluez_sink.E8_07_BF_46_F4_B1
$ pactl set-sink-volume 1 50%

$ sudo apt install -y mpd mpc
$ sudo vi /etc/mpd.conf
$ sudo systemctl restart mpd
$ mpc
(#1 - 60/300) RadioMv.com | Russian Christian Radio
[playing] #92/929   0:55/0:00 (0%)
volume: 50%   repeat: off   random: off   single: off   consume: off

$ mpc volume 45
(#1 - 60/300) RadioMv.com | Russian Christian Radio
[playing] #92/929   1:26/0:00 (0%)
volume: 45%   repeat: off   random: off   single: off   consume: off  
# /etc/mpd.conf

music_directory         "/var/lib/mpd/music"  
playlist_directory      "/var/lib/mpd/playlists"  
db_file                 "/var/lib/mpd/tag_cache"  
log_file                "/var/log/mpd/mpd.log"  
pid_file                "/run/mpd.pid"  
state_file              "/var/lib/mpd/state"  
sticker_file            "/var/lib/mpd/sticker.sql"

user                    "mpd"  
bind_to_address         "0.0.0.0"

input {  
        plugin         "curl"
}

audio_output {  
        type           "alsa"
        name           "My ALSA Device"
        device         "hw:0,0"        # optional
}
audio_output {  
        type           "pulse"
        name           "My Pulse Output"
#       server          "127.0.0.1"
#       sink            "bluez_sink.E8_07_BF_46_F4_B1"
}

audio_output {  
        type            "httpd"
        name            "My HTTP Stream"
        encoder         "vorbis"
        port            "8800"
        bitrate         "128"
        format          "44100:16:1"
        always_on       "yes"
        tags            "yes"
}

filesystem_charset      "UTF-8"  
id3v1_encoding          "UTF-8"  

I've set bluetooth speaker as default audio sink.
To send audio to multiple outputs:

  • alsa: device is needed, otherwise default sink is used.
  • pulse: server/sink is not needed, use default sink. They're needed inside docker container.
  • httpd: for client streaming

Docker部署

mpd:  
  image: easypi/mpd-arm
  ports:
    - "6600:6600"
    - "8800:8800"
  volumes:
    - ./mpd.conf:/etc/mpd.conf
    - ./music:/var/lib/mpd/music
    - ./playlists:/var/lib/mpd/playlists
  devices:
    - /dev/snd
  restart: always
# mpd.conf

music_directory    "/var/lib/mpd/music"  
playlist_directory "/var/lib/mpd/playlists"  
db_file            "/var/lib/mpd/database"  
log_file           "/var/log/mpd/mpd.log"  
pid_file           "/var/run/mpd.pid"  
state_file         "/var/lib/mpd/state"  
sticker_file       "/var/lib/mpd/sticker.sql"

input {  
        plugin "curl"
}

input {  
        plugin "alsa"
}

audio_output {  
        type            "alsa"
        name            "My ALSA Device"
        mixer_type      "software"
}

audio_output {  
        type            "httpd"
        name            "My HTTP Stream"
        encoder         "vorbis"
        port            "8800"
        bitrate         "128"
        format          "44100:16:1"
        always_on       "yes"
        tags            "yes"
}

audio_output {  
        type            "pulse"
        name            "My Pulse Output"
        server          "172.17.0.1"
        sink            "bluez_sink.E8_07_BF_46_F4_B1"
}

172.17.0.1 is the ip address of docker0 interface.

TODO

  • [x] Connect to BT speaker after system reboot.
    My BT speaker will connect most recent device.
  • [ ] Run pulseaudio service under user session?
    Run /usr/bin/start-pulseaudio-x11 ?
  • [ ] Compile mpd on alpine with pulse enabled.

References