How to read characteristics in background BLE using CoreBluetooth - ios

I'm developing app which reads specific characteristics in the background mode. Can this be achieved in the background with selecting the background mode as "Uses Bluetooth LE Accessories" in the plist. Is there any chances of rejecting the app in background if we read particular characteristics of a known service? Reading the characteristics should happen continuously. If we setNotify to "True" will this work in the background. Please provide some valuable suggestions/work around if anyone is aware of it. Thanks in advance.

Can this be achieved in the background with selecting the background mode as "Uses Bluetooth LE Accessories" in the plist.
Yes you can definitely read ble characteristics when your app is in background mode setting the “Uses Bluetooth LE Accessories” background mode in the capabilities tab
Is there any chances of rejecting the app in background if we read particular characteristics of a known service?
If your app needs ble background mode and correctly declares it in the capabilities tab, the app won’t be rejected
Reading the characteristics should happen continuously. If we setNotify to "True" will this work in the background
In order to get notified each time the ble characteristic of your ble peripheral changes its value, you can correctly setNotify to true on the desired characteristic. Each time the characteristic changes its value the peripheral(_:didUpdateStateFor:Error) callback will be triggered and you can get the updated value. Setting the background mode correctly, this callback will be triggered even if your application is in background and your phone is locked.

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
error: Error?)
{
print("characteristic: ", characteristic)
guard let string = String(bytes: characteristic.value!, encoding: .utf8) else {
return
}
}

Capabilities -> On Background Modes -> Select External accessory communication & Uses Bluetooth LE accessories

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")
}
}
}

Related

How to stream audio and send data over Bluetooth in Swift?

I'm developing and app for iOS which needs to connect to a Bluetooth Device. I've been able to find and connect to devices near me using CoreBluetooth and GATT profile. The writing and reading of characteristics worked as well. These values will eventually be written when a slider is moved. However, now I also need to be able to stream audio to the Bluetooth Device.
When I looked around for streaming audio over bluetooth I noticed this could be done with AVFoundation by allowing output over bluetooth. However, then the device has to use A2DP profile. But this causes the app to be unable to read/write characteristics?
I got this piece of code to read, notify and write to specific characteristics.
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")
if characteristic.uuid == batteryLevelCharacteristicCBUUID { peripheral.readValue(for: characteristic) }
}
if characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
if characteristic.uuid == batteryLevelCharacteristicCBUUID {
peripheral.setNotifyValue(true, for: characteristic)
}
}
if characteristic.properties.contains(.write) {
print("\(characteristic.uuid): properties contains .write")
if characteristic.uuid == writeTestCharacteristicCBUUID {
print("Writing characteristic found! Trying to write now.")
let string = "Hello World!"
let data = string.data(using: .utf8)!
peripheral.writeValue(data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
}
This question seems similar but it is mentioned that it's not possible. Is possible send hexadecimal data to A2DP Bluetooth Device in swift?
However, I was thinking. If you look at a headset for example when you press buttons there has to be some data transfer. Can this somehow be used for what I want to do? But I can't seem to find any information about these signals or how to use those in my code.
So what I want is to be able to stream the audio and also send small values like percentages.
If you know any information that might help me please let me know. Thanks in advance!

sending data to bluetooth module to be read by arduino rom iOS swift 3

If i want to send data to a bluetooth module, which is connected to the Arduino, what code lines are specifically the ones I need to take notice of.
I want to send something like, the number '75' to the bluetooth module and the Arduino will read it
thanks
Bluetooth LE is very long and cumbersome with many delegates in between. The minimum path to write your data is:
Make sure you have bluetooth permission: CBCentralManagerDelegate.centralManagerDidUpdateStateand if so start scanning with scanForPeripherals
CBCentralManagerDelegate.didDiscover If this is the peripheral you want then set yourself as its delegate
CBPeripheralDelegate.peripheral:didDiscoverServices: If this is the service you want then stop scanning and discoverCharacteristics:for: service
CBPeripheralDelegate.peripheral:didDiscoverCharacteristicsFor: service If a characteristic in the array of characteristics is the one you want then:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.uuid == CBUUID(string: characteristicIdentifier) {
let value: UInt8 = 75
let data = Data(bytes: [value])
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
}
}

Cannot Subscribe to Notify Characteristic in iOS CoreBluetooth

I have successfully connected to BLE devices using the same code, but this device is not letting me subscribe. It's frustrating because I cannot figure out how to get a meaningful error message.
There is a Read/Notify/Indicate characteristic that I am attempting to listen to. The code I send is:
peripheral.setNotifyValue(true, for: characteristic)
my listener is:
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if ((error) != nil) {
NSLog("Error changing notification state: " + error!.localizedDescription)
}
if characteristic.isNotifying {
NSLog("Notification Updated")
}
}
The error I get is:
Error changing notification state: Unknown ATT error.
I have downloaded another app that interfaces with this device successfully, so I know that the hardware is functioning properly.
I don't know if there is some sort of encryption or other setting that needs to be sent to allow communication with this device. Any advice would be appreciated.

Scanning for Peripherals offering specific services, But doesn't find Services on discovered peripheral

I'm experimenting with the CoreBluetooth Framework for iOS and I'm facing an issue that I find very Strange.
Once I have a Connected Peripheral, I want to discover it's services - but it fails. Despite searching for and finding Peripherals which offer a specific service. See the code below:
func centralManagerDidUpdateState(central: CBCentralManager) {
if(central.state != .PoweredOn) {
print("Returning due to wrong state: ", central.state)
return;
}
print("Starting peripheral scan")
manager.scanForPeripheralsWithServices(serviceUUID, options: nil)
}
Peripheral Scanning is started and looking for specified serviceUUIDs. In this case: serviceUUIDs = [CBUUID(string: "13333333-3333-3333-3333-333333333337")]. If I change the String, No service is discovered at all (as expected).
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered peripheral: ", peripheral.name)
if(peripheral.name == "PizzaSquat") {
print("Advertising data: ", advertisementData)
print("RSSI: ", RSSI)
activePeripheral = peripheral
print("Connecting to peripheral")
manager.connectPeripheral(peripheral, options: nil)
}
}
On a Linux computer, I have a Bleno instance running which is displaying as a Peripheral with the name "PizzaSquat" (And obviously offers a Service, since it's discovered at all. So when this Peripheral is discovered, I connect to it.
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("Connected to Peripheral: ", peripheral.name)
if(peripheral.services == nil) {
print("Starting service discovery")
peripheral.delegate = self
peripheral.discoverServices(serviceUUIDs)
} else {
print("Already has services: ")
for service: CBService in peripheral.services! {
print(service)
}
}
}
Once connected (to "PizzaSquat"), I check if it already has services (It doesn't) and start Service discovery. Everything is correct up to this point - But no Services are ever discovered. I expect the below function to be called:
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Discovered new Service")
for service: CBService in peripheral.services! {
print(service)
}
}
with at least one service, Since the Peripheral shouldn't be discovered at all if it didn't have the specified Service!
I have also tried to call peripheral.discoverServices(nil) to discovery any Service, but still no response.
Could someone help me find the problem?
When you scan for CBPeripheral objects, the peripheral service UUID is obtained from the advertisement packet. The peripheral is basically saying, "Oh yeah, I have this service UUID!" iOS picks that up and passes the CBPeripheral to you.
But the peripheral might not be synced with what is in the advertisement packet. It's a non-binding agreement that the advertisement packet matches what's actually in the peripheral's service/characteristic hierarchy.
So what this means is that it's possible for a peripheral to advertise one service UUID, but actually contain another (or none).
Check out LightBlue on iOS or nRF Control Panel on Android to see if the service/characteristic hierarchy matches what you think it does. In LightBlue you should be able to view the advertisement data and compare it to the services on the peripheral you're connected to.

How to get the characteristic(s) of HM-10 Bluetooth LE in iOS

I am trying to send some data from an iOS device to the HM-10 Bluetooth LE Module connected to an arduino. Problem is after connecting to the module discoverServices doesn't return a characteristic for the service.
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
if(error != nil) {
print(error?.description)
}
for service in peripheral.services! {
let thisService = service as CBService
print("Service: \(thisService.description)")
print("Characteristic: \(thisService.characteristics)")
}
}
This outputs:
Service: <CBService: 0x137e84ea0, isPrimary = YES, UUID = FFE0>
Characteristic: nil
I am a beginner with this arduino stuff as well as iOS. So any suggestions would be welcome. Maybe there is a way to write to the bluetooth module without knowing the characteristic... I have no idea.
I finally managed to get the answer. Because of my rudimentary understanding of the CoreBluetooth Framework I forgot to call discoverCharacteristics in didDiscoverServices. Well, I am really learning by doing. (I somehow thought discoverServices would call discoverCharacteristics itself.)

Resources