Skip to content

Commit

Permalink
fixed Bluetooth LE not advertising after disconnect
Browse files Browse the repository at this point in the history
also added Beacon support
  • Loading branch information
colemancda committed Apr 30, 2016
1 parent e58f7ac commit 94503eb
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 15 deletions.
6 changes: 3 additions & 3 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "PureSwift/JSON-C" "e8e38025563d43e425536200002ad13c3f35a98b"
github "PureSwift/SwiftFoundation" "aca70ddca59f69455775295ed8e9399d062de8de"
github "PureSwift/Bluetooth" "5f4aa3c090ff7f7e6b05241d582ef1071199a80c"
github "PureSwift/BluetoothLinux" "38e3d54686d29e831a8e9ddb261d55457fb1ea2a"
github "PureSwift/SwiftFoundation" "2490130e2a72183f5fe209df82b0fc012f41f532"
github "PureSwift/Bluetooth" "39e77da8f45b4a781e790257f7f8c5db76986431"
github "PureSwift/BluetoothLinux" "88eb1f0d646a460efd143c88f4f9adba50416ad4"
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ let package = Package(
Target(
name: "GATT")
],
exclude: ["Xcode", "Sources/PeripheralUnitTestsClient", "Sources/GATT/DarwinCentral.swift"]
exclude: ["Xcode", "Sources/PeripheralUnitTestsClient", "Sources/GATT/DarwinCentral.swift", "Sources/GATT/DarwinPeripheral.swift"]
)
38 changes: 38 additions & 0 deletions Sources/GATT/Beacon.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Beacon.swift
// GATT
//
// Created by Alsey Coleman Miller on 4/29/16.
// Copyright © 2016 PureSwift. All rights reserved.
//

import SwiftFoundation

/// Describes an iBeacon to be advertised.
public struct Beacon {

/// The unique ID of the beacons being targeted.
public var UUID: SwiftFoundation.UUID

/// The value identifying a group of beacons.
public var major: UInt16

/// The value identifying a specific beacon within a group.
public var minor: UInt16

/// The received signal strength indicator (RSSI) value (measured in decibels) for the device.
public var RSSI: Int8

#if os(Linux) || XcodeLinux
/// The advertising interval.
public var interval: UInt16 = 200
#endif

public init(UUID: SwiftFoundation.UUID, major: UInt16, minor: UInt16, RSSI: Int8) {

self.UUID = UUID
self.major = major
self.minor = minor
self.RSSI = RSSI
}
}
46 changes: 45 additions & 1 deletion Sources/GATT/DarwinPeripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Bluetooth

import Foundation
import CoreBluetooth
import CoreLocation

/// The platform specific peripheral.
public typealias PeripheralManager = DarwinPeripheral
Expand Down Expand Up @@ -84,15 +85,58 @@ import Bluetooth
log?("Now powered on")
}

#if os(OSX)

public func start() throws {

let advertisementData = [CBAdvertisementDataLocalNameKey: localName]

try start(advertisementData)
}

#endif

#if XcodeLinux

public func start(beacon: Beacon? = nil) throws {

fatalError("Not supported on OS X")
}

#endif

#if os(iOS)

public func start(beacon: Beacon? = nil) throws {

var advertisementData = [String: AnyObject]()

if let beacon = beacon {

let beaconRegion = CLBeaconRegion(proximityUUID: beacon.UUID.toFoundation(), major: beacon.major, minor: beacon.minor, identifier: beacon.UUID.rawValue)

let mutableDictionary = beaconRegion.peripheralData(withMeasuredPower: NSNumber(value: beacon.RSSI))

advertisementData = NSDictionary.init(dictionary: mutableDictionary) as! [String: AnyObject]
}

advertisementData[CBAdvertisementDataLocalNameKey] = localName

try start(advertisementData)
}

#endif

@inline(__always)
private func start(_ advertisementData: [String: AnyObject]) throws {

assert(startAdvertisingState == nil, "Already started advertising")

let semaphore = dispatch_semaphore_create(0)

startAdvertisingState = (semaphore, nil) // set semaphore

internalManager.startAdvertising([CBAdvertisementDataLocalNameKey: localName])
internalManager.startAdvertising(advertisementData)

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

Expand Down
28 changes: 26 additions & 2 deletions Sources/GATT/LinuxPeripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,19 @@

// MARK: - Methods

public func start() throws {
public func start(beacon: Beacon? = nil) throws {

guard isServerRunning == false else { return }

if let beacon = beacon {

try adapter.enableBeacon(UUID: beacon.UUID, major: beacon.major, minor: beacon.minor, RSSI: beacon.RSSI, interval: beacon.interval)

} else {

try adapter.enableAdvertising()
}

let adapterAddress = try Address(deviceIdentifier: adapter.identifier)

let serverSocket = try L2CAPSocket(adapterAddress: adapterAddress, channelIdentifier: ATT.CID, addressType: .LowEnergyPublic, securityLevel: .Low)
Expand Down Expand Up @@ -116,7 +125,22 @@
}
}

catch { peripheral.log?("Error: \(error)"); return }
catch {

/// Turn on LE advertising after disconnect (Linux turns if off for some reason)
if let disconnectError = error as? POSIXError where disconnectError.rawValue == 104 {

peripheral.log?("Central \(newSocket.address) disconnected")

do { try peripheral.adapter.enableAdvertising() }

catch { peripheral.log?("Could not restore advertising. \(error)") }

return
}

peripheral.log?("Error: \(error)"); return
}
}
})
}
Expand Down
24 changes: 18 additions & 6 deletions Sources/GATT/Peripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ public protocol NativePeripheral {

associatedtype ServiceIdentifier

#if os(iOS) || os(Linux) || XcodeLinux

/// Start advertising the peripheral and listening for incoming connections.
///
/// - Note: Can optionally advertise as iBeacon in iOS and Linux.
func start(beacon: Beacon?) throws

#elseif os(OSX)

/// Start advertising the peripheral and listening for incoming connections.
func start() throws

#endif

/// Stop the peripheral.
func stop()

/// The closure to call for internal logging.
var log: (String -> ())? { get }

/// Attempts to add the specified service to the GATT database.
Expand All @@ -29,12 +47,6 @@ public protocol NativePeripheral {
/// Clears the local GATT database.
func clear()

/// Start advertising the peripheral and listening for incoming connections.
func start() throws

/// Stop the peripheral.
func stop()

var willRead: ((central: Central, UUID: Bluetooth.UUID, value: Data, offset: Int) -> ATT.Error?)? { get set }

var willWrite: ((central: Central, UUID: Bluetooth.UUID, value: Data, newValue: Data) -> ATT.Error?)? { get set }
Expand Down
2 changes: 1 addition & 1 deletion Sources/PeripheralUnitTestsServer/ServerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let peripheral: PeripheralManager = {
peripheral.waitForPoweredOn()
#endif

for service in TestData.services {
for service in TestProfile.services {

try! peripheral.add(service: service)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/PeripheralUnitTestsServer/ServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class ServerTests: XCTestCase {

peripheral

for service in TestData.services {
for service in TestProfile.services {

for characteristic in service.characteristics {

Expand Down
6 changes: 6 additions & 0 deletions Xcode/GATT.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
6E17DC211CB1DC150043C644 /* DarwinCentral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E17DC201CB1DC150043C644 /* DarwinCentral.swift */; };
6E17DC231CB1DC250043C644 /* Central.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E17DC221CB1DC250043C644 /* Central.swift */; };
6E1C311C1CD4723C00E8F7EB /* Beacon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1C311B1CD4723C00E8F7EB /* Beacon.swift */; };
6E2A5D621CC8436800BA7E15 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2A5D611CC8436800BA7E15 /* AppDelegate.swift */; };
6E2A5D641CC8436800BA7E15 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2A5D631CC8436800BA7E15 /* ViewController.swift */; };
6E2A5D671CC8436800BA7E15 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6E2A5D651CC8436800BA7E15 /* Main.storyboard */; };
Expand All @@ -34,6 +35,7 @@
6E7BD6401CB0FBA500F0ABF3 /* SwiftFoundation.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6E7BD63B1CB0FB9A00F0ABF3 /* SwiftFoundation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6E7BD64B1CB0FD9500F0ABF3 /* GATTTest.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E7BD64A1CB0FD9500F0ABF3 /* GATTTest.h */; settings = {ATTRIBUTES = (Public, ); }; };
6E7BD6521CB0FE1B00F0ABF3 /* TestProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E7BD6511CB0FE1B00F0ABF3 /* TestProfile.swift */; };
6E809EDA1CD476E100B06F45 /* Beacon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1C311B1CD4723C00E8F7EB /* Beacon.swift */; };
6E96454C1CB10330002E7C87 /* ClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E7BD6201CB0F9B200F0ABF3 /* ClientTests.swift */; };
6E96454D1CB10333002E7C87 /* CentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E7BD61F1CB0F9B200F0ABF3 /* CentralManager.swift */; };
6EAB9AD61CBF9354002635A9 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EAB9AD51CBF9354002635A9 /* main.swift */; };
Expand Down Expand Up @@ -140,6 +142,7 @@
/* Begin PBXFileReference section */
6E17DC201CB1DC150043C644 /* DarwinCentral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinCentral.swift; sourceTree = "<group>"; };
6E17DC221CB1DC250043C644 /* Central.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Central.swift; sourceTree = "<group>"; };
6E1C311B1CD4723C00E8F7EB /* Beacon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Beacon.swift; sourceTree = "<group>"; };
6E2A5D5F1CC8436800BA7E15 /* TestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
6E2A5D611CC8436800BA7E15 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6E2A5D631CC8436800BA7E15 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -373,6 +376,7 @@
6E7BD6181CB04DB800F0ABF3 /* LinuxPeripheral.swift */,
6E17DC221CB1DC250043C644 /* Central.swift */,
6E17DC201CB1DC150043C644 /* DarwinCentral.swift */,
6E1C311B1CD4723C00E8F7EB /* Beacon.swift */,
);
path = GATT;
sourceTree = "<group>";
Expand Down Expand Up @@ -689,6 +693,7 @@
6EE84DA41CAF424E00A40C4D /* DarwinPeripheral.swift in Sources */,
6E17DC211CB1DC150043C644 /* DarwinCentral.swift in Sources */,
6EE84DA21CAF422100A40C4D /* Peripheral.swift in Sources */,
6E1C311C1CD4723C00E8F7EB /* Beacon.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -703,6 +708,7 @@
6EF45FC51CC6D355001F7A39 /* DarwinPeripheral.swift in Sources */,
6EF45FC61CC6D355001F7A39 /* DarwinCentral.swift in Sources */,
6EF45FC71CC6D355001F7A39 /* Peripheral.swift in Sources */,
6E809EDA1CD476E100B06F45 /* Beacon.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

0 comments on commit 94503eb

Please sign in to comment.