Download this article in .PDF format
This file type includes high-resolution graphics and schematics when applicable.
Java is a mature development platform, now pushing past 20 years in age. As such, it’s ideally suited to developing embedded applications due to its simplicity, ubiquity, and a rich set of both standard and open-source libraries. Below are five key points that you should know when starting to develop embedded Java applications.
1. There are lots of libraries to choose from.
Developing code for an application can be challenging, especially when starting from scratch. Oftentimes, you have to write code to provide functionality that really should be in a library, but there isn’t one for the platform you’re using.
Because Java has been around for so long and is (by many surveys) the most popular programming language on the planet, many people have written libraries that support pretty much anything you can think of. For embedded applications, this is a huge bonus thanks to Java’s “Write once, run anywhere” philosophy. Just because a RESTful web service library was developed on a laptop doesn’t mean it won’t run in an embedded environment.
Many of these libraries are released under an open-source license, which signifies the price is also ideally suited to devices that may only cost a few dollars when deployed in the field.
2. Interfacing to sensors and actuators.
Embedded applications often need to communicate with a wide range of sensors and actuators to collect data and control external devices.A number of well-defined standards exist for connecting external electronics to an embedded device, including GPIO, I2C, SPI, and serial connections via a UART. To simplify the access to these interfaces, which normally requires complex native code, Java can use open-source libraries to make this simple.
On the Raspberry Pi, a library called Pi4J (http://pi4j.com/) provides a simple set of APIs to interact with externally connected devices. For example, there are classes for GpioPinInput and GpioPinOutput, which handle input and output from GPIO pins. There’s also a GpioPinPwmOutput class that makes pulse-width-modulation output (for perhaps controlling a stepper motor) very straightforward.
In addition, a project under the OpenJDK open-source project is porting the Device Access API from Java ME to Java SE (http://openjdk.java.net/projects/dio/).When this is completed, it will provide a platform-independent way of accessing all interfaces covered by Pi4J as well as a number of others.
3. Doing more than one thing at once.
Right from when Java was first released, it included the ability to execute code in separate threads, unlike many other popular embedded programming languages.The Thread class and Runnable interface make parallel code execution very simple and straightforward.This is particularly important today, as the trend is very much toward multicore processors even in resource-constrained embedded devices.
Having the ability to create multiple threads is only part of the solution to developing parallel code.Threads must often cooperate in how they access and manipulate data within an application.Java provides an extensive set of tools to deal with this:
• A rich set of APIs to provide support for well-understood parallel-programming concepts through the concurrency utilities. The ability to use semaphores, mutexes, read-write locks, and atomic operations, all through standard classes and methods, is very compelling.
• The fork-join framework, which provides a simple way of taking a large task that can be broken down into smaller sub-tasks and doing this recursively until the tasks are small enough to assign to a single thread. The framework handles all of the hard work for the developer, from allocating a pool of threads, to manipulating queues of tasks in the most effective way, to delivering the completed results.
• The latest additions in JDK 8 of lambda expressions and streams.This enables a task to be processed using a functional rather than imperative approach.By avoiding the use of explicit loops and mutable state it is easy to switch between processing the data sequentially to having the work completed in parallel.
4. Using native code from Java.
Despite the wide range of standard and optional APIs available in Java, certain situations arise, especially in embedded application development, where it’s necessary to use specific native code.Having access to explicit memory locations for memory-mapped access is one good example.
Java provides a simple method for doing this, called the Java Native Interface. To use native code within a Java application, you create references to methods you require and mark them as native.The javah tool, included in the JDK, is used to generate a header file. The developer then writes code in, for example, C to implement the functions defined in the header file. These are typically wrapper functions to specific library calls that developers want to use in their Java code.
Having compiled the header file and native code into a library, and set the LD_LIBRARY_PATH variable appropriately, the Java code can then use the native code through simple method calls. There’s full support for mapping between native types and Java types.
5. Debugging an application remotely.
An important part of application development involves testing and then eliminating bugs. For embedded applications, this can often become more challenging since the target device may not have a display.Generating debugging messages isn’t possible, and it may be necessary to rely on more extreme techniques like flashing LEDs to indicate the status of application code. This can prove to be laborious, and with some types of bugs, almost impossible to debug.
Java has an extensive set of tooling available due to its popularity. A range of free and open-source integrated development environments (IDEs) is available, in addition to commercial products.
Because Java applications run on a virtual machine, an additional layer exists between the application code and the physical machine. For debugging purposes, this is very useful. Interfaces like the Java Management Extensions (JMX) can be accessed remotely via a network connection so that an application can be debugged remotely. Full control of the application can be taken over from the IDE running on a laptop in order to inspect value of variables and set break points. It’s even possible to single-step through code remotely, giving the developer time to examine in detail exactly what’s happening and determine the source of the problem quickly and efficiently.
Looking for parts? Go to SourceESB.