Autonomous Selection for V5 (Example Code Provided)

I want to make this clear right off the bat: this is not a request for support, but a post to help others. I am not sure if this is the right category for this type of post.

I’ve seen a fair few threads where people are looking to make code that allows them to select their autonomous routine before the match. Our team has been doing this since the days of our 393 electronics, originally by using a potentiometer with a piece of paper stuck to it for labeling and a small piece of metal attached to the pot for spinning it around and selecting.

With the addition of the screen on the controller, a good new option has opened up: an interactive menu. When I started programming sophomore year, I threw together a “selection screen” that I want to share here now to help those who wish to also have selection code. I make no promises about the reliability of the code in edge cases, but it has worked quite well for the past couple of years.

With an explanation below, here is the code:

int team = 0;
int autonRoutine = 0;
int variation = 0;

std::string teams[5] = {"", "Red", "Blue", "Skills", ""};
int teamCount = 3;
std::string routines[6] = {"", "No Auton", "Strong", "Weak", "Home Row",  ""};
int sideCount = 4;
std::string autonVariation[4] = {"", "Home Row Goal", "Center Goal", ""};
int variationCount = 2;

void incSelection() { selection++; }
void decSelection() { selection--; }

bool confirm = false;
void confirmSelection()
{
  confirm = true;
}

int selection = 1;

void printToScreen(std::string list[], int options)
{
  while((Competition.isEnabled() == false && Competition.isCompetitionSwitch() == true) && !confirm)
  {
    int curSelection = selection;
    if(curSelection > options)
    {
      curSelection = options;
      selection = options;
    }
    if(curSelection < 1)
    {
      curSelection = 1;
      selection = 1;
    }

    Controller.Screen.clearScreen();
    Controller.Screen.setCursor(1, 1);
    Controller.Screen.print("%s", list[curSelection-1].c_str());
    Controller.Screen.setCursor(2, 1);
    Controller.Screen.print("%s", list[curSelection].c_str());
    Controller.Screen.setCursor(3, 1);
    Controller.Screen.print("%s", list[curSelection+1].c_str());

    vex::task::sleep(100);
  }
  confirm = false;
}

void selectionScreen()
{
  Controller.ButtonUp.pressed(decSelection);
  Controller.ButtonDown.pressed(incSelection);

  Controller.ButtonA.pressed(confirmSelection);



  //Get team
  printToScreen(teams, teamCount);
  team = selection;
  selection = 1;

  if(team != 3) //If not skills, get auton type
  {
    printToScreen(routines, sideCount);
    autonRoutine = selection;
    selection = 1;

    printToScreen(autonVariation, variationCount);
    variation = selection;
    selection = 1;
  }

  Controller.Screen.clearScreen();
  Controller.Screen.setCursor(1, 1);
  Controller.Screen.print("Team: %s", teams[team].c_str());
  Controller.Screen.setCursor(2, 1);
  Controller.Screen.print("Auton: %s", routines[autonRoutine].c_str());
  Controller.Screen.setCursor(3, 1);
  Controller.Screen.print("Type: %s", autonVariation[variation].c_str());
}

In order, here is what each thing does.

int team = 0;
int autonRoutine = 0;
int variation = 0;

These are just some variables to store the information retrieved from the driver. The number they store is the index in the corresponding array that has been selected.

std::string teams[5] = {"", "Red", "Blue", "Skills", ""};
int teamCount = 3;
std::string routines[6] = {"", "No Auton", "Strong", "Weak", "Home Row",  ""};
int sideCount = 4;
std::string autonVariation[4] = {"", "Home Row Goal", "Center Goal", ""};
int variationCount = 2;

These are the aforementioned corresponding arrays. The variables here, “teamCount”, “sideCount”, and “variationCount” refer to the amount of valid options. There are empty quotes at the ends of the array to allow the following code to print one index past the valid options in each direction without crashing. It just makes the code easier to understand in my mind.

void incSelection() { selection++; }
void decSelection() { selection--; }

These are functions that I bind to “Controller.ButtonUp.pressed()” and “Controller.ButtonDown.pressed()”. This is how the user will navigate the menu presented. I unbind these functions once autonomous starts.

bool confirm = false;
void confirmSelection()
{
  confirm = true;
}

This is a similar situation when compared to the prior code block. I bind this to “Controller.ButtonA.pressed()” and use it to detect when the user has confirmed their selection.

int selection = 1;

void printToScreen(std::string list[], int options)
{
  while((Competition.isEnabled() == false && Competition.isCompetitionSwitch() == true) && !confirm)
  {
    int curSelection = selection;
    if(curSelection > options)
    {
      curSelection = options;
      selection = options;
    }
    if(curSelection < 1)
    {
      curSelection = 1;
      selection = 1;
    }

    Controller.Screen.clearScreen();
    Controller.Screen.setCursor(1, 1);
    Controller.Screen.print("%s", list[curSelection-1].c_str());
    Controller.Screen.setCursor(2, 1);
    Controller.Screen.print("%s", list[curSelection].c_str());
    Controller.Screen.setCursor(3, 1);
    Controller.Screen.print("%s", list[curSelection+1].c_str());

    vex::task::sleep(100);
  }
  confirm = false;
}

This is the brains of what displays the selections. The function takes a list of strings and an integer representing how many valid options there are. It assumes that index 0 and index “options + 1” are blank strings that can be safely accessed and printed. It will forever loop as long as a competition switch is connected, nothing has been enabled, and a selection is yet to be chosen.

To prevent any weird errors happening by the “selection” variable changing in the middle of this loop, the selection is stored in the “curSelection” variable and only that variable is used when presenting the screen. Then, there are a few checks to make sure the user does not navigate outside of the accepted range.

Finally, this loops until autonomous is enabled or the user confirms a selection. After confirming a selection, the variable “selection” will be have the index of whatever item was printed in the center of the screen. This will be used in the final section of this code.

void selectionScreen()
{
  Controller.ButtonUp.pressed(decSelection);
  Controller.ButtonDown.pressed(incSelection);

  Controller.ButtonA.pressed(confirmSelection);



  //Get team
  printToScreen(teams, teamCount);
  team = selection;
  selection = 1;

  if(team != 3) //If not skills, get auton type
  {
    printToScreen(routines, sideCount);
    autonRoutine = selection;
    selection = 1;

    printToScreen(autonVariation, variationCount);
    variation = selection;
    selection = 1;
  }

  Controller.Screen.clearScreen();
  Controller.Screen.setCursor(1, 1);
  Controller.Screen.print("Team: %s", teams[team].c_str());
  Controller.Screen.setCursor(2, 1);
  Controller.Screen.print("Auton: %s", routines[autonRoutine].c_str());
  Controller.Screen.setCursor(3, 1);
  Controller.Screen.print("Type: %s", autonVariation[variation].c_str());
}

This is the function I run when I actually do my selection code. This binds the controller buttons to interact with the screen, sets up the order I present things, and makes sure the selections are stored in the proper variables.

In each layer of selection, I run the “printToScreen” function with the array I want the user to select from. After that function finally returns, I store whatever “selection” was set to in the corresponding variable, reset “selection” back to 1, and move on to the next screen. In my case, I consider “skills” its own thing since team color does not matter in skills and there is a dedicated autonomous for skills. This is why I have a specific check for whether or not “team == 3”.

Finally, after all of this has been done, I have stored information corresponding to what autonomous routine it is that I actually want to run, and I can use those in some if-statements to run the proper code. I present this information to the user in an easy to read format so that they may double check their selections.

Note that I have a “no auton” option. Please have this as an option for your own teams. Sometimes things go wrong with autonomous. A sensor goes crazy. The field does match the way you have it set up at home. It happens. It’s happened to us.

The only thing worse than no autonomous is a bad autonomous that gets in the way. Don’t do that to your partner.

I have linked here a video that demonstrates this code in action. I hope this post helps somebody else figure out how to do their autonomous selection, and perhaps somebody will find a good way to improve upon this code. I can already think of something that would go wrong if someone were actively trying to break the code or were very unfortunate, but in practice, I don’t see this ever happening.

6 Likes