Delay & Reset
Bluebite.sh
~70 (bash)
Bluetooth LE
Where We Are
Parts 1 and 2 built every piece of the Café Bluebite application step by step: initializing Bluetooth, creating tracking files, scanning for devices, detecting new arrivals with grep, checking SDP for OPUSH support, and pushing the advertisement with obexftp.
This final part adds Step 11 (the delay and file rotation that makes the loop sustainable), then assembles all the code into the complete, runnable Bluebite.sh script. We also look at what would need to be improved for a production deployment and close the chapter with a look at where we go next — Bluetooth Low Energy.
Step 11 — Delay and File Rotation
Without Step 11 the loop would have two serious problems. First, it would run at full CPU speed with no pause between scans — hammering the 2.4 GHz band every few milliseconds and interfering with every Bluetooth and Wi-Fi device nearby. Second, the “previous devices” file would never get updated, so every device would be treated as new on every iteration, and the same phone would get bombarded with the advertisement dozens of times per minute.
Step 11 solves both problems with exactly two lines:
# Step 11a: Rotate the device files
# Copy the current scan results into the "previous" file.
# This means on the NEXT loop iteration, any device currently
# in range will be in the previous list and won't get
# processed again unless they leave range and come back.
cp temp_bt_devices.txt previous_bt_devices.txt
# Step 11b: Wait 5 seconds before the next scan
# This gives the radio a breather and prevents flooding.
# 5 seconds is a reasonable balance: short enough to catch
# someone who just walked in, long enough not to spam the airwaves.
sleep 5s
Why copy the file BEFORE sleeping, not after? If we slept first and then copied, and a new device appeared during the sleep window, that device could theoretically be in the next scan’s results but our “previous” file would still be from two iterations ago. By rotating files immediately before sleeping, we ensure the previous file is always exactly one scan behind the current one — making the comparison in Step 5 accurate every time.
The Complete Bluebite.sh Script
This is the entire working script assembled from all 11 steps. Every block is explained with comments so you understand exactly what each line contributes. Save this as Bluebite.sh, make it executable with chmod +x Bluebite.sh, and run it with sudo ./Bluebite.sh.
#! /bin/bash
# Bluebite.sh — Bluetooth Proximity Advertisement System
# Detects new Bluetooth devices in range and pushes an
# advertisement text file to any that support OBEX Object Push.
# Run with: sudo ./Bluebite.sh
# Stop with: Ctrl-C
##############################################################
# FUNCTION: check_sdp
# Purpose : Query a remote device's SDP server to find out
# if it supports OBEX Object Push (OPUSH).
# If found, extract and save the RFCOMM channel number.
# Input : $1 = BD_ADDR of the remote device (e.g. 00:1A:2B:3C:4D:5E)
# Output : Returns 0 if OPUSH found (also sets $channel_num)
# Returns 1 if OPUSH NOT found
##############################################################
function check_sdp ()
{
# Clear / create the temp files for this run
> temp_sdp.txt
> temp_sdp1.txt
# Query the remote device's SDP server for OPUSH service.
# The result is stored in temp_sdp.txt for further parsing.
sdptool search --bdaddr $1 OPUSH > temp_sdp.txt
# Search the SDP output for the human-readable service name.
# If sdptool found OPUSH, the file will contain a line with
# "OBEX Object Push" in it.
# We throw away grep's printed output (to /dev/null) and
# only use its exit status ($?).
grep "OBEX Object Push" temp_sdp.txt > /dev/null
retval=$?
if [ $retval -eq 0 ]
then
# OPUSH is supported! Now find the RFCOMM channel number.
# The channel line in sdptool output looks like:
# Channel: 9
grep "Channel: " temp_sdp.txt > temp_sdp1.txt
retval=$?
if [ $retval -eq 0 ]
then
# Extract the number after the colon.
# "cut -d: -f2" splits on ":" and takes field 2.
# Backtick syntax captures the command's output as a string.
channel_num=`cut -d: -f2 temp_sdp1.txt`
echo "OBEX Object Push service found on Channel: $channel_num"
fi
fi
# This function returns the same value as our grep result:
# 0 = OPUSH found, 1 = OPUSH not found
return $retval
}
##############################################################
# FUNCTION: push_advt
# Purpose : Push the advertisement file to a remote device
# using OBEX Object Push over RFCOMM.
# Input : $1 = BD_ADDR of the target device
# Uses: global $channel_num (set by check_sdp)
# Output : None (file transfer result shown by obexftp)
##############################################################
function push_advt ()
{
# Print the command for logging/debug purposes
echo "Executing: obexftp --bluetooth $1 -B $channel_num -p advt.txt"
# obexftp flags:
# --bluetooth $1 target device Bluetooth address
# -B $channel_num RFCOMM channel number (from SDP)
# -p advt.txt -p = "put" (push) this local file
obexftp --bluetooth $1 -B $channel_num -p advt.txt
}
##############################################################
# MAIN SCRIPT STARTS HERE
##############################################################
# --- Step 1: Initialize the Bluetooth adapter ---
echo "Initializing the Bluetooth Controller on hci0"
hciconfig hci0 up
# Brings the Bluetooth hardware up from powered-off state.
# Change hci0 to hci1 if your dongle is the second adapter.
echo "Configuring the name to Cafe Bluebite"
hciconfig hci0 name "Cafe Bluebite"
# Sets the Bluetooth device name. Other devices would see this
# name if they scanned for us (though we disable that next).
echo "Disabling Simple Secure Pairing"
hciconfig hci0 sspmode 0
# Turns off SSP (Bluetooth 2.1+ pairing mode).
# Improves compatibility with older legacy phones.
echo "Switching off inquiry and page scans."
hciconfig hci0 noscan
# Disables both discoverable mode (inquiry scan) and
# connectable mode (page scan).
# WE can still scan outward — but nobody can find or reach US.
# --- Step 2: Initialize device tracking file ---
# Create an empty file to store the previous iteration's device list.
# Starting empty means every device in the first scan will be "new".
> previous_bt_devices.txt
# --- Step 3: Create the advertisement message ---
# Write the deal-of-the-day text to advt.txt.
# This is the file that gets pushed to every eligible phone.
echo "Welcome to the shopping mall !!" > advt.txt
echo "Visit Cafe Bluebite for exciting deals" >> advt.txt
echo "Get 15% off if you show this message" >> advt.txt
echo "Starting infinite loop. Press Ctrl-C to exit program."
##############################################################
# MAIN LOOP — Runs forever. Press Ctrl-C to stop.
##############################################################
while [ 0 -eq 0 ]
do
# --- Step 4: Scan for Bluetooth devices ---
# Run inquiry scan. Output includes BD_ADDR, clock offset, and
# Class of Device for each device found within ~10 metres.
hcitool inq > temp_bt_devices.txt
# Extract only the BD_ADDR column from the raw inquiry output:
# tail -n +2 skips the first "Inquiring ..." header line
# cut -c2-18 keeps only characters 2-18 = the 17-char BD_ADDR
tail -n +2 temp_bt_devices.txt | cut -c2-18 > new_bt_devices.txt
# Count and display how many devices are visible right now
num_devices=`cat new_bt_devices.txt | wc -l`
echo "Found $num_devices devices in this iteration"
# --- Steps 5 and 6: Compare and find new devices ---
# Loop over every BD_ADDR in the current scan results
for line in $(cat new_bt_devices.txt)
do
# Check if this BD_ADDR was present in the previous scan.
# Output goes to /dev/null — we only care about exit status $?
grep $line previous_bt_devices.txt > /dev/null
# If $? equals 1, grep did NOT find it = this is a NEW device
if [ "$?" -eq "1" ]
then
echo "New Device found: $line"
# --- Step 7: SDP service capability check ---
echo "Checking Services..."
check_sdp $line
# --- Step 8: Was OPUSH found? ---
if [ "$?" -eq "0" ]
then
# --- Step 9: Push the advertisement ---
echo "Device supports OPUSH. Pushing advertisement."
push_advt $line
else
echo "Device does not support OPUSH. Skipping."
fi
fi
# Step 10: The for loop handles moving to the next device
done
# --- Step 11: Rotate files and pause before next iteration ---
# Update the "previous" list to reflect what we saw this cycle.
# Next iteration's comparison will use this as the baseline.
cp temp_bt_devices.txt previous_bt_devices.txt
# Pause for 5 seconds before scanning again.
# This prevents radio flooding and excessive CPU usage.
sleep 5s
done # End of infinite while loop
Here is the complete sequence to get the script running on your Linux machine:
# 1. Create the script file
nano Bluebite.sh
# Paste the complete code above, then save with Ctrl+O, exit with Ctrl+X
# 2. Make the script executable
chmod +x Bluebite.sh
# 3. Verify obexftp is installed (install if needed)
which obexftp || sudo apt-get install -y obexftp
# 4. Verify your Bluetooth adapter is present
hciconfig -a
# 5. Run the script (requires root for hciconfig)
sudo ./Bluebite.sh
# You should see output like:
# Initializing the Bluetooth Controller on hci0
# Configuring the name to Cafe Bluebite
# Disabling Simple Secure Pairing
# Switching off inquiry and page scans.
# Starting infinite loop. Press Ctrl-C to exit program.
# Found 3 devices in this iteration
# New Device found: 68:ED:43:25:0E:99
# Checking Services...
# OBEX Object Push service found on Channel: 9
# Device supports OPUSH. Pushing advertisement.
# Executing: obexftp --bluetooth 68:ED:43:25:0E:99 -B 9 -p advt.txt
# ...
# 6. To stop the script
# Press Ctrl-C
hcitool inq → scans ~10s → saves to temp filetail | cut → strips to BD_ADDR list in new_bt_devices.txtgrep checks: was this addr in previous file?NO → process. YES → skip (already got the ad)
sdptool search OPUSH → gets channel number (or fails)obexftp --bluetooth $addr -B $channel -p advt.txtcp temp_bt_devices.txt previous_bt_devices.txt → rotatesleep 5s → pause 5 seconds → back to top of while loopDisclaimer — Important Notes Before You Deploy
The textbook is explicit about this, and it deserves to be highlighted clearly for students:
“The code provided here is only for educational purposes to illustrate the use of different Bluetooth related commands and features. It has been tested with only a few mobile phones and may not work with all phones or lead to some unknown problems. So you are advised to use it at your own risk.”
Here are the specific limitations and why they exist:
The script does not check if hciconfig hci0 up succeeded, whether hcitool inq returned useful data, or whether obexftp transferred the file successfully. In a real app, every command needs a return-code check with appropriate recovery logic.
If two new devices appear at the same time and both use OPUSH, the script processes them sequentially. The second device’s push might take so long that its SDP session times out, or the inquiry scan misses devices that left while we were busy sending. A real-world app would use parallel background processes (& in bash, or threads in C).
When you restart the script, previous_bt_devices.txt is cleared. A phone that is still in range when you restart will immediately get the ad again. A production system would store seen addresses in a persistent database with timestamps.
Sending unsolicited Bluetooth messages to people’s phones without consent may violate privacy laws in many countries (GDPR in Europe, various local regulations). This was a common practice in the early Bluetooth era (called “Bluejacking”) but is now legally murky in many jurisdictions. Always get proper legal advice before deploying proximity marketing systems.
Most modern smartphones (Android 6+, iOS) have changed their Bluetooth security model. By default they reject OBEX Object Push from unpaired devices. The technique in this tutorial worked well on Nokia/Sony Ericsson phones from the mid-2000s to early 2010s. For modern devices you would need the user to explicitly accept the pairing and file transfer, which defeats the “automatic” nature of the application.
If you were building this for commercial deployment, here are the enhancements you would implement. These are also excellent exercises to practice your Bluetooth and Linux programming skills:
Store sent-to addresses in a SQLite database with a timestamp. Only send to a device again after X hours (e.g., once per day). Survives script restarts.
# Using sqlite3 in bash
sqlite3 seen_devices.db \
"CREATE TABLE IF NOT EXISTS devices
(addr TEXT, last_seen INTEGER);"
Run SDP check and push in the background for each device, so multiple pushes can happen simultaneously instead of waiting for one to finish before starting the next.
# Run push in background (&)
# The subshell () prevents variable pollution
(check_sdp $line && push_advt $line) &
Trap the SIGINT signal (Ctrl-C) to run cleanup before exiting — remove temp files, restore hciconfig to discoverable mode, log statistics.
# Trap Ctrl-C and run cleanup
trap "cleanup_and_exit" SIGINT
cleanup_and_exit() {
echo "Shutting down..."
rm -f temp_*.txt
hciconfig hci0 piscan
exit 0
}
Use the CoD value from hcitool inq to pre-filter devices. Only check SDP on devices whose major class is “Phone” (0x200) — skip all others without an SDP round-trip.
# Parse CoD from inq output
# Major class 0x200 = Phone/tablet
if echo "$cod" | grep -q "0x02"; then
check_sdp $addr
fi
Chapter Summary — What We Covered in “Getting the Hands Wet”
This chapter, titled “Getting the Hands Wet,” was your first real hands-on encounter with Bluetooth programming on Linux. Here is a consolidated view of everything you learned:
- Connecting a USB Bluetooth dongle
- Verifying with
hciconfig -a - Installing BlueZ tools
- Understanding hci0 interface
hciconfig hci0 up/downhciconfig noscan/piscanhcitool inq / scansdptool browse / search
- Service records structure
- Service Class IDs (UUIDs)
- Protocol Descriptor Lists
- RFCOMM channel discovery
- Requirements → design → code
- Bash functions and return codes
- File I/O and grep for comparison
- OBEX push with obexftp
With this chapter, the coverage of Classic Bluetooth (also called BR/EDR — Basic Rate / Enhanced Data Rate) is complete. You now have a solid foundation in Bluetooth’s history, architecture, protocols, profiles, and practical Linux development.
The next section of the course transitions to Bluetooth Low Energy (BLE) — also known as Bluetooth Smart or Bluetooth 4.0+. BLE is a fundamentally different radio technology optimized for IoT, wearables, beacons, and sensor networks. It trades throughput for dramatically lower power consumption.
| Feature | Classic Bluetooth (BR/EDR) | Bluetooth Low Energy (BLE) |
|---|---|---|
| Power consumption | Medium–High | Very Low (coin cell lasts years) |
| Data throughput | Up to 3 Mbps (EDR) | ~1 Mbps (low overhead) |
| Connection time | Seconds (page/inquiry) | Milliseconds (advertising) |
| Typical use cases | Audio streaming, file transfer | IoT sensors, beacons, wearables |
| Pairing required | Usually yes | Often no (read-only sensors) |
| Protocol model | Profiles (A2DP, HFP, OPP…) | GATT Services & Characteristics |
| Linux tool | hcitool, sdptool, obexftp | bluetoothctl, gatttool, hcitool lescan |
Everything you learned in this chapter carries forward. The hci0 interface, BlueZ architecture, and command-line tools are the same for BLE — you just use different commands and a different mental model for how devices expose data (GATT attributes instead of RFCOMM profiles).
- Bluetooth Core Specification 4.0 — bluetooth.org — The official spec covering HCI, L2CAP, SDP, RFCOMM, and all Bluetooth 4.0 features including BLE.
- Bluetooth Profile Specifications — bluetooth.org — Individual profile specs for A2DP, AVRCP, HFP, OPP, and all other standard profiles.
- BlueZ Official Website — bluez.org — Source code, documentation, mailing list, and latest releases for the Linux Bluetooth stack.
- BlueZ GitHub Repository —
github.com/bluez/bluez— The actual source code with examples intest/andtools/directories.
🎉 Chapter Complete!
You have built a real Bluetooth application from requirements to running code. Up next: Bluetooth Low Energy, GATT, and modern IoT development with BlueZ.
