ROBOTC: Functions

I recently responded to a question about subroutines on the VEX Forum.  I wrote up a fairly detailed response, so I decided to turn it into a blog post.

Oftentimes, in programming, we come across situations where we need to do the same operation (or a slight variation thereof) multiple times. For this situation, we have what is called a function (or subroutine, or method) [see note at bottom of page]. I’ll use the terms interchangeably in this post. Subroutines are very useful tools for programmers.  They reduce the time it takes to write a program, and also reduce program size.

This post describes (in quite a bit of detail) how subroutines are declared and used in ROBOTC.  I also show how to pass or return values to or from a function.

Declaring a function in ROBOTC

With all the #pragmas and comments stripped out, the competition template is basically:

void pre_auton(){
}

task autonomous() {
}

task usercontrol() {
}

Tasks (for example, usercontrol() and autonomous()) are a different beast than subroutines, and may be covered in another post at another time. pre_auton() is a subroutine. The competition background code tells the Cortex when to run this subroutine.

Note that the subroutines and tasks all have curly braces (that is, “{}”) after them. This defines the beginning and end of the subroutine/task. Note also that there is program space outside of all the subroutines and tasks. This is where you define your custom subroutines.

So, if I had a subroutine “driveForward()” that drives the robot forward for 1 second, my code would look like:

void driveForward() {
  motor[leftMotor] = 127;
  motor[rightMotor] = 127;
  wait1Msec(1000);
  motor[leftMotor] = 0;
  motor[rightMotor] = 0;
} 

void pre_auton() {
}

task autonomous() {
}

task usercontrol() {
}

(I’m not declaring motor names here, but just use common sense when reading it.)

Using a function in ROBOTC

To use the function in your autonomous code, your autonomous task would look like this:

task autonomous() {
  driveForward();
}

So, when the Cortex is walking through the code, it comes across the “driveForward();” line and jumps to where the subroutine “driveForward()” was written. It then executes all the code there (turn on motors, wait, stop), and returns to where it left off in the autonomous task.

Parameters

However, the above approach isn’t really practical–after all, we might want to drive forward for one second, do something else, then forward for two seconds, do something else, then forward for 5 seconds. What now?

Parameters to the rescue! These parameters are variables which you provide values for when you run the function. An example is best to demonstrate. We’ll make our subroutine from the previous example drive forward for a specified number of milliseconds.

The parameters are defined just like normal variables (type followed by name) and go inside the parenthesis after the function name:

void driveForward(int duration) {
  motor[leftMotor] = 127;
  motor[rightMotor] = 127;
  wait1Msec(duration);

  motor[leftMotor] = 0;
  motor[rightMotor] = 0;
}

Note that we don’t assign a value to the variable anywhere inside the function. Instead, we assign this value when we call the function:

task autonomous() {
  driveForward(1000); //One second 
  //Do something else, like raise an arm

  driveForward(2000); //Two seconds

  //Do something else, like lower the arm

  driveForward(5000); //Five Seconds
}

Can we use two (or more) parameters, for example, duration and power? Certainly!  Here’s how:

void drive(int duration, int power) {
  motor[leftMotor] = power;
  motor[rightMotor] = power;

  wait1Msec(duration);
  motor[leftMotor] = 0;
  motor[rightMotor] = 0;
}

Note that power can be any integer in [-127, 127], so this allows the robot to drive forward and backward! This is why I renamed the subroutine to simply “drive.”

This is called from the autonomous task as:

task autonomous() {
  drive(1000, 127); //Forward 1 second at full speed
  drive(2000, -50); //Backwards 2 seconds at less than half speed
}

You can have a lot of functions in your ROBOTC program (I forget how many–there is a limit, but it’s ridiculously high. You won’t hit it unless you’re doing something crazy.)

Return Values

What if we want a function like we have in math class? You know, like \(f(x) = 2x\). If you ask for the value of \(f(4)\), you get \(8\) back out. How do we get a value back out of our subroutines?

The naive but wrong approach is:

void f(int x) {
    int returnValue = 2x;
  } 

  //later in program...

  task autonomous() {
  f(4);
  int y = returnValue;
}

This doesn’t work. This is because returnValue ceases to be declared after f stops running. So, what does work?

Notice the “void” at the beginning of the function declaration. Void (in English) means things along the lines of “nothing” or “null.” Think of this as saying the function will return Nothing. If you want to have the function return a value, we place the value type there.

Then, we tell the program what to return with the very creatively named “return” statement.

So, to make the above code work:

int f(int x) {
  return 2x;
} 

task autonomous() {
  int y = f(4); //Now y holds the value 8.
}

Conclusion

Subroutines are an invaluable tool for programmers.  I hope that this post has helped introduce you to the world of subroutines.  If you still have questions or responses, please leave a comment below!  (Note: comments are moderated, so they may take a little bit to be shown as public.)

The Note at the Bottom of Page

From what I know, there actually is a difference between a function, subroutine, and method. No one I’ve talked to really seems to care, but here’s what I’ve seen, anyway:
A subroutine is a “mini-program.” It just does a job. It can have parameters, but no return value.

A function is just like a function in mathematics. It can do a job and have parameters, just like a subroutine. However, a function returns a value, just like in math. So, an example of a function would be \(f(x) = 2x\). We put in 5, it returns 10.

A method is the term used when programming in Java, and it is an all inclusive term for both subroutines and functions.

Posted in Robot Programming, ROBOTC, Tutorial, VEX

Twitter