Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Control an Arduino from Java

November 13, 2013

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 Strings. 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 files 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

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.
 


Video