-
-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Table of Contents VEX Coding Basics (Preface) 2 Programming as a Whole 2 Programming Essentials 2 The Operators 3 Conditionals/Conditionals 3 General Syntax Rules 5 Data Types 6 Command (Function Call) Formatting 8 Waiting 8 Section 1: Object Basics 9 Common Parameters 9 Defining Objects 10 Controllers 11 Motors 11 Potentiometers 12 Accelerometer 13 Generic Analog Inputs/Outputs 13 Bumper Sensor/Limit Sensor 14 Generic Digital Inputs/Outputs 14 Quadrature Encoders 14
VEX Coding Basics (Preface) Coding for VEX isn’t as difficult as learning a programming language from scratch. While any sort of coding will have a steep programming curve (and VEX’s languages are no exception), once you attain a certain level of knowledge it becomes second nature to just write a program. This guide will contain all you need to get started with programming your robot for this year’s VEX Robotics Competition. Before you start though, please read the footnotes (the little numbers, like the one after “all” on the previous line), they will contain problem solving techniques, references to learn more about a topic, credits, extra hints to help smooth your debugging experience, and other notes which will sometimes just be jokes. And as a final note, please read the entire preface section (until you see another large title like the one above, as if you’re completely new to coding this will contain vital information to learning programming. This is because I’m going to use terminology that you probably haven’t heard about if you have never opened Atom, Visual Studio Code, or Notepad++ before . A few notes on how this document is formatted. While I’m making this to allow you to read straight through it and try some examples of this code (I recommend having a microcontroller to test code snippets along with a few motors or sensors), this document also contains a full table of contents to serve as a reference document if you need to know how to do something and are too lazy to look it up on the VEX API . Code examples will be formatted with the Century 11 font type because the only good thing about Calibri (this font) is that it looks good and is easy to read even at 11p. Programming as a Whole Programming has become easier over the years. With the evolution of libraries (pieces of already written code you insert into your program with a method called including ) instead of sending binary (0s or 1s) data to a sensor, you write a command that does the dirty work for you. If you want to see what that looks like, ask me or look on my GitHub . All you really need to do in VEX is know simple conditionals, (e.g. if, if else, else, ||, |, &, &&) the command to pause your program for a set amount of time, and the commands to read sensors or execute commands. These abilities allow you to fluently talk to a computer (MCU) so that it obeys the commands sent to it by its master (you ). Programming Essentials Here are the things you absolutely need to know about programming in CPP (C++) before ever starting to code in VEX. These essentials are also nearly universal in all “C” based programming languages . The Operators Math is a thing in programming too. So is logic and spelling. I know, its terrible that you have to spell “motor” right to actually program. It’s hard. OPERATOR SYMBOL WHAT IT DOES EXAMPLE AND && Returns “TRUE” if the expressions it is comparing are both true. Otherwise returns false. 1&&1 returns TRUE 1&&0 returns FALSE 0&&0 returns FALSE BITWISE AND & Compares bits. If both bits being compared are “1”, sets that bit position to be “1”, otherwise it sets it to “0”. 101&001 returns 001 OR || If either thing being compared is “TRUE”, will return “TRUE”. Otherwise returns “FALSE”. 1 || 1 returns TRUE 1 || 0 returns TRUE 0 || 0 returns FALSE BITWISE OR | Compares bits. If either bit being compared is “1”, it sets that bit position to be “1”, otherwise it sets that bit position to “0”. Only works with binary. 101&001 returns 101 MODULO % Returns the remainder of a division problem. 10%2 returns 0 10%3 returns 1 TEST FOR IDENTITY == This will test for equality between the left and right sides of it. 10==10 returns TRUE 10==5 returns FALSE ASSIGNMENT = This sets a variable to another string or number of some sort. variable=14 would set variable to: 14 INEQUALITY != Tests for if the 2 sides are not equal. 14 != 3 returns TRUE INVERSION/NOT ! Toggles an expression from 1 to 0 !0 returns 1
Conditionals/Conditionals Conditionals are key to anything that you want to react based on a condition in the world around it. Being able to do this based on sensor data recorded from the outside world make your robot begin to become autonomous and self-aware of the world around it, which is a big step up from just taking commands in terms of complexity and usefulness. Any robot with sensors or a remote control will use conditionals as the basis on which their robot will be controlled and control itself. CONDITIONAL WHAT IT DOES EXAMPLE if Tests if its condition (the part in parentheses) is true, and if it is it will execute the code in curly braces. if (variable==14) { //code here }
CONDITIONAL WHAT IT DOES EXAMPLE else if Can be placed below the lines of an if statement and serves as another if based on the condition inside its parentheses and that the prior if or else if statement was false. if (variable==14) { //assume variable ≠ 14 } else if (variable==15) { //code to be run if //variable==15 } else Can be placed below the lines of an if or else if statement and will be executed upon the sole condition that the conditional above it was false. You basically need one of these for every if statement that isn’t in a drivetrain loop if (variable==14) { //assume variable ≠ 14 } else if (variable==15) { //assume variable ≠ 15 } else { //code to be run if the //variable ≠ 14 or 15 } while This conditional statement constantly loops/calls the code inside it if the statement in its parentheses stays true. while (variable==5) { //code to be looped while //variable==5 } EXAMPLE P.1 int variable = 0; //initializes a variable with the integer data type to 0 if (variable==1) { variable = 6; //sets the variable “variable” to 6 if “variable” == 1 } else if (variable==4) { variable = 1; //sets the variable “variable” to 1 if “variable” == 4 } else { variable = 4; //sets the variable “variable” to 4 if “variable” didn’t equal 4 or 1 } EXAMPLE P.1 EXPLAINATION In example P.1, we have a variable named “variable”. It has a type of integer (you can learn about data types below in General Syntax Rules) and is initially set to zero. You must initialize variables to something unless they are unsigned (more on that in the variables section). Next, we have a stack of comparisons. The if statement checks if “variable” is set to “1”. If “variable” is “1”, it will execute the command of setting “variable” to “6”. However, if “variable” did not equal one, the else if will be executed. That else if will check if “variable” is set to “4”. In this case “variable” did not equal “4”, so the next comparison will be executed. The next comparison is an “else”. It will always execute if the statements above it don’t execute their code. So, our else statement will set “variable” to “4” and exit the comparison stack. General Syntax Rules If you’ve ever programmed in any other programming language, you know that every programming language has basically its own syntax, or language, rules. For anyone who hasn’t programmed before, syntax means the rules for formatting your code so that your compiler (what reads and interprets your code) . If you don’t follow these rules, your code won’t compile (be translated into a form an MCU can understand) and won’t upload either. CPP has an extremely similar syntax to C or C#, and in my opinion is very simple to understand and do correctly. However, it isn’t as easy to use different types of numbers (see later in this section) without doing something called “casting” which you’ll learn about later if you don’t know what it is already . THE NUMBER ONE RULE OF PROGRAMMING IN CPP IS TO ALWAYS PUT A SEMI-COLON AFTER ANY LINE OF CODE THAT ISN’T A CONDITIONAL. OTHERWISE YOUR CODE WILL NOT WORK AT ALL! A few general things about syntax. In CPP, “indentation” or “tabbing” doesn’t matter. The compiler can tell what all your code means and does without you adding spaces or tabs to the beginning of each line that is nested inside another statement. However, it is best practice to always “tab” or indent your code. It makes it so much easier to understand, find errors in syntax or anything else, and look at. EXAMPLE P.2 Not best practice Best practice if (variable == 1) { variable = 3;} if (variable == 1) { variable = 3; } Also, if you want to put a comment in your code you can do so by putting // before your comment. It will turn everything after it on that line into a comment. If you want to make a multiline comment (multiple lines of comments/text that isn’t code), start it by putting /* before your comment, and end the comment with /. Make sure to end it though, since / would comment out everything after it in your entire program if you didn’t have a / after it. Comments also can stack (you can have comments in comments). However, you should be using “#IFDEF ”s (covered later) if you have comments in comments. EXAMPLE P.3 int variable = 0; //initializes variable. Now this line is commented out. if (1 ) { / variable = 1; float variable1 = 1; / } EXAMPLE P.3 EXPLAINATION On line 1 of P.3, we initialize a variable. After our semi-colon, there is a comment started by “//”. Now that text is commented out it is not looked at by the compiler when it compiles this code. Next, we have an if statement on line 2 followed by its condition which is always true (see footnote 24). Then we have the multiline comment. It is opened on line 4 by the /. Now everything until the */ on line 7 is commented out and will not be read by the compiler. Then the if statement is closed by the curly brace. Data Types While data types are sometimes converted between types for you with the types of data we’ll be using in VEX, it is still important to get them correct all the time as they can cause some nasty problems or errors that aren’t exactly easy to find the cause of. Data types are the format in which the data in a variable is stored. For example, an int is the data type for holding a max 32-bit integer. Unless you are using massive numbers, 32 bits will suffice . Here’s a massive table of the “common” data types. Data Type English Name Example Int Integer 23783 Float Decimal Number 382.45 uint_8 8-bit Number 234 uint_16 16-bit Integer 2544 uint_32 32-bit Integer 478589 uint_64 64-bit Integer 5443346346 Char Character Array (list of characters) A47ke Bool Boolean (true or false) TRUE Double Double Float (larger float) 34783.2783 Void No value. Used for functions that don’t return anything (see what this means in Functions). String A string, or text with numbers Hi there
Different data types require different formatting for when you define a variable with a specific datatype. Here is the list of what each of the above datatypes require. Some don’t even require anything when you define them or use them, like most numerical datatypes. Data Type Formatting Formatting Required? Int (int) No Float (float) No uint_8 (uint_8) No uint_16 (uint_16) No uint_32 (uint_32) No uint_64 (uint_64) No Char [char] Yes Bool None N/A Double (double) No Void N/A N/A String “string” Yes
As I mentioned earlier, different datatypes can automatically be converted between each other. For example, an int can be “cast” or converted to a float without intervention on your part. However, it is always best practice to cast data types you are trying to convert. Here’s how to cast: before your data type to be converted, add (datatype) where datatype is the datatype that you want to cast your existing data to. Here’s an example: EXAMPLE P.4 int var1 57; float var2 0.0;
var2 = (float) var1;
EXAMPLE P.4 EXPLAINATION In P.4, we have 2 variables, var1, an int, and var2, a float . We wanted to set var2 to the value of var1, but their datatypes don’t match. So, we cast var1 to a float using “(float)” and set it to var2. DISCLAIMER ABOUT DATATYPES While most datatypes are compatible to be cast to each other, some are not. For example, you cannot cast anything to a void, or try and cast a bool to a String. Also, “string” is not a valid datatype, only “String”. “string” will not work! Command (Function Call) Formatting In CPP, we use commands, also known as function calls, to make our robots move or perform an action for us. The “function” that function calls call are prewritten blocks of code (either written by you or VEX in our case) that are placed earlier in your program or in an included file (see more on included files in their section. We can call a function by using its name, and supplying any parameters it requires in parentheses: testing_function1(variable1); function name parameter
Most functions require one or more parameters, unless they are only for executing macros that don’t depend on data to execute. If your function needs more than one parameter (for example starting a motor), use this template (parameter names are shortened to p(num) for this example: SpinFrontMotors(vex::directionType::rev,100,vex::velocityUnits::pct); function name p1 p2 p3
This would send 3 parameters to your function of “SpinFrontMotors”. Most likely the motors would spin at 100% reverse power if that is what your function was setup to do. You’ll see more of vex::directionType and vex::velocityUnits in a later section dealing with motors. Everything from reading sensors, waiting for a task to complete, to turning a motor uses function calls in VEX, so get used to this. But, CPP is an object-oriented programming language. That means that you can define “objects”, like a motor, which have different function calls within them. For instance, you can stop a motor, start a motor, get its current position, apply braking and more. Waiting In VEX autonomous programs, it is common to use waits or delays to control when your robot will change direction, stop, or turn. To do this, you use waits: vex::task::sleep(time);
For this command, time is in milliseconds (1000ms = 1sec). Be warned though, this command suspends your entire task, which means that if you wait in your driver control task, no controls will respond until the waiting time has elapsed. Section 1: Object Basics As I said in the preface, VEX takes advantage of CPP be object oriented by creating objects for physical parts of your robot. These can include 3-Wire Ports, Motors, “Smart” Sensors , the MCU itself, Controllers, the Competition Controller, “Legacy” Sensors , the Vision Sensor, and more. Objects allow you to call different functions that apply to that object, like reading a motor’s speed without needing a separate function for each task. The syntax for objects and their function is usually as follows: myFrontDriveMotor.efficiency(vex::percentUnits); object name function function parameters
The code above would ask myFrontDriveMotor for its efficiency in percent form. If you set this to a variable, you could create a readout on the MCU screen. Common Parameters Below is a table of all custom parameters and parameter categories VEX supplies for you to use. They are usually never universal, meaning that they can only be supplied to certain objects, like vex::controllerType::primary can only be supplied to the definition of a controller object, and cannot be applied to a motor. You do not need to have “vex::” in front of every VEX parameter. I have also chosen the “most common” parameter option for the example. Parameter Type Options Example percentUnits pct vex::percentUnits::pct timeUnits sec, msec vex::timeUnits::msec currentUnits amp vex::currentUnits::amp voltageUnits volt, mV vex::voltageUnits::volt powerUnits watt vex::powerUnits::watt torqueUnits Nm, inLb vex::torqueUnits::Nm rotationUnits deg, rev, raw vex::rotationUnits::deg velocityUnits pct, rpm, dps vex::velocityUnits::pct distanceUnits mm, in, cm vex::distanceUnits::in analogUnits pct, range8bit, range10bit, range12bit, mV vex::analogUnits::pct directionType fwd, rev vex::directionType::fwd brakeType coast, brake, hold vex::brakeType::brake gearSetting ratio36_1, ratio18_1, ratio6_1 vex::gearSetting::ratio18:1 fontType mono12, mono15, mono20, mono30, mono40, mono60, prop20, prop30, prop40, prop60 vex::fontType::mono30 triportType analogInput, analogOutput, digitalInput , digitalOutput, button, potentiometer, lineSensor, lightSensor, gyro, accelerometer, motor, servo, quadEncoder, sonar, motorS vex::triportType::motor vex::triportType::potentiometer vex::triportType::quadEncoder vex::triportType::analogInput controllerType primary, partner vex::controllerType::primary
As you can see, there’s quite a few. But, since most objects only take 1-3 of these parameters and user-supplied numbers, it’s not a requirement that you know all of them from memory, just the really common ones like percentUnits, brakeType, velocityUnits, and distanceUnits. Defining Objects To use your hardware in the real world, we must tell our program it’s connected and where first. We also need to supply that object’s current parameters and what we want it to be called. You can call an object anything unique and alphanumeric (nothing else has that name). If you’re reading this straight through, it’s finally time to open up VEXcode. Make sure your version is above Preview 04, and if possible use the latest version available . I’m going to cover VEXcode software basics in Section 2, so if you don’t know how to use the software, go there first and come back here. A list of all objects VEX has implemented into their class is available at the VEX API. For this section I’m going to be using the default competition program, as it sets up all the tasks, namespaces, header files, and required initializations (which we’re going to add to in a minute) for you . The best area to define your objects (your motors and such) is right where VEX already defined a brain (MCU) and competition . Right under where the comment saying // define your global instances of motors and other devices here
is where we’re going to define our objects. Here’s a list of what our “robot” looks like and what this tutorial will be based on: Name Type Extra Info Required leftDrive Motor Port 1, Not Reversed, 18_1 rightDrive Motor Port 2, Not Reversed, 18_1 leftArm Motor Port 4, Reversed, 36_1 rightArm Motor Port 5, Not Reversed, 36_1 claw Motor Port 7, Reversed, 6_1 armPot Potentiometer Three Wire Port A clawPot Potentiometer Three Wire Port B controller Controller Primary
Our “robot” has 5 motors, 2 potentiometers, and a controller that we need to define in our program. Controllers If you are programming a VEX robot, you basically need a controller all the time, whether it is to drive the robot, start a program remotely, or trigger a script. Luckily, they are easy to define and setup. All declarations start with “vex::”. Until version 1.0 of the VEXcode SDK gets released, you must include the class identifier, which is what “vex::” is. It tells your compiler that the object type you are trying to define is inside of the “vex” class, and it should look there for more information. Next, you need to supply the type of object you want to define. In our case, this is a controller, so put controller right after the “vex::” so it looks like “vex::controller”. Now you’ve told the compiler that inside of the “vex” library, it should define an object with the type of “controller”. Third, name your object and prepare to add parameters. You need to give your controller-type object a name that identifies it. Keep in mind when naming objects, especially controllers, that you will need to type this name to call its functions all the time in the body of your program; so, choose something easy to remember and easy to type. I usually choose “Controller” for my primary controller. Here’s what defining a primary controller looks like in VEXcode. vex::controller Controller = vex::controller(vex::controllerType::primary); class & type obj name class & type parameters
In that example we defined a primary controller. This is the main controller for your robot, but you can add a second controller, called a partner controller, if you wish. All you must do is change “vex::controllerType::primary” to “vex::controllerType::partner”. In the case of a primary controller with no partner controller defined, you don’t need to even write “vex::controllerType::primary”, so your code could look like this: vex::controller Controller = vex::controller(); class & type obj name class & type Just don’t forget to add your parentheses! In this case the controllerType of primary is implied. Motors The largest part of defining and initializing all the parts of your robot is your motors. You usually have the most motors out of all other objects, and sadly, unlike RobotC you don’t have a wizard to do this for you. Motors also require a few more parameters than Controllers, so it take a bit longer. It’s also important to get this right, otherwise you have motors turning the wrong directions which can cause damage to your newly built robot and our newly bought motors. The parameters needed to define a motor are as follows: name, port, gear setting, is reversed. If you check back on page 9, all these parameters are listed there so you can see their options. Let’s start with our leftDrive motor. Start out with the same idea as the controller. “vex::” and then your object type: motor. Then name your motor and put the type after the = again. vex::motor leftDrive = vex::motor
Now it’s time for parameters! The order in which these go is: (port, gear_setting, is_reversed). vex::motor leftDrive = vex::motor(PORT1,vex::gearSetting::ratio18_1,false); class&type name class&type port gearSetting isReversed
There you go, your first motor is now defined. Here’s the rest of them if you were wondering: vex::motor leftDrive = vex::motor(PORT1,vex::gearSetting::ratio18_1,false); vex::motor rightDrive = vex::motor(PORT2,vex::gearSetting::ratio18_1,false); vex::motor leftArm = vex::motor(PORT4,vex::gearSetting::ratio36_1,true); vex::motor rightArm = vex::motor(PORT5,vex::gearSetting::ratio36_1,false); vex::motor claw = vex::motor(PORT7,vex::gearSetting::ratio6_1,true);
And that’s how you define motors. With v5, you can only have 8 motors, so that probably (hopefully) all you’ll need to define. Once you define these motors, you can use them anywhere in that file, whether that be in autonomous or driver control. I’ll mention how to do multi-file programming in the advanced topics section later. Potentiometers For those who are unaware, potentiometers measure the rotation of an axle. VEX’s potentiometers use the three wire ports on the MCU. They’re easy to setup as they only require one parameter: their port. VEX shortens potentiometer in their class down to pot, so you don’t have to type that much. As always, you need to specify which class (vex) and what type (pot) of object you want to define. vex::pot armPot = vex::pot();
Now you need to supply its parameters. Three wire ports are designated by letters A-H, and have the parameter type of vex::triport::portLetter where portLetter is A-H. Here’s an example: vex::triport::A
Now that just needs to go inside the parentheses from the previous example and you’ve successfully defined a potentiometer. This is pretty much the method for defining and initializing any 3-wire sensor . Also, a side note: make sure to capitalize the 3-wire letter. Here’s what it looks like: vex::pot armPot = vex::pot(vex::triport::A); For our “robot”, this is what our 2 potentiometers would look like when defined: vex::pot armPot = vex::pot(vex::triport::A); vex::pot clawPot = vex::pot(vex::triport::B); If you’re only going to be following along with these examples, you’ve now initialized all your objects. But, I’m going to cover a ton of other things you can define as well, even though they aren’t in the “robot”. Accelerometer Accelerometers are basically the same as potentiometers to initialize/define . I could just copy and paste the instructions from potentiometers and replace pot and potentiometer with accelerometer. So that’s what this is. As always, you need to specify which class (vex) and what type (accelerometer) of object you want to define. vex::accelerometer anAccelerometer = vex::accelerometer();
Now you need to supply its parameters. Remember, three wire ports are designated by letters A-H, and have the parameter type of vex::triport::portLetter where portLetter is A-H. Now that just needs to go inside the parentheses from the previous example and you’ve successfully defined an accelerometer. This is pretty much the method for defining and initializing any 3-wire sensor . Also, a side note: make sure to capitalize the 3-wire letter. Here’s what it looks like:
vex::accelerometer anAccelerometer = vex::accelerometer(vex::triport::B); Generic Analog Inputs/Outputs While I’m not sure if there is a competition-legal use of a sensor that requires a generic analog input/output, here’s how to initialize one if you encounter this situation. You should be able to use a generic analog input (analog_in) for sensors like a potentiometer, but I recommend using the potentiometer-specific object type because it’s a lot easier to change between units since VEX’s class does the math for you. The object type for a generic analog input is “analog_in”, and only requires the three-wire port specification. Remember that you need to capitalize the 3-wire letter. Here’s how to initialize one: vex::analog_in analogInput = vex::analog_in(vex::triport::C);
For an analog output, all you must do is replace “in” with “out”. So “vex::analog_in” becomes “vex::analog_out”: vex::analog_out analogOutput = vex::analog_out(vex::triport::C); Bumper Sensor/Limit Sensor Since these 2 sensors are basically the same sensor (they detect pressure based off an arm or pad), I’ll group them into the same group. Their only major difference is that one is a “bumper” type, and the other is a “limit” type. I hope you can determine which sensor has which type . Along with the accelerometers, potentiometers, and generic analog inputs, bumpers and limits only require a port designation for a parameter. As always, you need to specify which class (vex) and what type (bumper/limit) of object you want to define. vex::bumper aBumperSensor = vex::bumper(); vex::limit aLimitSensor = vex::limit();
Now you need to supply their parameters. Remember, three wire ports are designated by letters A-H, and have the parameter type of vex::triport::portLetter where portLetter is A-H. Now that just needs to go inside the parentheses from the previous example and you’ve successfully defined a bumper or limit sensor. This is pretty much the method for defining and initializing any 3-wire sensor as I’ve said before. Again: make sure to capitalize the 3-wire letter. Here’s what it looks like:
vex::bumper aBumperSensor = vex::bumper(vex::triport::D); vex::limit aLimitSensor = vex::limit(vex::triport::E);
Generic Digital Inputs/Outputs Much like an Arduino or Raspberry Pi , the v5 MCU can send digital signals to outside sources and receive them. Again, while there isn’t a legal/practical competition use for this functionality, it’s useful to know for use in other applications. “vex::digital_in” is the class and type for inputs, and they require a port. vex::digital_in aDigitalInput = vex::digital_in(vex::triport::F);
For outputs, all you need to change is “in” to “out”. So “vex::digital_in” becomes “vex::digital_out”. vex::digital_in aDigitalInput = vex::digital_in(vex::triport::G);
Quadrature Encoders While v5 motors have built-in encoders which are fairly accurate, many PID (tracked) drivetrains use separate encoders on free-spinning wheels for extremely accurate motion tracking of a robot’s position using some algorithms. Unlike the previous sensors, quad encoders use 2 three-pin connects, which should add a layer of complexity to our initialization. However, it just makes it harder for the people building the robot. Instead of simply defining two ports, the first for input and the second for output, you define only one port. How does this work? Well, in the v5 system, you can set the port for the first (input) wire of an encoder. The output wire will go in the next port (e.g. port A and port B or port D and port E). For this example, I’m going to say the input is in Triport C, so the output would go in D . As always, you need to specify which class (vex) and what type (encoder) of object you want to define. vex::encoder anEncoder = vex::encoder();
Now you need to supply their parameters. Remember, three wire ports are designated by letters A-H, and have the parameter type of vex::triport::portLetter where portLetter is A-H. Now that just needs to go inside the parentheses from the previous example and you’ve successfully defined an encoder. Again: make sure to capitalize the 3-wire letter. Here’s what it looks like:
vex::encoder anEncoder = vex::encoder(vex::triport::C); Now if your encoder input wire is in triport C and your output wire is in triport D, your encoder will deliver the appropriate data to your MCU for you to use (learn more about how to use an encoder in a later section).