Button State Switching

Status
Not open for further replies.

digitalelements

Active member
Hi There,

I hoping for some suggestions and guidance in coding. I have two momentary buttons that when pressed, send out their MIDI note on messages.
I'd like to change the MIDI note message of one button while another butting is being held down. I've tried for several hours to come up with this code
on my own through research and trial and error. Unfortunately I'm stuck. any info would be greatly appreciated. The code I've been tinkering with is below.

Thank you !


Code:
// Button Mode Switching

#include <Bounce.h>
const int channel = 1;
byte pgcNum = 0;




Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);


void setup() {
  

  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);

}

void loop() {
  
int ledPin = 13;
int btn0State = button0.fallingEdge();
int btn1State = button1.fallingEdge();

 
  button0.update();
  button1.update();


  if(btn0State == HIGH) { usbMIDI.sendNoteOn(60, 99, channel);
                           digitalWrite(ledPin,HIGH);
                           delay(10);
                           digitalWrite(ledPin,LOW);
     }
  

  if(btn1State == HIGH) { usbMIDI.sendNoteOn(62, 99, channel);
                           digitalWrite(ledPin,HIGH);
                           delay(10);
                           digitalWrite(ledPin,LOW);

     }
                           

                    

  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}
 
Or maybe Someone can give me an Idea/clue as to how to properly use the "Update" in the Bounce Library ?

Code:
//How To Check Button Press


#include <Bounce.h>


const int channel = 1;
int ledPin = 13;



Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5); 


void setup() {

  
  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);

}

void loop() {


  
  button0.update();
  button1.update();



  
  if (button0.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
    digitalWrite(ledPin,HIGH);
    delay(10);
    digitalWrite(ledPin,LOW);
  }
  if (button1.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
    digitalWrite(ledPin,HIGH);
    delay(10);
    digitalWrite(ledPin,LOW);

  }
 

  if (button0.risingEdge()) {
    
  }
  if (button1.risingEdge()) {
   

  }

  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}
 
I have examples on the forum... try 'toggle' and 'midi'



You're really close now...

One thing, I think you want to declare these outside the main loop
Code:
int ledPin = 13;
int btn0State = button0.fallingEdge();
int btn1State = button1.fallingEdge();

And instead of setting them to the current bounce value set them to their default state when you declare them and then toggle their values within the code.

You already have the variable to store the state... but you need to keep track of it separately from the bounce value.
 
So ... I've made some progress. ( thank you Oddson! ) the code does allow me to toggle the message that is sent by pressing another button. my next goal is to have the message change happen momentarily .... when button0 is pressed, button 1 message changes. when button0 is released, button1 message returns to it's default message. Do I need to store both the falling and rising states ?


Thank you!


Code:
// Remote Toggle WIP

#include <Bounce.h>

const int channel = 1;
int ledPin = 13;
int btn0State = HIGH;
int btn1State = HIGH;




Bounce button0 = Bounce(0, 10);
Bounce button1 = Bounce(1, 10);

void setup() {
  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  
}

void loop() {

  button0.update();
  button1.update();


  if(button0.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);
    digitalWrite(ledPin,HIGH);
    delay(10);
    digitalWrite(ledPin,LOW);                
     
    btn0State = !btn0State;
  } 
  
   if(button1.fallingEdge()) {
    if(btn0State) usbMIDI.sendNoteOn(62, 99, channel);                     
    else usbMIDI.sendNoteOn(64, 99, channel);
    digitalWrite(ledPin,HIGH);
    delay(10);
    digitalWrite(ledPin,LOW);

  }
    if (button0.risingEdge()) {
    
  }
    if (button1.risingEdge()) {
   

  }
   

    
  while (usbMIDI.read()) {
}
}
 
My bad... as usual I didn't read the original post carefully and I thought you were looking for latching with momentarily switches.

In my defence your original code does not scan for rising edges so I assumed you'd figured out you don't need them.
And I'm not 100% sure I understand now.

If you are trying to have the state of one switch alter which CC message is sent by others you don't really need to debounce the control switch as you can just test its state directly with a regular digital read.

I've tried this with a physical toggle but it should work with a momentary switch as well.

Arrays and 'for...' loops are the easiest way to keep track of which CC you mean to send under which state but you need to understand how they work.

Are there two switches or three? Does the control switch change both MIDI buttons? Do the buttons send 0/127 for off/on with the switch state?

Are you looking for suggestions or code?
 
Hi Oddson,

Thanks so much for the replies!.. I really appreciate your time.


My bad... as usual I didn't read the original post carefully and I thought you were looking for latching with momentarily switches.

No worries !... maybe I didn't do so well in explaining myself.


In my defence your original code does not scan for rising edges so I assumed you'd figured out you don't need them.
And I'm not 100% sure I understand now.

NO defense needed ...I wasn't 100% sure I needed them in the beginning as I was only sending out MIDI note on messages and MIDI program changes.


If you are trying to have the state of one switch alter which CC message is sent by others you don't really need to debounce the control switch as you can just test its state directly with a regular digital read.

The control switch will also play a roll in sending a MIDI message. it may be a different note number. Or a control message based on these code snippets.
So does that mean I should keep the debounce ?


Code:
 if (button0.fallingEdge() == HIGH && pgcNum < 127) {
    pgcNum++;
    usbMIDI.sendProgramChange(pgcNum,channel);
  }
  if (button1.fallingEdge() == HIGH && pgcNum >= 1) {
    pgcNum--;
    usbMIDI.sendProgramChange(pgcNum,channel);
  }
  if (button2.fallingEdge()) {
    usbMIDI.sendRealTime(usbMIDI.Start);
  }
  if (button3.fallingEdge()) {
    usbMIDI.sendRealTime(usbMIDI.Stop);
  }
  if (button4.fallingEdge()) {
    usbMIDI.sendNoteOn(64, 99, channel);  // 64 = E4
  }
  if (button5.fallingEdge()) {
    usbMIDI.sendNoteOn(65, 99, channel);  // 65 = F4
  }



I've tried this with a physical toggle but it should work with a momentary switch as well.


Indeed the code will toggle the MIDI message.



Arrays and 'for...' loops are the easiest way to keep track of which CC you mean to send under which state but you need to understand how they work.

I'll will try and study up on Arrays and for ... Loops as you mentioned. As I am trying to learn some of this coding along the way.

Are there two switches or three? Does the control switch change both MIDI buttons? Do the buttons send 0/127 for off/on with the switch state?

This is 2 switches. which will ultimately be normally open footswitches. This will be part of a larger project and code. This 2 switch code is me trying to work out "proof Of Concept" behavior maybe that's not really the best practices when working with this type of coding ?


Are you looking for suggestions or code?

I guess a little of both honestly. I will do the leg work and try to take it as far as I can. And , would greatly appreciate any advice or "Home Work" checking.


Kind Regards,


Chris
 
I meant to reply sooner.

Do debounce anytime you are taking action based on a change, that's what it's for.

But if you are only testing the state of a pin in a conditional statement then you can usually just read the current state of the pin.

Maybe if you tell me what you want to make?
 
RE:pM

If the modifier switches are to be only active while held then you don't need to debounce the signal for the modifier action.

Within each loop set variables for the modifiers

Mod1 = digitalRead(1);
Mod0 = digitalRead(0);


Even if they bounce like mad it won't matter as that only happens during the time you would otherwise be waiting for the bounce to return... and during that time it isn't really correct anyway as it's reading the old state until the delay has passed.

So you should be able to send on falling edge and set test variables or read the pins directly when used as modifiers.
 
Hey Oddson,

I've taken a step back ( in Code ) .. I've got my piezo drum input and trying to explore where I might define the Mod1 & Mod0 variables to
make the switches happen. Here's a look at what I have so far.



Code:
// ControlPad Message WIP


#include <Bounce.h>

byte pgcNum = 0;
int ledPin = 13;
int Mod0 = digitalRead(0);
int Mod1 = digitalRead(1);
const int channel = 1; 
const int analogPin = A0;
const int thresholdMin = 80;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject




Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);


void setup() {

  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);

}


void loop() {


  button0.update();
  button1.update();


 if (button0.fallingEdge() == HIGH || pgcNum < 127) {  //FootSwitch 1
    pgcNum++;
    usbMIDI.sendProgramChange(pgcNum,channel);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
    
  }
  

 if (button1.fallingEdge()) { // FootSwitch 1
    usbMIDI.sendRealTime(usbMIDI.Stop);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
  }

  
  int piezo = analogRead(analogPin);
  peakDetect(piezo);

  
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int voltage) {
  static int state;  // 0=idle, 1=looking for peak, 2=ignore aftershocks
  static int peak;   // remember the highest reading
  static elapsedMillis msec; // timer to end states 1 and 2

  switch (state) {
    // IDLE state: wait for any reading is above threshold.  Do not set
    // the threshold too low.  You don't want to be too sensitive to slight
    // vibration.
    case 0:
      if (voltage > thresholdMin) {
        peak = voltage;
        msec = 0;
        state = 1;
      }
      return;

    // Peak Tracking state: capture largest reading
    case 1:
      if (voltage > peak) {
        peak = voltage;     
      }
      if (msec >= peakTrackMillis) {  // ControlPad
        pgcNum < 127;
        pgcNum++;
        usbMIDI.sendProgramChange(pgcNum,channel);
        digitalWrite(ledPin, HIGH);
        delay(10);
        digitalWrite(ledPin, LOW);
        msec = 0;
        state = 2;
      }
      return;

    // Ignore Aftershock state: wait for things to be quiet again.
    default:
      if (voltage > thresholdMin) {
        msec = 0; // keep resetting timer if above threshold
      } else if (msec > aftershockMillis) {
       // usbMIDI.sendNoteOff(note, 0, channel);
        state = 0; // go back to idle when
      }

      
}
}



I'll try to bring in my encoder once I have my Switching working.


I have noticed in the code above that I get a blast of PGCs when the sketch starts .. and then it settles in. I'm sure it is the order that I've placed some of the code ?



Kind Regards,


Chris
 
I'll need some time to decipher your code.

Do you have proper signal protection on your Teensy input from the piezo?
 
Code:
        pgcNum < 127;
        pgcNum++;
I don't think this does what you want...
https://www.cprogramming.com/tips/tip/increment-and-decrement-counters-with-rollover.

I'm still not sure what you're up to and why you would increment here??


Also you should declare the mod0 and mod1 as Boolean ('bool' in C if I recall) and you have to read them inside the main loop - they are changing all the time and have to be read in the loop.

First observations... will have a proper look later... I'll need to look at Paul's code again so I can spot your additions better.
 
Code:
pgcNum < 127;
pgcNum++;


I don't think this does what you want...

PGC stands for Program Change as in MIDI Program Change. The behavior is actually what I'm looking for. I've looked at the output in a MIDI scope and
used it to control the software it is intended for.



I'm still not sure what you're up to and why you would increment here??

This outputs single MIDI program changes control pad is hit. or, the footswitch is pressed. the only thing that is strange
is the burst of program changes at the start of the sketch. which I suspect is the Piezo input settling in at start up.
I'll be wiring the extra diodes up tomorrow as they arrived after I started working with this code.


Also you should declare the mod0 and mod1 as Boolean ('bool' in C if I recall) and you have to read them inside the main loop - they are changing all the time and have to be read in the loop.

Ok .. I'll read up on booleans for this code,

First observations... will have a proper look later... I'll need to look at Paul's code again so I can spot your additions better.


Thank you very much ! :)
 
The line pgcNum < 127; reads True or False but does nothing... or if it does it's news to me.

I assumed you were attempting to deal with incrementing beyond 127.
 
Ahhh ok ... sorry ... are you referring to that snippet in the Piezo portion of the code. Or in general ?

Indeed it is meant to stop ( Non-Wrappping ) of the heist value.


Does the line make more sense in this context ?


Code:
if (button0.fallingEdge() == HIGH && pgcNum < 127) {
    pgcNum++;
    usbMIDI.sendProgramChange(pgcNum,channel);
  }
  if (button1.fallingEdge() == HIGH && pgcNum >= 1) {
    pgcNum--;
    usbMIDI.sendProgramChange(pgcNum,channel);
 
You're getting warmer.

I think you are generally very close.

First, the falling edges return True or False on there own, you don't need to test them.

Second, we don't want to test them as you can't assume the thing that triggered this state in the code (a timer running over) will happen with the rising or falling edges of either button... but those state variables will know if that switch is LOW or not.

You might want to set these to the opposite of the pin states so the logic reads more clearly as you have active LOW buttons:

Mod0 = !digitalRead(0);
Mod1 = !digitalRead(1);


Then I think you want this...
if (Mod0 && pgcNum < 127) ..,

Also, you don't want wrapping at 0/127 limits? That was my assumption and the reason for the link in the earlier post.
 
Given this test is very infreqently run; you would do better to test the pin directly here.

if (!digitalRead(0) && pgcNum < 127) ..,

I think this is where a pro would define a macro to make the test logic readable... you wouldn't need a variable and certainly you don't need to flip the state of it in every pass of the main loop. But it also won't matter given how little those few instructions add to a loop.

However, the delays with your button events for LED timing are very problematic for the timing in the piezo section and you'll need to switch to a non-blocking method.
 
Hmmm OK ....

I'm struggling pretty heavily with this one .. can't seem to figure out how to switch the message sent by the control pad with !digitalRead.

there are only 4 messages that get sent by ControlPad, Footswitches, and encoder. And I guess this is what you mean by macros.

Code:
byte pgcNum = 0
#define Inc = 
#define Dec =
#define Start =
#define Stop =


The ControlPad ( Piezo) code has a switch statement. I assume I need to let the pad return to it's initial state before I can check the footswitches. and change
the outgoing MIDI message. .. and as I try to get my head around it seems like I need a switch " in front " of the switch .


Burning brain cells !
 
Ignore the second message... that's for later. Macros are good to understand when you read code but you don't need to use them.

Do it in steps and use serial print to check on the state of variables.

You can remove extraneous stuff once you get things working.


Sorry about the second message... not relevant to what you are doing except the part about delay commands. You need to get rid of them in the long run but as long as you're not changing the button states near to the piezo hit it should be ok.

So you need to understand Paul's code... I'll put that in anot her post....
 
So this is what I"m tinkering with today .... please don't laugh if you can help it ;)

!digitalread0 ... works ... but !digitalread1 ... not so much





Code:
// ControlPad Message WIP


#include <Bounce.h>

bool Mod0 = digitalRead(0);
bool Mod1 = digitalRead(1);
const int channel = 1;
int ledPin = 13;
byte pgcNum = 0; 
const int analogPin = A0;
const int thresholdMin = 80;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject



Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);


void setup() {

  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  

}


void loop() {
  
  
  button0.update();
  button1.update();


 if (button0.fallingEdge() && pgcNum < 127) {  //FootSwitch 1
    pgcNum++;
    usbMIDI.sendProgramChange(pgcNum,channel);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
    
  }
  

 if (button1.fallingEdge()) { // FootSwitch 2
    usbMIDI.sendRealTime(usbMIDI.Stop);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
  }

  
  int piezo = analogRead(analogPin);
  peakDetect(piezo);

  
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int voltage) {
  static int state;  // 0=idle, 1=looking for peak, 2=ignore aftershocks
  static int peak;   // remember the highest reading
  static elapsedMillis msec; // timer to end states 1 and 2


      if (!digitalRead(0) && pgcNum >= 1 ) {
         switch (state) {
    // IDLE state: wait for any reading is above threshold.  Do not set
    // the threshold too low.  You don't want to be too sensitive to slight
    // vibration.
    case 0:
      if (voltage > thresholdMin) {
        peak = voltage;
        msec = 0;
        state = 1;
      }
      return;

    // Peak Tracking state: capture largest reading
    case 1:
      if (voltage > peak) {
        peak = voltage;    
      }

      
      if (msec >= peakTrackMillis)  {  // ControlPad
        msec = 0;
        state = 2;
       
      }
      return;

    // Ignore Aftershock state: wait for things to be quiet again.
      default:
      if (voltage > thresholdMin) {
        msec = 0; // keep resetting timer if above threshold
      } else if (msec > aftershockMillis) {
       // usbMIDI.sendNoteOff(note, 0, channel);
        state = 0; // go back to idle when
        pgcNum--;
        usbMIDI.sendProgramChange(pgcNum,channel);
        
      }
        
      if (!digitalRead(1));
         
          switch (state) {
    // IDLE state: wait for any reading is above threshold.  Do not set
    // the threshold too low.  You don't want to be too sensitive to slight
    // vibration.
    case 0:
      if (voltage > thresholdMin) {
        peak = voltage;
        msec = 0;
        state = 1;
      }
      return;

    // Peak Tracking state: capture largest reading
    case 1:
      if (voltage > peak) {
        peak = voltage;    
      }

      
      if (msec >= peakTrackMillis)  {  // ControlPad
        msec = 0;
        state = 2;
        
       
      }
      

    // Ignore Aftershock state: wait for things to be quiet again.
      default:
      if (voltage > thresholdMin) {
        msec = 0; // keep resetting timer if above threshold
      } else if (msec > aftershockMillis) {
       // usbMIDI.sendNoteOff(note, 0, channel);
        state = 0; // go back to idle when
        usbMIDI.sendRealTime(usbMIDI.Start);
        
        
      }

               
}
}
}
}
 
The ControlPad ( Piezo) code has a switch statement. I assume I need to let the pad return to it's initial state before I can check the footswitches. and change
the outgoing MIDI message. .. and as I try to get my head around it seems like I need a switch " in front " of the switch

I'd say you switch inside his case instead as there is only one spot where it matters.

You don't wait to check the footswitches as they are always available whether you read them on every pass or if you read them directly they will be HIGH or LOW based on your poddling human-speed changes your feet caused hundreds or thousands of passes through the loop ago.

So let's stick to setting two state variables defined as type bool called Mod0 and Mod1 during the main loop.

In Paul's code you correctly spotted where the action is. Case 1 happens just once every several thousand runs through the code even when you are playing and when it does you can test Mod0 and Mod1 and send the appropriate midi message based on the current values in those variables.

If you expect only one at time to be True (pin is LOW) then you can just test one and the other under the 'else' of the first test.

Psuedo-code
Code:
If (Mod0){
  Send midi type 0
}else{
  If (Mod1){
    Send midi type 1
  }
}

You could also test separately but you need to consider what it will do if both are True.

If you need four different results then you'd need some way to set the parameters of the midi message you send, but as described I don't think you need this.
 
Last edited:
I didn't notice your reply between mine... it will be a while before I can have a look... I don't laugh at code... unless it's meant to be funny.
 
Still tweaking and trying things ... Of Course this one does NOT work. But, I'm just trying to figure things out with trial and error.

Code:
// ControlPad Message WIP


#include <Bounce.h>

  bool Mod0 = digitalRead(0);
  bool Mod1 = digitalRead(1);
const int channel = 1;
int ledPin = 13;
byte pgcNum = 0; 
const int analogPin = A0;
const int thresholdMin = 80;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject



Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);


void setup() {

  pinMode(13, OUTPUT);
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  

}


void loop() {

 
  button0.update();
  button1.update();



 if (button0.fallingEdge() && pgcNum < 127) {  //FootSwitch 1
    pgcNum++;
    usbMIDI.sendProgramChange(pgcNum,channel);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
    
  }
  

 if (button1.fallingEdge()) { // FootSwitch 2
    usbMIDI.sendRealTime(usbMIDI.Stop);
    digitalWrite(ledPin, HIGH);
    delay(10);
    digitalWrite(ledPin, LOW);
  }

  
  int piezo = analogRead(analogPin);
  peakDetect(piezo);

  
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int voltage) {
  static int state;  // 0=idle, 1=looking for peak, 2=ignore aftershocks
  static int peak;   // remember the highest reading
  static elapsedMillis msec; // timer to end states 1 and 2



         switch (state) {
    // IDLE state: wait for any reading is above threshold.  Do not set
    // the threshold too low.  You don't want to be too sensitive to slight
    // vibration.
    case 0:
      if (voltage > thresholdMin) {
        peak = voltage;
        msec = 0;
        state = 1;
      }
      return;

    // Peak Tracking state: capture largest reading
    case 1:
      if (voltage > peak) {
        peak = voltage;    
      }

      
      if (msec >= peakTrackMillis)  {  // ControlPad
        msec = 0;
        state = 2;
       
      }
      return;

    // Ignore Aftershock state: wait for things to be quiet again.
      default:
      if (voltage > thresholdMin) {
        msec = 0; // keep resetting timer if above threshold
       } else if (msec > aftershockMillis) {
        state = 0; // go back to idle when
         pgcNum++;
         usbMIDI.sendProgramChange(pgcNum,channel);

        }

        
        if (Mod0) {
        Serial.println("Foot Switch 1");     
         pgcNum--;
         usbMIDI.sendProgramChange(pgcNum,channel);


     
        if (Mod1) {
        Serial.println("Foot Switch 2");
        usbMIDI.sendRealTime(usbMIDI.Start);
       
        }
        
        
     

     }
        
     

}
}
 
Status
Not open for further replies.
Back
Top