Moving forward while spinning the intake. Raising the arm during a turn. Ejecting into a goal while still rolling forward. Three clean methods for running multiple mechanisms at the same time during autonomous — from simple to powerful.
pid_wait() is a blocking call. The program stops at that line and waits until the drive PID finishes. While it waits, your intake, arm, and claw code never execute. The robot moves — but it does not do anything else while moving.intake.move() or arm.move() before starting the drive. The mechanism runs while the drive PID executes. Simplest approach — works when you want a mechanism running for the entire drive duration.pid_drive_set() and pid_turn_set() are non-blocking — they start the movement and return immediately. Only pid_wait() blocks. So anything you call before pid_wait() happens concurrently with the drive.Start as many mechanisms as you want before calling pid_wait(). All of them run while the drive executes:
move_absolute() returns immediately but the arm takes time to physically reach its target. If the drive finishes before the arm reaches position, you will start scoring before the arm is ready. Either extend the drive distance slightly to give the arm time, or check arm position before scoring — shown in the Full Route Example.Chain multiple pid_wait_until() calls in a single drive to trigger different actions at different points:
pid_wait() alone.move() call at a trigger point — for example, an arm that needs to hold position at a preset while the drive does multiple movements, or a mechanism that responds to a sensor during the autonomous sequence.Start a task, do other movements, then kill the task when done:
A task that raises the arm to a position and holds it there, freeing the main thread to handle driving:
chassis.pid_drive_set() from inside a task — only drive from the main autonomous thread. Tasks are for mechanism control (motors, pneumatics, sensors) not drive control. EZ Template’s PID runs in its own internal task and manages the drive for you.| Situation | Method | Code |
|---|---|---|
| Intake runs entire drive | Method 1 | move() then pid_drive_set() |
| Arm rises during a turn | Method 1 | move_absolute() then pid_turn_set() |
| Intake starts mid-drive | Method 2 | pid_wait_until(dist) then move() |
| Eject starts 5" before stopping | Method 2 | pid_wait_until(dist) then move(-127) |
| Mechanism runs across multiple drive moves | Method 3 | pros::Task |
| Arm holds at height, then scores on signal | Method 3 | pros::Task with armTaskDone flag |
pros::delay() is a yield point — it tells the scheduler to run other tasks while this one waits. Without delay calls in a loop, a task monopolizes the CPU (starvation). Understanding that concurrency is simulated on a single core — not truly parallel — explains why race conditions and shared state bugs occur.pros::delay(). What happens to the rest of your robot code?