RIoT Control is a tool that automates the deployment of your Java code to a Raspberry Pi or similar device. It finds your device on a local network, copies your java archives to it, creates a service configuration file, and starts your application or sets it as a service.
Currently, it is only available as a plugin for the SBT build tool.
The easy way first: There's a template that will create an empty project that's already set up. After installing sbt, execute the following from your root git directory or similar:
sbt new riot-framework/streams.g8
Check the template's github template for more details.
If you have an existing SBT project, add sbt-riotctl
to your project/plugins.sbt
file:
addSbtPlugin("org.riot-framework" % "sbt-riotctl" % "0.3")
RIoT Control depends on SBT's JavaServerAppPackaging
, and will only be active if you've enabled it in your build.sbt
:
enablePlugins(JavaServerAppPackaging)
In general, RIoT Control will then try to use sane default for everything, including the host name, username, and password used to log in to your Pi (it will use Raspbian's defaults). If you need to override any of them, add the corresponding configuration to your project's settings in build.sbt
, for example:
lazy val root = (project in file("."))
.enablePlugins(JavaServerAppPackaging)
.settings(
// Deployment Targets (hostname, username, password):
riotTargets := Seq(
riotTarget("raspberrypi", "pi", "raspberry")
),
// Port to use for remote debugging:
riotDbgPort := 8000,
// Packages and features needed by your code:
riotPrereqs := "oracle-java8-jdk wiringpi",
riotRequiresI2C := false,
riotRequiresSPI := false
)
RIoT Control will deploy the files generated by SBT in the stage
directory, and use the scripts generated by JavaServerAppPackaging
to start your Application.
To copy your application to your device and run it:
sbt riotRun
Then press [Return] twice to terminate it.
To debug your application, similarly run:
sbt riotDebug
and point your Remote Debugger to the device's address and the port defined in the riotDbgPort
setting (e.g. raspberrypi.local:8000
).
If you want run the application in the background:
sbt riotStart
then stop it with:
sbt riotStop
Once you're happy with the result, you can enable the Service that RIoT has created for your Application, which will cause it to start automatically when your device boots:
sbt riotInstall
To disable it, use the command:
sbt riotUninstall
In general, if issuing more than one command, it's considerably faster to start SBT in interactive mode:
sbt
then enter the commands above (without sbt, for example just riotRun
). This allows for quick development: Modify your code in the editor, then change to your terminal and execute riotRun
in the interactive SBT shell.
Many IDE have some sort of plugin to integrate with SBT, speeding up the development cycle even more.
Problems tend to arise most often when installing prerequisite packages. This only happens during the first deployment of an Application, and may take a long time depending on the packages installed.
As the apt-get traffic is proxied through the installation tool (your Pi may not have internet access), it may run out of memory when downloading large packages (such as the Java JDK). The following error is an indication that this is happening:
[info] E: Failed to fetch http://debian.anexia.at/raspbian/raspbian/pool/main/o/openjdk-11/openjdk-11-jdk-headless_11.0.3+7-5_armhf.deb Connection failed [IP: 127.0.0.1 8080]
[info] E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
If this is the case, make sure sbt
has enough memory. The easiest way to do this is to add a file named .jvmopts
in your project's root directory:
-Xms1024M
-Xmx4096M
-Xss2M
-XX:MaxMetaspaceSize=1024M
A freshly installed Pi without internet access will have a system date that is far in the past. RIoT tries to detect this and set the time to your development machine's time, but if this fails for some reason, subsequent package installations will fail with rather confusing messages such as:
[info] E: Release file for http://raspbian.raspberrypi.org/raspbian/dists/buster/InRelease is not valid yet (invalid for another 80d 10h 26min 56s). Updates for this repository will not be applied.
In this case, make sure your Pi's system clock is somewhat correct. It may help to wait for a while after the Raspberry Pi has booted, to allow it to synchronise to internet time sources or others.
RIoT control runs after JavaServerAppPackaging
, and uses the files it generates.
First, it checks that the prerequisite packages defined in the riotPrereqs
setting are installed (by default the JDK 8 and the WiringPi library), and otherwise installs them with apt-get
. A local SOCKS5 proxy is started locally through which apt-get
accesses the package repositories, so this step works even if your Pi has no direct internet access (this is often the case if it's connected directly to your laptop).
Then, it checks that certain features, such as SPI or I2C ports, are enabled, depending on the riotRequires...
settings.
Next, the stage
directory is copied, in which JavaServerAppPackaging
has built or copied all JARs, and generated start scripts. The target directory on the Pi is /usr/local/
followed by your project's name.
A systemd 'service unit configuration' file is generated in /etc/systemd/system/
and named after your project (followed by .service
, as is the convention).
This systemd service is then started, stopped, enabled or disabled depending on the RIoT Control command issued.
RIoT Control will first try to access your Raspberry Pi by Hostname (e.g. if it is called 'raspberrypi', RIoT Control will attempt to connect to raspberrypi
and raspberrypi.local
).
If this fails, then RIoT Control will attempt to use mDNS (a service provided by Bonjour / Zeroconf) to discover an SSH service with the name provided. If your Raspberry Pi has the Avahi service installed and running (this is the case by default), it will publish its SSH port this way, as well as its current IP address.