Motor control after making a turn

Hey guys hows it going!

so i've just started looking into arduino programming. Im trying to help my little brother with some programming for a school project but we've run into a dead end.
Basically the project requires you control a robot through an obstacle course. What sort of code would you need to run this?

Were using an IR sensor to measure distance (were only using the analog values so no need to convert it to physical distance) till the bot is close enough to a wall, then it turns right or left, and follows this procedure through a series of turns. Problem we have is, its a 2 wheeled bot, so i know how to make it stop and turn e.g. left by turning left motor off or reverse and right motor forwards, but then after that, how do you make it move both motors? I know it seems like a easy problem but i cant figure it out :frowning:

Any help is appreciated :slight_smile:

You have to decide how long to leave it turning, then set both motors to forward to make it go forward.

To decide when to stop turning you might want to check the IR distance measurement and stop turning when the wall is no longer seen.

It always helps if you post your code - it may be that your motor controller is unusual.

here is my program code so far. Does it seem correct? Note that i have purposely left the sensor values (sensvalue) blank in the if statements. The intervals are also not correctly timed so ignore the actual value

// Define the sensor and motors as variables
int IRsensor = 0;
int leftmotor = 1;
int rightmotor = 2;

// This variable holds the state the system is in. It will start at 0 and count upwards for
// how every many state the system will have.
int state = 0;
//Create Flag Variable for State
int flag = 0;

// This long variable will hold the previous number of milli seconds that have ellapsed since
// the board was turned on. It is used for timing delays.
long previousMillis;

// This long variable will hold the current amount of ellapsed millis seconds since the board was turned on
long currentMillis;

// This variable will hold the desired delay time in millis seconds
long interval;

void setup() 
{
  Serial.begin(9600);
  
  //Define the inputs and outputs
  pinMode(IRsensor,INPUT);
  pinMode(leftmotor,OUTPUT);
  pinMode(rightmotor,OUTPUT);

}

void loop() 
{
  //Create variable 'val', and print sensor value into it
  int sensval = analogRead(IRsensor);
  
   // Update the current milli seconds. Millis() returns the number 
  // of milli seconds that have ellapsed sincethe board was turned on
  currentMillis = millis(); 
  
  //Define the variosu cases depending on IR sensor input
  switch(state)
  
  case 0: //wait two seconds before moving
    previousMillis = currentMillis;
    interval = 3000;
    state = 1;
    break;
    
        
    case 1: // check if wait time has finished then move forwards.
    if(currentMillis - previousMillis > interval){
      Forwards(); // This function is defined at the bottom.
      state = 2;
    }
    break;
    
    
    
    case 2: // continue moving forward untill the 1st wall is close, then turn right
    if(sensvalue < && state == 2){
      RightTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 3000;
      state = 3;
    }
    break;
    
    
    
    case 3: //Keep moving after first turn
    if(currentMillis - previousMillis > interval && state == 3){
      Forwards(); // This function is defined at the bottom.
      flag = 1
    }
    break;
    
    case 4: // Keep moving forward till 2nd wall, then turn Right 
    if(sensvalue < && flag == 1){
      RightTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 3000;
      state = 4;
    }
    break;
    
    case 5: //Keep moving after 2nd turn
    if(currentMillis - previousMillis > interval && state == 4){
      Forwards(); // This function is defined at the bottom.
      flag = 2
    }
    break;
    
    case 6: // Keep moving forward till 3rd wall, then turn Left 
    if(sensvalue < && flag == 2){
      LeftTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 3000;
      state = 5;
    }
    break;
    
    case 7: //Keep moving after 3rd turn
    if(currentMillis - previousMillis > interval && state == 5){
      Forwards(); // This function is defined at the bottom.
      flag = 2
    }
    break;
    
    default: // if no cases match then default is executed.
    //Do nothing
    break;
    
  }

  
}  

// Motor control programs, depending on the case
void Forwards(){
  digitalWrite(leftmotor,HIGH);
  digitalWrite(rightMotor,HIGH);
}

void LeftTurn(){
  digitalWrite(leftmotor,LOW);
  digitalWrite(rightmotor,HIGH);
}

void RightTurn(){
  digitalWrite(leftmotor,HIGH);
  digitalWrite(rightmotor,LOW);
}

void Stop(){
  digitalWrite(leftmotor,LOW);
  digitalWrite(rightmotor,LOW);
}

Moderator edit: [code] ... [/code] tags added. (Nick Gammon)

The switch statement takes care of the state so you don't need to put "&& state == n" in all those if-statements.

State 0: Prepare some data and go to State 1

State 1: When 3 seconds expires, go to State 2

State 2: If sensor value below some limit, start turning right an go to State 3

State 3: After 3 seconds, change direction to forward. STAY IN STATE 3 //// This seems wrong.

As near as I can tell the other states are never reached.

my idea was to set a value to teh state variable, then use that as a condition for the next step. Im trying to get multiple conditions before each case is run e.g for case 2, it will only occur if the sensor value is below some value, and when state = 2, which is after case 1 has occured, thus forcing each step to happen in sequence.
Isnt the program run in a step structure, i.e. case 0, case 1, case 2, etc ? Im an undergrad in engineering so i've done some statement list programming on PLC's and i was trying to apply that to this.

kenny0987:
Isnt the program run in a step structure, i.e. case 0, case 1, case 2, etc ? Im an undergrad in engineering so i've done some statement list programming on PLC's and i was trying to apply that to this.

Not at all.

The switch (and cases) are like a lengthy "if". IF case 1 ELSE case 2 ELSE case 3 and so on. But the whole thing is done once.

Once you're in "state == 3", there are no more assignments to "state", so that's where you'll stay.

kenny0987:
here is my program code so far. Does it seem correct?

Not according to the compiler, for which I have quite a bit of respect:

sketch_may30i.cpp: In function 'void loop()':
sketch_may30i:48: error: break statement not within loop or switch
sketch_may30i:51: error: case label '1' not within a switch statement
sketch_may30i:56: error: break statement not within loop or switch
sketch_may30i:60: error: case label '2' not within a switch statement
sketch_may30i:61: error: 'sensvalue' was not declared in this scope
sketch_may30i:67: error: break statement not within loop or switch
sketch_may30i:71: error: case label '3' not within a switch statement
sketch_may30i:75: error: expected `;' before '}' token
sketch_may30i:76: error: break statement not within loop or switch
sketch_may30i:78: error: case label '4' not within a switch statement
sketch_may30i:79: error: 'sensvalue' was not declared in this scope
sketch_may30i:85: error: break statement not within loop or switch
sketch_may30i:87: error: case label '5' not within a switch statement
sketch_may30i:91: error: expected `;' before '}' token
sketch_may30i:92: error: break statement not within loop or switch
sketch_may30i:94: error: case label '6' not within a switch statement
sketch_may30i:95: error: 'sensvalue' was not declared in this scope
sketch_may30i:101: error: break statement not within loop or switch
sketch_may30i:103: error: case label '7' not within a switch statement
sketch_may30i:107: error: expected `;' before '}' token
sketch_may30i:108: error: break statement not within loop or switch
sketch_may30i:110: error: case label not within a switch statement
sketch_may30i:112: error: break statement not within loop or switch
sketch_may30i:61: error: label 'state' used but not defined
sketch_may30i:79: error: label 'flag' used but not defined
sketch_may30i.cpp: At global scope:
sketch_may30i:117: error: expected declaration before '}' token

So then my program would run case 0, then halt? I thought it looped and ran whichever case had the conditions satisfied?
How would i be able to make it run those steps in sequence?

Thanks again for the help guys :slight_smile:

kenny0987:
So then my program would run case 0, then halt?

No, it likely ends up in state 3 as noted above.

I thought it looped and ran whichever case had the conditions satisfied?

It does loop and the switch statement runs whichever case matches your state variable.

How would i be able to make it run those steps in sequence?

You control it with the state variable - your code needs to set it to an appropriate value for the next iteration of loop. If you want it to run those steps in ascending order, each case needs to increment the state variable. Try searching the forums for state machine; there are a number of examples posted that illustrate this.

How about this then?

// Define the sensor and motors as variables
int IRsensor = 0;
int leftmotor = 1;
int rightmotor = 2;

// This variable holds the state the system is in. It will start at 0 and count upwards for
// how every many state the system will have.
int state = 0;
//Create Flag Variable for State
int flag = 0;

// This long variable will hold the previous number of milli seconds that have ellapsed since
// the board was turned on. It is used for timing delays.
long previousMillis;

// This long variable will hold the current amount of ellapsed millis seconds since the board was turned on
long currentMillis;

// This variable will hold the desired delay time in millis seconds
long interval;

void setup()
{
  Serial.begin(9600);
 
  //Define the inputs and outputs
  pinMode(IRsensor,INPUT);
  pinMode(leftmotor,OUTPUT);
  pinMode(rightmotor,OUTPUT);

}

void loop()
{
  //Create variable 'val', and print sensor value into it
  int sensvalue = analogRead(IRsensor);
 
   // Update the current milli seconds. Millis() returns the number
  // of milli seconds that have ellapsed sincethe board was turned on
  currentMillis = millis();
 
  //Define the variosu cases depending on IR sensor input
  switch(state){
 
  case 0: //wait four seconds before moving
    previousMillis = currentMillis;
    interval = 4000;
    state = 1;
    break;
    
        
    case 1: // check if 4 sec has finished then move forwards.
    if(currentMillis - previousMillis > interval){
      Forwards(); // This function is defined at the bottom.
      state = 2;
    }
    break;
    
    
    
    case 2: // continue moving forward untill the FIRST DEAD END is close, then turn right
    if(sensvalue < 135 && state == 2){
      RightTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 435;
      state = 3;
    }
    break;
    
    
    
    case 3: //Keep moving after first turn
    if(currentMillis - previousMillis > interval && state == 3){
      Forwards(); // This function is defined at the bottom.
      flag = 1;
      state = 4;
    }
    break;
    
    
    case 4: // Keep moving forward till OBSTACLE, then turn Right
    if(sensvalue < 135 && flag == 1){
      RightTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 435;
      state = 5;
    }
    
    break;
    
    case 5: //Keep moving after 2nd turn
    if(currentMillis - previousMillis > interval && state == 5){
      Forwards(); // This function is defined at the bottom.
      flag = 2;
      state = 6;
    }
    
    break;
    
    case 6: // Keep moving forward till DED END, then turn Left
    if(sensvalue < 135 && flag == 2){
      LeftTurn(); // This function is defined at the bottom.
      previousMillis = currentMillis;
      interval = 435;
      state = 7;
    }
    
    break;
    
    case 7: //Keep moving after DEAD END
    if(currentMillis - previousMillis > interval && state == 7){
      Forwards(); // This function is defined at the bottom.
      flag = 3;
      state = 8;
      interval = 1500;
    }
    
    break;
    
    case 8: //Turn 15 degrees Left
    if(currentMillis - previousMillis > interval && flag == 8){
      LeftTurn(); // This function is defined at the bottom.
      flag = 4;
      state = 9;
      interval = 110;
    }
    
    break;
    
    case 9: //Keep moving after Turning 15 Degrees for 5 seconds
    if(currentMillis - previousMillis > interval && state == 9){
      Forwards(); // This function is defined at the bottom.
      flag = 5;
      state = 10;
      interval = 5000;
    }
    
    break;
    
    case 10: //Stop after 5 seconds
    if(currentMillis - previousMillis > interval && flag == 5){
      Stop(); // This function is defined at the bottom.
      flag = 6;
      state = 11;
      }
      
      break;
          
    default: // if no cases match then default is executed.
    //Do nothing
    break;
    
  }
 
}  

// Motor control programs, depending on the case
void Forwards(){
  digitalWrite(leftmotor,HIGH);
  digitalWrite(rightmotor,HIGH);
}

void LeftTurn(){
  digitalWrite(leftmotor,LOW);
  digitalWrite(rightmotor,HIGH);
}

void RightTurn(){
  digitalWrite(leftmotor,HIGH);
  digitalWrite(rightmotor,LOW);
}

void Stop(){
  digitalWrite(leftmotor,LOW);
  digitalWrite(rightmotor,LOW);
}

Looks OK. Somewhat more simply though:

  //Define the various cases depending on IR sensor input
  switch(state)
  {

  // blah blah

  }

  state++;  // next state

Why have each state add 1 when you always want to go from one state to the next?

Also, looking at your code I'm sorta wondering what happens when you reach state 11.

i dont quite follow. Correct me if im wrong, but you mean that, rather than have state = ... after each case, i simply delete each of these, then at the end of the switch statement, add in state ++ ?

Basically for this project, the bot is split into 2 parts. The first part, containing all the electronics, transports a ball though an path with obstacles till it gets to a bucket, whereby the bot drops the ball into the bucket. Within the bucket lies the 2nd part, purely mechanical, and is supposed to climb a certain length rope while holding onto the ball, then stop and hold it up there indefinitely.

...easy right?

kenny0987:
i dont quite follow. Correct me if im wrong, but you mean that, rather than have state = ... after each case, i simply delete each of these, then at the end of the switch statement, add in state ++ ?

Yes that was what I was getting at. Some state machines have all sorts of complex states (eg. state 5 leads to state 9 which leads to state 2 or 3). But in your case you seem to be going up one state at a time.

You still need to think of how to handle what to do when you reach your last state.

eg.

  state++;  // next state
  if (state > 11)
    state = 0;

Doing an unconditional state++ after the switch statement isn't going to work for all cases. Consider case 9; there's a timing condition that must be satisfied before moving on to the next state. It is a slightly unusual application of a state machine though as there's no possibility of choosing a new path through your states at run time - you could simply write it sequentially (though I can see no reason to).

It sounds like the system will simply dead reckon its way round the maze and state 11 is a means to effectively end the program by using the default case. If so, presumably case 10 will have the ball drop code added.

Fair enough. Disregard my suggestion, I hadn't thought it through. :slight_smile:

the ball drop will be controlled either by a relay or by some mechanical latch or sorts, depending on the budget.

Well i reckon the movement part is done, Cheers again for the help guys, much appreciated :slight_smile:

Hopefully this works on the actual robot

Hopefully this works on the actual robot

If you haven't tested with the actual hardware yet, might I suggest that you do so soon? You will likely discover all sorts of minor issues that'll need work.

yup were testing it this coming week