Module 2 - Programming Fundamentals

05. Operators & Decisions

Calculate, compare and branch: arithmetic, = vs ==, Boolean logic, if/else chains and switch.

Estimated time 2 sessions + optional extrasIn class Week 2 · Sessions 6–7

Learning outcomes

  • Use +, -, *, / and % and explain integer division
  • Distinguish assignment (=) from equality (==) and other comparisons
  • Combine conditions with &&, || and !
  • Write an if / else if / else chain that selects one action
  • Use switch to choose between fixed values of one variable
  • Read INPUT_PULLUP buttons as pressed-when-LOW

Parts and preparation

Arduino Uno, two push buttons, jumper wires. Onboard LED is the output - no extra LED required.

Before power: inspect wiring, confirm supply voltage and ensure all connected circuits share GND.

Decide from inputs

Sketches spend most of their life reading the world, then choosing what to do. Operators calculate and compare. if / else if / else picks a branch.

This lesson drives the onboard LED from two buttons: both pressed = steady on, exactly one = flash, neither = off.

Flowchart of the two-button sketch: read both buttons, test both pressed for a steady LED, then either pressed for a flashing LED, otherwise off, then back to the top of loop, with a table of all four button combinations
Read inputs, test both, then either, then choose steady / flash / off.

Arithmetic operators

Use + - * / and % to calculate. % (modulo) is the remainder after division - handy for wrapping and for simple blink patterns.

Integer trap: when both operands are integers, / discards the fraction. 5 / 2 is 2. Write 5.0 / 2 (or cast) when you need 2.5.

The five arithmetic operators with examples, 5 sweets shared between 2 cups to show 5 / 2 is 2 and 5 % 2 is 1, and a table showing how millis() / 250 % 2 makes the LED flash
Five arithmetic operators. Integer division drops the remainder unless you use a float.
OperatorMeaningExample
+Add3 + 2 -> 5
-Subtract3 - 2 -> 1
*Multiply3 * 2 -> 6
/Divide5 / 2 -> 2 (ints)
%Remainder5 % 2 -> 1

Assignment vs comparison

= stores a value into a variable. == asks whether two values are equal and produces true or false.

Writing if (x = 1) by mistake assigns 1 to x and often looks always true - a classic bug. In comparisons you also use !=, <, <=, > and >=.

The line bool aPressed = digitalRead(buttonA) == LOW annotated: == asks if the pin is LOW, = stores the answer; plus the if (x = 1) bug and the comparison operators
= stores. == compares. Memorise this before hunting wiring faults.
bool aPressed = digitalRead(buttonA) == LOW;  // compare
ledOn = true;                                 // assign

Boolean operators

&& (AND) is true only when both sides are true. || (OR) is true when either side is true (or both). ! (NOT) flips true to false and false to true.

Parentheses keep complex tests readable: if ((a && b) || alarm).

A truth table of AND and OR for two buttons with the LED result, AND drawn as two switches in series and OR as two in parallel, and NOT flipping true and false
AND both, OR either, NOT reverse. Prefer && and || over bitwise & and | here.

Truth table for two buttons

Name the pressed flags clearly: aPressed and bPressed are true when that button is down. Then the logic matches everyday English: both, either, neither.

Truth table of A and B for AND and OR results
Both true only for AND. OR lights up whenever at least one is true.

if / else if / else

An if chain tests conditions in order. Only the first true branch runs; the rest are skipped. else covers whatever is left when no earlier test matched.

Separate if statements are different: each is tested on its own, so more than one body can run. Use a chain when the outcomes should be mutually exclusive (steady vs flash vs off).

The if, else if, else chain for the two buttons with a table of which branch runs for neither, one or both buttons, and why testing either before both makes both-pressed flash
One chain, one winner. Order matters: test both before either.
if (aPressed && bPressed) {
  // steady
} else if (aPressed || bPressed) {
  // flash
} else {
  // off
}

switch: one variable, several fixed values

When one variable selects between several fixed values - a mode number, a menu choice, a character typed in Serial - a switch reads more clearly than a long if chain.

switch compares the variable with each case label. Execution starts at the matching case and runs until break. Forget the break and the next case runs too (fall-through). default runs when no case matches, like a final else.

switch only works with whole-number types (byte, int, char) and constant case labels. Use if for ranges and combined conditions, such as temperature > 30 && fanAllowed.

UseWhen
if / else if / elseRanges, && / || combinations, float comparisons
switchOne whole-number variable compared with fixed values (modes, menu keys)
switch (mode) {
  case 0:
    digitalWrite(ledPin, LOW);     // off
    break;
  case 1:
    digitalWrite(ledPin, HIGH);    // steady
    break;
  case 2:
    digitalWrite(ledPin, millis() / 250 % 2);   // flash
    break;
  default:
    Serial.println("Unknown mode");
    break;
}

INPUT_PULLUP: pressed is LOW

Course buttons wire from the pin to GND with pinMode(..., INPUT_PULLUP). INPUT_PULLUP switches on a resistor inside the chip that pulls the pin up to 5 V, so an open button reads HIGH. Pressing the button connects the pin straight to GND, so it reads LOW.

Wiring the 4-leg button: push it into the breadboard so it straddles the centre gap. Connect one leg to the Uno pin (D2 for button A, D3 for button B) and the diagonally opposite leg to GND. Diagonal legs are always on opposite sides of the switch, so this works whichever way round the button sits.

That is why the sketch uses digitalRead(pin) == LOW for pressed - not == HIGH. If your LED behaviour feels inverted, check this first.

INPUT_PULLUP circuit with the chip's internal pull-up resistor: not pressed the pin reads HIGH, pressed the button joins D2 to GND and it reads LOW; plus the code and why diagonal legs of a 4-leg button work
Active-low press: compare to LOW when using INPUT_PULLUP.

What the worked sketch practises

Two buttons on D2 and D3. Both pressed: LED steady HIGH. Exactly one pressed: LED flashes using millis() / 250 % 2 so loop stays responsive (no delay). Neither: LED LOW.

Order in the chain is important: the both test must come before the either test, or both-pressed would be treated as a single-button flash.

Wiring and safe build sequence

  1. Place each 4-leg button straddling the breadboard's centre gap
  2. Button A: one leg to D2, the diagonally opposite leg to GND
  3. Button B: one leg to D3, the diagonally opposite leg to GND
  4. pinMode INPUT_PULLUP for both (no external resistor required)
  5. Output: onboard LED (LED_BUILTIN)
Power rule: switch off before moving wires. Arduino I/O pins are control signals; high-current loads require a driver and suitable external supply.

Worked sketch

Download .ino
const byte buttonA = 2;
const byte buttonB = 3;
const byte ledPin = LED_BUILTIN;

void setup() {
  pinMode(buttonA, INPUT_PULLUP);
  pinMode(buttonB, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // INPUT_PULLUP: pressed connects pin to GND -> reads LOW
  bool aPressed = digitalRead(buttonA) == LOW;
  bool bPressed = digitalRead(buttonB) == LOW;

  if (aPressed && bPressed) {
    digitalWrite(ledPin, HIGH);                 // both: steady on
  } else if (aPressed || bPressed) {
    digitalWrite(ledPin, millis() / 250 % 2);   // one: flash
  } else {
    digitalWrite(ledPin, LOW);                  // none: off
  }
}

How the code works

  1. Pressed is LOW because INPUT_PULLUP holds an open input HIGH.
  2. Test both (&&) before either (||) so two presses are not treated as one.
  3. millis() / 250 % 2 flips 0/1 about four times per second without delay(): millis() counts milliseconds since reset, / 250 turns that into quarter-seconds, and % 2 keeps only 0 or 1 - off, on, off, on. You meet millis() properly in Lesson 11.
  4. = would be wrong inside the if tests - those need == for comparison.

Test and record evidence

Expected result: No button: LED off. Exactly one button: LED flashes. Both buttons: LED steady on.

Practical evidence checklist

Common faults and checks
  • If behaviour feels inverted, remember pressed reads LOW with INPUT_PULLUP.
  • Check && versus || and == versus =.
  • Button always looks pressed (or never): use diagonally opposite legs, or rotate the button 90 degrees across the centre gap.
  • If both buttons flash instead of going steady, your both-branch may be missing or ordered after either.
  • Floating feel with no pull-up: confirm INPUT_PULLUP, not plain INPUT.
Challenge (extension - optional): Give button A priority: if A is pressed, ignore B and hold the LED steady on. Only when A is released should B control flashing. Explain how your if order enforces that. Then rewrite the output part with a mode variable (0 off, 1 flash, 2 steady) and a switch.

Check your understanding

Q1. What happens in a switch if a case has no break?

Show answer

Execution falls through and runs the next case as well.

Q2. What is the difference between = and ==?

Show answer

= assigns a value; == compares for equality.

Q3. When does A || B evaluate true?

Show answer

When A is true, B is true, or both are true.

Q4. What is 5 / 2 as integers on Arduino?

Show answer

2 - integer division discards the remainder.

Q5. Why compare digitalRead to LOW for a course button?

Show answer

INPUT_PULLUP wiring is active-low: pressed connects the pin to GND.

Q6. Why put the both-pressed test before the either-pressed test?

Show answer

Otherwise both pressed would match the either branch and flash instead of staying steady.

Code Along 3: Operators & Decisions

Practise in your browser

Write and test this lesson's code on a virtual Uno. Work through the 7 exercises in order. Press Test and each one checks itself.

Exercises passed 0 / 7About 75-100 minOpen as its own page

Comparing values

A comparison is either true or false. C++ has six comparison operators: == (equal), != (not equal), <, <=, > and >=.

An if statement runs its block only when the condition is true. else gives the other path. For more than two paths, add else if in between: if (t > 30) { ... } else if (t > 25) { ... } else { ... } - the tests are checked in order and only the first true one runs. Watch out: a single = stores a value, a double == compares two values.

Flowchart of the two-button sketch: read both buttons, test both pressed for a steady LED, then either pressed for a flashing LED, otherwise off, then back to the top of loop, with a table of all four button combinations
Only the first true branch of an if / else if chain runs.
Worked example

Deciding with if and else

Change temperature to 20 and press Run again to see the other branch.

int temperature = 28;

void setup() {
  Serial.begin(9600);
  if (temperature > 25) {
    Serial.println("Fan ON");
  } else {
    Serial.println("Fan OFF");
  }
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Expected output
Fan ON
Exercise 3.1

Above the threshold?

Not started

reading is a value from analogRead(), between 0 and 1023. The start of the line is printed for you. Add an if / else that finishes it with : HIGH if the reading is 512 or more, and : LOW if it is below 512 - exactly as in the sample.

Sample output
Reading 700: HIGH
int reading = 700;

void setup() {
  Serial.begin(9600);
  Serial.print("Reading ");
  Serial.print(reading);
  // Finish the line: ": HIGH" or ": LOW"

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 3.2

Fix the = bug

Not started

This sketch always reports five presses - and it even changes the count! Find the bug and fix it. Read the compiler warning: it points straight at the problem.

The real Arduino IDE hides warnings by default. Turn them on in File > Preferences > Compiler warnings.

Sample output
Not five presses
buttonPresses = 3
int buttonPresses = 3;

void setup() {
  Serial.begin(9600);
  if (buttonPresses = 5) {
    Serial.println("Five presses - resetting");
  } else {
    Serial.println("Not five presses");
  }
  Serial.print("buttonPresses = ");
  Serial.println(buttonPresses);
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 3.3

Even or odd pulse count

Not started

A counter has recorded pulses pulses. Print whether that count is even or odd, as in the sample. A number is even when dividing by 2 leaves no remainder - use %.

Sample output
7 pulses: odd
int pulses = 7;

void setup() {
  Serial.begin(9600);
  // Print: 7 pulses: odd

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 3.4

Battery level bands

Not started

Turn a battery voltage into a status word:

  • 4.00 V or more: Full
  • 3.60 to 3.99 V: Good
  • 3.30 to 3.59 V: Low
  • below 3.30 V: Critical

Print it like the sample, using an if / else if / else chain.

Sample output
3.70 V: Good
float volts = 3.7;

void setup() {
  Serial.begin(9600);
  // Print: 3.70 V: Good

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Combining conditions

  • && (AND) is true only when both sides are true.
  • || (OR) is true when at least one side is true.
  • ! (NOT) flips true and false.

Brackets make mixed conditions clear: (a && b) || c.

Worked example

AND, OR and NOT

Predict the output before you press Run. Then change alarmArmed to true.

bool doorOpen = true;
bool alarmArmed = false;
int lightLevel = 120;

void setup() {
  Serial.begin(9600);
  if (doorOpen && alarmArmed) {
    Serial.println("ALARM!");
  } else if (doorOpen || lightLevel < 200) {
    Serial.println("Check the room");
  } else {
    Serial.println("All quiet");
  }
  if (!alarmArmed) {
    Serial.println("Alarm is not armed");
  }
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Expected output
Check the room
Alarm is not armed
Exercise 3.5

Pump interlock

Not started

A pump may only run when its switch is on and the tank is not yet full. Print Pump running when both are true, and Pump stopped otherwise.

Sample output
Pump running
bool pumpSwitch = true;
bool tankFull = false;

void setup() {
  Serial.begin(9600);
  // Print: Pump running   or   Pump stopped

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 3.6

Thermostat with a dead band

Not started

A heater should not click on and off every time the temperature wobbles. Use these rules:

  • below 18 °C: switch the heater on
  • above 22 °C: switch the heater off
  • from 18 to 22 °C: leave heaterOn as it is

Update heaterOn, then print the temperature and the heater state like the sample.

Sample output
17.50 C: heater ON
float temperature = 17.5;
bool heaterOn = false;

void setup() {
  Serial.begin(9600);
  // Apply the rules to heaterOn


  // Print: 17.50 C: heater ON

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Worked example

Choosing with switch

switch compares one whole number or character with several fixed values. break ends each case; default catches everything else.

char command = 'G';

void setup() {
  Serial.begin(9600);
  switch (command) {
    case 'R':
      Serial.println("Red");
      break;
    case 'G':
      Serial.println("Green");
      break;
    default:
      Serial.println("Unknown command");
  }
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Expected output
Green
Exercise 3.7

Mode selector

Not started

A rotary switch sets mode: 1 is Manual, 2 is Auto, 3 is Test. Anything else is Unknown. Print the mode with a switch, exactly as in the sample.

Sample output
Mode 2: Auto
int mode = 2;

void setup() {
  Serial.begin(9600);
  Serial.print("Mode ");
  Serial.print(mode);
  Serial.print(": ");
  // switch on mode and print its name

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Try it on a real Uno

Download Above the threshold?, replace the fixed reading with int reading = analogRead(A0); inside loop(), add delay(500); and upload it with a potentiometer on A0. Turn the knob and watch HIGH and LOW swap over as you pass the middle.

Want more? Code Along chapter 3 has a Deeper exercise that goes further than this lesson. It is optional.