How do I handle NSInlineData from Bluetooth scanning? - ios

I want to use data that is passed through Bluetooth scan.
and I want to split and use data from NSInlineData.
I import data via Bluetooth scan as follows.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if !peripherals.contains(peripheral){
peripherals.append(peripheral)
}
let key = peripheral.identifier.uuidString
let data = advertisementData.description
let rawData = advertisementData
rawData.forEach { (key, val) in
print("\(key) : \(val)")
}
print("uuid: \(key)")
if let previous = datas[key] {
if (previous != data) {
datas.updateValue(data, forKey: key)
rawdatas.updateValue(advertisementData, forKey: key)
}
} else {
datas[key] = data
rawdatas[key] = advertisementData
}
}
The form of the data is as follows.
//uuid: 81DB0A8E-6C47-1ADF-E1C5-3D7D4269D66D
//kCBAdvDataIsConnectable : 0
//kCBAdvDataServiceData : {
// FEAA = <20000200 19400000 03d80000 0000>;
//}
//kCBAdvDataServiceUUIDs : (
// FEAA
//)
The following data were extracted.
if rawdata["kCBAdvDataServiceData"] != nil{
let parseData:NSDictionary = rawdata["kCBAdvDataServiceData"] as! NSDictionary
let data = parseData.allValues.first
// data => optional(<20000200 19400000 03d80000 0000>)
// data type => _NSinlineData
}
I want to take out the number 5,6,7,8(0200) from the data and replace it with a decimal number. I don't know how to handle NSInlineData.

rawdata["kCBAdvDataServiceData"] as? [CBUUID: Data]
NSDictionary is bridged to Dictionary in Swift, which can be represented as [:].
_NSInlineData is the internal type (as indicated by _). It is NSData, which is bridged to Data in Swift.

Related

Cannot Convert String to Int from BLE Data

I've spent 3 weeks avoiding this question on Stackover flow but I'm at my wits end.
I don't know why I can create a string manually and convert it into an INT but can't do the same thing from BLE data.
This is my code:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
print("ERROR ON UPDATING VALUE FOR CHARACTERISTIC: \(characteristic) - \(String(describing: error?.localizedDescription))")
return
}
guard let data = characteristic.value else{
return
}
// let myString = "345345" // I CAN MANUALLY SET A STRING
// let myInt = Int(myString) ?? 2
// print (myInt + 523436) I CAN CONVERT AND USE THIS INT
let weightStringData = swiftDataToString(ESP32Data: data)
func swiftDataToString(ESP32Data:Data) -> String? {
return String(data: data, encoding: .utf8)
}
if let myNewstring = weightStringData{
// print(myNewstring) // THIS PRINTS AS A STRING
let myInt = Int(myNewstring) ?? 2 //THIS DOES NOTHING IT SEEMS OTHER THAN SET THE CONSTANT TO "2"
print (myInt + 9898) //CANT USE THE INT LIKE IN THE ABOVE MANUAL EXAMPLE
}else{
print("Nothing")
}
}
}

Swift converting BLE characteristic value to a string

I am having an issue that is making no sense. Everything that I am doing I would think is pretty standard for BLE. Anyways, I have a BLE module that I am sending string data from to an iOS application and I am displaying this data on an interface. Since this is my first run using BLE code on an iOS application, I am using the tutorial from adafruit as a guide which can be found here: Adafruit tutorial
As I said, pretty standard stuff. Nothing special here. The code that I have written is able to perform all of the BLE steps. But, when it gets the data, the data is /0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0. At first, I thought the source on the transmitting side was incorrect. But I opened another test app that lets me see sent BLE data and the data coming in is correctly formatted. It must be an issue with my code. I am not sure why this is happening or what the issue is. I could really use another pair of eyes to see if there is any issue with my code:
My code init code:
required init(parent: ViewController) {
self.parent = parent
self.bluetoothOn = false;
bodyTemperature = 0
airPressure = 0
lightLevel = 0
fanSpeed = 0
Humidity = 0
otherBatteryLevel = 0
wifiStatus = "Good"
super.init()
self.centralManager = CBCentralManager(delegate: self, queue: nil)
// self.periphal = CBPeripheralManager(delegate: self, queue: nil)
}
My didupdateState code:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if #available(iOS 10.0, *) {
if centralManager.state == CBManagerState.poweredOn{
bluetoothOn = true
self.centralManager.scanForPeripherals(withServices: [ATP_SERVICE_UUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
}
} else {
// Fallback on earlier versions
}
}
My didDiscover code:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
stopScan()
self.blePeripheral = peripheral
self.RSSI = RSSI
self.blePeripheral.delegate = self
centralManager?.connect(blePeripheral, options: nil)
print("Found BLE module")
// blePeripheral.discoverServices([ATP_SERVICE_UUID])
}
my didConnect code:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to BLE device")
centralManager?.stopScan()
blePeripheral.delegate = self
blePeripheral.discoverServices([ATP_SERVICE_UUID])
}
My didDiscoverServices code:
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else{
return
}
for service in services{
peripheral.discoverCharacteristics([ATP_UART_WRITE14_UUID, ATP_UART_READ13_UUID, ATP_NOTIFY_UUID], for: service)
}
print("Discovered Services: \(services)")
}
My didDiscoverCharacteristics code:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else{
return
}
print ("Found \(characteristics.count) characteristics")
for characteristic in characteristics{
if characteristic.uuid.isEqual(ATP_UART_READ13_UUID){
readCharacteristic = characteristic
// blePeripheral.setNotifyValue(true, for: readCharacteristic!)
blePeripheral.readValue(for: characteristic)
print("Rx Characteristic: \(characteristic.uuid)")
}
if(characteristic.uuid.isEqual(ATP_UART_WRITE14_UUID)){
writeCharacteristic = characteristic
print("Tx characteristic: \(characteristic.uuid)")
}
}
}
My didupdateNotificationState code:
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if(error != nil){
print("Error changing notification state:\(String(describing: error?.localizedDescription))")
} else {
print("Characteristic's value subscribed")
}
if(characteristic.isNotifying){
print("Subscribed. Notification has begun for: \(characteristic.uuid)")
}
}
And lastly, my didUpdateValueFor:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == readCharacteristic{
// let ASCIIString = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.ascii)
if stringFromData != "" {
let textArray = stringFromData!.components(separatedBy: "\n\r")
for line in textArray{
if line.hasPrefix("bodyTemperature:") {
var splitString = line.characters.split(separator: ":").map(String.init)
bodyTemperature = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: bodyTemperature)
} else if line.hasPrefix("enclosurePressure:") {
var splitString = line.characters.split(separator: ":").map(String.init)
airPressure = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: airPressure)
} else if line.hasPrefix("lightLevel:") {
var splitString = line.characters.split(separator: ":").map(String.init)
lightLevel = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: lightLevel)
} else if line.hasPrefix("humidity:") {
var splitString = line.characters.split(separator: ":").map(String.init)
Humidity = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: Humidity)
} else if line.hasPrefix("BATT:") {
var splitString = line.characters.split(separator: ":").map(String.init)
otherBatteryLevel = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: otherBatteryLevel)
} else if line.hasPrefix("fanSpeed:") {
var splitString = line.characters.split(separator: ":").map(String.init)
fanSpeed = Int(splitString[1])!
self.delegate?.PQEData(didRecieveData: fanSpeed)
} else if line.hasPrefix("wifiStatus:") {
var splitString = line.characters.split(separator: ":").map(String.init)
wifiStatus = splitString[1]
self.delegate?.PQEData(didRecieveData: wifiStatus)
}
}
}
}
}
Additional Notes:
1) In the didDiscoverCharaceristics function, I have commented out the call to blePeripheral.setNotifyValue function. That is because on the line for the call to peripheral.discoverCharacteristics call, I am already discovering for the NOTIFY characteristics. I have also tested with the setNotifyValue uncommented and the results are the same.
2) I have a local variable called blePeriphal that stores a reference to the periphal for the ble device. Same applies for writeCharacteristic and readCharacteristic
3) When the blePeripheral.setNotifyValue function was uncommented, the didUpdateNotificationStateFor function was executed. However, the if(error != nil) statement would be true and the error that I would get would be: Error changing notification state:Optional("The request is not supported."). I am not sure if this is related. However, the didUpdateValueFor function would still get executed.
4) The code that is giving me the issue is found in the didUpdateValueFor function and the line is: var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.ascii)
5) Yes, I did try var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.utf8) and var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) and the results were the same. StringFromData is all /0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0
Update:
6) So after some testing, I have come across an issue that may be related. The didUpdateValueFor delegate is only being called once and after that, never again. I have verified this by constantly sending a string once every 1/4 second.

Can't get CKRecord.ID from CloudKit notificationInfo and userInfo structure

I have setup a subscription on the public database for a CloudKit application. I want to retrieve the recordName from the notificationInfo that is returned. I have not been able to find any documentation to explain how to retrieve the recordName with framework methods, so I have attempted to parse the info manually it does not work, but this seems seriously brute force and I'm not sure I can count on the notificationInfo structure always being the same. Any guidance would be appreciated. Here is the code snippet.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
var changedCKRecord : String
let dict = userInfo as! [String : NSObject]
let notification = CKNotification(fromRemoteNotificationDictionary: dict)
let db = CloudKitDatabase.shared
if notification.subscriptionID == db.ckStyleSubscriptionID {
db.handleNotification()
completionHandler(.newData)
print(".newData for CKStyle returned")
for (key,value) in dict {
if key == "ck" {
let newDict : [String : NSObject] = value as! [String : NSObject]
for (key2,value2) in newDict {
if key2 == "qry" {
let thirdDict : [String : NSObject] = value2 as! [String : NSObject]
for (key3,value3) in thirdDict {
if key3 == "rid" {
if let end = value3 as? String {
changedCKRecord = end
print(changedCKRecord as Any)
}
}//if key3
}//for key3
}//if key2
}//for key 2 in
}//if key is ck
}//for in
} else {
completionHandler(.noData)
print(notification.subscriptionID as Any)
print(db.ckStyleSubscriptionID)
print(".noData for CKStyle returned")
}//if notification else
}//didReceiveRemoteNotification
If I print the notificationInfo:
[AnyHashable("aps"): {"content-available" = 1;},
AnyHashable("ck"): {
ce = 2;
cid = "iCloud. the real bundle identifier ";
ckuserid = "_c4478d4d3a212da39cca27eb17dffe03";
nid = "58372f81-d10b-4e3b-98ae-40a0d1e048a2";
qry = {
dbs = 2;
fo = 2;
rid = "97DB306F-3277-07A1-04F5-40228A5EF036";
sid = "ckstyle-changes";
zid = "_defaultZone";
zoid = "_defaultOwner";
};
}]
If the notification is a result of a subscription, then CKNotification(fromRemoteNotificationDictionary:) will actually return an instance of CKQueryNotification. You can then access the CKQueryNotification's recordID property to obtain the id of the record that changed.
let dict = userInfo as! [String : NSObject]
let notification = CKNotification(fromRemoteNotificationDictionary: dict)
if let queryNotification = notification as? CKQueryNotification,
let recordName = queryNotification.recordID?.recordName {
print("Record name is \(recordName)")
}
}

ios ble "Characteristic User Description"

Trying to retrieve readable information from an characteristics by using the function:
peripheral.discoverDescriptors(for: characteristic)
Later the delegate method:
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?)
is called but how can I get the string description? When I read the value from the descriptors it's always nil.
let descriptors = characteristic.descriptors! as [CBDescriptor]
for descriptor in descriptors {
print("\(#function): descriptor = \(descriptor) UUID = \(descriptor.uuid) value = \(descriptor.value)")
}
However, if I'm browsing and connecting with an BLE scanner it can read the characteristic human readable descriptors.
Reading descriptors is a two-step process, much like discovering and then reading characteristics.
Try:
public func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
guard let descriptors = characteristic.descriptors else { return }
for descr in descriptors {
peripheral.readValue(for: descr)
}
}
And then fill in the blanks of:
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
switch descriptor.uuid.uuidString {
case CBUUIDCharacteristicExtendedPropertiesString:
guard let properties = descriptor.value as? NSNumber else {
break
}
print(" Extended properties: \(properties)")
case CBUUIDCharacteristicUserDescriptionString:
guard let description = descriptor.value as? NSString else {
break
}
print(" User description: \(description)")
case CBUUIDClientCharacteristicConfigurationString:
guard let clientConfig = descriptor.value as? NSNumber else {
break
}
print(" Client configuration: \(clientConfig)")
case CBUUIDServerCharacteristicConfigurationString:
guard let serverConfig = descriptor.value as? NSNumber else {
break
}
print(" Server configuration: \(serverConfig)")
case CBUUIDCharacteristicFormatString:
guard let format = descriptor.value as? NSData else {
break
}
print(" Format: \(format)")
case CBUUIDCharacteristicAggregateFormatString:
print(" Aggregate Format: (is not documented)")
default:
break
}
}
The string constants and the associated data types came from the overview table here.
In my (limited) experience, descriptors don't reveal anything particularly interesting.

How to get NSDictionary from NSData out of bluetooth characteristic in Swift

I try to get NSDictionary from NSData of bluetooth characteristic but I got error message, "NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive".
peripheralManager send NSDictionary like this to Central.
func peripheralManager(peripheral: CBPeripheralManager!, didReceiveReadRequest request: CBATTRequest!) {
var responseDictonary: Dictionary = [
"id" : 11111,
"name" : "hoge"
]
request.value = NSKeyedArchiver.archivedDataWithRootObject(responseDictonary)
peripheralManager.respondToRequest(request, withResult: CBATTError.Success)
}
CentralManager recieve peripheral like this.
func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
if (error != nil) {
return
}
if characteristic.UUID == BLECharacteristicUUID {
let data : NSData = characteristic.value
if let recieveDictonary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary {
var id = recieveDictonary["id"] as Int
var name = recieveDictonary["name"] as String
Tracker.sharedInstance.debug("\(id) \(name)")
}
}
}
Do you have any solutions?
Try doing the following it might help you find the root cause.
Just try to unarchive immediately after you archive in the first method,
It will help you find if there is any issue while archiving itself.
(your archiving code looks fine still make sure its working properly)
check for a nil value for "data" in the second method.

Resources