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

Sloth

Lightning-fast hot code reload for FTC

What is Sloth?

Sloth is the fastest hot code reload library for FTC, developed by Dairy Foundation. It enables ultra-fast Write → Run → Test development cycles by uploading only your TeamCode to the robot in under 1 second, eliminating the typical 40+ second wait.

Sloth is also a Sinister runtime that enables classpath scanning and dynamic loading on Android FTC, supporting libraries that need to react to hot reloading.

Speed Comparison: Traditional deployment takes 40+ seconds. Sloth takes under 1 second.

Why Use Sloth?

Problems with Traditional FTC Development:

  • Every code change requires full rebuild (40+ seconds)
  • Slow feedback loop kills productivity
  • Testing small changes takes forever
  • Waiting disrupts flow state

Sloth Solutions:

  • Upload code in under 1 second
  • Changes persist across restarts and power cycles
  • Safe deployment - processes changes after OpMode ends
  • Works with FTC Dashboard, Panels, and other libraries
  • Includes @Pinned annotation to prevent hot-swapping critical classes
  • Supports custom classpath scanning and hot reload listeners

Key Improvements Over Fastload

Sloth is the successor to fastload with major enhancements:

FeaturefastloadSloth
Upload Speed~7 seconds~1 second
PersistenceLost on restartSurvives restarts
Deployment SafetyImmediateAfter OpMode ends
Runtime CapabilitiesBasicFull Sinister runtime
Dashboard SupportLimitedFull hot reload support
SDK UpdatesManualAutomatic (including hardware map)

How It Works

Sloth uses dynamic class loading to:

  1. Detect Changes: Monitors TeamCode for modifications
  2. Upload Fast: Sends only changed code (not entire app)
  3. Safe Swap: Waits for OpMode to end before applying changes
  4. Update SDK: Refreshes hardware map and other SDK components
  5. Notify Libraries: Triggers hot reload listeners in compatible libraries

Scope and Limitations

Hot reloaded:

  • Classes in org.firstinspires.ftc.teamcode package
  • Your OpModes and custom classes
  • Drivers uploaded with your code

NOT hot reloaded:

  • Classes outside TeamCode package
  • @Pinned classes (and their subclasses)
  • Library changes (requires full install)
  • SDK modifications

Important: If you make changes that aren't hot reloaded (libraries, SDK, etc.), perform a full install to propagate them.

Installation

Option 1: Dairy Templates (Recommended)

Use Dairy Templates with the teamcode-sloth template:

ftc {
    dairy {
        implementation(Sloth)
        // Optional: Sloth-compatible FTC Dashboard
        implementation(slothboard)
        // Optional: Sloth-compatible Panels
        implementation(ftControl.fullpanels)
    }
}

plugins {
    id("dev.frozenmilk.teamcode") version "11.0.0-1.1.0"
    id("dev.frozenmilk.sinister.sloth.load") version "0.2.4"
}

Option 2: Manual Installation

Step 1: Add Repositories

Add to your TeamCode build.gradle:

repositories {
    maven {
        url = "https://repo.dairy.foundation/releases"
    }
    maven {
        url = "https://repo.dairy.foundation/snapshots"
    }
}
repositories {
    maven {
        url = uri("https://repo.dairy.foundation/releases")
    }
    maven {
        url = uri("https://repo.dairy.foundation/snapshots")
    }
}

Step 2: Install Sloth Library

Choose based on whether you use other Dairy libraries:

If NOT using other Dairy 1.x.x libraries:

dependencies {
    implementation("dev.frozenmilk.sinister:Sloth:0.2.4")
}

If using Mercurial, Core, or other Dairy libraries:

dependencies {
    // This includes Sloth
    implementation("dev.frozenmilk.dairy:Core:2.2.4")
}

⚠️ Remove any existing Util or Sinister dependencies - Core provides them.

Step 3: Install Load Plugin

Add to top of TeamCode build.gradle:

buildscript {
    repositories {
        mavenCentral()
        maven {
            url "https://repo.dairy.foundation/releases"
        }
    }
    dependencies {
        classpath "dev.frozenmilk:Load:0.2.4"
    }
}

Add after the apply plugin: lines:

apply plugin: 'dev.frozenmilk.sinister.sloth.load'

Step 4: Optional - FTC Dashboard

For Sloth-compatible FTC Dashboard:

dependencies {
    implementation("com.acmerobotics.slothboard:dashboard:0.2.4+0.5.1")
}

If using Road Runner, exclude standard dashboard:

implementation("com.acmerobotics.roadrunner:ftc:0.1.25") {
    exclude group: "com.acmerobotics.dashboard"
}
implementation("com.acmerobotics.roadrunner:actions:1.0.1") {
    exclude group: "com.acmerobotics.dashboard"
}

Step 5: Optional - Panels

For Sloth-compatible Panels:

dependencies {
    implementation("com.bylazar.sloth:fullpanels:0.2.4+1.0.12")
}

Step 6: Setup Gradle Tasks

Configure Android Studio tasks for easy deployment.

Add deploySloth task:

  1. Edit Configurations
  2. Add new Configuration → Gradle
  3. Task: deploySloth
  4. Save

Add removeSlothRemote to TeamCode configuration:

  1. Edit TeamCode configuration
  2. Add Gradle task
  3. Task: removeSlothRemote
  4. Put it first in the task list
  5. Save

Tip: Type :TeamCode in the Gradle Project box to get the right context.

Usage

Development Workflow

  1. Initial Deploy: Run deploySloth task (deploys full app with Sloth)
  2. Make Changes: Edit your TeamCode files
  3. Fast Deploy: Run deploySloth again (uploads only changes in ~1s)
  4. Test: OpMode stops → Changes apply → Test immediately
  5. Repeat: Keep iterating rapidly

Example: Fast Iteration

@TeleOp(name = "Quick Test")
public class QuickTest extends LinearOpMode {
    @Override
    public void runOpMode() {
        DcMotor motor = hardwareMap.get(DcMotor.class, "motor");
        
        waitForStart();
        
        while (opModeIsActive()) {
            // Try different values quickly
            double power = 0.5;  // Change this
            motor.setPower(power);
            
            telemetry.addData("Power", power);
            telemetry.update();
        }
        // Stop OpMode → Changes deployed automatically
    }
}

Workflow:

  1. Run OpMode with power = 0.5
  2. Stop OpMode
  3. Change to power = 0.7
  4. Hit deploySloth (takes less than 1 second)
  5. Run OpMode again - new value active!
@TeleOp(name = "Quick Test")
class QuickTest : LinearOpMode() {
    override fun runOpMode() {
        val motor = hardwareMap.get(DcMotor::class.java, "motor")
        
        waitForStart()
        
        while (opModeIsActive()) {
            // Try different values quickly
            val power = 0.5  // Change this
            motor.power = power
            
            telemetry.addData("Power", power)
            telemetry.update()
        }
        // Stop OpMode → Changes deployed automatically
    }
}

Workflow:

  1. Run OpMode with power = 0.5
  2. Stop OpMode
  3. Change to power = 0.7
  4. Hit deploySloth (takes under 1 second)
  5. Run OpMode again - new value active!

The @Pinned Annotation

Use @Pinned to prevent hot reloading specific classes:

import dev.frozenmilk.sinister.targeting.Pinned;

// This class WON'T be hot reloaded
@Pinned
public class CriticalHardwareConfig {
    // Hardware initialization that shouldn't change mid-session
    public static final double WHEEL_DIAMETER = 4.0;
    public static final double GEAR_RATIO = 1.5;
}

// Subclasses of @Pinned are also pinned
public class ExtendedConfig extends CriticalHardwareConfig {
    // This also won't hot reload
}
import dev.frozenmilk.sinister.targeting.Pinned

// This class WON'T be hot reloaded
@Pinned
class CriticalHardwareConfig {
    companion object {
        // Hardware initialization that shouldn't change mid-session
        const val WHEEL_DIAMETER = 4.0
        const val GEAR_RATIO = 1.5
    }
}

// Subclasses of @Pinned are also pinned
class ExtendedConfig : CriticalHardwareConfig() {
    // This also won't hot reload
}

Use @Pinned for:

  • Hardware configuration classes
  • Critical constants
  • Classes with native code
  • Classes that break when hot reloaded

Real-World Use Cases

1. PID Tuning

@TeleOp(name = "PID Tuner")
public class PIDTuner extends LinearOpMode {
    @Override
    public void runOpMode() {
        DcMotorEx motor = hardwareMap.get(DcMotorEx.class, "arm");
        
        // Tune these quickly with Sloth
        double kP = 0.01;
        double kI = 0.0;
        double kD = 0.0;
        
        PIDController controller = new PIDController(kP, kI, kD);
        
        waitForStart();
        
        while (opModeIsActive()) {
            double current = motor.getCurrentPosition();
            double target = 1000;
            
            double power = controller.calculate(current, target);
            motor.setPower(power);
            
            telemetry.addData("Position", current);
            telemetry.addData("Target", target);
            telemetry.addData("Power", power);
            telemetry.update();
        }
    }
}

// Change kP, kI, kD values → deploySloth → test immediately!
@TeleOp(name = "PID Tuner")
class PIDTuner : LinearOpMode() {
    override fun runOpMode() {
        val motor = hardwareMap.get(DcMotorEx::class.java, "arm")
        
        // Tune these quickly with Sloth
        val kP = 0.01
        val kI = 0.0
        val kD = 0.0
        
        val controller = PIDController(kP, kI, kD)
        
        waitForStart()
        
        while (opModeIsActive()) {
            val current = motor.currentPosition.toDouble()
            val target = 1000.0
            
            val power = controller.calculate(current, target)
            motor.power = power
            
            telemetry.addData("Position", current)
            telemetry.addData("Target", target)
            telemetry.addData("Power", power)
            telemetry.update()
        }
    }
}

// Change kP, kI, kD values → deploySloth → test immediately!

2. Debugging Motion Profiles

@Autonomous(name = "Motion Debug")
public class MotionDebug extends LinearOpMode {
    @Override
    public void runOpMode() {
        // Quickly test different profiles
        double maxVel = 50.0;  // Change these
        double maxAccel = 30.0;
        double distance = 24.0;
        
        waitForStart();
        
        // Run motion with current values
        runMotionProfile(maxVel, maxAccel, distance);
        
        // Stop → Change values → deploySloth → Run again
    }
}
@Autonomous(name = "Motion Debug")
class MotionDebug : LinearOpMode() {
    override fun runOpMode() {
        // Quickly test different profiles
        val maxVel = 50.0  // Change these
        val maxAccel = 30.0
        val distance = 24.0
        
        waitForStart()
        
        // Run motion with current values
        runMotionProfile(maxVel, maxAccel, distance)
        
        // Stop → Change values → deploySloth → Run again
    }
}

Precautions

What Can Break Hot Reloading

Installing/changing libraries - Requires full install

dependencies {
    // If you add this:
    implementation("com.newlibrary:lib:1.0.0")
    // You MUST do full install, not deploySloth
}

Changing @Pinned annotations - Needs full install

// If you add or remove @Pinned:
@Pinned  // ← Added this
public class MyClass { }
// Full install required!

Modifying files outside TeamCode - Not hot reloaded

src/main/java/
  com/qualcomm/...  ← SDK files (not hot reloaded)
  org/firstinspires/ftc/teamcode/  ← Your code (hot reloaded)

Pedro Pathing Warning

If you used an older Pedro Pathing quickstart, move your files to org.firstinspires.ftc.teamcode package or they won't be hot reloaded.

Integration with Libraries

FTC Dashboard (Slothboard)

Sloth includes a modified FTC Dashboard called "Slothboard":

  • Full hot reload support for @Config classes
  • OpModes automatically refresh
  • Configuration variables update without full install

Panels

Sloth-compatible Panels provides:

  • Full plugin system with hot reload
  • Real-time configurables
  • Live telemetry updates

Custom Libraries

Make your library Sloth-compatible:

import dev.frozenmilk.sinister.SinisterFilter

// Listen for hot reloads
class MyLibraryModule : SinisterFilter {
    override fun accept(clazz: Class<*>): Boolean {
        // Return true for classes you care about
        return clazz.isAnnotationPresent(MyAnnotation::class.java)
    }
    
    override fun init(clazz: Class<*>) {
        // Called when Sloth loads/reloads this class
        println("Reloaded: ${clazz.name}")
    }
}
import dev.frozenmilk.sinister.SinisterFilter;

// Listen for hot reloads
public class MyLibraryModule implements SinisterFilter {
    @Override
    public boolean accept(Class<?> clazz) {
        // Return true for classes you care about
        return clazz.isAnnotationPresent(MyAnnotation.class);
    }
    
    @Override
    public void init(Class<?> clazz) {
        // Called when Sloth loads/reloads this class
        System.out.println("Reloaded: " + clazz.getName());
    }
}

Comparison: Sloth vs Traditional

AspectTraditionalSloth
First Deploy40+ seconds40+ seconds
Subsequent Deploys40+ seconds<1 second
Changes PersistYesYes
Works Across RestartsYesYes
Library ChangesFull installFull install
TeamCode ChangesFull installHot reload
OpMode SafetyImmediateAfter OpMode ends
Dashboard/PanelsStandardHot reload support

Tips for Success

  1. Initial Setup Takes Time: First deploySloth is still slow (full install)
  2. Subsequent Deploys Are Fast: Every deploy after that is under 1 second
  3. Stop OpMode First: Changes apply when OpMode ends
  4. Full Install When Needed: Library/SDK changes need full install
  5. Use @Pinned Wisely: Only pin truly critical classes
  6. Organize Code Well: Keep frequently-changed code together
  7. Test Iteratively: Make small changes and test frequently
  8. Watch for Errors: If something breaks, try full install

Troubleshooting

"Changes Aren't Applying"

  • Did you run deploySloth?
  • Did you stop the OpMode?
  • Are files in org.firstinspires.ftc.teamcode package?
  • Is the class @Pinned?

"It's Still Slow"

  • Check you're running deploySloth not full install
  • Ensure Load plugin is properly configured
  • Verify Sloth dependency is correct

"Library Not Working"

  • Some libraries need special Sloth versions (Dashboard, Panels)
  • Check if library supports hot reloading
  • Ask library maintainer about Sloth compatibility

When to Use Sloth

Use Sloth when:

  • Actively developing and testing
  • Tuning PIDs and constants
  • Debugging autonomous routines
  • Rapid prototyping
  • Learning FTC programming

Don't need Sloth when:

  • Code is stable (competition day)
  • Only making library changes
  • Working with small codebase (few changes)
  • Team uncomfortable with advanced tools

Resources

  • GitHub: Dairy-Foundation/Sloth
  • Documentation: docs.dairy.foundation
  • Templates: Dairy Templates
  • Repository: repo.dairy.foundation
  • Sinister Docs: Sinister Overview

Next Steps

  1. Choose installation method (Templates or Manual)
  2. Install Sloth with Load plugin
  3. Configure deploySloth and removeSlothRemote tasks
  4. Run first deploySloth (full install)
  5. Make a small code change
  6. Run deploySloth again (should be less than 1 second)
  7. Test the change immediately
  8. Enjoy lightning-fast iteration!

Pro Tip: Combine Sloth with FTC Dashboard or Panels for an unbeatable development experience with real-time tuning and visualization.

Panels

All-in-one plugin-based dashboard for FTC robots

Vision Overview

Introduction to computer vision in FTC robotics

On this page

What is Sloth?Why Use Sloth?Key Improvements Over FastloadHow It WorksScope and LimitationsInstallationOption 1: Dairy Templates (Recommended)Option 2: Manual InstallationStep 1: Add RepositoriesStep 2: Install Sloth LibraryStep 3: Install Load PluginStep 4: Optional - FTC DashboardStep 5: Optional - PanelsStep 6: Setup Gradle TasksUsageDevelopment WorkflowExample: Fast IterationThe @Pinned AnnotationReal-World Use Cases1. PID Tuning2. Debugging Motion ProfilesPrecautionsWhat Can Break Hot ReloadingPedro Pathing WarningIntegration with LibrariesFTC Dashboard (Slothboard)PanelsCustom LibrariesComparison: Sloth vs TraditionalTips for SuccessTroubleshooting"Changes Aren't Applying""It's Still Slow""Library Not Working"When to Use SlothResourcesNext Steps