[Vex C++]: Pressing a Button to Reverse Drive

Vex C++
VCS Version: 20180820.10.20
VEXox Version: Don’t have it on me so not sure but I updated it yesterday so it should be up-to-date.

So I’m a relatively low-skilled programmer and if it wasn’t just telling a button to make something spin or telling something to spin for so many milliseconds then I don’t know what I’m doing. Only really used integers and voids and callbacks one year with a programming instructor in Middle School and I don’t remember what I was doing.
Anyways, I’d like to learn how to make a function that basically just reverses my drive motors when I toggle it to make it easier driving because my robot has intake and shooter on one side and cap-flipper on the other which sounds pretty simple. I’ve been trying to figure it out but I’m unskilled. I was hoping to use something similar to program my flywheel and I already typed that all out but compilation failures so I come to the lords of VEX for insight. Thank you. I’m just using a modified sample drive program if that information is needed.

Here is some code that I haven’t tested at all. Regardless, it is more like psuedocode than code.

The first set of if statements contains code for toggling the “reverse direction” button. As soon as you let go of the button, Button A in this case, it will toggle the boolean “drivetrainReversed.” Then, the second set of if statements will make the y speed negative depending on the boolean “drivetrainReversed.” Notice that I don’t reverse the rotation direction, because even when the robot is driving backwards, clockwise is clockwise and counterclockwise is counterclockwise.

To adapt this code to your robot:
• Change “ControllerName” to your controller object’s name, e.g. “Controller1”
• Change the motor names (e.g. “myDriveMotor1” to the actual drive motor names on your robot)
• Make sure you know how the motors need to be reversed and whatnot for the drivetrain

bool reverseBtnPressed = false;
bool drivetrainReversed = false;

//Driver loop
while(true)
{
    //Inside driver loop

    //Grab controller values
    int y = controllerName.Axis3.position(percentUnits::pct);
    int r = controllerName.Axis1.position(percentUnits::pct);

    //Switch drivetrain direction upon Button A release
    if(controllerName.ButtonA.pressing() && !reverseBtnPressed)
    {
        reverseBtnPressed = true;
    }
    else if(reverseBtnPressed)
    {
        drivetrainReversed = true;
        reverseBtnPressed = false;
    }

    //Set drivetrain
    if(drivetrainReversed)
    {
        //Make y negative if necessary, but leave r the same
        y = -y;
    }

    //Set all drivetrain motors
    myDriveMotor1.spin(directionType::fwd, y + r, velocityUnits::pct);
    myDriveMotor2.spin(directionType::fwd, y + r, velocityUnits::pct);
    myDriveMotor3.spin(directionType::rev, y - r, velocityUnits::pct);
    myDriveMotor4.spin(directionType::rev, y - r, velocityUnits::pct);

    //Delay
    task::sleep(20);
}

Note: If you have tank drive (let me know), things will be different. Instead of y and r, you would just flip both the left and right inputs. Here’s an example:


if(drivetrainReversed)
{
    //Instead of y = -y
    left = -left;
    right = -right;
}

Your if statement doesn’t quite work right. Look at the possibilities:

  1. start with drivetrainReversed = false and reverseBtnPressed = false. Both the if and else if fail, so it stays that way. Good. Then you press ButtonA. The first part of the if runs, and reverseBtnPressed = true now. A moment later as the code cycles through again (not when the ButtonA is released) the if fails but the else if runs, drivetrainReversed = true and reverseBtnPressed = false. Looks good. A moment later the if statement runs and reverseBtnPressed = true. Not great that it runs again and this back-and-forth will happen an unknown number of times, but at least you’re guaranteed to run the else if after you’ve released the button.
  2. start with drivetrainReversed = true and reverseBtnPressed = false, the state you’ve now reached. You have no way of making drivetrainReversed = false. And you can’t just change drivetrainReversed = true; to drivetrainReversed = !drivetrainReversed; because the code always fires off an unknown number of times and that would just make the final state random.

An easier way to deal with the button press for swapping is this:

void swapDirections () {
   drivetrainReversed = !drivetrainReversed;
}

and then when you start driver control, but not inside the while loop:


controllerName.ButtonA.pressed(swapDirections);

That’s not actually correct. If you push both sticks equally, yes it will drive the opposite direction in a straight line. But if you try to turn you’ll find it turning opposite to what you expect. Note how you only put a negative on the y and not the r in your arcade control. You want to effectively do the same thing with tank control. That’s done by swapping the sticks in addition to using negatives.

So I definitely agree that this if statement is broken (my fault!). I think this will work…


//Switch drivetrain direction upon Button A release
    if(controllerName.ButtonA.pressing())
    {
        if(!buttonReversed)
        {
            reverseBtnPressed = true;
        }
    }
    else if(reverseBtnPressed)
    {
        drivetrainReversed = !drivetrainReversed;
        reverseBtnPressed = false;
    }

I guess I totally forgot how to do a rising state :confused:

However, I definitely agree with you that having a callback function is simpler. The only reason I personally wouldn’t choose that is because it expands the breadth of driver control from contained wholly within the drivercontrol function to possibly polluting the global namespace. However, this is just preference and I absolutely see the merit to having the callback function. It’s much simpler to read and easier to follow, so I’d recommend it as well for OP.

You’re totally right! Would the following, then, work?


if(drivetrainReversed)
{
    left = -right;
    right = -left;
}

Thanks again for catching all of my mistakes… I could have really mislead OP, and should have definitely double-checked my advice before giving any; it’s better to say nothing than to suggest wrongly. Thank you, callen!

I also wanted to add this function so that our driver would have an easier time with parking. Here’s the solution I came up with, hope it helps!

// Driver Variables
bool DToggled;

//Driver Functions
void DToggle(){
    if (DToggled == true){
        DToggled = false;
        Controller1.rumble("-");
    }
    else if (DToggled == false){
        DToggled = true;
        Controller1.rumble(".");
    }
}
void PrintToggle(){
    if (DToggled == true){
        Controller1.Screen.clearLine();
        Controller1.Screen.print("Reverse");
    }
    else if (DToggled == false){
        Controller1.Screen.clearLine();
        Controller1.Screen.print("Forward");
    }
}

I added a function that would print the direction the drive train was moving. It was just to help our driver keep track of things.

// Drive Train
        
        
        // Forward
        if (DToggled == false){
            // Right Motor value      
            RFMotor.spin(directionType::fwd, ((Controller1.Axis3.position(percentUnits::pct)) - (Controller1.Axis1.position(percentUnits::pct))), velocityUnits::pct);
            RBMotor.spin(directionType::fwd, ((Controller1.Axis3.position(percentUnits::pct)) - (Controller1.Axis1.position(percentUnits::pct))), velocityUnits::pct);
        
            // Left Motor values
            LFMotor.spin(directionType::fwd, ((Controller1.Axis3.position(percentUnits::pct)) + (Controller1.Axis1.position(percentUnits::pct))), velocityUnits::pct);
            LBMotor.spin(directionType::fwd, ((Controller1.Axis3.position(percentUnits::pct)) + (Controller1.Axis1.position(percentUnits::pct))), velocityUnits::pct);
        }
        
        // Reverse
        else if (DToggled == true){
            // Right Motor value (Toggled)
            RFMotor.spin(directionType::fwd, ((-(Controller1.Axis3.position(percentUnits::pct)) - (Controller1.Axis1.position(percentUnits::pct)))), velocityUnits::pct);
            RBMotor.spin(directionType::fwd, ((-(Controller1.Axis3.position(percentUnits::pct)) - (Controller1.Axis1.position(percentUnits::pct)))), velocityUnits::pct);
        
            // Left Motor values (Toggled)
            LFMotor.spin(directionType::fwd, ((-(Controller1.Axis3.position(percentUnits::pct)) + (Controller1.Axis1.position(percentUnits::pct)))), velocityUnits::pct);
            LBMotor.spin(directionType::fwd, ((-(Controller1.Axis3.position(percentUnits::pct)) + (Controller1.Axis1.position(percentUnits::pct)))), velocityUnits::pct);
        }
        
        // Stops the drive train when there is no input
        else{
            LFMotor.stop(brakeType::coast);
            LBMotor.stop(brakeType::coast);
            RFMotor.stop(brakeType::coast);
            RBMotor.stop(brakeType::coast);
        }
        
        // Locks the wheels in place for better traction
        if (Controller1.ButtonDown.pressing() == true){
            LFMotor.stop(brakeType::hold);
            LBMotor.stop(brakeType::hold);
            RFMotor.stop(brakeType::hold);
            RBMotor.stop(brakeType::hold);
        }

Yes, that will work, but it’s laborious and involves an extra boolean. This is the same thing simplified:


if(controllerName.ButtonA.pressing()) {
   reverseBtnPressed = true;
}
else if(reverseBtnPressed) {
        drivetrainReversed = !drivetrainReversed;
        reverseBtnPressed = false;
}

True about the breadth of the controller. Of course, it fires off faster than your code, too. Your code waits for the release to reverse the direction, while this one does it on the press.

No, that won’t work. Let’s say your sticks initially put left = 100 and right = 50. Then we want left = -50 and right = -100. But your code does this:

left = -right; // now left = -50, which is good
right = -left; // now right = 50, which is what it had been because you made it -(-(itself)).

If you want to use this method, you want to store the left value so you can set right = - (stored left value).

Alternatively, you can use if statements to put the values into the motors instead of swapping values around. In pseudocode:


if(drivetrainReversed) {
    leftMotor.spin(- right stick);
    rightMotor.spin(- left stick);
}
else {
    leftMotor.spin(left stick);
    rightMotor.spin(right stick);
}

That is intentional; I usually tend to do rising state instead of falling.

Classic problem lol; I meant to say “left = -rightInput” and vice versa, but forgot to append “Input.”

How do you set the state of the DToggled variable? Its very possible I just missed it in the code, if so sorry!

@Adam | 6105C I imagine that you just set the callback function of whichever button you want to “DToggled,” such as:


Controller1.ButtonA.pressed(DToggle);

for Button A of Controller1

Thank you all. I plan on trying it out tonight. I appreciate it.