Tutorial 18: State Change Detection and the Modulo Operator

Arduino Course for Absolute Beginners

State Change Detection and the Modulo Operator

In the last example, you learned how to employ a button with the Arduino. When you pressed and held it down things happened, and when you released it different things happened.

But there lies a blaring problem if you just want an on/off switch – namely, who is going to keep the button pressed when you get tired?

There are lots of buttons available. Some buttons will hold themselves down – press it, and it sticks and maintains contact between two wires. This type of button is a hardware solution to the “Who will hold the button?” dilemma.

We used a pushbutton in the last example – a type of momentary switch that connects two wires when you hold it down. What this lesson seeks to explore is how we can use a momentary pushbutton to work as an on/off switch by using clever code.

Let’s think about what happens when you press a button. In this example (and the last) we have a digital pin connected to 5 volts through a pushbutton. When we push the button, the 5 volts are applied to the digital pin. At one moment there is 0 voltage at the pin, and at the next moment there are 5 volts at the pin. When you release the button the pin goes back to 0 voltage. Consider the figure below:

 

State Change Detection Explanation

What we will do is write a program that says “when the voltage changes from 0 to 5 volts, then do something, otherwise don’t do jack”. This change in voltage is referred to as an edge. This sketch performs what is called edge detection.

An on/off button is useful, but to keep things interesting this program will require four button presses to turn on an LED. Adding this layer of complexity allows us to explore another interesting programming tool called the modulo operator.

If you like this tutorial, click here to check out FREE Video Arduino course – thousands of people have really enjoyed it.

You Will Need

  1. LED (1)
  2. 10,000 Ohm resistor (1)
  3. 220 Ohm Resistor (1)
  4. Momentary Push Button (1)
  5. Jumper Wires (3)
  6. Flannel button up short sleeve shirt

Step-by-Step Instructions

  1. Connect one of the Arduino GND pins to one of the long power rails on the breadboard – this will be the ground rail.
  2. Connect the short leg of the LED to this same ground rail on the breadboard.
  3. Connect the long leg to any inside row on the breadboard.
  4. Connect a 220-ohm resistor from pin 13 to the same row where the long leg of the LED is attached.
  5. Place the pushbutton on the breadboard.
  6. Connect a jumper wire from the 5-volt pin to one side of the pushbutton.
  7. Connect a jumper wire from pin 2 to the other side of the pushbutton.
  8. Connect one side of a 10k resistor to the ground rail on the breadboard. Connect the other side of the 10k resistor to the pushbutton – on the same side that pin 2 connects.
  9. Plug the Arduino board into your computer with a USB cable.
  10. Open up the Arduino IDE.
  11. Open the sketch for this section.
  12. Click the Verify button on the top left. It should turn orange and then back to blue.
  13. Click the Upload button. It will also turn orange and then blue once the sketch has finished uploading to your Arduino board.
  14. Open up the serial monitor window.
  15. Press the button a couple times and see how the LED at pin 13 reacts.

State Change Detection

This image created with Fritzing.

The Arduino Code

/*
  State change detection (edge detection)

 Often, you don't need to know the state of a digital input all the time,
 but you just need to know when the input changes from one state to another.
 For example, you want to know when a button goes from OFF to ON.  This is called
 state change detection, or edge detection.

 This example shows how to detect when a button or button changes from off to on
 and on to off.

 The circuit:
 * pushbutton attached to pin 2 from +5V
 * 10K resistor attached to pin 2 from ground
 * LED attached from pin 13 to ground (or use the built-in LED on
   most Arduino boards)

 created  27 Sep 2005
 modified 30 Aug 2011
 by Tom Igoe

This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/ButtonStateChange

 */

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button
      // wend from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of
  // the division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

}

Discuss the Sketch

This is the most complicated sketch thus far. Remember, the best way to keep long programs straight in your mind is to break them up into manageable chunks. The big chunks (called code blocks) in just about any Arduino sketch are the variable declaration and initializations, the setup(), and the loop(). Function definitions will be added to this list as the sketches become more complex.

In addition, you can start subdividing code in the loop() into separate chunks – consider the chunks as separate functional units. You could think, “Ok, this if statement accomplishes XYZ task or this for loop performs ABC task.” If you don’t mentally Kung-Fu chop the code into pieces, then trying to juggle all the different moving parts in your head becomes unwieldy.

Let’s first consider the variables declared and initialized. The variables used as pins are qualified as constants and all the others are integers used to track the state of the button:

const int buttonPin = 2;    // the pin that the pushbutton is attached to

const int ledPin = 13;       // the pin that the LED is attached to

int buttonPushCounter = 0;   // counter for the number of button presses

int buttonState = 0;        // current state of the button

int lastButtonState = 0;     // previous state of the button

The names of these variables infer their purpose, but as we dive into the code, each will make more sense.

The setup() for this sketch is standard fair – we need to set the pin modes and initiate serial communication with the serial port.

We use the pinMode() and Serial.begin() functions to accomplish this:

void setup() {

     pinMode(buttonPin, INPUT); // initialize the button pin as a input

     pinMode(ledPin, OUTPUT); // initialize the LED as an output

     Serial.begin(9600); // initialize serial communication

}

On a quick aside, notice that we almost always use variables to define pin numbers – or any number for that matter in a sketch – even if we only use the variable once. Why not just type the value of the pin in the pinMode() function?

The reason is that variables provide flexibility. It’s tempting to type the hard coded number if you think it will only be used once in the sketch. But what if you realize that using that hard coded number later down the sketch is advisable – now you have 2 hard coded numbers – and before you know it by the end of the program instead of typing that hard coded number once you have typed it five times. Then, if you want to change the value you will have to track down all the hard coded numbers – and I bet you will miss one – I always do.

As a rule of thumb, unless you absolutely, positively know that you will not change the value and that it will only be typed once – then use a variable. The baud rate 9600 that is used for Serial.begin() is a pretty stable number, an example of where hard coding makes sense. There are few examples like this – because variables are better than numbers.

Moving on to the loop(). We start by sampling the state of the digital pin where the pushbutton is attached. We want to know from the very start – is the button being pressed at this instant? The digitalRead() function returns a HIGH or LOW which is then stored in the buttonState variable:

buttonState = digitalRead(buttonPin); // read the pushbutton input pin

After we have sampled the buttonPin the first thing to ask is, “Has the state of the pin changed since we checked it last?” This is determined with an if statement:

if (buttonState != lastButtonState) {

In the first step, we stored the current buttonState, now we compare it with the lastButtonState using the NOT operator which is written as != in Arduino code. The != means “not equal to”. This condition says, “If the current button state does not equal the previous button state, then do something.”

If the button state is the same the condition is not met, and the code enclosed by the if statement is skipped. This if statement condition only executes when the state has changed. The first time through the loop() the lastButtonState variable equals zero (recall we initiated it at the top as zero), which is equivalent to LOW.

If this sketch was running on the Arduino for a couple seconds – and you press the button – this if statement would be executed. This is because the buttonState was LOW and when you press the button it changed to HIGH.

Now when you release the button, the state of the pin changes from HIGH to LOW – therefore the release of the button engages the if statement as well. These voltage transitions from HIGH to LOW are called state changes. This code is one example of a state change detector.

What code is executed inside the curly brackets of the if statement when a state change is detected?

The first thing encountered is a nested if-else statement:

if (buttonState != lastButtonState) {

  if (buttonState == HIGH) {

    // if the current state is HIGH then the button

    // went from off to on:

    buttonPushCounter++; // this just adds one to the value

    Serial.println(“on”);

    Serial.print(“number of button pushes: “);

    Serial.println(buttonPushCounter);

  } else {

    // if the current state is LOW then the button

    // went from on to off:

    Serial.println(“off”);

  }

}

The condition of the nested if statement is:

if (buttonState == HIGH)

To visualize this condition consider the actual button in its resting state (LOW), now imagine it has been pressed (HIGH). This if statement condition checks if buttonState changed from LOW to HIGH.

When the if statement condition is met:

  1. buttonPushCounter is incremented (the ++ just adds 1 to the variable)
  2. Information is sent to the serial port for display on the serial monitor window

The information sent to the serial monitor lets us know what the program is up to – it tells us how many times we pressed the button and the current buttonState value.

When the button is released – the pin state changes from HIGH to LOW and now the value read and assigned to the buttonState variable will be LOW. Because a value of LOW does not satisfy the if statement condition, the next code block executed is inside the else statement. All this code does is print some info to the serial monitor that lets you know the current button state is LOW.

Thus far the process in the loop() can be summarized as follows:

  1. Was the button pressed or released?
  2. If the button was pressed, the code will:
    • Increment the buttonPushCounter variable by 1
    • Print out information about the state of the button and the number of times it was pushed
  3. If the button was released:
    • Print the state of the button

That’s all there is to these seemingly complex nested if statements.

The next line of code we encounter is immediately after the close of the nested if statements. This code updates the lastButtonState variable:

lastButtonState = buttonState; //assign the current button state to the last button state

When the loop() starts again it compares the lastButtonState variable with the current sampled buttonState variable. In this way, we are always comparing the preceding state of the button to the current state of the button.

The final block of code in this sketch is what turns the LED on and off. Recall that it will take four button presses to turn the LED on. That is why we are tracking button presses. If the button has been pressed four times – turn the LED on, otherwise, turn it off. One way to implement this process is with an if-else statement:

if (buttonPushCounter % 4 == 0) {

     digitalWrite(ledPin, HIGH);

} else {

     digitalWrite(ledPin, LOW);

}

The condition used in this if statement is new and a little funky. The percent sign symbol is called the modulo operator.

if (buttonPushCounter % 4 == 0)

To understand what this condition means we need to learn how the modulo operator works.

          The modulo operator returns the remainder of an integer division.

Uhhhh…say what? Here is the deal, remember back in elementary school math class when you used remainders because decimals and fractions were too advanced for you?

Integer division (that is dividing two int variables with each other) works in the same manner, we do not use decimal points or fractions, but we are left with remainders.

If you divided 2 into 5, what would be the remainder?

5/2 = 2 remainder 1

Two goes into 5 twice with 1 left over.

Modulo Operator - Remainders

What if you divide 17 by 5?

  • 17/5 = 3 remainder 2

Ok, what if you divide 1 by 4? Remember, we are dealing only with integers here – not decimals or fractions.

  • 1 / 4 = 0 remainder 1.

Four does not go into one at all – it’s too big – the remainder is 1 (one is what remains of the dividend).

  • 2 / 4 = 0 remainder 2
  • 3 / 4 = 0 remainder 3
  • 4 / 4 = 1 remainder 0

Modulo operator demonstration graphic

Take a look at the following calculations and see if you can follow along:

  • 5 % 2 = 1
  • 17 % 5 = 2
  • 1 % 4 = 1
  • 2 % 4 = 2
  • 3 % 4 = 3
  • 4 % 4 = 0

I like to think of the dividend as a container of ice cream, and the divisor as an ice cream scoop. How many even scoops can I get out of the ice cream container with a given size scoop? Whatever is left in the ice cream container is the remainder – and this is what the modulo operator returns – it’s like the spoon that reaches down to get the stubborn ice cream in the corner of the container. If my ice cream scoop is too big to get any ice cream out of the container, then the whole container is the remainder.

Let’s look at the if statement condition again:

if (buttonPushCounter % 4 == 0)

Recall that the variable buttonPushCounter is keeping a tally of how many times we have pressed the button – this condition asks “If I divide the number of times the button has been pressed by 4, is the remainder equal to zero?” The only time this condition will be met is when 4 divides evenly into pushButtonCounter – and there is no remainder. So the ice cream scoop perfectly gets all the ice cream – none is left over. The values 4, 8, 12, 24 or any multiple of 4 will satisfy this requirement.

What the modulo operator allows us to do is maintain a cycle. Look at the calculations below:

Calculation

  The modulo operator returns the remainder of an integer division.

1 % 4 = 1

  One divided by four equals 0 remainder 1

2 % 4 = 2

  Two divided by four equals 0 remainder 2

3 % 4 = 3

  Three divided by four equals 0 remainder 3

4 % 4 = 0

  Four divided by four equals 1 remainder 0

5 % 4 = 1

  You can see that modulo operator cycles      through – the remainder always stays less  than the divisor by 1.

6 % 4 = 2

7 % 4 = 3

8 % 4 = 0

9 % 4 = 1

10 % 4 = 2

As the chart demonstrates, the modulo operator starts back at zero every fourth calculation. We can use this cycle to turn on the LED every fourth button press.

Let’s consider the final if statement one last time:

if (buttonPushCounter % 4 == 0) {

     digitalWrite(ledPin, HIGH);

} else {

     digitalWrite(ledPin, LOW);

}

If the button has been pushed 4 times, the code turns the LED on – otherwise it turns the LED off. This brings us to the end of the loop().

Let’s review:

  1. Check the state of the button
  2. If the button state has changed…
    • If it turned on (HIGH) – increment the button counter and print some information
    • If it turned off (LOW) – print some information
  3. Update the lastButtonState variable with the current button state
  4. Check to see if the button has been pressed 4 times…
    • If it has – turn on the LED
    • If it has not – turn off the LED
  5. Repeat a bazillion times.

Knowing how to employ edge detection (also known as state change detection) can be useful for many applications – it doesn’t just apply to pressing buttons. And while understanding when the modulo operator is not essential to good programming, it can can be a great little trick to keep up your sleeve.

Try On Your Own

  • Can you make this program detect the falling edge? That is, the state change from HIGH to LOW?
  • See if you can get another LED to turn on every 5 times the button is released.

Further Reading

installing Arduino libraries

Installing Arduino Libraries | Beginners Guide

IoT sewage project

Pumping poo! An IoT sewage project

ESP32 webOTA updates

How to update ESP32 firmware using web OTA [Guide + Code]

error message Brackets Thumbnail V1

expected declaration before ‘}’ token [SOLVED]

Compilation SOLVED | 1

Compilation error: expected ‘;’ before [SOLVED]

Learn how to structure your code