I have BLE source code that displays several warnings and I am new to
BLE. Please see the code below. I have tried replacing with readRSSI but tells me I can’t compare an Int with Void. How do I get an Int value for readRSSI? Or how should I change the code?
- (void)peripheralDidUpdateRSSI:(CBPeripheral * _Nonnull)peripheral error:(NSError * _Nullable)error
{
if (!isConnected)
return;
if (rssi != peripheral.RSSI.intValue)
{
rssi = peripheral.RSSI.intValue;
[[self delegate] bleDidUpdateRSSI:activePeripheral.RSSI];
}
}
*rssi is a static int.
*isConnected is a boolean.
Edit: The problem is that RSSI is deprecated since iOS 8.0.
There are two ways of obtaining the CBPeripheral's RSSI. The first is when the peripheral is discovered. You will get a call to the CBCentralManagerDelegate method
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
let rssi = intValue
...
}
If you are running in the foreground then you can supply a value of true to for the key CBCentralManagerScanOptionAllowDuplicatesKey in the scanning options to get repeated calls to didDiscover. This doesn't work in the background.
If you are connected to a peripheral then you can periodically call peripheral.readRSSI(). This will result in a callback to the didReadRSSI CBPeripheralDelegate method:
optional func peripheral(_ peripheral: CBPeripheral,
didReadRSSI RSSI: NSNumber,
error: Error?) {
let rssi = RSSI.intValue
...
}
swift 3
#IBAction func btnGetRSSI(_ sender: UIButton){
self.selectedPeripehral.readRSSI()
}
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
var rssiInt: Int!
rssiInt = RSSI.intValue
}
Related
var peripherals = [CBPeripheral]()
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let _ = peripheral.name {
if !peripherals.contains(peripheral) {
peripherals.append(peripheral)
table.reloadData()
}
}
}
Using the delegate method above, called after centralManager.scanForPeripherals(withServices: nil), I am showing available peripherals(only those with name).
Since some of the available devices are also mine, I am sure that bluetooth is turned off on them.
So why does this method returns them as available? Are they cached somewhere or?
How Can I convert a CBUUID to UINT16?
I want to procure the device information in the advertisement data as UINT16.
I am getting the error
Could not cast value of type 'CBUUID' (0x1d2715e68) to 'NSNumber' (0x1d25c9db0).
2019-12-04 14:17:18.731697-0500 ProjectName[717:125723] Could not cast value of type 'CBUUID' (0x1d2715e68) to 'NSNumber' (0x1d25c9db0).
My Advertisement Data in terminal
["kCBAdvDataTxPowerLevel": 0,"kCBAdvDataIsConnectable": 1, "kCBAdvDataTimestamp": 597179838.727399, "kCBAdvDataServiceUUIDs": <__NSArrayM 0x281252e20>(
22043200-9530-4EA8-9D21-04146852E51F
)
, "kCBAdvDataServiceData": {
"Device Information" = {length = 6, bytes = 0x010000020004};
}, "kCBAdvDataLocalName": base0]
I have highlighted the information which I need in Bold.
I have the following code below.
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard peripheral.state != .connected else {
return
}
let advertisementDataService = advertisementData[CBAdvertisementDataServiceDataKey]
let deviceInformation = advertisementDataService as! Dictionary<UInt16,AnyObject>
}
Thank You!!!
As per [the documentation[(https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey), the dictionary associated with CBAdvertisementDataServiceDataKey is of type <CBUUID,Data>. Having retrieved the dictionary you can then subscript it using the appropriate CBUUID instance (0x180A is the Device Information service).
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard peripheral.state != .connected else {
return
}
if let deviceInformation = advertisementData[CBAdvertisementDataServiceDataKey] as? Dictionary<CBUUID,Data>,
let data = deviceInformation[CBUUID(string:"0x180A")] {
// Do something with the data
}
}
I found out that when an iPhone is updating a local characteristic value, and also listen to notification for that characteristic, he will get notified even the fact that he was the one that updated the value, so the delegate :
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
Will be called, even that I was the one who change the value, and not the remote server (ble device). When the remote side send data, I will also get this delegate. Is it the way it should be ? I don't remember so .
I found out the same behavior on other 3rd apps that scan for Bluetooth LE.
I also found out that for some reason my code is not always get the delegate , maybe I am doing things wrong here:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let foundName=peripheral.name
let deviceName = "Name"
if foundName?.range(of: deviceName) != nil
{
self.centralManager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
self.centralManager.connect(peripheral, options: nil)
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: "Detected")
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices( nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services!
{
let thisService = service as CBService
print(thisService.uuid.uuidString)
if thisService.uuid.uuidString == serviceUUID {
peripheral.discoverCharacteristics(nil, for: thisService)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for charateristic in service.characteristics!
{
let thisCharacteristic = charateristic as CBCharacteristic
// check for data characteristic
if thisCharacteristic.uuid.uuidString == characteristicUUID {
print("REGISTERED CHARACTERISTIC:",thisCharacteristic)
self.peripheral.setNotifyValue(true, for: thisCharacteristic)
self.characteristic=thisCharacteristic
isConnected=true
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: "Connected")
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid.uuidString == characteristicUUID {
if let str = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
{
print("BLE:DATAIN:",str )
NotificationCenter.default.post(name: Notification.Name(rawValue: "Bluetooth"), object: str)
}
}
}
func sendData(data:String)
{
if(peripheral != nil)
{
print("BLE:SENT")
var bytesData = [UInt8](data.utf8)
let writeData = NSData (bytes: &bytesData, length: bytesData.count)
peripheral.writeValue(writeData as Data, for: characteristic, type: CBCharacteristicWriteType.withoutResponse)
}
}
After a day investigation, both the hardware chip and the iOS, I realized that the notify update- will notify you when there is a new value.
So, whats a new value ?
Its when the previous value is different from the current value.
For some hardware configurations, the previous value is kept on cache even after reset, and iOS will see it as a new value, even though the hardware was not updated it at all (!)
So when you register to notifications, iOS will check the initial value , and if it holds something other then zero/nil , a delegate will be called.
Its your job to clean the previous cached value in the chip in some way.
In general, I found out (maybe too late) that the best practice is to connect and stay connected as long as the app is running. This will eliminate all sorts of problems that happens when you connect/disconnect every time you need to send data.
Conclusion: connect once, when disconnected for any reason -reset your hardware automatically (using the host controller software)
I would like to discover BLE devices in my area and store their current RSSI value. The discovering works but I'm note sure, if my func didDiscoverPeripheral is really save... I think that I should wait for the didReadRSSI func before I leave the didDiscoverPeripheral. But how can I realize that in an easy way and is my opinion right?
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
CBperipheral = [peripheral]
peripheral.readRSSI()
}
func peripheral(peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: NSError?)
{
//Store the peripheral specific RSSI, Name and identifier
}
I would suggest something like this -
var peripherals = Set<CBPeripheral>
var peripheralRSSIs = Dictionary<CBPeripheral,NSNumber>()
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
self.peripherals.insert(peripheral)
self.peripheralRSSIs[peripheral]=RSSI
central.connectPeripheral(peripheral,options:nil) // Connect if you want to be able to retrieve additional RSSI values
}
func centralManager(_ central: CBCentralManager,
didConnectPeripheral peripheral: CBPeripheral)
{
peripheral.delegate=self
peripheral.readRSSI()
}
func peripheral(peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: NSError?)
{
if (error != nil) {
print(error)
} else {
self.peripheralRSSIs[peripheral]=RSSI
}
}
I solved the problem like this:
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
CBperipheral = [peripheral]
myStrings.append(StringPair("\(peripheral.name!)", "\(peripheral.name!)", "\(RSSI)"))
//Coose the right peripheral and connect!
}
I don't need very up-to-date values for the RSSI, only some guiding values. So I hope this code is conform with IOS 8 and later.
But it would be nice, if you could tell me the handling with the callback to my didReadRSSI for other problems like this.
btw. I have to write this: CBperipheral = [peripheral] to call my didConnectPeripheral after the call of CBmanager.connectPeripheral :)
I'm not sure why this code is failing to build and the error message seems quite cryptic.
Code:
var centralManager: CBCentralManager!;
var nrf8001Peripheral: CBPeripheral!;
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// initialize centralManager
self.centralManager = CBCentralManager(delegate: self, queue: nil);
// start scanning for device
self.centralManager.scanForPeripheralsWithServices([UART_SERVICE_UUID], options:nil);
}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData advertisementData: [NSObject : AnyObject]!, RSSI RSSI: NSNumber) {
//print out the name of the scanned peripheral
print("Discovered \(peripheral.name)")
//print out the UUID of the scanned peripheral
print("NSUUID string \(peripheral.identifier.UUIDString)")
//stop scanning when found
self.centralManager.stopScan()
//connect when found
self.centralManager.connectPeripheral(peripheral, options:nil);
}
And the error I receive from the XCode compiler is:
"Objective-C method 'centralManager:didDiscoverPeripheral:advertisementData:RSSI:' provided by method 'centralManager(:didDiscoverPeripheral:advertisementData:RSSI:)' conflicts with optional requirement method 'centralManager(:didDiscoverPeripheral:advertisementData:RSSI:)' in protocol 'CBCentralManagerDelegate'"
From looking through the CoreBluetooth documentation it seems as if the method syntax and parameters are correct, and the optionality of the parameters is copied directly from the spec sheet: https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManagerDelegate_Protocol/#//apple_ref/occ/intfm/CBCentralManagerDelegate/centralManager:didDiscoverPeripheral:advertisementData:RSSI:
Any help would be appreciated! Thank you
Per the comments:
Using XCode 7 beta
When I change the function declaration to:
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData advertisementData: [NSObject : AnyObject], RSSI RSSI: NSNumber)
I still get the same build error.
My centralManagerDidUpdateState:method is
func centralManagerDidUpdateState(central: CBCentralManager) {
print("centralManagerDidUpdateState:");
switch (central.state) {
case .PoweredOff:
print("CBCentralManagerStatePoweredOff");
case .Resetting:
print("CBCentralManagerStateResetting");
case .PoweredOn:
print("CBCentralManagerStatePoweredOn");
//scan for peripheral devices
self.centralManager.scanForPeripheralsWithServices([UART_SERVICE_UUID], options:nil);
case .Unauthorized:
print("CBCentralManagerStateUnauthorized");
case .Unsupported:
print("CBCentralManagerStateUnsupported");
default:
print("CBCentralManagerStateUnknown");
}
}
Thank you for the suggestions; I ended up finding the answer through the XCode 7 documentation. The XCode 6 syntax for the following functions was as follows:
func centralManagerDidUpdateState(central: CBCentralManager!) {}
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData advertisementData: [NSObject : AnyObject]!, RSSI RSSI: NSNumber) {}
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {}
func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {}
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {}
func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {}
func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {}
func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {}
However, these functions will conflict with the XCode 7 CoreBluetooth library declarations.
Note the differing uses of optionals as well as data types.
(XCode 6) error:NSError! vs. (XCode 7) error:NSError?
(XCode 6) advertisementData : [NSObject : AnyObject]! vs. (XCode 7) advertisementData [String : AnyObject]
The appropriate function declarations for XCode 7 beta are actually the following:
func centralManagerDidUpdateState(central: CBCentralManager) {}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {}
func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {}
Hope this is helpful to others having the same issues!