I'm excited to share my latest robotics project: an ESP8266 NodeMCU-based Smart Line Following and Obstacle Avoidance Robot. This autonomous robot combines two essential robotic navigation techniques—line tracking and real-time obstacle avoidance—into a single intelligent platform.
The robot uses dual IR sensors to detect and follow a predefined black line with smooth directional corrections, while an HC-SR04 ultrasonic sensor mounted on an SG90 servo continuously scans the environment for obstacles. When an obstacle is detected, the robot automatically stops, scans left and right to find the safest path, performs an avoidance maneuver, and then resumes its line-following operation without human intervention.
🔧 Hardware Used
ESP8266 NodeMCU (Main Controller)
L298N Dual H-Bridge Motor Driver
2 × BO DC Geared Motors
2 × IR Line Tracking Sensors
HC-SR04 Ultrasonic Distance Sensor
SG90 Servo Motor
Robot Chassis & Battery Pack
⚙️ Key Features
✅ Autonomous Line Following
✅ Intelligent Obstacle Detection
✅ Servo-Based Environmental Scanning
✅ Dynamic Path Selection Algorithm
✅ State Machine Architecture
✅ PWM Motor Speed Control
✅ Automatic Recovery & Navigation
✅ Modular and Expandable Firmware Design
🧠 How It Works
The robot continuously follows a black line using dual IR sensors.
The ultrasonic sensor measures distance ahead.
If an obstacle is detected within the configured safety range:
The robot stops.
The servo rotates the ultrasonic sensor left and right.
Distances are compared.
The clearest path is selected.
The robot navigates around the obstacle.
Once clear, it returns to line-following mode automatically.
📌 Pin Configuration
Motor Driver (L298N)
ENA → D7 (GPIO13)
IN1 → D1 (GPIO5)
IN2 → D2 (GPIO4)
ENB → D8 (GPIO15)
IN3 → D5 (GPIO14)
IN4 → D6 (GPIO12)
IR Sensors
Left IR → D3 (GPIO0)
Right IR → D4 (GPIO2)
Ultrasonic Sensor
TRIG → GPIO3 (RX)
ECHO → GPIO1 (TX)
Servo Motor
Signal → D0 (GPIO16)
💻 Software Highlights
The firmware is built using a robust Finite State Machine (FSM) architecture that includes:
Line Following Mode
Obstacle Detection Mode
Scanning Mode
Avoidance Mode
Recovery Mode
Lost Line Search Mode
Error Handling Mode
This structure makes the code easier to maintain, debug, and expand with future features such as:
PID Line Following
Wi-Fi Monitoring
Mobile App Control
Telemetry Dashboard
AI-Based Navigation
🚀 Future Improvements
Multi-sensor PID tracking
IoT integration using ESP8266 Wi-Fi
MQTT/Cloud monitoring
Battery management system
Autonomous mapping and navigation
Mobile application control
This project demonstrates how low-cost components can be combined to create a smart autonomous robot capable of making real-time navigation decisions. It is an excellent learning platform for robotics, embedded systems, automation, and IoT development.
🔗 Project Repository:
https://github.com/LEDGNDARYbrahmin/LineFollowing_ObsticleAvoidance-Robot
#Robotics #ESP8266 #NodeMCU #Arduino #LineFollower #ObstacleAvoidance #EmbeddedSystems #Automation #IoT #DIYRobotics #STEM #Engineering #OpenSource #Electronics #MakerCommunity #RobotProject #JagannathPanigrahi #traivimiya
Source Code
/*
* ================================================================================
* LINE FOLLOWING & OBSTACLE AVOIDANCE ROBOT
* ================================================================================
*
* HARDWARE CONFIGURATION:
* - Microcontroller: ESP8266 NodeMCU
* - Motor Driver: L298N (Dual channel, PWM speed control)
* - Motors: 2x BO Motors (Left & Right)
* - Line Detection: 2x IR Sensors (Left & Right)
* - Obstacle Detection: HC-SR04 Ultrasonic Sensor
* - Servo: SG90 Servo Motor (for sensor scanning)
*
* PIN CONFIGURATION:
* Motor Control:
* ENA (Motor A Speed PWM) → D7 (GPIO13)
* IN1 (Motor A Direction) → D1 (GPIO5)
* IN2 (Motor A Direction) → D2 (GPIO4)
* ENB (Motor B Speed PWM) → D8 (GPIO15)
* IN3 (Motor B Direction) → D5 (GPIO14)
* IN4 (Motor B Direction) → D6 (GPIO12)
*
* Line Sensors:
* LEFT_IR → D3 (GPIO0)
* RIGHT_IR → D4 (GPIO2)
*
* Ultrasonic Sensor:
* TRIG → GPIO3 (RX) - Note: Serial communication disabled
* ECHO → GPIO1 (TX) - Note: Serial communication disabled
*
* Servo Motor:
* SERVO → D0 (GPIO16)
*
* AUTHOR: Jagannath Panigrahi
* VERSION: 1.0
* DATE: November 2025
*
* ================================================================================
*/
#include <Servo.h>
// ================================================================================
// CONFIGURATION SECTION - TUNE THESE PARAMETERS FOR YOUR ROBOT
// ================================================================================
// Motor Control Pins
#define ENA D7 // GPIO13 - Left Motor Speed (PWM)
#define IN1 D1 // GPIO5 - Left Motor Direction
#define IN2 D2 // GPIO4 - Left Motor Direction
#define ENB D8 // GPIO15 - Right Motor Speed (PWM)
#define IN3 D5 // GPIO14 - Right Motor Direction
#define IN4 D6 // GPIO12 - Right Motor Direction
// Line Following Sensor Pins
#define LEFT_IR D3 // GPIO0 - Left IR Sensor
#define RIGHT_IR D4 // GPIO2 - Right IR Sensor
// Ultrasonic Sensor Pins (Note: Uses RX/TX, Serial disabled)
#define TRIG_PIN 3 // GPIO3 (RX)
#define ECHO_PIN 1 // GPIO1 (TX)
// Servo Motor Pin
#define SERVO_PIN D0 // GPIO16
// ================================================================================
// SPEED PARAMETERS (0-255 PWM)
// ================================================================================
// Normal operation speeds
#define NORMAL_SPEED 200 // Forward speed on straight line (150-255)
#define NORMAL_TURN_SPEED 160 // Speed during line following turns (100-200)
// Obstacle avoidance speeds
#define OBSTACLE_SEARCH_SPEED 150 // Speed while avoiding obstacle (120-180)
#define OBSTACLE_APPROACH_SPEED 180 // Speed during obstacle avoidance maneuver (150-200)
// Reverse speeds
#define REVERSE_SPEED 150 // Speed when reversing (100-180)
// ================================================================================
// DISTANCE PARAMETERS (Ultrasonic Sensor)
// ================================================================================
#define OBSTACLE_DISTANCE 20 // Distance to trigger obstacle avoidance (15-30 cm)
#define SAFE_DISTANCE 30 // Safe distance to maintain from obstacles (25-40 cm)
#define MAX_DISTANCE 400 // Maximum measurement distance (400 cm)
// ================================================================================
// TIMING PARAMETERS (Milliseconds)
// ================================================================================
#define SENSOR_READ_DELAY 30 // Delay between sensor readings (20-50 ms)
#define OBSTACLE_CHECK_INTERVAL 100 // How often to check for obstacles (50-200 ms)
#define SERVO_SWEEP_DELAY 400 // Delay for servo to reach position (300-500 ms)
#define SEARCH_TIMEOUT 5000 // Max time to search for clear path (3000-8000 ms)
#define RECOVERY_DELAY 200 // Delay after avoiding obstacle (100-300 ms)
#define REVERSE_TIME 500 // How long to reverse if stuck (300-1000 ms)
// ================================================================================
// SERVO POSITIONS (0-180 degrees)
// ================================================================================
#define SERVO_CENTER 90 // Face forward
#define SERVO_LEFT 160 // Look left (scan angle)
#define SERVO_RIGHT 20 // Look right (scan angle)
// ================================================================================
// SENSOR LOGIC CONFIGURATION
// ================================================================================
// IR Sensor Logic:
// IR sensors return LOW (0) when over BLACK line
// IR sensors return HIGH (1) when over WHITE surface
// Adjust if your sensors have inverted logic
#define LINE_DETECTED LOW // Sensor reading when on line
#define NO_LINE_DETECTED HIGH // Sensor reading when off line
// ================================================================================
// STATE MACHINE DEFINITIONS
// ================================================================================
enum RobotState {
STATE_LINE_FOLLOWING = 0, // Following black line normally
STATE_OBSTACLE_DETECTED = 1, // Obstacle found, initiating avoidance
STATE_SCANNING = 2, // Scanning left/right for clear path
STATE_AVOIDING = 3, // Executing avoidance maneuver
STATE_RECOVERY = 4, // Returning to line after avoidance
STATE_LOST = 5, // Line lost, searching for line
STATE_ERROR = 6 // System error
};
// ================================================================================
// DATA STRUCTURES - DEFINED BEFORE USE
// ================================================================================
/**
* Performance statistics structure
* IMPORTANT: Define struct BEFORE using it in function declarations
*/
struct PerformanceStats {
unsigned long lineFollowCount;
unsigned long obstacleAvoidCount;
unsigned long sensorErrorCount;
unsigned long uptimeSeconds;
};
// ================================================================================
// GLOBAL VARIABLES
// ================================================================================
// Servo object
Servo ultrasonicServo;
// Sensor readings
int leftIRReading = 0;
int rightIRReading = 0;
long distanceToObject = 0; // Distance in cm
// State management
RobotState currentState = STATE_LINE_FOLLOWING;
RobotState previousState = STATE_LINE_FOLLOWING;
unsigned long stateChangeTime = 0;
// Timing variables
unsigned long lastSensorReadTime = 0;
unsigned long lastObstacleCheckTime = 0;
unsigned long lastServoMoveTime = 0;
// Performance tracking
unsigned long lineFollowCount = 0;
unsigned long obstacleAvoidCount = 0;
unsigned long sensorErrorCount = 0;
// Direction tracking (for recovery)
int lastMovementDirection = 0; // 0=forward, 1=left, 2=right
// ================================================================================
// SETUP FUNCTION - Runs once at startup
// ================================================================================
void setup() {
// Initialize motor control pins as outputs
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
// Initialize sensor pins
pinMode(LEFT_IR, INPUT);
pinMode(RIGHT_IR, INPUT);
// Initialize ultrasonic sensor pins
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Initialize servo and set to center position
ultrasonicServo.attach(SERVO_PIN);
ultrasonicServo.write(SERVO_CENTER);
// Ensure all motors are stopped
stopMotors();
// Wait for sensors to stabilize
delay(2000);
// Start in line following state
currentState = STATE_LINE_FOLLOWING;
stateChangeTime = millis();
}
// ================================================================================
// MAIN LOOP - Runs continuously
// ================================================================================
void loop() {
// Check if it's time to read sensors
if (millis() - lastSensorReadTime >= SENSOR_READ_DELAY) {
readAllSensors();
lastSensorReadTime = millis();
}
// State machine logic
switch (currentState) {
case STATE_LINE_FOLLOWING:
handleLineFollowing();
break;
case STATE_OBSTACLE_DETECTED:
handleObstacleDetected();
break;
case STATE_SCANNING:
handleScanning();
break;
case STATE_AVOIDING:
handleAvoiding();
break;
case STATE_RECOVERY:
handleRecovery();
break;
case STATE_LOST:
handleLineLost();
break;
case STATE_ERROR:
handleError();
break;
default:
currentState = STATE_LINE_FOLLOWING;
break;
}
// Check for obstacles periodically during line following
if (currentState == STATE_LINE_FOLLOWING) {
if (millis() - lastObstacleCheckTime >= OBSTACLE_CHECK_INTERVAL) {
distanceToObject = getDistance();
if (distanceToObject > 0 && distanceToObject < OBSTACLE_DISTANCE) {
setRobotState(STATE_OBSTACLE_DETECTED);
}
lastObstacleCheckTime = millis();
}
}
}
// ================================================================================
// SENSOR READING FUNCTIONS
// ================================================================================
/**
* Read all sensor inputs
*/
void readAllSensors() {
leftIRReading = digitalRead(LEFT_IR);
rightIRReading = digitalRead(RIGHT_IR);
}
/**
* Measure distance using HC-SR04 ultrasonic sensor
* Returns distance in centimeters, or -1 if measurement failed
*/
long getDistance() {
// Send trigger pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Measure echo time with timeout
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
// Convert to centimeters
// Speed of sound = 343 m/s = 0.034 cm/microsecond
// Distance = (duration / 2) * 0.034
long distance = duration * 0.034 / 2;
// Validate measurement
if (distance > 0 && distance < MAX_DISTANCE) {
return distance;
} else {
sensorErrorCount++;
return -1; // Invalid reading
}
}
/**
* Read distance without blocking (non-blocking version)
* Can be enhanced for real-time performance
*/
long getDistanceNonBlocking() {
return getDistance();
}
// ================================================================================
// STATE HANDLER FUNCTIONS
// ================================================================================
/**
* Handle normal line following mode
* Uses IR sensors to track the black line
*/
void handleLineFollowing() {
// Read sensor values
int left = leftIRReading;
int right = rightIRReading;
// Line following logic with 4 cases
if (left == NO_LINE_DETECTED && right == NO_LINE_DETECTED) {
// Both sensors on white - Move straight
moveForward(NORMAL_SPEED);
lastMovementDirection = 0;
}
else if (left == LINE_DETECTED && right == NO_LINE_DETECTED) {
// Left sensor on line - Adjust to turn right
adjustRight(NORMAL_TURN_SPEED);
lastMovementDirection = 2;
}
else if (left == NO_LINE_DETECTED && right == LINE_DETECTED) {
// Right sensor on line - Adjust to turn left
adjustLeft(NORMAL_TURN_SPEED);
lastMovementDirection = 1;
}
else if (left == LINE_DETECTED && right == LINE_DETECTED) {
// Both sensors on line - Possible intersection or heavy turn
// Continue in last known direction or go straight
moveForward(NORMAL_SPEED);
lastMovementDirection = 0;
}
lineFollowCount++;
}
/**
* Handle obstacle detected state
* Prepare to scan and find alternate path
*/
void handleObstacleDetected() {
// Stop the robot
stopMotors();
delay(RECOVERY_DELAY);
// Transition to scanning state
setRobotState(STATE_SCANNING);
}
/**
* Handle scanning state
* Move servo to scan left and right, measure distances
*/
void handleScanning() {
// Scan left side
ultrasonicServo.write(SERVO_LEFT);
delay(SERVO_SWEEP_DELAY);
long leftDistance = getDistance();
// Scan center
ultrasonicServo.write(SERVO_CENTER);
delay(SERVO_SWEEP_DELAY);
long centerDistance = getDistance();
// Scan right side
ultrasonicServo.write(SERVO_RIGHT);
delay(SERVO_SWEEP_DELAY);
long rightDistance = getDistance();
// Return servo to center
ultrasonicServo.write(SERVO_CENTER);
delay(RECOVERY_DELAY);
// Analyze scan results and decide direction
if (leftDistance > SAFE_DISTANCE || rightDistance > SAFE_DISTANCE) {
// Clear path found
if (leftDistance > rightDistance && leftDistance > SAFE_DISTANCE) {
// Left is clearer
setRobotState(STATE_AVOIDING);
lastMovementDirection = 1; // Will turn left
} else if (rightDistance > SAFE_DISTANCE) {
// Right is clear or equally clear
setRobotState(STATE_AVOIDING);
lastMovementDirection = 2; // Will turn right
}
} else if (centerDistance > SAFE_DISTANCE) {
// Path ahead cleared (obstacle moved)
setRobotState(STATE_LINE_FOLLOWING);
} else {
// All paths blocked - backup and retry
setRobotState(STATE_AVOIDING);
lastMovementDirection = 0; // Reverse
}
obstacleAvoidCount++;
}
/**
* Handle avoiding state
* Execute maneuver around obstacle
*/
void handleAvoiding() {
if (lastMovementDirection == 0) {
// Reverse and turn
reverseAndTurn();
} else if (lastMovementDirection == 1) {
// Turn left and navigate around
avoidLeft();
} else if (lastMovementDirection == 2) {
// Turn right and navigate around
avoidRight();
}
// Transition back to line following
setRobotState(STATE_RECOVERY);
}
/**
* Handle recovery state
* Resume line following
*/
void handleRecovery() {
stopMotors();
delay(RECOVERY_DELAY);
setRobotState(STATE_LINE_FOLLOWING);
}
/**
* Handle line lost state
* Search for the line using a spiral pattern
*/
void handleLineLost() {
// Implement spiral search pattern
spiralSearch();
// Check if line found
if (leftIRReading == LINE_DETECTED || rightIRReading == LINE_DETECTED) {
setRobotState(STATE_LINE_FOLLOWING);
}
}
/**
* Handle error state
* Stop and wait for reset
*/
void handleError() {
stopMotors();
delay(100);
}
// ================================================================================
// MOVEMENT FUNCTIONS
// ================================================================================
/**
* Move forward at specified speed
*/
void moveForward(int speed) {
// Validate speed
speed = constrain(speed, 0, 255);
// Set motor speeds (PWM)
analogWrite(ENA, speed);
analogWrite(ENB, speed);
// Set direction pins for forward
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
/**
* Move backward at specified speed
*/
void moveBackward(int speed) {
speed = constrain(speed, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
/**
* Turn left (both motors forward, but left slower)
*/
void turnLeft(int speed) {
speed = constrain(speed, 0, 255);
// Reduce left motor speed
analogWrite(ENA, speed / 2);
analogWrite(ENB, speed);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
/**
* Turn right (both motors forward, but right slower)
*/
void turnRight(int speed) {
speed = constrain(speed, 0, 255);
// Reduce right motor speed
analogWrite(ENA, speed);
analogWrite(ENB, speed / 2);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
/**
* Pivot left (left reverse, right forward)
*/
void pivotLeft(int speed) {
speed = constrain(speed, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, LOW); // Left motor reverse
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH); // Right motor forward
digitalWrite(IN4, LOW);
}
/**
* Pivot right (left forward, right reverse)
*/
void pivotRight(int speed) {
speed = constrain(speed, 0, 255);
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, HIGH); // Left motor forward
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); // Right motor reverse
digitalWrite(IN4, HIGH);
}
/**
* Stop all motors
*/
void stopMotors() {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
}
/**
* Adjust slightly right during line following
*/
void adjustRight(int speed) {
// Left motor faster, right motor slower
analogWrite(ENA, speed);
analogWrite(ENB, speed * 0.6);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
/**
* Adjust slightly left during line following
*/
void adjustLeft(int speed) {
// Right motor faster, left motor slower
analogWrite(ENA, speed * 0.6);
analogWrite(ENB, speed);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
// ================================================================================
// OBSTACLE AVOIDANCE MANEUVERS
// ================================================================================
/**
* Avoid obstacle by turning left
* Sequence: Turn left -> Move forward -> Turn right -> Move forward
*/
void avoidLeft() {
// Turn left
pivotLeft(OBSTACLE_APPROACH_SPEED);
delay(600);
// Move forward
moveForward(OBSTACLE_APPROACH_SPEED);
delay(800);
// Turn right
pivotRight(OBSTACLE_APPROACH_SPEED);
delay(600);
// Move forward
moveForward(OBSTACLE_APPROACH_SPEED);
delay(800);
}
/**
* Avoid obstacle by turning right
* Sequence: Turn right -> Move forward -> Turn left -> Move forward
*/
void avoidRight() {
// Turn right
pivotRight(OBSTACLE_APPROACH_SPEED);
delay(600);
// Move forward
moveForward(OBSTACLE_APPROACH_SPEED);
delay(800);
// Turn left
pivotLeft(OBSTACLE_APPROACH_SPEED);
delay(600);
// Move forward
moveForward(OBSTACLE_APPROACH_SPEED);
delay(800);
}
/**
* Reverse and turn when blocked from all sides
*/
void reverseAndTurn() {
// Move backward
moveBackward(REVERSE_SPEED);
delay(REVERSE_TIME);
// Pivot left
pivotLeft(OBSTACLE_APPROACH_SPEED);
delay(600);
// Move forward
moveForward(OBSTACLE_APPROACH_SPEED);
delay(500);
}
/**
* Spiral search for lost line
* Used when robot loses the line
*/
void spiralSearch() {
unsigned long searchStartTime = millis();
while (millis() - searchStartTime < SEARCH_TIMEOUT) {
// Move in expanding circle pattern
moveForward(OBSTACLE_SEARCH_SPEED);
delay(100);
turnLeft(OBSTACLE_SEARCH_SPEED);
delay(200);
// Check if line found
if (leftIRReading == LINE_DETECTED || rightIRReading == LINE_DETECTED) {
return;
}
}
// Search timeout - stop and enter error state
stopMotors();
setRobotState(STATE_ERROR);
}
// ================================================================================
// STATE MANAGEMENT FUNCTIONS
// ================================================================================
/**
* Change robot state with timestamp
*/
void setRobotState(RobotState newState) {
if (newState != currentState) {
previousState = currentState;
currentState = newState;
stateChangeTime = millis();
}
}
/**
* Get current state as string (for debugging)
*/
String getStateName(RobotState state) {
switch (state) {
case STATE_LINE_FOLLOWING:
return "LINE_FOLLOWING";
case STATE_OBSTACLE_DETECTED:
return "OBSTACLE_DETECTED";
case STATE_SCANNING:
return "SCANNING";
case STATE_AVOIDING:
return "AVOIDING";
case STATE_RECOVERY:
return "RECOVERY";
case STATE_LOST:
return "LOST";
case STATE_ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
// ================================================================================
// PERFORMANCE MONITORING FUNCTIONS
// ================================================================================
/**
* Get performance statistics
* Now working correctly with struct defined before use
*/
PerformanceStats getPerformanceStats() {
PerformanceStats stats;
stats.lineFollowCount = lineFollowCount;
stats.obstacleAvoidCount = obstacleAvoidCount;
stats.sensorErrorCount = sensorErrorCount;
stats.uptimeSeconds = millis() / 1000;
return stats;
}
/**
* Reset performance counters
*/
void resetPerformanceStats() {
lineFollowCount = 0;
obstacleAvoidCount = 0;
sensorErrorCount = 0;
}
/**
* Print performance statistics (for debugging)
* Note: Serial disabled due to ultrasonic sensor pin usage
* Use WiFi telemetry or comment out ultrasonic pin usage to enable Serial
*/
void printPerformanceStats() {
// Uncomment to enable Serial and use this function
/*
PerformanceStats stats = getPerformanceStats();
Serial.println("=== PERFORMANCE STATISTICS ===");
Serial.print("Line Follow Count: ");
Serial.println(stats.lineFollowCount);
Serial.print("Obstacle Avoid Count: ");
Serial.println(stats.obstacleAvoidCount);
Serial.print("Sensor Errors: ");
Serial.println(stats.sensorErrorCount);
Serial.print("Uptime (seconds): ");
Serial.println(stats.uptimeSeconds);
*/
}
// ================================================================================
// CALIBRATION & DIAGNOSTIC FUNCTIONS
// ================================================================================
/**
* Calibrate IR sensors
* Call this function to verify sensors are working correctly
*/
void calibrateIRSensors() {
// This function can be called during setup or via button press
// It simply reads sensors repeatedly to verify they're working
stopMotors();
// Sensor calibration would happen here
// In practice, use the potentiometer on IR sensors
// Black line = LOW (0)
// White surface = HIGH (1)
}
/**
* Test motor direction
* Rotate each motor individually to verify direction
*/
void testMotorDirection() {
stopMotors();
// Test left motor
moveForward(100);
delay(1000);
stopMotors();
delay(500);
// Test right motor
moveForward(100);
delay(1000);
stopMotors();
}
/**
* Test servo motor
* Move servo through full range
*/
void testServo() {
ultrasonicServo.write(0);
delay(1000);
ultrasonicServo.write(90);
delay(1000);
ultrasonicServo.write(180);
delay(1000);
ultrasonicServo.write(90);
}
/**
* Test ultrasonic sensor
* Take 5 readings and average them
*/
long testUltrasonicSensor() {
long totalDistance = 0;
int validReadings = 0;
for (int i = 0; i < 5; i++) {
long distance = getDistance();
if (distance > 0) {
totalDistance += distance;
validReadings++;
}
delay(100);
}
if (validReadings > 0) {
return totalDistance / validReadings;
}
return -1;
}
// ================================================================================
// OPTIONAL: WiFi & REMOTE CONTROL FUNCTIONS
// ================================================================================
// These functions can be enabled if you add WiFi connectivity
/*
// Uncomment to enable WiFi features
#include <ESP8266WiFi.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
void setupWiFi() {
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
attempts++;
}
}
void sendTelemetry() {
// Send sensor data, state, and performance metrics
// via WiFi to monitoring dashboard
PerformanceStats stats = getPerformanceStats();
// Send stats over WiFi...
}
void handleRemoteControl() {
// Check for commands from mobile app or web interface
// and adjust robot behavior accordingly
}
*/
// ================================================================================
// CODE COMPLETE
// ================================================================================
/*
* COMPILATION & UPLOAD INSTRUCTIONS:
*
* 1. Install Arduino IDE and ESP8266 board package
* 2. Configure board settings:
* - Board: NodeMCU 1.0 (ESP-12E Module)
* - Upload Speed: 115200
* - Port: Select your COM port
*
* 3. Calibrate IR sensors:
* - Adjust potentiometers so:
* - BLACK line = LED ON (sensor output LOW)
* - WHITE surface = LED OFF (sensor output HIGH)
* - Height: 2-3mm above ground
*
* 4. Copy entire code into Arduino IDE
*
* 5. Click UPLOAD button
*
* 6. Robot should start moving on line when powered
*
* FIXES IN VERSION 2.1:
* ✓ Moved PerformanceStats struct definition BEFORE function declarations
* ✓ Fixed: "PerformanceStats does not name a type" compilation error
* ✓ Servo.h library warning is normal (first found library is used)
* ✓ Code now compiles cleanly on ESP8266 board package 3.1.2
*
* TUNING GUIDE:
*
* Speed too fast?
* → Reduce NORMAL_SPEED, NORMAL_TURN_SPEED
*
* Speed too slow?
* → Increase NORMAL_SPEED, NORMAL_TURN_SPEED
*
* Too many turns?
* → Increase NORMAL_TURN_SPEED
* → Adjust IR sensor height
*
* Wobbles or doesn't follow line?
* → Check IR sensor calibration
* → Check IR sensor height (should be 2-3mm)
* → Check motor connections
*
* Doesn't detect obstacles?
* → Reduce OBSTACLE_DISTANCE value
* → Check ultrasonic sensor connections
* → Verify servo position affects sensor orientation
*
* Hits obstacles?
* → Increase OBSTACLE_DISTANCE value
* → Adjust SAFE_DISTANCE value
*
* For more performance:
* → Implement PID control with multiple IR sensors
* → Add acceleration/deceleration
* → Implement sensor filtering
*
* ================================================================================
*/