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
Related
I have a device which I connect to using BLE. I start by checking if BLE is powered on, if so, I start scanning. Then after the device is discovered, I store the peripheral in a variable, then I try to connect to it. The connection succeeds. After that, I starting discovering the services and characteristics, and I manage to read them successfully. The problem is whenever I try to write data to the device I always get this error.
Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us.
Here is my code:
public func centralManagerDidUpdateState(_ central: CBCentralManager)
{
switch central.state {
case .unauthorized:
print("App does not support BLE")
case .poweredOff:
print("Bluetooth is turned off")
case .poweredOn:
print("Bluetooth is turned on")
print("Start scanning peripherals...")
central.scanForPeripherals(withServices: nil, options: nil)
default:
break
}
}
// To receive cell broadcast data
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let device = Device();
if(device.fromScanData(peripheral: peripheral, name: peripheral.name, rssi: RSSI.intValue, advertisementData: advertisementData))
{
if(device.Name.isEmpty || device.Name.count == 0){
return;
}
if(device.SN.isEmpty || device.SN.count != 8){
return;
}
if self.device.SN == device.SN
{
if !isDeviceConnected
{
print("Stop scanning")
print("Connecting...")
self.peripheral = peripheral
self.peripheral?.delegate = self
bluetoothManager.stopScan()
bluetoothManager.connect(self.peripheral!, options: nil)
}
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected successfully");
print("Trying to get equipment services and features...");
device.Peripheral = peripheral
self.peripheral?.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to connect");
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Connection has been disconnected");
isDeviceConnected = false;
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
guard let services = peripheral.services else {
if error != nil
{
print("Did Discover Services Error:\(String(describing: error!))")
}
else
{
print("Empty services")
}
return
}
print("Device Services......");
print("-------------- ("+peripheral.identifier.uuidString+")Service Services information: --------------------");
print("Number of services:\(services.count)")
for service in services
{
print("---- Service UUID:\(service.uuid) -----");
self.peripheral?.discoverCharacteristics(nil, for: service);
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
guard let characteristics = service.characteristics else {
if error != nil
{
print("Did Discover Characteristics Error:\(String(describing: error!))")
}
else
{
print("Empty Characteristics")
}
return
}
print("Feature Characteristic UUID List:");
for characteristic in characteristics
{
print("UUID: \(characteristic.uuid)");
self.peripheral?.readValue(for: characteristic);
}
}
func checkToken() {
print("Start Writing")
device.IsSaveOverwrite = true
let characteristic = characteristicList[characteristicHandle.GetCharacteristicUUID(type: .IsSaveOverwrite)]
let data = characteristicHandle.GetValue(device: device, type: .IsSaveOverwrite)
peripheral?.writeValue(data, for: characteristic!, type: CBCharacteristicWriteType.withResponse);
}
//Read the property successfully callback
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if(error != nil)
{
print("Did Update Characteristic with UUID \(characteristic.uuid) Error:\(String(describing: error!))")
return;
}
readConfigs.ConfigRespond(uuid: characteristic.uuid.uuidString);
device = characteristicHandle.SetDevice(device: device, type: characteristicHandle.GetCharacteristicType(uuid: characteristic.uuid), bytes: characteristic.value!);
// Check if all characteristics have been read, if so, start writing
if readConfigs.IsComplete() && readConfiguration == false
{
readConfiguration = true
hideActivityIndicator()
print("Read all characeteristics")
checkToken()
}
}
// Never gets called
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Write Config")
}
Could someone please point out what am missing?
I am working on developing BLE app using connection oriented channel . I am using nordic semiconductor nrf52 as a peripheral device and iPhone 6 as central manager.
I have used predefined PSM value provided by Bluetooth SIG that is 0x0025.
I am ble to connect to peripheral and open L2CAP channel successfully.
I am getting below error :
**[CoreBluetooth] WARNING: Unknown error: 436
2018-06-08 10:03:17.532709-0400 BluetoothTest[407:62057] [CoreBluetooth] **No known channel matching peer with psm 37****
Could please let me know how to proceed and what is meaning of error code 436
Below is my code :
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
//handling callback when a peripheral is discover
print("Central Manager PowerOn State Check:\(central.state.rawValue)")
if (peripheral.name?.contains("Nordic_OTSX") == true)
{
print(peripheral.name ?? "no name")
print("advertisement Data : \(advertisementData) ")
central.connect(peripheral, options: nil )
myPeripheral = peripheral
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
{
print("didConnect\(myPeripheral.debugDescription)")
myPeripheral.delegate = self
myPeripheral.discoverServices(nil)
}
//if error while making connection
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
{
print("error:\(error.debugDescription)")
}
//after opening L2CAP Channel
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
{
print("didOpen")
print(error.customMirror)
print(channel!.outputStream.debugDescription)
print(channel!.inputStream.debugDescription)
print(channel?.outputStream.hasSpaceAvailable)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
{
print("*******************************************************")
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let services = peripheral.services else {
return
}
//We need to discover the all characteristic
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
// bleService = service
}
print("Discovered Services: \(services)")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
print("*******************************************************")
if let charcterstics = service.characteristics
{
print("characteristics :")
for char in charcterstics
{
/* if char.uuid == buttonCharacteristicUUID
{
buttonCharacteristic = char
enableButtonNotifications(buttonCharacteristic!)
readButtonValue()
}*/
print(char.uuid.uuidString)
}
}
peripheral.openL2CAPChannel(0x0025)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I had a similar problem. I manage to solve it by creating a variable to save "channel: CBL2CAPChannel?" in the function
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
I was only saving the "channel.outputStream", which was the only one I needed. But it looks like if you don't save it, it will be closed.
0x25 PSM is for OTS. You need ATT PSM which is 0x1F
I'm trying to discover services from a BLE device I've connected to from my iOS app, but when my didDiscoverServices function gets called, the peripheral services array is empty.
The app connects to the device, runs peripheral.discoverServices(nil) and then the didDiscoverServices function gets called, but there are no services returned.
I've read a number of bluetooth related answers and other examples online and as best I can tell, my code is no different to what it should be, except it is not working. I feel like I must've missed something somewhere but I'm not sure what.
I've added my console log below for what I get when running the code, and the bluetooth code at the bottom for reference.
Bluetooth initialised
BLE is powered on
Optional("Nordic_Blinky") found at -72
Scanning stopped
Connect request sent
Connected to <CBPeripheral: 0x1c0301a70, identifier = 0887CF7F-98C8-3FCF-2D10-873FFFFB2B65, name = Nordic_Blinky, state = connected>
Discovering services
Services -- Optional([]) and Error -- nil
My BluetoothHandler class code is below
class BluetoothHandler : NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
// MARK: Properties
var manager: CBCentralManager!
var targetPeripheral: CBPeripheral!
// MARK: Shared Instance
static let sharedInstance = BluetoothHandler()
private override init() {
super.init()
self.startManager()
print("Bluetooth initialised")
}
// MARK: Functions
func startManager() {
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleMessage = ""
switch (central.state) {
case.poweredOn:
consoleMessage = "BLE is powered on"
manager.scanForPeripherals(withServices: nil, options: nil)
case.poweredOff:
consoleMessage = "BLE is powered off"
case.resetting:
consoleMessage = "BLE Resetting"
case.unknown:
consoleMessage = "BLE state unknown"
case.unsupported:
consoleMessage = "Device not supported by BLE"
case.unauthorized:
consoleMessage = "BLE not authorised"
}
print("\(consoleMessage)")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if (peripheral.name == "Nordic_Blinky") {
print("\(String(describing: peripheral.name)) found at \(RSSI)")
self.stopScan()
if targetPeripheral != peripheral {
targetPeripheral = peripheral
targetPeripheral!.delegate = self
manager.connect(targetPeripheral, options: nil)
print("Connect request sent")
}
}
else if (peripheral.name != nil) {
print("\(String(describing: peripheral.name))")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to \(peripheral)")
peripheral.delegate = self
peripheral.discoverServices(nil)
print("Discovering services")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Any) {
print("Connection failed", error)
}
func centralManager(_ central: CBCentralManager, didDisconnect peripheral: CBPeripheral) {
if self.targetPeripheral != nil {
self.targetPeripheral!.delegate = nil
self.targetPeripheral = nil
}
print("Connection disconnected")
self.startManager()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Services -- \(peripheral.services) and Error -- \(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
Try this: swift 3
If your peripheral(BLE device) have services then print in log.
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let services = peripheral.services as [CBService]!{
for service in services{
peripheral.discoverCharacteristics(nil, for: service)
}
}
print("==>",peripheral.services!)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for newChar: CBCharacteristic in service.characteristics!{
print(newChar)
}
}
I need to use bluetooth for my app. I only want to use the bluetooth connected by my iPhone. I found code blow for bluetooth:
///bluetooth scan for devices
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == .PoweredOn {
central.scanForPeripheralsWithServices(nil, options: nil)
} else {
print("Bluetooth not available.")
}
}
///bluetooth connect to a device
func centralManager(
central: CBCentralManager,
didDiscoverPeripheral peripheral: CBPeripheral,
advertisementData: [String : AnyObject],
RSSI: NSNumber) {
print("--didDiscoverPeripheral-")
print(peripheral.name)
//central.scanForPeripheralsWithServices(nil, options: nil)
/*let device = (advertisementData as NSDictionary)
.objectForKey(CBAdvertisementDataLocalNameKey)
as? NSString
if device?.containsString(self.BEAN_NAME!) == true {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connectPeripheral(peripheral, options: nil)
}*/
}
///bluetooth get services
func centralManager(
central: CBCentralManager,
didConnectPeripheral peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
///bluetooth get characteristics
func peripheral(
peripheral: CBPeripheral,
didDiscoverServices error: NSError?) {
for service in peripheral.services! {
let thisService = service as CBService
if service.UUID == BEAN_SERVICE_UUID {
peripheral.discoverCharacteristics(
nil,
forService: thisService
)
}
}
}
///bluetooth setup notifications
func peripheral(
peripheral: CBPeripheral,
didDiscoverCharacteristicsForService service: CBService,
error: NSError?) {
for characteristic in service.characteristics! {
let thisCharacteristic = characteristic as CBCharacteristic
if thisCharacteristic.UUID == BEAN_SCRATCH_UUID {
self.peripheral.setNotifyValue(
true,
forCharacteristic: thisCharacteristic
)
}
}
}
///bluetooth changes are coming
func peripheral(
peripheral: CBPeripheral,
didUpdateValueForCharacteristic characteristic: CBCharacteristic,
error: NSError?) {
var count:UInt32 = 0;
if characteristic.UUID == BEAN_SCRATCH_UUID {
characteristic.value!.getBytes(&count, length: sizeof(UInt32))
}
}
///bluetooth disconnect and try again
func centralManager(
central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: NSError?) {
central.scanForPeripheralsWithServices(nil, options: nil)
}
This code scan bluetooth, but I think I don't need to scan bluetooth. How to use the bluetooth which is already connected by iPhone ?
Thank you very much.
I'm trying to create an app that print via bluetooth with a printer
on xcode, i'm able to connect the printer, and also to see the services and the uuid's, but the problem is that when i try to see the characteristic of the services i found nil
do anyone have a idea about that problem ?
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
print("Service \(service)\n")
print("Discovering Characteristics for Service : \(service.uuid)")
print(service.characteristics)
}
}
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if #available(iOS 10.0, *){
switch (central.state) {
case CBManagerState.poweredOff:
print("CBCentralManagerState.PoweredOff")
case CBManagerState.unauthorized:
print("CBCentralManagerState.Unauthorized")
break
case CBManagerState.unknown:
print("CBCentralManagerState.Unknown")
break
case CBManagerState.poweredOn:
print("CBCentralManagerState.PoweredOn")
centralManager.scanForPeripherals(withServices: nil, options: nil)
centralManager.scanForPeripherals(withServices: nil, options: nil)
case CBManagerState.resetting:
print("CBCentralManagerState.Resetting")
case CBManagerState.unsupported:
print("CBCentralManagerState.Unsupported")
break
}}else{
switch central.state.rawValue{
case 0:
print("CBCentralManagerState.Unknown")
break
case 1:
print("CBCentralManagerState.Resetting")
case 2:
print("CBCentralManagerState.Unsupported")
break
case 3:
print("This app is not authorised to use Bluetooth low energy")
break
case 4:
print("Bluetooth is currently powered off.")
case 5:
print("Bluetooth is currently powered on and available to use.")
self.centralManager.scanForPeripherals(withServices: nil, options: nil)
break
default:
break
}}}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("name : \(peripheral.name )")
let device = (advertisementData as NSDictionary)
.object(forKey: CBAdvertisementDataLocalNameKey)
as? NSString
if device?.contains(BEAN_NAME) == true {
self.centralManager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
print("peripheral: \(self.peripheral)")
centralManager.connect(peripheral, options: nil)
print("peripheral: \(self.peripheral)")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print(error)
for characteristic in service.characteristics! {
print(anything)
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Sent")
}
You are missing an important step in your didDiscoverServices - you need to call discoverCharacteristics:for: -
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
print("Service \(service)\n")
print("Discovering Characteristics for Service : \(service.uuid)")
print(service.characteristics)
}
}
You will then get a call to your peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) delegate method