Search by Tags

Bluetooth (Linux)

 
Applicable for

Compare with Revision

Subscribe for this article updates

This article describes how to use Bluetooth in Embedded Linux. Currently, no Toradex module comes with Bluetooth hardware on-module. Support can easily be added by using a USB Bluetooth module.

Custom Kernel

Bluetooth support is compiled into the kernel for Tegra and i.MX6 based modules. A custom kernel is required if you need additional drivers or configurations or you use a Colibri VF module. Please refer to the following article for the setup/compilation of the same.

Build U-Boot and Linux Kernel from Source Code

To enable support for Bluetooth via USB the following options are required:

[user@host linux-toradex]$ make menuconfig
Networking support --->
<*> Bluetooth subsystem support --->
--- Bluetooth subsystem support
[*] Bluetooth Classic (BR/EDR) features
<*>   RFCOMM protocol support
[*]     RFCOMM TTY support
<*>   BNEP protocol support
[*]     Multicast filter support
[*]     Protocol filter support
<*>   HIDP protocol support
[ ] Bluetooth Low Energy (LE) features
[ ] Bluetooth self testing support
    Bluetooth device driver --->
    <*> HCI USB driver
< Exit > (4 times)

Then compile and deploy the custom kernel as mentioned in the above article.

During boot one gets the following kernel messages.

[    0.129961] Bluetooth: Core ver 2.20
[    0.130181] Bluetooth: HCI device and connection manager initialized
[    0.130252] Bluetooth: HCI socket layer initialized
[    0.130310] Bluetooth: L2CAP socket layer initialized
[    0.130424] Bluetooth: SCO socket layer initialized
[    1.732833] Bluetooth: RFCOMM TTY layer initialized
[    1.741896] Bluetooth: RFCOMM socket layer initialized
[    1.751184] Bluetooth: RFCOMM ver 1.11
[    1.758842] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
[    1.768638] Bluetooth: HIDP socket layer initialized

Custom Image

The user space utilities need to be added to the root file system. For this an OpenEmbedded build is required. Please refer the following article for setup of the same.

OpenEmbedded (core)

Add the following to "~/oe-core/build/conf/local.conf":

IMAGE_INSTALL_append = " bluez5 bluez-hcidump obexftp obex-data-server openobex python python-dbus python-pygobject dbus-glib"

Build the image for the module to be used and flash the image on the module.

Configuring the Device

A USB Bluetooth module should be detected after connecting it to the USB port.

root@colibri-vf:~# lsusb
Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)

Enable the systemd Bluetooth service with systemctl.

root@colibri-vf:~# systemctl enable bluetooth.service
root@colibri-vf:~# systemctl start bluetooth.service

hciconfig should show the connected bluetooth devices.

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
        DOWN 
        RX bytes:574 acl:0 sco:0 events:30 errors:0
        TX bytes:368 acl:0 sco:0 commands:30 errors:0

Enable the Bluetooth device.

root@colibri-vf:~# hciconfig hci0 up
Can't init device hci0: Operation not possible due to RF-kill (132)

If the above message is shown, run the rfkill command as below and then hciconfig.

root@colibri-vf:~# rfkill unblock all
root@colibri-vf:~# hciconfig hci0 up

The hcitool can be used to show the Bluetooth MAC address of the Bluetooth module connected with the respective hci connection.

root@colibri-vf:~# hcitool dev
Devices:
        hci0    00:1A:7D:DA:71:13

File Transfer

Scan for remote Bluetooth devices with hcitool.

root@colibri-vf:~# hcitool scan
Scanning ...
        14:DD:A9:36:DD:FC       ASUS_Z008D

One needs to pair the device. Starting with BSP version 2.7 bluetoothctl can be used for pairing.

root@colibri-vf:~# bluetoothctl 
[NEW] Controller 00:1A:7D:DA:71:13 apalis-imx6 [default]
[bluetooth]# agent KeyboardOnly
Agent registered
[bluetooth]# default-agent
Default agent request successful
[bluetooth]# scan on
Discovery started
[CHG] Controller 00:1A:7D:DA:71:13 Discovering: yes
[NEW] Device 14:DD:A9:36:DD:FC ASUS Zenfone2
[bluetooth]# pair 14:DD:A9:36:DD:FC
Attempting to pair with 14:DD:A9:36:DD:FC
[CHG] Device 14:DD:A9:36:DD:FC Connected: yes
Request passkey
[agent] Enter passkey (number in 0-999999): 100896
[CHG] Device 14:DD:A9:36:DD:FC Modalias: bluetooth:v001Dp1200d1436
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001105-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001112-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001115-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001116-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 0000111f-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 0000112f-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001132-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device 14:DD:A9:36:DD:FC Paired: yes
Pairing successful
[CHG] Device 14:DD:A9:36:DD:FC Connected: no

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.