Welcome to the guide on how to write an interfacing file. This file will be the interface your implementation files will implement, and your logic file will take this as it’s parameter during construction. This technically is the base of your subsystem, since everything is based around it.
This will, of course, be an interface. If you’re wondering why, google the purpose of an interface and come back once you have a solid understanding of it, with the context that this file is used for the baseline for all your implementation files (IOMotor, IOSim, etc.).
interface SubsystemIO
All your interfaces must contain a class inside of it for holding the inputs. An instance of this class will be passed into your updateInputs method: the overriden definition will be defined in your implementation file and called in your logic file. More information on the theory behind why we do this can be found in the Logging document. Logging with LoggableInputs
This class, titled SubsystemInputs, extends LoggableInputs. LoggableInputs defines the fromLog and toLog functions which updates the LogTable that AdvantageKit uses to store the logged information. Remember that this class is contained within the IO interface.
In here, you should define the variables which will store all the information you wish to keep about the motor. The following should always be declared here: a motor’s velocity, applied voltage, temperature, supply current, and stator current. A position variable should also be declared if a closed-loop request for this Subsystem exists (as in, if you care about the position of the motor). There should be one set of these for each motor controlled by this Subsystem. If multiple motors exist, they should either have unique names or be labelled as leader/follower.
You should also define your implementation of the fromLog and toLog methods. These will call .get and .put with all of your inputs, as shown in the example below. These must be converted to and from units when updating the LogTable.
For sake of the examples, MeasurementUnit is either Radian or Meter, MeasurementValue is either Angle or Length, .measurementUnit is either .radians, .meters, or unit equivalent, and MeasurementValueVelocity is AngularVelocity or LinearVelocity.
import org.littletonrobotics.junction.LogTable
import org.littletonrobotics.junction.inputs.LoggableInputs
class SubsystemInputs : LoggableInputs {
var subsystemPosition: MeasurementValue = 0.0.measurementUnit
var subsystemVelocity: MeasurementValueVelocity = 0.0.measurementUnit.perSecond
var subsystemAppliedVoltage: ElectricalPotential = 0.0.volts
var subsystemTemperature: Temperature = 0.0.celsius
var subsystemSupplyCurrent: Current = 0.0.amps
var subsystemStatorCurrent: Current = 0.0.amps
// the key names used in the toLog and fromLog must be identical
override fun toLog(table: LogTable) {
// include units in your log names! (may change depending on akit 2026... @ryan)
table.put("subsystemPositionMeasurement", subsystemPosition.inMeasurementUnit)
table.put("subsystemVelocityMeasurementPerSecond", subsystemVelocity.inMeasurementUnitperSecond)
// repeat for each one
}
override fun fromLog(table: LogTable) {
table.get("subsystemPositionMeasurement", subsystemPosition.inMeasurementUnit).let {
subsystemPosition = it.measurementUnit
}
table.get("subsystemVelocityMeasurementPerSecond", subsystemVelocity.inMeasurementUnitperSecond).let {
subsystemVelocity = it.measurementUnit.perSecond
}
}
}
We’ve made it to the actual “interface” part of the IO file. Here, we will write all the functions that we need to be implemented in the implementation files. This allows us to use polymorphism to call these methods from the logic file without direct reference to a single implementation.
First is the updateInputs method mentioned a tad bit earlier. This method will update all the inputs in your SubsystemInputs class.
fun updateInputs(inputs: SubsystemInputs) {}