<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.asteroidos.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Monperrus</id>
	<title>AsteroidOS - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.asteroidos.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Monperrus"/>
	<link rel="alternate" type="text/html" href="https://wiki.asteroidos.org/index.php/Special:Contributions/Monperrus"/>
	<updated>2026-09-03T04:18:58Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://wiki.asteroidos.org/index.php?title=Bluetooth&amp;diff=1032</id>
		<title>Bluetooth</title>
		<link rel="alternate" type="text/html" href="https://wiki.asteroidos.org/index.php?title=Bluetooth&amp;diff=1032"/>
		<updated>2026-08-29T21:13:25Z</updated>

		<summary type="html">&lt;p&gt;Monperrus: add A2DP audio playback&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Developers]]&lt;br /&gt;
An app named [https://github.com/AsteroidOS/AsteroidOSSync AsteroidOSSync] can be downloaded on Android to synchronize data from a phone to an AsteroidOS watch. Several profiles are already supported: Notifications, Weather, Music and Battery, more could come in the future.&lt;br /&gt;
&lt;br /&gt;
=Technical details=&lt;br /&gt;
On the phone, AsteroidOSSync uses the [https://github.com/AsteroidOS/AsteroidOSSync/tree/master/app/src/main/lib/sweetblue SweetBlue] library to access the Bluetooth Low Energy capabilities of Bluedroid as a central device. It scans for watches and implements the client part of the &amp;lt;span class=&amp;quot;gt-baf-word-clickable&amp;quot;&amp;gt;aforementioned&amp;lt;/span&amp;gt; profiles.&lt;br /&gt;
On the watch, data are usually received by a BCM20715 BT chip from Broadcom, this chip needs a proprietary firmware which is uploaded by the [https://github.com/AsteroidOS/meta-dory-hybris/blob/master/recipes-android/brcm-patchram-plus/brcm-patchram-plus/patchram.service patchram] systemd service. Once the firmware is uploaded an hci0 interface is created and can be set up with hciconfig.&lt;br /&gt;
The Linux Kernel can then receive BLE payloads. A newer Bluetooth subsystem (4.1) has been [https://github.com/AsteroidOS/meta-dory-hybris/blob/master/recipes-kernel/linux/linux-dory/0001-Backport-mainline-4.1-Bluetooth-subsystem.patch backported] to provide newer MGMT API functionalities needed by the userspace (e.g: DBus advertisement). This MGMT API is only used by the BlueZ5’s bluetoothd daemon which exposes various DBus abstraction API. Bluetoothd also had to be [https://github.com/AsteroidOS/meta-asteroid/blob/master/recipes-connectivity/bluez5/bluez5/advertise-name.patch patched] to expose more complete advertisement payloads.&lt;br /&gt;
Simple and common interactions with the bluetoothd’s DBus API like checking for the powered/connected status are implemented in [https://github.com/AsteroidOS/qml-asteroid/blob/master/src/utils/src/bluetoothstatus.cpp org.asteroid.utils.BluetoothStatus] of qml-asteroid. This class is for example used in asteroid-launcher, asteroid-music and [https://github.com/AsteroidOS/asteroid-settings/blob/master/BluetoothPage.qml asteroid-settings].&lt;br /&gt;
More complex interactions with bluetoothd are implemented in [https://github.com/AsteroidOS/asteroid-btsyncd asteroid-btsyncd], a user-space daemon running as a standard user (ceres) and acting as a DBus multiplexer between BlueZ and the apps. On the BlueZ side it registers BLE Services, Characteristics and Advertisement payloads. On the user side, it exposes specific API for each and every usage needed. For example, it communicates with asteroid-music with to the [https://github.com/AsteroidOS/asteroid-music/blob/master/main.qml#L98 MPRIS API], with asteroid-launcher based on the FreeDesktop’s notification API or with asteroid-weather thanks to data saved in the dconf “register”.&lt;br /&gt;
&lt;br /&gt;
=Bluetooth Personal Area Network=&lt;br /&gt;
In order to use BT PAN on AsteroidOS, you first need to modify the default image. Start by adding the following packages to the &amp;lt;i&amp;gt;IMAGE_INSTALL&amp;lt;/i&amp;gt; variable of &amp;lt;code&amp;gt;asteroid/src/meta-asteroid/classes/asteroid-image.bbclass&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;bluez5-testtools bluez5-noinst-tools python-dbus connman-client&amp;lt;/pre&amp;gt;&lt;br /&gt;
You also need to modify the bluez5’s main configuration in &amp;lt;code&amp;gt;asteroid/src/meta-asteroid/recipes-connectivity/bluez5/bluez5/main.conf&amp;lt;/code&amp;gt; to&lt;br /&gt;
&amp;lt;pre&amp;gt;[General]&lt;br /&gt;
#ControllerMode = le&lt;br /&gt;
DiscoverableTimeout = 180&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally, modify &amp;lt;code&amp;gt;build/conf/bblayers.conf&amp;lt;/code&amp;gt; to add the following line to &amp;lt;code&amp;gt;BBLAYERS&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;/path/to/src/meta-openembedded/meta-networking \&amp;lt;/pre&amp;gt;&lt;br /&gt;
After above changes, you can rebuild a new asteroid rootfs and install it to the watch as described in [[Building AsteroidOS]].&lt;br /&gt;
In order to watch logs and check on progress, open up first a first terminal on the watch via [[SSH]] and run:&lt;br /&gt;
&amp;lt;pre&amp;gt;tail -F /var/log/messages&amp;lt;/pre&amp;gt;&lt;br /&gt;
In a new terminal, download and push the bt-pan helper script: ([http://blog.fraggod.net/2015/03/28/bluetooth-pan-network-setup-with-bluez-5x.html more info])&lt;br /&gt;
&amp;lt;pre&amp;gt;wget https://raw.githubusercontent.com/mk-fg/fgtk/master/bt-pan&lt;br /&gt;
chmod +x ./bt-pan&lt;br /&gt;
scp bt-pan ceres@192.168.2.15:/tmp/bluetooth/bt-pan&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now, fix up DNS and make sure localhost is a known thing by modifying (if needed) &amp;lt;code&amp;gt;resolv.conf&amp;lt;/code&amp;gt; and hosts&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# cat /etc/resolv.conf&lt;br /&gt;
nameserver 8.8.8.8&lt;br /&gt;
nameserver ::1&lt;br /&gt;
sh-4.3# cat /etc/hosts&lt;br /&gt;
127.0.0.1 localhost&amp;lt;/pre&amp;gt;&lt;br /&gt;
Make sure you can see bluetooth in connman:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# connmanctl technologies&lt;br /&gt;
...&lt;br /&gt;
/net/connman/technology/bluetooth&lt;br /&gt;
Name = Bluetooth&lt;br /&gt;
Type = bluetooth&lt;br /&gt;
Powered = True&lt;br /&gt;
Connected = False&lt;br /&gt;
Tethering = False&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can now use bluetoothctl to establish a connection to your computer ([http://trac.gateworks.com/wiki/wireless/bluetooth#PersonalAreaNetworkDeviceProfilePAN more info])&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# bluetoothctl&lt;br /&gt;
[NEW] Controller 20:70:02:A0:1F:AC dory [default]&lt;br /&gt;
[NEW] Device 3C:15:C2:C7:DA:95 my-rmbp2&lt;br /&gt;
agent on&lt;br /&gt;
default-agent&lt;br /&gt;
discoverable on&lt;br /&gt;
pairable on&lt;br /&gt;
scan on&lt;br /&gt;
pair 3C:15:C2:C7:DA:95&lt;br /&gt;
trust 3C:15:C2:C7:DA:95&amp;lt;/pre&amp;gt;&lt;br /&gt;
Launch bt-pan client which triggers bnep0 creation from connman (you will see it in &amp;lt;code&amp;gt;/var/log/messages&amp;lt;/code&amp;gt;)&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# /tmp/bluetooth/bt-pan --debug client 3C:15:C2:C7:DA:95&lt;br /&gt;
DEBUG:root:Using local device (addr: 20:70:02:A0:1F:AC): /org/bluez/hci0&lt;br /&gt;
DEBUG:root:Using remote device (addr: 3C:15:C2:C7:DA:95): /org/bluez/hci0/dev_3C_15_C2_C7_DA_95&lt;br /&gt;
DEBUG:root:Connected to network (dev_remote: /org/bluez/hci0/dev_3C_15_C2_C7_DA_95, addr: 3C:15:C2:C7:DA:95) uuid &#039;nap&#039; with iface: bnep0&lt;br /&gt;
DEBUG:root:Finished&amp;lt;/pre&amp;gt;&lt;br /&gt;
At this point your default route should still be rndis0 (USB OTG) instead of bnep0:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ip route&lt;br /&gt;
default via 192.168.2.1 dev rndis0&lt;br /&gt;
192.168.2.0/24 dev rndis0 proto kernel scope link src 192.168.2.15&amp;lt;/pre&amp;gt;&lt;br /&gt;
If the ip/bcast/netmask fields are missing on bnep0:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ifconfig bnep0&lt;br /&gt;
bnep0 Link encap:Ethernet HWaddr 20:70:02:A0:1F:AC&lt;br /&gt;
inet6 addr: fe80::2270:2ff:fea0:1fac%1202001524/64 Scope:Link&lt;br /&gt;
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1&lt;br /&gt;
RX packets:39 errors:0 dropped:0 overruns:0 frame:0&lt;br /&gt;
TX packets:4 errors:0 dropped:0 overruns:0 carrier:0&lt;br /&gt;
collisions:0 txqueuelen:1000&lt;br /&gt;
RX bytes:10504 (10.2 KiB) TX bytes:148 (148.0 B)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fix it with ifconfig and verify it worked:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ifconfig bnep0 192.168.2.2 netmask 255.255.255.0 broadcast 192.168.2.255&lt;br /&gt;
sh-4.3# ifconfig bnep0&lt;br /&gt;
bnep0 Link encap:Ethernet HWaddr 20:70:02:A0:1F:AC&lt;br /&gt;
inet addr:192.168.2.2 Bcast:192.168.2.255 Mask:255.255.255.0&lt;br /&gt;
inet6 addr: fe80::2270:2ff:fea0:1fac%1201331708/64 Scope:Link&lt;br /&gt;
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1&lt;br /&gt;
RX packets:134 errors:0 dropped:0 overruns:0 frame:0&lt;br /&gt;
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0&lt;br /&gt;
collisions:0 txqueuelen:1000&lt;br /&gt;
RX bytes:40369 (39.4 KiB) TX bytes:252 (252.0 B)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now that both routes are available, data are still going over rndis0 instead of bnep0:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ip route&lt;br /&gt;
default via 192.168.2.1 dev rndis0&lt;br /&gt;
192.168.2.0/24 dev rndis0 proto kernel scope link src 192.168.2.15&lt;br /&gt;
192.168.2.0/24 dev bnep0 proto kernel scope link src 192.168.2.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete default route and aad a new default one over bnep0&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ip route delete default&lt;br /&gt;
sh-4.3# ip route change default via 192.168.2.1 dev bnep0&amp;lt;/pre&amp;gt;&lt;br /&gt;
Verify default is now over bnep0:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ip route&lt;br /&gt;
default via 192.168.2.1 dev bnep0&lt;br /&gt;
192.168.2.0/24 dev rndis0 proto kernel scope link src 192.168.2.15&lt;br /&gt;
192.168.2.0/24 dev bnep0 proto kernel scope link src 192.168.2.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can now test your connection via ping and nslookup&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.3# ping -c 1 8.8.8.8&lt;br /&gt;
PING 8.8.8.8 (8.8.8.8): 56 data bytes&lt;br /&gt;
64 bytes from 8.8.8.8: seq=0 ttl=42 time=80.094 ms&lt;br /&gt;
sh-4.3# nslookup www.google.com&lt;br /&gt;
Server: 8.8.8.8&lt;br /&gt;
Address 1: 8.8.8.8 google-public-dns-a.google.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Bluetooth Audio and HID=&lt;br /&gt;
You will need to enable legacy mode in order to connect to legacy (BR/EDR) mode Bluetooth devices. To enable legacy mode you will need to edit the configuration file &#039;&#039;&#039;on the watch&#039;&#039;&#039; via [[SSH]] use the following command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.4# vi /etc/bluetooth/main.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit the contents to the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;[General]&lt;br /&gt;
#ControllerMode = le&lt;br /&gt;
DiscoverableTimeout = 180&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next you will need to restart Bluetooth. To avoid weird behaviour its best to restart the entire system, use the following command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.4# reboot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now you are ready to connect a Bluetooth device to AsteroidOS use bluetoothctl to connect to a Bluetooth device:&lt;br /&gt;
&amp;lt;pre&amp;gt;sh-4.4# bluetoothctl&lt;br /&gt;
Agent registered&lt;br /&gt;
[bluetooth]# agent on&lt;br /&gt;
Agent is already registered&lt;br /&gt;
[bluetooth]# default-agent&lt;br /&gt;
[bluetooth]# discoverable on&lt;br /&gt;
[bluetooth]# pairable on&lt;br /&gt;
[bluetooth]# scan on&lt;br /&gt;
Default agent request successful&lt;br /&gt;
Changing pairable on succeeded&lt;br /&gt;
Changing discoverable on succeeded&lt;br /&gt;
[CHG] Controller 43:43:A0:12:1F:AC Discoverable: yes&lt;br /&gt;
Discovery started&lt;br /&gt;
[CHG] Controller 43:43:A0:12:1F:AC Discovering: yes&lt;br /&gt;
[NEW] Device 27:EC:10:01:00:1A Gamepad.&lt;br /&gt;
[bluetooth]# pair 27:EC:10:01:00:1A&lt;br /&gt;
Attempting to pair with 27:EC:10:01:00:1A&lt;br /&gt;
[CHG] Device 27:EC:10:01:00:1A Paired: yes&lt;br /&gt;
Pairing successful&lt;br /&gt;
[Gamepad.]# connect 27:EC:10:01:00:1A&lt;br /&gt;
Attempting to connect to 27:EC:10:01:00:1A&lt;br /&gt;
Connection successful&lt;br /&gt;
[Gamepad.]#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A2DP audio playback ===&lt;br /&gt;
&lt;br /&gt;
Tested on the Huawei Watch 2 (`sawfish`) with a Bose NC 700 headset.&lt;br /&gt;
&lt;br /&gt;
AsteroidOS configures BlueZ for BLE-only operation by default. To use ordinary&lt;br /&gt;
Bluetooth audio headsets, enable both BLE and BR/EDR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
sudo cp -a /etc/bluetooth/main.conf /etc/bluetooth/main.conf.before-a2dp&lt;br /&gt;
sudo sed -i \&lt;br /&gt;
  &#039;s/^[[:space:]]*ControllerMode[[:space:]]*=[[:space:]]*.*/ControllerMode = dual/&#039; \&lt;br /&gt;
  /etc/bluetooth/main.conf&lt;br /&gt;
sudo systemctl restart bluetooth.service&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pair, trust and connect the headset using `bluetoothctl`. A successful A2DP&lt;br /&gt;
connection lists `Audio Sink` in `bluetoothctl info HEADSET_ADDRESS`, and&lt;br /&gt;
PulseAudio creates a sink named like&lt;br /&gt;
`bluez_sink.HEADSET_ADDRESS_WITH_UNDERSCORES.a2dp_sink`.&lt;br /&gt;
&lt;br /&gt;
On current images, `module-stream-restore-nemo` can restore the `x-maemo`&lt;br /&gt;
media role at `-20000` volume (silence). Fix its fallback rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
sudo cp -a /etc/pulse/x-maemo-stream-restore.table \&lt;br /&gt;
  /etc/pulse/x-maemo-stream-restore.table.before-a2dp&lt;br /&gt;
sudo sed -i \&lt;br /&gt;
  &#039;s/^sink-input-by-media-role:x-maemo[[:space:]]\+-20000$/sink-input-by-media-role:x-maemo 0/&#039; \&lt;br /&gt;
  /etc/pulse/x-maemo-stream-restore.table&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload the module as user `ceres`:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
pactl list short modules | grep stream-restore-nemo&lt;br /&gt;
pactl unload-module MODULE_ID&lt;br /&gt;
pactl load-module module-stream-restore-nemo \&lt;br /&gt;
  restore_device=no restore_volume=yes restore_muted=no \&lt;br /&gt;
  route_table=/etc/pulse/x-maemo-route.table \&lt;br /&gt;
  fallback_table=/etc/pulse/x-maemo-stream-restore.table use_voice=false&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A previously stored stream setting can still set a new playback stream to 0%.&lt;br /&gt;
While playback is active, inspect and fix it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
pactl list sink-inputs&lt;br /&gt;
pactl set-sink-input-mute SINK_INPUT_ID 0&lt;br /&gt;
pactl set-sink-input-volume SINK_INPUT_ID 100%&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the headset as the default output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
pactl set-default-sink bluez_sink.HEADSET_ADDRESS_WITH_UNDERSCORES.a2dp_sink&lt;br /&gt;
pactl set-sink-mute bluez_sink.HEADSET_ADDRESS_WITH_UNDERSCORES.a2dp_sink 0&lt;br /&gt;
pactl set-sink-volume bluez_sink.HEADSET_ADDRESS_WITH_UNDERSCORES.a2dp_sink 100%&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test playback with a tone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;&lt;br /&gt;
gst-launch-1.0 -q \&lt;br /&gt;
  audiotestsrc wave=sine freq=880 num-buffers=430 ! \&lt;br /&gt;
  audioconvert ! audioresample ! \&lt;br /&gt;
  pulsesink device=bluez_sink.HEADSET_ADDRESS_WITH_UNDERSCORES.a2dp_sink&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Monperrus</name></author>
	</entry>
</feed>