Swift nsinputstream not giving me the correct data from Trimble

I've developed this for the android and simply used a bufferedreader and

br.readline()
and the code properly interpreted the data coming through.

I should be getting data that looks like this:

$GPRMC,191830.00,A,4159.87613,N,09338.29258,W,0.065,303.4,270815,0.8,E,D*21

what I am getting is data that looks like this:

(WK#

The code in viewDidLoad()


if accessoryList.count > 0 {

var accessory = accessoryList[0] as! EAAccessory

accessory.delegate = self

println(accessoryList[0].description)

session = EASession(accessory: accessoryList[0] as! EAAccessory, forProtocol: "com.trimble.mcs.gnss")

if session != nil {

inputStream = session.inputStream

/

inputStream.delegate = self

inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

inputStream.open()

}

}


and the stream code


func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {

let inStream = aStream as? NSInputStream

switch (eventCode){

case NSStreamEvent.OpenCompleted:

NSLog("Stream opened")

break

case NSStreamEvent.HasBytesAvailable:

/

let bufferSize = 8

var buffer = [UInt8](count: bufferSize, repeatedValue: 0)

while(session.inputStream!.hasBytesAvailable) {

let result :Int = session.inputStream!.read(&buffer, maxLength: bufferSize)

if(result > 0){

var output = NSString(bytes: &buffer, length: bufferSize, encoding: NSUTF8StringEncoding)

if(output != nil) {

print(output!)

}

}

}

println("")

break

case NSStreamEvent.ErrorOccurred:

NSLog("ErrorOccurred")

break

case NSStreamEvent.EndEncountered:

NSLog("EndEncountered")

break

default:

NSLog("unknown.")

}

}


Is there something I'm missing? I'm getting data, it's just not in english. I know that the encoding is UTF8. Any help would be appreciated!

Answered by DTS Engineer in 60814022

I don't believe it's the MFi program.

If you’re using External Accessory framework then, yes, the accessory is MFi. An accessory must be MFi for the EA framework to talk to it.

It seems like your device has two interfaces:

  • an NMEA 0183 interface, which produces ASCII data — This is what you’re Android app is getting.

  • an EA stream, which produces binary data — This is what your iOS app is getting.

If you want to continue with the EA stream approach, you will either have to talk to the vendor to get a specification of the format of that stream or reverse engineer it for yourself.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I can find only one bad line in your code.


This line:

var output = NSString(bytes: &buffer, length: bufferSize, encoding: NSUTF8StringEncoding)

should be like this:

var output = NSString(bytes: &buffer, length: result, encoding: NSUTF8StringEncoding)

After calling read(_:maxLength:), `result` holds the actually read length and you should pass it to NSString(bytes:length:encoding:).


I'm not sure this solves all your issues, so try it as a starting point.

Yeah not really. I have put result there too. You are correct in saying that's wrong.


Do I need to have some kind of setup for MFi? It seems according to MFi 3rd party wouldn't have to setup anything. Or do I need to sign it somehow?

This streams great on the android and I'm getting strange data on ios

Other parts of your code is sort of textbook implementation of EA framework, so I cannot say any more without more info provided.

Not-shown part of your code may interfering, unseen outputStream may be relevant, the device may need some kind of communicaiton protocol...

Do I need to have some kind of setup for MFi?

In general, MFi requires that each app using the device be authorised by the device vendor. I don’t think that’s a technical restriction but rather one applied at App Review time.

If you are the device vendor then you can request formal support for this via the MFi DTS support incidents. I’m not sure of the actual mechanics of this because I don’t support MFi.

If not, I recommend that you talk to the device vendor. It’s likely you need to talk to them at some point anyway.

Finally, before you do any of that it’s probably a good idea to test that your Swift stream reading code works in general. One option here is to wire it up to a bound pair stream rather than an EA stream. Those streams behave slightly differently, but they’re close enough to verify that the basics.

You can create a bound pair of streams using

+[NSStream getBoundStreamsWithBufferSize:inputStream:outputStream:]
.

The TLSTool sample code shows an example of how redirect data from stdin to a stream. You could use this to test the Swift code you’re using to handle the other half of the bound pair.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I added this to the info.plist Supported external accessory protocols -> com.trimble.mcs.gnss


The data comes through but strangely it's 128 bytes and it should be more like 384, which is what I'm getting on the android when connected.


after doing this:


let bufferSize = 512

var buffer = Array<UInt8>(count: bufferSize, repeatedValue: 0)

if(session.inputStream!.hasBytesAvailable) {

let result :Int = session.inputStream!.read(&buffer, maxLength: bufferSize)

if(result > 0){

let output = NSString(bytes: &buffer, length: result, encoding: NSUTF8StringEncoding)

if output != nil {

print(output!)

}

}

}


and printing the stream below it to see if it has anymore bytes available it returns false which is good


print(inputStream!.hasBytesAvailable)


output is always nil and returns nothing even though the stream was read

I tried some form of this, but I'm still getting an empty input stream. Or the value it returns is nil. It's like something is blocking the data from being real but it's still recieving from the protocol com.trimble.mcs.gnss


EADemo does the same thing, says I'm getting 124 bytes each tick but it's always empty


I don't believe it's the MFi program. From what I've read it doesn't look like I should need anything from the vendor.

Do I need to setup my account with something on? When I click on the Target I have Wireless Accessory Configuration On. Maybe there's something else that needs to be turned on.

What's the content of the data bytes your read?

The second line below generates hex dump for the data.

                let result :Int = session.inputStream!.read(&buffer, maxLength: bufferSize)
                print(buffer[0..<result].map{String(format: "%02x", Int32($0))}.joinWithSeparator(" "))


When you get nil from NSString(bytes:length:encoding:), some invalid bytes may be included in the bytes.


And generally, stream(_:handleEvent:), with NSStreamEvent.HasBytesAvailable, is called multiple times for a long (what is `long` may depend on many things) input. You need to accumulate all input bytes till end-of-line appears.


Have you checked your stream(_:handleEvent:) is called once or multiple times?

The stream is called once a second. So it ticks correctly just like the Trimble is transmitting at.


I got this hex data (it's 128 bytes still), not sure what to make of it:


02 28 57 4b 07 11 05 00 0f 07 47 08 4a 02 60 00 00 00 00 14 00 00 00 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 01 44 03 02 28 40 29 03 00 00 10 09 08 4a 02 6d 07 47 00 00 00 26 19 3f 46 4b f8 00 03 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 c7 03

It seems your device is sending data in a binary format. Can't you find some sort of protocol descriptions for selecting data format in the tech sheet of your device?

I don't have any specs of any sort. I'm kind of reverse engineering a project.

The android side just collects a buffer and prints out nice strings. The format is UTF8. The binary data doesn't seem correct though that I'm getting to the iPhone. What is a protocol description? Is there something else I'll want to add to the protocol services more than just "com.trimble.mcs.gnss". Why is this so much different on the iPhone? Is there some kind of setting blocking getting real data? I have a strong feeling it's something really simple that isn't enabled.

I don't know how your device is programmed to work when connected to Android.

The fact you have shown indicates that your device is sending 128-byte binary data every one second. That's all. I cannot say anything specific for a certain device.

I changed it to c and got this


let hexArray = buffer[0..<result].map{String(format: "%c", UInt8($0))}.joinWithSeparator("")

each line on one tick



(WK‰Gی05³(@)E ی<G&?FKøó

(WK‹Gې5¡(@)F ې#G&?FKøß

(WKG۔5(@)G ۔

G&?FKøÎ

(WKGۗè5|(@)H ۗõG&?FKøº

(WK‘GۛÐ5j(@)I ۛÝG&?FKø§

(WK“G۟¸5X(@)J ۟ÃG&?FKø’

The hex value's received from the android when the Trimble isn't recieving real data


2447504747412c2c2c2c2c2c2c2c2c2c4d2c2c4d2c2c2a3536da2447504753562c2c2c2a3739da244750524d432c3137353630332e30312c562c2c2c2c2c2c2c3236303831352c2c2c4e2a3732da000


The hex value's received from the iphone


0228574b0711a5000f074708e19ee000000000140000003500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020001970302284029d30000100908e19eeb074700000026193f464bf80003000000000000010000000000000000000000004803


I see a lot of 0's which match c2 locations on the android hex. They are comma's when converted to unicode (c2) so the zeroes must be commas. Is there a way I can bit shift it to match the android hex? Perhaps there's some work I need to do with little endian/big endian.

Even better got this when using %01x


print(buffer[0..<result].map{String(format: "%01x", Int32($0))}.joinWithSeparator(""))


android

2447504747412c2c2c2c2c2c2c2c2c2c4d2c2c4d2c2c2a3536da2447504753562c2c2c2a3739da244750524d432c3137353630332e30312c562c2c2c2c2c2c2c3236303831352c2c2c4e2a3732da000


ios

228574b711bd0f7478e99dc00000140003500000000000000000000000000000000000000000000000000002019632284029df001098e99dcc74700026193f464bf80300000010000000000003c3

Your android hex is not correct. I believe `da` must be `0d 0a`, which is CR+LF. And comma is `2c`.

You'd better generate two hex-digits per byte and separator between version of hex dump.


The binary format your device sending to iOS seems to be using `00` (or its sequence) as no-data indication, but I don't see any position of them matching the hex of your Android version. Converting them using simple bit-shifting is hopeless. Find another way.

Yeah can't find a way to convert. I've tried just about everything, little endian. I've tried adding or subtracting to make zeroes c2's and vice versa the 02 to be a 24 so that the statement starts with a $ sign.


the android data is fine:

2447504747412c2c2c2c2c2c2c2c2c2c4d2c2c4d2c2c2a3536da2447504753562c2c2c2a3739da244750524d432c3137353630332e30312c562c2c2c2c2c2c2c3236303831352c2c2c4e2a3732da000


24 is $ then G and the 2c's are commas.


it directly translates to


$GPGGA, with some commas and some sections with data.


Something is off, but at least I know data is coming through.

Swift nsinputstream not giving me the correct data from Trimble
 
 
Q