The if and else statements in Arduino are the foundational C++ control structures that allow your microcontroller to make decisions based on sensor inputs, timers, or serial commands. While the syntax is straightforward, conditional logic is where 90% of embedded bugs hide. A single misplaced semicolon or an assumption about floating-point math can cause your sketch to compile perfectly but fail silently on the bench.
In this guide, we will build a Multi-Threshold Environmental Alarm to demonstrate robust if/else branching, then tear down the most common syntax and logic traps that trip up both beginners and experienced makers.
Project Build: Multi-Threshold Environmental Alarm
To see conditional logic in action, we are building an alarm that monitors temperature and light. It uses an if / else if / else chain to trigger different responses based on severity thresholds. This project targets the Arduino Nano V3 (ATmega328P), chosen for its breadboard-friendly footprint and identical logic behavior to the Uno.
Time to Build: 45 minutes.
Parts List
- Microcontroller: Arduino Nano V3 (ATmega328P, 5V/16MHz variant)
- Temp/Humidity Sensor: DHT22 (AM2302) with 10kΩ pull-up resistor included on module
- Light Sensor: GL5528 LDR with 10kΩ pull-down resistor
- Display: 16x2 LCD with PCF8574 I2C backpack (address 0x27)
- Output: 5V Active Buzzer (KY-012 module)
Pin Mapping Table
| Component | Arduino Nano Pin | Notes |
|---|---|---|
| DHT22 Data | D2 | Requires 10kΩ pull-up to 5V (often built into module) |
| LDR Analog Out | A0 | Voltage divider with 10kΩ to GND |
| I2C LCD SDA | A4 | Standard Nano I2C data line |
| I2C LCD SCL | A5 | Standard Nano I2C clock line |
| Active Buzzer (+) | D8 | Driven HIGH to sound |
| Buzzer / LCD GND | GND | Common ground required |
The Complete If and Else Arduino Code
Below is the complete, compilable sketch. Notice how the if and else blocks handle not just the environmental thresholds, but also include error handling for the DHT22 sensor using the isnan() function.
#include
#include
#include
// --- Pin Definitions ---
#define DHTPIN 2
#define DHTTYPE DHT22
#define LDR_PIN A0
#define BUZZER_PIN 8
// --- Threshold Constants ---
const float TEMP_CRITICAL = 30.0;
const float TEMP_WARNING = 25.0;
const int LIGHT_DARK_THRESHOLD = 300; // 10-bit ADC value (0-1023)
// --- Object Initialization ---
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2); // 0x27 is default PCF8574 address
void setup() {
Serial.begin(115200);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
dht.begin();
lcd.init();
lcd.backlight();
lcd.print("System Booting...");
delay(1000);
}
void loop() {
// 1. Read Sensors
float temperature = dht.readTemperature();
int lightLevel = analogRead(LDR_PIN);
// 2. Error Handling: Check if DHT22 read failed
if (isnan(temperature)) {
lcd.clear();
lcd.print("DHT22 ERROR!");
Serial.println(F("Failed to read from DHT sensor!"));
// Fast beep for hardware fault
tone(BUZZER_PIN, 2000, 100);
delay(500);
return;
}
// 3. Core Conditional Logic (if / else if / else)
if (temperature >= TEMP_CRITICAL) {
// CRITICAL STATE
lcd.clear();
lcd.print("CRITICAL HEAT!");
lcd.setCursor(0, 1);
lcd.print(temperature);
digitalWrite(BUZZER_PIN, HIGH); // Solid alarm
Serial.println("ALARM: Critical temperature reached.");
} else if (temperature >= TEMP_WARNING && lightLevel < LIGHT_DARK_THRESHOLD) {
// WARNING STATE (Compound condition: warm AND dark)
lcd.clear();
lcd.print("WARN: Warm+Dark");
lcd.setCursor(0, 1);
lcd.print(temperature);
tone(BUZZER_PIN, 1000, 250); // Slow intermittent beep
Serial.println("WARNING: Elevated temp in low light.");
} else {
// NORMAL STATE
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
lcd.print("Normal: ");
lcd.print(temperature);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("Light: ");
lcd.print(lightLevel);
}
delay(2000); // DHT22 requires ~2s between reads
}
Debugging Conditional Logic: 3 Things to Check First
When your if and else Arduino logic compiles but the hardware behaves erratically, do not start rewriting your whole sketch. Check these three specific failure modes first:
- Assignment (
=) vs. Equality (==): Writingif (lightLevel = 500)assigns 500 to the variable. Because 500 is non-zero, C++ evaluates the condition astrueevery single time. Always use==for comparison. - The Stray Semicolon: Writing
if (temperature > 30.0); { triggerAlarm(); }places a semicolon immediately after the condition. The compiler interprets the semicolon as an empty statement to execute if true, and the curly brace block executes unconditionally. Remove the semicolon. - Floating-Point Exact Matches: Never write
if (temperature == 25.0). Due to IEEE 754 floating-point representation, a calculated float might actually be25.000001. Instead, use a range check:if (abs(temperature - 25.0) < 0.01).
Common Compiler Errors in if/else Blocks
Syntax errors halt compilation immediately. Here are the exact error strings the Arduino IDE throws when your conditional syntax breaks, ranked by how often they occur, along with the fix.
| Exact Error String | Ranked Cause | The Fix |
|---|---|---|
error: expected primary-expression before 'else' |
#1 Most Common: You placed a semicolon at the end of the if statement, or you omitted the curly braces on a multi-line if block right before the else. |
Remove the semicolon after the if(...) condition. Ensure every if immediately followed by an else uses { } braces. |
error: lvalue required as left operand of assignment |
#2 Most Common: You accidentally used == on the left side of an assignment, or tried to assign a value to a literal inside the condition (e.g., if (5 = x)). |
Check your operators. The left side of = must be a variable. Inside if(), use ==. |
error: expected '}' at end of input |
#3 Most Common: Nested if/else blocks have mismatched curly braces. You opened three { but only closed two }. |
Use the Arduino IDE auto-format tool (Ctrl+T / Cmd+T). It will visually align braces and expose the missing closing bracket. |
Extending and Simplifying Your Conditional Logic
As your project grows, a massive chain of if / else if / else if becomes unreadable and slow to debug. Here is how to extend and simplify your logic based on the data type you are evaluating.
switch/case: If your if/else chain is evaluating a single integer or character variable against exact discrete values (like a state machine or serial command parser), replace it with a switch statement. Note: You cannot use switch with floats or strings in standard Arduino C++.
Using Lookup Tables for Sensor Thresholds:
If you have multiple temperature thresholds that trigger different fan speeds, do not write 10 else if statements. Store the thresholds and PWM values in an array and iterate through it. This separates your data from your logic, making it trivial to tune your thresholds without risking a syntax error in your control flow.
// Simplification Example: Lookup Table instead of massive else/if chain
const int tempThresholds[] = {20, 25, 30, 35};
const int fanPWM[] = {0, 64, 128, 255};
for (int i = 3; i >= 0; i--) {
if (temperature >= tempThresholds[i]) {
analogWrite(FAN_PIN, fanPWM[i]);
break; // Exit loop once the highest matching threshold is found
}
}
Frequently Asked Questions
Can I use multiple conditions in if and else Arduino statements?
Yes. You can combine multiple conditions using logical operators. Use && (Logical AND) to require all conditions to be true, and || (Logical OR) to require at least one condition to be true. For example: if (temp > 30 && humidity > 60). Always wrap individual conditions in parentheses to ensure the correct order of operations, like this: if ((temp > 30) || (lightLevel < 100)).
Why is my Arduino else statement not working or executing unexpectedly?
If your else block executes when it shouldn't, or is skipped entirely, check for the "stray semicolon" bug. If you write if (sensorValue > 500);, the semicolon terminates the if statement immediately. The compiler treats the subsequent else as an orphan, which usually triggers a compiler error, but if structured as a standalone block, the code inside the braces will just run unconditionally. Additionally, verify that your sensor isn't returning NaN (Not a Number), as any comparison with NaN evaluates to false, forcing the code directly into the else block.
How do I use floating-point numbers in if and else Arduino logic safely?
Because floating-point math on the ATmega328P is emulated in software and subject to precision loss, exact equality checks (==) will frequently fail. To safely use floats in if/else logic, define an "epsilon" (a tiny acceptable margin of error) and check if the absolute difference between your variables is less than that margin. For example: if (abs(voltage - 3.3) < 0.05). This accounts for minor ADC noise and calculation rounding.
What is the difference between nested if statements and else if?
An else if chain evaluates conditions sequentially and stops at the first true condition, making it mutually exclusive and highly efficient for threshold checking. A nested if (an if placed inside the curly braces of another if) requires the parent condition to be true before the child condition is even evaluated. Use else if for distinct ranges (e.g., grading scales), and use nested if statements for dependent conditions (e.g., if (machineIsOn) { if (temp > 50) { triggerAlarm(); } }).






