JUno /ˈxu.no/ is an ahead-of-time compiler for running a practical subset of Java on Arduino boards & M5Stack Cardputer. It keeps javac as the Java frontend, performs
closed-world linking on the development machine, and emits Assembly.
Traditionally, Java programs require a JVM to run. However, in memory-constrained environments, running Java programs inside a JVM can be difficult. Juno enables developers to create small Java programs for microcontrollers such as Arduino.
Arduino UNO Q
|
Arduino UNO R4 WiFi
|
Arduino UNO R3 WiFi Rev2
|
Arduino UNO R3
|
Arduino Nano ESP32
|
| Accessory | Image | Supported features |
|---|---|---|
| Arduino Nano Connector Carrier | ![]() |
Passive carrier for the Arduino Nano ESP32: microSD card slot on SPI, four Grove sockets, and one Qwiic connector. |
M5Stack Cardputer Adv
|
Requirements: Arduino CLI, JDK 27+, Maven 3.10+.
On macOS, install Arduino CLI with Homebrew and verify that it is on your PATH:
brew update
brew install arduino-cli
arduino-cli versionSee Arduino's installation guide for direct downloads and other options.
sdk env install
./mvnw clean installBlink runs unmodified on every
supported board. Its built-in LED alternates on and off every 500 ms (one full cycle per second).
package io.github.jabrena.juno.api.hardware.gpio;
import io.github.jabrena.juno.hardware.ArduinoUno;
import io.github.jabrena.juno.hardware.gpio.DigitalOutput;
import io.github.jabrena.juno.util.Delay;
public final class Blink {
public static void main(String[] args) {
DigitalOutput led = DigitalOutput.of(ArduinoUno.getGpio().getBuiltinLed());
while (true) {
led.high();
Delay.millis(500);
led.low();
Delay.millis(500);
}
}
}The project provides a Maven plugin to help the user with the common operations. juno:upload generates the sketch, verifies it with arduino-cli, and flashes it in one step.
Blink has no board restriction, so pick a target with -Djuno.board:
./mvnw -f juno-examples/pom.xml compile juno:upload \
-Djuno.main=io.github.jabrena.juno.api.hardware.gpio.Blink \
-Djuno.board=arduino-uno-q
# -Djuno.board=arduino-uno-r4-wifi
# -Djuno.board=arduino-uno-r3-wifi-rev2
# -Djuno.board=arduino-uno-r3
# -Djuno.board=arduino-nano-esp32Select another supported board with -Djuno.board. The plugin auto-detects the port when exactly one matching board is
connected; otherwise, specify it with -Djuno.port=<PORT>.
After uploading the program, you could monitor what happen in the board using the following command:
./mvnw -f juno-examples/pom.xml juno:monitor \
-Djuno.board=arduino-uno-qThe juno Maven plugin provides these goals. The commands below run them from the examples module;
add -Djuno.main=<fully-qualified-class-name> and -Djuno.board=<id> when the entry point has no single-board
@Board declaration.
| Goal | Purpose | Command |
|---|---|---|
juno:boards |
Lists the boards and serial ports detected by Arduino CLI without touching a board. | ./mvnw -f juno-examples/pom.xml juno:boards |
juno:install-deps |
Installs the Arduino core and optional libraries required by the examples. | ./mvnw -f juno-examples/pom.xml juno:install-deps |
juno:compile |
Generates the assembly, C++ runtime shim, and Arduino sketch wrapper. | ./mvnw -f juno-examples/pom.xml compile juno:compile -Djuno.main=io.github.jabrena.juno.api.hardware.gpio.Blink -Djuno.board=arduino-uno-r4-wifi |
juno:verify |
Generates the sketch and compiles it with Arduino CLI without using physical hardware. | ./mvnw -f juno-examples/pom.xml compile juno:verify -Djuno.main=io.github.jabrena.juno.api.hardware.gpio.Blink -Djuno.board=arduino-uno-r4-wifi |
juno:upload |
Generates, verifies, and uploads the sketch to the selected or detected board. | ./mvnw -f juno-examples/pom.xml compile juno:upload -Djuno.main=io.github.jabrena.juno.api.hardware.gpio.Blink -Djuno.board=arduino-uno-r4-wifi |
juno:monitor |
Opens the serial monitor for the selected or detected board. | ./mvnw -f juno-examples/pom.xml juno:monitor -Djuno.board=arduino-uno-r4-wifi |
For example, verify the Grove Temperature and Humidity Sensor Pro monitor for a UNO WiFi Rev2 without uploading it:
./mvnw -f juno-examples/pom.xml compile juno:verify \
-Djuno.main=io.github.jabrena.juno.api.hardware.device.grove.sensors.GroveDht22Monitor \
-Djuno.board=arduino-uno-r3-wifi-rev2Juno is an ahead-of-time compiler, not a JVM, so it intentionally supports a focused, predictable
subset of Java that can be translated into native code for resource-constrained Arduino boards.
Programs are first compiled normally with javac; Juno then performs closed-world linking from
main, follows every reachable method, and validates the bytecode before generating the sketch.
Valid Java is therefore not automatically valid Juno input. If reachable code needs an unsupported
bytecode instruction, JDK feature, or invokedynamic bootstrap, compilation stops instead of
emitting code with uncertain behavior. The diagnostic identifies the method, bytecode offset, and
unsupported operation or bootstrap. The currently supported Java subset includes:
- Entrypoints and methods:
static void main(String[]), the embedded-friendlystatic void main(), static methods with primitive arguments and results, direct closed-world calls, and removal of unreachable methods. SeeHelloWorldandMethods. - Primitive types:
boolean,byte,short,char,int,long,float, anddouble. The example prints each type's size and range usingjavac-foldedBYTES,MIN_VALUE, andMAX_VALUEconstants. SeePrimitiveTypes. - Values and control flow: constants, mutable static fields, local variables, arithmetic,
bitwise and shift operations, comparisons, conditionals,
switch, and loops. SeeVariables,Operators, andControlFlow. - Arrays: fixed-size primitive arrays (
booleanthroughdouble, including fixed-size multidimensional arrays) allocated from the fixed 8 KiB arena, andstatic finalread-only lookup tables with an all-constant initializer, kept in flash instead of RAM. SeeArrays. - Strings: string literals,
String.valueOf(int),length(), andcharAt(int)for runtime string references,equals, plus+concatenation of strings and aStringBuilder. SeeStrings. - BigInteger and BigDecimal: arbitrary-precision
java.math.BigIntegerandBigDecimal, withMathContextand theRoundingModeconstants. Arithmetic,pow,sqrt, division by scale,RoundingModeorMathContext,setScale,round, comparisons, conversions andtoString/toPlainStringfollow the JDK's results, scales and rounding. Values are immutable blocks in the 8 KiB arena and are garbage-collected, so precision is limited by memory, not by a fixed width. SeeScopedValuesPrecision. - LocalDateTime:
now(),MIN, the date and time getters,equalsand ISO-8601toString.now()gets UTC from an SNTP server once Wi-Fi is up (JUNO_SNTP_HOST, defaultpool.ntp.org;JUNO_SNTP_PORT, default123) and returnsLocalDateTime.MINwhile it is unavailable. Works on every Wi-Fi board, including the UNO R3 with the Arduino WiFi Shield. SeeSntpTimeandSntpTimeSD(server read from the SD card). - UUID:
java.util.UUIDsupports construction, parsing, formatting, comparison, 16-byte conversion throughUuidCodec, andrandomUUID()on all boards. On the classic UNO R3, random UUIDs are weak and non-cryptographic; use them only for node identities, as shown byWeakUuid. - Objects and interfaces: enums, records, classes, constructors, instance fields, class inheritance, virtual
dispatch, and directly implemented interfaces with closed-world dispatch. Inheritance and inherited/default
implementations are available on every board, but the AVR boards have much less flash, SRAM and arena space, so
the Arduino build or runtime may fail for larger object graphs. See
ObjectsandInterfaces. - Lambdas and method references: non-capturing and capturing lambdas, plus static, bound,
unbound, and constructor references emitted through
LambdaMetafactory.metafactory. SeeLambdas. - Math and random numbers: the documented
java.lang.Mathsubset and Arduino-backed bounded pseudorandom numbers. SeeMathFunctionsSinglePrecision,MathFunctionsDoublePrecisionandRandomNumbers. - Exceptions:
throw,try/catch(including multi-catch and catching by a supertype),finally, nested handlers, propagation between methods, custom final exception classes, and try-with-resources. SeeExceptions. - Concurrency: preemptive virtual threads (
Thread.startVirtualThread,join,isAlive) and the supported JDK 27 previewStructuredTaskScopesubset with cooperative subtasks (up to four threads includingmain, withThread.sleepandThread.yield), restricted monitors andReentrantLock, andScopedValuebindings inherited by forked subtasks. Platform threads (new Thread) andThread.ofVirtual()builders are rejected at compile time;AtomicInteger,AtomicBooleanandAtomicLongare available. SeeSynchronization,VirtualThreads,StructuredConcurrency(subtasks owned by a scope that is joined and closed before the owner reads their results), andScopedValuesPrecision(a scopedMathContextshared by three parallel subtasks). - Board services: GPIO, clocks and delays, serial I/O, LED matrices, mouse input, Wi-Fi,
HTTP/HTTPS, bounded JSON inspection, and read-only SPI SD-card files through Juno's intrinsic APIs.
See
Blink(GPIO and delays),SerialCounter(serial I/O),HttpServerStatus(clocks),LedMatrixHeart(LED matrix),WifiStatus(Wi-Fi),WifiStatusSD(Wi-Fi credentials loaded from an SD card),WifiStatusSDLcd(the same, with an LCD Keypad Shield),WifiProvisioning(access point and HTTP server to provide Wi-Fi credentials),HttpsMethods(HTTP/HTTPS and JSON),Miner2(UDP between boards: factor numbers together and chain the verified answers as a leaderless, Bitcoin-style proof-of-work chain), andSdFileOperations(SD card). - Shields: stack a shield on the board as designed, with no rewiring, and use it through Juno's APIs;
a combination whose shields share pins, such as the LCD Keypad Shield on a UNO R3 with the Arduino WiFi
Shield, is rejected at compile time. The Grove Base Shield brings
Grove connectors on the UNO pin map, as shown by
GroveDht22Monitor. The LCD Keypad Shield (16x2 display, buttons, backlight) is shown inLcdKeypadDemo. The ELEGOO 2.8" TFT touch shield (display, touch, microSD) is shown inTouchPaintTFTandWifiStatusSDTFT. The AZDelivery Data Logger Shield provides the SD card forWifiStatusSD. The Arduino WiFi Shield brings Wi-Fi, UDP, TCP, plain HTTP and the SNTP clock to the UNO R3, as shown byUdpEchoandPlainHttpWeather.
This is a multi-module Maven build:
juno-api/— the programming model: the Java-facing hardware API the compiler recognizes as intrinsics (api/) and the@Boardannotations used to select a compilation target (annotations/). This is the only artifact programs compile against.juno-compiler/— the compiler (classfile,bytecode,linker,backend, …), which depends onjuno-api. Builds the executablejuno-compiler/target/juno-compiler-<version>.jar.juno-maven-plugin/— Maven goals for generating a sketch (juno:compile), compiling it with Arduino CLI (juno:verify), uploading it (juno:upload), and opening the serial monitor (juno:monitor).juno-examples/— example Java programs written againstjuno-api, built like any other Maven module so they're checked for compile errors on every build.juno-site/— the Roq/Quarkus static site generator behind the documentation site.docs/is entirely generated from Markdown underjuno-site/src/main/resources/content/— see Documentation site below to regenerate it.juno-third-parties/— integrations maintained outside the core compiler, currently including the independent H8/300 firmware generator for the LEGO RCX, its S-record, and the generated Java representation consumed byjuno-examples.
Juno is an ahead-of-time compiler, not a virtual machine. An Arduino board has no JVM, so nothing is
interpreted at run time: the Java program is compiled with javac as usual, and Juno then translates the
resulting JVM bytecode into native ARM assembly that the Arduino toolchain builds into the sketch's firmware.
The compiler is closed-world: it only sees the classes reachable from main, so it can resolve every call
at build time, drop everything unused, and reject anything outside the supported subset before generating a
single instruction. That is also why there is no dynamic class loading, reflection, or general heap.
Hello.java ──javac──▶ Hello.class ──▶ Juno ──▶ Hello.S + HelloShim.cpp + HelloAsm.ino ──arduino-cli──▶ board
│
classfile → bytecode → linker → lowering → optimize → backend
- Write and compile Java. Programs use the Juno hardware API and optionally select a board with
@Board. Maven runsjavac; Juno consumes the resulting.classfiles. - Link and validate. Juno starts at
main, keeps only reachable code, converts hardware calls into intrinsics, and rejects unsupported bytecode with a precise diagnostic. - Lower and optimize. Reachable methods become Juno IR, where compiler passes simplify the program and report risks such as excessive arena use, recursion, or unsafe array access.
- Generate the sketch. The backend emits native assembly and a small C++ runtime shim. The Maven plugin
packages them with a minimal
.inoundertarget/juno/<Main>Asm/. - Build and run. Arduino CLI compiles the generated sketch for the selected board. Use
juno:verifyto compile it,juno:uploadto flash it, andjuno:monitorto read its serial output.
- https://dev.java/
- https://docs.oracle.com/javase/specs/jls/se25/html/index.html
- https://docs.oracle.com/en/java/javase/27/docs/api/index.html
- https://github.com/openjdk/jcstress/tree/master/jcstress-samples
- https://openjdk.org/
- https://openjdk.org/groups/conformance/JckAccess/
- https://github.com/openjdk
- https://docs.arduino.cc/hardware/uno-q/
- https://docs.arduino.cc/hardware/uno-r4-wifi/
- https://docs.arduino.cc/hardware/uno-wifi-rev2/
- https://docs.arduino.cc/hardware/uno-rev3/
- https://docs.arduino.cc/hardware/nano-esp32/
- https://lejos.sourceforge.io/
- https://lejos.sourceforge.io/ev3/docs/index.html
- https://lejos.sourceforge.io/nxt/nxj/api/index.html
- https://lejos.sourceforge.io/rcx/api/index.html
- https://pbrick.info/index.html-p=74.html
- https://tinyvm.sourceforge.net/
- https://sunspotdev.org/
- https://sunspotdev.org/docs/index.html
- https://bearssl.org/














