Clock & Delay
Timing intrinsics: Delay and Clock.
Clock
(io.github.jabrena.juno.api) is a compiler intrinsic that reads the board's monotonic uptime
clock, backed directly by the Arduino core's millis()/micros().
import io.github.jabrena.juno.api.Clock;
int uptimeMillis = Clock.millis();
int uptimeMicros = Clock.micros();
Both readings start at (approximately) zero at boot and increase monotonically while the board
runs, with no relation to wall-clock time — there is no real-time clock or NTP support. Both
methods return an int; the underlying counters are unsigned 32-bit values on the Arduino core,
so millis() wraps back through zero after about 49.7 days of continuous uptime, and micros()
after about 71.6 minutes. A program that must run unattended across a wrap should compare elapsed
time with subtraction (Clock.millis() - start), which stays correct across exactly one wrap
because of unsigned/twos-complement overflow arithmetic, rather than comparing timestamps directly
with </>.
Clock vs. Delay
Delay (millis/micros) blocks
the program for a fixed duration and does nothing else meanwhile. Clock instead reports elapsed
time, so a program built around it can do other work — polling a button, updating a display,
handling network I/O — while still acting on a schedule. Reach for Clock whenever a loop needs
to do more than one timed thing at once; reach for Delay when blocking is genuinely fine, as in
the classic single-LED blink.
Example: non-blocking periodic action
import io.github.jabrena.juno.annotations.ArduinoUnoR4WiFi;
import io.github.jabrena.juno.annotations.Board;
import io.github.jabrena.juno.api.Clock;
import io.github.jabrena.juno.api.io.DigitalOutput;
import io.github.jabrena.juno.api.io.Gpio;
@Board(ArduinoUnoR4WiFi.class)
public final class ClockBlink {
private static final int LED_PIN = 13;
private static final int BUTTON_PIN = 2;
private static final int BLINK_INTERVAL_MILLIS = 500;
public static void main(String[] args) {
DigitalOutput led = DigitalOutput.of(LED_PIN);
Gpio.pinMode(BUTTON_PIN, Gpio.INPUT_PULLUP);
int lastToggle = Clock.millis();
while (true) {
// Blink on a schedule...
if (Clock.millis() - lastToggle >= BLINK_INTERVAL_MILLIS) {
led.toggle();
lastToggle = Clock.millis();
}
// ...while still reacting immediately to other input every iteration.
if (!Gpio.digitalRead(BUTTON_PIN)) {
led.high();
}
}
}
}
Unlike a Delay.millis(500) blink, this loop never stops watching the button, because Clock
only measures time — it never pauses execution.
The repository's
Pomodoro timer
uses the same pattern for real: Clock.millis() is its sole time source for a countdown displayed
on an LcdKeypadShield, so the displayed time stays accurate regardless of
how long each tick's own display/print work takes.
Measuring how long something took
Clock.micros() is precise enough to profile a routine's execution time directly on the board,
which is useful when Serial is the only visibility into a running program:
int start = Clock.micros();
someExpensiveOperation();
int elapsedMicros = Clock.micros() - start;
Serial.print("elapsed us: ");
Serial.println(elapsedMicros);
Troubleshooting
- Elapsed time looks wrong after a very long uptime: this is the
millis()/micros()32-bit wraparound described above. Always compute elapsed time asClock.millis() - start(subtraction), neverClock.millis() > deadline(direct comparison), so a single wrap doesn't produce a spuriously huge or negative elapsed value. - A
Clock-scheduled action seems to run late:Clock.millis()/Clock.micros()only report time — they don't preempt anything. A loop iteration that blocks onDelayor a slow HID/I/O call still delays the nextClockcheck by that same amount.