Running command on secondary controller axis change

I’m guessing this is an easy one, but I can only find references on how to do this with buttons.

So the scenario, I have my winch motor running on an axis on our secondary controller. I’m setup as a command based program, the default command is our drive base. How can I trigger a command to run when the second controller moves its axis? I’d rather have it on an axis so we can control speed rather than an on/off button. Thanks!

I would advise against starting a command when an axis reads nonzero; it is a somewhat unnatural way to use a command (is there no conceivable difference in your implementation between commanding a 0 output, and not having a command? do you want your exit command logic to occur every time the joystick passes over 0?). A better solution would be to have the command always running in the background (make it the default command for the winch subsystem), or to have an “enable” button for it.

However, if you are dead-set on enabling the command when the axis reads nonzero, you have a few options:

Firstly, you can directly put, in teleop periodic, code that checks if the axis has become nonzero between the previous iteration and the current iteration, and if so, schedules the command (via scheduler.add). Keep in mind that, unless you want to constantly be scheduling the command over and over again while the axis is nonzero, you must use logic that detects if the axis has changed from zero to nonzero. This is an easy approach, but inelegant and inflexible.

Secondly, you can create an instance of InternalButton and, similarly, call setPressed with “axis != 0” during teleopperiodic. The command can then be bound to this button as per usual. This is slightly less-inelegant, and slightly less-inflexible. It still requires ugly extraneous code cluttering your periodic method; I do not like it.

Thirdly, you can subclass button, and override get() to return true when the axis value is nonzero. Then, construct an instance of your subclass and use it as you would any other button. This is probably the best solution, but is slightly less-obvious to beginning programmers as it involves building directly off of lower-level WPIlib code.

Hmm, ok… I guess maybe I’m thinking about this the wrong way. If I set the default command on my winch subsystem, will that command just keep running the execute phase till I tell it to stop? And this won’t impact the drive base command that is running as the default command for my drive base system?

Each subsystem can have its own default command. The only constraint on concurrent commands is that you cannot concurrently schedule commands that run on the same subsystem (as specified through the requires() method). The scheduler will execute the state machines for each currently-scheduled command in parallel. As your winch and your drive are (or should be, at least) separate subsystems, there is no reason that running a winch command would interfere with the drive command (presuming your winch does not use the same motors as your drive).

(Note: the subsystem-level locking on commands is actually something I am not a fan of, as often commands do not actually use an entire subsystem yet it would not make sense to split the subsystem in two, such as a drive with a gear shifter - unfortunately, there really aren’t any simple ways to work around this other than neglecting to declare a lock with requires() in the first place, which is probably worse on the whole…)

Ok, that makes a bit more sense for what I’m trying to do. Thanks for the quick replies, I appreciate it! =D

No problem. If I might give you some other tips for using the command-based framework, while I have the opportunity:

By following screensteps, you will probably end up writing a winch command that polls a joystick which has been declared as a public variable somewhere in your code.

In general, I recommend against doing this - it results in inflexible code with a confusing information-flow. Instead, pass the joystick (or, better yet, pass a functional interface - lambda expressions are your friend!) to the command in its constructor. Suddenly, it is immediately clear from outside the command code itself what joystick is controlling the command, and it is also possible to re-use the command later on with a different joystick (or, if you have sent it a functional interface, with any method that returns a value for the motor to run, which may not come from a joystick at all!). You can also now make the joystick private, which makes it much easier to keep track of which things are using it (and thus makes it easier to later change things involving that joystick without breaking all of your code in hard-to-predict ways).

Similar benefits can be seen from passing subsystems to the commands that run on them.

It may seem like extra overhead, but it pays dividends in the long-run.

If the command is running continuously during Tele-Op, you could set a method that runs if the stick value != 0, or even better set a deadzone to avoid accidental bumping.

That might look like this;


Joystick joy1 = new Joystick(0);

if(joy1.getRawAxis(0) != 0){
//Run motor code
}

Else{
//Set motor speed to 0
}

Sent from my Z971 using Tapatalk