didDiscoverPeripheral called very slowly - ios

I have a question regarding CoreBluetooth programming in Mac OS X. I have some BLE beacons here that I want to detect. When I execute similar code on my iPhone, it seems to work just fine, but when I try this on my Mac, it seems to only notify of beacons in the neighbourhood every two seconds or so (or even more, but at least 2 seconds). The beacons are beaconing every 500ms, so I would expect to be notified every 500ms.
Is there someone out there who knows how I can tell Mac OS to be a little stricter with reporting the beacons?
I must say I'm quite new to Swift, so all comments on style will also be appreciated.
import Foundation
import CoreBluetooth
class CentralManagerDelegate : NSObject, CBCentralManagerDelegate {
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == .PoweredOn {
central.scanForPeripheralsWithServices(nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : "YES"])
print("Started scanning")
}
else {
central.stopScan()
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Beacon found with RSSI: " + RSSI.stringValue);
}
}
var _centralManagerDelegate = CentralManagerDelegate()
var _centralManager = CBCentralManager(delegate: _centralManagerDelegate, queue: dispatch_get_main_queue())
while true {
dispatch_main()
}

Related

Connecting IOS app to Raspberry Pi 4 over bluetooth

I created a new app with swiftUI and trying to use coreBluetooth library to connect Raspberry Pi 4. I searched all the google and I stuck at connecting Raspberry Pi 4. I can find all the devices (peripherals) but I'm not able to connect them.
Here is the code;
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
var myCentral: CBCentralManager!
#Published var isSwitchedOn = false
#Published var peripherals = [Peripheral]()
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
}
else {
isSwitchedOn = false
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
var peripheralName: String!
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
else {
peripheralName = "Unknown"
}
if peripheral.identifier.uuidString == "6D915395-3E79-0072-22A3-009DDC331F7C" {
myCentral.stopScan()
myCentral.connect(peripheral, options: nil)
print("okokokokok")
}
let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
print(newPeripheral)
peripherals.append(newPeripheral)
}
func centralManager(central: CBCentralManager!,didConnectPeripheral peripheral: CBPeripheral!)
{
peripheral.discoverServices(nil)
print("Connected")
print(peripheral.name!)
print(peripheral.services!)
}
}
Deleted some of code parts bc of stackoverfow
Your problem for the connection was not holding a reference to the CBPeripheral object to which you want to connect. So you've solved it by referencing to it in your BLEManager class.
Now how can you send and get data to / from your Raspberry? Well you must implement the CBPeripheralDelegate in your BLEManager class.
After the central manager finishes discovering the available services it will call didDiscoverServices method of CBPeripheralDelegate; where you need to invoke discoverCharacteristics for the discovered service.
Again, when characteristics has been discovered for the given service with the discoverCharacteristics(_:for:) method which you invoked in the didDiscoverServices callback, you can set the notify value if you want to know about characteristic updates.
After the characteristics has been discovered you can send values by using writeValue method of CBPeripheral instance to which you've referenced it when connected.
When you send data from the Raspberry for a certain charachteristic, the didUpdateValueFor method of CBPeripheralDelegate will get called. In this callback; you can obtain the received data from the CBCharacteristic.value property. Note that the value property is optional, hence you need to handle it with care.

Can't connect to BLE Peripheral in some iOS devices

BLE works fine on 7 Plus (iOS 14.4.2) and 6 (iOS 12). But on XR (14.4.2) and 11 connection stuck after centralManager.connect(peripheral, options: nil) (infinite connection)
The peripheral is in connection mode because other smartphones cannot detect it.
At first I thought that the problem was with the radio module of the peripheral device itself (NRF52), but the problem also occurred with the debug board.
Rebooting the smartphone did not help.
It's funny that the app works fine on a MacBook with an M1 chip
Part of code:
// Peripheral model
init(withPeripheral peripheral: CBPeripheral, advertisementData advertisementDictionary: [String : Any], andRSSI currentRSSI: NSNumber, using manager: CBCentralManager) {
centralManager = manager
basePeripheral = peripheral
RSSI = currentRSSI
super.init()
advertisedName = parseAdvertisementData(advertisementDictionary)
basePeripheral.delegate = self
}
public func connect() {
centralManager.delegate = self
centralManager.connect(basePeripheral, options: nil)
print("Connecting to \(advertisedName ?? "device")...")
// logs stops here
}
public func disconnect() {
centralManager.cancelPeripheralConnection(basePeripheral)
print("Cancelling connection with \(advertisedName ?? "device")...")
// triggers on VC dismiss
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state != .poweredOn {
print("Central Manager stated changed to \(central.state)")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
if peripheral == basePeripheral {
print("Connected to \(advertisedName ?? "device")")
delegate?.peripheralDidConnect()
discoverPrimaryServices()
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if peripheral == basePeripheral {
print("Disconnected from \(advertisedName ?? "device")")
delegate?.peripheralDidDisconnect()
}
}
"The peripheral is in connection mode because other smartphones cannot detect it." Did you mean that other smartphones can detect it?
Given the phones you've listed as working and not working, I would expect that your board is having trouble with Bluetooth 5 (which was first supported on the iPhone 8). The NRF52 supports BT5 (it supports 5.2), but if you've written your own firmware you may have broken the support. I'd start by making sure you're running the most vanilla code you can from Nordic.

Send informations with Bluetooth in Swift IOS language

I'm actually in informatics school and I have some trouble with the connection between my own self signed app coded in Swift(Xcode 11.3 and for IOS 13.2 device) and a BLE module SH-HC-08 plugged on an Arduino. (UUID : FE60E626-C58C-69F5-324F-43C7C409A93D)
I'm totally new into swift language and self-learning this way of thinking in this language.
I'm using CoreBluetooth to detect if the Bluetooth on my iPhone is On or off.
I can't actually check if my peripheral is detected, and can't send any information by my app.
(don't understand how to configure the methods in CoreBluetooth)
I downloaded apps caled LightBlue and BluetoothLE on app store. The first one give me fews information about my Bluetooth device and the second one allow me to sends Ascii Strings to my Arduino by my Bluetooth module.
LightBlue information 1:
LightBlue information 2:
BluetoothLE app:
As I said before, i already :
Import CoreBluetooth
Add CBCentralManagerDelegate & CBPeripheralDelegate to my class ViewController
instantiate CBCentralManager and CBPeripheral
setup the centralManagerDidUpdateState function
CoreBluetooth code:
import UIKit
import CoreBluetooth
var texteEnvoyer:String?
var test = false
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager:CBCentralManager!
var module : CBPeripheral!
#IBOutlet weak var labelConnection: UILabel!
#IBOutlet weak var switchConnection: UISwitch!
#IBOutlet weak var labelTextField: UITextField!
#IBOutlet weak var labelTest: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
refresh()
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
if(central.state == .poweredOff)
{
let alertController = UIAlertController(title: "Bluetooth", message: "Le bluetooth est déconnecté, veuillez l'activer et vous connecter au module.", preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
present(alertController, animated: true, completion: nil)
test = false
}
if(central.state == .poweredOn)
{
test = true
}
refresh()
}
I tried to play with the others methods like didDiscoverPeripheral or didConnected but I don't understand what i do and can't find any help to send information (preferred in String) by a self made application with bluetooth.
I tried that to discover my module but I don't have any "print" in my logs and don't know if my request was done or not... if I do it well or not too.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Name: \(peripheral.name)")
self.module = peripheral
self.module.delegate = self
if(peripheral.name == "SG-HC-08"){
manager.stopScan()
manager.connect(self.module, options: nil)
print(peripheral.name)
}
}
Someone have any experience with that or any help to give it will help me so much.
Thanks
Jikko,
I would be careful with using Global Variables.
Your global variables are the two declared outside of your class scope.
var texteEnvoyer:String?
var test = false
These can sometimes cause issues with your program such as memory leaks and retention. Im not saying that this is the case for you. But if you continue to develop and publish applications this may be something to rethink. Im sure for your purposes now this is totally fine.
To your point of "nothing in the console form the peripheral" we can try adding in some print statements to help us debug what is actually happening.
For starters:
func centralManagerDidUpdateState(_ central: CBCentralManager){
//.....
print("ENTERED THE FUNCTION")
if(peripheral.name == "SG-HC-08"){
manager.stopScan()
manager.connect(self.module, options: nil)
print("REACHED HERE")
}
}
IF in the console you see "REACHED HERE" then we know the code was called and executed. However this does not mean a successful connection.
If we do not see the print statement "ENTERED THE FUNCTION" , or "REACHED HERE" then that means there is a basic error. The function was never called or executed.
I would first create a function like so:
func startScan(){
if let central = manager {
central.scanForPeripheralsWithServices(nil, options: nil)
print("started Scanning")
}
}
And then in viewDidLoad()
call the start scan
override func viewDidLoad(){
super.viewDidLoad(animated)
startScan()
}
And then once you have discovered a peripheral use the didDiscoverPeripheral method (which will be called first)
func centralManager(_ central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
print("DISCOVERED PERIPHERAL", peripheral)
// manager.connectPeripheral(peripheral, options: nil)
}
You can then connect to it once discovered.
Hope this helps
EDIT ----
try adding in these two lines to your function.
peripheral.delegate = self
peripheral.discoverServices(nil)
like so
func centralManager(_ central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
print("DISCOVERED PERIPHERAL", peripheral)
// manager.connectPeripheral(peripheral, options: nil)
}
I forgot to add in the underscore "_" to the function parameters
func centralManager(_ central: ....) {}
--------------------^
Please find some informations on how delegation (1) and BLE (2) work.
1) When you instantiate your CBCentralObject manager, you pass your view controller (self) as the delegate of the central manager which means that some Bluetooth events handled by your central manager will be forwarded to your view controller.
In your case, it seems that you didn't implement the centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) method which is called every time a new peripheral is discovered.
Once you get the discovered peripheral, you can continue the process with the other delegates methods
2) If you want to communicate with a BLE device you have several steps to follow:
discover the device
connect to it
discover services and
characteristics
Then you can write data on any characteristic you want, or subscribe to listen to data emitted by your BLE device.

iOS Bluetooth delegate connect function not being called

I'm trying to listen to all Bluetooth connect/disconnect events. Even though the delegate's centralManagerDidUpdateState function is called, nothing happens when I connect or disconnect Bluetooth devices.
I'm confused as to what's going wrong. I initialize the Central Manager/delegate like this:
var btDelegate: CBCentralManagerDelegate = BluetoothDelegate()
var btManager = CBCentralManager(delegate: btDelegate, queue: nil)
BluetoothDelegate is defined like so:
import Foundation
import CoreBluetooth
class BluetoothDelegate : NSObject, CBCentralManagerDelegate {
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
println("connect") //this line is not called
}
func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
println("disconnect") //this line is not called
}
func centralManagerDidUpdateState(central: CBCentralManager!) {
println("state update") //this line is called
}
}
Note: I can continuously receive more state update events such as when I toggle Bluetooth, even though connect and disconnect are not called.
From your code it looks like you haven't started scanning for peripherals. Once you have confirmed that your central is in powered on state from centralManagerDidUpdateState method you should start scanning for your peripherals.
(The bluetooth devices you connected from bluetooth settings panel are irrelevant. You can't have access to them. (as far as I know) Incase you want to scan and find out your device on your own below code will help)
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
break
case CBCentralManagerState.PoweredOn:
startScan() // start scanning once the bluetooth is On
break
default:
break
}
}
And your startScan method will be (You can provide service UUID, use nil if you want all )
func startScan(){
if let central = btManager {
central.scanForPeripheralsWithServices(nil, options: nil)
println("started Scanning")
}
}
After that whenever you discover a peripheral didDiscoverPeripheral method will be called first.
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println(peripheral)
// btManager.connectPeripheral(peripheral, options: nil)
}
From there you collect the peripheral and then invoke connectPeripheral method of CBCentralManager. If the connection is successful didConnectPeripheral method will be called.
You should go through this documentation for complete details

Which method in swift can be used to find the distance between the BLE devices?

i have implemented two methods for checking availability of the Bluetooth feature and to find the discoverable device around us. Now i would like to find the distance of the peripheral around us., for these feature which method can be used and what codes to be implemented. expecting a solution with an example code
func centralManagerDidUpdateState(central: CBCentralManager!) {
println("\(__LINE__) \(__FUNCTION__)")
println("checking state")
if central.state != .PoweredOn {
// In a real app, you'd deal with all the states correctly
return
}
central.scanForPeripheralsWithServices(nil,options:nil)
}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!)
{
var localname: NSString = advertisementData[CBAdvertisementDataLocalNameKey]! as NSString
println("Discovered: \(peripheral.name)")
peri = ["\(peripheral.name)"]
if localname != " "
{
//centralManager!.stopScan()
//self.peripheral = peripheral
peripheral.delegate = self
centralManager!.connectPeripheral(peripheral, options: nil)
}
}
-thank you
You can get an approximate distance by read RSSI method. It will not return distance in any centimeter or metric values. You should convert it to a understandable format

Resources