Adding an autonomous mode to a remote-controlled Robot

Hello everyone,
I have recently built a robot that can be controlled using a TV remote. I took a remote, decoded some of it's buttons and programmed the robot to respond to each press of certain buttons. The robot worked perfectly and i was able to control it. Afterwards i even added some Leds and a buzzer.
after I played with it for a while I've decided i want to add an "autonomous" mode, using a ping sensor, that will be activated by the press of a determined button on the remote. In another words, a press of a button (The play button, for example) will make the robot avoid obstacles.
So i added a ping sensor.
Unfortunately, I have a small problem with the code.

#include <pitches.h>

#include <Servo.h>
#include <IRremote.h>
Servo SERVO_1;
Servo SERVO_2;
int RECV_PIN = 11;
int redled = 1;
int blueled = 2;
int greenled = 3;
#define trigPin 8
#define echoPin 9
IRrecv irrecv(RECV_PIN);

decode_results results;

#define UP 807FD02F
#define DOWN 807F50AF
#define RIGHT 807F52AD
#define LEFT 807F10EF
#define ENTER 807FCA35
#define ONE 807FA05F
#define TWO 807FA25D
#define THREE 807F22DD

 



void setup()
{
Serial.begin(9600);
  pinMode(1, OUTPUT); 
 pinMode(2, OUTPUT); 
 pinMode(3, OUTPUT);
  pinMode (8, OUTPUT);
  pinMode (9, INPUT);
  digitalWrite(1, HIGH);
 digitalWrite(2, HIGH);
 digitalWrite(3, HIGH);
  SERVO_1.attach(5);
  SERVO_2.attach (6);
  SERVO_1.write (93);
SERVO_2.write (93);
  irrecv.enableIRIn();
// Start the receiver

}

void loop() {

  
  if (irrecv.decode(&results)) {
 
    irrecv.resume();
    if (results.value == 0x807FD02F){     // 0xtype your IR remote button value note down in the previous step
SERVO_1.write (180);
SERVO_2.write (0);

       }
       else if(results.value == 0x807F50AF){
SERVO_1.write (0);
SERVO_2.write (180);
    }
    else if(results.value == 0x807F52AD){
     SERVO_1.write (0);
SERVO_2.write (0);
      
    } else if(results.value == 0x807F10EF){
    
      SERVO_1.write (180);
SERVO_2.write (180);

    }else if(results.value == 0x807FCA35){
       SERVO_1.write (95);
SERVO_2.write (95);
    
    }
  else if(results.value == 0x807FA25D){
digitalWrite(1, HIGH);
 digitalWrite(2, HIGH);
 digitalWrite(3, HIGH);
 digitalWrite(3, LOW);  // green
 delay(500);            
 digitalWrite(3, HIGH);
 digitalWrite(2, LOW);   // blue
 delay(500); 
 digitalWrite(2, HIGH);
 digitalWrite(1, LOW);   // red
 delay(500);
 digitalWrite(3, LOW);  // yellow
 delay(500);
 digitalWrite(1, HIGH);
 digitalWrite(2, LOW);   // Cyan (Light Blue)
 delay(500); 
 digitalWrite(3, HIGH); 
 digitalWrite(1, LOW);   // Magenta (purple)
 delay(500);
 digitalWrite(3, LOW);  // White
 delay(500);
 digitalWrite(1, HIGH);
 digitalWrite(2, HIGH);
 digitalWrite(3, HIGH); 
    }
 else if(results.value == 0x807FA05F){
    long duration, distance;
  digitalWrite(trigPin, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin, HIGH);
//  delayMicroseconds(1000); - Removed this line
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 29.1;
  if (distance < 25) {  // This is where the LED On/Off happens
   Serial.println ("Close Obstacle detected!" );

Serial.print ( distance);
Serial.print ( " CM!");

  SERVO_1.write (0);
SERVO_2.write (0);

}
  else {
   Serial.println ("No obstacle detected. going forward");
 SERVO_1.write (180);
SERVO_2.write (0); 
  }  
      
    }  
}

}

The problem in the code is obvious. When the button is pressed, the obstacle avoiding string starts, but in order for it to work correctly, it needs to loop itself. in another words, whenever the string ends it needs to start from the beginning and scan again, but it doesn't do it, because it is a part of a "if-else" condition. My question is, How can I make the obstacle avoidance string loop itself whenever i press that button? does "Switch-case" loop the cases' results ? Is that even possible to make a loop inside the loop ? (I think not, since the arduino doesn't have core functioning).
If anyone has any suggestions about how can i accomplish this task, i would like to hear it.

Thanks,

Roy.

BTW, I have already built obstalce avoiding robots in the past, But i haven't built ones that have an obstacle avoiding "Mode".

When the button is pressed, the obstacle avoiding string starts, but in order for it to work correctly, it needs to loop itself.

That may be obvious to you. It's gibberish to me. What does "the obstacle avoiding string starts" mean? Code is executed. Strings (and strings) are stored. They are data. Data doesn't start.

What the heck does "loop itself" mean? for loops loop. While loops loop. Nothing "loops itself".

My question is, How can I make the obstacle avoidance string loop itself whenever i press that button?

You can't. The question itself is meaningless.

PaulS:
That may be obvious to you. It's gibberish to me. What does "the obstacle avoiding string starts" mean? Code is executed. Strings (and strings) are stored. They are data. Data doesn't start.

What the heck does "loop itself" mean? for loops loop. While loops loop. Nothing "loops itself".
You can't. The question itself is meaningless.

You are right. By saying "String" i meant the part of the code:

  long duration, distance;
  digitalWrite(trigPin, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin, HIGH);
//  delayMicroseconds(1000); - Removed this line
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 29.1;
  if (distance < 25) {  // This is where the LED On/Off happens
   Serial.println ("Close Obstacle detected!" );

Serial.print ( distance);
Serial.print ( " CM!");

  SERVO_1.write (0);
SERVO_2.write (0);

}
  else {
   Serial.println ("No obstacle detected. going forward");
 SERVO_1.write (180);
SERVO_2.write (0); 
  }

You see, In order to make a robot avoid obstacles you need to have this kind of code, and in order for it to work it needs to "loop", Meaning whenever this part of code ends it should go back to the start, and scan again using the sensor. Therefore if you put this code alone in the "Void loop" the robot will work just fine.
Anyway thank you for your answer, I hope someone would have an idea.
By the way, do you know whether the "Switch-case" repeats it self ?
Meaning if i write this kind of code:

switch (var) {
    case 1:
     digitalWrite (8, HIGH);
delay (1000);
digitalWrite (8,LOW);
delay (1000);
    case 2:
      //do something when var equals 2
      break;
    default: 
      // if nothing else matches, do the default
      // default is optional
  }

Does the "Do something" Repeats itself? For example, in case 1, will the led blink forever ?

As long as it is constructed correctly switch/case will cause only the code valid for the current case of the switch variable to be executed each time through the switch/case and it will be executed each time the switch/case is entered.

In your example you do not have a break; at the end of case 1 so the program will fall through to the case 2 code.

So with that in mind

Does the "Do something" Repeats itself?

Yes
and

, in case 1, will the led blink forever ?

Yes, but add a break; at the end of the case and get rid of those delays if you want your program to do anything in the real world.

UKHeliBob:
As long as it is constructed correctly switch/case will cause only the code valid for the current case of the switch variable to be executed each time through the switch/case and it will be executed each time the switch/case is entered.

In your example you do not have a break; at the end of case 1 so the program will fall through to the case 2 code.

So with that in mindYes
and Yes, but add a break; at the end of the case and get rid of those delays if you want your program to do anything in the real world.

Thank you. Your comment was really helpful.
Can you suggest how to rewrite the code (Switch-case instead of If-Else) ?
I didn't really get what variable and label you have to put in.]

switch (var) {
  case label:
    // statements
    break;
  case label:
    // statements
    break;
  default: 
    // statements

You would learn more by writing it yourself but here is an example that you can see working

byte action = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  Serial.println();
  Serial.print("action = ");
  Serial.println(action);

  switch (action)
  {
    case 0:
      Serial.println("Executing code for case 0");
      action = 1;
      break;

    case 1:
      Serial.println("Executing code for case 1");
      action = 2;
      break;

    case 2:
      Serial.println("Executing code for case 2");
      action = random(0, 10);
      break;

    default:
      Serial.println("No match. Executing code for the default case");
      action = 0;
      break;
  }
  delay(1000);
}

NOTE - the delay is only there so that you have time to see what is going on. It is normally to be avoided so that the program does not stop while it executes.

Can you suggest how to rewrite the code (Switch-case instead of If-Else) ?

There is no particular reason to. The HEX code generated will be nearly identical, as the compiler effectively converts the switch statement to a series of if/else if/else statements. (Effectively, not exactly, for you pendants!)

UKHeliBob:
You would learn more by writing it yourself but here is an example that you can see working

byte action = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  Serial.println();
  Serial.print("action = ");
  Serial.println(action);

switch (action)
  {
    case 0:
      Serial.println("Executing code for case 0");
      action = 1;
      break;

case 1:
      Serial.println("Executing code for case 1");
      action = 2;
      break;

case 2:
      Serial.println("Executing code for case 2");
      action = random(0, 10);
      break;

default:
      Serial.println("No match. Executing code for the default case");
      action = 0;
      break;
  }
  delay(1000);
}




NOTE - the delay is only there so that you have time to see what is going on. It is normally to be avoided so that the program does not stop while it executes.

PaulS:
There is no particular reason to. The HEX code generated will be nearly identical, as the compiler effectively converts the switch statement to a series of if/else if/else statements. (Effectively, not exactly, for you pendants!)

So if what paul says is True, then there is no reason to write your code, since the obstacle avoiding code does not repeat itself ?

As long as you write the if/else or the switch/case correctly then either will do what you want. Personally I like to use switch/case when there are more than a couple of values to be tested and acted upon because I find it easier to write, read and maintain but it is a matter of personal choice.

There is a problem with switch/case when it comes to testing multiple values to decide whether action should be taken because cases cannot include logical tests such as && and || for instance.

It looks like the current code waits for an IR code and then performs the action associated with that code. That's simple enough for turn-left/turn-right commands.

But you want it to receive a command and go into automatic mode [until another command is entered]. So it needs to have some kind of memory to remember that it's in automatic mode or 'wait mode'. Maybe a boolean variable called AutomaticMode which can be true or false.

Have a look at the code here: Arduino Robot Remote Control Tutorial. The code is totally different to yours and you need to understand why. loop() always runs. It runs thousands of times per second, checking if a new IR code has arrived. It also checks if the motors are running (isActing) and it can turn off the motors if they have run for the specified time.

An important part of this approach is the Arduino isn't locked in to doing one action at a time. You can push "left" and while it is turning left you can push any other button to stop that and do a different action.