SensorValue vs. getMotorEncoder

Under RobotC, I wonder what is the difference between accessing quadrature encoders this or that way.
Suppose you have a motor on port2 with a quad encoder wired into dgtl1+dgtl2 and properly configured in the motor/sensor setup, including the linkage between the two. (I’ll use direct port names below instead of the configured aliases).

My students were using a mix of SensorValue[dgtl1], getMotorEncoder(motor2), resetMotorEncoder(motor2) and SensorValue[dgtl1]=0 and it was causing them some troubles that we traced to the discrepancies between read values (and/or the datalog reported values). They have since pretty much standardized on only accessing the SensorValue[port], since when not using built-in PID, the RobotC runtime doesn’t even need to know and shouldn’t care about the linkage.

I have seen some people around the net complaining to the same tune, so I wonder:
Is one accessing a different internal value than the other? How? Why?
Which approach is preferable? Why?

I have no idea but coming from experience and to give you the quickest answer.

Encoders and Gyros work different than other analog or digital sensors (like a potentiometer or a bumper switch). Essentially, if you’re dealing with an encoder value; use getMotorEncoder since it was built for that specific purpose.

I might be wrong, but someone can correct me.

I’m not sure about zeroing out the encoder, but SensorValue is the straight reading, and if the encoder is linked to a motor, getMotorEncoder or nMotorEncoder will reverse the number if the motor is reversed. I haven’t tested if zeroing one will do anything to the other.

Our teams use SensorValue for it all and just do SensorValue(NameofTask) = 0; to clear it.

IMEs = getMotorEncoder

Quadrature Encoder = SensorValue

I agree, there are too many ways, it’s a byproduct of ROBOTC having been evolved over more than 10 years of development and many different platforms.

SensorValue is the generic way of reading the value of any type of sensor, it was at one time long ago the only way to read sensors and was created when ROBOTC mostly ran on 8 and 16 bit cpus. SensorValue returns an integer but values are really 16 bit, that is, they will wrap at 32767. This is ok for many sensors but can be a problem for encoders if the count gets large.

nMotorEncoder and getMotorEncoder return values as long integer (32 bit values) and also considers the reverse flag on the motor. The idea is that, with correct wiring, the encoder will always increment as the motor is commanded with positive values. getMotorEncoder was introduced, I believe, when we added support for the VexIQ platform. There was a move towards making everything function based rather than property based ( the weird way ROBOTC uses the array like naming to access things like motor speed and sensor value)

So I took the time and run an experiment. Code below.
Observations:

  1. writing to the SensorValue resets both the SensorValue and the getMotorEncoder output.
  2. Calling resetMotorEncoder() resets just the getMotorEncoder output, SensorValue stays.
  3. The SensorValue doesn’t wrap at 32768. Not even on 65536 on latest RobotC.

My recommendation to the team (for the time being, we’ll see for V5):
Stick with SensorValue for both read and reset. Reset that way is more reliable, you know what are you measuring and logging in the datalogs. The RobotC doesn’t even need to know about the coupling, so less room for errors.

Log:


At start: Sensor=0, enc=0
1s forward: Sensor=530, enc=530
quad reset: Sensor=0, enc=0
1s back: Sensor=-514, enc=-514
enc reset: Sensor=-514, enc=0
after 0s: Sensor=-514, enc=0
after 1s: Sensor=-16, enc=498
...]
after 127s: Sensor=71672, enc=72186
after 128s: Sensor=72241, enc=72755
after 129s: Sensor=72810, enc=73324

Source:


#pragma config(Sensor, dgtl1,  quad,           sensorQuadEncoder)
#pragma config(Motor,  port10,          mot,           tmotorVex269_HBridge, openLoop, encoderPort, dgtl1)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

void report(char *label) {
    writeDebugStream("%s: Sensor=%d, enc=%d\n", label, SensorValue[quad], getMotorEncoder(mot));
}

void runMotorFor(int millis, int speed=50) {
    setMotor(mot, speed);
    wait1Msec(millis);
    setMotor(mot, 0);
}

task main()
{
    clearDebugStream();
    SensorValue[quad] = 0;
    resetMotorEncoder(mot);
    report("At start");

    runMotorFor(1000);
    report("1s forward");

    SensorValue[quad] = 0;
    report("quad reset");

    runMotorFor(1000, -50);
    report("1s back");

    resetMotorEncoder(mot);
    report("enc reset");

    setMotor(mot, 50);
    for (int i=0; i<1000; i++) {
        string time;
        sprintf(time, "after %ds", i);
        report(time);
        wait(1, seconds);
    }
}