Skip to content

Commit 33d9fba

Browse files
committed
feat: Initialize FRC robot project with core structure, utilities, and VS Code settings.
1 parent 22d3f9b commit 33d9fba

10 files changed

Lines changed: 293 additions & 16 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"editor.defaultFormatter": "richardwillis.vscode-spotless-gradle"
6868
},
6969
"[java]": {
70-
"editor.defaultFormatter": "richardwillis.vscode-spotless-gradle"
70+
"editor.defaultFormatter": "redhat.java"
7171
},
7272
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable"
7373
}

src/main/java/frc/robot/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
public final class Constants {
2323
public static final Mode simMode = Mode.SIM;
2424
public static final Mode currentMode = RobotBase.isReal() ? Mode.REAL : simMode;
25+
public static final int PDH_ID = 1;
2526

2627
public static enum Mode {
2728
/** Running on a real robot. */

src/main/java/frc/robot/Robot.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import edu.wpi.first.wpilibj2.command.Command;
1717
import edu.wpi.first.wpilibj2.command.CommandScheduler;
18+
import frc.robot.util.battery.BatteryUtils;
19+
1820
import org.littletonrobotics.junction.LogFileUtil;
1921
import org.littletonrobotics.junction.LoggedRobot;
2022
import org.littletonrobotics.junction.Logger;
@@ -84,6 +86,11 @@ public Robot() {
8486
robotContainer = new RobotContainer();
8587
}
8688

89+
@Override
90+
public void robotInit() {
91+
BatteryUtils.scheduleMonitor();
92+
}
93+
8794
/** This function is called periodically during all modes. */
8895
@Override
8996
public void robotPeriodic() {
@@ -104,11 +111,13 @@ public void robotPeriodic() {
104111

105112
/** This function is called once when the robot is disabled. */
106113
@Override
107-
public void disabledInit() {}
114+
public void disabledInit() {
115+
}
108116

109117
/** This function is called periodically when disabled. */
110118
@Override
111-
public void disabledPeriodic() {}
119+
public void disabledPeriodic() {
120+
}
112121

113122
/** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
114123
@Override
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package frc.robot.subsystems;
2+
3+
import edu.wpi.first.wpilibj2.command.SubsystemBase;
4+
import frc.robot.util.motors.MotorIO.Motor;
5+
import frc.robot.util.motors.controllers.SparkBaseController;
6+
7+
public class exampleSubsystem extends SubsystemBase {
8+
9+
private final SparkBaseController motor;
10+
11+
public exampleSubsystem(Motor motor) {
12+
13+
this.motor = new SparkBaseController(motor);
14+
15+
}
16+
17+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package frc.robot.util;
2+
3+
4+
import edu.wpi.first.networktables.GenericEntry;
5+
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
6+
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
7+
import edu.wpi.first.wpilibj2.command.button.Trigger;
8+
9+
10+
/**
11+
* AutoResetDashboardTrigger
12+
* -------------------------
13+
* A dashboard-backed one-shot trigger designed to behave like
14+
* a gamepad "onTrue" button.
15+
*
16+
* Behavior:
17+
* - Exposes a Shuffleboard Toggle Button
18+
* - Detects a FALSE -> TRUE transition (rising edge)
19+
* - Fires TRUE for exactly one scheduler loop
20+
* - Automatically resets the dashboard value back to FALSE
21+
*
22+
* Intended use cases:
23+
* - Single-action commands (reset, zeroing, shoot-once, test actions)
24+
* - Debug and pit utilities where holding a button is unsafe
25+
*
26+
* Not intended for:
27+
* - whileTrue / continuous commands
28+
* - toggle or latch-style behaviors
29+
*/
30+
public final class AutoResetDashboardTrigger {
31+
32+
33+
/** NetworkTables-backed boolean entry shown on Shuffleboard */
34+
private final GenericEntry buttonEntry;
35+
36+
37+
/** Cached previous value for edge detection */
38+
private boolean previousValue = false;
39+
40+
41+
/**
42+
* Creates an auto-resetting dashboard trigger.
43+
*
44+
* @param tabName Shuffleboard tab name
45+
* @param buttonName Label displayed on the dashboard
46+
*/
47+
public AutoResetDashboardTrigger(String tabName, String buttonName) {
48+
ShuffleboardTab tab = Shuffleboard.getTab(tabName);
49+
50+
51+
this.buttonEntry = tab.add(buttonName, false)
52+
.withWidget("Toggle Button")
53+
.getEntry();
54+
}
55+
56+
57+
/**
58+
* Returns a WPILib Trigger that fires exactly once
59+
* when the dashboard button is pressed.
60+
*
61+
* @return one-shot Trigger (rising-edge based)
62+
*/
63+
public Trigger trigger() {
64+
return new Trigger(() -> {
65+
boolean currentValue = buttonEntry.getBoolean(false);
66+
67+
68+
// Rising edge detection
69+
if (currentValue && !previousValue) {
70+
buttonEntry.setBoolean(false); // auto-reset immediately
71+
previousValue = true;
72+
return true;
73+
}
74+
75+
76+
previousValue = currentValue;
77+
return false;
78+
});
79+
}
80+
81+
82+
/**
83+
* Programmatically fires the trigger once.
84+
* Useful for tests or internal control logic.
85+
*/
86+
public void fireOnce() {
87+
buttonEntry.setBoolean(true);
88+
}
89+
}
90+
91+
92+
/*
93+
========================
94+
Example usage (RobotContainer)
95+
========================
96+
97+
98+
AutoResetDashboardTrigger shootOnce =
99+
new AutoResetDashboardTrigger("Driver", "Shoot Once");
100+
101+
102+
shootOnce.trigger()
103+
.onTrue(new ShootCommand());
104+
105+
106+
// Designed for onTrue only
107+
// whileTrue / toggle usage is intentionally unsupported
108+
*/

src/main/java/frc/robot/util/Motors/MotorConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package frc.robot.util.Motors;
1+
package frc.robot.util.motors;
22

33
public class MotorConfig {
44

src/main/java/frc/robot/util/Motors/MotorIO.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1212
// GNU General Public License for more details.
1313

14-
package frc.robot.util.Motors;
14+
package frc.robot.util.motors;
1515

1616
import org.littletonrobotics.junction.AutoLog;
1717

@@ -21,23 +21,18 @@ public static class MotorIOInputs {
2121
public MotorIOData motorData = new MotorIOData(false, 0, null, false);
2222
}
2323

24-
record MotorIOData(boolean isConnected, int canID, MotorType motorType, boolean hasError) {}
24+
record MotorIOData(boolean isConnected, int canID, ControllerType controllerType, boolean hasError) {}
2525

26-
enum MotorType {
26+
enum ControllerType {
2727
TALON,
2828
SPARKMAX,
2929
SPARKFLEX
3030
}
31+
32+
record Motor(ControllerType controllerType, int canID) {
3133

32-
public default void updateInputs(MotorIOInputs inputs) {}
33-
34-
public default void setOpenLoop(double output) {}
35-
36-
public default void setDutyCycle(double dutyCycle) {}
37-
38-
public default void setCoastMode(boolean coastMode) {}
39-
40-
public default void applyConfig(MotorConfig config) {}
34+
}
4135

36+
public default void updateInputs(MotorIOInputs inputs) {}
4237

4338
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package frc.robot.util.motors.controllers;
2+
3+
import com.revrobotics.spark.ClosedLoopSlot;
4+
import com.revrobotics.spark.SparkBase;
5+
import com.revrobotics.spark.SparkBase.PersistMode;
6+
import com.revrobotics.spark.SparkBase.ResetMode;
7+
import com.revrobotics.spark.SparkFlex;
8+
import com.revrobotics.spark.SparkLowLevel.MotorType;
9+
import com.revrobotics.spark.SparkMax;
10+
import com.revrobotics.spark.config.SparkBaseConfig;
11+
import com.revrobotics.spark.config.SparkBaseConfig.IdleMode;
12+
import com.revrobotics.spark.config.SparkFlexConfig;
13+
import com.revrobotics.spark.config.SparkMaxConfig;
14+
15+
import frc.robot.util.motors.MotorIO;
16+
17+
public class SparkBaseController implements MotorIO {
18+
19+
private SparkBase spark;
20+
private SparkBaseConfig sparkConfig;
21+
22+
/**
23+
* Setups the motor..
24+
*
25+
* @param motor The {@link Motor} dataclass containing motor configuration
26+
* details
27+
* @return the {@code MotorConfigurator} instance for chaining
28+
* @throws IllegalArgumentException if the motor type is unsupported
29+
*/
30+
public SparkBaseController(Motor motor) {
31+
createSparkMotor(motor.controllerType(), motor.canID());
32+
}
33+
34+
void createSparkMotor(ControllerType motorType, int canID) {
35+
switch (motorType) {
36+
case SPARKMAX:
37+
spark = new SparkMax(canID, MotorType.kBrushless);
38+
sparkConfig = new SparkMaxConfig();
39+
break;
40+
case SPARKFLEX:
41+
spark = new SparkFlex(canID, MotorType.kBrushless);
42+
sparkConfig = new SparkFlexConfig();
43+
break;
44+
default:
45+
throw new IllegalArgumentException("Invalid SparkBase Motor type");
46+
}
47+
48+
}
49+
50+
public void setNeutralMode(boolean b) {
51+
sparkConfig.idleMode(b ? IdleMode.kBrake : IdleMode.kCoast);
52+
spark.configure(sparkConfig, ResetMode.kNoResetSafeParameters, PersistMode.kNoPersistParameters);
53+
}
54+
55+
public void setClosedLoopRampRate(int closedLoopRampRate) {
56+
sparkConfig.closedLoopRampRate(closedLoopRampRate);
57+
spark.configure(sparkConfig, ResetMode.kNoResetSafeParameters, PersistMode.kNoPersistParameters);
58+
}
59+
60+
public void setOpenLoopRampRate(int openLoopRampRate) {
61+
sparkConfig.openLoopRampRate(openLoopRampRate);
62+
spark.configure(sparkConfig, ResetMode.kNoResetSafeParameters, PersistMode.kNoPersistParameters);
63+
}
64+
65+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package frc.robot.util.battery;
2+
3+
import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean;
4+
import org.littletonrobotics.junction.networktables.LoggedNetworkNumber;
5+
import org.littletonrobotics.junction.networktables.LoggedNetworkString;
6+
7+
import edu.wpi.first.math.filter.LinearFilter;
8+
import edu.wpi.first.wpilibj.DriverStation;
9+
import edu.wpi.first.wpilibj2.command.Command;
10+
11+
public class BatteryMonitor extends Command {
12+
13+
private static final int SAMPLE_COUNT = 50;
14+
private static final double MIN_VOLTAGE = 12.35;
15+
private static final String NT_TOPIC = "Battery/";
16+
17+
private final LinearFilter filter;
18+
private final LoggedNetworkBoolean lowBattery;
19+
private final LoggedNetworkNumber voltage;
20+
private double avgVoltage;
21+
22+
public BatteryMonitor() {
23+
filter = LinearFilter.movingAverage(SAMPLE_COUNT);
24+
lowBattery = new LoggedNetworkBoolean(NT_TOPIC + "Low");
25+
voltage = new LoggedNetworkNumber(NT_TOPIC + "Voltage");
26+
27+
avgVoltage = BatteryUtils.getCurrentVoltage();
28+
}
29+
30+
@Override
31+
public void initialize() {
32+
for (int i = 0; i < SAMPLE_COUNT; i++) {
33+
filter.calculate(avgVoltage);
34+
}
35+
}
36+
37+
@Override
38+
public void execute() {
39+
avgVoltage = filter.calculate(BatteryUtils.getCurrentVoltage());
40+
41+
boolean isLow = avgVoltage <= MIN_VOLTAGE;
42+
43+
if (isLow && !DriverStation.isEnabled()) {
44+
lowBattery.set(true);
45+
voltage.set(avgVoltage);
46+
} else {
47+
lowBattery.set(false);
48+
}
49+
}
50+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package frc.robot.util.battery;
2+
3+
import edu.wpi.first.wpilibj.Alert;
4+
import edu.wpi.first.wpilibj.PowerDistribution;
5+
import edu.wpi.first.wpilibj.PowerDistribution.ModuleType;
6+
import edu.wpi.first.wpilibj.RobotController;
7+
import edu.wpi.first.wpilibj2.command.Command;
8+
import frc.robot.Constants;
9+
10+
public class BatteryUtils {
11+
12+
public static final double DEFAULT_VOLTAGE = 12.35;
13+
14+
private static final PowerDistribution powerDistribution = new PowerDistribution(Constants.PDH_ID, ModuleType.kRev);
15+
16+
private static final Command monitor = new BatteryMonitor().ignoringDisable(true);
17+
18+
public static double getTotalCurrent() {
19+
return powerDistribution.getTotalCurrent();
20+
}
21+
22+
public static double getCurrentVoltage() {
23+
return RobotController.getBatteryVoltage();
24+
}
25+
26+
public static void scheduleMonitor() {
27+
if (!monitor.isScheduled()) {
28+
monitor.schedule();
29+
}
30+
}
31+
32+
}

0 commit comments

Comments
 (0)