Button Inputs Tutorial

Today we’re going to learn about all the button inputs you can use in your Blinks game. It won’t be super complicated, but it should be enough to get you started.

We’re going to break down the button presses and discuss and implement them as we go. We’ll start with the simplest type: buttonDown(). As you might expect, the buttonDown() method returns true if the button is down, and false if it is not. We can make a super simple loop to show that.

void loop() {
  if (buttonDown()) {
    setColor(RED);
  } else {
    setColor(WHITE);
  }
}

Install this and watch in wonder as it… does exactly what you expect. Not much more to say about that. Now we’re going to move on to two related button presses - buttonPressed() and buttonReleased(). To understand these, and really all of the rest of the presses, we need to talk about the idea of “flags” and how they work.

When a button is first pressed (transitions from up to down), it sets the buttonPressed() flag to true. This flag remains true until it is checked via the buttonPressed method. This means that from the first time a button is pressed during your game, that flag becomes and remains true, even if you never query it. But once you do query it, it returns to false. If you’re wondering how this applies to buttonDown(), good news: it doesn’t really. But it does for the rest of the methods we’re using, so it’s pretty important.

So back to buttonPressed(). We’re gonna make a function that creates a little fade when you first press the button. To do that, we’ll need to use a simple timer. Let’s instantiate that at the top of the code (before anything else) and define a fade time.

Timer pressFadeTimer;
#define PRESS_FADE_DURATION 500

Now let’s take the display code out of loop() itself. In its place we’ll put a listener for “buttonReleased()” that looks like this:

void loop() {
  if (buttonPressed()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
  }
}

We’ll also create an actual display loop, first with the buttonDown display code, then with a bit of code that displays the fade.

void displayLoop() {
  //set color for when there are no active clicks or presses, just button down
  if (buttonDown()) {
    setColor(RED);
  } else {
    setColor(WHITE);
  }

  //check for press fades
  if (!pressFadeTimer.isExpired()) {//the fade is currently active
    setColor(dim(YELLOW, pressFadeTimer.getRemaining() / 2));//dims from 250 to 0
  }
}

As you can see, it checks that the timer is running, then uses the time remaining to create a simple linear fade. If you’re confused about the way this is done, check out the tutorial on timers and fades. Importantly, the fade code comes after the buttonDown code, which means it will overwrite those colors when it’s active. If you install this, you’ll see that the moment you hit the button it triggers that fade, but comes up red once it’s over. We can do the exact same trick with buttonReleased(). Just go back into the loop and duplicate the code for buttonPressed(), replacing it with buttonReleased(), like this:

void loop() {
  if (buttonPressed()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
  }

  if (buttonReleased()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
  }
  
  displayLoop();
}

The next type of button press we’re going to look at is the Long Press. This one is… tricky. When a button is held down continuously for 1.5 seconds, the “buttonLongPressed” flag is set to true. The tricky part is that if you hold the button for too long, the Blink will go into programming mode, or even to sleep. We recommend making a big show of the long press so that players know they’ve passed the threshold and can let go. For our little demo, we’re going to change from red to blue. We’ll start by adding a boolean and a listener.

bool longPressing = false;

...

  if (buttonLongPressed()) {
    longPressing = true;
  }

Then we’ll add a little statement to our display code like this:

  if (buttonDown()) {
    if (longPressing) {
      setColor(BLUE);
    } else {
      setColor(RED);
    }
  } else {
    setColor(WHITE);
  }

And lastly, just for our convenience, I’m going to reduce the length of the press fade by changing the duration to 500 and only dividing it by 2 in the display function. It’ll just make it quicker.

If you install this, you’ll notice that the light does turn blue after you hold it for 1.5 seconds. But you might also notice that the light doesn’t is now permanently blue when you are holding the button. This is because the boolean is never set to false. Let’s quickly do that inside the “buttonReleased” check.

  if (buttonReleased()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
    longPressing = false;
  }

So that should fix it. We’ve now covered all of the “press” functions. What’s left is the “click” functions: single-click, double-click, multi-click, and click-count. And for everyone’s sake, we’re just going to make an entirely new script for this, because I don’t want to get involved with mixing all these design ideas together. So scrap that one, and let’s start a new script!

In this new script, we’re going to start simply by declaring a variable to keep track of clicks.

byte clicks = 1;

There are three types of clicks. For the single-click flag to be set to true, the player must press and release the button a single time within X seconds. For double-click, they must take this action twice, with no more than X seconds between the two clicks. Multi-clicks follow the same logic, except as long as the user keeps clicking with less than X seconds between, the click count keeps expanding. To build our loop, we’ll create a listener for each type of click, then set the clicks variable inside those listeners according to the number of clicks. It should look like this:

void loop() {
  // put your main code here, to run repeatedly:

  if (buttonSingleClicked()) {
    clicks = 1;
  }

  if (buttonDoubleClicked()) {
    clicks = 2;
  }

  if (buttonMultiClicked()) {
    clicks = buttonClickCount();
  }

}

You’ll notice that I’ve used the “buttonClickCount” function. All this does is return the number of clicks input as part of a multi-click.

To display what we’ve just done, we’ll create and call a little display loop. This will light up the number of faces corresponding to the number of clicks, with anything greater than 6 just showing up as red.

void displayLoop() {
  setColor(OFF);
  
  FOREACH_FACE(f) {
    if (f < clicks) { //this face should be lit
      setColorOnFace(WHITE, f);
    }

    if (clicks > 6) {
      setColor(RED);
    }
  }
}

Simply call displayLoop() at the end of loop(), and you’ll see the results of your script!

That about wraps it up for button inputs. They are, as you might imagine, crazy important in Blinks development. In case you missed anything in the tutorial, here’s the full scripts for both examples.

Button Presses Script

Timer pressFadeTimer;
#define PRESS_FADE_DURATION 500

bool longPressing = false;

void setup() {

}

void loop() {
  if (buttonPressed()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
  }

  if (buttonReleased()) {//begin the press fade
    pressFadeTimer.set(PRESS_FADE_DURATION);
    longPressing = false;
  }

  if (buttonLongPressed()) {
    longPressing = true;
  }

  displayLoop();
}

void displayLoop() {
  //set color for when there are no active clicks or presses, just button down
  if (buttonDown()) {
    if (longPressing) {
      setColor(BLUE);
    } else {
      setColor(RED);
    }
  } else {
    setColor(WHITE);
  }

  //check for press fades
  if (!pressFadeTimer.isExpired()) {//the fade is currently active
    setColor(dim(YELLOW, pressFadeTimer.getRemaining() / 2));//dims from 250 to 0
  }
}

Button Clicks Script

byte clicks = 1;

void setup() {

}

void loop() {
  // put your main code here, to run repeatedly:

  if (buttonSingleClicked()) {
    clicks = 1;
  }

  if (buttonDoubleClicked()) {
    clicks = 2;
  }

  if (buttonMultiClicked()) {
    clicks = buttonClickCount();
  }

  displayLoop();
}

void displayLoop() {
  setColor(OFF);
  
  FOREACH_FACE(f) {
    if (f < clicks) { //this face should be lit
      setColorOnFace(WHITE, f);
    }

    if (clicks > 6) {
      setColor(RED);
    }
  }
}
3 Likes