Serial ports

22 Apr

Serial ports
We are now going to examine how to access your computer’s serial ports. In order to do this, we need some sort of serial device to talk to. A serial port loopback plug is probably the simplest. These are very cheap and easy to make using the proper female connector for your serial port. The necessary connections are shown in the figures below for both 9- pin and 25-pin serial ports. Note that the dotted line is an optional wire that some loopback plugs have. This is not necessary for the examples in the book (but it won’t hurt either). Once your loopback plug is installed on your serial port, compile and run the program SerialLoopTest.java in the listing below. Don’t forget that you need comm.jar on your CLASSPATH environment variable for this to compile and run.

Listing 3-22: SerialLoopTest.java

import java.io.*;
import java.util.*;
import javax.comm.*;
public class SerialLoopTest implements Runnable,
SerialPortEventListener {
static CommPortIdentifier portId;
static Enumeration portList;
static InputStream inputStream;
static OutputStream outputStream;
static SerialPort serialPort;
Thread readThread;
static String message2send = “Hello Port!”;
static String messagereceived;
static byte[] inbuf = new byte[20];
int i = 0;
static String portname;
public static void main(String[] args) {
// check out the command line args
if (args.length < 1) {
System.out.println( “Specify a port! (COM1 or /dev/ ttyS0).” );
return;
} else {
portname = args[0];
System.out.println( “Testing port: “ + portname );
}
try {
// Get the ID of this port
portId =
CommPortIdentifier.getPortIdentifier(portname);
// Is it a serial port?
if (portId.getPortType() !=
CommPortIdentifier.PORT_SERIAL)
System.out.println( “Port is not a serial port”);
return;
}
}
catch(NoSuchPortException e) {
System.out.println(“No Such Port!”);
return;
}
catch (Exception e) { System.out.println(e); }
SerialLoopTest tester = new SerialLoopTest();
}
public SerialLoopTest() {
try {
serialPort = (SerialPort) portId.open(“Serial Loop Test”, 2000);
}
catch(PortInUseException e) {
System.out.println(“Port In Use.”);
return;
}
catch (Exception e) {
System.out.println(e);
return;
}
try {
serialPort.addEventListener(this);
}
catch (TooManyListenersException e) {
System.out.println(e); }
// Turn on some notifiers so we can catch them with an event listener.
serialPort.notifyOnDataAvailable(true);
serialPort.notifyOnCTS(true);
serialPort.notifyOnDSR(true);
// We don’t really need to set the port parameters for a loop back test
// but if you did, this is how you would.
try {
serialPort.setSerialPortParams(19200,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
}
catch (UnsupportedCommOperationException e) {
System.out.println(e);
}
try {
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
}
catch (IOException e) { System.out.println(e); }
readThread = new Thread(this);
readThread.start();
}
public void run() {
// send characters out the port
try {
outputStream.write( message2send.getBytes() );
}
catch (IOException e){ System.out.println(e); }
System.out.println(“Flipping  RTS…”);
serialPort.setRTS( ! serialPort.isRTS() );
System.out.println(“Flipping  DTR…”);
serialPort.setDTR( ! serialPort.isDTR() );
try { Thread.sleep(1000); } catch (Exception e) { }
serialPort.removeEventListener();
serialPort.close();
System.out.print( i + “ bytes read from port “ + portname
+ “. “ );
if (i<1) {
System.out.println( “Maybe something is not working.” );
}
else { System.out.println();                                }
}
public void serialEvent(SerialPortEvent event) {
// determine whuch event has happened
switch(event.getEventType()) {
case SerialPortEvent.BI:
case SerialPortEvent.OE:
case SerialPortEvent.FE:
case SerialPortEvent.PE:
System.out.println(“Some status line changed.”);
break;
case SerialPortEvent.CD:
System.out.println(“Status line CD changed.”);
break;
case SerialPortEvent.CTS:
System.out.println(“Status line CTS changed.”);
break;
case SerialPortEvent.DSR:
System.out.println(“Status line DSR changed.”);
break;
case SerialPortEvent.RI:
System.out.println(“Status line RI changed.”);
break;
case  SerialPortEvent.OUTPUT_BUFFER_EMPTY:
System.out.println(“Buffer  Empty”);
break;
case  SerialPortEvent.DATA_AVAILABLE:
byte[] readBuffer = new byte[20];
try {
while (inputStream.available() > 0) {
int numBytes = inputStream.read(readBuffer);
i += numBytes;
messagereceived = new String(readBuffer);
System.out.println(“Read: “ + messagereceived);
}
}
catch (IOException e) { System.out.println(e); }
break;
}
}
}

The output looks like this:

C:\> javac SerialLoopTest
C:\> java SerialLoopTest COM1
Testing port: COM1
Read: Hello Po
Read: rt!
Flipping RTS…
Status line CTS changed.
Flipping DTR…
Status line DSR changed.
11 bytes read from port COM1.

Notice that the “Hello Port!” is split between two reads. This will vary depending on the capability of your computer and what other applications are running. For Linux machines you would use /dev/ttyS0 (or the name of your serial port) and you will get the same output. The line portId = CommPortIdentifier.getPortIdentifier(portname); finds the port named by portname and assigns its identifier to portId. Once you have the port identifier you can then determine the type of port (getPortType()), its name (getName()), and if it is already owned by another process (isCurrentlyOwned(); and getCurrentOwner()). This port identifier is the internal Java name for the port that you might call COM1 or /dev/ttyS0. The getPortIdentifier() method can throw the NoSuchPortException exception if this port does not exist. This is not always accurate. Many PC motherboards are configured to support two serial ports but with only one port having the necessary driver chips installed (to save money). Java will not throw the NoSuchPortException exception for this; it thinks the port is really there. Also, on Linux, if you have a real serial port that you don’t have permission to read or write to, Java will throw the NoSuchPortException exception. This method will catch clearly wrong port names (such as /dev/hda1 or CON1).

The line serialPort = (SerialPort) portId.open(“Serial Loop Test”, 2000); opens the serial port and assigns an owner to it. The number 2000 is a timeout value. The open method will wait this many milliseconds for the port to open before timing out. If the port is owned by another Java application then this method will throw a PortInUseException exception. If the port is owned by another, non- Java, application then this exception will not be thrown. We have found this does not work reliably, tending not to notice if any application (Java or not) is using a serial port. We then set up an event listener, serialPort.addEventListener(this); so we can watch the activity on the serial port. The event listener, further down in the listing, will tell us what activity is going on for that port. We tell the event listener that we are particularly interested in the following events:

serialPort.notifyOnDataAvailable(true);
serialPort.notifyOnCTS(true);
serialPort.notifyOnDSR(true);

The line serialPort = (SerialPort) portId.open(“Serial Loop Test”, 2000); opens the serial port and assigns an owner to it. The number 2000 is a timeout value. The open method will wait this many milliseconds for the port to open before timing out. If the port is owned by another Java application then this method will throw a PortInUseException exception. If the port is owned by another, non- Java, application then this exception will not be thrown. We have found this does not work reliably, tending not to notice if any application (Java or not) is using a serial port. We then set up an event listener, serialPort.addEventListener(this); so we can watch the activity on the serial port. The event listener, further down in the listing, will tell us what activity is going on for that port. We tell the event listener that we are particularly interested in the following events:

serialPort.setSerialPortParams(19200,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);

In this case, setting these parameters has no effect on the workings of this program. I show these here as examples only. Once the port is open and configured, we can start reading from and writing to it. The lines:

inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();

get the input and output streams for this port so we can read and write to it. We then start a thread running to talk to the port.

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

This doesn’t need to be in a thread, but this is a common way to do this. The rest of the communication with the serial port takes place in the run method and in the serialEvent method. This is where the loopback testing begins; the run method

writes data and changes status lines while the serialEvent method reads the data and watches the status lines. The run method does three things. We write a message to the serial port using the outputStream.write method, we then check the RTS and DTR status lines, and invert them.

outputStream.write( message2send.getBytes() );
serialPort.setRTS( ! serialPort.isRTS() );
serialPort.setDTR( ! serialPort.isDTR() );

Originally we tried setting the status lines to either true or false but we discovered that they are left in an arbitrary state and this technique didn’t work very satisfactorily. Besides, this gives us a chance to try using some of the methods to examine the state of the status lines (like isDTR(), isRTS()). We are pretty much done with sending data to the port. The sleep is necessary so the program won’t exit before the event listener has a chance to catch the activity and report on it. Don’t forget to remove the event listener and close the port. While this may not seem like an important thing to do for this little simple program, we found that on some Windows machines the Java program won’t exit and return to the shell until these are done.

try { Thread.sleep(1000); } catch (Exception e) { }
serialPort.removeEventListener();
serialPort.close();

As soon as the data is sent, the SerialEvent event listener is triggered. The serialEvent listener is a simple switch that examines the event type and reports that. In the case of the event being SerialPortEvent.DATA_AVAILABLE we then read from the serial port’s input stream.

while (inputStream.available() > 0) {
int numBytes = inputStream.read(readBuffer);
i += numBytes;
messagereceived = new String(readBuffer);
System.out.println(“Read: “ + messagereceived);
}

Once again, we want to call your attention to the output from this program. Notice that not all of the data is read in at the same time. This could be the result of many different things, such as the process being interrupted by another event or the data being sent slower than it is being read. In either case, this is probably typical for an event listener: you will not get everything you are expecting in one event. This can be dealt with by simply implementing a buffer in your program and each read appends the data to the buffer.

Summary
While some of these examples have been fairly detailed, they are a far cry from a full tutorial on each topic. We hope to have shown you a wide sampling of the methods and techniques you will need to assist you in developing robust programs for communicating with your network-enabled devices. There are a number of excellent references for Java. If you need more detail than what is presented in this quick review, then you should check the references listed at the end of this chapter.

References
1.  Java 2 Platform, Standard Edition, v 1.3 API Specification, http://java.sun.com/j2se/1.3/docs/api/index.html

2.  Campione, Mary and Walrath, Kathy, The Java Tutorial Second Edi- tion: Object-Oriented Programming for the Internet, http://web2.java.sun.com/docs/books/tutorial/, Addison-Wesley, 1998.

3.  Chan, Patrick and Lee, Rosanna, The Java Developers Almanac 2000, Addison-Wesley, 2000.

4.  Harold, Elliotte Rusty, Java Network Programming, O’Reilly & Associates, 2000.

5.  The Java Language Specification, http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html.

6.  Interfacing the Serial/RS232 Port, http://www.senet.com.au/~cpeacock (one of the many available loopback plug pinout
diagrams).

Random Posts

Comments are closed.