Arduino Smart Robot Car DIY Tutorial

For electronics enthusiasts looking to build a smart robot car, this TechSparks article will utilize Arduino for the project. This DIY project will start with assembling the frame and continue until the final setup and testing. After the construction, you can control it remotely using the Bluetooth feature on your phone and includes an ultrasonic obstacle avoidance module to help you navigate around obstacles while driving. This is the ultimate form of the Arduino smart robot car we’ve built, so let’s get started!

arduino smart car

Table of Contents

Project Core Materials and Assembly

Project Core Materials

In this project, we used a car kit to build the chassis frame, equipped with an 18650 3.7V battery and the corresponding battery holder. In terms of hardware, we chose the Arduino UNO as the control board, but if you prefer other control boards, this article is still applicable. Additionally, we opted for the L298N motor driver module. Apart from these, completing this project will require basic tools and materials such as a soldering iron, solder, power cables, DuPont wires, pin headers, and connectors.

Assembly of the Car Chassis

In the car kit, the motors and connecting wires come separately, so you need to make the connections yourself. A useful tip is to directly solder the power cables to the motors rather than just bundling them together. This prevents poor connections due to vibrations during operation.

When installing the chassis, first, insert the mounting brackets into the backplate to securely fix the motors. Next, attach the four pre-soldered motors to the backplate. Then, insert six steel rods into the holes on the backplate to connect the upper and lower backplates. Finally, mount the four wheels on the backplate.

Installation and Programming of Drive Module

We’ve completed the installation of the car framework as mentioned above, but it’s still not operational. Now let’s implement this magic using L298N driver module and Arduino.

Introduction to the L298N Driver Module

Introduction to pin assignment of L298N driver module

Beginners might not be familiar with what a driver module is and why it’s needed. In their understanding, all a 4WD car needs is a battery, and turning it on should make it run. However, in reality, 4WD cars have two drawbacks: cannot turn and cannot adjust speed. In this Arduino smart robot car project, our goal is to build a car with remote control capabilities, which is why we need a driver module. We’ve chosen the L298N as the driver module, and its pin functions are as follows:

  • +12V: This pin represents the maximum voltage the driver module can accept. The voltage range of the L298N chip typically falls between 5 to 12V. To meet this requirement, we’ve connected two 18650 batteries in series to achieve a voltage of 7.4V.
  • GND: This pin represents the negative terminal of the power source. To ensure that all modules share the same voltage reference point, different modules need to be connected to the same GND.
  • +5V: Typically, this pin is used to power other devices. The L298N module contains an internal voltage regulator circuit that converts the high voltage from the “+12V” pin into a stable +5V voltage.
  • ENA/ENB: The L298N is an H-bridge motor driver chip with two motor control output channels, each controlling one motor. By controlling the current direction and intensity of these two channels, we can achieve control over the robot car’s forward, backward, and turning movements.
  • IN1/IN2 and IN3/IN4: These pins are used to control the motor’s direction. By applying different voltage levels to these pins, we can change the motor’s rotation direction.
  • OUT1/OUT2 and OUT3/OUT4: These pins serve similar functions to the ones mentioned above.

Hardware Wiring and Installation

  1. First, split the extended motor wires that run from the chassis into two groups for the left and right sides and connect them to the driver module.
  2. Connect the motor wires to OUT1, OUT2, OUT3, and OUT4, ensuring that you tighten the screws above the blue terminal blocks using a screwdriver.
  3. Install the battery holder at the rear of the car and connect the power wires to the +12V and GND pins. Remember that the red wire connects to +12V, while the black wire connects to GND.
  4. Secure the Arduino board firmly in the middle of the car.
  5. Connect the Arduino board’s “5V” pin to the L298N driver module’s “+5V” pin using a red wire.
  6. Connect the Arduino board’s “GND” pin to the L298N driver module’s “GND” pin using a black wire.
  7. Finally, use male-to-female DuPont wires to connect Arduino board pins 4/5/6/7 to L298N driver module pins IN1/IN2/IN3/IN4, respectively.

Hardware connected arduino smart car

Basic Smart Car Motion Control Code

				
					// Define five motion states
#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4
// Define the required pins
int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;
void setup() {
  // Set the motor control pins to OUTPUT mode
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}
void loop() {
  // Loop through each of the five motion states: forward, backward, left, right, and stop
  int cmd;
  for(cmd = 0; cmd < 5; cmd++) {
    motorRun(cmd);
    delay(2000); // Execute each command for 2 seconds
  }
}
// Motion control function
void motorRun(int cmd) {
  switch (cmd) {
    case FORWARD:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case BACKWARD:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    case TURNLEFT:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case TURNRIGHT:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    default:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

				
			

Motor motion is achieved by controlling the driver module, specifically depending on the state of the two sets of pins IN1/IN2/EN and IN3/IN4/ENB. In this Arduino smart car project, we only control the motor’s motion state without adjusting the motor’s speed. Therefore, the ENA and ENB pins are connected to a high level by default, and we only need to operate the two sets of pins IN1/IN2 and IN3/IN4 to control the car’s motion. Here, the motor’s motion is achieved by setting two pins on the same side to different output voltage states. The following shows the correspondence between inputs and outputs for the L298N:

EnterOutput
IN1IN2OUT1OUT1
HLHL
LHLH
EnterOutput
IN3IN4OUT3OUT4
HLHL
LHLH

From the above table, we can clearly understand how to control the car’s motion. By setting different output voltages on the two pins on the same side, you can achieve different motion states. To improve code readability and execution efficiency, we have encapsulated the four motion states of the car into functions. This way, when programming, you only need to call the corresponding function, and there’s no need to write detailed pin control code each time.

				
					void motorRun(int cmd)
{
  switch(cmd){
    case FORWARD:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case BACKWARD:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case TURNLEFT:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

				
			

Add Speed Control Function

Pulse Width Modulation

When you need to control the speed of a motor in an Arduino project, you can use Pulse Width Modulation (PWM) technology. PWM allows you to adjust the current supplied to the motor, thereby changing its speed. This is achieved by generating PWM signals with varying duty cycles at different time intervals. In Arduino, you can use the analogWrite() function to generate PWM signals. It’s important to note that on boards like Arduino UNO, only the pins marked with a tilde (~) symbol support PWM functionality.

Arduino UNO PWM pins

Here’s a simple explanation: If you want to output a 5V voltage, you just need to keep the output at a high level. If you want to output 3.75V, you need to keep the output high for 75% of one cycle and low for 25% of the time. Similarly, if you want to output 2.5V, you need to keep the output high for 50% of one cycle and low for the other 50% of the time.

PWM signal waveform

Smart Car Speed Control Code

				
					int leftCounter = 0, rightCounter = 0;
unsigned long time = 0, old_time = 0; // Time markers
unsigned long time1 = 0; // Time marker
float lv, rv; // Left and right wheel speeds
#define STOP        0
#define FORWARD     1
#define BACKWARD    2
#define TURNLEFT    3
#define TURNRIGHT   4
#define CHANGESPEED 5
int leftMotor1 = 16;
int leftMotor2 = 17;
int rightMotor1 = 18;
int rightMotor2 = 19;
bool speedLevel = 0;
int leftPWM = 5;
int rightPWM = 6;
void setup() {
  // Set up your initial configuration here to run once:
  Serial.begin(9600); 
  attachInterrupt(0, RightCount_CallBack, FALLING);
  attachInterrupt(1, LeftCount_CallBack, FALLING);
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
  pinMode(leftPWM, OUTPUT);
  pinMode(rightPWM, OUTPUT);
}
void loop() {
  // Your main code goes here to run repeatedly:
  SpeedDetection();
 
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    
    Serial.print(cmd);
    motorRun(cmd);
    if (speedLevel)  // Change speed according to different gears
    {
      analogWrite(leftPWM, 120);
      analogWrite(rightPWM, 120);
    }
    else {
      analogWrite(leftPWM, 250);
      analogWrite(rightPWM, 250);
    }
  }  
}
/*
 * Speed Calculation
 */
bool SpeedDetection() {
  time = millis(); // in milliseconds, calculate current time
  if (abs(time - old_time) >= 1000) // If the timing has reached 1 second
  {  
    detachInterrupt(0); // Turn off external interrupt 0
    detachInterrupt(1); // Turn off external interrupt 1
    // Convert the pulse count obtained from the encoder disk every second into the current speed value
    // The speed unit is rotations per minute (rpm). This encoder disk has 20 holes.
    Serial.print("left:");
    lv = (float)leftCounter * 60 / 20; // Left wheel motor speed
    rv = (float)rightCounter * 60 / 20; // Right wheel motor speed
    Serial.print("left:");
    Serial.print(lv); // Upload the current speed of the left wheel motor to the upper computer in high and low bytes
    Serial.print("     right:");
    Serial.println(rv); // Upload the current speed of the right wheel motor to the upper computer in high and low bytes
    // Restore to the initial state of encoder speed measurement
    leftCounter = 0; // Reset the pulse count value to zero for the next second pulse count calculation
    rightCounter = 0;
    old_time = millis(); // Record the time point when the speed measurement occurred
    attachInterrupt(0, RightCount_CallBack, FALLING); // Reopen external interrupt 0
    attachInterrupt(1, LeftCount_CallBack, FALLING); // Reopen external interrupt 1
    return true;
  }
  else
    return false;
}
/*
 * Right Wheel Encoder Interrupt Service Function
 */
void RightCount_CallBack() {
  rightCounter++;
}
/*
 * Left Wheel Encoder Interrupt Service Function
 */
void LeftCount_CallBack() {
  leftCounter++;
}
/*
 * Car Motion Control Function
 */
void motorRun(int cmd) {
  switch (cmd) {
    case FORWARD:
      Serial.println("FORWARD"); // Output state
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case BACKWARD:
      Serial.println("BACKWARD"); // Output state
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); // Output state
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); // Output state
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case CHANGESPEED:
      Serial.println("CHANGE SPEED"); // Output state
      if (speedLevel)  // Switch gears when receiving a gear change command
        speedLevel = false;
      else
        speedLevel = true;
      break;
     default:
      Serial.println("STOP"); // Output state
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

				
			

Adding Ultrasonic Obstacle Avoidance Function

Material Preparation

To enable your Arduino robot car to achieve intelligent obstacle avoidance function, you need to purchase the three components in the picture above, which are HC-SR04, SG90 and fixing bracket.

HC-SR04 is a widely used ultrasonic distance sensor module. Its operation involves sending ultrasonic pulses to the target object and then measuring the time it takes for the ultrasonic waves to travel from the sensor to the object and back to the sensor. By measuring the time and knowing the speed of sound, you can accurately calculate the distance from the object to the sensor.

SG90 is a micro servo motor commonly used in small electronic and robotics projects. It operates with a closed-loop control system, rotating to a specific position based on the pulse width of control signals from a microcontroller or a remote controller.

For the bracket, it is recommended to purchase it from the same supplier to ensure compatibility between the SG90 and the bracket.

Servo Installation

Calibration is crucial before installing a servo motor. This is because the operation of a servo motor involves control signals, with the signal modulation chip receiving the signals through the receiver’s channel and generating a control signal that includes a DC bias voltage. Inside the servo motor, there is a reference circuit that generates a baseline signal with a 20-millisecond period and a 1.5-millisecond pulse width.

This baseline signal is used to compare the voltage from a potentiometer, creating a voltage difference and outputting it. Ultimately, the polarity of the voltage difference determines the direction (forward or reverse) in which the drive chip controls the motor. Once the motor reaches the desired speed, the cascaded reduction gears will rotate the potentiometer to nullify the voltage difference, stopping the motor’s rotation.

Servo motor control typically requires a time-base pulse of around 20 milliseconds. Within this pulse, the angle control pulse portion resides in the high-level part, usually between 0.5 milliseconds and 2.5 milliseconds, with a total duration of 2 milliseconds. Taking a servo motor capable of rotating 180 degrees as an example, this means the following relationship:

High Voltage TimeCorresponding Position
0.5ms
1.0ms45°
1.5ms90°
2.0ms135°
2.5ms180°

When the same control signal is applied to a servo, it moves to a fixed position rather than executing circular motion. Within the servo’s range of motion, each position corresponds to a specific control signal. Therefore, it’s necessary to initialize the servo before mounting it on a fixed bracket. Servos typically have three wires: brown (GND), red (VCC), and orange (control signal). Here’s an example initialization program:

				
					#include <Servo.h>  // Include the servo control library's header file
#define PIN_SERVO 9  // Servo signal control pin
Servo myservo;
void setup()
{
  myservo.attach(PIN_SERVO);  // Initialize the servo
}
void loop()
{
  myservo.write(90);  // Set the servo's position
}

				
			

After completing the servo initialization, assemble these components together. However, please pay attention to a critical detail: the ultrasonic module’s operational principle requires that there are no obstacles in front of it during assembly, and it should continuously face forward. This is exceptionally important to ensure that the ultrasonic sensor accurately measures the distance in front of it. The final assembly should resemble the image shown below:

Final assembly drawing of steering gear and ultrasonic module

Ultrasonic Wiring

At this point, you’ll need to connect the ultrasonic module. However, you might find that the “5V” and “GND” pins on the Arduino board are insufficient. To address this, you need to create an expansion board. This expansion board typically has two rows of pins. One row is used to expand the “5V” connections, while the other is used to expand the “GND” connections. This setup allows for easier connection of additional components and sensors if you wish to add more functionality to your robot car.

Add an expansion board to the smart car

For the ultrasonic module connections, you’ll generally need to connect four pins. Specifically, connect the “VCC” pin to the “5V” pin on the Arduino UNO development board, and connect the “GND” pin to the board’s “GND” pin. Simultaneously, connect the “Trig” pin to the board’s “8” pin, and connect the “Echo” pin to the board’s “7” pin.

Smart car after ultrasonic module wiring

Smart Car Ultrasonic Obstacle Avoidance Function Code

				
					#include <Servo.h>
#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4
int leftMotor1 = 16;
int leftMotor2 = 17;
int rightMotor1 = 18;
int rightMotor2 = 19;
int leftPWM = 5;
int rightPWM = 6;
Servo myServo;  // Servo motor
int inputPin = 7;   // Define ultrasonic signal receiving interface
int outputPin = 8;  // Define ultrasonic signal sending interface
void setup() {
  // Initialize the serial communication
  Serial.begin(9600);
  
  // Initialize the servo motor pin
  myServo.attach(9);
  
  // Initialize the motor control pins
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
  pinMode(leftPWM, OUTPUT);
  pinMode(rightPWM, OUTPUT);
  
  // Initialize the ultrasonic control pins
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
}
void loop() {
  // Put your main code here, to run repeatedly
  avoidance();
}
void motorRun(int cmd, int value) {
  analogWrite(leftPWM, value);  // Set PWM output for speed control
  analogWrite(rightPWM, value);
  
  switch (cmd) {
    case FORWARD:
      Serial.println("FORWARD"); // Output the status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    case BACKWARD:
      Serial.println("BACKWARD"); // Output the status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case TURNLEFT:
      Serial.println("TURN LEFT"); // Output the status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case TURNRIGHT:
      Serial.println("TURN RIGHT"); // Output the status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    default:
      Serial.println("STOP"); // Output the status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}
void avoidance() {
  int pos;
  int dis[3];  // Distances
  motorRun(FORWARD, 200);
  myServo.write(90);
  dis[1] = getDistance(); // Middle distance
  
  if (dis[1] < 30) {
    motorRun(STOP, 0);
    
    // Rotate the servo to the left
    for (pos = 90; pos <= 150; pos += 1) {
      myServo.write(pos);
      delay(15);
    }
    
    dis[2] = getDistance(); // Left distance
    
    // Rotate the servo to the right
    for (pos = 150; pos >= 30; pos -= 1) {
      myServo.write(pos);
      delay(15);
      if (pos == 90)
        dis[1] = getDistance(); // Middle distance
    }
    
    dis[0] = getDistance();  // Right distance
    
    if (dis[0] < dis[2]) {
      // Turn left
      motorRun(TURNLEFT, 250);
      delay(500);
    } else {
      // Turn right
      motorRun(TURNRIGHT, 250);
      delay(500);
    }
  }
}
int getDistance() {
  digitalWrite(outputPin, LOW); // Set the ultrasonic signal sending interface to low level for 2 microseconds
  delayMicroseconds(2);
  digitalWrite(outputPin, HIGH); // Set the ultrasonic signal sending interface to high level for 10 microseconds, at least 10 microseconds
  delayMicroseconds(10);
  digitalWrite(outputPin, LOW); // Keep the ultrasonic signal sending interface at low level
  int distance = pulseIn(inputPin, HIGH); // Read the pulse time
  distance = distance / 58; // Convert the pulse time to distance (unit: centimeters)
  Serial.println(distance); // Output distance value
  if (distance >= 50) {
    // If the distance is greater than or equal to 50 cm, return 50
    return 50;
  }
  
  return distance;
}

				
			

Add Bluetooth Control Function

Arduino smart car HC-05 Bluetooth module

Based on the previous steps, we have successfully built a mobile car, but at this point, we cannot yet achieve remote control. To implement this function, we need the power of a Bluetooth module, and TechSparks recommends choosing HC-05. This is because it already has the Bluetooth communication protocol built-in, eliminating the need for us to develop and debug the communication protocol ourselves. Therefore, all we need to do is send the data we want to transmit to the Bluetooth module via the serial port, and the Bluetooth module will automatically send the data to the paired Bluetooth device using the Bluetooth protocol. Since Bluetooth communication relies on the serial port, it is advisable to first familiarize yourself with Arduino’s serial communication. The connection between HC-05 and the Arduino board is as follows:

  • TX connects to the “RX” pin.
  • RX connects to the “TX” pin.
  • GND connects to the “GND” pin.
  • VCC connects to the “5V” or “3.3V” pin.

Arduino smart car connected to HC-05 Bluetooth module

				
					#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4
int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  // USART read
  if(Serial.available() > 0) {
    char cmd = Serial.read(); // Read data sent to the serial port by the Bluetooth module
  
    Serial.print(cmd);
    motorRun(cmd);
  }
}
void motorRun(int cmd) {
  switch(cmd) {
    case FORWARD:
      Serial.println("FORWARD"); // Output status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case BACKWARD:
      Serial.println("BACKWARD"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); // Output status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      Serial.println("STOP"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

				
			

Add Tracking Function

Tracking Module Installation

In the service sector, we can observe that some basic tasks can be performed by robots, and one of these tasks is utilizing line tracking functionality. In order to make this Arduino smart robot car have this capability, we add the TCRT5000 sensor.

The TCRT5000 sensor is composed of an infrared-emitting diode and a photosensitive transistor. The infrared-emitting diode continuously emits infrared light. When the emitted infrared light is not reflected back or is reflected back but with insufficient intensity, the photosensitive transistor remains in a closed state. In this condition, the module’s output is at a low level, indicating that the diode remains off.

Arduino intelligent robot car TCRT5000

When an object to be detected appears within the sensor’s detection range, the intensity of the reflected infrared light is strong enough, and the photosensitive transistor saturates. In this state, the module’s output is at a high level, indicating that the diode is lit. This means that the sensor has detected the presence of an object.

Because black objects have a strong ability to absorb infrared light, when the infrared light emitted by the sensor shines on a black line, the infrared light will be absorbed by the black line, causing the photosensitive transistor of the sensor to close. In this case, an LED on the module will turn off. When no black line is detected, two LEDs on the module will remain lit.

To ensure the proper functioning of the line tracking module, it is typically necessary to place the sensors at a distance of 1-2 centimeters from the black line to be detected. It is recommended to mount the line tracking module on a piece of cardboard and leave about 1 centimeter of space between the modules. The line tracking modules output varying voltages by detecting differences in reflection distance, which are analog signals. However, since we only need to determine whether a black line is detected, we use the “DO” digital signal output. Connect the “DO” pins of multiple line tracking modules from right to left, with the front of the car as the positive direction, to the development board’s “10,” “11,” “12,” and “13” pins.

TCRT5000 wiring

Track Function Code

				
					#include <Servo.h>
#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4
int leftMotor1 = 16;
int leftMotor2 = 17;
int rightMotor1 = 18;
int rightMotor2 = 19;
int trac1 = 10; // Ordered from the far right in front of the car
int trac2 = 11;
int trac3 = 12;
int trac4 = 13;
int leftPWM = 5;
int rightPWM = 6;
Servo myServo;  // Servo motor
int inputPin = 7;   // Define ultrasonic signal receiving interface
int outputPin = 8;  // Define ultrasonic signal emitting interface
void setup() {
  // Put your setup code here, to run once:
  // Serial initialization
  Serial.begin(9600);
  // Servo motor pin initialization
  myServo.attach(9);
  // Speed measurement pin initialization
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
  pinMode(leftPWM, OUTPUT);
  pinMode(rightPWM, OUTPUT);
  // Line tracking module D0 pin initialization
  pinMode(trac1, INPUT);
  pinMode(trac2, INPUT);
  pinMode(trac3, INPUT);
  pinMode(trac4, INPUT);
}
void loop() {
  // Put your main code here, to run repeatedly:
  tracing();
}
void motorRun(int cmd, int value) {
  analogWrite(leftPWM, value);  // Set PWM output, i.e., set speed
  analogWrite(rightPWM, value);
  switch (cmd) {
    case FORWARD:
      Serial.println("FORWARD"); // Output status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    case BACKWARD:
      Serial.println("BACKWARD"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case TURNLEFT:
      Serial.println("TURN  LEFT"); // Output status
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
    case TURNRIGHT:
      Serial.println("TURN  RIGHT"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
    default:
      Serial.println("STOP"); // Output status
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}
void tracing() {
  int data[4];
  data[0] = digitalRead(10);
  data[1] = digitalRead(11);
  data[2] = digitalRead(12);
  data[3] = digitalRead(13);
  if (!data[0] && !data[1] && !data[2] && !data[3])  // No black line detected on both sides
  {
    motorRun(FORWARD, 200);
  }
  if (data[0] || data[1])  // Black line detected on the right
  {
    motorRun(TURNRIGHT, 150);
  }
  if (data[2] || data[3])  // Black line detected on the left
  {
    motorRun(TURNLEFT, 150);
  }
  if (data[0] && data[3])  // Stop when black lines are detected on both sides
  {
    motorRun(STOP, 0);
    while (1);
  }
  Serial.print(data[0]);
  Serial.print("---");
  Serial.print(data[1]);
  Serial.print("---");
  Serial.print(data[2]);
  Serial.print("---");
  Serial.println(data[3]);
}

				
			

More content you may be interested in

How to Enable Multi-Core on ESP32 Microcontroller
How to Enable Multi-Core on ESP32 Microcontroller

This comprehensive guide elucidates the process of enabling multi-core functionality on the ESP32 microcontroller, leveraging its dual-core architecture for enhanced parallel processing capabilities. Through detailed

Arduino MQ-3 Alcohol Sensor Project
Arduino MQ-3 Alcohol Sensor Project

Creating an alcohol sensing project with an MQ-3 sensor and Arduino allows for accurate alcohol concentration monitoring in environments like bars and factories. The MQ-3

Arduino Temperature Alarm Project
Arduino Temperature Alarm Project

This Arduino temperature alarm project effectively demonstrates how to create a practical temperature monitoring system. By utilizing the LM35 temperature sensor and a buzzer, it

Arduino Breathing LED Project
Arduino Breathing LED Project

A breathing light is an LED lighting effect that simulates the human breathing process, gradually increasing or decreasing in brightness within a specific cycle. This

Scroll to Top