Bluetooth Device for QR Code and Ad Push-Technical Implementation Guide

I. Hardware Selection & Mature Cases

1. Development Boards/Modules(For Prototyping)

HardwareFeaturesCostBest For
ESP32 Dual-core MCU + BLE/Wi-Fi, strong community $5-12 Rapid prototyping, fast development
nRF52840 (Adafruit/Seeed) BLE 5.0, ultra-low power, high performance $12-23 Pre-production testing, performance needs
TI CC2640/CC2652 Professional BLE SoC, extremely low power $8-15 Battery devices, long runtime
Raspberry Pi Pico W Low power + BLE, cost-effective $6-11 Simple prototypes

**Recommended starting point:**ESP32 for its mature ecosystem and extensive documentation.

2. Commercial Ready-to-Use Devices

DeviceFeaturesPriceNotes
Estimote Beacon Mature iBeacon/Eddystone devices $25-50/unit Industrial grade, waterproof, programmable
Kontakt.io Beacon Enterprise Beacon with management platform $20-40/unit Suitable for large-scale deployment
RadBeacon Open-source Beacon, highly customizable $30/unit Supports multiple broadcast formats
Chinese: Minew BLE 5.0 support $15-30/unit Complete management platform available

Recommended commercial solution: Use Estimote Beacon or Minew products with their SDKs and APIs for custom data broadcasting.


II. Core Implementation Principles

Broadcast Method Selection

  1. iBeacon Format(Apple Standard)

    • Structure: UUID + Major + Minor + TX Power

    • Pros: Excellent iOS compatibility, system-level support

    • Cons: Limited data capacity(31 bytes)

  2. Eddystone Format(Google Standard)

    • Supports Eddystone-URL(broadcasts a URL)

    • Pros: Good Android compatibility, can display directly in notification

    • Cons: Requires URL shortening service

  3. Custom Manufacturer Data

    • Add custom data to broadcast packets

    • Pros: Flexible, can broadcast any data(QR content, JSON, etc.)

    • Requirement: Requires companion app for parsing

**Recommended approach:**Broadcast both iBeacon + custom data for compatibility and functionality.


III. Complete Code Examples

1. ESP32 Firmware-Broadcasting QR Codes & Ads

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// Device configuration
#define DEVICE_NAME "SmartBeacon_001"
#define COMPANY_ID 0x1234 // Replace with your company ID

// iBeacon parameters
#define BEACON_UUID "a0b1c2d3-e4f5-a6b7-c8d9-e0f1a2b3c4d5"
#define MAJOR 1001
#define MINOR 2001
#define TX_POWER 0xC9 // -55dBm

// QR code and advertisement content
String qrCodeData = "https://example.com/pay?id=123&amount=50";
String adContent = "Today's Special: Buy 1 Coffee Get 1 Free";

BLEAdvertising *pAdvertising;

void setup() {
Serial.begin(115200);

// Initialize BLE
BLEDevice::init(DEVICE_NAME);
BLEServer *pServer = BLEDevice::createServer();
pAdvertising = pServer->getAdvertising();

// Configure advertising parameters
setupBeacon();
setupCustomData();

// Start advertising
pAdvertising->start();
Serial.println("Beacon started advertising...");

// Dynamic content updates (example: rotate ads every 10s)
updateAdvertisingContent();
}

void loop() {
delay(10000);
updateAdvertisingContent();
}

void setupBeacon() {
BLEAdvertisementData advertisementData;

// iBeacon packet structure
uint8_t beaconData[25] = {
0x02, 0x15, // iBeacon prefix
0xa0, 0xb1, 0xc2, 0xd3, 0xe4, 0xf5, 0xa6, 0xb7, // UUID
0xc8, 0xd9, 0xe0, 0xf1, 0xa2, 0xb3, 0xc4, 0xd5,
(MAJOR >> 8) & 0xFF, MAJOR & 0xFF, // Major
(MINOR >> 8) & 0xFF, MINOR & 0xFF, // Minor
TX_POWER
};

advertisementData.setManufacturerData(COMPANY_ID, beaconData, sizeof(beaconData));
pAdvertising->setAdvertisementData(advertisementData);
}

void setupCustomData() {
BLEAdvertisementData scanResponseData;

// Build custom data packet
String fullData = "QR:" + qrCodeData + ";AD:" + adContent;

// BLE limit: 31 bytes max
if (fullData.length() > 28) {
fullData = fullData.substring(0, 28);
}

scanResponseData.setManufacturerData(COMPANY_ID,
(uint8_t*)fullData.c_str(), fullData.length());
pAdvertising->setScanResponseData(scanResponseData);
}

void updateAdvertisingContent() {
static int adIndex = 0;
String ads[] = {
"Today's Special: Buy 1 Coffee Get 1 Free",
"New User Discount: $5 Off First Order",
"Weekend Special: Cakes 20% Off",
"Follow Our Account for Coupons"
};

adContent = ads[adIndex % 4];
adIndex++;

// Reconfigure advertising data
setupCustomData();
pAdvertising->start();

Serial.println("Updated ad: " + adContent);
}

2. Android Client(Kotlin)

// BeaconScanner.kt
import android.bluetooth.le.*
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
private lateinit var bleScanner: BluetoothLeScanner
private var scanning = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
bleScanner = bluetoothAdapter.bluetoothLeScanner

startBLEScan()
}

private fun startBLEScan() {
val filters = mutableListOf()
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()

bleScanner.startScan(filters, settings, scanCallback)
scanning = true
}

private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
val scanRecord = result.scanRecord

// Check for iBeacon
scanRecord?.bytes?.let {
if (isIBeacon(it)) {
processIBeacon(it)
}
}

// Check custom manufacturer data
val manufacturerData = scanRecord?.getManufacturerSpecificData(0x1234)
manufacturerData?.let {
val dataString = String(it)
parseCustomData(dataString)
}
}
}

private fun isIBeacon(data: ByteArray): Boolean {
return data.size >= 25 && data[0] == 0x02.toByte() && data[1] == 0x15.toByte()
}

private fun processIBeacon(data: ByteArray) {
// Parse iBeacon data
val uuidBytes = data.copyOfRange(2, 18)
val major = ((data[18].toInt() and 0xFF) shl 8) or
(data[19].toInt() and 0xFF)
val minor = ((data[20].toInt() and 0xFF) shl 8) or
(data[21].toInt() and 0xFF)

// Convert to hex string
val uuid = uuidBytes.joinToString("") { "%02x".format(it) }

// Fetch content from server
fetchContent(uuid, major, minor)
}

private fun parseCustomData(data: String) {
data.split(";").forEach { part ->
when {
part.startsWith("QR:") -> {
val qrContent = part.substring(3)
displayQRCode(qrContent)
}
part.startsWith("AD:") -> {
val adContent = part.substring(3)
showAdNotification(adContent)
}
}
}
}

private fun fetchContent(uuid: String, major: Int, minor: Int) {
Thread {
try {
val url = "https://yourserver.com/api/content?uuid=$uuid&major=$major&minor=$minor"
// Perform network request...

// Response format: {"qr": "payment_link", "ad": "ad_content"}
// Parse and display
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}

override fun onDestroy() {
super.onDestroy()
if (scanning) {
bleScanner.stopScan(scanCallback)
}
}
}

3. iOS Client(Swift)

// BeaconScanner.swift
import CoreBluetooth
import CoreLocation
import UserNotifications

class BeaconScanner: NSObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
private var discoveredDevices: [CBPeripheral] = []

override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: .main)
}

func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
startScanning()
}
}

func startScanning() {
centralManager.scanForPeripherals(
withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]
)
}

func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any],
rssi RSSI: NSNumber) {

// Check for manufacturer data
if let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data {

// Detect iBeacon
if manufacturerData.count >= 25 &&
manufacturerData[0] == 0x02 &&
manufacturerData[1] == 0x15 {
handleIBeacon(manufacturerData)
}

// Check for custom data (Company ID: 0x1234)
if manufacturerData.count > 2 {
let companyId = UInt16(manufacturerData[0]) << 8 | UInt16(manufacturerData[1])
if companyId == 0x1234 {
let customData = manufacturerData.subdata(in: 2..

4. Server API(Node.js + Express)

// server.js
const express = require('express');
const app = express();
app.use(express.json());

// Device configuration database (use MongoDB/MySQL in production)
const deviceConfigs = {
'a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5_1001_2001': {
qrContent: 'https://example.com/pay?id=123&amount=50',
adContent: 'Today\'s Special: Coffee Buy 1 Get 1 Free',
redirectUrl: 'https://example.com/menu',
validUntil: '2024-12-31',
priority: 1
}
};

// API: Get content by device identifier
app.get('/api/content', (req, res) => {
const { uuid, major, minor } = req.query;
const deviceKey = `$

{uuid}
_$

{major}
_$

{minor}
`;

const config = deviceConfigs[deviceKey];

if (config) {
res.json({
success: true,
qr: config.qrContent,
ad: config.adContent,
redirect: config.redirectUrl,
priority: config.priority,
timestamp: Date.now()
});
} else {
res.json({
success: false,
message: 'Device not configured'
});
}
});

// API: Admin portal - Update device configuration
app.post('/api/admin/device', (req, res) => {
const { deviceId, qrContent, adContent, priority } = req.body;

// Add authentication middleware in production
if (!deviceId) {
return res.status(400).json({ error: 'Device ID required' });
}

deviceConfigs[deviceId] = {
qrContent,
adContent,
priority: priority || 1,
updatedAt: Date.now(),
updatedBy: req.headers['x-user-id'] // From auth token
};

res.json({
success: true,
message: 'Device configuration updated'
});
});

// API: Device management
app.get('/api/admin/devices', (req, res) => {
// Return all device configurations
res.json({
devices: deviceConfigs,
count: Object.keys(deviceConfigs).length,
timestamp: Date.now()
});
});

// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'OK',
uptime: process.uptime(),
version: '1.0.0'
});
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port $

{PORT}
`);
});

IV. Deployment Recommendations

1. Development Workflow

  1. Hardware Prototype: Flash ESP32 with the Arduino code

  2. Mobile Testing: Install Android/iOS test apps

  3. Server Setup: Deploy Node.js server with APIs

  4. Management Portal: Build web interface for content management

2. Performance Optimization

  • Advertising Interval: Adjust between 100ms-1s for power/performance balance

  • Data Compression: Use URL shortening services for QR codes

  • Caching Strategy: Implement client-side caching to reduce server requests

3. Production Considerations

  • Security: Implement HTTPS, API authentication(JWT), input validation

  • Scalability: Use Redis for caching, implement rate limiting

  • Monitoring: Add logging, metrics collection, and alerting

  • Backup: Regular database backups and disaster recovery plan

4. Compliance Implementation

  • Privacy Policy: Clearly explain data collection and usage

  • User Consent: Request Bluetooth and notification permissions at runtime

  • Opt-out: Provide easy way to disable ads in app settings

  • GDPR/CCPA Compliance: Implement data access and deletion endpoints

V. Commercial Solutions

Quick Deployment Options

  1. Use Existing Platforms:

    • Google Nearby API: Complete nearby interactions solution

    • Apple Core Location with Beacons: Native iOS beacon support

    • Kontakt.io Cloud: Enterprise beacon management platform

    • Estimote Cloud: Device management and analytics

  2. Hybrid Approach:

    • Purchase pre-programmed beacons from manufacturers

    • Use their cloud platform for management

    • Develop custom mobile app for your specific use case

  3. White-label Solutions:

    • Companies like Beaconstac, OnTheGo Platforms offer customizable solutions

    • Typically $500-2000/month for platform + hardware

Cost Estimation

ComponentPrototype CostProduction Cost (1000 units)
Hardware $15-50/unit $8-20/unit (bulk pricing)
Mobile App $5,000-15,000 $20,000-50,000 (full featured)
Backend Server $500/month (cloud) $2,000-5,000/month (scaled)
Maintenance - 15-20% of initial cost annually

Time to Market

  • Prototype: 2-4 weeks

  • MVP(Minimum Viable Product): 6-8 weeks

  • Production Ready: 12-16 weeks

  • App Store Approval: Additional 2-4 weeks

This solution provides a solid foundation for testing your business concept with minimal investment. For payment functionality, additional security certifications and payment processor integration would be required in Phase 2.

Next Steps:

  1. Order ESP32 development kits

  2. Set up basic server with Node.js

  3. Build simple Android/iOS app using provided code

  4. Test in controlled environment

  5. Gather user feedback and iterate

Would you like detailed code for any specific component or have questions about particular implementation aspects?contact us:This email address is being protected from spambots. You need JavaScript enabled to view it.

 


Login