PokeTime gets a makeover at the Code Clinic

In the planning phase for our last Devoxx4Kids Event in Karlsruhe we decided to pick up the awesome PokeTime workshop by Cassandra and Stephen Chin. I was a mentor for this workshop at JCrete4Kids this year and really liked the mixture of hardware tinkering, building software snippets and playing the finished game in the end.

A few weeks after the initial planning I cloned the code from Github and tried to run it on my Raspberry Pi. I was not able to compile it without further preparation and probably some support from Cassandra. 🙁

Being a somewhat experienced breaker and builder of things I thought that I could make it on my own. 😉 This post tells about the different steps that lead to a successful PokeTime workshop in Karlsruhe.

Build environment

The root cause for the failing build was the lack of the right build environment. I am sure that on the SD cards that we used at the JCrete4Kids event all the needed libs were in place. Since I did not have an image of those cards I was kind of lost.

The existing build was done with Ant. Since my time with Ant is long ago and I did not want to go back there I created a vanilla pom.xml descriptor for a Maven build and started my first build to see what was missing.

OpenJDK and JavaFX

The PokeTime application is a game UI that runs with keyboard controls as well as sensor input from Raspberry Pi sensors. So the main (external) dependencies are JavaFX and Pi4J. My gut feeling told me that JavaFX would be the harder part. Indeed, JavaFX is only included in Oracle JDK 8 and not in OpenJDK. After JDK 8 it has been totally removed and handed over to the the OpenFJX project and made available under GPL license.

Since I was moving parts anyways I switched from JDK 8 to JDK 11 and introduced the OpenJFX dependencies in my Maven descriptor:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <javafx.version>11</javafx.version>
    <java.version>11</java.version>
    <pi4j.version>1.1</pi4j.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.release>${java.version}</maven.compiler.release>
  </properties>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-controls</artifactId>
        <version>${javafx.version}</version>
        <classifier>linux</classifier>
    </dependency>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-fxml</artifactId>
      <version>${javafx.version}</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-graphics</artifactId>
        <version>${javafx.version}</version>
        <classifier>linux</classifier>
    </dependency>

Pi4J

So, the JavaFX part was sorted out e.g. no more build issues for JavaFX controls. 🙂 Next one was the Pi4J library. This one is needed to make the GPIO connector of the Raspberry Pi usable in your Java code.

    <dependency>
      <groupId>com.pi4j</groupId>
      <artifactId>pi4j-core</artifactId>
      <version>${pi4j.version}</version>
    </dependency>
    <dependency>
      <groupId>com.pi4j</groupId>
      <artifactId>pi4j-device</artifactId>
      <version>${pi4j.version}</version>
    </dependency>

Once the Pi4J dependency was added I was able to start the game using the Maven JavaFX plugin: mvn javafx:run. The game started and I was enthusiastic. Famous last words: “Now I need to check out how this runs on my Raspberry Pi..”

Write once run everywhere.. 😉

For development speed and overall comfort reasons I had switched from the Raspberry Pi to my Linux desktop in the meantime. My naive self was still narrating the early Java slogan “write once write everywhere”. However, once I tried the latest version of the code on the Raspberry Pi it failed miserably at run time. Surprise, surprise: most of the code in the used libraries is platform dependent.

On my Linux box I did not realize this since the OpenJFX Maven plugin managed the platform aspects of JavaFX and the Pi4J code was always encapsulated by PiSystem.isPiUnix. This code was just not run on my Linux desktop. 🙂

On the Raspberry Pi however, I got something like

Unable to determine hardware version. I see: Hardware   : BCM2835 - expecting BCM2708 or BCM2709.

So somehow there was some trouble loading the right native libraries for Pi4J. Of course I was not alone with this error: https://github.com/Pi4J/pi4j/issues/349. And the solution I found after some time of research looked simple. Just add this system property to the start of your Java program: -Dpi4j.linking=dynamic.

However, just add a system property to startup initiated by a Maven plugin is not a trivial thing at all. Since a new JVM is forked from the initial Maven call all parameters that you hand over to the Maven call are lost for the JavaFX application. At least I did not find a solution for this.

Maven shade plugin and fat jars to the rescue!

After I read the relevant parts of the code of the Open JFX Maven plugin I found no way to specify system properties for the started JavaFX application and gabe up on this approach. In the end there must be a way to produce a jar file suitable for application shipment. I don’t start my IntelliJ Idea (yes, it’s built with JavaFX) with mvn javafx:start. 😉

I used the Maven shade plugin to create a fat jar of the application and all of it’s dependencies. In order to package to right version for the platform-dependent OpenJFX parts I needed to add a <classifier>linux</classifier> to the relevant dependencies.

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <shadedArtifactAttached>true</shadedArtifactAttached>
                        <shadedClassifierName>project-classifier</shadedClassifierName>
                        <outputFile>shade\${project.artifactId}.jar</outputFile>
                        <transformers>
                            <transformer implementation=
                                                 "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
				  <mainClass>sample.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

The resulting fat jar could then be started via a start script that included the right parameter for the Pi4J system property. Finally the PokeTime application started on my Raspberry Pi 2b! 😀

Fun at the Devoxx4Kids

In the end we had a successful PokeTime workshop at the October 2019 Devoxx4Kids event in Karlsruhe. A huge thank you goes out to Cassandra and Stephen Chin for the initial application and concept as well as to Patrick Helm (my colleague from synyx) who led the workshop efforts including hardware order, assembly and production of the poke balls! 🙂

Also a huge thank you for all the other organziers and mentors of the Karlsruhe Devoxx4Kids events!