CrocoDocs
CrocoDocs

Introduction

What is CrocoDocs?Season Breakdown

Getting Started

Programming in FTCJavaBlocksAndroid Studio

Control Systems

IntroductionJoystick MappingPID ControlMotion ProfilingKalman FilterLow-Pass Filter

Autonomous

IntroductionTime vs Encoder-Based MovementOdometryMotion PlanningPure PursuitSensor Fusion

Codebase Etiquette and Good Practices

IntroductionNaming ConventionsCode OrganizationComments and DocumentationTeam Collaboration

Libraries

LibrariesNextFTCPedro PathingFTC DashboardMercurialPanelsSloth

Sensors and Vision

Vision OverviewVision Basics

FTC Dashboard

Real-time telemetry, configuration, and debugging tool

What is FTC Dashboard?

FTC Dashboard is a real-time debugging and configuration tool that runs in your web browser. It lets you see telemetry data, adjust variables on-the-fly, send commands to your robot, and even view camera streams — all without touching your phone or Driver Station.

Think of it like Chrome DevTools, but for your FTC robot.

Why Use FTC Dashboard?

Real-Time Debugging:

  • See all telemetry in a clean web interface
  • No squinting at tiny phone screens
  • Graphs for visualizing sensor data
  • Save telemetry logs for later analysis

Live Tuning:

  • Adjust PID constants without redeploying code
  • Change autonomous positions on the fly
  • Test different configurations instantly
  • No more "compile, deploy, test, repeat"

Camera View:

  • Stream camera feed to browser
  • See exactly what your vision pipeline detects
  • Debug AprilTag and color detection
  • Draw debug information on the camera feed

Field View:

  • Visualize robot position on a field map
  • See path planning in real-time
  • Perfect for autonomous debugging

Installation

Step 1: Add the Dependency

In your build.gradle (Module: TeamCode):

dependencies {
    implementation 'com.acmerobotics.dashboard:dashboard:0.4.15'
    // Check GitHub for latest version
}

Step 2: Sync Project

Click "Sync Now" in Android Studio.

Step 3: Connect to Dashboard

  1. Make sure your computer and Control Hub are on the same network
  2. Open a web browser
  3. Go to: http://192.168.43.1:8080/dash (or your Control Hub's IP address)

That's it! Dashboard is now running.

Basic Usage

Sending Telemetry

import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;

@TeleOp(name = "Dashboard Example")
public class DashboardExample extends LinearOpMode {
    @Override
    public void runOpMode() {
        FtcDashboard dashboard = FtcDashboard.getInstance();
        DcMotor motor = hardwareMap.get(DcMotor.class, "motor");
        
        waitForStart();
        
        while (opModeIsActive()) {
            TelemetryPacket packet = new TelemetryPacket();
            
            // Add data to dashboard
            packet.put("Motor Power", motor.getPower());
            packet.put("Motor Position", motor.getCurrentPosition());
            packet.put("Loop Time", getRuntime());
            
            // Send to dashboard
            dashboard.sendTelemetryPacket(packet);
            
            // Also show on Driver Station
            telemetry.addData("Motor Power", motor.getPower());
            telemetry.update();
        }
    }
}
import com.acmerobotics.dashboard.FtcDashboard
import com.acmerobotics.dashboard.telemetry.TelemetryPacket

@TeleOp(name = "Dashboard Example")
class DashboardExample : LinearOpMode() {
    override fun runOpMode() {
        val dashboard = FtcDashboard.getInstance()
        val motor = hardwareMap.get(DcMotor::class.java, "motor")
        
        waitForStart()
        
        while (opModeIsActive()) {
            val packet = TelemetryPacket()
            
            // Add data to dashboard
            packet.put("Motor Power", motor.power)
            packet.put("Motor Position", motor.currentPosition)
            packet.put("Loop Time", runtime)
            
            // Send to dashboard
            dashboard.sendTelemetryPacket(packet)
            
            // Also show on Driver Station
            telemetry.addData("Motor Power", motor.power)
            telemetry.update()
        }
    }
}

Configuration Variables

Use @Config to make variables adjustable from the dashboard:

import com.acmerobotics.dashboard.config.Config;

@Config
@TeleOp(name = "PID Tuning")
public class PIDTuning extends LinearOpMode {
    // These can be adjusted in the dashboard!
    public static double kP = 0.05;
    public static double kI = 0.01;
    public static double kD = 0.004;
    
    public static int targetPosition = 1000;
    
    @Override
    public void runOpMode() {
        DcMotor armMotor = hardwareMap.get(DcMotor.class, "arm");
        PIDController pid = new PIDController(kP, kI, kD);
        
        waitForStart();
        
        while (opModeIsActive()) {
            // Update PID with dashboard values
            pid.setCoefficients(kP, kI, kD);
            
            double power = pid.calculate(targetPosition, armMotor.getCurrentPosition());
            armMotor.setPower(power);
            
            telemetry.addData("Position", armMotor.getCurrentPosition());
            telemetry.addData("Target", targetPosition);
            telemetry.addData("Power", power);
            telemetry.update();
        }
    }
}
import com.acmerobotics.dashboard.config.Config

@Config
@TeleOp(name = "PID Tuning")
class PIDTuning : LinearOpMode() {
    companion object {
        // These can be adjusted in the dashboard!
        @JvmField var kP = 0.05
        @JvmField var kI = 0.01
        @JvmField var kD = 0.004
        
        @JvmField var targetPosition = 1000
    }
    
    override fun runOpMode() {
        val armMotor = hardwareMap.get(DcMotor::class.java, "arm")
        val pid = PIDController(kP, kI, kD)
        
        waitForStart()
        
        while (opModeIsActive()) {
            // Update PID with dashboard values
            pid.setCoefficients(kP, kI, kD)
            
            val power = pid.calculate(targetPosition.toDouble(), armMotor.currentPosition.toDouble())
            armMotor.power = power
            
            telemetry.addData("Position", armMotor.currentPosition)
            telemetry.addData("Target", targetPosition)
            telemetry.addData("Power", power)
            telemetry.update()
        }
    }
}

Now you can tune PID values in real-time from your browser!

Field Overlay

Draw robot position and paths on a field diagram:

import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.canvas.Canvas;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;

@Autonomous(name = "Field Overlay")
public class FieldOverlay extends LinearOpMode {
    @Override
    public void runOpMode() {
        FtcDashboard dashboard = FtcDashboard.getInstance();
        
        waitForStart();
        
        double x = 0, y = 0, heading = 0;
        
        while (opModeIsActive()) {
            // Update robot position (from odometry/localization)
            x += 0.5;  // Example: moving forward
            
            TelemetryPacket packet = new TelemetryPacket();
            Canvas canvas = packet.fieldOverlay();
            
            // Draw robot position
            canvas.setStroke("#3F51B5");  // Blue
            canvas.strokeCircle(x, y, 9);  // Robot as a circle
            
            // Draw heading line
            double headingX = x + 9 * Math.cos(heading);
            double headingY = y + 9 * Math.sin(heading);
            canvas.strokeLine(x, y, headingX, headingY);
            
            // Draw target position
            canvas.setStroke("#FF5722");  // Red
            canvas.strokeCircle(48, 48, 5);
            
            packet.put("X", x);
            packet.put("Y", y);
            packet.put("Heading", Math.toDegrees(heading));
            
            dashboard.sendTelemetryPacket(packet);
        }
    }
}
import com.acmerobotics.dashboard.FtcDashboard
import com.acmerobotics.dashboard.canvas.Canvas
import com.acmerobotics.dashboard.telemetry.TelemetryPacket

@Autonomous(name = "Field Overlay")
class FieldOverlay : LinearOpMode() {
    override fun runOpMode() {
        val dashboard = FtcDashboard.getInstance()
        
        waitForStart()
        
        var x = 0.0
        var y = 0.0
        var heading = 0.0
        
        while (opModeIsActive()) {
            // Update robot position (from odometry/localization)
            x += 0.5  // Example: moving forward
            
            val packet = TelemetryPacket()
            val canvas = packet.fieldOverlay()
            
            // Draw robot position
            canvas.setStroke("#3F51B5")  // Blue
            canvas.strokeCircle(x, y, 9.0)  // Robot as a circle
            
            // Draw heading line
            val headingX = x + 9 * Math.cos(heading)
            val headingY = y + 9 * Math.sin(heading)
            canvas.strokeLine(x, y, headingX, headingY)
            
            // Draw target position
            canvas.setStroke("#FF5722")  // Red
            canvas.strokeCircle(48.0, 48.0, 5.0)
            
            packet.put("X", x)
            packet.put("Y", y)
            packet.put("Heading", Math.toDegrees(heading))
            
            dashboard.sendTelemetryPacket(packet)
        }
    }
}

Camera Streaming

Stream camera feed to the dashboard:

import com.acmerobotics.dashboard.FtcDashboard;
import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName;
import org.openftc.easyopencv.OpenCvCamera;
import org.openftc.easyopencv.OpenCvCameraFactory;
import org.openftc.easyopencv.OpenCvCameraRotation;

@TeleOp(name = "Camera Stream")
public class CameraStream extends LinearOpMode {
    @Override
    public void runOpMode() {
        FtcDashboard dashboard = FtcDashboard.getInstance();
        
        // Set up camera
        int cameraMonitorViewId = hardwareMap.appContext.getResources()
            .getIdentifier("cameraMonitorViewId", "id", 
                hardwareMap.appContext.getPackageName());
        
        WebcamName webcamName = hardwareMap.get(WebcamName.class, "Webcam 1");
        OpenCvCamera camera = OpenCvCameraFactory.getInstance()
            .createWebcam(webcamName, cameraMonitorViewId);
        
        // Start streaming to dashboard
        dashboard.startCameraStream(camera, 30);  // 30 FPS
        
        camera.openCameraDeviceAsync(new OpenCvCamera.AsyncCameraOpenListener() {
            @Override
            public void onOpened() {
                camera.startStreaming(640, 480, OpenCvCameraRotation.UPRIGHT);
            }
            
            @Override
            public void onError(int errorCode) {
                telemetry.addData("Camera Error", errorCode);
                telemetry.update();
            }
        });
        
        waitForStart();
        
        while (opModeIsActive()) {
            // Your code here
            sleep(50);
        }
    }
}
import com.acmerobotics.dashboard.FtcDashboard
import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName
import org.openftc.easyopencv.OpenCvCamera
import org.openftc.easyopencv.OpenCvCameraFactory
import org.openftc.easyopencv.OpenCvCameraRotation

@TeleOp(name = "Camera Stream")
class CameraStream : LinearOpMode() {
    override fun runOpMode() {
        val dashboard = FtcDashboard.getInstance()
        
        // Set up camera
        val cameraMonitorViewId = hardwareMap.appContext.resources
            .getIdentifier("cameraMonitorViewId", "id", 
                hardwareMap.appContext.packageName)
        
        val webcamName = hardwareMap.get(WebcamName::class.java, "Webcam 1")
        val camera = OpenCvCameraFactory.getInstance()
            .createWebcam(webcamName, cameraMonitorViewId)
        
        // Start streaming to dashboard
        dashboard.startCameraStream(camera, 30.0)  // 30 FPS
        
        camera.openCameraDeviceAsync(object : OpenCvCamera.AsyncCameraOpenListener {
            override fun onOpened() {
                camera.startStreaming(640, 480, OpenCvCameraRotation.UPRIGHT)
            }
            
            override fun onError(errorCode: Int) {
                telemetry.addData("Camera Error", errorCode)
                telemetry.update()
            }
        })
        
        waitForStart()
        
        while (opModeIsActive()) {
            // Your code here
            sleep(50)
        }
    }
}

Graphing Data

Create live graphs for sensor data:

TelemetryPacket packet = new TelemetryPacket();

// Add data points to create a graph
packet.put("Motor Velocity", motor.getVelocity());
packet.put("Target Velocity", targetVelocity);
packet.put("Voltage", hardwareMap.voltageSensor.get("Control Hub").getVoltage());

dashboard.sendTelemetryPacket(packet);

The dashboard automatically creates graphs for numeric values over time!

OpMode Selection

You can start/stop OpModes from the dashboard:

  1. Open the dashboard in your browser
  2. Navigate to the "Op Mode" tab
  3. Select and run OpModes without touching your phone
  4. Perfect for testing on the practice field

Tips for Maximum Effectiveness

1. Organize Your Telemetry

// Use prefixes to group related data
packet.put("Drive/FrontLeft", frontLeft.getPower());
packet.put("Drive/FrontRight", frontRight.getPower());
packet.put("Arm/Position", armMotor.getCurrentPosition());
packet.put("Arm/Target", armTarget);

2. Use @Config for Everything You Might Tune

@Config
public class RobotConstants {
    public static double DRIVE_SPEED = 0.8;
    public static double TURN_SPEED = 0.6;
    public static double ARM_SPEED = 0.5;
    
    public static double ARM_HIGH = 1000;
    public static double ARM_MID = 500;
    public static double ARM_LOW = 100;
}

3. Add Visual Indicators

// Use canvas to draw debug information
Canvas canvas = packet.fieldOverlay();
canvas.setStroke("#4CAF50");  // Green for success
canvas.fillCircle(detectionX, detectionY, 3);

4. Log Important Events

if (gamepad1.a) {
    packet.put("Event", "Button A Pressed");
    packet.put("Timestamp", getRuntime());
}

Common Use Cases

PID Tuning:

  • Adjust kP, kI, kD in real-time
  • See response curves on graphs
  • Save working values to code

Vision Debugging:

  • Stream camera to see what robot sees
  • Draw detection boxes on overlay
  • Verify color thresholds

Autonomous Development:

  • Visualize path on field overlay
  • Track position errors
  • Fine-tune waypoint coordinates

Performance Monitoring:

  • Track loop times
  • Monitor battery voltage
  • Check motor temperatures

Pros and Cons

Advantages:

  • ✅ Real-time debugging without code changes
  • ✅ Clean web interface accessible from any device
  • ✅ Essential for PID tuning
  • ✅ Camera streaming is incredibly useful
  • ✅ Field visualization helps with autonomous
  • ✅ Industry-standard tool

Disadvantages:

  • ❌ Requires network connection to Control Hub
  • ❌ Slight performance overhead
  • ❌ Another tool to learn
  • ❌ Can be overwhelming with too much data

When to Use FTC Dashboard

Always use it for:

  • PID tuning
  • Autonomous debugging
  • Vision pipeline development
  • Performance optimization

Consider skipping for:

  • Simple drive practice (Driver Station is fine)
  • Competition matches (it's just for testing)

Resources

  • GitHub: FTC Dashboard Repository
  • Documentation: README and examples on GitHub
  • Community: r/FTC subreddit and Discord

Best Practices

  1. Keep It Running: Start dashboard at beginning of practice
  2. Organize Data: Use clear naming and grouping
  3. Graph Sparingly: Too many graphs slow things down
  4. Save Configs: Document working @Config values
  5. Use During Testing: Not during competition matches

Example Project Structure

TeamCode/
  config/
    RobotConstants.java      (@Config for all tunable values)
  opmodes/
    TeleOpMain.java          (Basic driving)
    PIDTuning.java           (Use dashboard for tuning)
    VisionTest.java          (Camera streaming)
  subsystems/
    Drive.java               (Send drive telemetry)
    Arm.java                 (Send arm position)

Next Steps

  1. Add FTC Dashboard dependency
  2. Convert telemetry to use TelemetryPacket
  3. Add @Config to tunable variables
  4. Test PID tuning with live graphs
  5. Set up camera streaming
  6. Create field overlay for autonomous!

Pedro Pathing

Modern path following system with smooth trajectories

Mercurial

Command-based robotics framework by Dairy Foundation

On this page

What is FTC Dashboard?Why Use FTC Dashboard?InstallationStep 1: Add the DependencyStep 2: Sync ProjectStep 3: Connect to DashboardBasic UsageSending TelemetryConfiguration VariablesField OverlayCamera StreamingGraphing DataOpMode SelectionTips for Maximum Effectiveness1. Organize Your Telemetry2. Use @Config for Everything You Might Tune3. Add Visual Indicators4. Log Important EventsCommon Use CasesPros and ConsWhen to Use FTC DashboardResourcesBest PracticesExample Project StructureNext Steps