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
|