>include <WiFi.
h#
>include <WebServer.h#
>include <HCSR04.h#
>include <Wire.h#
include <MPU6050_tockn.h> // Library for IMU if you want to use it #
Motor Driver Pins //
const int IN1 = 26; // Right motor direction
const int IN2 = 25; // Right motor direction
const int ENA = 27; // Right motor speed control
const int IN3 = 12; // Left motor direction
const int IN4 = 13; // Left motor direction
const int ENB = 14; // Left motor speed control
Ultrasonic Sensor //
;UltraSonicDistanceSensor centerSensor(19, 18)
WiFi credentials //
;"const char* ssid = "Mohamed_72_EXT
;"const char* password = "12344321
MPU6050 IMU //
;MPU6050 mpu6050(Wire)
;float yaw = 0
Web Server //
;WebServer server(80)
Path and Robot state //
define MAX_PATH_POINTS 100#
{ struct Point
;int x
;int y
;}
;Point pathPoints[MAX_PATH_POINTS]
;int pathLength = 0
;int currentPathIndex = 0
;bool isRobotRunning = false
;bool obstacleDetected = false
Robot parameters //
const int robotWidth = 200; // mm
const int wheelDiameter = 65; // mm
const float wheelBase = 170; // mm (distance between wheels)
const int obstacleThreshold = 200; // mm
const int baseSpeed = 150; // 0-255 for PWM
const int turnSpeed = 120; // 0-255 for PWM
Movement functions //
{ )(void stopMotors
;digitalWrite(IN1, LOW)
;digitalWrite(IN2, LOW)
;analogWrite(ENA, 0)
;digitalWrite(IN3, LOW)
;digitalWrite(IN4, LOW)
;analogWrite(ENB, 0)
{ void moveForward(int speed)
;digitalWrite(IN1, HIGH)
;digitalWrite(IN2, LOW)
;analogWrite(ENA, speed)
;digitalWrite(IN3, HIGH)
;digitalWrite(IN4, LOW)
;analogWrite(ENB, speed)
{ void moveBackward(int speed)
;digitalWrite(IN1, LOW)
;digitalWrite(IN2, HIGH)
;analogWrite(ENA, speed)
;digitalWrite(IN3, LOW)
;digitalWrite(IN4, HIGH)
;analogWrite(ENB, speed)
{ void turnLeft(int speed)
;digitalWrite(IN1, HIGH)
;digitalWrite(IN2, LOW)
;analogWrite(ENA, speed)
;digitalWrite(IN3, LOW)
;digitalWrite(IN4, HIGH)
;analogWrite(ENB, speed)
{ void turnRight(int speed)
;digitalWrite(IN1, LOW)
;digitalWrite(IN2, HIGH)
;analogWrite(ENA, speed)
;digitalWrite(IN3, HIGH)
;digitalWrite(IN4, LOW)
;analogWrite(ENB, speed)
{ )(void setup
;Serial.begin(115200)
Initialize motor control pins //
;pinMode(IN1, OUTPUT)
;pinMode(IN2, OUTPUT)
;pinMode(ENA, OUTPUT)
;pinMode(IN3, OUTPUT)
;pinMode(IN4, OUTPUT)
;pinMode(ENB, OUTPUT)
;)(stopMotors
Initialize IMU //
;)(Wire.begin
;)(mpu6050.begin
;mpu6050.calcGyroOffsets(true)
Connect to WiFi //
;WiFi.begin(ssid, password)
;Serial.print("Connecting to WiFi")
{ while (WiFi.status() != WL_CONNECTED)
;delay(500)
;)"."(Serial.print
}
;)(Serial.println
;Serial.print("Connected to WiFi. IP Address: ")
;Serial.println(WiFi.localIP())
Configure web server routes //
;server.on("/", handleRoot)
;server.on("/drawPath", HTTP_POST, handleDrawPath)
;server.on("/startRobot", HTTP_GET, handleStartRobot)
;server.on("/stopRobot", HTTP_GET, handleStopRobot)
;server.on("/resetPath", HTTP_GET, handleResetPath)
;server.on("/status", HTTP_GET, handleStatus)
Start the server //
;)(server.begin
;Serial.println("HTTP server started")
{ )(void loop
;)(server.handleClient
Update IMU data //
;)(mpu6050.update
;)(yaw = mpu6050.getAngleZ
Check for obstacles //
;)(float distance = centerSensor.measureDistanceCm
{ if (distance > 0 && distance < obstacleThreshold)
;obstacleDetected = true
;)(stopMotors
;Serial.println("Obstacle detected!")
{ else }
;obstacleDetected = false
Execute path following if robot is running //
{ if (isRobotRunning && !obstacleDetected && pathLength > 0)
;)(followPath
delay(50); // Small delay to avoid flooding the loop
{ )(void followPath
{ if (currentPathIndex >= pathLength)
End of path reached //
;)(stopMotors
;isRobotRunning = false
;return
Calculate relative position to next point //
In a real implementation, you would need position tracking //
This is a simplified version assuming we know our position //
Get current target point //
;Point targetPoint = pathPoints[currentPathIndex]
:For real implementation, you would //
Calculate angle to target point .1 //
Rotate robot to face target .2 //
Move forward until reaching target .3 //
Proceed to next point .4 //
For demonstration, we'll just increment the path index //
and make some movement as if following a path //
;static unsigned long lastMoveTime = 0
;static int moveState = 0
Simple state machine for demonstration //
if (millis() - lastMoveTime > 1000) { // Change state every second
;moveState = (moveState + 1) % 4
;)(lastMoveTime = millis
{ switch (moveState)
:case 0
;moveForward(baseSpeed)
;break
:case 1
;turnRight(turnSpeed)
;break
:case 2
;moveForward(baseSpeed)
;break
:case 3
Move to next point in path //
;++currentPathIndex
{ if (currentPathIndex >= pathLength)
;)(stopMotors
;isRobotRunning = false
;break
}
}
}
Web Server Handlers //
{ )(void handleRoot
("String html = R
>DOCTYPE html!<
>html<
>head<
>title>ESP32 Robot Control</title<
>"meta name="viewport" content="width=device-width, initial-scale=1 <
>style<
body { font-family: Arial, sans-serif; text-align: center; }
canvas-container { margin: 20px auto; position: relative; }#
canvas { border: 1px solid #000; }
{ btn.
;padding: 10px 20px
;margin: 5px
;font-size: 16px
;background-color: #4CAF50
;color: white
;border: none
;border-radius: 4px
;cursor: pointer
btn:hover { background-color: #45a049; }.
btn-danger { background-color: #f44336; }.
btn-danger:hover { background-color: #d32f2f; }.
btn-warning { background-color: #ff9800; }.
btn-warning:hover { background-color: #ed8c00; }.
status { margin: 15px; font-weight: bold; }#
>style/<
>head/<
>body<
>h1>ESP32 Path Following Robot</h1<
>"div id="canvas-container<
>canvas id="drawingCanvas" width="600" height="400"></canvas <
>div/<
>div<
>button class="btn" id="clearBtn">Clear Path</button <
>button class="btn" id="saveBtn">Save Path</button <
>button class="btn" id="startBtn">Start Robot</button <
>button class="btn btn-danger" id="stopBtn">Stop Robot</button <
>div/<
>div id="status">Robot Status: Ready</div<
>script<
;const canvas = document.getElementById('drawingCanvas')
;const ctx = canvas.getContext('2d')
;const statusDiv = document.getElementById('status')
;let isDrawing = false
;][ = let pathPoints
Initialize canvas //
;'ctx.fillStyle = '#ffffff
;ctx.fillRect(0, 0, canvas.width, canvas.height)
Setup event listeners //
;canvas.addEventListener('mousedown', startDrawing)
;canvas.addEventListener('mousemove', draw)
;canvas.addEventListener('mouseup', stopDrawing)
;canvas.addEventListener('mouseout', stopDrawing)
Touch support //
;canvas.addEventListener('touchstart', handleTouch)
;canvas.addEventListener('touchmove', handleTouch)
;canvas.addEventListener('touchend', stopDrawing)
;document.getElementById('clearBtn').addEventListener('click', clearCanvas)
;document.getElementById('saveBtn').addEventListener('click', savePath)
;document.getElementById('startBtn').addEventListener('click', startRobot)
;document.getElementById('stopBtn').addEventListener('click', stopRobot)
Setup status polling //
;setInterval(updateStatus, 2000)
{ function startDrawing(e)
;isDrawing = true
;draw(e)
{ function draw(e)
;if (!isDrawing) return
;)(const rect = canvas.getBoundingClientRect
;const x = e.clientX - rect.left
;const y = e.clientY - rect.top
;ctx.lineWidth = 5
;'ctx.lineCap = 'round
;'ctx.strokeStyle = '#3498db
{ if (pathPoints.length === 0)
Start a new path //
;pathPoints.push({x: Math.round(x), y: Math.round(y)})
;)(ctx.beginPath
;ctx.arc(x, y, 4, 0, 2 * Math.PI)
;'ctx.fillStyle = '#e74c3c
;)(ctx.fill
{ else }
Continue the path //
;const lastPoint = pathPoints[pathPoints.length - 1]
;)(ctx.beginPath
;ctx.moveTo(lastPoint.x, lastPoint.y)
;ctx.lineTo(x, y)
;)(ctx.stroke
;pathPoints.push({x: Math.round(x), y: Math.round(y)})
}
}
{ function handleTouch(e)
;)(e.preventDefault
{ if (e.type === 'touchstart')
;isDrawing = true
;if (!isDrawing) return
;const touch = e.touches[0]
;)(const rect = canvas.getBoundingClientRect
;const x = touch.clientX - rect.left
;const y = touch.clientY - rect.top
{ ,'const mouseEvent = new MouseEvent('mousemove
,clientX: touch.clientX
clientY: touch.clientY
;)}
;draw(mouseEvent)
{ )(function stopDrawing
;isDrawing = false
{ )(function clearCanvas
;'ctx.fillStyle = '#ffffff
;ctx.fillRect(0, 0, canvas.width, canvas.height)
;][ = pathPoints
;fetch('/resetPath')
;'statusDiv.textContent = 'Robot Status: Path cleared
{ )(function savePath
{ if (pathPoints.length === 0)
;alert('Please draw a path first!')
;return
}
Sample points to reduce data (take every 5th point) //
;][ = const sampledPoints
{ for (let i = 0; i < pathPoints.length; i += 5)
;sampledPoints.push(pathPoints[i])
{ if (!sampledPoints.includes(pathPoints[pathPoints.length - 1]))
;sampledPoints.push(pathPoints[pathPoints.length - 1])
{ ,'fetch('/drawPath
,'method: 'POST
{ :headers
,'Content-Type': 'application/json'
,}
body: JSON.stringify({ points: sampledPoints })
)}
then(response => response.text()).
{ >= then(data.
;'statusDiv.textContent = 'Robot Status: Path saved
)}
{ >= catch(error.
;statusDiv.textContent = 'Error: ' + error
;)}
}
{ )(function startRobot
fetch('/startRobot')
then(response => response.text()).
{ >= then(data.
;'statusDiv.textContent = 'Robot Status: Running
;)}
}
{ )(function stopRobot
fetch('/stopRobot')
then(response => response.text()).
{ >= then(data.
;'statusDiv.textContent = 'Robot Status: Stopped
;)}
}
{ )(function updateStatus
fetch('/status')
then(response => response.json()).
{ >= then(data.
;' :let statusText = 'Robot Status
{ if (data.obstacleDetected)
;' !statusText += 'Obstacle Detected
{ if (data.isRunning)
;'statusText += 'Running - Following path
{ if (data.pathProgress > 0)
;`statusText += ` (${data.pathProgress}%)
{ else }
;'statusText += 'Ready
;statusDiv.textContent = statusText
)}
{ >= catch(error.
;console.error('Error updating status:', error)
;)}
}
>script/<
>body/<
>html/<
;")
;server.send(200, "text/html", html)
{ )(void handleDrawPath
{ if (server.hasArg("plain"))
;String json = server.arg("plain")
Clear previous path //
;pathLength = 0
Parse JSON and extract path points //
This is a simplified parser. In production code, use ArduinoJson library //
;)"["(int startPos = json.indexOf
;)"]"(int endPos = json.lastIndexOf
{ if (startPos != -1 && endPos != -1)
;String pointsStr = json.substring(startPos + 1, endPos)
Extract each point //
;int currentPos = 0
{ while (currentPos < pointsStr.length() && pathLength < MAX_PATH_POINTS)
;int xStart = pointsStr.indexOf("\"x\":", currentPos)
;int xEnd = pointsStr.indexOf(",", xStart)
;int yStart = pointsStr.indexOf("\"y\":", currentPos)
;int yEnd = pointsStr.indexOf("}", yStart)
{ if (xStart == -1 || xEnd == -1 || yStart == -1 || yEnd == -1)
;break
;)(int x = pointsStr.substring(xStart + 4, xEnd).toInt
;)(int y = pointsStr.substring(yStart + 4, yEnd).toInt
;pathPoints[pathLength].x = x
;pathPoints[pathLength].y = y
;++pathLength
;currentPos = yEnd + 1
;server.send(200, "text/plain", "Path received with " + String(pathLength) + " points")
;currentPathIndex = 0
;Serial.println("New path received with " + String(pathLength) + " points")
{ else }
;server.send(400, "text/plain", "Invalid JSON format")
{ else }
;server.send(400, "text/plain", "No data received")
}
}
{ )(void handleStartRobot
{ if (pathLength > 0)
;isRobotRunning = true
;currentPathIndex = 0
;server.send(200, "text/plain", "Robot started")
;Serial.println("Robot started")
{ else }
;server.send(400, "text/plain", "No path available")
;Serial.println("Cannot start: No path available")
}
}
{ )(void handleStopRobot
;isRobotRunning = false
;)(stopMotors
;server.send(200, "text/plain", "Robot stopped")
;Serial.println("Robot stopped")
{ )(void handleResetPath
;pathLength = 0
;currentPathIndex = 0
;isRobotRunning = false
;)(stopMotors
;server.send(200, "text/plain", "Path reset")
;Serial.println("Path reset")
{ )(void handleStatus
;"{" = String status
;"," + status += "\"isRunning\":" + String(isRobotRunning ? "true" : "false")
;"," + status += "\"obstacleDetected\":" + String(obstacleDetected ? "true" : "false")
Calculate path progress as percentage //
;int progress = 0
{ if (pathLength > 0 && currentPathIndex > 0)
;progress = (currentPathIndex * 100) / pathLength
;"," + status += "\"pathProgress\":" + String(progress)
Distance sensor reading //
;)(float distance = centerSensor.measureDistanceCm
;"," + status += "\"distance\":" + String(distance)
IMU orientation //
;status += "\"yaw\":" + String(yaw)
;"}" =+ status
;server.send(200, "application/json", status)