Single button actions on controller

Does anyone know how to program, during driver controller, the controller to make multiple actons on a single button. Like push a button and the robot moves forward and raises it’s arm?

What programming environment are you using?

john I know how to do that. i can help you out

If anyone knows how to do this on robotC and could give me a little help that would be great. I know how to map buttons to certain actions, but is there any way that our second driver can retain control of parts of the robot while the function is triggered. (ie being able to still drive while a second driver triggers the arm to lift to a certain height)

There are a couple of ways to do this, but I’m going to do this with multitasking because it’s simple to demonstrate and very adaptable. Multitasking is running two pieces of code at the same time(they don’t really execute at the same time, but it looks like that to the user). Here is what you do:


task moveArm(){ //define your task
  while(true){

    if(vexRT[Btn6UXmtr2]){
      motor[armServo] = 60;
    }
    
    else if(vexRT[Btn6DXmtr2]){
      motor[port2] = -20;
    }

    wait1MSec(1); //used to make multitasking run properly
  }
}

task main(){
  StartTask(moveArm); //start running your task
  while(true){
    motor[port3] = vexRT[Ch2];
    wait1MSec(1);//Used to make multitasking run properly
  }
}

I wrote this without robotC in front of me, so some syntax may be a bit off, but the idea is the same. This will allow you to use your second controller without interrupting the other functions.

Multitasking is all goods if you’re not calling the same functions from two tasks - if you are, using if statements inside a single while(true) loop is much more reliable. Multitasking does have it’s place, you just have to be cautious how and when you use it.

Here’s some semi-pseudo RobotC code to make the arm behave in a semi-autonomous manner. Here, a driver can pick up a stack of tubes and depending on which goal he intends to score, can press the appropriate button once and proceed to drive to that goal. The arm will continue towards its selected position with no further input from the driver. The driver only need concentrate on getting to the selected goal. Once at the goal he can then pressed the manual controls to lower arm. Also at any time when the arm is moving to the new position, pressing the manual will drop it into manual mode. Of course with all those buttons on the Cortex, you could for example accurately score tubes as well by moving slowly back while lowering arm. Here’s the basic structure of this algorithm.

int commanded_arm_position; //global parameter for task

//this task uses arm motor and potentiometer on shaft
//to create a "servo" where global "commanded_arm_position"
//variable is the parameter to this task.  Motors will drive the
//arm/shaft so that the arm_potentiometer value equals the
//commanded_arm_control variable. Probably best to implement this
//using P or PI control

task arm_control()
{
 while(1) //infinite loop to drive arm to position & maintain it
 {
   if (commanded_arm_control > Sensor(arm_potentiometer) motor[arm]=127; //drive arm up
  else if (commanded_arm_control < Sensor(arm_potentiometer) motor[arm]=-127; //drive arm down
  else motor[arm]=0;  //Stop
  }
}

//this is the competition mode user_control task where the driver controls the
//drive train but just press certain predefined buttons on the controller to 
//autonomously move the arm to the desired positions

task user_control()
{
 int arm_position=400;               //This value a function on how you mount the potentiometer

 commanded_arm_position=400;  // 400 is potentiometer value when arm is down
                                            //900 is the upper limit of arm position
 startTask (arm_control);

 while(1)
 {

  ... code to set appropriate joystick values to drive motors
  ... code to controls claws

  //code to control arm position manually
  //if up or down button pressed, increment/decrement arm_position variable

  if (arm_up_button_pressed ) arm_position++; //Need to limit between 400 & 900
  if (arm_dn_button_pressed ) arm_position--;

  //code to make arm move to preset arm positions
  //Depending on which button pressed (Cortex Joystick controller)
  //Jam arm_position with appropriate Potentiometer values

  if (arm_preset_ground) arm_position=400; ground level ready to pickup
  if (arm_preset_low_goal_de) arm_position=500; just at low goal for de-scoring
  if (arm_preset_low_goal_sc) arm_position=600; just above low goal for scoring
  if (arm_preset_high_goa_del) arm_position=800; just at high goal for de-scoring
  if (arm_preset_high_goal_sc) arm_position=900;just above high goal for scoring

  //Send value to task arm_control() to move arm
  //independent of drive or claw functions

  commanded_arm_position = arm_position;
 }
}

Thanks for the help on the multitasking.

So now im encountering a different issue with multitsking. I am currently trying to program a half speed button for my robots intake. Every time I try to just do a standard IF statment for a facebutton button controlling the half speed along with the IF statments for the button running the intake fullspeed it never ends up working. Here is my code.


task arms(){
//Intake Full Speed
  if(vexRT[Btn5U] == 1){
    intakeLeft = 127;
    intakeRight = -127;
  }
  else if(vexRT[Btn5D] == 1){
    intakeLeft = -127;
    intakeRight = 127;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }

//Intake Half Speed
  if(vexRT[Btn7U] == 1){
    intakeLeft = 63;
    intakeRight = -63;
  }
  else if(vexRT[Btn7D] == 1){
    intakeLeft = -63;
    intakeRight = 63;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }
wait1Msec(1);
}

task main() {
	while (true){
	StartTask(arms);
	wait1Msec(1);
	}
}

You should not be starting a task in a while true loop. This is how your code should be organized:


task arms(){
  while(true){
    //code to control arm
    EndTimeSlice();
    //or wait1MSec(1) works fine if you don't understand time slices
  }
}

task main(){
  StartTask(arms);
  while(true){
    EndTimeSlice();
  }
}

Thanks again for the help.

So I arranged my code like you instructed. Though, currently when I press the rear buttons for intaking nothing happens and when I push the facebuttons for half speed the intake works but but it seems(not 100% sure) like it is traveling at full speed.


task arms(){
  while (true){
  ... Lift Control etc...
//Intake
  if(vexRT[Btn5U] == 1){
    intakeLeft = 127;
    intakeRight = -127;
  }
  else if(vexRT[Btn5D] == 1){
    intakeLeft = -127;
    intakeRight = 127;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }
   wait1Msec(1);
  }
}

task main() {
  StartTask(arms);
while(true) {
...Driving Control...etc
  int intakeRight;
  int intakeLeft;
  motor[port8] = intakeRight;
  motor[port9] = intakeLeft;
  //Intake Half Speed
  
  if(vexRT[Btn7U] == 1){
    intakeLeft = 63;
    intakeRight = -63;
  }
  else if(vexRT[Btn7D] == 1){
    intakeLeft = -63;
    intakeRight = 63;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }  
  wait1Msec(1);
  }
}

In the while loop of your main task, you define intakeRight and intakeLeft. Since the arm control task is not part of the main task, it does not have access to those variables. You have to define the variables before you define the arm control task. Even if you do this though, your motors will be very jerky. It helps to go through the code as the microprocessor does (line by line) and see what is affecting the motors. In your case, two sets of buttons are directly controlling your motors. so If you have the back buttons pressed, the microprocessor will tell the motors to move at full power, and then tell them to move at zero power because your front buttons are not pressed. I do not see why you need to use multitasking for this application. It seems more reasonable to do it all in the main while loop.

I think your problem is that you’re writing the button control speed to the arm, but then cancelling it immediately with the driver control. (See highlighted code below)


task arms(){
  while (true){
  ... Lift Control etc...
//Intake
  if(vexRT[Btn5U] == 1){
    intakeLeft = 127;
    intakeRight = -127;
  }
  else if(vexRT[Btn5D] == 1){
    intakeLeft = -127;
    intakeRight = 127;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }
   wait1Msec(1);
  }
}

task main() {
  StartTask(arms);
while(true) {
...Driving Control...etc
  int intakeRight;
  int intakeLeft;
** 
  motor[port8] = intakeRight;
  motor[port9] = intakeLeft;**
  //Intake Half Speed
  
  if(vexRT[Btn7U] == 1){
    intakeLeft = 63;
    intakeRight = -63;
  }
  else if(vexRT[Btn7D] == 1){
    intakeLeft = -63;
    intakeRight = 63;
  }
**  else{
    intakeLeft = 0;
    intakeRight = 0;
  }  **
  wait1Msec(1);
  }
}

Put the half-speed control in the “arms” task, but have it be a continuation of the if/then that is already there:


task arms(){
  while (true){
  ... Lift Control etc...
//Intake full speed
  if(vexRT[Btn5U] == 1){
    intakeLeft = 127;
    intakeRight = -127;
  }
  else if(vexRT[Btn5D] == 1){
    intakeLeft = -127;
    intakeRight = 127;
  }
  //Intake Half Speed
  else if(vexRT[Btn7U] == 1){
    intakeLeft = 63;
    intakeRight = -63;
  }
  else if(vexRT[Btn7D] == 1){
    intakeLeft = -63;
    intakeRight = 63;
  }
  else{
    intakeLeft = 0;
    intakeRight = 0;
  }
   wait1Msec(1);
  }
}

task main() {
  StartTask(arms);
while(true) {
...Driving Control...etc
**  int intakeRight;
  int intakeLeft;
  motor[port8] = intakeRight;
  motor[port9] = intakeLeft;**
  wait1Msec(1);
  }
}

Now it will only turn the motors off if no buttons are pressed.

Hope this helps!
-Andrew

Though all the other posters’ methods of programming a robot to concurrently execute tasks are effective, the simplest possible way to multitask (keeping in mind the KISS principle) is to simply use boolean flags. You set a boolean variable to true when the desired button is pressed, and put all arm related code together in a neat switch statement, which allows you to handle arm and drivebase code separately. Thus, the arm moves while the driver is free to control the drive motors. In the example below, the down button on channel 7 sets my boolean flag variable (opAutoEnabled) to true (ignore the wait statements, those were put in so that the operator auto was triggered when the operator released the button), and I put all the arm code in a switch statement that compartmentalizes the necessary code. If the flag is true, then the arm moves to the proper height. If it is false, the operator is free to control the arm. Before this switch statement, if you add the lines to set the drive motors, both the drive and the arm will work concurrently because the arm motors are given set values when operator auto is enabled. This eliminates the need for tasks, which can be a pain to debug.

if(vexRT[Btn7DXmtr2] == 1 && !opAutoEnabled)
    { 
      wait1Msec(30);
      if(vexRT[Btn7DXmtr2] == 0)
        opAutoEnabled=true;
      
    }
    else if(vexRT[Btn7DXmtr2] == 1 && opAutoEnabled)
    {
      wait1Msec(30);
      if(vexRT[Btn7DXmtr2] == 0)
        opAutoEnabled=false;

    }
    switch(opAutoEnabled)
    {
      case true:
		    if(SensorValue[armPot]<DESCORE-20)
		      motor[lArmTop]=motor[lArmBott]=motor[rArmTop]=motor[rArmBott]=30;
		    else if(SensorValue[armPot]>DESCORE+20)
		      motor[lArmTop]=motor[lArmBott]=motor[rArmTop]=motor[rArmBott]=-30;
			  else if (SensorValue[armPot]<DESCORE+20 && SensorValue[armPot]>DESCORE-20)
			  {
			    motor[lArmTop]=motor[lArmBott]=motor[rArmTop]=motor[rArmBott]=0;
			  }
			  break;
			case false:
			  motor[lArmTop]=motor[lArmBott]=motor[rArmTop]=motor[rArmBott]=vexRT[Ch3Xmtr2];
			  break;
	}

^ Nice! ^ :slight_smile:
Also, at least in ROBOTC, you have a max number of tasks… (I think it’s five, but the template uses some…)

Question: Why the switch/case structure instead of an if/then/else? I’m just curious - I don’t know too much about the differences…

Because in a switch case you can only check for equality, not a range (=, not >)

So? Why not a if/then/else? He’s only testing true/false…


...
if(variable) //variable is boolean
{
...
}
else //variable has to be false
{
...
}
...

I’ve mainly seen switch/case statements used when there are multiple cases:
(syntax is wrong, but this should get my point across…)


switch (variable) //Variable is an int
{
  case 1:
  break;
  case 2:
  break;
  case 3:
  break;
  ...
}

I misread what you were asking and really only skimmed the code (seems to be a common theme for me today for some reason)

A switch case and If/Else statement would work equally well here, however in this case a single if/else statement would work well.

As for tasks in ROBOTC:
you can have a total of 8 tasks running as any given time including task main. Overall, you can only have 20 tasks defined in the whole of the code file, including task main, and the competition control tasks. Just a note about task control on the Cortex: based on our experiences with it this year, I would recommend being very careful where you use it because switching tasks uses more resources than it should (at least in my opinion)

Thanks for clearing that up - I’ve been wondering about that… :slight_smile:

ROBOTC does give you a task limit, but that doesn’t stop you from writing your own multitasking code :D. I’ve used boolean flags to do pseudo-multitasking from time to time. I even wrote a module to run two tasks simultaneously. But that was when I was using two cortex microcontrollers linked together. You usually don’t need to run more than 8 tasks at a time.

:eek:
I’d like to see a picture of that… :wink: