Bluetooth (Linux)
This article describes how to use Bluetooth in Embedded Linux. For modules that don't come with Bluetooth hardware on-module, support can easily be added by using a USB Bluetooth module. The table below presents the modules with integrated Bluetooth:
Toradex SoMs featuring the U-BLOX MAYA-W160-00B Wi-Fi / Bluetooth module:
System on Module | Product Number | Version |
---|---|---|
Verdin AM62 Quad 2GB Wi-Fi / Bluetooth | 0076 | V1.1A and newer |
Verdin AM62 Dual 1GB Wi-Fi / Bluetooth | 0075 | V1.1A and newer |
Verdin AM62 Solo 512MB Wi-Fi / Bluetooth | 0072 | V1.1A and newer |
Toradex SoMs featuring the Azurewave AW-CM276NF Wi-Fi / Bluetooth module:
System on Module | Product Number | Version |
---|---|---|
Apalis iMX8 QuadMax 4GB Wi-Fi / Bluetooth IT | 0037 | V1.0A and newer |
Apalis iMX8 QuadPlus 2GB Wi-Fi / Bluetooth | 0048 | V1.0A and newer |
Colibri iMX8X QuadXPlus 2GB Wi-Fi / Bluetooth IT | 0038 | V1.0A and newer |
Colibri iMX8X DualX 1GB Wi-Fi / Bluetooth | 0051 | V1.0A and newer |
Colibri iMX6ULL 512MB Wi-Fi / Bluetooth IT | 0040 | V1.1A and newer |
Colibri iMX6ULL 512MB Wi-Fi / Bluetooth | 0045 | V1.1A and newer |
Verdin iMX8M Mini Quad 2GB Wi-Fi / Bluetooth IT | 0055 | V1.0B and newer |
Verdin iMX8M Mini DualLite 1GB Wi-Fi / Bluetooth IT | 0060 | V1.1A and newer |
Verdin iMX8M Plus Quad 2GB Wi-Fi / Bluetooth IT | 0064 | V1.1A and newer |
Verdin iMX8M Plus Quad 4GB Wi-Fi / Bluetooth IT | 0058 | V1.0B and newer |
Verdin iMX8M Plus Quad 8GB Wi-Fi / Bluetooth IT | 0070 | V1.1A and newer |
Toradex SoMs featuring the Wi2Wi WM828CC6 Wi-Fi / Bluetooth module:
Wi2Wi WM828CC6 has been replaced by Azurewave AW-CM276NF on newer Toradex SoMs.
System on Module | Version |
---|---|
Colibri iMX6ULL 512MB Wi-Fi / Bluetooth | V1.0x |
Wi-Fi adapter characteristics summary:
Wi-fi Adapter | Wi-Fi Standards | Bluetooth version | Adapter's Chipset | Antenna Configuration |
---|---|---|---|---|
AzureWave AW-CM276NF | 802.11 a/b/g/n/ac | 5.3 | NXP 88W8997 | 2.4/5 GHz Dual-band 2x2 Wi-Fi 5 (802.11ac) |
Wi2Wi WM828CC6 | 802.11 a/b/g/n/ac | 4.2 | NXP 88W8887 | 2.4/5 GHz Dual-band 1x1 Wi-Fi 5 (802.11ac) |
u-blox MAYA-W160-00B | 802.11 a/b/g/n | 5.2 | NXP IW416 | 2.4/5 GHz Dual Band 1x1 Wi-Fi 4 (802.11n) |
Custom Kernel/Backports
BSP 2.7b4 and newer
Since the BSP 2.7b4, Bluetooth support is added to the Toradex pre-built image through the kernel driver backports with BlueZ 5 bluetooth software stack. Please see the article below for instructions about how to cross-compile the driver backports:
Kernel Driver Backports Integration
Custom Image
BlueZ 5 is already shipped in the pre-built Reference Images for Yocto Project and is the software stack used for Bluetooth interaction.
Additional user space utilities can be added to a custom Linux image. For this, a Yocto Project/OpenEmbedded build is required. Please refer to the following articles:
- Build a Reference Image with Yocto Project
- Custom meta layers, recipes and images in Yocto Project (hello-world examples)
An example is provided with some useful packages. Add the following to your conf/local.conf
:
IMAGE_INSTALL_append = " obexftp obex-data-server python3 python3-dbus python3-pygobject python3-pybluez"
Build the image for your computer on module and install it with the Toradex Easy Installer.
Configuring the Device
In order to use Bluetooth properly, set the antenna as described in the article Operating Toradex Wi-Fi/BT Capable Modules Using Dual and Single Antenna Configuration.
A USB Bluetooth module should be detected after connecting it to the USB port (if using a Bluetooth USB dongle):
root@colibri-vf:~# lsusb
Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
There is a systemd Bluetooth service already enabled. Start it:
root@colibri-vf:~# systemctl start bluetooth.service
Enable the Bluetooth device using ConnMan.
root@colibri-imx6ull:~# connmanctl enable bluetooth
Enabled bluetooth
hciconfig should show the connected bluetooth devices up and running. Other information such as the MAC address and device bus are also provided.
For a USB dongle:
root@colibri-vf:~# hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1A:7D:DA:71:13 ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING
RX bytes:574 acl:0 sco:0 events:30 errors:0
TX bytes:368 acl:0 sco:0 commands:30 errors:0
For an SDIO module:
root@colibri-imx6ull:~# hciconfig
hci0: Type: Primary Bus: SDIO
BD Address: 00:19:88:5E:10:B1 ACL MTU: 1021:7 SCO MTU: 120:6
UP RUNNING
RX bytes:1882 acl:0 sco:0 events:83 errors:0
TX bytes:2075 acl:0 sco:0 commands:83 errors:0
Bluetoothctl
Starting with BSP version 2.7, bluetoothctl was added to the Linux image. It enables scanning, pairing and connecting to devices, among other features.
Start bluetoothctl:
root@colibri-imx6ull:~# bluetoothctl
[NEW] Controller 00:19:88:5E:10:B1 colibri-imx6ull [default]
Agent registered
List all available options:
[bluetooth]# help
Available commands:
list List available controllers
show [ctrl] Controller information
...
Scan for Bluetooth devices:
[bluetooth]# scan on
After you enable scanning, it will begin to list available devices:
Discovery started
[CHG] Controller 00:19:88:5E:10:B1 Discovering: yes
[NEW] Device 98:39:8E:1B:D8:88 Galaxy A5 (2016)
[CHG] Device 98:39:8E:1B:D8:88 RSSI: -86
The device number listed is the device MAC address. In the example above its value is 98:39:8E:1B:D8:88. You can stop scanning once you find the device you are looking for:
[bluetooth]# scan off
Pair to the device using its MAC address:
[bluetooth]# pair 98:39:8E:1B:D8:88
Attempting to pair with 98:39:8E:1B:D8:88
[CHG] Device 98:39:8E:1B:D8:88 Connected: yes
Request confirmation
[agent] Confirm passkey 117022 (yes/no):
Confirm that the passkey is the same as displayed on your device:
[agent] Confirm passkey 117022 (yes/no): yes
[CHG] Device 98:39:8E:1B:D8:88 Modalias: bluetooth:v0075p0100d0200
[CHG] Device 98:39:8E:1B:D8:88 UUIDs: 00001105-0000-1000-8000-00805f9b34fb
...
[CHG] Device 98:39:8E:1B:D8:88 ServicesResolved: yes
[CHG] Device 98:39:8E:1B:D8:88 Paired: yes
Pairing successful
...
[CHG] Device 98:39:8E:1B:D8:88 Connected: no
Trust paired device:
[bluetooth]# trust 98:39:8E:1B:D8:88
[CHG] Device 98:39:8E:1B:D8:88 Trusted: yes
Changing 98:39:8E:1B:D8:88 trust succeeded
Connect to the Bluetooth device:
[bluetooth]# connect 98:39:8E:1B:D8:88
Attempting to connect to 98:39:8E:1B:D8:88
[CHG] Device 98:39:8E:1B:D8:88 Connected: yes
Connection successful
[CHG] Device 98:39:8E:1B:D8:88 ServicesResolved: yes
[Galaxy A5 (2016)]#
List the connected device information, including supported services:
[Galaxy A5 (2016)]# info 98:39:8E:1B:D8:88
Device 98:39:8E:1B:D8:88
Name: Galaxy A5 (2016)
Alias: Galaxy A5 (2016)
Class: 0x5a020c
Icon: phone
Paired: yes
Trusted: yes
Blocked: no
Connected: yes
LegacyPairing: no
UUID: OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb)
UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb)
UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
UUID: Headset AG (00001112-0000-1000-8000-00805f9b34fb)
UUID: PANU (00001115-0000-1000-8000-00805f9b34fb)
UUID: NAP (00001116-0000-1000-8000-00805f9b34fb)
UUID: Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)
UUID: Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb)
UUID: Message Access Server (00001132-0000-1000-8000-00805f9b34fb)
UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb)
UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)
UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
UUID: Vendor specific (936da01f-9abd-4d9d-80c7-02af85c822a8)
Modalias: bluetooth:v0075p0100d0200
ManufacturerData Key: 0x0075
ManufacturerData Value: 0x77
ManufacturerData Value: 0x98
ManufacturerData Value: 0x39
ManufacturerData Value: 0x8e
ManufacturerData Value: 0x1b
ManufacturerData Value: 0xd8
ManufacturerData Value: 0x88
Bluetooth profiles
BlueZ - C API
You may want to explore the BlueZ API documentation.
obexftp and other old utilities like rfcomm or sdptool don't seem to work correctly with BlueZ 5+ unless you do the following procedure.
In our BSP 2.8, we updated the BlueZ stack into version 5.46. However, this meant that, by default, most of the older available tools that use the C API won't work out of the box.
However, you can change the Bluetooth service to launch the Bluetooth daemon with the compatibility mode to enable the use of this older API.
Edit the Bluetooth service to enable the compatibility mode:
root@colibri-imx6ull:~# systemctl status bluetooth
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset
Active: active (running) since Mon 2019-04-01 18:57:46 UTC; 10min ago
Docs: man:bluetoothd(8)
Main PID: 302 (bluetoothd)
Status: "Running"
CGroup: /system.slice/bluetooth.service
└─302 /usr/libexec/bluetooth/bluetoothd
Apr 01 18:57:46 colibri-imx6ull systemd[1]: Starting Bluetooth service...
Apr 01 18:57:46 colibri-imx6ull bluetoothd[302]: Bluetooth daemon 5.46
Apr 01 18:57:46 colibri-imx6ull systemd[1]: Started Bluetooth service.
Apr 01 18:57:46 colibri-imx6ull bluetoothd[302]: Starting SDP server
Apr 01 18:57:47 colibri-imx6ull bluetoothd[302]: Bluetooth management interface
root@colibri-imx6ull:~# vi /lib/systemd/system/bluetooth.service
Then we change the line ExecStart from:
ExecStart=/usr/libexec/bluetooth/bluetoothd
to
ExecStart=/usr/libexec/bluetooth/bluetoothd --compat
Finally, we save the file and reload the daemon:
root@colibri-imx6ull:~# systemctl daemon-reload
root@colibri-imx6ull:~# systemctl restart bluetooth
Using SPP
SPP, known as Serial Port Profile, is one of the most basic Bluetooth profiles that could confirm the operation between both devices.
At this point, you should have your Bluetooth enabled, the compatibility mode enabled and, for simplicity, have both devices paired and trusted already.
Since the following tools are based on the older C API, with BlueZ 5 the compatibility mode must be used.
Add the SPP to the list of Bluetooth profiles. In the example below the Bluetooth channel number to access this profile is 1:
root@colibri-imx6ull:~# sdptool add --channel=1 SP
Serial Port service registered
Enable the background listening for raw connections on the channel 1:
root@colibri-imx6ull:~# rfcomm --raw listen /dev/rfcomm0 1 &
Waiting for connection on channel 1
Check the manual pages of rfcomm and sdptool for more complete information.
On the other device, connect to the Toradex module by using an SPP compatible application. We have tested to be working with:
- Serial Bluetooth Terminal - Android
- Bluetooth Serial Terminal - Windows 10 (Needs some configuration on Windows)
- From another rfcomm
$ sudo rfcomm --raw connect 0 <MAC Address of the module> 1
Connected /dev/rfcomm0 to XX:XX:XX:XX:XX:XX on channel 1
Press CTRL-C for hangup
At this point you should be able to send and read information through the rfcomm port with the echo and cat commands.
root@colibri-imx6ull:~# echo "Hello World from Toradex" > /dev/rfcomm0
"Hello World from Toradex" should appear on the other end.
And if we send something from the other end, we can read it like this:
root@colibri-imx6ull:~# cat /dev/rfcomm0
Hello Toradex!
Disclaimer: As we have no control of what it is included or removed on BlueZ, there is a chance that in future revisions the compatibility mode might be removed. We recommend to use the newer D-bus API for new developments. However, we don't expect to make any revision changes on our 2.8 BSP.
BlueZ - D-bus API
At the moment of writing this documentation (Q4 2019), there are not many newer tools updated tools that use the D-bus API and therefore, for an out-of-the-box experience, it is very likely that you would want to enable the compatibility mode of the C API (See above).
You may find more information about this under BlueZ 5 D-Bus API Documentation.
Merely as an example, here is a project of an SPP echo server and client that will make the serial port service available for other devices: SPP BlueZ5 example.
This program will capture the messages sent through SPP from another device and send them back to it. It is just a mere PoC, but feel free to use it as reference for your development.
PyBluez
PyBluez is a Python module that allows access to system Bluetooth resources. See how to add it to a custom Linux image in the section Custom Image above. Check the project GitHub page for documentation.
If you would like to test the samples provided by PyBluez, clone the git repository to the board:
opkg update
opkg install git
git clone https://github.com/karulis/pybluez.git
Then you can run the samples using the Python interpreter:
python pybluez/examples/<sample-folder>/<sample-name>.py
Running PyBluez samples
Search for nearby devices
root@colibri-imx6ull:~# python pybluez/examples/simple/inquiry.py
performing inquiry...
found 3 devices
98:39:8E:1B:D8:88 - Galaxy A5 (2016)
3C:77:E6:D2:1E:9C - NOTEJOAO
F4:B7:E2:DD:33:C2 - leonardo
List device services
root@colibri-imx6ull:~# python pybluez/examples/simple/sdp-browse.py 98:39:8E:1B:D8:88
found 11 services on 98:39:8E:1B:D8:88
Service Name: None
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: L2CAP
channel/PSM: 31
svc classes: ['1801']
profiles: []
service id: None
Service Name: None
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: L2CAP
channel/PSM: 31
svc classes: ['1800']
profiles: []
service id: None
Service Name: AV Remote Control Target
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: L2CAP
channel/PSM: 23
svc classes: ['110C']
profiles: [('110E', 260)]
service id: None
Service Name: Advanced Audio
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: L2CAP
channel/PSM: 25
svc classes: ['110A']
profiles: [('110D', 259)]
service id: None
Service Name: Headset Gateway
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: RFCOMM
channel/PSM: 2
svc classes: ['1112', '1203']
profiles: [('1108', 258)]
service id: None
Service Name: Handsfree Gateway
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: RFCOMM
channel/PSM: 3
svc classes: ['111F', '1203']
profiles: [('111E', 263)]
service id: None
Service Name: Android Network Access Point
Host: 98:39:8E:1B:D8:88
Description: NAP
Provided By: None
Protocol: L2CAP
channel/PSM: 15
svc classes: ['1116']
profiles: [('1116', 256)]
service id: None
Service Name: Android Network User
Host: 98:39:8E:1B:D8:88
Description: PANU
Provided By: None
Protocol: L2CAP
channel/PSM: 15
svc classes: ['1115']
profiles: [('1115', 256)]
service id: None
Service Name: OBEX Phonebook Access Server
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: RFCOMM
channel/PSM: 19
svc classes: ['112F']
profiles: [('1130', 257)]
service id: None
Service Name: SMS/MMS
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: RFCOMM
channel/PSM: 4
svc classes: ['1132']
profiles: [('1134', 258)]
service id: None
Service Name: OBEX Object Push
Host: 98:39:8E:1B:D8:88
Description: None
Provided By: None
Protocol: RFCOMM
channel/PSM: 12
svc classes: ['1105']
profiles: [('1105', 256)]
service id: None
Run the L2CAP example
Install PyBluez in a computer that has Bluetooth and clone the PyBluez repository. For Ubuntu:
user@pc:~$ sudo apt-get install bluez libbluetooth-dev
user@pc:~$ pip install pybluez
user@pc:~$ git clone https://github.com/karulis/pybluez.git
Run the L2CAP server sample on your computer:
user@pc:~$ python pybluez/examples/simple/l2capserver.py
Run the L2CAP sample on the SoM. Type something and press Enter. See that the message is sent to your computer.
root@colibri-imx6ull:~# python pybluez/examples/simple/l2capclient.py F4:B7:E2:DD:33:C2
trying to connect to F4:B7:E2:DD:33:C2 on PSM 0x1001
connected. type stuff
this is a test
('Data received:', 'Echo => this is a test')
user@pc:~$ python pybluez/examples/simple/l2capserver.py
('Accepted connection from ', ('00:19:88:5E:10:B1', 4097))
('Data received: ', 'this is a test')
File Transfer
You must pair to another device before transferring files. Please see the Bluetoothctl section above for an example on how to pair.
Alternatively, a Python sample for pairing is provided below. Currently it only works with BSP version 2.5 and bluez4.
Python sample for Bluetooth pairing
The below simple-agent python script has been taken from the Openmoko - Manually using Bluetooth article. Note that this script works only with BSP version 2.5 and bluez4. First transfer the below python script to the module.
#!/usr/bin/python
import gobject
import sys
import dbus
import dbus.service
import dbus.mainloop.glib
class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"
class Agent(dbus.service.Object):
exit_on_release = True
def set_exit_on_release(self, exit_on_release):
self.exit_on_release = exit_on_release
@dbus.service.method("org.bluez.Agent", in_signature="", out_signature="")
def Release(self):
print "Release"
if self.exit_on_release:
mainloop.quit()
@dbus.service.method("org.bluez.Agent", in_signature="os", out_signature="")
def Authorize(self, device, uuid):
print "Authorize (%s, %s)" % (device, uuid)
@dbus.service.method("org.bluez.Agent", in_signature="o", out_signature="s")
def RequestPinCode(self, device):
print "RequestPinCode (%s)" % (device)
return raw_input("Enter PIN Code: ")
@dbus.service.method("org.bluez.Agent", in_signature="o", out_signature="u")
def RequestPasskey(self, device):
print "RequestPasskey (%s)" % (device)
passkey = raw_input("Enter passkey: ")
return dbus.UInt32(passkey)
@dbus.service.method("org.bluez.Agent", in_signature="ou", out_signature="")
def DisplayPasskey(self, device, passkey):
print "DisplayPasskey (%s, %d)" % (device, passkey)
@dbus.service.method("org.bluez.Agent", in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
print "RequestConfirmation (%s, %d)" % (device, passkey)
confirm = raw_input("Confirm passkey (yes/no): ")
if (confirm == "yes"):
return
raise Rejected("Passkey doesn't match")
@dbus.service.method("org.bluez.Agent", in_signature="s", out_signature="")
def ConfirmModeChange(self, mode):
print "ConfirmModeChange (%s)" % (mode)
@dbus.service.method("org.bluez.Agent", in_signature="", out_signature="")
def Cancel(self):
print "Cancel"
def create_device_reply(device):
print "New device (%s)" % (device)
mainloop.quit()
def create_device_error(error):
print "Creating device failed: %s" % (error)
mainloop.quit()
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
if len(sys.argv) > 1:
path = manager.FindAdapter(sys.argv[1])
else:
path = manager.DefaultAdapter()
adapter = dbus.Interface(bus.get_object("org.bluez", path), "org.bluez.Adapter")
path = "/test/agent"
agent = Agent(bus, path)
mainloop = gobject.MainLoop()
if len(sys.argv) > 2:
if len(sys.argv) > 3:
device = adapter.FindDevice(sys.argv[2])
adapter.RemoveDevice(device)
agent.set_exit_on_release(False)
adapter.CreatePairedDevice(sys.argv[2], path, "DisplayYesNo", reply_handler=create_device_reply, error_handler=create_device_error)
else:
adapter.RegisterAgent(path, "DisplayYesNo")
print "Agent registered"
mainloop.run()
#adapter.UnregisterAgent(path)
#print "Agent unregistered"
The above script can also be downloaded from here. Give it executable permissions by running chmod and then pair the relevant device with the simple-agent script, which in this example is as follows.
root@colibri-vf:~# chmod 755 simple-agent.py
root@colibri-vf:~# ./simple-agent.py hci0 14:DD:A9:36:DD:FC
This will pair the two devices. Should the devices have been paired before, one may get an error message as below.
root@colibri-vf:~# ./simple-agent.py hci0 14:DD:A9:36:DD:FC
Creating device failed: org.bluez.Error.AlreadyExists: Already Exists
In the above scenario directly proceed with the file transfer by obexftp.
On an Android Lollipop device install an application like Bluetooth File Transfer which provides the obexftp service. Enable Bluetooth and open the Bluetooth File Transfer application for enabling obexftp service on the Android device.
For some Android devices running Icecream Sandwich, the obexftp service might be available by default which could be enabled from the Bluetooth settings or elsewhere depending on the device.
From the module a file can be transferred as follows.
root@colibri-vf:~# obexftp -b 14:DD:A9:36:DD:FC -p file.txt
Browsing 14:DD:A9:36:DD:FC ...
Connecting..\done
Tried to connect for 20ms
Sending "file.txt"...|done
Disconnecting../done
The transferred file is now available on the device and is shown by the Bluetooth File Transfer application.
Notes
The python packages python-dbus, python-pygobject and python itself are added for using D-Bus with Python in the simple-agent python script which is used for pairing. On a device with limited NAND flash like the Colibri VF50 it is possible that the generated image with these packages included, might not fit in the flash memory. It should be possible to do what the simple-agent python script does using the low-level D-Bus C API.