VEX C++ V5 - Creating autonomous routine in arcade control program

What I am trying to do is use the arcade control program and add a C++ function that is called (i.e., executed) when pressing the R1 button on the front of the V5 contoller. The code I tried didn’t do anything. It compiled okay, but didn’t seem to execute. Here is a snippet showing what I added to the standard arcade program code:

// Function I want to execute when R1 button is pressed:
void dizzy(){
LeftMotor.rotateFor(720,rotationUnits::deg);
//more code here to do some autonomous activity.
}
int main() {
// I added this:
Controller1.ButtonR1.pressed(dizzy);

int armSpeedPCT = 30;
int clawSpeedPCT = 35;

while(true) {
    //Drive Control
    //Set the left and right motor to spin forward…

Is the function call in your while true loop?

No. It’s in the int main(), but not in the while loop. I was thinking the command sets up the ‘callback’ function and I can then press the R1 button at any time and it should execute the designated function - in this case: dizzy(). Is this the way to do it?

yes, using pressed as you have will register dizzy as a callback. However, you will have issues if you send control values to LeftMotor from main as well as dizzy. perhaps post more of your code and we can make suggestions.

2 Likes

If it isn’t in your while loop, you won’t be able to use in while the program is running because it will be executing the loop infinitely. If you want to be able to use the function you have to put the call inside the while loop.

You are obviously in general correct. However in this case, as explained by everyone else in the thread that is not the way the pressed function works and you are incorrect.

1 Like

Ok. It seems that I am trying to combine two incompatible tasks. Thanks.

Sorry, maybe it wasnt clear. You seem to be using the pressed function to configure a callback correctly. As @jpearman mentioned we need to see more code to see what other mistakes you might be making.

I was being overly harsh to @Anomalocaris not yourself.

1 Like

Here is (finally) the complete code:

#include "robot-config.h"
void dizzy(){
    LeftMotor.rotateFor(720,rotationUnits::deg);
}

int main() {
    Controller1.ButtonR1.pressed(dizzy);
    int armSpeedPCT  = 30;
    int clawSpeedPCT = 35;
    while(true) {
        LeftMotor.spin( vex::directionType::fwd, (Controller1.Axis3.value() + Controller1.Axis4.value())/3.5, vex::velocityUnits::pct); //(Axis3+Axis4)/2
        RightMotor.spin(vex::directionType::fwd, (Controller1.Axis3.value() - Controller1.Axis4.value())/3.5, vex::velocityUnits::pct);//(Axis3-Axis4)/2
        
        if(Controller1.ButtonX.pressing()) { //If button up is pressed...
            ArmMotor.spin(vex::directionType::fwd, armSpeedPCT, vex::velocityUnits::pct);
        }
        else if(Controller1.ButtonB.pressing()) { //If the down button is pressed...
            ArmMotor.spin(vex::directionType::rev, armSpeedPCT, vex::velocityUnits::pct);
        }
        else {
            ArmMotor.stop(vex::brakeType::brake);
        }
        
        if(Controller1.ButtonA.pressing()) {
            ClawMotor.spin(vex::directionType::fwd, clawSpeedPCT, vex::velocityUnits::pct);
        }
        else if(Controller1.ButtonY.pressing()) { 
            ClawMotor.spin(vex::directionType::rev, clawSpeedPCT, vex::velocityUnits::pct);
        }
        else { 
            ClawMotor.stop(vex::brakeType::brake);        
        }
       
		vex::task::sleep(20); 
    }
}

Yep, anything that dizzy does is immediately usurped by the first line of your loop. A concept called a mutex can help coordinate usage of a shared resource between two different threads. The three VEX-like C++ offerings (Robot Mesh Studio, VCS, and VEXcode) have a class called vex::mutex.

The way a mutex works is like a single key to a door, where threads are the people trying to use the key and the shared resource is behind the door. When a thread lock's a mutex, it waits to take the key before continuing. If a thread try_lock's a mutex, it takes the key if it’s available and returns true or false depending on if it successfully took the key or not. A thread calling unlock on the mutex returns the key for another thread (or itself) to pick up later.

Similarly to an architect adding additional doors into a room, it’s entirely possible for you the programmer to add other ways into the room (which is bad). It is incumbent on the programmer to make sure any thread trying to use the resource (LeftMotor in your case) only ever tries to do it by going through the door. So your code could look something like this if it used a mutex:

vex::mutex LeftMotorMutex();

void dizzy () {
    LeftMotorMutex.lock()
    LeftMotor.rotateFor(720,rotationUnits::deg);
    LeftMotorMutex.unlock()
}

...

while (true) {
    if (LeftMotorMutex.try_lock()) {
        LeftMotor.spin(...);
        LeftMotorMutex.unlock()
    }
    ...
}
1 Like

Thanks for the clear explanation and sample code. That makes a lot of sense. I’m going to try it and report back. (It may take a few days, however. I’m not near the robot to test it right now.)

1 Like

Just to add John explains it in terms of a mutex. But I think it’s helpful to understand that all a mutex is is a global variable every separate part of your program agrees corresponds to controlling a motor. There is nothing fancy going on.

1 Like

For this simple example, mostly yes.

The fancy stuff going on happens when you don’t immediately release a mutex lock, as a mutex also pays attention to which thread is holding the key. So for this case where a thread is doing something with the mutex then immediately unlocking it, a global boolean variable that just records “motor in use” would suffice, but if you were doing operations where you might want to hold onto the lock for a while, a mutex is useful as it also records who is using it. So if a thread were to have three conditions, A, B, and C, where A and B are sufficient to try and lock a mutex but it only unlocks on C, it would have the correct behavior when it saw A, B, then C in order. For a simple Boolean lock, it would do A, fail to do B, then do C. It also avoids a thread unlocking someone else’s lock when it isn’t supposed to, so it would only unlock on C if it had first seen A or B.

The other thing a mutex should be doing is entering a critical section where other threads aren’t allowed to do anything while it picks up the key. This is important in the case of a preemptive scheduling paradigm (used by Robot Mesh Studio and PROS) in which a thread can be paused by the scheduler at any point. (VCS and VEXcode use a cooperative scheduler that only yields when instructed to, with yields built into most vex namespace calls.) This means that if you used a simple Boolean variable and did the following check:

if (!resourceInUse) {
    resourceInUse = true;
    useResource();
    resourceInUse = false;
}

It would produce wrong behavior if the thread was swapped between checking the flag and using the flag. Some other thread could check the flag in the meantime, not knowing that this thread had just checked it and was about to start using it. try_lock on a mutex should never be interruptible by a scheduler between the “check” and “lock” steps.

***There is one very important warning when using mutexes with any scheduler, though, is that if a thread is killed from elsewhere while it’s holding a mutex lock, the mutex will remain locked for good. One thing that kills threads is competition control, so if you’re using a mutex with competition control, you’ll want some way to detect and destroy mutexes that have become permanently locked.

2 Likes