|
| 1 | +package org.firstinspires.ftc.robotcontroller.external.samples; |
| 2 | + |
| 3 | +import com.qualcomm.robotcore.eventloop.opmode.Disabled; |
| 4 | +import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; |
| 5 | +import com.qualcomm.robotcore.eventloop.opmode.TeleOp; |
| 6 | +import com.qualcomm.robotcore.hardware.Gamepad; |
| 7 | +import com.qualcomm.robotcore.util.ElapsedTime; |
| 8 | + |
| 9 | +/** |
| 10 | + * This sample illustrates using the rumble feature of many gamepads. |
| 11 | + * |
| 12 | + * Note: Some gamepads "rumble" better than others. |
| 13 | + * The Xbox & PS4 have a left (rumble1) and right (rumble2) rumble motor. |
| 14 | + * These two gamepads have two distinct rumble modes: Large on the left, and small on the right |
| 15 | + * The ETpark gamepad may only respond to rumble1, and may only run at full power. |
| 16 | + * The Logitech F310 gamepad does not have *any* rumble ability. |
| 17 | + * |
| 18 | + * Moral: You should use this sample to experiment with your specific gamepads to explore their rumble features. |
| 19 | + * |
| 20 | + * The rumble motors are accessed through the standard gamepad1 and gamepad2 objects. |
| 21 | + * Several new methods were added to the Gamepad class in FTC SDK Rev 7 |
| 22 | + * The key methods are as follows: |
| 23 | + * |
| 24 | + * .rumble(double rumble1, double rumble2, int durationMs) |
| 25 | + * This method sets the rumble power of both motors for a specific time duration. |
| 26 | + * Both rumble arguments are motor-power levels in the 0.0 to 1.0 range. |
| 27 | + * durationMs is the desired length of the rumble action in milliseconds. |
| 28 | + * This method returns immediately. |
| 29 | + * Note: |
| 30 | + * Use a durationMs of Gamepad.RUMBLE_DURATION_CONTINUOUS to provide a continuous rumble |
| 31 | + * Use a power of 0, or duration of 0 to stop a rumble. |
| 32 | + * |
| 33 | + * .rumbleBlips(int count) allows an easy way to signal the driver with a series of rumble blips. |
| 34 | + * Just specify how many blips you want. |
| 35 | + * This method returns immediately. |
| 36 | + * |
| 37 | + * .runRumbleEffect(customRumbleEffect) allows you to run a custom rumble sequence that you have |
| 38 | + * built using the Gamepad.RumbleEffect.Builder() |
| 39 | + * A "custom effect" is a sequence of steps, where each step can rumble any of the |
| 40 | + * rumble motors for a specific period at a specific power level. |
| 41 | + * The Custom Effect will play as the (un-blocked) OpMode continues to run |
| 42 | + * |
| 43 | + * .isRumbling() returns true if there is a rumble effect in progress. |
| 44 | + * Use this call to prevent stepping on a current rumble. |
| 45 | + * |
| 46 | + * .stopRumble() Stop any ongoing rumble or custom rumble effect. |
| 47 | + * |
| 48 | + * .rumble(int durationMs) Full power rumble for fixed duration. |
| 49 | + * |
| 50 | + * Note: Whenever a new Rumble command is issued, any currently executing rumble action will |
| 51 | + * be truncated, and the new action started immediately. Take these precautions: |
| 52 | + * 1) Do Not SPAM the rumble motors by issuing rapid fire commands |
| 53 | + * 2) Multiple sources for rumble commands must coordinate to avoid tromping on each other. |
| 54 | + * |
| 55 | + * This can be achieved several possible ways: |
| 56 | + * 1) Only having one source for rumble actions |
| 57 | + * 2) Issuing rumble commands on transitions, rather than states. |
| 58 | + * e.g. The moment a touch sensor is pressed, rather than the entire time it is being pressed. |
| 59 | + * 3) Scheduling rumble commands based on timed events. e.g. 10 seconds prior to endgame |
| 60 | + * 4) Rumble on non-overlapping mechanical actions. e.g. arm fully-extended or fully-retracted. |
| 61 | + * 5) Use isRumbling() to hold off on a new rumble if one is already in progress. |
| 62 | + * |
| 63 | + * The examples shown here are representstive of how to invoke a gamepad rumble. |
| 64 | + * It is assumed that these will be modified to suit the specific robot and team strategy needs. |
| 65 | + * |
| 66 | + * ######## Read the telemetry display on the Driver Station Screen for instructions. ###### |
| 67 | + * |
| 68 | + * Ex 1) This example shows a) how to create a custom rumble effect, and then b) how to trigger it based |
| 69 | + * on game time. One use for this might be to alert the driver that half-time or End-game is approaching. |
| 70 | + * |
| 71 | + * Ex 2) This example shows tying the rumble power to a changing sensor value. |
| 72 | + * In this case it is the Gamepad trigger, but it could be any sensor output scaled to the 0-1 range. |
| 73 | + * Since it takes over the rumble motors, it is only performed when the Left Bumper is pressed. |
| 74 | + * Note that this approach MUST include a way to turn OFF the rumble when the button is released. |
| 75 | + * |
| 76 | + * Ex 3) This example shows a simple way to trigger a 3-blip sequence. In this case it is |
| 77 | + * triggered by the gamepad A (Cross) button, but it could be any sensor, like a touch or light sensor. |
| 78 | + * Note that this code ensures that it only rumbles once when the input goes true. |
| 79 | + * |
| 80 | + * Ex 4) This example shows how to trigger a single rumble when an input value gets over a certain value. |
| 81 | + * In this case it is reading the Right Trigger, but it could be any variable sensor, like a |
| 82 | + * range sensor, or position sensor. The code needs to ensure that it is only triggered once, so |
| 83 | + * it waits till the sensor drops back below the threshold before it can trigger again. |
| 84 | + * |
| 85 | + * Use Android Studio to Copy this Class, and Paste it into your team's code folder with a new name. |
| 86 | + * Remove or comment out the @Disabled line to add this OpMode to the Driver Station OpMode list. |
| 87 | + */ |
| 88 | + |
| 89 | +@Disabled |
| 90 | +@TeleOp(name="Concept: Gamepad Rumble", group ="Concept") |
| 91 | +public class ConceptGamepadRumble extends LinearOpMode |
| 92 | +{ |
| 93 | + boolean lastA = false; // Use to track the prior button state. |
| 94 | + boolean lastLB = false; // Use to track the prior button state. |
| 95 | + boolean highLevel = false; // used to prevent multiple level-based rumbles. |
| 96 | + boolean secondHalf = false; // Use to prevent multiple half-time warning rumbles. |
| 97 | + |
| 98 | + Gamepad.RumbleEffect customRumbleEffect; // Use to build a custom rumble sequence. |
| 99 | + ElapsedTime runtime = new ElapsedTime(); // Use to determine when end game is starting. |
| 100 | + |
| 101 | + final double HALF_TIME = 60.0; // Wait this many seconds before rumble-alert for half-time. |
| 102 | + final double TRIGGER_THRESHOLD = 0.75; // Squeeze more than 3/4 to get rumble. |
| 103 | + |
| 104 | + @Override |
| 105 | + public void runOpMode() |
| 106 | + { |
| 107 | + // Example 1. a) start by creating a three-pulse rumble sequence: right, LEFT, LEFT |
| 108 | + customRumbleEffect = new Gamepad.RumbleEffect.Builder() |
| 109 | + .addStep(0.0, 1.0, 500) // Rumble right motor 100% for 500 mSec |
| 110 | + .addStep(0.0, 0.0, 300) // Pause for 300 mSec |
| 111 | + .addStep(1.0, 0.0, 250) // Rumble left motor 100% for 250 mSec |
| 112 | + .addStep(0.0, 0.0, 250) // Pause for 250 mSec |
| 113 | + .addStep(1.0, 0.0, 250) // Rumble left motor 100% for 250 mSec |
| 114 | + .build(); |
| 115 | + |
| 116 | + telemetry.addData(">", "Press Start"); |
| 117 | + telemetry.update(); |
| 118 | + |
| 119 | + waitForStart(); |
| 120 | + runtime.reset(); // Start game timer. |
| 121 | + |
| 122 | + // Loop while monitoring buttons for rumble triggers |
| 123 | + while (opModeIsActive()) |
| 124 | + { |
| 125 | + // Read and save the current gamepad button states. |
| 126 | + boolean currentA = gamepad1.a ; |
| 127 | + boolean currentLB = gamepad1.left_bumper ; |
| 128 | + |
| 129 | + // Display the current Rumble status. Just for interest. |
| 130 | + telemetry.addData(">", "Are we RUMBLING? %s\n", gamepad1.isRumbling() ? "YES" : "no" ); |
| 131 | + |
| 132 | + // ---------------------------------------------------------------------------------------- |
| 133 | + // Example 1. b) Watch the runtime timer, and run the custom rumble when we hit half-time. |
| 134 | + // Make sure we only signal once by setting "secondHalf" flag to prevent further rumbles. |
| 135 | + // ---------------------------------------------------------------------------------------- |
| 136 | + if ((runtime.seconds() > HALF_TIME) && !secondHalf) { |
| 137 | + gamepad1.runRumbleEffect(customRumbleEffect); |
| 138 | + secondHalf =true; |
| 139 | + } |
| 140 | + |
| 141 | + // Display the time remaining while we are still counting down. |
| 142 | + if (!secondHalf) { |
| 143 | + telemetry.addData(">", "Halftime Alert Countdown: %3.0f Sec \n", (HALF_TIME - runtime.seconds()) ); |
| 144 | + } |
| 145 | + |
| 146 | + |
| 147 | + // ---------------------------------------------------------------------------------------- |
| 148 | + // Example 2. If Left Bumper is being pressed, power the rumble motors based on the two trigger depressions. |
| 149 | + // This is useful to see how the rumble feels at various power levels. |
| 150 | + // ---------------------------------------------------------------------------------------- |
| 151 | + if (currentLB) { |
| 152 | + // Left Bumper is being pressed, so send left and right "trigger" values to left and right rumble motors. |
| 153 | + gamepad1.rumble(gamepad1.left_trigger, gamepad1.right_trigger, Gamepad.RUMBLE_DURATION_CONTINUOUS); |
| 154 | + |
| 155 | + // Show what is being sent to rumbles |
| 156 | + telemetry.addData(">", "Squeeze triggers to control rumbles"); |
| 157 | + telemetry.addData("> : Rumble", "Left: %.0f%% Right: %.0f%%", gamepad1.left_trigger * 100, gamepad1.right_trigger * 100); |
| 158 | + } else { |
| 159 | + // Make sure rumble is turned off when Left Bumper is released (only one time each press) |
| 160 | + if (lastLB) { |
| 161 | + gamepad1.stopRumble(); |
| 162 | + } |
| 163 | + |
| 164 | + // Prompt for manual rumble action |
| 165 | + telemetry.addData(">", "Hold Left-Bumper to test Manual Rumble"); |
| 166 | + telemetry.addData(">", "Press A (Cross) for three blips"); |
| 167 | + telemetry.addData(">", "Squeeze right trigger slowly for 1 blip"); |
| 168 | + } |
| 169 | + lastLB = currentLB; // remember the current button state for next time around the loop |
| 170 | + |
| 171 | + |
| 172 | + // ---------------------------------------------------------------------------------------- |
| 173 | + // Example 3. Blip 3 times at the moment that A (Cross) is pressed. (look for pressed transition) |
| 174 | + // BUT !!! Skip it altogether if the Gamepad is already rumbling. |
| 175 | + // ---------------------------------------------------------------------------------------- |
| 176 | + if (currentA && !lastA) { |
| 177 | + if (!gamepad1.isRumbling()) // Check for possible overlap of rumbles. |
| 178 | + gamepad1.rumbleBlips(3); |
| 179 | + } |
| 180 | + lastA = currentA; // remember the current button state for next time around the loop |
| 181 | + |
| 182 | + |
| 183 | + // ---------------------------------------------------------------------------------------- |
| 184 | + // Example 4. Rumble once when gamepad right trigger goes above the THRESHOLD. |
| 185 | + // ---------------------------------------------------------------------------------------- |
| 186 | + if (gamepad1.right_trigger > TRIGGER_THRESHOLD) { |
| 187 | + if (!highLevel) { |
| 188 | + gamepad1.rumble(0.9, 0, 200); // 200 mSec burst on left motor. |
| 189 | + highLevel = true; // Hold off any more triggers |
| 190 | + } |
| 191 | + } else { |
| 192 | + highLevel = false; // We can trigger again now. |
| 193 | + } |
| 194 | + |
| 195 | + // Send the telemetry data to the Driver Station, and then pause to pace the program. |
| 196 | + telemetry.update(); |
| 197 | + sleep(10); |
| 198 | + } |
| 199 | + } |
| 200 | +} |
0 commit comments