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