>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
;[Link](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 //
;)([Link]
;)([Link]
;[Link](true)
Connect to WiFi //
;[Link](ssid, password)
;[Link]("Connecting to WiFi")
{ while ([Link]() != WL_CONNECTED)
;delay(500)
;)"."([Link]
}
;)([Link]
;[Link]("Connected to WiFi. IP Address: ")
;[Link]([Link]())
Configure web server routes //
;[Link]("/", handleRoot)
;[Link]("/drawPath", HTTP_POST, handleDrawPath)
;[Link]("/startRobot", HTTP_GET, handleStartRobot)
;[Link]("/stopRobot", HTTP_GET, handleStopRobot)
;[Link]("/resetPath", HTTP_GET, handleResetPath)
;[Link]("/status", HTTP_GET, handleStatus)
Start the server //
;)([Link]
;[Link]("HTTP server started")
{ )(void loop
;)([Link]
Update IMU data //
;)([Link]
;)(yaw = [Link]
Check for obstacles //
;)(float distance = [Link]
{ if (distance > 0 && distance < obstacleThreshold)
;obstacleDetected = true
;)(stopMotors
;[Link]("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 = [Link]('drawingCanvas')
;const ctx = [Link]('2d')
;const statusDiv = [Link]('status')
;let isDrawing = false
;][ = let pathPoints
Initialize canvas //
;'[Link] = '#ffffff
;[Link](0, 0, [Link], [Link])
Setup event listeners //
;[Link]('mousedown', startDrawing)
;[Link]('mousemove', draw)
;[Link]('mouseup', stopDrawing)
;[Link]('mouseout', stopDrawing)
Touch support //
;[Link]('touchstart', handleTouch)
;[Link]('touchmove', handleTouch)
;[Link]('touchend', stopDrawing)
;[Link]('clearBtn').addEventListener('click', clearCanvas)
;[Link]('saveBtn').addEventListener('click', savePath)
;[Link]('startBtn').addEventListener('click', startRobot)
;[Link]('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 = [Link]
;const x = [Link] - [Link]
;const y = [Link] - [Link]
;[Link] = 5
;'[Link] = 'round
;'[Link] = '#3498db
{ if ([Link] === 0)
Start a new path //
;[Link]({x: [Link](x), y: [Link](y)})
;)([Link]
;[Link](x, y, 4, 0, 2 * [Link])
;'[Link] = '#e74c3c
;)([Link]
{ else }
Continue the path //
;const lastPoint = pathPoints[[Link] - 1]
;)([Link]
;[Link](lastPoint.x, lastPoint.y)
;[Link](x, y)
;)([Link]
;[Link]({x: [Link](x), y: [Link](y)})
}
}
{ function handleTouch(e)
;)([Link]
{ if ([Link] === 'touchstart')
;isDrawing = true
;if (!isDrawing) return
;const touch = [Link][0]
;)(const rect = [Link]
;const x = [Link] - [Link]
;const y = [Link] - [Link]
{ ,'const mouseEvent = new MouseEvent('mousemove
,clientX: [Link]
clientY: [Link]
;)}
;draw(mouseEvent)
{ )(function stopDrawing
;isDrawing = false
{ )(function clearCanvas
;'[Link] = '#ffffff
;[Link](0, 0, [Link], [Link])
;][ = pathPoints
;fetch('/resetPath')
;'[Link] = 'Robot Status: Path cleared
{ )(function savePath
{ if ([Link] === 0)
;alert('Please draw a path first!')
;return
}
Sample points to reduce data (take every 5th point) //
;][ = const sampledPoints
{ for (let i = 0; i < [Link]; i += 5)
;[Link](pathPoints[i])
{ if ()
;[Link](pathPoints[[Link] - 1])
{ ,'fetch('/drawPath
,'method: 'POST
{ :headers
,'Content-Type': 'application/json'
,}
body: [Link]({ points: sampledPoints })
)}
then(response => [Link]()).
{ >= then(data.
;'[Link] = 'Robot Status: Path saved
)}
{ >= catch(error.
;[Link] = 'Error: ' + error
;)}
}
{ )(function startRobot
fetch('/startRobot')
then(response => [Link]()).
{ >= then(data.
;'[Link] = 'Robot Status: Running
;)}
}
{ )(function stopRobot
fetch('/stopRobot')
then(response => [Link]()).
{ >= then(data.
;'[Link] = 'Robot Status: Stopped
;)}
}
{ )(function updateStatus
fetch('/status')
then(response => [Link]()).
{ >= then(data.
;' :let statusText = 'Robot Status
{ if ([Link])
;' !statusText += 'Obstacle Detected
{ if ([Link])
;'statusText += 'Running - Following path
{ if ([Link] > 0)
;`statusText += ` (${[Link]}%)
{ else }
;'statusText += 'Ready
;[Link] = statusText
)}
{ >= catch(error.
;[Link]('Error updating status:', error)
;)}
}
>script/<
>body/<
>html/<
;")
;[Link](200, "text/html", html)
{ )(void handleDrawPath
{ if ([Link]("plain"))
;String json = [Link]("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 = [Link]
;)"]"(int endPos = [Link]
{ if (startPos != -1 && endPos != -1)
;String pointsStr = [Link](startPos + 1, endPos)
Extract each point //
;int currentPos = 0
{ while (currentPos < [Link]() && pathLength < MAX_PATH_POINTS)
;int xStart = [Link]("\"x\":", currentPos)
;int xEnd = [Link](",", xStart)
;int yStart = [Link]("\"y\":", currentPos)
;int yEnd = [Link]("}", yStart)
{ if (xStart == -1 || xEnd == -1 || yStart == -1 || yEnd == -1)
;break
;)(int x = [Link](xStart + 4, xEnd).toInt
;)(int y = [Link](yStart + 4, yEnd).toInt
;pathPoints[pathLength].x = x
;pathPoints[pathLength].y = y
;++pathLength
;currentPos = yEnd + 1
;[Link](200, "text/plain", "Path received with " + String(pathLength) + " points")
;currentPathIndex = 0
;[Link]("New path received with " + String(pathLength) + " points")
{ else }
;[Link](400, "text/plain", "Invalid JSON format")
{ else }
;[Link](400, "text/plain", "No data received")
}
}
{ )(void handleStartRobot
{ if (pathLength > 0)
;isRobotRunning = true
;currentPathIndex = 0
;[Link](200, "text/plain", "Robot started")
;[Link]("Robot started")
{ else }
;[Link](400, "text/plain", "No path available")
;[Link]("Cannot start: No path available")
}
}
{ )(void handleStopRobot
;isRobotRunning = false
;)(stopMotors
;[Link](200, "text/plain", "Robot stopped")
;[Link]("Robot stopped")
{ )(void handleResetPath
;pathLength = 0
;currentPathIndex = 0
;isRobotRunning = false
;)(stopMotors
;[Link](200, "text/plain", "Path reset")
;[Link]("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 = [Link]
;"," + status += "\"distance\":" + String(distance)
IMU orientation //
;status += "\"yaw\":" + String(yaw)
;"}" =+ status
;[Link](200, "application/json", status)