WiFi Controlled Robot Car using ESP8266 (NodeMCU)

Introduction

In this project, we will build a WiFi-controlled robotic car using the ESP8266 NodeMCU.
The robot creates its own WiFi network, and you can control it using a mobile browser — no internet required.

This is a perfect beginner-to-intermediate IoT + robotics project.


Components Required

  • ESP8266 NodeMCU
  • L298N Motor Driver Module
  • DC Motors (2 or 4)
  • Robot chassis + wheels
  • Battery (7.4V Li-ion or 2×18650)
  • Jumper wires
  • USB cable

Circuit Diagram (Connections Explained)

Motor Driver → Motors

  • OUT1 → Left Motor
  • OUT2 → Left Motor
  • OUT3 → Right Motor
  • OUT4 → Right Motor

Motor Driver → ESP8266

L298NESP8266
IN1D1
IN2D2
IN3D5
IN4D6
ENAD7
ENBD8

Power Connections (VERY IMPORTANT)

Correct Way:

  • Battery → L298N (12V input)
  • ESP8266 → USB power (recommended)
  • GND of ESP8266 ↔ GND of L298N

Wrong Way (Avoid this)

  • DO NOT power ESP8266 from L298N 5V
    –> Causes reset, WiFi issues, unstable behavior

Arduino IDE Setup

1. Install ESP8266 Board

Go to:

  • File → Preferences
  • Add this URL:
http://arduino.esp8266.com/stable/package_esp8266com_index.json

Then:

  • Tools → Board Manager
  • Install ESP8266 Boards by ESP8266 Community

2. Select Board

  • Tools → Board → NodeMCU 1.0 (ESP-12E Module)

Working Principle

  • ESP8266 creates a WiFi hotspot: RobotCar
  • Phone connects to it
  • A web page with buttons is served
  • Buttons send commands using AJAX (no page reload)
  • Motors move accordingly

Complete Code

#include <ESP8266WiFi.h>

// ===== MOTOR PINS =====
#define IN1 D1
#define IN2 D2
#define IN3 D5
#define IN4 D6

// ===== SPEED PINS (PWM) =====
#define ENA D7
#define ENB D8

int speedValue = 300; // 0 - 1023

// ===== WIFI =====
const char* ssid = "RobotCar";
const char* password = "12345678";

WiFiServer server(80);

// ===== MOTOR FUNCTIONS =====
void forward() {
  analogWrite(ENA, speedValue);
  analogWrite(ENB, speedValue);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void backward() {
  analogWrite(ENA, speedValue);
  analogWrite(ENB, speedValue);

  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

void left() {
  analogWrite(ENA, speedValue);
  analogWrite(ENB, speedValue);

  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void right() {
  analogWrite(ENA, speedValue);
  analogWrite(ENB, speedValue);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

void stopCar() {
  analogWrite(ENA, 0);
  analogWrite(ENB, 0);

  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}

// ===== SETUP =====
void setup() {
  Serial.begin(115200);

  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);

  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);

  analogWriteRange(1023);
  analogWriteFreq(1000);

  stopCar();

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);

  Serial.println("WiFi Started");
  Serial.println(WiFi.softAPIP());

  server.begin();
}

// ===== LOOP =====
void loop() {
  WiFiClient client = server.available();
  if (!client) return;

  String request = client.readStringUntil('\r');
  client.flush();

  // ===== MOVEMENT COMMANDS =====
  if (request.indexOf("GET /forward") != -1) forward();
  else if (request.indexOf("GET /backward") != -1) backward();
  else if (request.indexOf("GET /left") != -1) left();
  else if (request.indexOf("GET /right") != -1) right();
  else if (request.indexOf("GET /stop") != -1) stopCar();

  // ===== SPEED COMMANDS =====
  else if (request.indexOf("GET /speedlow") != -1) speedValue = 400;
  else if (request.indexOf("GET /speedmed") != -1) speedValue = 700;
  else if (request.indexOf("GET /speedhigh") != -1) speedValue = 1023;

  // ===== RESPONSE =====
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: close");
  client.println();

  client.println("<!DOCTYPE html><html><head>");
  client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");

  // ===== STYLE =====
  client.println("<style>");
  client.println("body { text-align:center; font-family: Arial; background:#111; color:white; }");
  client.println("button { width:100px; height:100px; font-size:18px; margin:10px; border-radius:50%; border:none; }");
  client.println(".f { background:green; }");
  client.println(".b { background:blue; }");
  client.println(".l { background:orange; }");
  client.println(".r { background:orange; }");
  client.println(".s { background:red; }");
  client.println(".sp { width:80px; height:50px; border-radius:10px; }");
  client.println("</style>");

  // ===== JAVASCRIPT =====
  client.println("<script>");
  client.println("function send(cmd){");
  client.println("var x=new XMLHttpRequest();");
  client.println("x.open('GET','/'+cmd,true);");
  client.println("x.send();");
  client.println("}");
  client.println("</script>");

  client.println("</head><body>");

  client.println("<h2>Robot Control</h2>");

  // ===== SPEED BUTTONS =====
  client.println("<p>Speed Control</p>");
  client.println("<button class='sp' onclick=\"send('speedlow')\">Slow</button>");
  client.println("<button class='sp' onclick=\"send('speedmed')\">Medium</button>");
  client.println("<button class='sp' onclick=\"send('speedhigh')\">Fast</button><br><br>");

  // ===== MOVEMENT BUTTONS =====
  client.println("<button class='f' onclick=\"send('forward')\">Forward</button><br>");
  client.println("<button class='l' onclick=\"send('left')\">Left</button>");
  client.println("<button class='s' onclick=\"send('stop')\">Stop</button>");
  client.println("<button class='r' onclick=\"send('right')\">Right</button><br>");
  client.println("<button class='b' onclick=\"send('backward')\">Backward</button>");

  client.println("</body></html>");

  client.stop();
}

How to Use

  1. Upload code
  2. Power ESP8266
  3. Open Settings in your mobile, then go to WiFi and select “RobotCar”. Then type password “12345678”:
RobotCar

4. Open browser and type:

192.168.4.1

5. Use buttons to control the robot

Important Tips

  • Turn OFF mobile data
  • Use Chrome browser
  • Keep ESP powered properly
  • Keep motors and ESP power separate

Troubleshooting Guide

1. WiFi not visible

Causes:

  • Code not uploaded properly
  • ESP not powered

Fix:

  • Press RESET button
  • Check Serial Monitor (115200 baud)

2. Connected but page not opening

Fix:

  • Turn OFF mobile data
  • Use:
192.168.4.1

(Do not put this in Google search)


3. Buttons work only once

Cause:

  • Page reload issue

Fix:

  • Already solved using AJAX in this code

4. Weird symbols on buttons

Cause:

  • Unicode not supported

Fix:

  • Use text (Forward, Left, etc.)

5. ESP8266 not detected

Causes:

  • Power issue
  • USB driver issue

Fix:

  • Use USB cable
  • Check drivers:
    • CP2102 USB to UART Bridge
    • CH340 USB to Serial

6. Robot not moving correctly

Fix:

  • Swap motor wires
  • Adjust logic in code

7. ESP resets when motor runs

Cause:

  • Voltage drop

Fix:

  • Use separate power supply
  • Use buck converter (LM2596 recommended)

Improvements You Can Add

  • Voice control
  • Obstacle avoidance
  • Camera streaming using ESP32-CAM

Conclusion

This project demonstrates how to build a simple yet powerful IoT robot using ESP8266.
With proper power management and AJAX-based control, you get a smooth and responsive robotic car.