Bluetooth LE on iOS10 strange behavior - ios

I found out that when an iPhone is updating a local characteristic value, and also listen to notification for that characteristic, he will get notified even the fact that he was the one that updated the value, so the delegate :
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
Will be called, even that I was the one who change the value, and not the remote server (ble device). When the remote side send data, I will also get this delegate. Is it the way it should be ? I don't remember so .
I found out the same behavior on other 3rd apps that scan for Bluetooth LE.
I also found out that for some reason my code is not always get the delegate , maybe I am doing things wrong here:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let foundName=peripheral.name
let deviceName = "Name"
if foundName?.range(of: deviceName) != nil
{
self.centralManager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
self.centralManager.connect(peripheral, options: nil)
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: "Detected")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices( nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services!
{
let thisService = service as CBService
print(thisService.uuid.uuidString)
if thisService.uuid.uuidString == serviceUUID {
peripheral.discoverCharacteristics(nil, for: thisService)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for charateristic in service.characteristics!
{
let thisCharacteristic = charateristic as CBCharacteristic
// check for data characteristic
if thisCharacteristic.uuid.uuidString == characteristicUUID {
print("REGISTERED CHARACTERISTIC:",thisCharacteristic)
self.peripheral.setNotifyValue(true, for: thisCharacteristic)
self.characteristic=thisCharacteristic
isConnected=true
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: "Connected")
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid.uuidString == characteristicUUID {
if let str = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
{
print("BLE:DATAIN:",str )
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: str)
}
}
}
func sendData(data:String)
{
if(peripheral != nil)
{
print("BLE:SENT")
var bytesData = [UInt8](data.utf8)
let writeData = NSData (bytes: &bytesData, length: bytesData.count)
peripheral.writeValue(writeData as Data, for: characteristic, type: CBCharacteristicWriteType.withoutResponse)
}
}

After a day investigation, both the hardware chip and the iOS, I realized that the notify update- will notify you when there is a new value.
So, whats a new value ?
Its when the previous value is different from the current value.
For some hardware configurations, the previous value is kept on cache even after reset, and iOS will see it as a new value, even though the hardware was not updated it at all (!)
So when you register to notifications, iOS will check the initial value , and if it holds something other then zero/nil , a delegate will be called.
Its your job to clean the previous cached value in the chip in some way.
In general, I found out (maybe too late) that the best practice is to connect and stay connected as long as the app is running. This will eliminate all sorts of problems that happens when you connect/disconnect every time you need to send data.
Conclusion: connect once, when disconnected for any reason -reset your hardware automatically (using the host controller software)

Related

CC2650 Module cannot Connect via Bluetooth with iOS app

I try to develop an iOS app using Swift. This app will be controlled Texas Instrument's CC2650 module via Bluetooth. I'm using iOS Core Bluetooth library. I took Bluetooth connection codes from
https://github.com/hoiberg/HM10-BluetoothSerial-iOS
import UIKit
import CoreBluetooth
var serial: BluetoothSerial!
final class BluetoothSerial: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var delegate: BluetoothSerialDelegate!
var centralManager: CBCentralManager!
}
var writeType: CBCharacteristicWriteType = .withResponse
init(delegate: BluetoothSerialDelegate) {
super.init()
self.delegate = delegate
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func startScan() {
guard centralManager.state == .poweredOn else { return }
let uuid = CBUUID(string: "0x180A")
centralManager.scanForPeripherals(withServices: [uuid], options: nil)
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid])
for peripheral in peripherals {
let StrenghtOfRSSI = peripheral.readRSSI()
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: nil)
}
}
func stopScan() {
centralManager.stopScan()
}
func connectToPeripheral(_ peripheral: CBPeripheral) {
pendingPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
func sendMessageToDevice(_ message: String) {
guard isReady else { return }
if let data = message.data(using: String.Encoding.init(rawValue: UInt(message)!)) {
connectedPeripheral?.writeValue(data, for: writeCharacteristic!, type: .withResponse)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: RSSI)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
pendingPeripheral = nil
connectedPeripheral = peripheral
delegate.serialDidConnect(peripheral)
peripheral.discoverServices([CBUUID(string: "F0001110-0451-4000-B000-000000000000")])
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidDisconnect(peripheral, error: error as NSError?)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
pendingPeripheral = nil
delegate.serialDidFailToConnect(peripheral, error: error as NSError?)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidChangeState()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
if bluetoothCounter == false {
if characteristic.uuid == CBUUID(string: "F0001112-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic)
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
} else {
if characteristic.uuid == CBUUID(string: "F0001113-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
let data = characteristic.value
guard data != nil else { return }
delegate.serialDidReceiveData(data!)
if let str = String(data: data!, encoding: String.Encoding.utf8) {
delegate.serialDidReceiveString(str)
} else {
print("Received an invalid string!")
}
}
}
But I have a problem with it. When my app scan for the peripheral, it can not find CC2650 module directly. However, there is something interesting situation happening. When I open BLE Scanner app (https://itunes.apple.com/us/app/ble-scanner-4-0/id1221763603?mt=8) I can discover my CC2650 module and send messages. After that, I open my app I can also discover my CC2650 and can send messages. My app cannot find CC2650 module directly.
I cannot solve this problem. I try everything which I found about Bluetooth connection.
I need some help with it.
Thank you guys.
I would first check to see the status of the BLE adapter using something like:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleMsg = ""
switch(central.state) {
case .poweredOff:
consoleMsg = "BLE is off"
case .poweredOn:
consoleMsg = "BLE is on"
self.centralManager.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
consoleMsg = "BLE is resetting"
case .unauthorized:
consoleMsg = "BLE is UA"
case .unknown:
consoleMsg = "BLE status unknown"
case .unsupported:
consoleMsg = "BLE is unsupported"
}
self.delegate?.statusUpdated(statusText: consoleMsg)
print("\(consoleMsg) \n", terminator: "")
}
This code will replace your startScan method, if it prints "BLE is on" you are okay and it will start scanning for peripherals.
From your code it looks like you aren't actually connecting to a peripheral either.
For example, you call centralManager.connect(peripheral, options: nil) only within connectToPeripheral and I don't see a call to that from within the serialDidDiscoverPeripheral section.
You need to scan, then register for the didDiscoverPeripheral delegate method, check to see if that peripheral is the one you want (via a UUID or device name, you can get this from the BLE explorer app you are using), then connect to it, then scan for services then interact with them (read, write, notify etc).
You can add:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = peripheral.name {
print(peripheralName)
if (peripheralName == "Your peripheral name"){
self.peripheral = peripheral
self.peripheral.delegate = self // This is to subscribe to delegate methods from the peripheral
self.centralManager.stopScan() // Found your peripheral so stop scanning
self.centralManager.connect(self.peripheral, options: nil)
}
}
}
That will connect (and print out to the console) to a peripheral. Then you need to scan for services (you can start this within the didConnect delegate method:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
Then you appear to have the code for discovering services and characteristics so that should all work ok.
Side note:
You call
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid]) which will only retrieve already connected peripherals, so if you connect to it via another app (e.g. the BLE explorer) that is why it appears in your app.

BLE Peripheral delegate not called

I recently upgraded from Swift 3 to Swift 4 and iOS 10.3.3 to iOS 11.1.
I'm developing an application that uses BLE to communicate bi-directionally. The workflow is as follows:
PERIPHERAL - Advertise Identity
CENTRAL - Receive Identity (process it...)
CENTRAL - Respond to peripheral
PERIPHERAL - Receive response from central
Done
My code was working perfectly before the update but now it's not. At the end of step 4, I execute the following line:
peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse)
This should call the following delegate method but it doesn't:
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
print("Did Write")
print("Error=\(error?.localizedDescription)")
}
It should also (and was calling) the following delegate method on the PERIPHERAL device but it doesn't:
public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
print("did receive write request")
}
The service and characteristic are set as follows :
let prefs = Preferences()
let strServiceUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_SERVICE_UUID, defaultVal: "")!
let strCharacteristicUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_CHARACTERISTIC_UUID, defaultVal: "")!
print("ServiceUUID=\(strServiceUUID)")
print("CharacteristicUUID=\(strCharacteristicUUID)")
mServiceUUID = CBUUID(string: strServiceUUID)
mCharacterUUID = CBUUID(string: strCharacteristicUUID)
mCBBluetoothServices = CBMutableService(type: mServiceUUID, primary: true)
//lets configure the data we want to advertise for
var characteristics : [CBCharacteristic] = []
//let strData : String = "933911"
//let data = strData.data(using: .utf8)
let cbProperties: CBCharacteristicProperties = [.read, .write, .notify]
let cbPermissions: CBAttributePermissions = [.readable, .writeable]
mIdentityObjectCharacteristic = CBMutableCharacteristic(type: mCharacterUUID,
properties: cbProperties,
value: nil,
permissions: cbPermissions)
characteristics.append(mIdentityObjectCharacteristic)
mCBBluetoothServices.characteristics = characteristics
mCBPeripheralManager.add(mCBBluetoothServices)
I am not sure why upgrading the OS and Swift versions broke your code, however, it looks to me like you may be using the wrong delegate method?
Try using this
func peripheral(CBPeripheral, didWriteValueFor: CBCharacteristic, error: Error?)
instead of this
func peripheral(CBPeripheral, didWriteValueFor: CBDescriptor, error: Error?)
Swift 4
For any kind of update characteristic (ex. read/write characteristic), then the didUpdateValueFor delegate will be called.
So, first check in the following delegate methods.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateValueForChar", characteristic)
if let error1 = error{
alertMSG(titleString: "Error", subTitleString: "Found error while read characteristic data, Plase try again", buttonTitle: "OK")
print(error1)
}
else{
print("Update Characteristic: ", characteristic)
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Write Characteristic :", characteristic)
}
Swift 5
iOS 13
Some things to check:
Make sure to set the peripheral's delegate to whichever controller is conforming to the CBPeripheralDelegate protocol (this should also be the same controller that needs to implement the peripheral(_:didWriteValueFor:error:) method).
Make sure you are not specifying a .withoutResponse write type.
As mentioned by this other answer, there are two very similar delegate methods that have the signature peripheral(_:didWriteValueFor:error:). Make sure you are implementing the correct one.
When writing to a characteristic:
writeValue(_:for:type:):
func writeValue(_ data: Data,
for characteristic: CBCharacteristic,
type: CBCharacteristicWriteType)
peripheral(_:didWriteValueFor:error:)
func peripheral(_ peripheral: CBPeripheral,
didWriteValueFor characteristic: CBCharacteristic,
error: Error?)
When writing to a descriptor:
writeValue(_:for:)
func writeValue(_ data: Data,
for descriptor: CBDescriptor)
peripheral(_:didWriteValueFor:error:)
func peripheral(_ peripheral: CBPeripheral,
didWriteValueFor descriptor: CBDescriptor,
error: Error?)
It is easy to confuse the 2 sets of write and delegate methods.
Since you are using:
peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse)
The code for the write and delegate pair should be something like this:
class BluetoothController: CBCentralManagerDelegate, CBPeripheralDelegate {
...
func writeSomething(to characteristic: CBCharacteristic, of peripheral: CBPeripheral) {
let something = "1234"
NSLog("Writing \(something) to \(characteristic.uuid.uuidString)")
peripheral.delegate = self // <===== You may have forgotten this?
peripheral.writeValue(something.data(using: .utf8)!,
for: characteristic,
type: .withResponse)
}
...
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
NSLog("Write error: \(String(describing: error))")
} else {
NSLog("Wrote value to \(characteristic.uuid.uuidString)")
}
}
}

Swift BLE "didDiscoverServices" not executing. Am I missing something?

I've been reading the tutorial to use the nRF8001 from Adafruit and connecting it to an iOS device via Arduino. So far I've set up everything correctly and it works fine in their app.
I'm trying to write my own app that (for now) does the exact same thing, I read their tutorial and from what I understand I copied the code as close as I could, so far I can get the connection and most things seem to be working (UI-wise), however I can't seem to do anything past connecting to the device:
Here's my code for AFTER connecting:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
//What to do when it discovers a peripheral, add it to the array list
print("Peripheral found: " + (peripheral.name ?? "Unknown Name"))
peripheralsFoundNames.append((peripheral.name ?? "Unknown Name"))
peripheralsFoundData.append((advertisementData.description ))
peripheralsFoundCB.append(peripheral)
peripheralsFoundRSSIs.append(RSSI)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to device!")
displayStatusAlert(localmsg: "Connection Succesful!")
NotificationCenter.default.post(name: Notification.Name(rawValue: DEVICE_READY_KEY), object: self)
data?.length = 0 //clear any data that might be stored
peripheral.discoverServices([BLETemperatureService])
print("Here at didConnect, connected to:" + peripheral.name!)
// Here needs to add code to check if it's a single or multi-channel device via the advertisement data or some other constant, maybe the name?
}
As you can see, I am calling explicitly peripheral.discoverServices, and then i have a print statement that executes. Then I have the following (NOTE NONE OF THE BELOW LINES SEEM TO EXECUTE AT ANY TIME (AT LEAST NOT THE PRINT STATEMENTS):
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("here at diddisoverservices")
if ((error) != nil){
displayStatusAlert(localmsg: "Error: \n" + (error?.localizedDescription ?? "Error Unknown" ))
}
guard let services = peripheral.services
else{
return
}
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
print ("Discovered!")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if ((error) != nil){
displayStatusAlert(localmsg: "Error: \n" + (error?.localizedDescription ?? "Error Unknown" ))
}
guard let characteristics = service.characteristics
else{
return
}
for characteristic in characteristics {
//looks for the right characteristic
print("looking for characteristic")
if characteristic.uuid.isEqual(BLERXCharacteristic) {
deviceConnectedRXChar = characteristic
//Once found, subscribe to the this particular characteristic
peripheral.setNotifyValue(true, for: deviceConnectedRXChar!)
peripheral.readValue(for: characteristic)
print("Rx Characteristic: \(characteristic.uuid)")
}
if characteristic.uuid.isEqual(BLETXCharacteristic){
deviceConnectedTXChar = characteristic
print("Tx Characteristic: \(characteristic.uuid)")
}
peripheral.discoverDescriptors(for: characteristic)
}
print ("Characteristic discovered")
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == deviceConnectedRXChar {
if let ASCIIstring = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) {
receivedDataString = ASCIIstring
print("Value Recieved: \((receivedDataString as String))")
NotificationCenter.default.post(name: Notification.Name(rawValue: DEVICE_SENT_DATA), object: nil)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) is a CBPeripheralDelegate method.
So what you were missing is setting the CBPeripheral object delegate.
So just before doing peripheral.discoverServices([BLETemperatureService]), you need to do peripheral.delegate = self.

Issues reading a characteristic from a paired BLE device

Using CoreBluetooth on iOS(10.3) i'm unable to read a characteristics value when bonded(paired?) to a custom BLE HID Device. If I remain unbonded I can read the characteristic fine.
The BLE device implements HID over GATT(HOGP) and an additional service with a single notify characteristic. This additional service/characteristic is what I'm trying to read, not the HID over GATT data which I know is filtered out.
It appears that didDiscoverServices never gets called. I do see didConnect being called.
I'm able to accomplish this on Android so I don't think it's an issue with the BLE device.
Relevant code below:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var showAlert = true
var message = ""
switch central.state {
case .poweredOff:
message = NSLocalizedString("Bluetooth on this device is currently powered off.", comment: "")
case .unsupported:
message = NSLocalizedString("This device does not support Bluetooth Low Energy.", comment: "")
case .unauthorized:
message = NSLocalizedString("This app is not authorized to use Bluetooth Low Energy.", comment: "")
case .resetting:
message = NSLocalizedString("The BLE Manager is resetting; a state update is pending.", comment: "")
case .unknown:
message = NSLocalizedString("The state of the BLE Manager is unknown.", comment: "")
case .poweredOn:
showAlert = false
message = NSLocalizedString("Bluetooth LE is turned on and ready for communication.", comment: "")
let lastPeripherals = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: Device.DeviceServiceUUID)])
print("count: \(lastPeripherals.count)")
if lastPeripherals.count > 0{
let device = lastPeripherals.last!;
connectingPeripheral = device;
centralManager.connect(connectingPeripheral, options: nil)
} else {
centralManager.scanForPeripherals(withServices: [CBUUID(string: "7340")], options: nil)
}
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == deviceName {
// save a reference to the device
device= peripheral
device!.delegate = self
// Request a connection to the peripheral
centralManager.connect(device!, options: nil)
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if error != nil {
print("****** DISCONNECTION DETAILS: \(error!.localizedDescription)")
}
device = nil
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
print("ERROR DISCOVERING SERVICES: \(error?.localizedDescription)")
return
}
if let services = peripheral.services {
for service in services {
print("DISCOVERED SERVICE: \(service)")
if (service.uuid == CBUUID(string: Device.DeviceServiceUUID)) {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if error != nil {
print("ERROR DISCOVERING CHARACTERISTICS: \(error?.localizedDescription)")
return
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
// Message Data Characteristic
if characteristic.uuid == CBUUID(string: Device.MessageCharacteristicUUID) {
// Enable the message notifications
messageCharacteristic = characteristic
device?.setNotifyValue(true, for: characteristic)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
print("ERROR ON UPDATING VALUE FOR CHARACTERISTIC: \(characteristic) - \(error?.localizedDescription)")
return
}
// extract the data from the characteristic's value property and display the value based on the characteristic type
if let dataBytes = characteristic.value {
if characteristic.uuid == CBUUID(string: Device.MessageCharacteristicUUID) {
displayMessage(dataBytes)
}
}
}

not getting data / services from BLE device in ios

I need to connect with a BLE device and then handle data as per sent via different button in it.
For that I wrote following code.
import CoreBluetooth
class HomeViewController: UIViewController,CBPeripheralDelegate,CBCentralManagerDelegate
{
var centralManager : CBCentralManager!
var peri : CBPeripheral!
override func viewDidLoad()
{
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == .Unknown
{
print("Unknown")
}
else if central.state == .Unsupported
{
print("Unsupported")
}
else if central.state == .Unauthorized
{
print("Unauthorized")
}
else if central.state == .Resetting
{
print("Resetting")
}
else if central.state == .PoweredOn
{
print("Powered On")
startScan()
}
else if central.state == .PoweredOff
{
print("Powered Off")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered: \(peripheral.name) at \(RSSI)")
print("AdvertisementData:\(advertisementData)")
if peri != peripheral
{
peri = peripheral
centralManager.connectPeripheral(peripheral, options: nil)
}
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Failed to connect \(peripheral) cause of \(error)")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
// centralManager.stopScan()
print("Available services:\(peripheral.services)")
}
func peripheral(peripheral: CBPeripheral, didDiscoverIncludedServicesForService service: CBService, error: NSError?) {
print("Services\(service) and error\(error)")
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Services and error\(error)")
}
func startScan(){
print("Scanning...")
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
}
And here is my Log for this code.
Powered On
Scanning...
Discovered: Optional("**** BLE") at 127
AdvertisementData:["kCBAdvDataIsConnectable": 1, "kCBAdvDataServiceUUIDs": (
1802
)]
connected to <CBPeripheral: 0x12756d910, identifier = 6197****-EB0A-F1E8-BEF4-1AFAC629C5BC, name = **** BLE, state = connected>
Available services:nil
This is output is generated when one button is clicked from BLE device. But I am unable to receive or read data when another button is clicked.
Android developer of same app has integrated with both button.
So there is no any problem in device.
Can anyone help me to guide where I'm going wrong in this code??
Pandafox's answer is perfect just one thing is missing from it.
Which is setting delegate of peripheral.
Here is the complete code to discover peripheral, connect to it and discover its services and characteristics.
1.Connect peripheral
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered: \(peripheral.name) at \(RSSI)")
print("AdvertisementData:\(advertisementData)")
if peri != peripheral
{
peri = peripheral
peri.delegate = self
centralManager.connectPeripheral(peri, options: nil)
}
}
Connection failure or success
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Failed to connect \(peripheral) cause of \(error)")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
// centralManager.stopScan()
peripheral.discoverServices(nil)
}
3.DiscoverServices
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Services:\(peripheral.services) and error\(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
Discover Characteristics and set notification
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?)
{
print("peripheral:\(peripheral) and service:\(service)")
for characteristic in service.characteristics!
{
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
Handle notification for update value of characteristics
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?)
{
print("characteristic changed:\(characteristic)")
}
You also have to discover the services and characteristics after connecting to the device.
For example, in your "didConnectPeripheral" method, you will have to do something like:
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
peripheral.delegate = self
peripheral.discoverServices(nil)
print("Discovering services!")
}
And then:
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Discovered services: \(peripheral.services), Error\(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
And then you have to handle each characteristic:
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError)
You must remember to store each characteristic manually, as they will be deallocated if you don't.
In order to receive streaming data (notifications) you will have to enable notify for each characteristic.
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
You also have to implement:
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError)
Order to handle the incoming values.
As you can see, there's quite a bit of boiler plate code required to get started.
After connecting to the peripheral you have to call discoverServices on the peripheral with the UUID of the services you want to discover, you then have to discover the characteristics of the service. If you want updates when a button is clicked, you will have to turn notifications on for the characteristic corresponding to that button
I would highly recommend this link from apple for follow up reading if you still need help. It describes what you need to do step by step in a better fashion than I could ever describe here.
Apple BLE Guide

Resources