Home : Course Map : Chapter 23 :
Serial Port IO Demo
JavaTech
Course Map
Chapter 23

Access Platform
System Properties
  Demo 1
Run Ext. Programs
  Demo 2
Java Comm API
Port Classes
Port Operations
  Demo 3   Demo 4   Demo 5
Serial Port IO
Demo: Serial Port
  Code Listings

    Demo 6
Parallel Port
Port I/O Apps
Exercises

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

Here we describe a program to connect over the serial line to a device that sends a temperature reading. In Chapter 24: Javelin Demo 1 we will describe in detail this Java powered device that does the temperature measurements. Here we just assume that there is a device at the other end of the serial line that understands our protocol for requesting the temperature value.

The program uses the classes and methods discussed in the previous sections for communications over the serial port. The program illustrates how you might use a Java program to obtain data from any device via a serial line.

The program will also work with the client/server system discussed in Chapter 14 and so illustrate a complete system for access to data from a remote device over the Internet.

We have programmed the external device to follow a simple protocol for obtaining its data. This protocol requires that the application on the desktop (or whatever platform with JVM and javax.comm) first sends a password (actually a two byte numerical value) to the device. If this is accepted, the device program will send a two byte integer data value for the temperature in units of 0.5 degrees Celsius.

The desktop (or any platform that can handle J2SE and javax.comm)  program consists of two primary classes:

  • GetJavelinData is in charge of the sending and receiving data over the serial port with the device (a Javelin Stamp board discussed in Chapter 23). This class implements the Runnable interface and its run() method contains a loop that first sends the password and then reads the value sent to it from the sensor. It converts each byte pair received into a 4-byte int value using the byte array to primitive types techniques discussed in Chapter 9. Before repeating the data read operation, the loop pauses for a period whose length is passed via the constructor parameter.

  • SerialToJavelin provides a graphical user interface with a dropdown menu on the menu bar that lists the serial ports on the platform. Selecting one of these will cause the program to attempt to open the port. If it succeeds, it passes the port s SerialPort object to an instance of GetJavelinData. Hitting the Go button will invoke the start() method in the  GetJavelinData object, which will begin its data taking loop. Hitting Stop will invoke the stop method in that object. A text area displays the temperature values, which are printed from the GetJavelinData loop via print methods of the Outputable interface. A file menu provides for saving this data to a file.

The complete code listings for these two classes can be found on the following page.

When the program SerialToJavelin first begins, it builds a menu with the names of the serial ports on the platform. It invokes the method getPorts(), shown below, which uses the CommPortIdentifier static method getPortIdentifiers() to obtain the CommPortIdentifier object for each port on the platform. The serial types of ports are saved in an instance of Hashtable.

 The getPorts() method in SerialToJavelin
  /**
    *  Use the CommPortIdentifier static method to obtain a list of
    *  ports on the platform. Pick out the serial ports from the
    *  list and save in a Hash table. Use the port names as keys.
   **/
  static void getPorts () {
    // First get the list of ports on this platform
    Enumeration port_list = CommPortIdentifier.getPortIdentifiers ();

    // Scan through the list and get the serial ports
    while (port_list.hasMoreElements ())  {
      CommPortIdentifier port_id =
         (CommPortIdentifier)port_list.nextElement ();

      if  (port_id.getPortType () == CommPortIdentifier.PORT_SERIAL) {
          fNumSerialPorts__++;
          fSerialTable__.put (port_id.getName (), port_id);
      }
    }
  } // getPorts

 

When the user selects a serial port from the menu the actionPerformed() method is invoked. The following code shows the actions taken to open the port.

 The actionPerformed() method in SerialToJavelin
  public void actionPerformed ( ActionEvent e ) {
    boolean status = false;

    String command = e.getActionCommand ();

    if  (command.equals ("Go")) {

      ... tests and actions for other commands ...
    }

    // Scan the serial ports names to look for a match
    // to the menu items.
    Enumeration enum_ports = fSerialTable__.keys ();
    while (enum_ports.hasMoreElements ()) {
      String port_name =  (String)enum_ports.nextElement ();

      if (command.equals (port_name) )  {
          fSelectedPortID__ =  
           (CommPortIdentifier)fSerialTable__.get (port_name);
          if  (fCurrentPort__ != null) fCurrentPort__.close ();

          // Open port Allow for 20 seconds block
          try {
            fCurrentPort__ =
              (SerialPort)fSelectedPortID__.open (
                         "Serial to Javelin",20000);
          }
          catch (PortInUseException pie) {
            println ("Error: Port in use");
            fCurrentPort__ = null;
            fSelectedPortID__ = null;
            return;
          }

          // set up the serial port
          try {
            fCurrentPort__.setSerialPortParams (
                        BAUD_RATE, SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
          }
          catch (UnsupportedCommOperationException uce) {
            // This error shouldn't happen
          }

          try {
            getStreams ();
          }
          catch (IOException ioe) {
              println ("Error: Cannot open streams to port");
              fCurrentPortLabel.setText ("No Port Open");
              fCurrentPort__.close ();
              return;
          }
          fCurrentPortLabel.setText (port_name);
          fGetJavelinData.setStreams (fPortInStream,fPortOutStream);
          fGoButton.setEnabled (true);
      }
    }
  } // actionPerformed ()

 

The code first looks for a match to the port name. It then attempts to open the port. If it succeeds it sets the serial port parameters to a baud rate of 9600, eight data bits, one stop bit, and no parity. The serial line device (a Javelin Stamp evaluation card in this case) must, of course, use the same settings.

The input and output streams for the serial port are then obtained. The input stream is wrapped as a DataInputStream and the Output stream is wrapped with a PrintStream.

  void getStreams() throws IOException {
      portInStream =
          new DataInputStream(currentPort.getInputStream());

      portOutStream =
          new PrintStream(currentPort.getOutputStream(), true);
  }

When the user hits the Go button, the actionPerformed() method is invoked again and the code section for the Go command invokes the start() method in the GetJavelinData, which is shown on the following page.

The parameters in the constructor,

  GetJavelinData(Outputable parent, int rate)

include a reference to the Outputable parent that will be used for callbacks to the  instance of SerialToJavelin. This class implements the interface by overriding its print(String) and println(String) methods with ones that place strings on the text area in the user interface. The rate parameter determines how often the data value from the device is read.

The data from the sensor arrive over the serial port as a sequence of bytes. Each pair of bytes must be converted to an int primitive value. We use the techniques described in Chapter 9 in which a byte array becomes the source for a ByteArrayInputStream, which in turn is wrapped with a DataInputStream class. The latter class offers the readInt() method that will return an  int value with the data bytes in the lower 2 bytes of the 4 byte value.

Note that the programs in the desktop and in the device follow Java's standard big endian format in which the bytes arrive in order of the highest order byte first and the lowest order byte last. [Since in big endian the highest order byte is in the lowest address and the lowest order byte is the highest address. Sending the data would start at the low end of the memory address, e.g. an array, and work upward.]

The loop in the run() method first sends the password, which here consists of just a two byte value that will match a value set in the device program. If the password is not accepted, the thread processing stops. Otherwise, the raw data value is read. Then a calibration is done, which here simply consists of a slope and offset correction. (The data is generated in units of 0.5 degrees Celsius.) The Outputable reference provides for a callback to the user interface to print the temperature value.

Though many improvements and custom features can be added, this program illustrates all the essentials of communicating over the serial line with a device such as a sensor to obtain data of interest. In Chapter 23 we will show how to set up the other end of the line with a device that illustrates the application of embedded Java processors.

 

References and Web Resources

 


Latest update: Dec. 13, 2004
  
  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.