Skip to main content

Embedded Linux Bluetooth Development with BlueZ and BLE

·1375 words·7 mins
Embedded Linux BlueZ Bluetooth BLE Yocto Linux RFCOMM GATT C Programming IoT
Table of Contents

Embedded Linux Bluetooth Development with BlueZ and BLE

Bluetooth remains one of the most practical short-range wireless technologies for embedded systems. It provides a reasonable balance between power consumption, implementation complexity, cost, and ecosystem maturity, making it ideal for industrial devices, IoT gateways, handheld equipment, and wireless peripherals.

On Linux systems, Bluetooth development almost always revolves around BlueZ, the official Linux Bluetooth protocol stack maintained by the Linux community. Beyond being a protocol stack, BlueZ also provides a large set of user-space tools, daemons, APIs, and debugging utilities.

This article walks through a complete Bluetooth communication workflow in Embedded Linux:

  • Hardware initialization
  • Device pairing and connection
  • Classic Bluetooth RFCOMM serial communication
  • BLE GATT interaction
  • Programmatic control through C code

The examples focus on practical command-line workflows and low-level Linux integration suitable for embedded developers.

🧰 Experimental Environment and Setup
#

The test platform used in this article consists of:

  • A custom Yocto-based Linux system
  • Linux kernel 5.15
  • USB Bluetooth adapter using the CSR8510 chipset
  • BlueZ user-space stack

Most generic USB Bluetooth adapters rely on the btusb kernel driver, which modern kernels usually load automatically.

After inserting the adapter, verify that the hardware is recognized correctly:

dmesg | grep Bluetooth
hciconfig -a

The first command checks kernel logs for Bluetooth initialization messages.

The second command displays detailed information about available HCI devices. The hciconfig utility behaves similarly to ifconfig, but specifically for Bluetooth Host Controller Interface (HCI) devices.

A correctly initialized adapter typically appears as:

hci0:   Type: Primary  Bus: USB
        BD Address: XX:XX:XX:XX:XX:XX
        UP RUNNING

If the system reports that hciconfig is missing, install the BlueZ packages.

On Ubuntu:

sudo apt install bluez bluez-tools

On embedded systems, BlueZ usually needs to be included during Yocto image generation or cross-compiled manually.

Next, start the Bluetooth daemon:

systemctl start bluetooth
systemctl status bluetooth

At this point, the low-level Bluetooth stack should be operational.

🧠 Understanding the BlueZ Architecture
#

BlueZ is built around the bluetoothd daemon running in user space.

Internally:

  • bluetoothd manages devices and protocols
  • D-Bus acts as the communication layer
  • Client tools interact with BlueZ through D-Bus APIs

The commonly used bluetoothctl utility is simply an interactive frontend that translates user commands into D-Bus operations.

The relationship can be simplified as:

User Command → bluetoothctl → D-Bus → bluetoothd → Bluetooth Hardware

Understanding this architecture becomes important later when integrating Bluetooth into embedded applications programmatically.

🔗 Pairing and Connecting with bluetoothctl
#

Launch the interactive management shell:

sudo bluetoothctl

The shell prompt changes to:

[bluetooth]#

A typical Bluetooth initialization sequence looks like this:

power on
agent on
default-agent
scan on

What Each Command Does
#

power on
#

Enables the Bluetooth adapter.

The adapter state changes from:

Powered: no

to:

Powered: yes

agent on
#

Starts a Bluetooth agent responsible for handling authentication and pairing requests.

Without an active agent, pairing operations may fail because no process is available to handle PIN confirmation or authorization requests.

default-agent
#

Registers the active agent as the system-wide default.

This allows automatic handling of pairing dialogs and confirmation prompts.

scan on
#

Starts device discovery.

Nearby Bluetooth devices begin appearing in the terminal output along with:

  • MAC address
  • Device name
  • Device type

Example:

[NEW] Device 11:22:33:44:55:66 HC-05

Once the target device appears, stop scanning:

scan off

Then initiate pairing:

pair 11:22:33:44:55:66

Some classic Bluetooth modules such as the HC-05 require a PIN code, commonly:

1234

After pairing succeeds, mark the device as trusted and connect:

trust 11:22:33:44:55:66
connect 11:22:33:44:55:66

Successful connection output looks similar to:

Connection successful

You can inspect device capabilities using:

info 11:22:33:44:55:66

Trusted devices automatically reconnect later without repeating the pairing procedure.

📡 Using Bluetooth as a Wireless Serial Port
#

Classic Bluetooth supports the Serial Port Profile (SPP), which emulates serial communication over RFCOMM.

This is extremely useful for:

  • MCU debugging
  • Transparent UART replacement
  • Wireless telemetry
  • Industrial serial bridging

After connecting to an SPP-capable device, determine the RFCOMM channel number.

Most HC-05 style modules use:

RFCOMM Channel 1

You can verify this using:

sdptool browse

Bind the remote serial service to a local device node:

sudo rfcomm bind 0 11:22:33:44:55:66 1

This creates:

/dev/rfcomm0

The Bluetooth link now behaves like a standard Linux serial device.

Receiving Data
#

Open a terminal and monitor incoming traffic:

sudo cat /dev/rfcomm0

Sending Data
#

In another terminal:

echo "hello from i.MX6" | sudo tee /dev/rfcomm0

The remote side immediately receives the message.

This effectively creates a wireless UART bridge.

Releasing the Binding
#

After use, release the RFCOMM binding:

sudo rfcomm release 0

Unlike physical UARTs, RFCOMM devices do not require baud-rate configuration because Bluetooth internally handles timing and transport.

For many embedded debugging tasks, lightweight tools such as cat and echo are often more reliable than full terminal emulators.

📶 Working with BLE Devices Through GATT
#

Bluetooth Low Energy (BLE) no longer uses serial-style communication.

Instead, BLE relies on:

  • ATT (Attribute Protocol)
  • GATT (Generic Attribute Profile)

Data is organized as:

  • Services
  • Characteristics
  • Descriptors

BlueZ provides a built-in GATT interface directly inside bluetoothctl.

Scanning for BLE Devices
#

Start BLE scanning explicitly:

scan le

Once the device is found:

trust 11:22:33:44:55:66
connect 11:22:33:44:55:66

Entering the GATT Menu
#

Switch into GATT mode:

menu gatt

List all available attributes:

list-attributes

Example output:

/org/bluez/hci0/dev_11_22_33_44_55_66/service000a
/org/bluez/hci0/dev_11_22_33_44_55_66/service000a/char000b

Each characteristic includes:

  • UUID
  • Access permissions
  • Notification support
  • Read/write capability

Reading Characteristic Values
#

Select an attribute:

select-attribute /org/bluez/hci0/dev_11_22_33_44_55_66/service000a/char000b

Read the value:

read

Output appears in hexadecimal form.

Example:

54 65 6d 70 20 31

Decoded ASCII:

Temp 1

Subscribing to Notifications
#

Enable notifications:

notify on

Incoming updates appear automatically:

[CHG] Attribute ... Value: ...

Writing Characteristic Values
#

Example:

write 0x01

The meaning depends entirely on the target device protocol.

Common use cases include:

  • Enabling notifications
  • Sending control commands
  • Triggering measurements
  • Configuring sensors

Once finished:

back

returns to the main menu.

For rapid BLE debugging and reverse engineering, bluetoothctl is surprisingly powerful and often eliminates the need for external GUI tools.

💻 Programmatic Bluetooth Control
#

Interactive debugging is useful during development, but production systems require application-level integration.

Several implementation approaches exist.

Shell Script Automation
#

Simple workflows can use:

  • bluetoothctl --
  • Bash scripts
  • expect

However, these approaches become difficult to maintain for larger projects.

D-Bus API Integration
#

The preferred approach is communicating directly with BlueZ through D-Bus APIs.

Common choices include:

Language Common Library
Python pydbus, bleak
C libdbus, GDBus
C++ sdbus-c++

D-Bus provides full control over:

  • Device scanning
  • Pairing
  • Connections
  • GATT discovery
  • Characteristic read/write
  • Notification handling

For production-grade embedded applications, D-Bus integration is usually the cleanest architecture.

⚙️ BLE Scanning Through HCI Sockets in C
#

BlueZ also exposes low-level HCI interfaces for direct controller communication.

The following example demonstrates a simple BLE scanner implemented in C using:

  • HCI sockets
  • libbluetooth
  • Passive scanning mode

The program:

  • Opens hci0
  • Configures BLE scan parameters
  • Receives advertising packets
  • Displays:
    • Device address
    • RSSI
    • Raw advertising data

Compile the program using:

gcc ble_scan.c -o ble_scan -lbluetooth

Example execution:

./ble_scan 5

The scan duration defaults to 5 seconds.

Key Concepts Demonstrated
#

The example illustrates several important low-level Bluetooth concepts:

HCI Device Access
#

sock = hci_open_dev(dev_id);

This opens direct communication with the Bluetooth controller.

Event Filtering
#

hci_filter_set_event(EVT_LE_META_EVENT, &flt);

Only BLE-related events are received.

Scan Configuration
#

scan_params_cp.type = 0x01;

This enables passive scanning mode.

Advertising Packet Parsing
#

evt_le_meta_event *meta

Advertising packets are decoded directly from HCI events.

Why HCI Sockets Matter
#

Direct HCI access is useful for:

  • Protocol analyzers
  • BLE sniffers
  • Manufacturing tools
  • Low-level debugging
  • Custom Bluetooth stacks

However, implementing complete GATT functionality directly on raw HCI sockets becomes extremely complex.

For higher-level BLE communication, D-Bus remains the preferred engineering approach.

🚀 Final Thoughts
#

BlueZ provides an extremely capable Bluetooth ecosystem for Embedded Linux systems, ranging from simple command-line pairing workflows to full low-level HCI access.

For embedded developers, the practical workflow usually evolves through several stages:

  1. Validate hardware with hciconfig
  2. Debug connections using bluetoothctl
  3. Prototype RFCOMM or GATT communication
  4. Integrate D-Bus APIs into production applications

The tooling may initially appear fragmented, but once the BlueZ architecture becomes familiar, Linux Bluetooth development becomes remarkably flexible and powerful.

Whether building:

  • Industrial gateways
  • Wireless sensor systems
  • BLE peripherals
  • Embedded AI edge devices
  • Serial replacement links

BlueZ remains one of the most mature and production-proven Bluetooth stacks available in the embedded Linux ecosystem.

Related

RTOS vs Linux in IoT: Why Real-Time OS Is Making a Comeback
·828 words·4 mins
RTOS Linux IoT Embedded Systems Edge Computing
Modern Embedded Linux Teaching Project for Universities in 2026
·1443 words·7 mins
Embedded Linux Buildroot Yocto Raspberry Pi Real-Time Linux Industrial-Automation Device Drivers WebSocket Education
Wind River Linux LTS 25: Advancing Secure and Scalable Edge Linux Platforms
·542 words·3 mins
Wind River Linux Yocto Project Embedded Linux Edge Computing Cybersecurity BSP