Using buttons to toggle joystick functions

We are experimenting with different methods of programming in driver control and would like to use a button on the controller to toggle the functionality of the joystick. For example, when the button is not pressed the joysticks do one thing (such as moving the robot forward/backward). But then if the button is pressed, and held the joysticks cause the robot to do something else (such as strafing).

In addition to the switch statement (see code below), we’ve also tried if statements and while loops - but so far they all seem to have the same problem. Although the condition is met (tested by printing values to the controller), the joysticks only work as intended before the button state changes (in the initial mode). After the button is pressed (or released), the functions then stop depending on the state of the button.

So far we have tried putting sleep commands at the end of each section in case things are just processing too fast. We also tried using rotateFor commands instead of spin commands (with waitForCompletion set to true to make sure the commands were completing and not overlapping). None of this made any difference.

Would anyone have any other suggestions or ideas on this? Here is our most recent attempt using the switch statement.

void leftDriveForwardAndBackward() {
LeftFrontDriveMotor.setVelocity(100,velocityUnits::rpm);
LeftBackDriveMotor.setVelocity(100,velocityUnits::rpm);
LeftFrontDriveMotor.spin(vex::directionType::fwd, Controller1.Axis3.value(),vex::velocityUnits::pct);
LeftBackDriveMotor.spin(vex::directionType::fwd, Controller1.Axis3.value(),vex::velocityUnits::pct);
}

void rightDriveForwardAndBackward() {
RightFrontDriveMotor.setVelocity(100,velocityUnits::rpm);
RightBackDriveMotor.setVelocity(100,velocityUnits::rpm);
RightFrontDriveMotor.spin(vex::directionType::fwd, Controller1.Axis2.value(),vex::velocityUnits::pct);
RightBackDriveMotor.spin(vex::directionType::fwd, Controller1.Axis2.value(),vex::velocityUnits::pct);
}

void driveSideways() {
LeftFrontDriveMotor.setVelocity(100,velocityUnits::rpm);
LeftBackDriveMotor.setVelocity(100,velocityUnits::rpm);
RightFrontDriveMotor.setVelocity(100,velocityUnits::rpm);
RightBackDriveMotor.setVelocity(100,velocityUnits::rpm);
LeftFrontDriveMotor.spin(vex::directionType::fwd, Controller1.Axis1.value(),vex::velocityUnits::pct);
LeftBackDriveMotor.spin(vex::directionType::rev, Controller1.Axis1.value(),vex::velocityUnits::pct);
RightFrontDriveMotor.spin(vex::directionType::rev, Controller1.Axis1.value(),vex::velocityUnits::pct);
RightBackDriveMotor.spin(vex::directionType::fwd, Controller1.Axis1.value(),vex::velocityUnits::pct);
}

void usercontrol( void ) {
int allowStrafing = Controller1.ButtonLeft.pressing();
while(true){
allowStrafing = Controller1.ButtonLeft.pressing();
vex::task::sleep(5);
switch(allowStrafing) {
case 0:
Controller1.Axis3.changed(leftDriveForwardAndBackward);
Controller1.Axis2.changed(rightDriveForwardAndBackward);
break;
case 1:
Controller1.Axis1.changed(driveSideways);
break;
}
}
}

Hello~
Only code in Modkit with young kids, but this is normally pretty straightforward.

What we do is this:

First, the controller runs an infinite loop that assigns the value of a global variable to either 0 or 1. If the button is pressed, set variable to 1, else 0.

Second, in a separate infinite loop (or loops - if controlling two different motors), assign each motor or other “listener” to check the status of that variable. If 0, drive motor turns, else drive motor stops.

If 1, another motor (say, a lift motor) runs, else, that motor stops.

I’m sure another user can give you specific code examples, but that logic always works for us. Best of luck!

1 Like

Here is some simple toggle code for a button that may help. The button does not need to be constantly pressed.

void usercontrol(void) {
  bool xPressed = false; // arm button
  int  optSel=0;

  while (1) {
    if (Controller1.ButtonX.pressing() && !xPressed) {
      xPressed = true; // disarm button
      switch (optSel) {
        case 0: Brain.Screen.printAt(10, 210, "selected option %d", optSel);
        break;
        case 1: Brain.Screen.printAt(10, 210, "selected option %d", optSel);
        break;
        case 2: Brain.Screen.printAt(10, 210, "selected option %d", optSel);
        break;
        case 3: Brain.Screen.printAt(10, 210, "selected option %d", optSel);
        break;
        default: Brain.Screen.printAt(10, 210, "selected option %d", optSel);
          optSel=-1; // set to -1 when past end so optSel++ results in 0
        break
      }
      optSel++;
    } else if (!Controller1.ButtonX.pressing() && xPressed) {
      xPressed = false; // arm button
    }
    wait(20, msec); // Sleep the task for a short amount of time to
  } // while (1)
} // usercontrol()

all code below this is untested

I don’t see much support for X-TankDrive. The X-Arcade is more natural and the joystick mapping can be much easier since strafe and yaw can be assigned to either axis 1 or 4.

void tankDrive (bool allowStrafing) {
  left = Controller1.Axis3.position(percent); 
  right = Controller1.Axis2.position(percent);
  strafe = 0;
  if (allowStraffing) {
    strafe = Controller1.Axis1.position(percent);
  }
  LeftFrontDriveMotor.spin(forward, left + strafe,percent);
  LeftBackDriveMotor.spin(forward, left - strafe,percent);
  RightFrontDriveMotor.spin(forward, right - strafe,percent);
  RightBackDriveMotor.spin(forward, right + strafe,percent);
}

alternately get rid of toggle and just use ButtonLeft and ButtonRight. but always strafe at the same percent speed.

void tankDrive () {
  double left = Controller1.Axis3.position(percent); 
  double right = Controller1.Axis2.position(percent);
  double strafe = 50 * (Controller1.ButtonRight.pressing() - Controller1.ButtonLeft.pressing());
  LeftFrontDriveMotor.spin(forward, left + strafe,percent);
  LeftBackDriveMotor.spin(forward, left - strafe,percent);
  RightFrontDriveMotor.spin(forward, right - strafe,percent);
  RightBackDriveMotor.spin(forward, right + strafe,percent);
}

make sure left motor are setup as normal and right motors are reverse.

2 Likes

Basically, the main problem I see is that when buttonleft is being pressed, allow strafing will alternate between true and false several times in a single press. There are several posts that can demonstrate how to solve this. I also don’t think your code will work correctly. This is because the drive functions only run when the controller’s axis has changed. This may also affect the code but I’m not sure. I would just run some sort of task with both drive functions kind of like this:

//Make sure this variable is a global
int allowstrafe = 0;

int Tankdrive(){
  if(allowstrafe  == 0){
//drive functions here
  }
  return 1;
}
int Strafedrive(){
  if(allowstrafe == 1){
//strafe functions here
  }
  return 1;
}

void usercontrol(){
  vex::task tankdrive(Tankdrive);
  vex::task strafedrive(Strafedrive);
  //Button code here, make sure to look up how to control toggling
  //Task functions go on indefinetly and are never blocking. The variable
  //controls it so the right function runs at the right time
}

Here is one of several posts that uses button toggling


Hope this helps!

1 Like