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 https://java.sun.com/products/javacomm.
There are also third party APIs and an open source implementation from https://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 [email protected]. Read his blog at rickproctor.blogspot.com.
The source code for ComControl.java can be found at CodeCentral.
Connect with Us