From 7c019b0a145d613a123b9fdf6e5ab616c22ff193 Mon Sep 17 00:00:00 2001 From: q-field Date: Tue, 24 Feb 2026 16:04:57 -0500 Subject: [PATCH] Add support for using PWM encoders plugged into the motor controller instead of CANcoders. --- build.gradle | 2 +- .../swerve/core/SwerveModuleImpl.java | 28 +++++++++++++------ .../swerve/core/config/EncoderConfig.java | 13 ++++++++- .../swerve/core/config/EncoderType.java | 9 ++++++ .../hardware/rev/neospark/NSAngleMotor.java | 10 +++++-- .../hardware/rev/neospark/NSFAngleMotor.java | 6 ++-- .../hardware/rev/neospark/NSMAngleMotor.java | 6 ++-- 7 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ca/team1310/swerve/core/config/EncoderType.java diff --git a/build.gradle b/build.gradle index b9c88c2..507fbd7 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ java { } // set a valid semver version -version = '4.1.2' +version = '5.0.0' repositories { mavenCentral() diff --git a/src/main/java/ca/team1310/swerve/core/SwerveModuleImpl.java b/src/main/java/ca/team1310/swerve/core/SwerveModuleImpl.java index 60a52c7..5254357 100644 --- a/src/main/java/ca/team1310/swerve/core/SwerveModuleImpl.java +++ b/src/main/java/ca/team1310/swerve/core/SwerveModuleImpl.java @@ -1,5 +1,6 @@ package ca.team1310.swerve.core; +import ca.team1310.swerve.core.config.EncoderType; import ca.team1310.swerve.core.config.ModuleConfig; import ca.team1310.swerve.core.hardware.cancoder.CanCoder; import ca.team1310.swerve.core.hardware.rev.neospark.NSFAngleMotor; @@ -14,7 +15,6 @@ class SwerveModuleImpl implements SwerveModule { private static final double ANGLE_ENCODER_SYNC_PERIOD_MS = 500; - private final Notifier encoderSynchronizer = new Notifier(this::syncAngleEncoder); private final String name; private final Coordinates location; @@ -40,10 +40,15 @@ class SwerveModuleImpl implements SwerveModule { measuredState.setLocation(cfg.location()); this.driveMotor = getDriveMotor(cfg, maxAttainableModuleSpeedMps); this.angleMotor = getAngleMotor(cfg); - this.angleEncoder = getAbsoluteAngleEncoder(cfg); - - this.encoderSynchronizer.setName("RunnymedeSwerve Angle Encoder Sync " + name); - this.encoderSynchronizer.startPeriodic(ANGLE_ENCODER_SYNC_PERIOD_MS / 1000); + if (cfg.absoluteAngleEncoderConfig().type() == EncoderType.CANCODER) + this.angleEncoder = getAbsoluteAngleEncoder(cfg); + else this.angleEncoder = null; + + if (angleEncoder != null) { + Notifier encoderSynchronizer = new Notifier(this::syncAngleEncoder); + encoderSynchronizer.setName("RunnymedeSwerve Angle Encoder Sync " + name); + encoderSynchronizer.startPeriodic(ANGLE_ENCODER_SYNC_PERIOD_MS / 1000); + } driveMotorFaultPresent = new Alert("Swerve Drive Motor [" + name + "] Fault Present", Alert.AlertType.kError); @@ -77,8 +82,12 @@ private DriveMotor getDriveMotor(ModuleConfig cfg, double maxAttainableModuleSpe private AngleMotor getAngleMotor(ModuleConfig cfg) { return switch (cfg.angleMotorConfig().type()) { - case NEO_SPARK_FLEX -> new NSFAngleMotor(cfg.angleMotorCanId(), cfg.angleMotorConfig()); - case NEO_SPARK_MAX -> new NSMAngleMotor(cfg.angleMotorCanId(), cfg.angleMotorConfig()); + case NEO_SPARK_FLEX -> + new NSFAngleMotor( + cfg.angleMotorCanId(), cfg.angleMotorConfig(), cfg.absoluteAngleEncoderConfig()); + case NEO_SPARK_MAX -> + new NSMAngleMotor( + cfg.angleMotorCanId(), cfg.angleMotorConfig(), cfg.absoluteAngleEncoderConfig()); }; } @@ -108,7 +117,7 @@ public synchronized void readState() { public synchronized void readVerboseState() { measuredState.setVelocity(driveMotor.getVelocity()); measuredState.setDriveOutputPower(driveMotor.getMeasuredVoltage()); - measuredState.setAbsoluteEncoderAngle(angleEncoder.getPosition()); + measuredState.setAbsoluteEncoderAngle(angleMotor.getPosition()); } public synchronized ModuleState getState() { @@ -138,7 +147,8 @@ public synchronized void setDesiredState(ModuleDirective desiredState) { public boolean checkFaults() { boolean driveFaults = driveMotor.hasFaults(); boolean angleFaults = angleMotor.hasFaults(); - boolean angleEncoderFaults = angleEncoder.hasFaults(); + boolean angleEncoderFaults = false; + if (angleEncoder != null) angleEncoderFaults = angleEncoder.hasFaults(); driveMotorFaultPresent.set(driveFaults); angleMotorFaultPresent.set(angleFaults); diff --git a/src/main/java/ca/team1310/swerve/core/config/EncoderConfig.java b/src/main/java/ca/team1310/swerve/core/config/EncoderConfig.java index 4417db3..c8ff4be 100644 --- a/src/main/java/ca/team1310/swerve/core/config/EncoderConfig.java +++ b/src/main/java/ca/team1310/swerve/core/config/EncoderConfig.java @@ -3,8 +3,19 @@ /** * Configuration for the encoder. * + * @param type the type of encoder, either Cancoder or Integrated encoder * @param inverted true if the encoder is upside down with respect to its expected orientation * @param retrySeconds how long to wait before retrying communication with the encoder * @param retryCount how many times to retry communication with the encoder */ -public record EncoderConfig(boolean inverted, double retrySeconds, int retryCount) {} +public record EncoderConfig( + EncoderType type, boolean inverted, double retrySeconds, int retryCount) { + + public static EncoderConfig cancoder(boolean inverted, double retrySeconds, int retryCount) { + return new EncoderConfig(EncoderType.CANCODER, inverted, retrySeconds, retryCount); + } + + public static EncoderConfig integrated(boolean inverted) { + return new EncoderConfig(EncoderType.INTEGRATED, inverted, 0, 0); + } +} diff --git a/src/main/java/ca/team1310/swerve/core/config/EncoderType.java b/src/main/java/ca/team1310/swerve/core/config/EncoderType.java new file mode 100644 index 0000000..a6cf20b --- /dev/null +++ b/src/main/java/ca/team1310/swerve/core/config/EncoderType.java @@ -0,0 +1,9 @@ +package ca.team1310.swerve.core.config; + +/** The type of motor used in the swerve drive. */ +public enum EncoderType { + /** A CTRE CANcoder. */ + CANCODER, + /** An encoder plugged directly into the angle motor controller */ + INTEGRATED +} diff --git a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSAngleMotor.java b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSAngleMotor.java index 9cb71c2..07c5b95 100644 --- a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSAngleMotor.java +++ b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSAngleMotor.java @@ -5,6 +5,8 @@ import static ca.team1310.swerve.utils.SwerveUtils.normalizeDegrees; import ca.team1310.swerve.core.AngleMotor; +import ca.team1310.swerve.core.config.EncoderConfig; +import ca.team1310.swerve.core.config.EncoderType; import ca.team1310.swerve.core.config.MotorConfig; import com.revrobotics.spark.FeedbackSensor; import com.revrobotics.spark.SparkBase; @@ -27,8 +29,9 @@ public abstract class NSAngleMotor extends NSBase implem * * @param spark The spark motor controller * @param cfg The configuration of the motor + * @param encoderConfig the configuration of the absolute encoder */ - public NSAngleMotor(T spark, MotorConfig cfg) { + public NSAngleMotor(T spark, MotorConfig cfg, EncoderConfig encoderConfig) { super(spark); SparkMaxConfig config = new SparkMaxConfig(); config.inverted(cfg.inverted()); @@ -83,13 +86,16 @@ public NSAngleMotor(T spark, MotorConfig cfg) { // configure PID controller config .closedLoop - .feedbackSensor(FeedbackSensor.kPrimaryEncoder) .pidf(cfg.p(), cfg.i(), cfg.d(), cfg.ff()) .iZone(cfg.izone()) .outputRange(-180, 180) .positionWrappingEnabled(true) .positionWrappingInputRange(-180, 180); + if (encoderConfig.type() == EncoderType.INTEGRATED) + config.closedLoop.feedbackSensor(FeedbackSensor.kAbsoluteEncoder); + else config.closedLoop.feedbackSensor(FeedbackSensor.kPrimaryEncoder); + // send them to the motor doWithRetry( () -> diff --git a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSFAngleMotor.java b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSFAngleMotor.java index 8c379f9..7d57e21 100644 --- a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSFAngleMotor.java +++ b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSFAngleMotor.java @@ -1,5 +1,6 @@ package ca.team1310.swerve.core.hardware.rev.neospark; +import ca.team1310.swerve.core.config.EncoderConfig; import ca.team1310.swerve.core.config.MotorConfig; import com.revrobotics.spark.SparkFlex; import com.revrobotics.spark.SparkLowLevel; @@ -15,8 +16,9 @@ public class NSFAngleMotor extends NSAngleMotor { * * @param canId The CAN ID of the motor * @param cfg The configuration of the motor + * @param encoderConfig the configuration of the absolute encoder */ - public NSFAngleMotor(int canId, MotorConfig cfg) { - super(new SparkFlex(canId, SparkLowLevel.MotorType.kBrushless), cfg); + public NSFAngleMotor(int canId, MotorConfig cfg, EncoderConfig encoderConfig) { + super(new SparkFlex(canId, SparkLowLevel.MotorType.kBrushless), cfg, encoderConfig); } } diff --git a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSMAngleMotor.java b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSMAngleMotor.java index 6f2edec..bf1d260 100644 --- a/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSMAngleMotor.java +++ b/src/main/java/ca/team1310/swerve/core/hardware/rev/neospark/NSMAngleMotor.java @@ -1,5 +1,6 @@ package ca.team1310.swerve.core.hardware.rev.neospark; +import ca.team1310.swerve.core.config.EncoderConfig; import ca.team1310.swerve.core.config.MotorConfig; import com.revrobotics.spark.SparkLowLevel; import com.revrobotics.spark.SparkMax; @@ -15,8 +16,9 @@ public class NSMAngleMotor extends NSAngleMotor { * * @param canId The CAN ID of the motor * @param cfg The configuration of the motor + * @param encoderConfig the configuration of the absolute encoder */ - public NSMAngleMotor(int canId, MotorConfig cfg) { - super(new SparkMax(canId, SparkLowLevel.MotorType.kBrushless), cfg); + public NSMAngleMotor(int canId, MotorConfig cfg, EncoderConfig encoderConfig) { + super(new SparkMax(canId, SparkLowLevel.MotorType.kBrushless), cfg, encoderConfig); } }