05 - Controller Mode (Handheld)

Overview

In this lesson, we will be working on controlling our robot with some form of handheld controller - A USB video game controller plugged into the computer and the VEX Controller for the physical robot. This page goes over how to set this up and the necessary code you will write.

    1. Built in Controller Mode (partially) - Briefly explains how RobotC Level Builder has a built in controller mode (partially).

    2. Connecting your USB Controller to Your Computer - How to plug in a handheld USB controller to Windows, how to make sure it is being detected, and how to check which buttons are which.

    3. Coding Joystick Configuration in RobotC - What necessary lines of code you need to write to be able to use a handheld controller with RobotC

  1. Overall Logic of Coding a Controller - Goes over the overall logic of writing your code for controller mode.

  2. Sample Code to Use a Controller - This is a sample code you can copy and paste into RobotC that will allow you to move forward by pressing a button on your controller.

    1. Important Note - Debugger Window - In RobotC it is often necessary to open the joystick debugger window for RobotC to detect inputs from Windows at all. Also, this shows you which button is named which.

    2. Actual Coding Names for a Handheld Controller - How to discover which buttons, joysticks, triggers and d-pad inputs are called what in RobotC.

    3. Macro - The idea is that you can pre-program certain sets of instructions to a single button click such as "turn 90 degrees", or any string of commands.

    4. What is VJoy - A brief explanation of what this stands for and how we will use it.

    5. How to Set Up VJoy - This takes you through the actual setup of VJoy, describing how to use and a few common issues that might arise.

  3. Common Mistakes

Built-in Controller Mode (partially)

The RobotC Level Builder actually has a built-in controller mode (partially). The standard controller setup shown in the table above is a built-in feature of RobotC Level Builder. If you compile and download a program to your virtual robot in Level Builder, you can freely use these controller buttons until you press the "Play" button. As soon as you press the "Play" button, it is now running only your compiled program and not any built-in RobotC code.

Standard Setup in RobotC Level Builder

"W" key - Robot moves forward

"S" key - Robot moves in reverse

"A" key - Robot turns left

"D" key - Robot turns right

"<" key - Robot arm moves up

">" key - Robot arm moves down

"}" key - Robot claw opens

"{" key - Robot claw closes

We will be completely programming our own controller mode as the physical robot has no pre-programmed controller mode. As well, the Curriculum Companion that we will go back to off and on also do not have a built-in controller mode. The above pre-built controller mode only works in Level Builder and only before the "Play" button is pressed.

Connecting your USB Controller to Your Computer

    1. Connect the USB controller to your computer. Most modern computers will automatically detect a USB game controller connected and set it up for you.

      1. \

    1. Now we will test that Windows is detecting inputs from the controller. In your Control Panel, find "Set Up USB Game Controllers". This can easily be found using the Cortana search bar.

    1. Select the controller you have plugged in (there is likely only one controller shown), and click on "Properties"

    1. You will be shown a screen that looks like the one below. Begin to move the different joysticks and press the buttons on your controller. Every button, trigger, joystick and d-pad press should register on the screen.

If this does not work correctly, you really have two options:

    1. Find the correct drivers on the internet that match your Operating System and actual controller you are holding, download these, install them and try again.

    2. Trying a different controller. This is often the easiest fix if you have extra controllers lying around.

Coding Joystick Configuration in RobotC

Just like you need to include #pragma config(StandardModel, "RVW CLAWBOT") in your code to tell RobotC which robot your are using (CLAWBOT), you need to tell RobotC that you will be using commands, functions and variables from a controller input.

We need to include two main things in your code to correctly get access to the controller:

    1. RobotC Commands - At the top of the code you are writing you need to include #include "JoystickDriver.c"

    2. This sets up the main joystick commands and functions that you will write in RobotC

    3. Windows Inputs - In your main body of code, we will leave a forever loop running that will constantly detect when buttons are pressed. The very first line in this forever loop needs to be: getJoystickSettings(joystick); Each time this is called, this gets the inputs being pressed in that cycle of the loop (lasting a fraction of a second).

The code should look like this:

#pragma config(StandardModel, "RVW CLAWBOT") // Gets the standard setup for the CLAWBOT

#include "JoystickDriver.c" // Gets the C code for the joystick setup in RobotC for functions such as buttons being pressed

task main()

{

while(true) {

getJoystickSettings(joystick); // Gets the current joystick setup you have setup in Windows/Vjoy

// Write your code here that will actually detect buttons being pressed and tell the robot what to do

}

}

Overall Logic of Coding a Controller

The overall idea is that we need to write code that is constantly checking/waiting for you to press a button. We do this by writing a forever loop without any wait times. In this way, the code is always continuously running through the forever loop (no stops), even if you are pressing a button. I clocked my controller code on my circa 2010 Samsung laptop, and while moving forward (holding down a button), the loop was running through about 450,000 times per second.

The overall idea of your code will be this:

#pragma, #include, etc

In your main, a forever loop starts here

get joystick settings from windows

IF button 1 pressed

do this

IF button 2 pressed

do this

IF button 3 pressed

do this

... etc etc with each of the 8 buttons

ELSE

set all motors to zero

In this way, when it is running through the program hundreds of thousand of times a second (or million depending on comp speed), it is instantly setting the motors to the values you want then checking again and again and again. As soon as you let go of the button, the "ELSE" says everything stay off until you press something again.

Note: If you want to clock how many times your code runs through your forever loop per second (as I was just curious), you can use this code: Link. Compile and download the program to your robot. Make sure you open the debugger window that shows "Global Variables". It will keep a score/count of repetitions while you have button 1 pressed (the "W" key). Use your cell phone timer and do these simultaneously - press start on your phone timer and press-and-hold the "W" key. When you are ready (after 20-30 seconds for a good average), press stop on the timer and take your finger off of the "W" key. Divide the score/count by the time and you get the average number of times the forever loop ran per second.

Sample Code to Use a Controller

Below is some code that you can copy and paste into your RobotC. The code detects when "Button 1" on "Joystick 1" is pressed.

While "Button 1" is being pressed, the robot's left and right motors will be on full power forward.

As soon as you let go of "Button 1", the robot's motors will be set to zero.

While "Button 2" is being pressed, the robot's left and right motors will be on full power reverse.

Again this loop is running continuously hundreds of thousands of times per second. Each time it checks, it sets the power level of the motors. There are no wait times.

#pragma config(StandardModel, "RVW CLAWBOT")

#include "JoystickDriver.c"

task main()

{

while(true) { // This is the forever loop that keeps continuously running over and over waiting for a button to be pressed

getJoystickSettings(joystick);

if ( joy1Btn(1) == 1 ) { // This means "If Button 1 on Controller #1 is pressed"

motor[leftMotor] = 127; // If button 1 is pressed, move forward (over and over in fractions of seconds)

motor[rightMotor] = 127;

}

else if ( joy1Btn(2) == 1 ) { // This means "Otherwise, if Button 2 on Controller #1 is pressed"

motor[leftMotor] = -127; // If button 2 is pressed, move backwards (over and over in fractions of seconds)

motor[rightMotor] = -127;

}

else {

motor[leftMotor] = 0; // If neither button 1 or button 2 are being pressed, don't move at all (over and over every fraction of a second)

motor[rightMotor] = 0;

}

}

}

Note - Notice that you are using "joy1Btn" every time no matter which button you are referring to. This is because you are talking about Controller #1 or Joystick #1, then the number in parenthesis is the number of the button on Controller #1 that you are talking about.

Important Note - Debugger Window

Big picture - there are two really important reasons to open up the "Joystick Control - Basic" debugger window that is shown below:

    1. RobotC often doesn't "connect" with Windows and receive joystick input at all until you open this debugger window. None of your code will work until this window is open (about 50% of the time).

    2. When you have this window open, you can press any button and see the name of the button in the button left where it says "Buttons: _____". This tells you which button is which for you to write code with.

RobotC sometimes does not detect controller buttons. It is often necessary to open the Debugger Window called "Joystick Control - Basic" to kick start the connection between Windows and RobotC. You can open this debugger window from the top menu by selecting "Robot ---> Debugger Windows ---> Joystick Control - Basic"

In RobotC from the top menu select:

Robot --> Debugger Windows --> Joystick Control - Basic

You will see the screen below.

Note that alternatively to manually opening this debugger window, you can include this line of code in your program and it will open the debugger window each time when compiled

#pragma debuggerWindows("joystickSimple");

Refresh the Connection - If ever you open the debugger window above and see the "Joystick Not Equipped" message, you can refresh the connection. Select the drop-down menu underneath and select "Refresh List". This usually always gets the connection going.

Actual Coding Names for a Handheld Controller

Note - On the back of our classroom controllers is a little switch that determines how the inputs/buttons are setup in windows. Set this switch to the right side so it is on the "D" - rather than the "X" on the left. From now on it will show up in windows as a "Dual Action" rather than as an "F310" controller.

Windows can support any number of handheld controllers from wired USB controllers to Bluetooth connected video game console controllers. These controllers will all function the same, with minor button mapping differences.

Setting up each individual type of controller will have a slightly different process (automatic, installing drivers, pairing with Bluetooth, etc). However, once correctly detected by your computer, every controller will basically have these inputs:

    1. Buttons

    2. D-Pad

    3. Two Joysticks

    4. Triggers?

1. Buttons - The buttons on your controller will show up as buttons numbered from zero to seven (or however many buttons your controller has).

You can check which button is which by simply having the debugger window open and pressing buttons.

Important Note - the button number that shows up in the debugger window and the button number we code with are always off by one.

The below image shows button 1 being pressed on the controller (we will use the button #1 in our code), yet it registers as button zero in the debugger window. You will always add one to this debugger window value, because the array of buttons starts counting at zero, while RobotC starts counting at 1. The #include "JoystickDriver.c" sets the values of the buttons this way, and you could even manually change this back yourself.

While pressing Button #1,

the robot will drive forward and when not pressing it the robot will stay stationary

When Button #1 is pressed,

the robot will drive forward for one second and then stop

if ( joy1Btn(1) == 1 ) {

motor[leftMotor] = 60;

motor[rightMotor] = 60;

wait1Msec(1000);

}

motor[leftMotor] = 0;

motor[rightMotor] = 0;

This sort of code with a wait time would be used as a "Macro" set of instructions. You could have a button that turns your robot exactly 90 degrees to the right for example. Nothing else will happen while this wait time is occurring in your loop. Your robot will be completely unresponsive during this time and all past motor values will stay at their current level until the wait time is over.

if ( joy1Btn(1) == 1 ) {

motor[leftMotor] = 60;

motor[leftMotor] = 60;

} else {

motor[leftMotor] = 0;

motor[leftMotor] = 0;

}

Note that joy1Btn(1) refers to "Button #1" in images of the controller, but shows up in the debugger window as "Buttons: 00"

2. D-Pad - The D-PAD is digital in the sense that each of 8 possible directions is represented by a number (like a single button in each direction). There is not a spectrum or gradient of values.

The command you use: joystick.joy1_TopHat

The values of this are:

    • -1 if it is not pressed

    • 0-7 depending on which direction of the "octant" you are point. Each direction has a discrete number (up, down, left, right, and the four diagonals). The numbering starts with a zero as pointed up and then going around clockwise in numerical order.

      • 0 = Up

      • 1 = Diagonally up and right

      • 2 = Right

      • ... etc

To check which button is which, have the "Joystick Control - Basic" debugger window open and just start pressing directions. In the display at the bottom left, the direction number will show up by "POV". Check out the image below and notice that when no direction is pressed (leftmost) the value of POV is -1. When a direction is pressed (as shown in the top red circles), then a new POV value shows up at the bottom.

Coding with the D-Pad is the same as coding with any other joystick button. Below is the same code, based on two separate types of inputs. The first one uses whether or not "Button 1" is pressed, while the second is using the D-Pad's "Up" arrow.

Using "Button 1" to Move Forward

if ( joy1Btn(1) == 1 ) {

motor[leftMotor] = 60;

motor[leftMotor] = 60;

)

Using the D-Pad "Up" Direction to Move Forward

if ( joystick.joy1_TopHat == 0 ) {

motor[leftMotor] = 60;

motor[leftMotor] = 60;

)

3. Two Joysticks - The two joysticks have a spectrum/gradient of values. The #include "JoystickDriver.c" sets the values of the joysticks to range from -127 to +127 so they are easily used with the motors.

The Left Joystick is "x1" and "y1" on Joy1 (Controller #1)

The Right Joystick is "x2" and "y2" on Joy1 (Controller #1)

The code you use is:

  • joystick.joy1_x1

  • joystick.joy1_y1

  • These represent the x- and y- values of the joystick location. It is like a coordinate plane that goes from -127 to +127. The values are usually opposite what you want, as when you move the joystick up, the part of the joystick inside the controller is actually going down.

You can use this in your code like this:

while(true) {

getJoystickSettings(joystick);

motor[leftMotor] = joystick.joy1_y1;

}

The code you use is:

  • joystick.joy1_x2

  • joystick.joy1_y2

  • These represent the x- and y- values of the joystick location. It is like a coordinate plane that goes from -127 to +127. The values are usually opposite what you want, as when you move the joystick up, the part of the joystick inside the controller is actually going down.

You can use this in your code like this:

while(true) {

getJoystickSettings(joystick);

motor[rightMotor] = joystick.joy1_y2;

}

Together these joysticks can be used in a forever loop like this:

while(true) {

getJoystickSettings(joystick);

motor[leftMotor] = -joystick.joy1_y1;

motor[rightMotor] = -joystick.joy1_y2;

}

4. Triggers - The left and right triggers are the oddball here. Depending on the controller and the version of Windows you are using, these may show up as a z-axis joystick or as button numbers. You will have to test this out in the debugger window and see how your controller and version of Windows behaves. It is not necessary to use this, but given the tutorials about buttons and joysticks already, simply apply the correct method to the triggers - whichever way they function on your computer.

Macros

Over the next few weeks we will have you program a set of commands that will be executed by a single button press. For example, when a button is pressed just once, have the robot travel forward 100cm then turn right 90 degrees and travel another 100cm.

We will refer to these stored commands as a "Macro". Each of these will be pre-programmed with a set of instructions.

Common Mistakes

    1. Button turns a motor on, but the motor doesn't stop when button is released - This is because you are not setting the motor to zero in the ELSE section of your code. Make sure that your ELSE section clearly sets all four motors to power levels of zero. This means that if you are not pressing any buttons, the robot will not move at all (unless it is still finishing some previous macro command that involves wait times).

    2. The arm motor won't move! - I coded it correctly, and even when I check the debugger window it says the arm motor has a power level of 60, yet it doesn't move at all! The reason for this is that the arm motor must lift the arm and the claw - it needs more power. If you try to raise the arm motor up with a power level of 30, it won't move. Even with a power level of 60 it may not move. Always use a power level of 127 when raising the arm motor (especially if it is holding an object in the claw).