Make toggle buttons to send data on release

Hello,

I've searched the whole internet soon I think, but no answers out there. I'm trying to make toggle buttons to send data only when released, and it would be a good plan to make it not send anything while pressed to not overflood the midi protocol I'm using.

I think that I should be able to use pretty much the same code as in mode 1 with the temporary reading, since it sends only when released. But it's getting confusing when it's a toggle button as in mode 4.

The temporary reading are interfering with the buttonstate i guess?

Do you see my idea of how to solve it? What am I missing?

Original:

//MULTIPLEXER 5
//DIGITAL IN FOR BUTTONS ________________________________
  for (int i = 0; i < 16; i++) { //loop through 16 mux channels
      if (toReadDigitalMux5[i] == 1) { //we read the choosen mux channel input
        tempDigitalRead5 = readMux5(i);
        if (pushbuttonMode[i] == 1 && tempDigitalRead5 != buttonState[i]) { //___NORMAL MODE (1)
          delay(20); //just a delay for noise to ensure push button was actually hit
          if (readMux5(i) == tempDigitalRead5) { //check if pushbutton is still the same
            if (tempDigitalRead5 == LOW) { //button pressed, turn note on
              midiSend('p', 1, i); //call note on/off function
            } else { //button released
              midiSend('p', 0, i);
            }
            buttonState[i] = tempDigitalRead5; //update the state (on or off)
          }
        } else { //___ALL OTHER MODES (2,3,4)
          if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce) { //check bounce time
            if (pushbuttonMode[i] == 2) { //mode 2 - only note on
              midiSend('p', 1, i);
            } else if (pushbuttonMode[i] == 3) { //mode 3 - only note off
              midiSend('p', 0, i);
            } else { //mode 4 - toggle
              if (buttonState[i] == 1) { //on->off
                midiSend('p', 0, i);
                buttonState[i] = 0;
              } else { //off->on
                midiSend('p', 1, i);
                buttonState[i] = 1;
              }
            }
            timeHit[i] = millis();
          }
        }
      }
    }

Edited:

//MULTIPLEXER 5
//DIGITAL IN FOR BUTTONS ________________________________
  for (int i = 0; i < 16; i++) { //loop through 16 mux channels
      if (toReadDigitalMux5[i] == 1) { //we read the choosen mux channel input
        tempDigitalRead5 = readMux5(i);
        if (pushbuttonMode[i] == 1 && tempDigitalRead5 != buttonState[i]) { //___NORMAL MODE (1)
          delay(20); //just a delay for noise to ensure push button was actually hit
          if (readMux5(i) == tempDigitalRead5) { //check if pushbutton is still the same
            if (tempDigitalRead5 == LOW) { //button pressed, turn note on
              midiSend('p', 1, i); //call note on/off function
            } else { //button released
              midiSend('p', 0, i);
            }
            buttonState[i] = tempDigitalRead5; //update the state (on or off)
          }
        } else { //___ALL OTHER MODES (2,3,4)
          if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce) { //check bounce time
            if (pushbuttonMode[i] == 2) { //mode 2 - only note on
              midiSend('p', 1, i);
            } else if (pushbuttonMode[i] == 3) { //mode 3 - only note off
              midiSend('p', 0, i);
            } else { //mode 4 - toggle

           if (readMux5(i) == tempDigitalRead5) { //check if pushbutton is still the same
            if (tempDigitalRead5 == LOW) { // if pressed > do nothing
              } else { //if released > send data
              if (buttonState[i] == 1) { //on->off
                midiSend('p', 0, i);
                buttonState[i] = 0;
              } else { //off->on
                midiSend('p', 1, i);
                buttonState[i] = 1;
              }
              }
             } 
            }
            timeHit[i] = millis();
          }
        }
      }
    }

The complete working code on GitHub, sending data when button is pressed, instead of released:
https://github.com/Imbecillen/midi-controller/blob/master/Teensy_monster_edit_buttons_simple_working.ino

I think you want to change this line:

      if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce)   //check bounce time

to this:

      if (readMux5(i) == HIGH && (millis() - timeHit[i]) > pbBounce)   //check bounce time

johnwasser:
I think you want to change this line:

      if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce)   //check bounce time

to this:

      if (readMux5(i) == HIGH && (millis() - timeHit[i]) > pbBounce)   //check bounce time

If I just change the line it's sending constantly, but it must be something with changing from LOW to HIGH to make this tweak work out. If I can use this line further down in mode 4 maybe... And isolate it so it's just sends when tempDigitalRead5 is HIGH as well? It's so many LOW & HIGH to consider in this case, haha! I need to think about it, thanks!

Imbecillen:
If I just change the line it's sending constantly,

I see now. Mode 4 doesn't bother to check for a button state changes. It will repeatedly toggle for as long as the button is pressed. By changing from LOW to HIGH it will toggle for as long as the button is NOT pressed.
Since you are only using Mode 4 you can greatly simplify the code by getting rid of the Mode 1, Mode 2, and Mode 3 parts. Then it is easier to add the State Change Detection and Debounce. NOTE: You need a new array of toggle states since the original code used "buttonState" for the toggle state, leaving you with no way to detect button state changes.

//MULTIPLEXER 5
//DIGITAL IN FOR BUTTONS ________________________________
for (int i = 0; i < 16; i++)   //loop through 16 mux channels
{
  if (!toReadDigitalMux5[i])  
    continue;   // This channel is not marked for reading


  unsigned long currentTime = millis();
  static unsigned long lastStateChangeTime = 0; // For debounce
  
  tempDigitalRead5 = readMux5(i);
  // StateChangeDetection and Debounce
  if (tempDigitalRead5 != buttonState[i] && currentTime - lastStateChangeTime > 20UL)
  {
    buttonState[i] = tempDigitalRead5;
    lastStateChangeTime = currentTime;


    if (tempDigitalRead5 == HIGH)   // Button just released
    {
      toggleState[i] = !toggleState[i];  // Toggle
      midiSend('p', toggleState[i], i);
    }
  }
}

johnwasser:
I see now. Mode 4 doesn't bother to check for a button state changes. It will repeatedly toggle for as long as the button is pressed. By changing from LOW to HIGH it will toggle for as long as the button is NOT pressed.
Since you are only using Mode 4 you can greatly simplify the code by getting rid of the Mode 1, Mode 2, and Mode 3 parts. Then it is easier to add the State Change Detection and Debounce. NOTE: You need a new array of toggle states since the original code used "buttonState" for the toggle state, leaving you with no way to detect button state changes.

I was trying your code and it first of all goes through all pins and sets the buttons to HIGH when starting the microcontroller, not to weird but it reads randomly. Not in any specific order.

The release function works though, besides when I press button 1, 5, 9 and 12 as it says that I have pressed several buttons at the same time. On the other buttons it works as intended. Super happy about this! :slight_smile:

I would like to keep the other modes since I want my code flexible, when my code is finished I want to build various midi-controllers with different functions. I tried to fit your code into the old structure but didn't come up with a good result. I will continue and try more, but I think my brain needs some rest from this project, it's about to get complex and the bugs are getting piled on each other as I continue..

Thanks for your help so long!

Imbecillen:
I tried to fit your code into the old structure but didn't come up with a good result.

Since the existing code splits out the mode before it adds State Change Detection or Debounce you will have to either re-arrange the code or add State Change Detection and Debounce in each mode. You will also have to add a toggleState array since your can't use buttonState for both State Change Detection and Toggle.

//MULTIPLEXER 5
//DIGITAL IN FOR BUTTONS ________________________________
for (int i = 0; i < 16; i++)   //loop through 16 mux channels
{
  if (toReadDigitalMux5[i] == 1)   //we read the choosen mux channel input
  {
    tempDigitalRead5 = readMux5(i);


    // Look for a state change and debounce
    if (tempDigitalRead5 != buttonState[i]  && millis() - timeHit[i] > 20)
    {
      buttonState[i] = tempDigitalRead5; //update the state (LOW=PRESSED, HIGH=RELEASED)
      timeHit[i] = millis();


      if (pushbuttonMode[i] == 1)   //___NORMAL MODE (1)
      {
        midiSend('p', tempDigitalRead5 == LOW, i); // note on/off when button is PRESSED/RELEASED
      }
      else if (pushbuttonMode[i] == 2)   //mode 2 - only note on
      {
        if (tempDigitalRead5 == LOW)  // Send note on when button is PRESSED (Active LOW)
          midiSend('p', 1, i);
      }
      else if (pushbuttonMode[i] == 3)     //mode 3 - only note off
      {
        if (tempDigitalRead5 == LOW)  // Send note off when button is PRESSED (Active LOW)
          midiSend('p', 0, i);
      }
      else     //mode 4 - toggle
      {
        if (tempDigitalRead5 == HIGH)   // Toggle on button RELEASE  (Active LOW)
        {
          toggleState[i] = !toggleState[i];
          midiSend('p', toggleState[i], i);
        }
      }
    }
  }
}

You might be interested in my MIDI Controller library. It handles debouncing, state change detection, filtering, etc. for you, and it has built-in support for multiplexers.

Pieter

PieterP:
You might be interested in my MIDI Controller library. It handles debouncing, state change detection, filtering, etc. for you, and it has built-in support for multiplexers.

Pieter

I have actually already checked it out, but it was a long time ago and I can imagine it's been updated since. My knowledge as well, but not my patience so I will definitely check it out! Thanks :slight_smile:

johnwasser:
Since the existing code splits out the mode before it adds State Change Detection or Debounce you will have to either re-arrange the code or add State Change Detection and Debounce in each mode. You will also have to add a toggleState array since your can't use buttonState for both State Change Detection and Toggle.

Sweet!

Your code works great, and it also looks neater and easier to understand than the original! Unfortunately the bug is still there. When pressing button 1, 5, 9 and 13 they keep sending random midi messages from random pins, compared to the other buttons which just sends one message even if the buttons is held down and also from the right pin.

The complete code on GitHub

I will take a further look at it when I got some more energy. It's most likely something in the code at least, the hardware seem to work as it should.

This is what the serial monitor says, starting from button 16, 15, 14 to show the correct message and then 13 which is going random until I release the button. Button 13 are for the most showing pin 0 and 12.

pin   = 15
state = 1

pin   = 14
state = 1

pin   = 13
state = 1

pin   = 0
state = 0

pin   = 0
state = 1

pin   = 0
state = 0

pin   = 0
state = 1

pin   = 12
state = 0

pin   = 0
state = 0

While button 1 is showing pin 0, 4 and 8.

pin   = 4
state = 1

pin   = 4
state = 0

pin   = 4
state = 1

pin   = 8
state = 1

pin   = 4
state = 0

pin   = 8
state = 0

pin   = 0
state = 1

pin   = 4
state = 1

pin   = 8
state = 1

pin   = 4
state = 0

pin   = 4
state = 1

pin   = 8
state = 0

pin   = 4
state = 0

pin   = 4
state = 1

pin   = 4
state = 0

pin   = 4
state = 1

pin   = 4
state = 0

...and button 5:

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 4
state = 0

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 4
state = 1

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 8
state = 0

pin   = 8
state = 1

pin   = 8
state = 0

pin   = 4
state = 0

pin   = 8
state = 1

...and 9!

pin   = 12
state = 0

pin   = 0
state = 1

pin   = 8
state = 0

pin   = 12
state = 1

pin   = 0
state = 0

pin   = 12
state = 0

pin   = 12
state = 1

pin   = 8
state = 1

pin   = 12
state = 0

pin   = 0
state = 1

pin   = 12
state = 1

pin   = 12
state = 0

pin   = 12
state = 1

pin   = 12
state = 0

pin   = 12
state = 1

pin   = 12
state = 0

pin   = 8
state = 0

pin   = 0
state = 0

The symptoms you describe sound to me like they may be due to inputs are a least partially floating. If you add an external 1K pull-up on Pin 6 do the symptoms go away?

johnwasser:
The symptoms you describe sound to me like they may be due to inputs are a least partially floating. If you add an external 1K pull-up on Pin 6 do the symptoms go away?

I tried and no difference I'm afraid, it should be something with the code bcs my first code for the buttons work great without floating, after trying it again. Sure the first code sends midi constantly while pressing a button, but the message is correct and is really on/off every each, while this code now sends totally randomly on a few pins.

It may ofc be some correlation that the new code doesn't like the hardware design as well. I will have to try some bug checking and see if it helps. If you come up with an idea I'm more than happy to listen and try it.

Yesterday I made a Git rep over the project and will keep it up to date so check it out if you want and if it can help in some way!

johnwasser:
The symptoms you describe sound to me like they may be due to inputs are a least partially floating. If you add an external 1K pull-up on Pin 6 do the symptoms go away?

I've been soldering a good fashion hole-pcb prototype of everything now so the hardware can't affect anything. I changed a bit in the code as well so I'm not sure of what reason, but the constant sending trouble disappeared. I still got the problem with double or triple-sending of midi messages at button 1, 5, 9 and 13 though. Which is in fact every forth pin. It feels like there is a logic problem with the reading somewhere...

I also tested button mode 1,2 and 3 now. It won't work at all with 1, 5, 9 and 13 but the others, feels strange. Any ideas?

Something must conflict or don't read as it should.