The Micronaut Pi4J Minimal Example project is a simple Micronaut application running the Pi4J V2 code from the pi4j-example-minimal project. The goal of this project is to build and run the application as a GraalVM native image on a Raspberry Pi.
The GraalVM JDK and native-image build tool for ARM requires a 64-bit processor and a 64-bit operating system.
This project has been tested on Ubuntu Server 20.04 LTS 64-bit.
This project has not been tested on raspios_arm64, but the project should work on raspios_arm64
.
sdkman can be used to install the GraalVM JDK. GraalVM JDK is used to install native-image
build tool.
sdk list java
sdk install java 21.3.0.r11-grl
java -version
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment GraalVM CE 21.3.0 (build 11.0.13+7-jvmci-21.3-b05)
OpenJDK 64-Bit Server VM GraalVM CE 21.3.0 (build 11.0.13+7-jvmci-21.3-b05, mixed mode)
gu install native-image
See pi4j-example-minimal for setting up the electronics for this project.
On target Raspberry Pi
- Download Package Information From All Configured Sources
sudo apt update
- Install Build Essential (C/C++ compilers, make)
sudo apt install build-essentials
- Install libz-dev
sudo apt install libz-dev
- Download and Install pigpio library
- NOTE: Python is not required.
The steps in this section were performed and the generated configuration files are committed to the project repository. These steps are included for informational purposes to document the complete build process.
- Clone project on Raspberry Pi
- cd project directory
- sudo ./gradlew run
- The
jvmArgs
for therun
build task in build.gradle configures the GraalVM tracing agent to generate thenative-image
configuration files in the./tmp
directory.- jni-config.json
- predefined-classes-config.json
- proxy-config.json
- reflect-config.json
- resource-config.json
- serialization-config.json
- The
- Exercise the application to completion by pressing the switch several times until the LED stops blinking.
- Copy the required configuration files to the native-image configuration directory
./src/main/resources/META-INF/native-image/com.pi4j/pi4j-core
. This directory follows the Micronaut convention for the native-image configuration directory./src/main/resources/META-INF/native-image/<groupId>/<artifactId>/<app>
. By using this convention, the Micronaut Gradle plugin with automatically use these configuration files in the native image build.cp ./tmp/jni-config.json ./src/main/resources/META-INF/native-image/com.pi4j/pi4j-core
cp ./tmp/proxy-config.json ./src/main/resources/META-INF/native-image/com.pi4j/pi4j-core
The native image build requires approximately 8 GB of RAM to execute. An attempt to execute the native image build on a Raspberry Pi 4B with 4GB RAM resulted in the build exiting with an error code of 137
Out of Memory
.
Oracle Cloud Infrastructure offers an ARM compute instance. The Oracle Cloud Free Tier offers an Ampere A1 Compute instance with up to 4 cores and up to 24 GB RAM for free. The native image successfully built with 3 cores and 16 GB ram in a little over 3 minutes.
NOTE: If an Oracle Cloud compute instance is used to build the native image, GraalVM JDK
and the native-image
tool must be installed on the compute instance. See Project Requirements
- native-image.properties contains the required
native-image
command line arguments. This property file is automatically used in the native-image build because the file is located in the directory following the Micronaut convention.- The
com.pi4j.library.pigpio.internal.PIGPIO
class statically loads the JNI librarylibpi4j-pigpio.so
.native-image
must be configured to statically load thePIGPIO
class at runtime instead of at build time.--initialize-at-run-time=com.pi4j.library.pigpio.internal.PIGPIO
- The
- The
nativeImage
build task in build.gradle is configured to pass the optional--verbose
command line argument tonative-image
. Using verbose output helps when trying to understand build or runtime errors. ./gradlew nativeImage
builds the native image../build/native-image
contains the native image build artifacts.application
application.build_artifacts.txt
- If the
native-image
application
is built on a host and not the runtime machine, secure copy theapplication
file to target machine.scp ./build/native-image/application
<user>@<target ip>:~
- From the user's home directory on the Raspberry Pi target. The
application
must be executed withsudo
privileges because the underlying pigpio library opens a file descriptor for/dev/mem
. Thepi4j.library.path
java system property must be defined when running the native image, so theapplication
knows where to find the JNI library. For simplicity,libpi4j-pigpio.so
was extracted out ofpi4j-library-pigpio-2.0.jar
and committed to this project's repository in the./lib
directory.sudo ./application -Dpi4j.library.path=<path to this project on Raspberry Pi>/lib
FROM arm64v8/fedora:34
COPY ./build/native-image/application /app/application
COPY ./lib/* /usr/local/lib/
RUN ln -s /usr/local/lib/libpigpio.so.1 /usr/local/lib/libpigpio.so
RUN ln -s /usr/local/lib/libpigpio_if.so.1 /usr/local/lib/libpigpio_if.so
RUN ln -s /usr/local/lib/libpigpio_if2.so.1 /usr/local/lib/libpigpio_if2.so
ENV LD_LIBRARY_PATH=/usr/local/lib
ENTRYPOINT ["/app/application", "-Dpi4j.library.path=/usr/local/lib"]
docker build -t pi4j/pi4j-minimal-example:latest .
docker save pi4j/pi4j-minimal-example:latest | gzip > pi4j_minimal_example_latest.tar.gz
docker load < pi4j_minimal_example_latest.tar.gz
docker run --cap-add=SYS_RAWIO --device=/dev/mem:/dev/mem --device=/dev/i2c-1:/dev/i2c-1 --device=/dev/vcio:/dev/vcio pi4j/pi4j-minimal-example:latest