Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

J2ME & Embedded Systems


Mar03: J2ME & Embedded Systems

The Java 2 Micro Edition (J2ME) is a flexible tool for development in environments where resources don't support the full Java 2 Standard Edition. While most of the focus of J2ME development is on games and information management applications for mobile phones and PDAs, other interesting platforms and applications are also a possibility. In this article, I use J2ME, some common security sensors, and a small single-board computer that runs J2ME to implement a network appliance that monitors and reports on the activities in an area. (The complete source code for the application is available electronically; see "Resource Center," page 5.)

For this application, the hardware platform I use is an evaluation board for the aJile Systems aJ-100 processor called the SaJe (for "Systronix aJile Euroboard") made by Systronix (http://saje.systronix.com/). It includes the aJ-100 processor, memory, and input/output devices, including 10baseT Ethernet. The aJ-100 is a member of a processor family that includes the aJ-80, which is similar to the aJ-100 but has a smaller, 8-bit data bus and no JTAG port. The aJ-100 and aJ-80 are notable because they interpret Java bytecode directly as their instruction set, so they have no interpreter or just-in-time compiler—their opcodes are Java Virtual Machine instructions. This doesn't affect how you write the Java code, but does make building and deploying different from using a desktop virtual machine.

Hello World for the aJ-100

Building an application for the aJ-100 is a three-step process:

1. Compile the code using the Java compiler, replacing the J2SE classes with the J2ME classes in the bootclasspath.

2. Build the deployable binary using aJile's JemBuilder tool. This is where you specify the hardware configuration you'll be using.

3. Deploy the binary to the target board using aJile's Charade tool and a JTAG connection.

Example 1 is source code for a "Hello, World" program for the aJ-100. To compile Example 1 using javac, use the command:

% javac -bootclasspath

$J2ME_CLASSES_JAR Hello.java

In this command, J2ME_CLASSES_JAR refers to the classes that come with the J2ME toolkit and implement the fundamental classes like java.lang.Object for J2ME.

Now you need to create a deployable binary using the JemBuilder tool. JemBuilder is a graphical tool that converts Java class files into a binary file that can be loaded onto the aJ-100.

1. Start JemBuilder and select File->New Project.

2. Enter a directory for the output files and click next.

3. Select the run time "Runtime_cldc" and the configuration that corresponds to your hardware; mine is "SajeConfiguration"

While the aJ-100 can run more than one independent JVM at a time, I only use one in this application so when prompted for NAME, enter JVM0; for Class Name, enter Hello; for Class Path, use the directory containing Hello.class; and for Drivers, select nothing because this example does not need drivers.

Once the project and JVM are configured, you're ready to build the binary by selecting Project->Build. If all goes well, the build output ends with "Jemcfg completed successfully." If the output reports an error, the text in the build window can help diagnose classpath errors and the like. There are dozens of options that go into the JemBuilder configuration to specify things like memory bank sizes, I/O ports, and so on. Fortunately, the board vendor supplied the configurations for my evaluation board. If you're using a custom board, ensure the settings are correct.

Next, hook up the JTAG connector to your PC and to the target board, then run the Charade debugger. The JemBuilder tool created several scripts as part of its build process, one of which can be used to initialize and load Charade. Select File->Execute, then the load.sod file in the JemBuilder output directory. Once the download finishes, click the Run button to start the program.

When the program runs, the "Hello World" output is prefixed with "[TEXTIO0.0]", indicating that it is the standard output stream from JVM 0. The SaJe board doesn't have a console, so the aJ-100 run-time and Charade debugger have a protocol for conveying standard output and error streams from the aJ-100 to Charade. Alternatively, you can configure JemBuilder to route the streams to the serial port. You'll see a couple of lines of text output as the system is initialized, followed by the program's text output.

J2ME for Embedded Systems

J2ME comes in several variants tailored for different classes of devices. The smallest configuration of the J2ME is called the Connection Limited Device Configuration (CLDC) and intended for devices like mobile phones and PDAs, which have little memory, slow and intermittent network connectivity, and may run from batteries—making power conservation important. CLDC is the configuration supported by the aJile processor family.

On top of the J2ME configurations are additional standard class libraries called "profiles." The most common profile is the Mobile Information Device Profile (MIDP), which is tailored for devices like mobile phones and low-end PDAs. MIDP implements features important in an interactive device, for example a GUI. MIDlets (programs written for the MIDP) can be compiled once and run on any device that supports the profile.

While this works for mobile phones, embedded systems have different requirements. They typically need to monitor and control sensors and actuators, handle high-resolution timers, and process sensor data that may be best represented as floating-point data. The J2ME MIDP doesn't address any of these needs, so vendors of embedded Java technology (like aJile Systems) provide additional class libraries to meet them.

The aJ-100 processor includes a set of general-purpose timer/counters which can be used to trigger software events at programmed delays or intervals. The timer/counters can also be used to generate simple waveforms. The Java classes to control the timer/counters are in the com.ajile.drivers.gptc package. The aJ-100 also supports a serial port. Classes to control the serial port are in com.ajile.drivers.serialport. Many aJ-100 boards (including the SaJe) use flash memory for persistent storage, so the aJ-100 support library includes flash memory classes in the com.ajile.drivers.flash package.

Interfacing Devices to the aJ-100

My application calls for a mixture of motion detectors, which detect when an object is moving in its field of view, and door/window sensors, which detect when a door or window is opened. The system remembers the most recent events and, upon request, displays them to a web browser. Consequently, there are two parts to building this application: reading the sensors and interacting with the browser.

Both the motion detector and the door/window sensor work by opening/closing a circuit like a switch. The motion detector opens the circuit when it senses something moving; the door sensor opens the circuit when the door is opened.

As mentioned, the aJ-100 has many of the features required of embedded controllers, including timers, general-purpose digital I/O, and serial I/O ports. This application requires general-purpose digital I/O functions, which are in the Java packages com.ajile.drivers.gpio and com.ajile.components.

The GpioPin class is used to control one of the I/O pins on the aJ-100. To create an instance of GpioPin, I use the constructor with an argument that describes which pin I want to control. The aJ-100 has five GPIO ports of eight pins each. The ports are lettered A-E and the pins are numbered 0-7, so to create a GpioPin object for port A, pin 0 I could use:

GpioPin a0 = new GpioPin(GpioPin .GPIOA_BIT0);

Sometimes, when the level changes on an input pin, the transition is noisy and more than one transition edge can be detected as the input bounces between high and low. To avoid this, you need to filter ("debounce") the noisy transitions. The software library for the aJ-100 includes a class that debounces an input pin called "PushButton." Here's how to create a debounced input pin:

PushButton a0 = new PushButton(Gpio- Pin.GPIOA_BIT0);

I use this class rather than GpioPin so I don't get spurious events when the sensors' states change.

The aJ-100 digital general-purpose I/O pins expect a TTL signal. So, if the voltage on the pin is +5, it reads a digital "1" and if it the voltage is 0 it reads a digital "0." The sensors just make or break a circuit, so I need a simple circuit to convert their state into a voltage level. Figure 1 is a simple circuit that connects to the sensor, +5V, ground, and a GPIO pin. For this application, I was able to take the power and ground from the evaluation board.

When motion is detected or the door is opened, the state of a sensor changes and I'd like the code to be able to register that change. This would be a job for an interrupt service routine (ISR) in a typical embedded system, but Java doesn't support low-level constructs like interrupts. Instead, Java supports the concept of events to capture asynchronous actions. This concept is used by the AWT and Swing GUI toolkits to notify programs of button clicks, mouse motion, and the like. The aJ-100 includes a set of classes that let you register for and receive events when activity on a GPIO pin occurs.

The interfaces that must be implemented to listen for events are in the com.ajile.events package. The PushButton class can fire two types of events: AssertEvents and TriggerEvents. AssertEvents include the state of the GPIO pin, while TriggerEvents do not. For this application, I'm only interested in when the sensors activate and not when they deactivate, so I configure the PushButton object to just fire events when motion is detected or the door is opened like this:

inPin.setTriggerReportPolicy(PushButton .TRIGGER_ON_PUSH);

I use an inner class as a listener for events from each of the GPIO pins. This application only uses eight GPIO pins, so I just register listeners for one port of eight pins. When motion is detected and the pin changes state, the triggerEvent method of the EventListener inner class is called. This method adds a MotionEvent object to the "events" Vector so a record of the event is available to web clients. Listing One is the complete GPIO initialization and event registration code.

Building the Network Security Device

The other part of this device is the embedded web server. Neither J2ME nor the aJile tools provide an HTTP server, so I add some code that can act like a web server. It has limited capabilities in that it ignores the HTTP request and always responds with the security event status list.

Network sockets are handled much differently in J2ME than J2SE. In J2SE, the socket classes are in the java.net package. In J2ME, all I/O channels are treated as "connector" objects, and their implementation and availability varies by device. Many J2ME profiles don't have server socket connectors, so it would be impossible to set up this kind of a web server on them. Devices like mobile phones aren't often called on to be servers, so this restriction makes sense for them. My application, though, is always on and available, so it makes good use of the server socket connector provided with the aJ-100 run-time library.

J2ME connectors are created by using the static open method of the javax.microedition.io Connector class. Example 2 creates a server socket on the default HTTP port (80) and tells the JVM that this socket will be used for reading/writing. Then it waits for a connection from a client. The StreamConnection object returned by acceptAndOpen() contains the input/output streams that you can use to communicate with the client.

This server has to be able to handle just enough of HTTP to be able to work with browsers, so I hard-coded some of the protocol handling. For instance, the browsers I've tried require that the HTTP request headers be read before they will accept the response. I don't care what the request is, but I have to read it nonetheless. HTTP headers always end with two carriage-return/line-feed pairs, so I just read and discard data until I see that pattern. Also, the first part of the HTTP response the program generates is always the same, it starts every response with the header string in Example 3. The last two lines are actually the start of the HTML document and not part of the HTTP protocol, but it's convenient and efficient to handle them together.

When the application starts up, it initializes the GPIO pins and listeners just mentioned and enters a loop waiting for clients. When a client connects, the program sends it an HTML table of the most recent SecurityEvents as stored in the "events" Vector. A server that needed to service several clients would likely use a separate thread for each connection so they could proceed in parallel. In this case, I assume the load on the server will be light, so the servicing of each request can proceed sequentially. This assumption reduces the resources required of the server by not requiring additional memory to store the thread information and additional compute cycles to switch thread contexts. Listing Two shows the simple web server code.

Building the security web server code is just like building the "Hello, World" application with a couple of small exceptions. It requires some additional driver support for the Ethernet device and the GPIO port. Loading only necessary drivers is important to conserve memory in small devices like the aJ-100. Drivers are configured into the deployable binary file using the JemBuilder tool after the code is compiled. Select Project->Drivers and in the dialog box select "Ethernet" and "Port_E" from the available drivers. This causes the code to support the 10baseT interface and GPIO port E to be linked into the JVM image file. Next, open the folder in the left panel and select JVM0->Drivers->Ethernet to open the Ethernet configuration panel. Enter the IP address information as appropriate for your network. The easiest way to do this is to select "Save IP Address as System Property" then enter an IP address (like "192.168.1.1") and a network mask (like "255.255.255.0"). The gateway address doesn't matter unless your device has to communicate with other subnets through a gateway. If so, you'll need to enter the gateway's IP address here.

All that's left is to load and run the program using Charade and see the results. Figure 2 is an example page served up by the aJ-100 board. The table shows which detectors had events and how long ago the event occurred. It would have been nice to be able to show the actual time of the event, but the SaJe board has no time-of-day function. After a reset, System.currentTimeMillis returns zero.

Conclusion

While this program is specifically tailored to a security web server application, it could be extended to support many other applications. For example, rather than serving HTML to a browser, the network server could serve an XML document. This would make it easy to integrate as a web service into a larger application.

Also, the techniques used for general-purpose I/O pins are applicable to many kinds of sensors and actuators. I showed them used as input pins, but they can also be used as outputs to control motors or other actuators for robotics. The large number of I/O pins on the aJ-100 makes it a great choice for simple but flexible device control applications.

DDJ

Listing One

/* Initialize the GPIO pins and register listeners */
private void initializeGpio() {
    int [] pins = {
        GpioPin.GPIOE_BIT0, GpioPin.GPIOE_BIT1,
        GpioPin.GPIOE_BIT2, GpioPin.GPIOE_BIT3,
        GpioPin.GPIOE_BIT4, GpioPin.GPIOE_BIT5,
        GpioPin.GPIOE_BIT6, GpioPin.GPIOE_BIT7};
        
        for (int i=0; i<pins.length; i++) {
            PushButton inPin = new PushButton(pins[i]);
            inPin.setTriggerReportPolicy(PushButton.TRIGGER_ON_PUSH);
            inPin.addTriggerListener(new EventListener(i));
        }
}
/* This inner class is a listener for events from a PushButton object */
private class EventListener implements TriggerEventListener {
    int detectorNumber;
    public EventListener(int detectorNumber) {
        this.detectorNumber = detectorNumber;
    }
  public void triggerEvent() {
        if (events.size() == MAX_EVENTS)
            events.removeElementAt(0);
        MotionEvent me = new MotionEvent(detectorNumber, 
                                         System.currentTimeMillis());
        events.addElement(me);
  }
}

Back to Article

Listing Two

StreamConnectionNotifier scn = null;
initializeGpio();
try {
  scn = (StreamConnectionNotifier)Connector.open(
  "serversocket://:" + myListenPort,
  Connector.READ_WRITE);
} catch (IOException ioe) {
  System.out.println(
  "Can't open server socket: "+ioe.getMessage());
}
while (true) {
  try {
    StreamConnection sc = scn.acceptAndOpen();
    InputStream is = sc.openInputStream();
    OutputStream os = sc.openOutputStream();

    eatInputStream(is);
    StringBuffer buf = new StringBuffer(header);
    buf.append("<BODY><TABLE border=\"1\"><TR><TD><B>Sensor 
                                     Number</B><TD><B>How Long Ago</B>");
    long now = System.currentTimeMillis();
    for (int i=events.size()-1; i>=0; i--) {
      MotionEvent event = (MotionEvent)events.elementAt(i);
      buf.append("<TR><TD align=center>");
      buf.append(String.valueOf(event.detectorNumber));
      buf.append("<TD>");
      buf.append(event.getTime(now));
    }
    buf.append("</TABLE></BODY></HTML>");
    buf.append("\r\n\r\n");
    os.write(buf.toString().getBytes());
    os.close();
    sc.close();
  } catch (IOException ioe) {
    System.err.println("IOException: "+ioe.getMessage());
  }
}

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.