how to prevent duplicate peripherals in corebletooth? - swift4

I am new to Corebluetooth. I want to prevent duplicate peripherals when scanning. Can anyone help me?
And one more question, how to connect peripheral with in range ?
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
{
if (peripheral.name != nil) && (peripheral.name == "EXP") ||
(peripheral.name == "EXP")
{
let key = peripheral.identifier.uuidString
let data = advertisementData.description
if let previous = datas[key]
{
if (previous != data)
{
print("Different \(String(describing: peripheral.name)): \ . (data)")
}
} else
{
print("\(String(describing: peripheral.name)): \(data)");
datas[key] = data
}
peripherals.append(peripheral)
lblDeviceCount.isHidden = false
lblDeviceCount.text = "\(peripherals.count) Devices Found"
tblPeriPheral.reloadData()
}
}

You can use a loop to prevent duplicate peripherals. If new peripheral has an identifier equal to an existing peripheral in peripherals (the variable you are using) then don't append it to peripherals.
for existing in peripherals {
if existing.identifier == peripheral.identifier { return }
}
For the second part, you should read about RSSI, it will help you know whether you're closer or far from the device. Depending on the range you can make decision whether to connect to it or not.

Related

How to scan multiple BLE devices on iOS

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

Background Scan and Advertising between two ıos device [duplicate]

I have an app which is working in background. I use CBPeripheralManager to Advertising and CBCentralManager to scan. I use two ıos (IOS 11.3 and IOS 13.4.1) device. First one is advertising foreground and background. Second one is scan foreground and background. I can scan;
App in the background, phone is unlocked - Works perfect
App in background, phone is locked, screen is lighted - Works perfect
App in background, phone locked, screen is off - Doesn't work!
/* I check it Advertising app which run background show in Android Device */
What is the problem. Please let me know. How can solve this problem? I want to scan both in background. My code is given bellow;
let scanOptions = [
CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: true)
]
let services = [CBUUID(string: "e2c56db5-dffb-48d2-b060-d0f5a71096e0")]
let advertisingData = [
CBAdvertisementDataLocalNameKey: "xxx",
CBAdvertisementDataServiceUUIDsKey:[CBUUID(string: "e2c56db5-dffb-48d2-b060-d0f5a71096e0")]
] as [String : Any]
func initLocal() {
peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
cbCentralManager = CBCentralManager(delegate: self, queue: nil,options: nil)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
peripheralManager.startAdvertising(advertisingData)
}
else if peripheral.state == .poweredOff {
peripheralManager.stopAdvertising()
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn{
central.scanForPeripherals(withServices: services,options:scanOptions)
print("scanning...")
}
else {
print("Bluetooth is not active")
}
}
func centralManager(_ central: CBCentralManager,didDiscover peripheral: CBPeripheral,advertisementData: [String : Any],
rssi RSSI: NSNumber)
{
print("RSSI : \(RSSI)")
}
This is my info.plist;
You seem to be expecting duplicates since you've set CBCentralManagerScanOptionAllowDuplicatesKey. That key is ignored in the background. If you're expecting to see the same device more than once via advertising, that's impossible. Discovering new devices that you haven't seen before should work, however. Are you having trouble with that? (You should explain the details of exactly how you're testing this, what precise behaviors you see, and what you expect to see. Bluetooth is very subtle. The details matter quite a lot, and "not working" is far too vague.)

My Corebluetooth framework is connecting to multiple device

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.

Cant search for custom CBUUID

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 :)

Bluetooth UUID for car handsfree devices

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.