Unit 5 · Lesson 6

SmartDashboard and Shuffleboard

Every robot system you build produces data: motor current, sensor readings, state machine flags, PID error. Without a way to see that data in real time, you are debugging blind. SmartDashboard and Shuffleboard are the windows into your robot's live state — and they work whether the robot is enabled or sitting in a pit with the Driver Station connected.

By the end of this lesson, you will:

  • Publish numbers, booleans, strings, and choosers to SmartDashboard using the correct put methods
  • Read values from SmartDashboard for over-the-wire tuning without redeploying code
  • Organize your Shuffleboard layout into tabs for Drive, Mechanisms, and Diagnostics
  • Implement a SendableChooser for autonomous strategy selection from the Driver Station
  • Describe how NetworkTables underlies SmartDashboard and Shuffleboard

Why the Dashboard Exists

You cannot attach a debugger to a robot and pause it mid-match. The next best thing is live telemetry — a stream of data flowing from robot code to a screen on the driver station laptop. SmartDashboard (and its successor Shuffleboard) receives that stream over the same network link as the Driver Station control connection.

The data flows through NetworkTables — a distributed key-value store built into WPILib. When you call SmartDashboard.putNumber("Speed", 0.5), you're writing a value to a NetworkTable entry named "Speed." Shuffleboard, AdvantageScope, and any other tool connected to that table can read it. Your robot code never knows who's listening — it just publishes.

Dashboard Type Explorer

SmartDashboard can publish four primitive types plus more complex widgets. Click each to see the API call, what widget it appears as, and the FRC use case.

Dashboard Type Explorer — click any type
📊 Number
🔵 Boolean
💬 String
📋 Chooser
← click any type to see the API and FRC use case

Writing the Basic Put Calls

All dashboard calls go in robotPeriodic() so they update in every mode — including disabled. This is the single most important dashboard rule: data that only updates in teleop is useless for pre-match diagnostics.

// In robotPeriodic() — updates every 20ms in all modes @Override public void robotPeriodic() { // Drive system SmartDashboard.putNumber("Battery V", RobotController.getBatteryVoltage()); SmartDashboard.putNumber("Left Amps", leftMotor.getStatorCurrent().getValueAsDouble()); SmartDashboard.putNumber("Right Amps", rightMotor.getStatorCurrent().getValueAsDouble()); SmartDashboard.putNumber("Drive Vel", leftMotor.getVelocity().getValueAsDouble()); // Intake state SmartDashboard.putBoolean("Has Game Piece", intake.hasGamePiece()); SmartDashboard.putBoolean("Beam Break Raw", beamBreak.get()); SmartDashboard.putString("Intake State", intakeState.name()); // Gyro SmartDashboard.putNumber("Yaw Deg", gyro.getYaw().getValueAsDouble()); // Match info SmartDashboard.putNumber("Match Time", DriverStation.getMatchTime()); SmartDashboard.putString("Alliance", DriverStation.getAlliance().toString()); }

Reading from the Dashboard: Live Tuning

SmartDashboard is not just for output — you can also read from it. This lets you change constants like PID gains or speed limits from the laptop without redeploying. The robot reads the value every cycle, so changes take effect immediately.

// In robotInit() — set the DEFAULT value (what the dashboard shows if nobody has changed it) SmartDashboard.putNumber("Speed Scale", 0.6); SmartDashboard.putNumber("Intake Speed", 0.65); SmartDashboard.putNumber("P Gain", 0.08); // In teleopPeriodic() — read the CURRENT value each cycle double speedScale = SmartDashboard.getNumber("Speed Scale", 0.6); // default if key missing double intakeSpeed = SmartDashboard.getNumber("Intake Speed", 0.65); double kP = SmartDashboard.getNumber("P Gain", 0.08); // Use the values as you normally would drive.arcadeDrive(fwd * speedScale, rot * speedScale);
💡 Dashboard tuning vs. Constants.java

Dashboard tuning is a development tool, not a production workflow. Use it during calibration sessions to find good values quickly. Once you find a working value, move it into Constants.java and remove the getNumber() call. A production robot that reads all its constants from the dashboard is a robot that loses its tuning if the dashboard connection is interrupted, or if someone accidentally edits a value during a match.

Autonomous Strategy with SendableChooser

A SendableChooser displays a dropdown menu on the dashboard. Your drive team selects the autonomous strategy before the match; your code reads the selection and runs the corresponding routine. This eliminates deploying code between matches to swap auto programs.

// Declare — usually as a field private final SendableChooser<String> autoChooser = new SendableChooser<>(); @Override public void robotInit() { // Add options — setDefaultOption marks the safe fallback autoChooser.setDefaultOption("Do Nothing", "none"); autoChooser.addOption("Drive Forward 1m", "drive1m"); autoChooser.addOption("Drive + Score", "driveScore"); autoChooser.addOption("Two Piece Center", "twoPiece"); // Publish to Shuffleboard — appears as a dropdown widget SmartDashboard.putData("Auto Strategy", autoChooser); } @Override public void autonomousInit() { // Read the selection made by the drive team String selected = autoChooser.getSelected(); System.out.println("Auto selected: " + selected); switch (selected) { case "drive1m": autoState = AutoState.DRIVE_FORWARD; break; case "driveScore": autoState = AutoState.DRIVE_SCORE; break; case "twoPiece": autoState = AutoState.TWO_PIECE; break; default: autoState = AutoState.IDLE; } }

Shuffleboard: Organized Layout

SmartDashboard drops everything in one flat list. Shuffleboard lets you organize data into tabs and position widgets precisely. This is what the drive team actually looks at during a match — a well-organized dashboard panel is faster to read under pressure than a cluttered list.

// Shuffleboard API — organized into named tabs import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; @Override public void robotInit() { // Get or create named tabs ShuffleboardTab driveTab = Shuffleboard.getTab("Drive"); ShuffleboardTab mechTab = Shuffleboard.getTab("Mechanisms"); ShuffleboardTab diagTab = Shuffleboard.getTab("Diagnostics"); // Add widgets to tabs — position (col, row) and size (width, height) driveTab.addDouble("Left Amps", () -> leftMotor.getStatorCurrent().getValueAsDouble()) .withPosition(0, 0).withSize(2, 1); driveTab.addDouble("Right Amps", () -> rightMotor.getStatorCurrent().getValueAsDouble()) .withPosition(2, 0).withSize(2, 1); driveTab.addBoolean("Has Game Piece", () -> intake.hasGamePiece()) .withPosition(0, 1).withSize(2, 1); // Chooser on drive tab (drive team selects auto) driveTab.add("Auto Strategy", autoChooser).withPosition(4, 0).withSize(3, 1); // Shuffleboard entries update automatically — no need to put() each cycle }

The drive team reads the dashboard in a fraction of a second. Every layout decision either helps or hurts that reading speed.

  • Drive tab: Battery voltage (top left — highest priority), alliance color, match time, game-piece indicator, auto strategy chooser. Nothing else. If the drive team has to hunt for these values, the layout is wrong.
  • Mechanisms tab: State machine labels for each subsystem, current draw per motor, sensor states. Used by the operator and pit crew during calibration.
  • Diagnostics tab: Raw sensor values, encoder positions, CAN bus health, watchdog timing. Used by programmers only — never in a match context.
  • Boolean box indicators: Use large boolean boxes with clear labels (green = good, red = fault) rather than tiny text fields for status that the drive team needs to see at a glance.
  • Save the layout. Shuffleboard layouts are saved as a JSON file. Commit it to your Git repository. A rebuilt laptop restores the layout in seconds.

Dashboard as Debugging Checklist

Select each scenario below to see which dashboard items to add and what they reveal about the underlying problem.

Dashboard Scenarios
01 The robot feels "laggy" — driver input seems delayed
02 The intake sometimes stops randomly during teleop
03 The autonomous routine worked at practice but not at the event
Dashboard items to add
🔍 LRI Observation

During pit visits at competition I ask teams to pull up their Shuffleboard and show me the robot's current state with the robot enabled and stationary. Teams with a well-organized dashboard — battery voltage visible at the top, current per motor, state machine labels, game piece indicator — can confirm in fifteen seconds that everything is nominal before queuing. Teams with a flat list of 40 values in SmartDashboard have to hunt. Teams with no dashboard at all are debugging with their eyes. A robot with a clean dashboard is a robot whose problems get caught before the match, not during it.

⚙️ 🔌 System Check
  • All dashboard writes in robotPeriodic(). Data visible in disabled, auto, and teleop is infinitely more useful than data visible only during a match.
  • Set default values in robotInit(), read current values in periodic. The putNumber() call in robotInit() initializes the dashboard entry — the getNumber() call in periodic reads whatever the user has set, defaulting to the init value if the key is missing.
  • Move dashboard-tuned values to Constants.java before competition. Dashboard reads are a development tool. Production robots should not depend on the dashboard connection for correct behavior.
  • Use setDefaultOption() for a safe autonomous fallback. The default option loads if no human selection has been made — set it to "Do Nothing" or the simplest safe auto so a DS connection issue doesn't produce a rogue autonomous.
  • Commit your Shuffleboard layout file to Git. The layout lives in a JSON file in your project. Adding it to source control means any team laptop restores the organized layout without manual reconfiguration.

Knowledge Check

A team puts their battery voltage display in teleopPeriodic(). The drive team checks the dashboard before a match while the robot is disabled. What do they see?
  • 1The current battery voltage — SmartDashboard updates even when disabled
  • 2The last value published — which may be from the end of a previous match, or the default value if the robot was never enabled; the display is stale and useless for pre-match battery decisions
  • 3Zero — SmartDashboard shows 0 when no recent value was published
  • 4An error message indicating the robot is disabled
A SendableChooser has no setDefaultOption() set. The DS laptop crashes and reconnects two seconds before autonomous starts. What does autoChooser.getSelected() return?
  • 1The last option the drive team selected before the crash
  • 2null — if no default was set and no selection was made, getSelected() returns null; code that passes null to a switch without a null check will throw a NullPointerException and disable the robot program
  • 3The first option added with addOption()
  • 4An empty string
What is the correct workflow for using SmartDashboard to tune a PID gain during a practice session?
  • 1Leave the getNumber() calls in the code permanently — live tuning is always better than Constants.java
  • 2Use putNumber() in robotInit() to set a default, getNumber() in periodic to read it, iterate on the value from the dashboard during testing, then move the final value to Constants.java and remove the getNumber() call before competition
  • 3Edit Constants.java between each test run and redeploy
  • 4Use Shuffleboard's built-in PID tuner widget — it automatically updates the robot's PID controller
💪 Practice Prompt

Build a Competition-Ready Dashboard

  1. Move all your existing SmartDashboard calls from any method into robotPeriodic(). Verify that battery voltage, motor current, and sensor states are visible while the robot is disabled.
  2. Add a SendableChooser<String> with at least three auto options (including "Do Nothing" as the default). Publish it to SmartDashboard. In autonomousInit(), read the selection and print it to the console.
  3. Implement one dashboard-readable constant: put SmartDashboard.putNumber("Speed Scale", 0.6) in robotInit(). Read it each teleop cycle with getNumber(). Demonstrate live tuning by changing the value on the dashboard while the robot is running and observing the speed change without redeployment.
  4. Organize your SmartDashboard into a Shuffleboard layout with two tabs: "Drive" (battery, motor amps, game piece indicator, auto chooser) and "Diagnostics" (raw sensor values, encoder positions). Save the layout file and commit it to Git.
  5. Bonus: Add a "Fault Summary" string to SmartDashboard that concatenates any active fault conditions: if a motor is drawing over a threshold current, if the gyro yaw is changing when the robot is stationary, or if a limit switch is stuck. A single-string health indicator is faster to scan than five individual booleans.