This repository contains our Java/WPILib robot code with a simulation-first workflow, a swerve drivetrain, vendor motor abstractions, and CI with test coverage. This guide explains how to set up your environment, run the sim, deploy to the robot, and contribute.
This codebase is currently in a transitional state as we migrate from the 2025 season to the 2026 season:
- ✓ WPILib 2026 Migration - Upgraded to 2026.1.1-beta-2 toolchain
- ✓ PathPlanner Integration - Using 2026-compatible PathPlanner for autonomous path planning
- ✓ FuelSubsystem Implementation - Adapted from 2026 KitBot reference implementation with team Context pattern
- ✓ TemplateMechanism Example - Phoenix6 TalonFX reference subsystem for new mechanism development
- ✓ Legacy Subsystem Removal - Removed 2025-specific mechanisms (climb, elevator, vision/Limelight)
- ✓ PhotonVision Integration - Vision subsystem with dual-camera AprilTag detection and SmartDashboard telemetry
- ✓ Enhanced Telemetry System - Level-aware data capture (NONE/MATCH/LAB/VERBOSE), USB auto-detection, structured type support, AdvantageScope-compatible logging for match replay
This codebase uses a Context-based configuration pattern inspired by the 2026 KitBot reference implementation:
- Context Classes: Lombok
@Builderpattern for testable, flexible configuration (e.g.,FuelSubsystemContext,DrivetrainContext) - Subsystems: Accept Context objects via constructor dependency injection
- Command Factories: Subsystems expose command factory methods (
getIntakeCommand(),getLaunchCommand()) - KitBot Best Practices: SparkMaxConfig with proper reset/persist modes, voltage-based control for simple mechanisms, SmartDashboard tuning
- MK5i Swerve Module integration (planned hardware upgrade)
- Additional 2026 game-specific mechanisms
- PathPlanner autonomous routine development
- Further refinement of subsystem implementations
For new subsystem development, refer to:
FuelSubsystem- SparkMax-based roller mechanism (adapted from KitBot)TemplateMechanism- TalonFX-based mechanism baselineDrivetrain.captureTelemetry()- Example of telemetry integration pattern
- Prerequisites
- Quick Start
- Project Layout
- Driver Station Controls
- Running the Simulator
- Deploying to the Robot
- PhotonVision Development Loop
- Telemetry System
- Testing & Code Coverage
- Formatting & Static Checks
- Configuration & Constants
- Resources
- Contributing
- License
- OS: Windows 10/11, macOS, or Linux
- Java: JDK 17 (LTS)
java -version
- IDE: one of
- VS Code + WPILib Extension (recommended)
- IntelliJ IDEA / Eclipse (fine; use Gradle tasks)
- Git: for cloning & PRs
- Gradle: not required — project uses the Gradle Wrapper (
./gradlew)
Vendor libraries are managed via vendordeps JSON under
./vendordeps/. GradleRIO & wrapper versions inbuild.gradlepin the WPILib toolchain.
git clone <repo-url>
cd <repo-dir>
# First build (downloads WPILib & vendors)
./gradlew build
# Open the folder in your IDE (VS Code: install WPILib extension + Set Team Number)src/main/java/frc/robot/
commands/ # Command-based routines (e.g., swervedrive/)
subsystems/ # Subsystems (drivetrain/, fuel/, vision/, template/)
sim/ # Simulation helpers (SwerveModuleSim, physics)
support/ # Utilities and abstractions
├── Telemetry.java # Enhanced telemetry API
├── TelemetryLevel.java # Verbosity levels (NONE/MATCH/LAB/VERBOSE)
├── TelemetryConfig.java # Configuration with USB detection
└── sparkmax/ # REV SPARK MAX abstractions
Constants.java # Global constants (units in identifiers where possible)
src/main/deploy/
telemetry.properties # Telemetry configuration (level, USB settings)
vendordeps/ # Vendor JSONs (REV, CTRE Phoenix6, PathPlanner, etc.)
Driver Station (3 Controllers):
USB 0: DRIVER CONTROLLER
├─ Swerve drive (left stick: translation, right stick: rotation)
├─ Right trigger: acceleration/gas
├─ Left trigger: half-speed mode (≥0.5 threshold)
├─ Gyro reset (B button)
└─ Wheel lock (right stick button)
USB 1: MANIPULATION/OPERATOR CONTROLLER
├─ Fuel intake (left bumper - hold)
├─ Fuel launch (right bumper - spin up → launch sequence)
└─ Fuel eject (X button - hold)
USB 2: DEBUG CONTROLLER
└─ (Reserved for testing and overrides)
- Primary job: Drive the robot using swerve drivetrain
- Left stick: Translation (forward/back, strafe left/right)
- Right stick: Rotation
- Right trigger: Acceleration multiplier
- Left trigger: Half-speed mode for precision
- B button: Reset gyro/NavX heading
- Right stick button: Toggle wheel lock (X-pattern for defense)
- Primary job: Operate fuel subsystem
- Left bumper (hold): Intake fuel into mechanism
- Right bumper (hold): Launch sequence (1 second spin-up, then launch)
- X button (hold): Eject/reverse fuel out of intake
- Primary job: Testing and overrides
- Currently reserved for development/testing purposes
- Used during practice for mechanism testing and troubleshooting
./gradlew simulateJavaThis launches WPILib Sim GUI.
- Sim GUI → Driver Station → Joysticks: Ensure Keyboard is index 0.
- Configure Keyboard:
- Axis 0 ← A/D (strafe left/right)
- Axis 1 ← W/S (forward/back)
- (Optional) Axis 4 ← rotation
- In simulation we read
DriverStation.getStickAxis(0, axis), so the above mappings immediately drive the robot.
- We reset odometry on sim enable so field-relative starts at heading ~0°.
- Only one pose update per loop in sim (no double counting between
periodic()andsimulationPeriodic()). - Swerve modules use
SwerveModuleState.optimize(...)and a snappy steering loop for crisp direction changes.
Set your team number via VS Code WPILib (“Set Team Number”) or GradleRIO config.
# On the FRC network, robot powered
./gradlew deployThen enable using the FRC Driver Station.
The VisionSubsystem provides AprilTag detection using PhotonVision with support for dual cameras. This section describes the complete development and validation workflow.
The vision subsystem follows our Context-based pattern:
- VisionSubsystem (
src/main/java/frc/robot/subsystems/vision/VisionSubsystem.java) - Main subsystem class - VisionSubsystemContext (
src/main/java/frc/robot/subsystems/vision/VisionSubsystemContext.java) - Configuration with camera names and telemetry settings - Constants.Vision (
src/main/java/frc/robot/Constants.java) - Camera transforms, thresholds, and configuration constants
The VisionSubsystem currently provides:
- ✓ Dual Camera Support - Front and rear cameras configured independently
- ✓ AprilTag Detection - Detects and logs all visible AprilTag IDs
- ✓ SmartDashboard Telemetry - Real-time camera status and detection data
- ✓ Connection Monitoring - Tracks camera connectivity status
- ✓ Target Details - Yaw, pitch, area, and skew for each detected tag
When the robot is running, the following data is available in SmartDashboard/Shuffleboard:
Vision/Status- Overall system status stringVision/TotalTagsDetected- Combined tag count from both cameras
Vision/FrontCamera/Connected- Connection status (boolean)Vision/FrontCamera/TargetCount- Number of detected tagsVision/FrontCamera/DetectedTags- Comma-separated list of tag IDs (e.g., "[4, 7, 14]")Vision/FrontCamera/TimestampSeconds- Result timestamp in secondsVision/FrontCamera/BestTargetID- Fiducial ID of the best targetVision/FrontCamera/BestTargetYaw- Yaw angle to best target
When enableVerboseLogging is true in VisionSubsystemContext:
Vision/FrontCamera/Tag{ID}/Yaw- Yaw angle for specific tagVision/FrontCamera/Tag{ID}/Pitch- Pitch angle for specific tagVision/FrontCamera/Tag{ID}/Area- Target area percentageVision/FrontCamera/Tag{ID}/Skew- Target skew angle
- Mount cameras on robot (front and rear recommended)
- Connect cameras to roboRIO via USB or Ethernet
- Ensure cameras have clear view of field AprilTags
- Note physical mounting positions for calibration
On each camera's coprocessor (Raspberry Pi, Orange Pi, etc.):
-
Install PhotonVision (if not already installed)
- Download latest release from photonvision.org
- Flash to coprocessor following official docs
-
Access PhotonVision UI
- Navigate to
http://photonvision.local:5800orhttp://<coprocessor-ip>:5800
- Navigate to
-
Configure Camera Settings
- Go to Settings → Cameras
- Name cameras to match Constants.Vision:
- Front camera:
photonvision-front - Rear camera:
photonvision-rear
- Front camera:
- CRITICAL: Camera names MUST match exactly
-
Configure AprilTag Pipeline
- Create new pipeline (type: AprilTag)
- Set tag family to 36h11 (2024/2025/2026 FRC standard)
- Enable Multi-target mode to detect multiple tags simultaneously
- Tune exposure/brightness for consistent detection
-
Set Static IP (Recommended)
- Configure coprocessor with static IP on robot network
- Example:
10.TE.AM.11for front,10.TE.AM.12for rear - Update mDNS hostname if using
photonvision.local
Goal: Verify cameras connect and PhotonVision NetworkTables communication works
# Deploy robot code
./gradlew deploy
# Open Driver Station and enable robot (TeleOp or Test mode)Open SmartDashboard/Shuffleboard:
-
Check
Vision/FrontCamera/ConnectedandVision/RearCamera/Connected- Both should show
true(green) - If
false: Check PhotonVision coprocessor power, network connection, camera names
- Both should show
-
Check
Vision/Status- Should show "No Targets Detected" or "Tracking" when tags are visible
- If "No Cameras Connected": Verify camera names in PhotonVision UI match Constants.Vision
Troubleshooting:
- Camera not connecting:
- Verify PhotonVision service is running on coprocessor
- Check network connectivity:
ping <coprocessor-ip>from Driver Station - Confirm camera names match exactly (case-sensitive)
- Restart PhotonVision service
- NetworkTables not updating:
- Verify robot and coprocessor are on same network
- Check PhotonVision → Settings → NetworkTables client (should show roboRIO IP)
Goal: Verify cameras detect AprilTags and log correct IDs
Setup:
- Print 2026 FRC AprilTags (download from firstfrc.blob.core.windows.net)
- Position tags in camera field of view
- Use known tag IDs (e.g., Blue Speaker Center = ID 7, Red Speaker Center = ID 4)
Test Procedure:
-
Enable robot in TeleOp mode
-
Position Tag ID 7 in front camera view
-
Observe SmartDashboard:
Vision/FrontCamera/TargetCountshould increase to1or moreVision/FrontCamera/DetectedTagsshould show"[7]"Vision/FrontCamera/BestTargetIDshould show7
-
Move tag to rear camera view
-
Observe SmartDashboard:
Vision/RearCamera/TargetCountshould increaseVision/RearCamera/DetectedTagsshould show"[7]"
-
Position multiple tags (IDs 4, 7, 14) in view simultaneously
-
Verify multi-target detection:
DetectedTagsshould show all IDs:"[4, 7, 14]"TotalTagsDetectedshould show3
Expected Results:
- ✓ Correct tag IDs logged for each camera
- ✓ Multiple tags detected simultaneously
- ✓
BestTargetYawupdates as tag moves laterally - ✓ Timestamp values update continuously
Troubleshooting:
- Tags not detected:
- Check lighting conditions (AprilTags need good contrast)
- Verify pipeline is active in PhotonVision UI (green indicator)
- Adjust exposure/brightness in PhotonVision camera settings
- Ensure tags are printed at correct size (8.5" for 2026 field tags)
- Wrong tag IDs logged:
- Verify tag family setting (must be 36h11)
- Check tag print quality (damaged/blurry tags may misidentify)
- Intermittent detection:
- Increase camera exposure slightly
- Reduce coprocessor CPU load (close unnecessary processes)
- Check for camera focus issues
Goal: Test on practice field and calibrate camera transforms
On Practice Field:
-
Deploy robot code and enable
-
Drive robot around field
-
Monitor SmartDashboard continuously:
- Verify tags detected from various distances (0.5m to 4m+)
- Check which camera sees which tags (front vs rear)
- Note detection range limits
-
Log observations:
- Maximum reliable detection distance
- Which field positions provide best multi-tag visibility
- Any blind spots or occlusion issues
Camera Transform Calibration:
The VisionSubsystem needs accurate camera-to-robot transforms for future pose estimation.
-
Measure physical camera positions:
- X: Forward distance from robot center (meters, positive = forward)
- Y: Lateral distance from robot center (meters, positive = left)
- Z: Height above robot center (meters, positive = up)
-
Measure camera orientations:
- Roll: Rotation around X-axis (radians, typically 0)
- Pitch: Rotation around Y-axis (radians, angle camera is tilted)
- Yaw: Rotation around Z-axis (radians, 0 = forward, π = backward)
-
Update Constants.Vision in Constants.java:
public static final Transform3d FRONT_CAMERA_TO_ROBOT = new Transform3d(
new Translation3d(0.3, 0.0, 0.5), // Example: 30cm forward, centered, 50cm up
new Rotation3d(0, -Math.toRadians(15), 0) // Example: 15° pitched down
);
public static final Transform3d REAR_CAMERA_TO_ROBOT = new Transform3d(
new Translation3d(-0.3, 0.0, 0.5), // Example: 30cm rearward, centered, 50cm up
new Rotation3d(0, -Math.toRadians(15), Math.PI) // Example: 15° pitched down, 180° yaw
);- Redeploy and validate:
- These transforms will be used for SwerveDrivePoseEstimator integration (next phase)
Goal: Confirm VisionSubsystem integrates cleanly with existing robot code
Test Checklist:
- Robot boots successfully with VisionSubsystem enabled
- No NetworkTables conflicts (check for error messages in Driver Station console)
- Swerve drivetrain still operates normally with vision running
- No performance degradation (check robot loop timing in Driver Station)
- All subsystems (Drivetrain, FuelSubsystem, VisionSubsystem) show in SmartDashboard
Autonomous Testing:
- Run existing autonomous routines with VisionSubsystem active
- Verify no interference with PathPlanner path following
- Check that vision data is available throughout autonomous period
Defined in Constants.Vision and must match PhotonVision UI configuration:
public static final String PHOTON_CAMERA_FRONT = "photonvision-front";
public static final String PHOTON_CAMERA_REAR = "photonvision-rear";In VisionSubsystemContext.defaults():
enableVerboseLogging- Logs detailed per-tag telemetry (default: true)enableCameraTelemetry- Enables camera-level telemetry (default: true)
In Constants.Vision (for future pose estimation):
POSE_AMBIGUITY_THRESHOLD- Reject targets with ambiguity > 0.2MAX_POSE_ESTIMATION_DISTANCE- Max distance (meters) to trust vision
The current VisionSubsystem provides proof-of-life functionality. Future enhancements will include:
-
SwerveDrivePoseEstimator Integration
- Fuse vision measurements with drivetrain odometry
- Use
addVisionMeasurement()with timestamp and standard deviations - Implement ambiguity filtering and multi-tag validation
-
Automatic Target Alignment
- Commands to align robot to AprilTags (e.g., speaker, amp)
- PID controllers for yaw/distance control using vision feedback
-
Autonomous Localization
- Reset odometry using vision at autonomous start
- Continuous pose correction during auto routines
-
Field-Relative Corrections
- Periodic heading correction using tag orientations
- Handle alliance color flipping (red vs blue)
- PhotonVision Docs: https://docs.photonvision.org/
- PhotonLib Java API: https://docs.photonvision.org/en/latest/docs/programming/photonlib/index.html
- 2026 AprilTag Layout: https://firstfrc.blob.core.windows.net/frc2026/FieldAssets/AprilTags.pdf
- WPILib Pose Estimation: https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-pose-estimators.html
The Telemetry system provides high-fidelity data capture for post-match analysis, debugging, and performance tuning. Data is automatically logged to USB storage (when available) in WPILib's .wpilog format, which can be replayed in AdvantageScope for full match visualization.
┌─────────────────────────────────────────────────────────────────────────┐
│ Robot Code │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Drivetrain │ │ Vision │ │ Fuel │ │ Commands │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └────────────────┴────────────────┴────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Telemetry │ ← Single instrumentation │
│ │ (Level-aware) │ point for all data │
│ └────────┬────────┘ │
└───────────────────────────────────┼──────────────────────────────────────┘
│
┌─────────────────────┴─────────────────────┐
│ │
┌──────▼──────┐ ┌───────▼───────┐
│ DataLog │ │ NetworkTables │
│ (.wpilog) │ │ (real-time) │
└──────┬──────┘ └───────────────┘
│
┌──────▼──────┐
│ USB Stick │ ← Auto-detected at /media/sda1/FRC_LOGS/
│ (portable) │
└──────┬──────┘
│
┌──────▼──────┐
│ AdvantageScope │ ← Replay, visualize, analyze
└─────────────────┘
src/main/java/frc/robot/support/
├── Telemetry.java # Main telemetry API (record, event, error)
├── TelemetryLevel.java # Verbosity levels (NONE, MATCH, LAB, VERBOSE)
└── TelemetryConfig.java # Configuration with USB detection
src/main/deploy/
└── telemetry.properties # Deploy-time configuration
| Level | Use Case | Data Rate | What Gets Logged |
|---|---|---|---|
NONE |
Emergency/Competition backup | 0 Hz | Nothing (silent mode) |
MATCH |
Competition matches | ~20 Hz | Essential: pose, commands, errors |
LAB |
Practice/testing | ~50 Hz | MATCH + motor currents, sensor raw values |
VERBOSE |
Deep debugging | ~100+ Hz | LAB + PID internals, all signals |
Telemetry is automatically initialized in Robot.java:
@Override
public void robotInit() {
// Loads config from telemetry.properties or uses defaults
Telemetry.initialize(TelemetryConfig.fromDeployDirectory());
// ...
}
@Override
public void robotPeriodic() {
// Captures all registered subsystems
Telemetry.periodic();
// ...
}Register subsystems for automatic capture:
// In subsystem constructor
public MySubsystem(MySubsystemContext context) {
// ... initialization ...
// Register for automatic telemetry capture
Telemetry.registerSubsystem("MySubsystem", this::captureTelemetry);
}
// Capture method - called automatically by Telemetry.periodic()
private void captureTelemetry(String prefix) {
// MATCH level - essential data (always captured at MATCH+)
Telemetry.record(prefix + "/Position", getPosition(), TelemetryLevel.MATCH);
Telemetry.record(prefix + "/Velocity", getVelocity(), TelemetryLevel.MATCH);
// LAB level - detailed data (captured at LAB+)
Telemetry.record(prefix + "/MotorCurrent", motor.getOutputCurrent(), TelemetryLevel.LAB);
Telemetry.record(prefix + "/MotorTemp", motor.getMotorTemperature(), TelemetryLevel.LAB);
// VERBOSE level - debug data (only at VERBOSE)
Telemetry.record(prefix + "/PID/Error", pidController.getPositionError(), TelemetryLevel.VERBOSE);
Telemetry.record(prefix + "/PID/Setpoint", pidController.getSetpoint(), TelemetryLevel.VERBOSE);
}// Significant events (always captured regardless of level)
Telemetry.event("Intake/Acquired", "Coral detected at " + Timer.getFPGATimestamp());
Telemetry.event("Auto/PathStarted", "Running path: " + pathName);
// Errors (always captured + shown in DriverStation)
Telemetry.error("Launcher", "Overcurrent detected", motorCurrent);
Telemetry.error("Vision", "Camera disconnected", exception);Native WPILib struct support for visualization:
// Pose2d - appears as field visualization in AdvantageScope
Telemetry.record("Drivetrain/Pose", getPose2d(), TelemetryLevel.MATCH);
// ChassisSpeeds - velocity vectors
Telemetry.record("Drivetrain/Speeds", getChassisSpeeds(), TelemetryLevel.MATCH);
// SwerveModuleState[] - swerve visualizer
Telemetry.record("Drivetrain/ModuleStates", getSwerveModuleStates(), TelemetryLevel.LAB);
// Pose2d[] - trajectory visualization
Telemetry.recordPoses("Auto/PlannedPath", trajectoryPoses, TelemetryLevel.LAB);Located at src/main/deploy/telemetry.properties:
# Telemetry verbosity level (NONE, MATCH, LAB, VERBOSE)
telemetry.level=LAB
# Enable USB stick detection for log storage
telemetry.usb.enabled=true
# Log NetworkTables data for full replay in AdvantageScope
telemetry.networktables.enabled=true
# Custom log path (optional, overrides USB detection)
# telemetry.path=/home/lvuser/custom_logsChange level during operation via NetworkTables:
- Open SmartDashboard/Shuffleboard
- Navigate to
Telemetry/Level - Change value to:
NONE,MATCH,LAB, orVERBOSE
Or programmatically:
Telemetry.setLevel(TelemetryLevel.VERBOSE); // For debugging
Telemetry.setLevel(TelemetryLevel.MATCH); // For competition- Format: FAT32 or ext4 (FAT32 recommended for cross-platform)
- Capacity: 8GB+ recommended (typical match log: 10-20MB)
- Speed: USB 2.0 minimum, USB 3.0 preferred
- Insert USB stick into roboRIO USB port
- System auto-creates
FRC_LOGS/directory - Logs are written with timestamped filenames:
FRC_YYYYMMDD_HHMMSS.wpilog
| Path | Description |
|---|---|
/media/sda1/FRC_LOGS |
Primary USB mount on roboRIO 2 |
/U/FRC_LOGS |
Alternate USB mount point |
/home/lvuser/logs |
Fallback (internal storage) |
"During qualification match 47, our launcher failed at 1:45 into the match. The driver pressed the launch button, but nothing happened."
PIT AREA
├── Remove USB stick from roboRIO
├── Insert into pit laptop
└── Navigate to: FRC_LOGS/FRC_20240315_142500.wpilog
AdvantageScope
├── File → Open Log → Select .wpilog file
├── Data tree shows all logged signals:
│ ├── Drivetrain/
│ │ ├── Pose (Pose2d) → Field visualization
│ │ ├── Speeds (ChassisSpeeds)
│ │ └── ModuleStates (SwerveModuleState[])
│ ├── Launcher/
│ │ ├── Voltage
│ │ ├── Current
│ │ ├── RPM
│ │ └── Error (event log)
│ └── Match/
│ ├── Mode (Auto/Teleop/Disabled)
│ └── Time
- Use timeline to navigate to 1:45 (failure time)
- Add signals to graph:
Launcher/Voltage- Was voltage commanded?Launcher/Current- Motor current drawLauncher/RPM- Actual motor speed
- Look for the event marker:
Launcher/Error
Timeline: 1:44:800 ──────────────────────── 1:46:200
Launcher/Voltage
12V ┤ ┌────────────────────
│ │ ← Voltage commanded correctly
0V ┤────────────────────┘
Launcher/Current
40A ┤ ┌──┐ ← Current spike (STALL!)
20A ┤ │ │
0A ┤────────────────────┘ └──────
Launcher/RPM
3000 ┤
0 ┤────────────────────────────── ← RPM stays at 0!
Launcher/Error
─────────────────────────X─────────────────────
└── "Overcurrent: 42.3A"
Diagnosis: Motor stalled (12V applied, 42A current, 0 RPM = mechanical jam)
AdvantageScope supports synchronized playback:
- Field2d view: See robot position at failure
- Swerve visualizer: Check drivetrain state
- Slow motion: Step through critical moments
// Primitives (with level)
Telemetry.record(String key, double value, TelemetryLevel level);
Telemetry.record(String key, int value, TelemetryLevel level);
Telemetry.record(String key, boolean value, TelemetryLevel level);
Telemetry.record(String key, String value, TelemetryLevel level);
Telemetry.record(String key, double[] values, TelemetryLevel level);
// Structured types (AdvantageScope visualization)
Telemetry.record(String key, Pose2d pose, TelemetryLevel level);
Telemetry.record(String key, Pose3d pose, TelemetryLevel level);
Telemetry.record(String key, Rotation2d rotation, TelemetryLevel level);
Telemetry.record(String key, ChassisSpeeds speeds, TelemetryLevel level);
Telemetry.record(String key, SwerveModuleState[] states, TelemetryLevel level);
Telemetry.recordPoses(String key, Pose2d[] poses, TelemetryLevel level);
// Legacy API (defaults to MATCH level)
Telemetry.record(String key, double value);
Telemetry.record(String key, String value);// Events (always captured)
Telemetry.event(String key, String description);
Telemetry.event(String key); // Auto-timestamps
// Errors (always captured + DriverStation notification)
Telemetry.error(String subsystem, String message);
Telemetry.error(String subsystem, String message, double value);
Telemetry.error(String subsystem, String message, Throwable cause);// Register for automatic capture
Telemetry.registerSubsystem(String name, Consumer<String> captureFunction);
// Unregister (if needed)
Telemetry.unregisterSubsystem(String name);// Level management
TelemetryLevel Telemetry.getCurrentLevel();
void Telemetry.setLevel(TelemetryLevel level);
// Status
boolean Telemetry.isInitialized();
// Lifecycle
void Telemetry.initialize(TelemetryConfig config);
void Telemetry.periodic(); // Call in robotPeriodic()
void Telemetry.shutdown(); // Called automatically in Robot.close()When telemetry is active, the following entries are published:
Telemetry/Level- Current verbosity level (editable)Telemetry/Initialized- Initialization statusTelemetry/LogPath- Where logs are being written
Match/Mode- Current mode (Disabled/Auto/Teleop/Test)Match/Time- Match time remainingMatch/Alliance- Red/Blue/Unknown
Drivetrain/Pose- Current robot poseDrivetrain/Speeds- Chassis velocitiesDrivetrain/Heading- Robot headingDrivetrain/ModuleStates/Current- Actual swerve statesDrivetrain/ModuleStates/Desired- Commanded swerve statesDrivetrain/Error- Error messages (if any)
// MATCH: Data you NEED for competition analysis
Telemetry.record("Arm/Position", getPosition(), TelemetryLevel.MATCH);
// LAB: Data useful for tuning and practice
Telemetry.record("Arm/MotorCurrent", getCurrent(), TelemetryLevel.LAB);
// VERBOSE: Data only needed for deep debugging
Telemetry.record("Arm/PID/Derivative", pidD, TelemetryLevel.VERBOSE);// Good: Hierarchical, descriptive
"Drivetrain/FL/DriveMotor/Current"
"Intake/State"
"Auto/PathFollowing/Error"
// Avoid: Flat, ambiguous
"flCurrent"
"state"
"error"public void startIntake() {
Telemetry.event("Intake/Started");
// ...
}
public void stopIntake() {
Telemetry.event("Intake/Stopped", "reason=" + stopReason);
// ...
}// Good: Includes relevant values
Telemetry.error("Launcher", "Overcurrent detected", motorCurrent);
Telemetry.error("Vision", "Tag ambiguity too high", ambiguity);
// Avoid: Vague messages
Telemetry.error("Launcher", "Error occurred");- Check USB detection:
- Look at
Telemetry/LogPathin SmartDashboard - Should show
/media/sda1/FRC_LOGSif USB detected
- Look at
- Verify USB is mounted:
- SSH to roboRIO:
ssh admin@roborio-XXXX-frc.local - Run:
ls /media/sda1
- SSH to roboRIO:
- Check USB format: Must be FAT32 or ext4
- Check permissions:
FRC_LOGSdirectory should be writable
- Reduce telemetry level:
LAB→MATCH - Remove high-frequency VERBOSE-level records
- Use USB with more capacity
- Clean old logs periodically
- Ensure
.wpilogextension (not.log) - Download latest AdvantageScope version
- Check log file isn't corrupted (partial write during power loss)
- AdvantageScope: https://github.com/Mechanical-Advantage/AdvantageScope
- WPILib Data Logging: https://docs.wpilib.org/en/stable/docs/software/telemetry/datalog.html
- NetworkTables: https://docs.wpilib.org/en/stable/docs/software/networktables/index.html
- JUnit 5 is enabled via
useJUnitPlatform(). - Mockito (including the JUnit 5 extension) is available for mocks/spies/captors.
Example test skeleton:
@ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class)
class ExampleTest {
@org.mockito.Mock SomeDep dep;
@org.junit.jupiter.api.Test
void works() {
// arrange
// act
// assert
}
}We enforce minimum coverage and generate HTML & XML reports.
// build.gradle (Groovy DSL)
plugins {
id 'java'
id 'jacoco'
}
test {
useJUnitPlatform()
}
jacoco {
toolVersion = "0.8.10"
}
jacocoTestReport {
dependsOn test
reports {
html.required = true // build/reports/jacoco/test/html/index.html
xml.required = true // build/reports/jacoco/test/jacocoTestReport.xml
csv.required = false
}
}
jacocoTestCoverageVerification {
dependsOn test
violationRules {
rule {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.03 // <-- adjust threshold as we expand our test coverage
}
}
}
}
check.dependsOn jacocoTestReport, jacocoTestCoverageVerificationCreate .github/workflows/ci.yml:
name: FRC Team 2534 CI Pipeline
on:
push:
branches:
- main
- Dev
- Testing
pull_request:
jobs:
build:
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2025-22.04
steps:
- uses: actions/checkout@v4
- name: Add repository to git safe directories
run: git config --global --add safe.directory $GITHUB_WORKSPACE
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Compile and run tests on robot code
run: ./gradlew --no-daemon clean check jacocoTestReport jacocoTestCoverageVerification
- name: Upload JaCoCo HTML report
uses: actions/upload-artifact@v4
if: always()
with:
name: jacoco-html
path: build/reports/jacoco/test/html
- name: Upload JaCoCo XML (for tooling)
uses: actions/upload-artifact@v4
if: always()
with:
name: jacoco-xml
path: build/reports/jacoco/test/jacocoTestReport.xmlIf coverage drops below the configured threshold, the CI job fails and the PR turns red. Reviewers can download the HTML report artifact and open
index.html.
We use Spotless for formatting with a Palantir config.
# Auto-format
./gradlew spotlessApply
# Build + run tests + coverage gates
./gradlew check- Keep units in identifiers (e.g.,
MAX_SPEED_MPS,NOMINAL_BATT_VOLTS,VOLTS_PER_MPS). - Use
*Contextclasses for tunables (PID gains, gear ratios, conversion factors). - For REV SPARK MAX Alternate Encoder on the real robot, configure the data port mode + CPR before selecting
kAlternateOrExternalEncoder. In simulation, prefer the primary encoder feedback unless you fully simulate the alt encoder.
- WPILib Docs: https://docs.wpilib.org/
- WPILib Java API: https://first.wpi.edu/wpilib/allwpilib/docs/release/java/
- Kinematics & Odometry (Swerve): https://docs.wpilib.org/en/stable/docs/software/kinematics-and-odometry/index.html
- Robot Simulation (WPILib): https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/index.html
- 2026 KitBot Reference: https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples
- REV SPARK MAX Docs: https://docs.revrobotics.com/sparkmax/
- CTRE Phoenix 6 (TalonFX): https://v6.docs.ctr-electronics.com/
- PathPlanner: https://pathplanner.dev/
- navX-MXP: https://pdocs.kauailabs.com/navx-mxp/
- PhotonVision: https://docs.photonvision.org/
- Branch from
main. - Keep changes focused; add/extend unit tests.
- Open a PR with:
- Why + What changed
- Testing notes (sim and/or real)
- Risks / required config
- CI must pass (build, tests, coverage gates).
This project follows the WPILib BSD license model unless otherwise noted in the repository.