We’ve had some feedback recently from teams who have had issues with competition control. I’ve looked at some of the team code and noticed that often problems are being caused by tasks started in either the driver or autonomous section of the program being allowed to run during the other phase of the match. This can occur with programs written in either VEXcode or PROS (and I assume RobotMesh, but I’m not planning to specifically discuss that programming environment in this topic) although there are differences that are specific to each type of program.
TL;DR
If you create any new tasks at the beginning of autonomous or driver control that may interfere with the operation of the robot, those tasks should be disabled/stopped during the other phase of the match,
First a little background.
Competition control on the V5 is relatively simple. All competition control information is collected by the V5 controller either through the legacy competition control port on the back, from the match simulation controls built into the controller or, most recently, through the USB port when connected to a live remote event. The competition status only consists of four pieces of information.
- whether competition control is connected (or in the case of USB, is enabled)
- the type of competition control, a competition switch or a field controller.
- The current phase of the match, driver or autonomous.
- Whether the robot should be disabled or enabled.
This status is sent to the V5 brain in every message that the controller sends. The V5 brain, as we have discussed before, has two cpus that run vexos and user programs. The competition status is made available to both.
The cpu that runs vexos uses the status to do things such as disabling the motors and controller when the robot should be disabled and logging the different match phases. The cpu that runs user code is responsible for using the status to start and stop the driver and autonomous parts of your program. In the case of VEXcode, this is all part of the system code that’s included with vexos. An event system is monitoring the competition status and sending events to the VEXcode competition class. When using PROS, however, the competition status is monitored by PROS directly in the system daemon and tasks are created and deleted as necessary.
I’ll now focus on VEXcode and cover PROS in the next post.
VEXcode provides a competition class, it’s used to start one of two user provided tasks (threads and tasks are the same as far as this discussion is concerned). The default names for these are “autonomous” and “usercontrol” but any valid function can be registered with the competition class if you want to call them something else. The competition class will only allow one of these two tasks to run at any one time, it disables both when the robot is disabled. However, any tasks that your code creates in main, pre_auton or at the beginning of these tasks are not under control of the competition class and will continue to run as the competition control status changes. This may be desired behavior, I often use a separate task in my code that constantly displays the status of motors and sensors on the brain screen. I would want this task to run during the entire match, there’s no need to stop it. However, tasks running PID loops or other code that directly controls motors should only be running when needed.
One important thing to understand is that there is a good chance that your driver control code will have been called even before the autonomous phase of the match. Whether this happens will depend on when the user program is started and when the controller is connected to field control or the live remote system. If you run the program first and subsequently connect to field control, the driver code will have been started and the robot disabled only when field control is connected. Reversing the order, connect to the field first and then start the user program, will generally avoid this from happening. It’s also important to understand that event handlers (for example, the function that gets called when you register using pressed and release calls on buttons) should only ever be registered once, if you register them at the beginning of driver control additional events will be added if driver control is started more than once. So how do we deal with all of this, a couple of options exist.
- set the Competition.bStopAllTasksBetweenModes flag
Competition.bStopAllTasksBetweenModes = true;
This tells the competition class to stop all tasks when the robot becomes disabled and also removes all event handlers. It effectively tries to reset the program back to the state it was when main was entered. The downside to this is that, as it says, all tasks will be stopped, even those you may wish to keep running. I’ll post a demo using this flag later on.
- Keep track of any tasks you start and make sure they are stopped between competition phases. Register any events in main or pre_auton rather than in the auton or driver tasks.
The easiest way of doing this is using global task (or thread) instances that are then used to stop tasks when required. As the competition class does not provide a callback when entering the disabled state you would need to add additional code to be able to do this, there are a couple of options.
- poll the IsEnabled() flag
bool bWasEnabled = false;
while (true) {
bool bEnabled = Competition.isEnabled();
// If you want to kill tasks when going disabled.
// do it here.
if( !bEnabled && bWasEnabled ) {
killTasks();
}
// save enabled state
bWasEnabled = bEnabled;
this_thread::sleep_for(20);
}
- use an mevent
while (true) {
if( Competition.DISABLED ) {
killTasks();
}
this_thread::sleep_for(20);
}
and what on earth is an “mevent” you ask ?
ok, quick digression.
A mevent is a little discussed feature of VEXcode that is part way between polling (ie. checking something periodically in a loop) and a full event handler where a callback (a fancy name for a function called by some system code) is registered. An mevent is something that can be checked occasionally in a loop but remembers if something has happened since the last time it was checked. mevents are available on everything (I think) where a normal event handler can be used, for example, controller buttons all have mevents in the form of
Controller.ButtonL1.PRESSED or Controller.ButtonL1.RELEASED
and bonus points to anyone who can tell me why they are named mevent.
anyway, lots of words, perhaps easiest thing is to look at examples.
I’ll add those in the next couple of posts