USB MIDI Controller debounce

Hello everybody.
I assembled a Midi foot controller.
For those who do not understand, I will explain. I wanted to switch presets in Ableton live 11 by pressing a button.
The project was assembled on the basis of Arduino Leonardo.

Everything works great.
The problem is this: When you press the buttons, the signal can pass twice, as can be seen from the LED on the board, and accordingly turn off and on the assigned action.

The buttons are all instant, operational, and work perfectly.

Due to my illiteracy in development, I can assume that you need to reduce the sensitivity of the analog button.
Later I realized that there was no button bouncing in the code.

Help me add debounce switching to the code.

Link to the project source GitHub - hugbed/midi-controller: DIY midi controller (Arduino ATmega32U4)

/*
  Example:
    Midi-controller with 10 inputs and a hardcoded intensity. 
  Modification: 29/10/2017
  Description:
    - Support 10 switches (Use uint16_t instead of uint8_t for flags).
    - Hard-coded intensity
  Author: Hugo Bédard

  Original example:
    This examples shows how to make a simple seven keys MIDI keyboard with volume control
  Created: 4/10/2015
  Author: Arturo Guadalupi <a.guadalupi@arduino.cc>
  http://www.arduino.cc/en/Tutorial/MidiDevice
*/

#include <MIDIUSB.h>
#include <pitchToFrequency.h>
#include <pitchToNote.h>

#define NUM_BUTTONS 10

const uint8_t button1 = 21;
const uint8_t button2 = 20;
const uint8_t button3 = 19;
const uint8_t button4 = 18;
const uint8_t button5 = 15;
const uint8_t button6 = 14;
const uint8_t button7 = 16;
const uint8_t button8 = 10;
const uint8_t button9 = 8;
const uint8_t button10 = 9;

const uint8_t buttons[NUM_BUTTONS] = {
  button1,
  button2,
  button3,
  button4,
  button5,
  button6,
  button7,
  button8,
  button9,
  button10
};

const byte notePitches[NUM_BUTTONS] = {
  pitchC3,
  pitchD3,
  pitchE3,
  pitchF3,
  pitchG3,
  pitchA3,
  pitchB3,
  pitchC4,
  pitchD4,
  pitchE4
};

uint16_t pressedButtons = 0x00;
uint16_t previousButtons = 0x00;
uint8_t intensity = 64;

void setup() {
  for (int i = 0; i < NUM_BUTTONS; i++)
    pinMode(buttons[i], INPUT);
}

void loop() {
  readButtons();
  playNotes();
}

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

void readButtons()
{
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (digitalRead(buttons[i]) == LOW)
    {
      bitWrite(pressedButtons, i, 1);
      delay(50);
    }
    else
      bitWrite(pressedButtons, i, 0);
  }
}

void playNotes()
{
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (bitRead(pressedButtons, i) != bitRead(previousButtons, i))
    {
      if (bitRead(pressedButtons, i))
      {
        bitWrite(previousButtons, i , 1);
        noteOn(0, notePitches[i], intensity);
        MidiUSB.flush();
      }
      else
      {
        bitWrite(previousButtons, i , 0);
        noteOff(0, notePitches[i], 0);
        MidiUSB.flush();
      }
    }
  }
}

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

There are countless examples of debouncing code online. Could you clarify where you're getting stuck?

Note that there are existing libraries that support debouncing out of the box, e.g. Control Surface: NoteButton.ino.

#include <MIDIUSB.h>
#include <pitchToNote.h>

const uint8_t NUM_BUTTONS = 3;
const uint8_t buttonPin[NUM_BUTTONS] = { 2, 8, 9}; //external pull down resistor

const byte notePitches[NUM_BUTTONS] = {  pitchC3, pitchD3, pitchE3 };

uint16_t previousButtons = 0x00;
uint8_t intensity = 64;

void setup() {
}

void loop() {
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    bool b = digitalRead( buttonPin[i]);
    if (b && !bitRead(previousButtons, i))    {
      noteOn(0, notePitches[i], intensity);
      bitWrite(previousButtons, i, 1);
    }
    if (!b && bitRead(previousButtons, i))    {
      bitWrite(previousButtons, i, 0);
      noteOn(0, notePitches[i], 0);
    }
  }
  delay(50);
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t note = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(note);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  noteOn(channel, pitch, 0);
}

I rewrote the code leaving 4 buttons
Your code does not work for me, random midi values are triggered.

I even tried adding the library <Bounce2.h>
It didn't work out either
Once again, I apologize, I’m not good at development, is it possible to change my new code?

#include "MIDIUSB.h"
#include "PitchToNote.h"
#define NUM_BUTTONS  3

const uint8_t button1 = 9;

const uint8_t button2 = 8;

const uint8_t button3 = 2;


const int intensityPot = 0;  //A0 input

const uint8_t buttons[NUM_BUTTONS] = {button1, button2, button3, };

const byte notePitches[NUM_BUTTONS] = {pitchC3, pitchD3, pitchE3, };

uint8_t notesTime[NUM_BUTTONS];

uint8_t pressedButtons = 0x00;

uint8_t previousButtons = 0x00;

uint8_t intensity;

void setup() {

  for (int i = 0; i < NUM_BUTTONS; i++)

    pinMode(buttons[i], INPUT_PULLUP);
}

void loop() {

  readButtons();

  readIntensity();

  playNotes();
}

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {

  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};

  MidiUSB.sendMIDI(event);
}

void readButtons()
{

  for (int i = 0; i < NUM_BUTTONS; i++)

  {

    if (digitalRead(buttons[i]) == LOW)

    {

      bitWrite(pressedButtons, i, 1);

      delay(50);

    }

    else

      bitWrite(pressedButtons, i, 0);

  }
}

void readIntensity()
{

  int val = analogRead(intensityPot);

  intensity = (uint8_t) (map(val, 0, 1023, 0, 127));
}

void playNotes()
{

  for (int i = 0; i < NUM_BUTTONS; i++)

  {

    if (bitRead(pressedButtons, i) != bitRead(previousButtons, i))

    {

      if (bitRead(pressedButtons, i))

      {

        bitWrite(previousButtons, i , 1);

        noteOn(0, notePitches[i], intensity);

        MidiUSB.flush();

      }

      else

      {

        bitWrite(previousButtons, i , 0);

        noteOff(0, notePitches[i], 0);

        MidiUSB.flush();

      }

    }

  }
}

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {

  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};

  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {

  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};

  MidiUSB.sendMIDI(noteOff);
}
![IMG_20240720_191015|224x500](upload://lLHXrQtCwBXKKHiPR1SU5F6e79B.jpeg)

probably because you listen not existed pins:

1 Like

Thanks for the answer.
The issue has been resolved.

I'll add my code

#include "MIDIUSB.h"
#include "PitchToNote.h"
#define NUM_BUTTONS  3

const uint8_t button1 = 9;
const uint8_t button2 = 8;
const uint8_t button3 = 2;

const int intensityPot = 0;  // A0 input

const uint8_t buttons[NUM_BUTTONS] = {button1, button2, button3};
const byte notePitches[NUM_BUTTONS] = {pitchC3, pitchD3, pitchE3};

uint8_t notesTime[NUM_BUTTONS];
uint8_t pressedButtons = 0x00;
uint8_t previousButtons = 0x00;
uint8_t intensity;

// Дебаунс параметры
const unsigned long DEBOUNCE_DELAY = 50; // Время дебаунса в миллисекундах
unsigned long lastDebounceTime[NUM_BUTTONS] = {0};  // Последнее время изменения состояния кнопок
uint8_t buttonState[NUM_BUTTONS] = {0};  // Текущее состояние кнопок
uint8_t lastButtonState[NUM_BUTTONS] = {0};  // Предыдущее состояние кнопок

void setup() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(buttons[i], INPUT_PULLUP);
  }
}

void loop() {
  readButtons();
  readIntensity();
  playNotes();
}

// Первый параметр — это тип события (0x0B = управление изменениями).
// Второй параметр — это тип события, объединенный с каналом.
// Третий параметр — это номер управления (0-119).
// Четвертый параметр — это значение управления (0-127).
void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

void readButtons() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    int reading = digitalRead(buttons[i]);

    // Проверка, если кнопка изменила состояние
    if (reading != lastButtonState[i]) {
      lastDebounceTime[i] = millis();  // Сброс времени дебаунса
    }

    if ((millis() - lastDebounceTime[i]) > DEBOUNCE_DELAY) {
      // Если прошло достаточно времени, то обновляем состояние кнопки
      if (reading != buttonState[i]) {
        buttonState[i] = reading;
        // Запоминаем состояние кнопки
        bitWrite(pressedButtons, i, buttonState[i] == LOW);
      }
    }

    // Сохраняем текущее состояние для следующей итерации
    lastButtonState[i] = reading;
  }
}

void readIntensity() {
  int val = analogRead(intensityPot);
  intensity = (uint8_t) (map(val, 0, 1023, 0, 127));
}

void playNotes() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    if (bitRead(pressedButtons, i) != bitRead(previousButtons, i)) {
      if (bitRead(pressedButtons, i)) {
        bitWrite(previousButtons, i , 1);
        noteOn(0, notePitches[i], intensity);
        MidiUSB.flush();
      } else {
        bitWrite(previousButtons, i , 0);
        noteOff(0, notePitches[i], 0);
        MidiUSB.flush();
      }
    }
  }
}

// Первый параметр — это тип события (0x09 = note on, 0x08 = note off).
// Второй параметр — note-on/note-off, объединенный с каналом.
// Канал может быть любым от 0 до 15. Обычно сообщается пользователю как 1-16.
// Третий параметр — это номер ноты (48 = среднее До).
// Четвертый параметр — это скорость (64 = нормальная, 127 = самая быстрая).
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}