I'm studying Core Bluetooth framework and I did a test project about it for learning:
class ViewController: UIViewController,CBCentralManagerDelegate,CBPeripheralDelegate {
var centralManager: CBCentralManager = CBCentralManager()
var peripheral: CBPeripheral? = nil
override func viewDidLoad()
{
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
central.scanForPeripherals(withServices: nil, options: nil)
}
#available(iOS 5.0, *)
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
{
let device = (advertisementData as NSDictionary)
.object(forKey: CBAdvertisementDataLocalNameKey)
as? NSString
let isMyIphone = device?.contains("iPhone")
}
}
For starting I would like to see Bluetooth name around me, for this reason I have 2 iPhone.
One I use for execute this 'app' for scanning and I would like to see the name of the other iPhone (in Bluetooth setting is name is 'iPhone'), but when I start scanning the method 'didDiscover' is called but the device constant is nil.
Why? What I wrong?
is better if you use
peripheral.name
instead
let device = (advertisementData as NSDictionary)
.object(forKey: CBAdvertisementDataLocalNameKey)
as? NSString
Related
When I pass CBUUID as array to scanForPeripherals withServices nothing happened. (Xcode 12.5, Swift 5)
Step 1. Getting all devices near me and getting identifiers:
//
// ViewController.swift
//
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
private var centralManager : CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices:nil, options: nil)
}
}
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let name = peripheral.name else { return }
print("\nName : \(name)")
print(peripheral.identifier)// Getting identifier here.(9SD480E0-4362-4412-6350-D143DC3D14BE)
}
}
Step 2. (WRONG) Calling with identifiers and nothing is being discovered:
//
// ViewController.swift
//
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
private var centralManager : CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices: [CBUUID.init(string: "9SD480E0-4362-4412-6350-D143DC3D14BE")], options: nil)
}
}
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// NOTHING IS BEING DISCOVERED
}
}
Step 3. The correct path to follow is Step 1 (for getting id's) and Step 3 (to retrieve peripherals). Thank you #Paulw11:
//
// ViewController.swift
//
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
private var centralManager : CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
guard let uuid = UUID(uuidString: "9SD480E0-4362-4412-6350-D143DC3D14BE") else { return }
let result = centralManager.retrievePeripherals(withIdentifiers: [uuid])
print(result) // CBPeripheral
}
}
}
I just tried a sample code to find the list of all bluetooth devices that are connected or discoverable.
class ViewController: UIViewController, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
NSLog("Update state \(central.state)")
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices: nil, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
NSLog("Peripheral \(peripheral)")
}
}
The problem is it simply does not show all bluetooth devices that I can see in the Bluetooth menu in Settings app. Moreover, I can't even see device that is already connected to iPhone. What I am doing wrong?
I would ideally like to see all headphone & microphone accessories, not others. Not sure which serviceUUID values I can use and if there is any standard for those values.
I am having the problem with Xcode 9.2 and iOS 11.1.2,"didDiscover" is working fine and my peripheral is saved in an array before I call connect, but "didConnect" or "didFailToConnect" not called, so the peripheral state will stay at "connecting"...
Please help
var manager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
//manager = CBCentralManager (delegate: self, queue: DispatchQueue.main)
//manager = CBCentralManager.init(delegate: self, queue: nil, options:[:])
//manager.delegate = self
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let name = peripheral.name else { return }
print("BR peripheral.name = \(name), rssi = \(RSSI), adertisementData = \(advertisementData)")
if (peripheral.name?.hasPrefix("testBT"))! {
peripheralArray.append(peripheral)
manager.connect(peripheralArray.first!, options: [:])
print("Connecting to peripheral \(peripheral)")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("BT connected!")
manager.stopScan()
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("BT disconnected!")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("BT failed!")
}
Try making variable global:
var myPeripheral:CBPeripheral?
Than, in your code edit it:
if (peripheral.name?.hasPrefix("testBT"))! {
myPeripheral = peripheral
//HERE WRITE DELEGATE, NOT IN didConnect
myPeripheral!.delegate = self
//peripheralArray.append(myPeripheral)
manager.connect(myPeripheral!, options: nil)
print("Connecting to peripheral \(peripheral)")
}
Now I think it should work. I would also start scanning on centralManagerDidUpdateState to make sure bluetooth is working, smth like:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state{
case .poweredOn:
startScan() // own method where i start scanning
case .poweredOff:
print("blth powered off")
case .unsupported:
print("bltyh noit supported")
}
}
etc. there is more states, you can check it in documentation. The centralManagerDidUpdateState method is beeing called after initializing in vievDidLoad your CBCentralManager var (manager = CBCentralManager(delegate: self, queue: nil))
Hope it helps
BTW, you are making an array to store there connected peripherals. Just have in mind that whenever you return from the controller where you store that array and go back to it that array will be empty again. That's why if you need your bluetooth class working globally I would suggest you to make a BluetoothService class that would have a Singleton implemented and you could access any state anywhere in your project
I'm using CoreBluetooth framework in my iOS application to scan and connect to other bluetooth devices. I'm able to scan successfully for devices using below code:
var centralManager: CBCentralManager?
var peripherals = [CBPeripheral]()
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
In the CBCentralManagerDelegate, I implemented below code:
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if (central.state == .poweredOn){
self.centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if !peripherals.contains(peripheral) {
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
print("\(String(describing: localName))" )
peripherals.append(peripheral)
tableView.reloadData()
}
}
}
From above code localName is always nil. Am I missing any code?
To add more info,
When I am scanning for bluetooth devices in my Mac, I'm able to see the names of the devices. But when I am scanning for bluetooth devices on my iPhone, none of them are listed.
I cross checked and made sure that I enabled blue tooth on 4 more devices with me (iPad, 2 Android Phones and 1 Android TV)
Swift with core bluetooth library:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
let peripheralLocalName_advertisement = ((advertisementData as NSDictionary).value(forKey: "kCBAdvDataLocalName")) as? String
if (((advertisementData as NSDictionary).value(forKey: "kCBAdvDataLocalName")) != nil)
print(peripheralLocalName_advertisement)//peripheral name from advertismentData
print(peripheral.name)//peripheral name from peripheralData
peripherals.append(peripheral)
arrayPeripheral.append(advertisementData)
}
}
I changed in your code only source of name - instead of:
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
I use:
let localName = peripheral.name
and now I see name of BLE devices around me.
I changed in your code only source of name - instead of:
let localName = advertisementData[CBAdvertisementDataLocalNameKey]
I use:
let localName = peripheral.name
and now I see name of BLE devices around me.
The didDiscoverPeripheral callback unfortunately can return nil for CBPeripheral names and also the advertisementData's local name for the very first callback your app gets after you start a scan.
If you know that the peripheral that you're working with will contain a local name in the Bluetooth advertisement packet, you can choose to ignore the first callback that contains a nil name.
// In didDiscoverPeripheral
guard let peripheralName = peripheral.name else { return }
print("Did discover peripheral: \(peripheralName), RSSI: \(RSSI)")
I'm currently developing with CoreBluetooth and I've managed to get the RSSI-strength. I can print it in the console, but when I want to use it in an IBAction, it wont work. I guess it's because the RSSI var isn't a local variable.
Here is the method where I get the RSSI-strength from:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let device = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? String
let rssi = RSSI // The const that stores the RSSI-data
print(rssi)
if device?.contains(BLE_NAME) == true {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connect(peripheral, options: nil)
}
}
And here is the IBAction that I want the rssi const to be in:
#IBAction func btnPressed(_ sender: Any) {
rssiLbl.text = "\(rssi)"
}
However, I don't know how to make this work. I bet that this is a really basic question for the most of you, but I just can't make it work.
As others have said. Declare rssi as a class-level variable:
private var rssi: NSNumber?
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let device = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? String
self.rssi = RSSI
print(self.rssi)
if device?.contains(BLE_NAME) == true {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connect(peripheral, options: nil)
}
}
#IBAction func btnPressed(_ sender: Any) {
if let rssi = self.rssi {
rssiLbl.text = "\(rssi)"
} else {
rssiLbl.text = "Undetermined"
}