Making Decisions: If-Then Logic in Robot Behavior

Master if-then logic for autonomous robots. Learn conditional statements, decision trees, boolean operators, and intelligent behavior programming.

Making Decisions: If-Then Logic in Robot Behavior

If-then logic in robotics programming creates autonomous decision-making by evaluating sensor conditions and executing different actions based on those evaluations—if a distance sensor detects an obstacle within 20 centimeters, then turn right; if a line sensor sees black, then adjust steering. This conditional logic transforms robots from machines that blindly follow predetermined sequences into intelligent systems that adapt their behavior based on environmental conditions they encounter.

Your robot sits motionless on the floor, sensors reading the environment perfectly, motors functioning flawlessly, code executing without errors—yet it does nothing useful. It reads that an obstacle is 10 centimeters ahead but continues driving forward into it. It detects the edge of a line but doesn’t adjust steering to stay on track. It senses that battery voltage has dropped dangerously low but keeps running until shutdown. The hardware works perfectly, the sensors provide accurate data, but your robot lacks the one crucial capability that separates autonomous machines from preprogrammed automata: the ability to make decisions based on what it perceives.

Decision-making through if-then logic is what transforms sensor readings into intelligent actions. Without conditional logic, your robot can only execute fixed sequences: move forward for three seconds, turn right for one second, repeat forever. With if-then statements, your robot becomes adaptive: IF an obstacle appears ahead, THEN turn away from it; IF the line moves left, THEN steer right; IF battery is low, THEN return to charging station. Every autonomous behavior—from the simplest obstacle avoidance to the most sophisticated navigation—fundamentally relies on conditional logic that connects perception to action.

This article teaches you how to implement effective decision-making in robots using if-then logic, from basic single conditions through complex multi-sensor decision trees. You’ll learn how to structure conditions clearly, combine multiple tests with boolean operators, avoid common logic errors, and create sophisticated behaviors that respond intelligently to complex situations. Whether you’re programming your first line-following robot or designing advanced autonomous systems, mastering conditional logic represents the essential skill that brings robot intelligence to life.

Understanding Conditional Logic: The Foundation of Decisions

Before writing complex decision-making code, understanding how conditional statements work at a fundamental level helps you think clearly about robot behavior.

The Basic If Statement

The simplest decision structure evaluates one condition and executes code only if that condition is true:

C++
if (condition) {
  // Code here executes only if condition is true
}
// Code here always executes (after the if block)

Real robotics example:

C++
int distance = measureDistance();

if (distance < 20) {
  // Obstacle detected within 20cm
  stopMotors();
  Serial.println("Obstacle! Stopping.");
}

// Continue with rest of program

The condition distance < 20 evaluates to either true or false. If true, the code block executes (motors stop). If false, the code block is skipped entirely.

If-Else: Choosing Between Two Actions

When you need different actions for true and false conditions, use if-else:

C++
if (condition) {
  // Executes if condition is true
} else {
  // Executes if condition is false
}

Real robotics example:

C++
int lightLevel = analogRead(lightSensorPin);

if (lightLevel > 500) {
  // Bright environment
  setMotorSpeed(200);  // Move fast
  Serial.println("Bright - full speed");
} else {
  // Dark environment
  setMotorSpeed(100);  // Move slowly
  Serial.println("Dark - reduced speed");
}

Exactly one of the two code blocks always executes—never both, never neither.

If-Else-If: Multiple Exclusive Conditions

For more than two options, chain multiple conditions with else-if:

C++
if (condition1) {
  // Executes if condition1 is true
} else if (condition2) {
  // Executes if condition1 is false AND condition2 is true
} else if (condition3) {
  // Executes if condition1 and condition2 are false AND condition3 is true
} else {
  // Executes if all conditions are false
}

Real robotics example:

C++
float distance = measureDistance();

if (distance < 15) {
  // Very close - back up
  setMotors(-150, -150);
  Serial.println("TOO CLOSE - Backing up");
} else if (distance < 30) {
  // Close - turn
  setMotors(100, 200);
  Serial.println("Close - Turning");
} else if (distance < 60) {
  // Moderate distance - slow approach
  setMotors(120, 120);
  Serial.println("Approaching carefully");
} else {
  // Far away - full speed ahead
  setMotors(200, 200);
  Serial.println("Path clear - full speed");
}

Only one block executes—the first condition that evaluates to true. Remaining conditions are not even checked once a true condition is found.

Nested If Statements: Decisions Within Decisions

You can place if statements inside other if statements for complex logic:

C++
if (outerCondition) {
  // Executes if outerCondition is true
  
  if (innerCondition) {
    // Executes if BOTH outerCondition AND innerCondition are true
  }
}

Real robotics example:

C++
bool obstacleAhead = (frontDistance < 30);
bool pathClearLeft = (leftDistance > 50);
bool pathClearRight = (rightDistance > 50);

if (obstacleAhead) {
  // Obstacle detected - need to turn
  Serial.println("Obstacle ahead!");
  
  if (pathClearRight) {
    // Right is clear - turn right
    turnRight();
    Serial.println("Turning RIGHT");
  } else if (pathClearLeft) {
    // Left is clear - turn left
    turnLeft();
    Serial.println("Turning LEFT");
  } else {
    // Both sides blocked - back up
    backUp();
    Serial.println("BLOCKED - Backing up");
  }
} else {
  // No obstacle - continue forward
  moveForward();
}

Nested if statements create decision trees where each branch leads to progressively specific actions.

Comparison Operators: Building Conditions

Conditions in if statements use comparison operators to test relationships between values.

Basic Comparison Operators

Equal to: ==

C++
if (sensorValue == 512) {
  // Executes if sensorValue is exactly 512
}

Warning: Use == (double equals) for comparison, not = (single equals for assignment). This is an extremely common error:

C++
if (x = 5) { }   // WRONG - assigns 5 to x, always true!
if (x == 5) { }  // CORRECT - compares x to 5

Not equal to: !=

C++
if (motorState != STOPPED) {
  // Executes if motorState is anything except STOPPED
}

Greater than: >

C++
if (temperature > 25) {
  // Executes if temperature exceeds 25
}

Less than: <

C++
if (distance < 20) {
  // Executes if distance is less than 20
}

Greater than or equal: >=

C++
if (batteryVoltage >= 11.0) {
  // Executes if voltage is 11.0 or higher
}

Less than or equal: <=

C++
if (speed <= maxSpeed) {
  // Executes if speed is maxSpeed or lower
}

Practical Comparison Examples

Range testing:

C++
// Check if value is within acceptable range
if (sensorValue >= 300 && sensorValue <= 700) {
  Serial.println("Value in normal range");
}

Threshold with hysteresis:

C++
if (temperature > 30) {
  fanOn = true;
} else if (temperature < 25) {
  fanOn = false;
}
// Between 25-30: fan stays in current state

Multiple sensor agreement:

C++
if (leftSensor > 500 && rightSensor > 500) {
  // Both sensors agree - high confidence
  Serial.println("Line detected by both sensors");
}

Boolean Operators: Combining Multiple Conditions

Complex robot behaviors require testing multiple conditions simultaneously. Boolean operators combine simple conditions into compound conditions.

AND Operator: &&

Both conditions must be true for the overall expression to be true:

C++
if (condition1 && condition2) {
  // Executes only if BOTH conditions are true
}

Truth table:

  • true && truetrue
  • true && falsefalse
  • false && truefalse
  • false && falsefalse

Robotics example:

C++
float frontDist = measureFrontDistance();
int lightLevel = analogRead(lightSensor);

// Only proceed if path is clear AND there's enough light
if (frontDist > 50 && lightLevel > 300) {
  moveForward();
  Serial.println("Conditions good - moving");
} else {
  stopMotors();
  Serial.println("Waiting for better conditions");
}

Multiple AND conditions:

C++
if (batteryOK && noObstacle && lineDetected && buttonPressed) {
  // All four conditions must be true
  startMission();
}

OR Operator: ||

At least one condition must be true for the overall expression to be true:

C++
if (condition1 || condition2) {
  // Executes if EITHER condition is true (or both)
}

Truth table:

  • true || truetrue
  • true || falsetrue
  • false || truetrue
  • false || falsefalse

Robotics example:

C++
// Stop if obstacle ahead OR battery low
if (frontDistance < 15 || batteryVoltage < 10.5) {
  emergencyStop();
  Serial.println("Emergency stop triggered!");
  
  if (frontDistance < 15) {
    Serial.println("Reason: Obstacle");
  }
  if (batteryVoltage < 10.5) {
    Serial.println("Reason: Low battery");
  }
}

Multiple OR conditions:

C++
// Turn if obstacle detected by any sensor
if (frontSensor || leftSensor || rightSensor || rearSensor) {
  executeTurnSequence();
}

NOT Operator: !

Inverts a boolean value (true becomes false, false becomes true):

C++
if (!condition) {
  // Executes if condition is FALSE
}

Truth table:

  • !truefalse
  • !falsetrue

Robotics example:

C++
bool lineDetected = checkLineSensor();

if (!lineDetected) {
  // Line NOT detected - robot wandered off
  Serial.println("LINE LOST - Searching...");
  searchForLine();
}

Double negative (avoid for clarity):

C++
// Confusing
if (!(!motorRunning)) { }

// Clear equivalent
if (motorRunning) { }

Combining Boolean Operators

You can combine AND, OR, and NOT for complex logic:

C++
// Complex condition
if ((distance > 30 && speed < 100) || emergencyStop) {
  // Executes if:
  // - Distance is safe AND speed is moderate, OR
  // - Emergency stop is activated
}

Use parentheses for clarity:

C++
// Ambiguous - what executes first?
if (a && b || c && d) { }

// Clear - parentheses show intent
if ((a && b) || (c && d)) { }

Robotics example – navigation decision:

C++
bool pathClearAhead = (frontDistance > 40);
bool obstacleLeft = (leftDistance < 20);
bool obstacleRight = (rightDistance < 20);
bool lowBattery = (batteryVoltage < 11.0);

// Complex navigation logic
if (pathClearAhead && !lowBattery) {
  // Path clear and battery OK - continue forward
  moveForward();
} else if (!pathClearAhead && !obstacleRight && !lowBattery) {
  // Obstacle ahead, right is clear, battery OK - turn right
  turnRight();
} else if (!pathClearAhead && !obstacleLeft && !lowBattery) {
  // Obstacle ahead, left is clear, battery OK - turn left
  turnLeft();
} else {
  // Complicated situation or low battery - stop and assess
  stopAndAssess();
}

Practical Decision-Making Patterns

Certain decision-making patterns appear repeatedly in robotics. Understanding these patterns accelerates development.

Pattern 1: Threshold Detection

Simple comparison to determine if a value crosses a meaningful boundary:

C++
// Single threshold
const int obstacleThreshold = 300;
int distanceReading = analogRead(distanceSensor);

if (distanceReading > obstacleThreshold) {
  Serial.println("Obstacle detected!");
  avoidObstacle();
}

Multiple thresholds for graduated response:

C++
const int nearThreshold = 200;
const int mediumThreshold = 400;
const int farThreshold = 600;

int distance = analogRead(distanceSensor);

if (distance < nearThreshold) {
  Serial.println("DANGER - Very close");
  setSpeed(0);  // Stop
} else if (distance < mediumThreshold) {
  Serial.println("Warning - Obstacle nearby");
  setSpeed(100);  // Slow
} else if (distance < farThreshold) {
  Serial.println("Caution - Object detected");
  setSpeed(150);  // Medium
} else {
  Serial.println("All clear");
  setSpeed(200);  // Full speed
}

Pattern 2: Range Checking

Determining if a value falls within acceptable bounds:

C++
// Check if sensor reading is valid/reasonable
int reading = analogRead(sensorPin);
const int minValid = 50;
const int maxValid = 950;

if (reading >= minValid && reading <= maxValid) {
  // Reading is within expected range - use it
  processValidReading(reading);
} else {
  // Reading is out of range - likely sensor error
  Serial.println("Sensor reading out of range - ignoring");
  useDefaultValue();
}

Inverted range check (outside bounds):

C++
// Detect anomalous values
if (temperature < -10 || temperature > 50) {
  Serial.println("Temperature sensor malfunction!");
  triggerError();
}

Pattern 3: State-Based Decisions

Different actions based on current operational state:

C++
enum RobotState {
  SEARCHING,
  APPROACHING,
  AVOIDING,
  STOPPED
};

RobotState currentState = SEARCHING;

void loop() {
  float distance = measureDistance();
  
  if (currentState == SEARCHING) {
    // Looking for target
    if (distance < 100) {
      currentState = APPROACHING;
      Serial.println("Target found - approaching");
    }
    scanArea();
    
  } else if (currentState == APPROACHING) {
    // Moving toward target
    if (distance < 20) {
      currentState = STOPPED;
      Serial.println("Reached target");
    } else if (distance > 150) {
      currentState = SEARCHING;
      Serial.println("Lost target - searching");
    }
    moveTowardTarget();
    
  } else if (currentState == AVOIDING) {
    // Avoiding obstacle
    if (obstacleCleared()) {
      currentState = SEARCHING;
      Serial.println("Obstacle cleared");
    }
    executeAvoidance();
    
  } else if (currentState == STOPPED) {
    // At target
    stopMotors();
  }
  
  // Check for obstacles in any state
  if (distance < 10 && currentState != STOPPED) {
    currentState = AVOIDING;
    Serial.println("OBSTACLE - Avoiding");
  }
}

Pattern 4: Priority-Based Decisions

Handling multiple conditions with different importance levels:

C++
void makeDecision() {
  // Check conditions in priority order (most important first)
  
  // Priority 1: Emergency conditions
  if (batteryVoltage < 10.0) {
    emergencyShutdown();
    return;  // Exit immediately
  }
  
  if (frontDistance < 10) {
    emergencyStop();
    return;
  }
  
  // Priority 2: Warning conditions
  if (batteryVoltage < 11.0) {
    returnToBase();
    return;
  }
  
  if (frontDistance < 25) {
    avoidObstacle();
    return;
  }
  
  // Priority 3: Normal operations
  if (lineDetected) {
    followLine();
    return;
  }
  
  // Default: Nothing special detected
  searchPattern();
}

Using return exits the function immediately after executing high-priority actions, preventing lower-priority code from running.

Pattern 5: Debounced Digital Input

Preventing false triggers from noisy digital sensors:

C++
const int buttonPin = 2;
const int debounceDelay = 50;  // milliseconds

int lastButtonState = LOW;
int buttonState;
unsigned long lastDebounceTime = 0;

void loop() {
  int reading = digitalRead(buttonPin);
  
  // If state changed, reset debounce timer
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  
  // If enough time has passed, accept new state
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      
      // Only act on button press (LOW to HIGH transition)
      if (buttonState == HIGH) {
        Serial.println("Button pressed (debounced)");
        toggleRobotState();
      }
    }
  }
  
  lastButtonState = reading;
}

Pattern 6: Sensor Fusion (Multi-Sensor Agreement)

Making decisions based on multiple sensor inputs:

C++
// Read all sensors
int frontLeft = analogRead(A0);
int frontCenter = analogRead(A1);
int frontRight = analogRead(A2);

const int obstacleThreshold = 400;

// Count how many sensors detect obstacle
int obstacleCount = 0;
if (frontLeft > obstacleThreshold) obstacleCount++;
if (frontCenter > obstacleThreshold) obstacleCount++;
if (frontRight > obstacleThreshold) obstacleCount++;

// Decision based on consensus
if (obstacleCount >= 2) {
  // High confidence - multiple sensors agree
  Serial.println("Obstacle confirmed by multiple sensors");
  stopAndTurn();
} else if (obstacleCount == 1) {
  // Low confidence - only one sensor
  Serial.println("Possible obstacle - slowing");
  reduceSpeed();
} else {
  // No obstacle
  Serial.println("Path clear");
  fullSpeed();
}

Advanced Decision Structures

Beyond basic if-then logic, several advanced structures enable more sophisticated decision-making.

Switch-Case Statements

For many discrete conditions based on a single variable, switch-case is cleaner than multiple if-else-if:

C++
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST
};

Direction heading = NORTH;

switch (heading) {
  case NORTH:
    Serial.println("Heading North");
    motorLeft = 200;
    motorRight = 200;
    break;
    
  case SOUTH:
    Serial.println("Heading South");
    motorLeft = -200;
    motorRight = -200;
    break;
    
  case EAST:
    Serial.println("Heading East");
    motorLeft = 200;
    motorRight = -200;
    break;
    
  case WEST:
    Serial.println("Heading West");
    motorLeft = -200;
    motorRight = 200;
    break;
    
  default:
    Serial.println("Unknown heading!");
    stopMotors();
    break;
}

Important: Always include break statements or execution will “fall through” to the next case.

Ternary Operator (Conditional Expression)

For simple if-else assignments, the ternary operator is more concise:

C++
// Traditional if-else
int speed;
if (distance > 50) {
  speed = 200;
} else {
  speed = 100;
}

// Ternary operator (same result)
int speed = (distance > 50) ? 200 : 100;

Syntax: condition ? valueIfTrue : valueIfFalse

Robotics example:

C++
// Set LED based on obstacle detection
digitalWrite(ledPin, (obstacleDetected ? HIGH : LOW));

// Choose motor direction based on line position
int correction = (linePosition > 0) ? 50 : -50;

Use ternary operators for simple cases; stick with if-else for complex logic requiring multiple statements.

Lookup Tables

For complex mappings between input values and output actions, lookup tables can be more efficient than long if-else chains:

C++
// Map distance ranges to motor speeds
const int numRanges = 5;
const int distanceThresholds[numRanges] = {10, 20, 40, 70, 100};
const int motorSpeeds[numRanges] = {0, 80, 120, 160, 200};

int getMotorSpeed(int distance) {
  for (int i = 0; i < numRanges; i++) {
    if (distance < distanceThresholds[i]) {
      return motorSpeeds[i];
    }
  }
  return motorSpeeds[numRanges - 1];  // Max speed if beyond all thresholds
}

void loop() {
  int distance = measureDistance();
  int speed = getMotorSpeed(distance);
  setMotors(speed, speed);
}

Complete Decision-Making Examples

Let’s examine complete, working examples demonstrating decision-making in real robotics applications.

Example 1: Obstacle-Avoiding Robot

Simple autonomous navigation using if-then logic:

C++
// Sensor pins
const int frontSensorPin = A0;
const int leftSensorPin = A1;
const int rightSensorPin = A2;

// Motor pins
const int motorLeftPin = 9;
const int motorRightPin = 10;

// Thresholds and speeds
const int obstacleThreshold = 400;
const int turnSpeed = 150;
const int forwardSpeed = 180;

void setup() {
  pinMode(motorLeftPin, OUTPUT);
  pinMode(motorRightPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Read all sensors
  int frontDistance = analogRead(frontSensorPin);
  int leftDistance = analogRead(leftSensorPin);
  int rightDistance = analogRead(rightSensorPin);
  
  // Decision tree
  if (frontDistance > obstacleThreshold) {
    // Obstacle directly ahead
    Serial.println("Obstacle ahead!");
    
    if (rightDistance < obstacleThreshold && leftDistance < obstacleThreshold) {
      // Both sides clear - choose based on which is more clear
      if (rightDistance < leftDistance) {
        Serial.println("Turning LEFT (more clear)");
        turnLeft();
      } else {
        Serial.println("Turning RIGHT (more clear)");
        turnRight();
      }
    } else if (rightDistance < obstacleThreshold) {
      // Right is clear
      Serial.println("Turning RIGHT");
      turnRight();
    } else if (leftDistance < obstacleThreshold) {
      // Left is clear
      Serial.println("Turning LEFT");
      turnLeft();
    } else {
      // Both sides blocked - back up
      Serial.println("BLOCKED - Backing up");
      backUp();
    }
  } else {
    // Path clear ahead
    Serial.println("Moving forward");
    moveForward();
  }
  
  delay(100);
}

void moveForward() {
  analogWrite(motorLeftPin, forwardSpeed);
  analogWrite(motorRightPin, forwardSpeed);
}

void turnLeft() {
  analogWrite(motorLeftPin, 0);
  analogWrite(motorRightPin, turnSpeed);
  delay(500);  // Turn for 500ms
}

void turnRight() {
  analogWrite(motorLeftPin, turnSpeed);
  analogWrite(motorRightPin, 0);
  delay(500);
}

void backUp() {
  analogWrite(motorLeftPin, -turnSpeed);
  analogWrite(motorRightPin, -turnSpeed);
  delay(300);  // Back up for 300ms
}

Example 2: Line-Following Robot with Proportional Steering

Using sensor values to make graduated steering decisions:

C++
// Sensor array pins (5 sensors across line)
const int sensorPins[5] = {A0, A1, A2, A3, A4};
const int numSensors = 5;

// Motor pins
const int motorLeftPin = 9;
const int motorRightPin = 10;

// Parameters
const int baseSpeed = 150;
const int lineThreshold = 500;

void loop() {
  // Read all line sensors
  int sensorValues[numSensors];
  for (int i = 0; i < numSensors; i++) {
    sensorValues[i] = analogRead(sensorPins[i]);
  }
  
  // Calculate line position (-2 = far left, 0 = center, +2 = far right)
  float linePosition = calculateLinePosition(sensorValues);
  
  // Decision: Adjust motors based on line position
  if (linePosition == -999) {
    // Line not detected - stop
    Serial.println("LINE LOST!");
    analogWrite(motorLeftPin, 0);
    analogWrite(motorRightPin, 0);
  } else {
    // Line detected - steer toward it
    int steeringCorrection = linePosition * 30;  // Multiply position by gain
    
    int leftSpeed = baseSpeed + steeringCorrection;
    int rightSpeed = baseSpeed - steeringCorrection;
    
    // Constrain to valid range
    leftSpeed = constrain(leftSpeed, -255, 255);
    rightSpeed = constrain(rightSpeed, -255, 255);
    
    analogWrite(motorLeftPin, abs(leftSpeed));
    analogWrite(motorRightPin, abs(rightSpeed));
    
    Serial.print("Line position: ");
    Serial.print(linePosition);
    Serial.print(", Motors: L=");
    Serial.print(leftSpeed);
    Serial.print(" R=");
    Serial.println(rightSpeed);
  }
  
  delay(10);
}

float calculateLinePosition(int sensors[]) {
  float weightedSum = 0;
  int totalWeight = 0;
  
  // Weight each sensor by its position
  for (int i = 0; i < numSensors; i++) {
    if (sensors[i] > lineThreshold) {
      // Sensor sees line
      int weight = i - 2;  // -2, -1, 0, 1, 2
      weightedSum += weight * sensors[i];
      totalWeight += sensors[i];
    }
  }
  
  if (totalWeight > 0) {
    return weightedSum / totalWeight;  // Weighted average position
  } else {
    return -999;  // No line detected
  }
}

Example 3: Battery Monitor with Multiple Warning Levels

Graduated responses to battery status:

C++
const int batteryPin = A0;

// Voltage thresholds (assuming voltage divider)
const float fullVoltage = 12.6;
const float normalVoltage = 11.5;
const float lowVoltage = 11.0;
const float criticalVoltage = 10.5;

// State tracking
bool warningGiven = false;
bool criticalWarningGiven = false;

void loop() {
  float voltage = readBatteryVoltage();
  
  // Multi-level decision tree
  if (voltage >= normalVoltage) {
    // Battery healthy
    warningGiven = false;
    criticalWarningGiven = false;
    normalOperation();
    
  } else if (voltage >= lowVoltage) {
    // Battery getting low
    if (!warningGiven) {
      Serial.println("WARNING: Battery below normal level");
      flashLED(2);  // Visual warning
      warningGiven = true;
    }
    reducedOperation();
    
  } else if (voltage >= criticalVoltage) {
    // Battery critically low
    if (!criticalWarningGiven) {
      Serial.println("CRITICAL: Battery very low - returning to base");
      flashLED(5);  // Urgent visual warning
      criticalWarningGiven = true;
    }
    returnToBase();
    
  } else {
    // Battery dangerously low
    Serial.println("EMERGENCY: Battery depleted - shutting down");
    emergencyShutdown();
  }
  
  delay(1000);  // Check every second
}

float readBatteryVoltage() {
  int rawValue = analogRead(batteryPin);
  // Convert ADC to voltage (adjust for your voltage divider)
  float voltage = rawValue * (5.0 / 1023.0) * 3.0;  // Example conversion
  return voltage;
}

void normalOperation() {
  Serial.println("Battery OK - normal operation");
  // Full-speed operation
}

void reducedOperation() {
  Serial.println("Reduced power mode");
  // Slow down, reduce LED brightness, etc.
}

void returnToBase() {
  Serial.println("Navigating to charging station");
  // Special navigation to charger
}

void emergencyShutdown() {
  Serial.println("Shutting down to protect battery");
  // Stop all motors, save state, shut down
  while(1);  // Halt program
}

void flashLED(int times) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN, LOW);
    delay(200);
  }
}

Common Logic Errors and How to Avoid Them

Even experienced programmers make logic errors. Recognizing common mistakes helps you avoid them.

Error 1: Using Assignment (=) Instead of Comparison (==)

C++
// WRONG - Assigns 100 to speed, then tests if 100 is true (always true!)
if (speed = 100) {
  Serial.println("This always executes!");
}

// CORRECT - Compares speed to 100
if (speed == 100) {
  Serial.println("Executes only when speed equals 100");
}

Prevention: Enable compiler warnings; some compilers warn about assignments in conditions.

Error 2: Incorrect Operator Precedence

C++
// Ambiguous - what gets tested first?
if (a && b || c && d) { }

// Probably meant this
if ((a && b) || (c && d)) { }

// Or maybe this
if (a && (b || c) && d) { }

Prevention: Always use parentheses to make precedence explicit, even when not strictly necessary.

Error 3: Floating-Point Equality Comparison

C++
float voltage = readVoltage();

// WRONG - Floating-point values rarely equal exactly
if (voltage == 3.3) {
  // Might never execute due to rounding errors!
}

// CORRECT - Check if within small range
if (abs(voltage - 3.3) < 0.1) {
  // Executes if voltage is between 3.2 and 3.4
}

Prevention: Never use == with float/double; always use range checking.

Error 4: Unreachable Code Due to Logic Order

C++
int distance = 50;

// WRONG - Second condition never reached
if (distance < 100) {
  Serial.println("Less than 100");
} else if (distance < 50) {
  // Never executes! All values < 50 are also < 100
  Serial.println("Less than 50");
}

// CORRECT - Check narrower ranges first
if (distance < 50) {
  Serial.println("Less than 50");
} else if (distance < 100) {
  Serial.println("Between 50 and 100");
}

Prevention: Order conditions from most specific to least specific.

Error 5: Missing Braces Leading to Wrong Scope

C++
// WRONG - Only first line is inside if statement
if (obstacleDetected)
  Serial.println("Obstacle!");
  stopMotors();  // ALWAYS executes, not just when obstacle detected!

// CORRECT - Braces make scope clear
if (obstacleDetected) {
  Serial.println("Obstacle!");
  stopMotors();  // Both lines execute only if condition is true
}

Prevention: Always use braces, even for single-line if statements.

Comparison Table: Decision Structures

StructureUse WhenAdvantagesDisadvantagesExample
ifSingle condition checkSimple, clearLimited to one pathif (x > 5) { }
if-elseTwo mutually exclusive optionsEnsures one path executesOnly two optionsif (x > 5) { } else { }
if-else-ifMultiple exclusive conditionsMany options, clear priorityCan become verboseif (x < 5) { } else if (x < 10) { }
switch-caseMany discrete values of single variableClean for many casesOnly works with discrete valuesswitch(state) { case A: }
Nested ifHierarchical decisionsModels complex logicCan become hard to readif (a) { if (b) { } }
TernarySimple conditional assignmentConcise, inlineOnly for simple casesx = (y > 5) ? 10 : 20;
Lookup tableMany value mappingsFast, data-drivenRequires setup, uses memoryspeeds[distanceIndex]

Best Practices for Robot Decision Logic

Writing clear, maintainable decision logic requires discipline and good habits.

Use Meaningful Variable Names

C++
// Unclear
if (x > 500) { y = 1; }

// Clear
if (distanceSensor > obstacleThreshold) { obstacleDetected = true; }

Define Constants for Magic Numbers

C++
// Bad - What does 350 mean?
if (analogRead(A0) > 350) { }

// Good - Clear meaning
const int LINE_THRESHOLD = 350;
if (analogRead(lineSensorPin) > LINE_THRESHOLD) { }

Comment Complex Conditions

C++
// Complex condition needs explanation
// Stop if battery critically low OR
// (obstacle very close AND unable to turn)
if (batteryVoltage < 10.5 || 
    (frontDistance < 10 && !canTurn())) {
  emergencyStop();
}

Extract Complex Conditions into Functions

C++
// Hard to understand
if (s1 > 500 && s2 > 500 && s3 < 300 || s4 > 400 && batteryOK) { }

// Clear and testable
if (lineDetected() && batteryOK) { }

bool lineDetected() {
  return (sensor1 > 500 && sensor2 > 500 && sensor3 < 300) || 
         (sensor4 > 400);
}

Handle Edge Cases

C++
// Check for invalid sensor readings
int distance = analogRead(distanceSensor);

if (distance == 0 || distance == 1023) {
  // Sensor error - min or max reading
  useDefaultBehavior();
} else {
  // Valid reading - use normally
  processDistance(distance);
}

Test Decisions Systematically

Test all branches of your decision logic:

C++
void testDecisionLogic() {
  Serial.println("Testing decision logic...");
  
  // Test case 1: Close obstacle
  testObstacleResponse(10);   // Should trigger emergency stop
  
  // Test case 2: Moderate distance
  testObstacleResponse(30);   // Should trigger caution
  
  // Test case 3: Far distance
  testObstacleResponse(100);  // Should continue normal
  
  Serial.println("Tests complete");
}

Conclusion: Logic as the Robot’s Intelligence

If-then logic transforms robots from mechanical devices into intelligent systems. Sensors provide data, motors enable action, but conditional logic creates the intelligence that connects perception to appropriate response. Every autonomous behavior—whether simple obstacle avoidance or sophisticated multi-goal planning—builds on the foundation of well-crafted conditional statements that evaluate situations and choose actions.

The decision-making skills you’ve developed in this article—understanding if-else structures, combining conditions with boolean operators, implementing common patterns, avoiding logic errors—apply universally across all programming and robotics platforms. The specific syntax might vary between Arduino, Python, C++, or other languages, but the logical principles remain constant. A well-structured decision tree in Arduino translates directly to equivalent logic in any other programming environment.

Start simple with your decision logic: a single if statement checking one sensor and controlling one actuator. As this becomes natural, add complexity: multiple conditions combined with AND/OR operators, if-else-if chains for multiple options, nested decisions for hierarchical logic, state machines for behavior sequencing. Each project builds understanding and confidence, developing intuition for how to structure decisions clearly and effectively.

Remember that the best decision logic is not the most complex—it’s the logic that clearly expresses your robot’s behavior in a way you and others can understand, test, and modify. Clever one-line conditionals might feel satisfying but can become maintenance nightmares. Clear, well-commented decision trees might span many lines but remain comprehensible months later when you need to debug or enhance functionality.

Most importantly, decision logic is where your robot’s personality and intelligence emerge. The same hardware with different decision logic exhibits entirely different behaviors. Your choices about what conditions matter, how sensors combine, which actions correspond to which situations—these design decisions transform generic components into unique robot behaviors. Master conditional logic, and you master the essential skill that brings robot intelligence to life.

Share:
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments

Discover More

What is Artificial Intelligence? A Complete Beginner’s Guide

Learn what artificial intelligence really is. Understand AI fundamentals, how it works, types of AI,…

Essential Tools Every Robotics Beginner Should Own

Discover the essential tools every robotics beginner needs. Learn which tools to buy first, what…

Getting Started with Flutter: Installation and Setup

Learn how to install Flutter, set up your development environment, and create your first app…

Understanding Matrices and Vectors in AI Applications

Learn how matrices and vectors power AI applications. Understand image processing, NLP, recommendation systems, and…

The Realistic Costs of Starting in Robotics

Discover the real costs of starting robotics from $50 to $500+. Learn what you need…

Why Arduino Changed Hobbyist Robotics Forever

Discover how Arduino revolutionized hobbyist robotics by making microcontrollers accessible to everyone. Learn why Arduino…

Click For More
0
Would love your thoughts, please comment.x
()
x