Hi all!
I am trying to run a command when a ToggleButton is pressed on Shuffleboard. Our robot is command-based, so I would prefer avoiding periodic polling.
In the past our team has used the SmartDashboard API, but I decided to migrate to the newer and more fully-featured Shuffleboard API. I found an example on WPILib’s own docs that seems to do exactly what I want:
ShuffleboardTab tab = Shuffleboard.getTab("Shooter");
GenericEntry shooterEnable = tab.add("Shooter Enable", false).getEntry();
new NetworkButton(shooterEnable).onTrue(new InstantCommand(m_shooter::enable));
But when I tried this, it didn’t work, because NetworkButton
does not have a constructor that takes a GenericEntry
. However, I did find a ChiefDelphi post (in C++) that seems to have a workaround:
frc::Shuffleboard::GetTab("Subsystems")
.GetLayout("Drive")
.AddBoolean("Reset Gyro",
[this] {
return(m_resetGyroButton);
})
.WithWidget(frc::BuiltInWidgets::kToggleButton)
.WithSize(2, 1)
.WithPosition(0, 7);
frc2::NetworkButton(
nt::NetworkTableInstance::GetDefault()
.GetBooleanTopic("/Shuffleboard/Subsystems/Drive/Reset Gyro")
)
.OnTrue(frc2::cmd::RunOnce(
[this] {
m_drive.ZeroHeading();
m_resetGyroButton = false;
},
{&m_drive})
);
However, this is a violation of DRY as the Subsystems/Drive/Reset Gyro
is repeated and thus vulnerable to changes in one but not the other. After a lot of searching I finally came up with this:
/*
* (Taken from our 2024 code)
* ShuffleboardTab dashboard;
* ShuffleboardLayout addMap(ShuffleboardTab, ~)
*/
Topic isEnabled =
ShuffleboardTabWithMaps.addMap(dashboard, "<name>", "%s", Map.of(
"<key>", value
)).add(
"IsEnabled", false
)
.withWidget(BuiltInWidgets.kToggleSwitch)
.getEntry()
.getTopic();
new NetworkButton(new BooleanTopic(isEnabled))
.onFalse(new InstantCommand(() -> { /* code */ }, m_arm))
.onTrue(new InstantCommand(() -> { /* code */}, m_arm));
But all of this seems awfully verbose - getEntry
, getTopic
, constructing a BooleanTopic
, constructing a NetworkButton
- just to bind a function to a button press, when code to do the same thing for a controller button is merely:
m_driverController.<button>().onTrue(/* whatever */);
Am I doing something wrong or is the API really this complicated? And I find it strange the lack of documentation on this subject, or perhaps I didn’t look hard enough, but I was debugging this for at least an hour.
Any help is appreciated.
Thanks,
2262