Robot C Graphical : is there a "stop all task"s in graphical for vex, we are trying to make a button that is an emergency stop. it is currently pretty laggy the way we have it written. Any suggestion how to write the code in graphical for an e-stop button?
How do you have it written?
We would like this:
-If Button Pressed
-Stop All Tasks
Right now we have to do this:
- if button pressed
- stop motor 1
-stop motor 2 - turn off flashlight
-etc
well, stopping tasks won’t stop the motors for you anyway. stopAllMotors()? (but you’d have to stop the tasks anyway, since they’d typically re-engage the motors again few millis later…)
This is one way to do it outside of the GUI.
void StopMotors(){
motor[rightMotor] = 0;
motor[leftMotor] = 0;
}
void MotorLogic(){
if(SensorValue(sonar) <= 10)
{
motor[rightMotor] = 32;
motor[leftMotor] = -32;
}
else
{
motor[rightMotor] = 127;
motor[leftMotor] = -127;
}
}
task main()
{
while(true)
{
if(SensorValue(e_stopBtn) == 1)
{
StopMotors(); // pauses the program.
}
else
{
MotorLogic(); // Runs the program.
}
wait1Msec(10);
}
}
You seem to have two quite similar threads going. I pointed out in the other thread that a lot of complexity is being needlessly added. You can do something like the following:
bool eStop = false;
task main() {
while(eStop != true){
if(SensorValue(sonar) <= 10) {
motor[rightMotor] = 32;
motor[leftMotor] = -32;
}
else {
motor[rightMotor] = 127;
motor[leftMotor] = -127;
}
if(SensorValue(e_stopBtn) == 1) {
eStop = true;
}
wait1Msec(10);
}
}
No need for tasks, and it can quickly be coded in all sorts of graphical languages.
task main() {
while((SensorValue(e_stopBtn) != 1){
if(SensorValue(sonar) <= 10) {
motor[rightMotor] = 32;
motor[leftMotor] = -32;
}
else {
motor[rightMotor] = 127;
motor[leftMotor] = -127;
}
wait1Msec(10);
}
}
To shorten the code and the number of variables you’d have, I’d just make the condition: “while the stopping button is NOT equal to one, keep looping”. However, @callen’s way is more intuitive. But I have a feeling that you’re asking for an emergency stop for driver…
I had debated writing it that way. Not being sure what other ways it might be employed, I chose not to as it’s much more portable this way. For example, let’s say the original is not while(true), but there is a sequence of things. You can make sure you check for the emergence stop in a bunch of places, and things that don’t inherently have checks can be placed inside if statements. Then you want a single emergency stop to be remembered.
+1 understandable
I’m new to the board, so let me introduce myself a bit here. I’m a volunteer in a local Vex robotics club, a parent to a member there, and a software engineer (so I teach programming to the club members).
Let me give you a viewpoint from a developer with many years of experience.
What you need to think carefully about is this construct:
motor left_id ] = 32;
motor right_id ] = 32;
It is central to your issue. It should be avoided.
It is typical that such constructs are “drive” concepts. Make that a function.
void drive( int lpower, int rpower )
{
motor left_id ] = lpower;
motor right_id ] = rpower;
}
This is more convenient everywhere you code changes in drive power. Everywhere you’re currently setting motor power for two motors (and in some systems FOUR separate motors), you can merely code:
drive( 32, 32 );
Why do this? Simple. Your main goal is that there should be ONE SINGLE PLACE TO CONTROL POWER.
If EVERYWHERE in your application you’re writing “drive” to power the drive train, then the drive function becomes the place where you can globally control that power.
// assume some global emergency power flag exists
Bool eStop = false;
void drive( int lpower, int rpower )
{
if ( eStop )
{
motor left_id ] = 0;
motor right_id ] = 0;
}
else {
motor left_id ] = lpower;
motor right_id ] = rpower;
}
}
With this, EVERY place you “drive” the bot, you’re checking for epower. You no longer need to do this at the top of a loop.
Now, when you respond to the button for emergency shut off, you set eStop to true, then call “drive( 0, 0)”, and then ALL TASKS THAT MIGHT DRIVE WILL AUTOMATICALLY APPLY ZERO POWER.
THEN, you can can stop tasks at leisure by other means.
However, there is another potential problem that requires attention.
You mention TASKS, but the code you provide is in the “main” task - there’s only one of those.
However, if you DO have multiple tasks running, it is possible that multiple tasks are attempting to control the motors AT THE SAME TIME. This means the program could attempt to stop the motors WHILE ANOTHER TASK is in the process of applying power. Which one “wins” depends on chaos.
So, you should lock access to the “drive” function, so that ONLY ONE THREAD can control motor power at a time.
// assume some global emergency power flag exists
volatile Bool eStop = false;
// setup a semaphore
TSemaphore DriveSync;
// call this one time during initialization code
void initSemaphores()
{
SemaphoreInitialize( DriveSync );
}
void drive( int lpower, int rpower )
{
SemaphoreLock( DriveSync );
if ( eStop )
{
motor left_id ] = 0;
motor right_id ] = 0;
}
else {
motor left_id ] = lpower;
motor right_id ] = rpower;
}
SemaphoreUnlock( DriveSync );
}
Now, even if multiple tasks are calling “drive”, only one at a time will prevail.
One could argue that the global value “eStop” needs protection with a semaphore, but fortunately it doesn’t matter in THIS case.
It might require the “volatile” keyword, though. This keyword instructs the compiler that the value of eStop should not be cached when being evaluated. If this is not used there could be times when a test of eStop is made, but the code responds to an “out of date” version stored locally. The volatile keyword enforces a rule by which the code is sure to obtain a “fresh” read on the value, so that if other threads have changed eStop, the test involves the actual, current value and not some cached value local to a thread.
This represents a fundamental concept of software engineering. Every time something is repeated (like you’re always writing TWO motor[id]=p together), that is probably a CONCEPT that deserves a function (in C++ this might become an object). A concept like “drive” provides a wrapper around the concept, so that universal behavior of that concept becomes possible (without having to duplicate such behavior in dozens or hundreds of places peppered all around the code).
How do I get this to stop when it reaches the end of the ‘follow line’ loop?
#pragma config(Sensor, S1, touch, sensorTouch)
#pragma config(Sensor, S2, sound, sensorSoundDB)
#pragma config(Sensor, S3, light, sensorLightActive)
#pragma config(Sensor, S4, sonar, sensorSONAR)
#pragma config(Motor, motorB, rightMotor, tmotorNXT, PIDControl, driveRight, encoder)
#pragma config(Motor, motorC, leftMotor, tmotorNXT, PIDControl, driveLeft, encoder)
//!!Code automatically generated by ‘ROBOTC’ configuration wizard !!//
#pragma config(Sensor, S1, touch, sensorTouch)
#pragma config(Sensor, S2, sound, sensorSoundDB)
#pragma config(Sensor, S3, light, sensorLightActive)
#pragma config(Sensor, S4, sonar, sensorSONAR)
#pragma config(Motor, motorB, rightMotor, tmotorNXT, PIDControl, driveRight, encoder)
#pragma config(Motor, motorC, leftMotor, tmotorNXT, PIDControl, driveLeft, encoder)
//!!Code automatically generated by ‘ROBOTC’ configuration wizard !!//
task main()
{
while(SensorValue(touch) == 0) //a while loop is declared with the touchsensor’s value being 0 as it true condition
{
motor[motorC] =50;
motor[motorB] =50;
}
motor[motorC] =-75;
motor[motorB] =-75;
wait1Msec(2000);
motor[motorC] = 45;
motor[motorB] = 50;
wait1Msec(1500);
motor[motorC] = -15;
motor[motorB] = 100;
wait1Msec(650);
motor[motorC] = 50;
motor[motorB] = 50;
wait1Msec(1800);
while(true)
{
if(SensorValue(light) < 45)
{
motor[motorB] = 80;
motor[motorC] = 0;
}
else
{
setMotor(motorB, 0);
setMotor(motorC,80);
}
}
}
The problem is that you don’t really know that you’ve reached the end of the line v. hit a corner or similar. If you have other ways to detect, you could keep a check going within that loop (encoders, gyro, maybe number of times you’ve run right turn v. left turn). Then if you turn a full 180 degrees (measured one of those ways or another), you know you’ve reached the end of the path.
Meanwhile, you change the while(true) to while(inLineFollow) or something like that. You set the boolean inLineFollow true initially and then false when you hit this 180-degree turn.
You might follow exiting the loop by reversing the 180-degree turn to be at the end of the line and facing the original direction.
I am at the literal beginning of understanding this.
First consider how you as a person recognize that a line of tape has ended instead of making a turn. Now realized you’ve got the robot following an edge of the tape. Consider what the edge does when you consider a line to have ended. It goes to the end, across the end, and then back down the opposite side. So right now if you send your robot down the line, when it hits the end it will keep turning (left or right - I don’t know if the line is light on a dark surface or dark on a light surface) until it finds the opposite side and follows that edge back toward where it came from.
So, using what you’ve got and no more sensors, we want to watch for this full reversal. You can use the following approach. First, define two variables. Sure, the boolean isn’t necessary, but from a coding perspective it helps you read the code directly, which is a good thing. Meanwhile, I’m going to assume it turns right at the very end until retracing its path backward currently; if it’s the opposite, just swap.
bool lineHasNotEnded = true;
int consecutiveRightTurns = 0;
Then change your edge-follow loop as follows. The 20 at the end will need to be adjusted. Figure out how many times it has to turn right until it gets back to the other side of the original line. Change this 20 to be a little bit less than that but close to it. If you make this number too small, it will think it has reached the end when it’s only reached a sharp corner. If you make this number too big, it will find the opposite side of the tape and not stop. Also, you’ll need some delays in your code or you won’t be able to do this counting well. Adjust this delay before adjusting the 20.
while(lineHasNotEnded) {
if(SensorValue(light) < 45) {
motor[motorB] = 80;
motor[motorC] = 0;
wait1Msec(10);
consecutiveRightTurns = 0;
}
else {
setMotor(motorB, 0);
setMotor(motorC,80);
wait1Msec(10);
consecutiveRightTurns = consecutiveRightTurns + 1;
}
if(consecutiveRightTurns > 20) {
lineHasNotEnded = false;
}
}
If you want to reorient the robot so it’s faced the way it was going instead of backward, follow that while loop with this:
for(int i=0; i++; i<consecutiveRightTurns) {
setMotor(motorB, 0);
setMotor(motorC,-80);
wait1Msec(10);
}
Also, next time you should start your own thread for this. I hadn’t realized you weren’t the OP extending what was asked before. This is really a separate question.