One shot buttons

I am writing a sketch for a MIDI keyboard with up and down octave buttons. I have tried the code with if statements and while statements, but I cannot make the button operate once for every press. I get a continuous stream until I lift the button. I have put the "Serial.println" statements for tracing only.

Here is the relevant code:

#include "Controller.h"

byte firstNote;

byte notes[12];
byte tones[12];
byte button = 0;
byte buttonUp = 8;
byte buttonDown = 7;
byte switchState[2] = {LOW, LOW};

void setup() {
  Serial.begin(9600);
  button = 3;
  setKeys();
  pinMode(buttonUp, INPUT_PULLUP);
  pinMode(buttonDown, INPUT_PULLUP);
}

void loop() {

  if(digitalRead(buttonUp) == LOW){
    setUpButton();
  }
  delay(100);
  switchState[0] = LOW;

  if(digitalRead(buttonDown) == LOW){
    setDownButton();
  }
  delay(100);
  switchState[1] = LOW;
}


void setUpButton(){
  if(switchState[0] == LOW){
      button = button + 1;
    if(button > 5){
      button = 5;
    }
    switchState[0] = HIGH;
    Serial.println("UP");
    setKeys();
  }
}

void setDownButton(){
  if(switchState[1] == LOW){
    button = button - 1;
    if(button < 0){
      button = 0;
    }
    switchState[1] = HIGH;
    Serial.println("Down");
    setKeys();
  }
}

void setKeys(){
  switch(button){
    case 1:
      firstNote = 36;
      fillNotes();
    break;

    case 2:
      firstNote = 48;
      fillNotes();
    break;

    case 3:
      firstNote = 60;
      fillNotes();
    break;

    case 4:
      firstNote = 72;
      fillNotes();
    break;

    case 5:
      firstNote = 83;
      fillNotes();
    break;
  }
}

void fillNotes(){
  for(byte i = 0; i <= 11; i++){
    tones[i] = notes[i] + firstNote + i;
    Serial.println(firstNote);
  }
}
  if(digitalRead(buttonUp) == LOW){
    setUpButton();
  }

  • Suggest you only use switch change in state, not switch level.
3 Likes

Thanks LarryD. I don't understand what you mean. Can you elaborate please.

https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection/

Once you've got a pushbutton working, you often want to do some action based on how many times the button is pushed. To do this, you need to know when the button changes state from off to on, and count how many times this change of state happens. This is called state change detection or edge detection. In this tutorial we learn how to check the state change, we send a message to the Serial Monitor with the relevant information and we count four state changes to turn on and off an LED.

3 Likes

You need to know what level it was last time you checked as well as this time. If it was HIGH and is now LOW, that's the change to trigger action upon.

Caveat: contact switches like buttons will "bounce" for 2+ milliseconds (dirty buttons mash and rock) when pressed and released. Prepare to learn about debouncing, a necessary lesson!

1 Like
  • As the others have offered . . .

  • Debouncing can be handled by looking for changes in state say, every 50ms.

  • A change in state is either HIGH to LOW or LOW to HIGH.
    Assume a switch press is LOW.
    When 50ms expires, and we see the lastState was HIGH and now the current State is LOW, we know the switch was pressed; hence we then do what we are supposed to do when the switch is closed.

  • In the next 50ms, lastState will be LOW and the current state is LOW, hence no change in state, hence we do not do anything this time.

1 Like

Hello johnd2117

The IPO model should be the correct reference for a pseudo code:

INPUT: read and debounce new button state
PROCESSING: recognise the button change and save the state
OUTPUT: perform action on the new state

hth

3 Likes
const byte PinBut = A1;
      byte butState;

void
loop (void)
{
    byte but = digitalRead (PinBut);
    if (butState != but)  {         // state change
        butState  = but;
        delay (20);                 // debounce
        if (LOW == but)
            Serial.println ("press");
    }
}

void
setup (void)
{
    Serial.begin (9600);

    pinMode (PinBut, INPUT_PULLUP);
    butState = digitalRead (PinBut);
}

and before you ask about how to handle many buttons

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}
1 Like

How long before the OP is going to have to learn to Not Use Delay or Blocking Loops when trying to do more than one thing at a time as in watch a button while blinking a led?
That is not an advanced topic, it's what beginners learn to get past being beginners. It's a step past switch transitions and the one before state machines.
The ones it is hard for are the ones who have to UNLEARN "the right way" they learned first then rutted down into.

I almost gave you a heart.

this isn't about delay/millis, it's about recognizing a button press event and debouncing

Do the delay() but don't get married to it, don't invest much time in it because before long you will want to have something running with a pause or stop button and that's when you will find out that delay code prevents that.

For sure, do some things with delay in them just to learn other lessons but keep them small. Any code that does One Thing At A Time works fine with delays and there are lots of projects OTAAT.

With pin state change and all code that works with change you use code that deals with time in the past/previous vs now sense and that is what to learn about... keeping track by using variables to hold and compare was to is. Watch for that and see how it works to do what you want. What to do depends on something changing changing, even the time as in is it lunch time then get lunch!

When you are ready to code for More Than One Thing At A Time, just ask and you will get lots of help!

Hello johnd2117

I have another tip:

As the delay() function is known to block the programme flow, a non-blocking timer function is necessary.
As a basis for this, I recommend the BlinkWithOutDelay example from the IDE as the mother of all Arduino timers.
The BWOD example can easily be converted into a timer module that can generally be used for all applications.

1 Like

I like the BWOD example but they make some mistakes.
They mix the variable types used for time.

Time variables should always be unsigned integer types!

Where a round clock is good for 12 hour intervals....

unsigned long with millis is good for almost 50 day intervals
unsigned int with millis is good for over 65 seconds

Time math ises unsigned subtraction to work across rollover without any need to check or use if-else logic.

Sorry, BWOD doesn't teach that and will break just before 25 days.

1 Like

Thanks gcjr. Those snippets look good. I'll save them to my snipits and have a play with them.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.