🚚
Autonomous Delivery Robot
A fun step-by-step Arduino project with circuit diagrams & code
📋 What We're Making
An autonomous delivery robot is a smart little car that can drive around all by itself, dodge obstacles, and (with upgrades) deliver small packages! We'll build it using an Arduino, a motor driver, and an ultrasonic "eye" sensor. Let's get rolling! 🎉
🧰 Parts You'll Need
🧠Arduino Uno R3
⚙️L298N Motor Driver
🔋2x DC Geared Motors + Wheels
🛞Caster Wheel
📡HC-SR04 Ultrasonic Sensor
👀2x IR Sensors (optional)
🔌12V Battery Pack
📦Robot Chassis
🔗Jumper Wires
📱HC-05 Bluetooth (optional)
🔋 Power Connections
- Battery (+) → L298N 12V input
- Battery (–) → L298N GND and Arduino GND (this shared ground is super important!)
- L298N 5V output → Arduino 5V (or power Arduino separately)
⚙️ L298N Motor Driver → Arduino
| L298N Pin | Arduino Pin |
|---|---|
| ENA | Pin 9 |
| IN1 | Pin 8 |
| IN2 | Pin 7 |
| ENB | Pin 10 |
| IN3 | Pin 6 |
| IN4 | Pin 5 |
| OUT1, OUT2 | Left Motor |
| OUT3, OUT4 | Right Motor |
| GND | Arduino GND |
📡 Ultrasonic Sensor (HC-SR04) → Arduino
| HC-SR04 Pin | Arduino Pin |
|---|---|
| VCC | 5V |
| GND | GND |
| TRIG | Pin 11 |
| ECHO | Pin 12 |
👀 IR Line Sensors (Optional)
| IR Sensor | Arduino Pin |
|---|---|
| Left IR OUT | A0 |
| Right IR OUT | A1 |
| VCC | 5V |
| GND | GND |
🛠️ Step-by-Step Build
Step 1: Build the Chassis 🏗️
Mount the two geared motors on either side of the chassis base. Attach the caster wheel at the front or back to keep your robot balanced.
Mount the two geared motors on either side of the chassis base. Attach the caster wheel at the front or back to keep your robot balanced.
Step 2: Mount the Motor Driver ⚙️
Fix the L298N module in the center so the wires to both motors are short and neat. Connect the motor wires to OUT1-OUT4.
Fix the L298N module in the center so the wires to both motors are short and neat. Connect the motor wires to OUT1-OUT4.
Step 3: Wire the Power System 🔋
Connect the battery pack to the L298N's 12V terminal. Make sure the battery, L298N, and Arduino all share the same GND — this is the #1 mistake beginners make!
Connect the battery pack to the L298N's 12V terminal. Make sure the battery, L298N, and Arduino all share the same GND — this is the #1 mistake beginners make!
Step 4: Connect the Arduino 🧠
Wire ENA, IN1, IN2, IN3, IN4, ENB from the L298N to the Arduino pins shown in the table above.
Wire ENA, IN1, IN2, IN3, IN4, ENB from the L298N to the Arduino pins shown in the table above.
Step 5: Mount the Sensor 📡
Place the HC-SR04 at the front of the robot, facing forward like a pair of eyes. Connect TRIG to pin 11 and ECHO to pin 12.
Place the HC-SR04 at the front of the robot, facing forward like a pair of eyes. Connect TRIG to pin 11 and ECHO to pin 12.
Step 6: Add IR Sensors (Optional) 👀
If you want line-following too, mount both IR sensors underneath the front, close to the ground, spaced apart to straddle a line.
If you want line-following too, mount both IR sensors underneath the front, close to the ground, spaced apart to straddle a line.
Step 7: Upload the Code 💻
Plug the Arduino into your computer with a USB cable and upload the code below using the Arduino IDE.
Plug the Arduino into your computer with a USB cable and upload the code below using the Arduino IDE.
Step 8: Test & Calibrate 🎯
Power on, check the motor directions first, then test obstacle detection, then let it roam!
Power on, check the motor directions first, then test obstacle detection, then let it roam!
Always double-check your wiring before turning on the power — loose wires are the #1 cause of "my robot won't move!"
💻 The Code
🚧 Obstacle-Avoiding Robot Code
// Autonomous Delivery Robot - Obstacle Avoidance
// Motor Driver Pins
#define ENA 9
#define IN1 8
#define IN2 7
#define ENB 10
#define IN3 6
#define IN4 5
// Ultrasonic Sensor Pins
#define TRIG 11
#define ECHO 12
long duration;
int distance;
void setup() {
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
Serial.begin(9600);
}
int getDistance() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
duration = pulseIn(ECHO, HIGH);
return duration * 0.034 / 2; // cm
}
void moveForward() {
analogWrite(ENA, 180);
analogWrite(ENB, 180);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
void moveBackward() {
analogWrite(ENA, 180);
analogWrite(ENB, 180);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
void turnRight() {
analogWrite(ENA, 180);
analogWrite(ENB, 180);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
void turnLeft() {
analogWrite(ENA, 180);
analogWrite(ENB, 180);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
void stopMotors() {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
}
void loop() {
distance = getDistance();
Serial.println(distance);
if (distance > 20) {
moveForward();
} else {
stopMotors();
delay(200);
moveBackward();
delay(300);
stopMotors();
delay(200);
turnRight();
delay(400);
stopMotors();
delay(200);
}
delay(50);
}
🛣️ Line-Following Code (Extra Feature)
#define leftIR A0
#define rightIR A1
void loop() {
int leftVal = digitalRead(leftIR);
int rightVal = digitalRead(rightIR);
if (leftVal == LOW && rightVal == LOW) {
moveForward();
} else if (leftVal == HIGH && rightVal == LOW) {
turnLeft();
} else if (leftVal == LOW && rightVal == HIGH) {
turnRight();
} else {
stopMotors();
}
}
Copy the "helper" functions (moveForward, turnLeft, etc.) from the first code block into this one if you want to use both together!
🧪 Testing & Troubleshooting
🎯 Calibration Tips
- Motors spinning the wrong way? Swap the two wires for that motor on the L298N output.
- Robot bumping into things too late or too early? Change the
distance > 20number in the code. - Robot too fast or too slow? Adjust the speed number (180) — lower for gentle, higher for zippy!
- IR sensors acting weird? Turn the little screw (potentiometer) on the sensor to calibrate.
🔧 Quick Fix Table
| Problem 😟 | Solution 😊 |
|---|---|
| Robot doesn't move at all | Check that battery, L298N, and Arduino all share the same GND |
| Robot moves crazily / randomly | Look for loose jumper wires and make sure power is steady |
| Ultrasonic gives weird numbers | Make sure the sensor is mounted flat, not tilted |
| Motors are weak or slow | Battery might be low — recharge or check connections |
🌟 Bonus: Super Robot Mode (3-in-1!)
Want your robot to do everything? This advanced code combines Manual Control (Bluetooth + IR Remote), Line Following, and Obstacle Avoiding with a Servo "Head" — all in one sketch! Switch modes using your phone app or remote control. 🎮📱
🧩 Extra Parts Needed
📶HC-05 Bluetooth Module
📺IR Remote + Receiver
🦾Servo Motor (sensor head)
🔌 Extra Wiring
| Component | Arduino Pin |
|---|---|
| HC-05 RX | Pin 2 (via SoftwareSerial) |
| HC-05 TX | Pin 3 |
| IR Receiver | A5 |
| Servo (sensor head) | A4 |
| Right IR Sensor | A0 |
| Left IR Sensor | A1 |
| Ultrasonic Echo | A2 |
| Ultrasonic Trigger | A3 |
| ENA / IN1 / IN2 / IN3 / IN4 / ENB | 10 / 9 / 8 / 7 / 6 / 5 |
You'll need the
IRremote library installed in your Arduino IDE (Sketch → Include Library → Manage Libraries → search "IRremote").🎮 Mode Controls
- 8 = Manual Mode (drive with buttons/remote)
- 9 = Auto Line Follower Mode 🛣️
- 10 = Auto Obstacle Avoider Mode 🚧 (with scanning servo head!)
- 1-5 = Forward, Backward, Left, Right, Stop
#include <SoftwareSerial.h>
SoftwareSerial BT_Serial(2, 3); // RX, TX
#include <IRremote.h>
const int RECV_PIN = A5;
IRrecv irrecv(RECV_PIN);
decode_results results;
#define enA 10//Enable1 L298 Pin enA
#define in1 9 //Motor1 L298 Pin in1
#define in2 8 //Motor1 L298 Pin in1
#define in3 7 //Motor2 L298 Pin in1
#define in4 6 //Motor2 L298 Pin in1
#define enB 5 //Enable2 L298 Pin enB
#define servo A4
#define R_S A0 //ir sensor Right
#define L_S A1 //ir sensor Left
#define echo A2 //Echo pin
#define trigger A3 //Trigger pin
int distance_L, distance_F = 30, distance_R;
long distance;
int set = 20;
int bt_ir_data; // variable to receive data from the serial port and IRremote
int Speed = 130;
int mode=0;
int IR_data;
void setup(){ // put your setup code here, to run once
pinMode(R_S, INPUT); // declare if sensor as input
pinMode(L_S, INPUT); // declare ir sensor as input
pinMode(echo, INPUT );// declare ultrasonic sensor Echo pin as input
pinMode(trigger, OUTPUT); // declare ultrasonic sensor Trigger pin as Output
pinMode(enA, OUTPUT); // declare as output for L298 Pin enA
pinMode(in1, OUTPUT); // declare as output for L298 Pin in1
pinMode(in2, OUTPUT); // declare as output for L298 Pin in2
pinMode(in3, OUTPUT); // declare as output for L298 Pin in3
pinMode(in4, OUTPUT); // declare as output for L298 Pin in4
pinMode(enB, OUTPUT); // declare as output for L298 Pin enB
irrecv.enableIRIn(); // Start the receiver
irrecv.blink13(true);
Serial.begin(9600); // start serial communication at 9600bps
BT_Serial.begin(9600);
pinMode(servo, OUTPUT);
for (int angle = 70; angle <= 140; angle += 5) {
servoPulse(servo, angle); }
for (int angle = 140; angle >= 0; angle -= 5) {
servoPulse(servo, angle); }
for (int angle = 0; angle <= 70; angle += 5) {
servoPulse(servo, angle); }
delay(500);
}
void loop(){
if(BT_Serial.available() > 0){ //if some date is sent, reads it and saves in state
bt_ir_data = BT_Serial.read();
Serial.println(bt_ir_data);
if(bt_ir_data > 20){Speed = bt_ir_data;}
}
if (irrecv.decode(&results)) {
Serial.println(results.value,HEX);
bt_ir_data = IRremote_data();
Serial.println(bt_ir_data);
irrecv.resume(); // Receive the next value
delay(100);
}
if(bt_ir_data == 8){mode=0; Stop();} //Manual Android Application and IR Remote Control Command
else if(bt_ir_data == 9){mode=1; Speed=130;} //Auto Line Follower Command
else if(bt_ir_data ==10){mode=2; Speed=255;} //Auto Obstacle Avoiding Command
analogWrite(enA, Speed); // Write The Duty Cycle 0 to 255 Enable Pin A for Motor1 Speed
analogWrite(enB, Speed); // Write The Duty Cycle 0 to 255 Enable Pin B for Motor2 Speed
if(mode==0){
//===============================================================================
// Key Control Command
//===============================================================================
if(bt_ir_data == 1){forword(); } // if the bt_data is '1' the DC motor will go forward
else if(bt_ir_data == 2){backword();} // if the bt_data is '2' the motor will Reverse
else if(bt_ir_data == 3){turnLeft();} // if the bt_data is '3' the motor will turn left
else if(bt_ir_data == 4){turnRight();} // if the bt_data is '4' the motor will turn right
else if(bt_ir_data == 5){Stop(); } // if the bt_data '5' the motor will Stop
//===============================================================================
// Voice Control Command
//===============================================================================
else if(bt_ir_data == 6){turnLeft(); delay(400); bt_ir_data = 5;}
else if(bt_ir_data == 7){turnRight(); delay(400); bt_ir_data = 5;}
}
if(mode==1){
//===============================================================================
// Line Follower Control
//===============================================================================
if((digitalRead(R_S) == 0)&&(digitalRead(L_S) == 0)){forword();} //if Right Sensor and Left Sensor are at White color then it will call forword function
if((digitalRead(R_S) == 1)&&(digitalRead(L_S) == 0)){turnRight();}//if Right Sensor is Black and Left Sensor is White then it will call turn Right function
if((digitalRead(R_S) == 0)&&(digitalRead(L_S) == 1)){turnLeft();} //if Right Sensor is White and Left Sensor is Black then it will call turn Left function
if((digitalRead(R_S) == 1)&&(digitalRead(L_S) == 1)){Stop();} //if Right Sensor and Left Sensor are at Black color then it will call Stop function
}
if(mode==2){
//===============================================================================
// Obstacle Avoiding Control
//===============================================================================
distance_F = Ultrasonic_read();
Serial.print("S=");Serial.println(distance_F);
if (distance_F > set){forword();}
else{Check_side();}
}
delay(10);
}
long IRremote_data(){
if(results.value==0xFF02FD){IR_data=1;}
else if(results.value==0xFF9867){IR_data=2;}
else if(results.value==0xFFE01F){IR_data=3;}
else if(results.value==0xFF906F){IR_data=4;}
else if(results.value==0xFF629D || results.value==0xFFA857){IR_data=5;}
else if(results.value==0xFF30CF){IR_data=8;}
else if(results.value==0xFF18E7){IR_data=9;}
else if(results.value==0xFF7A85){IR_data=10;}
return IR_data;
}
void servoPulse (int pin, int angle){
int pwm = (angle*11) + 500; // Convert angle to microseconds
digitalWrite(pin, HIGH);
delayMicroseconds(pwm);
digitalWrite(pin, LOW);
delay(50); // Refresh cycle of servo
}
//**********************Ultrasonic_read****************************
long Ultrasonic_read(){
digitalWrite(trigger, LOW);
delayMicroseconds(2);
digitalWrite(trigger, HIGH);
delayMicroseconds(10);
distance = pulseIn (echo, HIGH);
return distance / 29 / 2;
}
void compareDistance(){
if (distance_L > distance_R){
turnLeft();
delay(350);
}
else if (distance_R > distance_L){
turnRight();
delay(350);
}
else{
backword();
delay(300);
turnRight();
delay(600);
}
}
void Check_side(){
Stop();
delay(100);
for (int angle = 70; angle <= 140; angle += 5) {
servoPulse(servo, angle); }
delay(300);
distance_L = Ultrasonic_read();
delay(100);
for (int angle = 140; angle >= 0; angle -= 5) {
servoPulse(servo, angle); }
delay(500);
distance_R = Ultrasonic_read();
delay(100);
for (int angle = 0; angle <= 70; angle += 5) {
servoPulse(servo, angle); }
delay(300);
compareDistance();
}
void forword(){ //forword
digitalWrite(in1, HIGH); //Right Motor forword Pin
digitalWrite(in2, LOW); //Right Motor backword Pin
digitalWrite(in3, LOW); //Left Motor backword Pin
digitalWrite(in4, HIGH); //Left Motor forword Pin
}
void backword(){ //backword
digitalWrite(in1, LOW); //Right Motor forword Pin
digitalWrite(in2, HIGH); //Right Motor backword Pin
digitalWrite(in3, HIGH); //Left Motor backword Pin
digitalWrite(in4, LOW); //Left Motor forword Pin
}
void turnRight(){ //turnRight
digitalWrite(in1, LOW); //Right Motor forword Pin
digitalWrite(in2, HIGH); //Right Motor backword Pin
digitalWrite(in3, LOW); //Left Motor backword Pin
digitalWrite(in4, HIGH); //Left Motor forword Pin
}
void turnLeft(){ //turnLeft
digitalWrite(in1, HIGH); //Right Motor forword Pin
digitalWrite(in2, LOW); //Right Motor backword Pin
digitalWrite(in3, HIGH); //Left Motor backword Pin
digitalWrite(in4, LOW); //Left Motor forword Pin
}
void Stop(){ //stop
digitalWrite(in1, LOW); //Right Motor forword Pin
digitalWrite(in2, LOW); //Right Motor backword Pin
digitalWrite(in3, LOW); //Left Motor backword Pin
digitalWrite(in4, LOW); //Left Motor forword Pin
}
This code uses pins 5-10 for motors instead of 5-10 from before, and moves sensors to analog pins (A0-A5) to make room for the servo and IR receiver. Update your wiring to match this new table!
🚀 Cool Upgrades to Try
- 🛰️ Add a GPS module (NEO-6M) so it can navigate outdoors to a delivery spot
- 📶 Swap to an ESP32 board for Wi-Fi tracking and a control dashboard on your phone
- 🔄 Mount the ultrasonic sensor on a servo so it can scan side-to-side like a head
- 📷 Add an ESP32-CAM so the robot can "see" and recognize objects
- 📦 Build a little tray or door that opens to drop off the "package"
Comments
Post a Comment