The Beginners Guide to ROBOTC Volume 1, 3rd Edition Written by George Gillard Published: 18-July-2016 Introduction ROBOTC is an application used for programming robots. There are many different versions of ROBOTC, but I highly recommend using the latest version, and use the same version across your whole team. That way, different people in your team can program the same robot without having to download the firmwares every time. In this guide to ROBOTC, I'm using ROBOTC for VEX Robotics version 4.52, a VEX Cortex Microcontroller and a VEXnet Joystick. ROBOTC for VEX Robotics is able to program the VEX Cortex and VEX IQ Brain. If you wish to program the discontinued VEX PIC Microcontroller, you will need to install ROBOTC 3.X for Cortex & PIC. There are also many different styles of programming - this guide has been written for the way that I personally program. In this guide, I will show you the basics of coding in ROBOTC, from setting up a file for your robot, to writing code to control your robot via remote control and even basic autonomous routines with sensors. Sections 1. Setting Up 2. Driver Control Code 3. Downloading a Program 4. Basic Autonomous Routines 5. Introduction to Sensors This guide is provided to assist those learning how to program VEX Robots. This is a free document, but I ask that you ask for my consent before redistributing online, but please feel free to share a link. This document, along with my others, are available for free download from http://georgegillard.com. Page 2 Published: 18-July-2016 © George Gillard 1 Setting Up 1.1 New Files Once you have opened ROBOTC, open a new file (the big “New File” button, File New… File, or Ctrl + N). If this is for a robot that will be competing in the VEX Robotics Competition, open a new competition template (File New… Competition Template). Check that the platform type in ROBOTC matches the sort of robot which you wish to program (Robot Platform Type, Figure 1.1.1). Figure 1.1.1 1.2 Setting Up Motors and Sensors In your new file, use the toolbar to go to the Motors and Sensors Setup window (the big “Motors and Sensors Setup” button, Robot Motors and Sensors Setup, or in an existing file just double click the “#pragma config” lines at the top). A new window will open (Figure 1.2.1), where you can enter names for all of your motors and sensors according to their corresponding port numbers which they are physically plugged into. Page 3 Published: 18-July-2016 © George Gillard Figure 1.2.1 1.2.1 Motors In the Motors tab, enter appropriate names for each motor, in the boxes that correspond to the physical ports which they are plugged into. Make sure you do not leave spaces in the names – e.g. use “leftDrive”, “LeftDrive”, or “left_drive” instead of “left drive”. Select the type of motor in the dropdown menu (although which type you select generally doesn’t matter for most cases), and reverse the motor if it spins the wrong way when you use it later on. Ignore the other options for now. Tip: Plug in your motors such that they mirror across all of the ports (e.g. if you have two motors, left and right, consider plugging one into port 1 and one into port 10, or 2 and 9, etc.). The VEX Cortex has two breakers which each serve half of the ports, so particularly on intense robots which draw lots of current, by sharing the motors across the breakers, you are less likely to run into issues. It can also be easier to remember which motors plug into which ports this way. Tip: Use reasonably short and concise names, but with enough detail in them that they make sense. “leftDrive” is much easier to understand than “LD”, but shorter and easier to use than “leftDriveWheels”. Page 4 Published: 18-July-2016 © George Gillard 1.2.2 Sensors There are three sensors tabs for the three categories of sensors. For analog and digital sensors, navigate to the relevant tab, put an appropriate name in the correct box, and select the type of sensor from the dropdown menu. Note that for Quadrature Encoders and Ultrasonic Rangefinders (SONAR), you will need to use two ports, but you only need to name the first port. For Ultrasonic Rangefinders (SONAR), there are different options for the resolution – choose an option that will give you enough detail for what you require. I2C Sensors such as the Integrated Motor Encoders are a little more complicated. Navigate to the I2C Sensors tab, and type in appropriate names, in the box that corresponds to each sensor. I2C_1 is the first sensor in the daisy-chain setup of sensors, that is, the one closest to the Cortex. Select the sensor type, then go back to the Motors tab. Find the relevant motor for each Integrated Motor Encoder, and in the Encoder Port dropdown menu, select the correct I2C sensor number. 1.2.3 LCD Displays If you have an LCD display on your robot, go to the Serial Ports tab, and for whichever UART port you use, select the VEX 2x16 LCD option. Once you have set up all motors and sensors, select “OK”. 1.3 Saving At this point, it’s a good idea to save your file. Either use the big “Save” button, Ctrl + S, or use the File menu. By default, your file will autosave when you compile your code (which you can use F7 to do), this setting is located at View Preferences Auto File Save Before Compile. It’s wise to regularly save your code, particularly if you do not use the auto save before compiling option. Page 5 Published: 18-July-2016 © George Gillard 2 Driver Control Code In this section, we’ll go over how to control a robot from a joystick. When we want to control the speed of a motor, we write: motor[name] = speed; The speed value ranges from -127 to +127, where -127 is full speed in reverse, +127 is full speed forwards, and 0 is no power. Note: It is crucial to add the semicolon “;” to the end of the line. The semicolon indicates the end of the command. To control the motors from a joystick, we need to be able to get a value from a particular control on the joystick, and use that to send a value for the speed to the motor. We then need to keep updating this value, so that the motor speed changes to correspond what we do with the joystick. To understand what goes on with the joystick, we split it up into 8 channels, as follows: Channel 1, the sideways channel on the right thumb-stick Channel 2, the vertical channel on the right thumb-stick Channel 3, the vertical channel on the left thumb-stick Channel 4, the sideways channel on the left thumb-stick Channel 5, the back buttons on the left (looking from the front) Channel 6, the back buttons on the right (looking from the front) Channel 7, the 4 buttons on the left of the joystick, on the top Channel 8, the 4 buttons on the right of the joystick, on the top Each of these channels outputs a number, which is the value we use to control the motors. If we were to take one of the thumb-stick channels, it would range from -127 to +127. Conveniently, this matches the output of the motors, which makes it all very easy to match them up. If we were to take one of the buttons, each button returns a value of 0 (not pressed), or 1 (pressed). How we get these values in code though, changes a bit. For a thumb-stick, we write: vexRT[Ch_] where _ is where we write the channel number. For example, the value of the vertical left hand joystick would be: vexRT[Ch3] Page 6 Published: 18-July-2016 © George Gillard For a button, we write: vexRT[Btn__] where __ is where we write the button number. First we write the number (5/6/7/8), and then the letter (U/D/L/R) which describes the button (Up/Down/Left/Right). For example, the value of the upper left hand back button would be: vexRT[Btn5U] For convenience, these numbers and letters are marked on the joystick. If we wanted to use a partner joystick (so two remote controls), to access the values on the second joystick, we add “Xmtr2” to the end. For example: vexRT[Ch3Xmtr2] vexRT[Btn5UXmtr2] Note: For the older 75mHz transmitter that works with the older PIC controllers, Channels 1-4 are the same as for the VEXnet Joystick that works with the Cortex, however, the buttons on the back work like thumb-stick channels instead of buttons, called as Ch5 and Ch6, which each range from -127 (bottom button pressed) to +127 (top button pressed). So, now, we want to take the values we have got from the joystick, and use them to power the motors. 2.1 Controlling Wheels The two main styles of driving are tank and arcade control. Tank control is where we use the two vertical channels on the two thumb-sticks to control each side of the drive-train. Arcade control is where we use one vertical channel to control forwards/backwards movement, and one horizontal channel to control the turning of the robot. 2.1.1 Tank Control This is perhaps the easiest control of all to code. All we need to do is write the output value from the joystick thumb-sticks directly to the motors, as follows: motor[leftDrive] = vexRT[Ch3]; motor[rightDrive] = vexRT[Ch2]; Page 7 Published: 18-July-2016 © George Gillard 2.1.2 Arcade Control Slightly more complicated, this is where we first meet mathematical operators in ROBOTC. We either add or subtract the horizontal (turn) channel to the vertical (forwards/backwards) channel. To determine to which one we add or subtract, we just need to think a little about what happens in each case. When the thumb-stick is pushed to the right hand side, we get a positive value, and when it is pushed to the left hand side, a negative value. When we push the joystick to the right, we generally want the robot to rotate clockwise when viewed from the top (the front would swing to the right). So, in order to achieve that, the wheels on the left hand side would need to go forwards (positive), and the wheels on the right hand side would need to go backwards (negative). Hence, we end up with: motor[leftDrive] = vertical + horizontal; motor[rightDrive] = vertical – horizontal; 2.2 Controlling Other Mechanisms If we’d wish to control a mechanism with a thumb-stick, we’d generally be able to control it much like tank control on a drive-train. To control anything with buttons, we need to be able to use values of 0 and 1 to control a speed much greater than 0 or 1. There are a few ways of doing this - through a series of decisions, or elegantly through a bit of math. Let’s just focus on using two buttons to control something – such as the up and down buttons on channel 5. 2.2.1 Controlling with Buttons via Decisions To do this, we need to be able to say if the up button is pressed, to go at a speed in one direction, and if the bottom button is pressed, to go at a speed in the other direction. Say we want to use the buttons to lift an arm, up and down, where up is positive. We can use if/else statements to do this. An “if” structure is where we have a condition, and then make some action depending on the result of this condition. if (condition) { action; } For the case of using the upper button to make the arm go up, we could write: if (upper button is pressed) { motor[arm] = 127; } Page 8 Published: 18-July-2016 © George Gillard Which in proper code is: Figure 2.2.1.1 Notice how two equal signs are used in the condition. When we are testing if something is equal to something else, we always use double equal signs. The result of the condition is always either true or false, that is, 1 or 0. Of course, the output of the button is also 1 or 0, so we can simplify this further: Figure 2.2.1.2 Now, we need to be able to lower the arm too, and also we need to be able to stop it (because nothing will stop unless you tell it to!). This is where we can bring in the “else” case – what happens if the condition in the if statement is not true. Figure 2.2.1.3 When there is only one command as the action for the if condition, the curly brackets can be omitted to make your code even more concise if you wanted so: Figure 2.2.1.4 Page 9 Published: 18-July-2016 © George Gillard 2.2.2 Controlling with Buttons via Mathematical Operators We can combine the output buttons, and scale them up by multiplying (*), to produce a value which ranges from -127 to +127. We can then feed this straight into our motor from this point, making an elegant solution out of the multiple inputs. Figure 2.2.2.1 Or, Figure 2.2.2.2 If you are using a competition template, your driver control code should all be placed inside the while loop of the usercontrol() task. Remove the placeholder function. If you are not using a competition template, ensure your file has a main() task below the motors and sensors setup #pragma config lines, like shown below: Figure 2.2.2.3 Once you have this, you will need to have a while loop inside the main() task, so that the values for the motors continue to update as the control from the joystick changes. The while loop looks similar to an if statement, as it has a condition at the top, and then curly brackets below which show the beginning and end of the loop. The condition needs to always be true so that the driver control code runs for infinity (don’t worry about this in competition, even if your loop theoretically runs for infinity, the competition control will stop your robot). Such conditions could be “1 == 1”, or simply just “true”. Put all of your driver control code within the loop and you’re done! Note: if you are programming for the PIC microcontroller and not using the competition template, you need to include the following line at the very beginning of the main() task before the while loop to tell the microcontroller to operate in a user control mode. This is unnecessary for the Cortex microcontroller, or if you are using a competition template. bIfiAutonomousMode = false; Page 10 Published: 18-July-2016 © George Gillard 3 Downloading a Program 3.1 Firmware If the robot that you are using has been programmed using a different version of ROBOTC, or has never been programmed before, you will need to download the firmwares. If you are using Cortex, by clicking “Robot Download Firmware Automatically Update VEX Cortex” ROBOTC will update all the firmwares required. If you are using a VEXnet Joystick, you will need to also download the firmwares onto that, too. For the Joystick firmware, plug in the USB – USB cable into the underside of the Joystick, where the VEXnet key would go. Just like the Cortex microcontroller, you can easily download the firmware by clicking “Robot Download Firmware Automatically Update VEXnet Joystick”. In the case where you need to manually load the firmwares, you can select them through “Robot Download Firmware Manually Update Firmware”. For the Cortex Microcontroller (Manually): Select “Master CPU Firmware Standard File” Once downloaded successfully, go through the menu again, except choose “ROBOTC Firmware Standard File”. For the VEXnet Joystick (Manually): Select “VEXnet Joystick Firmware Standard File”. Note: The VEXnet 2.0 (white) keys also have their own firmwares. These can be downloaded through a separate VEXnet Key 2.0 Update Utility, available from the VEX Robotics website. 3.2 Downloading the Program To download a program, hit F5 on your keyboard (you may need to press a function button to use F5). Alternatively, press the big “Download to Robot” button, or go through the menu: “Robot Compile and Download Program”. If the program fails to download, check that the robot is switched on and plugged into the computer with the VEX programming cable (USB – USB for Cortex, USB – Serial for PIC). Also, make sure the programming cable is plugged into the right place on the robot (the Serial port on the far left for PIC, or the USB port on the top of the Cortex, where the VEXnet key goes). If you are using VEXnet, you can download a program wirelessly. This can be done by plugging the serial cable into the serial (or “Program”) port on the VEXnet Joystick or VEXnet Upgrade transmitter module (for PIC). It is a good idea to regularly compile your code as you build it up to check for errors. You can do this with F7, or by clicking the big “Compile Program” button, or go through the menu: “Robot Compile Program”. Page 11 Published: 18-July-2016 © George Gillard 4 Basic Autonomous Routines Autonomous routines are essentially a series of commands that the robot executes by itself – with no human driver input. Whilst the robot may be programmed to make some decisions that alter it’s routine, the routines for the VEX Robotics Competition tend to be fully planned step-by-step. If you are using a Competition Template in your code, your autonomous routine(s) should go in the autonomous() task. If not, just code into the main() task. The easiest autonomous to make is a timed autonomous. This is basically something along the lines of “Go forward for 5 seconds at full speed then turn left for 1 second and go forwards at half speed”. To do something for a specific length of time, we use: wait1Msec(length of time here); or wait10Msec(length of time here); The difference is counting in either milliseconds or 10s of milliseconds. The main difference between counting in milliseconds or 10s of milliseconds is that the command “wait1Msec” can count up to a maximum of 32.768 seconds, whereas the command “wait10Msec” can count a maximum of 327.68 seconds. It is important to say what is going to happen after that length of time, for instance in the following code: Figure 4.1 This code does not explain what the robot will do after five seconds, so it will continue to go forwards. If you wanted it to stop after five seconds, you would need to tell it to do so, like shown below: Figure 4.2 Page 12 Published: 18-July-2016 © George Gillard So say if we wanted the robot to go forwards for 5 seconds, then turn left for one second, and then go forwards for another 2 and a half seconds, the code would look like this: Figure 4.3 4.1 Commenting When programming autonomous routines, it is particularly useful to add helpful comments, so that it is easier to understand for other people, and to help you as you code. In ROBOTC, you can add comments in your code to help explain what a particular line does, or to make a note. Comments have no function on the robot – they’re purely for us humans to read. The two types of comments are as below: // single line comment or /* area comment */ The difference is, a single line comment can be used to explain what is happening at the end of a line of code, whereas a multiple line comment can be used to comment out large areas of text, or temporarily disable an area of code by converting it into a comment. Comments show up green in ROBOTC, for easy spotting. Page 13 Published: 18-July-2016 © George Gillard 5 Introduction to Sensors Sensors are used for more accurate and precise programming. For instance, instead of saying, go forward for 5 seconds, you can say go forward 5 rotations. For this, we would use an encoder. Instead of saying lift the arm for 2 seconds, we could replace that with lift the arm to a certain angle. A good example of why sensors can make your programming more accurate, is that the arm may have already been slightly raised, therefore timing how long to raise the arm would not get to the right position. Generally, you would use a potentiometer on an arm, as it is a rotation sensor, like an encoder. The difference between a potentiometer and an encoder is that a potentiometer can't spin numerous times, and unlike an encoder, cannot be reset. Sensors can also be used to tell when a robot has hit something, distance between it and an object and plenty more. Sensors should be setup in the Motors and Sensors Setup window, as per the instructions in Section 1. The output from the sensor is given as a numeric value. Different sensors will give a different range of values. To get the value of a sensor, we use: sensorValue(name) Or, for an integrated motor encoder: nMotorEncoder[motor’s name] 5.1 Bumper and Limit Switches The first sensor we will use is a Bumper Switch. They are little buttons generally used for hitting into walls. Limit switches are programmed exactly the same way; the only major difference is that a limit switch has a thin metal tab that often snaps off. These switches, known as touch sensors, have 2 different values. They are 1, or 0 – pressed and released, respectively. In this example, the robot will drive forwards and stop if the bumper switch is pressed. Figure 5.1.1 Page 14 Published: 18-July-2016 © George Gillard Note: The condition could also be rewritten as: while(!sensorValue(bumper)) Where “!” basically means “not” something – i.e. if the sensor value is not 1. Note: The “motor[name] = 127;” commands could be placed before the while loop – since nothing is changing within the loop, it could be placed just before the loop. The above code will work once – that is, once the button has been pressed, the program is over. If we wanted the robot to continuously check if the button is pressed, and if pulled away from the wall such that the button is no longer pressed, drive again until it is pressed, we would need to write an infinite loop, and within that loop, run a quick check: Figure 5.1.2 5.2 Potentiometers and Encoders Potentiometers and encoders are very simple rotational sensors to use. As explained before, the main difference between the two is that potentiometers can only be rotated within a limited range, whereas encoders can be spun continuously. This makes potentiometers more useful on an arm, since they are more compact the larger quadrature encoders, only use one wire due to being an analog sensor, and have a greater resolution than encoders. For a drive train, you’d need to use encoders should you wish to measure rotation of the wheels. It is also quite common however to use an integrated motor encoder on the motor powering an arm, however you’d need a way of resetting the position at some point – such as by having a button at the bottom to detect when the arm is down, and using this to reset the encoder(s) to zero. Page 15 Published: 18-July-2016 © George Gillard In this example, we use a potentiometer to raise an arm to a “height” of 2000. Figure 5.2.1 Note: the less than “<” symbol can be used to determine if one value is less than another. Similarly, the symbols “>”, “<=” and “>=” can also be used for other purposes. We can also use “&&” as “and”, or “||” as “or”. This example uses integrated motor encoders to drive until one side has reached 1000 units: Figure 5.2.2 Conclusion I hope that this guide has been of good use to you, and that you now feel more confident in programming using ROBOTC. When I began programming, I was incredibly nervous the first time I tried to write code. Thankfully, ROBOTC is very forgiving, and will point out most issues with your code when you compile it. In the current day and age, learning to program is an incredibly useful skill, and ROBOTC is a great language to learn in. I have other guides available on my website, and there are plenty of other fantastic resources online. For example, ROBOTC have a great wiki/API on their website, which has more-or-less everything you’d need to know to be able to use the language. ROBOTC also provide plenty of sample programs, which are useful for learning. I would like to thank everyone who has helped me with this guide, in particular Michael Lawton and Richard Paul, who taught me to program at first and have inspired me to continue. Page 16 Published: 18-July-2016 © George Gillard