Skip to content

Creating your first Robot

The Robot class is the heart of your robot’s code. It serves as a centralized container for all your hardware devices and subsystems (Attachments), providing a clean structure and powerful features to simplify your code.

You should create a new Robot class for each type of physical robot you’ve built.

First, create a new class that inherits from the abstract Robot class. This class requires an FTC HardwareMap to be passed to its constructor.

import com.qualcomm.robotcore.hardware.HardwareMap
import dev.kingssack.volt.robot.Robot
class MyRobot(hardwareMap: HardwareMap) : Robot(hardwareMap) {
// Your hardware and attachments will be defined here
}

Volt uses Kotlin’s property delegation to make hardware initialization clean and concise. Instead of calling hardwareMap.get() for every device, you can use the helper functions provided by the Robot class.

To define a device, use the by keyword followed by the appropriate helper function, passing the device name as configured in your robot’s hardware configuration.

Here are some of the available Hardware Property Delegates:

  • motor(name: String) for DcMotor
  • servo(name: String) for Servo
  • crServo(name: String) for CRServo
  • imu(name: String) for IMU
  • huskyLens(name: String) for HuskyLens
  • distanceSensor(name: String) for Rev2mDistanceSensor
  • colorSensor(name: String) for NormalizedColorSensor
  • analogInput(name: String) for AnalogInput

Let’s add a DcMotor and an IMU:

import com.qualcomm.robotcore.hardware.HardwareMap
import dev.kingssack.volt.robot.Robot
class MyRobot(hardwareMap: HardwareMap) : Robot(hardwareMap) {
// Motors
private val motor1 by motor("m1")
// Sensors
private val imu by imu("imu")
}

In Volt, subsystems like an intake mechanism, shooter, or arm are called Attachments. The Robot class is designed to manage these attachments automatically.

To add Attachments, use the provided attachment builder function.

Let’s add an attachment to control our DcMotor:

class MyRobot(hardwareMap: HardwareMap) : Robot(hardwareMap) {
private val motor1 by motor("m1")
private val imu by imu("imu")
// Attachments
val exampleAttachment = attachment { DcMotorAttachment("Example", motor1, 0.5, 1000) }
}

The Robot class manages its state through a StateFlow of type RobotState. This allows you to reactively observe the robot’s status from your OpModes. The possible states are:

  • Initializing: The Robot has not finished initializing.
  • Initialized: The Robot has finished initializing, and all Attachments have been discovered.
  • Idle: No Attachments are busy.
  • Running: At least one Attachment reports that it is busy.
  • Fault(val error: Throwable): An error has occurred.

The update() method is called every tick while an OpMode is running.

The Robot class can also declare Actions using the VoltActionBuilder. These can be complex sequences controlling your Actions or anything else.

For example let’s add a score sequence:

class MyRobot(hardwareMap: HardwareMap) : Robot(hardwareMap) {
private val motor1 by motor("m1")
private val imu by imu("imu")
val exampleAttachment = attachment { DcMotorAttachment("Example", motor1, 0.5, 1000) }
// Actions
fun score() = voltAction {
+motor1.goTo(0.6, 200)
+motor1.goTo(0.5, 0)
}
}
package org.firstinspires.ftc.teamcode.robot
import dev.kingssack.volt.attachment.DcMotorAttachment
import dev.kingssack.volt.core.voltAction
import dev.kingssack.volt.robot.Robot
import com.qualcomm.robotcore.hardware.HardwareMap
class MyRobot(hardwareMap: HardwareMap) : Robot(hardwareMap) {
private val motor1 by motor("m1")
private val imu by imu("imu")
val exampleAttachment = attachment { DcMotorAttachment("Example", motor1, 0.5, 1000) }
fun score() = voltAction {
+motor1.goTo(0.6, 200)
+motor1.goTo(0.5, 0)
}
}