A condition makes the program take one path rather than another depending on the situation: if the temperature goes past a threshold, then switch the fan on. Without conditions, a program would only ever do one thing, always the same one. This page covers the two structures of choice (if / else and switch / case) and the operators (comparison, logical) that make up the test itself.
What is it for?
Every system that reacts rests on conditions: crossing a threshold, detecting a press, changing mode. That is what turns a program which runs through a sequence into a program which decides. Mastering conditions means being able to write “react like this in one case, like that in another”, the very heart of controlling a mechanism.
Comparison operators
A condition is an expression that is worth true or false (bool). You build it with comparison operators:
Operator
Meaning
Example that is true
==
equal to
mode == 2
!=
different from
etat != HIGH
<>
less than / greater than
t > 25
<=>=
less than or equal / greater than or equal
niveau >= 100
Watch the difference right away between = (assignment: storing a value) and == (comparison: testing for equality). This is pitfall number one (see below).
Logical operators
To combine several conditions, three logical operators:
&& (and) — true if both conditions are true: t >= 18 && t <= 25;
|| (or) — true if at least one of them is: bouton == LOW || force == true;
! (not) — inverts a condition: !enMarche.
Warning
&& is not &.&& is the logical operator (it combines true/false conditions). & is the bitwise operator (it acts on the bits of a number, see bit manipulation). Confusing the two inside a condition gives a wrong but silent result. The same goes for || (logical) and | (bitwise).
if / else if / else
The most common structure of choice. You start simple and build up:
if (t > 25) { // a single case digitalWrite(VENTILO, HIGH);}if (t > 25) { // two mutually exclusive cases digitalWrite(VENTILO, HIGH);} else { digitalWrite(VENTILO, LOW);}if (t < 18) { // several cases, tested in order Serial.println("cold");} else if (t <= 25) { Serial.println("ok");} else { Serial.println("hot");}
Inside a cascade, conditions are tested from top to bottom, and only the first block that is true runs. The final else catches “all the other cases”.
switch / case
When you are branching on the successive values of a single variable, a cascade of if gets heavy. The switch reads better:
switch (mode) { case 0: Serial.println("stop"); break; // leaves the switch case 1: Serial.println("manual"); break; case 2: Serial.println("auto"); break; default: // all the other cases Serial.println("unknown mode");}
Each case ends with break, which leaves the switch. Without it, execution “falls” into the next case (see Pitfalls). The default (optional) catches the values you did not plan for. The switch is everywhere in state machines, where you branch on the current state.
The ternary operator
For the very smallest choices (assigning one value or another depending on a condition), C++ offers a condensed form of the if/else: the ternary operatorcondition ? value_if_true : value_if_false.
digitalWrite(LED, mesure > SEUIL ? HIGH : LOW); // same as the if/else belowif (mesure > SEUIL) { digitalWrite(LED, HIGH); }else { digitalWrite(LED, LOW); }
You have already come across it in the sketch in the structure of a program. Keep it for cases that stay simple and readable (one assignment, one argument): as soon as there are several actions, or you feel tempted to nest two of them, go back to the plain if/else.
Code to read
This sketch sorts a reading into three zones with an if cascade, and shows the switch inside a function that prints the mode.
const int BROCHE = A0;const int LED = 13;void setup() { pinMode(LED, OUTPUT); Serial.begin(115200);}void loop() { int mesure = analogRead(BROCHE); // 0..1023 if (mesure < 300) { // cascade: three zones Serial.println("low"); digitalWrite(LED, LOW); } else if (mesure <= 700) { Serial.println("mid"); digitalWrite(LED, LOW); } else { Serial.println("high"); digitalWrite(LED, HIGH); // alert in the high zone only } delay(500);}// A switch example: branching on a mode numbervoid afficherMode(int mode) { switch (mode) { case 0: Serial.println("stop"); break; case 1: Serial.println("manual"); break; case 2: Serial.println("auto"); break; default: Serial.println("unknown"); }}
The if cascade handles an interval (measurement zones). The switch handles discrete values (mode numbers). Picking one or the other according to the nature of the test is what makes the code clear.
Pitfalls
= instead of ==.if (etat = HIGH)assignsHIGH to etat instead of comparing it, and the condition is then always true. The code compiles without complaint. The behaviour is wrong. Read every test twice.
A break missing in a switch. Without break, execution carries on into the next case (the fall-through effect): you trigger several cases instead of one. Put a break at the end of every case.
Comparing two float values with ==. Floats are approximate: if (t == 25.0) is rarely true even when you expect it to be. Compare against a tolerance instead: if (abs(t - 25.0) < 0.1).
A condition that is always true or always false.if (vitesse > 0 || vitesse < 100) is always true (every number satisfies one of the two). What was meant was almost certainly &&. Check the logic of your || and &&.
&&/& and ||/| mixed up. See the box above: the bitwise version inside a condition gives a silently wrong result.
Exercises
Exercise 1 — A comfort range
Write the condition that lights an LED only if the temperature t lies between 18 and 25 °C inclusive.
Answer to exercise 1
if (t >= 18 && t <= 25) { digitalWrite(LED, HIGH);} else { digitalWrite(LED, LOW);}
The && requires both bounds to hold. With ||, the LED would be on almost all the time (every temperature satisfies at least one of the two inequalities).
Exercise 2 — The switch that goes off the rails
This switch prints manualandauto when mode is 1. Why, and how do you fix it?
switch (mode) { case 1: Serial.println("manual"); case 2: Serial.println("auto");}
Answer to exercise 2
The break statements are missing: when mode == 1, execution enters case 1, prints manual, then falls into case 2 and prints auto. The fix:
switch (mode) { case 1: Serial.println("manual"); break; case 2: Serial.println("auto"); break;}
Where it fits in the project
Step 4 of the proof of concept phase — reacting to a sensor threshold, choosing an action according to a mode: conditions are the first tool of control logic.
State machines — a switch on the current state is the skeleton of a state machine, the central structure for controlling a mechanism.