Control an Arduino from Java You can communicate with and control an Arduino from Java running on another computer.



You can run Java SE Embedded or Java ME on a Raspberry Pi, but the Arduino is a bit too constrained to run Java directly. However, with the help of serial port communication software, you can communicate with and control an Arduino from Java running on another computer. Although the code to do so has been published on the Arduino site, it's still a little tricky. In this blog, I'll go over how to make this work from different host operating systems, as well as write an Arduino sketch to do something useful (thanks to Mike Riley for help with the sketch).

First, locate and download the RXTX library. When you unzip the downloaded file, you'll notice directories for various operating systems (OSs). Make note of which ones you're using, as you'll need those specific files.

Next, create a new Java project in the IDE of your choice, and be sure to copy the following RXTX files (from the download in the first step) into the project directory:

All OSs: RXTXcomm.jar

For Mac OS X: librxtxSerial.jnilib (from the Mac_OS_X subdirectory)

For Linux: librxtxSerial.so (from the correct Linux subdirectory)

For Solaris: librxtxSerial.so (from the correct Solaris subdirectory)

For Windows: rxtxSerial.dll (from the Windows subdirectory)

Next, modify your project's settings to include RXTXcomm.jar on the class path, and the path to the native library in the command line via the -Djava.library.path parameter, like this:

java -Djava.library.path=/Users/ericjbruno/ArduinoTest1 -cp ./RXTXcomm.jar:./build/classes arduinotest1.ArduinoTest1

Connecting Via Java

The trickiest part of the code to get working is finding the correct serial port to connect to the Arduino. This part varies by OS. On the Mac, the serial port should begin with "/dev/tty.usbmodemXXXX". On Windows, it's usually "COM3", and on Linux, it will be one of the "/dev/tty" or "/dev/usbdev/" ports. In the code, I've included an array of the port String s. Just comment out the ones not for your host OS, or better yet, add code to detect your OS at runtime and use the proper String :

private static final String PORT_NAMES[] = { "/dev/tty.usbmodem", // Mac OS X "/dev/usbdev", // Linux "/dev/tty", // Linux "/dev/serial", // Linux "COM3", // Windows };

When the sample application (available for download here) starts up, it iterates through all of the system ports looking for a match for your OS, and then attempts to connect to it:

// Enumerate system ports and try connecting to Arduino over each while (portId == null && portEnum.hasMoreElements()) { CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement(); for (String portName : PORT_NAMES) { if ( currPortId.getName().equals(portName) || currPortId.getName().startsWith(portName)) { // Try to connect to the Arduino on this port serialPort = (SerialPort)currPortId.open(appName, TIME_OUT); portId = currPortId; break; } } }

If it finds a match, it will break out of the for and while loops, and then connect on that port and configure it:

// set port parameters serialPort.setSerialPortParams( DATA_RATE, // 9600 baud SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

The last step in initialization is to add an event listener (more on this later) to receive events from the Arduino, and tell it to call us back when there's data available:

// add event listeners serialPort.addEventListener(this); serialPort.notifyOnDataAvailable(true);

Running the code on my host system (a Macbook), I get the following output (I added some tracing to the code to help):

Experimental: JNI_OnLoad called. Stable Library ========================================= Native lib Version = RXTX-2.1-7 Java lib Version = RXTX-2.1-7 Trying: port /dev/tty.Bluetooth-Serial-1 port /dev/cu.Bluetooth-Serial-1 port /dev/tty.Bluetooth-Serial-2 port /dev/cu.Bluetooth-Serial-2 port /dev/tty.EricsiPhone5-WirelessiAP port /dev/cu.EricsiPhone5-WirelessiAP port /dev/tty.Bluetooth-Modem port /dev/cu.Bluetooth-Modem port /dev/tty.Bluetooth-PDA-Sync port /dev/cu.Bluetooth-PDA-Sync port /dev/tty.usbmodem1411 Connected on port /dev/tty.usbmodem1411

If the classpath and library path settings are correct, the RXTX library will load and output the version information at the top of the output above. Next, you can see the names of the serial ports available on my Macbook, and how it finally finds a match and connects to port "/dev/tty.usbmodem1411". We've now successfully connected to an Arduino from a Java application, but to make this more fun, let's do something useful with this connectivity.

The Arduino Sketch

In this sample application, I plan to control a device from the Arduino via Java. To do so, I'm using a PowerSwitch Tail, which takes low-voltage input to turn a connected AC device on/off. Simply connect the PowerSwitch Tail to pins 13 and 2 (ground) on the Arduino as shown below.

All we have to do now is send some data over the serial port to the Arduino to instruct it to turn the PowerSwitch Tail on (along with the AC appliance plugged into it) by setting pin 13 high, and pin 2 low. To turn the appliance off, we just do the opposite. Here's the Arduino sketch to make this happen (once you load this onto your Arduino you're good to go from that point onward):

int led = 13; // LED connected to digital pin 13 int pts = 2; // Powertail Switch 2 connected to digital pin 2 int recv = 0; // byte received on the serial port void setup() { // initialize onboard LED (led), Powertail (pts) and serial port pinMode(led, OUTPUT); pinMode(pts, OUTPUT); Serial.begin(9600); } void loop() { // if serial port is available, read incoming bytes if (Serial.available() > 0) { recv = Serial.read(); // if 'y' (decimal 121) is received, turn LED/Powertail on // anything other than 121 is received, turn LED/Powertail off if (recv == 121){ digitalWrite(led, HIGH); digitalWrite(pts,LOW); } else { digitalWrite(led, LOW); digitalWrite(pts,HIGH); } // confirm values received in serial monitor window Serial.print("--Arduino received: "); Serial.println(recv); } }

As a side effect, turning pin 13 high also turns on an LED on the Arduino, which is useful for debugging or if you don't have a PowerSwitch Tail.

To make this all work from Java, simply write the correct data (a 'y' in this case) as the command to turn the LED and connected AC switch on, or any other data (i.e., 'n' or anything really) to turn both off:

String data = "y"; output = serialPort.getOutputStream(); output.write( data.getBytes() );

The Arduino sketch also writes back to the host over the serial port to verify that it received a command.The following code receives and processes these and other events (this method is part of the RXTX SerialPortEventListener interface, which is provided as a listener in the initialization code):

public synchronized void serialEvent(SerialPortEvent oEvent) { try { switch (oEvent.getEventType() ) { case SerialPortEvent.DATA_AVAILABLE: if ( input == null ) { input = new BufferedReader( new InputStreamReader( serialPort.getInputStream())); } String inputLine = input.readLine(); System.out.println(inputLine); break; default: break; } } catch (Exception e) { System.err.println(e.toString()); } }

Finally, to ensure that the serial port and Arduino are left in a state to communicate again, make sure you close the serial port. This removes any filesystem locks on the host computer, created to ensure only one device at a time communicates with the Arduino:

if ( serialPort != null ) { serialPort.removeEventListener(); serialPort.close(); }

Other Important Steps

On UNIX-based systems, the RXTX library places its lock ∩¼üles in the folder /var/lock. If this doesn't exist, communication will not work, although you won't receive any obvious errors as to why. To create this directory if missing, open the terminal application and create the lock directory as the root user or equivalent:

> sudo mkdir /var/lock

Enter your root or administrator password if prompted.

Finally, on UNIX-based systems, you may also need to run the Java application with root (or equivalent) access to ensure the lock file can be created in /var/lock. Running the application as a user with the proper privilege or via the sudo command will do the trick.

Programming an Arduino from Java has opened a whole new world for me. I've been able to control sets of Arduinos from Java applications on a Raspberry Pi to do all sorts of fun and useful things. For more fun Arduino projects, check out arduino.cc, Dr. Dobb's Mike Riley's site, or Dr. Dobb's blogger Al Williams' posts on Arduino.

You can download the complete sample Java code here.

Happy coding!

-EJB