Introduction: ALFRED: Sensor Based 4WD Vehicle

This Instuctable shows how to make a sensor based vehicle. The sensors used are the ultrasonic sensor, microphone sound sensor and IR sensor receiver.

The vehicle is aware of his surroundings so he will try not to hit obstacles but if he does he will detect the sound of impact and back up. He can be controlled with a remote.

More detailed explanation of Alfred:

By using the arrows on the remote and the middle button, the remote can tell Alfred where to go. When pressed forward he will go until he can’t go further or told to stop. When told to go left or right, he will turn in the desired direction for a couple of seconds on his axis and will continue from where ever he left off.

If there is a wall or another obstacle at a certain distance in front of him, Alfred will stop. After he stops, he looks to his left and right to see where it is best to go next. If there is another obstacle on his right, he chooses to go left. He backs up for a second and turns to the chosen direction and continues to drive until he encounters the next obstacle or is told to stop by the remote control.

In case Alfred does not see an obstacle in front of him and hits it, he will hear the sound of impact and will back up and look around where to go next, just like before.

Alfred will go on until told to stop or gets in trouble.

Step 1: Collect Parts Needed

You will need:

  • Arduino (Can be either UNO or MEGA, I used UNO)
  • A chassis (Can be homemade or bought, I got mine from Amazon)
  • 4x motors and wheels
  • 2x L293D motor drivers
  • Pushbutton
  • Ultrasonic Sensor HC-SR04
  • Microphone Sound Sensor KY-038
  • Infrared Sensor Receiver KY-022 and a remote
  • Servo Motor SG90
  • 2x LEDs
  • Breadboard (I used 2x small ones)
  • Wires by need
  • Power source (USB power bank)

Have in mind that you can just use the pushbutton to start with, the remote is not necessary but optional. Although it is kind of cool controlling the vehicle with a TV remote.

Step 2: Put Together the Chassis (body)

You can use your own imagination of the chassis, or buy one like I did - just what ever floats your boat.

The chassis I used for the project I bought from Amazon. The chassis is made of plastic and the tires are of high-quality thermoplastic rubber, wear resistant, and suitable for driving on rugged roads (they say). The body is two layered and has a length of 26cm, width of 15cm, and height of 4cm. It came with the motors that can take up to 6V and 120mA.

On the picture I already have started little bit on the next step.

Step 3: Have the Car Up and Running With a Pushbutton

Here you will need:

  • Assembled car from step 2
  • Arduino UNO/MEGA
  • Power source (Power bank)
  • 2x L293D
  • 4x DC motors plus wheels (if it is not already on the chassis)
  • (2x mini) Breadboard
  • Pushbutton
  • 2x LEDs (optional, but can be convenient for troubleshooting)
  • Wires

The last picture mainly shows chaos, it just shows my own real life connection. The second picture shows how the L293D works generally but the first picture shows how it all is supposed to connect, according to my code.

You will need two L293D chips if you are using four motors for the wheels like I did, except two of them act the same but that is difficult with turning (rotating). I use one chip for the front wheels and the other for the back wheels (one motor for each side of the chip).

I found it easy for the long run to find out which way the motor turns with what connection - I had to solder the wires to the motors so I used red for forward and black for backward. Then I paired that one to a pin on the Arduino right away. For example: leftforwardfront is pin 2. That is the front left motor, going forward (red wire); leftreversefront is pin 3 which is the front left motor again, but going reverse (so black wire). Those two are on the same side of the first chip. See first picture.

Here is the code I used for the motors, the LEDs, and the pushbutton.

int leftforwardfront = 2;                      //Setting digital pins for frontwheels on the arduino
int leftreversefront = 3;
int rightforwardfront = 4;
int rightreversefront = 5;
int leftforwardback = 6;                       //Setting digital pins for backwheels
int leftreverseback = 7;
int rightforwardback = 8; int rightreverseback = 9; int led1 = 13; //Setting digital pin for LED (RED) int led2 = 12; //Setting digital pin for LED (GREEN) int button = A3; //Pushbutton int buttonstate = 0; //variable for extra button</p><p> void setup() { pinMode(leftforwardfront,OUTPUT); //the motor is an output pinMode(leftreversefront,OUTPUT); //^ pinMode(rightforwardfront,OUTPUT); pinMode(rightreversefront,OUTPUT); pinMode(leftforwardback,OUTPUT); pinMode(leftreverseback,OUTPUT); pinMode(rightforwardback,OUTPUT); pinMode(rightreverseback,OUTPUT); pinMode(led1,OUTPUT); //The LEDs are outputs pinMode(led2,OUTPUT); pinMode(button,INPUT_PULLUP); stopp(); //Start Alfred in the stop void. delay(1000); //Let's give him a second to get ready for action } void loop() { buttonstate = digitalRead(button); //Reads if button is pressed or not if(buttonstate == LOW){ //IF button is pressed (HIGH when it is open, LOW when it is pressed) if(forwardstate == HIGH){ //Stop if going forward stopp(); } else if(reversestate == HIGH){ //Stop if in reverse stopp(); } else{ //Go forward if neither forward(); } } } void forward() //Void to make Alfred go forward { digitalWrite(leftforwardfront,HIGH); // run FORWARD front wheels digitalWrite(leftreversefront,LOW); digitalWrite(rightforwardfront,HIGH); digitalWrite(rightreversefront,LOW); digitalWrite(leftforwardback,HIGH); // run FORWARD back wheels digitalWrite(leftreverseback,LOW); digitalWrite(rightforwardback,HIGH); digitalWrite(rightreverseback,LOW); digitalWrite(led2,HIGH); //Turns on green LED if motors are on digitalWrite(led1,LOW); } void stopp() //Void to make Alfred stop { digitalWrite(leftforwardfront,LOW); // stop RIGHT front wheels digitalWrite(leftreversefront,LOW); digitalWrite(rightforwardfront,LOW); digitalWrite(rightreversefront,LOW); digitalWrite(leftforwardback,LOW); // stop RIGHT back wheels digitalWrite(leftreverseback,LOW); digitalWrite(rightforwardback,LOW); digitalWrite(rightreverseback,LOW); digitalWrite(led1,HIGH); //Turns on red LED if motors are off digitalWrite(led2,LOW); }

Step 4: Adding the Distance Sensor and Servo Motor

This is the biggest step of them all.

In this step we will need:

  • Ultrasonic sensor HC-SR04
  • Servo Motor SG90
  • A little tape or glue or something to fasten the modules

I simply duct-taped one "wing" from the servo motor to the ultrasensor and duct-taped the servo motor to the body of the car. I chose to to screw the wing to the servo motor incase he hits something that way - the sensor simply just falls off instead of possibly breaking something.

Have in mind that it depends on how the servo motor turns and how you look at it whether it's default at 0 degrees or 90 degrees. I have it so it looks forward at 90 degrees.

The ultrasonic sensor has four pins which all need to be connected:
• VCC: Power, +5V
• Trig: Triggers the waves, connected to any Digital I/O pin on the Arduino.
• Echo: Echo receives the waves and outputs the time in microseconds, connected to any Digital I/O pin.
• Gnd: Ground, 0V

The servo motor has three wires which all need to be connected:
• Orange: Digital Input (PWM)
• Red: Power, +5V
• Brown: Ground, 0V

This code includes the servo motor and ultrasonic sensor along with the code in last step:

#include <NewPing.h>                           //Library for Ultrasensor:
#include <Servo.h>                             //Library for Servo:
#define maxdist 200                            // Maximum distance to ping for (in centimeters) [MAX 400-500cm]. 

int trigger = A0;                              //Setting analog pin for the trigger on the ultrasonic sensor.
int echo = A1;                                 //Setting analog pin for the echo on the ultrasonic sensor.
NewPing sonar(trigger, echo, maxdist);         //Setting the pins for NewPing library and maximum distance (defined above).
int leftDist = 0;                              //Variable as the left distance, start as 0.
int rightDist = 0;                             //Variable as the right distance, start as 0.
int currentDist = 0;                           //Variable as the current distance, start as 0.
int maxDist = 35;                              //Variable for maximum distance in CM
int readping(){                                //Behaves as a 'void', having this as a 'void' would not do 'return'.
  delay(70);                                      //Waits 70ms between pings. 29ms should be the shortest delay between pings.
  digitalWrite(echo,LOW);                         //Fixerupper, otherwise might get stuck in HIGH and just showes 0cm
  int bounce = sonar.ping();                      //Send ping, get ping time in microseconds (uS).
  int cm = bounce/US_ROUNDTRIP_CM;                //Transforms it to cm.
  return cm;

int leftforwardfront = 2;                      //Setting digital pins for frontwheels on the arduino
int leftreversefront = 3;
int rightforwardfront = 4;
int rightreversefront = 5;
int leftforwardback = 6;                       //Setting digital pins for backwheels
int leftreverseback = 7;
int rightforwardback = 8;
int rightreverseback = 9;

Servo servo1;                                  //The servo object's name for the library
int servopin = 10;                             //Setting digital pin for the servo motor
int servoleft = 180;                           //Default servo turning left in degrees
int servoright = 5;                            //Default servo turning right in degrees
int servoforward = 90;                         //Default servo turning forward in degrees

int led1 = 13;                                 //Setting digital pin for LED (RED)
int led2 = 12;                                 //Setting digital pin for LED (GREEN)

int button = A3;                               //Extra button in case the remote control is lost or not around.

int start = LOW;                               //variable for starting the car so he won't check his surroundings while staying still
int forwardstate = LOW;                        //variable for remembering where he was going before turning
int reversestate = LOW;
int buttonstate = 0;                           //variable for extra button
void setup() 
{ Serial.begin(9600);                          //Setup the serial monitor 
  servo1.attach(servopin);                     //Start up the servo motor
  
                                               //PinMode tells the arduino if the pin is an out or an input
  pinMode(leftforwardfront,OUTPUT);            //the motor is an output
  pinMode(leftreversefront,OUTPUT);            //^
  pinMode(rightforwardfront,OUTPUT);
  pinMode(rightreversefront,OUTPUT);
  pinMode(leftforwardback,OUTPUT);
  pinMode(leftreverseback,OUTPUT);
  pinMode(rightforwardback,OUTPUT);
  pinMode(rightreverseback,OUTPUT);
  pinMode(led1,OUTPUT);                        //The LEDs are outputs
  pinMode(led2,OUTPUT);
  pinMode(button,INPUT_PULLUP);
  
  stopp();                                     //Start Alfred in the stop void. 
  delay(1000);                                 //Let's give him a second to get ready for action
}
void loop() {
  Serial.print(currentDist);                   //Prints the distance he reads
  Serial.print(" cm");                         //When he shows 0 cm, it just means he is not getting any signal. 
  Serial.println();                            //Makes a new line
  delay(500);                                  //Prints every half a second

  buttonstate = digitalRead(button);           //Reads if button is pressed or not
  if(buttonstate == LOW){                      //IF button is pressed (HIGH when it is open, LOW when it is pressed)
    if(forwardstate == HIGH){                  //Stop if going forward
      stopp();
      start = LOW; 
    }
    else if(reversestate == HIGH){             //Stop if in reverse
      stopp();
      start = LOW; 
    }
    else{                                      //Go forward if neither
      forward();
      start = HIGH; 
    }
  }if(start == HIGH){                                  //only if Alfred is already going
    delay(500);
    currentDist = readping();                  //Read signal from ultra sonic sensor
    if (currentDist < maxDist && currentDist != 0) {  //If the current distance is less than desired max distance AND current distance is not 0
      stopp();                                 //If above is true do the following. Stop the wheels
      blinkred();                              //Lets me know by blinking the red LED that Alfred spotted someting in his way
      lookaround();                            //And will look around to see where to go next
    }  
  }
}
void lookaround()                              //Void for looking around to see where to go
{
  servo1.write(servoleft);                     //Look left
  delay(500);                                  //give him time to actually look 
  leftDist = readping();                       //read distance and store as a variable 
  delay(500);
  servo1.write(servoright);                    //Look right
  delay(500);
  rightDist = readping(); 
  delay(500);
  servo1.write(servoforward);                  //Look forward
  delay(100);
  compareDist();                               //go to void compare distance
}
void compareDist()                             //compares distance between left side and right side
{
  if (leftDist>rightDist)                      //checks if distance on left is more than distance on right
   {
    reverse();                                 //back up a little bit
    delay(1000); 
    left();                                    //if true, go left
    delay(2000);
    forward();                                 //continue
   }
   else if (rightDist>leftDist)                //checks if distance on right is more than distance on left
   {
    reverse();                                 //back up a little bit
    delay(1000);
    right();                                   //if true, go right 
    delay(2000);
    forward();                                 //continue
   }
   else                                        //if both ways as good or as bad, just turn around
   {
    reverse();
    delay(500);                              
    left();                                    //left or right..doesn't really matter.
    delay(2500);                               //find out how long it takes to turnaround 180°
    forward();
   }
}
void blinkred()                                //simple code to make the red LED blink for a bit to give me information on what's going on
{
  delay(100);
  digitalWrite(led1,LOW);
  delay(100);
  digitalWrite(led1,HIGH);
  delay(100);
  digitalWrite(led1,LOW);
  delay(100);
  digitalWrite(led1,HIGH);
}
void blinkgreen()                              //simple code to make the red LED blink for a bit to give me information on what's going on
{
  delay(100);
  digitalWrite(led2,HIGH);
  delay(100);
  digitalWrite(led2,LOW);
  delay(100);
  digitalWrite(led2,HIGH);
  delay(100);
  digitalWrite(led2,LOW);
}
void forward()                                 //Void to make Alfred go forward
{
  digitalWrite(leftforwardfront,HIGH);         // run FORWARD front wheels
  digitalWrite(leftreversefront,LOW);
  digitalWrite(rightforwardfront,HIGH);
  digitalWrite(rightreversefront,LOW);
  digitalWrite(leftforwardback,HIGH);          // run FORWARD back wheels
  digitalWrite(leftreverseback,LOW);
  digitalWrite(rightforwardback,HIGH);
  digitalWrite(rightreverseback,LOW); 
  digitalWrite(led2,HIGH);                     //Turns on green LED if motors are on
  digitalWrite(led1,LOW);
  servo1.write(servoforward);
  forwardstate = HIGH;
  reversestate = LOW;
}
void reverse()                                 //Void to make Alfred go reverse
{
  digitalWrite(leftforwardfront,LOW);          // run REVERSE front wheels
  digitalWrite(leftreversefront,HIGH);
  digitalWrite(rightforwardfront,LOW);
  digitalWrite(rightreversefront,HIGH);
  digitalWrite(leftforwardback,LOW);           // run REVERSE back wheels
  digitalWrite(leftreverseback,HIGH);
  digitalWrite(rightforwardback,LOW);
  digitalWrite(rightreverseback,HIGH);
  digitalWrite(led2,HIGH);                     //Turns on green LED if motors are on
  digitalWrite(led1,LOW);
  forwardstate = LOW;
  reversestate = HIGH;
}
void left()                                    //Void to make Alfred turn left
{
  digitalWrite(leftforwardfront,LOW);          // run LEFT front wheels
  digitalWrite(leftreversefront,HIGH);
  digitalWrite(rightforwardfront,HIGH);
  digitalWrite(rightreversefront,LOW);
  digitalWrite(leftforwardback,LOW);           // run LEFT back wheels
  digitalWrite(leftreverseback,HIGH);
  digitalWrite(rightforwardback,HIGH);
  digitalWrite(rightreverseback,LOW);
  digitalWrite(led2,HIGH);                     //Turns on green LED if motors are on
  digitalWrite(led1,LOW);
  servo1.write(servoleft);                     //Look left when going left, to add character
}
void right()                                   //Void to make Alfred turn right
{
  digitalWrite(leftforwardfront,HIGH);         // run RIGHT front wheels
  digitalWrite(leftreversefront,LOW);
  digitalWrite(rightforwardfront,LOW);
  digitalWrite(rightreversefront,HIGH);
  digitalWrite(leftforwardback,HIGH);          // run RIGHT back wheels
  digitalWrite(leftreverseback,LOW);
  digitalWrite(rightforwardback,LOW);
  digitalWrite(rightreverseback,HIGH);
  digitalWrite(led2,HIGH);                     //Turns on green LED if motors are on
  digitalWrite(led1,LOW);
  servo1.write(servoright);                    //Look right when going right, to add character
}
void stopp()                                   //Void to make Alfred stop
{
  digitalWrite(leftforwardfront,LOW);          // stop RIGHT front wheels
  digitalWrite(leftreversefront,LOW);
  digitalWrite(rightforwardfront,LOW);
  digitalWrite(rightreversefront,LOW);
  digitalWrite(leftforwardback,LOW);           // stop RIGHT back wheels
  digitalWrite(leftreverseback,LOW);
  digitalWrite(rightforwardback,LOW);
  digitalWrite(rightreverseback,LOW);
  digitalWrite(led1,HIGH);                     //Turns on red LED if motors are off
  digitalWrite(led2,LOW);
  forwardstate = LOW;
  reversestate = LOW;
  servo1.write(servoforward);
}

The code should be well commented but if not I'll try to explain further;

In the loop I start off by using the Serial.print command. Therefore, I can then check if the ultrasonic sensor is working. That is mostly for troubleshooting but only when the Arduino is connected to the computer.

Digital outputs and inputs were almost exclusively used, even though they were connected as analog pins on the Arduino. I did this because I didn’t have space with the digital pins. The ultrasonic sensor was the only module that was restricted to analog pins. That is because I needed the information from the sensor as a value for the distance rather than simply high or low.

There is a way not to use the library for the ultrasonic sensor, but I chose to use it anyway.

I used the function delay several times in the code, mostly to give Alfred a little time to act on the previous command.

The loop itself is a relatively small part of the code, the definitions and other void take the most space. I became fond of using void as it is very helpful for repetitive functions: one is for when Alfred looks around after seeing an object; another one for when he compares the values which he obtained in the previous void and acts on it; another two for blinking each LED for a bit; and another five voids for each direction and to stop.

The loop is split into three parts: the Serial.print command; the remote control (step 6); and the last part is where the magic happens.

Step 5: Add Sound Sensor Incase He Doesn't See Obstacle

I added this step last minute. I did not like how sometimes Alfred was going on angles towards a wall or there was an obstacle that he simply did not "see" and didn't stop. This was problematic so I added the microphone sound sensor to "hear" the sound of impact.

In this step we will only add the module:

  • Microphone Sound sensor KY-038

The sensor has four pins but only three need to be connected:

• AO: Analog Out, direct microphone signal as voltage value.
• G: Ground, 0V
• +: Power, +5V
• DO: Digital Out, I/O signal where the threshold is changed with the potentiometer.

The module has a microphone on the end that feeds an analog signal to an amplifier. The amplifier does not return an absolute value, but a relative value that needs to be defined for the desired environment that can be altered with a potentiometer on the module.

I used the digital output for the sound sensor, that way I can change the sensitivity by adjusting the potentiometer. Surely it is possible to use the analog output, but then you just set a certain value instead of HIGH or LOW.

Have in mind the value is different for each environment.

Here is the part of the code I used only with the sound sensor:

int soundpin = A2;                             //Setting DO pin for sound sensor (digitalRead/Write works on all pins.
int sound = 0;                                 //Setting variable for sound, starting at 0

void setup() {
pinMode(sound, INPUT); //The sound sensor is an input stopp(); //Start Alfred in the stop void. delay(1000); //Let's give him a second to get ready for action } void loop() { if(start == HIGH){ delay(500); sound = digitalRead(soundpin); //Read signal from sound sensor if(sound == LOW){ //If the threshold is reached (LOW because it is inverted), do following. stopp(); //Stop the wheels blinkgreen(); //Lets me know by blinking the green LED that Alfred bumped into something. lookaround(); //And will look around to see where to go next } } }

Step 6: (Optional But Fun) Controlling With a Remote

It is possible to use any remote, I started with a remote that came with my Arduino set but soon enough I lost it and used a TV remote instead, it just has to be defined in the code.

For this step we will need:

  • A remote.
  • Infrared sensor receiver KY-022.

It has three pins which all need to be connected:
• -: Ground, 0V
• PLUS: Power, +5V (also works with +3.3V)
• S: Digital Output (preferable pin 11)

Have in mind that this connection is only for the module KY-022 - if you use the chip by itself it is a different connection and you might fry it - just double check the connections by looking up the data sheet.

The location does not really matter, but I put mine of the backside of the car, facing backwards.

This is the code I used for the remote:

#include <boarddefs.h>
#include <IRremote.h>
#define samup       0xE0E006F9                 //Hex codes I am using for samsung controller
#define samdown     0xE0E08679                 
#define samleft     0xE0E0A659 
#define samright    0xE0E046B9 
#define sammiddle   0xE0E016E9 

int recv_pin = 11;                             //Setting digital pin for receiver sensor for remote control
IRrecv irrecv(recv_pin);                       //Setting the name for receiving IR for the library
decode_results results;                        //Setting the name for the returning results for the library

int start = LOW;                               //variable for starting the car so he won't check his surroundings while staying still
int forwardstate = LOW;                        //variable for remembering where he was going before turning
int reversestate = LOW;
void setup() {
  Serial.begin(9600);                          //Setup the serial monitor 
  irrecv.enableIRIn();                         //Start the remote receiver
  
  stopp();                                     //Start Alfred in the stop void. 
  delay(1000);                                 //Let's give him a second to get ready for action
}
void loop() {
  Serial.print("Remote control HEX code: ");
  Serial.print(results.value, HEX);            //Serial print for when I want to know what code a remote's button is sending.        
  Serial.println();                            //Makes a new line
  delay(500);                                  //Prints every half a second</p><p>  }
  if(irrecv.decode(&results)){                 //Here it reads the HEX code from receiver.
    switch(results.value)                      //If he reads a known hex code as defined above, pairs it with a case below
    {
      case samup:                              //If 'up' is pressed on the controller:
        forward();                             //go forward (void). 
        start = HIGH;                          //Sets the 'start' variable as HIGH so he can start detect his surroundings
        break; 
      case samdown:                            //If 'down' is pressed:
        reverse();
        break;
      case samleft:                            //If 'left' is pressed:
        left();
        delay(1000);                           //turn left for 1000ms
        if(forwardstate == HIGH){              //If he was going forward before,
          forward();                           //Continue going forward
        }
        else if(reversestate == HIGH){         //If he was going reverse
          reverse();                           //Continue going reverse
        }
        else{                                  //If neither
          stopp();                             //Just stop then
        }
        break;
      case samright:                           //If 'right' is pressed:
        right();
        delay(1000);                           //turn right for 1000ms
        if(forwardstate == HIGH){              //If he was going forward before,
          forward();                           //Continue going forward
        }
        else if(reversestate == HIGH){         //If he was going reverse
          reverse();                           //Continue going reverse
        }
        else{                                  //If neither
          stopp();                             //Just stop then
        }
        break;
      case sammiddle:                          //If the middle button is pressed:
        stopp();                               //stop (void)
        start = LOW;                           //We don't want him to check his surroundings when told to stop
        break;      
     }
     delay(100);                               //A little delay just to prevent false readings
     irrecv.resume();                          //Receives next value from remote
  }
}

When finding the hex code for the buttons on your remote, you just need to set up the Serial.print to read what code you are sending. Each remote (unless they are of the exact same kind and is supposed to have the same code, like TV remotes of same kind) have separate codes so it is unlikely you will use the same definition for the buttons as me.

Have in mind the hex code "0xFFFFFFFF" means repetition, some remotes have that, others simply repeat the same code.

Step 7: Wrapping It Up!

I will attach the whole code if I confused you with the separation of the code in previous steps. But please only use it to help you make your own, that is probably one of the best ways to learn - at least better than just copy paste.

I attached the libraries I used.

Also, this is the how I connected it all to the Arduino:

Digital Pins
2. 1 st L293D INPUT 1
3. 1 st L293D INPUT 2
4. 1 st L293D INPUT 4
5. 1 st L293D INPUT 3
6. 2 nd L293D INPUT 1
7. 2 nd L293D INPUT 2
8. 2 nd L293D INPUT 4
9. 2 nd L293D INPUT 3
10. Servo Motor Digital Input
11. IR Sensor Digital Output
12. Green LED
13. Red LED

Analog Pins
A0. Trigger Pin for Ultrasonic Sensor
A1. Echo Pin for Ultrasonic Sensor
A2. DO Pin for Sound Sensor
A3. Pin for Switch

Step 8: Thank You!

This is the first Instructables that I make so please rate it so I can see what to fix and comment if there are any questions.

I am open to any ideas that might makes this project better or even simpler. There is always room for upgrades or updates and sometime we just need to see things with another perspective. I am not very fond of the sound sensor so if there is a better alternative that maybe I haven't thought of - let me know!

-snaedisdaniels