Skip to content

Match lifecycle

A match runs on its own clock, whether or not your code is ready. This page is about when MARLIN calls your code, what happens in the gaps between phases, and why your callbacks do not need to watch the clock themselves.

The normal MARLIN lifecycle is:

construct Robot → configure hardware → run match

You call configure() once, then run(). run() takes over from there: it waits for the match to start, calls your code for each phase, and idles every effector the rest of the time.

Phase callbacks

Robot.run() accepts three optional callbacks:

robot.run(
    initialize=initialize,
    autonomous=autonomous,
    driver=driver,
)
Callback When it runs
initialize(robot) Once, before the first phase starts; setup only
autonomous(robot) During the Autonomous Period
driver(robot) During the Driver Controlled Period

If you leave a callback out, MARLIN does nothing during that phase and idles every effector.

initialize() is for setup, not movement

initialize() runs before the match starts, so it is where you read sensors, print, and work out values the other callbacks need:

def initialize(robot):
    print("Battery:", robot.battery_voltage, "V")

Commanding a thruster or servo there does nothing, and MARLIN gives you no error. The match has not started, so MARLIN idles every output as soon as initialize() returns. Anything that positions your robot goes at the top of autonomous() or driver() instead:

def autonomous(robot):
    robot.servo("M2").angle(0)      # stow the arm
    while robot.running:
        robot.thruster("M1").set_duty(25)

Use robot.running inside phase callbacks

Each phase callback owns its own while loop:

def autonomous(robot):
    while robot.running:
        robot.thruster("M1").set_duty(25)

robot.running is true only while:

  • the current phase is the Autonomous Period or the Driver Controlled Period
  • the matching callback is active
  • that phase has not ended.

When the pool server moves on, robot.running becomes false and the loop exits. It becomes true again inside the next matching callback.

Returning early

A callback may return before its phase ends:

def autonomous(robot):
    robot.thruster("M1").set_duty(30)
    # Returning idles all outputs. This callback is not called again.

When any phase callback returns, MARLIN idles every effector. The connection to the robot stays up, but MARLIN will not call that function again for the rest of that phase. If you return early from driver(), the robot is dead in the water until the next phase starts.

Idling stops a thruster, but it moves a servo: a servo is always holding some angle, so idling sends it to the centre of its travel, 90°. Anything you positioned with angle() returns there when the phase ends. See Effector commands and safety.

Match timing

For Link Lockdown, a Match is three minutes of playtime: 20 seconds of Autonomous Period followed by 2 minutes 40 seconds of Driver Controlled Period. The game manual is the source of truth for official timing and match rules.

flowchart LR
    auton["Autonomous Period<br/>20 seconds<br/>runs autonomous(robot)"]
    pause["Pause<br/>no fixed length<br/>your code does not run"]
    driver["Driver Controlled Period<br/>2 minutes 40 seconds<br/>runs driver(robot)"]
    post["Match over<br/>your code does not run"]

    auton --> pause --> driver --> post

The two phases are separated by a pause that does not count towards playtime and has no set length. The pool server stays there until a match official starts the Driver Controlled Period. Your code does not run during the pause and every effector is idled, so plan for an unknown gap between autonomous() ending and driver() starting. The same is true after the match ends.

E-Stop

robot.estopped reports E-Stop state but cannot change it:

if robot.estopped:
    print("Robot is E-Stopped")

E-Stop authority belongs to the MARLIN extension and the pool dashboard. There is no Python API to trigger or clear E-Stop, ensuring that safety control remains independent of your program.

run() idles all outputs and returns when the match ends or an E-Stop is triggered.