Robotics & Arduino
Lesson 3 · Hands on

Project 12

Full system — manual, sweep and homing

The code.



#include <Servo.h>

// --- Pins ---
const int POT_PIN    = A0;
const int SERVO_PIN  = 9;
const int TRIG_PIN   = 12;
const int ECHO_PIN   = 11;

// Radar Distance Feedback LEDs
const int RED_LED    = 2;
const int BLUE_LED   = 3;
const int ORANGE_LED = 4;
const int GREEN_LED  = 5;

// Touch Interface Pins
const int TOUCH_PIN  = 10;
const int MODE_LED_1 = 6;
const int MODE_LED_2 = 7;
const int MODE_LED_3 = 8;

// --- Calibration Limits ---
const int SERVO_MIN  = 15;   
const int SERVO_MAX  = 165;  

// --- Global Variables ---
Servo myServo;
float currentDistance = 0.0;
long echoDuration = 0;
int currentServoAngle = 90; // Tracks the current position of the servo arm

// Touch & Mode State Variables
int activeMode = 1;           // Mode 1 = Manual, Mode 2 = Sweep, Mode 3 = Homing
int currentTouchState = LOW;  
int lastTouchState = LOW;     

// Auto-Sweep Direction Trackers
int sweepDirection = 1;       // 1 = sweeping up, -1 = sweeping down

void setup() {
  Serial.begin(9600);
  
  myServo.attach(SERVO_PIN);
  myServo.write(currentServoAngle);
  
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  
  pinMode(RED_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);
  pinMode(ORANGE_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  
  pinMode(TOUCH_PIN, INPUT);
  pinMode(MODE_LED_1, OUTPUT);
  pinMode(MODE_LED_2, OUTPUT);
  pinMode(MODE_LED_3, OUTPUT);
  
  updateModeIndicators();
}

void loop() {
  // Check if user changed the operating mode via the touch sensor
  checkTouchSensor();
  
  // Every mode requires distance tracking to drive the feedback LEDs
  readDistance();   
  updateRadarLEDs(); 

  // Branch execution into the chosen system behavior
  switch (activeMode) {
    case 1:
      runManualMode();
      break;
    case 2:
      runSweepMode();
      break;
    case 3:
      runHomingMode();
      break;
  }
  
  delay(30); // Stabilizes the servo movement and avoids spamming the execution loop
}

// --- CORE SYSTEM MODES ---

// Mode 1: Manual Potentiometer Turning
void runManualMode() {
  int potValue = analogRead(POT_PIN);
  currentServoAngle = map(potValue, 0, 1023, SERVO_MIN, SERVO_MAX);
  myServo.write(currentServoAngle);
}

// Mode 2: Constant Automated Sweep
void runSweepMode() {
  // Step the angle by 1 degree per execution slice
  currentServoAngle += sweepDirection;
  
  // Reverse when boundaries are intersected
  if (currentServoAngle >= SERVO_MAX) {
    currentServoAngle = SERVO_MAX;
    sweepDirection = -1;
  } 
  else if (currentServoAngle <= SERVO_MIN) {
    currentServoAngle = SERVO_MIN;
    sweepDirection = 1;
  }
  
  myServo.write(currentServoAngle);
}

// Mode 3: Dynamic Object Homing
void runHomingMode() {
  bool isTargetInSweetSpot = (currentDistance >= 20.0 && currentDistance <= 30.0 && echoDuration > 0);
  
  if (isTargetInSweetSpot) {
    // Lock the servo in position; an object is successfully tracked in the green zone!
    return; 
  }
  
  // If the target is lost or out of the sweetspot, execute standard search sweeping
  currentServoAngle += sweepDirection;
  
  if (currentServoAngle >= SERVO_MAX) {
    currentServoAngle = SERVO_MAX;
    sweepDirection = -1;
  } 
  else if (currentServoAngle <= SERVO_MIN) {
    currentServoAngle = SERVO_MIN;
    sweepDirection = 1;
  }
  
  myServo.write(currentServoAngle);
}

// --- CORE UTILITY FUNCTIONS ---

void checkTouchSensor() {
  currentTouchState = digitalRead(TOUCH_PIN);

  if (currentTouchState == HIGH && lastTouchState == LOW) {
    activeMode++;
    if (activeMode > 3) {
      activeMode = 1;
    }
    updateModeIndicators();
    
    Serial.print("--- Mode Shifted to: ");
    Serial.println(activeMode);
    delay(150); // Software debounce to eliminate contact bounce noise
  }
  lastTouchState = currentTouchState;
}

void updateModeIndicators() {
  digitalWrite(MODE_LED_1, LOW);
  digitalWrite(MODE_LED_2, LOW);
  digitalWrite(MODE_LED_3, LOW);

  if (activeMode == 1)      digitalWrite(MODE_LED_1, HIGH);
  else if (activeMode == 2) digitalWrite(MODE_LED_2, HIGH);
  else if (activeMode == 3) digitalWrite(MODE_LED_3, HIGH);
}

void readDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  echoDuration = pulseIn(ECHO_PIN, HIGH, 25000); // 25ms timeout handles maximum room scale scanning cleanly
  currentDistance = echoDuration * 0.0343 / 2.0;
}

void updateRadarLEDs() {
  digitalWrite(RED_LED, LOW);
  digitalWrite(BLUE_LED, LOW);
  digitalWrite(ORANGE_LED, LOW);
  digitalWrite(GREEN_LED, LOW);

  if (echoDuration == 0 || currentDistance <= 0) {
    digitalWrite(RED_LED, HIGH);
  }
  else if (currentDistance > 0 && currentDistance < 2) {
    digitalWrite(BLUE_LED, HIGH);
  }
  else if (currentDistance >= 20 && currentDistance <= 30) {
    digitalWrite(GREEN_LED, HIGH);
  }
  else if ((currentDistance >= 2 && currentDistance < 20) || (currentDistance > 30 && currentDistance <= 50)) {
    digitalWrite(ORANGE_LED, HIGH);
  }
}
Full-system circuit: pot to A0; servo signal D9; touch output D10; ultrasonic Trig D12 and Echo D11; distance LEDs D2–D5; mode LEDs D6–D8; seven separate LED resistors, separate regulated 5V servo supply, and common ground.