Trailblazer Robotics
Unit 7 · Week 13

Tasks, Classes, & Debugging

The last skills you need before the capstone.

You'll learn

Tasks (parallel programming)

Sometimes you need two things to happen at the same time. The driver wants to raise the arm while the intake keeps spinning. Odometry needs to update while autonomous code runs. That's what tasks are for.

int intakeTask() {
  while (true) {
    if (Controller1.ButtonL1.pressing()) {
      Intake.spin(forward);
    } else {
      Intake.stop();
    }
    wait(20, msec);
  }
}

int main() {
  task t1(intakeTask);     // runs forever in the background
  // ... main code runs without waiting for intakeTask
}
Use sparingly. Tasks are powerful but easy to misuse: programs with too much parallelization quickly become bug-prone and unmaintainable. Start with one or two tasks (odometry, intake) and add more only when you have a real reason.

Classes: packaging a subsystem

A class bundles related data and functions. For a drivetrain:

class Drive {
  public:
    motor &left;
    motor &right;

    Drive(motor &l, motor &r) : left(l), right(r) {}

    void forward(int pct) {
      left.spin(vex::forward, pct, percent);
      right.spin(vex::forward, pct, percent);
    }

    void stop() {
      left.stop();
      right.stop();
    }

    void turnRight(int deg) {
      left.spinFor(vex::forward,  deg, vex::deg, false);
      right.spinFor(vex::reverse, deg, vex::deg, true);
    }
};

// Usage
motor L = motor(PORT1, false);
motor R = motor(PORT6, true);
Drive drive(L, R);

int main() {
  drive.forward(50);
  wait(1, seconds);
  drive.turnRight(90);
  drive.stop();
}

Now your main code reads like English. Behind the scenes, the Drive class hides the messy motor details. Add encoder-based driving and PID inside the class, and your top-level code never changes.

The debugging mindset

Most beginners debug by guessing. Pros debug by narrowing down. The five-step process:

  1. Reproduce. Can you make the bug happen on demand? If not, find that out first.
  2. Isolate. Comment out half the code. Does the bug still happen? Now you know which half to look at.
  3. Observe. Print sensor values, state, and key variables to the Brain screen. Don't assume, measure.
  4. Hypothesize. Form a specific guess: "I think the gyro is returning 0 because it isn't calibrated yet."
  5. Test. Change one thing. Re-run. Did it fix it? If yes, great. If no, go back to step 4 with new information.

Useful debugging tools

// Print to the Brain screen
Brain.Screen.clearScreen();
Brain.Screen.setCursor(1, 1);
Brain.Screen.print("error: %.2f", error);
Brain.Screen.setCursor(2, 1);
Brain.Screen.print("power: %.2f", motorPower);

// Print to the terminal for longer logs
printf("encoder L=%.1f R=%.1f\n",
       LeftMotor.position(degrees),
       RightMotor.position(degrees));

Version control (use Git)

Use Git. At minimum: commit when something works. That way, when your "small change" breaks everything, you can roll back. For a high schooler: GitHub Desktop is the easy way in, install it, click Commit when things work, click Discard when they don't.

Curated references

Coding Challenge

Build a Drive class with turnByGyro()

Create a Drive class that wraps your left and right motors plus the gyro. Give it three public methods:

Then write a 4-line autonomous routine that uses only your class to drive in a 24-inch square. The point is that good classes hide complexity from the code that uses them.

No solution is given for this challenge.

Find the Bug

Two programs that misuse tasks or classes. Spot the bug.

Problem 13A
This intake task is supposed to run forever in the background, spinning the intake whenever button L1 is pressed. The button works once, then the intake never responds again. Why?
int intakeTask() {
  if (Controller1.ButtonL1.pressing()) {
    Intake.spin(forward);
  } else {
    Intake.stop();
  }
  return 0;
}

int main() {
  task t1(intakeTask);
  while (true) {
    wait(20, msec);
  }
}
Problem 13B
This Drive class compiles but when you call drive.forward(50), the robot doesn't move at all. The motors are powered. What is the class doing wrong?
class Drive {
  public:
    motor left  = motor(PORT1, false);
    motor right = motor(PORT6, true);

    void forward(int pct) {
      left.spin(vex::forward, pct, percent);
      right.spin(vex::forward, pct, percent);
    }
};

motor L = motor(PORT1, false);
motor R = motor(PORT6, true);
Drive drive;

int main() {
  drive.forward(50);
  wait(2, seconds);
  drive.left.stop();
  drive.right.stop();
}
← Week 12 Next: Capstone →