4 digital buttons, multiple combinations

Hi,

I'm about to build a simple midi controller. I have 4 tactile switches that will send out MIDI commands and light a LED so I know which MIDI effect is active. I got this part to work already, using debounce. All switches & LEDs are connected to the digital I/Os of my Arduino UNO.

So to add a little fanciness to my project. I want to send out another signal when I press two switches together at the same time. The command that is triggered by pressing two switches together should not have an effect on the LEDs. (If LED A is inactive, B is active their states should stay the same when A & B are pressed together - they should only change states if only one switch is pressed.

So far I haven't been successful as the single switch commands overrule the double switch commands.
Is there a proper example on how to do this somewhere?

Here's my code - work in progress - the X & Y stuff is supposed to cover the functions that are activated when A & B respectively C & D is pressed at once.

Thanks for your help...

/*
A simple MIDI controller
Feb. 2012
*/
 
/* IO Definition for switches & LEDs */

const int switchA = 12;
const int switchB = 11;
const int switchC = 10;
const int switchD = 9;

const int ledA = 5;
const int ledB = 4;
const int ledC = 3;
const int ledD = 2;

/* Variables for Debouncing & status changes */

int currentValA = 0;
int currentValB = 0;
int currentValC = 0;
int currentValD = 0;
int currentValX = 0;
int currentValY = 0;

int oldValA = 0;
int oldValB = 0;
int oldValC = 0;
int oldValD = 0;
int oldValX = 0;
int oldValY = 0;

int stateA = 0;
int stateB = 0;
int stateC = 0;
int stateD = 0;
int stateX = 0;
int stateY = 0;

int actionA = 0;
int actionB = 0;
int actionC = 0;
int actionD = 0;
int actionX = 0;
int actionY = 0;


int midiPgm = 1;

/* (De-)activation of effects */

void activateA() {
    if (actionA == 0) {
    Serial.println("Switch A activated");
    digitalWrite(ledA,HIGH);
    actionA = HIGH;
    }
}  

void deactivateA() {
    if (actionA == HIGH) {
    Serial.println("Switch A deactivated");
    digitalWrite(ledA,LOW);
    actionA = LOW;
    }    
}  

void activateB() {
    if (actionB == 0) {
    Serial.println("Switch B activated");
    digitalWrite(ledB,HIGH);
    actionB = HIGH;
    }
}  

void deactivateB() {
    if (actionB == HIGH) {
    Serial.println("Switch B deactivated");
    digitalWrite(ledB,LOW);
    actionB = LOW;
    }    
}  

void activateC() {
    if (actionC == 0) {
    Serial.println("Switch C activated");
    digitalWrite(ledC,HIGH);
    actionC = HIGH;
    }
}  

void deactivateC() {
    if (actionC == HIGH) {
    Serial.println("Switch C deactivated");
    digitalWrite(ledC,LOW);
    actionC = LOW;
    }    
}  
void activateD() {
    if (actionD == 0) {
    Serial.println("Switch D activated");
    digitalWrite(ledD,HIGH);
    actionD = HIGH;
    }
}  

void deactivateD() {
    if (actionD == HIGH) {
    Serial.println("Switch D deactivated");
    digitalWrite(ledD,LOW);
    actionD = LOW;
    }    
}  

void triggerX() {
    if (actionX == 0) {
    Serial.println("A & B pressed together");
    if (midiPgm > 1) { 
       midiPgm = midiPgm - 1;
       }
    else {
       midiPgm = 127;
    }
    actionX = HIGH;
    
    Serial.print("MIDI Pgm #: ");
    Serial.print(midiPgm);
    }   
}

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

  pinMode(ledA, OUTPUT);     
  pinMode(ledB, OUTPUT);      
  pinMode(ledC, OUTPUT);
  pinMode(ledD, OUTPUT);     
 
  pinMode(switchA, INPUT);
  pinMode(switchB, INPUT);
  pinMode(switchC, INPUT);
  pinMode(switchD, INPUT);
  
}


void loop() {
  
  /* reading & storing switch values */
  currentValA = digitalRead(switchA);
  currentValB = digitalRead(switchB);
  currentValC = digitalRead(switchC);
  currentValD = digitalRead(switchD);
  
  
  /* stuff that happens when two switches are pressed together
  -> MIDI program up & down */

  if ((currentValA == HIGH) && (currentValB == HIGH)) {
    stateX = 1 - stateX;
    delay(100);
  }
  
  oldValX = currentValX;
  
  if (stateX == 1) {
    triggerX();
  } 

  /* stuff that happens when only one switch is pressed */

  if ((currentValA == HIGH) && (oldValA == LOW)) {
    stateA = 1 - stateA;
    delay(100);
  }

  oldValA = currentValA;
  
  if (stateA == 1) {
    activateA();
  } else
    deactivateA();
    

    
  if ((currentValB == HIGH) && (oldValB == LOW)) {
    stateB = 1 - stateB;
    delay(100);    
  }

  oldValB = currentValB;
  
  if (stateB == 1) {
    activateB();
  } else
    deactivateB();
    
    
  if ((currentValC == HIGH) && (oldValC == LOW)) {
    stateC = 1 - stateC;
    delay(100);    
  }

  oldValC = currentValC;
  
  if (stateC == 1) {
    activateC();
  } else
    deactivateC();
    
    
  if ((currentValD == HIGH) && (oldValD == LOW)) {
    stateD = 1 - stateD;
    delay(100);    
  }

  oldValD = currentValD;
  
  if (stateD == 1) {
    activateD();
  } else
    deactivateD();

    
}

First off on this forum post code in a box. Modify your post, highlight the code and hit the #icon. Then I will look at it.

as the single switch commands overrule the double switch commands.

Yes it will if you think about it. So what you have to do is to delay the action on the single press for a short time and look to see if there is a double press, then do what is appropriate. So when you find a key press, delay and then look again for a double.

At this stage don't bother with debounce, most of the time the problems it can cause disappear naturally. In your case there will be a natural delay between detecting a key press and looking again by having to send the MIDI notes. Only if you have everything in and you get problems resort to debounce remedies.

Thanks Mike, I'll tinker some more then...

Thanks for modifying the first post.

If I were doing this then I would think about using a Boolean variable that was triggered on a key press and cleared in the key press action.
Then when you detected a key press you could set this variable and time a delay using the millis() timer (see the blink without delay example). If no other key was detected (the Boolean variable for that key) by the time the delay expired then you could do the single action, otherwise do the double one.
I hope that helps and good luck.

Perfect - thanks, that helps a lot :smiley:

So I've worked a little more on this and now it looks like this:

/*
A simple MIDI controller
Feb. 2012
*/
 
/* IO Definition for switches & LEDs */

const int switchA = 12;
const int switchB = 11;
const int switchC = 10;
const int switchD = 9;

const int ledA = 5;
const int ledB = 4;
const int ledC = 3;
const int ledD = 2;

/* Variables for status changes */

boolean switchStateA = false;
boolean switchStateB = false;
boolean switchStateC = false;
boolean switchStateD = false;

boolean actionStateA = false;
boolean actionStateB = false;
boolean actionStateC = false;
boolean actionStateD = false;


int midiPgm = 1;

/* (De-)activation of effects */

void triggerA() {
    Serial.println("Here we go2");
    if  (actionStateA == false) {
      actionStateA = true;
      activateA();
    }
    else {
      deactivateA();      
    }
}

void activateA() {
    Serial.println("Switch A activated");
    digitalWrite(ledA,HIGH);
    }
  

void deactivateA() {
    if (actionStateA == HIGH) {
    Serial.println("Switch A deactivated");
    digitalWrite(ledA,LOW);
    actionStateA = false;
    }    
}  

void activateB() {
    if (actionStateB == 0) {
    Serial.println("Switch B activated");
    digitalWrite(ledB,HIGH);
    actionStateB = true;
    }
}  

void deactivateB() {
    if (actionStateB == true) {
    Serial.println("Switch B deactivated");
    digitalWrite(ledB,LOW);
    actionStateB = false;
    }    
}  

void activateC() {
    if (actionStateC == 0) {
    Serial.println("Switch C activated");
    digitalWrite(ledC,HIGH);
    actionStateC = true;
    }
}  

void deactivateC() {
    if (actionStateC == true) {
    Serial.println("Switch C deactivated");
    digitalWrite(ledC,LOW);
    actionStateC = false;
    }    
}  
void activateD() {
    if (actionStateD == 0) {
    Serial.println("Switch D activated");
    digitalWrite(ledD,HIGH);
    actionStateD = true;
    }
}  

void deactivateD() {
    if (actionStateD == true) {
    Serial.println("Switch D deactivated");
    digitalWrite(ledD,LOW);
    actionStateD = false;
    }    
}  

/*
void triggerX() {
    if (actionStateX == 0) {
    Serial.println("A & B pressed together");
    if (midiPgm > 1) { 
       midiPgm = midiPgm - 1;
       }
    else {
       midiPgm = 127;
    }
    actionX = true;
    
    Serial.print("MIDI Pgm #: ");
    Serial.print(midiPgm);
    }   
}
*/

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

  pinMode(ledA, OUTPUT);     
  pinMode(ledB, OUTPUT);      
  pinMode(ledC, OUTPUT);
  pinMode(ledD, OUTPUT);     
 
  pinMode(switchA, INPUT);
  pinMode(switchB, INPUT);
  pinMode(switchC, INPUT);
  pinMode(switchD, INPUT);
  
}


void loop() {
  /* setting the switch states */
  
  if (digitalRead(switchA) == HIGH) {switchStateA = true; Serial.println("Here we go");}
  if (digitalRead(switchB) == HIGH) {switchStateB = true;}
  if (digitalRead(switchC) == HIGH) {switchStateC = true;}
  if (digitalRead(switchD) == HIGH) {switchStateD = true;}
  
  
  /* checking the switch states & determining the action */
  
  if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == false)) {triggerA;}
  /*
  if ((switchStateA == false) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerB;}
  if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == false)) {triggerC;}
  if ((switchStateA == false) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerD;}
  
  if ((switchStateA == true) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerX;}
  if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == true)) {triggerY;}
  
  /* more fun for later...
  if ((switchStateA == false) && (switchStateB == true) && (switchStateC == true) && (switchStateD == false)) {triggerP;}
  if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerQ;}
  */

}

It's work in progress - the millis stuff is still missing and not all of the triggers aren't finished yet.
I didn't get the led A to light up, so I set up two println commands to see what's working and what not.
I can get the "Here we go" message, but not "Here we go2" - in other words: the call of the triggerA routine doesn't work.

Currently I can't see why. Thanks for your help...

I can get the "Here we go" message, but not "Here we go2"

When calling a function that passes no parameters you still need the brackets.
So change that line to:-

  if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == false)) {triggerA();}

Thanks Mike - that’s the kind of mistake I like to kick myself for :wink:

I rewrote quite a bit of my code now. Unfortunately, I’m still not there… Please have a look at my code.
When I press switches C & D, I’ll get the Trigger Y message repeating on my serial console - as expected.
When I release the switches I usually get a state change of C and / or D as I can’t release the switches together.
I tried to implement an debounce with millis (see code comment - unsuccessfully).
I also tried to implement the complete command on Trigger X, but I can’t get this to be called at all when I press A & B together.

So how would I have to debounce this properly? I read the instructions, but I’m not sure if this should be implemented in the loop routine or if I should place separate debounces within my trigger routines.

/*
A simple MIDI controller
Feb. 2012
*/
 
/* IO Definition for switches & LEDs */

const int switchA = 12;
const int switchB = 11;
const int switchC = 10;
const int switchD = 9;

const int ledA = 5;
const int ledB = 4;
const int ledC = 3;
const int ledD = 2;

/* Variables for status changes */

boolean switchStateA = false;
boolean switchStateB = false;
boolean switchStateC = false;
boolean switchStateD = false;

boolean lastSwitchStateA = true;
boolean lastSwitchStateB = true;
boolean lastSwitchStateC = true;
boolean lastSwitchStateD = true;

boolean triggerStateA = false;
boolean triggerStateB = false;
boolean triggerStateC = false;
boolean triggerStateD = false;
boolean triggerStateX = false;
boolean triggerStateY = false;

boolean lastTriggerStateA = false;
boolean lastTriggerStateB = false;
boolean lastTriggerStateC = false;
boolean lastTriggerStateD = false;
boolean lastTriggerStateX = true;
boolean lastTriggerStateY = false;

unsigned long currentMillis;
long previousMillis = 0;
long interval = 100;

int midiPgm = 1;

/* (De-)activation of effects */

void triggerA() {
  if (triggerStateA != lastTriggerStateA) {
    if (triggerStateA == true) {Serial.println("Trigger A on"); activateA();}
    else {Serial.println("Trigger A off"); deactivateA();}
    
  lastTriggerStateA = triggerStateA;
  }
}

void triggerB() {
  if (triggerStateB != lastTriggerStateB) {
    if (triggerStateB == true) {Serial.println("Trigger B on"); activateB();}
    else {Serial.println("Trigger B off"); deactivateB();}
    
  lastTriggerStateB = triggerStateB;
  }
}

void triggerC() {
  if (triggerStateC != lastTriggerStateC) {
    if (triggerStateC == true) {Serial.println("Trigger C on"); activateC();}
    else {Serial.println("Trigger C off"); deactivateC();}
    
  lastTriggerStateC = triggerStateC;
  }
}

void triggerD() {
  if (triggerStateD != lastTriggerStateD) {
    if (triggerStateD == true) {Serial.println("Trigger D on"); activateD();}
    else {Serial.println("Trigger D off"); deactivateD();}
    
  lastTriggerStateD = triggerStateD;
  }
}

void triggerX() {
  if (triggerStateX != lastTriggerStateX) {
    if (triggerStateD == true) {Serial.println("Trigger X on"); }
    else {Serial.println("Trigger X off"); }
    
  lastTriggerStateX = triggerStateX;
  }
}

/*
void triggerX() {
  Serial.println("Trigger X");
  if (triggerStateX = lastTriggerStateX) {

    
    if (midiPgm > 1) { 
       midiPgm = midiPgm - 1;
       }
    else {
       midiPgm = 127;
    }
    
    Serial.print("MIDI Pgm #: ");
    Serial.println(midiPgm);
    
    lastTriggerStateX = !triggerStateX;
    }   
}
*/

void triggerY() {
  Serial.println("Trigger Y");
}


void activateA()   {Serial.println("Switch A activated");   digitalWrite(ledA,HIGH); }  
void deactivateA() {Serial.println("Switch A deactivated"); digitalWrite(ledA,LOW);  }
        
void activateB()   {Serial.println("Switch B activated");   digitalWrite(ledB,HIGH); }  
void deactivateB() {Serial.println("Switch B deactivated"); digitalWrite(ledB,LOW);  }

void activateC()   {Serial.println("Switch C activated");   digitalWrite(ledC,HIGH); }  
void deactivateC() {Serial.println("Switch C deactivated"); digitalWrite(ledC,LOW);  }

void activateD()   {Serial.println("Switch D activated");   digitalWrite(ledD,HIGH); }  
void deactivateD() {Serial.println("Switch D deactivated"); digitalWrite(ledD,LOW);  }


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

  pinMode(ledA, OUTPUT);     
  pinMode(ledB, OUTPUT);      
  pinMode(ledC, OUTPUT);
  pinMode(ledD, OUTPUT);     
 
  pinMode(switchA, INPUT);
  pinMode(switchB, INPUT);
  pinMode(switchC, INPUT);
  pinMode(switchD, INPUT);
  
}


void loop() {

  /* setting the switch states */
  switchStateA = digitalRead(switchA);
  if (switchStateA != lastSwitchStateA) {
    if (switchStateA == true) {Serial.println("A on");}
    else {Serial.println("A off");triggerStateA = !triggerStateA;}
  }  
  lastSwitchStateA = switchStateA;


  switchStateB = digitalRead(switchB);
  if (switchStateB != lastSwitchStateB) {
    if (switchStateB == true) {Serial.println("B on");}
    else {Serial.println("B off");triggerStateB = !triggerStateB;}
  }  
  lastSwitchStateB = switchStateB;

  switchStateC = digitalRead(switchC);
  if (switchStateC != lastSwitchStateC) {
    if (switchStateC == true) {Serial.println("C on");}
    else {Serial.println("C off");triggerStateC = !triggerStateC;}
  }  
  lastSwitchStateC = switchStateC;

  switchStateD = digitalRead(switchD);
  if (switchStateD != lastSwitchStateD) {
    if (switchStateD == true) {Serial.println("D on");}
    else {Serial.println("D off");triggerStateD = !triggerStateD;}
  }  
  lastSwitchStateD = switchStateD;


  /* checking the switch states & determining the action */
  /*
  currentMillis = millis();
  
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
 */
    if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == false)) {triggerA();}  
    if ((switchStateA == false) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerB();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == false)) {triggerC();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerD();}
  
    if ((switchStateA == true) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerX();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == true)) {triggerY();}
  
    /* more fun for later...
    if ((switchStateA == false) && (switchStateB == true) && (switchStateC == true) && (switchStateD == false)) {triggerP;}
    if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerQ;}
    */
}

When I release the switches I usually get a state change of C and / or D as I can't release the switches together.

Don't you just love software development, I guess you never thought about that, it comes with experience.

So what I would suggest is that once you have detected a double press, you set another variable and don't detect a single press until that is reset. You reset it after a certain amount of time has passed, either a delay for testing or a millis() time out when you are doing it right.

Again it is a bit early to consider debounce but for now just do a delay(20); after you detect a key change.

I messed around with it some more, tried to place the delay in the loop once the double press is detected (which works only once per compile and is very shaky) and also in the trigger routine. Both solutions didn't work out. I also tried the button class, which makes some things easier - without success so far.

I can't believe that it's that complicated... I searched the web for any examples, but couldn't find them so far. (So maybe it's not as trivial as I first thought :D)

So... If anyone has an example code that I could look at - this would be appreciated.

I put even more thought into this...

My current approach is to first collect the switches HIGH values for 20ms, then in a second step process the values and do a second read of the switch states after that so that I don't get stuck with all switches being on high - however still reflecting the ones that are held longer. For debugging, I even set the duration from 20ms to 1000, which didn't help either.

Here's the code:

/*
A simple MIDI controller
Feb. 2012
*/
 
/* IO Definition for switches & LEDs */

const int switchA = 12;
const int switchB = 11;
const int switchC = 10;
const int switchD = 9;

const int ledA = 5;
const int ledB = 4;
const int ledC = 3;
const int ledD = 2;

/* Variables for status changes */
boolean switchStateA = false;
boolean switchStateB = false;
boolean switchStateC = false;
boolean switchStateD = false;

boolean switchStateLongA = false;
boolean switchStateLongB = false;
boolean switchStateLongC = false;
boolean switchStateLongD = false;

boolean lastSwitchStateA = true;
boolean lastSwitchStateB = true;
boolean lastSwitchStateC = true;
boolean lastSwitchStateD = true;

boolean triggerStateA = false;
boolean triggerStateB = false;
boolean triggerStateC = false;
boolean triggerStateD = false;
boolean triggerStateX = false;
boolean triggerStateY = false;

boolean lastTriggerStateA = false;
boolean lastTriggerStateB = false;
boolean lastTriggerStateC = false;
boolean lastTriggerStateD = false;
boolean lastTriggerStateX = false;
boolean lastTriggerStateY = false;

unsigned long currentMillis;
long previousMillis = 0;
long interval = 20;

int midiPgm = 1;

/* (De-)activation of effects */

void triggerA() {
  if (triggerStateA != lastTriggerStateA) {
    if (triggerStateA == true) {Serial.println("Trigger A on"); activateA();}
    else {Serial.println("Trigger A off"); deactivateA();}
    
  lastTriggerStateA = triggerStateA;
  }
}

void triggerB() {
  if (triggerStateB != lastTriggerStateB) {
    if (triggerStateB == true) {Serial.println("Trigger B on"); activateB();}
    else {Serial.println("Trigger B off"); deactivateB();}
    
  lastTriggerStateB = triggerStateB;
  }
}

void triggerC() {
  if (triggerStateC != lastTriggerStateC) {
    if (triggerStateC == true) {Serial.println("Trigger C on"); activateC();}
    else {Serial.println("Trigger C off"); deactivateC();}
    
  lastTriggerStateC = triggerStateC;
  }
}

void triggerD() {
  if (triggerStateD != lastTriggerStateD) {
    if (triggerStateD == true) {Serial.println("Trigger D on"); activateD();}
    else {Serial.println("Trigger D off"); deactivateD();}
    
  lastTriggerStateD = triggerStateD;
  }
}

void triggerX() {
  if (triggerStateX != lastTriggerStateX) {
    if (triggerStateX == true) {Serial.println("Trigger X"); activateX();}
    else {Serial.println("ReTrigger X off"); deactivateX();}
    
  lastTriggerStateX = triggerStateX;
  }
}

/*
void triggerX() {
  Serial.println("Trigger X");
  if (triggerStateX = lastTriggerStateX) {

    
    if (midiPgm > 1) { 
       midiPgm = midiPgm - 1;
       }
    else {
       midiPgm = 127;
    }
    
    Serial.print("MIDI Pgm #: ");
    Serial.println(midiPgm);
    
    lastTriggerStateX = !triggerStateX;
    }   
}
*/

void triggerY() {
  Serial.println("Trigger Y");

}


void activateA()   {Serial.println("Switch A activated");   digitalWrite(ledA,HIGH); }  
void deactivateA() {Serial.println("Switch A deactivated"); digitalWrite(ledA,LOW);  }
        
void activateB()   {Serial.println("Switch B activated");   digitalWrite(ledB,HIGH); }  
void deactivateB() {Serial.println("Switch B deactivated"); digitalWrite(ledB,LOW);  }

void activateC()   {Serial.println("Switch C activated");   digitalWrite(ledC,HIGH); }  
void deactivateC() {Serial.println("Switch C deactivated"); digitalWrite(ledC,LOW);  }

void activateD()   {Serial.println("Switch D activated");   digitalWrite(ledD,HIGH); }  
void deactivateD() {Serial.println("Switch D deactivated"); digitalWrite(ledD,LOW);  }

void activateX()   {Serial.println("Switch X activated"); }  
void deactivateX() {Serial.println("Switch X deactivated");}



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

  pinMode(ledA, OUTPUT);     
  pinMode(ledB, OUTPUT);      
  pinMode(ledC, OUTPUT);
  pinMode(ledD, OUTPUT);     
 
  pinMode(switchA, INPUT);
  pinMode(switchB, INPUT);
  pinMode(switchC, INPUT);
  pinMode(switchD, INPUT);
  
}


void loop() {

  /*
  Serial.print("CurrentMillis: ");
  Serial.println(currentMillis);
  Serial.print("previoustMillis: ");
  Serial.println(previousMillis);
*/
  
  if  (currentMillis + interval > millis()) {

    Serial.println("Checking...");
    if (digitalRead(switchA)==HIGH) {switchStateA = true;}
    if (digitalRead(switchB)==HIGH) {switchStateB = true;}
    if (digitalRead(switchC)==HIGH) {switchStateC = true;}
    if (digitalRead(switchD)==HIGH) {switchStateD = true;}
  } 
  
 else{
     currentMillis = millis();
  Serial.println("Acting...");
  /* setting the switch states */
  /* switchStateA = digitalRead(switchA); */
  if (switchStateA != lastSwitchStateA) {
    if (switchStateA == true) {Serial.println("A on");}
    else {Serial.println("A off");triggerStateA = !triggerStateA;}
  }  
  lastSwitchStateA = switchStateA;
  switchStateA = digitalRead(switchA);


  /*  switchStateB = digitalRead(switchB); */
  if (switchStateB != lastSwitchStateB) {
    if (switchStateB == true) {Serial.println("B on");}
    else {Serial.println("B off");triggerStateB = !triggerStateB;}
  }  
  lastSwitchStateB = switchStateB;
  switchStateB = digitalRead(switchB);

/*  switchStateC = digitalRead(switchC); */
  if (switchStateC != lastSwitchStateC) {
    if (switchStateC == true) {Serial.println("C on");}
    else {Serial.println("C off");triggerStateC = !triggerStateC;}
  }  
  lastSwitchStateC = switchStateC;
  switchStateC = digitalRead(switchC);

/*  switchStateD = digitalRead(switchD); */
  if (switchStateD != lastSwitchStateD) {
    if (switchStateD == true) {Serial.println("D on");}
    else {Serial.println("D off");triggerStateD = !triggerStateD;}
  }  
  lastSwitchStateD = switchStateD;
  switchStateD = digitalRead(switchD);

  /* checking the switch states & determining the action */
  /* first the multiple switches */
    
    if ((switchStateA == true) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerX();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == true)) {triggerY();}

    if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == false)) {triggerA();}  
    if ((switchStateA == false) && (switchStateB == true) && (switchStateC == false) && (switchStateD == false)) {triggerB();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == true) && (switchStateD == false)) {triggerC();}
    if ((switchStateA == false) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerD();}

  
    /* more fun for later...
    if ((switchStateA == false) && (switchStateB == true) && (switchStateC == true) && (switchStateD == false)) {triggerP;}
    if ((switchStateA == true) && (switchStateB == false) && (switchStateC == false) && (switchStateD == true)) {triggerQ;}
    */
 }
}

Unfortunately, it still doesn't react to two switches at once. Currently I have no clue what else I could do to get this to work... Please help :frowning:

So maybe it’s not as trivial as I first thought

You are so right, it is quite a poser.

Anyway I had a go this afternoon and this is what I came up with, enjoy:-

// Button press example by Mike Cook Feb 2012 - no rights reserved
// Detects one press or two buttons pressed at the same time (that is the tricky bit)

// define logic levels read from buttons. 
// in my hardware buttons to ground with internal pull up enabled
#define PRESSED LOW
#define RELEASED HIGH

int pins[] = { 8, 9, 10, 11 }; // Input pins wired to buttons
boolean currentButton[4];
boolean lastButton[4];
long int startTime[4], releaseTime[4];
long int actionDelay = 200, releaseDelay = 200; // delay between first detection and action

// *************************************************
void setup() {
   Serial.begin(9600);
     for(int i = 0; i<4; i++){
   pinMode(pins[i], INPUT);
   digitalWrite(pins[i], HIGH); // enable pull ups 
  }
 Serial.println("Button press test ");
  readButtons();
  saveButtons();
}

void loop(){
  readButtons();
  lookForChange();
  lookForAction();
  saveButtons();
}

// ***************************************************
void readButtons(){
  for(int i = 0; i<4; i++){ 
  currentButton[i] = digitalRead(pins[i]); 
  if(currentButton[i] != lastButton[i]) delay(30); // debounce
  }
}

// ***************************************************
void lookForChange(){  // detect a change in the button state
  for(int i = 0; i<4; i++){
    if(lastButton[i] != currentButton[i]) { // we have a change in button state
       if(currentButton[i] == PRESSED){ // set a time for action to start
         startTime[i] = millis() + actionDelay;
         releaseTime[i] = 0;
       } 
       if(currentButton[i] == RELEASED){ // set a time for release
        startTime[i] = 0;
        releaseTime[i] = millis() + releaseDelay;
       }
    }  // end of if we have a change
  }
} // end of function

// ****************************************************
void lookForAction(){ // see if we need to do anything
    for(int i = 0; i<4; i++){ // look at all the buttons
     if(startTime[i] != 0 && millis() > startTime[i]) { // time to initiate an action
       action(i); // do what action we need to do
       for(int j = 0; j<4; j++){startTime[j] = 0 ;} // zero all start times as we have just done the action
      }
     if(releaseTime[i] != 0 && millis() > releaseTime[i]) { // time to clear pending releases
     releaseTime[i] = 0 ;
      startTime[i] = 0;
      for(int j = 0; j<4; j++){ 
       releaseTime[j] = 0 ; // zero all release time as we have just timed out one
       startTime[j] = 0;  // zero all start times as we need to start afresh
        } 
      } 
    } // end of look at all the buttons
}  // end of function

// *****************************************************
void saveButtons(){
  for(int i = 0; i<4; i++){lastButton[i] = currentButton[i]; }
} 

// *****************************************************
void action(int act){ // need to do something now based on the first button
// first see if any other buttons are being held down
boolean loneButton = true;
   for(int i = 0; i<4; i++){
      if( currentButton[i] == PRESSED && i != act) loneButton = false;
   }
  if(loneButton) { // if we just have one button pressed
  Serial.print("Button number ");
  Serial.print( act + 1);  // so we don't have button 0 displayed although we do have this in the code
  Serial.println(" is pressed");  
  } else { // if we have more than one button pressed
  // Use the first button we see that is pressed that is not the time out one
  // for(int z =0; z<4; z++) {Serial.print(currentButton[z]); Serial.print(" ");}  Serial.println(act);  // uncomment for debugging
  for(int i = 0; i<4; i++){
    // Serial.println(i);  // uncomment for debugging
     if( (currentButton[i] == PRESSED) && (i != act) ){ // do the action for two buttons pressed
       Serial.print("Button number ");
       Serial.print( act + 1);  // so we don't have button 0 displayed although we do have this in the array
       Serial.print(" and ");  
       Serial.print( i + 1 ); 
       Serial.println(" is pressed");
     } // end of do the action for two buttons pressed
   }
  } // end of else when we have more than one button pressed
  
} // end of function

Thanks Mike, I just had to change the pins and swap the PRESSED / RELEASED to get it to work for me, but now it does.
I'll need some time to play around with it now...

I had a great idea (that I didn't get to work right away) - the point in making my own MIDI controller is so that I get the functionality I need in a small box. I could pack even more different functions into this if the MIDI command is only sent once the switch has been released. That way I can measure the time it's been pressed and distinguish not only between single & double presses, but also vary the MIDI command by the length of the press. (F.e. short press on switch A enables / disables the overdrive, long press does the same for the tuner of my MIDI preamp.)

There's room for improvement of the project - I'll report back when I get it to work (or am stuck again).

Cheers,

M.