Using and debugging sensors with ROBOTC

Over the last few days there have been some questions about the use of sensors in ROBOTC. More specifically, the questions have been of the following form.

I’m using the gyro and my robot does not stop turning.

I’m using an encoder and my robot does not stop.

etc.

So I figure it’s time to take a step back and talk a little about how to configure and debug sensors with ROBOTC.

Always test and configure the sensors before writing any code to use them.

ROBOTC has a wonderful debugger, you don’t need to write any significant amount of code to test your sensors, all you really need is to configure them in the “motors and sensors” dialog and then download something simple so ROBOTC understands what they are all configured as.

I setup a demo robot with the following sensors installed.
gyro
potentiometer
ultrasonic
quad encoder (optical shaft encoder)
An IME on a high speed 393 motor
and a couple of leds to show the code was running.

I then created a small test program that looked as follows.

#pragma config(I2C_Usage, I2C1, i2cSensors)
#pragma config(Sensor, in1,    gyro_1,         sensorGyro)
#pragma config(Sensor, in3,    pot_1,          sensorPotentiometer)
#pragma config(Sensor, dgtl1,  led_1,          sensorLEDtoVCC)
#pragma config(Sensor, dgtl2,  led_2,          sensorLEDtoVCC)
#pragma config(Sensor, dgtl3,  sonar_1,        sensorSONAR_inch)
#pragma config(Sensor, dgtl7,  encoder_1,      sensorQuadEncoder)
#pragma config(Sensor, I2C_1,  ime_1,          sensorQuadEncoderOnI2CPort,    , AutoAssign)
#pragma config(Motor,  port1,  motor_1,        tmotorVex393HighSpeed, openLoop, reversed, encoder, encoderPort, I2C_1, 1000)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

task main()
{
    static  int count;    

    // reset the gyro
    SensorType in1 ] = sensorNone;
    wait1Msec(1000);
    SensorType in1 ] = sensorGyro;
    
    while(1)
        {
        // flash some leds to show is running
        SensorValue led_1 ] = (count >> 4) & 1;
        SensorValue led_2 ] = (count >> 6) & 1;
        
        count++;
        
        wait1Msec(8);
        }
}

The code doesn’t really do much, it’s initializes the gyro by disabling and then re-enabling the sensor, then enters an infinite loop that flashes the leds (you don’t have to do this, I already had the leds installed).

compile and download this code and then open the “sensors” and “motors” debug windows. These are enabled using the robot->Debugger_Windows menu after the debugger has been started.

[ATTACH]7789[/ATTACH]

The default position for these windows is at the bottom of the main ROBOTC window as tabbed options. They can be dragged away from this position if desired so they can be seen simultaneously. Here are these two windows when the above code was running.

[ATTACH]7790[/ATTACH]

As the robot is manually moved and each sensor changed, the values in these debug windows will change. Here are some thoughts about each one.

Gyro

Rotate the robot, does the value of the gyro sensor change? When you rotate the robot clockwise does the value increase or decrease, remember how this is changing so that when you write the autonomous code you check for values with the correct sign. Is the gyro value steady when the robot is not moving? The gyro’s value is shown in 1/10 degrees and it will drift a little even the robot is not moving. The default range for the gyro is -3600 to 3600, if you need values outside this range the full scale value of the gyro can be adjusted (see the ROBOTC wiki) or you can wrap the sensor reading call with a function that tracks the absolute value (search the forums for my gyro library).

Potentiometer

The potentiometer should show values that change from 0 at one end of it’s travel to 4095 at the other. It’s usually best to avoid the extreme ends of the range, I would suggest you try and adjust the potentiometer’s position so that you have a range of 500 to 3500 or thereabouts.

If the potentiometer is part of an arm system, does the value increase when moving the arm up? If the value decreases you may want to remove and reverse it on the shaft, it’s always easier to standardize on the direction that you expect to see increasing values. If you really cannot reverse the pot then I would surround it with code that reverses the range for you, something like this.

int readPot()
{
    potvalue = SensorValue pot_1 ];
    return( 4095 - potvalue );
}

Sonar

The sonar (ultrasonic sensor) has two connections to the cortex and connects to digital ports. The wire labeled “input” (the one with the yellow wire) goes to the lower numbered digital port. The wire labeled “output” (the one with the orange wire) goes to the higher numbered (and sequential) digital port. In my example above it used digital 3 and digital 4. If the sonar is correctly connected then the value in the sensors debug window should change as you wave your hand in front of it. If the sonar shows a value of (-1) then check the connections and make sure that an object is within range. When the sonar is working you may be able to hear the ultrasonic pulses it sends out as rapid clicks (I can, you should be able to). Check that you configured the sonar for the units you expect to use, either mm, cm or inches. Avoid using the “raw” sonar setting, not very useful.

Quad encoder
This is the large red encoder sometimes known as an optical shaft encoder.

Turn the shaft of the encoder, do you see the value increment or decrement in the sensors debug window? If the values just changes between 0 and 1 then you have not correctly connected it to the cortex, the encoder wires need to be in sequential digital ports. If the encoder counts in the opposite direction to what you would like then swap the two cables going to the cortex (swap ports, don’t reverse the polarity), this will reverse the counter. It’s best to have the encoder count increase as motors are given positive command speeds and the robot drives forward. If one or both of these are incorrect then change the robot setup until you have that situation. Obviously your code can compensate for this but it really does keep things clear of motor forwards causes the encoder count to increase.

You can assign a quad encoder to a motor in the same way an IME is assigned to a motor, this allows you to use nMotorEncoder to retrieve its value in the way I explain in the next paragraph.

IME

The IME works in a similar way to the quad encoder. The IME value is visible in two different places, the sensor debug windows shows raw encoder counts. The motors debug window shows the same value, however, it may have a different sign as the reverse flag of the motor is also taken into account.

The IME sensor value can be retrieved in your code in two different ways.

SensorValue is given the I2C port number and will return a 16 bit number, that is, a number in the range -32768 to 32767.

nMotorEncoder is given the motor port number and will return a 32 bit number.

I suggest that you always use nMotorEncoder when using IMEs, the IME is physically attached to the motor, using nMotorEncoder and the motor port helps when changing the order of IMEs in the I2C daisy chain.

Some final thoughts.

Avoid using digital ports 4 and 10 at the same time with either ultrasonic or quad encoders. There is a hardware issue that causes a conflict with these two when used with these sensors.

Make sure the sensor is connected to the correct type of port, analog or digital. the only type of sensors that can theoretically use either are switches (bump or limit).

Adjust the sensor (and motors) for logical output, motor forwards, robot forwards and sensor counter increasing.

Check the sensors and adjust everything before trying to run autonomous code.

Once everything is as you would like then document this in your engineering notebook, draw connection diagrams if necessary.
robotc_sensors_1.jpg
robotc_sensors.jpg

1 Like

Thanks for the great post - I’m going to send this link on to the other programmers on our team :).

Just one question - I remember reading about the port 4 and 10 conflict somewhere but I can’t seem to find where now. Could you explain the technical reason behind this conflict or link me to wherever it was explained before?

1 Like

The short answer is that they share the same interrupt.

some old posts discussing this.
https://vexforum.com/showpost.php?p=325870&postcount=3
https://vexforum.com/showpost.php?p=314422&postcount=6

1 Like

I started playing around with this and it seems like it should be possible to get a quad encoder working in the analog ports too, no?

It’s definitely possible to tell whether the sensor is “high” or “low” based on the output value and using that could one not then follow the same algorithm for calculating direction and distance as when it’s in a digital port?

I understand that the sonars probably wouldn’t work because of the lack of interrupts but I imagine OSEs could be made to work.

The quad encoders need interrupts, they actually need more resource that the sonars as both inputs need to interrupt on rising and falling edges. The software overhead to sample each input in code will be too great unless the encoder is only moving slowly, you would probably start to miss counts at perhaps one revolution per second, I’ve done that for simple rotary switches which perhaps only have 32 counts per rev and it’s a PITA.

The analog ports can be reprogrammed to act like digital ports, they use PA0 (port A 0) through PA3 and PC0 through PC3. There would be some conflict with digital 11 and 12 which use PD0 and PD1.

One issue with the analog ports is that they use different external components giving a different input impedance, see the wiki.

So, yes, technically you could probably make two quad encoders work (in ConVEX, not ROBOTC), but what’s the point.

Switches and jumpers work without modification.

1 Like

Ok, couldn’t resist, here’s some code that reads analog inputs 1 & 2 and decodes the quad encoder with software rather than interrupts. I DON’T recommend anyone actually uses this although I guess it could be used as an LCD menu selector or something.

#pragma config(Sensor, in1,    encoder_1_a,    sensorAnalog)
#pragma config(Sensor, in2,    encoder_1_b,    sensorAnalog)
#pragma config(Sensor, dgtl1,  led_1,          sensorLEDtoVCC)
#pragma config(Sensor, dgtl2,  led_2,          sensorLEDtoVCC)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

task main()
{
    static  int count = 0;    
    static  int pa, pb;
    static  int old_pa, old_pb;
    
    
    while(1)
        {
        // Display encoder count on the leds
        SensorValue led_1 ] = (count     ) & 1;
        SensorValue led_2 ] = (count >> 4) & 1;
        
        // read and threshold the two inputs
        pa = (SensorValue encoder_1_a ] > 2048) ? 1 : 0;
        pb = (SensorValue encoder_1_b ] > 2048) ? 1 : 0;
        
        // detect state change and decode
        if( pa != old_pa )
            {
            // input a change state
            if( pa ^ pb )count--; else count++;
            }
        else
        if( pb != old_pb )
            {
            // input b change state
            if( pa ^ pb )count++; else count--;
            }
        old_pa = pa;
        old_pb = pb;
        
        wait1Msec(1);
        }
}
1 Like

So I had written a program that used the gyro, and reset it in the same way that you show in your sample code. The problem I am having is that 1/4 of the time the correct values show up and it works great. The other 3/4 of the timethe gyro values fluctuate randomly with very large swings in values (i.e. 3000 -> 2345 -> 3451 -> 37 -> 2133).

void pre_auton
{
SensorValue in7 ] = SensorNone;
SensorValue in8 ] = SensorNone;
Wait1Msec( 2000 );
SensorValue in7 ] = SensorGyro;
SensorValue in8 ] = SensorGyro;;
}

So you are using two gyros ? Do both of them behave the same way?

Is the robot stationary during the period that you are initializing them?

Yes, two gyros. One is used for executing turns in autonomous. The other is for the robot to know if it is tipping. Both gyros do behave in the same way. When they are initialized the robot is stationary, but not always pointing the same direction. Could that have something to do with it?

Not sure which sample code you are using, this is how I usually initialize.

    // Cause the gyro to reinitialize (a theory anyway)
    SensorType[GyroInput] = sensorNone;

    // Wait 1/2 sec
    wait10Msec(50);

    // Gyro should be motionless here
    SensorType[GyroInput] = sensorGyro;

    // Wait 1/2 sec
    wait10Msec(50);

You need a delay after the sensor is set to be a gyro before using it. That may not be the issue as once you leave pre_auton ROBOTC will wait for the competition to start. I’ve never tried two gyros on the same robot, if you just try one does it work? If you switch the analog ports to be potentiometers, what are the raw values you read (turn on the initialization code as well), most gyros seem to return about 1860 (+/- 10) when stationary.

Which version of ROBOTC are you using ?

1 Like

I can’t test right now as I am at home, but I will see if any of these work tomorrow… Thanks!