RobotC programming tips

  1. 4 years ago

    jpearman

    2 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    We've had thread like this before, however, though I would start a new one for RobotC programmers to post their tips and tricks.

    To start it off here are a couple that may not be known.

    functions can have default values for their parameters, for example.

    / pointless demo code
    
    void
    forward( int speed = 100 )
    {
        motor[ port2 ] = speed;   
    }
    
    task main()
    {
        // Forward at default
        forward( );
       
        // Forward at speed 10
        forward( 10 );
       
        // Do nothing
        while( true ){
           wait10Msec(500);
        }
    }

    If the function "forward" is called with no parameters then the default of 100 is used.

    If you have included your own library of functions, for example.

    #include "motorLib.c"

    you can suppress warnings about unused functions by including the line

    #pragma systemFile            // eliminates warning for "unreferenced" functions

    in the top of that file

  2. I guess I'll start off with something simple: ternary operators. Obviously this isn't specifically for ROBOTC, but many beginners don't know about it. I find it to be very useful in shortening code. A ternary operation will look something like this:

    value = (boolean statement) ? (value if statement is true) : (value if statement is false);

    So in a real world example:

    motor[port2] = abs(vexRT[Ch2]) > 10 ? vexRT[Ch2] : 0;

    In contrast to this, the if else statement would look like this:

    if(abs(vexRT[Ch2]) > 10)){
      motor[port2] = vexRT[Ch2];
    }else{
      motor[port2] = 0;
    }

    You can also nest ternary statements. For example, if you wanted to say: move the motor forward at the value of the joystick if it is positive and above the threshold value, but move the motor backward at 1/2 joystick value of it is negative and below the threshold value, then you can nest the ternary statements to make a if else if else statement.

    motor[port2] = vexRT[Ch2] > 10 ? vexRT[Ch2] : vexRT[Ch2 < -10 ? (vexRT[Ch2] / 2) : 0;
  3. jpearman

    2 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    It's possible to keep other files on the cortex, goto Robot->File Management menu. You can save a copy of the source code on the cortex using this dialog box.

  4. Structs are useful when doing more complex programming. For those who don't know what they are, structs are variables made of a collection of other variables. Again, this is not unique to ROBOTC, but still useful in general, and not as widely known to beginning programmers. I'll demonstrate with an example:

    typedef struct{
      int maxPos;
      int minPos;
      int armSpeed;
      int prevSensorValues[10];
    } robotArm;

    This is an example of a struct. If you have multiple arms on your robot, you can have a struct to hold the properties of each one.

    A struct is implemented, and its components are accessed like this:

    robotArm leftArm;
    leftArm.maxPos = 2000;
    leftArm.minPos = 150;
    leftArm.armSpeed = 100;
  5. Thorondor

    3 Oct 2011 New Jersey 3057

    how do i import a library of functions? Cause that would be epic. Right now my program is cluttered with all the functions i have written, which i can share with you guys. It might not be written in the best way possible, but they are quite nifty. I don't have the actual code with me so i can't copy past but the one i like a lot is basically this:

    void drivedistance(int distance)
    {
      rotations = distance / "wheel circumference";
      rotations = rotations * "number of rotations of optical shaft for one rotation of wheel";
      while(SensorValue(shaft) < rotations)
    {
      GO FORWARD
    }
     stop
    }

    i think thats basically all it is. I have other versions for intaking and things like that but its pretty useful if you know the exact number of inches you want to move. This way ure not just changing arbitrary values of optical shaft encoder numbers.

  6. jpearman

    3 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    @Thorondor how do i import a library of functions? Cause that would be epic. Right now my program is cluttered with all the functions i have written, which i can share with you guys. It might not be written in the best way possible, but they are quite nifty. I don't have the actual code with me so i can't copy past but the one i like a lot is basically this:

    Not suite sure what you are asking, if you want to share code there is a wiki here http://www.vexforum.com/local_links.php?catid=26 you could upload a file for sharing.

    If you are asking how to use a library with RobotC, well, RobotC doesn't really use libraries in the traditional sense but you can include another source file at the beginning of you code using the #include directive.

    #include "myfile.c"

    The directory for these files can be set in the preferences dialog.

  7. JesseCRN

    3 Oct 2011 Indianapolis, IN 323Z

    Thanks guys!

  8. jpearman

    3 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    Here's another tip demonstrating the use of the switch statement.

    Let's say you have a motor that you want to drive forward if you press one button and backwards if you press a different button. If you press both or neither buttons the motor should be stopped. You could program this with cascaded if..then..else statements but another way is to combine the two buttons into one variable and use a switch statement. The "<<" is a shift left operator which is often used instead of multiply by 2. DriveLiftMotor would be a function to actually send values to the motor. Everything in capitals are constants defined elsewhere in the code.

        // LiftCtl will be 0, 1, 2 or 3 depending on which buttons are pressed
        LiftCtl = (vexRT[ LIFT_DN_BUTTON ] << 1) + vexRT[ LIFT_UP_BUTTON ];
    
        switch( LiftCtl )
            {
            case    1:  // first button pressed
                DriveLiftMotor( LIFT_UP_SPEED );
                break;
            case    2: // second button pressed
                DriveLiftMotor( LIFT_DN_SPEED );
                break;
            default:  // either neither or both buttons pressed
                DriveLiftMotor( 0 );
                break;
            }
  9. jpearman

    5 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    There have been several questions recently about moving part of the robot until it has reached a certain position. I though I would post a RobotC example showing how to drive a motor forwards or backwards while monitoring an encoder. This same idea could be used for raising a lift or anything else where an encoder is used to monitor the rotation of a motor.

    Anytime a loop is used waiting for a terminal condition to be found it's important to think about what will happen if something goes wrong. This code also shows how to use a timeout so that if an error occurs, the motor stalls for example, the test will stop after a period of time.

    This code is a simple example and does not use multi tasking or the built in timer both of which could be used as part of this.

    #pragma config(Sensor, dgtl1,  encoder, sensorQuadEncoder)
    #pragma config(Motor,  port2,  MyMotor, tmotorNormal, openLoop)
    //*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
    
    /*-----------------------------------------------------------------------------*/
    /*                                                                             */
    /* Drive motor until an encoder has counted for a number of counts             */
    /* or a timeout period has passed.                                             */
    /*                                                                             */
    /* Assumes the encoder will count up when the motors are running forwards      */
    /*                                                                             */
    /*-----------------------------------------------------------------------------*/
    
    #define TIMEOUT_CNT_PER_SEC    10
    #define MOTOR_FWD_SPEED        64
    #define MOTOR_REV_SPEED      (-64)
    #define MOTOR_STOP_SPEED        0
    
    int 
    DriveByEncoder( int encoder_count, int timeout_in_seconds = 5 )
    {
        int  timeout;
        
        // Drive motor until encoder has moved a number counts or
        // timeout_in_seconds seconds have passed
    
        // Zero the encoder
        SensorValue[ encoder ] = 0;
       
        // Run the motor forwards or backwards
        if( encoder_count > 0 )
            motor[ MyMotor ] = MOTOR_FWD_SPEED;
        else
            motor[ MyMotor ] = MOTOR_REV_SPEED;
            
        // run loop until encoder hits encoder_count counts or timeout reached
        
        for( timeout=(timeout_in_seconds*TIMEOUT_CNT_PER_SEC); timeout > 0; timeout-- )
            {
            // check encoder
            if( encoder_count > 0 )
                {
                // going forwards
                if( SensorValue[ encoder ] >= encoder_count )
                    break;
                }
            else
                {
                // going backwards
                if( SensorValue[ encoder ] <= encoder_count )
                    break;
                }
            
            // wait 1/10 second
            wait1Msec( 100 );
            }
            
        // Stop the motor
        motor[ MyMotor ] = MOTOR_STOP_SPEED;
        
        // See if we sucessfully found the right encoder value
        if( timeout <= 0 )
            {
            // there was an error - perhaps do something
            // return error
            return (-1);
            }
        else
            // return success
            return 0;
    }
    
    /*-----------------------------------------------------------------------------*/
    /*                                                                             */
    /*  Test the new function                                                      */
    /*                                                                             */
    /*-----------------------------------------------------------------------------*/
    
    task main()
    {
        // forwards until 200 counts
        DriveByEncoder( 200 );
    
        // short wait
        wait10Msec( 200 );
        
        // backwards for 3000 counts with a 10 second timeout
        DriveByEncoder( -3000, 10 );
        
        // demo done
        while(true)
            wait1Msec( 10 );
    }
  10. There have been several questions recently about moving part of the robot until it has reached a certain position. I though I would post a RobotC example showing how to drive a motor forwards or backwards while monitoring an encoder. This same idea could be used for raising a lift or anything else where an encoder is used to monitor the rotation of a motor.

    This is a great topic to talk about, so I'm going to do a related post about keeping an arm at a certain position with a potentiometer. I want to demonstrate the use of "fuzzy logic". I will use a simple mathematical function to prevent jerkiness in movement while keeping the arm at a certain position. If you want to find out more information on fuzzy logic (and I hope you do), look at this website: http://www.societyofrobots.com/programming_fuzzy_logic.shtml
    Society of robots is a great site to visit for anything robotics related in general.
    Anyway, here is an example:

    const double ERR_MTRPWR_RATIO = .05;
    const int MTR_THRESH = 20
    const int MAX_ARM = 3000;
    const int MIN_ARM = 500;
    
    int armPosition = 0;
    int motorPower = 0;
    
    task keepArmInPosition(){
      while(true){
        motorPower = (armPosition - SensorValue[armPot]) * ERR_MTRPWR_RATIO;
        motor[arm] = abs(motorPower) > MTR_THRESH ? motorPower : 0;
        endTimeSlice();
      }
    }
    
    task main(){
      StartTask(keepArmInPosition);
      while(true){
        //code
        armPos = 300;
        endTimeSlice();
      }
    }

    This code keeps the motor power proportional to the error in the potentiometer value (the difference in the potentiometer value of where you want the arm to be, and where the arm is).

    Here is a rundown of the constants:

    ERR_MTRPWR_RATIO: How much the motor responds to one unit of error. How much the motor power will change for every potentiometer tick away from your expected arm position.

    MTR_THRESH: Threshold below which the motor will not run. This is to prevent burned out motors and jerkiness. Since you will never exactly stay at the potentiometer value that you want (the ticks are very very small) this is to stop the motor from spasming when the error is very small.

    MAX_ARM: The maximum potentiometr value that you want your arm to go to.

    MIN_ARM: The minimum potentiometer value that you want your arm to go to.

  11. jpearman

    10 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    RobotC V3.04 was released today which contains a new function "BackupBatteryLevel". Although the cortex always flashed a red led in competition mode if the backup battery was disconnected, it's now possible to check all the batteries in software. A task could be started before the autonomous code runs which would monitor all batteries and compare the voltage to a user defined level then display an error if, for example, a battery was disconnected. The error could be indicated using an LED in a digital port or perhaps flashing the LCD display (if available). A battery on the power expander can be monitored if the power expander status output is connected to an analog input port.

  12. jpearman

    11 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    Some of the uses for the #define statement, not all necessarily good programming but it shows that it has other uses beyond just defining constants. More explanation here http://en.wikipedia.org/wiki/C_preprocessor

    #pragma config(UART_Usage, UART1, VEX_2x16_LCD, baudRate19200, IOPins, None, None)
    #pragma config(Motor,  port1,           ,              tmotorNormal, openLoop)
    #pragma config(Motor,  port2,           ,              tmotorNormal, openLoop)
    //*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
    
    #define DEBUG
    
    #define ABS(x)          ( (x)>=0?(x):-(x) )
    
    #define DRIVE_MOTOR(x)  do{\
                            motor[port1] = (x);\
                            motor[port2] = (x);\
                            }while(0)
    
    #define JOY_DRIVE       vexJSLeftV
    
    #define DISPLAY_MOTORS  do{\
                            displayLCDNumber(1, 0, motor[port1], 3);\
                            displayLCDNumber(1, 8, motor[port2], 3);\
                            }while(0)
    
    task main()
    {
        while(true)
          {
          DRIVE_MOTOR( vexRT[ JOY_DRIVE ] ); 
          wait1Msec(20);
    
    #ifdef  DEBUG
          DISPLAY_MOTORS;
    #endif      
          }
    }
  13. jgraber

    12 Oct 2011 Dallas Texas metroplex

    Since I don't do much C coding in real life,
    I've not seen the "do { X;Y; } while (0);" construct before,
    but I see it explained at your link and places like this
    http://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for

    "not all necessarily good programming"
    If you are going to provide examples of bad(=not good) programming,
    its helpful to list them as "pitfalls to avoid".

    The use of #define Stick3 3
    to provide context for why this number is 3,
    particularly when there are many parameters in a subroutine call.

  14. jpearman

    12 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    @jgraber "not all necessarily good programming"
    If you are going to provide examples of bad(=not good) programming,
    its helpful to list them as "pitfalls to avoid".

    The problem with software development is that many consider it an art form. What I may consider good, others may consider bad. With this particular post I was reacting to another thread asking about the #define statement, I though I would show a couple of alternative uses to hopefully cause a few students to think "Oh, I didn't know you could do that". Unfortunately as I almost never use #define in this way in my own code (specifically the macros with the do{..}while(0) ) I was attempting to placate a negative reaction from others with the "not necessarily good programming" comment.

    In reality the use of macros can be a benefit in RobotC due to the way it implements subroutines. Consider the following

    // Send value to motor using define statement
    #define SET_MOTOR(index, value)  motor[index] = (value)
    
    // Send value to motor using subroutine
    void
    SetMotor( int index, int value )
    {
        motor[index] = value;
    }

    One a macro the other a subroutine, they do exactly the same thing but which is the better one to use. Normally I would always use the subroutine but RobotC has a difference from many C compilers in that parameters are passed to subroutines using global variables rather that pushed onto a stack (which is why it doesn't support recursion). Normally this does not matter much but consider what may happen if the SetMotor subroutine were called from several tasks. Task1 may setup the global variables index and value followed by a call to SetMotor, if at that moment the task scheduler were to switch to another task also calling SetMotor the globals may be overwritten. This is a hypothetical case and makes certain assumptions about the RobotC task scheduler but you get the idea. Using the macro as opposed to the subroutine is safer in this instance but not something I would normally recommend (I would actually make this type of call an inline function but RobotC does not support that either :( ) so "good programming" is very subjective here.

  15. jgraber

    12 Oct 2011 Dallas Texas metroplex

    OK, that explains a few things.
    I haven't installed RobotC again since my last computer upgrade,
    or I would use the "view ASM" feature to compare Subr vs Define.
    I assume that using the Define would be more like an inline subr.
    Small parts of my day job involve optimizing out as many 1ns operations as I can,
    so I'm overly sensitive to implied overhead of loops and branches, even though that isn't useful in this context.

    Since Motor port updates run once per 20ms, is there a way to synchronize the code to that update cycle?
    For example, keep integrating the gyro/accel/wheel-encoders as fast as you can, but plan on doing a motor update only once in 20ms.

  16. jpearman

    12 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    @jgraber OK, that explains a few things.
    I haven't installed RobotC again since my last computer upgrade,
    or I would use the "view ASM" feature to compare Subr vs Define.
    I assume that using the Define would be more like an inline subr.
    Small parts of my day job involve optimizing out as many 1ns operations as I can,
    so I'm overly sensitive to implied overhead of loops and branches, even though that isn't useful in this context.

    Since Motor port updates run once per 20ms, is there a way to synchronize the code to that update cycle?
    For example, keep integrating the gyro/accel/wheel-encoders as fast as you can, but plan on doing a motor update only once in 20ms.

    When posting here I have trouble finding the right balance between simple and complex code. I also want to post something useful without just giving the code away, the students need to learn on their own but also need good examples on which to base their own code.

    RobotC V3 has now introduced semaphores and these can be used to protect tasks sharing global data or calling non re-entrant code, that's what I'm trying to teach our own team but not everyone will be ready for that yet.

    In terms of synchronizing with the motors, it's a bit tricky as there are two asynchronous processes going on. The first is SPI communication from the user cpu to the supervisor cpu, this runs every 15mS and sends new motor values as well as receiving joystick data (my guess). The second is the pwm update rate of the supervisor cpu running at around 18mS. I posted some info on this a couple of weeks ago here.http://www.vexforum.com/showthread.php?t=52447&highlight=motor+update+rate I've been recommending our students update motors (ie. have tasks running with a delay at the end) about every 25mS but in practice it doesn't make that much difference, anything between 10mS and 50mS is probably ok.

  17. @jpearman RobotC V3 has now introduced semaphores and these can be used to protect tasks sharing global data or calling non re-entrant code

    I was going to do a post on this, but I think that we should wait until ROBOTC releases some documentation about what is supported and what is not. They have said that it will be ereleased soon : http://robotc.net/forums/viewtopic.php?f=52&t=3561

    Does someone want to do a post on multitasking as a precursor to this? That would be very useful and you can't understand semaphores until you understand multitasking.

  18. jpearman

    13 Oct 2011 Moderator, ROBOTC Tech Support Rockstar, Los Angeles 8888

    @magicode I was going to do a post on this, but I think that we should wait until ROBOTC releases some documentation about what is supported and what is not. They have said that it will be ereleased soon : http://robotc.net/forums/viewtopic.php?f=52&t=3561

    Documentation, that's a novel concept :) The help files are so out of date it's not funny. Anyway, I'm using semaphores and they work as far as I can tell. There are only three functions, I can write up an explanation sometime.

    Does someone want to do a post on multitasking as a precursor to this? That would be very useful and you can't understand semaphores until you understand multitasking.

    This will probably take a number of posts, I guess it's either you magicode or I as we are the only ones contributing code to this thread. Let me have a think and try and come up with a simple but worthwhile example.

  19. I was trying to come up with a good relevant example for multitasking as well. It seems that the type of things that multitasking should really be used for are too complicated to demonstrate in a beginners tutorial. This is a good place to start though: http://robotc.net/forums/viewtopic.php?f=11&t=3341

  20. fretless_kb

    13 Oct 2011 Parker CO 1166

    Hey there,
    I'm pretty much a novice to RobotC but I am trying to learn how to get things done and would appreciate if someone would share information on ways to use the emulator. I found yesterday that you can select PC emulator as a target download. and you can get the 16 line LCD display up for feedback on what the code would look like on your robot as well as the regular debugger windows. I see there seems to be a way to attach a joystick to your pc. the one in the help picture is not a vex but possibly a x box 360 controller. does anyone know how to go about using a joystick with the emulator and if the emulator can be configured to provide some sort of sensor feedback? even if you can't use sensors it would be a very valuable tool to test logic flows.

    Cheers Kb

  21. Newer ›
 

or Sign Up to reply!