Swift Core Bluetooth communication with OBD2 - ios

I'm trying to observe velocity of my car using an OBD2, to try if the hardware works i had use the app "Auto Doctor" and it works.
But i need some more features and so i started to create my own application
To send command to the characteristic i use the following code:
var commandSended = String() {
didSet {
if let peripheral = self.peripheralConnected {
for char in characteristicConnected {
peripheral.writeValue(self.commandSended.data(using: .utf8)!, for: char, type: .withoutResponse)
peripheral.writeValue(self.commandSended.data(using: .ascii)!, for: char, type: .withoutResponse)
peripheral.readValue(for: char)
}
} else {
self.myTerminal.printToTerminalCommand("You are not connected to the peripheral")
}
}
}
characteristicConnected: are all the characteristic found
Independently which command i send to the characteristic i receive every time the same amount of byte and i can't decode it using utf8 or ascii.
I hope I have supply you enough information.

The answers from (almost all) OBD2 adapters are ASCII string responses, so you need to decode the NSData into ASCII. To extract the actual payload for your responses, you need to learn about decoding the various car protocols. A lot of this information is freely available on the web, for some things you might want to buy the official SAE standards.
That said, even though you are using Swift, you might be interested in using an OBD2 library that does all the heavy lifting for you. As a matter of fact, I wrote one, which you can download at https://github.com/mickeyl/LTSupportAutomotive.
Update: And since then I rewrote something similar in Swift, which might be even more interesting for you. Please have a look at https://github.com/Cornucopia-Swift/CornucopiaUDS

Related

Difference b/w self.packetFlow.ReadBytes vs socket read

I am trying to read data from packet tunnel NEPacketTunnelProvider.
Right now what i am doing is trying to read the data using
self.packetFlow.readPackets { [weak self] (packets: [Data], protocols: [NSNumber]) in }
function.
It seems to be working fine. But now i want to read the packets by using the network handle of self.packetFlow function like this
let tunFd = self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
and use this file descriptor to read the packet data from the tunnel. like this
char *buffer1 = malloc(1024);
ssize_t length = read(tunFd, buffer1, 1024);
But now when i read the data from socket like this. It seems like data is not similar to what i am receiving earlier using the self.packetFlow.
Does anybody else know why it is happening. and what should i do get the similar data as in self.packetFlow.readPackets..
Any pointer of help will be appreciated.
I found the answer or my own query. There are 4 bytes of extra protocol information added to the data while we use file descriptor. We need to handle it as well.

swift gzip data to string issue

I use gzip library to convert data to gzip data
If I want to get string from data I do String(data: data, encoding: .utf8) . But if I do the same for gzip data I get nil string, because as far as I understand it's impossible to convert gzip data to string with .utf8 encoding.
I need that because I want to compare data with a server developer (because he sometimes he says that my data is incorrect format, but sometimes it's ok, and that's strange because I use the same library, and I have thoughts that may be the problem is on server side).
Is there any way to convert gzip data to any string to compare it then later?
If this is just for debug purpose, then I think the quickest way is:
let myGZipNSData = myGZipData as! NSData //I wrote a "!" just for the sample)
print("myGZipNSData: \(myGZipNSData)")
It should output:
myGZipNSData: <5b226d79 41727261 7956616c 75653022 2c226d79 41727261 7956616c 75653122 5d>
This relies on -description method of NSData which print "<hexValue>".
Do not rely on it on release version, almost never rely on -description (yes, Apple could change the behavior of -description in next release. It happened on a different class).
Other way (safer) to get almost the same result, you can check this question: How to convert Data to hex string in swift which you get you the same result (almost, less space, and no </>) as the previous one.
Other way: Base 64
Use base64EncodedString() method of Data:
let base64Str = myGZipData?.base64EncodedString()
print("base64Str: \(base64Str!)")
Output:
base64Str: WyJteUFycmF5VmFsdWUwIiwibXlBcnJheVZhbHVlMSJd
Now, there should be other way:
Have a array representation with Int values (between 0/255 instead of hex), but it seems to be for debug purpose so check if you can "reproduce the current solutions" (working/fast implementation) I gave you, and check with the server developer what he/she can do on his/her side too to compare.
Note:
For the purpose of this sample, myGZipData is constructed this way:
let array = ["myArrayValue0", "myArrayValue1"]
let myGZipData = try? JSONSerialization.data(withJSONObject: array, options:[])
It's not really a GZipData, but the goal was to quickly have a Data object "big enough".

A loop to continuously collect the Wifi strength of nearby access points

Assume I have an iPhone connected to a wifi network with 3+ access points.
I'd like to collect all possible fields around wifi access strength/signal/etc from EACH access point and use that to triangulate, even while in background.
while true {
...
for access_point in access_points {
...
signal_strength = ...
}
}
I've been reading previous SO answers and other posts, and seems like it wasn't allowed on iOS without a jailbreak for a while, but is now availiable again.
Anyone can show a code snippet of how I'd go about doing this? All new to iOS development..
It's been quite a while since I worked with this, so I did a quick check again and now I am fairly certain you misunderstood something you've read. As far as I can tell, Apple did not suddenly revert their previous decision to restrict the public frameworks to scan for access points, i.e. specific MAC addresses and their signal strength.
You can query the specific rssi (signal strength) for a network (i.e. for an ssid), but not for individual MAC addresses. Before iOS 5 you could do that using private APIs, then you could do it with private APIs on a jailbroken device and that's pretty much it.
I don't have the code of my own, old stuff at hand (I used to do this for indoor location tracking before we switched to use iBeacons), so I can't provide you with a sample snippet myself. My code is dated and no longer functioning anyways, but you might find something here.
I would be really interested in the sources you mention that claim iOS 10 now allows this again. Apple closed this for privacy considerations (officially at least, and although this might be true in part it also means developers dealing with location-tracking now need to rely fully on Apple's framework for that only), so I highly doubt they went back on it.
Also, note that this is for sure not something trivial, especially if you're new to iOS development. I haven't even tackled the background idea, you can safely forget about that, because no matter what you do, you will not have a scanner that runs continuously in the background. That's against a very core principle of iOS programming.
I've answered how to ping ALL wifi networks in this question;
func getInterfaces() -> Bool {
guard let unwrappedCFArrayInterfaces = CNCopySupportedInterfaces() else {
print("this must be a simulator, no interfaces found")
return false
}
guard let swiftInterfaces = (unwrappedCFArrayInterfaces as NSArray) as? [String] else {
print("System error: did not come back as array of Strings")
return false
}
for interface in swiftInterfaces {
print("Looking up SSID info for \(interface)") // en0
guard let unwrappedCFDictionaryForInterface = CNCopyCurrentNetworkInfo(interface) else {
print("System error: \(interface) has no information")
return false
}
guard let SSIDDict = (unwrappedCFDictionaryForInterface as NSDictionary) as? [String: AnyObject] else {
print("System error: interface information is not a string-keyed dictionary")
return false
}
for d in SSIDDict.keys {
print("\(d): \(SSIDDict[d]!)")
}
}
return true
}
You may have seen this feature in jailbroken apps as it is possible to do this using private libraries, which means that apps that are sold on the iOS store can't be sold if they utilise them.

Transfer large binary string to BLE device from ios app using corebluetooth

I want send large binary string to BLE device(peripheral) from my ios app(central device). Its working fine with small string, but when iam trying to send large string, It was not receiving and the connection was automatically disconnecting. I have read that we need to divide the large data into multiple chunks to send it. But i didn't find any working sample on that.
Please look at the code send the string
let stringToSend = "0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000,0001010101010101111111111111000000#"
.
let data : NSData = stringToSend.dataUsingEncoding(NSUTF8StringEncoding)!
if positionCharacteristic != nil {
self.polarH7HRMPeripheral.writeValue(data, forCharacteristic: positionCharacteristic, type: .WithoutResponse)
self.polarH7HRMPeripheral.setNotifyValue(true, forCharacteristic: positionCharacteristic)
}
Thanks in advance
Some devices registers are limited in size. They can't contain more than X bytes for processing.
Let's assume said BLE device expects a long command in a format of a few short commands.
Let's also assume said device able to receive short commands as well.
How would the device know the difference between a short command and a partial long command? Using a command format which states command type.
For example:
A command will contain 1 header byte, 6 content bytes, 1 command type byte.
Partial long command will have 11111111 as its command type with the final part as 11111110.
Short command will have 00000000 as the its command type.
This is how I "defined" a simple protocol for difference commands for a device.
I would recommend you look for that protocol in the developer manual of that device
Some device accepts 20 bytes at most, so you have to spit your string.
for strMsgPart in stringToSend.split(by: 20) {
if positionCharacteristic != nil {
self.polarH7HRMPeripheral.writeValue(strMsgPart, forCharacteristic: positionCharacteristic, type: .WithoutResponse)
self.polarH7HRMPeripheral.setNotifyValue(true, forCharacteristic: positionCharacteristic)
}
}

Bluetooth peripheral doesn't answer

I am in big trouble with my exams.
I have to write an iOS app that uses an external sensor made by Texas Instruments, it's called TI Sensortag.
TI's documentation, in my humble opinion, is really poor and complicated to understand for an entry level programmer.
I tried to ask in the E2E forum but they weren't able to help me, their answer was something like "Um, well, we don't know, go away and ask someone else", ...
I added the CoreBluetooth framework to my project an created a CentralManager. I am able to find my device, connect and get his name and (sometimes) his RSSI.
Now what I'm trying to do is to ask my CBPeripheral object if it has some services for me or something like that. I've found the Complete Attribute Table but I have no idea how to use it...
I know I have to activate some services or something like that but I really don't now ho to do it, I googled a lot but I've not found something helpful...
I'm trying to enable my sensor with this method, but I'm doing something wrong.
-(void) configureSensorTag
{
uint8_t myData = 0x01;
NSData *data = [[NSData alloc] initWithBytes:&myData length:1];
[BLEUtility writeCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA02-0451-4000-B000-000000000000" data:data];
[BLEUtility setNotificationForCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA01-0451-4000-B000-000000000000" enable:YES];
NSLog(#"Configured TI SensorTag IR Termometer Service profile");
}
Moreover I'm trying to retrive Sensortag's services with this method
[peripheral discoverServices:nil];
and his delegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(#"Found service");
if (error) {
NSLog(#"Error: %#", error);
}
}
but it is never called.
Has someone any idea?
Thank you very much!
Unfortunately I can't help you with the details of the iOS, but I can help you with understanding the sensor tag. If you look at that attribute PDF you linked you'll find entries marked "GATT_CLIENT_CHAR_CFG_UUID". It's 16 bits of flags where only the 2 least significant bits are used. It even says in there 'Write "01:00" to enable notifications, "00:00" to disable'. (That's the least significant bit because it's encoded in little-endian format)
So, you're sending a 0x01 to turn on the IR temperature sensor, but you haven't turned on the notifications. Turning it on will then cause the device to stream notifications back to the client. The accelerometer doesn't require turning on, so maybe you should try that first.
I have no idea what that second chunk of code is supposed to be doing... sorry.
Ok ok I got it,
there were any software problem, I mean, not by iOS side.
Sensortag has a wrong firmware and so it did'n work.
I've changed Sensortag and now everything works fine.
Thank you anyway!

Resources