Code
WPILib 2027 vendor library — every command the motor accepts from robot code.
The vendor library is under active development. Method names on this page can change. Java only. C++ and Python TBD.
Install
Put SwyftCycloneV4.json in the robot project’s vendordeps/ folder
and rebuild. Year token: 2027.
Class: com.swyftrobotics.cyclone.v4.SwyftCyclone. Construct with the CAN ID
you assigned in SWYFT Link (1–62). Invert is set in SWYFT Link, not in this API.
import com.swyftrobotics.cyclone.v4.SwyftCyclone;
public class Intake {
private final SwyftCyclone motor = new SwyftCyclone(5);
}
A second constructor takes a SystemCore CAN bus id. Default is CAN_S0.
Output
Four motion commands. Sign is direction. Call the command every loop
(teleopPeriodic / execute). A setpoint older than 100 ms coasts.
Do not send it once from initialize or from a background timer.
| Method | Units | Use |
|---|---|---|
setDutyCycle(d) / set(d) | −1…1, fraction of bus | Open-loop bring-up. Same quantity, two names. |
setCurrent(a) | amperes, signed | Torque mode. Bounded by the stator limit. |
setVelocity(rpm) | mechanical RPM, signed | Closed-loop speed. |
setPosition(rad) | rotor radians, signed, multi-turn | Closed-loop position. Long way around. Relative to power-on, not absolute. |
setDutyCycle is bring-up. The stator limit does not govern this mode.
Use setCurrent or setVelocity for a mechanism that can stall.
0.5 is half bus, not half free speed. 50 clamps to full output.
@Override
public void teleopPeriodic() {
motor.setDutyCycle(stick.getY()); // every loop
// motor.setCurrent(20); // A, signed
// motor.setVelocity(2000); // RPM
// motor.setPosition(Math.PI); // rotor rad
}
setPosition is rotor-side. The device has no gear ratio. Apply
SensorToMechanismRatio on the host — see Mechanism units.
Stop, e-stop, faults
| Method | What it does |
|---|---|
disable() | Coast this command path and drop the setpoint. |
estop() | Latching kill. Coasts. Refuses re-arm until releaseEstop(). |
releaseEstop() | Clears the e-stop latch. Then clearFaults() if needed. |
clearFaults() | Request. Refused while the cause is still present, and while e-stop is latched. Re-read hasHardFault(). |
@Override
public void disabledInit() {
motor.disable();
}
public void panic() {
motor.estop();
}
public void pitClear() {
motor.releaseEstop();
motor.clearFaults();
if (motor.hasHardFault()) {
// cause still present — read SWYFT Link
}
}
E-stop coasts. Do not test it on a motor holding a load in the air. Faults
Current limits
Apply from robotInit(), not from periodic. apply blocks.
Set only stator and supply. Starting points and the unsupported fields:
Current limits
import com.swyftrobotics.cyclone.v4.CurrentLimitsConfigs;
@Override
public void robotInit() {
var cfg = new CurrentLimitsConfigs();
cfg.StatorCurrentLimit = 20; // A
cfg.SupplyCurrentLimit = 20; // A
motor.getConfigurator().apply(cfg);
}
SupplyCurrentLowerLimit and SupplyCurrentLowerTime throw.
If that throw leaves robotInit(), the Driver Station reports no robot code.
Neutral mode
V4 coasts when uncommanded. setNeutralMode(NeutralMode.COAST) succeeds.
BRAKE throws — there is no brake path. isBrakeModeSupported() is false.
Design the mechanism for coast (holding current, or a mechanical brake, on an elevator).
motor.setNeutralMode(SwyftCyclone.NeutralMode.COAST);
Mechanism units
Gear ratio is host-side. The device never stores it. Set it every boot with
configureMechanism — not through the configurator. Then use
setMechanismPosition / getMechanismPosition in rotations of the
mechanism. Call the position command every loop.
import com.swyftrobotics.cyclone.v4.MechanismConfigs;
@Override
public void robotInit() {
var mech = new MechanismConfigs();
mech.SensorToMechanismRatio = 12.8; // rotor turns per mechanism turn
mech.ContinuousWrap = true; // shortest path (azimuth)
motor.configureMechanism(mech);
}
@Override
public void teleopPeriodic() {
motor.setMechanismPosition(0.25); // mechanism rotations
}
Position is relative to power-on. After a brownout, re-zero against a known
reference. hasLikelyRebooted() flags that.
Follow
A follower copies the leader’s applied output (after the leader’s current
limit), not the leader’s request. The leader is not told. Follow is a command, not
flash — a battery swap or brownout on that motor clears it. Put it in
robotInit() so the pair comes back every boot. If only that motor reboots
and the RIO stays up, robotInit() does not run; re-issue when
hasLikelyRebooted() is true. Disable, fault, and e-stop do not cancel
follow; the pair resumes when the follower re-arms. Any other motion command on
the follower cancels it.
import com.swyftrobotics.cyclone.v4.Follower;
import com.swyftrobotics.cyclone.v4.MotorAlignmentValue;
@Override
public void robotInit() {
left.setControl(new Follower(right.getDeviceID(), MotorAlignmentValue.Opposed));
}
@Override
public void teleopPeriodic() {
if (left.hasLikelyRebooted()) {
left.setControl(new Follower(right.getDeviceID(), MotorAlignmentValue.Opposed));
left.clearRebootFlag();
}
}
// left.stopFollowing();
Do not follow a follower. A follower still obeys its own enable gate, faults,
and current limits. If the leader disappears, the follower coasts on the 100 ms
deadman. Do not also setDutyCycle on the follower in a default command —
that cancels follow on the first tick.
Telemetry
getLatest() is non-blocking. The returned object is reused — copy fields
you need across loops. isAlive(0.1) is false if no motion frame has arrived
in 100 ms; an unplugged controller otherwise looks like a stationary one.
SwyftCycloneData d = motor.getLatest();
if (!motor.isAlive(0.1)) {
return; // no CAN frames
}
double rpm = d.velocityRpm;
double amps = d.supplyCurrentAmps; // bus; regen reads ~0
double volts = d.busVoltage;
double tempC = d.temperatureC;
boolean fault = d.hasHardFault();
String faults = d.faultString();
There is no phase-current field. Use supplyCurrentAmps.
d.mode is the law the ISR actually ran, not the command you sent.
LEDs
User lights 1–5, left to right, arc at the top. Status lights cannot be set. Status lights
import com.swyftrobotics.cyclone.v4.LedAnimation; motor.setLEDs(0, 120, 255); // all five motor.setLED(3, 255, 0, 0); // center motor.setLEDs(255, 40, 0, LedAnimation.ChaseForward); motor.clearLEDs(); // back to status
Channels 0–255. Black on all five returns control to the controller; that is
not off — use clearLEDs(). Rainbow ignores hue but must not be passed black.
Colour persists until power loss; set team colour in robotInit().
Identify and calibrate
| Method | What it does |
|---|---|
identify() | White flash ~2.5 s on the status lights. |
calibrate() / calibrate(amps) | Spins the shaft. Robot must be enabled. Dropped if sent while disabled, not queued. |
saveConfiguration() | Persist device config across power loss. Does not persist follow or mechanism ratio. |
Calibrate spins the shaft. Area clear, robot enabled. Do not call it from
robotInit().