Café Bluebite
BlueZ / Bash
OBEX Object Push
SDP / hcitool
SEO Keywords:
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?
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.
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:
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
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.
b. Set friendly name to “Cafe Bluebite”
c. Disable discoverable & connectable mode (noscan)
previous_bt_devices.txtadvt.txthcitool inq → save to new_bt_devices.txtprevious_bt_devices.txt vs new_bt_devices.txtsdptool search --bdaddr $addr OPUSH on the new deviceobexftp --bluetooth $addr -B $channel -p advt.txtsleep 5s then loop back to Step 4Three 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
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
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.
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.
(File push / pull logic)
(Object Exchange layer)
(Serial port emulation, Channel 9)
(Segmentation & reassembly)
(Bluetooth wireless)
Code Prerequisites — Installing obexftp
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
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.
Device is totally invisible and unreachable from the outside.
Can still initiate connections.
Fully discoverable AND connectable.
Default “visible” mode.
Connectable but NOT discoverable.
For paired devices 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.
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
