Uart / GPS driver sample buffer overflow

1.1k views Asked by At

I am trying the sample for the GPS driver with a raspberry pi 3 and the Ultimate GPS V3 breakout board.

Here is the full source code: https://github.com/androidthings/drivers-samples/tree/master/gps

The GPS board is connected following this schematics: enter image description here

When launching the sample app, I get the following error:

com.example.androidthings.driversamples E/AndroidRuntime: FATAL EXCEPTION: main


Process: com.example.androidthings.driversamples, PID: 1299
   java.nio.BufferOverflowException
       at java.nio.Buffer.nextPutIndex(Buffer.java:508)
       at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:142)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.processBuffer(NmeaGpsModule.java:178)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.readUartBuffer(NmeaGpsModule.java:160)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.access$000(NmeaGpsModule.java:35)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule$1.onUartDeviceDataAvailable(NmeaGpsModule.java:139)
       at com.google.android.things.pio.UartDevice$UartDeviceCallbackDispatch.dispatchInterruptEvent(UartDevice.java:507)
       at com.google.android.things.pio.CallbackDispatch.onFileDescriptorEvents(CallbackDispatch.java:127)
       at android.os.MessageQueue.dispatchEvents(MessageQueue.java:282)
       at android.os.MessageQueue.nativePollOnce(Native Method)
       at android.os.MessageQueue.next(MessageQueue.java:323)
       at android.os.Looper.loop(Looper.java:136)
       at android.app.ActivityThread.main(ActivityThread.java:6077)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Update 1

After enabling the debug in the contrib-driver project, I see a new error: W/NmeaParser: Invalid checksum (62), expected 108

12-28 17:53:28.638 1378-1378/com.example.androidthings.driversamples D/XXX: Debug version used
12-28 17:53:29.451 1378-1378/com.example.androidthings.driversamples I/Choreographer: Skipped 34 frames!  The application may be doing too much work on its main thread.
12-28 17:53:29.862 1378-1378/com.example.androidthings.driversamples W/NmeaParser: Invalid checksum (62), expected 108
12-28 17:53:29.862 1378-1378/com.example.androidthings.driversamples D/XXX: Buffer reset
12-28 17:53:30.427 1378-1378/com.example.androidthings.driversamples D/AndroidRuntime: Shutting down VM
12-28 17:53:30.428 1378-1378/com.example.androidthings.driversamples E/AndroidRuntime: FATAL EXCEPTION: main
   Process: com.example.androidthings.driversamples, PID: 1378
   java.nio.BufferOverflowException
       at java.nio.Buffer.nextPutIndex(Buffer.java:508)
       at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:142)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.processBuffer(NmeaGpsModule.java:179)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.readUartBuffer(NmeaGpsModule.java:161)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule.access$000(NmeaGpsModule.java:35)
       at com.google.android.things.contrib.driver.gps.NmeaGpsModule$1.onUartDeviceDataAvailable(NmeaGpsModule.java:140)
       at com.google.android.things.pio.UartDevice$UartDeviceCallbackDispatch.dispatchInterruptEvent(UartDevice.java:507)
       at com.google.android.things.pio.CallbackDispatch.onFileDescriptorEvents(CallbackDispatch.java:127)
       at android.os.MessageQueue.dispatchEvents(MessageQueue.java:282)
       at android.os.MessageQueue.nativePollOnce(Native Method)
       at android.os.MessageQueue.next(MessageQueue.java:323)
       at android.os.Looper.loop(Looper.java:136)
       at android.app.ActivityThread.main(ActivityThread.java:6077)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
12-28 17:53:30.439 1378-1378/com.example.androidthings.driversamples I/Process: Sending signal. PID: 1378 SIG: 9

Update 2

After increasing the buffer size by 4, I was able to get few messages in. I see some messages have some junk that would explain the overflow:

12-28 23:38:17.393 2366-2366/? D/XXX: message: ��GPGGA,233817.000,3742.1931,N,12208.3976,W,1,04,1.96,164.3,M,-25.5,M,,*58
12-28 23:38:17.394 2366-2366/? D/XXX: Buffer reset
12-28 23:38:17.394 2366-2366/? D/XXX: message: GPGSA,A,3,23,03,26,22,,,,,,,,,2.20,1.96,1.00*0B
12-28 23:38:17.395 2366-2366/? D/XXX: Buffer reset
12-28 23:38:17.544 2366-2366/com.example.androidthings.driversamples D/XXX: message: GP��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RMC,233817.000,A��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,3742.1931,N,12208.3976,W,0.42,205.67,281216,,,A*71
12-28 23:38:17.545 2366-2366/com.example.androidthings.driversamples D/XXX: Buffer reset
12-28 23:38:17.545 2366-2366/com.example.androidthings.driversamples D/XXX: message: GPVTG,205.67,T,,M,0.42,N,0.78,K,A*32
12-28 23:38:17.546 2366-2366/com.example.androidthings.driversamples D/XXX: Buffer reset

I have no idea where that junk could come from...

2

There are 2 answers

3
Dave McKelvie On BEST ANSWER

In some cases processBuffer() will process the whole buffer (512 bytes) regardless of how many bytes are read from the UART. If it happens to miss a start or end character you can end up with a lot of '0' in the message buffer. You can modify this to be like this:

int count;
while ((count = uart.read(buffer, buffer.length)) > 0) {
  processBuffer(buffer, count);
}

then modify this to be more like this:

private void processBuffer(byte[] buffer, int length) {
    for (int i = 0; i < length; i++) {
        if (mParser.getFrameStart() == buffer[i]) {
            handleFrameStart();
        } else if (mParser.getFrameEnd() == buffer[i]) {
            handleFrameEnd();
        } else {
            //Insert all other characters into the buffer
            mMessageBuffer.put(buffer[i]);
        }
    }
}

Then you won't be filling the message buffer with garbage if an end character gets dropped. I noticed this happening in a project I'm working on that uses this source code. The readUartBuffer() method can read split messages, i.e., the start or end of a message. If it does, bad things happen.

2
Blundell On

Looking at the demo code https://github.com/androidthings/drivers-samples/blob/master/gps/src/main/java/com/example/androidthings/driversamples/GpsActivity.java#L35 the Baud rate is set correctly as per the data sheet: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-ultimate-gps.pdf

And looking at the contrib-driver for NmeaGpsModule the message buffer length is twice the size of the buffer read from the driver https://github.com/androidthings/contrib-drivers/blob/master/gps/src/main/java/com/google/android/things/contrib/driver/gps/NmeaGpsModule.java#L169 so it can't overflow with just one read.

A theory for the issue could be that the message buffer is not being cleared after it receives data - therefore you get a BufferOverflowException after a few packets.

The buffer is reset in 2 places:

When a new frame starts:

https://github.com/androidthings/contrib-drivers/blob/master/gps/src/main/java/com/google/android/things/contrib/driver/gps/NmeaGpsModule.java#L187

Or when a frame ends:

https://github.com/androidthings/contrib-drivers/blob/master/gps/src/main/java/com/google/android/things/contrib/driver/gps/NmeaGpsModule.java#L209


I don't have the hardware to debug your issue, however I can recommend how you could do it:

This goes for anyone wanting to debug a contrib-driver

Fork / Make a copy of this android library: https://github.com/androidthings/contrib-drivers/tree/master/gps

Edit the build.gradle to add this dependency: https://github.com/novoda/bintray-release (follow the README for instructions).

Once you add that dependency the build.gradle will look like this:

apply plugin: 'com.android.library'
apply plugin: 'com.novoda.bintray-release' // new code

android {
    compileSdkVersion 24
    buildToolsVersion '24.0.3'

    defaultConfig {
        minSdkVersion 24
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
}

buildscript {
    repositories {
        jcenter()
   }
    dependencies {
        classpath 'com.novoda:bintray-release:0.4.0'  // new code
    }
}

dependencies {
    compile 'com.android.support:support-annotations:24.2.0'
    provided 'com.google.android.things:androidthings:0.1-devpreview'
}

publish {  // new code
    userOrg = 'google'
    groupId = 'com.google.android.things.contrib'
    artifactId = 'driver-gps'
    publishVersion = '0.1-DEBUG'
}

Now you can release your own version of the GPS driver for debugging, but first:

Edit the java files, to add logging. In the NmeaGpsModule.java of your fork/copy, make two changes:

private void init(UartDevice device, int baudRate, Handler handler) throws IOException {
    Log.d("XXX", "MY VERSION BEING USED"); // new code
    mDevice = device;
    mDevice.setBaudrate(baudRate);
    mDevice.registerUartDeviceCallback(mCallback, handler);

    mParser = new NmeaParser();
}

and

private void resetBuffer() {
    Log.d("XXX", "BUFFER BEING RESET");  // new code
    mMessageBuffer.clear();
    mFrameFlag = false;
}

To release this version run this command in a terminal from the same folder as the build.gradle:

 ./gradlew clean build bintrayUpload -PdryRun=true

Now you have released a dependency! Back in your application (in this case the driver-samples) https://github.com/androidthings/drivers-samples/blob/master/gps/build.gradle#L39:

Change the build.gradle to have a different dependency:

 compile 'com.google.android.things.contrib:driver-gps:0.1'

becomes

 compile 'com.google.android.things.contrib:driver-gps:0.1-DEBUG'

and tell it to look locally for this dependency:

repositories {
    mavenLocal()
    jcenter()
}

Now rerun the application and you should see your logs! First the sanity log to prove it worked "MY VERSION BEING USED" then the buffer being reset "BUFFER BEING RESET" if the buffer isn't being reset .. you need to check your hard wiring or add more logs to figure out why.