bluez project – Building a Real Bluetooth Advertisement App on Linux

 

🛍️ Café Bluebite — Part 1
Bluez project Building a Real Bluetooth Advertisement App on Linux | Requirements, SDP Browsing & Initialization
Project
Café Bluebite
Stack
BlueZ / Bash
Protocol
OBEX Object Push
Discovery
SDP / hcitool

SEO Keywords:

Bluetooth advertisement app Linux sdptool browse OPUSH OBEX Object Push BlueZ hciconfig noscan Bluetooth proximity marketing BlueZ bash script Bluetooth mall advertisement hcitool inq BD_ADDR SDP service discovery Linux obexftp Linux install

Introduction — Learning Bluetooth by Building Something Real

The best way to understand any technology is to build something real with it. Up to this point you have learned what Bluetooth is, how its layers work, how profiles are structured, and how to run basic commands like hciconfig, hcitool scan, and sdptool browse.

Now it is time to combine all of that knowledge into a complete, working application. This three-part series walks you through building Café Bluebite — a Bluetooth-based advertisement system designed for a shopping mall. The application automatically detects anyone whose phone enters Bluetooth range and silently pushes a promotional message to their device using the OBEX Object Push protocol.

This part covers: the project requirements, the SDP service browsing that powers device capability checks, the high-level design flowchart, and the first initialization step of the code.

What Is Café Bluebite?

The Problem Being Solved

Imagine a café sitting inside a busy shopping mall. They want to advertise their “deal of the day” to anyone walking past — but printed signs are easy to ignore. They want to push the deal directly to people’s phones, automatically, without the customer doing anything.

The technology that makes this possible is Bluetooth’s OBEX Object Push Profile (OPP). When a person’s phone is discoverable (or even just connectable) and has Bluetooth on, a nearby device can push a small file — like a text message or coupon — directly to the phone. Many phones from the early Bluetooth era would automatically accept such pushes from unknown devices, making it a viable proximity marketing technique.

The Café Bluebite project implements exactly this scenario using a Linux PC with a Bluetooth dongle, running a bash script that continuously monitors for new phones, checks their capabilities, and pushes the ad.

Proximity Marketing OBEX OPP Continuous Scanning Automatic Push Bash Scripting

Requirements Specification

The requirements given to us by Café Bluebite are deliberately simple. In real software engineering, you always start from a requirements document. Here is what was specified:

R1
Advertise the deal of the day to any user who enters the shopping mall and has Bluetooth switched on.
R2
Deliver the advertisement to the person’s mobile phone using Bluetooth, without requiring any action from the person.

These two requirements are all we have to work from. From them we derive the entire design. Notice what is not required: we do not need to receive any data back from the phones, and we do not need to connect to headsets, keyboards, or any non-display device.

High-Level Design — The Application Flowchart

Understanding the Flow Before Writing Any Code

Before writing a single line of code, a good developer draws a flowchart or block diagram. The flowchart for Café Bluebite has 11 steps. Let’s walk through each one and understand the reasoning behind the key decisions.

Café Bluebite — Complete Application Flowchart (11 Steps)

START

Step 1 — Initialize Bluetooth
a. Switch Bluetooth ON (hciconfig hci0 up)
b. Set friendly name to “Cafe Bluebite”
c. Disable discoverable & connectable mode (noscan)

Step 2 — Initialize Device List File
Create empty previous_bt_devices.txt

Step 3 — Create Advertisement File
Write deal-of-the-day text into advt.txt

↻   INFINITE LOOP BEGINS   ↻

Step 4 — Bluetooth Inquiry Scan
Run hcitool inq → save to new_bt_devices.txt

Step 5 — Compare Device Lists
Compare previous_bt_devices.txt vs new_bt_devices.txt

Step 6 — New Device Found? ◇
YES → continue to Step 7 NO → jump to Step 11 (delay)
↓ YES

Step 7 — Check SDP Services (OPUSH)
Run sdptool search --bdaddr $addr OPUSH on the new device

Step 8 — OK to Push Message? ◇
YES (OPUSH found) → Step 9 NO → skip, try next device
↓ YES

Step 9 — Push Advertisement File
Run obexftp --bluetooth $addr -B $channel -p advt.txt

Step 10 — Any More New Devices? ◇
YES → loop back to Step 7 NO → continue to Step 11
↓ NO more devices

Step 11 — Delay Before Next Iteration
Copy temp file to previous list. sleep 5s then loop back to Step 4
↻   BACK TO STEP 4 (runs forever until Ctrl-C)   ↻

Why Was the App Designed This Way? (Key Decision Explanations)

Three design decisions in the flowchart need special attention. Understanding why these choices were made will help you adapt the design for your own projects.

Step 1c — Why disable discoverable and connectable mode?

We are the advertisement sender, not the receiver. If we left our Linux device discoverable and connectable, nearby phones might try to pair with us, send us files, or establish connections we don’t want. By running hciconfig hci0 noscan we turn off both Inquiry Scan (makes us discoverable) and Page Scan (makes us connectable). Our device can now only initiate connections outward — it cannot be the target of an incoming connection.

Step 7 — Why check SDP for OPUSH before sending?

Bluetooth is used by dozens of device types — headsets, keyboards, mice, printers, hands-free car kits. None of these have a screen, so sending them a promotional text message would be pointless. By querying the SDP server and looking for the OBEX Object Push service record (service UUID: OPUSH), we confirm two things: (1) the device has a display and can receive push messages, and (2) we know exactly which RFCOMM channel to connect on. If the device doesn’t have OPUSH registered, it is not a phone or tablet and we skip it entirely.

Step 11 — Why use an infinite loop with a sleep delay?

A shopping mall has a constant flow of people arriving and leaving. We want to keep scanning indefinitely. The sleep 5s pause prevents us from flooding the 2.4 GHz radio band with continuous inquiries, which would interfere with other Bluetooth devices and drain our adapter. In a production deployment you would replace the infinite loop with logic that checks a shutdown signal or a scheduled stop time, but for learning purposes an infinite loop that you terminate with Ctrl-C is perfectly fine.

Understanding SDP Records — The Foundation of Service Discovery

What Does sdptool browse Actually Return?

Before writing any code, run sdptool browse against a phone that is nearby. This tells you exactly what the phone can do over Bluetooth. The output is a list of Service Records. Each service record describes one Bluetooth profile the device supports.

Here is an annotated real-world output from browsing a mobile phone at address 68:ED:43:25:0E:99:

$ sdptool browse 68:ED:43:25:0E:99
Browsing 68:ED:43:25:0E:99 ...

Service Name: Dialup Networking
Service RecHandle: 0x10000
Service Class ID List:
    "Dialup Networking" (0x1103)
    "Generic Networking" (0x1201)
Protocol Descriptor List:
    "L2CAP" (0x0100)
    "RFCOMM" (0x0003)
      Channel: 1
Profile Descriptor List:
    "Dialup Networking" (0x1103)
    Version: 0x0100

Service Name: Voice gateway
Service RecHandle: 0x10001
Service Class ID List:
    "Headset Audio Gateway" (0x1112)
    "Generic Audio" (0x1203)
Protocol Descriptor List:
    "L2CAP" (0x0100)
    "RFCOMM" (0x0003)
      Channel: 2
Profile Descriptor List:
    "Headset" (0x1108)
    Version: 0x0100

Service Name: Hands-free
Service RecHandle: 0x10002
Service Class ID List:
    "Handsfree Audio Gateway" (0x111f)
    "Generic Audio" (0x1203)
Protocol Descriptor List:
    "L2CAP" (0x0100)
    "RFCOMM" (0x0003)
      Channel: 3
Profile Descriptor List:
    "Handsfree" (0x111e)
    Version: 0x0105

Anatomy of a Single SDP Service Record
Service Name A human-readable string describing the profile (e.g., “Hands-free”, “OBEX Object Push”). This is what you see when a phone lists Bluetooth services in its settings menu.
Service RecHandle A unique 32-bit handle (like a database ID) that identifies this service record on this device. If you need to query one specific service again later you use this handle. Assigned locally by the device’s SDP server.
Service Class ID List The official UUID(s) that identify which Bluetooth profile this service implements. These UUIDs are defined by the Bluetooth SIG. For example, 0x1103 = Dialup Networking, 0x110a = A2DP Audio Source. Multiple UUIDs can appear because a service might be both “Generic Audio” and “Headset Audio Gateway”.
Protocol Descriptor List The protocol stack needed to reach this service. Almost every service shows L2CAP at the bottom. RFCOMM on top means you connect via a serial-port style channel. AVDTP means it’s audio streaming. The Channel number here is critical — it tells you which RFCOMM channel to dial into.
Profile Descriptor List Confirms which Bluetooth profile specification this service follows, along with the version number of that spec. This lets the connecting device verify that both sides are running compatible versions of the same profile.

Critical insight for Café Bluebite: In the output above we do NOT see an OBEX Object Push record. This phone only has Dialup Networking, Voice Gateway, and Hands-Free. To push advertisements we need to find a phone that has an OBEX Object Push service record with a channel number. The check_sdp function in the script handles exactly this check.

Searching for OPUSH Specifically with sdptool

Instead of browsing ALL services (which can be slow and return a lot of output), you can ask sdptool to search for one specific service by name:

# Search specifically for OBEX Object Push on a remote device
sdptool search --bdaddr 68:ED:43:25:0E:99 OPUSH

If the phone supports OPUSH, the output looks like this:

Service Name: OBEX Object Push
Service RecHandle: 0x10005
Service Class ID List:
  "OBEX Object Push" (0x1105)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 9
  "OBEX" (0x0008)
Profile Descriptor List:
  "Object Push" (0x1105)
    Version: 0x0100

The key piece of information here is Channel: 9. This is the RFCOMM channel number we must pass to obexftp when we push our advertisement file. Every phone may have OPUSH on a different channel — this is why we cannot hardcode the channel number in our script and must extract it from the SDP query at runtime.

OBEX Object Push Protocol Stack
OBEX Application
(File push / pull logic)
OBEX Protocol
(Object Exchange layer)
RFCOMM
(Serial port emulation, Channel 9)
L2CAP
(Segmentation & reassembly)
ACL Radio Link
(Bluetooth wireless)

Code Prerequisites — Installing obexftp

Why obexftp Is Needed and How to Install It

The Café Bluebite script uses a command-line tool called obexftp to push the advertisement file to the phone. This tool handles the entire OBEX Object Push session — it connects to the phone on the correct RFCOMM channel, opens an OBEX session, and transfers the file in one command.

obexftp is not always pre-installed. To check if it is available:

# Check if obexftp is installed
which obexftp
# or
obexftp --version

If the command is not found, install it with:

# On Debian / Ubuntu
sudo apt-get update
sudo apt-get install obexftp

# Verify it installed correctly
obexftp --version

During installation you may see dependency prompts. Accept them. The package downloads and installs automatically if you are online. Once done, obexftp --version should print the version number without error.

Modern note: On very recent Linux systems (Ubuntu 22.04+), obexftp has been deprecated in favour of the DBus-based bluetooth-sendto or the obex-client provided by BlueZ itself via bluetoothctl. For learning purposes the old obexftp approach is still the clearest to understand.

Step 1 — Initialize the Bluetooth Controller

What Initialization Does and Why Each Line Matters

The very first thing the script does is set up the Bluetooth adapter to behave exactly the way we want — active enough to scan for devices, but invisible to those devices so they don’t bother us with incoming requests.

Here is the Step 1 code with a detailed explanation of each command:

#! /bin/bash
# Step 1: Initialize Bluetooth

echo "Initializing the Bluetooth Controller on hci0..."
hciconfig hci0 up
# Brings the Bluetooth adapter up from a powered-off state.
# hci0 is the Linux name for the first Bluetooth adapter.
# If you have a second dongle it would be hci1.

echo "Configuring the name to Cafe Bluebite..."
hciconfig hci0 name "Cafe Bluebite"
# Sets the Bluetooth "friendly name" of our adapter.
# This is the name that would show up on other devices
# if they scanned for us — though we are about to make
# ourselves invisible, so this mainly identifies our
# device in local logs and BlueZ tools.

echo "Disabling Simple Secure Pairing..."
hciconfig hci0 sspmode 0
# Disables Simple Secure Pairing (SSP).
# SSP is a pairing mechanism from Bluetooth 2.1.
# Disabling it can help compatibility with older phones
# that use legacy PIN-based pairing.

echo "Switching off inquiry and page scans..."
hciconfig hci0 noscan
# This is the most important line in Step 1.
# "noscan" disables BOTH:
#   - Inquiry Scan  (makes us discoverable when others run hcitool scan)
#   - Page Scan     (makes us connectable when others try to connect to us)
# After this command, WE can still initiate scans and connections,
# but nobody can find or connect to US.

hciconfig Scan Modes — What Each Setting Controls
hciconfig hci0 noscan
🔴
Disables BOTH scans.
Device is totally invisible and unreachable from the outside.
Can still initiate connections.
hciconfig hci0 piscan
🟡
Enables BOTH scans.
Fully discoverable AND connectable.
Default “visible” mode.
hciconfig hci0 pscan
🟢
Page Scan only.
Connectable but NOT discoverable.
For paired devices only.
hciconfig hci0 iscan
🔵
Inquiry Scan only.
Discoverable but NOT directly connectable.
Rarely used in practice.

After Step 1 completes, our adapter looks like this to the outside world: it is completely invisible. Other devices cannot find it by scanning and cannot connect to it. However, from our perspective, we are fully operational — we can run hcitool inq to find other devices and connect to them as the initiating party.

What’s Coming in Part 2?

Part 1 covered the requirements, the flowchart, SDP service records, and the initialization code (Step 1). In Part 2 we write the meat of the application:

  • Step 2: Create the empty device tracking file
  • Step 3: Write the advertisement message
  • Step 4: Run hcitool inq and parse the BD_ADDRs
  • Step 5: Compare old and new device lists with grep
  • Step 7: The check_sdp() function
  • Steps 8 & 9: Pushing the advertisement with obexftp

Continue the Café Bluebite Tutorial

Part 1 complete — you understand the design. Now let’s write the core scanning and push logic.

Part 2: Scanning, SDP Check & OBEX Push → Part 3: Complete Code & Summary

Leave a Reply

Your email address will not be published. Required fields are marked *