BlueZ (Official)
Linux / Ubuntu
hciconfig, hcitool
C / Shell
Introduction — Why BlueZ?
Hello students welcome to embeddedpathashalas free bluetooth development course, this post BlueZ tutorial : Bluetooth on Linux is part of our bluez programming course, here You can read about Bluetooth protocols and profiles all day, but the best way to really understand them is to run them yourself. This guide walks you through setting up a real Bluetooth development environment on Linux and introduces you to the essential command-line tools that let you interact with Bluetooth hardware directly.
The tool we use is BlueZ — the official Linux Bluetooth protocol stack. It is the standard Bluetooth implementation for Linux and is included in virtually every major Linux distribution. BlueZ is modular, efficient, and supports all the core Bluetooth layers and protocols from HCI up through the profiles. It is the starting point for any serious Bluetooth development on Linux.
This guide assumes you have some familiarity with working in a Linux terminal, basic shell scripting, and ideally some C programming background — though you can follow along with just terminal knowledge for most of it.
BlueZ is the name for the entire Bluetooth software stack on Linux. When you plug a Bluetooth dongle into your Linux PC, BlueZ is the software layer that talks to the hardware via the USB driver, implements all the Bluetooth protocols (HCI, L2CAP, RFCOMM, SDP, AVDTP, etc.), and exposes APIs so that user-space programs (like your music player or file manager) can use Bluetooth without knowing anything about the low-level radio protocol.
(e.g., PulseAudio)
(OBEX push)
(BlueZ daemon)
sdptool etc.
The key insight is that BlueZ is split across two layers: kernel space (the actual protocol stack implemented as kernel modules) and user space (the daemon bluetoothd and utilities like hcitool). The kernel part handles the time-sensitive radio protocol; the user-space part handles policy decisions and exposes APIs to applications via D-Bus.
Setting Up Your Bluetooth Lab
Before you start experimenting with BlueZ, you need to gather a few items:
Any modern Linux distribution works — Ubuntu, Debian, Fedora, Arch Linux, etc. The examples here use Ubuntu 12.10, but any current version is actually better as BlueZ has improved significantly. Even a Raspberry Pi running Raspberry Pi OS is a great choice for Bluetooth experiments.
A USB Bluetooth dongle is the cheapest and most flexible option. They are widely available for just a few dollars. Look for ones based on the Cambridge Silicon Radio (CSR) chip — they have excellent Linux support. If your laptop already has a built-in Bluetooth module, that works just as well and you can skip buying a dongle.
Tip: Check lsusb after plugging in the dongle to confirm it is detected.
You need at least one other Bluetooth device to discover and connect to. Good options include: a smartphone (Android or iPhone), a Bluetooth headset, a Bluetooth keyboard or mouse, or a Bluetooth speaker. The more devices you have, the more profile examples you can try out.
As of when this material was written, BLE devices were not as widespread as they are today. Now in 2024, almost every smartphone, fitness tracker, smartwatch, and many IoT sensors support BLE. Any BLE-capable device will work for BLE experiments covered in later chapters.
Installing BlueZ tools on Ubuntu/Debian:
sudo apt-get update
sudo apt-get install bluetooth bluez bluez-tools
# Also install development libraries if you want to write C programs
sudo apt-get install libbluetooth-dev
# Verify bluetoothd daemon is running
sudo systemctl status bluetooth
Essential BlueZ Commands
hciconfig is the first command you will use. It does for Bluetooth what ifconfig (or ip) does for network interfaces — it shows the status of your Bluetooth adapter and lets you bring it up or down.
The most informative form of the command is:
sudo hciconfig -a
Running this on a machine with a USB Bluetooth dongle produces output like this:
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1B:DC:05:B5:B3 ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING PSCAN
RX bytes:1127 acl:0 sco:0 events:39 errors:0
TX bytes:655 acl:0 sco:0 commands:38 errors:0
Features: 0xff 0xff 0x8f 0x7e 0xd8 0x1f 0x5b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF PARK
Link mode: SLAVE ACCEPT
Name: 'ubuntu-1'
Class: 0x6e0100
Service Classes: Networking, Rendering, Capturing, Audio, Telephony
Device Class: Computer, Uncategorized
HCI Version: 4.0 (0x6) Revision: 0x1d86
LMP Version: 4.0 (0x6) Subversion: 0x1d86
Manufacturer: Cambridge Silicon Radio (10)
hci0 The interface name. Like eth0 for Ethernet, hci0 is your first Bluetooth adapter. If you have two dongles, the second appears as hci1.BD Address Bluetooth Device Address — the unique 48-bit hardware address of your adapter. Formatted like a MAC address (e.g., 00:1B:DC:05:B5:B3). Every Bluetooth device in the world has a different one.ACL MTU 310:10 means 10 ACL buffers, each 310 bytes in size. ACL (Asynchronous Connection-Less) is used for general data transfer like A2DP audio or file transfers. More buffers = better throughput.SCO MTU 64:8 means 8 SCO buffers of 64 bytes each. SCO (Synchronous Connection-Oriented) is used for voice calls (Hands-Free Profile). Small buffers are fine because voice data is low-bandwidth but time-sensitive.UP RUNNING PSCAN Status flags. UP = adapter is on. RUNNING = actively operational. PSCAN = Page Scan enabled, meaning this device will respond if another device tries to connect to it.HCI Version 4.0 here = Bluetooth 4.0 (which adds BLE support alongside classic BR/EDR). LMP (Link Manager Protocol) version matches the Bluetooth specification version.Manufacturer Cambridge Silicon Radio (10) — the chipset maker. CSR (now owned by Qualcomm) is one of the most popular Bluetooth chip vendors. Number 10 is CSR’s Company ID assigned by the Bluetooth SIG.Just like a network interface can be brought up or down, you can enable or disable your Bluetooth adapter with two commands. These require root privileges (either run as root or prefix with sudo).
To turn OFF (disable) the Bluetooth adapter:
sudo hciconfig hci0 down
To turn ON (enable) and initialise the Bluetooth adapter:
sudo hciconfig hci0 up
After running hci0 up, the adapter reinitialises — it resets the controller hardware, downloads firmware if needed, and puts the adapter in a ready state. You can verify it worked by running hciconfig again and checking for the UP RUNNING flags.
Why would you turn it off? Sometimes BlueZ or a Bluetooth controller gets into a bad state after a crash or a failed connection. Doing a down followed by up is the Bluetooth equivalent of restarting a network interface — it clears any stuck state and re-initialises the hardware cleanly.
─────────────▶
hciconfig hci0 down
Once your adapter is up and running, you want to find other Bluetooth devices nearby. The hcitool command is your Swiss Army knife for this. There are two ways to scan:
Performs a raw Bluetooth inquiry. This sends an “inquiry” radio transmission on channel 0 and listens for responses from any discoverable device within ~10 metres. It collects:
- BD_ADDR — the hardware address of each found device
- Clock offset — timing info used to connect faster next time
- Class of Device — a 24-bit number describing what type of device it is
Does NOT look up device names — faster but less informative.
hcitool inq
Does the same inquiry as inq, but also performs a Name Request for each found device. A name request is a separate Bluetooth procedure where your device connects briefly to each discovered device and asks “what is your name?” This gives you human-readable device names.
More informative but slower (extra radio round-trips per device).
hcitool scan
Example output from both commands:
# hcitool inq — raw inquiry, no names
$ hcitool inq
Inquiring ...
68:ED:43:25:0E:99 clock offset: 0x298e class: 0x7a020c
00:17:83:DC:72:E9 clock offset: 0x7596 class: 0x1a0114
# hcitool scan — inquiry + name lookup
$ hcitool scan
Scanning ...
00:17:83:DC:72:E9 WM_nareshg
68:ED:43:25:0E:99 BlackBerry 8520
68:ED:43:25:0E:99 The BD_ADDR of a found device (a BlackBerry 8520 phone). This is its permanent hardware address — like a MAC address but for Bluetooth. You use this to connect to or query the device.clock offset: 0x298e The clock offset between your device’s Bluetooth clock and the found device’s clock. Bluetooth uses frequency hopping and timing synchronisation — knowing the clock offset lets you reconnect to a device much faster next time.class: 0x7a020c Class of Device (CoD) — a 24-bit number packed with information. It describes both the major device class (phone, computer, audio, etc.) and minor class (smartphone vs feature phone, laptop vs desktop). Used by device browsers to show the correct icon.Getting detailed info about a specific device:
# Get the remote device's clock (tests that you can reach it)
hcitool info 00:17:83:DC:72:E9
# Check RSSI (signal strength) — negative dBm, closer to 0 is stronger
hcitool rssi 00:17:83:DC:72:E9
# Get the link quality (0–255, higher is better)
hcitool lq 00:17:83:DC:72:E9
After discovering a device’s address with hcitool, the next step is usually to ask that device “what Bluetooth services do you support?” This is done through the SDP — Service Discovery Protocol.
Every Bluetooth device that offers services (like a headset offering the Hands-Free profile, or a phone offering audio streaming) registers those services with its own local SDP server. When another device connects and asks “what can you do?”, the SDP server responds with a list of service records.
The command to query a remote device’s SDP server is:
sdptool browse [BD_ADDR]
# Example: browse services on a BlackBerry phone
sdptool browse 68:ED:43:25:0E:99
# Browse ALL services recursively
sdptool browse --tree 68:ED:43:25:0E:99
Example partial output from sdptool browse:
Browsing 68:ED:43:25:0E:99 ...
Service Name: Headset
Service RecHandle: 0x10000
Service Class ID List:
"Headset" (0x1108)
"Generic Audio" (0x1203)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Service Name: Handsfree
Service RecHandle: 0x10001
Service Class ID List:
"Handsfree" (0x111e)
"Generic Audio" (0x1203)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 2
Service Name: Audio Source
Service RecHandle: 0x10002
Service Class ID List:
"Audio Source" (0x110a)
Protocol Descriptor List:
"L2CAP" (0x0100)
"AVDTP" (0x0019)
Version: 0x0102
command
AttributeRequest
AttributeResponse
(list of service records)
(e.g., Phone)
(bluetoothd)
• A2DP Source
• HFP
• AVRCP
The SDP browse always happens on L2CAP channel with PSM (Protocol Service Multiplexer) value 1 — this is the reserved PSM for SDP across all Bluetooth devices. You do not need to know the service’s channel number in advance; SDP discovery tells you which RFCOMM channel or AVDTP PSID to connect on.
Searching for a specific profile instead of browsing all services:
# Search only for A2DP Source service
sdptool search --bdaddr 68:ED:43:25:0E:99 AUDIO_SOURCE
# Search for Hands-Free Profile
sdptool search --bdaddr 68:ED:43:25:0E:99 Handsfree
# List services on your own local machine
sdptool browse local
Here is the typical sequence a developer follows when exploring an unknown Bluetooth device for the first time. These are the exact steps you would run in a terminal:
hciconfig -a
sudo hciconfig hci0 up
hcitool scan
sdptool browse <BD_ADDR>
bluez-simple-agent / bluetoothctl pair <BD_ADDR>
The command-line tools are great for manual exploration, but when you are writing a Bluetooth application in C, you interact with BlueZ through the HCI socket API directly. Here is a minimal C example that performs device inquiry (the equivalent of hcitool scan) programmatically:
#include <stdio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int main(void)
{
int dev_id, sock;
inquiry_info *devices = NULL;
int max_rsp = 255;
int num_rsp;
int i;
char addr[19] = { 0 };
char name[248] = { 0 };
/* Get the first available HCI device (hci0) */
dev_id = hci_get_route(NULL);
/* Open an HCI socket on that device */
sock = hci_open_dev(dev_id);
if (dev_id < 0 || sock < 0) {
perror("Failed to open HCI device");
return 1;
}
/* Allocate memory for inquiry results */
devices = (inquiry_info*) malloc(max_rsp * sizeof(inquiry_info));
/*
* hci_inquiry: perform a Bluetooth inquiry
* 8 = inquiry length in units of 1.28s → total ~10 seconds
* IREQ_CACHE_FLUSH = flush the cache before starting
*/
num_rsp = hci_inquiry(dev_id, 8, max_rsp, NULL, &devices, IREQ_CACHE_FLUSH);
printf("Found %d devices:\n", num_rsp);
for (i = 0; i < num_rsp; i++) {
/* Convert binary BD_ADDR to human-readable string */
ba2str(&(devices+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
/* Read the remote device's Bluetooth name */
if (hci_read_remote_name(sock, &(devices+i)->bdaddr,
sizeof(name), name, 0) < 0) {
strcpy(name, "[unknown]");
}
printf(" %s %s\n", addr, name);
}
free(devices);
close(sock);
return 0;
}
Compile and run:
# -lbluetooth links against the BlueZ client library
gcc bt_scan.c -o bt_scan -lbluetooth
# Run with sudo (HCI raw sockets need root)
sudo ./bt_scan
What this code does step by step:
hci_get_route(NULL)— finds the default HCI device (hci0)hci_open_dev(dev_id)— opens a raw HCI socket to that adapterhci_inquiry()— sends Inquiry radio packets for ~10 seconds and collects BD_ADDRshci_read_remote_name()— for each found device, connects briefly and requests its nameba2str()— converts the 6-byte binary address into a human-readable colon-separated string
| Command | What It Does | Needs Root? |
|---|---|---|
hciconfig -a |
Show all adapter info (address, buffers, version, manufacturer) | No |
hciconfig hci0 up |
Enable and initialise the adapter | Yes |
hciconfig hci0 down |
Disable the adapter | Yes |
hcitool inq |
Discover devices (address + class only) | No |
hcitool scan |
Discover devices + resolve names | No |
hcitool info <addr> |
Detailed info about a specific remote device | No |
sdptool browse <addr> |
List all services on a remote device via SDP | No |
sdptool browse local |
List services registered on your own machine | No |
Ready to Go Deeper?
Now that you have your Bluetooth dev environment running, the next step is writing your first RFCOMM socket application and then moving into Bluetooth Low Energy.
