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:
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:
| 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:
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:
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:
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.