L4 · PAI-110

Async Tasks & the Real-Time Loop

Structure firmware the Embassy way — concurrent concerns on one real-time loop — by running a navigator AND a fixed-rate LED heartbeat off the millisecond clock, without blocking.

01
Challenge

Try this first — before any explanation.

Ground control needs proof the firmware is alive: the status LED must blink at ~2 Hz WHILE the rover navigates. The starter navigates but the LED is dark. Add the heartbeat — and keep it ticking the whole drive.

The Bench

Two concurrent concerns on one non-blocking real-time loop — navigate AND blink, Embassy-style.

! { loop {} }\nuse physical_ai_hal::*;\n\n// Two concurrent concerns, Embassy-style: a NAVIGATOR that drives to the goal,\n// and a HEARTBEAT that blinks the LED at ~2 Hz so ground control knows the\n// firmware is alive. This starter navigates but never blinks. Add the heartbeat\n// using millis() — toggle the LED every 250 ms — WITHOUT blocking the loop.\nfn control(p: &mut Peripherals) {\n // --- navigator task ---\n let dist = p.goal.distance();\n let bearing = p.goal.bearing();\n let turn = 1.0 * bearing;\n let forward = (0.8 * dist).min(0.6);\n p.motors.set(forward - turn, forward + turn);\n\n // --- heartbeat task (TODO) ---\n // let on = (millis() / 250.0) as i32 % 2 == 0;\n // p.led.set(on);\n}\ncontrol_loop!(control);\n","task":{"goal":[1.2,1.2],"requireBlink":6,"time":12.0,"tol":0.2},"title":"Async Tasks & the Real-Time Loop"}">
PYTHON · NUMPY · IN-BROWSER

Async Tasks & the Real-Time Loop

Two concurrent concerns on one non-blocking real-time loop — navigate AND blink, Embassy-style.

02
Model

The idea, built visually.

Real firmware never does one thing. Embassy models this as async tasks: a navigator and a heartbeat, each await-ing time, cooperatively scheduled on one core — no busy-waiting, no blocking. The golden rule of the real-time loop: never block. You don't sleep(250ms) inside the loop — that freezes steering. Instead you check the clock: has 250 ms elapsed? then toggle. Each tick does a slice of every task and returns. That's how one loop runs many concerns in lockstep with real time.

▣ Stage animation: Two async task lanes (navigator, heartbeat) interleaving on one timeline; 'sleep(250)=FROZEN' red anti-pattern vs 'check millis()' green; LED pulsing 2 Hz while the rover curves to the pad.

03
Guided practice

Build it up, step by step.

1. Keep the navigator — that's your first task. 2. Heartbeat off the clock — don't block. Uncomment let on = (millis() / 250.0) as i32 % 2 == 0; then p.led.set(on); — toggles every 250 ms from time alone. 3. Why not delay? A loop/busy delay here hangs every other concern — each tick must return fast. 4. Reach the pad with the heartbeat ticking the whole way.

04
Feedback

How the Bench grades your run.

PASS WHEN Rover reaches the pad AND the heartbeat LED toggles at ~2 Hz the whole way — two concurrent tasks, one non-blocking loop.

  • Reached the pad but the LED never toggled — add: let on = (millis() / 250.0) as i32 % 2 == 0; p.led.set(on);
  • Heartbeat toggled too few times — period too long. 250 ms gives ~2 Hz; divide millis() by 250, not 2500.
  • Everything froze/timed out — you blocked the loop. Never block: derive the LED from millis() and return each tick.
05
Retrieve & space

Bring back what you've already mastered.

  • Real-time-loop golden rule: never ____ inside the loop (block).
  • Embassy runs many tasks on one core by ____ scheduling (cooperative).
  • To blink at 2 Hz you toggle every ____ ms (250).
06
Mastery gate

What you must demonstrate to advance.

Firmware running two concurrent timed behaviours on one non-blocking loop — the embedded-Rust/Embassy concurrency model on real physics.

07
Project

How this feeds your build.

The async structure the metal capstone uses to fuse navigation, safety, and telemetry.