Using the Trimble Android Serial API

Posted by admin at 10:24 AM on Nov 4, 2013

Share:


The Trimble Juno T41's serial port accessory can be used to interface the T41 with a wide variety of pre-existing hardware. The Trimble SerialPort API presents data from serial devices directly to Android applications, without requiring elevated permissions or error-prone JNI code. The nature of serial data presents several challenges to clean integration into an Android application. These include:

  • obtaining and setting up a SerialPort object
  • reading the data without causing interface lag
  • properly handling the SerialPort lifecycle
  • translating the data from a stream of bytes to a more useful format for Android.

To begin, obtain a list of attached serial devices:

File[] serialPorts = SerialPort.getSerialPortList();

This method returns the file objects corresponding to any externally-connected serial devices, which are used to open those devices for reading and writing. If the array is empty, no devices are connected. To open a serial port, call the SerialPort constructor:

SerialPort p = new SerialPort(serialPorts[0], 115200, 0);

The first argument to the constructor is a File object corresponding to a serial device. The second is the speed in baud, and the third is for flags to be passed to the SerialPort API. The latter will be zero (that is, no flags) for almost all applications. Consult the Trimble API documentation for details on further configuration of the serial port object.

A SerialPort object has methods for reading and writing:

byte[] buffer = new byte[1024];<br>p.read(buffer); p.write(buffer);<br>p.read(buffer, 256, 768); p.write(buffer, 256, 768);

The first form reads up to buffer.length bytes into buffer, or writes the entirety of buffer to the serial port. The second form reads up to 768 bytes into buffer, starting at index 256, or writes 768 bytes to the serial port starting at buffer 256.

SerialPort read calls are blocking and can't be interrupted. They should therefore be made in a background thread to avoid hanging the UI. There are a few choices for organizing background calls, which are governed by Android and SerialPort lifecycle considerations. SerialPort objects have a close method, which must be explicitly called to free system resources. Failure to close SerialPort objects before discarding them leaks resources, and will eventually cause the SerialPort to fail with errors until the device is rebooted.

This restriction imposes a strict requirement that any Android component in which the SerialPort API is used: it must be able to notify the SerialPort API that it's closing, so that the SerialPort API can shut down cleanly. For applications with only one activity, the activity itself can be used. For applications with more than one activity, an Android service is a workable candidate. (Remember that an Android service does not have a separate work thread.) SDG's most recent serial data application used static classes to handle starting and stopping a serial reading and parsing thread as necessary; each activity that needed data was responsible for registering for data when it started and unregistering when it finished. Android's AsyncTask type could be used for a similar mechanism. The most important thing to remember, though, is that your application must close any SerialPort objects if references to those objects will be lost.

The blocking, non-interruptible nature of SerialPort read calls also means that a read call made when there's no data available will block until its thread terminates. Don't forget to take this into account if your application stops and restarts read threads—if your thread termination code is insufficiently robust, the original thread might survive and compete with the new thread for data.

The final topic covered in this article is simpler. Translation to Java data types from raw serial data can be carried out using the ByteBuffer class, which wraps a byte array and interprets portions of it as the desired types. Notice that ByteBuffer and Java/Android in general are big-endian. If your data is little-endian, be sure to set the appropriate property on your ByteBuffers.

For more information, or to contract with SDG Systems to assist with your Android application, please contact us.


Loading Conversation