Panels
All-in-one plugin-based dashboard for FTC robots
What is Panels?
Panels is a comprehensive, plugin-based dashboard for FTC robots developed by Lazar from team 19234 ByteForce. It provides a modern web interface for controlling your robot, visualizing telemetry, tuning parameters, and debugging - all wirelessly from any device on your network.
Panels 1.0 is entirely plugin-driven, allowing you to extend it with custom features using a Svelte frontend and Kotlin backend.
Key Features
Panels provides a complete FTC development toolkit:
- OpMode Control: Driver Station-like interface with Auto/TeleOp selection and timers
- Real-time Telemetry: Live robot data display that can mirror Driver Hub
- Field View: Drawable canvas with coordinate mapping for path visualization
- Graph View: Live data plotting for PID tuning and debugging
- Capture Mode: Record and replay matches for post-analysis
- Configurables: Tune variables in real-time without reuploading code
- Limelight Support: Wireless Limelight 3A control and pipeline tuning
- Dual Gamepads: Control robot wirelessly with two FTC gamepads
- Themes: Customizable colors, layouts, and widget arrangements
- Plugin System: Extend with custom Svelte + Kotlin plugins
- Cross-Platform: Access from any device (laptop, tablet, phone)
- Camera Stream: Optimized FTC webcam streaming
- Battery Monitor: Real-time battery level display
- Ping Monitor: Track input delay and connection quality
- Integrated Docs: Read documentation offline within Panels
- Auto-Updates: Plugins check for updates automatically
Installation
Standard Installation
Add the Dairy repository to your TeamCode build.gradle:
repositories {
maven {
url = 'https://repo.dairy.foundation/releases'
}
}
dependencies {
// Panels (latest: 1.0.12)
implementation 'com.bylazar:fullpanels:1.0.12'
}repositories {
maven {
url = uri("https://repo.dairy.foundation/releases")
}
}
dependencies {
// Panels (latest: 1.0.12)
implementation("com.bylazar:fullpanels:1.0.12")
}With Sloth (Hot Reload)
If you're using Sloth for fast code deployment:
dependencies {
// Sloth-compatible Panels
implementation 'com.bylazar.sloth:fullpanels:0.2.4+1.0.12'
}dependencies {
// Sloth-compatible Panels
implementation("com.bylazar.sloth:fullpanels:0.2.4+1.0.12")
}You can also add individual Panels plugins separately. Check the Dairy repository for all available modules.
Accessing Panels
After installation, connect to Panels dashboard:
- Connect to Robot: Connect your computer to Robot WiFi
- Open Browser: Navigate to
http://192.168.43.1:8000(or your Control Hub IP) - Use Panels: Full dashboard interface loads in browser
Any Device: Panels works on laptops, tablets, and phones - anything with a web browser!
Core Plugins
Panels comes with essential plugins out of the box:
OpMode Control Plugin
Driver Station-style interface:
- Auto/TeleOp Sections: Organized OpMode selection
- Init/Start/Stop: Full OpMode lifecycle control
- Timers: Built-in match timers with auto-stop
- Status Display: Current OpMode and robot state
Telemetry Plugin
Text-based telemetry display:
- Mirrors Driver Hub telemetry
- Organized data display
- Real-time updates
- Customizable formatting
Field Plugin
Interactive field canvas:
- Draw robot position and path
- Coordinate-mapped rendering
- Support for popular pathing libraries (Pedro, Road Runner)
- Custom shape rendering
Graph Plugin
Live data visualization:
- Plot telemetry values over time
- Multiple data series
- Perfect for PID tuning
- Zoom and pan controls
- Export data
Configurables Plugin
Real-time variable tuning:
- Edit values without reuploading code
- Support for all data types
- Instant updates
- Perfect for autonomous tuning
import com.bylazar.ftcontrol.annotations.Configurable;
@TeleOp(name = "Tuning Demo")
public class TuningDemo extends LinearOpMode {
// Make variable tunable in Panels
@Configurable
public double armPower = 0.5;
@Configurable
public int targetPosition = 1000;
@Override
public void runOpMode() {
DcMotor armMotor = hardwareMap.get(DcMotor.class, "arm");
waitForStart();
while (opModeIsActive()) {
// Use tunable values - they update in real-time!
armMotor.setTargetPosition(targetPosition);
armMotor.setPower(armPower);
telemetry.addData("Arm Power", armPower);
telemetry.addData("Target", targetPosition);
telemetry.update();
}
}
}import com.bylazar.ftcontrol.annotations.Configurable
@TeleOp(name = "Tuning Demo")
class TuningDemo : LinearOpMode() {
// Make variable tunable in Panels
@Configurable
var armPower = 0.5
@Configurable
var targetPosition = 1000
override fun runOpMode() {
val armMotor = hardwareMap.get(DcMotor::class.java, "arm")
waitForStart()
while (opModeIsActive()) {
// Use tunable values - they update in real-time!
armMotor.targetPosition = targetPosition
armMotor.power = armPower
telemetry.addData("Arm Power", armPower)
telemetry.addData("Target", targetPosition)
telemetry.update()
}
}
}Limelight Plugin
Wireless Limelight 3A control:
- Live camera stream
- Pipeline switching
- Camera stats tracking
- Telemetry logging
- No USB connection needed
Gamepads Plugin
Wireless robot control:
- Support for up to 2 FTC gamepads
- Driver 1 and Driver 2
- Full button and joystick mapping
- Pre-configured and ready to use
Capture Plugin
Record and replay matches:
- Record all telemetry data
- Replay matches later
- Debug intermittent issues
- Analyze match performance
Themes Plugin
Customize Panels appearance:
- Full color control
- Resizable layouts
- Grid-based widget system
- Navlets for navbar customization
- Multiple tabbed widget groups
- Pre-made themes (blue, red, green, dark, light)
Camera Stream Plugin
FTC webcam streaming:
- Optimized for FTC cameras
- Inspired by Limelight Stream Widget
- Low latency
- Adjustable quality
Lights Plugin
goBILDA-style RGB indicators:
- Fast glanceable feedback
- Visual status indicators
- Development aid
Battery & Pinger Plugins
System monitoring:
- Real-time battery level
- Input delay tracking
- Connection quality monitoring
- Keep system responsive
Integrated Docs Plugin
Built-in documentation:
- Read Panels docs within dashboard
- Works offline
- No internet needed
Using Panels
Basic Telemetry
@TeleOp(name = "Panels Basic")
public class PanelsBasic extends LinearOpMode {
@Override
public void runOpMode() {
DcMotor motor = hardwareMap.get(DcMotor.class, "motor");
waitForStart();
while (opModeIsActive()) {
double power = -gamepad1.left_stick_y;
motor.setPower(power);
// Telemetry shows in Panels AND Driver Hub
telemetry.addData("Motor Power", power);
telemetry.addData("Position", motor.getCurrentPosition());
telemetry.update();
}
}
}@TeleOp(name = "Panels Basic")
class PanelsBasic : LinearOpMode() {
override fun runOpMode() {
val motor = hardwareMap.get(DcMotor::class.java, "motor")
waitForStart()
while (opModeIsActive()) {
val power = -gamepad1.left_stick_y
motor.power = power
// Telemetry shows in Panels AND Driver Hub
telemetry.addData("Motor Power", power)
telemetry.addData("Position", motor.currentPosition)
telemetry.update()
}
}
}Field Visualization
import com.bylazar.ftcontrol.field.Field;
@Autonomous(name = "Field Demo")
public class FieldDemo extends LinearOpMode {
@Override
public void runOpMode() {
waitForStart();
// Draw on field (coordinate-mapped)
Field field = Field.getInstance();
while (opModeIsActive()) {
// Draw robot position
field.drawRobot(24, 12, Math.toRadians(90));
// Draw path
field.drawLine(0, 0, 24, 24);
field.drawCircle(48, 48, 6);
field.update();
}
}
}import com.bylazar.ftcontrol.field.Field
@Autonomous(name = "Field Demo")
class FieldDemo : LinearOpMode() {
override fun runOpMode() {
waitForStart()
// Draw on field (coordinate-mapped)
val field = Field.getInstance()
while (opModeIsActive()) {
// Draw robot position
field.drawRobot(24.0, 12.0, Math.toRadians(90.0))
// Draw path
field.drawLine(0.0, 0.0, 24.0, 24.0)
field.drawCircle(48.0, 48.0, 6.0)
field.update()
}
}
}Graph Data
import com.bylazar.ftcontrol.graph.Graph;
@TeleOp(name = "PID Tuning")
public class PIDTuning extends LinearOpMode {
@Override
public void runOpMode() {
Graph graph = Graph.getInstance();
PIDController controller = new PIDController(0.01, 0.0, 0.0);
waitForStart();
while (opModeIsActive()) {
double target = 1000;
double current = motor.getCurrentPosition();
double output = controller.calculate(current, target);
// Plot on graph
graph.addPoint("Target", target);
graph.addPoint("Current", current);
graph.addPoint("Output", output);
graph.update();
}
}
}import com.bylazar.ftcontrol.graph.Graph
@TeleOp(name = "PID Tuning")
class PIDTuning : LinearOpMode() {
override fun runOpMode() {
val graph = Graph.getInstance()
val controller = PIDController(0.01, 0.0, 0.0)
waitForStart()
while (opModeIsActive()) {
val target = 1000.0
val current = motor.currentPosition.toDouble()
val output = controller.calculate(current, target)
// Plot on graph
graph.addPoint("Target", target)
graph.addPoint("Current", current)
graph.addPoint("Output", output)
graph.update()
}
}
}Creating Custom Plugins
Panels supports custom plugins using Svelte (frontend) and Kotlin (backend):
Plugin Structure
my-plugin/
├── frontend/ (Svelte UI)
│ ├── Plugin.svelte
│ └── components/
└── backend/ (Kotlin logic)
└── MyPlugin.ktExample Plugin
package com.myteam.plugins
import com.bylazar.ftcontrol.plugin.Plugin
import com.bylazar.ftcontrol.plugin.PluginMetadata
@PluginMetadata(
name = "My Custom Plugin",
version = "1.0.0",
description = "Does something cool"
)
class MyPlugin : Plugin {
override fun init() {
// Plugin initialization
}
override fun update() {
// Called every loop
val data = computeSomething()
sendToFrontend("myData", data)
}
}<script lang="ts">
import { onMount } from 'svelte';
import { panelsAPI } from '$lib/panels';
let myData = 0;
onMount(() => {
// Listen for backend data
panelsAPI.on('myData', (data) => {
myData = data;
});
});
</script>
<div class="plugin">
<h2>My Custom Plugin</h2>
<p>Data: {myData}</p>
</div>Check the Panels Plugin Development docs for full guide.
Panels vs FTC Dashboard
| Feature | Panels | FTC Dashboard |
|---|---|---|
| UI | Modern Svelte | React-based |
| Plugins | Full plugin system | Limited |
| Limelight | Built-in support | Requires setup |
| Gamepads | Wireless control | No |
| Themes | Highly customizable | Basic |
| Field View | Yes | Yes |
| Graphs | Yes | Yes |
| Configurables | Yes | Yes (@Config) |
| OpMode Control | Yes | Limited |
| Capture/Replay | Yes | No |
| Learning Curve | Moderate | Low |
When to Use Panels
Use Panels when:
- You want all-in-one dashboard solution
- You need Limelight wireless control
- You want wireless gamepad control
- You like modern, customizable UI
- You want plugin extensibility
- You need capture/replay for debugging
Use FTC Dashboard when:
- You want simplest setup
- You only need basic telemetry
- Your team is already familiar with it
- You don't need Limelight features
Tips for Success
- Bookmark Dashboard: Save
http://192.168.43.1:8000for quick access - Organize Widgets: Use tabs to group related data
- Use Configurables: Tune parameters without reuploading
- Graph Everything: Visual feedback helps PID tuning
- Custom Themes: Make dashboard easy on your eyes
- Try Plugins: Explore all built-in plugins
- Capture Matches: Record for post-analysis
- Learn Hotkeys: Speed up your workflow
Resources
- Website: panels.bylazar.com
- Documentation: Panels Docs
- GitHub: ftcontrol/ftcontrol-panels
- Repository: Dairy Foundation Repo
- Team: 19234 ByteForce
- Creator: Lazar
Next Steps
- Add Panels to your
build.gradle - Sync Gradle and build project
- Deploy to robot
- Connect to robot WiFi
- Open
http://192.168.43.1:8000in browser - Explore all the plugins
- Try
@Configurablefor tuning - Customize your theme
- Create custom plugins if needed
Pro Tip: Combine Panels with Sloth for hot code reload and live Configurables updates - the ultimate FTC development experience!