How can I see the BLE services of a peripheral device? - ios

I would to make app which will allow me to download some data from a peripheral device. I can connect with the peripheral device, but I can’t download services which are supported by this device. I don’t have second app which works as a peripheral. The second device is an iPad which has a virtual peripheral made in LightBlue.app. Sometimes it’s called Blank and sometimes it’s called iPad, I don’t know why.
This is my code:
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate{
#IBOutlet var coreBluetooth: UILabel!
#IBOutlet var discoveredDevices: UILabel!
#IBOutlet var foundBLE: UILabel!
#IBOutlet var connected: UILabel!
var centralManager:CBCentralManager!
var blueToothReady = false
var connectingPeripheral:CBPeripheral!
override func viewDidLoad() {
super.viewDidLoad()
startUpCentralManager()
}
func startUpCentralManager() {
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func discoverDevices() {
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: (NSDictionary), RSSI: NSNumber!) {
discoveredDevices.text = "Discovered \(peripheral.name)"
println("Discovered: \(peripheral.name)")
centralManager.stopScan()
if peripheral.name == "iPad" || peripheral.name == "Blank"
{
println("ok")
centralManager.connectPeripheral(peripheral, options: nil)
self.connectingPeripheral = peripheral
}
}
func centralManagerDidUpdateState(central: CBCentralManager!) { //BLE status
switch (central.state) {
case .PoweredOff:
coreBluetooth.text = "CoreBluetooth BLE hardware is powered off"
case .PoweredOn:
coreBluetooth.text = "CoreBluetooth BLE hardware is powered on and ready"
blueToothReady = true;
case .Resetting:
coreBluetooth.text = "CoreBluetooth BLE hardware is resetting"
case .Unauthorized:
coreBluetooth.text = "CoreBluetooth BLE state is unauthorized"
case .Unknown:
coreBluetooth.text = "CoreBluetooth BLE state is unknown"
case .Unsupported:
coreBluetooth.text = "CoreBluetooth BLE hardware is unsupported on this platform"
}
if blueToothReady {
discoverDevices()
}
}
func centralManager(central: CBCentralManager!,didConnectPeripheral peripheral: CBPeripheral!)
{
connectingPeripheral.discoverServices(nil)
println("Connected")
foundBLE.textColor = UIColor.redColor()
foundBLE.text = "Connected to: \(peripheral.name)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func scanBLE(sender: UIButton) {
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
func connectingPeripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!)
{
println("Services \(connectingPeripheral.services)")
}
}

You need to set the peripheral's delegate to self in func centralManager(central: CBCentralManager!,didConnectPeripheral peripheral: CBPeripheral!) in order to get the call back when the services are discovered -
func centralManager(central: CBCentralManager!,didConnectPeripheral peripheral: CBPeripheral!)
{
connectingPeripheral.delegate=self
connectingPeripheral.discoverServices(nil)
println("Connected")
foundBLE.textColor = UIColor.redColor()
foundBLE.text = "Connected to: \(peripheral.name)"
}
You will then get a call back to func connectingPeripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!)

swift 3
var peripheralDevice:CBPeripheral!
//if connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connected")
self.peripheralDevice.discoverServices(nil)
}
//if disconnect
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Disconnect")
}
//fail to connect
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Fail to connect, Please try again.")
}

Related

App not printing text after connecting to bluetooth device (Swift)

I'm currently trying to make an app that connects to a bluetooth device and once done prints out the
I'm having the problem once it connects it doesn't print out the services and instead prints out "while delegate is either nil or does not implement peripheral:didDiscoverServices:". But the problem is that I do have didDiscoverServices.
Here is my viewcontroller
import UIKit
import CoreBluetooth
let rowerServiceCBUUID = CBUUID(string: "CE060000-43E5-11E4-916C-0800200C9A66")
class HRMViewController: UIViewController {
#IBOutlet weak var heartRateLabel: UILabel!
#IBOutlet weak var bodySensorLocationLabel: UILabel!
var centralManager: CBCentralManager!
var pmPeripheral: CBPeripheral!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
// Make the digits monospaces to avoid shifting when the numbers change
heartRateLabel.font = UIFont.monospacedDigitSystemFont(ofSize: heartRateLabel.font!.pointSize, weight: .regular)
}
func onHeartRateReceived(_ heartRate: Int) {
heartRateLabel.text = String(heartRate)
print("BPM: \(heartRate)")
}
}
extension HRMViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("central.state is .unknown")
case .resetting:
print("central.state is .resetting")
case .unsupported:
print("central.state is .unsupported")
case .unauthorized:
print("central.state is .unauthorized")
case .poweredOff:
print("central.state is .poweredOff")
case .poweredOn:
print("central.state is .poweredOn")
centralManager.scanForPeripherals(withServices: [rowerServiceCBUUID])
#unknown default: break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
pmPeripheral = peripheral
centralManager.stopScan()
centralManager.connect(pmPeripheral!)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected!")
pmPeripheral.discoverServices(nil)
}
}
extension HRMViewController: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print(service)
pmPeripheral.delegate = self
}
}
}
Does anyone know what I should do? Thanks.
Although you set your central manager's delegate, you have not set the peripheral's delegate. In centralManager(_,didDiscover,advertisementData), add:
pmPeripheral.delegate = self
That should resolve your warning and the delegate method will be called.

swift: pair with bluetooth device with button click

Until now the iPhone has paired to a bluetooth device when the app is opened, but I want to pair with a button click instead. I tried to put all the scanning and connection functions in a IBAction, but nothing happens when I push the button. In the first function after the IBAction I have a print output to check if it even enters the function, but I don't get the print. I also tried to put a delay after the manager.scanForPeripherals but didn't work either. What am I missing here? anyone know?
Thanks for any replies! here is my code (without the delay):
var manager: CBCentralManager!
var device: CBPeripheral?
var characteristics: [CBCharacteristic]?
var serviceUUID = "1234"
var char1 = "FFE1"
let deviceName = "HMSoft"
var connected = CBPeripheralState.connected
var disconnected = CBPeripheralState.disconnected
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager(delegate: self, queue: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn: break
// manager.scanForPeripherals(withServices: nil, options: nil)
case .unknown: break
case .resetting: break
case .unsupported: break
case .unauthorized: break
case .poweredOff:
break
}
}
#IBAction func connectBluetooth(_ sender: Any) {
manager.scanForPeripherals(withServices: nil, options: nil)
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == self.deviceName {
// save a reference to the sensor tag
self.device = peripheral
self.device!.delegate = self
// Request a connection to the peripheral
self.manager.connect(self.device!, options: nil)
print("Check")
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
return
}
if let services = peripheral.services {
for service in services {
if (service.uuid == CBUUID(string: serviceUUID)) {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
device = peripheral
characteristics = service.characteristics
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if error != nil {
return
}
}
}
When you initialise the CBCentralManager and set its delegate, the delegate will start receiving events.
So, if you only want the CBCentralManager to be active once the user has tapped a button, rather than as soon as the screen loads, the minimum you have to do is move the code that initialises the manager from viewDidLoad to the button action.
So:
Move manager = CBCentralManager(delegate: self, queue: nil) from viewDidLoad to #IBAction func connectBluetooth(_ sender: Any),
Remove the call to scanForPeripherals from the IBAction, and;
Uncomment // manager.scanForPeripherals(withServices: nil, options: nil) in centralManagerDidUpdateState.
Don't put the function func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) inside the button.
Try instead this:
#IBAction func connectBluetooth(_ sender: Any) {
manager = CBCentralManager(delegate: self, queue: nil)
manager.scanForPeripherals(withServices: nil, options: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == self.deviceName {
// save a reference to the sensor tag
self.device = peripheral
self.device!.delegate = self
// Request a connection to the peripheral
self.manager.connect(self.device!, options: nil)
print("Check")
}
}
}
It's quite important that the device know that BLE is on before you can scan.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
case .unsupported:
print("BLE is unsupported")
case .unauthorized:
print("BLE is unauthorized")
case .unknown:
print("BLE is unknown")
case .resetting:
print("BLE is reseting")
case .poweredOff:
print("BLE is powered off")
case .poweredOn:
print("BLE is powered on")
//must be called
}
}

can't search and display BLE device name with swift3

I'm coding BLE test apps for iOS with Swift 3 and I'm struggling with a problem that I can't scan devices.
I want to call the method "print("devicename: \(peripheral)")" at the last block.
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate {
private var isScanning = false
var centralManager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
self.centralManager = CBCentralManager(delegate: self,queue: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
centralManager.scanForPeripherals(withServices: nil,options: nil)
} else {
print("not ready")
}
}
private func centralManager(central: CBCentralManager!,didDiscoverPeripheral peripheral: CBPeripheral!,advertisementData: [String : AnyObject]!,RSSI: NSNumber!)
{
print("devicename: \(peripheral)")
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager){
switch central.state {
case .poweredOn: central.scanForPeripherals(withServices: nil, options: nil)
default: print("Please turn on bluetooth")
break
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
print("----->",advertisementData)
print("==>", peripheral)
}

Can't connect to AirPods via BLE

I am trying to connect automatically to my AirPods with a simple app using BLE. I get the name of the device and the status to "connecting", but for some reason I can't connect to it. Function 'didConnect peripheral' is never triggered.
I tried all different approaches from tutorials and from other posts, tried to store the peripheral data in an array to keep the reference but nothing seem to work.
Is there any step which I can get some extra info between 'didDiscover' and 'didConnect'?
Working in XCode 9.2, using Swift 4 and iOS 11.2 on iPhone.
Here's my code:
let deviceName = "AirPods de Roger"
var isConnected = false
var manager: CBCentralManager!
var peripheralBLE: CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch manager.state {
case.poweredOff:
print("BLE service is powered off")
case.poweredOn:
print("BLE service is powered on and scanning")
manager.scanForPeripherals(withServices: nil, options: nil)
default:
print("BLE service in another state")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if peripheral.name == deviceName && isConnected == false {
self.manager.stopScan()
self.peripheralBLE = peripheral
self.peripheralBLE?.delegate = self
manager.connect(peripheral, options: nil)
isConnected = true
print("\(peripheral.name) pre-connected")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
lblConnected.isHidden = false
print("AirPods Connected")
peripheral.discoverServices(nil)
}
This one is my current implementation:
import UIKit
import CoreBluetooth
var manager: CBCentralManager!
var peripheralBLE: CBPeripheral!
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
let deviceName = "AirPods de Iván"
var isConnected = false
#IBOutlet weak var Label: UILabel!
#IBAction func Click(_ sender: UIButton) {
self.connect()
}
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func connect() {
manager.connect(peripheralBLE, options: nil)
print("connect")
self.updateLabelStatus()
}
func disconnect() {
manager.cancelPeripheralConnection(peripheralBLE!)
print("disconnect")
self.updateLabelStatus()
}
func updateLabelStatus() {
switch peripheralBLE.state {
case.connected:
Label.text = "connected"
case.disconnected:
Label.text = "disconnected"
case.connecting:
Label.text = "connecting"
case.disconnecting:
Label.text = "disconnecting"
default:
Label.text = "label"
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch manager.state {
case.poweredOff:
print("BLE service is powered off")
case.poweredOn:
print("BLE service is powered on and scanning")
manager.scanForPeripherals(withServices: nil, options: nil)
default:
print("BLE service in another state")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if peripheral.name == deviceName && isConnected == false {
print("found AirPods \(peripheral)")
peripheralBLE = peripheral
peripheralBLE!.delegate = self
manager.stopScan()
self.updateLabelStatus()
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("AirPods Connected")
peripheral.discoverServices(nil)
self.updateLabelStatus()
}
func centralManager(_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?) {
print("AirPods Connect error")
print(error)
self.updateLabelStatus()
}
}
I am finding the device but when I try to connect, nothing happens
BLE service is powered on and scanning
found AirPods <CBPeripheral: 0x1c4103f00, identifier = 0E6FCF72-B86E-FB10-DD62-4A575BAD0ECC, name = AirPods de Iván, state = disconnected>
connect
I can connect other devices with this code
I can connect the airpods with my mac without problems with a similar code
I can't connect airpods with my iphone :S
Something strange happens here I am almost sure that the code is right
Since the AirPods advertise with kCBAdvDataIsConnectable = 0, they're not supposed to be connected via BLE.

Receive string data via Bluetooth

I'm creating a simple BLE app that communicates with a single peripheral. The phone acts as the central. I have an iPad which I'm using as the peripheral for testing. It has the app LightBlue installed to simulate a peripheral. The peripheral is supposed to send a string of data in this format.
TEM:25.11 | HUM:70 | PM10:43 | PM25:32
So I created a blank virtual peripheral in LightBlue with one service.
Below is my code for Bluetooth connectivity handling.
import UIKit
import CoreBluetooth
class ViewController: UIViewController {
fileprivate let serviceUUID = CBUUID(string: "19B10010-E8F2-537E-4F6C-D104768A1214")
fileprivate let characteristicUUID = CBUUID(string: "19B10011-E8F2-537E-4F6C-D104768A1214")
fileprivate var manager: CBCentralManager!
fileprivate var peripheral: CBPeripheral!
fileprivate var characteristic: CBCharacteristic!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopScan()
}
fileprivate func startScan() {
manager.scanForPeripherals(withServices: [serviceUUID], options: nil)
}
fileprivate func stopScan() {
manager.stopScan()
}
fileprivate func disconnectFromDevice() {
guard let peripheral = peripheral else { return }
manager.cancelPeripheralConnection(peripheral)
}
fileprivate func restoreCentralManager() {
manager.delegate = self
}
}
// MARK: - CBCentralManagerDelegate
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unsupported:
print("Unsupported")
case .unauthorized:
print("Unauthorized")
case .poweredOn:
print("Powered On")
startScan()
case .resetting:
print("Resetting")
case .poweredOff:
print("Powered Off")
case .unknown:
print("Unknown")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Discovered \(String(describing: peripheral.name)) at \(RSSI)")
if peripheral.name == nil || peripheral.name == "" {
return
}
if self.peripheral == nil || self.peripheral.state == .disconnected {
stopScan()
self.peripheral = peripheral
central.connect(peripheral, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
self.peripheral = nil
central.scanForPeripherals(withServices: nil, options: nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
self.peripheral = nil
}
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
}
}
// MARK: - CBPeripheralDelegate
extension ViewController: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
print("No. of services: \(services.count)")
for service in services {
print(service.uuid)
if service.uuid == serviceUUID {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
print("characteristic: \(characteristic.uuid)")
if characteristic.uuid == characteristicUUID {
self.characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
peripheral.readValue(for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print(error)
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == characteristicUUID {
print("Got reply from: \(characteristic.uuid)")
print(characteristic.value)
if let data = characteristic.value, let string = String(data: data, encoding: String.Encoding.utf8) {
print(string)
} else {
print("No response!")
}
}
}
}
The discovering and connecting part works just fine. The problem is I don't receive that data string from the peripheral.
The method peripheral(_:didUpdateValueFor:error:) does get fired. I get the Got reply from: 19B10011-E8F2-537E-4F6C-D104768A1214 output in the console. However when I tried to see if there's any data by printing out the characteristic.value, it returns nil.
Not sure if it's something wrong with my code. Or I've configured the peripheral on LightBlue wrong. Does LightBlue send out data automatically? I don't see any Send button or anything anywhere either.
I uploaded a demo project here as well.
Your LightBlue configuration showing that you can only write value on that particular characteristic You have to make this read also

Resources