The Java Communications API: A Working Example - By Rick Proctor

By: Rick Proctor

Abstract: A How-To on accessing serial ports in the Windows environment using the Java Communications API

Introduction

In this article we’ll discuss using the Java Communications API to access COM ports in Windows. The following example is taken from a java program that reads barcodes from an omni-directional scanner. It checks the barcode against a remote object and, based on the results, sends codes to a PLC (program logic controller) to fire a diverter arm.

The Java Communications API

Sun provides an API extension for communicating with parallel and serial ports, specifically for Solaris and Windows environments. The API can be found at http://java.sun.com/products/javacomm. There are also third party APIs and an open source implementation from http://www.rxtx.org that works on several platforms including Linux.

Using the API, you can create an object for each port (enumeration), open ports, resolve port contention between Java objects, and perform asynchronous and synchronous communications.

Installing the Java Communications API in a Windows Environment

There are some tricks involved in getting the Java Communications API to correctly interact with the Windows system. Among the items that you download from Sun are three very important files:

  • comm.jar
  • win32com.dll
  • javax.comm.properties

For the JVM to recognize the serial ports, proper placement of these files is important. I’ve read lot’s of messages on the boards, tried various ways of placing these files in a Windows system, and have found the following installation methods to be effective:

comm.jar should be placed in:

 
    %JAVA_HOME%/lib
%JAVA_HOME%/jre/lib/ext

win32com.dll should be placed in:

    %JAVA_HOME%/bin
%JAVA_HOME%/jre/bin
%windir%System32

javax.comm.properties should be placed in:

 
    %JAVA_HOME%/lib
%JAVA_HOME%/jre/lib

Once you’ve done this, compile and execute one of the sample programs provided by Sun to ensure that you’ve properly installed everything.

Installing the Java Communications API to run in JBuilder

Depending on how you’ve configured JBuilder, you may need to install the win32com.dll and the javax.comm.properties files in their corresponding directories within the JVM running under JBuilder. For example, I use the JVM that came with JBuilder 9 as well as an external JVM I downloaded from Sun. To make JBuilder properly run the api, I placed the winw32comm.dll and the javax.comm.properties file in their corresponding folders underneath the path C:JBuilder9jdk1.4.

Next you need to add the comm.jar package to JBuilder. This is relatively simple. On the JBuilder Menu across the top click on Project

Click on Project Properties:

Select the Required Libraries tab:

Click the add button:

Click on the New button and enter a name for your library and select the path to the Java Communications API and click on the OK button:

Now you’re ready to write your first program using the Java Communications API.

The Program

The example we’re going to use reads input from one serial port (COM4) and sends data to another serial port (COM 5) (note: You do not have to use COM4 and COM5. Use any available serial ports on your system.). Our program is named ComContol.

ComControl


package serialio;

//Sun's serial port driver
import javax.comm.*;
import java.io.*;
import java.util.*;

public class ComControl implements Runnable, SerialPortEventListener {
    static CommPortIdentifier portId1;
    static CommPortIdentifier portId2;

    InputStream inputStream;
    OutputStream outputStream;
    SerialPort serialPort1, serialPort2;
    Thread readThread;
    protected String divertCode = "10";
    static String TimeStamp;

To access the comm.jar package we must first import javax.comm.*. We also import java.io.* to gain access to the InputStream and OutputStream classes. Finally, we import the java.util.* class to provide access to the EventListener class.

Notice that ComControl implements the Runnable interface. We implement the Runnable interface because we want the class to run as a thread. There are two ways we could do this. First, we could extend the Thread class. In doing so, we would have access to all of public methods of Thread. Or, we could implement the Runnable interface. It provides a single abstract method called run(). Using one or the other really depends on what we’re doing. In this example we’re overriding the run() method and not any other methods of Thread. A good rule of thumb is to implement Runnable when overriding the only run() method, extend Thread to access its other methods.

Note: We didn’t need to import a package for the Thread class because it is part of the java.lang package.

ComControl also implements the SerialPortEventListener interface. Like other event listeners, the SerialPortEventListener interface extends the EventListener interface (defined in the java.util package). Without getting into too much detail, listeners use Java’s delegation model where events are treated as objects. The delegation model allows a program to delegate event handling to a listener by registering the listener at runtime. Whenever an event occurs for a particular listener, it is dispatched to all of the listeners registered for it. The event is dispatched by calling a method of the corresponding event interface.

We now need to set up some variables. Since we’re using two serial ports, we define the objects portId1 and portId2 with a datatype of CommPortIdentifier. CommPortIdentifier is the main class that controls access to serial ports. CommPortIdentifier is used to negotiate with the driver to discover which communication ports are available and then select a port for opening. It uses methods in other classes like CommPort and SerialPort to communicate to the port.

We must also set up an InputStream (inputStream) to read the data coming through the serial port and an OutputStream (outputStream) to send data to the serial port. The InputStream class is part of the java.io package.

Next, we create some SerialPort objects: serialPort1 and SerialPort2. SerialPort is an abstract class that defines the low-level interface to a RS-232 device such as a serial port. It defines the minimum functionality needed to communicate with a serial port.

We define a Thread (readThread) because earlier we implemented the Runnable interface. As a result, we’re going to instantiate a Thread object after we’ve taken care of some housekeeping items.

main() method


    public static void main(String[] args) {

        try {
          portId1 = CommPortIdentifier.getPortIdentifier("COM4");
          portId2 = CommPortIdentifier.getPortIdentifier("COM5");
          ComControl reader = new ComControl();
      }

    catch
      (Exception e) {
      TimeStamp = new java.util.Date().toString();
      System.out.println(TimeStamp + ": COM4 " + portId1);
      System.out.println(TimeStamp + ": COM5 " + portId2);
      System.out.println(TimeStamp + ": msg1 - " + e);
     }
    };

Let’s now take a look at the main() method. We start by putting everything in a try block. We do this because it is possible that the serial ports we’re looking for do not exist or are busy. Inside our try block we attempt to get a Handle for the serial ports (portId1 and portId2) by executing the ComPortIdentifier.getPortIdentifer() method. This method returns a ComPortIdentifier object and throws a NoSuchPortException if a particular port does not exist. The last thing we do in the try block is instantiate ComControl using a constructor that we will talk about in the following section.

After the try block, we have a catch block. In this example, we’re catching any Exception that may occur. We could just have easily caught just the NoSuchPortException. The first thing we do is load the current date and time into a string called TimeStamp. While it’s not necessary to do this, it is useful if for logging to an external file.

Using System.out.println(), we display the port identifier objects for COM4 and COM5. We want to see if either of the port identifier objects are null…a null value indicates that the program could not find the port. Finally, we display the exception itself.

ComControl() Constructor


    public ComControl() {

        try {
            TimeStamp = new java.util.Date().toString();
            serialPort1 = (SerialPort) portId1.open("ComControl", 2000);
            System.out.println(TimeStamp + ": " + portId1.getName() + " opened for scanner input");
            serialPort2 = (SerialPort) portId2.open("ComControl", 2000);
            System.out.println(TimeStamp + ": " + portId2.getName() + " opened for diverter output");

        } catch (PortInUseException e) {}
        try {
            inputStream = serialPort1.getInputStream();
        } catch (IOException e) {}
        try {
            serialPort1.addEventListener(this);
        } catch (TooManyListenersException e) {}
        serialPort1.notifyOnDataAvailable(true);
        try {

            serialPort1.setSerialPortParams(9600,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_NONE);

            serialPort1.setDTR(false);
            serialPort1.setRTS(false);

            serialPort2.setSerialPortParams(9600,
                SerialPort.DATABITS_7,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_EVEN);

        } catch (UnsupportedCommOperationException e) {}

        readThread = new Thread(this);
        readThread.start();
    }

We start the ComControl() constructor with a try block. In this case, we are instantiating SerialPort objects (serialPort1 and serialPort2) by executing portId1’s and portId2’s open() method. Recall portId1 and portId2 are ComPortIdentifier objects and the open() method comes from ComPortIdentifier. It throws the PortInUseException. In our example, we’ll ignore this exception and go on. In the real world you would most likely want to handle this exception.

The open() method we’re using in this example has two parameters. The first parameter is a string containing the name of the application opening or owning the port. This parameter is useful in resolving contention issues between programs. The next parameter is an int containing the timeout in milliseconds that the open() method will wait for a port to open.

We have added a couple of System.out.println() methods that use our TimeStamp string and a ComPortIdentifier.getName() method (portId1.getName() and portId2.getName() where portId1 and portId2 are ComPortIdentifier objects). The getName() method returns the system assigned name of the serial port. In our case this should be COM4 and COM5.

In order to check for incoming data on the serialPort1 object we just created, we need to call its .getInputStream() method. We’ll see the results of this event later. The .getInputStream() method throws an IOException. We want to ignore this because often the serial port will be idle.

After we’ve opened the ports, we need to register an EventListener for COM4. Again we create a try block and invoke the addEventListener() method within it. The exception for the addEventListener is TooManyListenersExeception. In this case, that’s not an issue so we’ll ignore the exception.

Next we define the properties of our serial ports using the setSerialPortParams() method. This method allows us to set the flow control, data bits, parity, and stop bits. We are concerned only with the Data bits, stop bits, and parity. The device that we’re using in this case does not have the Data Terminal Ready (DTR) wire pinned out so we’ll use the serialPort1.setDTR() method to set DTR to false. Our input device doesn’t support Request To Send (RTS) handshaking either, we’ll use the serialPort1.setRTS() method to set RTS to false.

Note: The settings I use in this example apply to the serial devices I’m connecting to. Your serial devices may (probably) have completely different settings. Check with your device manual for the proper settings.

The last thing we want to do is to create a Thread. This We define a Thread (readThread) and execute its start() method.

run() method

public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {}
    }

As noted earlier, we implemented the Runnable interface. Now, we override the run() method of that interface. Reviewing the code above you’ll see that we’re calling the Thread.sleep() method within a try…catch block. The sleep() method is actually an overloaded method within the Thread class. It can be passed either a long that represents the time in milliseconds that you want the thread to cease execution, or it can be passed a long and an int that represents the time in milliseconds plus the time in nanoseconds that a thread will sleep. We want the thread to sleep for 100 milliseconds.


serialEvent() method

Now that we’ve built the code to create and start a SerialPortEventListener object (ComControl), we need to do something when events occur. Thus, we override the SerialPortEventListener interface’s abstract serialEvent() method. This method is executed when a serial event occurs. It receives a SerialPortEvent object.

Let’s take a look at the code.


    public void serialEvent(SerialPortEvent event) {
        switch(event.getEventType()) {
        case SerialPortEvent.BI:
        case SerialPortEvent.OE:
        case SerialPortEvent.FE:
        case SerialPortEvent.PE:
        case SerialPortEvent.CD:
        case SerialPortEvent.CTS:
        case SerialPortEvent.DSR:
        case SerialPortEvent.RI:
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
            break;
        case SerialPortEvent.DATA_AVAILABLE:
            StringBuffer readBuffer = new StringBuffer();
            int c;
            try {
                 while ((c=inputStream.read()) != 10){
                   if(c!=13)  readBuffer.append((char) c);
                 }
                 String scannedInput = readBuffer.toString();
                 TimeStamp = new java.util.Date().toString();
                 System.out.println(TimeStamp + ": scanned input received:" + scannedInput);
                 inputStream.close();
                 if(scannedInput.substring(0,1).equals("F")){
                   outputStream = serialPort1.getOutputStream();
                   outputStream.write(divertCode.getBytes());
                   System.out.println(TimeStamp + ": diverter fired");
                   outputStream.close();
                   } else {
                   System.out.println(TimeStamp + ": diverter not diverted");
                   }
            } catch (IOException e) {}

            break;
        }
    }

Looking at the code you’ll see a set of case statements. We test these case statements by using event.getEventType() method; where event is the object that was passed to the serialEvent() method. The getEventType() method returns an int.

Although I’ve defined several cases here, primarily to show you everything you can check for in a serial event, in this example you and I are only interested in 2 events: SerialPortEvent.OUTPUT_BUFFER_EMPTY and SerialPortEvent.DATA_AVAILABLE. In the case of OUTPUT_BUFFER_EMPTY, we want to break out of the serialEvent method altogether because we’ve finished receiving data. In the case of DATA_AVAILABLE, we want examine the data coming from our serialPort1 object and, if it’s the data we’re looking for, send data to our serialPort2 object.

If you refer back to the ComControl() constructor, you’ll see that we attempted to read bytes into our static inputStream object using the serialPort1.getInputStream() method. In the DATA_AVAILABLE case we’ll interpret the bytes coming from the serialPort1 object. We’ve created a while block that reads each byte coming in from serialPort1 and tests to see whether the byte is an ANSI character 10 (linefeed) or an ANSI character 13 (carriage control). The reason we do this is because the input device is sending a carriage control/linefeed at the end of each transmission. If the inputStream byte is anything other than a carriage control/linefeed we want to store in our readBuffer object.

As soon as a carriage return/linefeed combination is encountered we break out of the while block. Although we can deal with a Stringbuffer, depending on the situation my personal preference is to use the toString() method in order to create a String object. We will create String object is called scannedInput. And, because I like to see what’s going on during program execution, we’ll write the info using System.out.println().

If the first character of the scannedInput string is an “F” then we want to send a signal to the serialPort2 object. Serial port objects can be written to using OutputStream objects. First, we obtain serialPort2’s OutputStream by calling the getOutptStream() method. Previously we created a String called divertCode that contains “10”. In our example, a divertCode of “10” tells a Program Logic Controller (PLC) to fire a particular diverter. Since we want to write this string to our serialPort2 object, we use the .write() method of the OutputStream object outputStream. The write() method is looking for an array of byte datatypes, so we have to convert the divertCode string to a byte array using the .getbBytes() method. After we’ve written to serialPort2, we log the information and break out of the serialEvent method and wait for the next serialEvent to occur.

Summary

While java is generally a platform independent language, there are times that we need to perform platform specific functions such as RS-232 communications. The Java Communications API allows us to do this in straightforward manner. In this article, we focused on reading and writing serial i/o. Although we covered the basics, I believe you’ll find this information useful as you build your own RS-232 applications.


About the Author

Rick Proctor has over 20 years experience in the IT industry. Since 2001 Rick has been Vice President of Information Technology for Thomas Nelson, Inc. Rick can be reached at tech_dude@yahoo.com. Read his blog at rickproctor.blogspot.com.

The source code for ComControl.java can be found at CodeCentral.


Server Response from: ETNASC01