Can't write JSON to peripheral with CoreBluetooth - swift

I have a paired bluetooth peripheral which I have to send some credentials in a JSON like as follows
{"SSID":"WIFI_SSID", "Password": "WIFI_PASSWORD"}
And after the information has been sent the peripheral should connect to the provided WiFi credentials but I'm not completely sure the process is being performed correctly.
As soon the bluetooth peripheral has connected I start the process,
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// As soon as the device has connected
peripheral.delegate = self
// Only discover the service I know it's for writing
peripheral.discoverServices([SERVICE_UUID])
}
The discoverServices will call the peripheral(:didDiscoverServices error)
func peripheral( _ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
Calling the following method where I do all the logic
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
let characteristic = characteristic as CBCharacteristic
debugPrint(characteristic.properties)
// This prints the following properties, reading + writing
// __C.CBCharacteristicProperties(rawValue: 10)
if characteristic.uuid.isEqual(RX_UUID) {
do {
let msgDictionary = ["SSID": wiFiCredentials.SSID, "Password": wiFiCredentials.Password]
let jsonData = try JSONSerialization.data(withJSONObject: msgDictionary,options:[])
peripheral.writeValue(jsonData, for: characteristic, type: CBCharacteristicWriteType.withResponse)
} catch let error as NSError {
debugPrint("Error in auth message JSON: \(error.description)")
}
}
}
}
Up to this point I think everything it's correct. After calling writeValue and setting the type to CBCharacteristicWriteType.withResponse I should expect something in the peripheral(:didWriteValueFor characteristic: error) method. What I receive in that method is the next error
Error Domain=CBATTErrorDomain Code=3 \"Writing is not permitted.\" UserInfo={NSLocalizedDescription=Writing is not permitted.}"
What I guess is that when I write the JSON value I shouldn't use the .withResponse flag and use .withoutResponse. If I do so I get the following log in the console.
[CoreBluetooth] WARNING: Characteristic <CBCharacteristic: 0x28388a040, UUID = 3E9D2532-2F00-11E9-9602-A44CC81C989A, properties = 0xA, value = (null), notifying = NO> does not specify the "Write Without Response" property - ignoring response-less write
Which confirms to me that I have to use the .writeWithResponse.
Is there something I am missing in the process? The JSON has to be sent using GATT and AFAIK this is the correct approach to do it. Are the printed CBCharacteristicProperties correct?
Things I've done:
The JSON is not the problem. I've tried writing a random variable "1".data(using: .ascii) and still receive the same error.

Related

Swift Corebluetooth can't get unlimited responses to CBPeripheral .writeValue

I'm trying to get a peripheral to send a response each time my central does the .writeValue command. But it will only send it once.
didConnect
didDiscoverServices
didDiscoverCharacteristicsFor ...
// Enable notify from control characteristic:
for characteristic in serviceCharacteristics where characteristic.uuid == My_sensor_service.sensor_control_characteristicUUID
{
// ...
sensor_control_CBPeripheral = peripheral
sensor_control_CBCharacteristic = characteristic
sensor_control_CBPeripheral!.setNotifyValue(true, for: sensor_control_CBCharacteristic! )
//...
}
A few seconds later...
didUpdateValueFor My_sensor_service.sensor_notification_characteristicUUID ...
// Tell peripheral to send data:
sensor_control_CBPeripheral!.writeValue( command_data, for: sensor_control_CBCharacteristic!, type: .withResponse )
A few seconds later...
didUpdateValueFor My_sensor_service.sensor_control_characteristicUUID
let data = characteristic.value! // got correct data
A few seconds later...
didUpdateValueFor My_sensor_service.sensor_notification_characteristicUUID ...
// Tell peripheral to send more data:
sensor_control_CBPeripheral!.writeValue( command_data, for: sensor_control_CBCharacteristic!, type: .withResponse )
...but, I never get another:
didUpdateValueFor My_sensor_service.sensor_control_characteristicUUID
Peripheral just keeps sending...
didUpdateValueFor My_sensor_service.sensor_notification_characteristicUUID ...

CoreBluetooth and Omron Evolv Blood Pressure Monitor

I have been working on trying to support the Omron Evolv Blood Pressure Monitor (BPM) in my app, via CoreBluetooth. Using the Bluetooth SIG documentation about BPM’s (https://www.bluetooth.com/specifications/specs/ and then BLP and BLS) I could connect with the monitor.
I used the following characteristics:
Blood pressure measurement, 2A35
Blood pressure feature, 2A49
Page 10 in the BLS documentation states that the Blood Pressure Measurement is the property Indicate, which to my knowledge behaves similar to the Notify property.
To clarify some code I call in the delegate methods of CBPeripheralDelegate:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connected!")
bloodPressurePeripheral.discoverServices([bloodPressureService])
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
services.forEach { service in
print("discovered service: \(service) \n")
peripheral.discoverCharacteristics(nil, for: service)
}
}
In the didDiscoverCharacteristicsFor function I simply loop through the characteristics and check their property.
if char.properties.contains(.read) {
print("\(char.uuid): properties contains .read")
peripheral.readValue(for: char)
}
if char.properties.contains(.indicate) {
print("\(char.uuid): properties contains .indicate")
peripheral.setNotifyValue(true, for: char)
}
if char.properties.contains(.notify) {
print("\(char.uuid): properties contains .notify")
peripheral.setNotifyValue(false, for: char)
I tried both readValue and setNotifyValue for indicate both I still get the following result:
<CBCharacteristic: 0x2829880c0, UUID = 2A35, properties = 0x20, value = (null), notifying = NO>
2A35: properties contains .indicate
<CBCharacteristic: 0x282988180, UUID = 2A49, properties = 0x2, value = {length = 2, bytes = 0x2700}, notifying = NO>
2A49: properties contains .read
I don't really understand why the value of 2A35 is null. I know there are values because with the Omron application I can get the measurements.
My actual questions is: Has anyone has any experience in connecting with (Omron) BPM's using CoreBluetooth and what am I overlooking?
Thanks for answering!
I have tried connecting Ormon Evolv with Android device.
I will tell you what I have learned from it.
NB:- Ble Devices communicates asynchronously, you have to do one GATT operation (eg, read, write, enable notification, enable indication) at a time. The next operation is to be done only after the previous one is successfully done.
My device had the following services.
DEVICE_INFO_SERVICE with UUID 180a
BATTERY_SERVICE_UUID with UUID 180f
CURRENT_TIME_SERVICE_UUID with UUID 1805
BLOOD_PRESSURE_SERVICE_UUID with UUID 1810
The first GATT operation after a successful connect is gatt.discoverServices() . (the event onServicesDiscoverd is trigged, in android)
If you just need the BP reading without reading status and time stamp, just enable indication for UUID 2A35
val bloodPressureService = gatt?.getService(BLOOD_PRESSURE_SERVICE_UUID)
val bloodPressureChar = bloodPressureService?.getCharacteristic(BLOOD_PRESSURE_CHAR_UUID)
gatt?.setCharacteristicNotification(bloodPressureChar, true)
val bloodDescriptor = bloodPressureChar?.getDescriptor(CCC_DESCRIPTOR_UUID)
bloodDescriptor?.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
gatt?.writeDescriptor(bloodDescriptor)

Why does Swift unexpectedly insert “Optional” at the beginning of stringFromData?

I run this code on an iPad to create virtual BLE peripheral.
It starts advertising.
I run the central on iPhone.
Central detects peripheral and connects and subscribes.
Peripheral log unexpectedly has "Optional" in the log, though it's not in stringFromData.
IMAGE SHOWS stringFromData CONTENT AND LOG.........
class PeripheralViewController: UIViewController {
var packet_to_send = Data()
. . .
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
os_log("Central subscribed to characteristic")
// Init 1st sim packet:
packet_to_send = ("antenna data chunk " + String( packet_number )).data(using: .utf8)!
let stringFromData = String(data: packet_to_send, encoding: .utf8)
os_log("initial packet_to_send %d bytes = '%s'.", packet_to_send.count, String( describing: stringFromData))
stringFromData is an Optional. When you use String(describing:) to get the description of an Optional, it will be "Optional(yourDescription)" rather than "yourDescription".
You can avoid this by converting the Optional<String> into a String using optional binding or by providing a default value.
let stringFromData = String(data: packet_to_send, encoding: .utf8) ?? ""
os_log("initial packet_to_send %d bytes = '%s'.", packet_to_send.count, stringFromData)

UUID not allowed in peripherial didDiscoverCharacteristicsfor service

I am trying to make two programs that run on separate devices communicate with each other over bluetooth with CoreBluetooth. I can find and connect peripherals from the manager, and I can browse services in connected peripherals, but when I try and try and discover characteristics, I get the error The specified UUID is not allowed for this operation. and as expected the service's characteristics come up nil.
What is this supposed to mean? I have tried to discover characteristics with specifying the UUID of the target and without, both show this error.
this is the function that prints the error.
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print(error.localizedDescription)//prints "The specified UUID is not allowed for this operation."
if service.characteristics != nil {
for characteristic in service.characteristics! {
if characteristic.uuid == CBUUID(string: "A4389A32-90D2-402F-A3DF-47996E123DC1") {
print("characteristic found")
peripheral.readValue(for: characteristic)
}
}
}
}
this is where I look for peripherals.
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if peripheral.services != nil {
for service in peripheral.services! {
if service.uuid == CBUUID(string: "dc495108-adce-4915-942d-bfc19cea923f") {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
this is how I add the service characteristic on the other device.
service = CBMutableService(type: CBUUID(string:"dc495108-adce-4915-942d-bfc19cea923f"), primary: true)
characteristic = CBMutableCharacteristic(type: CBUUID(string: "A4389A32-90D2-402F-A3DF-47996E123DC1"), properties: .write, value: nil, permissions: .writeable)
service.characteristics = [characteristic]
I tried a number of different combinations of properties and permissions (including .read/.readable) and I get the same error.
You are attempting to read the value of a characteristic that you have set as write-only, so Core Bluetooth gives you an error; the read operation is not valid for the specified characteristic.
If you want your characteristic to be both readable and writable you need to specify this:
service = CBMutableService(type: CBUUID(string:"dc495108-adce-4915-942d-bfc19cea923f"), primary: true)
let characteristic = CBMutableCharacteristic(type: CBUUID(string: "A4389A32-90D2-402F-A3DF-47996E123DC1"), properties: [.write, .read], value: nil, permissions: [.writeable, .readable])
service.characteristics = [characteristic]

Swift BLE peripheral writeValue is not working

My current code is:
#IBAction func sendData(sender: UISwitch) {
if advertisingSwitch.on {
var parameter = NSInteger(45)
let data = NSData(bytes: &parameter, length: 1)
if let connectedPeripheral = discoveredPeripheral {
println("========= In connected peripheral \(connectedPeripheral)")
//println("========= Send data is \(currentSendData)")
println("========= Characteristic is \(sendDataCharacteristic)")
println("========= data length is \(data.bytes)")
self.sendDataToCentral(connectedPeripheral, characteristic: sendDataCharacteristic!, data: data)
}
}
}
private func sendDataToCentral(peripheral: CBPeripheral, characteristic: CBCharacteristic, data: NSData) {
println("data is \(data)")
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithoutResponse)
println("writed characteristic \(characteristic)")
}
When I checked the Peripheral, it is connected showing:
BPeripheral: 0x1700ee980, identifier = E2377588-84CB-87ED-570A-B51614287B3C, name = TAv22u-FDF1, state = connected
The characteristic is getting from service scan with known UUID. I am sure the characteristic I got has function "write", which is
<CBCharacteristic: 0x174086090, UUID = FFE9, properties = 0x8, value = (null), notifying = NO>
after I executing function:
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithoutResponse)
The value in characteristic is not changing. I don't know where did I get wrong.
peripheral.writeValue(data, forCharacteristic: characteristic, type:
CBCharacteristicWriteType.WithResponse)
try .withResponse and check what is response from peripheral.