I made a DIY footswitch with 8 arcade button and 1 analog input that works as expression pedal. The goal is to switch on/off fx (or channels, or launch clips…) in ableton live 11.
The code I used seems to work fine with my analog input: it sends CC and I can route it perfectly for tweak for example volume or sliders.
But I have a problem with buttons… they are sending midi note on/off events which have to be mapped, and ableton receives them, but the toggle isn’t working fine… sometimes it is switched on, sometimes doesn’t…
I have to push once and once and it isn’t being fine because as a guitarist I need to be focus into playing and not so much into “oh, this fx isn’t working now, let’s try again”
I decided to use ableton’s midi monitor to see what is happening and I realized that every time I press one button it is receiving several NOTE ON/NOTE OFF messages, instead of just one… it makes the “toggle” reacts crazy and maybe you could just read my code and help me with it, because I’ve been many days going crazy with it and I have no answers…. Maybe could I add some delay? Have I to change a few lines? Lots of questions for this poor guitarist trying to learn some programming. THANK YOU IN ADVANCE
Sorry, one friend is helping me with the code and he copied one and adapted it to my needs. I’m using just one analog input. Thank you for your fast response
and on that matter, you have only de-bounce 'released' but not 'pressed', you should de-bounce both.
a change of state of the pin(button) should deactivate it for about 50 - 100 ms for any other state change.
It sounds really interesting, but I’m newbie into programming… could you PLEASE change my code applying these changes? I would try it and send some feedback.
basically you need to do it like this.
first correct this
// struct for buttons
struct button{
uint8_t port;
bool pressed;
uint32_t count; // uint8_t count; // just make sure that millis() doesn't roll over it's a 32-bit variable.
};
define the debounce
#define DEBOUNCE 50
and check for state change and debounce like this
for (int i = 0; i < 8; i++) {
bool pressed = false;
if (!digitalRead(buttons[i].port)) pressed = true; // either pressed or not
if ((buttons[i].pressed != pressed) && (millis() - buttons[i].count > DEBOUNCE)) { // change of button state
if (pressed) {
uint8_t note = buttons[i].port + 31;
midiEventPacket_t noteOn = {0x09, 0x90 | 1, note, 127};
MidiUSB.sendMIDI(noteOn);
}
else { // not pressed
uint8_t note = buttons[i].port + 31;
midiEventPacket_t noteOff = {0x08, 0x80 | 1, note, 0};
MidiUSB.sendMIDI(noteOff);
}
buttons[i].pressed = pressed;
buttons[i].count = millis();
}
}
Thank you very much for your tips. I will try my best and I hope all this will fix that kind of bug that is making my little machine not useful… the concept is really simple, and many controllers do it perfectly… I think I’m reaching the goal. Love for you all. I will close the conversation as soon I will be able to get it working perfectly.
Based in these tips, I tried several things and the key for me was adjusting fine the DEBOUNCE DELAY time. Actually the best setup for me is 700 ms, because if I let my foot over the button this time, the note will switch OFF, but my stomps are faster. However I have to wait no less than these 700 ms until push again, because the button won’t work… for me is correct. Each user could config this setting easily.
I leave here the final sketch that worked for me for my 8 BUTTON and 1 POT (the sketch sets 4 but I only use 1) MIDI FOOTSWITCH.
Thank you all for your help
#include "MIDIUSB.h"
// struct for buttons
struct button{
uint8_t port;
bool pressed;
uint32_t count;
};
// struct for potentiometers
struct pot{
uint8_t port;
uint8_t last;
};
button buttons[8];
pot pots[4];
unsigned long debounce_delay = 700; // debounce delay
void setup()
{
// declaring each button port sequentially
for(int i = 0; i < 8; i++){
buttons[i].port = 9 - i;
buttons[i].pressed = false;
buttons[i].count = 0;
pinMode(buttons[i].port, INPUT_PULLUP);
}
// declaring each potentiometer sequentially
for(int i = 0; i < 1; i++){
pots[i].port = i + A0;
pots[i].last = 0;
}
}
void loop()
{
// button press logic, checks each button in a loop with debounce
for(int i = 0; i < 8; i++){
if(!digitalRead(buttons[i].port)){
if(!buttons[i].pressed && millis() - buttons[i].count > debounce_delay){
uint8_t note = i + 60;
midiEventPacket_t noteOn = {0x09, 0x90 | 8, note, 127};
MidiUSB.sendMIDI(noteOn);
buttons[i].pressed = true;
} else if (buttons[i].pressed && millis() - buttons[i].count > debounce_delay) {
buttons[i].count = millis();
}
} else if (buttons[i].pressed == true){
uint8_t note = i + 60;
midiEventPacket_t noteOff = {0x08, 0x80 | 8, note, 0};
MidiUSB.sendMIDI(noteOff);
buttons[i].pressed = false;
buttons[i].count = millis();
}
}
// potentiometer reading logic, checks each potentiometer for moving a certain threshold to prevent jiggling
for(uint8_t i = 0; i < 1; i++){
uint8_t val = analogRead(pots[i].port) * (127.0 / 685.0);
if(val > 127)
val = 127;
if(abs(val - pots[i].last) > 1){
pots[i].last = val;
midiEventPacket_t event = {0x0B, 0xB0 | 8, pots[i].port, val};
MidiUSB.sendMIDI(event);
}
}
MidiUSB.flush(); // send MIDI data via USB
}
actually the best would be to debounce also the release as i had shown you, then a debounce time of 50ms would easily suffice.
If you just debounce the press, you are not dealing with the issue at all.
I don’t know how doing it, because I defined the debounce as 700, how can I add this second shorter debounce to my code? I couldn’t see clearly in your post
you don't need to add a second debounce, you need to put the debounce timing check in the proper spot, so it applies to any change of state of the button, both release and press. I mean nevermind you know, whatever works for you, i am sure i spelled it out sufficiently.
I tried but it doesn’t work as expected…
The second “short debounce” switch on again the fx instantly in my software if I am not enough fast releasing my foot. I assure the last code I posted is working super fine. The only thing I added was a little DEAD ZONE (1%) at the beginning and end of the CC to avoid weird imprecise behavior of the knob when reaching limits as it follows:
BEFORE VOID SETUP:
button buttons[8];
pot pots[4];
unsigned long debounce_delay = 700; // debounce delay
unsigned int dead_zone = 1; // dead zone for potentiometers
AND WHEN VOID LOOP CONFIG
// potentiometer reading logic, checks each potentiometer for moving a certain threshold to prevent jiggling
for(uint8_t i = 0; i < 1; i++){
uint8_t val = analogRead(pots[i].port) * (127.0 / 685.0);
if (val < dead_zone) {
val = 0;
} else if (val > (127 - dead_zone)) {
val = 127;
} else {
val = map(val, dead_zone, (127 - dead_zone), 0, 127);
}
if(abs(val - pots[i].last) > 1){
pots[i].last = val;
midiEventPacket_t event = {0x0B, 0xB0 | 8, pots[i].port, val};
MidiUSB.sendMIDI(event);
}
}
But thank you for your comments, you all helped me to solve this brain mess for me
This is because you are looking at the signal level of the digital input, not at the edge. That is when the button becomes pressed as opposed to when the button is being pressed.
Look at the "state change detection" example in the Arduino's examples.
bool pressed = false;
if (!digitalRead(buttons[i].port)) pressed = true; // either pressed or not
if ((buttons[i].pressed != pressed) && (millis() - buttons[i].count > DEBOUNCE)) {
is exactly this, all is executed if the switch has changed state and debounce time has expired, thereafter it is determined if it was a release or a press.
It doesn't really, there is just the debounce, but of course the type change for the stored millis() (uint8_t is really to small and is going to cause unpredictable behavior with it rolling over in a bit over 1/4 second.
But when you press the button it does two state changes physically: press and release, and in the most cases it sends two impulses, each one with each movement… even the LED in arduino blinks twice… for me it isn’t working, just my sketch with long debounce is working fine for me, I have only the “bug” of having to wait 700 ms to press again the button but I said in my setup it isn’t being a problem at all…