Debouncing a button array for MIDI

Hi People,

I've spent the last few days writing up some code trying to get 4 buttons, with a long debounce (conductive fabric) and a panic timer (in case the material twists) working, with very little success. This is my first time working with arrays and I thought I had it nutted, but I just don't know where I've gone wrong. Any help would be appreciated.

// constants won't change. They're used here to 
// set pin numbers:
const int inputPin[] = {2, 4, 7, 8}; // these are the pins you connect the buttons to.
const int ledPin[] = {3, 5, 6, 9};
const int qty = sizeof(inputPin) / sizeof(inputPin[0]);

// Variables will change:
int ledState[qty];         // the current state of the output pin
boolean ledDir[qty];       // switch to tell LED to fade up or down
int buttonState[qty];             // the current reading from the input pin
int buttonRead[qty];
unsigned long microsStart;

int lastButtonState[] = {0, 0, 0, 0};   // the previous reading from the input pin
byte midiChannels[] = {1, 2, 3, 4}; // these are the midi channels each sensor transmits on
byte midiNotes[] = {36, 37, 38, 39}; // these are the midi notes sent by the sensors
byte midiVelocities[] = {127, 127, 127, 127}; // and velocities...

int noteCount;               // Set a countdown timer for the Note On

int timer = 10;             //set a global timer for note hold (is referred to by noteCount etc.)

long previousMillis = 0;     //This is the counter used for the time, which in turn is used for the noteCount
long interval = 12;

long previousPanic = 0;
int panicTrigger = 1000;
int panicTime[] = {0, 0, 0, 0,};
int panic[qty];

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(9600);

  for (int i = 0; i < qty; i++)
  {
    pinMode(inputPin[i], INPUT_PULLUP);
    buttonState[i] = 0xFF;
    buttonRead[i] = 1;
    pinMode(ledPin[i], OUTPUT);
  }
}

void loop() {

  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;

    unsigned long currentPanic = millis();

    //    if (currentPanic - previousPanic > panicTrigger) {  //SORT THIS OUT!!!
    //    previousPanic = currentPanic;

    buttonFilter();

    for (int j = 0; buttonRead[j] < qty; j++) {
      // send note
      for (int k = 0; panicTime[k] < panicTrigger; k++)  {
        if (buttonRead[j] == 1 && panicTime[k] < panicTrigger)  {
          panicTime[k]++;
        }
        else  {                 //may need to remove
          panicTime[k] = 0;
        }
        if (panicTime[k] >= panicTrigger)  {
          panic[k] = 1;
          Serial.println("panic at button [k]");
        }
      }
      if (buttonRead[j] == 1 && panic[j] != 1)  {
        noteOn(0x8f+midiChannels[j], midiNotes[j], midiVelocities[j]);
        if (ledDir[j] == 1)  {
          ledState[j]++;
        }
        if (ledDir[j] == 0)  {
          ledState[j]--;
        }
        //          if (panic[j] == 1)  {
        //          // NEED OME NOTEOFF HERE
        //        noteOff(0x7f+midiChannels[j], midiNotes[j], midiVelocities[j]);       
        //      }
      }
    }
    // loop from the highest pin to the lowest:
    for (int j = qty - 1; j >= 0; j--) { 
      // send note
      if (buttonRead[j] == 1 && panic[j] != 1)  {
        noteOn(0x8f+midiChannels[j], midiNotes[j], midiVelocities[j]);
        ledState[j] + 1;
      }
      /*      else (buttonRead[j] == 1 && panic[j] != 1 && ledState[j] > 0)  {
       noteOn(0x8f+midiChannels[j], midiNotes[j], midiVelocities[j]);
       ledState[j]--;
       } */
      if (panic[j] == 1)  {
        noteOff(0x7f+midiChannels[j], midiNotes[j], midiVelocities[j]);        
        ledState[j] = 0;
      }
    }
  }
  for (int k = 0; ledPin[k] < qty; k++)  {
    analogWrite(ledPin[k], ledState[k]);
  }
}

void buttonFilter(void)
{
  if (micros() - microsStart >= 2000) // minimum interval between bounces = 2 ms
  {
    for (int i = 0; i < qty; i++)
    {
      buttonState[i] = (buttonState[i] << 1) | digitalRead(inputPin[i]); // shift and read
      if ((buttonState[i] & B11111) == B01111) // if rising and high for 3 stable reads
      {
        buttonRead[i] = 1;
      }
      if ((buttonState[i] & B11111) == B10000) // if falling and low for 3 stable reads
      {
        buttonRead[i] = 0;
      }
    }
  }
  microsStart = micros();
}

//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}
void noteOff(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

You have:

const int inputPin[] = {
  2, 4, 7, 8}; // these are the pins you connect the data lines to. Pins 1 amd 2 are already taken by the Sparkfun Midi Shield...

to define those pins, but the comment says pin 2 is already taken. Is that the way it should be?

You also have:

  if (ledState ==

which prevents the code from compiling.

Thanks for noting those mistakes, it should read 0 and 1 (serial TX and RX), and the ledState was accidently posted after I started working on it again and my original copy to clipboard didn't work. woops

Also the baud rate is set for the serial terminal(9600baud), not midi (31250baud)

Edits made. Thanks

const int qty = sizeof(inputPin);

That doesn't work that way. What you've assigned to qty is the size of the whole array. It works like this:

const int qty = sizeof(inputPin) / sizeof(inputPin[0]);

Basically the size of the whole array divided by the size of one element.

I'd suggest you may want to do some research into structures.

Thanks. I've copied and pasted from a few other forum topics, and actually had no hardware to test past checking to see if it would compile.

Thank you for your suggestion, Feel free to point me in the direction of anything you would recommend, I'm pretty time poor, but I'm really trying to learn as much as I can.

I'll make the edits to my original post.

http://www.cplusplus.com/doc/tutorial/variables/

http://www.cplusplus.com/doc/tutorial/structures/

There's a couple.

Ok, rewritten, but I cannot get the second variable && lastButtonRead[i] != 1 to work. When it is commented out, everything works, except it sent the midi note everytime through, which I do not want. Can anyone tell me what i'm doing wrong?

/*
Some half-baked **** by AC
 Jul 2015
 
 mostly stolen from:
 
 Tom Igoe 
 http://www.arduino.cc/en/Tutorial/Midi
 
 David A. Mellis
 
 Ladyada herself - Limor Fried
 http://www.arduino.cc/en/Tutorial/Debounce
 */


// constants won't change. They're used here to 
// set pin numbers:
const int inputPin[] = {2, 4, 7, 8}; // these are the pins you connect the data lines to. Pins 1 amd 2 are already taken by the Sparkfun Midi Shield...
const int ledPin[] = {3, 5, 6, 9};
const int qty = sizeof(inputPin) / sizeof(inputPin[0]);

// Variables will change:
int ledState[qty];         // the current state of the output pin
boolean ledDir[qty];       // switch to tell LED to fade up or down
int fadeAmnt[qty] = {1, 1, 1, 1};
boolean buttonState[qty];             // the current reading from the input pin
boolean buttonRead[qty];
boolean lastButtonRead[qty];

long previousMillis = 0;
long interval = 2;

unsigned long microsStart;

int lastButtonState[] = {0, 0, 0, 0};   // the previous reading from the input pin
byte midiChannels[] = {1, 2, 3, 4};     // these are the midi channels each sensor transmits on
byte midiNotes[] = {36, 37, 38, 39};    // these are the midi notes sent by the sensors
byte midiVelocities[] = {127, 127, 127, 127};   // and velocities...

void setup() {
  //  Set baud rate:
  Serial.begin(9600);  // MIDI (31250)

  for (int i = 0; i < qty; i++)
  {
    pinMode(inputPin[i], INPUT_PULLUP);
    buttonState[i] = 0xFF;
    buttonRead[i] = 1;
    pinMode(ledPin[i], OUTPUT);
  }
}

void loop() {
  
  unsigned long currentMillis = millis();
   
  buttonFilter();
  
  for (int i = 0; i < qty; i++)  {
    if (buttonRead[i] == 0 && lastButtonRead[i] != 1)  { //&& buttonDir[i] == 0
      Serial.print("yes, you're doing it right ");
      Serial.println(i);
      noteOn(0x8f+midiChannels[i], midiNotes[i], midiVelocities[i]);
      lastButtonRead[i] = 1;
      fadeAmnt[i] = 15;
      ledDir[i] = 1;
    }
    
    if (buttonRead[i] == 1 && lastButtonRead[i] != 0) {   // && buttonDir[i] == 1
      Serial.print("c'mon, touch me ");
      Serial.println(i);
      noteOff(0x7f+midiChannels[i], midiNotes[i], midiVelocities[i]);
      lastButtonRead[i] = 0;
      fadeAmnt[i] = 5;
      ledDir[i] = 0; 
    }
  
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
      
  if(ledDir[i] == 1 && ledState[i] > 0) {
    ledState[i] = ledState[i] + fadeAmnt[i];  
  }
      else if( ledDir[i] == 0 && ledState[i] < 255){
       ledState[i] = ledState[i] - fadeAmnt[i];
      }
  if(ledState[i] == 255){
    ledDir[i] = 1;
  }
  if(ledState[i] == 0){
    ledDir[i] = 1;  
  }
  }
    analogWrite(ledPin[i], ledState[i]);
  }
}

void buttonFilter(void)
{
  if (micros() - microsStart >= 2000) // minimum interval between bounces = 2 ms
  {
    for (int i = 0; i < qty; i++)
    {
      buttonState[i] = (buttonState[i] << 1) | digitalRead(inputPin[i]); // shift and read
      if ((buttonState[i] & B11111) == B01111) // if rising and high for 3 stable reads
      {
        buttonRead[i] = 1;
      }
      if ((buttonState[i] & B11111) == B10000) // if falling and low for 3 stable reads
      {
        buttonRead[i] = 0;
      }
    }
  }
  microsStart = micros();
}

//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}
void noteOff(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

Ok, so I almost got it working... It needed an else after the if/s for the buttonReads... also the else NEEDS to have a serial command in it, can anyone explain why?

// constants won't change. They're used here to 
// set pin numbers:
const int inputPin[] = {2, 3, 6, 8}; // these are the pins you connect the buttons to.
const int ledPin[] = {5, 9, 10, 11};
const int qty = sizeof(inputPin) / sizeof(inputPin[0]);

// Variables will change:
int ledState[qty];         // the current state of the output pin
boolean ledDir[qty];       // switch to tell LED to fade up or down
int fadeAmnt[qty] = {1, 1, 1, 1};     // set different fade speed levels
int buttonState[qty];             // the current reading from the input pin
boolean buttonRead[qty];
boolean lastButtonRead[qty];
unsigned long microsStart;

//int lastButtonState[] = {0, 0, 0, 0};   // the previous reading from the input pin
byte midiChannels[] = {1, 2, 3, 4}; // these are the midi channels each sensor transmits on
byte midiNotes[] = {36, 37, 38, 39}; // these are the midi notes sent by the sensors
byte midiVelocities[] = {127, 127, 127, 127}; // and velocities...

//int noteCount;               // Set a countdown timer for the Note On

//int timer = 10;             //set a global timer for note hold (is referred to by noteCount etc.)

long previousMillis = 0;     //This is the counter used for the time, which in turn is used for the noteCount
long interval = 12;

//long previousPanic = 0;
//int panicTrigger = 1000;
//int panicTime[] = {0, 0, 0, 0,};
//int panic[qty];

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  //Serial2.begin(9600)

  for (int i = 0; i < qty; i++)
  {
    pinMode(inputPin[i], INPUT_PULLUP);
    buttonState[i] = 0xFF;
    buttonRead[i] = 1;
    lastButtonRead[i] = 1;
    pinMode(ledPin[i], OUTPUT);
  }
}

void loop() {

  buttonFilter();

  for (int j = 0; j < qty; j++) {

    if (buttonRead[j] == 0 && lastButtonRead[j] != 1)  
    {
      Serial.print("button ");
      Serial.print(j);
      Serial.println(" pressed");
      noteOn(0x8f+midiChannels[j], midiNotes[j], midiVelocities[j]);
      lastButtonRead[j] = 1;
      fadeAmnt[j] = 10;
      ledDir[j] = 1;
    }
    if (buttonRead[j] == 1 && lastButtonRead[j] != 0)  
    {
      Serial.print("button ");
      Serial.print(j);
      Serial.println(" released");
      noteOff(0x7f+midiChannels[j], midiNotes[j], midiVelocities[j]);
      lastButtonRead[j] = 0;
      fadeAmnt[j] = 30;
      ledDir[j] = 0; 
    }
    else 
    {
     Serial.println("");
    }
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval)  {
   previousMillis = currentMillis;

    if((ledDir[j] == 0) && (ledState[j] >  0))  
    {
      ledState[j] = ledState[j] - fadeAmnt[j];  
    }
     if((ledDir[j] == 1) && (ledState[j] < 255))  
    {
     ledState[j] = ledState[j] + fadeAmnt[j];
    }
    if((ledState[j]) >= (255 - fadeAmnt[j])){
      ledDir[j] = 0;
    }
    if((ledState[j]) <= (0 + fadeAmnt[j])){
      ledDir[j] = 1;  
    }
  }
  analogWrite(ledPin[j], ledState[j]);
  }
}

void buttonFilter(void)
{
  if (micros() - microsStart >= 2000) // minimum interval between bounces = 2 ms
  {
    for (int i = 0; i < qty; i++)
    {
      buttonState[i] = (buttonState[i] << 1) | digitalRead(inputPin[i]); // shift and read
      if ((buttonState[i] & B11111) == B01111) // if rising and high for 3 stable reads
      {
        buttonRead[i] = 1;
      }
      if ((buttonState[i] & B11111) == B10000) // if falling and low for 3 stable reads
      {
        buttonRead[i] = 0;
      }
    }
  }
  microsStart = micros();
}

//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}
void noteOff(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

Hi,

I've posted about tis issue in another topic, but I havn't gotten a reply and can't for the life of me, figure out what is going on. (Debouncing a button array for MIDI - Programming Questions - Arduino Forum)

I ave 4x button, that when pressed, each send a midi note, and light a LED. which is fine, and working. The trouble is when a button isn't being pressed, I am forced to send a serial command (Serial.println("")) in the "else" condition. This is flooding the serial tx and causing problems with the midi receiver.

Here is the most up to date version of my code

// constants won't change. They're used here to 
// set pin numbers:
const int inputPin[] = {2, 3, 4, 6}; // these are the pins you connect the buttons to.
const int ledPin[] = {5, 9, 10, 11};
const int qty = sizeof(inputPin) / sizeof(inputPin[0]);
const int dummyPin = 6;

// Variables will change:
int ledState[qty];         // the current state of the output pin
boolean ledDir[qty];       // switch to tell LED to fade up or down
int fadeAmnt[qty] = {1, 1, 1, 1};     // set different fade speed levels
int buttonState[qty];             // the current reading from the input pin
boolean buttonRead[qty];
boolean lastButtonRead[qty];
unsigned long microsStart;

//int lastButtonState[] = {0, 0, 0, 0};   // the previous reading from the input pin
byte midiChannels[] = {1, 2, 3, 4}; // these are the midi channels each sensor transmits on
byte midiNotes[] = {36, 37, 38, 39}; // these are the midi notes sent by the sensors
byte midiVelocities[] = {127, 127, 127, 127}; // and velocities...

//int noteCount;               // Set a countdown timer for the Note On

//int timer = 10;             //set a global timer for note hold (is referred to by noteCount etc.)

long previousMillis = 0;     //This is the counter used for the time, which in turn is used for the noteCount
long interval = 12;

//long previousPanic = 0;
//int panicTrigger = 1000;
//int panicTime[] = {0, 0, 0, 0,};
//int panic[qty];

void setup() {
  //  Set MIDI baud rate:
  //Serial.begin(31250);
  Serial.begin(9600);

  for (int i = 0; i < qty; i++)
  {
    pinMode(inputPin[i], INPUT_PULLUP);
    buttonState[i] = 0xFF;
    buttonRead[i] = 1;
    lastButtonRead[i] = 1;
    pinMode(ledPin[i], OUTPUT);
  }
  pinMode(dummyPin, OUTPUT);
}

void loop() {

  buttonFilter();

  for (int j = 0; j < qty; j++) {

    if (buttonRead[j] == 0 && lastButtonRead[j] != 1)  
    {
      Serial.print("button ");
      Serial.print(j);
      Serial.println(" pressed");
      noteOn(0x8f+midiChannels[j], midiNotes[j], midiVelocities[j]);
      lastButtonRead[j] = 1;
      fadeAmnt[j] = 5;
      ledState[j] = 200;
      ledDir[j] = 1;
      digitalWrite(dummyPin, LOW);
    }
    if (buttonRead[j] == 1 && lastButtonRead[j] != 0)  
    {
      Serial.print("button ");
      Serial.print(j);
      Serial.println(" released");
      noteOff(0x7f+midiChannels[j], midiNotes[j], midiVelocities[j]);
      lastButtonRead[j] = 0;
      fadeAmnt[j] = 15;
      ledState[j] = 100;
      ledDir[j] = 0;
      digitalWrite(dummyPin, LOW);
    }
    else
    {
      Serial.println("");
      digitalWrite(dummyPin, HIGH);
    }
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval)  {
   previousMillis = currentMillis;

    if((ledDir[j] == 0) && (ledState[j] >  0))  
    {
      ledState[j] = ledState[j] - fadeAmnt[j];  
    }
     if((ledDir[j] == 1) && (ledState[j] < 255))  
    {
     ledState[j] = ledState[j] + fadeAmnt[j];
    }
    if((ledState[j]) >= (255 - fadeAmnt[j])){
      ledDir[j] = 0;
    }
    if((ledState[j]) <= (0 + fadeAmnt[j])){
      ledDir[j] = 1;  
    }
  }
  analogWrite(ledPin[j], ledState[j]);
  }
  delay(1);
}

void buttonFilter(void)
{
  if (micros() - microsStart >= 2000) // minimum interval between bounces = 2 ms
  {
    for (int i = 0; i < qty; i++)
    {
      buttonState[i] = (buttonState[i] << 1) | digitalRead(inputPin[i]); // shift and read
      if ((buttonState[i] & B11111) == B01111) // if rising and high for 3 stable reads
      {
        buttonRead[i] = 1;
      }
      if ((buttonState[i] & B11111) == B10000) // if falling and low for 3 stable reads
      {
        buttonRead[i] = 0;
      }
    }
  }
  microsStart = micros();
}

//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}
void noteOff(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

Any help will be greatly appreciated

The trouble is when a button isn't being pressed, I am forced to send a serial command ...

What is forcing you, exactly?

If I comment that line out, the button presses stop registering. It's doing my head in :confused:

If doing a serial print prevents some sort of problem, it is the time taken to do it that is helping. Try a small delay. Possibly the button needs debouncing.

Ahh, I had just added a 1ms delay, I'll nudge that up a little and see if it helps. I take this means my code is rubbish. I've spent a bit of time reading Jimmy60's suggestions, and realising I have very little idea what i'm doing. Thank you for your help.

BlueJayLouche:
I take this means my code is rubbish.

I wouldn't go that far. Timing issues have caught out a lot of experienced programmers.