Home : Course Map : Chapter 23 :
Serial Port I/O 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

Below we show the classes GetJavelinData and SerialToJavelin with which we connect  to a device over the serial line to obtain temperature readings. (See Chapter 24: Javelin Demo 1 for a description of the Java powered device that does these temperature measurements.)

GetJavelinData does the low level work of opening a serial port, sending a request to the device for data, and receiving the data. The SerialToJavalin class provides the user interface for controlling the I/O with the device and displaying the data. It uses an instance of GetJavelinData to do the serial I/O. It implements the Outputable interface and sends text to the JTextArea component..

The GetJavelinData and SerialToJavelin classes are described in more detail in the previous section.

SerialToJavaelin in Action


SerialToJavelin
+Outputable
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.io.*;
import javax.comm.*;
import java.util.*;

/**
  * This class provides a user interface for communications with a
  * device connected via the serial port. In this case, the device is
  * the Javelin evaluation card discussed in Chapter 24. The class is
  * intended to demonstrate the classes, methods, and fields in the
  * javax.comm package needed to access and do I/O over a serial port.
  *
  * The interface includes a frame with a menu bar. One menu provides
  * a list of serial ports to select that is connected to the device. A
  * JTextArea will show output from the device. The class implements the
  * Outputable interface in which the methods print (String) and
  * println (String) sent strings to the text area.
  *
  * The class creates an instance of the GetJavelinData class, which
  * has methods for communicating with the Javelin. For example, the
  * bytes that stream in from the serial line must be converted to
  * character or int primitive types. The methods "call back" via an
  * Outputable reference with which the print (String),
  * println (String) methods will send output to the text area.
  *
  * A JFileChooser is provided to select file names for saving the data.
  * Also, included is FileFilter subclass to use with the FileChooser
  * for selecting web pages.
  *
 **/
public class SerialToJavelin extends JFrame
       implements ActionListener, Outputable
{
  static int fNumSerialPorts__ = 0;
  static Hashtable fSerialTable__ = new Hashtable ();
  static CommPortIdentifier fSelectedPortID__;
  static SerialPort fCurrentPort__;

  int BAUD_RATE = 9600;

  // The input and output streams over the port
  DataInputStream fPortInStream;
  PrintStream     fPortOutStream;

  // GUI Components
  JLabel fCurrentPortLabel;
  JMenuItem fMenuSave;
  JMenuItem fMenuClose;
  JTextArea fTextArea;
  JButton fGoButton;
  JButton fResetButton;
  JButton fStopButton;

  // Filter for the JFileChooser
  DataFilter fDataFilter = new DataFilter ();

  // Reference to the helper class object
  GetJavelinData fGetJavelinData = null;
  // ------------------------------------------------------------------

  public static void main (String [] args) {
    // Get the table of serial ports for the current platfrom.
    getPorts ();

      // Can pass frame title in command line arguments
    String title="Serial Comm to Javelin";
    if  (args.length != 0) title = args[0];

    SerialToJavelin f = new SerialToJavelin (title);
    f.setVisible (true);
  } // main
  // ------------------------------------------------------------------

  /** Pass a title to the frame via the constructor  argument. **/
  SerialToJavelin (String title) {
    super (title);

    Container content_pane = getContentPane ();

    // Create a user interface.
    content_pane.setLayout ( new BorderLayout () );
    fTextArea = new JTextArea ("");
    // Add to a scroll pane so that a long list of
    // data can be seen.
    JScrollPane area_scroll_pane = new JScrollPane (fTextArea);

    content_pane.add ( area_scroll_pane, "Center");

    // Control panel
    JPanel control_panel = new JPanel (new GridLayout (1,4));
    fCurrentPortLabel = new JLabel ("No Port Open");
    control_panel.add (fCurrentPortLabel);

    fGoButton = new JButton ("Go");
    fGoButton.setEnabled (false);
    fGoButton.addActionListener (this);
    control_panel.add (fGoButton);

    fStopButton = new JButton ("Stop");
    fStopButton.addActionListener (this);
    fStopButton.setEnabled (false);
    control_panel.add (fStopButton);

    fResetButton = new JButton ("Reset");
    fResetButton.setEnabled (false);
    fResetButton.addActionListener (this);
    control_panel.add (fResetButton);

    // Put controls below the text area.
    content_pane.add (control_panel, "South");

    // Use the helper method makeMenuItem
    // for making the menu items and registering
    // their listener.
    JMenu m = new JMenu ("File");
    m.add (fMenuSave  = makeMenuItem ("Save"));
    m.addSeparator ();
    m.add (fMenuClose = makeMenuItem ("Quit"));

    JMenu mPorts = new JMenu ("Ports");

    // List the serial ports
    Object [] port_names = fSerialTable__.keySet ().toArray ();
    Arrays.sort (port_names);
    for  (int i=0; i < port_names.length; i++) {
       mPorts.add (makeMenuItem ( (String)port_names[i]));
    }

    JMenuBar mb = new JMenuBar ();
    mb.add (m);
    mb.add (mPorts);

    setJMenuBar (mb);
    setSize (400,400);

    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Create Java data object
    fGetJavelinData = new GetJavelinData (this, 1);

  } // ctor
  // ------------------------------------------------------------------

  /**
    *  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
  // ------------------------------------------------------------------

  /** Open the input and output streams **/
  void getStreams () throws IOException {
      fPortInStream =
          new DataInputStream (fCurrentPort__.getInputStream ());
      fPortOutStream =
          new PrintStream (fCurrentPort__.getOutputStream (), true);
  } // getStreams
  // ------------------------------------------------------------------

  /**
    * Process events from the frame menu,the chooser, and
    * start the data taking with the GetJavelinData instance.
   **/
  public void actionPerformed ( ActionEvent e ) {
    boolean status = false;

    String command = e.getActionCommand ();

    if  (command.equals ("Go")) {
      fGetJavelinData.start ();
      fGoButton.setEnabled (false);
      fResetButton.setEnabled (false);
      fStopButton.setEnabled (true);

      return;

    } else if  (command.equals ("Stop") ) {
      fGetJavelinData.stop ();
      fGoButton.setEnabled (true);
      fStopButton.setEnabled (false);
      fResetButton.setEnabled (true);
      println ("");
      println ("  Stopped data taking");
      println ("");
      return;

    } else if (command.equals ("Reset")) {
      fTextArea.setText ("");
      return;
    } else if  (command.equals ("Save")) {
       // Save a file
       status = saveFile ();
       if  (!status)
           JOptionPane.showMessageDialog (
           null,
           "IO error in saving file!!", "File Save Error",
           JOptionPane.ERROR_MESSAGE);

      return;

    } else if (command.equals ("Quit")) {
      dispose ();
      return;
    }

    // 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 ()
  // ------------------------------------------------------------------

  /**
    * Outputable interface method intended for printing to
    * a text area. Adds a carriage return to the string.
   **/
  public void println (String str) {
    fTextArea.append (str + CR);
  }
  // ------------------------------------------------------------------

  /**
    * Outputable interface method intended for printing to
    * a text area.
   **/
  public void print (String str) {
    fTextArea.append (str);
  }
  // ------------------------------------------------------------------

  /**
    * This "helper method" makes a menu item and then
    * registers this object as a listener to it.
   **/
  private JMenuItem makeMenuItem (String name) {
    JMenuItem m = new JMenuItem ( name );
    m.addActionListener ( this );
    return m;
  }
  // ------------------------------------------------------------------

  /**
    * Use a JFileChooser in Save mode to select files
    * to open. Use a filter for FileFilter subclass to select
    * for *.java files. If a file is selected, then write the
    * the string from the textarea into it.
   **/
   boolean saveFile () {
      File file = null;
      JFileChooser fc = new JFileChooser ();

      // Start in current directory
      fc.setCurrentDirectory (new File ("."));

      // Set filter for web pages.
      fc.setFileFilter (fDataFilter);

      // Set to a default name for save.
      fc.setSelectedFile (file);

      // Open chooser dialog
      int result = fc.showSaveDialog (this);

      if  (result == JFileChooser.CANCEL_OPTION) {
      return true;
    } else if  (result == JFileChooser.APPROVE_OPTION)
      {
        file = fc.getSelectedFile ();
        if  (file.exists ()) {
            int response = JOptionPane.showConfirmDialog (null,
                "Overwrite existing file?","Confirm Overwrite",
                 JOptionPane.OK_CANCEL_OPTION,
                 JOptionPane.QUESTION_MESSAGE);
            if  (response == JOptionPane.CANCEL_OPTION) return false;
        }
        return writeFile (file, fTextArea.getText ());
      } else {
        return false;
      }
  } // saveFile
  // ------------------------------------------------------------------

  /**
    * Use a PrintWriter wrapped around a BufferedWriter, which in turn
    * is wrapped around a FileWriter, to write the string data to the
    * given file.
   **/
  public static boolean writeFile (File file, String dataString) {

    try {
      PrintWriter out =
        new PrintWriter (new BufferedWriter (new FileWriter (file)));
      out.print (dataString);
      out.flush ();
      out.close ();
    }
    catch (IOException e) {
         return false;
    }
    return true;
  } // writeFile

} // class SerialToJavelin
DataFilter (In SerialToJavelin.java file)
/** Class to use with JFileChooser for selecting *.data files. **/
class DataFilter extends javax.swing.filechooser.FileFilter{

  public boolean accept (File f) {
    return f.getName ().toLowerCase ().endsWith (".data")
        || f.isDirectory ();
  }

  public String getDescription (){
    return "Serial data  (*.data)";
  }
} // class DataFilter
GetJavelinData
import java.io.*;
import javax.comm.*;
import java.util.*;

/**
  * This program communicates over a serial port with the
  * the Javelin evaluation card. It obtains data values from
  * the device following a simple protocol that involves first
  * sending a "password"  (a two byte number) and then reading
  * two bytes at a time that are converted to an int primitive
  * type value.
  *
  * The Javelin has been programmed to respond appropriately
  * to the protocol used here. See Chapter 23 for information
  * about the Javelin programming.
  *
  * The class is runnable so that the data taking loop runs in
  * a thread.
 **/
public class GetJavelinData implements Runnable {

  // These are the input and output streams over the port
  DataInputStream fPortInStream;
  PrintStream     fPortOutStream;

  // This array and streams are used to convert a stream
  // of bytes to primitive type values.
  byte [] fByteArray;
  ByteArrayInputStream fByteIn;
  DataInputStream fDataIn;

  // Values are returned as text to the fParent that
  // implements the Outputable interface.
  Outputable fParent;

  // Use for text from device.
  StringBuffer fStrBuf = new StringBuffer ();

  // Data taking parameters
  //   flag for the data taking thread
  boolean fTakeData = false;
  int fPauseTime    = 1000;

  // Data description and info
  String fDataDescription = "Temperature";
  double fSlope           = 0.5;
  double fOffset          = 0.0;
  String fDataUnit        = "C";

  /**
    * Constructor
    * @param fParent implements the Outputable interface for
    *  print output.
    * @param rate is the data taking rate in number of values
    * per second.
   **/
  public GetJavelinData (Outputable fParent, int rate) {
    this.fParent = fParent;
    this.fPauseTime = 1000/rate; // Pause time in milliseconds

    // Set up the byte array and streams for converting
    // bytes to an int primitive value.
    fByteArray = new byte[4];
    fByteIn = new ByteArrayInputStream (fByteArray);
    fDataIn = new DataInputStream (fByteIn);

  } // ctor

  /** Obtain streams for the serial lines. **/
  void setStreams (DataInputStream inStream,
                  PrintStream outStream) {
      fPortInStream = inStream;
      fPortOutStream= outStream;
  } // setStreams

  /** Start data taking. **/
  public void start () {
    if (fPortInStream != null && fPortOutStream != null) {
      fTakeData = true;
      Thread thread = new Thread (this);
      thread.start ();
    }
  } // start

  /**  Stop data taking. **/
  public void stop ()
  {  fTakeData = false; }


  /**
    * In this method data is obtained in a loop until the fTakeData
    * flag goes false.
    * A simple protocol requires that it first sends a password
    * value to the device using the sendPW () method.
    * If that value is accepted, then receiveInt () gets two
    * bytes from the data stream and returns a int value created
    * from them. The loop pauses for a time set by fPauseTime
    * parameter before getting the next value;
   **/
  public void run () {

    fParent.println ("Begin reading data from port.");

    // Now get data
    do {

      try {
        // The "login"
        if  (!sendPW ()) return;
        // Get the raw data from the serial connection.
        int data = receiveInt ();
        // Calibrate the data.
        double correctedData = fSlope*data - fOffset;
        // Print the corrected data on the Outputable parent.
        fParent.println (fDataDescription +" = " + correctedData
                         + fDataUnit);

      }
      catch (IOException e) {
        fParent.println ("Input or output error: " + e);
        break;
      }
      try {
        // Pause before next data request.
        Thread.sleep (fPauseTime);
      }
      catch (InterruptedException ie) { }
    } while (fTakeData ) ;

  }

  /**
    * For our simple protocol, a value  (0x2201) must first be
    * sent to the device before it will send back data. A text
    * response is returned from the device.
   **/
  public boolean sendPW () {

    // Send 2 byte password.
    byte [] out = new byte[2];
    out[0] = 0x22;
    out[1] = 0x01;
    fPortOutStream.write (out,0,2);
    fPortOutStream.flush ();

    try {
      String reply = receiveText ();
      if  (!reply.equals ("PW OK!")) {
           fParent.println ("Password accepted");
           return true;
      }else{
           fParent.println ("Bad Password!");
           return false;
      }
    }
    catch (IOException e) {
      fParent.println ("Input or output error: " + e);
      return false;
    }
  } // sendPW


  /**
    * Read text one byte at a time. Cast each byte to char
    * and then append to a Stringbuffer.
   **/
  public String receiveText () throws IOException {
    byte ch;
    fStrBuf.delete (0,fStrBuf.length ());
    while  (  (ch =  (byte)fPortInStream.read ()) != -1) {
      fStrBuf.append ( (char)ch);
      // Use \r as an end of text marker.
      if  (ch =='\r') break;
    }
    return fStrBuf.toString ();
  } // receiveText

  /**
    *  Receives 2 bytes from Javelin and converts them to
    *  an int value using a ByteArrayInputStream.
    *
    *  Javelin sends bytes in Big-Endian manner, which means the
    *  high order byte arrives first. For example, if the
    *  value 258=0x0102 were sent from the Javelin, the
    *  byte 0x01 arrives first and then 0x02.
    *
    *  The readInt () method in DataInputStream, which wraps the
    *  ByteArrayInputStream, treats the four bytes in the array
    *  as a four byte int value. So to give a value 258, the
    *  values in the four byte array must go as:
    *
    *   fByteArray=     [0]         [1]       [2]        [3]
    *              [00000000] [00000000] [00000001] [00000010]
    *
    *  We therefore place the first byte  obtained from the Javelin
    *  into the element 2 of the array and the second byte into
    *  the element 3.
   **/
  public int receiveInt () throws IOException {

    int input =0;

    // Read high order byte first
    if ((input = fPortInStream.read ()) == -1) throw new IOException ();
    // A cast to byte truncates 3 top bytes from int value
    fByteArray[2] =  (byte)input;

    // Read low order byte
    if ((input = fPortInStream.read ()) == -1) throw new IOException ();
    fByteArray[3] =  (byte)input;

    ByteArrayInputStream fByteIn = new ByteArrayInputStream (fByteArray);
    DataInputStream fDataIn = new DataInputStream (fByteIn);
    return fDataIn.readInt ();
  } // receiveInt

} // class GetJavalinData

 

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.