Button.pressed(callback) combined with limit switch

Our team is looking to implement three operations for tray lifting.

Two of the operations are of a manual nature (holding down ((pressing)) R1 to lift the tray and then holding down ((pressing)) R2 to lower tray until a limit switch is triggered thereby stopping motor. These two operations are not the problem; the coding for them works correctly.

The problem becomes when we want to include a ‘quick’ tray reset (run the tray down automatically until limit switch is triggered thereby stopping the tray lift motor); with this action triggered by a SINGLE PRESS of the button down button.

The question we have encountered, " is how to combine the callback function parameter of the – button.pressed(callback) – with the Boolean logic of the – (trayLiftLimit.pressing()==false) . ‘False’ being that the motor runs the tray lift down, because the tray is up from just stacking cubes and as the robot pulls away we want to quickly return the tray back down, until the Boolean becomes ‘true’ by the limit switch being triggered.

How do we combine this two ideas???

The code we are including is from our header file containing the functions for our robot to preform tray operations.

We fully understand how to include the header file in the ‘main’ and call the functions. Our team just can’t figure out how to set up the functions in the header file…

/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       trayLift.h                                                */
/*    Author:       NMS_RP                                                    */
/*    Created:      22 Nov 2019                                               */
/*    Description:  Tray Lift Functions File for TT                           */
/*                                                                            */
/*----------------------------------------------------------------------------*/

#include "robot-config.h"
controller con = controller(controllerType::primary );

// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]          [Port(s)]
// TrayLift             Red Motor       8 
// trayLiftLimit        Limit Switch    3-Wire Port B                                  
// ---- END VEXCODE CONFIGURED DEVICES ----

// tray lift speed variable
int trayLiftSpeedPCT = 100;

/** --- Tray Movement Shortend Function Call Setup ---
 *  tray lift up:     run tray lift up manually by pressing of Button R1 (Manual Operation)
 *  tray lift down:   runs tray lift down manually by pressing of Button R2 (Manual Operation)
 *  tray lift reset:  runs tray lift down from BUTTONDOWN press till limit switch is triggered (Automatic Reset)
 */

void trayLiftSpinUp(){
  TrayLift.spin(directionType::rev, trayLiftSpeedPCT, velocityUnits::pct);
}
void trayLiftSpinDown(){
  TrayLift.spin(directionType::fwd, trayLiftSpeedPCT, velocityUnits::pct);
}
void trayLiftReset(){
  trayLiftSpinDown(); 
}

/** --- Tray Movement IF-Else Function (Manual Operation) ---
 *  - tray lift up:     run tray lift up manually by pressing of Button R1 (Manual Operation)
 *  - tray lift down:   runs tray lift down manually by pressing of Button R2 (Manual Operation)
 */
void trayLift(){
  if(con.ButtonR1.pressing()){
    trayLiftSpinUp();
  }
  else if(con.ButtonR2.pressing() && (trayLiftLimit.pressing()==false)){
    trayLiftSpinDown();
  }
  else{
    TrayLift.stop(brakeType::brake);
  }
}

/**  --- tray lift reset (Automatic Operation) --- 
 *  - runs tray lift down from BUTTONDOWN press till limit switch is triggered
 *  - ??? how to implement limit switch with VEXcode TEXT --> button.pressed(callback);
 */
void trayReset(){
  con.ButtonDown.pressed(trayLiftReset);
  // ?? how to include a limit switch Boolean Logic trigger to stop motor
    }

Inside of the trayLiftReset function you can put a while loop that checks whether the limit switch is pressed or not. If the limit switch is pressed the while loop will exit and the tray will brake.

1 Like

Let me see if I got this… You have two manual operations and one automated operation that control the same motor. For the manual operations, you need to continually check for a button press, and for the automated operation, you need to remember when a button has been pressed. All the down operations cease when the limit switch is tripped.

Sounds like you have two states, manual and auto. You will need a variable to keep track of the state. Here is the pseudocode:

// declare state variable
boolean manual_mode = true

repeat forever   
...    
if manual_mode or R1 pressed or R2 pressed        
    set manual_mode to true
    call trayLift() function     
if quick button pressed
        set manual_mode to false   // auto mode
        start moving tray motor
    if auto mode and limit pressed
        stop tray motor
        set manual_mode to true

Note that for the automated move, it may be easier to use the motor encoders to determine when the lift is down, rather than using a physical limit switch. Replacing spin() with startRotateTo() for the automated move would do this for you.

In response to your reply jrp62:

Your logic looks correct, however the devil is in the details as you are expressing my problem:

R1 pressed or R2 pressed

how do we record this without bringing in the the button.pressed(callback) problem?

So for example, correct me if i am wrong, we need to be able to record/recognize a state change in the button without triggering the callback function event.

So for example, in the last function in the header file I attached, trayReset, if the callback function did not have to be present, and we could take a state reading on the button, a person could simply use if/else statements to set the condition of: button pressed to run motor down till limit switch is triggered thereby shutting motor off.

Your answer assumes my problem…taking a state reading from the buttons absent triggering an event.

Read the API documentation. There is a function called “pressing” that returns the current state of the button. The function “pressed” is the event-oriented function

1 Like

Yes Mentor_355v I have looked at the API documentation and fully understand the difference between “pressing” and “pressed”. Please read my entire topic post to fully understand and appreciate the nature of the problem I am describing. And yes you are probably correct in that I probably used the wrong verbiage in my latter post attempting to clarify just what I was seeking.

Hmmm… I think I understand what you are trying to make your robot do, but not your approach to coding it.

I am noticing a typo with the indentation in my pseudocode. Let me take care of that first, it should be:

// declare state variable
boolean manual_mode = true

repeat forever   
...    
if manual_mode or R1 pressed or R2 pressed        
    set manual_mode to true
    call trayLift() function     
if quick button pressed
    set manual_mode to false   // auto mode
    start moving tray motor
if auto mode and limit pressed
    stop tray motor
    set manual_mode to true

It is not necessary to use the button.pressed(callback) feature to implement this. It can be accomplished in a normal control loop. The driver would only need to press the down button once to make the tray go down. The lift motors would not be stopped until either the limit switch was triggered or one of the manual buttons was pressed.

If this must be implemented using a button callback, then there are a couple of possible alternatives. 1. Eliminate the limit switch and use the startRotateTo() method instead of spin() when resetting the tray. 2. As thorstenl312 suggested, put a while loop inside the callback function.

One thing that I haven’t mentioned is that it is considered a best practice to avoid setting the actual motor speed in multiple places. It is better to have a variable for speed, that can be changed in many places, and then set the actual motor speed in one place. However, if a button.callback is used to set the speed variable, then it would still be necessary to set the actual motor speed in a control loop somewhere. That would kind of defeat the purpose of using a callback, plus the speed variable would have to be global in scope.

1 Like

take A look at the circumstances which leads the lift.stop function gets called.

Draw out a state diagram of the behavior you want. Understand the control flow of what happens when each of the 3 buttons is interacted with. If there is some state that needs to be persisted for some period of time, you may need a global variable to hold that state

///////////////////////////////////
//
// --- Forum Proposed Solution ---
//
//////////////////////////////////
bool manual_mode = true;          // declare state variable for tray lift
void trayLiftSelection(){
  if(( manual_mode == true ) || ( ButtonR1.pressing() == true ) || ( ButtonR2.pressing() == true  ){
    manual_mode = true;
    trayLift();
  }
  if( ButtonDown.pressing() == true ){
    manual_mode = false;
    trayLiftSpinDown();
  }
  if (( manual_mode == false ) && ( trayLiftLimit.pressing(); )){
    TrayLift.stop(brakeType::brake);
    manual_mode = true;
  }
}

Will not build. Cannot read state of buttons R1 or R2.

You need to add the name of the controller before the Button.
ButtonR1.pressing() → Controller1.ButtonR1.pressing()

I’m pretty sure that logic you’ve encoded will not lead to the behavior you want.

What happens the first time this function is called, assuming manual_mode 's value is not changed anywhere else?

Thank you jrp62 !!

After much work and thought I combined your logic and thorsten312’s idea of the ‘while’ loop to code three options that are stable and work.

/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       trayLift.h                                                */
/*    Author:       NMS_RP                                                    */
/*    Created:      25 Nov 2019                                               */
/*    Description:  Tray Lift Functions File for TT                           */
/*                                                                            */
/*----------------------------------------------------------------------------*/

// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]          [Port(s)]
// TrayLift             Red Motor       8 
// trayLiftLimit        Limit Switch    3-Wire Port B                                  
// ---- END VEXCODE CONFIGURED DEVICES ----

// VEX V5 C++ Project
#include "robot-config.h"

// #region config_globals
controller con = controller( controllerType::primary );

int trayLiftSpeedPCT = 100;       // traylift speed variable

/** --- Tray Movement Shortend Function Call Setup ---
 *  tray lift up:     run tray lift up manually by pressing of Button R1 (Manual Operation)
 *  tray lift down:   runs tray lift down manually by pressing of Button R2 (Manual Operation)
 *  tray lift reset:  runs tray lift down from BUTTONDOWN press till limit switch is triggered (Automatic Reset)
 */

void trayLiftSpinUp(){
  TrayLift.spin(directionType::rev, trayLiftSpeedPCT, velocityUnits::pct);
}
void trayLiftSpinDown(){
  TrayLift.spin(directionType::fwd, trayLiftSpeedPCT, velocityUnits::pct);
}

/////////////////////////////////////////
//
// --- Forum Proposed Solution ---
//
/////////////////////////////////////////
bool manual_mode = true;          // declare state variable for tray lift
void trayLiftSelection(){
  if((( manual_mode == true ) && (( con.ButtonR1.pressing()) == true )))
  manual_mode = true;
  while(( manual_mode == true ) && (( con.ButtonR1.pressing() == true))){
    trayLiftSpinUp();}
    TrayLift.stop( brakeType::brake );
    manual_mode = true;
    
   if(( manual_mode == true ) && ((con.ButtonR2.pressing()) == true ))
    manual_mode = true;
    while(( manual_mode == true ) && ( con.ButtonR2.pressing() == true ) && ( trayLiftLimit.pressing() == false )){
    trayLiftSpinDown();}
    TrayLift.stop( brakeType::brake );
    manual_mode = true;
  
  if((con.ButtonDown.pressing()) == true )
    manual_mode = false;
    while(( manual_mode == false ) && (( trayLiftLimit.pressing()) == false )){
    trayLiftSpinDown();}
    TrayLift.stop( brakeType::brake );
    manual_mode = true;
}
2 Likes

Good catch on the → controller1 prefix. And thank you for the idea of the ‘while’ loop.

Build is stable and working!!

1 Like

I am newish to programming so i dont know what I am supposed to do. Can someone please check my code and advice what goes into the callback.

if( LimitSwitchB.pressed(void (*callback)()){

piston.set( false );

}

else if(Controller1.ButtonY.pressed(void (*callback)()){

piston.set( false );

}

else if(Controller1.ButtonA.pressed(void (*callback)()){

piston.set( true );

}

else {

piston.set( true );

it should just be

 while(true) {

if(Controller1.ButtonA.pressed()) { 
//what happens when its being pressed
}else {
//what happens when its not being pressed
}
}
1 Like

so what is the (*callback) for?

there are still problems.

if( LimitSwitchB.pressed(void )()){

piston.set( false );

}

else {

piston.set( false );

}

else if(Controller1.ButtonA.pressed(void ()){

piston.set( true );

}

else {

piston.set( false);

“pressing()” not “pressed”

pressing returns current state of the switch

pressed registers a callback.

you can think of them as
Call this function when the switch is “pressed”

and
am I “pressing” the switch., then …

2 Likes

I understand the pressing, but I want to use pressed

This is not for V5, but exactly the same concept.

#include "vex.h"

using namespace vex;

// A global instance of vex::brain used for printing to the V5 brain screen
vex::brain       Brain;

void
myCallback() {
    static int press_count = 0;
    Brain.Screen.printAt( 10, 30, "pressed %d", ++press_count );
    return;
}

int main() {
    // register once and only once outside of any loops
    Brain.buttonCheck.pressed( myCallback );

    while(1) {
        // Allow other tasks to run
        this_thread::sleep_for(10);
    }
}

myCallback will be called every time the buttonCheck is pressed.

2 Likes