Debouncing multiplexed buttons

I've been working on this for hours how and nothing seems to work.

I have a Mayhew Labs Mux Sheild with 16 push buttons on the first Mux. I'm trying to debounce the input, but nothing I've tried seems to work. Here is the code I have:

#define CONTROL0 5    
#define CONTROL1 4
#define CONTROL2 3
#define CONTROL3 2

int buttonState[16];
int buttonStatePrev[16];

void setup()
{
 for(int i=0; i < sizeof(buttonState); i++){
    buttonState[i] = 0;
    buttonStatePrev[i] = 0;
  }
 pinMode(CONTROL0, OUTPUT);
  pinMode(CONTROL1, OUTPUT);
  pinMode(CONTROL2, OUTPUT);
  pinMode(CONTROL3, OUTPUT);
  
  MIDI.begin(4);    
  //Serial.begin(9600);
  Serial.begin(115200);
  
  pinMode(14, INPUT);         
  pinMode(15, OUTPUT);
  pinMode(16, INPUT);
    
  digitalWrite(14, HIGH);       
  digitalWrite(15, LOW);
  //digitalWrite(16, HIGH);
}

void loop{
 for (int i=0; i < 16; i++)
    {
      buttonStatePrev[i] = buttonState[i]; 
      buttonState[i] = digitalReadMUX(i);
      
      //Serial.println(buttonState[i]);
      
      if (buttonState[i] == 1 && buttonStatePrev[i] == 0) {
        MIDI.sendNoteOn(58-i,127,1);
      } else if(buttonState[i] == 0 && buttonStatePrev[i] == 1){
        MIDI.sendNoteOff(58-i,0,1);
      }
     
    }
}

void selectMUXPin(int pin)
{
    digitalWrite(CONTROL0, pin & 8); 
    digitalWrite(CONTROL1, pin & 4); 
    digitalWrite(CONTROL2, pin & 2);
    digitalWrite(CONTROL3, pin & 1);
}

int digitalReadMUX(int pin)
{
  selectMUXPin(pin);
  return (!digitalRead(14));
}

I tried importing the Bounce library, declaring a bounce object on pin 14 (the pin of the first mux) and in digitalReadMux return the bounce.read(), but for some reason that just completely destroys the logic - only the button on pin M01 pin 1 seems to be read (the rest do nothing) and the resulting MIDI message consists of 16x noteOn messages followed by 16 noteOff messages.

Can anyone help me out here? I've been at this for hours now, and I'm making absolutely no progress.

Bump. Can a mod please move this to Multiplexing and LEDs? Might get more help there.

void loop()
{
  for (int i=0; i < 16; i++)
  {
    buttonState[i] = digitalReadMUX(i);                 //get current button state
    if (buttonState[i] == 1 && buttonStatePrev[i] == 0) //the button state has changed
    {
      buttonStateStart[i] = millis(); 
      //note the time it changed
    }
    if ((millis() - buttonStateStart[i]) > debounceTime)//has been in this state for some time
    {
      //so do stuff here
      buttonStatePrev[i] = buttonState[i];                //remember the button state  
    }
  }
}

Yes I've seen that, but how do I incorporate this block of code:

 if (buttonState[i] == 1 && buttonStatePrev[i] == 0) {
        MIDI.sendNoteOn(58-i,127,1);
      } else if(buttonState[i] == 0 && buttonStatePrev[i] == 1){
        MIDI.sendNoteOff(58-i,0,1);
      }

Its meant to send the NoteOn when state changes from off to on and a NoteOff when its state changes from on to off. It works as is, except for the bouncing issue. When I incorporate your code, it sends nothing.

void loop()
{
  for (int i=0; i < 16; i++)
  {
    buttonState[i] = digitalReadMUX(i);                 //get current button state
    if (buttonState[i] == 1 && buttonStatePrev[i] == 0) //the button state has changed
    {
      buttonStateStart[i] = millis(); 
      //note the time it changed
    }
    
    if ((millis() - buttonStateStart[i]) > debounceTime)//has been in this state for some time
    {
      //so do stuff here
      if (buttonState[i] == 1 && buttonStatePrev[i] == 0) {
        MIDI.sendNoteOn(58-i,127,1);
      } else if(buttonState[i] == 0 && buttonStatePrev[i] == 1){
        MIDI.sendNoteOff(58-i,0,1);
      }
      
      buttonStatePrev[i] = buttonState[i];
    }
  }
}

At this point I've tried a thousand variations and I still can't get it to debounce properly; I either get a stream of NoteOns and no NoteOffs, or NoteOn NoteOffs that are still bouncing all over the place.

I don't have the hardware to try the multiplexed version using an array of button data but this is a de-bounced button state change program. Adding the arrays and reading from the multiplexed input should be relatively simple.

byte buttonPin = 8;
byte currentButtonState = 0;
byte prevButtonState = 1;
byte wasPrevButtonState = 1;
long buttonStateStart;
long debounceTime = 100;

void setup() 
{
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() 
{
  currentButtonState = digitalRead(buttonPin);
  if (currentButtonState != prevButtonState)  //button state has changed
  {
    buttonStateStart = millis();  //remember the time of the state changed
    wasPrevButtonState = prevButtonState;
    prevButtonState = currentButtonState;    //ready for the next change check 
  }

  if ((millis() - buttonStateStart) > debounceTime)  //has been in this state for some time
  {    
    if (currentButtonState == 0  && wasPrevButtonState == 1)
    {
      Serial.println("action 1");
      wasPrevButtonState = currentButtonState;        //stop the action happening again until state changes
    }
    else if (currentButtonState == 1  && wasPrevButtonState == 0)
    {
      Serial.println("action 2");
      wasPrevButtonState = currentButtonState;        //stop the action happening again until state changes
    }
  }
}

Try this:

void loop()
{
  for (int i=0; i < 16; i++)
  {
    if ((millis() - buttonStateStart[i]) > debounceTime)// Allow some time to pass between changes
    {
      buttonState[i] = digitalReadMUX(i);        //get current button state
      if (buttonState[i] != buttonStatePrev[i]) //the button state has changed
      {
        if (buttonState[i] == 1) {
          MIDI.sendNoteOn(58-i,127,1);
        }
        else {
          MIDI.sendNoteOff(58-i,0,1);
        }
        buttonStateStart[i] = millis(); 
        buttonStatePrev[i] = buttonState[i];
      }
    }
  }
}

It seems so obvious now, kicking myself! Thank you Fungus!

I think you can simplify it a bit, use less RAM:

void loop()
{
  for (int i=0; i < 16; i++)
  {
    if ((millis() - buttonStateStart[i]) > debounceTime)// Allow some time to pass between changes
    {
      byte b = digitalReadMUX(i);        //get current button state
      if (b != buttonStatePrev[i]) //the button state has changed
      {
        if (b == 1) {
          MIDI.sendNoteOn(58-i,127,1);
        }
        else {
          MIDI.sendNoteOff(58-i,0,1);
        }
        buttonStatePrev[i] = b;
        buttonStateStart[i] = millis(); 
      }
    }
  }
}