Run and Debug Java Code with Eclipse

Programming the latest LEGO MINDSTORMS brick EV3 with Java is ease of development for people who know this programming language. And more important, it bursts all limitations of the proprietary LEGO development environment. A third advantage, depending on personal taste, is that Linux users will be happy to to develop without running Microsoft Windows.

As I am a Linux user the following guide is tested with Linux and need to be adapted to work for other platforms. I would be happy to receive your feedback to make it a useful guide for all users no matter which OS they prefer. The Eclipse version I use is “Kepler”

The project leJOS provides everything you need to start to develop programs to control the EV3 brick. You just need to follow the guides given by their wiki pages. The page [Developing with leJOS] describes very detailed all the steps you need to do to get your first program running but I find it inconvenient to build the jar, copy the jar and running the jar manually. Millions of clicks that should be automated.

Thus, I started to experiment with maven to do all the steps automatically. Additionally, I wanted to be able to debug the code on the fly. So, here is what you need to do after you have done steps described in [Developing with leJOS].

Create a new project from scratch

  1. Click in the menu File -> New -> Project
  2. Expand in the popup window the folder “Maven”, select “Maven Project” and click next
  3.  (dependend on your preference) Check “Create a simple project (skip archtype selection)” and “Use default Workspace location”. Click next
  4. Now we need to configure the project:
    • Group Id: MavenRobot
    • Artifact Id: MavenRobot
    • Version: 0.0.1-SNAPSHOT
    • Name: MavenRobot
    • Description: This is an example maven project to run Java code one the EV3 brick
  5. click Finish

Convert ev3classes to maven

  1. Select project ev3classes in the package explorer with a simple right click
  2. Select “configure” and then “convert to Maven” in the context menu
  3. Leave the settings as they are in the popup window and click finish
  4. Finally install it into the local repository with another right click on ev3classes in the package explorer, Run As and Maven Install

Configure Maven

Open the file pom.xml in the root directory of your project. It should look like this.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelversion>4.0.0</modelversion>
  <groupid>MavenRobot</groupid>
  <artifactid>MavenRobot</artifactid>
  <version>0.0.1-SNAPSHOT</version>
  <name>MavenRobot</name>
  <description>This is an example maven project to run Java code one the EV3 brick</description>

  <!-- The following configuration goes here -->

</project>

Lets assume the following project configuration.

  • Brick IP address: 192.168.0.10
  • Home directory on the brick: /home/root
  • User name: root
  • The debugging port: 4000
  • The main class of this project will be: MakeHurray

This can be specified as follows:


<project ...="">
   ...
  <properties>
    <brick.host>192.168.0.10</brick.host>
    <brick.home>/home/root</brick.home>
    <brick.user>root</brick.user>
    <brick.debug.port>4000</brick.debug.port>
    <project.main.class>MakeHurray</project.main.class>
  </properties>
   ...
</project>

The project shall be packaged as a jar with all dependencies for later execution. In order the archive this goal the maven plugin maven-assembly-plugin has to be configured as follows and bound to the package phase.


<project ...="">
   ...
  <properties>
     ...
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <artifactid>maven-assembly-plugin</artifactid>
        <configuration>
          <archive>
             <manifest>
               <mainclass>${project.main.class}</mainclass>
             </manifest>
          </archive>
          <descriptorrefs>
            <descriptorref>jar-with-dependencies</descriptorref>
          </descriptorrefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin> 
      ...
    </plugins> 
  </build>
</project>

I haven’t find any suitable way to copy the jar file to the brick (using scp) and execute it there with maven. The next best solution seems to be using ANT within maven.

The following snippet describes how to use the maven-antrun-plugin to copy the packaged jar to the brick.


<project ...="">
   ...
  <properties>
     ...
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <artifactid>maven-assembly-plugin</artifactid>
        ...
      </plugin> 
      <plugin>
        <inherited>false</inherited>
        <groupid>org.apache.maven.plugins</groupid>
        <artifactid>maven-antrun-plugin</artifactid>
        <version>1.7</version>
        <executions>
          <execution>
            <phase>install</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <echo message="Push to server ${project.build.directory}/${project.build.finalName}*.jar">
                <scp trust="yes" username="${brick.user}" password="" todir="root@${brick.host}:${brick.home}">
                  <fileset dir="${project.build.directory}">
                    <include name="${project.build.finalName}*.jar">
                  </include></fileset>
                </scp>
              </echo></target>
            </configuration>
          </execution>        
        ...
      </executions></plugin> 
      ...
    </plugins> 
  </build>
</project>

If it is necessary to run maven only up to this step, it can be executed with mvn install or alternatively with a right click on the project, Run As and maven install.

And here is how to run or debug it.


<project ...="">
   ...
  <properties>
     ...
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <artifactid>maven-assembly-plugin</artifactid>
        ...
      </plugin> 
      <plugin>
        <inherited>false</inherited>
        <groupid>org.apache.maven.plugins</groupid>
        <artifactid>maven-antrun-plugin</artifactid>
        <version>1.7</version>
        <executions>
          <execution>
            <phase>install</phase>
            ...
          </execution>        
          <execution>
            <id>default-cli</id>          
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <!-- SSH -->
                <taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec" classpathref="maven.plugin.classpath">
                <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.plugin.classpath">                         
                <!-- run the jar -->
                <if>
                  <equals arg1="${debug}" arg2="true">
                  <then>
                    <sshexec host="${brick.host}" trust="yes" username="${brick.user}" password="" command="jrun -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${brick.debug.port},suspend=y -cp ${brick.home}/${project.build.finalName}-jar-with-dependencies.jar ${project.main.class}">
                  </sshexec></then>
                  <else>
                    <sshexec host="${brick.host}" trust="yes" username="${brick.user}" password="" command="jrun -cp ${brick.home}/${project.build.finalName}-jar-with-dependencies.jar ${project.main.class}">
                  </sshexec></else>
                </equals></if>
              </taskdef></taskdef></target>
            </configuration>
          </execution>        
        <dependencies>
          <!-- ANT dependencies --> 
          <dependency>
            <groupid>commons-net</groupid>
            <artifactid>commons-net</artifactid>
            <version>1.4.1</version>
          </dependency>
          <dependency>
            <groupid>ant</groupid>
            <artifactid>ant-commons-net</artifactid>
            <version>1.6.5</version>
          </dependency>
          <dependency>
            <groupid>ant</groupid>
            <artifactid>ant-jsch</artifactid>
            <version>1.6.5</version>
          </dependency>
          <dependency>
            <groupid>jsch</groupid>
            <artifactid>jsch</artifactid>
            <version>0.1.29</version>
          </dependency>
          <dependency>
            <groupid>ant-contrib</groupid>
            <artifactid>ant-contrib</artifactid>
            <version>20020829</version>
          </dependency>
        </dependencies>
      </executions></plugin> 
    </plugins> 
  </build>
  ...
</project>

Finally we need to let maven know about the dependencies of the project. This is at least JNA and ev3classes.


<project ...="">
   ...
  <properties>
     ...
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <artifactid>maven-assembly-plugin</artifactid>
        ...
      </plugin> 
      <plugin>
        <inherited>false</inherited>
        <groupid>org.apache.maven.plugins</groupid>
        <artifactid>maven-antrun-plugin</artifactid>
        <version>1.7</version>
        <executions>
          <execution>
            <phase>install</phase>
            ...
          </execution>        
          <execution>
            <id>default-cli</id>          
            ...
          </execution>        
        <dependencies>
          <!-- ANT dependencies --> 
          ...
        </dependencies>
      </executions></plugin> 
    </plugins> 
  </build>
  <dependencies>
    <dependency>
      <groupid>ev3classes</groupid>
      <artifactid>ev3classes</artifactid>
      <scope>compile</scope>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupid>net.java.dev.jna</groupid>
      <artifactid>jna</artifactid>
      <version>3.2.7</version>
    </dependency>  	
  </dependencies>
</project>

Start programming

Please create a class called MakeHurray which might look like the following:

import lejos.hardware.Button;
import lejos.hardware.LCD;

public class MakeHurray {
   public static void main(String[] args) {
      LCD.clear();
      LCD.drawString("Hurray", 0, 5);
      Button.waitForAnyPress();
      LCD.clear();
      LCD.refresh();
   }
}

For execution you may run:

  • mvn install antrun:run -Ddebug=false or alternatively when you want to debug
  • mvn install antrun:run -Ddebug=true

You can always create a debug or run configuration in eclipse. Just select in the menu Run -> run configuration or debug configuration. Add a new launch configuration under Maven Build.

  • Base directory: /home/USER/workspace/MavenRobot
  • Goals: install antrun:run -Ddebug=false

And click Run

You should be able to see “Hurray” on the screen.

Debuging

If you run the jar with mvn install antrun:run -Ddebug=true the execution will be suspended. That gives the time to connect the Eclipse debugger.

Click on Run in the menu and select Debug Configurations…. A new launch configuration has to be created under Remote Java Application. The setup should look like follows

  • Project: MavenRobot
  • Connection Type: Standard (Socket Attach)
  • Connection Properties – Host: IP ADDRESS of your brick
  • Connection Properties – Port: 4000
  • Allow termination of remote VM should be checked

Finally you click Debug and the execution should stop at the first breakpoint you set.

That’s it. I hope this guide was helpful for you. If you have any ideas or suggestions, please let me know.

Mario Lüder

Here is the complete pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelversion>4.0.0</modelversion>
  <groupid>MavenRobot</groupid>
  <artifactid>MavenRobot</artifactid>
  <version>0.0.1-SNAPSHOT</version>
  <name>MavenRobot</name>
  <description>This is an example maven project to run Java code one the EV3 brick</description>
  
  <properties>
  	<brick.host>192.168.0.10</brick.host>
  	<brick.home>/home/root</brick.home>
  	<brick.user>root</brick.user>
  	<brick.debug.port>4000</brick.debug.port>
  	<project.main.class>MakeHurray</project.main.class>
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <artifactid>maven-assembly-plugin</artifactid>
        <configuration>
          <archive>
             <manifest>
               <mainclass>${project.main.class}</mainclass>
             </manifest>
          </archive>
          <descriptorrefs>
            <descriptorref>jar-with-dependencies</descriptorref>
          </descriptorrefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin> 
      
      <plugin>
        <inherited>false</inherited>
        <groupid>org.apache.maven.plugins</groupid>
        <artifactid>maven-antrun-plugin</artifactid>
        <version>1.7</version>
        <executions>
          <execution>
            <phase>install</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <echo message="Push to server ${project.build.directory}/${project.build.finalName}*.jar">
                <scp trust="yes" username="${brick.user}" password="" todir="root@${brick.host}:${brick.home}">
                  <fileset dir="${project.build.directory}">
                    <include name="${project.build.finalName}*.jar">
                  </include></fileset>
                </scp>
              </echo></target>
            </configuration>
          </execution>        
          <execution>
            <id>default-cli</id>          
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <!-- SSH -->
                <taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec" classpathref="maven.plugin.classpath">
                <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.plugin.classpath">                         
                <!-- run the jar -->
                <if>
                  <equals arg1="${debug}" arg2="true">
                  <then>
                    <sshexec host="${brick.host}" trust="yes" username="${brick.user}" password="" command="jrun -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${brick.debug.port},suspend=y -cp ${brick.home}/${project.build.finalName}-jar-with-dependencies.jar ${project.main.class}">
                  </sshexec></then>
                  <else>
                    <sshexec host="${brick.host}" trust="yes" username="${brick.user}" password="" command="jrun -cp ${brick.home}/${project.build.finalName}-jar-with-dependencies.jar ${project.main.class}">
                  </sshexec></else>
                </equals></if>
              </taskdef></taskdef></target>
            </configuration>
          </execution>        
        </executions>
        
        <dependencies>
          <dependency>
            <groupid>commons-net</groupid>
            <artifactid>commons-net</artifactid>
            <version>1.4.1</version>
          </dependency>
          <dependency>
            <groupid>ant</groupid>
            <artifactid>ant-commons-net</artifactid>
            <version>1.6.5</version>
          </dependency>
          <dependency>
            <groupid>ant</groupid>
            <artifactid>ant-jsch</artifactid>
            <version>1.6.5</version>
          </dependency>
          <dependency>
            <groupid>jsch</groupid>
            <artifactid>jsch</artifactid>
            <version>0.1.29</version>
          </dependency>
          <dependency>
            <groupid>ant-contrib</groupid>
            <artifactid>ant-contrib</artifactid>
            <version>20020829</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins> 
  </build>
  
  <dependencies>
    <dependency>
      <groupid>ev3classes</groupid>
      <artifactid>ev3classes</artifactid>
      <scope>compile</scope>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupid>net.java.dev.jna</groupid>
      <artifactid>jna</artifactid>
      <version>3.2.7</version>
    </dependency>  	
  </dependencies>
</project>