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,843 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DASHBOARD_KNOWLEDGE = void 0;
|
|
4
|
+
exports.DASHBOARD_KNOWLEDGE = {
|
|
5
|
+
configPattern: `
|
|
6
|
+
## @Config Annotation Pattern (FTC Dashboard)
|
|
7
|
+
|
|
8
|
+
### Import
|
|
9
|
+
\`\`\`java
|
|
10
|
+
import com.acmerobotics.dashboard.config.Config;
|
|
11
|
+
\`\`\`
|
|
12
|
+
|
|
13
|
+
### Requirements
|
|
14
|
+
- The class MUST be annotated with \`@Config\`
|
|
15
|
+
- Fields MUST be \`public static\`
|
|
16
|
+
- Fields must NOT be \`final\`
|
|
17
|
+
|
|
18
|
+
### Basic Usage
|
|
19
|
+
\`\`\`java
|
|
20
|
+
@Config
|
|
21
|
+
public class RobotConstants {
|
|
22
|
+
public static double DRIVE_SPEED = 0.8;
|
|
23
|
+
public static int ENCODER_TICKS = 1120;
|
|
24
|
+
public static boolean USE_GYRO = true;
|
|
25
|
+
}
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
### Custom Dashboard Group Name
|
|
29
|
+
\`\`\`java
|
|
30
|
+
@Config("Arm Settings")
|
|
31
|
+
public class ArmConstants {
|
|
32
|
+
public static double ARM_POWER = 0.5;
|
|
33
|
+
public static int ARM_TARGET = 300;
|
|
34
|
+
}
|
|
35
|
+
\`\`\`
|
|
36
|
+
By default the group name is the class simple name. \`@Config("CustomName")\` overrides this.
|
|
37
|
+
|
|
38
|
+
### Disabling a Config Class
|
|
39
|
+
Add \`@Disabled\` to prevent it from appearing on the dashboard:
|
|
40
|
+
\`\`\`java
|
|
41
|
+
import com.qualcomm.robotcore.eventloop.opmode.Disabled;
|
|
42
|
+
|
|
43
|
+
@Config
|
|
44
|
+
@Disabled
|
|
45
|
+
public class DeprecatedConstants {
|
|
46
|
+
public static double OLD_VALUE = 1.0;
|
|
47
|
+
}
|
|
48
|
+
\`\`\`
|
|
49
|
+
|
|
50
|
+
### Supported Types
|
|
51
|
+
- Primitives: \`boolean\`, \`int\`, \`long\`, \`float\`, \`double\`
|
|
52
|
+
- \`String\`
|
|
53
|
+
- \`enum\` types
|
|
54
|
+
- Custom objects (recursively expands all \`public\` non-\`static\` non-\`final\` instance fields)
|
|
55
|
+
- Arrays of supported types
|
|
56
|
+
|
|
57
|
+
### Custom Object Example
|
|
58
|
+
\`\`\`java
|
|
59
|
+
@Config
|
|
60
|
+
public class PIDConstants {
|
|
61
|
+
public static class PIDCoefficients {
|
|
62
|
+
public double kP;
|
|
63
|
+
public double kI;
|
|
64
|
+
public double kD;
|
|
65
|
+
|
|
66
|
+
public PIDCoefficients(double kP, double kI, double kD) {
|
|
67
|
+
this.kP = kP;
|
|
68
|
+
this.kI = kI;
|
|
69
|
+
this.kD = kD;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static PIDCoefficients DRIVE_PID = new PIDCoefficients(0.05, 0.0, 0.01);
|
|
74
|
+
public static PIDCoefficients ARM_PID = new PIDCoefficients(0.1, 0.0, 0.02);
|
|
75
|
+
}
|
|
76
|
+
\`\`\`
|
|
77
|
+
On the dashboard, \`DRIVE_PID\` expands into editable fields for \`kP\`, \`kI\`, and \`kD\`.
|
|
78
|
+
|
|
79
|
+
### Kotlin Usage
|
|
80
|
+
In Kotlin, fields must use \`@JvmField var\` because the dashboard reads/writes fields directly via reflection — Kotlin properties with getters/setters are not supported.
|
|
81
|
+
\`\`\`kotlin
|
|
82
|
+
@Config
|
|
83
|
+
object RobotConstants {
|
|
84
|
+
@JvmField var DRIVE_SPEED = 0.8
|
|
85
|
+
@JvmField var USE_GYRO = true
|
|
86
|
+
@JvmField var ENCODER_TICKS = 1120
|
|
87
|
+
}
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
### Thread Safety
|
|
91
|
+
For \`long\` and \`double\` fields that are read in a loop thread and written from the dashboard, mark them \`volatile\` to guarantee visibility across threads:
|
|
92
|
+
\`\`\`java
|
|
93
|
+
@Config
|
|
94
|
+
public class TuningConstants {
|
|
95
|
+
public static volatile double HEADING_KP = 1.0;
|
|
96
|
+
public static volatile long DELAY_MS = 250;
|
|
97
|
+
public static int TICKS = 100; // int/boolean are already atomic, volatile optional
|
|
98
|
+
}
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
### CRITICAL: Copy Semantics Pitfall
|
|
102
|
+
Dashboard modifies the static field at runtime. If you copy the value into a local variable or constructor parameter, you will never see updates.
|
|
103
|
+
|
|
104
|
+
**WRONG** — caching the value into a constructor parameter:
|
|
105
|
+
\`\`\`java
|
|
106
|
+
@Config
|
|
107
|
+
public class DriveConstants {
|
|
108
|
+
public static double MAX_SPEED = 0.8;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public class MecanumDrive {
|
|
112
|
+
private final double maxSpeed;
|
|
113
|
+
|
|
114
|
+
public MecanumDrive(HardwareMap hardwareMap) {
|
|
115
|
+
// BUG: maxSpeed is copied once; dashboard changes to MAX_SPEED are never seen
|
|
116
|
+
this.maxSpeed = DriveConstants.MAX_SPEED;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public void drive(double forward, double strafe, double turn) {
|
|
120
|
+
// This always uses the stale value captured in the constructor
|
|
121
|
+
double speed = forward * maxSpeed;
|
|
122
|
+
// ...
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
**RIGHT** — reading the static field at point of use:
|
|
128
|
+
\`\`\`java
|
|
129
|
+
public class MecanumDrive {
|
|
130
|
+
public MecanumDrive(HardwareMap hardwareMap) {
|
|
131
|
+
// No cached copy
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public void drive(double forward, double strafe, double turn) {
|
|
135
|
+
// Reads the live static field every loop iteration — dashboard changes take effect immediately
|
|
136
|
+
double speed = forward * DriveConstants.MAX_SPEED;
|
|
137
|
+
// ...
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
### ValueProvider<T> — Programmatic Config
|
|
143
|
+
For variables that cannot be simple static fields, use the ValueProvider interface:
|
|
144
|
+
\`\`\`java
|
|
145
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
146
|
+
import com.acmerobotics.dashboard.config.ValueProvider;
|
|
147
|
+
|
|
148
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
149
|
+
|
|
150
|
+
dashboard.addConfigVariable("Motors", "leftPower", new ValueProvider<Double>() {
|
|
151
|
+
@Override
|
|
152
|
+
public Double get() {
|
|
153
|
+
return leftMotor.getPower();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@Override
|
|
157
|
+
public void set(Double value) {
|
|
158
|
+
leftMotor.setPower(value);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Remove when no longer needed
|
|
163
|
+
dashboard.removeConfigVariable("Motors", "leftPower");
|
|
164
|
+
\`\`\`
|
|
165
|
+
`,
|
|
166
|
+
telemetry: `
|
|
167
|
+
## FTC Dashboard Telemetry
|
|
168
|
+
|
|
169
|
+
### Getting the Dashboard Instance
|
|
170
|
+
\`\`\`java
|
|
171
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
172
|
+
|
|
173
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
174
|
+
\`\`\`
|
|
175
|
+
\`getInstance()\` returns the singleton. It is available during \`init()\`, \`start()\`, and \`loop()\` (or their LinearOpMode equivalents).
|
|
176
|
+
|
|
177
|
+
### Approach 1: MultipleTelemetry (Recommended Standard Pattern)
|
|
178
|
+
Sends telemetry to BOTH the Driver Station AND the dashboard simultaneously. This is THE standard pattern used by most FTC teams.
|
|
179
|
+
\`\`\`java
|
|
180
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
181
|
+
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
|
|
182
|
+
|
|
183
|
+
@TeleOp(name = "TeleOp Example")
|
|
184
|
+
public class MyTeleOp extends LinearOpMode {
|
|
185
|
+
@Override
|
|
186
|
+
public void runOpMode() {
|
|
187
|
+
// Wrap both telemetry objects — all calls go to DS + dashboard
|
|
188
|
+
telemetry = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
|
|
189
|
+
|
|
190
|
+
waitForStart();
|
|
191
|
+
|
|
192
|
+
while (opModeIsActive()) {
|
|
193
|
+
telemetry.addData("Loop Time", "%.1f ms", getRuntime() * 1000);
|
|
194
|
+
telemetry.addData("Heading", "%.2f deg", getHeading());
|
|
195
|
+
telemetry.update(); // Sends to both DS and dashboard
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
\`\`\`
|
|
200
|
+
|
|
201
|
+
### dashboard.getTelemetry()
|
|
202
|
+
\`\`\`java
|
|
203
|
+
Telemetry dashTelemetry = dashboard.getTelemetry();
|
|
204
|
+
dashTelemetry.addData("key", value);
|
|
205
|
+
dashTelemetry.update(); // Sends only to dashboard
|
|
206
|
+
\`\`\`
|
|
207
|
+
Returns a standard \`Telemetry\` adapter that speaks the dashboard protocol. Use this standalone if you only want dashboard output, or wrap it with \`MultipleTelemetry\`.
|
|
208
|
+
|
|
209
|
+
### Approach 2: TelemetryPacket (Full Control)
|
|
210
|
+
For field overlay drawing and advanced telemetry, use packets directly.
|
|
211
|
+
\`\`\`java
|
|
212
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
213
|
+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
|
|
214
|
+
|
|
215
|
+
@TeleOp(name = "Packet Example")
|
|
216
|
+
public class PacketExample extends LinearOpMode {
|
|
217
|
+
@Override
|
|
218
|
+
public void runOpMode() {
|
|
219
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
220
|
+
|
|
221
|
+
waitForStart();
|
|
222
|
+
|
|
223
|
+
while (opModeIsActive()) {
|
|
224
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
225
|
+
|
|
226
|
+
// Key-value data (appears in telemetry panel)
|
|
227
|
+
packet.put("x", robotX);
|
|
228
|
+
packet.put("y", robotY);
|
|
229
|
+
packet.put("heading", Math.toDegrees(robotHeading));
|
|
230
|
+
|
|
231
|
+
// Free-form log lines
|
|
232
|
+
packet.addLine("Status: Running");
|
|
233
|
+
packet.addLine("Loop #" + loopCount);
|
|
234
|
+
|
|
235
|
+
// Field overlay drawing (see Canvas section)
|
|
236
|
+
packet.fieldOverlay()
|
|
237
|
+
.setFill("blue")
|
|
238
|
+
.fillCircle(robotX, robotY, 9);
|
|
239
|
+
|
|
240
|
+
dashboard.sendTelemetryPacket(packet);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
### TelemetryPacket API
|
|
247
|
+
| Method | Description |
|
|
248
|
+
|--------|-------------|
|
|
249
|
+
| \`put(String key, Object value)\` | Add a key-value pair to the telemetry data |
|
|
250
|
+
| \`addLine(String text)\` | Add a free-form log line |
|
|
251
|
+
| \`fieldOverlay()\` | Returns the \`Canvas\` for drawing on the field overlay |
|
|
252
|
+
| \`new TelemetryPacket(false)\` | Create a packet that suppresses the default field image background |
|
|
253
|
+
|
|
254
|
+
### Sending and Timing
|
|
255
|
+
\`\`\`java
|
|
256
|
+
dashboard.sendTelemetryPacket(packet); // Send one packet
|
|
257
|
+
dashboard.setTelemetryTransmissionInterval(50); // Set interval in ms (default: 100ms)
|
|
258
|
+
dashboard.clearTelemetry(); // Clear all telemetry on the dashboard
|
|
259
|
+
\`\`\`
|
|
260
|
+
- Default transmission interval: **100 ms**
|
|
261
|
+
- Buffer capacity: **100 packets** — if you produce packets faster than they are consumed, the oldest are dropped.
|
|
262
|
+
|
|
263
|
+
### GOTCHA: Double-Packet Problem
|
|
264
|
+
If you use MultipleTelemetry AND manually call \`sendTelemetryPacket()\`, each loop iteration sends TWO packets — one from \`telemetry.update()\` (via MultipleTelemetry) and one from \`sendTelemetryPacket()\`. This wastes bandwidth and can cause confusing graph behavior.
|
|
265
|
+
|
|
266
|
+
**Pick ONE approach:**
|
|
267
|
+
- **MultipleTelemetry** for simple key-value telemetry to both DS and dashboard.
|
|
268
|
+
- **TelemetryPacket + sendTelemetryPacket()** when you need field overlay drawing or full packet control. If you also need DS telemetry, call \`telemetry.addData()\`/\`telemetry.update()\` on the original (non-wrapped) telemetry separately.
|
|
269
|
+
|
|
270
|
+
### Complete Working Example — Both Approaches Combined Correctly
|
|
271
|
+
\`\`\`java
|
|
272
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
273
|
+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
|
|
274
|
+
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
|
|
275
|
+
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
|
|
276
|
+
|
|
277
|
+
@TeleOp(name = "Dashboard Demo")
|
|
278
|
+
public class DashboardDemo extends LinearOpMode {
|
|
279
|
+
@Override
|
|
280
|
+
public void runOpMode() {
|
|
281
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
282
|
+
|
|
283
|
+
double robotX = 0, robotY = 0, robotHeading = 0;
|
|
284
|
+
|
|
285
|
+
waitForStart();
|
|
286
|
+
|
|
287
|
+
while (opModeIsActive()) {
|
|
288
|
+
// Update robot state (placeholder logic)
|
|
289
|
+
robotX += gamepad1.left_stick_x * 0.5;
|
|
290
|
+
robotY -= gamepad1.left_stick_y * 0.5;
|
|
291
|
+
robotHeading += gamepad1.right_stick_x * 0.05;
|
|
292
|
+
|
|
293
|
+
// --- Dashboard telemetry via TelemetryPacket (with field overlay) ---
|
|
294
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
295
|
+
packet.put("x", robotX);
|
|
296
|
+
packet.put("y", robotY);
|
|
297
|
+
packet.put("heading (deg)", Math.toDegrees(robotHeading));
|
|
298
|
+
|
|
299
|
+
packet.fieldOverlay()
|
|
300
|
+
.setStroke("blue")
|
|
301
|
+
.strokeCircle(robotX, robotY, 9)
|
|
302
|
+
.setStroke("green")
|
|
303
|
+
.strokeLine(
|
|
304
|
+
robotX, robotY,
|
|
305
|
+
robotX + 12 * Math.cos(robotHeading),
|
|
306
|
+
robotY + 12 * Math.sin(robotHeading)
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
dashboard.sendTelemetryPacket(packet);
|
|
310
|
+
|
|
311
|
+
// --- Driver Station telemetry (separate, no MultipleTelemetry wrapper) ---
|
|
312
|
+
telemetry.addData("X", "%.1f", robotX);
|
|
313
|
+
telemetry.addData("Y", "%.1f", robotY);
|
|
314
|
+
telemetry.addData("Heading", "%.1f deg", Math.toDegrees(robotHeading));
|
|
315
|
+
telemetry.update();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
\`\`\`
|
|
320
|
+
`,
|
|
321
|
+
canvas: `
|
|
322
|
+
## Canvas API — FTC Dashboard Field Overlay
|
|
323
|
+
|
|
324
|
+
The Canvas is accessed via \`packet.fieldOverlay()\` on a \`TelemetryPacket\`. All Canvas methods are **fluent/chainable** (they return \`this\`).
|
|
325
|
+
|
|
326
|
+
### Coordinate System
|
|
327
|
+
- Units: **inches**
|
|
328
|
+
- Origin: **center of the field**
|
|
329
|
+
- **+X**: toward the right when facing the red alliance wall
|
|
330
|
+
- **+Y**: away from the red alliance wall (toward the blue alliance wall)
|
|
331
|
+
- The default field image is 144 x 144 inches (12 ft x 12 ft)
|
|
332
|
+
|
|
333
|
+
### Shape Methods (Exact Signatures)
|
|
334
|
+
|
|
335
|
+
| Method | Description |
|
|
336
|
+
|--------|-------------|
|
|
337
|
+
| \`fillCircle(double x, double y, double r)\` | Filled circle at (x, y) with radius r |
|
|
338
|
+
| \`strokeCircle(double x, double y, double r)\` | Outlined circle at (x, y) with radius r |
|
|
339
|
+
| \`fillRect(double x, double y, double w, double h)\` | Filled rectangle — (x, y) is the CENTER, w and h are full width/height |
|
|
340
|
+
| \`strokeRect(double x, double y, double w, double h)\` | Outlined rectangle — (x, y) is the CENTER |
|
|
341
|
+
| \`fillPolygon(double[] xPoints, double[] yPoints)\` | Filled polygon from coordinate arrays |
|
|
342
|
+
| \`strokePolygon(double[] xPoints, double[] yPoints)\` | Outlined polygon from coordinate arrays |
|
|
343
|
+
| \`strokePolyline(double[] xPoints, double[] yPoints)\` | Open polyline (not closed) from coordinate arrays |
|
|
344
|
+
| \`strokeLine(double x1, double y1, double x2, double y2)\` | Line segment from (x1,y1) to (x2,y2) |
|
|
345
|
+
|
|
346
|
+
### Text Methods
|
|
347
|
+
|
|
348
|
+
| Method | Description |
|
|
349
|
+
|--------|-------------|
|
|
350
|
+
| \`fillText(String text, double x, double y, String font, double theta)\` | Filled text at (x,y), rotated by theta radians |
|
|
351
|
+
| \`strokeText(String text, double x, double y, String font, double theta)\` | Outlined text at (x,y), rotated by theta radians |
|
|
352
|
+
| \`fillText(String text, double x, double y, String font, double theta, boolean usePageFrame)\` | With explicit page frame flag |
|
|
353
|
+
| \`strokeText(String text, double x, double y, String font, double theta, boolean usePageFrame)\` | With explicit page frame flag |
|
|
354
|
+
|
|
355
|
+
- \`font\`: CSS font string, e.g. \`"16px monospace"\`, \`"bold 12px sans-serif"\`
|
|
356
|
+
- \`theta\`: rotation in radians
|
|
357
|
+
- \`usePageFrame\`: when \`true\`, coordinates are in page/pixel frame (ignores field transforms); when \`false\` (default), coordinates are in the field/transform frame
|
|
358
|
+
|
|
359
|
+
### Image Drawing
|
|
360
|
+
\`\`\`java
|
|
361
|
+
canvas.drawImage(String path, double x, double y, double w, double h)
|
|
362
|
+
canvas.drawImage(String path, double x, double y, double w, double h, boolean usePageFrame)
|
|
363
|
+
\`\`\`
|
|
364
|
+
- Custom images go in: \`TeamCode/src/main/assets/images/\`
|
|
365
|
+
- \`path\` is relative to the assets directory, e.g. \`"/images/myRobot.png"\`
|
|
366
|
+
- (x, y) is the center; w and h are full dimensions in inches
|
|
367
|
+
|
|
368
|
+
### Style Methods
|
|
369
|
+
|
|
370
|
+
| Method | Description |
|
|
371
|
+
|--------|-------------|
|
|
372
|
+
| \`setFill(String cssColor)\` | Set fill color — any CSS color: \`"red"\`, \`"#FF0000"\`, \`"rgba(255,0,0,0.5)"\` |
|
|
373
|
+
| \`setStroke(String cssColor)\` | Set stroke/outline color |
|
|
374
|
+
| \`setStrokeWidth(int px)\` | Set stroke width in pixels |
|
|
375
|
+
| \`setAlpha(double alpha)\` | Set global transparency: 0.0 (invisible) to 1.0 (opaque) |
|
|
376
|
+
|
|
377
|
+
### Transform Methods
|
|
378
|
+
Each transform method **OVERRIDES** the previous transform of that type (they do NOT compose/accumulate).
|
|
379
|
+
|
|
380
|
+
| Method | Description |
|
|
381
|
+
|--------|-------------|
|
|
382
|
+
| \`setTranslation(double x, double y)\` | Translate subsequent drawings by (x, y) |
|
|
383
|
+
| \`setRotation(double theta)\` | Rotate subsequent drawings by theta radians |
|
|
384
|
+
| \`setScale(double sx, double sy)\` | Scale subsequent drawings |
|
|
385
|
+
|
|
386
|
+
To combine transforms, apply them before drawing. They affect all subsequent draw calls until changed.
|
|
387
|
+
|
|
388
|
+
### Common Pattern: Drawing a Robot on the Field
|
|
389
|
+
\`\`\`java
|
|
390
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
391
|
+
Canvas canvas = packet.fieldOverlay();
|
|
392
|
+
|
|
393
|
+
double robotX = 24.0; // inches from center
|
|
394
|
+
double robotY = -36.0; // inches from center
|
|
395
|
+
double heading = Math.toRadians(45); // radians
|
|
396
|
+
double robotRadius = 9.0; // inches (typical 18" robot)
|
|
397
|
+
|
|
398
|
+
// Draw robot body
|
|
399
|
+
canvas.setStroke("blue")
|
|
400
|
+
.setStrokeWidth(2)
|
|
401
|
+
.strokeCircle(robotX, robotY, robotRadius);
|
|
402
|
+
|
|
403
|
+
// Draw heading indicator line
|
|
404
|
+
canvas.setStroke("green")
|
|
405
|
+
.strokeLine(
|
|
406
|
+
robotX, robotY,
|
|
407
|
+
robotX + robotRadius * Math.cos(heading),
|
|
408
|
+
robotY + robotRadius * Math.sin(heading)
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
dashboard.sendTelemetryPacket(packet);
|
|
412
|
+
\`\`\`
|
|
413
|
+
|
|
414
|
+
### Common Pattern: Drawing a Path/Trajectory
|
|
415
|
+
\`\`\`java
|
|
416
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
417
|
+
Canvas canvas = packet.fieldOverlay();
|
|
418
|
+
|
|
419
|
+
// Define waypoints
|
|
420
|
+
double[] xPoints = {0, 12, 24, 36, 48};
|
|
421
|
+
double[] yPoints = {0, 12, 12, 0, -12};
|
|
422
|
+
|
|
423
|
+
// Draw the path as a polyline
|
|
424
|
+
canvas.setStroke("red")
|
|
425
|
+
.setStrokeWidth(1)
|
|
426
|
+
.strokePolyline(xPoints, yPoints);
|
|
427
|
+
|
|
428
|
+
// Highlight waypoints
|
|
429
|
+
canvas.setFill("yellow")
|
|
430
|
+
.setStroke("black")
|
|
431
|
+
.setStrokeWidth(1);
|
|
432
|
+
for (int i = 0; i < xPoints.length; i++) {
|
|
433
|
+
canvas.fillCircle(xPoints[i], yPoints[i], 2);
|
|
434
|
+
canvas.strokeCircle(xPoints[i], yPoints[i], 2);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
dashboard.sendTelemetryPacket(packet);
|
|
438
|
+
\`\`\`
|
|
439
|
+
|
|
440
|
+
### Complete Example with Transforms
|
|
441
|
+
\`\`\`java
|
|
442
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
443
|
+
Canvas canvas = packet.fieldOverlay();
|
|
444
|
+
|
|
445
|
+
// Draw a robot-shaped rectangle rotated to the robot's heading
|
|
446
|
+
double robotX = 10, robotY = 20;
|
|
447
|
+
double heading = Math.toRadians(30);
|
|
448
|
+
|
|
449
|
+
canvas.setTranslation(robotX, robotY)
|
|
450
|
+
.setRotation(heading)
|
|
451
|
+
.setStroke("blue")
|
|
452
|
+
.setStrokeWidth(2)
|
|
453
|
+
.strokeRect(0, 0, 18, 18) // 18x18 inch robot centered at transform origin
|
|
454
|
+
.setStroke("red")
|
|
455
|
+
.strokeLine(0, 0, 12, 0); // heading arrow
|
|
456
|
+
|
|
457
|
+
// Reset transforms for other drawings
|
|
458
|
+
canvas.setTranslation(0, 0)
|
|
459
|
+
.setRotation(0);
|
|
460
|
+
|
|
461
|
+
// Draw field elements in absolute coordinates
|
|
462
|
+
canvas.setFill("yellow")
|
|
463
|
+
.fillCircle(0, 0, 3); // mark field center
|
|
464
|
+
|
|
465
|
+
dashboard.sendTelemetryPacket(packet);
|
|
466
|
+
\`\`\`
|
|
467
|
+
`,
|
|
468
|
+
camera: `
|
|
469
|
+
## Camera Streaming to FTC Dashboard
|
|
470
|
+
|
|
471
|
+
### Method 1: VisionPortal Processor (Recommended for modern FTC SDK)
|
|
472
|
+
|
|
473
|
+
Create a class that implements both \`VisionProcessor\` (for VisionPortal) and \`CameraStreamSource\` (for dashboard):
|
|
474
|
+
|
|
475
|
+
\`\`\`java
|
|
476
|
+
import android.graphics.Bitmap;
|
|
477
|
+
import android.graphics.Canvas;
|
|
478
|
+
|
|
479
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
480
|
+
import com.acmerobotics.dashboard.config.Config;
|
|
481
|
+
|
|
482
|
+
import org.firstinspires.ftc.robotcore.external.function.Consumer;
|
|
483
|
+
import org.firstinspires.ftc.robotcore.external.function.Continuation;
|
|
484
|
+
import org.firstinspires.ftc.robotcore.external.stream.CameraStreamSource;
|
|
485
|
+
import org.firstinspires.ftc.robotcore.internal.camera.calibration.CameraCalibration;
|
|
486
|
+
import org.firstinspires.ftc.vision.VisionProcessor;
|
|
487
|
+
import org.opencv.android.Utils;
|
|
488
|
+
import org.opencv.core.Mat;
|
|
489
|
+
|
|
490
|
+
import java.util.concurrent.atomic.AtomicReference;
|
|
491
|
+
|
|
492
|
+
public class CameraStreamProcessor implements VisionProcessor, CameraStreamSource {
|
|
493
|
+
private final AtomicReference<Bitmap> lastFrame = new AtomicReference<>(
|
|
494
|
+
Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
@Override
|
|
498
|
+
public void init(int width, int height, CameraCalibration calibration) {
|
|
499
|
+
// Called when the processor is initialized
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
@Override
|
|
503
|
+
public Object processFrame(Mat frame, long captureTimeNanos) {
|
|
504
|
+
// Convert the OpenCV Mat to an Android Bitmap
|
|
505
|
+
Bitmap bitmap = Bitmap.createBitmap(frame.width(), frame.height(), Bitmap.Config.RGB_565);
|
|
506
|
+
Utils.matToBitmap(frame, bitmap);
|
|
507
|
+
lastFrame.set(bitmap);
|
|
508
|
+
return null; // No user context needed
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
@Override
|
|
512
|
+
public void onDrawFrame(Canvas canvas, int onscreenWidth, int onscreenHeight,
|
|
513
|
+
float scaleBmpPxToCanvasPx, float scaleCanvasDensity,
|
|
514
|
+
Object userContext) {
|
|
515
|
+
// Optional: draw annotations on the DS preview
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
@Override
|
|
519
|
+
public void getFrameBitmap(Continuation<? extends Consumer<Bitmap>> continuation) {
|
|
520
|
+
continuation.dispatch(new Consumer<Bitmap>() {
|
|
521
|
+
@Override
|
|
522
|
+
public void accept(Bitmap bitmapConsumer) {
|
|
523
|
+
bitmapConsumer = lastFrame.get();
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
\`\`\`
|
|
529
|
+
|
|
530
|
+
**Using it in your OpMode:**
|
|
531
|
+
\`\`\`java
|
|
532
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
533
|
+
import org.firstinspires.ftc.vision.VisionPortal;
|
|
534
|
+
|
|
535
|
+
@TeleOp(name = "Camera Stream Demo")
|
|
536
|
+
public class CameraStreamDemo extends LinearOpMode {
|
|
537
|
+
@Override
|
|
538
|
+
public void runOpMode() {
|
|
539
|
+
CameraStreamProcessor cameraProcessor = new CameraStreamProcessor();
|
|
540
|
+
|
|
541
|
+
VisionPortal portal = new VisionPortal.Builder()
|
|
542
|
+
.addProcessor(cameraProcessor)
|
|
543
|
+
.setCamera(hardwareMap.get(WebcamName.class, "Webcam 1"))
|
|
544
|
+
.build();
|
|
545
|
+
|
|
546
|
+
FtcDashboard.getInstance().startCameraStream(cameraProcessor, 30);
|
|
547
|
+
|
|
548
|
+
waitForStart();
|
|
549
|
+
|
|
550
|
+
while (opModeIsActive()) {
|
|
551
|
+
telemetry.addLine("Streaming to dashboard...");
|
|
552
|
+
telemetry.update();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Camera stream auto-stops when OpMode ends
|
|
556
|
+
portal.close();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
\`\`\`
|
|
560
|
+
|
|
561
|
+
### Method 2: EasyOpenCV (OpenCvWebcam)
|
|
562
|
+
\`\`\`java
|
|
563
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
564
|
+
import org.openftc.easyopencv.OpenCvCamera;
|
|
565
|
+
import org.openftc.easyopencv.OpenCvCameraFactory;
|
|
566
|
+
import org.openftc.easyopencv.OpenCvCameraRotation;
|
|
567
|
+
import org.openftc.easyopencv.OpenCvWebcam;
|
|
568
|
+
|
|
569
|
+
@TeleOp(name = "EasyOpenCV Stream")
|
|
570
|
+
public class EasyOpenCVStream extends LinearOpMode {
|
|
571
|
+
@Override
|
|
572
|
+
public void runOpMode() {
|
|
573
|
+
int cameraMonitorViewId = hardwareMap.appContext.getResources()
|
|
574
|
+
.getIdentifier("cameraMonitorViewId", "id", hardwareMap.appContext.getPackageName());
|
|
575
|
+
|
|
576
|
+
OpenCvWebcam webcam = OpenCvCameraFactory.getInstance()
|
|
577
|
+
.createWebcam(hardwareMap.get(WebcamName.class, "Webcam 1"), cameraMonitorViewId);
|
|
578
|
+
|
|
579
|
+
webcam.setPipeline(new MyPipeline());
|
|
580
|
+
|
|
581
|
+
webcam.openCameraDeviceAsync(new OpenCvCamera.AsyncCameraOpenListener() {
|
|
582
|
+
@Override
|
|
583
|
+
public void onOpened() {
|
|
584
|
+
webcam.startStreaming(320, 240, OpenCvCameraRotation.UPRIGHT);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
@Override
|
|
588
|
+
public void onError(int errorCode) {
|
|
589
|
+
telemetry.addData("Camera Error", errorCode);
|
|
590
|
+
telemetry.update();
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// OpenCvWebcam implements CameraStreamSource
|
|
595
|
+
FtcDashboard.getInstance().startCameraStream(webcam, 0); // 0 = unlimited FPS
|
|
596
|
+
|
|
597
|
+
waitForStart();
|
|
598
|
+
|
|
599
|
+
while (opModeIsActive()) {
|
|
600
|
+
sleep(20);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
webcam.stopStreaming();
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
\`\`\`
|
|
607
|
+
|
|
608
|
+
### Method 3: Limelight 3A
|
|
609
|
+
\`\`\`java
|
|
610
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
611
|
+
import com.qualcomm.hardware.limelightvision.Limelight3A;
|
|
612
|
+
|
|
613
|
+
@TeleOp(name = "Limelight Stream")
|
|
614
|
+
public class LimelightStream extends LinearOpMode {
|
|
615
|
+
@Override
|
|
616
|
+
public void runOpMode() {
|
|
617
|
+
Limelight3A limelight = hardwareMap.get(Limelight3A.class, "limelight");
|
|
618
|
+
limelight.start();
|
|
619
|
+
|
|
620
|
+
// Limelight3A implements CameraStreamSource
|
|
621
|
+
FtcDashboard.getInstance().startCameraStream(limelight, 0);
|
|
622
|
+
|
|
623
|
+
waitForStart();
|
|
624
|
+
|
|
625
|
+
while (opModeIsActive()) {
|
|
626
|
+
sleep(20);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
limelight.stop();
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
\`\`\`
|
|
633
|
+
|
|
634
|
+
### Camera Stream API
|
|
635
|
+
\`\`\`java
|
|
636
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
637
|
+
|
|
638
|
+
// Start streaming from any CameraStreamSource
|
|
639
|
+
dashboard.startCameraStream(source, maxFps); // maxFps: 0 = unlimited
|
|
640
|
+
|
|
641
|
+
// Stop the camera stream
|
|
642
|
+
dashboard.stopCameraStream();
|
|
643
|
+
|
|
644
|
+
// Send a single Bitmap image (useful for processed frames)
|
|
645
|
+
dashboard.sendImage(bitmap);
|
|
646
|
+
|
|
647
|
+
// Set JPEG compression quality (0-100, default 50)
|
|
648
|
+
dashboard.setImageQuality(75);
|
|
649
|
+
\`\`\`
|
|
650
|
+
|
|
651
|
+
### Important Notes
|
|
652
|
+
- The camera stream **auto-stops** when the OpMode ends.
|
|
653
|
+
- \`maxFps\` of \`0\` means unlimited — frames are sent as fast as they are produced.
|
|
654
|
+
- Lower \`setImageQuality()\` values reduce bandwidth but degrade image quality.
|
|
655
|
+
- Only ONE camera stream can be active at a time. Starting a new stream replaces the previous one.
|
|
656
|
+
`,
|
|
657
|
+
setup: `
|
|
658
|
+
## FTC Dashboard Installation & Setup
|
|
659
|
+
|
|
660
|
+
### Gradle Configuration
|
|
661
|
+
|
|
662
|
+
**Step 1:** Add the Maven repository to \`build.dependencies.gradle\` (or your project-level \`build.gradle\`):
|
|
663
|
+
\`\`\`groovy
|
|
664
|
+
repositories {
|
|
665
|
+
maven { url = 'https://maven.brott.dev/' }
|
|
666
|
+
}
|
|
667
|
+
\`\`\`
|
|
668
|
+
|
|
669
|
+
**Step 2:** Add the dependency (in your TeamCode \`build.gradle\`):
|
|
670
|
+
\`\`\`groovy
|
|
671
|
+
dependencies {
|
|
672
|
+
implementation 'com.acmerobotics.dashboard:dashboard:0.5.1'
|
|
673
|
+
}
|
|
674
|
+
\`\`\`
|
|
675
|
+
|
|
676
|
+
### Accessing the Dashboard
|
|
677
|
+
|
|
678
|
+
| Connection Method | URL |
|
|
679
|
+
|-------------------|-----|
|
|
680
|
+
| **Control Hub** (Wi-Fi Direct) | \`http://192.168.43.1:8080/dash\` |
|
|
681
|
+
| **Phone** (Wi-Fi Direct) | \`http://192.168.49.1:8080/dash\` |
|
|
682
|
+
|
|
683
|
+
Open the URL in any modern web browser (Chrome recommended) on a device connected to the Robot Controller's Wi-Fi network.
|
|
684
|
+
|
|
685
|
+
### Dashboard Features
|
|
686
|
+
- **Browser OpMode Controls**: Start, stop, and init OpModes directly from the dashboard web interface without needing a Driver Station.
|
|
687
|
+
- **Gamepad Support**: Connect a gamepad to the computer running the dashboard browser; the dashboard forwards gamepad input to the robot as gamepad1/gamepad2.
|
|
688
|
+
- **Live Telemetry Graphs**: Numeric telemetry values are automatically graphed over time.
|
|
689
|
+
- **Configuration Panel**: Live-edit \`@Config\` variables without redeploying code.
|
|
690
|
+
- **Camera Stream**: View camera feed in the browser.
|
|
691
|
+
- **Field Overlay**: Draw on a top-down field diagram in real time.
|
|
692
|
+
|
|
693
|
+
### Disabling for Competition (RS09)
|
|
694
|
+
FTC rule RS09 prohibits wireless communication to non-approved devices during competition. The dashboard must be disabled before competing:
|
|
695
|
+
|
|
696
|
+
\`\`\`java
|
|
697
|
+
// In your OpMode or robot initialization
|
|
698
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
699
|
+
// Dashboard is enabled by default; no special action needed for practice
|
|
700
|
+
// For competition, disable the dashboard to comply with RS09
|
|
701
|
+
\`\`\`
|
|
702
|
+
|
|
703
|
+
The simplest approach: do NOT connect to the dashboard network during matches. The dashboard only activates when a browser client connects. Alternatively, you can avoid including the dashboard dependency in your competition build, or call \`FtcDashboard.getInstance().setEnabled(false)\` at init.
|
|
704
|
+
|
|
705
|
+
### Complete Minimal OpMode with Dashboard
|
|
706
|
+
\`\`\`java
|
|
707
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
708
|
+
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
|
|
709
|
+
import com.acmerobotics.dashboard.config.Config;
|
|
710
|
+
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
|
|
711
|
+
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
|
|
712
|
+
|
|
713
|
+
@Config
|
|
714
|
+
@TeleOp(name = "Dashboard Quickstart")
|
|
715
|
+
public class DashboardQuickstart extends LinearOpMode {
|
|
716
|
+
public static double MOTOR_POWER = 0.5;
|
|
717
|
+
public static int TARGET_POSITION = 1000;
|
|
718
|
+
|
|
719
|
+
@Override
|
|
720
|
+
public void runOpMode() {
|
|
721
|
+
telemetry = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
|
|
722
|
+
|
|
723
|
+
waitForStart();
|
|
724
|
+
|
|
725
|
+
while (opModeIsActive()) {
|
|
726
|
+
telemetry.addData("Motor Power", MOTOR_POWER);
|
|
727
|
+
telemetry.addData("Target", TARGET_POSITION);
|
|
728
|
+
telemetry.addData("Time", "%.1f s", getRuntime());
|
|
729
|
+
telemetry.update();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
\`\`\`
|
|
734
|
+
`,
|
|
735
|
+
api: `
|
|
736
|
+
## FtcDashboard Public API Summary
|
|
737
|
+
|
|
738
|
+
\`\`\`java
|
|
739
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
740
|
+
|
|
741
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
742
|
+
\`\`\`
|
|
743
|
+
|
|
744
|
+
### Singleton Access
|
|
745
|
+
|
|
746
|
+
| Method | Description |
|
|
747
|
+
|--------|-------------|
|
|
748
|
+
| \`static FtcDashboard getInstance()\` | Returns the singleton dashboard instance. Available after SDK initialization, during init/start/loop. |
|
|
749
|
+
|
|
750
|
+
### Telemetry
|
|
751
|
+
|
|
752
|
+
| Method | Description |
|
|
753
|
+
|--------|-------------|
|
|
754
|
+
| \`void sendTelemetryPacket(TelemetryPacket packet)\` | Send a telemetry packet with key-value data and optional field overlay |
|
|
755
|
+
| \`void clearTelemetry()\` | Clear all telemetry data displayed on the dashboard |
|
|
756
|
+
| \`Telemetry getTelemetry()\` | Get a standard Telemetry adapter that forwards to the dashboard |
|
|
757
|
+
| \`void setTelemetryTransmissionInterval(int ms)\` | Set how often telemetry is sent to the browser (default: 100ms) |
|
|
758
|
+
|
|
759
|
+
### Camera Streaming
|
|
760
|
+
|
|
761
|
+
| Method | Description |
|
|
762
|
+
|--------|-------------|
|
|
763
|
+
| \`void startCameraStream(CameraStreamSource source, double maxFps)\` | Start streaming from a CameraStreamSource; maxFps 0 = unlimited |
|
|
764
|
+
| \`void stopCameraStream()\` | Stop the active camera stream |
|
|
765
|
+
| \`void sendImage(Bitmap bitmap)\` | Send a single Bitmap image to the dashboard |
|
|
766
|
+
| \`void setImageQuality(int quality)\` | Set JPEG quality 0-100 (default: 50) |
|
|
767
|
+
|
|
768
|
+
### Configuration Variables
|
|
769
|
+
|
|
770
|
+
| Method | Description |
|
|
771
|
+
|--------|-------------|
|
|
772
|
+
| \`void addConfigVariable(String group, String name, ValueProvider<?> provider)\` | Register a programmatic config variable under the given group |
|
|
773
|
+
| \`void removeConfigVariable(String group, String name)\` | Remove a previously registered config variable |
|
|
774
|
+
| \`void updateConfig()\` | Force a config update — pushes current static field values to connected clients |
|
|
775
|
+
|
|
776
|
+
### OpMode & State Control
|
|
777
|
+
|
|
778
|
+
| Method | Description |
|
|
779
|
+
|--------|-------------|
|
|
780
|
+
| \`boolean isEnabled()\` | Returns whether the dashboard is currently enabled |
|
|
781
|
+
| \`void suppressOpMode(boolean suppress)\` | When true, suppresses the current OpMode from appearing in the dashboard OpMode list |
|
|
782
|
+
|
|
783
|
+
### Typical Usage Pattern
|
|
784
|
+
\`\`\`java
|
|
785
|
+
import com.acmerobotics.dashboard.FtcDashboard;
|
|
786
|
+
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
|
|
787
|
+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
|
|
788
|
+
import com.acmerobotics.dashboard.config.Config;
|
|
789
|
+
import com.acmerobotics.dashboard.config.ValueProvider;
|
|
790
|
+
|
|
791
|
+
@Config
|
|
792
|
+
@TeleOp(name = "Full API Demo")
|
|
793
|
+
public class FullApiDemo extends LinearOpMode {
|
|
794
|
+
public static double GAIN = 1.0;
|
|
795
|
+
public static int THRESHOLD = 100;
|
|
796
|
+
|
|
797
|
+
@Override
|
|
798
|
+
public void runOpMode() {
|
|
799
|
+
FtcDashboard dashboard = FtcDashboard.getInstance();
|
|
800
|
+
|
|
801
|
+
// Option A: MultipleTelemetry for simple key-value data
|
|
802
|
+
telemetry = new MultipleTelemetry(telemetry, dashboard.getTelemetry());
|
|
803
|
+
|
|
804
|
+
// Register a dynamic config variable
|
|
805
|
+
dashboard.addConfigVariable("Sensors", "brightness", new ValueProvider<Integer>() {
|
|
806
|
+
@Override
|
|
807
|
+
public Integer get() {
|
|
808
|
+
return 50; // read from sensor
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
@Override
|
|
812
|
+
public void set(Integer value) {
|
|
813
|
+
// apply brightness
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
waitForStart();
|
|
818
|
+
|
|
819
|
+
while (opModeIsActive()) {
|
|
820
|
+
// Option B: Full packet with field overlay
|
|
821
|
+
TelemetryPacket packet = new TelemetryPacket();
|
|
822
|
+
packet.put("gain", GAIN);
|
|
823
|
+
packet.put("threshold", THRESHOLD);
|
|
824
|
+
|
|
825
|
+
packet.fieldOverlay()
|
|
826
|
+
.setFill("green")
|
|
827
|
+
.fillCircle(0, 0, 9);
|
|
828
|
+
|
|
829
|
+
dashboard.sendTelemetryPacket(packet);
|
|
830
|
+
|
|
831
|
+
// Driver Station telemetry (using original, not MultipleTelemetry to avoid double-send)
|
|
832
|
+
// If you used MultipleTelemetry above AND sendTelemetryPacket, comment out one approach.
|
|
833
|
+
telemetry.addData("Gain", GAIN);
|
|
834
|
+
telemetry.update();
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Cleanup
|
|
838
|
+
dashboard.removeConfigVariable("Sensors", "brightness");
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
\`\`\`
|
|
842
|
+
`,
|
|
843
|
+
};
|