Handling Input
Our programs are given input by the gamepads. There are two gamepads which are both accessible from the HardwareManager
class, named gamepad1
and gamepad2
.
Connection Gamepads to the Driver Station
NOTE
If you have an Android phone running a version later than Android 7.0, you can use your own device as a driver station. However, only approved devices can be used in a competition.
To connect a gamepad to a driver station, you will first need to find a suitable USB cable. Most controllers have a USB-A connector on the end, so you will need a USB-A to Micro-USB adapter to connect it to the driver station.
Once the controller is connected, you need to specify which controller should be used as gamepad1
and which should be used as gamepad2
. You do this by holding the OPTIONS
button on the controller (MENU
button on the Xbox controller) and pressing the A
button on the controller you want to use as gamepad1
. The controller you want to use as gamepad2
should be pressed with the B
button.
An indicator light on the gamepads will show which controller is gamepad1
and which is gamepad2
. Blue light indicates gamepad1
and red light indicates gamepad2
. Some gamepads do not have a light indicator.
Using the Controllers
The gamepads have a variety of buttons and triggers that can be used to control the robot. The gamepad buttons are accessed through the gamepad1
and gamepad2
objects in the HardwareManager
class. Each gamepad has properties corresponding to the buttons and axes on the controller. Buttons give values of true
or false
depending on whether they are pressed or not, triggers give values between 0
and 1
, and joysticks give values between -1
and 1
in both the x and y directions.
Examples
gamepad1.a // Boolean value for the A button
gamepad1.left_stick_x // Float value for the x-axis of the left joystick
gamepad2.right_trigger // Float value for the right trigger
Common Programming Techniques
TIP
Whenever you use a hardware device, it slightly slows down other processes. It is important to only use the hardware when necessary. These techniques will help with this.
Storing the Previous State
A common programming technique is to store the previous state of the gamepad and compare it to the current state. This is useful for detecting when a button is pressed or released, or when a joystick is moved. We do this by copying the current state of the gamepad to a variable at the end of the loop.
fun run() {
val previousState = Gamepad()
while (true) {
// ...
// Your code here
// ...
// Store the current state of the gamepad
previousState.copy(gamepad1)
}
}
You can use the previousState
variable to compare the current state of the gamepad to the previous state.
Rising Edge Detection
A common programming technique is to determine when a button is pressed, but not perform the action again until the button is released and pressed again. This is known as rising edge detection. It makes use of storing a previous state of the gamepad and comparing it to the current state.
if (gamepad1.a && !previousState.a) {
// The A button was just pressed
}
Falling Edge Detection
Falling edge detection is the opposite of rising edge detection. It determines when a button is released, but not perform the action again until the button is pressed and released again.
if (!gamepad1.a && previousState.a) {
// The A button was just released
}
Axis Change Detection
Being able to detect when an axis value changes is useful for optimizing code. Often, it is not necessary to perform an action all the time, and sometimes we only want to perform an action when the joystick is moved. This can be done by comparing the current state of the joystick to the previous state.
if (gamepad1.left_stick_x != previousState.left_stick_x) {
// The x-axis of the left joystick has changed
}
Toggles
A toggle is a button which makes an action occur until it is pressed again. This uses a boolean variable to keep track of the state of the toggle and changes it when the button is pressed. It uses a rising edge detector to only change the state when the button is pressed, not when it is held.
var toggleState = false // This must go outside the while loop
// ...
if (gamepad1.a && !previousState.a) {
toggleState = !toggleState // Change the toggleState variable to the opposite of what it was
if (toggleState) {
// The toggle is true
} else {
// The toggle is false
}
}
Gamepad Feedback
WARNING
Gamepad feedback is not available on all gamepads. The Logitech F310 gamepad does not have any feedback, while the Xbox 360 gamepad has vibration feedback and not LED feedback.
Gamepads have two ways of providing feedback: vibration and LED lights.
Vibration Feedback
Simple vibration feedback is a simple on/off vibration. It can be used to provide feedback to the driver when a certain action is performed. It is used with the rumble()
function of the gamepad. It takes in one argument, the duration of the vibration in milliseconds.
gamepad1.rumble(1000) // Vibrate for 1 second
There is also the rumbleBlips()
function which takes in one argument: the number of blips. A blip is a short vibration. The function will vibrate for the duration of the blip, then pause for the same duration, and repeat for the number of blips specified.
gamepad1.rumbleBlips(3) // Vibrate 3 times
LED Feedback
The LED on the gamepad is controlled by the setLedColor()
function. It takes in four arguments: the red component, the green component, the blue component, and the duration in milliseconds. The color components are between 0.0
and 1.0
.
gamepad1.setLedColor(1.0, 0.0, 0.0, 1000) // Set the LED to red for 1 second
If you want the LED to stay on indefinitely, you can set the duration to a special constant.
gamepad1.setLedColor(0.0, 1.0, 0.0, Gamepad.LED_DURATION_CONTINUOUS) // Set the LED to green indefinitely
WARNING
The Driver Station uses LEDs to specify which gamepad is gamepad1
and which is gamepad2
. A green LED indicates no assignment, blue indicates gamepad1
, and red indicates gamepad2
. If your code sets the LED to any of those colors, it may confuse the user into thinking the gamepad is not connected properly. For that reason, avoid manually setting the LED to red, green, or blue.
Allow Gamepad Remapping
Sometimes, you want a script to be reusable. To make a script is reusable as possible, you want to allow the script to be able to change the gamepad input it takes. This can be done by defining a function which gives the gamepad input as a return value.
Kotlin allows us to use functions as variables. This means we can pass a function as an argument to the constructor of a script. First, we need to allow the script to take a function as an argument.
class MyScript(private val gamepadInput: () -> Boolean) : Script() {
// ...
}
This makes a script which takes a function which returns a boolean value. This function can be called whenever we need the gamepad input. An example of such a function would be
val gamepadInput = { gamepad1.a }
Then, to get the value of the button, we can just call the function.
gamepadInput()
Say we are designing a script and we want to allow the user to change the button which activates something, but we also want to provide a default value. We can do this by providing a default value to the constructor of the script.
class MyScript(private val gamepadInput: () -> Boolean = { gamepad1.a }) : Script() {
// ...
}
TIP
If you want to see more information regarding this topic, you can check out any of the other scripts and use those as examples.