Send once [Uno + Sparkfun MIDI Breakout]

// If you are new to this thread, it might save you time to skip to a point of some changes: [Reply #8 Send once [Uno + Sparkfun MIDI Breakout] - #9 by system - Project Guidance - Arduino Forum](http://Reply #8 Send once [Uno + Sparkfun MIDI Breakout] - #9 by system - Project Guidance - Arduino Forum)

I am developing an Arduino MIDI controller for theatrical playback software: a set of pushbuttons that send a MIDI command to an audio playback application. Stop, Go, Previous-cue-in-list, and Next-cue-in-list are the desired commands, which take the form of Note On messages in the playback application.

I have made extensive use of the MIDI, Debounce, and Pushbutton tutorials.

The problem at hand is that I only want the MIDI Note On sent a single time with each button push. As I have it programmed now, it is being read by MIDI Monitor for the entire duration that the button is pressed. Any thoughts?

#include <MIDI.h>
#include <Bounce.h>

// set the pins:

const int buttonStop = 2; 
const int buttonGo = 3;
const int buttonLast = 4;
const int buttonNext = 5;   

// variables for reading the pushbutton status:

int stopState = 0;         
int goState = 0;  
int lastState = 0;  
int nextState = 0; 

// debounce the pushbuttons at 5ms:
Bounce bounceStop = Bounce(buttonStop,5); 
Bounce bounceGo = Bounce(buttonGo,5); 
Bounce bounceLast = Bounce(buttonLast,5); 
Bounce bounceNext = Bounce(buttonNext,5); 

void setup() {
  
  // start MIDI with input channel set to 4
  MIDI.begin(4);            	
  
  // initialize the pushbutton pin as an input:
  pinMode(buttonStop, INPUT);  
  pinMode(buttonGo, INPUT);
  pinMode(buttonLast, INPUT);
  pinMode(buttonNext, INPUT);  
}

void loop(){
  
  // read the state of the pushbutton value:
  stopState = digitalRead(buttonStop);
  goState = digitalRead(buttonGo);
  lastState = digitalRead(buttonLast);
  nextState = digitalRead(buttonNext);
  
  // update the debouncer:
  bounceStop.update ( );
  bounceGo.update ( );
  bounceLast.update ( );
  bounceNext.update ( );

  // get the update value:
 int valueStop = bounceStop.read();
 int valueGo = bounceGo.read();
 int valueLast = bounceLast.read();
 int valueNext = bounceNext.read();


  // if a button is pressed, send a MIDI message:
  if (stopState == HIGH) {     
    MIDI.sendNoteOn(1,127,5); 
  } 
  
    if (goState == HIGH) {     
    MIDI.sendNoteOn(2,127,5);
  } 
  
    if (lastState == HIGH) {     
    MIDI.sendNoteOn(3,127,5);  
  } 
  
    if (nextState == HIGH) {     
    MIDI.sendNoteOn(4,127,5);
  } 
 
}

I have tried removing all the debounce programming from my sketch. No change.
I have tried increasing my debounce interval to as much as 1 second. No change.
I have tried setting the button status back to 0 after the NoteOn, like this:

  if (stopState == HIGH) {     
    MIDI.sendNoteOn(1,127,5); 
    stopState = 0;  
  }

I have tried the code with the 'if' statements changed to 'switch/case' e.g. in the place of:

  if (stopState == HIGH) {     
    MIDI.sendNoteOn(1,127,5); 
  }

I tried:

    switch (stopState) {     
      case HIGH:
      MIDI.sendNoteOn(1,127,5); 
      break;
  }

Thanks in advance for anything you might offer.

You need to look for a change. That is, if the button is now HIGH, and it was previously LOW (or vice-versa). Then use the debounce to make sure the bounces are not counted as extra changes.

I guess I am not sure how to do that. Tomorrow I am going to simplify my code to have just one button until I get this working properly, and then add the other buttons back after I am successful. In the meantime, I have tried creating a variable that will hold the last value recorded by the digitalRead and then use it to compare the states required in the if statements, but I am not quite making the jump in logic necessary. You can see two of my attempts below, marked by comments. I tried a couple of other things but forget what they were exactly but they produced no MIDI message.

void loop(){
  
  // read the state of the pushbutton value:
  stopState = digitalRead(buttonStop);
  goState = digitalRead(buttonGo);
  lastState = digitalRead(buttonLast);
  nextState = digitalRead(buttonNext);
  
  stopwas = stopState;
  gowas = goState;
  lastwas = lastState;
  nextwas = nextState;
  
  // update the debouncer:
  bounceStop.update ( );
  bounceGo.update ( );
  bounceLast.update ( );
  bounceNext.update ( );

  // get the update value:
 int valueStop = bounceStop.read();
 int valueGo = bounceGo.read();
 int valueLast = bounceLast.read();
 int valueNext = bounceNext.read();


  // if a button is pressed, send a MIDI message:
  if (stopwas != stopState) {                                          // here I just tried to succeed by comparing the difference in variables
    MIDI.sendNoteOn(1,127,5);                                       // this MIDI message never fires under these conditions.
    Bounce bounceStop = Bounce(buttonStop,5); 
  } 
  
    if (gowas == LOW && goState == HIGH) {                  // another way of comparing the variables
    MIDI.sendNoteOn(2,127,5);                                      // again, this MIDI message never fires under these conditions.
    Bounce bounceGo = Bounce(buttonGo,5);  
  } 
  
    if (lastwas == LOW && lastState == HIGH) {     
    MIDI.sendNoteOn(3,127,5);  
    Bounce bounceLast = Bounce(buttonLast,5); 
  } 
  
    if (nextwas == LOW && nextState == HIGH) {     
    MIDI.sendNoteOn(4,127,5);
    Bounce bounceNext = Bounce(buttonNext,5);
  } 
 
}

Note: I have changed the title of the thread to 'Send Once' rather than 'Run Once' because, of course, I want the sketch to continue running, but only send the MIDI command once when a button is pushed.

Something like:

  goState = digitalRead(buttonGo);

  if (goState == LOW && previousGoState == HIGH)  // transition?
    {

    // do stuff

    } // end of transition

  previousGoState = goState;  // remember so we detect transitions

That's assuming you have the buttons on a pull-up, so that pressing the button makes it go LOW (default: HIGH).

Ah! Thanks so much. This is getting me closer and I am seeing the logic now.

Some experiementation yet... It looks like I might have to fuss with debounce times or something because I am not yet recording 100% consistent results with button pushes but I am definitely seeing considerable progress, thanks to your help!

A simple debounce is:

  goState = digitalRead(buttonGo);

  if (goState == LOW && previousGoState == HIGH)  // transition?
    {
    delay (10);   // debounce

    // do stuff

    } // end of transition

  previousGoState = goState;  // remember so we detect transitions

You can do fancier ones that don't involve doing a delay.

Indeed, I will hope to do something that doesn't involve a delay, because as I understand it, while the delay is counted, the sketch is essentially paused. This is why I included the bounce library; so that the sketch will remain active.

Sure, the Bounce library will do it without delays.

I re-read the Bounce documentation which allowed me a more comprehensive understanding of what that library can do for me, and I think that I have found a more elegant way to program this sketch by using Bounce to tidy up some redundancies in my sketch.

That said, I am still frequently recording more than one MIDI message per button contact. I am going to try to some different buttons when I can get some later in the week, but if there is any further wisdom on this problem it would be much appreciated!

I have tried bounce values as small as 5 ms and as great as 1000 ms with this code:
Also, further to a comment from Nick Gammon earlier in this thread, the buttons are on pull-down, not pull-up. (Is pull-up better and why?)

#include <MIDI.h>
#include <Bounce.h>

// set the pins:
const int buttonStop = 2; 
const int buttonGo = 3;
const int buttonLast = 4;
const int buttonNext = 5;   

// debounce the pushbuttons at 500 milliseconds:
Bounce bounceStop = Bounce(buttonStop, 5); 
Bounce bounceGo = Bounce(buttonGo, 5); 
Bounce bounceLast = Bounce(buttonLast, 5); 
Bounce bounceNext = Bounce(buttonNext, 5); 

void setup() {
  
  MIDI.begin();   
    	
  // initialize the pushbutton pin as an input:
  pinMode(buttonStop, INPUT);  
  pinMode(buttonGo, INPUT);
  pinMode(buttonLast, INPUT);
  pinMode(buttonNext, INPUT);  
}

void loop(){

// check to see if the state of the buttons has changed. if a button has been pushed, send a MIDI message:
    if (bounceStop.update() && bounceStop.read() == HIGH) {   
     MIDI.sendNoteOn(1,127,5);
     Bounce bounceStop = Bounce(buttonStop, 100); 
  } 
  
    if (bounceGo.update() && bounceGo.read() == HIGH) {       
       MIDI.sendNoteOn(2,127,5);
       Bounce bounceGo = Bounce(buttonGo, 100); 
    }
  
    if (bounceLast.update() && (bounceLast.read() == HIGH)) {       
       MIDI.sendNoteOn(3,127,5);  
       Bounce bounceLast = Bounce(buttonLast, 100);
    } 
  
    if (bounceNext.update() && bounceNext.read() == HIGH) {       
       MIDI.sendNoteOn(4,127,5);
       Bounce bounceNext = Bounce(buttonNext, 100);
    }
 
}

I am immensely grateful for your help and guidance.

sonvivant:
Also, further to a comment from Nick Gammon earlier in this thread, the buttons are on pull-down, not pull-up. (Is pull-up better and why?)

void setup() {

MIDI.begin();   
  // initialize the pushbutton pin as an input:
  pinMode(buttonStop, INPUT); 
  pinMode(buttonGo, INPUT);
  pinMode(buttonLast, INPUT);
  pinMode(buttonNext, INPUT); 
}

...

The simplest thing is to have the buttons wired, from the pin to ground, so when you press it, the pin is grounded. To make this work you need pull-up, eg.

void setup() {
  
  MIDI.begin();     	
  // initialize the pushbutton pin as an input:
  pinMode(buttonStop, INPUT_PULLUP);  
  pinMode(buttonGo, INPUT_PULLUP);
  pinMode(buttonLast, INPUT_PULLUP);
  pinMode(buttonNext, INPUT_PULLUP); 
}

...

Now the (weak) pull-up keeps the pins high, and when you push the button they go low. So you look for a high -> low transition.