Building an executable pi4j jar

Hey,

today I want to show you how you can build a pi4j jar file which can be executed on the rasperry pi. I will tell you, why I chose pi4j for using GPIO-pins with Java and how you can build an executable jar with the pi4j library bundled.

Why pi4j?

I chose pi4j because it seems it’s the most popular library for using GPIO-pins on the pi with Java. I just didn’t find any other implementation which leaves an impression of a well documented and alive project. (And in addition a colleague of mine recommended it 😉

The pi4j library is just a small wrapper around wiringPi. It comes bundled with the wiringPi native libraries and calls them via JNI. The object model seems to be well-conceived and the documentation is complete. In addition pi4j is available as a Maven Dependency at Maven Central. As I’m using Maven every day at work this was an important point.

How to build the jar-file?

First I have to say this task really, really ennerved me. The solution is simple, but I will tell you how I reached the result. (If you just want to see the solution, scroll down some paragraphs until you see “Solution:”).

The first thing I did was googling how to build an executable jar file. So I configured the maven-jar-plugin to set the Main-Class in the manifest. That worked well until I got a ClassNotFoundException because I wanted to create the GpioController from pi4j library as it is described here. Okay no problem so far, because I realized that the pi4j-core.jar didn’t get bundled to my jar-file and because of this, the class couldn’t be found.

I chose to use the maven-assembly-plugin to bundle the pi4j jars and then it began to get funny. Because the pi4j-core Maven artifact depends on pi4j-native there are always the files

pi4j-native-0.0.5-hard-float.so
pi4j-native-0.0.5-soft-float.so

added to your project. Because of these files the maven-assembly-plugin always failed, because it states something like this:

Failed to execute goal org.apache.maven.plugins:maven-assembly-plugin:2.5.1:single (default-cli) on project Pathfinder: Failed to create assembly: Error adding file-set for 'com.pi4j:pi4j-native:so:hard-float:0.0.5' to archive: Error adding archived file-set. PlexusIoResourceCollection not found for: .../.m2/repository/com/pi4j/pi4j-native/0.0.5/pi4j-native-0.0.5-hard-float.so: No such archiver: 'so'.

I really tried to avoid this by configuring my own assembly descriptor as shown here. I especially tried to avoid unpacking the files, but the maven-assembly-plugin ignored them.

Because the maven-assembly-plugin didn’t work, I then tried to get things to run with the maven-jar-plugin by adding an external lib-folder to the classpath. Basically this worked, but I always got an exception like this

OpenJDK 64-Bit Server VM warning: You have loaded library /tmp/libpi4j.so which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c ', or link it with '-z noexecstack'.
Nov 18, 2014 5:59:16 PM com.pi4j.util.NativeLibraryLoader load
SCHWERWIEGEND: Failed to load library [pi4j] using the System.load(file) method using embedded resource file: [jar:file:/home/uli/.m2/repository/com/pi4j/pi4j-core/0.0.5/pi4j-core-0.0.5.jar!/lib/soft-float/libpi4j.so]
Nov 18, 2014 5:59:16 PM com.pi4j.util.NativeLibraryLoader load
SCHWERWIEGEND: ERROR:  The native library [pi4j : libpi4j.so] could not be found in the JVM library path nor could it be loaded from the embedded JAR resource file; you may need to explicitly define the library path '-Djava.library.path' where this native library can be found.
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.pi4j.wiringpi.Gpio.wiringPiSetup()I
	at com.pi4j.wiringpi.Gpio.wiringPiSetup(Native Method)
	at com.pi4j.io.gpio.RaspiGpioProvider.(RaspiGpioProvider.java:47)
	at com.pi4j.io.gpio.GpioFactory.getDefaultProvider(GpioFactory.java:102)
	at com.pi4j.io.gpio.impl.GpioControllerImpl.(GpioControllerImpl.java:67)
	at com.pi4j.io.gpio.GpioFactory.getInstance(GpioFactory.java:85)
	at de.buschbaum.java.pathfinder.Main.main(Main.java:18)

And this really was a problem. I couldn’t figure out, why the native library is not loaded. I have to mention at this point, that I tried this on my laptop. I thought, just get it run until a certain point (because I knew the laptop has no GPIO-pins) and then run it on the pi.

I even installed pi4j via the debian package on my laptop to be shure that the libraries are available, but it didn’t work. And then I got the idea of debugging into the ClassLoader and I found the method

static void loadLibrary(Class fromClass, String name,
                            boolean isAbsolute)

in class java.lang.ClassLoader.

And the debugger gave me the exception that the native libraries were found, but they are not matching my archtitecture which really makes sense:

 /tmp/libpi4j.so: /tmp/libpi4j.so: falsche ELF-Klasse: ELFCLASS32 (Possible cause: architecture word width mismatch)

The whole time I thought I got a problem of bundling the native libraries but the real problem was, that the libraries were found but are not compatible to my laptop.

In addition I found out that the native libraries which are included via the pi4j-native dependency are not necessary because the pi4j-core artifact contains the native libraries. This solved my problem with unpacking and assembling the jar with the maven-assembly-plugin.

Solution:

So all in all:

– The pi4j-native dependency must be excluded, because it can’t be bundled by using the maven-assembly-plugin.

This is done by adding this to the pom.xml:

  
<dependencies> 
<dependency> 
<groupId>com.pi4j</groupId> 
<artifactId>pi4j-core</artifactId> 
<version>0.0.5</version> 
<exclusions> <exclusion> 
<groupId>com.pi4j</groupId> 
<artifactId>pi4j-native</artifactId> 
</exclusion> 
</exclusions> 
</dependency> 
</dependencies>      

– Build the jar by using the default jar-with-depencies assembly descriptor and add a main class:

        
<build>
 <plugins>
 <plugin>
 <artifactId>maven-assembly-plugin</artifactId>
 <version>2.5.1</version>
 <configuration>
 <archive>
 <manifest>
 <mainClass>de.buschbaum.java.pathfinder.Main</mainClass>
 </manifest>
 </archive>
 <descriptorRefs>
 <descriptorRef>jar-with-dependencies</descriptorRef>
 </descriptorRefs>
 </configuration>
 </plugin>
 </plugins>
 </build>

That’s it!

Testing

I used the following class to check whether everything works correct. It just toggles a pin. The GPIO-pin naming can be found here (for b+):

package de.buschbaum.java.pathfinder;

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

/**
 * Hello world!
 *
 */
public class Main {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Hello Pi!");
        System.out.println("Creating GpioController...");
        GpioController controller = GpioFactory.getInstance();
        System.out.println("Creating myLed Pin...");
        GpioPinDigitalOutput myLed = controller.provisionDigitalOutputPin(RaspiPin.GPIO_04, "My LED", PinState.LOW);
        while (true) {
            System.out.println("Setting myLed state to high...");
            myLed.setState(PinState.HIGH);
            System.out.println("Waiting 5 seconds");
            Thread.sleep(5000);
            System.out.println("Setting myLed state to low...");
            myLed.setState(PinState.LOW);
            System.out.println("Waiting 5 seconds");
            Thread.sleep(5000);
        }
    }
}

As you can see, it works:

 

 

raspberry pi basic pin toggling on

raspberry pi basic pin toggling on

raspberry pi basic pin toggling off

raspberry pi basic pin toggling off

 

Source code can be found here.

Regards

Uli

Advertisements

One response to “Building an executable pi4j jar

  1. Pingback: I2C Arduino and Raspberry pi using pi4j | Dev Stuff·

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s