NEO - getting internal encoder value does not seem to work

We have done all our previous builds with CTR hardware, and I think we understand it pretty well. We are evaluating a possibility to replace all or some of it with NEOs. So, we’re building a simple project - tank drive with 2 NEOs, one on each side.
Our first task is to simply display encoder values as motors are moving. Since we use brushless NEOs, it is my understanding we HAVE TO to use internal NEO encoders if we want to use 1ms loop and have an encoder directly connecting to the NEO (please, comment, if that’s not the case).

The end result is - we turn the robot ON, encoders are set to 0.

I turn the wheel of the robot (by hand; the robot is not enabled) so presumably the motor moves. And … the value on SmartDashboard does not change.

I would appreciate a comment what did we do wrong.

In the REV software telemetry moving the wheel manually does change the graph, so I believe it sees it. So, how do I get the “ticks” of the encoder, corresponding distances or whatever I can use to track the amount of the movement? The code is below, if you have time. Sorry for the noob questions.


The motors are defined this way:

private CANSparkMax rightMotor = new CANSparkMax(21, MotorType.kBrushless);
private CANSparkMax leftMotor = new CANSparkMax(20, MotorType.kBrushless);

eencoders are defined this way:

rightEncoder = rightMotor.getEncoder(Type.kHallSensor, 42);
leftEncoder = leftMotor.getEncoder(Type.kHallSensor, 42);

I wanted to see if I can reset the encoders to 0 (I assume these are not absolute?), so I did this -


Finally, I have this for my smartdahsboard subsystem (these lines run every 20ms):

SmartDashboard.putNumber("Left Encoders",RobotContainer.driveSubsystem.getLeftEncoder());
SmartDashboard.putNumber("Right Encoders",RobotContainer.driveSubsystem.getRightEncoder());

with these methods:

/** Get the number of tics moved by the left encoder */
public double getLeftEncoder() {
  return leftEncoder.getPosition();

/** Get the number of tics moved by the left encoder */
public double getRightEncoder() {
  return rightEncoder.getPosition();

As a side note, we updated firmware, configured unique CAN IDs etc. We did not, however, do any advanced programming of SPARKs. Our full (and very simple) code for all this is here:

Any advice or help is always appreciated.

1 Like

I believe you need to use the getEncoder() method but without the parameters to access the internal encoder on the neos

//Like this:
rightEncoder = rightMotor.getEncoder();
leftEncoder = leftMotor.getEncoder();

See Revlib docs:

1 Like

Not specific to your problem, but you can use an alternate encoder as well if you like. The REV throughbore encoder is the easiest to use with REV’s adapter, but you could make your own adapter board for any encoder. You still connect the neo’s encoder cable to the encoder port, but then you connect the external encoder to the data port on top of the sparkMAX. In your code you need to set the sparkMax into alternate encoder mode but then you can use the external encoder for PID control.

Look at absoluteEncoder and angleController in this code for an example:
Note that this is specifically the REV throughbore encoder.

1 Like

We tried using

rightEncoder = rightMotor.getEncoder();
instead of
rightEncoder = rightMotor.getEncoder(Type.kHallSensor, 42);

and the result was the same.

The point is - we want to use encoder that is directly attached to SPRK, which controls NEO. And from what I am reading, if you use other encoders, you cannot do so. In other words, if you use NEO (brushless), you MUST attach its data cable to REV (let me know if that’s not the case).

The reason why I’d like to stick with that - any encoders not attached to the same SPARK that control NEO will use 20ms PID loop, and I probably cannot use hardware PID.

Try throwing
in the constructor of both subsytems. This ensures the periodic() function gets run appropriately.

Wpilib docs has more information:

The subsystems are definitely registered.

I actually put System.out.print for the word “test” just before printing the encoder on SmartDashboard, and it indeed prints “test” every 20ms.

So, it’s the encoder value update that is an issue, not the subsystem periodic method.

Thanks for the examples.

In fact, I see

driveEncoder = driveMotor.getEncoder();

in that code as well. So, what happens when you plug NEO to the encoder port and the external encoder to the data port?
The method to get to the encoder object is the same. Does it mean that once you plug in external encoder, the internal one will not work? Will it still be Ok with the brushless motor? I thought that the encoder there in some way controlled the brushless cycle.

Well While I’m staring at your code more I can give a bit of insight on this:

You can still access the Neo encoder with getEncoder() like you would usually, and can access the second encoder with getAlternateEncoder()

The extra parameters on getEncoder() are designed to be used when driving a brushed motor and plugging a seperate encoder into the JST encoder port.

There’s details on which pins to use and how to configure an alternate encoder here:

The drive motor is only using the NEO’s internal encoder; it’s the angle motor that has the external encoder attached. The NEO’s encoder is still used by the sparkMax to run the motor, but the getAbsoluteEncoder method lets you access an (absolute) external encoder. angleController.setFeedbackDevice then tells the sparkMax to use the absolute encoder for PID control.

1 Like

Are you still testing using this method?

If so, it might be smart to try writing up some code to make the wheels spin on enable, and seeing if that changes the encoder values. As far as I can see all your code for encoder readings is fine, and this is the last potential problem area I see.

Yes, that’s our plan for the next session - to see if the motor-induced wheel turn would make a difference. Still, the REV software that configures the motor did detect the encoder change. So, I still have a feeling I am not doing something right.

With CTR there are no such issues - you turn the motor, you get the change right away.

Yes, I see that, thanks -

absoluteEncoder = angleMotor.getAbsoluteEncoder(Type.kDutyCycle);


Sounds good. We will eventually try it. But for the trajectories we need the linear drive encoder to work as well. And in your case you’re saying it’s a built-in encoder? I am wondering then why it did not work for me … I will try applying other settings that you did for the drive motor… Unless you have other ideas?

As a followup - we traced the issue to the flaky CAN connection.

So, everything works as expected.

Thanks, everyone, for the guidance.


Glad to hear you got everything worked out, best of luck with the rest of your neo experiments :slight_smile: