I am creating an iOS-application that for now should transfer text messages between two iOS-devices. I want to/have to use Core Bluetooth for that. One device is becoming the peripheral and one the central.
The setup of the peripheral with one service containing two characteristics (1 read, 1 write) is no problem. I used the LightBlue App to connect to the peripheral and i also can see its characteristics.
When i use the central role device i find the peripheral, can connect and also see its characteristics. I store the peripheral and the characteristic both in own variables and use them later to call the writeValue(_ data: Data, for characteristic: CBCharacteristic, type: CBCharacteristicWriteType) of the stored peripheral which throws no error. But the peripheral doesn't seem to get that message because none of its didWriteValueFor or didUpdateValueFor methods are called. The central cannot subscribe to the characteristics too.
Also when i use the peripheral to update the value of its own characteristic, my check with the LightBlue App is not successful because the value always stays empty and the central doesn't get notified too of course.
It looks like everything that comes out of the application doesn't work. Only the data coming in (f.e. from the LightBlue App) seems to work.
Has anyone ever had a similar problem? I used many different tutorials and did exactly what is described there (because it was pretty much the same procedure everywhere).
Thanks in advance!
I use XCode Version 10.1, code in Swift and use an iPhone XS Max (iOS 12.1) and an iPad Air 2 (iOS 12.0) for testing.
Here is my peripheral class:
import CoreBluetooth
import UIKit
class BLEPeripheralManager: NSObject {
//MARK:- Properties
static let shared = BLEPeripheralManager()
//just some delegates for other classes
var peripheralDataReceiver: PeripheralDataReceiver?
var peripheralChatDataReceiver: PeripheralChatDataReceiver?
var peripheralManager: CBPeripheralManager
var subscribedCentrals: [CBCentral] = []
var readCharacteristic: CBMutableCharacteristic = {
let uuidStringChar1 = UUIDs.characteristicUUID1
let uuidChar1 = CBUUID(string: uuidStringChar1)
let char = CBMutableCharacteristic(type: uuidChar1, properties: .read, value: nil, permissions: .readable)
return char
}()
var writeCharacteristic: CBMutableCharacteristic = {
let uuidStringChar2 = UUIDs.characteristicUUID2
let uuidChar2 = CBUUID(string: uuidStringChar2)
let char = CBMutableCharacteristic(type: uuidChar2, properties: .write, value: nil, permissions: .writeable)
return char
}()
//MARK:- Private Methods
private override init() {
self.peripheralManager = CBPeripheralManager(delegate: nil, queue: nil)
super.init()
self.peripheralManager.delegate = self
}
private func setupManager() {
let uuidStringServ = UUIDs.serviceUUID
let uuidServ = CBUUID(string: uuidStringServ)
let transferService = CBMutableService(type: uuidServ, primary: true)
transferService.characteristics = [self.readCharacteristic, self.writeCharacteristic]
self.peripheralManager.add(transferService)
}
private func teardownServices() {
self.peripheralManager.removeAllServices()
}
private func clearSubscribers() {
self.subscribedCentrals.removeAll()
}
//MARK:- Public Methods
public func sendMessage(fromPeripheral peripheral: String, text: String) {
if text.isEmpty { return }
let chatMessage = ChatMsg(messageText: text, fromDevice: peripheral)
let encoder = JSONEncoder()
do {
let data = try encoder.encode(chatMessage)
print(self.readCharacteristic.uuid)
if self.peripheralManager.updateValue(data, for: self.readCharacteristic, onSubscribedCentrals: nil) == false {
print("Update from Peripheral failed (ReadCharacteristic)")
} else {
print("Message sent (ReadCharacteristic)")
}
if self.peripheralManager.updateValue(data, for: self.writeCharacteristic, onSubscribedCentrals: nil) == false {
print("Update from Peripheral failed (WriteCharacteristic)")
} else {
print("Message sent (WriteCharacteristic)")
}
} catch {
print("Error in encoding data")
}
}
func startAdvertising() {
let services = [CBUUID(string: UUIDs.serviceUUID)]
let advertisingDict = [CBAdvertisementDataServiceUUIDsKey: services]
self.peripheralManager.startAdvertising(advertisingDict)
}
public func stopAdvertising() {
self.peripheralManager.stopAdvertising()
}
public func checkIfAdvertising() -> Bool {
return self.peripheralManager.isAdvertising
}
}
extension BLEPeripheralManager: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .poweredOff:
print("Peripheral powered off")
self.teardownServices()
self.clearSubscribers()
case .poweredOn:
print("Peripheral powered on")
self.setupManager()
case .resetting:
print("Peripheral resetting")
case .unauthorized:
print("Unauthorized Peripheral")
case .unknown:
print("Unknown Peripheral")
case .unsupported:
print("Unsupported Peripheral")
}
}
//doesn`t get called
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
for request in requests {
if let value = request.value {
if let messageText = String(data: value, encoding: String.Encoding.utf8) {
//
}
}
}
}
//doesn`t get called
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
var shouldAdd: Bool = true
for sub in self.subscribedCentrals {
if sub == central {
shouldAdd = false
}
}
if shouldAdd { self.subscribedCentrals.append(central) }
}
}
Here is my central class:
import CoreBluetooth
import UIKit
class BLECentralManager: NSObject {
static let shared = BLECentralManager()
//just some delegates for other classes
var centralDataReceiver: CentralDataReceiver?
var centralChatDataReceiver: CentralChatDataReceiver?
var centralManager: CBCentralManager
var peripheralArray: [CBPeripheral] = []
var writeTransferPeripheral: (peripheral: CBPeripheral, characteristic: CBCharacteristic)?
var readTransferPeripheral: (peripheral: CBPeripheral, characteristic: CBCharacteristic)?
private override init() {
self.centralManager = CBCentralManager(delegate: nil, queue: nil, options: nil)
super.init()
self.centralManager.delegate = self
}
private func startScan() {
self.centralManager.scanForPeripherals(withServices: [CBUUID(string: UUIDs.serviceUUID)], options: nil)
//self.centralManager.scanForPeripherals(withServices: nil, options: nil)
}
public func connectTo(index: Int) {
self.centralManager.connect(self.peripheralArray[index], options: nil)
}
public func sendMessage(fromPeripheral peripheral: String, text: String) {
let chatMsg = ChatMsg(messageText: text, fromDevice: peripheral)
let encoder = JSONEncoder()
do {
let data = try encoder.encode(chatMsg)
self.writeTransferPeripheral?.peripheral.writeValue(data, for: (self.writeTransferPeripheral?.characteristic)!, type: .withoutResponse)
} catch {
print("Error in encoding data")
}
}
public func getActiveConnections() -> String {
var connString: String = ""
let conns = self.centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: UUIDs.serviceUUID)])
for peri in conns {
if connString == "" {
connString = "\(peri)"
} else {
connString = "\(connString), \(peri)"
}
}
return connString
}
public func getMessages() {
self.readTransferPeripheral?.peripheral.readValue(for: (self.readTransferPeripheral?.characteristic)!)
}
public func lookForPeripherals() {
self.peripheralArray.removeAll()
self.startScan()
}
public func getPeripherals() -> [CBPeripheral] {
return self.peripheralArray
}
private func getNameOfPeripheral(peripheral: CBPeripheral) -> String {
if let name = peripheral.name {
return name
} else {
return "Device"
}
}
}
extension BLECentralManager: CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOff:
print("BLE has powered off")
centralManager.stopScan()
case .poweredOn:
print("BLE is now powered on")
self.startScan()
case .resetting:
print("BLE is resetting")
case .unauthorized:
print("Unauthorized BLE state")
case .unknown:
print("Unknown BLE state")
case .unsupported:
print("This platform does not support BLE")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.peripheralArray.append(peripheral)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.centralDataReceiver?.connectionEstablished(peripheral: peripheral)
print("Connection Established")
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
self.centralDataReceiver?.connectionTornDown()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print(characteristic.uuid)
let char = characteristic as CBCharacteristic
if char.uuid.uuidString == UUIDs.characteristicUUID2 {
self.writeTransferPeripheral?.peripheral = peripheral
self.writeTransferPeripheral?.characteristic = char
self.writeTransferPeripheral?.peripheral.setNotifyValue(true, for: (self.writeTransferPeripheral?.characteristic)!)
} else if char.uuid.uuidString == UUIDs.characteristicUUID1 {
self.readTransferPeripheral?.peripheral = peripheral
self.readTransferPeripheral?.characteristic = char
self.readTransferPeripheral?.peripheral.setNotifyValue(true, for: (self.readTransferPeripheral?.characteristic)!)
}
}
}
//doesn`t get called
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if let data = characteristic.value {
if data.isEmpty { return }
if let text = String(data: characteristic.value!, encoding: String.Encoding.utf8) {
self.centralChatDataReceiver?.receiveMessage(fromPeripheral: self.getNameOfPeripheral(peripheral: peripheral), text: text)
}
}
}
//doesn`t get called
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let data = characteristic.value {
if data.isEmpty { return }
if let text = String(data: characteristic.value!, encoding: String.Encoding.utf8) {
self.centralChatDataReceiver?.receiveMessage(fromPeripheral: self.getNameOfPeripheral(peripheral: peripheral), text: text)
}
}
}
//doesn`t get called
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("Notification state changed")
}
}
peripheralManager(_:didReceiveWrite:)
method of theCBPeripheralManagerDelegate
protocol – Paulw11