ESP8266 Smart Line Following & Obstacle Avoidance Robot

J
Jagannath Panigrahi
June 24, 2026
4 likes
0 comments

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
 * 
 * ================================================================================
 */

Comments (0)

No comments yet. Start the discussion!

Add a Comment