Bluetooth Setup
HCI over UART
For RealTek UART-linked Bluetooth device.
stty -F /dev/ttyS1 115200
rfkill block bluetooth
sleep 1
rfkill unblock bluetooth
rtk_hciattach -n -s 115200 /dev/ttyS1 rtk_h5
BlueZ
# /etc/bluetooth/main.conf
[General]
Class = 0x000100
DiscoverableTimeout = 0
AlwaysPairable = true
PairableTimeout = 0
ReverseServiceDiscovery = true
NameResolving = true
ControllerMode = bredr
MultiProfile = multiple
FastConnectable = true
JustWorksRepairing = always
[GATT]
Cache = no
[Policy]
ReconnectAttempts=7
bluetoothctl discoverable yes
bluetoothctl pairable yes
BlueZ Agent for trusting everybody
BlueZ D-Bus interface behavior: it accepts on succeed and rejects on error raised.
Script from StackOverflow answer:
#!/usr/bin/env python
import dbus
import dbus.mainloop.glib
import dbus.service
from gi.repository import GLib
AGENT_PATH = "/org/bluez/AuthorizeServiceAgent"
AGENT_INTERFACE = 'org.bluez.Agent1'
CAPABILITY = "NoInputNoOutput"
class Agent(dbus.service.Object):
@dbus.service.method(AGENT_INTERFACE, in_signature="os", out_signature="")
def AuthorizeService(self, device, uuid):
pass
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
agent = Agent(bus, AGENT_PATH)
obj = bus.get_object("org.bluez", "/org/bluez")
manager = dbus.Interface(obj, "org.bluez.AgentManager1")
manager.RegisterAgent(AGENT_PATH, CAPABILITY)
manager.RequestDefaultAgent(AGENT_PATH)
GLib.MainLoop().run()
Wireplumber
Bluetooth configuration - Wireplumber
Add this configuration so that BlueZ can be accessed by Wireplumber running under any user session.
# /usr/share/wireplumber/wireplumber.conf.d/bluez.conf
wireplumber.profiles = {
main = {
monitor.bluez.seat-monitoring = disabled
}
}
Audio Server
Pulseaudio
Install pulseaudio-bluetooth to support BlueZ.
# /etc/pulse/default.pa
# Accept remote audio connection on protocol Pulse.
load-module module-native-protocol-tcp auth-ip-acl=0.0.0.0/0
Pipewire
/etc/pipewire/pipewire-pulse.conf.d/pulse.conf
pulse.properties = {
server.address = [
"unix:native"
"tcp:4713"
]
pulse.default.format = S16
pulse.default.position = [ FL FR ]
}
Down-mix stereo to mono
In some cases like …
- speech broadcasting or
- listening to podcasts
… you want all the content mixed into mono channel and playback them on all speakers.
Pulseaudio
# /etc/pulse/default.pa
# Mix stereo channels into mono and play on both speakers.
load-module module-remap-sink sink_name=mono master=@DEFAULT_SINK@ channels=2 channel_map=mono,mono remix=yes
# Playback to mono.
set-default-sink mono
PipeWire
# /etc/pipewire/pipewire.conf.d/mono.conf
context.modules = [{
name = libpipewire-module-combine-stream
args = {
combine.mode = sink
node.name = "mono_sink"
node.description = "Mono Sink"
combine.props = {
audio.position = [ MONO ]
}
stream.rules = [{
matches = [{
media.class = "Audio/Sink"
}]
actions = {
create-stream = {
combine.audio.position = [ MONO ]
audio.position = [ FL FR ]
}
}
}]
}
}]
Start services
systemctl enable-linger pipewire-user
systemctl enable --now bluetooth
machinectl shell [email protected]
systemctl enable --user --now pipewire pipewire-pulse wireplumber
Client
Pipewire
# ~/.config/pipewire/pipewire.conf.d/pulse.conf
context.modules = [{
name = libpipewire-module-pulse-tunnel
args = {
tunnel.mode = sink
reconnect.interval.ms = 5000
pulse.server.address = "tcp:192.168.1.1:4713"
pulse.latency = 500
audio.rate = 44100
audio.channels = 2
audio.position = [ FL FR ]
audio.format = S16
stream.props = {
node.name = "pipewired"
node.description = "Pipewire Server"
}
}
}]