ftc-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +224 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +15 -0
- package/build/knowledge/dashboard.d.ts +8 -0
- package/build/knowledge/dashboard.js +843 -0
- package/build/knowledge/examples.d.ts +1 -0
- package/build/knowledge/examples.js +1800 -0
- package/build/knowledge/ftc-sdk.d.ts +8 -0
- package/build/knowledge/ftc-sdk.js +1088 -0
- package/build/knowledge/ftclib.d.ts +10 -0
- package/build/knowledge/ftclib.js +1403 -0
- package/build/knowledge/gradle.d.ts +8 -0
- package/build/knowledge/gradle.js +956 -0
- package/build/knowledge/hardware.d.ts +22 -0
- package/build/knowledge/hardware.js +1949 -0
- package/build/knowledge/panels.d.ts +10 -0
- package/build/knowledge/panels.js +572 -0
- package/build/knowledge/pedro.d.ts +8 -0
- package/build/knowledge/pedro.js +1548 -0
- package/build/knowledge/roadrunner.d.ts +3 -0
- package/build/knowledge/roadrunner.js +481 -0
- package/build/knowledge/vision.d.ts +12 -0
- package/build/knowledge/vision.js +1869 -0
- package/build/prompts/registry.d.ts +2 -0
- package/build/prompts/registry.js +634 -0
- package/build/resources/registry.d.ts +2 -0
- package/build/resources/registry.js +520 -0
- package/build/server.d.ts +2 -0
- package/build/server.js +17 -0
- package/build/tools/registry.d.ts +2 -0
- package/build/tools/registry.js +660 -0
- package/package.json +34 -0
|
@@ -0,0 +1,1088 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FTC_SDK_KNOWLEDGE = void 0;
|
|
4
|
+
exports.FTC_SDK_KNOWLEDGE = {
|
|
5
|
+
overview: `
|
|
6
|
+
## FTC (FIRST Tech Challenge) Overview
|
|
7
|
+
|
|
8
|
+
FIRST Tech Challenge (FTC) is a robotics competition where teams design, build, program, and operate
|
|
9
|
+
robots built with Android-based control systems. Robots are programmed using Java (primarily) or
|
|
10
|
+
Kotlin via Android Studio.
|
|
11
|
+
|
|
12
|
+
### SDK Details
|
|
13
|
+
- **Current SDK Version**: 11.1.0
|
|
14
|
+
- **Platform**: Android-based (REV Control Hub / REV Expansion Hub)
|
|
15
|
+
- **IDE**: Android Studio (recommended), OnBot Java, or Blocks
|
|
16
|
+
- **Language**: Java (primary), Kotlin (supported)
|
|
17
|
+
|
|
18
|
+
### Project Structure
|
|
19
|
+
Team-written code lives in:
|
|
20
|
+
\`\`\`
|
|
21
|
+
TeamCode/src/main/java/org/firstinspires/ftc/teamcode/
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
Package declaration for all team files:
|
|
25
|
+
\`\`\`java
|
|
26
|
+
package org.firstinspires.ftc.teamcode;
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Or with sub-packages:
|
|
30
|
+
\`\`\`java
|
|
31
|
+
package org.firstinspires.ftc.teamcode.opmodes;
|
|
32
|
+
package org.firstinspires.ftc.teamcode.subsystems;
|
|
33
|
+
package org.firstinspires.ftc.teamcode.util;
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
### Key SDK Modules
|
|
37
|
+
- **FtcRobotController**: The main Android app module (DO NOT modify unless advanced)
|
|
38
|
+
- **TeamCode**: Where ALL team code goes — OpModes, subsystems, utilities
|
|
39
|
+
- **RobotCore**: Low-level hardware abstraction (provided by SDK)
|
|
40
|
+
- **Hardware**: Hardware device interfaces (provided by SDK)
|
|
41
|
+
|
|
42
|
+
### Control System Hardware
|
|
43
|
+
- **REV Control Hub**: Main robot controller (has built-in Android computer)
|
|
44
|
+
- **REV Expansion Hub**: Additional I/O (connected via RS-485 to Control Hub)
|
|
45
|
+
- **Driver Station**: Android phone or REV Driver Hub running the DS app
|
|
46
|
+
- **Gamepads**: Logitech F310 or Xbox-compatible (plugged into Driver Station)
|
|
47
|
+
`,
|
|
48
|
+
opmodePatterns: `
|
|
49
|
+
## OpMode Patterns and Lifecycle
|
|
50
|
+
|
|
51
|
+
There are two base classes for writing OpModes in FTC:
|
|
52
|
+
|
|
53
|
+
### 1. Iterative OpMode (extends OpMode)
|
|
54
|
+
|
|
55
|
+
The iterative OpMode uses a state-machine-friendly callback lifecycle:
|
|
56
|
+
|
|
57
|
+
\`\`\`
|
|
58
|
+
init() → init_loop() → start() → loop() → stop()
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
**Lifecycle Methods:**
|
|
62
|
+
- \`init()\` — Called ONCE when INIT is pressed. Initialize hardware, set starting positions.
|
|
63
|
+
- \`init_loop()\` — Called REPEATEDLY after init() until START is pressed. Use for sensor calibration, waiting feedback.
|
|
64
|
+
- \`start()\` — Called ONCE when START is pressed (or auto-start timer fires). Reset timers, start motion.
|
|
65
|
+
- \`loop()\` — Called REPEATEDLY after start(). Main robot logic goes here. Runs until STOP.
|
|
66
|
+
- \`stop()\` — Called ONCE when STOP is pressed. Clean up resources, stop motors.
|
|
67
|
+
|
|
68
|
+
**When to use:** Best for finite-state-machine (FSM) based designs, TeleOp with subsystem states,
|
|
69
|
+
and when you need non-blocking architecture. Ideal for complex autonomous routines with many parallel actions.
|
|
70
|
+
|
|
71
|
+
**Complete Example:**
|
|
72
|
+
\`\`\`java
|
|
73
|
+
package org.firstinspires.ftc.teamcode;
|
|
74
|
+
|
|
75
|
+
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
|
|
76
|
+
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
|
|
77
|
+
import com.qualcomm.robotcore.hardware.DcMotor;
|
|
78
|
+
import com.qualcomm.robotcore.hardware.Servo;
|
|
79
|
+
|
|
80
|
+
@TeleOp(name = "Iterative TeleOp Example", group = "Examples")
|
|
81
|
+
public class IterativeTeleOpExample extends OpMode {
|
|
82
|
+
|
|
83
|
+
private DcMotor frontLeft;
|
|
84
|
+
private DcMotor frontRight;
|
|
85
|
+
private DcMotor backLeft;
|
|
86
|
+
private DcMotor backRight;
|
|
87
|
+
private Servo claw;
|
|
88
|
+
|
|
89
|
+
private boolean clawOpen = false;
|
|
90
|
+
private boolean previousA = false;
|
|
91
|
+
|
|
92
|
+
@Override
|
|
93
|
+
public void init() {
|
|
94
|
+
frontLeft = hardwareMap.get(DcMotor.class, "frontLeft");
|
|
95
|
+
frontRight = hardwareMap.get(DcMotor.class, "frontRight");
|
|
96
|
+
backLeft = hardwareMap.get(DcMotor.class, "backLeft");
|
|
97
|
+
backRight = hardwareMap.get(DcMotor.class, "backRight");
|
|
98
|
+
claw = hardwareMap.get(Servo.class, "claw");
|
|
99
|
+
|
|
100
|
+
frontLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
101
|
+
backLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
102
|
+
|
|
103
|
+
frontLeft.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
|
104
|
+
frontRight.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
|
105
|
+
backLeft.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
|
106
|
+
backRight.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
|
|
107
|
+
|
|
108
|
+
claw.setPosition(0.0); // closed
|
|
109
|
+
|
|
110
|
+
telemetry.addData("Status", "Initialized");
|
|
111
|
+
telemetry.update();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@Override
|
|
115
|
+
public void init_loop() {
|
|
116
|
+
// Could do sensor calibration here
|
|
117
|
+
telemetry.addData("Status", "Waiting for start...");
|
|
118
|
+
telemetry.update();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Override
|
|
122
|
+
public void start() {
|
|
123
|
+
resetRuntime();
|
|
124
|
+
telemetry.addData("Status", "Running");
|
|
125
|
+
telemetry.update();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@Override
|
|
129
|
+
public void loop() {
|
|
130
|
+
// Mecanum drive
|
|
131
|
+
double drive = -gamepad1.left_stick_y; // Negate Y: pushing stick forward = negative value
|
|
132
|
+
double strafe = gamepad1.left_stick_x;
|
|
133
|
+
double rotate = gamepad1.right_stick_x;
|
|
134
|
+
|
|
135
|
+
double frontLeftPower = drive + strafe + rotate;
|
|
136
|
+
double frontRightPower = drive - strafe - rotate;
|
|
137
|
+
double backLeftPower = drive - strafe + rotate;
|
|
138
|
+
double backRightPower = drive + strafe - rotate;
|
|
139
|
+
|
|
140
|
+
// Normalize
|
|
141
|
+
double maxPower = Math.max(1.0, Math.max(
|
|
142
|
+
Math.max(Math.abs(frontLeftPower), Math.abs(frontRightPower)),
|
|
143
|
+
Math.max(Math.abs(backLeftPower), Math.abs(backRightPower))
|
|
144
|
+
));
|
|
145
|
+
frontLeft.setPower(frontLeftPower / maxPower);
|
|
146
|
+
frontRight.setPower(frontRightPower / maxPower);
|
|
147
|
+
backLeft.setPower(backLeftPower / maxPower);
|
|
148
|
+
backRight.setPower(backRightPower / maxPower);
|
|
149
|
+
|
|
150
|
+
// Toggle claw on A button (rising edge detection)
|
|
151
|
+
if (gamepad1.a && !previousA) {
|
|
152
|
+
clawOpen = !clawOpen;
|
|
153
|
+
claw.setPosition(clawOpen ? 0.5 : 0.0);
|
|
154
|
+
}
|
|
155
|
+
previousA = gamepad1.a;
|
|
156
|
+
|
|
157
|
+
telemetry.addData("Drive", "FL=%.2f FR=%.2f BL=%.2f BR=%.2f",
|
|
158
|
+
frontLeftPower, frontRightPower, backLeftPower, backRightPower);
|
|
159
|
+
telemetry.addData("Claw", clawOpen ? "Open" : "Closed");
|
|
160
|
+
telemetry.addData("Runtime", "%.1f seconds", getRuntime());
|
|
161
|
+
telemetry.update();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Override
|
|
165
|
+
public void stop() {
|
|
166
|
+
frontLeft.setPower(0);
|
|
167
|
+
frontRight.setPower(0);
|
|
168
|
+
backLeft.setPower(0);
|
|
169
|
+
backRight.setPower(0);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
### 2. LinearOpMode (extends LinearOpMode)
|
|
175
|
+
|
|
176
|
+
The linear OpMode runs as a single sequential method:
|
|
177
|
+
|
|
178
|
+
\`\`\`
|
|
179
|
+
runOpMode() {
|
|
180
|
+
// initialize
|
|
181
|
+
waitForStart();
|
|
182
|
+
// sequential logic
|
|
183
|
+
}
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
**Key Methods:**
|
|
187
|
+
- \`runOpMode()\` — The single entry point. ALL code goes here.
|
|
188
|
+
- \`waitForStart()\` — Blocks until the START button is pressed.
|
|
189
|
+
- \`opModeIsActive()\` — Returns true while OpMode is running (not stopped). Use in while loops.
|
|
190
|
+
- \`opModeInInit()\` — Returns true while in INIT phase (before START). Use for init loops.
|
|
191
|
+
- \`idle()\` — Yields thread to allow other operations to run. Use in tight loops.
|
|
192
|
+
- \`sleep(long milliseconds)\` — Pauses execution for the specified time (only safe in LinearOpMode).
|
|
193
|
+
|
|
194
|
+
**When to use:** Best for sequential autonomous routines, simple TeleOp, beginners, and when
|
|
195
|
+
step-by-step execution flow is clearer. Ideal for linear autonomous paths.
|
|
196
|
+
|
|
197
|
+
**Complete Example:**
|
|
198
|
+
\`\`\`java
|
|
199
|
+
package org.firstinspires.ftc.teamcode;
|
|
200
|
+
|
|
201
|
+
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
|
|
202
|
+
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
|
|
203
|
+
import com.qualcomm.robotcore.hardware.DcMotor;
|
|
204
|
+
import com.qualcomm.robotcore.hardware.Servo;
|
|
205
|
+
import com.qualcomm.robotcore.util.ElapsedTime;
|
|
206
|
+
|
|
207
|
+
@Autonomous(name = "Linear Auto Example", group = "Examples")
|
|
208
|
+
public class LinearAutoExample extends LinearOpMode {
|
|
209
|
+
|
|
210
|
+
private DcMotor frontLeft;
|
|
211
|
+
private DcMotor frontRight;
|
|
212
|
+
private DcMotor backLeft;
|
|
213
|
+
private DcMotor backRight;
|
|
214
|
+
private Servo claw;
|
|
215
|
+
|
|
216
|
+
private ElapsedTime runtime = new ElapsedTime();
|
|
217
|
+
|
|
218
|
+
@Override
|
|
219
|
+
public void runOpMode() {
|
|
220
|
+
// INIT PHASE
|
|
221
|
+
frontLeft = hardwareMap.get(DcMotor.class, "frontLeft");
|
|
222
|
+
frontRight = hardwareMap.get(DcMotor.class, "frontRight");
|
|
223
|
+
backLeft = hardwareMap.get(DcMotor.class, "backLeft");
|
|
224
|
+
backRight = hardwareMap.get(DcMotor.class, "backRight");
|
|
225
|
+
claw = hardwareMap.get(Servo.class, "claw");
|
|
226
|
+
|
|
227
|
+
frontLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
228
|
+
backLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
229
|
+
|
|
230
|
+
claw.setPosition(0.5); // grip preloaded sample
|
|
231
|
+
|
|
232
|
+
telemetry.addData("Status", "Initialized");
|
|
233
|
+
telemetry.update();
|
|
234
|
+
|
|
235
|
+
// Init loop — runs until START pressed
|
|
236
|
+
while (opModeInInit()) {
|
|
237
|
+
telemetry.addData("Status", "Waiting for start...");
|
|
238
|
+
telemetry.update();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// PLAY PHASE — waitForStart() has been replaced by opModeInInit() above
|
|
242
|
+
runtime.reset();
|
|
243
|
+
|
|
244
|
+
// Drive forward
|
|
245
|
+
setDrivePower(0.5, 0.5, 0.5, 0.5);
|
|
246
|
+
sleep(1000);
|
|
247
|
+
|
|
248
|
+
// Stop
|
|
249
|
+
setDrivePower(0, 0, 0, 0);
|
|
250
|
+
|
|
251
|
+
// Strafe right
|
|
252
|
+
setDrivePower(0.5, -0.5, -0.5, 0.5);
|
|
253
|
+
sleep(500);
|
|
254
|
+
|
|
255
|
+
// Stop and release
|
|
256
|
+
setDrivePower(0, 0, 0, 0);
|
|
257
|
+
claw.setPosition(0.0); // open claw
|
|
258
|
+
sleep(500);
|
|
259
|
+
|
|
260
|
+
telemetry.addData("Status", "Auto Complete in %.1f sec", runtime.seconds());
|
|
261
|
+
telemetry.update();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private void setDrivePower(double fl, double fr, double bl, double br) {
|
|
265
|
+
frontLeft.setPower(fl);
|
|
266
|
+
frontRight.setPower(fr);
|
|
267
|
+
backLeft.setPower(bl);
|
|
268
|
+
backRight.setPower(br);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
\`\`\`
|
|
272
|
+
|
|
273
|
+
### OpMode Annotations
|
|
274
|
+
|
|
275
|
+
\`\`\`java
|
|
276
|
+
// Autonomous OpMode — appears in Autonomous dropdown on Driver Station
|
|
277
|
+
@Autonomous(name = "Blue Left Auto", group = "Competition")
|
|
278
|
+
public class BlueLeftAuto extends LinearOpMode { ... }
|
|
279
|
+
|
|
280
|
+
// TeleOp OpMode — appears in TeleOp dropdown on Driver Station
|
|
281
|
+
@TeleOp(name = "Main TeleOp", group = "Competition")
|
|
282
|
+
public class MainTeleOp extends OpMode { ... }
|
|
283
|
+
|
|
284
|
+
// Disable an OpMode — hides it from the Driver Station without deleting code
|
|
285
|
+
@TeleOp(name = "Old TeleOp", group = "Deprecated")
|
|
286
|
+
@Disabled
|
|
287
|
+
public class OldTeleOp extends OpMode { ... }
|
|
288
|
+
\`\`\`
|
|
289
|
+
|
|
290
|
+
**Annotation Parameters:**
|
|
291
|
+
- \`name\` — Display name on Driver Station (required, should be descriptive)
|
|
292
|
+
- \`group\` — Groups OpModes together in the dropdown (optional but recommended)
|
|
293
|
+
- \`@Disabled\` — Hides the OpMode from the Driver Station list
|
|
294
|
+
|
|
295
|
+
### Choosing Between OpMode Types
|
|
296
|
+
| Criteria | Iterative (OpMode) | Linear (LinearOpMode) |
|
|
297
|
+
|---|---|---|
|
|
298
|
+
| Control flow | Callback-based (FSM) | Sequential (top-to-bottom) |
|
|
299
|
+
| sleep() safe? | NO — blocks the loop | YES — natural flow |
|
|
300
|
+
| TeleOp | Preferred | Works fine |
|
|
301
|
+
| Complex auto | FSM pattern needed | Simpler sequential code |
|
|
302
|
+
| Parallel actions | Natural with states | Requires threads or FSM overlay |
|
|
303
|
+
| Pedro Pathing | Works great with FSM | Works with built-in follower loop |
|
|
304
|
+
`,
|
|
305
|
+
hardwareMap: `
|
|
306
|
+
## hardwareMap Usage
|
|
307
|
+
|
|
308
|
+
The \`hardwareMap\` object is available in all OpModes. It maps configured device names (from the
|
|
309
|
+
Robot Configuration on the Driver Station) to Java hardware objects.
|
|
310
|
+
|
|
311
|
+
### Basic Pattern
|
|
312
|
+
\`\`\`java
|
|
313
|
+
DeviceType variable = hardwareMap.get(DeviceType.class, "configName");
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
The \`"configName"\` MUST match the name in the robot's active configuration on the Driver Station exactly.
|
|
317
|
+
|
|
318
|
+
### All Hardware Types
|
|
319
|
+
|
|
320
|
+
#### Motors
|
|
321
|
+
\`\`\`java
|
|
322
|
+
// Basic DC Motor
|
|
323
|
+
DcMotor motor = hardwareMap.get(DcMotor.class, "motor");
|
|
324
|
+
|
|
325
|
+
// Extended DC Motor (adds velocity control, current monitoring, PID access)
|
|
326
|
+
DcMotorEx motorEx = hardwareMap.get(DcMotorEx.class, "motor");
|
|
327
|
+
|
|
328
|
+
// You can also cast:
|
|
329
|
+
DcMotorEx motorEx = (DcMotorEx) hardwareMap.get(DcMotor.class, "motor");
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
**Motor Configuration:**
|
|
333
|
+
\`\`\`java
|
|
334
|
+
motor.setDirection(DcMotor.Direction.FORWARD); // or REVERSE
|
|
335
|
+
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE); // or FLOAT
|
|
336
|
+
motor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER); // raw power
|
|
337
|
+
motor.setMode(DcMotor.RunMode.RUN_USING_ENCODER); // velocity-regulated
|
|
338
|
+
motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER); // reset encoder count
|
|
339
|
+
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION); // PID to target
|
|
340
|
+
|
|
341
|
+
// RUN_TO_POSITION pattern (order matters!):
|
|
342
|
+
motor.setTargetPosition(1000); // SET TARGET FIRST
|
|
343
|
+
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION); // THEN set mode
|
|
344
|
+
motor.setPower(0.5); // THEN set power
|
|
345
|
+
while (motor.isBusy()) { /* wait */ } // Wait for completion
|
|
346
|
+
motor.setPower(0);
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
#### Servos
|
|
350
|
+
\`\`\`java
|
|
351
|
+
// Standard Servo (position 0.0 to 1.0)
|
|
352
|
+
Servo servo = hardwareMap.get(Servo.class, "servo");
|
|
353
|
+
servo.setPosition(0.5); // move to center
|
|
354
|
+
|
|
355
|
+
// Extended Servo (adds PWM range control)
|
|
356
|
+
ServoImplEx servoEx = hardwareMap.get(ServoImplEx.class, "servo");
|
|
357
|
+
servoEx.setPwmRange(new PwmControl.PwmRange(500, 2500)); // custom PWM range
|
|
358
|
+
servoEx.setPwmDisable(); // disable PWM signal (servo goes limp)
|
|
359
|
+
servoEx.setPwmEnable(); // re-enable PWM signal
|
|
360
|
+
|
|
361
|
+
// Casting pattern:
|
|
362
|
+
ServoImplEx servoEx = (ServoImplEx) hardwareMap.get(Servo.class, "servo");
|
|
363
|
+
|
|
364
|
+
// Continuous Rotation Servo (power -1.0 to 1.0, no position control)
|
|
365
|
+
CRServo crServo = hardwareMap.get(CRServo.class, "crServo");
|
|
366
|
+
crServo.setPower(0.5); // spin at half speed
|
|
367
|
+
crServo.setPower(0); // stop
|
|
368
|
+
\`\`\`
|
|
369
|
+
|
|
370
|
+
#### IMU (Inertial Measurement Unit)
|
|
371
|
+
\`\`\`java
|
|
372
|
+
IMU imu = hardwareMap.get(IMU.class, "imu");
|
|
373
|
+
|
|
374
|
+
// Configure orientation based on how the Control Hub is mounted
|
|
375
|
+
imu.initialize(new IMU.Parameters(new RevHubOrientationOnRobot(
|
|
376
|
+
RevHubOrientationOnRobot.LogoFacingDirection.UP,
|
|
377
|
+
RevHubOrientationOnRobot.UsbFacingDirection.FORWARD
|
|
378
|
+
)));
|
|
379
|
+
|
|
380
|
+
// Reset yaw
|
|
381
|
+
imu.resetYaw();
|
|
382
|
+
|
|
383
|
+
// Read angles (in degrees)
|
|
384
|
+
YawPitchRollAngles angles = imu.getRobotYawPitchRollAngles();
|
|
385
|
+
double heading = angles.getYaw(AngleUnit.DEGREES);
|
|
386
|
+
double pitch = angles.getPitch(AngleUnit.DEGREES);
|
|
387
|
+
double roll = angles.getRoll(AngleUnit.DEGREES);
|
|
388
|
+
|
|
389
|
+
// Read angular velocity
|
|
390
|
+
AngularVelocity angVel = imu.getRobotAngularVelocity(AngleUnit.DEGREES);
|
|
391
|
+
double yawRate = angVel.zRotationRate;
|
|
392
|
+
\`\`\`
|
|
393
|
+
|
|
394
|
+
#### Distance Sensor
|
|
395
|
+
\`\`\`java
|
|
396
|
+
DistanceSensor distanceSensor = hardwareMap.get(DistanceSensor.class, "distance");
|
|
397
|
+
double distCM = distanceSensor.getDistance(DistanceUnit.CM);
|
|
398
|
+
double distIN = distanceSensor.getDistance(DistanceUnit.INCH);
|
|
399
|
+
|
|
400
|
+
// REV Color/Distance sensor also implements DistanceSensor:
|
|
401
|
+
// Configure as "REV Color Sensor V3" in config, access as DistanceSensor
|
|
402
|
+
\`\`\`
|
|
403
|
+
|
|
404
|
+
#### Color Sensor
|
|
405
|
+
\`\`\`java
|
|
406
|
+
NormalizedColorSensor colorSensor = hardwareMap.get(NormalizedColorSensor.class, "color");
|
|
407
|
+
colorSensor.setGain(2.0f); // optional gain adjustment
|
|
408
|
+
|
|
409
|
+
NormalizedRGBA colors = colorSensor.getNormalizedColors();
|
|
410
|
+
float red = colors.red;
|
|
411
|
+
float green = colors.green;
|
|
412
|
+
float blue = colors.blue;
|
|
413
|
+
float alpha = colors.alpha;
|
|
414
|
+
|
|
415
|
+
// REV Color Sensor V3 can also be used as a DistanceSensor (dual interface):
|
|
416
|
+
DistanceSensor distFromColor = (DistanceSensor) hardwareMap.get(NormalizedColorSensor.class, "color");
|
|
417
|
+
// OR
|
|
418
|
+
DistanceSensor distFromColor = hardwareMap.get(DistanceSensor.class, "color");
|
|
419
|
+
\`\`\`
|
|
420
|
+
|
|
421
|
+
#### Touch Sensor
|
|
422
|
+
\`\`\`java
|
|
423
|
+
TouchSensor touchSensor = hardwareMap.get(TouchSensor.class, "touch");
|
|
424
|
+
boolean isPressed = touchSensor.isPressed();
|
|
425
|
+
\`\`\`
|
|
426
|
+
|
|
427
|
+
#### Digital Channel
|
|
428
|
+
\`\`\`java
|
|
429
|
+
DigitalChannel digitalInput = hardwareMap.get(DigitalChannel.class, "digitalInput");
|
|
430
|
+
digitalInput.setMode(DigitalChannel.Mode.INPUT);
|
|
431
|
+
boolean state = digitalInput.getState(); // true = HIGH, false = LOW
|
|
432
|
+
|
|
433
|
+
// Magnetic limit switch example:
|
|
434
|
+
DigitalChannel limitSwitch = hardwareMap.get(DigitalChannel.class, "limitSwitch");
|
|
435
|
+
limitSwitch.setMode(DigitalChannel.Mode.INPUT);
|
|
436
|
+
boolean isTriggered = !limitSwitch.getState(); // Often active-low
|
|
437
|
+
\`\`\`
|
|
438
|
+
|
|
439
|
+
#### Analog Input
|
|
440
|
+
\`\`\`java
|
|
441
|
+
AnalogInput analogSensor = hardwareMap.get(AnalogInput.class, "analog");
|
|
442
|
+
double voltage = analogSensor.getVoltage(); // 0 to 3.3V
|
|
443
|
+
double maxVoltage = analogSensor.getMaxVoltage(); // typically 3.3V
|
|
444
|
+
|
|
445
|
+
// Potentiometer example:
|
|
446
|
+
double position = analogSensor.getVoltage() / analogSensor.getMaxVoltage(); // 0.0 to 1.0
|
|
447
|
+
\`\`\`
|
|
448
|
+
|
|
449
|
+
#### Webcam
|
|
450
|
+
\`\`\`java
|
|
451
|
+
WebcamName webcam = hardwareMap.get(WebcamName.class, "Webcam 1");
|
|
452
|
+
// Used with VisionPortal for AprilTag detection, TFOD, or custom processors
|
|
453
|
+
\`\`\`
|
|
454
|
+
|
|
455
|
+
#### GoBilda Pinpoint Odometry Computer
|
|
456
|
+
\`\`\`java
|
|
457
|
+
GoBildaPinpointDriver pinpoint = hardwareMap.get(GoBildaPinpointDriver.class, "pinpoint");
|
|
458
|
+
|
|
459
|
+
// Configure encoder resolution (ticks per mm)
|
|
460
|
+
pinpoint.setEncoderResolution(GoBildaPinpointDriver.GoBildaOdometryPods.goBILDA_4_BAR_POD);
|
|
461
|
+
// OR for custom pods:
|
|
462
|
+
// pinpoint.setEncoderResolution(13.26291192); // ticks per mm
|
|
463
|
+
|
|
464
|
+
// Set encoder directions
|
|
465
|
+
pinpoint.setEncoderDirections(
|
|
466
|
+
GoBildaPinpointDriver.EncoderDirection.FORWARD,
|
|
467
|
+
GoBildaPinpointDriver.EncoderDirection.FORWARD
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
// Set offsets from robot center (in mm)
|
|
471
|
+
pinpoint.setOffsets(-84.0, -168.0); // x offset, y offset
|
|
472
|
+
|
|
473
|
+
pinpoint.resetPosAndIMU(); // call in init
|
|
474
|
+
|
|
475
|
+
// In loop:
|
|
476
|
+
pinpoint.update(); // MUST call every loop
|
|
477
|
+
Pose2D pose = pinpoint.getPosition();
|
|
478
|
+
double x = pose.getX(DistanceUnit.MM);
|
|
479
|
+
double y = pose.getY(DistanceUnit.MM);
|
|
480
|
+
double heading = pose.getHeading(AngleUnit.RADIANS);
|
|
481
|
+
\`\`\`
|
|
482
|
+
|
|
483
|
+
#### SparkFun Optical Tracking Odometry Sensor (OTOS)
|
|
484
|
+
\`\`\`java
|
|
485
|
+
SparkFunOTOS otos = hardwareMap.get(SparkFunOTOS.class, "otos");
|
|
486
|
+
|
|
487
|
+
otos.setLinearUnit(DistanceUnit.INCH);
|
|
488
|
+
otos.setAngularUnit(AngleUnit.DEGREES);
|
|
489
|
+
|
|
490
|
+
// Offset from robot center
|
|
491
|
+
SparkFunOTOS.Pose2D offset = new SparkFunOTOS.Pose2D(0, 0, 0);
|
|
492
|
+
otos.setOffset(offset);
|
|
493
|
+
|
|
494
|
+
otos.setLinearScalar(1.0);
|
|
495
|
+
otos.setAngularScalar(1.0);
|
|
496
|
+
|
|
497
|
+
otos.calibrateImu();
|
|
498
|
+
otos.resetTracking();
|
|
499
|
+
|
|
500
|
+
// In loop:
|
|
501
|
+
SparkFunOTOS.Pose2D pos = otos.getPosition();
|
|
502
|
+
double x = pos.x;
|
|
503
|
+
double y = pos.y;
|
|
504
|
+
double h = pos.h;
|
|
505
|
+
\`\`\`
|
|
506
|
+
|
|
507
|
+
#### Limelight 3A
|
|
508
|
+
\`\`\`java
|
|
509
|
+
Limelight3A limelight = hardwareMap.get(Limelight3A.class, "limelight");
|
|
510
|
+
limelight.setPollRateHz(100);
|
|
511
|
+
limelight.start();
|
|
512
|
+
limelight.pipelineSwitch(0); // switch to pipeline 0
|
|
513
|
+
|
|
514
|
+
// In loop:
|
|
515
|
+
LLResult result = limelight.getLatestResult();
|
|
516
|
+
if (result != null && result.isValid()) {
|
|
517
|
+
double tx = result.getTx(); // horizontal offset
|
|
518
|
+
double ty = result.getTy(); // vertical offset
|
|
519
|
+
double ta = result.getTa(); // target area
|
|
520
|
+
|
|
521
|
+
// AprilTag results
|
|
522
|
+
List<LLResultTypes.FiducialResult> fiducials = result.getFiducialResults();
|
|
523
|
+
for (LLResultTypes.FiducialResult fr : fiducials) {
|
|
524
|
+
int id = fr.getFiducialId();
|
|
525
|
+
// fr.getRobotPoseFieldSpace() etc.
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
\`\`\`
|
|
529
|
+
|
|
530
|
+
### Bulk Read Optimization (LynxModule)
|
|
531
|
+
\`\`\`java
|
|
532
|
+
// Get all Lynx modules (Control Hub + Expansion Hubs)
|
|
533
|
+
List<LynxModule> allHubs = hardwareMap.getAll(LynxModule.class);
|
|
534
|
+
|
|
535
|
+
// Set bulk caching mode to MANUAL for best performance
|
|
536
|
+
for (LynxModule hub : allHubs) {
|
|
537
|
+
hub.setBulkCachingMode(LynxModule.BulkCachingMode.MANUAL);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// In your loop, clear cache ONCE at the start:
|
|
541
|
+
for (LynxModule hub : allHubs) {
|
|
542
|
+
hub.clearBulkCache();
|
|
543
|
+
}
|
|
544
|
+
// All subsequent hardware reads in this loop iteration use cached values
|
|
545
|
+
// This dramatically reduces I2C/USB traffic and speeds up loop times
|
|
546
|
+
\`\`\`
|
|
547
|
+
|
|
548
|
+
**Bulk Caching Modes:**
|
|
549
|
+
- \`OFF\` — Default. Every read is a separate USB transaction (slow).
|
|
550
|
+
- \`AUTO\` — Caches reads, auto-clears when a new read of same type is requested. Good for simple OpModes.
|
|
551
|
+
- \`MANUAL\` — You must call \`clearBulkCache()\` each loop. Best performance but you manage the cache.
|
|
552
|
+
|
|
553
|
+
### Common Casting Patterns
|
|
554
|
+
\`\`\`java
|
|
555
|
+
// DcMotor → DcMotorEx (adds velocity PID, current reading)
|
|
556
|
+
DcMotorEx motorEx = (DcMotorEx) hardwareMap.get(DcMotor.class, "motor");
|
|
557
|
+
|
|
558
|
+
// Servo → ServoImplEx (adds PWM range control, PWM disable)
|
|
559
|
+
ServoImplEx servoEx = (ServoImplEx) hardwareMap.get(Servo.class, "servo");
|
|
560
|
+
|
|
561
|
+
// ColorSensor → DistanceSensor (REV Color Sensor V3 dual interface)
|
|
562
|
+
DistanceSensor dist = (DistanceSensor) hardwareMap.get(NormalizedColorSensor.class, "color");
|
|
563
|
+
|
|
564
|
+
// These all work because the underlying hardware implementations implement multiple interfaces
|
|
565
|
+
\`\`\`
|
|
566
|
+
`,
|
|
567
|
+
gamepadApi: `
|
|
568
|
+
## Gamepad API
|
|
569
|
+
|
|
570
|
+
FTC provides two gamepads: \`gamepad1\` (primary driver) and \`gamepad2\` (secondary/operator).
|
|
571
|
+
Both are available as fields in any OpMode.
|
|
572
|
+
|
|
573
|
+
### Joysticks (Analog Sticks)
|
|
574
|
+
All stick values range from **-1.0 to 1.0**.
|
|
575
|
+
|
|
576
|
+
\`\`\`java
|
|
577
|
+
gamepad1.left_stick_x // Left stick horizontal: -1.0 (left) to 1.0 (right)
|
|
578
|
+
gamepad1.left_stick_y // Left stick vertical: -1.0 (UP) to 1.0 (DOWN) ← INVERTED!
|
|
579
|
+
gamepad1.right_stick_x // Right stick horizontal: -1.0 (left) to 1.0 (right)
|
|
580
|
+
gamepad1.right_stick_y // Right stick vertical: -1.0 (UP) to 1.0 (DOWN) ← INVERTED!
|
|
581
|
+
\`\`\`
|
|
582
|
+
|
|
583
|
+
**CRITICAL: Y-axis is inverted!** Pushing the stick FORWARD (up) gives a NEGATIVE value.
|
|
584
|
+
This means for driving forward, you almost always negate the Y value:
|
|
585
|
+
\`\`\`java
|
|
586
|
+
double drive = -gamepad1.left_stick_y; // Now forward = positive
|
|
587
|
+
\`\`\`
|
|
588
|
+
|
|
589
|
+
### Buttons (Digital — boolean)
|
|
590
|
+
\`\`\`java
|
|
591
|
+
gamepad1.a // A button (Xbox) / Cross (PS)
|
|
592
|
+
gamepad1.b // B button (Xbox) / Circle (PS)
|
|
593
|
+
gamepad1.x // X button (Xbox) / Square (PS)
|
|
594
|
+
gamepad1.y // Y button (Xbox) / Triangle (PS)
|
|
595
|
+
|
|
596
|
+
gamepad1.dpad_up // D-Pad up
|
|
597
|
+
gamepad1.dpad_down // D-Pad down
|
|
598
|
+
gamepad1.dpad_left // D-Pad left
|
|
599
|
+
gamepad1.dpad_right // D-Pad right
|
|
600
|
+
|
|
601
|
+
gamepad1.left_bumper // Left bumper (LB)
|
|
602
|
+
gamepad1.right_bumper // Right bumper (RB)
|
|
603
|
+
|
|
604
|
+
gamepad1.left_stick_button // Left stick click (L3)
|
|
605
|
+
gamepad1.right_stick_button // Right stick click (R3)
|
|
606
|
+
|
|
607
|
+
gamepad1.back // Back/Select button
|
|
608
|
+
gamepad1.guide // Guide/Home button (may not work on all controllers)
|
|
609
|
+
gamepad1.start // Start button
|
|
610
|
+
\`\`\`
|
|
611
|
+
|
|
612
|
+
### Triggers (Analog — float)
|
|
613
|
+
Trigger values range from **0.0 (released) to 1.0 (fully pressed)**.
|
|
614
|
+
|
|
615
|
+
\`\`\`java
|
|
616
|
+
gamepad1.left_trigger // Left trigger: 0.0 to 1.0
|
|
617
|
+
gamepad1.right_trigger // Right trigger: 0.0 to 1.0
|
|
618
|
+
\`\`\`
|
|
619
|
+
|
|
620
|
+
### Gamepad1 vs Gamepad2
|
|
621
|
+
- \`gamepad1\` — Connected to the FIRST gamepad slot on the Driver Station. Typically the **driver** (drivetrain control).
|
|
622
|
+
- \`gamepad2\` — Connected to the SECOND slot. Typically the **operator** (mechanisms: arm, claw, intake, etc.).
|
|
623
|
+
- Both gamepads have identical APIs.
|
|
624
|
+
- During competition, gamepads are assigned at the Driver Station.
|
|
625
|
+
|
|
626
|
+
### Common Drive Patterns
|
|
627
|
+
|
|
628
|
+
**Mecanum Drive (most common FTC drivetrain):**
|
|
629
|
+
\`\`\`java
|
|
630
|
+
double drive = -gamepad1.left_stick_y; // Forward/backward (negated!)
|
|
631
|
+
double strafe = gamepad1.left_stick_x; // Left/right strafe
|
|
632
|
+
double rotate = gamepad1.right_stick_x; // Rotation
|
|
633
|
+
|
|
634
|
+
double fl = drive + strafe + rotate;
|
|
635
|
+
double fr = drive - strafe - rotate;
|
|
636
|
+
double bl = drive - strafe + rotate;
|
|
637
|
+
double br = drive + strafe - rotate;
|
|
638
|
+
|
|
639
|
+
// Normalize so no motor exceeds 1.0
|
|
640
|
+
double max = Math.max(1.0, Math.max(Math.max(Math.abs(fl), Math.abs(fr)),
|
|
641
|
+
Math.max(Math.abs(bl), Math.abs(br))));
|
|
642
|
+
frontLeft.setPower(fl / max);
|
|
643
|
+
frontRight.setPower(fr / max);
|
|
644
|
+
backLeft.setPower(bl / max);
|
|
645
|
+
backRight.setPower(br / max);
|
|
646
|
+
\`\`\`
|
|
647
|
+
|
|
648
|
+
**Tank Drive:**
|
|
649
|
+
\`\`\`java
|
|
650
|
+
double leftPower = -gamepad1.left_stick_y;
|
|
651
|
+
double rightPower = -gamepad1.right_stick_y;
|
|
652
|
+
leftMotor.setPower(leftPower);
|
|
653
|
+
rightMotor.setPower(rightPower);
|
|
654
|
+
\`\`\`
|
|
655
|
+
|
|
656
|
+
**Arcade Drive:**
|
|
657
|
+
\`\`\`java
|
|
658
|
+
double drive = -gamepad1.left_stick_y;
|
|
659
|
+
double rotate = gamepad1.right_stick_x;
|
|
660
|
+
leftMotor.setPower(drive + rotate);
|
|
661
|
+
rightMotor.setPower(drive - rotate);
|
|
662
|
+
\`\`\`
|
|
663
|
+
|
|
664
|
+
### Button Edge Detection (Toggle Pattern)
|
|
665
|
+
Gamepads report CURRENT state each loop. For toggles, detect the rising edge:
|
|
666
|
+
|
|
667
|
+
\`\`\`java
|
|
668
|
+
// Fields
|
|
669
|
+
private boolean previousA = false;
|
|
670
|
+
private boolean toggleState = false;
|
|
671
|
+
|
|
672
|
+
// In loop():
|
|
673
|
+
boolean currentA = gamepad1.a;
|
|
674
|
+
if (currentA && !previousA) {
|
|
675
|
+
// Rising edge — button was just pressed
|
|
676
|
+
toggleState = !toggleState;
|
|
677
|
+
}
|
|
678
|
+
previousA = currentA;
|
|
679
|
+
\`\`\`
|
|
680
|
+
|
|
681
|
+
### Gamepad Deadzone
|
|
682
|
+
Sticks may not return exactly 0 when released. Apply a deadzone:
|
|
683
|
+
|
|
684
|
+
\`\`\`java
|
|
685
|
+
private double applyDeadzone(double value, double deadzone) {
|
|
686
|
+
return Math.abs(value) < deadzone ? 0.0 : value;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Usage:
|
|
690
|
+
double drive = applyDeadzone(-gamepad1.left_stick_y, 0.05);
|
|
691
|
+
\`\`\`
|
|
692
|
+
|
|
693
|
+
### Trigger as Button
|
|
694
|
+
\`\`\`java
|
|
695
|
+
boolean leftTriggerPressed = gamepad1.left_trigger > 0.5;
|
|
696
|
+
\`\`\`
|
|
697
|
+
`,
|
|
698
|
+
bestPractices: `
|
|
699
|
+
## Best Practices for FTC Coding
|
|
700
|
+
|
|
701
|
+
### Package Structure
|
|
702
|
+
Organize your TeamCode directory with sub-packages:
|
|
703
|
+
|
|
704
|
+
\`\`\`
|
|
705
|
+
org.firstinspires.ftc.teamcode/
|
|
706
|
+
├── opmodes/ # All OpModes (TeleOp, Autonomous)
|
|
707
|
+
│ ├── auto/ # Autonomous OpModes
|
|
708
|
+
│ └── teleop/ # TeleOp OpModes
|
|
709
|
+
├── subsystems/ # Hardware subsystems (Drivetrain, Arm, Intake, etc.)
|
|
710
|
+
├── util/ # Utility classes (PID controllers, math helpers, etc.)
|
|
711
|
+
└── pathing/ # Path following code, trajectories, waypoints
|
|
712
|
+
\`\`\`
|
|
713
|
+
|
|
714
|
+
### Naming Conventions
|
|
715
|
+
- **UPPER_SNAKE_CASE** — For \`@Config\` (FTC Dashboard) static fields:
|
|
716
|
+
\`\`\`java
|
|
717
|
+
@Config
|
|
718
|
+
public class ArmConstants {
|
|
719
|
+
public static double ARM_KP = 0.01;
|
|
720
|
+
public static double ARM_KI = 0.0;
|
|
721
|
+
public static double ARM_KD = 0.001;
|
|
722
|
+
public static int ARM_TARGET_HIGH = 2500;
|
|
723
|
+
}
|
|
724
|
+
\`\`\`
|
|
725
|
+
- **camelCase** — For methods and local/instance variables:
|
|
726
|
+
\`\`\`java
|
|
727
|
+
public void setArmPosition(int targetTicks) { ... }
|
|
728
|
+
private double currentPower = 0.0;
|
|
729
|
+
\`\`\`
|
|
730
|
+
- **PascalCase** — For class names:
|
|
731
|
+
\`\`\`java
|
|
732
|
+
public class MecanumDrivetrain { ... }
|
|
733
|
+
public class BlueLeftAuto extends LinearOpMode { ... }
|
|
734
|
+
\`\`\`
|
|
735
|
+
- **Config names** — Use camelCase in robot configuration:
|
|
736
|
+
\`\`\`
|
|
737
|
+
frontLeft, frontRight, backLeft, backRight, armMotor, clawServo
|
|
738
|
+
\`\`\`
|
|
739
|
+
|
|
740
|
+
### Common Mistakes and How to Avoid Them
|
|
741
|
+
|
|
742
|
+
#### 1. Forgetting follower.update() (Pedro Pathing)
|
|
743
|
+
\`\`\`java
|
|
744
|
+
// WRONG — robot won't move, path won't execute
|
|
745
|
+
follower.followPath(path);
|
|
746
|
+
// ... nothing happens
|
|
747
|
+
|
|
748
|
+
// CORRECT — must call update() every loop iteration
|
|
749
|
+
@Override
|
|
750
|
+
public void loop() {
|
|
751
|
+
follower.update(); // CRITICAL: call every loop!
|
|
752
|
+
// rest of your code
|
|
753
|
+
}
|
|
754
|
+
\`\`\`
|
|
755
|
+
|
|
756
|
+
#### 2. Caching @Config Values at Init Time
|
|
757
|
+
\`\`\`java
|
|
758
|
+
// WRONG — value is captured at init, Dashboard changes won't take effect
|
|
759
|
+
private double kP = ArmConstants.ARM_KP;
|
|
760
|
+
|
|
761
|
+
public void loop() {
|
|
762
|
+
double output = kP * error; // Always uses initial value
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// CORRECT — read the static field each loop for live tuning
|
|
766
|
+
public void loop() {
|
|
767
|
+
double output = ArmConstants.ARM_KP * error; // Picks up Dashboard changes
|
|
768
|
+
}
|
|
769
|
+
\`\`\`
|
|
770
|
+
|
|
771
|
+
#### 3. Using Thread.sleep() in Iterative OpMode
|
|
772
|
+
\`\`\`java
|
|
773
|
+
// WRONG — blocks the entire loop, stops ALL updates including telemetry
|
|
774
|
+
@Override
|
|
775
|
+
public void loop() {
|
|
776
|
+
motor.setPower(1.0);
|
|
777
|
+
Thread.sleep(1000); // NEVER DO THIS in OpMode.loop()!
|
|
778
|
+
motor.setPower(0.0);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// CORRECT — use a timer / state machine
|
|
782
|
+
private ElapsedTime timer = new ElapsedTime();
|
|
783
|
+
private enum State { DRIVING, STOPPED }
|
|
784
|
+
private State state = State.DRIVING;
|
|
785
|
+
|
|
786
|
+
@Override
|
|
787
|
+
public void start() {
|
|
788
|
+
timer.reset();
|
|
789
|
+
state = State.DRIVING;
|
|
790
|
+
motor.setPower(1.0);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
@Override
|
|
794
|
+
public void loop() {
|
|
795
|
+
switch (state) {
|
|
796
|
+
case DRIVING:
|
|
797
|
+
if (timer.seconds() > 1.0) {
|
|
798
|
+
motor.setPower(0.0);
|
|
799
|
+
state = State.STOPPED;
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
case STOPPED:
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
\`\`\`
|
|
807
|
+
|
|
808
|
+
#### 4. RUN_TO_POSITION Without setTargetPosition First
|
|
809
|
+
\`\`\`java
|
|
810
|
+
// WRONG — may run to position 0 or cause errors
|
|
811
|
+
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
|
|
812
|
+
motor.setTargetPosition(1000);
|
|
813
|
+
motor.setPower(0.5);
|
|
814
|
+
|
|
815
|
+
// CORRECT — set target BEFORE setting mode
|
|
816
|
+
motor.setTargetPosition(1000); // 1. Set target
|
|
817
|
+
motor.setMode(DcMotor.RunMode.RUN_TO_POSITION); // 2. Set mode
|
|
818
|
+
motor.setPower(0.5); // 3. Set power
|
|
819
|
+
\`\`\`
|
|
820
|
+
|
|
821
|
+
#### 5. Not Negating Gamepad Y Stick
|
|
822
|
+
\`\`\`java
|
|
823
|
+
// WRONG — forward on stick = robot goes backward
|
|
824
|
+
double drive = gamepad1.left_stick_y;
|
|
825
|
+
|
|
826
|
+
// CORRECT — negate Y so forward stick = positive value = forward motion
|
|
827
|
+
double drive = -gamepad1.left_stick_y;
|
|
828
|
+
\`\`\`
|
|
829
|
+
|
|
830
|
+
#### 6. Not Checking opModeIsActive() in LinearOpMode Loops
|
|
831
|
+
\`\`\`java
|
|
832
|
+
// WRONG — loop continues even after STOP is pressed
|
|
833
|
+
while (motor.isBusy()) {
|
|
834
|
+
// keeps running forever if you hit STOP
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// CORRECT — always check opModeIsActive()
|
|
838
|
+
while (opModeIsActive() && motor.isBusy()) {
|
|
839
|
+
idle();
|
|
840
|
+
}
|
|
841
|
+
\`\`\`
|
|
842
|
+
|
|
843
|
+
#### 7. Forgetting Direction Reversal on One Side
|
|
844
|
+
\`\`\`java
|
|
845
|
+
// WRONG — one side of drivetrain runs backward
|
|
846
|
+
frontLeft.setPower(1.0);
|
|
847
|
+
frontRight.setPower(1.0); // Robot turns instead of going straight
|
|
848
|
+
|
|
849
|
+
// CORRECT — reverse one side (typically left for most configurations)
|
|
850
|
+
frontLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
851
|
+
backLeft.setDirection(DcMotor.Direction.REVERSE);
|
|
852
|
+
\`\`\`
|
|
853
|
+
|
|
854
|
+
### Competition-Specific Rules
|
|
855
|
+
|
|
856
|
+
#### RS09 — No FTC Dashboard in Competition
|
|
857
|
+
- FTC Dashboard (the web-based tuning interface) is NOT allowed during competition matches.
|
|
858
|
+
- You MUST NOT have Dashboard actively serving during competition.
|
|
859
|
+
- However, you CAN keep the Dashboard library in your code — just don't access the web UI during matches.
|
|
860
|
+
- Dashboard is only for practice/tuning sessions.
|
|
861
|
+
|
|
862
|
+
#### Disable Wi-Fi Direct on Expansion Hub
|
|
863
|
+
- If using an Expansion Hub connected to a Control Hub, Wi-Fi Direct on the Expansion Hub MUST be disabled.
|
|
864
|
+
- The Expansion Hub connects via RS-485, not Wi-Fi.
|
|
865
|
+
- Having Wi-Fi Direct enabled on the Expansion Hub can cause interference and connectivity issues.
|
|
866
|
+
- Disable it via the REV Hardware Client or the Robot Controller settings.
|
|
867
|
+
|
|
868
|
+
### General Best Practices
|
|
869
|
+
- **Use Bulk Reads**: Always enable LynxModule bulk caching (MANUAL mode) for faster loop times.
|
|
870
|
+
- **Keep loop() fast**: Target <20ms per loop. No blocking calls, no heavy computation.
|
|
871
|
+
- **Use subsystem classes**: Encapsulate hardware in subsystem classes, not raw in OpModes.
|
|
872
|
+
- **ElapsedTime for timing**: Use \`ElapsedTime\` instead of \`System.currentTimeMillis()\`.
|
|
873
|
+
- **Telemetry for debugging**: Use \`telemetry.addData()\` liberally during development.
|
|
874
|
+
- **Zero power on stop**: Always set motors to 0 power in \`stop()\` or end of \`runOpMode()\`.
|
|
875
|
+
- **Brake mode for arms/lifts**: Use \`BRAKE\` zero power behavior for mechanisms that fight gravity.
|
|
876
|
+
- **FLOAT mode for drivetrain**: Sometimes preferred for driver feel during TeleOp.
|
|
877
|
+
- **Version control**: Use Git. Commit working code before making changes.
|
|
878
|
+
- **Comment your code**: Future you (and your teammates) will thank you.
|
|
879
|
+
`,
|
|
880
|
+
devEnvironment: `
|
|
881
|
+
## Development Environment Setup
|
|
882
|
+
|
|
883
|
+
Android Studio is the traditional IDE for FTC, but it is NOT required. The FTC SDK includes a
|
|
884
|
+
Gradle wrapper (\`gradlew\`/\`gradlew.bat\`) that handles the entire build process. Any editor or IDE
|
|
885
|
+
that supports Java and Gradle can be used — VS Code, IntelliJ IDEA, or even a plain text editor
|
|
886
|
+
with command-line builds.
|
|
887
|
+
|
|
888
|
+
### Prerequisites (All IDEs)
|
|
889
|
+
|
|
890
|
+
#### 1. JDK 17 (Required)
|
|
891
|
+
The FTC SDK build requires JDK 17. Install it from one of these sources:
|
|
892
|
+
- **Adoptium (recommended)**: https://adoptium.net/ — select JDK 17 LTS
|
|
893
|
+
- **Oracle JDK 17**: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html
|
|
894
|
+
- **macOS (Homebrew)**: \`brew install openjdk@17\`
|
|
895
|
+
- **Linux (apt)**: \`sudo apt install openjdk-17-jdk\`
|
|
896
|
+
- **Windows (winget)**: \`winget install EclipseAdoptium.Temurin.17.JDK\`
|
|
897
|
+
|
|
898
|
+
Verify installation:
|
|
899
|
+
\`\`\`bash
|
|
900
|
+
java -version
|
|
901
|
+
# Should show: openjdk version "17.x.x" or similar
|
|
902
|
+
\`\`\`
|
|
903
|
+
|
|
904
|
+
**IMPORTANT**: Do NOT rely on the JDK bundled with Android Studio Ladybug. Ladybug ships with
|
|
905
|
+
JDK 21 which is incompatible with the FTC SDK's Gradle configuration. Always install JDK 17
|
|
906
|
+
separately and point your IDE/build to it.
|
|
907
|
+
|
|
908
|
+
#### 2. Android SDK Command-Line Tools
|
|
909
|
+
You need the Android SDK for compiling Android APKs. There are two ways to get it:
|
|
910
|
+
|
|
911
|
+
**Option A: Install via Android Studio (easiest)**
|
|
912
|
+
- Install Android Studio, which bundles the full SDK
|
|
913
|
+
- SDK is typically installed at:
|
|
914
|
+
- **Windows**: \`C:\\Users\\<username>\\AppData\\Local\\Android\\Sdk\`
|
|
915
|
+
- **macOS**: \`~/Library/Android/sdk\`
|
|
916
|
+
- **Linux**: \`~/Android/Sdk\`
|
|
917
|
+
|
|
918
|
+
**Option B: Command-line only (no Android Studio)**
|
|
919
|
+
1. Download "Command line tools only" from https://developer.android.com/studio#command-line-tools-only
|
|
920
|
+
2. Unzip to a directory (e.g., \`~/android-sdk/cmdline-tools/latest/\`)
|
|
921
|
+
3. Install required SDK components:
|
|
922
|
+
\`\`\`bash
|
|
923
|
+
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
|
924
|
+
\`\`\`
|
|
925
|
+
|
|
926
|
+
#### 3. ANDROID_HOME Environment Variable
|
|
927
|
+
Set this to point to your Android SDK location:
|
|
928
|
+
|
|
929
|
+
**macOS/Linux** — add to \`~/.bashrc\`, \`~/.zshrc\`, or \`~/.profile\`:
|
|
930
|
+
\`\`\`bash
|
|
931
|
+
export ANDROID_HOME=~/Library/Android/sdk # macOS (Android Studio default)
|
|
932
|
+
# export ANDROID_HOME=~/Android/Sdk # Linux (Android Studio default)
|
|
933
|
+
# export ANDROID_HOME=~/android-sdk # manual install
|
|
934
|
+
export PATH="$ANDROID_HOME/platform-tools:$PATH" # adds adb to PATH
|
|
935
|
+
\`\`\`
|
|
936
|
+
|
|
937
|
+
**Windows** — System Environment Variables:
|
|
938
|
+
\`\`\`
|
|
939
|
+
ANDROID_HOME = C:\\Users\\<username>\\AppData\\Local\\Android\\Sdk
|
|
940
|
+
PATH += %ANDROID_HOME%\\platform-tools
|
|
941
|
+
\`\`\`
|
|
942
|
+
|
|
943
|
+
Verify:
|
|
944
|
+
\`\`\`bash
|
|
945
|
+
adb version
|
|
946
|
+
# Should show: Android Debug Bridge version x.x.x
|
|
947
|
+
\`\`\`
|
|
948
|
+
|
|
949
|
+
#### 4. ADB (Android Debug Bridge)
|
|
950
|
+
ADB is included in the Android SDK's \`platform-tools/\` directory. Once \`ANDROID_HOME\` is set
|
|
951
|
+
and \`platform-tools\` is in your PATH, \`adb\` should work from any terminal.
|
|
952
|
+
|
|
953
|
+
---
|
|
954
|
+
|
|
955
|
+
### VS Code Setup (Recommended Alternative to Android Studio)
|
|
956
|
+
|
|
957
|
+
VS Code is a fully viable IDE for FTC development. The Gradle wrapper handles all compilation
|
|
958
|
+
and deployment — VS Code just needs Java language support for code intelligence.
|
|
959
|
+
|
|
960
|
+
#### Required Extensions
|
|
961
|
+
1. **Extension Pack for Java** (\`vscjava.vscode-java-pack\`)
|
|
962
|
+
- Includes: Language Support for Java, Debugger for Java, Maven for Java, Test Runner
|
|
963
|
+
- Provides: autocomplete, go-to-definition, error highlighting, refactoring
|
|
964
|
+
2. **Gradle for Java** (\`vscjava.vscode-gradle\`)
|
|
965
|
+
- Provides: Gradle task runner sidebar, dependency management, sync
|
|
966
|
+
|
|
967
|
+
Install from the VS Code Extensions panel or via command line:
|
|
968
|
+
\`\`\`bash
|
|
969
|
+
code --install-extension vscjava.vscode-java-pack
|
|
970
|
+
code --install-extension vscjava.vscode-gradle
|
|
971
|
+
\`\`\`
|
|
972
|
+
|
|
973
|
+
#### VS Code Settings for FTC
|
|
974
|
+
Add these to your workspace \`.vscode/settings.json\`:
|
|
975
|
+
\`\`\`json
|
|
976
|
+
{
|
|
977
|
+
"java.configuration.runtimes": [
|
|
978
|
+
{
|
|
979
|
+
"name": "JavaSE-17",
|
|
980
|
+
"path": "/path/to/jdk-17",
|
|
981
|
+
"default": true
|
|
982
|
+
}
|
|
983
|
+
],
|
|
984
|
+
"java.jdt.ls.java.home": "/path/to/jdk-17",
|
|
985
|
+
"java.import.gradle.java.home": "/path/to/jdk-17",
|
|
986
|
+
"java.compile.nullAnalysis.mode": "disabled"
|
|
987
|
+
}
|
|
988
|
+
\`\`\`
|
|
989
|
+
|
|
990
|
+
**Find your JDK 17 path:**
|
|
991
|
+
\`\`\`bash
|
|
992
|
+
# macOS (Homebrew)
|
|
993
|
+
/usr/libexec/java_home -v 17
|
|
994
|
+
|
|
995
|
+
# macOS (Adoptium)
|
|
996
|
+
/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
|
|
997
|
+
|
|
998
|
+
# Linux (apt)
|
|
999
|
+
/usr/lib/jvm/java-17-openjdk-amd64
|
|
1000
|
+
|
|
1001
|
+
# Windows (typical)
|
|
1002
|
+
C:\\Program Files\\Eclipse Adoptium\\jdk-17.x.x-hotspot
|
|
1003
|
+
\`\`\`
|
|
1004
|
+
|
|
1005
|
+
#### Opening the FTC Project in VS Code
|
|
1006
|
+
1. Clone the FTC Robot Controller: \`git clone https://github.com/FIRST-Tech-Challenge/FtcRobotController.git\`
|
|
1007
|
+
2. Open the folder in VS Code: \`code FtcRobotController\`
|
|
1008
|
+
3. VS Code will detect the Gradle project automatically
|
|
1009
|
+
4. When prompted "Java projects found", click **Yes** to import
|
|
1010
|
+
5. Wait for Gradle sync to complete (first time takes 1-3 minutes)
|
|
1011
|
+
6. The Java extension will provide full autocomplete, error checking, and navigation
|
|
1012
|
+
|
|
1013
|
+
#### VS Code Build & Deploy
|
|
1014
|
+
Use the integrated terminal:
|
|
1015
|
+
\`\`\`bash
|
|
1016
|
+
# Build the APK
|
|
1017
|
+
./gradlew assembleDebug
|
|
1018
|
+
|
|
1019
|
+
# Deploy to connected device (USB or wireless ADB)
|
|
1020
|
+
./gradlew installDebug
|
|
1021
|
+
|
|
1022
|
+
# Clean and rebuild
|
|
1023
|
+
./gradlew clean assembleDebug
|
|
1024
|
+
\`\`\`
|
|
1025
|
+
|
|
1026
|
+
Or use the Gradle sidebar panel to run tasks visually.
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
### Android Studio Setup
|
|
1031
|
+
|
|
1032
|
+
Android Studio is the "official" IDE but is NOT required. If you choose to use it:
|
|
1033
|
+
|
|
1034
|
+
#### Installation
|
|
1035
|
+
1. Download from https://developer.android.com/studio
|
|
1036
|
+
2. Install — the installer includes the Android SDK and ADB
|
|
1037
|
+
3. Open Android Studio, go through the setup wizard
|
|
1038
|
+
4. Open the FTC project: **File → Open** → select the FtcRobotController folder
|
|
1039
|
+
|
|
1040
|
+
#### CRITICAL: Android Studio Ladybug JDK Issue
|
|
1041
|
+
Android Studio Ladybug (2024.2+) bundles JDK 21, which is **incompatible** with the FTC SDK's
|
|
1042
|
+
Gradle configuration. You MUST configure Android Studio to use JDK 17:
|
|
1043
|
+
|
|
1044
|
+
1. Install JDK 17 separately (see Prerequisites above)
|
|
1045
|
+
2. In Android Studio: **File → Settings → Build, Execution, Deployment → Build Tools → Gradle**
|
|
1046
|
+
3. Set **Gradle JDK** to your JDK 17 installation
|
|
1047
|
+
4. Click **Apply** and re-sync
|
|
1048
|
+
|
|
1049
|
+
#### Do NOT Upgrade Gradle
|
|
1050
|
+
When Android Studio prompts you to "upgrade the Gradle plugin" or "update the Gradle wrapper",
|
|
1051
|
+
**click NO / dismiss the notification**. The FTC SDK uses specific Gradle and AGP versions that
|
|
1052
|
+
are tested together. Upgrading can break the build.
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
### IntelliJ IDEA Setup
|
|
1057
|
+
|
|
1058
|
+
IntelliJ IDEA Community Edition (free) also works for FTC:
|
|
1059
|
+
|
|
1060
|
+
1. Download from https://www.jetbrains.com/idea/download/ — select **Community** (free)
|
|
1061
|
+
2. Open the FtcRobotController folder as a Gradle project
|
|
1062
|
+
3. IntelliJ will auto-detect Gradle and import the project
|
|
1063
|
+
4. Set the Gradle JDK to JDK 17: **File → Settings → Build → Gradle → Gradle JDK**
|
|
1064
|
+
5. Build/deploy from the terminal: \`./gradlew assembleDebug\` / \`./gradlew installDebug\`
|
|
1065
|
+
|
|
1066
|
+
IntelliJ provides similar Java intelligence to Android Studio (they share the same base platform)
|
|
1067
|
+
but without Android-specific UI designers (which FTC doesn't use anyway).
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1071
|
+
### Summary: IDE Comparison for FTC
|
|
1072
|
+
|
|
1073
|
+
| Feature | VS Code | Android Studio | IntelliJ IDEA |
|
|
1074
|
+
|---|---|---|---|
|
|
1075
|
+
| Java autocomplete | Yes (via extension) | Yes (built-in) | Yes (built-in) |
|
|
1076
|
+
| Gradle build | Yes (terminal + extension) | Yes (built-in) | Yes (built-in) |
|
|
1077
|
+
| ADB deploy | Yes (terminal) | Yes (Run button) | Yes (terminal) |
|
|
1078
|
+
| Lightweight | Very lightweight | Heavy (2-4 GB RAM) | Moderate |
|
|
1079
|
+
| Setup complexity | Easy | Easy but JDK issue | Easy |
|
|
1080
|
+
| Android-specific features | No (not needed for FTC) | Yes (not needed) | No |
|
|
1081
|
+
| Cost | Free | Free | Free (Community) |
|
|
1082
|
+
| Recommended for | Teams wanting speed + simplicity | Teams wanting traditional setup | Teams familiar with JetBrains |
|
|
1083
|
+
|
|
1084
|
+
**Bottom line:** All three work. VS Code + Gradle wrapper is the lightest-weight option and avoids
|
|
1085
|
+
the Android Studio Ladybug JDK compatibility issue entirely. Android Studio is familiar to most
|
|
1086
|
+
FTC teams but requires the JDK 17 workaround. Use whatever your team is most comfortable with.
|
|
1087
|
+
`
|
|
1088
|
+
};
|