Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Java Communications Without JNI

December 20, 2013

I recently wrote about using the RXTX library to program an Arduino over the USB comms port. This sort of communication requires Java to native system access, and some don't like some of the complexities in getting RXTX to work. For instance, you need all of the right native libraries in the right place, and you need to set the library path on the Java command line just right. PureJavaComm by Spare Time Labs is a pure Java alternative to RXTX — thanks to Al Williams for pointing this out to me.

PureJavaComm's goal is to provide Java-only access to the comms port requiring only the Java Native Access (JNA) library. JNA is an open source project maintained by Tim Wall that allows you to write Java code only to call into native libraries (i.e., DLLs on Windows). Without JNA, you would have to write potentially complex Java Native Interface (JNI) code or other glue code to make this work. For instance, the following code sample from the JNA Wikipedia page works as-is on Windows, Linux, or OS X, to call the native C runtime printf function:

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
 
/** Simple example of native library declaration and usage. */
public class HelloWorld {
    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary) Native.loadLibrary(
            (Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
        void printf(String format, Object... args);
    }
 
    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
        for (int i = 0; i < args.length; i++) {
            CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
        }
    }
}

Let's take a look at how it works, and explore an example using it with an Arduino.

Inside JNA

JNA works similar to Microsoft's Platform Invocation Services, COM's IDispatch, or Java reflection/introspection. It implements a single small layer of JNI code that acts as a stub to call into any native library from your Java code. It runs on most platforms that support Java, maps all Java primitive types to native types, handles conversion between Java and C strings, supports unicode, variable arguments, Microsoft COM, and variable arguments. See the JNA project page for a complete list of features, but this is the bulk of them.

C pointers are mapped to Java through the use of arrays, as in this example:

// C method
void fill_buffer(int *buf, int len);

// Equivalent JNA mapping
void fill_buffer(int[] buf, int len);

However, if you want the memory to be accessible beyond the local scope of a function call, you need to use a com.sun.jna.Memory object:

private com.sun.jna.Memory buffer = new com.sun.jna.Memory(1024);

private void doSomethingNative() {
    //...
    fill_buffer(buffer, 1024);
}

Using PureJavaComm for Serial Port Communications

To begin with the PureJavaComm library, I downloaded the code from github, and opened the project in NetBeans, although it works in Eclipse as well. I chose the "Clean and build project" method, and NetBeans proceeded to use the Maven build scripts to download all the dependencies and then build the project seamlessly, and without issue.

To test it with an Arduino, I took the sample code and project from my previous blog on Arduino, and modified it slightly. First, instead of RXTX, I included only two JAR files in my build. The first is jna-4.0.0.jar, which is included with PureJavaComm in the lib directory. Second, I used the output of the PureJavaComm build, purejavacomm-0.0.21.jar, found in the bin directory of the project.

The updated Java-to-Arduino code is below. Here's a quick summary of the changes (marked in the comments as well):

  1. Marked by "IMPORTS" in the comments, import the purejavacomms jtermios packages
  2. Marked by "PORTS", I noticed a slight difference in the COMM port names returned
  3. Marked by "CONTAINS", the difference in port names required this change
  4. Marked by "FLOW-CONTROL", setting some flow control parameters are required

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Enumeration;
import purejavacomm.*;       // IMPORTS
import jtermios.Termios.*;

public class ArduinoJavaComms implements SerialPortEventListener {
    SerialPort port = null;

    private String appName = getClass().getName();
    private BufferedReader input = null;
    
    private static final int TIME_OUT = 1000; // Port open timeout

    private static final String PORT_NAMES[] = {  // PORTS
        "tty.usbmodem", // Mac OS X
//        "usbdev", // Linux
//        "tty", // Linux
//        "serial", // Linux
//        "COM3", // Windows
    };
    
    public static void main(String[] args) {
        ArduinoJavaComms lightSensor = new ArduinoJavaComms();
        lightSensor.initialize();
    }

    public void initialize() {
        try {
            CommPortIdentifier portid = null;
            Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

            while (ported == null && portEnum.hasMoreElements()) {
                portid = (CommPortIdentifier)portEnum.nextElement();
                if ( portid == null ) 
                    continue;
                
                System.out.println("Trying: " + portid.getName());
                for ( String portName: PORT_NAMES ) {
                    if ( portid.getName().equals(portName) 
                      || portid.getName().contains(portName)) {  // CONTAINS
                        port = (SerialPort) portid.open("ArduinoJavaComms", TIME_OUT);
                        port.setFlowControlMode(
                                SerialPort.FLOWCONTROL_XONXOFF_IN+ 
                                SerialPort.FLOWCONTROL_XONXOFF_OUT); // FLOW-CONTROL
                        input = new BufferedReader(
                                    new InputStreamReader( port.getInputStream() ));

                        System.out.println( "Connected on port: " + portid.getName() );

                        port.addEventListener(this);
                        port.notifyOnDataAvailable(true);
                    }
                }
            }

            while ( true) {
                try { Thread.sleep(100); } catch (Exception ex) { }
            }
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void serialEvent(SerialPortEvent event) {
        try {
            switch (event.getEventType() ) {
                case SerialPortEvent.DATA_AVAILABLE: 
                    String inputLine = input.readLine();
                    System.out.println(inputLine);
                    break;

                default:
                    break;
            }
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Running this project requires that you have the two JAR files mentioned above in the class path, and that's it. I began receiving events from my Arduino the first time I tried it.

Happy coding!
-EJB

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video