The Ettronics Hightech Robotic Car is an Arduino-based educational robot designed to introduce learners to Arduino programming, sensors, motor control, and autonomous robotics. It can be programmed to operate as an obstacle avoidance robot, obsatcle following robot and remote controlled robot.
In this tutorial, we will program the robotic car to oprate as an obsatcle avoidance robot using an Android smartphone and the ArduinoDroid app. You do not need a computer to upload the program to the Arduino Nano.
The robotic car uses an Arduino Nano ATmega328P with the CH340 USB interface, an L9110 motor driver, and an HC-SR04 ultrasonic sensor. Once programmed, the robot can detect obstacles in front of it, stop, reverse, choose a new direction, and continue navigating autonomously.
Watch the Robotic Car Assembly and Wiring Tutorial First
Before programming the robotic car, the components need to be properly assembled and connected.
If you have not yet assembled your Ettronics Hightech Robotic Car, watch our robotic car assembly and wiring tutorial first. In that video, we demonstrate how to assemble the mechanical parts of the car and connect the Arduino Nano, L9110 motor driver, ultrasonic sensor, motors, and other components.
After completing the assembly and connections, you can proceed with this programming tutorial. and if you are yeet to get the Ettronics Hightech Robotic car, you can click here to purchase it now.
BUY THE ETTRONICS HIGHTECH ROBOTIC CAR KIT
What You Need
For this tutorial, you need:
- Ettronics Hightech Obstacle Avoidance Robotic Car kit
- L9110 motor driver
- HC-SR04 ultrasonic sensor
- Android smartphone
- USB OTG adapter
- USB cable for the Arduino Nano
- ArduinoDroid app
- The obstacle avoidance program provided in this tutorial
Your Android phone should be running Android 8.0 or later.
How the Robotic Car Works
The basic principle behind the robot is quite simple.
The HC-SR04 ultrasonic sensor continuously measures the distance between the car and objects in front of it.
When the path is clear, the Arduino commands the motors to move the car forward.
When an obstacle is detected within the programmed detection distance, the Arduino:
- Stops the car.
- Reverses briefly.
- Randomly chooses either left or right.
- Turns approximately 90°.
- Checks the new direction.
- If the path is clear, it continues forward.
- If the path is blocked, it tries another direction.
- After three unsuccessful attempts, it turns approximately 180° and continues.
The program also uses filtered ultrasonic readings. This is particularly useful when the car is operated on rough surfaces such as interlocking pavement, where vibration can sometimes cause unstable ultrasonic readings.
Arduino Nano and the CH340 USB Interface
The controller used in this robotic car is the Arduino Nano based on the ATmega328P.
This particular Nano uses the CH340 USB-to-serial interface for communication between the Arduino and the USB port.
This is important when programming the board from an Android phone because the phone needs to communicate with the Nano through its USB interface.
Connecting the Android Phone to the Arduino Nano
The first thing you need is a USB OTG adapter. you can buy one on the market, but If you have the Ettronics Hightech Arduino Kit, the USB OTG-Adapter is included in it. If you don’t have the Ettronics Hightech Arduino kit and yo uwant to purchase, clicke here to do so.
BUY THE ETTRONICS HIGHTECH ARDUINO KIT
Connect the components as follows:
Android phone → OTG adapter → USB cable → Arduino Nano

Once connected, Android may display a message asking whether you want to allow the ArduinoDroid application to access the connected USB device.
Allow the connection.
If the ArduinoDroid application does not detect the board immediately, check that:
- The OTG adapter is properly connected.
- Your phone supports USB OTG.
- The USB cable supports data communication and is not only a charging cable.
- The Arduino Nano is receiving power.
- ArduinoDroid has permission to access the USB device.
Install ArduinoDroid
Install the ArduinoDroid application on your Android phone.
The phone used for this tutorial should have Android 8.0 or newer.
ArduinoDroid provides an Arduino development environment directly on an Android device, allowing you to write, compile, and upload Arduino programs without using a computer.
Once the application is installed, connect the Arduino Nano to your phone through the USB OTG adapter.
Arduino Nano Connections
The Arduino Nano is connected to the L9110 motor driver through the Arduino-compatible extension board used in the Ettronics Hightech Robotic Car.
The motor control connections are:
| Arduino Nano | L9110 Motor Driver |
|---|---|
| D5 | A-1A |
| D6 | A-1B |
| D9 | B-1A |
| D10 | B-2B |
The HC-SR04 ultrasonic sensor is connected as follows:
| HC-SR04 | Arduino Nano |
|---|---|
| VCC | 5V |
| GND | GND |
| TRIG | A4 |
| ECHO | A5 |
The Arduino uses these pins to control the two motors and communicate with the ultrasonic sensor.
Setting Up ArduinoDroid
After connecting the Nano to your Android phone, open ArduinoDroid.
Create a new Arduino sketch and select the appropriate Arduino board. To do so,
- Click on the three dots on the top right corner of the App,
- Click settings,
- Click Board Type
- Click Arduino
- Scrool up and select Nano w/ Atmega/CH340G

The Obstacle Avoidance Program
The following is the program used for the Ettronics Hightech Obstacle Avoidance Robotic Car.
The program has been designed to provide stable operation even when the robot is driven on relatively rough surfaces.
// ============================================================
// Ettronics.com
// ROUGH-SURFACE OBSTACLE AVOIDANCE ROBOT
//
// Features:
// 1. Rapid acceleration from starting speed to maximum speed
// 2. Full-speed forward navigation
// 3. Ultrasonic filtering for rough surfaces
// 4. Multiple readings required before confirming an obstacle
// 5. Obstacle detection distance of 30 cm
// 6. Stops before reversing
// 7. Reverses briefly before every turn
// 8. Randomly turns approximately 90° LEFT or RIGHT
// 9. Checks whether the new direction is clear
// 10. Three unsuccessful checks -> approximately 180° turn
// 11. Accelerates rapidly back to maximum speed
// ============================================================
// ============================================================
// MOTOR DIRECTION DEFINITIONS
// ============================================================
#define stop 0
#define forward 1
#define back 2
#define left 3
#define right 4
// ============================================================
// ULTRASONIC SENSOR
// ============================================================
#define Trig A4
#define Echo A5
// ============================================================
// SPEED SETTINGS
// ============================================================
#define START_SPEED 130
#define MAX_SPEED 255
#define SPEED_STEP 30
#define ACCEL_DELAY 40
// ============================================================
// OBSTACLE SETTINGS
// ============================================================
#define OBSTACLE_DISTANCE 30
#define OBSTACLE_CONFIRMATIONS 3
// ============================================================
// TURNING SETTINGS
// ============================================================
#define REVERSE_TIME 250
#define TURN_90_TIME 450
#define TURN_180_TIME 900
// ============================================================
// ULTRASONIC SENSOR SETTINGS
// ============================================================
#define SENSOR_SAMPLES 5
#define MAX_VALID_DISTANCE 400
// ============================================================
// OTHER SETTINGS
// ============================================================
#define MAX_ATTEMPTS 3
#define SENSOR_SETTLE_TIME 250
// ============================================================
// GLOBAL VARIABLES
// ============================================================
int currentSpeed = START_SPEED;
// ============================================================
// SETUP
// ============================================================
void setup()
{
Serial.begin(9600);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
randomSeed(analogRead(A0));
motor(stop, 0, 0);
delay(500);
Serial.println("=================================");
Serial.println("Ettronics Obstacle Avoidance Car");
Serial.println("Rough Surface Mode");
Serial.println("=================================");
}
// ============================================================
// MAIN LOOP
// ============================================================
void loop()
{
float distance;
distance = GetFilteredDistance();
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
if (ObstacleConfirmed())
{
obstacleAvoidance();
}
else
{
accelerateToMaximum();
}
delay(20);
}
// ============================================================
// SINGLE ULTRASONIC READING
// ============================================================
float GetSingleDistance()
{
long duration;
digitalWrite(Trig, LOW);
delayMicroseconds(2);
digitalWrite(Trig, HIGH);
delayMicroseconds(10);
digitalWrite(Trig, LOW);
duration = pulseIn(Echo, HIGH, 30000);
if (duration == 0)
{
return -1;
}
float distance = duration / 58.0;
if (distance <= 0 || distance > MAX_VALID_DISTANCE)
{
return -1;
}
return distance;
}
// ============================================================
// FILTERED DISTANCE
// ============================================================
float GetFilteredDistance()
{
float readings[SENSOR_SAMPLES];
int validReadings = 0;
for (int i = 0; i < SENSOR_SAMPLES; i++)
{
float distance = GetSingleDistance();
if (distance > 0)
{
readings[validReadings] = distance;
validReadings++;
}
delay(8);
}
if (validReadings == 0)
{
return MAX_VALID_DISTANCE;
}
float total = 0;
for (int i = 0; i < validReadings; i++)
{
total += readings[i];
}
return total / validReadings;
}
// ============================================================
// OBSTACLE CONFIRMATION
// ============================================================
bool ObstacleConfirmed()
{
int obstacleCount = 0;
for (int i = 0; i < OBSTACLE_CONFIRMATIONS; i++)
{
float distance = GetFilteredDistance();
Serial.print("Confirmation ");
Serial.print(i + 1);
Serial.print(": ");
Serial.print(distance);
Serial.println(" cm");
if (distance > 0 && distance <= OBSTACLE_DISTANCE)
{
obstacleCount++;
}
else
{
return false;
}
delay(10);
}
if (obstacleCount >= OBSTACLE_CONFIRMATIONS)
{
return true;
}
return false;
}
// ============================================================
// MOTOR 1
// ============================================================
void motor1(int speed1, int speed2)
{
analogWrite(5, speed1);
analogWrite(6, speed2);
}
// ============================================================
// MOTOR 2
// ============================================================
void motor2(int speed1, int speed2)
{
analogWrite(9, speed1);
analogWrite(10, speed2);
}
// ============================================================
// MOTOR DIRECTION CONTROL
// ============================================================
void motor(int dir, int speed1, int speed2)
{
switch (dir)
{
case stop:
motor1(0, 0);
motor2(0, 0);
break;
case forward:
motor1(speed1, 0);
motor2(speed2, 0);
break;
case back:
motor1(0, speed1);
motor2(0, speed2);
break;
case left:
motor1(0, speed1);
motor2(speed2, 0);
break;
case right:
motor1(speed1, 0);
motor2(0, speed2);
break;
default:
motor1(0, 0);
motor2(0, 0);
break;
}
}
// ============================================================
// RAPID ACCELERATION
// ============================================================
void accelerateToMaximum()
{
if (currentSpeed >= MAX_SPEED)
{
currentSpeed = MAX_SPEED;
motor(forward, MAX_SPEED, MAX_SPEED);
return;
}
motor(forward, currentSpeed, currentSpeed);
delay(ACCEL_DELAY);
currentSpeed += SPEED_STEP;
if (currentSpeed >= MAX_SPEED)
{
currentSpeed = MAX_SPEED;
}
motor(forward, currentSpeed, currentSpeed);
}
// ============================================================
// RESET SPEED
// ============================================================
void resetSpeed()
{
currentSpeed = START_SPEED;
}
// ============================================================
// OBSTACLE AVOIDANCE
// ============================================================
void obstacleAvoidance()
{
int attempts = 0;
int direction;
float distance;
Serial.println();
Serial.println("==============================");
Serial.println("CONFIRMED OBSTACLE!");
Serial.println("==============================");
// Stop
motor(stop, 0, 0);
Serial.println("Robot stopped.");
delay(300);
resetSpeed();
// ========================================================
// TRY DIFFERENT DIRECTIONS
// ========================================================
while (attempts < MAX_ATTEMPTS)
{
attempts++;
Serial.print("Direction attempt: ");
Serial.println(attempts);
// ----------------------------------------------------
// REVERSE BEFORE TURNING
// ----------------------------------------------------
Serial.println("Reversing...");
motor(back, MAX_SPEED, MAX_SPEED);
delay(REVERSE_TIME);
// Stop before turning
motor(stop, 0, 0);
delay(150);
// ----------------------------------------------------
// RANDOMLY CHOOSE LEFT OR RIGHT
// ----------------------------------------------------
direction = random(0, 2);
if (direction == 0)
{
Serial.println("Turning LEFT approximately 90 degrees.");
motor(left, MAX_SPEED, MAX_SPEED);
delay(TURN_90_TIME);
}
else
{
Serial.println("Turning RIGHT approximately 90 degrees.");
motor(right, MAX_SPEED, MAX_SPEED);
delay(TURN_90_TIME);
}
// Stop after turning
motor(stop, 0, 0);
delay(SENSOR_SETTLE_TIME);
// ----------------------------------------------------
// CHECK NEW DIRECTION
// ----------------------------------------------------
distance = GetFilteredDistance();
Serial.print("New direction distance: ");
Serial.print(distance);
Serial.println(" cm");
// ----------------------------------------------------
// PATH CLEAR
// ----------------------------------------------------
if (distance > OBSTACLE_DISTANCE)
{
Serial.println("PATH CLEAR!");
resetSpeed();
motor(forward, START_SPEED, START_SPEED);
delay(ACCEL_DELAY);
return;
}
// ----------------------------------------------------
// PATH STILL BLOCKED
// ----------------------------------------------------
Serial.println("PATH BLOCKED.");
motor(stop, 0, 0);
delay(200);
}
// ========================================================
// THREE ATTEMPTS FAILED
// ========================================================
Serial.println();
Serial.println("==============================");
Serial.println("3 DIRECTIONS BLOCKED");
Serial.println("TURNING 180 DEGREES");
Serial.println("==============================");
// Reverse before 180-degree turn
motor(back, MAX_SPEED, MAX_SPEED);
delay(REVERSE_TIME);
// Stop
motor(stop, 0, 0);
delay(150);
// 180-degree turn
Serial.println("Turning 180 degrees...");
motor(right, MAX_SPEED, MAX_SPEED);
delay(TURN_180_TIME);
// Stop after turning
motor(stop, 0, 0);
delay(SENSOR_SETTLE_TIME);
// Start moving again
resetSpeed();
Serial.println("180 degree turn complete.");
motor(forward, START_SPEED, START_SPEED);
delay(ACCEL_DELAY);
}
Compiling and uploading the code
- Copy the whole code and paste in the editor environment
- Click the compile button. (When the code is done compiling, you will be notified)
- Click the upload button (When teh code is done uploading to the Arduino Nano, you will be notified)
See images of the Compile and Upload buttons below.

Once the code is uploaded, the car wheels will start moving automatically. At this point, unplug the robotic car from the phone, put the car on the floor and turn on the battery switch, the car will start moving on its own while avoiding obstacles.
Please note that the Android App used (ArduinoDroid) is a Freemium App, hence, if you want to remove ads from the app when using it, you can pay to purchase it, however, you can use the free version but with ads and limited use each time the app is opened.
Understanding the Important Parts of the Program
You don’t need to understand every line before uploading the program. However, understanding the major sections will help you modify the robot later and develop your own robotic projects.
1. Defining the Motor Directions
At the beginning of the program, we define four movement directions and a stop command:
#define stop 0
#define forward 1
#define back 2
#define left 3
#define right 4
These names make the rest of the program easier to understand.
Instead of writing complicated motor commands every time we want the robot to move, we can simply write:
motor(forward, 255, 255);
or:
motor(left, 255, 255);
2. Ultrasonic Sensor Connections
The HC-SR04 uses two signal pins.
The TRIG pin sends the ultrasonic pulse, while the ECHO pin receives the reflected signal.
In our robot:
#define Trig A4
#define Echo A5
Therefore:
- TRIG → A4
- ECHO → A5
The sensor is powered from the Arduino’s 5 V and GND connections.
3. Controlling the Motors
The L9110 motor driver allows the Arduino to control the direction and speed of the two motors.
The connections are:
| Arduino Nano | L9110 |
|---|---|
| D5 | A-1A |
| D6 | A-1B |
| D9 | B-1A |
| D10 | B-2B |
The Arduino uses PWM signals on these pins to control the motor speed.
For example:
motor(forward, MAX_SPEED, MAX_SPEED);
commands both motors to move the robot forward at maximum speed.
4. Starting Slowly and Accelerating
We don’t immediately start the robot at maximum speed.
The program starts at:
#define START_SPEED 130
and increases the speed rapidly using:
#define SPEED_STEP 30
#define ACCEL_DELAY 40
The robot therefore gets moving first and then rapidly reaches maximum speed.
The maximum PWM value is:
#define MAX_SPEED 255
This approach is useful because a small robotic vehicle can sometimes have difficulty starting from rest, particularly on rough surfaces.
5. Detecting an Obstacle
The obstacle detection distance is currently:
#define OBSTACLE_DISTANCE 30
This means the robot begins its obstacle avoidance procedure when an obstacle is detected approximately 30 cm in front of it.
We increased this from the original smaller value so that the car has more time to stop and maneuver.
6. Why We Take Multiple Ultrasonic Readings
One of the interesting challenges encountered while developing this robot was operation on a rough interlocking pavement surface.
The vibration of the car can affect ultrasonic measurements and occasionally produce an abnormal reading.
If the program reacted to every individual reading, the robot could mistakenly think there was an obstacle when the path was actually clear.
Therefore, the program takes several measurements and filters the results.
It also requires multiple consecutive obstacle confirmations before initiating an avoidance maneuver.
This makes the robot considerably more stable when operating on rough surfaces.
7. What Happens When an Obstacle Is Detected?
Once an obstacle is confirmed, the robot:
Obstacle detected
↓
STOP
↓
REVERSE briefly
↓
Randomly choose LEFT or RIGHT
↓
Turn approximately 90°
↓
Check the new direction
↓
Is the path clear?
↙ ↘
YES NO
↓ ↓
Forward Try again
The random direction is generated with:
direction = random(0, 2);
This gives the robot a choice between left and right.
8. Why Does the Robot Reverse Before Turning?
Before turning, the robot briefly reverses:
#define REVERSE_TIME 250
This gives the robot a little more space between itself and the obstacle before beginning the turn.
This is particularly useful when the obstacle is relatively close or when the robot is operating in a confined area.
9. What Happens When Three Directions Are Blocked?
The program allows three unsuccessful direction attempts.
If all three attempts fail, the robot assumes that continuing to search for another 90° direction is not useful.
It therefore performs an approximately 180° turn and continues in the opposite direction.
The 180° turn duration is controlled by:
#define TURN_180_TIME 900
The exact timing may need to be adjusted depending on the robot’s motors, battery voltage, wheel size and the surface on which it operates.
Testing the Robotic Car
After uploading the program, place the car on the floor with a clear path in front of it.
When powered on, the robot should:
Start → accelerate → reach full speed → continue forward.
When you place an object approximately 30 cm or less in front of it, the robot should:
Stop → reverse → turn left or right → check the new direction → continue if the path is clear.
If it encounters obstacles in several directions, it should eventually perform a 180° turn and continue navigating.
NB: If you place the robot and a very smooth surface, it might skid, a rough surface is most pre.erable
Adjusting the Robot
One of the advantages of using Arduino is that you can easily modify the program to change how the robot behaves.
Obstacle detection distance
Change:
#define OBSTACLE_DISTANCE 30
For example:
#define OBSTACLE_DISTANCE 40
will make the robot begin avoiding obstacles from farther away.
Starting speed
Change:
#define START_SPEED 130
If the robot struggles to start on your particular surface, you can increase this value.
90° turn
Change:
#define TURN_90_TIME 450
If the robot doesn’t turn far enough, increase the value. If it turns too far, reduce it.
180° turn
Change:
#define TURN_180_TIME 900
Again, this value needs to be calibrated for your particular robot.
Reverse time
The reverse distance is controlled by:
#define REVERSE_TIME 250
Increasing the value makes the robot reverse for a longer time before turning.
Troubleshooting
The Arduino Nano is not detected by the phone
Check:
- USB OTG is enabled/supported by the phone.
- The USB cable supports data.
- The Nano is receiving power.
- ArduinoDroid has USB permission.
- The correct Arduino Nano board is selected.
- The CH340-based Nano is properly connected.
The robot moves in the wrong direction
Check the motor connections and the motor direction definitions in the program.
If necessary, the motor control logic can be changed to reverse the direction of a motor.
The robot turns too much or too little
Adjust:
#define TURN_90_TIME 450
and:
#define TURN_180_TIME 900
The robot detects obstacles when there is nothing in front
Make sure the HC-SR04 is firmly mounted and facing forward.
Also check that the sensor is not vibrating excessively while the car is moving.
The program already uses multiple readings to reduce the effect of false ultrasonic readings.
The robot gets too close to obstacles
Increase:
#define OBSTACLE_DISTANCE 30
to 35 or 40 cm.
Programming the Robot with a Computer
The Ettronics Hightech Obstacle Avoidance Robotic Car can also be programmed using a computer.
In the next tutorial, we will demonstrate how to program the robotic car with a computer.
Conclusion
The Ettronics Hightech Obstacle Avoidance Robotic Car is more than just a small robot that moves around and avoids objects. It provides a practical way to learn how microcontrollers, ultrasonic sensors, motor drivers, PWM, programming logic, and autonomous decision-making work together.
In this tutorial, we programmed the robot using an Android phone and ArduinoDroid, eliminating the need for a computer.
The Arduino Nano receives distance information from the HC-SR04 ultrasonic sensor, processes the information, and controls the L9110 motor driver to determine how the robot should move.
By modifying a few parameters in the program, you can experiment with the robot’s speed, obstacle detection distance, turning angle, reverse time, and other behaviors.
This is also the foundation for more advanced robotic projects. Once you understand how this robot works, you can extend it with features such as Bluetooth control, line following, remote control, multiple operating modes, additional sensors, and more advanced autonomous navigation.
To learn practical electronics, Arduino, robotics and electronics circuit design, visit Ettronics.com.

