Self-Driving Robot

Cymraeg

Programming a Self-Driving Robot

This short series takes learners through videos and exercises about programming a robot vehicle, in this case, an Elegoo Smart Robot Car.

This mini-series follows on from our Electronics and Introduction to Arduino C series.

Please select a session heading to get started.

Video Lesson

Tinkercad Resources

As a reminder for how to log-in and use Tinkercad, please see Session One of our Online Electronics Series

We have designed and provided a Tinkercad simulation of the robot car's electronics for these exercises.

Select the option to "Tinker This" to save the design to your account for editing.

There are some changes to the design for this circuit. The simulation only has two motors as the robot's wheels are paired, and the line following sensors have been replaced with PIRs which are programmed in the same way.

Exercises

These challenges involve getting started with our Elegoo Smart Robot Car programming.

All learners should start with the bronze level and work their way up as far as they can.

Click on each challenge heading to expand.


The answers for each stage show only the the altered part of the code. The complete program can be viewed at the end of this section.

  1. Delete the the contents of the void setup() and void loop().


    • Keep the setup and loop functions ready for new content.

    If you are still stuck, please use the answer button below.

    Your program should look like this:

                        
      //C++ code
      //
      
      void setup()
      {
    
      }
    
      void loop()
      {
        
      }
                        
                      

  2. Define the following constants: leftDrivePin on pin 5, rightDrivePin on pin 6, leftForwardPin on pin 7, leftReversePin on pin 8, rightForwardPin on pin 11, and rightReversePin on pin 9.


    • Global variables and constants should be created before any functions.

    • Use the below structure for defining a constant:
                            
        #define constantName value
                            
                          

    • These lines do not have a semi-colon (;) at the end.

    If you are still stuck, please use the answer button below.


                        
      //C++ code
      //
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9
    
      void setup() 
                        
                      

  3. In the void setup() you will need to set the a pinMode for each of these constants.


    • These should all be set to OUTPUT.

    If you are still stuck, please use the answer button below.


                        
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
      }
                        
                      

  4. Create a new integer type variable called carSpeed. This can be set to any value between 0 (stop) and 255 (full speed).


    • Global variables and constants should be created before any functions.

    • Remember, Arduino C uses 'int' for an integer variable. This type of variable stores a whole number.

    If you are still stuck, please use the answer button below.


                        
      #define rightForwardPin 11
    
      int carSpeed = 200;
    
      void setup()
                        
                      

  5. Create five new functions named: robotForward(), robotReverse(), robotLeft(), robotRight(), and robotStop().


    • We will be using these functions in the void loop(), so best practice would be to write these above it in the program.

    If you are still stuck, please use the answer button below.


                        
      int carSpeed = 200;
    
      void robotForward()
      {
    
      }
    
      void robotReverse()
      {
    
      }
    
      void robotLeft()
      {
    
      }
    
      void robotRight()
      {
    
      }
    
      void robotStop()
      {
    
      }
    
      void setup()
                        
                      

  6. Using the below example for robotForward() as a template, complete these new functions.

                    
      void robotForward(){
    
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
    
      }
                    
                  

    Tip: You will need to initiate the serial monitor in the void setup() as we are printing to it.


    • Remember, to spin right, you will need the left wheels to go forward while the right wheels go in reverse. The opposite is true for spinning left.

    If you are still stuck, please use the answer button below.


                        
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
      }
                        
                      

  7. In the void loop(), create a program that makes the robot travel forward for 2 seconds, turn left for 1 second, stop for 3 seconds, reverse for 5 seconds, turns right for 4 seconds, forward for 3 seconds, and then stops.


    • To call a function, use the function name with brackets and end the line with a semi-colon (;).

    • For example, to call the drive forward function:
                            
        void loop()
        {
          robotForward();
        }
                            
                          

    • Use the delay() command to wait. Remember, the value inside these brackets is measured in milliseconds. So, delay(1000) will wait one second before moving to the next instruction.

    • This program is in the void loop() which will keep repeating the series of movements.

    If you are still stuck, please use the answer button below.


                        
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
      }
                        
                      



                          
      //C++ code
      //
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      
      int carSpeed = 200;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
      }
    
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
      }
                          
                        



These exercises continue to build on the same program.

The answers for each stage show only the the altered part of the code. The complete program can be viewed at the end of this section.

  1. Create new integer type variables called Echo and Trig. Set these to the the pin values they connect to on the Arduino.


    • These are set the same way and in the same section of the program as the carSpeed variable in the Bronze Level exercises.

    If you are still stuck, please use the answer button below.


                        
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
    
      void robotForward()
                        
                      

  2. Create pinModes in the void setup() for these.


    • The Echo is an input, whilst the trigger is an output.

    If you are still stuck, please use the answer button below.


                        
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT); 
      }
                        
                      

  3. To use the ultrasonic sensor we need a special function which will create an integer type value for the distance measured. Add the below function to your program:
                    
      int Distance_test(){
    
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
    
      }
                    
                  

    • Put this function before the void loop() in your program.

    • Make sure you have used the exact same variable names as given in step one, or the function will not run.

    If you are still stuck, please use the answer button below.


                        
      
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
      
      void setup()
    
                        
                      

  4. In your void loop(), call this new function and print the value on the serial monitor. Also, print "cm" on the end of this value in the serial monitor.


    • You can call the Distance_test() function inside the Serial.print() brackets.

    • You will need a second serial print command for the string of "cm".

    If you are still stuck, please use the answer button below.


                        
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        Serial.print(Distance_test());
        Serial.println("cm");
      }
                        
                      



                          
      //C++ code
      //
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT); 
      }
    
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        Serial.print(Distance_test());
        Serial.println("cm");
      }
                          
                        



These exercises continue to build on the same program.

The answers for each stage show only the the altered part of the code. The complete program can be viewed at the end of this section.

  1. Add the servo library to your program and name your servo as myServo.


    If you are still stuck, please use the answer button below.


                        
      //C++ code
      //
    
      #include <Servo.h>
      Servo myServo;  //creates a servo which can be called by name
    
      #define leftDrivePin 5 
                        
                      

  2. Define a new constant called servoPin and set it to the pin value that the servo connects to on the Arduino.


    • This can be added to the list of other constants defined in your program.

    • Remember, do not add a semi-colon at the end of the #define statement.

    If you are still stuck, please use the answer button below.


                        
      #define rightReversePin 9
      #define servoPin 3
      
      int carSpeed = 200;
                        
                      

  3. You will need to set which pin the servo is attached to in the void setup().


    • The command uses the following template:
                            
        servoName.attach(pinName);
                            
                          

    If you are still stuck, please use the answer button below.


                        
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
      }
                        
                      

  4. Create three new integer type variables called rightDistance, middleDistance, and leftDistance. Set these all to 0.


    • These are all global variables.

    If you are still stuck, please use the answer button below.


                        
      int Trig = A5;
      int rightDistance = 0;
      int middleDistance = 0;
      int leftDistance = 0;
    
      void robotForward()
                        
                      

  5. Inside you void loop(), write a program that will move the servo to 0°, take a ultrasonic reading, and then store it in the leftDistance variable. Use this to replace the lines added in exercise four of Silver Level.


    • Call the myServo.write() function (from the servo library) with the angle value inside the brackets.

    • Remember, to add a delay (500 milliseconds) to give the servo time to complete the movement before taking a reading.

    If you are still stuck, please use the answer button below.


                        
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
      }
                        
                      

  6. Repeat this for moving the servo to 90° for the middle distance, and 180° for the distance to the right.


                      
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
      }
                      
                    

  7. Using if-statements, work out which direction has the closest obstacle, and which has the clearest path. Print the answers in the serial monitor.


    • You will need to think about how to compare the values to work out the highest and lowest.

    • Remember, you can stack if and else statements inside each other.

    • You can use a Boolean "and" operator for this exercise. This allows us to check against two options. Arduino C uses && instead of the word "and" as shown below:

                            
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest Direction");
        }
                            
                          

    If you are still stuck, please use the answer button below.


                        
      if (rightDistance > middleDistance && rightDistance > leftDistance)
      {
        Serial.print("Right is the safest direction");
        if (middleDistance < leftDistance)
        {
          Serial.print("Do not go forwards");
        }
        else
        {
          Serial.print("Do not go left");
        }
      } 
      else if (leftDistance > middleDistance)
      {
        Serial.print("Left is the safest direction");
        if (middleDistance < rightDistance)
        {
          Serial.print("Do not go forwards");
        }
        else
        {
          Serial.print("Do not go right");
        }
      }
      else
      {
        Serial.print("Forwards is the safest direction");
        if (leftDistance < rightDistance)
        {
          Serial.print("Do not go left");
        }
        else
        {
          Serial.print("Do not go right");
        }
      }
                        
                      



                          
      //C++ code
      //
    
      #include <Servo.h>
      Servo myServo;  //creates a servo which can be called by name
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      #define servoPin 3
      
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
      int rightDistance = 0;
      int middleDistance = 0;
      int leftDistance = 0;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
      }
    
      void loop()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest direction");
          if (middleDistance < leftDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go left");
          }
        } 
        else if (leftDistance > middleDistance)
        {
          Serial.print("Left is the safest direction");
          if (middleDistance < rightDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
        else
        {
          Serial.print("Forwards is the safest direction");
          if (leftDistance < rightDistance)
          {
            Serial.print("Do not go left");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
      }
                          
                        


If you haven't already, remove the movement program inside the void loop() from the Bronze Level exercises. Then, see if you can write a program that has the robot drive itself along the clearest route. Remember, turning involves spinning on the spot, so you will need to work out when to stop turning to continue forward safely.


Video Lesson

Exercises

We shall be continung with your program from last session.

Make sure you've finished Session One (up to and including Gold) before starting these.

These challenges involve programming our Elegoo Smart Robot Car to follow a line.

All learners should start with the bronze level and work their way up as far as they can.

Click on each challenge heading to expand.


The Elegoo Smart Robot Car has three line follow sensors - these are not available on Tinkercad. Instead, we have used three PIR sensors which work with the same programming.

  1. Define global constants for the three PIR sensor pins.


    • Add these in the same section as the others from last session.

    • Possible names could be leftPIRPin, middlePIRPin, and rightPIRPin.

    • Look at the circuit to determine which pins they connect to.

    • Remember, when defining constants there is no semi-colon (;).

    If you are still stuck, please use the answer button below.


                        
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      #define servoPin 3
      #define leftPIRPin 2
      #define middlePIRPin 4
      #define rightPIRPin 10
                        
                      

  2. Set the pinModes for each of these sensors.


    • These are done in the void setup() function in the same format as the others.

    • PIR sensors are digital input devices.

    If you are still stuck, please use the answer button below.


                        
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
        pinMode(leftPIRPin, INPUT);
        pinMode(middlePIRPin, INPUT);
        pinMode(rightPIRPin, INPUT);
      }
                        
                      

  3. Create new functions to move the contents of the void loop() from last session to.


    • Consider a function for the movement program (Session One Bronze), and one for the ultrasonic obstacle detection and movement.

    • This will keep the code ready for calling if needed for later exercises.

    If you are still stuck, please use the answer button below.

    This is just one way you could split up the functions.

                        
      void setPath()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
      }
    
      void ultrasonicCheck()
      {
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest direction");
          if (middleDistance < leftDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go left");
          }
        } 
        else if (leftDistance > middleDistance)
        {
          Serial.print("Left is the safest direction");
          if (middleDistance < rightDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
        else
        {
          Serial.print("Forwards is the safest direction");
          if (leftDistance < rightDistance)
          {
            Serial.print("Do not go left");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
      }
    
      void loop()
      {
        
      }
                        
                      

  4. Add a new function called lineFollow() and call it in the void loop().


                      
      void lineFollow()
      {
    
      }
    
      void loop ()
      {
        lineFollow();
      }
                      
                    



                        
      //C++ code
      //
    
      #include <Servo.h>
      Servo myServo;  //creates a servo which can be called by name
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      #define servoPin 3
      #define leftPIRPin 2
      #define middlePIRPin 4
      #define rightPIRPin 10
      
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
      int rightDistance = 0;
      int middleDistance = 0;
      int leftDistance = 0;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
        pinMode(leftPIRPin, INPUT);
        pinMode(middlePIRPin, INPUT);
        pinMode(rightPIRPin, INPUT);
      }
    
      void setPath()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
      }
    
      void ultrasonicCheck()
      {
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest direction");
          if (middleDistance < leftDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go left");
          }
        } 
        else if (leftDistance > middleDistance)
        {
          Serial.print("Left is the safest direction");
          if (middleDistance < rightDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
        else
        {
          Serial.print("Forwards is the safest direction");
          if (leftDistance < rightDistance)
          {
            Serial.print("Do not go left");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
      }
    
      void lineFollow()
      {
    
      }
    
      void loop ()
      {
        lineFollow();
      }
        
                        
                      




  1. Create three local Boolean type variables, one for each PIR, inside the line following function.


    • These are local variable besides we are creating them inside a function. This means they can only be used in the same function.

    • Remember, Boolean variables are either true (1), or false (0).

    • You can create a Boolean type variable without needing to set it to something immediately. Here is an example for how to create one:

                            
        bool variableName;
                            
                          

    If you are still stuck, please use the answer button below.


                        
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
      }
                        
                      

  2. Now, set each of these new variables to the digital reading of the corresponding sensor's pin.


    • Here is an example of how to assign a digital reading to a variable:
                            
        variableName = digitalRead(pinName);
                            
                          

    • As these are local variables we need to do this in the same function they were created in.

    If you are still stuck, please use the answer button below.


                        
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
      }
                        
                      

  3. Print the values from these line sensors to the serial monitor.


    • We have already initiated the serial monitor last session.

    • You will need two print statements so you know which sensor reading is which in the monitor. One for a label, the other for the value.

    • For example:
                            
        Serial.print("First Sensor: ");
        Serial.println(variableName);
                            
                          

    If you are still stuck, please use the answer button below.


                        
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
      }
                        
                      

  4. Create an if-statement inside your line follow function checking if the left reading is true whilst both the middle and right readings are negative.


    • Remember, if(booleanVariable) is checking if the value of the Boolean variable is true (1). Whilst if(!booleanVariable) is checking to see if it is false.

    • In Arduino C programming we use && for and inside a comparison.

    If you are still stuck, please use the answer button below.


                        
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
        if(leftLine && !midLine && !rightLine)
        {
    
        }
      }
                        
                      

  5. Add an else-if statement for if the middle reading is true, whilst the other readings are false.


                      
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
        if(leftLine && !midLine && !rightLine)
        {
          
        }
        else if(!leftLine && midLine && !rightLine)
        {
    
        }
      }
                      
                    

  6. Do the same again for when the right sensor is positive whilst the other two are negative.


                      
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
        if(leftLine && !midLine && !rightLine)
        {
          
        }
        else if(!leftLine && midLine && !rightLine)
        {
          
        }
        else if(!leftLine && !midLine && rightLine)
        {
    
        }
      }
                      
                    

  7. Inside each of these if and else-if statements, call the appropriate movement functions to keep the robot following the line.


    • We want to try and keep the line visible on the middle sensor.

    • You will need to add a delay and stop to the movement to check if you've moved enough or too far.

    • For this exercise, turn for 100ms before checking the sensors again.

    • We already created movement functions last session which we can call for this.

    If you are still stuck, please use the answer button below.


                        
      if(leftLine && !midLine && !rightLine)
      {
        robotLeft();
        delay(100);
        robotStop();
      }
      else if(!leftLine && midLine && !rightLine)
      {
        robotForward();
        delay(100);
        robotStop();
      }
      else if(!leftLine && !midLine && rightLine)
      {
        robotRight();
        delay(100);
        robotStop();
      }                   
                        
                      



                          
      //C++ code
      //
    
      #include <Servo.h>
      Servo myServo;  //creates a servo which can be called by name
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      #define servoPin 3
      #define leftPIRPin 2
      #define middlePIRPin 4
      #define rightPIRPin 10
      
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
      int rightDistance = 0;
      int middleDistance = 0;
      int leftDistance = 0;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
        pinMode(leftPIRPin, INPUT);
        pinMode(middlePIRPin, INPUT);
        pinMode(rightPIRPin, INPUT);
      }
    
      void setPath()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
      }
    
      void ultrasonicCheck()
      {
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest direction");
          if (middleDistance < leftDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go left");
          }
        } 
        else if (leftDistance > middleDistance)
        {
          Serial.print("Left is the safest direction");
          if (middleDistance < rightDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
        else
        {
          Serial.print("Forwards is the safest direction");
          if (leftDistance < rightDistance)
          {
            Serial.print("Do not go left");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
      }
    
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
        if(leftLine && !midLine && !rightLine)
        {
          robotLeft();
          delay(100);
          robotStop();
        }
        else if(!leftLine && midLine && !rightLine)
        {
          robotForward();
          delay(100);
          robotStop();
        }
        else if(!leftLine && !midLine && rightLine)
        {
          robotRight();
          delay(100);
          robotStop();
        } 
      }
    
      void loop ()
      {
        lineFollow();
      }
                          
                        




  1. At the moment, our line following function is only looking for when a single sensor is reading as true. But, what if the line is visible to two of the sensors? Add new else-if statements for these conditions to move the robot appropriately.


    • If two sensors are both detecting the line, then we need a smaller adjustment than for just the left or the right.

    • Halve the turning time used in the previous if and else-if statements.

    If you are still stuck, please use the answer button below.


                        
      else if(leftLine && midLine && !rightLine)
      {
        robotLeft();
        delay(50);
        robotStop();
      }
      else if(!leftLine && midLine && rightLine)
      {
        robotRight();
        delay(50);
        robotStop();
      }
                        
                      

  2. Add statements and programs for if none or all three sensors are detecting a line.


    • If none of the sensors are detecting the line, you may want to consider setting a spin direction and doubling the duration before each check to try and relocate the line.

    • All three sensors detecting the line means that either you are crossing it, it's detecting more than one section of the line, or the line is thick enought to register across all 3 sensors.

    If you are still stuck, please use the answer button below.


                        
      else if (!leftLine && !midLine && !rightLine)
      {
        robotRight();
        delay(200);
        robotStop();
      }                
      else if (leftLine && midLine && rightLine)
      {
        robotForward();
        delay(100);
        robotStop();
      }
                        
                      

  3. Finally, add an else statement to the end of all these comparisons to capture anything missed. Inside this include an error message which prints in the serial monitor.


    • Try to make error messages that help you detect the problem. Do not just have the message "Error" for every catch point or you will struggle to find the source of the problem.

    If you are still stuck, please use the answer button below.


                        
        else
        {
          Serial.print("Error: Unexpected PIR readings");
        }
                        
                      



                          
      //C++ code
      //
    
      #include <Servo.h>
      Servo myServo;  //creates a servo which can be called by name
    
      #define leftDrivePin 5
      #define rightDrivePin 6
      #define leftForwardPin 7
      #define leftReversePin 8
      #define rightForwardPin 11
      #define rightReversePin 9 
      #define servoPin 3
      #define leftPIRPin 2
      #define middlePIRPin 4
      #define rightPIRPin 10
      
      int carSpeed = 200;
      int Echo = A4;
      int Trig = A5;
      int rightDistance = 0;
      int middleDistance = 0;
      int leftDistance = 0;
    
      void robotForward()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Forward");
      }
    
      void robotReverse()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Reverse");
      }
    
      void robotLeft()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, HIGH);
        digitalWrite(rightForwardPin, HIGH);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Spin Left");
      }
    
      void robotRight()
      {
        analogWrite(leftDrivePin, carSpeed);
        analogWrite(rightDrivePin, carSpeed);
        digitalWrite(leftForwardPin, HIGH);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, HIGH);
        Serial.println("Spin Right");
      }
    
      void robotStop()
      {
        analogWrite(leftDrivePin, 0);
        analogWrite(rightDrivePin, 0);
        digitalWrite(leftForwardPin, LOW);
        digitalWrite(leftReversePin, LOW);
        digitalWrite(rightForwardPin, LOW);
        digitalWrite(rightReversePin, LOW);
        Serial.println("Stop!");
      }
    
      int Distance_test()
      {
        digitalWrite(Trig, LOW);
        delayMicroseconds(2);
        digitalWrite(Trig, HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig, LOW);
        float distance = pulseIn(Echo, HIGH);
        distance = distance / 2 / 29;
        return (int)distance;
      }
    
      void setup()
      {
        pinMode(leftForwardPin, OUTPUT);
        pinMode(leftReversePin, OUTPUT);
        pinMode(rightReversePin, OUTPUT);
        pinMode(rightForwardPin, OUTPUT);
        pinMode(leftDrivePin, OUTPUT);
        pinMode(rightDrivePin, OUTPUT);
        Serial.begin(9600);
        pinMode(Echo, INPUT);    
        pinMode(Trig, OUTPUT);
        myServo.attach(servoPin);
        pinMode(leftPIRPin, INPUT);
        pinMode(middlePIRPin, INPUT);
        pinMode(rightPIRPin, INPUT);
      }
    
      void setPath()
      {
        robotForward();
        delay(2000);
        robotLeft();
        delay(1000);
        robotStop();
        delay(3000);
        robotReverse();
        delay(5000);
        robotRight();
        delay (4000);
        robotForward();
        delay(3000);
        robotStop();
        myServo.write(0);
        delay(500);
      }
    
      void ultrasonicCheck()
      {
        leftDistance = Distance_test();
        Serial.print(leftDistance);
        Serial.println("cm on left");
        myServo.write(90);
        delay(500);
        middleDistance = Distance_test();
        Serial.print(middleDistance);
        Serial.println("cm ahead");
        myServo.write(180);
        delay(500);
        rightDistance = Distance_test();
        Serial.print(rightDistance);
        Serial.println("cm on right");
        if (rightDistance > middleDistance && rightDistance > leftDistance)
        {
          Serial.print("Right is the safest direction");
          if (middleDistance < leftDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go left");
          }
        } 
        else if (leftDistance > middleDistance)
        {
          Serial.print("Left is the safest direction");
          if (middleDistance < rightDistance)
          {
            Serial.print("Do not go forwards");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
        else
        {
          Serial.print("Forwards is the safest direction");
          if (leftDistance < rightDistance)
          {
            Serial.print("Do not go left");
          }
          else
          {
            Serial.print("Do not go right");
          }
        }
      }
    
      void lineFollow()
      {
        bool leftLine;
        bool midLine;
        bool rightLine;
        leftLine = digitalRead(leftPIRPin);
        midLine = digitalRead(middlePIRPin);
        rightLine = digitalRead(rightPIRPin);
        Serial.print("Left PIR: ");
        Serial.println(leftLine);
        Serial.print("Middle PIR: ");
        Serial.println(midLine);
        Serial.print("Right PIR: ");
        Serial.println(rightLine);
        if(leftLine && !midLine && !rightLine)
        {
          robotLeft();
          delay(100);
          robotStop();
        }
        else if(!leftLine && midLine && !rightLine)
        {
          robotForward();
          delay(100);
          robotStop();
        }
        else if(!leftLine && !midLine && rightLine)
        {
          robotRight();
          delay(100);
          robotStop();
        }
        else if(leftLine && midLine && !rightLine)
        {
          robotLeft();
          delay(50);
          robotStop();
        }
        else if(!leftLine && midLine && rightLine)
        {
          robotRight();
          delay(50);
          robotStop();
        }
        else if (!leftLine && !midLine && !rightLine)
        {
          robotRight();
          delay(200);
          robotStop();
        }                
        else if (leftLine && midLine && rightLine)
        {
          robotForward();
          delay(100);
          robotStop();
        }
        else
        {
          Serial.print("Error: Unexpected PIR readings")
        }
      }
    
      void loop ()
      {
        lineFollow();
      }
                          
                        




Video Lesson

Exercises

We shall be continung with your program from last session.

Make sure you've finished Gold Level for both Session One and Two before starting these.

These challenges involve programming our Elegoo Smart Robot Car to respond to an IR remote control.

All learners should start with the bronze level and work their way up as far as they can.

Click on each challenge heading to expand.



  1. Define a constant for the IR receiver which is set to the correct pin on the Arduino.



  2. To use the IR remote, you will need to include its library of functions.


    • Use the #include command as we did for the servo library.

    • The IR remote library is call IRremote.hpp

    • Do not include a semi-colon at the end of a #include instruction.

    If you are still stuck, please use the answer button below.



  3. In your void setup(), initialise the IR receiver.


    • This function is from the IR remote library and replaces the need to set a pinMode for the receiver.

    • Use this function template:

    If you are still stuck, please use the answer button below.



  4. Inside your loop function, add an if statement to detect if a signal has been received from the remote.


    • The IR remote library includes a function called IrReceiver.decode() for checking if a signal is received.

    • This function returns TRUE if a signal is detected.

    If you are still stuck, please use the answer button below.



  5. Have the command received from the remote print into the serial monitor.


    • The necessary Serial.print() line needs to be inside the if statement for when an IR signal is received.

    • This uses another of the IR remote library's instructions - called IrReceiver.decodedIRData.command.

    • The commands are sent as HEX values which we need to include in the print instruction:
                            
        Serial.println(value, HEX);
                            
                          

    • To use HEX values, you will need to add 0x to the start of the value. For example if the command returns 18, the true value for comparisons in your program is 0x18.

    • You may wish to include some text before your value to identify it in the serial monitor, as well as to remind you that the value should start "0x" when applying it later.

    If you are still stuck, please use the answer button below.



  6. Currently, the program listens for only the first command sent. We need to resume the IR receiver after printing its value to the serial monitor.


    • This uses another function from the IR remote library:

    If you are still stuck, please use the answer button below.







  1. Using your current program, identify which commands are sent by each button on the controller.

    Here are the HEX values for some of the buttons on the IR remote:

    • Button 5 = 0x15
    • VOL+ = 0x1
    • Button 9 = 0x1A
    • EQ = 0xD

  2. Create a program in the void loop() to activate a different function according to IR commands for the following:

    • Line Following - the program from Session Two

    • Obstacle Avoidance - the program from Gold Level of Session One

    • Driving pattern - the program created in Bronze Level of Session One

    • Remote control - a new function you will be writing in Gold Level of Session Three



    • You will need to create the necessary if/else if statements inside the if signal received to identify the command and run the correct function.

    • You will be comparing the IR command received to HEX values. Here is an example:


    • You will also need to create a new function for the remote control program ready.

    If you are still stuck, please use the answer button below.

    New function:

    Remote command if statement:





  1. Write the program for your new remote control function. This will need to include different remote commands for forwards, backwards, left, right, and stop.


    • This will use the motor functions we created in Session One.

    • Including the same serial print commands for the receiver as in the void loop(), will assist with any debugging and checks.

    If you are still stuck, please use the answer button below.



  2. There is currently a problem with this program. The remote control function can only run once before exiting that mode because it returns to the void loop() after each action. Let us create a global variable we can use to store the mode selected in and use this to tell it when to exit the remote control program.


    • You can use an integer type variable with a number to represent each robot mode.

    • We will need to relocate where we resume the IR receiver to before the if statements of what to do according to the command. This means we need to create a local variable to store the command in before resuming. This will be a byte variable:

      This type of variable is needed to hold the HEX command value.

    • We can then add additional else if statements for the if (IrReceiver.decode) to keep the selected mode operating.

    • Select a different button on the IR remote to use for stopping and exiting a mode.

    If you are still stuck, please use the answer button below.

    Global variable:

    The if (IrReceiver.decode) statement using new command and mode variables:

    New else if statements:






  • Consider which buttons you have used for each control on the remote. Are they easy to operate? Rearrange if necessary. For example, is the reverse control under the forwards? is the left control on the left?

  • Add controls for increasing and decreasing the motor speeds whilst using the remote control.

  • Can you think of other behaviours you could create functions for and add remote control commands to activate?