HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

The JavaBean Component Architecture provides a means to reuse software and, when combined with tool support, can dramatically increase developer productivity. This model has been realized primarily in graphical display applications with AWT and Java Swing components. Recent advances in J2ME and the release of the Real-Time Specification for Java (RTSJ) allow similar productivity gains to be achieved with real-time and embedded-hardware driver components.

In this article we examine the advantages and disadvantages of using the JavaBean Component Architecture for embedded hardware components. We describe a slight variation of the JavaBean model that's used in the aJile Java processor runtime libraries that make Java components more suited to time-critical and embedded applications. We follow with an example application that uses this component model to program a 100% Java-based controller based on the aJile aJ-80 Java microprocessor for Lego Mindstorms sensors. We conclude with suggestions on how to apply this component model to different applications.

The JavaBean Component Architecture
The JavaBean Component Architecture has been used successfully for graphical applications using AWT and Swing components and by third-party software vendors to distribute reusable software components using standard packaging. The JavaBean model specifies properties that can configure the characteristics of the JavaBean from inside a development environment at design time. It also specifies events that are sent from the JavaBean to the application when an event of interest occurs. The JavaBean component specification also includes support for "bean info" files, and many development tools provide property editors to configure beans appropriately for the application under development. The JavaBean component model is well suited for graphical and nongraphical components for general-purpose computing systems, but, as we shall see, the JavaBean event model is less appropriate for time-critical or embedded systems.

Properties
JavaBean properties are get/set methods used to configure the behavior of the component at design time by a development tool, and at runtime by an application. Some extra overhead is associated with calling these get/set methods; however, it's minimal compared to the benefits gained. This method call overhead is also predictable, making JavaBean properties acceptable for embedded and real-time applications without modification.

Events
JavaBean events include an event listener interface that has one or more methods that will be called when an event is sent. An application implements the interface and registers it with the JavaBean, which calls the event methods at the appropriate time. Each of these event methods must also include a parameter of type EventObject. When a component sends an event, it creates a descendent of EventObject, initializes it, and passes it as a parameter to each registered event listener.

Normally, a JavaBean will create a new EventObject each time an event is sent since an event handler could keep a reference to the EventObject for the future. If the JavaBean reused the same EventObject, the internal state could be changed without the event listener expecting it. This event-transmission technique is less desirable for embedded or time-critical applications, because creating and discarding EventObjects is expensive. The new() operation for the EventObject is generally time-consuming and unpredictable as it could also initiate a garbage-collection cycle at an inappropriate time.

One alternative to creating a new EventObject each time an event is sent is to create one EventObject and reuse it. This would prevent the overhead associated with the new() operation and possible garbage collection at the expense of not strictly adhering to the JavaBean specification. This would also allow development tools to recognize the events and provide full tool support. This alternative still causes some overhead because the fields in the EventObject must be initialized with custom state information and the event handler has to access these fields to get the state information.

Cracked Events
A second alternative is to pass data directly to event handlers as primitive data types, bypassing the EventObject. This has the maximum speed advantage, at the expense of some development tools not recognizing these events as JavaBean events. This type of event is sometimes called a cracked event or primitive event. Development tool support for cracked events that still conform to the JavaBeans naming convention varies from no support to almost full support; Borland JBuilder, for instance, recognizes cracked events (see Figure 1). The Real-Time Specification for Java calls for a special real-time event called AsyncEvent that has no parameters. Perhaps, as the RTSJ gains in popularity, more tools support will be provided for this type of event.

Cracked events and their respective event listeners can be unique for each component and each type of event that's sent, or generic and reused for more than one component and for more than one event in the same component. Unique events have the advantage of being more appropriately named: each event is descriptive and specific to one type of event. Generic events, on the other hand, require fewer class definitions and are independent of any one component. This reduces the number of inner classes required to interface the components and also reduces call overhead. Components utilizing generic events can be wired together many times in a pipeline fashion, with little or no "glue code" required.

The aJile Real-Time Embedded Component Model
The aJile Real-Time Embedded Component Model (aRTEC), a variation on the JavaBean Component Architecture, uses standard JavaBean properties but eschews EventObjects in favor of cracked events. These choices enable development tool support, while avoiding many of the negative runtime consequences of the JavaBean event model.

Three cracked events are used for the majority of the aRTEC components: a TriggerEvent with no parameters, an AssertEvent with one Boolean parameter, and a DataEvent with one int parameter. The trigger event is sent as an indicator that some physical condition has occurred. It could be a timeout or a sensor threshold being crossed. It can also be received to control physical conditions, such as resetting a timer.

An AssertEvent is sent when a state change occurs. A true value in the parameter indicates some state is active and a false value indicates the state is inactive. For example, a lamp component can receive an AssertEvent that will trigger the lamp to turn on when a true value is passed, and turn off when a false value is passed. A DataEvent is used when a value is associated with the event. For example, a rotation sensor can send a DataEvent when the angle of rotation changes; the new angle data is passed as the parameter. There are additional types of cracked events used in the aJile embedded component libraries, but these three have been found to be adequate for most of our real-time needs.

Many JavaBeans can send the same event to more than one registered listener, called multicast events. To implement multicast events, some JavaBeans keep a vector of the registered listeners. When an event occurs, the vector is traversed and the event is sent to each registered listener. Other JavaBeans implement a multicaster, which is an event listener that relays the event to two additional listeners. If one of the additional listeners is also a multicaster, the event is relayed to two more listeners. In this manner any number of listeners can be registered without the use of a vector. The use of the multicasters is hidden behind the JavaBean event registration methods.

The advantages of using multicasters in a real-time environment are clear. When only one listener is registered and an event is sent, the event is sent by a single interface call with no looping and no overhead associated with the vector. However, as more listeners are registered for the same event, events will be routed through multiple multicasters, causing more overhead. At some point, if enough listeners are registered, multicasters will have more overhead than traversing a vector; however, it's rare to have more than one or two listeners for the same event.

TriggerEvents, AssertEvents, and DataEvents all have their own multicaster classes that are used internally to implement multicast events.

Real-Time Components
Often a timer is required in a real-time or an embedded application. The aRTEC OneShotTimer component implements a timer that can be started, stopped, or restarted. When its time expires, it sends a TriggerEvent and waits to be started again. If the timer has been started but has not yet expired, it's considered to be running. If the timer is restarted while it's running, the timer starts over, effectively extending the time delay. When the OneShotTimer is started, it sends an AssertEvent with true as the value to indicate that it's running. When the time expires, it sends an AssertEvent with false as the parameter to indicate the timer is no longer running. At that time a TriggerEvent is also sent.

The OneShotTimer can receive one TriggerEvent to start the timer and one to stop it. A trigger event sent directly to this object will start the timer. Because this class can implement the TriggerEventListener interface only once, an inner class is used to provide a triggerEventListener() method that will stop the timer.

The aRTEC libraries also include a PeriodicTimer and a Counter component with similar interfaces. Generally, these components provide a friendly front end to real-time execution primitives provided by the Real-Time Specification for Java.

Lego Mindstorms Overview
To demonstrate the ability of the aRTEC components to monitor and control embedded hardware, components were written for the Lego Mindstorms robot sensors. This platform was chosen because it's well known and exhibits many of the same characteristics as other embedded and real-time applications. Lego Mindstorms hardware components include motors, touch sensors, light sensors, rotation sensors, temperature sensors, and lamps.

The Lego Mindstorms controller unit is called the RCX. For our example program we won't be using the RCX, but rather an advance prototype of the Systronix JCX controller that uses the aJile aJ-80 direct execution Java processor. The JCX provides extremely efficient and time-deterministic execution of 100% Java-control applications. The JCX is a direct replacement for the RCX unit and in its base configuration supports eight input sensors and four output ports compatible with the Lego RCX sensor and motor ports.

Lego Components
Each of the Lego Mindstorms sensors and motors has a corresponding Java component. To use the component the physical Lego hardware needs to be attached to a JCX sensor or motor port, and a corresponding software component needs to be created and initialized to the correct port number. The port number is a JavaBeans property.

Motor
The motor component has a forward, reverse, off, and brake state. This state can be set at any time through the State property. The motor component can also receive four unique TriggerEvents to set the motor state corresponding to forward, reverse, off, and brake. Because a TriggerEventListener is an interface, the motor component can directly receive only one TriggerEvent. This is because a Java object can implement an interface only once. This default trigger event will set the motor state to forward. Three inner classes that implement the TriggerEventListener interface are used to set the state to reverse, off, or brake. Applications can obtain a reference to one of these inner classes to register it as an event listener to another component by calling the getReverseListener(), getOff- Listener(), or getBrakeListener() methods.

The power level of the motor can be set from zero to full power. Two properties set the motor power level: MaximumPowerLevel, which determines the power range, and PowerLevel, which determines the duty cycle for the application of power to the motor when it's in the forward or reverse state. Because of the real-time responsiveness of the aJ-80 direct execution Java processor used in the JCX, the duty cycle for the motor can be set in the high kHz range.

Touch Sensor
The touch sensor component monitors the state of one Lego touch sensor switch. The state of the switch can be checked by calling the isPushed() method. An AssertEvent is sent with true for the parameter when the button is pushed and false when the button is released. A TriggerEvent is also sent when the button is pushed and a second trigger event is sent when the button is released. This component also can receive an AssertEvent that will disable the touch sensor when false is passed and enable it when true is passed.

Light Sensor
The light sensor component monitors the light intensity from one Lego Mindstorms light sensor. The light intensity can be checked by calling the getLightIntensity() method, which returns a value between 0 and 4095, with 0 being the darkest. The light sensor also has a specific light range. The lower and upper limits of the range can be set using the UpperLimit and LowerLimit properties. When the light intensity transitions from inside the specified range to outside the range, a TriggerEvent is sent, as is an AssertEvent with false for the parameter. When the light intensity transitions to inside the range, a second TriggerEvent is sent as well as an AssertEvent with true for the parameter. If a situation were to occur where more than one light intensity range were required, it's possible to create more than one light sensor component and assign them to the same sensor port, each component configured for a unique intensity range. The light sensor also receives an AssertEvent that will disable the sensor when false is passed and enable it when true is passed.

Example: Autonomous Mobile Robot Line Follower
To show the ease of use of the components, we implemented an Autonomus mobile robot line follower (see Figure 2). The LineFollower application (see Listing 1) uses a light sensor, two motors, and a one-shot timer to drive a wheeled robot down a black line drawn on white paper. Because most of the functionality is in the components, the line follower code is extremely small and easy to maintain. The LineFollower application creates the components, configures them, and defines any event handlers that require special processing. It's also possible to use a JavaBean-friendly developer tool such as Borland JBuilder to autogenerate the code that creates and initializes the components (see Figure 1).

Figure 1
Figure  1:

The LineFollower application uses a light sensor to follow the line. When the robot is over a line, the motors will cause the robot to move forward. When the robot leaves a line, the motors will cause the robot to turn back to the line. This is done by remembering which direction the robot had to turn last time to find the line, and turn the other direction this time. This algorithm assumes that the robot crosses the line and exits on the opposite side. There's a possibility that the robot will leave the line on the same side that it entered it. This can happen if the line curves or if the robot finds the line before it completely turns toward it. In this case, the robot must again change directions to refind the line (see Figure 2). This condition is detected by monitoring the time since the robot left the line. If a time limit expires, the robot again changes directions, this time toward the line (see Figure 3).

Figure 2
Figure  2:

Figure 3
Figure  3:

The LineFollower application is arranged into three distinct sections: creating the components, configuring their properties, and defining any special event handlers. This same pattern is used for many applications that use JavaBeans.

The left and right motor components control the left- and right-drive wheels of the robot, respectively. When both motors are in the forward state, the robot will move forward. When one motor is in the forward state and the other is in the reverse state, the robot will turn. Initially both motors are set to the forward state so the robot will move forward until it first finds the line.

A light sensor component is created and initialized to a lower threshold of 0 and an upper threshold of 1,000. Test cases showed a reading of about 800 when the light sensor was over a black line and about 1,200 when it was not. These limits will configure the sensor to be in range when it is over a black line and out of range when it is not.

When the light sensor detects a line, it sends a TriggerEvent. An inner class is registered to receive this event and in turn calls the enteringLine() method. When a line is entered, the robot should stop turning and move forward so both motors are forced into the forward state. When the light sensor detects white space, it will send another trigger event indicating that it is now out of range. A second inner class is registered to receive this event and in turn calls the leavingLine() method.

When the robot is no longer tracking a line, it needs to start turning in the opposite direction. This is done in the triggerEvent() method that checks the direction variable and sets the motors to turn the other way. The enteringLine() method also has to start the timer so that the direction will again be switched if the line is not found.

A OneShotTimer component is used to reverse the direction of the robot if a line is not found. It's initialized to a period of 500ms and will send a TriggerEvent when a timeout occurs. Instead of using an inner class to receive the trigger event, our main class implements the triggerEvent() method directly and registers itself as the listener for OneShotTimer timeouts. When a timeout occurs, the triggerEvent() method will be called and will reverse the robot's turn direction.

The design and implementation of the Mindstorms components required a few weeks of effort, but after that the LineFollower application required only about an hour to write, and most of that time was spent writing detailed comments. This is a very small example, but a large example that uses similar components could achieve similar productivity.

The LineFollower implementation on the aJ-80 processor is also very memory-efficient. Passing the code through the aJile ROMizing tool, JEMBuilder, produced a standalone executable that required less than 80KB of ROM, and only 35KB of RAM when using the standard J2ME Connected Limited Device Configuration (CLDC) runtime. Finally, because of the Java bytecode execution efficiency of the JCX platform, the robot line follower was amply powered by 6AA batteries (the same power supply used by the Lego RCX); much of the power is consumed by the motors.

Conclusion
The LineFollower example program described above demonstrates 100% Java-based components that are used to control a Lego Mindstorms robot. The components were built specifically for the Lego hardware, but the concepts are applicable to many other types of embedded and real-time applications. Both a serial port or a debounced button component would be appropriate for an embedded controller, a joystick component for a gaming box, and an error logger component for an industrial-control application. In all these cases, design and implementation time can be drastically reduced and maintainability enhanced by using a standard, embedded real-time component model.

Java and the JavaBean Component Architecture are an excellent platform for building reusable applications. However, until recently the Java platform was not practical for embedded and real-time computing. The release of J2ME, the Real-Time Specification for Java (RTSJ), and the availability of high-speed low-power Java hardware have overcome these barriers, allowing embedded developers to take advantage of the productivity and portability of the Java platform. The addition of a real-time embedded component model makes real-time embedded Java environments all the more compelling for developers.

Author Bio
David Hardin is chief technical officer and cofounder of aJile Systems, Inc. He is a coauthor of The Real-Time Specification for Java, and a member of several Java expert groups convened under the Java Community Process. Dr. Hardin holds a PhD in electrical and computer engineering from Kansas State University. david.hardin@ajile.com

Mike Frerking is software lead at aJile Systems, Inc., where he develops tools and components for embedded and real-time Java development. Mike received his BSEE from Iowa State University. mike.frerking@ajile.com

	



Listing 1 

/** 
  * Controls a JCX with a light sensor and two motors to follow a line. 
  * The line follower has two guiding rules.  First if it crosses the line, 
  * it will change the direction to find the line again.  Second, 
  * if it doesn't cross the line after a specified amount of time, 
  * it is probably in error and will again flip to find the line. 
  */ 

package linefollower; 

import com.ajile.jcx.*; 
import com.ajile.components.OneShotTimer; 
import com.ajile.events.*; 

public class LineFollower implements TriggerEventListener { 
   // Create left hand side motor 
   Motor leftMotor = new Motor(Motor.MOTOR_PORT_0); 
   // Create right hand side motor 
   Motor rightMotor = new Motor(Motor.MOTOR_PORT_1); 
   // Create the light sensor 
   LightSensor lightSensor = new LightSensor(LightSensor.SENSOR_PORT_2); 
   // Create one shot timer 
   OneShotTimer timer = new OneShotTimer(); 

   // Indicates last turn direction. 
   public static final int LEFT = 0; 
   public static final int RIGHT = 1; 
   int direction = RIGHT; 

   /** 
    * Main entry point.  Creates one instance of this class 
    */ 
   public static void main(String[] args) { 
     LineFollower instance = new LineFollower(); 
   } 

   /** 
    * Initializes all JCX components and registers event handlers 
    */ 
   public LineFollower() { 

     // turn motors on 
     leftMotor.setState(Motor.MOTOR_FORWARD); 
     rightMotor.setState(Motor.MOTOR_FORWARD); 

     // initialize light sensor so on the line is in range (0-2000) 
     lightSensor.setLowerLimit(0); 
     lightSensor.setUpperLimit(1000); 

     // initialize the timer for a .5 second delay 
     timer.setIntervalMillis(500); 

     // register the flip triggerEvent method when a timeout occurs 
     timer.addTimerListener(this); 

     // use inner class to register the leavingLine method when the 
     // light sensor moves out of range 
     lightSensor.addOutOfRangeListener( new TriggerEventListener() { 
       public void triggerEvent() { 
         leavingLine(); 
       } 
     }); 

     // use inner class to register the enteringLine method when the 
     // light sensor moves in range 
     lightSensor.addInRangeListener( new TriggerEventListener() { 
       public void triggerEvent() { 
         enteringLine(); 
       } 
     }); 

   } 

   /** 
    * Called by the light sensor when light intensity 
    * increases past the upper threshold indicating that we 
    * are moving from dark to light. We lost a line. 
    */ 
   void leavingLine() { 
     // the line has been lost, so reverse turn 
     triggerEvent(); 
     // reset the timer in case line is not refound 
     timer.start(); 
   } 

   /** 
    * Called by the light sensor when light intensity 
    * decreases past the upper threshold indicating that we 
    * are moving from light to dark.  We found a line 
    */ 
   void enteringLine() { 
     // stop the timer 
     timer.stop(); 
     // Line found, move forward 
     leftMotor.setState(Motor.MOTOR_FORWARD); 
     rightMotor.setState(Motor.MOTOR_FORWARD); 
   } 

   /** 
    * Causes the JCX to turn in the opposite direction. 
    * If the JCX is presently going straght, this routine 
    * will still cause it to turn in the opposite direction 
    * as last time this routine was called. 
    */ 
   synchronized public void triggerEvent() { 
     if (direction == LEFT) { 
       direction = RIGHT; 
       leftMotor.setState(Motor.MOTOR_REVERSE); 
       rightMotor.setState(Motor.MOTOR_FORWARD); 
     } else { 
       direction = LEFT; 
       leftMotor.setState(Motor.MOTOR_FORWARD); 
       rightMotor.setState(Motor.MOTOR_REVERSE); 
     } 
   } 

} 

  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.