Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP


 
Open Source @ HP all of HP US
HP.com home

Open Source and Linux from HP

Primed for Business Advantage
» 

Large Enterprise Business

» Products
» Business & IT services
» Solutions
»

Open source & Linux

» Platforms & printers
» Linux distributions
» Indemnity
» Support matrices
» Security certifications
» Solutions portfolio
» HP Open Source Middleware Stacks
» Documentation
» Services & education
» Open source at HP
» Partner programs
1-888-475-4689
Content starts here

HP TechBriefs

 
Christopher Worsley Exploring Bluetooth from a Linux Perspective
by Christopher Worsley
 
This TechBrief provides guidance for C/C++ developers on the Linux platform who wish to learn about the command line tools and the basic programming considerations for communicating with Bluetooth devices.

» Introduction
» Command Line Tools
» Installing Development Libraries
» Application Development
» Summary
» Acknowledgement
» Footnotes


Introduction

A casual search on the web for information about Bluetooth provides a fascinating glimpse into this frequently debated wireless technology1. There are many articles that describe the details needed to install drivers and tailor configuration files. It is also fairly easy to find hints and tricks about the various command line programs that permit instantaneous communications between Bluetooth-enabled devices. However, there aren't too many references for C/C++ developers who are interested in adding Bluetooth features to their own software. Some of the initial questions are:
  • What include files and libraries are needed to compile a program that uses Bluetooth hardware?
  • How are Bluetooth devices discovered?
  • How is a connection established between two devices?
This TechBrief answers those questions through a brief introduction to the available tools and by building a Bluetooth application for Linux. Although the resulting code falls squarely into the category of a simple "Hello World" type of program, it is a good starting point for those who want to add Bluetooth capability to their own software.

Before starting in earnest, there are a few prerequisites that are important to take into account. First, having Bluetooth capability on your computer is a necessity. Recently manufactured PDAs and laptops quite often have an integrated Bluetooth module or at least the option to add one internally. If it doesn't, there are several Bluetooth models that can be connected via USB or PCMCIA/CF. Second, strongly consider installing a recent Linux distribution, preferably one that uses a 2.6 kernel and includes lots of extras (such as a full installation of Fedora Core 4). The reason is simple: all of the Bluetooth drivers, helper programs, and libraries are largely configured and ready for immediate use. That allows you to focus on learning the tools and API with working configuration files. Third, it is essential to have another Bluetooth-enabled device in the vicinity so that it can interact with your computer and the software you develop. For instance, many of the examples that appear in this brief refer to these Bluetooth-enabled devices:
  • An HP iPAQ 4700 running Windows Mobile 2003,
  • An HP Photosmart 8450 printer, and
  • A Space Machine GPS receiver.
Since this brief explores how Bluetooth can be used on Linux, some machines running Fedora Core 4 and Familiar Linux were configured and used to develop the examples. The machines referenced in this brief are:
  • HP Linux Desktop - an xw9300 dual-Opteron workstation running Fedora Core 4 (64-bit version) with a Dlink DBT-120 USB Bluetooth adapter2.;
  • HP Linux Laptop - an N610c laptop running Fedora Core 3 (32-bit version) with a Dlink DBT-120 USB Bluetooth adapter; and
  • HP Linux PDA - an iPAQ 3870 running Familiar Linux 0.8.2 with an integrated Cambridge Silicon Radio Bluetooth module3..
Since Bluetooth can be tremendously helpful in a mobile computing device like an iPAQ, it seems appropriate to feature those examples. Truly, it seems far more likely that you'd establish a connection between your GPS & PDA (or laptop) rather than to your dual-CPU tower. However, part of the fun of Linux is that the tools work the same way on each of these platforms whether you're using a PXA250-based iPAQ, a Pentium 4 laptop, or a 64-bit Opteron.

Using varied equipment in this article allows for a comprehensive set of examples, but certainly not required for trying your hand at using Bluetooth under Linux (obviously only two devices are needed). A shopping spree may not be entirely necessary to follow along with this TechBrief, but would be fun! Once your newly purchased equipment is powered up and running, it is time to verify things are working properly. The next section describes the tools that help you control interfaces and establish communication.

Command Line Tools

The official Bluetooth protocol stack for Linux is called BlueZ and can be found on the web at www.bluez.org. Although there are other Bluetooth stacks available for Linux such as Affix and OpenBT4., this TechBrief demonstrates the command line tools and programming interface that are part of the BlueZ suite.

The first order of business is to ensure your Bluetooth module has been correctly identified and drivers loaded. To do this, we'll exercise the BlueZ "hciconfig" program on an iPAQ as shown in Figure 15.

root@h3600:/usr/local/bin# hciconfig
hci0: Type: UART
      BD Address: 00:02:C7:09:57:01 ACL MTU: 128:8 SCO MTU: 64:8
      UP RUNNING PSCAN ISCAN
      RX bytes:1969 acl:0 sco:0 events:40 errors:0
      TX bytes:786 acl:0 sco:0 commands:23 errors:0

Figure 1: Using hciconfig

You can see that hciconfig responds with the details about the Bluetooth interfaces currently available (in this case hci0). The man page for hciconfig shows that it can be used to control all aspects of a Bluetooth interface; it takes parameters like up, down, reset, auth, encrypt, and so on. In fact, it should be thought of as a peer to ifconfig or iwconfig which manage wired and wireless network interfaces, respectively. This means that if you've ever configured a Linux machine to hop on a network, then you'll feel instantly comfortable managing a Bluetooth interface.

It is worth noting that some investigative work may be necessary at this stage if hciconfig doesn't list an interface that you were expecting. Google can be a helpful partner as you'll need to determine the exact model of your Bluetooth device and what drivers are known to work with it. An example of a helpful page that lists supported radios can be found at www.linuxgod.net/bluez-dev.html.

The next program to consider is hcitool. The image of a Swiss Army knife may be the perfect icon for hcitool because of its diverse capabilities. Only two functions are shown here (scan and info), so be absolutely sure to peek at the man page to understand all of the options. The fun really starts with Figure 2 as hcitool takes to the airwaves and scans the environment for other Bluetooth devices. By giving the argument "scan" to hcitool, it performs two steps:
  • Scans the RF environment looking for the devices that advertise themselves
  • Queries each discovered device for its name.
The data gathered by hcitool is then written to the standard output stream with the unique device addresses in the first column and their corresponding device names in the second column.

[root@valhalla Bluetooth]$ hcitool scan
Scanning ...
      00:0D:B5:00:79:BA      BT-GPS-0079BA
      00:0F:3D:39:AF:07      Photosmart 8400 series
      08:00:28:D4:99:18      iPAQ4700

Figure 2: Using hcitool

With a listing of the devices available in the immediate vicinity, it is effortless to interact with them. Figure 3 shows how hcitool fetches Bluetooth-specific protocol details from a remote device and how l2ping can be used to check connectivity and measure round-trip time.

[root@valhalla Bluetooth]$ hcitool info 08:00:28:D4:99:18 Requesting information ... BD Address: 08:00:28:D4:99:18 Device Name: iPAQ4700 LMP Version: 1.2 (0x2) LMP Subversion: 0xd1a Manufacturer: Texas Instruments Inc. (13) Features: 0xff 0xff 0x2d 0xf8 0x1b 0x18 0x00 0x80 <3-slot packets> <5-slot packets> <encryption> <slot offset> <timing accuracy> <role switch> <hold mode> <sniff mode> <park state> <RSSI> <channel quality> <SCO link> <HV2 packets> <HV3 packets> <u-law log> <A-law log> <CVSD> <power control> <transparent SCO> <enhanced iscan> <interlaced iscan> <interlaced pscan> <inquiry with RSSI> <extended SCO> <EV4 packets> <EV5 packets> <AFH cap. slave> <AFH class. slave> <AFH cap. master> <AFH class. master> <extended features> [root@valhalla Bluetooth]$ l2ping 08:00:28:D4:99:18 Ping: 08:00:28:D4:99:18 from 00:02:C7:09:57:01 (data size 20) ... 20 bytes from 08:00:28:D4:99:18 id 0 time 29.89ms 20 bytes from 08:00:28:D4:99:18 id 1 time 30.42ms 20 bytes from 08:00:28:D4:99:18 id 2 time 39.13ms 20 bytes from 08:00:28:D4:99:18 id 3 time 37.85ms 20 bytes from 08:00:28:D4:99:18 id 4 time 30.38ms 5 sent, 5 received, 0% loss

Figure 3: Interacting with other devices using hcitool and 12ping

With basic communication established, the next step is to interrogate a device in order to discover the services it offers. The key phrase to remember for this example is Service Discovery Protocol (SDP); it is the very powerful capability that allows one Bluetooth device to ask a peer what it is capable of doing. This feature is well described in Bluetooth folklore because it can dramatically simplify the user's experience in connecting to a device and using it. For example, a sophisticated GUI application would use SDP to find devices capable of printing when a user wants a hard copy of a document. This can be quite convenient for mobile users that work at a number of locations and want to leverage local resources without connecting USB cables, configuring printer drivers, etc. To provide an idea of the services a Bluetooth-enabled printer might provide, Figure 4 lists a portion of the responses from an HP Photosmart 8450 (please note that several services where omitted for the sake of brevity).

[root@halvdan ~]# sdptool browse 00:0F:3D:39:AF:07
Browsing 00:0F:3D:39:AF:07 ...
Service Name: Basic Printing
Service RecHandle: 0x10003
Service Class ID List:
  "Printing Status" (0x1123)
  "Direct Printing" (0x1118)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 4
  "OBEX" (0x0008)


…several services omitted for brevity…

Service Name: Hardcopy Cable Replacement
Service RecHandle: 0x10005
Service Class ID List:
  "HCR Print" (0x1126)
Protocol Descriptor List:
  "L2CAP" (0x0100)
Profile Descriptor List:
  "Hardcopy Cable Replacement" (0x1125)
    Version: 0x0100

Figure 4: Using adptool


There are many more elements in the BlueZ toolbox worth exploring. Hopefully enough has been described to whet your appetite! For those developing Linux applications where Bluetooth capability would be beneficial, there aren't too many steps required to make it happen. The next sections describe how to install the development libraries and then write an incredibly simple program that reads a stream of data from a Bluetooth-enabled GPS receiver.

Installing Development Libraries

In order to write software that uses Bluetooth, the development machine must have the proper libraries installed. To check if the required files are on your system, look in /usr/lib6. as shown in Figure 5.

[root@halvdan ~]# cd /usr/lib [root@halvdan lib]# ls -la *blue* -rw-r--r-- 1 root root 57900 Mar 2 2005 libbluetooth.a lrwxrwxrwx 1 root root 22 Oct 2 12:59 libbluetooth.so -> libbluetooth.so.1.0.15 lrwxrwxrwx 1 root root 22 Oct 2 11:20 libbluetooth.so.1 -> libbluetooth.so.1.0.15 -rwxr-xr-x 1 root root 54348 Mar 2 2005 libbluetooth.so.1.0.15

Figure 5: Checking for Bluetooth development library files on FC4 (32 bit version)


If the libraries aren't present, they need to be installed. There are essentially two approaches to consider:
  • Install a pre-built package, or
  • Build the libraries from the BlueZ source code.
Most major distributions offer a pre-built package. Fedora users can issue a "yum install" command to retrieve the components required for development7. An abbreviated version of the messages generated during the download and installation of the development libraries for the x86-64 appear in Figure 6.

[root@valhalla ~]# yum install bluez-libs-devel.x86_64 Setting up Install Process --> Populating transaction set with selected packages. Please wait. ---> Downloading header for bluez-libs-devel to pack into transaction set. bluez-libs-devel-2.15-1.x 100% |=========================| 4.5 kB 00:00 ---> Package bluez-libs-devel.x86_64 0:2.15-1 set to be updated --> Running transaction check … several messages omitted for brevity … Installing: bluez-libs-devel x86_64 2.15-1 base 52 k Running Transaction Installing: bluez-libs-devel ######################### [1/1] Installed: bluez-libs-devel.x86_64 0:2.15-1 Complete!

Figure 6: yum installation messages during install on FC4 (64-bit version)


However, there are times when a pre-built package is not widely available and the second approach (i.e., build your own) is the only real option. Begin at www.bluez.org, download the code, compile it, and arrange the library & include files. To illustrate, we'll take the iPAQ running Linux as a test case. It turns out to be trivial and takes only a few minutes with the right set of commands. If the ARM cross-compiler is not already an established part of your development environment, please refer to the handhelds.org site for a quick introduction into installing the compiler and other necessary tools8. After downloading the bluez-libs tar file (version 2.25 at the time of this writing), unpack it and run configure and make. The critical part is to run configure with special options:
  • Define CC to refer to the ARM cross compiler (which should look something like /usr/local/arm/3.4.1/bin/arm-linux-gcc),
  • Specify host and target as arm-linux, and
  • Specify the build environment as your development machine (i686).
Each step is presented in Figure 7. Please note that the output from tar, configure and make is significant, so those lines were omitted in their entirety to improve readability of the commands you enter.

$ tar xvfz bluez-libs-2.25.tar.gz …output from tar omitted for brevity… $ cd bluez-libs-2.25 $ CC=arm-linux-gcc ./configure --host=arm-linux --target=arm-linux --build=i686 …output from configure omitted for brevity… $ make …output from make omitted for brevity… $ cd src/.libs/ $ ls -la total 264 drwxr-xr-x 2 christopher users 4096 Feb 10 15:02 . drwxr-xr-x 4 christopher users 4096 Feb 10 15:02 .. -rw-r--r-- 1 christopher users 9180 Feb 10 15:02 bluetooth.o -rw-r--r-- 1 christopher users 31468 Feb 10 15:02 hci.o -rw-r--r-- 1 christopher users 79918 Feb 10 15:02 libbluetooth.a lrwxrwxrwx 1 christopher users 18 Feb 10 15:02 libbluetooth.la -> ../libbluetooth.la -rw-r--r-- 1 christopher users 845 Feb 10 15:02 libbluetooth.lai lrwxrwxrwx 1 christopher users 22 Feb 10 15:02 libbluetooth.so -> libbluetooth.so.1.0.25 lrwxrwxrwx 1 christopher users 22 Feb 10 15:02 libbluetooth.so.1 -> libbluetooth.so.1.0.25 -rwxr-xr-x 1 christopher users 80382 Feb 10 15:02 libbluetooth.so.1.0.25 -rw-r--r-- 1 christopher users 40556 Feb 10 15:02 sdp.o

Figure 7: Building libbluetooth from source


The important result from make is both the static and shared version of the library (libbluetooth.a and libbluetooth.so.*) which appear in the src/.libs directory. These files can be copied to your cross-compiler's lib directory, which is most likely in a path like /usr/local/arm/<version>/arm-linux/lib. Change the last line in libbluetooth.la to reflect its new home. It is also important to make the include files available, so be sure to copy them from the working directory (where the files were unpacked) to a place accessible to application developers. The proper spot is the include path that the cross-compiler uses, such as /usr/local/arm/<version>/arm-linux/include/bluetooth.

At this point, you have a Bluetooth-ready development environment for either the native x86 or an ARM-based host. Just be sure to tack on the -lbluetooth when compiling. With all of the hard work out of the way, it is time to write some code!

Application Development

The finishing touch for this TechBrief is a small program called show_nmea_stream.cpp which creates a connection to a Bluetooth-enabled GPS receiver and prints the arriving NMEA sentences. For those unfamiliar with the sentences emitted by a GPS receiver, an amazing amount of detailed information is available on the web9.

As far as writing for the Bluetooth interface is concerned, where should the prospective developer start? Perhaps one begins by examining bluetooth.h or even hci.h? Maybe reviewing rfcomm.h or l2cap.h is the appropriate place. Fortunately, we can relax because getting data from the GPS follows the time-tested Unix philosophy: it is just a byte stream. Since Bluetooth supports TCP/IP, this turns out to be an exercise in writing a basic socket client program. There are only three routines in show_nmea_stream.cpp:
  • rfcommConnect() which creates the socket connection to the GPS receiver
  • readLine() which assembles the bytes making up each sentence
  • main() is the program entry point and main control logic
By stepping through each routine, it'll become obvious that writing code that uses a Bluetooth connection can be remarkably easy. To start, inspect the code in Figure 8 for rfcommConnect(). It ought to bear a striking resemblance to the initialization code for the many socket client applications you've written in the past. There are only a couple of key differences. The first of which is the call to socket(): the domain argument is PF_BLUETOOTH and the protocol argument is BTPROTO_RFCOMM. The second difference is address structure given to connect() is of type sockaddr_rc as it must contain details needed by the Bluetooth driver (specifically the destination address and channel).

#include <stdint.h> #include <string.h> #include <string> #include <iostream> #include <sys/socket.h> /* provides SOCK_STREAM constant */ #include <bluetooth/bluetooth.h> /* BTPROTO_RFCOMM, bdaddr_t */ #include <bluetooth/rfcomm.h> /* sockaddr_rc */ using std::cout; static int rfcommConnect(bdaddr_t * deviceAddress, uint8_t channel) { cout << "creating socket" << endl; int s = ::socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if(s >= 0) { cout << "setting up address" << endl; struct sockaddr_rc address; memset(&address, 0, sizeof(struct sockaddr_rc)); address.rc_family = AF_BLUETOOTH; bacpy(&address.rc_bdaddr, deviceAddress); address.rc_channel = channel; cout << "connecting..." << endl; if(::connect(s,(struct sockaddr *)&address, sizeof(address))==0) cout << "connected!" << endl; else { ::close(s); s = -1; } } return s; }

Figure 8: The rfcommConnect function


Next is the readLine() function (see Figure 9); truth be told, there is nothing terribly special about it. In fact, it could easily appear in any program that reads ASCII characters from a descriptor until a CR/LF pair is found. The routine buffers characters until a sufficient number of 'end-of-line' bytes have been read. Many GPS receivers terminate sentences with a CR/LF pair, so typically the parameter eolsExpected is 2.

static std::string readLine(int d, int eolsExpected) { int eolCount = 0; std::string buffer; while(eolCount < eolsExpected) { char temp; ssize_t bytesRead = read(d, &temp, sizeof(char)); if(bytesRead == sizeof(char)) { if(temp<32) // is this an end-of-line marker? eolCount++; else buffer += temp; } else break; } return buffer; }

Figure 9: The readLine function


The last part of the application, main(), is listed in Figure 10. The Bluetooth address of the GPS receiver is hard-coded here for simplicity, but the real-world approach would be to call the BlueZ function hci_inquiry() to find the devices available at the time the program runs. In any case, the program must know the device address to use (in this case 00:0D:B5:00:79:BA). Finally, rfcommConnect() is invoked to establish the socket connection and readLine() is continuously called to read the byte stream.

int main(int argc, char **argv) { bdaddr_t deviceAddress; str2ba("00:0D:B5:00:79:BA", &deviceAddress); int sd = rfcommConnect(&deviceAddress, 1); if(sd >= 0) { while(1) { std::string nmeaSentence = readLine(sd, 2); if(nmeaSentence.length()>0) std::cout << nmeaSentence << std::endl; } close(sd); } else cerr << "no connection" << endl; return 1; }

Figure 10: Program entry point and main control logic


As you probably noticed by browsing the code, a few C++ elements appear in the program so g++ is required to build the executable. With all the earlier work to configure the BlueZ library, the step to compile couldn't be much easier! Just run g++ and link against the Bluetooth library. Finally, unleash the program and you'll see the stream of sentences generated by the GPS similar to those in Figure 11.

$ g++ show_nmea_stream.cpp -oshow_nmea_stream -lbluetooth $ ./show_nmea_stream creating socket setting up address connecting... connected! ,04,6.0,149.7,M,-33.4,M,0.0,0000*41 $GPGSA,A,3,24,28,07,04,,,,,,,,,24.1,6.0,23.4*39 $GPRMC,015832.589,A,3922.5499,N,07707.8792,W,0.21,84.31,160905,,*26

Figure 11: Compiling and running a simple Bluetooth program


That is all there is to it! Now that all of the basic mechanisms are in place, it is time to picture something a bit more grandiose and turn this code into a useful project. For example, folks having experience with GPS connected to PDAs and laptops quite often report that they enjoy using a mixture of several standalone geo-aware applications. The catch is this: it would be great to run them all simultaneously. With a little work, the show_nmea_stream.cpp program can be turned into a proxy that creates a fork of the NMEA sentence stream. That would allow more than one application to get data from a single-channel GPS receiver on the same host platform. Then again, that has already been accomplished by others, so building something different would be more exciting. Perhaps it would be fun to further refine the program into a multi-channel proxy that can feed the NMEA stream to applications across n Bluetooth clients on separate hosts. Software like this would be a bit more sophisticated and require a few calls to the functions that appear in the BlueZ API. Sounds like the start of another article!

Summary

Adding basic Bluetooth communications to a C/C++ application is a straight-forward endeavor. Those that have written client/server applications already possess the skills necessary to enhance their software to communicate with Bluetooth devices. The pre-compiled development libraries are out there, so install them on your system and give it a try. The command line tools provided in the BlueZ suite provide the means to control the interfaces, check their status, verify connectivity to other devices, and so on. There is much more in the world of BlueZ than described in this one article, so be sure to download the source code and look at how the tools themselves are written. They will be a great reference when you start a Linux & Bluetooth development project.

Acknowledgement

Thanks to Bill Reese, Jeff Burch, Gene Nenortas, Eric Mair, Doug Herro and John Harmon for their reviews of this TechBrief.

Christopher Worsley is a technical consultant in the Technology Solutions Group C&I Public Sector with an emphasis in the design, development and integration of signal processing systems. He has delivered software in C++, C, Java, and Ada in a range of computing environments including Linux, Tru64, VxWorks, Windows, Solaris and VMS. Christopher is based in Maryland.


Footnotes
1 There are plenty of articles that argue the relevance of Bluetooth. However, this TechBrief focuses on putting Bluetooth to work in a Linux environment, not competing technologies or market acceptance. However, for those so inclined, check out Bluetooth is Dead, Bluetooth: Dead or Alive?, Wireless USB killed the Bluetooth Star, and Bluetooth Gets it Right

2 The Dlink DBT-120 uses a Cambridge Silicon Radio.

3 If the idea of a Linux PDA piqued your interest, you might be interested to know that HP Labs in Massachusetts kicked off an initiative several years ago to port Linux to the iPAQ series of PDAs. See http://www.handhelds.org for more details. Effort to support newer iPAQ models continues today through the contributions by the open-source community and interested companies.

4 See http://affix.sourceforge.net and http://sourceforge.net/projects/openbt.

5 First enable the Bluetooth radio on the iPAQ by running the hciattach program. The arguments vary based on the exact model you have. A comprehensive list can be discovered somewhere in the wiki on the www.handhelds.org site, but here are a few examples:
  • On the iPAQ 3870, the Bluetooth serial port is /dev/ttySB0 so the command would be "hciattach /dev/ttySB0 bcsp 230400".
  • For the iPAQ 3900 series, use "hciattach /dev/tts/1 bcsp 921600".
  • The iPAQ hx4700 requires "hciattach -S /etc/bluetooth/TIInit_3.2.26.bts ttyS1 texas".


6 For FC4-64, check the /usr/lib64 directory for the libraries.

7 The name of the package differs slightly depending on the 32/64 platform; for FC4-32 use "yum install bluez-libs-devel" and for FC4-64 "yum install bluez-libs-devel.x86_64".

8 See ftp://ftp.handhelds.org/projects/toolchain/README

9 A terrific place to start is http://www.gpsinformation.org.



Was this article useful? Tell us what you think!
Printable version
Privacy statement Using this site means you accept its terms Feedback to Webmaster
© 2007 Hewlett-Packard Development Company, L.P.