I have an app that works (so far so good) in connecting my iOS device to a bluetooth arduino, as of now this has been mostly for practice, but now I received the real thing that I'm supposed to connect to.
The problem is that I can't find it! (when I search for it).
If I do scanwithservices: nil, I can see the device and connect to it. However if i scanwithservices: [device'sCBUUID] then I don't get anything.
I double/triple/quatrupled checked the CBUUID using other apps, using my own app and looking at the device documentation, however no matter what I can't find it.
It has a custom CBUUID, from what I've read that's not standard, the CBUUID is:
BLETPodService = CBUUID(string: "4D494B45-414c-5741-5953-524F434B5321")
Searching for this yields nothing, however if I scan for nil I find it and if i check it's characteristics using the Bluefruit app (from Adafruit) I can see it's services and characteristics ID and they match that string I posted in here!
I told a friend and he said it's a BLE bug thats been there for ages (regarding custom CBUUIDs), is this true? is there really no fix for this?
EDIT adding the full scanning code just FYI:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var statusMessage = ""
switch (central.state)
{
case .unsupported:
statusMessage = "Bluetooth Low Energy is not supported!"
displayStatusAlert(localmsg: statusMessage)
print(statusMessage)
case .unauthorized:
statusMessage = "Bluetooth Low Energy is not authorized!"
displayStatusAlert(localmsg: statusMessage)
print(statusMessage)
case .unknown:
statusMessage = "Bluetooth Low Energy is unknown!"
displayStatusAlert(localmsg: statusMessage)
print(statusMessage)
case .poweredOff:
statusMessage = "Bluetooth Low Energy is powered off!"
displayStatusAlert(localmsg: statusMessage)
print(statusMessage)
case .resetting:
statusMessage = "Bluetooth Low Energy is resetting!"
displayStatusAlert(localmsg: statusMessage)
print(statusMessage)
case .poweredOn:
statusMessage = "BLE is ready!" //If BLE is ready then start scanning right away!
peripheralsFoundNames.removeAll()
peripheralsFoundCB.removeAll()
peripheralsFoundRSSIs.removeAll()
peripheralsFoundData.removeAll() //Remove previous data from previous scans
central.scanForPeripherals(withServices: nil, options: nil)
}
}
//What happens when you discover a peripheral
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
//What to do when it discovers a peripheral, add it to the array list
print("Peripheral found: " + (peripheral.name ?? "Unknown Name"))
peripheralsFoundNames.append((peripheral.name ?? "Unknown Name"))
peripheralsFoundData.append((advertisementData.description ))
peripheralsFoundCB.append(peripheral)
peripheralsFoundRSSIs.append(RSSI)
}
I happen to be working on both BLE firmware and iOS App at the same time.
I had the same issue that the filter not working.
The reason that iOS couldn't find the device with the UUID is that the device doesn't include the UUID when it is advertising.
I had to change the device firmware to add service UUID in the advertising data packet. With device advertising the UUID, UUID filter worked as expected.
I also had this problem, but I've found some tricky way.
You can try to match UUIDs (string value) directly in didDiscoverPeripheral func:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
if let services = peripheral.services {
for service in services {
if service.uuid.uuidString.lowercased() == "4D494B45-414c-5741-5953-524F434B5321".lowercased() {
if !peripheralsFoundCB.contains(peripheral) {
peripheralsFoundCB.append(peripheral)
}
}
}
}
}
If this way doesn't work for you I have one more:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let services = peripheral.services {
for service in services {
if service.uuid.uuidString.lowercased() == "4D494B45-414c-5741-5953-524F434B5321".lowercased() {
if !peripheralsFoundCB.contains(peripheral) {
peripheralsFoundCB.append(periphera
}
}
}
}
}
Of course you should use
scanwithservices: nil
Swift 3:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.selectPeripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
let services = peripheral.services as [CBService]!{
for service in services{
if service.uuid.uuidString == "4D494B45-414c-5741-5953-524F434B5321"{
print("service found")
}
}
}
}
I am having exactly the same issue. Currently I might do the same solution suggested by the folks here! But I think its a problem from the hardware side.
I believe what is happening that your EE is not broadcasting the service UUID! What I mean by that if you printed the advertisedInfo you will not see the the following key-value pair in the info:
"kCBAdvDataServiceUUIDs": <__NSArrayM 0x14e6f3b0>("Your CBUUID")
You might see the name key-value and other stuff but definitely not the one I specified.
For that reason if he can do his part by broadcasting the service UUID's then you will be able to scan for that using the following method.
scanForPeripherals(withServices: ["Your CBUUID"], options: nil)
And you will see it :)
Related
I‘m developing a swift iOS app in which I need BLE scanning between multiple devices. Suppose I have 3 devices (A, B, and C). If all three devices are in the background, each device will only scan one other device and will stop scanning after. For example, A will only scan B, B will only scan A, and C will only scan A. After this initial background scan, no other devices will be scanned by any of the devices. However, I need A to scan both B and C, B to scan both A and C, and C to scan both A and B. I'm not sure how to approach this problem. Is there a way in which I can have an iOS device scan multiple devices in the background?
For Bluetooth peripheral and central, I used these codes from Apple's documentation:
https://developer.apple.com/documentation/corebluetooth/transferring_data_between_bluetooth_low_energy_devices
Also here is my code for setting up the central manager.
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
//this makes it so it connects and ensures it only works if both devices have ther app
//start sending message code
os_log("Discovered %s at %d", String(describing: peripheral.name), RSSI.intValue)
peripherals.append(peripheral)
if myPeripheral != peripheral {
// Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it.
myPeripheral = peripheral
// And finally, connect to the peripheral.
print("Connecting to perhiperal %#", peripheral)
myCentralManager.connect(peripheral, options: nil)
}
//end of messaging code
print("\nName : \(peripheral.name ?? "(No name)")")
print("RSSI : \(RSSI)")
currentRssi = Int(truncating: RSSI)
for ad in advertisementData {
print("AD Data: \(ad)")
}
}
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
print("will restore state central scene")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
os_log("Perhiperal Disconnected")
myPeripheral = nil
// We're disconnected, so start scanning again
if connectionIterationsComplete < defaultIterations {
retrievePeripheral()
} else {
print("Connection iterations completed")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Peripheral Connected")
// Stop scanning
myCentralManager.stopScan()
print("Scanning stopped")
// set iteration info
connectionIterationsComplete += 1
writeIterationsComplete = 0
// Clear the data that we may already have
data.removeAll(keepingCapacity: false)
// Make sure we get the discovery callbacks
peripheral.delegate = self
// Search only for services that match our UUID
peripheral.discoverServices([ServiceID])
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
os_log("Perhiperal Disconnected")
myPeripheral = nil
// We're disconnected, so start scanning again
if connectionIterationsComplete < defaultIterations {
retrievePeripheral()
} else {
print("Connection iterations completed")
}
}
private func retrievePeripheral() {
let connectedPeripherals: [CBPeripheral] = (myCentralManager.retrieveConnectedPeripherals(withServices: [CovtraceID]))
print("Found connected Peripherals with transfer service: %#", connectedPeripherals)
if let connectedPeripheral = connectedPeripherals.last {
print("Connecting to peripheral %#", connectedPeripheral)
self.myPeripheral = connectedPeripheral
myCentralManager.connect(connectedPeripheral, options: nil)
} else {
// We were not connected to our counterpart, so start scanning
myCentralManager.scanForPeripherals(withServices: [CovtraceID],
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
}
}
I'm working on a project with corebluetooth framework.When my central manager starts scanning for peripherals it connects to a single peripheral after sometime maybe one minute it starts connecting to multiple peripherals with same UUID.
Is there a way to tell the device to stick to one peripheral which is connected initially?
Variables
public var centralManager: CBCentralManager? = nil
public let baseLayerServices = [SkiinUUID(string: SkiinPeripheral.baseLayerService)]
#Published public var peripherals: AllPeripherals = AllPeripherals()
Did update state
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("state: \(self.getStateString())")
if central.state == .poweredOn {
self.showStateAlert = false
if let connectedPeripherals = self.centralManager?.retrieveConnectedPeripherals(withServices: self.baseLayerServices), connectedPeripherals.count > 0 {
print("Already connected: \(connectedPeripherals.map{$0.name}), self.peripherals: \(self.peripherals)")
self.centralManager?.stopScan()
}
else {
print("scanForPeripherals")
self.centralManager?.scanForPeripherals(withServices: self.baseLayerServices, options: nil)
}
}
else {
self.showStateAlert = true
}
}
didDiscover Peripheral Code
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard peripheral.state != .connected else {
return
}
print("didDiscover: \(peripheral.name ?? peripheral.identifier.uuidString)")
self.peripherals.baseLayer.top.add(cbPeripheral: peripheral)
self.centralManager?.connect(peripheral, options: nil)
self.observe(peripheral: self.peripherals.baseLayer.top)
}
When this code discovers a peripheral, it tries to connect to it. I don't see anywhere that it stops scanning, and it doesn't avoid connecting just because it's connected.
If you don't want to keep scanning after you discover something, call stopScanning(). If you don't want to connect when you're already connected (or connecting), then don't call connect() in those cases.
I am attempting to detect battery levels for connected BT devices on macOS. While I can get CBCentralManager to detect SOME nearby devices, all of the devices it detects are name=Null, even though there are numerous devices that should be detected and should have names (e.g., AirPods, magic trackpad, magic keyboard, etc).
My BT Manager class is:
import Cocoa
import CoreBluetooth
class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager = CBCentralManager()
var peripheralManager = CBPeripheralManager()
var discoveredPeripherals:[CBPeripheral]?
var selectedPeripheral:CBPeripheral?
let queue = DispatchQueue.main
let batteryLevelService = [CBUUID(string: "0x2A19")]
override init() {
self.centralManager = CBCentralManager(delegate: nil, queue: queue)
super.init()
centralManager.delegate = self
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOff:
print("BLE is powered off")
case .poweredOn:
print("BLE is powered on")
centralManager.scanForPeripherals(withServices: nil)
case .resetting:
print("BLE is resetting")
case .unauthorized:
print("BLE is not authorized")
case .unknown:
print("BLE state is unknown")
case .unsupported:
print("BLE is unsupported")
default:
print("Unable to determine BLE state")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// Code Here
}
}
I assign that class to an object in my main ViewController class.
I would normally have expected the console to show nearby discoverable devices as well as already-connected devices. However, all I get is:
<CBPeripheral: 0x600003508b00, identifier = C9A74282-A40C-46C1-9C2F-9646D2BCE8B5, name = (null), state = disconnected>
<CBPeripheral: 0x600003500840, identifier = 1EF2CD7F-8FAA-4510-A7DB-B4E060B2378B, name = (null), state = disconnected>
<CBPeripheral: 0x600003500a50, identifier = 1EF2CD7F-8FAA-4510-A7DB-B4E060B2378B, name = (null), state = disconnected>
<CBPeripheral: 0x600003508b00, identifier = BA372C81-993F-436D-994E-B31BDAB47BC7, name = (null), state = disconnected>
Not all devices advertise their name. There is very little space in an advertising packet (~30 bytes), and the name may not fit if there are other more-important things to advertise. A single custom service can use up 16 bytes. You may not be able to determine the name without connecting. Even then, the device may not have a BLE name.
I would normally have expected the console to show nearby discoverable devices as well as already-connected devices.
I'm not sure why you expect that. scanForPeripherals returns information about devices that are advertising. It is very common for a device to stop advertising when it's connected to (it's common for devices to only support a single connection). If you want to see connected devices, call retrieveConnectedPeripherals.
I am developing iOS app in that I'm using core BLE services and characteristics for read and write data.
Here I'm using service having unique CBUUID and this service having two default characteristics, I can able to read and write that characterisitcs but when we add another charecteristics in that service I'm unable to read that newly added characteristics.
Please check code as follows
For scan peripheral device
var centralManager: CBCentralManager!
centralManager = CBCentralManager(delegate: self, queue: nil)
After discover peripheral connect to particular device using following delegate methods
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any], rssi RSSI: NSNumber)
{
if(peripheral.name == "myBLE")
{
centralManager.stopScan()
centralManager.connect(myPeripheral)
}
}
After connecting bluetooth device, discover services
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
{
//centralManager.stopScan()
peripheral.discoverServices(nil) // For all services
}
We get services in following delegate methods
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
{
guard let services = peripheral.services else { return }
for service in services
{
peripheral.discoverCharacteristics(nil, for: service)
}
}
We get charecteristics in following delegate methods
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics
{
print(characteristic)
//Here we get only default characteristics, but not getting newly added characteristics
if characteristic.properties.contains(.read)
{
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify)
{
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
Here I am unable to read newly added characteristics, is there any solution please let me know.
Can anyone help me with the UUID for car handsfree peripheral connections, so I can call scanForPeripherals withServices: [SOME UUID] instead of nil, and only return handsfree. Is there a UUID or CBUUID for this? I hope this can be done at a service level and does not need to look at characteristics. Thanks in advance if you can help me.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
self.centralManager.scanForPeripherals(withServices: [UUID HERE] , options: nil)
} else {
print("central not powered on")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("peripheral discovered")
peripheralLabel.text = peripheral.name
}
Handsfree devices implement the Bluetooth Hands Free Protocol (HFP). This is not a Bluetooth Low Energy GATT service and so does not have a service UUID. They cannot be discovered using Core Bluetooth.