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)

  • Battery → L298N (12V input)
  • Battery → L298N GND
  • GND of ESP8266 ↔ GND of L298N
  • VV of ESP8266 ↔ 5V Output of L298N

    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.