juno-maven-plugin is the Maven integration for Juno. It turns compiling a Java entry point into a sketch, verifying it with arduino-cli, uploading it, and opening the serial monitor into ordinary Maven goals, so a full edit/flash/observe cycle needs no hand-written arduino-cli invocations. See docs/ARDUINO.md for installing arduino-cli itself.

Goals

Goal Default phase What it does
juno:env generate-sources Reads a git-ignored .env file and exports its entries into the Maven JVM's real process environment.
juno:install-deps (none — run explicitly) Installs the Arduino core and every optional library any current example needs (e.g. Mouse, ESP_SSLClient), once per machine.
juno:compile process-classes Runs Juno's compiler for <mainClass> and writes the generated .S assembly, Shim.cpp runtime, and .ino wrapper.
juno:verify verify Runs juno:compile, then compiles the generated sketch with arduino-cli compile — no board required.
juno:upload (none — run explicitly) Runs juno:compile and juno:verify, resolves the board's serial port, and flashes it with arduino-cli upload.
juno:boards (none — run explicitly) Lists the connected boards and serial ports (address, protocol, board name, FQBN). Read-only.
juno:monitor (none — run explicitly) Resolves the board's serial port and opens an interactive arduino-cli monitor session.

compile, verify, and upload each (re)build the sketch first, so running any one of them always reflects the current Java source — there is no separate "just compile" step to remember.

Configuration

Add the plugin to a module's pom.xml and set the entry-point class once, or override it per invocation with -Djuno.main=...:

<plugin>
    <groupId>io.github.jabrena</groupId>
    <artifactId>juno-maven-plugin</artifactId>
    <version>${project.version}</version>
    <configuration>
        <mainClass>${juno.main}</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>env</goal>
            </goals>
        </execution>
    </executions>
</plugin>

This is exactly how juno-examples/pom.xml is set up, with juno.main defaulting to io.github.jabrena.juno.api.Blink as a Maven property so any example can be selected without editing the POM.

Property Goals Default Purpose
juno.main compile, verify, upload (required) Fully qualified entry-point class.
juno.outputDirectory compile, verify, upload ${project.build.directory}/juno Where generated sketch directories are written.
juno.gcLog compile, verify, upload false Emit one Serial line per garbage collection (see docs/FEATURES.md).
juno.board compile, verify, upload (required only if @Board declares more than one board) Which declared board to build for (arduino-uno-r4-wifi, arduino-uno-q).
juno.arduinoCli verify, upload, boards, monitor, install-deps arduino-cli Executable name or path.
juno.fqbn compile, verify, upload, monitor derived from @Board Overrides the target FQBN.
juno.port upload, monitor auto-detected Serial port, when more than one matching board is connected.
juno.baudRate monitor 115200 Must match the program's Serial.begin(...) rate (see docs/SERIAL.md).
juno.envFile env ${project.basedir}/.env The KEY=VALUE file to export.

Every property can be set in <configuration> or passed with -D on the command line; -D always wins for a given run.

Typical workflow

# Once per machine: install the core and every optional library any example needs
./mvnw -f juno-examples/pom.xml juno:install-deps

# Compile and check with arduino-cli
./mvnw -f juno-examples/pom.xml compile juno:verify \
  -Djuno.main=io.github.jabrena.juno.api.Blink \
  -Djuno.board=arduino-uno-r4-wifi   # or arduino-uno-q

# Flash it to a connected board
./mvnw -f juno-examples/pom.xml compile juno:upload \
  -Djuno.main=io.github.jabrena.juno.api.Blink \
  -Djuno.board=arduino-uno-r4-wifi   # or arduino-uno-q

# Watch its Serial output
./mvnw -f juno-examples/pom.xml juno:monitor

upload and monitor auto-detect the port when exactly one matching board is connected; otherwise pass -Djuno.port=<PORT> (find it with arduino-cli board list). Always use juno:monitor to watch board output rather than a raw arduino-cli monitor call — it resolves the same port logic as upload and defaults to the baud rate every juno-examples program uses.

Supplying Wi-Fi credentials with juno:env

juno:env is a Maven-level alternative to exporting shell environment variables by hand for programs that call System.getenv(...) (Wi-Fi SSID/password, hosts, paths — see docs/INTERNET.md). It reads .env (KEY=VALUE per line, git-ignored, never committed) from the module's base directory and exports each entry into the real process environment before juno:compile runs in the same JVM:

JUNO_WIFI_SSID=your-network-name
JUNO_WIFI_PASSWORD=your-network-password

A missing .env file is not an error — nothing is exported, and modules that don't need it are unaffected. See docs/INTERNET.md for the full explanation, including the --add-opens JVM flags this goal needs (already configured in .mvn/jvm.config).

Troubleshooting

  • "Missing required Juno entry point": set <mainClass> in the plugin configuration or pass -Djuno.main=<fully-qualified-class-name>.
  • upload/monitor can't find a port: run ./mvnw juno:boards and pass the port explicitly with -Djuno.port=... when more than one matching board is attached.
  • Uploaded sketch targets the wrong board variant: pass -Djuno.fqbn=... to override the FQBN Juno derived from the entry point's @Board annotation; the plugin logs a warning whenever an override changes the effective target.
  • "@Board declares multiple boards ...; pass the target board explicitly": the entry point's @Board lists more than one board (e.g. @Board({ArduinoUnoQ.class, ArduinoUnoR4WiFi.class})); add -Djuno.board=<id> naming one of them, as printed in the error.
  • .env values aren't visible to System.getenv at compile time: confirm the module's build actually runs juno:env (an <execution> with goal env) before process-classes, and that .mvn/jvm.config's --add-opens flags are in effect (they are, by default, in this repository).