When I try to use function “getMotorTargetCompleted” described in help, RobotC throws an Undefined procedure error. What’s wrong, me, documentation or compiler?
Hi Philo,
I just tried this line of code: while(getMotorTargetCompleted(port2) != true) { } and the compiler accepted without issue. Can you confirm that you’re in VEX IQ mode (and not VEX Cortex mode) and are using ROBOTC 4.08?
Yes, I am in IQ mode, and with RobotC 4.08 (if that matters, I use the pre-release version from a few days ago, I have not reinstalled the released version). Here is the code:
#pragma config(Motor, motor7, Gripper, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor8, ArmLift, tmotorVexIQ, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard !!*//
void PickBrick(void)
{
// Pick Brick
setMotorTarget(Gripper, -200, 40);
while(getMotorTargetCompleted(Gripper) == false) wait1Msec(100);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
while(getMotorTargetCompleted(ArmLift) == false) wait1Msec(100);
}
task main()
{
PickBrick();
}
Error message:
File “test.c” compiled on Apr 02 2014 20:19:53
Error:Undefined procedure ‘getMotorTargetCompleted’.
Error:Undefined procedure ‘getMotorTargetCompleted’.
Ah ha… I see the problem now:
getMotorTargetCompleted - this is a VEX CORTEX specific function, hence why it may be (incorrectly) showing up in the text completion.
For the VEX IQ, you should use the following methods:
getMotorZeroVelocity(nMotorIndex) - Raw boolean value directly from the motor to indicate if the motor is moving.
getMotorMoving(nMotorIndex) - A macro that evaluates to: !getMotorZeroVelocity(nMotorIndex)
waitUntilMotorStop(nMotorIndex) - A macro that evaluates to specifically what you’re looking for ->
do
{
sleep(100);
while(!getMotorZeroVelocity(nMotorIndex))
{
sleep(1);
}
} while(false)
So in your code, update your PickBrick function in your program to say…
void PickBrick(void)
{
// Pick Brick
setMotorTarget(Gripper, -200, 40);
waitUntilMotorStop(Gripper);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
waitUntilMotorStop(ArmLift);
}
Actually waitUntilMotorStop was what I used previously, but I wanted to try something different as I have issues (setMotorTarget doesn’t move motor correctly, it stops before reaching target) in a multitask program.
But I am not yet certain that my program logic is flawless One thing for sure, it is not a resource sharing problem between tasks, because each motor is controlled by only one task.
setMotorTarget moves a motor to an absolute position. It’ll do so uninterrupted unless the motor is given a different command (such as a **setMotorPower(motor1, 0); **) and at that point it may not complete it’s movement. The good news is you can continue to send a “setMotorTarget” repeatedly, and the motor will simply not move if it is already at that target (it may jitter a little, however).
The VEX IQ motors are very accurate and have a tolerance of about 3 “counts” before it’s considered at target. With a scale of 960 counts per rotation, this works out to just around a degree of accuracy.
If you have a specific example with multitasking that you’re having trouble with, post the code here or send me an e-mail via [email protected] and I’ll be happy to work through it with you.
I am convinced that the motors are fine! the single task version of the program works perfectly
My color sorter has a conveyor belt moving the colored bricks, driven by one motor. Presence of a brick is detected by the distance sensor. The brick is then moved in front of the color sensor that reads color, and then to the pick-up position where the conveyor is stopped. An arm with a gripper seizes the brick, and lift it up, then moves the brick in a bin according to color. Quite simple actually… In order to speed up things, I wanted to restart the conveyor belt during the arm action, so I split the program in two taks, one that moves the conveyor and detects brick color, the other that moves the arm. Communication is done by a single global variable, a non 0 value tells the arm task to move the brick to that bin coordinate, and when the arm is done the variable is cleared.
Problem is that with the dual task program, the gripper is not actuated (I just see it jitter a bit) and the arm is not lift BUT the arm moves correctly to the output bin.
I’m pretty sure I make an obvious mistake somewhere but… can’t find it. So here is the buggy multitask program:
#pragma config(Sensor, port3, Color, sensorVexIQ_Color12Color)
#pragma config(Sensor, port4, Distance, sensorVexIQ_Distance)
#pragma config(Sensor, port5, ColorDisplay, sensorVexIQ_LED)
#pragma config(Sensor, port9, StartAgain, sensorVexIQ_LED)
#pragma config(Motor, motor1, Belt, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor2, ArmRotate, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor7, Gripper, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor8, ArmLift, tmotorVexIQ, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard !!*//
int ArmAngle;
void InitMotors(void)
{
setMotorBrakeMode(ArmLift, motorCoast);
setMotorCurrentLimit(ArmLift, 100);
setMotorSpeed(ArmLift, 40);
waitUntilMotorStop(ArmLift);
setMotorSpeed(ArmLift, 0);
wait1Msec(200);
resetMotorEncoder(ArmLift);
setMotorBrakeMode(ArmLift, motorHold);
setMotorBrakeMode(ArmRotate, motorCoast);
setMotorCurrentLimit(ArmRotate, 60);
setMotorSpeed(ArmRotate, 40);
waitUntilMotorStop(ArmRotate);
setMotorSpeed(ArmRotate, 0);
wait1Msec(200);
resetMotorEncoder(ArmRotate);
setMotorBrakeMode(ArmRotate, motorHold);
setMotorBrakeMode(Gripper, motorCoast);
setMotorCurrentLimit(Gripper, 100);
setMotorSpeed(Gripper, 40);
waitUntilMotorStop(Gripper);
setMotorSpeed(Gripper, 0);
wait1Msec(200);
resetMotorEncoder(Gripper);
setMotorBrakeMode(Gripper, motorHold);
resetMotorEncoder(Belt);
}
void ResetArm(void)
{
setMotorTarget(Gripper, -300, 40);
waitUntilMotorStop(Gripper);
setMotorTarget(ArmLift, -150, 40);
setMotorTarget(ArmRotate, 0, 100);
waitUntilMotorStop(ArmLift);
waitUntilMotorStop(ArmRotate);
}
void PickBrick(void)
{
// Pick Brick
setMotorTarget(Gripper, -200, 40);
waitUntilMotorStop(Gripper);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
waitUntilMotorStop(ArmLift);
}
void MoveArm(int ArmRotAngle)
{
// Turn Arm
setMotorTarget(ArmRotate, ArmRotAngle, 100);
waitUntilMotorStop(ArmRotate);
// Lower Arm
setMotorTarget(ArmLift, -150, 40);
waitUntilMotorStop(ArmLift);
// Drop Brick
setMotorTarget(Gripper, -300, 40);
waitUntilMotorStop(Gripper);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
waitUntilMotorStop(ArmLift);
}
// Arm rotate range: 0 (on belt) to -600
// Arm lift range: 0 (fully up) to -150 (on belt)
// Gripper range: 0 (fully close) to -300 (fully open)
task Dispatch()
{
int ArmRotAngle;
while(true)
{
while(ArmAngle == 0) wait1Msec(100);
PickBrick();
ArmRotAngle = ArmAngle;
ArmAngle = 0;
MoveArm(ArmRotAngle);
ResetArm();
}
}
task main()
{
int currentHueValue, detectedColor;
InitMotors();
int ArmRotAngle;
setColorMode(Color,colorTypeRGB_Hue_Reflected);
ArmAngle=0;
startTask(Dispatch);
while(true)
{
ResetArm();
setMotorSpeed(Belt, 35);
do
{
while (getDistanceStrongest(Distance)>40)
{
wait1Msec(20);
if(getMotorEncoder(Belt)>1100)
{
setMotorSpeed(Belt, 0);
setTouchLEDBlinkTime(StartAgain, 10, 10);
setTouchLEDColor(StartAgain, colorGreen);
while(getTouchLEDValue(StartAgain)==0)wait1Msec(100);
setTouchLEDColor(StartAgain, colorNone);
setMotorSpeed(Belt, 35);
resetMotorEncoder(Belt);
}
}
} while (getDistanceStrongest(Distance)>40);
resetMotorEncoder(Belt);
while (getMotorEncoder(Belt)<315) wait1Msec(10);
currentHueValue = getColorValue(Color);
if((currentHueValue <= 15)||(currentHueValue) > 250)
{
detectedColor = colorRed;
ArmRotAngle = -215;
}
else if((currentHueValue > 15) && (currentHueValue <= 40))
{
detectedColor = colorOrange;
ArmRotAngle = -275;
}
else if((currentHueValue > 40) && (currentHueValue <= 90))
{
detectedColor = colorYellow;
ArmRotAngle = -365;
}
else if((currentHueValue > 90) && (currentHueValue <= 130))
{
detectedColor = colorGreen;
ArmRotAngle = -480;
}
else if((currentHueValue > 131) && (currentHueValue <= 219))
{
detectedColor = colorBlue;
ArmRotAngle = -550;
}
setTouchLEDColor(ColorDisplay, detectedColor);
while (getMotorEncoder(Belt)<560) wait1Msec(10);
setMotorSpeed(Belt, 0);
wait1Msec(100);
while(ArmAngle != 0) wait1Msec(100);
ArmAngle = ArmRotAngle;
setTouchLEDColor(ColorDisplay, colorNone);
wait1Msec(100);
}
}
…And the working straight program (can’t attach both in the same message, size limit reached):
#pragma config(Sensor, port3, Color, sensorVexIQ_Color12Color)
#pragma config(Sensor, port4, Distance, sensorVexIQ_Distance)
#pragma config(Sensor, port5, ColorDisplay, sensorVexIQ_LED)
#pragma config(Sensor, port9, StartAgain, sensorVexIQ_LED)
#pragma config(Motor, motor1, Belt, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor2, ArmRotate, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor7, Gripper, tmotorVexIQ, PIDControl, encoder)
#pragma config(Motor, motor8, ArmLift, tmotorVexIQ, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard !!*//
void InitMotors(void)
{
setMotorBrakeMode(ArmLift, motorCoast);
setMotorCurrentLimit(ArmLift, 100);
setMotorSpeed(ArmLift, 40);
waitUntilMotorStop(ArmLift);
setMotorSpeed(ArmLift, 0);
wait1Msec(200);
resetMotorEncoder(ArmLift);
setMotorBrakeMode(ArmLift, motorHold);
setMotorBrakeMode(ArmRotate, motorCoast);
setMotorCurrentLimit(ArmRotate, 60);
setMotorSpeed(ArmRotate, 40);
waitUntilMotorStop(ArmRotate);
setMotorSpeed(ArmRotate, 0);
wait1Msec(200);
resetMotorEncoder(ArmRotate);
setMotorBrakeMode(ArmRotate, motorHold);
setMotorBrakeMode(Gripper, motorCoast);
setMotorCurrentLimit(Gripper, 100);
setMotorSpeed(Gripper, 40);
waitUntilMotorStop(Gripper);
setMotorSpeed(Gripper, 0);
wait1Msec(200);
resetMotorEncoder(Gripper);
setMotorBrakeMode(Gripper, motorHold);
resetMotorEncoder(Belt);
}
void ResetArm(void)
{
setMotorTarget(Gripper, -300, 40);
waitUntilMotorStop(Gripper);
setMotorTarget(ArmLift, -150, 40);
setMotorTarget(ArmRotate, 0, 100);
waitUntilMotorStop(ArmLift);
waitUntilMotorStop(ArmRotate);
}
void MoveArm(int ArmAngle)
{
// Pick Brick
setMotorTarget(Gripper, -200, 40);
waitUntilMotorStop(Gripper);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
waitUntilMotorStop(ArmLift);
// Turn Arm
setMotorTarget(ArmRotate, ArmAngle, 100);
waitUntilMotorStop(ArmRotate);
// Lower Arm
setMotorTarget(ArmLift, -150, 40);
waitUntilMotorStop(ArmLift);
// Drop Brick
setMotorTarget(Gripper, -300, 40);
waitUntilMotorStop(Gripper);
// Raise Arm
setMotorTarget(ArmLift, -80, 40);
waitUntilMotorStop(ArmLift);
}
// Arm rotate range: 0 (on belt) to -600
// Arm lift range: 0 (fully up) to -150 (on belt)
// Gripper range: 0 (fully close) to -300 (fully open)
task main()
{
int currentHueValue, detectedColor;
int ArmAngle;
InitMotors();
setColorMode(Color,colorTypeRGB_Hue_Reflected);
while(true)
{
ResetArm();
setMotorSpeed(Belt, 35);
do
{
while (getDistanceStrongest(Distance)>40)
{
wait1Msec(20);
if(getMotorEncoder(Belt)>1100)
{
setMotorSpeed(Belt, 0);
setTouchLEDBlinkTime(StartAgain, 10, 10);
setTouchLEDColor(StartAgain, colorGreen);
while(getTouchLEDValue(StartAgain)==0)wait1Msec(100);
setTouchLEDColor(StartAgain, colorNone);
setMotorSpeed(Belt, 35);
resetMotorEncoder(Belt);
}
}
} while (getDistanceStrongest(Distance)>40);
resetMotorEncoder(Belt);
while (getMotorEncoder(Belt)<315) wait1Msec(10);
currentHueValue = getColorValue(Color);
if((currentHueValue <= 15)||(currentHueValue) > 250)
{
detectedColor = colorRed;
ArmAngle = -215;
}
else if((currentHueValue > 15) && (currentHueValue <= 40))
{
detectedColor = colorOrange;
ArmAngle = -275;
}
else if((currentHueValue > 40) && (currentHueValue <= 90))
{
detectedColor = colorYellow;
ArmAngle = -365;
}
else if((currentHueValue > 90) && (currentHueValue <= 130))
{
detectedColor = colorGreen;
ArmAngle = -480;
}
else if((currentHueValue > 131) && (currentHueValue <= 219))
{
detectedColor = colorBlue;
ArmAngle = -550;
}
setTouchLEDColor(ColorDisplay, detectedColor);
while (getMotorEncoder(Belt)<560) wait1Msec(10);
setMotorSpeed(Belt, 0);
MoveArm(ArmAngle);
setTouchLEDColor(ColorDisplay, colorNone);
wait1Msec(100);
}
}
Thanks for any help…
Any hint about this issue?
Philo, I’m struggling with a very simular problem with my C4 robot. For me, it seems like the waitUntilMotorStop command is not working, correctly (or, at least as I expect)
One thing to point out in your program… You’re setting the current limit different for the two motors that aren’t working vs the one that is. setMotorCurrentLimit(ArmLift, 100); vs (60) That SHOULDN’T cause the problem, but maybe it is…
Start by setting them all to 60, and see if that changes anything.
Steve
Ah Thanks, Steve - Good news I am not alone with this issue. Current limit shouldn’t be a problem since it works fine on mono-task version (I’ll check nonetheless).
@jpearman: ResetArm is needed in main task to stat with correct position. It is not in main loop, only used when starting or re-starting after a lack of brick.
Keep in mind that with 4.08, the motor current commands have changed. From a previous post on the forums and from the 4.08 change logs:
A number of complaints received were in reference to the arbitrary “scale” of the motors when ModKit was using Amps/mA to indicate current. We updated the functions to keep the VEX IQ platform more in line across software packages.
The motor itself returns a value from 0-255 (a single byte) to represent the current. Internally, we now multiple that byte value by 14.6 per the developer documentation. So to convert from the old 0-255 values to the new values, you can do…
Byte Value = Current in mA / 14.6
Current In mA = Byte Value * 14.6
The limits for the motor’s current limiters are approximately 3723mA (255 * 14.6), although when reading from the getMotorCurrent(motor) function, I haven’t seen mA reading above 1000mA yet.
If you do not specify a motor current, the motor will default to the maximum as the limit (3723mA)
If you’re still using the old command with “byte” value from 4.06 and earlier, you’ll need to update from the byte value (0-255) to the new milliamp (mA) based value in 4.08. Multiplying your values by 14.6 should produce the result you’re looking for. This affects the following three commands:
- **setMotorCurrentLimit(nMotorIndex, nCurrent);**
- **getMotorCurrentLimit(nMotorIndex);**
- **getMotorCurrent(nMotorIndex);**
No, I properly made the changes when switching to 4.08. Power used is pretty low, better not break something if there is something unexpected is blocking movement
One other thing that may be causing issues for you as well - if the current limit is set REALLY low (as it might be if you’re using the old byte values), you may run into an issue where waitUntilMotorStop reports as “done” when the motor stalls out. This seems definitely like a bug, luckily it’s one you can fix yourself:
RobotCIntrinsics.c - Old Code (using getMotorZeroVelocity)
#define getMotorMoving(nMotorIndex) !getMotorZeroVelocity(nMotorIndex)
#define waitUntilMotorStop(nMotorIndex) do {sleep(100); while(!getMotorZeroVelocity(nMotorIndex)) sleep(1);} while(false)
RobotCIntrinsics.c - Fixed Code (using getMotorZeroPosition)
#define getMotorMoving(nMotorIndex) !getMotorZeroPosition(nMotorIndex)
#define waitUntilMotorStop(nMotorIndex) do {sleep(100); while(!getMotorZeroPosition(nMotorIndex)) sleep(1);} while(false)
You can quickly jump to this line of code in the RobotCIntrinsic.c file by right clicking on “waitUntilMotorStop” and selecting “Go to #define definition”
Making this small change will fix the issue where a stall condition will not cause the motor to incorrectly report as “reaching the target”. We’ll make the fix and release a new build with this resolved ASAP.
Mmhhh… The new definition does have drawbacks. I used waitUntilMotorStop at startup to calibrate motor position against mechanical stops, and this does no longer work as there is no zero position defined. I had to inlined old code in my init code to get it working - maybe two functions are needed?
…and this doesn’t solve my problem. I also tried to significantly increase motor current (to 200mA), no better.
You could implement something similar (which I plan on doing based on your feedback)
- Leave waitUntilMotorStop() as is.
- Add another command “waitUntilMotorTargetReached()” which will do the new modified version based on the reaching the specific target.
I’m not entirely sure why the waitUntilMotorStop() command would be behaving any differently - this is only reading a status bit directly from the motor, so it seem strange that it would be causing issues now but not in previous versions…
I was busy with VEX LDraw parts, so I have not yet checked, but after re-reading the code I think that you are right, ResetArm is the culprit… As I said initially, it was highly probable that I made an obvious mistake More later!
Edit: Got it working! thanks again jpearman for putting the problem under my nose… Last problem remaining is the distance sensor that sometimes misses a brick. But here it’s a simple sensor problem that occurs in single task too…