I'm having trouble getting Core Bluetooth to discover peripherals on iOS 8. Same code works fine on iOS 7 device. Initially I thought it would be a permissions issue since I had been doing some iBeacon work and there are some changes in Core Location permissions on iOS 8. I couldn't find anything online that helped with that however. Here is a link to a sample project that works fine for me on iOS 7 but not on iOS 8:
https://github.com/elgreco84/PeripheralScanning
If I run this project on an iOS 7 device it will log advertisement data for a number of devices around me. On iOS 8 the only output I see is that the Central Manager state is "Powered On".
It isn't valid to start scanning for peripherals until you are in the 'powered on' state. Perhaps on your iOS7 device you are lucky with timing, but the code is still incorrect. Your centralManagerDidUpdateState: should be
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStateUnsupported:
{
NSLog(#"State: Unsupported");
} break;
case CBCentralManagerStateUnauthorized:
{
NSLog(#"State: Unauthorized");
} break;
case CBCentralManagerStatePoweredOff:
{
NSLog(#"State: Powered Off");
} break;
case CBCentralManagerStatePoweredOn:
{
NSLog(#"State: Powered On");
[self.manager scanForPeripheralsWithServices:nil options:nil];
} break;
case CBCentralManagerStateUnknown:
{
NSLog(#"State: Unknown");
} break;
default:
{
}
}
}
And remove the call to scanForPeripheralsWithServices from didFinishLaunchingWithOptions
I ran into the same issue while building a very basic BLE scanner app. The required method "centralManagerDidUpdateState" was added. But nothing worked.
I believe the problem is related to queue.
Put the CBCentralManager instance in a dispatch_get_main_queue
This code snippet does that:
// BLE Stuff
let myCentralManager = CBCentralManager()
// Put CentralManager in the main queue
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
myCentralManager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
}
Using the Default Single View xCode start app. You can put this into the ViewController.swift file:
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
// BLE Stuff
let myCentralManager = CBCentralManager()
var peripheralArray = [CBPeripheral]() // create now empty array.
// Put CentralManager in the main queue
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
myCentralManager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark CBCentralManager Methods
func centralManagerDidUpdateState(central: CBCentralManager!) {
updateStatusLabel("centralManagerDidUpdateState")
switch central.state{
case .PoweredOn:
updateStatusLabel("poweredOn")
case .PoweredOff:
updateStatusLabel("Central State PoweredOFF")
case .Resetting:
updateStatusLabel("Central State Resetting")
case .Unauthorized:
updateStatusLabel("Central State Unauthorized")
case .Unknown:
updateStatusLabel("Central State Unknown")
case .Unsupported:
println("Central State Unsupported")
default:
println("Central State None Of The Above")
}
}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println("Did Discover Peripheral")
}
}
Related
I have RFID reader, which is not LE device.
https://www.tsl.com/products/1153-bluetooth-wearable-uhf-rfid-reader
I'm trying to write an iOS application, scan this device and connect it using swift CoreBluetooth library but my App finds everything besides this device. How is it possible to scan this reader?
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
break;
case .poweredOff:
break;
case .poweredOn:
manager.scanForPeripherals(withServices: nil)
break;
case .resetting:
break;
case .unauthorized:
break;
case .unsupported:
break;
default:
break;
}
}
}
That device states that is MFi certified and uses the SPP profile, not the BLE GATT profile. This means that you will need to use the External Accessory Framework, not Core Bluetooth, to communicate with it.
You will need to the manufacturer provided iOS SDK for the device. If they do and you want to release your app on the App Store then they will also need to approve your app and supply some paperwork to Apple.
The device says that it also supports the HID profile, so perhaps you could just treat it as a keyboard; This doesn't require any code but isn't the best user experience.
You need to use CoreNFC to read RFID tags. No need to use CoreBluetooth at all.
https://developer.apple.com/documentation/corenfc
I have a problem with the Bluetooth in Xcode. I can’t find a great solution on how to check if Bluetooth is on or not. I want just that. I searched around the web some solution, but nothing works for me. Any idea on how to check Bluetooth? I imported the CoreBluetooth class and I made this line of code:
if CBPeripheralManager.authorizationStatus() == .denied { code }
if CBPeripheralManager.authorizationStatus() == .authorized { code }
Implement CBCentralManagerDelegate delegate for that.
var manager:CBCentralManager!
viewDidLoad() { // Or init()
manager = CBCentralManager()
manager.delegate = self
}
Delegate method :
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
break
case .poweredOff:
print("Bluetooth is Off.")
break
case .resetting:
break
case .unauthorized:
break
case .unsupported:
break
case .unknown:
break
default:
break
}
}
you will need to use CBCentralManager and it provide delegate method "centralManagerDidUpdateState" https://developer.apple.com/documentation/corebluetooth/cbcentralmanager
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
if central.state == .poweredOn
{
print("Searching for BLE Devices")
// Scan for peripherals if BLE is turned on
}
else
{
// Can have different conditions for all states if needed - print generic message for now, i.e. Bluetooth isn't On
print("Bluetooth switched off or not initialized")
}
}
I'm trying to send a process in a background thread using the following code:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
print("running in the background queue")
btDiscovery
})
but the class is only processing while begin in foreground...any idea ?
EDIT1:
btDiscovery is a class which performs a BLE device scan every X seconds:
let btDiscoverySharedInstance = Beacon();
class Beacon: NSObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager?
private var peripheralBLE: CBPeripheral?
....
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
print("BLE powered off")
self.clearDevices()
case CBCentralManagerState.Unauthorized:
// Indicate to user that the iOS device does not support BLE.
print("BLE not supported")
break
case CBCentralManagerState.Unknown:
// Wait for another event
print("BLE unknown event")
break
case CBCentralManagerState.PoweredOn:
print("BLE powered on")
self.startScanning()
break
case CBCentralManagerState.Resetting:
print("BLE reset")
self.clearDevices()
case CBCentralManagerState.Unsupported:
print("BLE unsupported event")
break
}
}
func startScanning() {
print("Start scanning...")
if let central = centralManager {
central.scanForPeripheralsWithServices(nil, options: nil)
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered peripheral \(RSSI) dBM name: \(peripheral.name)")
print("UUID: \(peripheral.identifier.UUIDString)")
...
sleep(delayPolling)
self.startScanning()
}
when the app is launched and remains in foreground, the scan is performed correctly every "delayPolling" seconds.
but as soon as I put my app is background, the scan is paused. it restarts only when it comes back again in foreground.
I would need to leave this scan running in background every time (even if we set a lower priority to this thread).
EDIT2:
by reading the documentation https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
I can see that
When an app that implements the central role includes the UIBackgroundModes key with the bluetooth-central value in its Info.plist file, the Core Bluetooth framework allows your app to run in the background to perform certain Bluetooth-related tasks. While your app is in the background you can still discover and connect to peripherals, and explore and interact with peripheral data. In addition, the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked
I selected the corresponding options in my Info.plist file:
but my app is not running my thread in background.
I realize this is an old question, but scanning in the background requires that you supply a Service UUID.
central.scanForPeripheralsWithServices(nil, options: nil)
needs to be
central.scanForPeripheralsWithServices(serviceUUID, options: nil)
I am quite new to iOS programming and also to bluetooth protocol.
I have found a sample code written in swift and trying to modify it to work with my own bluetooth module. The module I have is DBM01 from dorji.
The service I need to use is FFF0 and the characteristics is FFF1 for sending a ASCII value.
When I use the LightBlue app on my macbook and connect to the board I have designed, which has the DBM01 module on it, I can send the char value of "1" and I get the expected response (Turning on a LED) and when I send value of "0" it turns the LED off.
Now with the code I have, I can connect to the DBM01 module. I can print its name. However, I cannot disconnect from it with the following function. I am also not sure if this is for disconnecting from the device or it is called automatically when the device is disconnected. Regardless, it does not work either way.
func centralManager(central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: NSError?)
My main problem is I really didn't understand how I specify the service and the characteristics that I am interested in and connect to specific device that has them.
I am also unable to send a message. When I try I got the predefined error, since the following condition did't hold
if writeCharacteristic != nil
Below is my full code.
Appreciate if you can point out where I am doing wrong and how I can achieve connecting to specific device with specific service and characteristics information and sending data.
//
// ViewController.swift
// bleSwift
//
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral!
var writeCharacteristic: CBCharacteristic!
var service: CBService!
var characteristic: CBCharacteristic!
var bluetoothAvailable = false
let message = "1"
#IBOutlet weak var deviceName: UILabel!
#IBOutlet weak var ServiceName: UILabel!
#IBOutlet weak var CharacteristicsName: UILabel!
func centralManagerDidUpdateState(central: CBCentralManager)
{
print("Checking state")
switch (central.state)
{
case .PoweredOff:
print("CoreBluetooth BLE hardware is powered off")
case .PoweredOn:
print("CoreBluetooth BLE hardware is powered on and ready")
bluetoothAvailable = true;
case .Resetting:
print("CoreBluetooth BLE hardware is resetting")
case .Unauthorized:
print("CoreBluetooth BLE state is unauthorized")
case .Unknown:
print("CoreBluetooth BLE state is unknown");
case .Unsupported:
print("CoreBluetooth BLE hardware is unsupported on this platform");
}
if bluetoothAvailable == true
{
discoverDevices()
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
// Stop scanning
self.centralManager.stopScan()
print("Stopped Scanning")
// Set as the peripheral to use and establish connection
//self.peripheral = peripheral
//self.peripheral.delegate = self
//self.centralManager.connectPeripheral(peripheral, options: nil)
peripheral.discoverServices([CBUUID(string: "FFF0")])
print("CONNECTED!!")
print(peripheral.name)
deviceName.text = peripheral.name
}
func discoverDevices() {
print("Discovering devices")
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
#IBAction func disconnectDevice(sender: AnyObject) {
func centralManager(central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: NSError?)
{
print("CONNECTION WAS DISCONNECTED")
deviceName.text = "Disconnected"
}
}
#IBAction func Scan(sender: AnyObject)
{
print("Scan")
centralManager = CBCentralManager(delegate: self, queue: nil)
}
#IBAction func Send(sender: AnyObject)
{
let data = message.dataUsingEncoding(NSUTF8StringEncoding)
if writeCharacteristic != nil
{
print("Sent")
peripheral!.writeValue(data!, forCharacteristic: writeCharacteristic, type: CBCharacteristicWriteType.WithoutResponse)
}
else
{
print("Couldn't Send")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In order to send data to your ble peripheral, you should:
Start scanning.
In your ble central manager delegate you'll recieve didDiscoverPeripheral callback with discovered peripheral. Set yourself as its delegate and connect to it: centralManager.connectPeripheral(...)
Receive didConnectPeripheral in delegate. Now call discoverServices for this peripheral.
Recieve didDiscoverServices in your delegate. Finaly, call discoverCharacteristics for your service.
You'll recieve characteristic in didDiscoverCharacteristic delegate method. After that, you'll be able to send data to exact characteristic of your peripheral.
To disconnect peripheral, call method centralManager.cancelPeripheralConnection(...)
Now I'm currently doing an application project that needs my iPhone to scan other nearby bluetooth devices and list them out. I'm wondering is my code has any problem?
Code:
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager (delegate: self, queue: nil)
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Peripheral: \(peripheral)")
}
func centralManagerDidUpdateState(central: CBCentralManager) {
print("Checking")
switch(central.state)
{
case.Unsupported:
print("BLE is not supported")
case.Unauthorized:
print("BLE is unauthorized")
case.Unknown:
print("BLE is Unknown")
case.Resetting:
print("BLE is Resetting")
case.PoweredOff:
print("BLE service is powered off")
case.PoweredOn:
print("BLE service is powered on")
print("Start Scanning")
manager.scanForPeripheralsWithServices(nil, options: nil)
default:
print("default state")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm using iPhone 5 (iOS 9) and I'm sure that my Bluetooth is turned on.
When I run the application in my iPhone, the console only log the following output:
Checking
BLE service is powered on
Start Scanning
But there is no Bluetooth device's name shown in the output. Even I turn on my iPad (iPad Mini 4 iOS 8) and the list still wouldn't update.
Sometimes it does scan my MacBook Pro Bluetooth and the output will have this:
Peripheral: <CBPeripheral: 0x14d70e00, identifier = 54738076-6C97-FD04-18CF-5E1AF6705865, name = vivien’s MacBook Pro, state = disconnected>
So, why is this happening? Can someone please explain to me?
case 1:
You must use GKSession to scan and connect with another iOS device,not CoreBluetooth.
case 2:
Your bluetooth device is a Bluetooth 3.0 accessory.Your iPhone can discover and show it in Setting->Bluetooth.
But this message isn't delivered to your app,so your app won't discover it.
Try again with a Bluetooth 4.0 accessory.