iBeacon SDK in Java: Complete Bluetooth Beacon Integration Guide

iBeacon is Apple's Bluetooth Low Energy (BLE) technology that enables devices to broadcast and detect proximity information. This guide demonstrates how to build a Java-based iBeacon SDK for detecting, monitoring, and ranging iBeacons on various platforms.

Why Build an iBeacon SDK in Java?

  • Cross-Platform Support: Run on Windows, Linux, macOS with Bluetooth adapters
  • Proximity Marketing: Enable location-based notifications and content
  • Indoor Navigation: Implement indoor positioning systems
  • Asset Tracking: Monitor equipment and inventory location
  • Attendance Systems: Track presence in specific areas

Prerequisites

  • Bluetooth 4.0+ Adapter with BLE support
  • Java 8+ with Bluetooth libraries
  • Maven/Gradle for dependency management

Step 1: Project Dependencies

Maven (pom.xml):

<dependencies>
<!-- Bluetooth LE Libraries -->
<dependency>
<groupId>com.github.hypfvieh</groupId>
<artifactId>bluez-dbus</artifactId>
<version>0.1.8</version>
</dependency>
<!-- TinyB Bluetooth Library (Linux) -->
<dependency>
<groupId>tinyb</groupId>
<artifactId>tinyb</artifactId>
<version>0.5.1</version>
</dependency>
<!-- BlueCove (Windows/macOS) -->
<dependency>
<groupId>net.sf.bluecove</groupId>
<artifactId>bluecove</artifactId>
<version>2.1.1</version>
</dependency>
<!-- BleGATT (Cross-platform) -->
<dependency>
<groupId>io.github.weltonfelix</groupId>
<artifactId>bluetooth-manager</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Event Bus for Beacon Events -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Spring Boot (Optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>

Step 2: Core Configuration

@Configuration
@ConfigurationProperties(prefix = "ibeacon")
@Data
public class IBeaconConfig {
private boolean enabled = true;
private long scanDuration = 10000; // ms
private long scanInterval = 5000;  // ms
private int rssiThreshold = -85;   // dBm
private double immediateRange = 0.5; // meters
private double nearRange = 3.0;      // meters
private double farRange = 15.0;      // meters
private String beaconFilter; // UUID to filter specific beacons
private boolean backgroundScanning = false;
private int maxBeaconCacheSize = 1000;
private long beaconTimeout = 30000; // ms
// Platform-specific configurations
private LinuxConfig linux = new LinuxConfig();
private WindowsConfig windows = new WindowsConfig();
private MacConfig mac = new MacConfig();
@Data
public static class LinuxConfig {
private String bluezInterface = "org.bluez";
private String adapterPath = "/org/bluez/hci0";
}
@Data
public static class WindowsConfig {
private boolean useNativeDriver = true;
private int comPort = 1;
}
@Data 
public static class MacConfig {
private boolean useCoreBluetooth = true;
}
}
@Component
@Slf4j
public class PlatformDetector {
public enum Platform {
WINDOWS, LINUX, MACOS, UNSUPPORTED
}
public Platform getCurrentPlatform() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
return Platform.WINDOWS;
} else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
return Platform.LINUX;
} else if (osName.contains("mac")) {
return Platform.MACOS;
} else {
return Platform.UNSUPPORTED;
}
}
public boolean isBluetoothSupported() {
try {
// Check if Bluetooth hardware is available
return checkBluetoothAvailability();
} catch (Exception e) {
log.warn("Bluetooth not supported on this platform", e);
return false;
}
}
private boolean checkBluetoothAvailability() {
// Platform-specific Bluetooth availability check
Platform platform = getCurrentPlatform();
switch (platform) {
case LINUX:
return checkLinuxBluetooth();
case WINDOWS:
return checkWindowsBluetooth();
case MACOS:
return checkMacBluetooth();
default:
return false;
}
}
private native boolean checkLinuxBluetooth();
private native boolean checkWindowsBluetooth(); 
private native boolean checkMacBluetooth();
}

Step 3: Core iBeacon Models

@Data
@AllArgsConstructor
@NoArgsConstructor
public class IBeacon {
// iBeacon advertisement structure
private String uuid;           // 16-byte UUID
private int major;            // 2-byte major number
private int minor;            // 2-byte minor number
private int txPower;          // Calibrated RSSI at 1 meter
private int rssi;             // Received signal strength
private String macAddress;    // Bluetooth MAC address
private String name;          // Beacon name if available
private Proximity proximity;  // Calculated proximity
private double distance;      // Estimated distance in meters
private long lastSeen;        // Last detection timestamp
private int packetCount;      // Number of packets received
private int manufacturer;     // Manufacturer ID (Apple = 76)
// Helper methods
public String getIdentifier() {
return String.format("%s:%d:%d", uuid, major, minor);
}
public boolean isValid() {
return uuid != null && !uuid.isEmpty() && manufacturer == 76; // Apple manufacturer ID
}
public void updateRssi(int newRssi) {
this.rssi = newRssi;
this.distance = calculateDistance();
this.proximity = calculateProximity();
this.lastSeen = System.currentTimeMillis();
this.packetCount++;
}
private double calculateDistance() {
if (rssi == 0 || txPower == 0) {
return -1.0; // Unknown distance
}
// Log-distance path loss model
double ratio = (txPower - rssi) / 20.0;
return Math.pow(10, ratio);
}
private Proximity calculateProximity() {
if (distance < 0) {
return Proximity.UNKNOWN;
} else if (distance <= 0.5) {
return Proximity.IMMEDIATE;
} else if (distance <= 3.0) {
return Proximity.NEAR;
} else if (distance <= 15.0) {
return Proximity.FAR;
} else {
return Proximity.FAR;
}
}
}
public enum Proximity {
UNKNOWN,    // Distance cannot be determined
IMMEDIATE,  // Within 0.5 meters
NEAR,       // Within 3 meters  
FAR         // Greater than 3 meters
}
@Data
public class BeaconRegion {
private String identifier;
private String uuid;
private Integer major;
private Integer minor;
private boolean notifyOnEntry = true;
private boolean notifyOnExit = true;
private boolean notifyOnDisplay = true;
public BeaconRegion(String identifier, String uuid) {
this.identifier = identifier;
this.uuid = uuid;
}
public BeaconRegion(String identifier, String uuid, Integer major, Integer minor) {
this.identifier = identifier;
this.uuid = uuid;
this.major = major;
this.minor = minor;
}
public boolean matches(IBeacon beacon) {
if (!uuid.equalsIgnoreCase(beacon.getUuid())) {
return false;
}
if (major != null && major != beacon.getMajor()) {
return false;
}
if (minor != null && minor != beacon.getMinor()) {
return false;
}
return true;
}
}
@Data
@AllArgsConstructor
public class BeaconEvent {
public enum EventType {
BEACON_DISCOVERED,
BEACON_UPDATED, 
BEACON_LOST,
REGION_ENTERED,
REGION_EXITED,
PROXIMITY_CHANGED
}
private EventType type;
private IBeacon beacon;
private BeaconRegion region;
private long timestamp;
private Map<String, Object> additionalData;
public BeaconEvent(EventType type, IBeacon beacon) {
this.type = type;
this.beacon = beacon;
this.timestamp = System.currentTimeMillis();
this.additionalData = new HashMap<>();
}
}

Step 4: Core Scanner Service

public interface BeaconScanner {
void startScanning();
void stopScanning();
boolean isScanning();
void addBeaconListener(BeaconListener listener);
void removeBeaconListener(BeaconListener listener);
List<IBeacon> getDiscoveredBeacons();
void setScanRegion(BeaconRegion region);
}
public interface BeaconListener {
void onBeaconDiscovered(IBeacon beacon);
void onBeaconUpdated(IBeacon beacon);
void onBeaconLost(IBeacon beacon);
void onRegionEntered(BeaconRegion region);
void onRegionExited(BeaconRegion region);
void onScanStarted();
void onScanStopped();
void onError(Exception error);
}
@Service
@Slf4j
public class IBeaconScanner implements BeaconScanner {
private final IBeaconConfig config;
private final PlatformDetector platformDetector;
private final List<BeaconListener> listeners = new CopyOnWriteArrayList<>();
private final Map<String, IBeacon> discoveredBeacons = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private volatile boolean isScanning = false;
private BeaconScannerStrategy scannerStrategy;
private ScheduledFuture<?> cleanupTask;
public IBeaconScanner(IBeaconConfig config, PlatformDetector platformDetector) {
this.config = config;
this.platformDetector = platformDetector;
initializeScannerStrategy();
}
@Override
public void startScanning() {
if (isScanning) {
log.warn("Scanner is already running");
return;
}
try {
if (scannerStrategy.startScanning()) {
isScanning = true;
startCleanupTask();
notifyScanStarted();
log.info("iBeacon scanning started");
}
} catch (Exception e) {
log.error("Failed to start beacon scanning", e);
notifyError(e);
}
}
@Override
public void stopScanning() {
if (!isScanning) {
return;
}
try {
scannerStrategy.stopScanning();
isScanning = false;
stopCleanupTask();
notifyScanStopped();
log.info("iBeacon scanning stopped");
} catch (Exception e) {
log.error("Error stopping beacon scanner", e);
notifyError(e);
}
}
@Override
public boolean isScanning() {
return isScanning;
}
@Override
public void addBeaconListener(BeaconListener listener) {
listeners.add(listener);
}
@Override
public void removeBeaconListener(BeaconListener listener) {
listeners.remove(listener);
}
@Override
public List<IBeacon> getDiscoveredBeacons() {
return new ArrayList<>(discoveredBeacons.values());
}
@Override
public void setScanRegion(BeaconRegion region) {
scannerStrategy.setScanRegion(region);
}
public void onBeaconDiscovered(IBeacon beacon) {
if (!beacon.isValid() || beacon.getRssi() < config.getRssiThreshold()) {
return;
}
String beaconKey = beacon.getIdentifier();
IBeacon existingBeacon = discoveredBeacons.get(beaconKey);
if (existingBeacon == null) {
// New beacon discovered
discoveredBeacons.put(beaconKey, beacon);
notifyBeaconDiscovered(beacon);
log.debug("New beacon discovered: {}", beacon.getIdentifier());
} else {
// Existing beacon updated
existingBeacon.updateRssi(beacon.getRssi());
notifyBeaconUpdated(existingBeacon);
}
}
public void cleanupExpiredBeacons() {
long currentTime = System.currentTimeMillis();
long timeout = config.getBeaconTimeout();
Iterator<Map.Entry<String, IBeacon>> iterator = discoveredBeacons.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, IBeacon> entry = iterator.next();
IBeacon beacon = entry.getValue();
if (currentTime - beacon.getLastSeen() > timeout) {
iterator.remove();
notifyBeaconLost(beacon);
log.debug("Beacon lost: {}", beacon.getIdentifier());
}
}
}
private void initializeScannerStrategy() {
PlatformDetector.Platform platform = platformDetector.getCurrentPlatform();
switch (platform) {
case LINUX:
scannerStrategy = new LinuxBeaconScanner(config);
break;
case WINDOWS:
scannerStrategy = new WindowsBeaconScanner(config);
break;
case MACOS:
scannerStrategy = new MacBeaconScanner(config);
break;
default:
scannerStrategy = new DummyBeaconScanner();
log.warn("Unsupported platform: {}", platform);
}
scannerStrategy.setBeaconCallback(this::onBeaconDiscovered);
}
private void startCleanupTask() {
cleanupTask = scheduler.scheduleAtFixedRate(
this::cleanupExpiredBeacons,
config.getBeaconTimeout() / 2,
config.getBeaconTimeout() / 2,
TimeUnit.MILLISECONDS
);
}
private void stopCleanupTask() {
if (cleanupTask != null) {
cleanupTask.cancel(false);
cleanupTask = null;
}
}
// Notification methods
private void notifyScanStarted() {
listeners.forEach(BeaconListener::onScanStarted);
}
private void notifyScanStopped() {
listeners.forEach(BeaconListener::onScanStopped);
}
private void notifyBeaconDiscovered(IBeacon beacon) {
listeners.forEach(listener -> listener.onBeaconDiscovered(beacon));
}
private void notifyBeaconUpdated(IBeacon beacon) {
listeners.forEach(listener -> listener.onBeaconUpdated(beacon));
}
private void notifyBeaconLost(IBeacon beacon) {
listeners.forEach(listener -> listener.onBeaconLost(beacon));
}
private void notifyError(Exception error) {
listeners.forEach(listener -> listener.onError(error));
}
@PreDestroy
public void cleanup() {
stopScanning();
scheduler.shutdown();
}
}

Step 5: Platform-Specific Scanner Implementations

// Scanner Strategy Interface
public interface BeaconScannerStrategy {
boolean startScanning();
boolean stopScanning();
void setScanRegion(BeaconRegion region);
void setBeaconCallback(Consumer<IBeacon> beaconCallback);
}
// Linux Implementation (using BlueZ)
@Slf4j
public class LinuxBeaconScanner implements BeaconScannerStrategy {
private final IBeaconConfig config;
private Consumer<IBeacon> beaconCallback;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private volatile boolean scanning = false;
public LinuxBeaconScanner(IBeaconConfig config) {
this.config = config;
}
@Override
public boolean startScanning() {
try {
bluetoothManager = BluetoothManager.getBluetoothManager();
bluetoothAdapter = bluetoothManager.getAdapter();
if (bluetoothAdapter == null) {
throw new RuntimeException("No Bluetooth adapter found");
}
if (!bluetoothAdapter.isPowered()) {
bluetoothAdapter.setPowered(true);
}
// Start discovery
bluetoothAdapter.startDiscovery();
scanning = true;
// Set up device discovery listener
setupDiscoveryListener();
return true;
} catch (Exception e) {
log.error("Failed to start Linux beacon scanner", e);
return false;
}
}
@Override
public boolean stopScanning() {
try {
if (bluetoothAdapter != null && scanning) {
bluetoothAdapter.stopDiscovery();
scanning = false;
}
return true;
} catch (Exception e) {
log.error("Error stopping Linux beacon scanner", e);
return false;
}
}
@Override
public void setScanRegion(BeaconRegion region) {
// Region filtering can be implemented here
}
@Override
public void setBeaconCallback(Consumer<IBeacon> beaconCallback) {
this.beaconCallback = beaconCallback;
}
private void setupDiscoveryListener() {
// Implementation for BlueZ device discovery and iBeacon parsing
// This would involve D-Bus communication with BlueZ
}
private IBeacon parseIBeaconData(byte[] manufacturerData, String macAddress, int rssi) {
// iBeacon format: 
// [0-1]: Company ID (0x004C for Apple)
// [2]: Data Type (0x02 for iBeacon)
// [3]: Data Length (0x15 = 21 bytes)
// [4-19]: UUID (16 bytes)
// [20-21]: Major (2 bytes)
// [22-23]: Minor (2 bytes)
// [24]: TX Power (1 byte, signed)
if (manufacturerData.length < 25) {
return null;
}
int companyId = ((manufacturerData[0] & 0xFF) << 8) | (manufacturerData[1] & 0xFF);
if (companyId != 0x004C) { // Apple company ID
return null;
}
if (manufacturerData[2] != 0x02 || manufacturerData[3] != 0x15) {
return null; // Not iBeacon format
}
// Parse UUID
byte[] uuidBytes = Arrays.copyOfRange(manufacturerData, 4, 20);
String uuid = bytesToUuid(uuidBytes);
// Parse Major
int major = ((manufacturerData[20] & 0xFF) << 8) | (manufacturerData[21] & 0xFF);
// Parse Minor  
int minor = ((manufacturerData[22] & 0xFF) << 8) | (manufacturerData[23] & 0xFF);
// Parse TX Power (signed byte)
int txPower = manufacturerData[24];
IBeacon beacon = new IBeacon();
beacon.setUuid(uuid);
beacon.setMajor(major);
beacon.setMinor(minor);
beacon.setTxPower(txPower);
beacon.setRssi(rssi);
beacon.setMacAddress(macAddress);
beacon.setManufacturer(companyId);
beacon.setLastSeen(System.currentTimeMillis());
return beacon;
}
private String bytesToUuid(byte[] bytes) {
StringBuilder uuid = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
uuid.append(String.format("%02x", bytes[i]));
if (i == 3 || i == 5 || i == 7 || i == 9) {
uuid.append("-");
}
}
return uuid.toString();
}
}
// Windows Implementation (using BlueCove)
@Slf4j
public class WindowsBeaconScanner implements BeaconScannerStrategy {
private final IBeaconConfig config;
private Consumer<IBeacon> beaconCallback;
private volatile boolean scanning = false;
private DiscoveryListener discoveryListener;
public WindowsBeaconScanner(IBeaconConfig config) {
this.config = config;
}
@Override
public boolean startScanning() {
try {
LocalDevice localDevice = LocalDevice.getLocalDevice();
DiscoveryAgent agent = localDevice.getDiscoveryAgent();
discoveryListener = new IBeaconDiscoveryListener();
// Start inquiry
agent.startInquiry(DiscoveryAgent.GIAC, discoveryListener);
scanning = true;
return true;
} catch (Exception e) {
log.error("Failed to start Windows beacon scanner", e);
return false;
}
}
@Override
public boolean stopScanning() {
try {
if (scanning) {
LocalDevice localDevice = LocalDevice.getLocalDevice();
DiscoveryAgent agent = localDevice.getDiscoveryAgent();
agent.cancelInquiry(discoveryListener);
scanning = false;
}
return true;
} catch (Exception e) {
log.error("Error stopping Windows beacon scanner", e);
return false;
}
}
@Override
public void setScanRegion(BeaconRegion region) {
// Region filtering implementation
}
@Override
public void setBeaconCallback(Consumer<IBeacon> beaconCallback) {
this.beaconCallback = beaconCallback;
}
private class IBeaconDiscoveryListener implements DiscoveryListener {
@Override
public void deviceDiscovered(RemoteDevice device, DeviceClass deviceClass) {
try {
// Check if device has iBeacon data
String name = device.getFriendlyName(false);
int rssi = 0; // RSSI not directly available in BlueCove
// Parse device services to find iBeacon data
// This is a simplified implementation
} catch (Exception e) {
log.debug("Error processing discovered device", e);
}
}
@Override
public void inquiryCompleted(int discType) {
if (scanning) {
// Restart inquiry for continuous scanning
try {
LocalDevice localDevice = LocalDevice.getLocalDevice();
DiscoveryAgent agent = localDevice.getDiscoveryAgent();
agent.startInquiry(DiscoveryAgent.GIAC, this);
} catch (Exception e) {
log.error("Failed to restart inquiry", e);
}
}
}
@Override
public void servicesDiscovered(int transID, ServiceRecord[] services) {
// Handle service discovery if needed
}
}
}
// Dummy implementation for unsupported platforms
public class DummyBeaconScanner implements BeaconScannerStrategy {
@Override
public boolean startScanning() {
return false;
}
@Override
public boolean stopScanning() {
return true;
}
@Override
public void setScanRegion(BeaconRegion region) {}
@Override
public void setBeaconCallback(Consumer<IBeacon> beaconCallback) {}
}

Step 6: Region Monitoring Service

@Service
@Slf4j
public class RegionMonitoringService implements BeaconListener {
private final Map<String, BeaconRegion> monitoredRegions = new ConcurrentHashMap<>();
private final Map<String, Set<String>> regionBeacons = new ConcurrentHashMap<>();
private final Map<String, Boolean> regionStates = new ConcurrentHashMap<>();
private final List<RegionListener> regionListeners = new CopyOnWriteArrayList<>();
private final IBeaconScanner beaconScanner;
public RegionMonitoringService(IBeaconScanner beaconScanner) {
this.beaconScanner = beaconScanner;
this.beaconScanner.addBeaconListener(this);
}
public void startMonitoringRegion(BeaconRegion region) {
monitoredRegions.put(region.getIdentifier(), region);
regionBeacons.put(region.getIdentifier(), new HashSet<>());
regionStates.put(region.getIdentifier(), false);
log.info("Started monitoring region: {}", region.getIdentifier());
}
public void stopMonitoringRegion(String regionIdentifier) {
monitoredRegions.remove(regionIdentifier);
regionBeacons.remove(regionIdentifier);
regionStates.remove(regionIdentifier);
log.info("Stopped monitoring region: {}", regionIdentifier);
}
public List<BeaconRegion> getMonitoredRegions() {
return new ArrayList<>(monitoredRegions.values());
}
public void addRegionListener(RegionListener listener) {
regionListeners.add(listener);
}
public void removeRegionListener(RegionListener listener) {
regionListeners.remove(listener);
}
@Override
public void onBeaconDiscovered(IBeacon beacon) {
checkRegionTransitions(beacon);
}
@Override
public void onBeaconUpdated(IBeacon beacon) {
checkRegionTransitions(beacon);
}
@Override
public void onBeaconLost(IBeacon beacon) {
checkRegionExits(beacon);
}
private void checkRegionTransitions(IBeacon beacon) {
for (BeaconRegion region : monitoredRegions.values()) {
if (region.matches(beacon)) {
String regionId = region.getIdentifier();
Set<String> beaconsInRegion = regionBeacons.get(regionId);
boolean wasEmpty = beaconsInRegion.isEmpty();
beaconsInRegion.add(beacon.getIdentifier());
if (wasEmpty && region.isNotifyOnEntry()) {
// Region entered
regionStates.put(regionId, true);
notifyRegionEntered(region, beacon);
}
}
}
}
private void checkRegionExits(IBeacon beacon) {
for (BeaconRegion region : monitoredRegions.values()) {
if (region.matches(beacon)) {
String regionId = region.getIdentifier();
Set<String> beaconsInRegion = regionBeacons.get(regionId);
beaconsInRegion.remove(beacon.getIdentifier());
if (beaconsInRegion.isEmpty() && regionStates.get(regionId) && region.isNotifyOnExit()) {
// Region exited
regionStates.put(regionId, false);
notifyRegionExited(region);
}
}
}
}
private void notifyRegionEntered(BeaconRegion region, IBeacon beacon) {
BeaconEvent event = new BeaconEvent(
BeaconEvent.EventType.REGION_ENTERED, beacon, region);
regionListeners.forEach(listener -> listener.onRegionEntered(event));
log.info("Region entered: {}", region.getIdentifier());
}
private void notifyRegionExited(BeaconRegion region) {
BeaconEvent event = new BeaconEvent(
BeaconEvent.EventType.REGION_EXITED, null, region);
regionListeners.forEach(listener -> listener.onRegionExited(event));
log.info("Region exited: {}", region.getIdentifier());
}
// Other interface methods with empty implementations
@Override public void onScanStarted() {}
@Override public void onScanStopped() {}
@Override public void onError(Exception error) {}
}
public interface RegionListener {
void onRegionEntered(BeaconEvent event);
void onRegionExited(BeaconEvent event);
}

Step 7: REST API Controllers

@RestController
@RequestMapping("/api/ibeacon")
@Slf4j
public class IBeaconController {
@Autowired
private IBeaconScanner beaconScanner;
@Autowired
private RegionMonitoringService regionService;
@PostMapping("/scan/start")
public ResponseEntity<?> startScanning() {
try {
beaconScanner.startScanning();
return ResponseEntity.ok("Beacon scanning started");
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("SCAN_START_ERROR", e.getMessage()));
}
}
@PostMapping("/scan/stop")
public ResponseEntity<?> stopScanning() {
try {
beaconScanner.stopScanning();
return ResponseEntity.ok("Beacon scanning stopped");
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("SCAN_STOP_ERROR", e.getMessage()));
}
}
@GetMapping("/beacons")
public ResponseEntity<?> getDiscoveredBeacons() {
try {
List<IBeacon> beacons = beaconScanner.getDiscoveredBeacons();
return ResponseEntity.ok(beacons);
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("BEACONS_ERROR", e.getMessage()));
}
}
@PostMapping("/regions/monitor")
public ResponseEntity<?> startMonitoringRegion(@Valid @RequestBody BeaconRegion region) {
try {
regionService.startMonitoringRegion(region);
return ResponseEntity.ok("Started monitoring region: " + region.getIdentifier());
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("REGION_MONITOR_ERROR", e.getMessage()));
}
}
@DeleteMapping("/regions/monitor/{regionId}")
public ResponseEntity<?> stopMonitoringRegion(@PathVariable String regionId) {
try {
regionService.stopMonitoringRegion(regionId);
return ResponseEntity.ok("Stopped monitoring region: " + regionId);
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("REGION_STOP_ERROR", e.getMessage()));
}
}
@GetMapping("/regions")
public ResponseEntity<?> getMonitoredRegions() {
try {
List<BeaconRegion> regions = regionService.getMonitoredRegions();
return ResponseEntity.ok(regions);
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("REGIONS_ERROR", e.getMessage()));
}
}
@GetMapping("/status")
public ResponseEntity<?> getScannerStatus() {
try {
Map<String, Object> status = new HashMap<>();
status.put("scanning", beaconScanner.isScanning());
status.put("beaconCount", beaconScanner.getDiscoveredBeacons().size());
status.put("regionCount", regionService.getMonitoredRegions().size());
status.put("platform", "Java iBeacon SDK");
return ResponseEntity.ok(status);
} catch (Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse("STATUS_ERROR", e.getMessage()));
}
}
}
@Data
@AllArgsConstructor
class ErrorResponse {
private String errorCode;
private String message;
private LocalDateTime timestamp;
public ErrorResponse(String errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
this.timestamp = LocalDateTime.now();
}
}

Step 8: Configuration File

application.yml:

ibeacon:
enabled: true
scan-duration: 10000
scan-interval: 5000
rssi-threshold: -85
immediate-range: 0.5
near-range: 3.0
far-range: 15.0
background-scanning: false
max-beacon-cache-size: 1000
beacon-timeout: 30000
linux:
bluez-interface: "org.bluez"
adapter-path: "/org/bluez/hci0"
windows:
use-native-driver: true
com-port: 1
mac:
use-core-bluetooth: true
logging:
level:
com.yourcompany.ibeacon: DEBUG

Step 9: Usage Examples

@Service
public class BeaconExampleService implements RegionListener {
@Autowired
private IBeaconScanner beaconScanner;
@Autowired
private RegionMonitoringService regionService;
@PostConstruct
public void initialize() {
// Add listeners
beaconScanner.addBeaconListener(new ConsoleBeaconListener());
regionService.addRegionListener(this);
// Define regions to monitor
BeaconRegion storeRegion = new BeaconRegion("store-entrance", 
"B9407F30-F5F8-466E-AFF9-25556B57FE6D", 100, 1);
regionService.startMonitoringRegion(storeRegion);
// Start scanning
beaconScanner.startScanning();
}
@Override
public void onRegionEntered(BeaconEvent event) {
System.out.println("Entered region: " + event.getRegion().getIdentifier());
// Trigger actions like sending notifications, logging, etc.
}
@Override
public void onRegionExited(BeaconEvent event) {
System.out.println("Exited region: " + event.getRegion().getIdentifier());
}
private static class ConsoleBeaconListener implements BeaconListener {
@Override
public void onBeaconDiscovered(IBeacon beacon) {
System.out.printf("Discovered: %s (RSSI: %d, Distance: %.2fm)%n",
beacon.getIdentifier(), beacon.getRssi(), beacon.getDistance());
}
@Override
public void onBeaconUpdated(IBeacon beacon) {
// Handle beacon updates
}
@Override
public void onBeaconLost(IBeacon beacon) {
System.out.println("Lost beacon: " + beacon.getIdentifier());
}
@Override public void onScanStarted() {}
@Override public void onScanStopped() {}
@Override public void onError(Exception error) {}
}
}

Key Features Implemented

  1. Cross-Platform Support: Linux, Windows, macOS implementations
  2. Beacon Discovery: Detect and parse iBeacon advertisements
  3. Distance Calculation: Estimate proximity using RSSI and TX power
  4. Region Monitoring: Define and monitor geographic regions
  5. Event System: Comprehensive event notifications
  6. REST API: Web interface for beacon management
  7. Background Scanning: Continuous monitoring capabilities
  8. Filtering: UUID, major, minor based filtering

Best Practices

  1. Error Handling: Comprehensive exception handling for Bluetooth operations
  2. Resource Management: Proper cleanup of Bluetooth resources
  3. Performance: Efficient scanning with configurable intervals
  4. Power Management: Optimize for battery-powered devices
  5. Security: Validate beacon data and prevent spoofing
  6. Logging: Detailed logging for debugging and monitoring
  7. Configuration: Externalized configuration for different environments

This comprehensive iBeacon SDK provides a robust foundation for building proximity-aware applications in Java, enabling beacon detection, region monitoring, and proximity-based automation across multiple platforms.

Leave a Reply

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


Macro Nepal Helper