SwiftUI - Choose a specific service when I scan a device in Bluetooth - swift

Choose a specific service when I scan a device
I did a program in which I want to scan a device with a specific service (my device is a thermometer "SHT31" and the service is the battery service "UUID: 180F") but when I run this program on my iPhone, it doesn't scan the device. Whereas when "withServices: nil" it scans the device. Do you have a solution?
My code :
import CoreBluetooth
class BLEConnection: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var myPeripheral: CBPeripheral!
let batteryServiceCBUUID = CBUUID(string: "0x180F")
func startCentralManager() {
self.centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
case .unsupported:
print("BLE is Unsupported")
break
case .unauthorized:
print("BLE is Unauthorized")
break
case .unknown:
print("BLE is Unknown")
break
case .resetting:
print("BLE is Resetting")
break
case .poweredOff:
print("BLE is Powered Off")
break
case .poweredOn:
print("BLE powered on")
central.scanForPeripherals(withServices: [batteryServiceCBUUID], options: nil)
break
#unknown default:
break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let pname = peripheral.name {
// print(pname)
if pname == "Smart Humigadget" {
self.centralManager.stopScan()
self.myPeripheral = peripheral
self.myPeripheral.delegate = self
self.centralManager.connect(peripheral, options: nil)
print("\(pname) is connected")
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.myPeripheral.delegate = self
self.myPeripheral.discoverServices(nil)
}
}

Use the following format (no hex prefix needed)
let batteryServiceCBUUID = CBUUID(string: "180F")

Related

How do I pair devices using core bluetooth?

I am trying to develop bluetooth based application that will provide users the functionality to share data via bluetooth pairing. I am facing issue while pairing the device.
This is my scenario
The user will login the application and will be prompted to enable bluetooth service. This is the code for the same :-
//MARK: - DECLARATIONS
var cbCentralManager : CBCentralManager?
var peripheral : CBPeripheral?
var peripherals: [CBPeripheral] = []
//MARK: - VIEW_METHODS
override func viewDidLoad() {
super.viewDidLoad()
self.setUpView()
}
func setUpView() {
cbCentralManager = CBCentralManager(delegate: self, queue: nil)
}
//MARK: - EXTENSION
extension ViewController : CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Bluetooth is enabled..")
central.scanForPeripherals(withServices: nil, options: nil)
} else {
print("Bluetooth is not enabled..")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard peripheral.name != nil else {return}
print("Sensor Found!")
//stopScan
cbCentralManager?.stopScan()
//connect
cbCentralManager?.connect(peripheral, options: nil)
self.peripheral = peripheral
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//discover all service
peripheral.discoverServices(nil)
peripheral.delegate = self
}
In the next step, on the click of button, near by BLE devices will be scanned.
func startScan() {
let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey:
NSNumber(value: false)]
peripherals = []
print("Now Scanning...")
self.timer.invalidate()
centralManager?.scanForPeripherals(withServices: nil, options: options)
Timer.scheduledTimer(timeInterval: 17, target: self, selector: #selector(self.cancelScan), userInfo: nil, repeats: false)
}
After the user selects a device to be paired from the list of scanned BLE devices, below code will be executed for establishing connection between devices.
func connectToDevice(device:CBPeripheral) {
centralManager = CBCentralManager(delegate: self, queue: .main)
self.blePeripheral = device
self.blePeripheral?.delegate = self
centralManager?.connect(self.blePeripheral!, options: nil)
}
The delegate methods are extended
extension HomeVC : CBPeripheralDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
return
}
print("Peripheral manager is running")
}
//Check when someone subscribe to our characteristic, start sending the data
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print("Device subscribe to characteristic")
}
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
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
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 peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
if let error = error {
print("\(error)")
let errorStr = "\(error)"
let alert = UIAlertController(title: "Alert", message: errorStr, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
I am stuck as I am not getting the alert for pairing devices. Can anyone guide me in this ?
Thanks in advance.

I'm trying to receive indicate via BLE connection, but I can't receive any data

I am creating an app for BLE connection for iOS.
I can connect to the peripheral from central (iPhone6: iOS12.9) and send commands with
I am able to send commands with writevalue.
https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518949-setnotifyvalue
In the above setNotifyValue, there is a description that seems to be accepted by indicate.
The following methods of didUpdateValueFor do not return.
/// When changing the characteristic
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518708-peripheral
If you know how to implement receiving data in indicate, please let me know.
The sample code is shown below.
I'm still working on it, so there may be some garbage code, sorry.
// ViewController.swift
import UIKit
import CoreBluetooth
import os
class ViewController: UIViewController {
/// https://qiita.com/eKushida/items/def628e0eff6c106d467
var serviceUUID : CBUUID!
var characteristicUUID : CBUUID!
var responseCharacteristicUUID : CBUUID!
var centralManager: CBCentralManager!
var peripheral: CBPeripheral!
var writeCharacteristic: CBCharacteristic!
var responsCharacteristic: CBCharacteristic!
var data = Data()
#IBOutlet weak var dispLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setup()
dispLabel.text = "Startup"
}
/// Initialize the central manager and UUID
private func setup() {
// Create an object representing the UUID.
self.serviceUUID = CBUUID(string: "XXXXX0000-XXXX-XXXX-XXXX-XXXXXXXXXX")
self.characteristicUUID = CBUUID(string: "XXXX2001-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
self.responseCharacteristicUUID = CBUUID(string: "XXXX2000-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
}
/// Pairing process
#IBAction func scan(_ sender: UIButton) {
print("Pairing process")
dispLabel.text = "Pairing process pressed"
self.centralManager = CBCentralManager(delegate: self, queue: nil)
}
/// Communication connection
#IBAction func connect(_ sender: UIButton) {
print("Communication connection")
/// https://qiita.com/MashMorgan/items/32500f158cb08d565786
/// https://knkomko.hatenablog.com/entry/2019/07/16/013443
let message = "**COMMAND**"
let command = message + "\r"
let writeData = Data(command.utf8)
print("writeData:" + String(data: writeData, encoding: .utf8)!)
peripheral.writeValue(writeData, for: writeCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
//MARK : - CBCentralManagerDelegate
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
//wait for power on and scan
case CBManagerState.poweredOn:
let services: [CBUUID] = [serviceUUID] ///serviceUUID
centralManager.scanForPeripherals(withServices: nil, options: nil)
// centralManager.scanForPeripherals(withServices: services, options: nil)
print("isScanning:" + String(centralManager.isScanning))
default:
break
}
}
/// Called when a peripheral is discovered
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
self.peripheral = peripheral
print("peripheral.name:" + String(peripheral.name ? "") + " peripheral.id:" + peripheral.identifier.uuidString)
if "XXXXXXX" == peripheral.name {
//start connection
self.centralManager.connect(self.peripheral, options: nil)
//peripheral is found, stop scanning
centralManager.stopScan()
}
}
/// called when connected
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connection successful serviceUUID:" + serviceUUID.uuidString)
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
dispLabel.text = "Peripheral connection successful"
}
/// Called when the connection fails
func centralManager(_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?) {
print("Connection failed")
}
/// When disconnected
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Disconnection: \(String(describing: error))")
}
}
//MARK : - CBPeripheralDelegate
extension ViewController: CBPeripheralDelegate {
/// Called when the characteristic is found
func peripheral(_ peripheral: CBPeripheral,
DidDiscoverCharacteristicsFor service: CBService,
error: Error?) {
if error ! = nil {
print(error.debugDescription)
return
}
guard let serviceCharacteristics = service.characteristics else {
// error handling
return
}
// Processing by characteristic
for characreristic in serviceCharacteristics {
if characreristic.uuid == characteristicUUID
{
// keep the characteristic for writing data
self.writeCharacteristic = characreristic
print("Write characreristic / UUID:" + characreristic.uuid.uuidString)
print("Write characreristic / properties: \(self.writeCharacteristic.properties)")
continue
}
if characreristic.uuid == responseCharacteristicUUID {
peripheral.setNotifyValue(true, for: characreristic)
self.responsesCharacteristic = characreristic
print("Responses characreristic / UUID:" + characreristic.uuid.uuidString)
print("Responses characreristic / properties: \(self.responsesCharacteristic.properties)")
continue
}
print("Other characreristic / UUID:" + characreristic.uuid.uuidString)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor: CBService, error: Error?){
print("peripheral didDiscoverIncludedServicesFor")
}
/// When writing data to the characteristic (called when sending a command)
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("peripheral didWriteValueFor")
guard error == nil else {
print("Error when writing characteristic data: \(String(describing: error))")
// failure handling
return
}
print(characteristic.value)
}
func peripheral(peripheral: CBPeripheral,
didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic,
error: NSError?)
{
print("peripheral didUpdateNotificationStateForCharacteristic")
if let error = error {
print("Notify state update failed.... .error: \(error)")
} else {
print("Notify state update succeeded! isNotifying: \(characteristic.isNotifying)")
}
}
func peripheral(peripheral: CBPeripheral,
didUpdateValueForCharacteristic characteristic: CBCharacteristic,
error: NSError?)
{
print("peripheral didUpdateValueForCharacteristic")
if let error = error {
print("Data update notification error: \(error)")
return
}
print("Data update! value: \(characteristic.value)")
}
/// When changing the characteristic
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("peripheral didUpdateValueFor")
guard error == nil else {
print("Error getting/changing characteristic value: \(String(describing: error))")
// failure handling
return
}
guard let data = characteristic.value else {
print("characteristic.value")
// failure process
return
}
// data will be passed to us
print(data)
}
}
I have example iOS projects (Central & Peripheral) which send/receive indications: https://github.com/alexanderlavrushko/BLEProof-collection
setNotifyValue here is called similarly as you do, it should be fine.
I suggest to check the way how the characteristic is created and updated on the Peripheral side, iOS example link.
Also there is a great iOS application LightBlue which can simulate a BLE device, see this guide:
Central - the topic "Subscribing to Characteristics" might be useful
Peripheral - "Adding a New Virtual Peripheral", but use Blank device and configure services/characteristics you need

CoreBluetooth CBCentralManager unwraps as nil even when unwrapped from the stateUpdateHandler's case .poweredOn. Any idea why?

CBCentralManager unwraps as nil even if accessed in the stateUpdateHandler on .poweredOn. If I put a sleep(1) before accessing CBCentralManager, there's no problem. Why is this happening? If it's .poweredOn surely there's a non-nil instance there already?
The following code worked until upgrading my OS to Catalina and Xcode to 11.5.
extension BLEController: 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("Bluetooth module is on. Searching...")
sleep(1) // works fine if this is here, but if .poweredOn it should be non-nil?
// unwraps as nil
guard let cManager = centralManager else {
print("centralManager is nil")
return
}
cManager.scanForPeripherals(withServices: [self.heartRateServiceCBUUID])
#unknown default:
return
}
}
}
Full code:
import Foundation
import CoreBluetooth
class BLEController: CBCentralManager {
var btQueue = DispatchQueue(label: "BT Queue")
var bpmReceived: ((Int) -> Void)?
var bpm: Int? {
didSet {
self.bpmReceived?(self.bpm!)
}
}
var centralManager: CBCentralManager!
var heartRatePeripheral: CBPeripheral!
let heartRateServiceCBUUID = CBUUID(string: "0x180D")
let heartRateMeasurementCharacteristicCBUUID = CBUUID(string: "2A37")
let batteryLevelCharacteristicCBUUID = CBUUID(string: "2A19")
func start() -> Void {
print("bluetooth started")
self.centralManager = CBCentralManager(delegate: self, queue: self.btQueue)
}
func stop() -> Void {
centralManager.cancelPeripheralConnection(heartRatePeripheral)
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any],
rssi RSSI: NSNumber) {
heartRatePeripheral = peripheral
heartRatePeripheral.delegate = self
centralManager.stopScan()
centralManager.connect(heartRatePeripheral)
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connected to HRM!")
heartRatePeripheral.discoverServices(nil)
}
func onHeartRateReceived(_ heartRate: Int) {
self.bpm = heartRate
}
}
extension BLEController: 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("Bluetooth module is on. Searching...")
sleep(1) // works fine if this is here, but if .poweredOn it should be non-nil?
// unwraps as nil
guard let cManager = centralManager else {
print("centralManager is nil")
return
}
cManager.scanForPeripherals(withServices: [self.heartRateServiceCBUUID])
#unknown default:
return
}
}
}
extension BLEController: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
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 {
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
error: Error?) {
switch characteristic.uuid {
case batteryLevelCharacteristicCBUUID:
let percent = batteryLevel(from: characteristic)
print("Battery level: \(percent)%")
case heartRateMeasurementCharacteristicCBUUID:
let bpm = heartRate(from: characteristic)
onHeartRateReceived(bpm)
default:
return
}
}
private func heartRate(from characteristic: CBCharacteristic) -> Int {
guard let characteristicData = characteristic.value else { return -1 }
let byteArray = [UInt8](characteristicData)
let firstBitValue = byteArray[0] & 0x01
if firstBitValue == 0 {
// Heart Rate Value Format is in the 2nd byte
return Int(byteArray[1])
} else {
// Heart Rate Value Format is in the 2nd and 3rd bytes
return (Int(byteArray[1]) << 8) + Int(byteArray[2])
}
}
private func batteryLevel(from characteristic: CBCharacteristic) -> Int {
guard let characteristicData = characteristic.value else { return -1 }
let byteArray = [UInt8](characteristicData)
return Int(byteArray[0])
}
}
The relevant code is:
self.centralManager = CBCentralManager(delegate: self, queue: self.btQueue)
and:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
...
case .poweredOn:
...
}
The call of the CBCentralManager initializer starts the action. You have to assume that the delegate will be immediately called from the initializer, i.e. the second piece of code is run before the initializer has returned and before the result has been assigned to the instance variable centralManager.
This will probably always happen if the Bluetooth device is already powered on when the initializer is called. If it isn't powered on yet, the delegate will be called later.
Anyway, you shouldn't need to worry about it. Instead of:
guard let cManager = centralManager else {
print("centralManager is nil")
return
}
cManager.scanForPeripherals(withServices: [self.heartRateServiceCBUUID])
just use:
central.scanForPeripherals(withServices: [self.heartRateServiceCBUUID])
Within the delegate, the CBCentralManager instance is available as it is passed as a parameter.

Cannot send value with CoreBluetooth

I'm trying to write value from central to peripheral through CoreBluetooth, but it doesn't work.
But I get error, that peripheral has no such characteristic, but it has.
Here is CoreBluetooth Extension:
CentralManager:
extension MapViewController: 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: [BLEModels.serviceUUID])
print ("Started scanning")
default:
print("Unknown error for CentralManger.state")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
print("\(String(describing: peripheral.name))")
availablePeripherals.append(peripheral)
self.centralManager.connect(peripheral, options: nil)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to peripheral!")
peripheral.delegate = self
peripheral.discoverServices([BLEModels.serviceUUID])
}
}
PeripheralManager:
extension MapViewController: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .unknown:
print("peripheral.state is .unknown")
case .resetting:
print("peripheral.state is .resetting")
case .unsupported:
print("peripheral.state is .unsupported")
case .unauthorized:
print("peripheral.state is .unauthorized")
case .poweredOff:
print("peripheral.state is .poweredOff")
case .poweredOn:
print("peripheral.state is .poweredOn")
peripheralManager.add(likeUService)
peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey:[BLEModels.serviceUUID]])
print ("Started advertising")
default:
print("Unknown error for PeripheralManger.state")
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
var centrals = self.subscribedCentrals[characteristic, default: [CBCentral]()]
centrals.append(central)
print ("Central subscribed")
self.subscribedCentrals[characteristic] = centrals
}
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
for request in requests{
let text = String(data: request.value!, encoding: .utf8)
print (text)
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, willRestoreState dict: [String : Any]) {
// Just for using background
}
}
Peripheral:
Here I'm trying to write value in didDiscoverServices.
extension MapViewController: CBPeripheralDelegate{
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
for service in peripheral.services ?? []{
if service.uuid == BLEModels.serviceUUID{
if !checkedPeripherals.contains(peripheral){
checkedPeripherals.append(peripheral)
newCompanion.peripheral = peripheral
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service:CBService, error: Error?){
for characteristic in service.characteristics ?? []{
switch characteristic.uuid{
case BLEModels.userInfoUUID:
peripheral.setNotifyValue(true, for: characteristic)
peripheral.readValue(for: characteristic)
case BLEModels.descriptionUUID:
peripheral.readValue(for: characteristic)
case BLEModels.sendInfoUUID:
peripheral.setNotifyValue(true, for: characteristic)
print ("Found send characteristic")
let data = "Send message".data(using: .utf8)
peripheral.writeValue(data!, for: sendInfoCharacteristic, type: .withoutResponse)
default:
print ("Get unknown type of characteristic")
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
switch characteristic.uuid {
case BLEModels.userInfoUUID:
let nameDataStr = String(data: characteristic.value!, encoding: .utf8)
userTitles.append(nameDataStr!)
newCompanion.firstName = nameDataStr!
print ("User info: \(String(describing: nameDataStr)))")
case BLEModels.descriptionUUID:
let descrDataStr = String(data: characteristic.value!, encoding: .utf8)
newCompanion.description = descrDataStr!
default:
print("Unknown characteristic")
}
if !(addedPeripherals.contains(newCompanion.peripheral!)){
addedPeripherals.append(newCompanion.peripheral!)
companions.append(newCompanion)
print ("Companion added")
print ("\(companions.count)")
DispatchQueue.main.async {
self.usersList.reloadData()
}
}
}
}
Here my sendInfoCharacteristic:
sendInfoCharacteristic = CBMutableCharacteristic(type: BLEModels.sendInfoUUID, properties: [ CBCharacteristicProperties.read, CBCharacteristicProperties.indicate], value: nil, permissions: [CBAttributePermissions.readable] )
Here is the error:
[CoreBluetooth] WARNING: <CBMutableCharacteristic: 0x283d5e4c0 UUID =
FF68B080-7028-11EA-BC55-0242AC130003, Value = {length = 20, bytes =
0xd0af20d182d0b5d0b1d18f20d0b2d0b8d0b6d183}, Properties = 0x4,
Permissions = 0x2, Descriptors = (null), SubscribedCentrals = (
)> is not a valid characteristic for peripheral <CBPeripheral:
0x282758000, identifier = 845358A5-0FA5-3009-8939-02B6FA6A43AF, name =
iPhone, state = connected>
And also didSubscribeCentral doesn't work, I made peripheral.SetNotifyValue, but central doesn't subscribe.
I'll be really grateful for some advice.

CBPeripheral disconnects after 30 seconds

I am using CoreBluetooth to write an app that subscribes to one characteristic with one property: 'notify' which will allow a Raspberry Pi 3 to send an integer digit to be displayed on the app. I have created the service with this characteristic using Pybleno, a Python direct port of Bleno. I'm able to connect to the service and read its characteristic, but only for 30 seconds - after which the didDisconnect method is fired. On my central I use time.sleep() to alter the frequency at which the data is sent. I noticed that no delay causes the peripheral to disconnect after about 10 seconds, whereas 20-30ms delay causes a disconnect after 30 seconds. Any help would be great! Thank you. (I followed a Core Bluetooth tutorial for reading a Heart Rate monitor)
import UIKit
import CoreBluetooth
let TranslatorServiceCBUUID = CBUUID(string: "16dedcf4-027f-435f-b1e6-22e601276949")
let PredictionCharacteristicCBUUID = CBUUID(string: "16DEDCF4-027F-435F-B1E6-22E601276950")
var raspberryAsPeripheral: CBPeripheral!
class HRMViewController: UIViewController {
#IBOutlet weak var heartRateLabel: UILabel!
var centralManager: CBCentralManager!
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 onDigitReceived(_ digit: Int) {
var predictedNumber : String
if (digit == 16) {
predictedNumber = ""
}
else {
predictedNumber = String(digit)
}
heartRateLabel.text = predictedNumber
print("Predicted digit: \(digit)")
}
}
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: [TranslatorServiceCBUUID])
}
}
func centralManager(_ central : CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
print(peripheral)
raspberryAsPeripheral = peripheral
raspberryAsPeripheral.delegate = self
centralManager.stopScan()
centralManager.connect(raspberryAsPeripheral)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("you connected with the raspberry pi")
raspberryAsPeripheral.discoverServices([TranslatorServiceCBUUID])
}
}
extension HRMViewController : CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print(service)
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)
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
error: Error?) {
switch characteristic.uuid {
case PredictionCharacteristicCBUUID:
let whichNumber = predictedValue(from: characteristic)
onDigitReceived(whichNumber)
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("DISSSCONNNNECCCTEDDDDD")
}
private func predictedValue(from characteristic: CBCharacteristic) -> Int {
guard let characteristicData = characteristic.value else { return -1 }
let byteArray = [UInt8](characteristicData)
return Int(byteArray[0])
/*
let firstBitValue = byteArray[0] & 0x01
if firstBitValue == 0 {
// Heart Rate Value Format is in the 2nd byte
return Int(byteArray[1])
} else {
// Heart Rate Value Format is in the 2nd and 3rd bytes
return (Int(byteArray[1]) << 8) + Int(byteArray[2])
}
*/
}
}