MIdi banks + lcd

I biult an 8 footcontroller and added an lcd Display to send via MIdi Out. 8 buttons works fine each of them are assigned to especif number bank, for expample button 1 bank 44, button 2 bank 60, the lcd 16x2 shows custom text for each button, I would lik to add 2 more buttons (9 & 10) to go up and down, the problmen is what I like to show the correct bank number on lcd, for example if last button was 1 that is bank 44 when I press up the lcd will show 45, if a press again 46, and 47 and so on, if the last button was 2 that is bank 60, if a press down should be on the lcd 59, 58, 57 if a press.

How can i write the 8 last states for that buttons, and write correct number on lcd? which should be the most easy code?

I did something similar for the bank selectors in my MIDI Controller library:

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);               // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

      if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

      previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

    const unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

class BankSelector
{
  public:
    BankSelector(uint8_t incrementPin, uint8_t decrementPin, uint8_t minSetting, uint8_t maxSetting)
      : incrementButton{incrementPin}, decrementButton{decrementPin},
        minSetting(minSetting), maxSetting(maxSetting), bankSetting(minSetting)
    {}
    void refresh() {
      uint8_t newBankSetting = bankSetting;
      if (incrementButton.isPressed())
        newBankSetting = bankSetting < maxSetting ? bankSetting + 1 : minSetting; // Increment bankSetting number or wrap around
      if (decrementButton.isPressed())
        newBankSetting = bankSetting == minSetting ? maxSetting : bankSetting - 1; // Decrement bankSetting number or wrap around
      if (newBankSetting != bankSetting) {
        if (onChangeFn != nullptr)
          onChangeFn(newBankSetting, bankSetting);
        bankSetting = newBankSetting;
      }
    }

    void onChange(void (*fn)(uint8_t newBankSetting, uint8_t bankSetting)) {
      onChangeFn = fn;
    }

    uint8_t getBankSetting() {
      return bankSetting;
    }

    void setBankSetting(uint8_t newBankSetting) {
      if (onChangeFn != nullptr)
        onChangeFn(newBankSetting, bankSetting);
      bankSetting = newBankSetting;
    }

  private:
    PushButton incrementButton, decrementButton;
    const uint8_t minSetting, maxSetting;
    uint8_t bankSetting;
    void (*onChangeFn)(uint8_t newBankSetting, uint8_t bankSetting) = nullptr;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

void printToLCD(uint8_t newBankSetting, uint8_t bankSetting) {
  Serial.print("New setting: ");
  Serial.println(newBankSetting);
  Serial.print("Previous setting: ");
  Serial.println(bankSetting);
  // ...
}

void initializeLCD() {
  // ...
}

const uint8_t incrementPin = 2;
const uint8_t decrementPin = 3;
const uint8_t minSetting = 1;
const uint8_t maxSetting = 16;

BankSelector bs = {incrementPin, decrementPin, minSetting, maxSetting};

/*--------------------------------------------------------------------------------------------------------------------------------*/

void setup() {
  Serial.begin(115200);
  initializeLCD();
  bs.onChange(printToLCD);
}

void loop() {
  bs.refresh();
}

Pieter

Thanks Pieter I would give a try and let you feedback when Make the changes.

actually i have problems to put in practice your code, here's and expample of my button 8, imagine that button 9 will read that last state was button 8 bank 40, and the Lcd should increase from 40 to 41

buttonGND8.poll();
if(buttonGND8.released()) {
// Serial.write("Button 8\n");
MIDI.sendProgramChange(40,1); // Send the MIDI command
ledOffAll();
analogWrite(ledPin8, 255);
lcd.clear();
lcd.print("41 Bank 8");
lcd.setCursor(0, 1);
lcd.print("Delay U2 Triplet");
delay(500);

I don't think I understand what you want to do.

Please post all of your code, using [code][/code] tags (by using the </> button) instead of [quote][/quote] tags.

You can't use delay when polling pushbuttons.

Pieter

Here is the code, it's pretty simple, 8 buttons change 8 user presets, and shows on display, I want button 9 and 10, (UP and DOWN) goes bank up o r bank down showing the bank number on lcd, if last button pressed was 8 the bank is 41, by Pressing UP (9) should go 42,43, and so.
The problem is give exact code to shows correct bank number on display remebering was last bank used by each button.
thanks

#include "Arduino.h"
#include "Switch.h"
#include "MIDI.h"
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);  // Inicia el LCD en la dirección 0x27, con 16 caracteres y 2 líneas  SDA 20 / SCL 21

// Define analog pins for LEDs
int ledPin1 = A0;
int ledPin2 = A1;
int ledPin3 = A2;
int ledPin4 = A3;
int ledPin5 = A4;   
int ledPin6 = A5;   
int ledPin7 = A6;   // Workaround, A6 just works as digital input
int ledPin8 = A7;   // Workaround, A7 just works as digital input
int ledPin9 = A8;   
int ledPin10 = A9;  

// Switches 
Switch buttonGND1 = Switch(2);    // button to GND, use internal 20K pullup resistor
Switch buttonGND2 = Switch(3);    // button to GND, use internal 20K pullup resistor
Switch buttonGND3 = Switch(4);    // button to GND, use internal 20K pullup resistor
Switch buttonGND4 = Switch(5);    // button to GND, use internal 20K pullup resistor
Switch buttonGND5 = Switch(6);    // button to GND, use internal 20K pullup resistor
Switch buttonGND6 = Switch(7);    // button to GND, use internal 20K pullup resistor
Switch buttonGND7 = Switch(8);    // button to GND, use internal 20K pullup resistor
Switch buttonGND8 = Switch(9);    // button to GND, use internal 20K pullup resistor
Switch buttonGND9 = Switch(10);    // button to GND, use internal 20K pullup resistor
Switch buttonGND10 = Switch(11);    // button to GND, use internal 20K pullup resistor

                                    //Switches 1 4 7 U
                                    //Switches 2 5 8 D
                                    //Switches 3 6 W iO

// MIDI init
MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  //  Set serial baud rate:  31200 for MIDI  /  9600 for debugging
  Serial.begin(31200);
   lcd.begin();                      
   lcd.backlight();
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("   DIGITECH   ");
   lcd.setCursor(0, 1);
   lcd.print("Edge Control 2.0");
   delay(500);
   
  
}

void loop() {
  buttonGND1.poll();  
  if(buttonGND1.released()) {
    // Serial.write("Button 1\n");
     Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(43);
    ledOffAll();
    analogWrite(ledPin1, 255);
    lcd.clear();
    lcd.print("44        Bank 1");
   lcd.setCursor(0, 1);
   lcd.print("Delay 500m");
   delay(500);
   
  }
  buttonGND2.poll();  
  if(buttonGND2.released()) {
    // Serial.write("Button 2\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(49);
    ledOffAll();
    analogWrite(ledPin2, 255);
    lcd.clear();
    lcd.print("50        Bank 2");
   lcd.setCursor(0, 1);
   lcd.print("EQ Blues");
   delay(500);
   
  }
  buttonGND3.poll();  
  if(buttonGND3.released()) {
    // Serial.write("Button 3\n");
   Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(57);
    ledOffAll();
    analogWrite(ledPin3, 255);
    lcd.clear();
    lcd.print("58        Bank 3");
   lcd.setCursor(0, 1);
   lcd.print("Reverb 8v Down");
   delay(500);
  
  }
  buttonGND4.poll();  
  if(buttonGND4.released()) {
    // Serial.write("Button 4\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(58);
    ledOffAll();
    analogWrite(ledPin4, 255);
    lcd.clear();
    lcd.print("59        Bank 4");
   lcd.setCursor(0, 1);
   lcd.print("Reverb Noise G.");
   delay(500);
   

  }  
  buttonGND5.poll();  
  if(buttonGND5.released()) {
    // Serial.write("Button 5\n");
   Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(60);
    ledOffAll();
    analogWrite(ledPin5, 255);
    lcd.clear();
    lcd.print("61        Bank 5");
   lcd.setCursor(0, 1);
   lcd.print("Reverb Tremolo N");
   delay(500);
  

  }  
  buttonGND6.poll();  
  if(buttonGND6.released()) {
    // Serial.write("Button 6\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(61);
    ledOffAll();
    analogWrite(ledPin6, 255);
    lcd.clear();
    lcd.print("62        Bank 6");
   lcd.setCursor(0, 1);
   lcd.print("Reverb + 8v 5^");
   delay(500);
    

  }  
  buttonGND7.poll();  
  if(buttonGND7.released()) {
    // Serial.write("Button 7\n");
    MIDI.sendProgramChange(59,1);      // Send the MIDI command
    ledOffAll();
    analogWrite(ledPin7, 255);
    lcd.clear();
    lcd.print("60       Bank 7");
   lcd.setCursor(0, 1);
   lcd.print("Reverb Phaser N.");
   delay(500);
   

  }  
  buttonGND8.poll();  
  if(buttonGND8.released()) {
    // Serial.write("Button 8\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(40);
    ledOffAll();
    analogWrite(ledPin8, 255);
    lcd.clear();
    lcd.print("41        Bank 8");
   lcd.setCursor(0, 1);
   lcd.print("Delay U2 Triplet");
   delay(500);
  

  }  
  buttonGND9.poll();  
  if(buttonGND9.released()) {
    // Serial.write("Button 9\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(40);
    ledOffAll();
    analogWrite(ledPin9, 255);
    lcd.clear();
    lcd.print("        Bank 9");
   lcd.setCursor(0, 1);
   lcd.print("Delay U2 Triplet");
   delay(500);
}

buttonGND10.poll();  
  if(buttonGND10.released()) {
    // Serial.write("Button 10\n");
    Serial.write(0xC1); // MIDI statusbyte: program change on channel 1.
    Serial.write(40);
    ledOffAll();
    analogWrite(ledPin10, 255);
    lcd.clear();
    lcd.print("        Bank 9");
   lcd.setCursor(0, 1);
   lcd.print("Delay U2 Triplet");
   delay(500);
}
}

void ledOffAll() {
    analogWrite(ledPin1, 0);       
    analogWrite(ledPin2, 0);
    analogWrite(ledPin3, 0);
    analogWrite(ledPin4, 0);
    analogWrite(ledPin5, 0);
    analogWrite(ledPin6, 0);
    analogWrite(ledPin7, 0);
    analogWrite(ledPin8, 0);
    analogWrite(ledPin9, 0);
    analogWrite(ledPin10, 0);
}


}

PieterP:
I don't think I understand what you want to do.

Please post all of your code, using [code][/code] tags (by using the </> button) instead of [quote][/quote] tags.

You can't use delay when polling pushbuttons.

Pieter

I left the "rough" code there, any ideas?

thanks

I still don't understand what it is you're trying to do.

PieterP:
I still don't understand what it is you're trying to do.

Do you undesrtand what my codes does?, 8 buttons that change programs, and I want to add two more (9 and 10) tu use as bank up and down sending programchange++ and --, but I want to show on lcd the correct bank number of my midi rack, If I pressed button 1 is program 44, if I want to bank up should I press button 9 and display will show on screen bank 45.

The up and down code is not the problem, the problem is how should I code the correct lines to show real bank number readling last states of the pressed 1 trhough 8 buttons.

thanks

bopelado:
Do you undesrtand what my codes does?, 8 buttons that change programs

I understand that part.

bopelado:
and I want to add two more (9 and 10) tu use as bank up and down sending programchange++ and --

What does programchange++ and -- mean?

bopelado:
but I want to show on lcd the correct bank number of my midi rack, If I pressed button 1 is program 44, if I want to bank up should I press button 9 and display will show on screen bank 45.

Just remember the last program number you sent, and increment or decrement it.

bopelado:
The up and down code is not the problem, the problem is how should I code the correct lines to show real bank number readling last states of the pressed 1 trhough 8 buttons.

Why do you need to know the last states of the buttons?

So if I understand correctly, you want two buttons that can go through all programs [0-127]. One to go up one program, and one to go down one program.
On top of that, you want 8 buttons to select specific programs.

So, for example:
Button 1 → Program 43
Button up → Program 44
Button up → Program 45
Button 4 → Program 58
Button down → Program 57
... → Program 127
Button up → Program 0
Button down → Program 127
...

Am I correct in that assumption?


There are many ways to improve your code.

First of all, you really need to learn how to use arrays, just copying the same piece of code 10 times is very bad practice.
Second, you are including the MIDI library, and you instantiate it, but you don't use it at all. Even worse, you overwrite its settings by calling Serial.begin with the wrong baud rate. The MIDI hardware baud rate is 31250, not 31200.
Serial.write(0xC1) doesn't send a program change on channel 1, but on channel 2. MIDI channels are zero-based.
You are using analogWrite (PWM) on pins that do not support PWM, and you are only using two states, 0 and 255, so there's no reason not to use digitalWrite.
The 500ms delays in your code block everything. I don't see any reason to use them. If you want to debounce your buttons, use millis() for timing, or use the PushButton class in my first post.

Pieter

PieterP:
So, for example:
Button 1 → Program 43
Button up → Program 44
Button up → Program 45
Button 4 → Program 58
Button down → Program 57
... → Program 127
Button up → Program 0
Button down → Program 127
...

Am I correct in that assumption?

thats what I want exactly and i want to show on lcd correct number as you mention

PieterP:
Just remember the last program number you sent, and increment or decrement it.

how can i do that? show correct info on LCD?

I would like to say that I start coding for aduino on may, I have a loty of experiencie on midi pc, synth, drum machines, but I never write a code o work with arduino, im totally new and start writing just for watching videos and reading a lot, so any impovment would help}}
thanks

This would be my approach:

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);               // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

      if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

      previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

    const unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

class ProgramSelector
{
  public:
    ProgramSelector(uint8_t incrementPin, uint8_t decrementPin, uint8_t minProgram, uint8_t maxProgram)
      : incrementButton{incrementPin}, decrementButton{decrementPin},
        minProgram(minProgram), maxProgram(maxProgram), program(minProgram)
    {}
    void refresh() {
      uint8_t newProgram = program;
      if (incrementButton.isPressed())
        newProgram = program < maxProgram ? program + 1 : minProgram; // Increment program number or wrap around
      if (decrementButton.isPressed())
        newProgram = program == minProgram ? maxProgram : program - 1; // Decrement program number or wrap around
      if (newProgram != program) {
        if (onChangeFn != nullptr)
          onChangeFn(newProgram, program);
        program = newProgram;
      }
    }
    void onChange(void (*fn)(uint8_t newProgram, uint8_t program)) {
      onChangeFn = fn;
    }
    uint8_t getProgram() {
      return program;
    }
    void setProgram(uint8_t newProgram) {
      if (onChangeFn != nullptr)
        onChangeFn(newProgram, program);
      program = newProgram;
    }
  private:
    PushButton incrementButton, decrementButton;
    const uint8_t minProgram, maxProgram;
    uint8_t program;
    void (*onChangeFn)(uint8_t newProgram, uint8_t program) = nullptr;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

const uint8_t incrementPin = 10;
const uint8_t decrementPin = 11;

const uint8_t channel = 1; // MIDI channel 1

const uint8_t minProgram = 0;
const uint8_t maxProgram = 127;

ProgramSelector ps = {incrementPin, decrementPin, minProgram, maxProgram};

PushButton presetButtons[] = {
  {2}, {3}, {4}, {5}, {6}, {7},  // ...
};
const size_t numberOfPresets = sizeof(presetButtons) / sizeof(presetButtons[0]);

const uint8_t presets[numberOfPresets] = {
  43,  49,  57,  58,  60,  61,  // ...
};
const uint8_t ledPins[numberOfPresets] {
  A0,  A1,  A2,  A3,  A4,  A5,  // ...
};
const char* presetNames[] = {
  "Delay 500m",
  "EQ Blues",
  "Reverb 8v Down",
  "Reverb Noise G.",
  "Reverb Tremolo N",
  "Reverb + 8v 5^",
  // ...
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

void refreshButtons() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presetButtons[i].isPressed())
      ps.setProgram(presets[i]);
}

const uint8_t NO_PRESET = -1;
uint8_t programToPreset(uint8_t program) {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presets[i] == program)
      return i;
  return NO_PRESET;
}

void onProgramChange(uint8_t newProgram, uint8_t oldProgram) {
  lcd.clear();
  lcd.print(newProgram + 1);
  static uint8_t previousPreset = NO_PRESET;
  if (previousPreset != NO_PRESET) {
    digitalWrite(ledPins[previousPreset], LOW);
  }
  uint8_t preset = programToPreset(newProgram);
  if (preset != NO_PRESET) {
    lcd.setCursor(10, 0);
    lcd.print("Bank ");
    lcd.setCursor(15, 0);
    lcd.print(preset + 1);
    lcd.setCursor(0, 1);
    lcd.print(presetNames[preset]);
    digitalWrite(ledPins[preset], HIGH);
  }
  previousPreset = preset;

  sendMIDIProgramChange(channel, newProgram);
}

void initializeLCD() {
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("   DIGITECH   ");
  lcd.setCursor(0, 1);
  lcd.print("Edge Control 2.0");
  delay(500);
}

void initializeMIDI() {
  Serial.begin(31250);
}

void initializeIO() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    pinMode(ledPins[i], OUTPUT);
}

void sendMIDIProgramChange(uint8_t channel, uint8_t program) {
  Serial.write(0xC0 | ((channel - 1) & 0xF));
  Serial.write(program & 0x7F);
}

/*--------------------------------------------------------------------------------------------------------------------------------*/

void setup() {
  initializeMIDI();
  initializeLCD();
  initializeIO();
  ps.onChange(onProgramChange);
}

void loop() {
  ps.refresh();
  refreshButtons();
}

You could rewrite the ProgramSelector class as normal functions, because you don't really need it to be object-oriented, but this is just what I started from.
I'll see if I find the time to document the code, if you don't understand what's going on, just ask.

Pieter

PieterP:
This would be my approach:

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);              // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

const unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

/--------------------------------------------------------------------------------------------------------------------------------/

class ProgramSelector
{
  public:
    ProgramSelector(uint8_t incrementPin, uint8_t decrementPin, uint8_t minProgram, uint8_t maxProgram)
      : incrementButton{incrementPin}, decrementButton{decrementPin},
        minProgram(minProgram), maxProgram(maxProgram), program(minProgram)
    {}
    void refresh() {
      uint8_t newProgram = program;
      if (incrementButton.isPressed())
        newProgram = program < maxProgram ? program + 1 : minProgram; // Increment program number or wrap around
      if (decrementButton.isPressed())
        newProgram = program == minProgram ? maxProgram : program - 1; // Decrement program number or wrap around
      if (newProgram != program) {
        if (onChangeFn != nullptr)
          onChangeFn(newProgram, program);
        program = newProgram;
      }
    }
    void onChange(void (*fn)(uint8_t newProgram, uint8_t program)) {
      onChangeFn = fn;
    }
    uint8_t getProgram() {
      return program;
    }
    void setProgram(uint8_t newProgram) {
      if (onChangeFn != nullptr)
        onChangeFn(newProgram, program);
      program = newProgram;
    }
  private:
    PushButton incrementButton, decrementButton;
    const uint8_t minProgram, maxProgram;
    uint8_t program;
    void (*onChangeFn)(uint8_t newProgram, uint8_t program) = nullptr;
};

/--------------------------------------------------------------------------------------------------------------------------------/

const uint8_t incrementPin = 10;
const uint8_t decrementPin = 11;

const uint8_t channel = 1; // MIDI channel 1

const uint8_t minProgram = 0;
const uint8_t maxProgram = 127;

ProgramSelector ps = {incrementPin, decrementPin, minProgram, maxProgram};

PushButton presetButtons[] = {
  {2}, {3}, {4}, {5}, {6}, {7},  // ...
};
const size_t numberOfPresets = sizeof(presetButtons) / sizeof(presetButtons[0]);

const uint8_t presets[numberOfPresets] = {
  43,  49,  57,  58,  60,  61,  // ...
};
const uint8_t ledPins[numberOfPresets] {
  A0,  A1,  A2,  A3,  A4,  A5,  // ...
};
const char* presetNames[] = {
  "Delay 500m",
  "EQ Blues",
  "Reverb 8v Down",
  "Reverb Noise G.",
  "Reverb Tremolo N",
  "Reverb + 8v 5^",
  // ...
};

/--------------------------------------------------------------------------------------------------------------------------------/

void refreshButtons() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presetButtons[i].isPressed())
      ps.setProgram(presets[i]);
}

const uint8_t NO_PRESET = -1;
uint8_t programToPreset(uint8_t program) {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presets[i] == program)
      return i;
  return NO_PRESET;
}

void onProgramChange(uint8_t newProgram, uint8_t oldProgram) {
  lcd.clear();
  lcd.print(newProgram + 1);
  static uint8_t previousPreset = NO_PRESET;
  if (previousPreset != NO_PRESET) {
    digitalWrite(ledPins[previousPreset], LOW);
  }
  uint8_t preset = programToPreset(newProgram);
  if (preset != NO_PRESET) {
    lcd.setCursor(10, 0);
    lcd.print("Bank ");
    lcd.setCursor(15, 0);
    lcd.print(preset + 1);
    lcd.setCursor(0, 1);
    lcd.print(presetNames[preset]);
    digitalWrite(ledPins[preset], HIGH);
  }
  previousPreset = preset;

sendMIDIProgramChange(channel, newProgram);
}

void initializeLCD() {
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("  DIGITECH  ");
  lcd.setCursor(0, 1);
  lcd.print("Edge Control 2.0");
  delay(500);
}

void initializeMIDI() {
  Serial.begin(31250);
}

void initializeIO() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    pinMode(ledPins[i], OUTPUT);
}

void sendMIDIProgramChange(uint8_t channel, uint8_t program) {
  Serial.write(0xC0 | ((channel - 1) & 0xF));
  Serial.write(program & 0x7F);
}

/--------------------------------------------------------------------------------------------------------------------------------/

void setup() {
  initializeMIDI();
  initializeLCD();
  initializeIO();
  ps.onChange(onProgramChange);
}

void loop() {
  ps.refresh();
  refreshButtons();
}



You could rewrite the ProgramSelector class as normal functions, because you don't really need it to be object-oriented, but this is just what I started from.
I'll see if I find the time to document the code, if you don't understand what's going on, just ask.

Pieter

You rock Peter! at first try everything is working fine, just a coulpe of questions, now I'm with Arduino Uno, but since has more buttons now I will go with Mega board, LCD pins on UNo are A4 and A5, on Mega should be 20 and 21 I guess right?

the button up and down works as I wanted, is there any change to wirte the name complete? I mean shows only number but if I pass thourgh bank 44 for explame shows the name complete writen on code of const char* presetNames. I gues is not easy as filling the names on that because should I write ll banks names and start from 1 to 128 and the variable should be different, each const characthert equals number preset above, this idea will work if I should have start from bank 1 and not 44 right?

thank!

bopelado:
You rock Peter! at first try everything is working fine, just a coulpe of questions, now I'm with Arduino Uno, but since has more buttons now I will go with Mega board

You really don't need a Mega if all you want to do is read buttons and drive LEDs. You can save a lot of pins by arranging your buttons in a matrix, and by using shift registers for your LEDs.
Do you need MIDI over USB? Or does your project require a 5-pin DIN connector?

bopelado:
LCD pins on UNo are A4 and A5, on Mega should be 20 and 21 I guess right?

Correct. Your LCD uses the I²C bus (pins SDA and SCL).

bopelado:
the button up and down works as I wanted, is there any change to wirte the name complete? I mean shows only number but if I pass thourgh bank 44 for explame shows the name complete writen on code of const char* presetNames. I gues is not easy as filling the names on that because should I write ll banks names and start from 1 to 128 and the variable should be different, each const characthert equals number preset above, this idea will work if I should have start from bank 1 and not 44 right?

thank!

The principle is the same, but instead of using the preset number [0, 7], use the program number [0, 127] to index an array of strings. The tricky part is fitting it all into the Arduino's memory. It's probably too much data to put in RAM, so you'll have to put it in flash using PROGMEM. This is a bit cumbersome, but doable.

const char program_0[] PROGMEM = "Effect Zero";
const char program_1[] PROGMEM = "Effect One";
const char program_2[] PROGMEM = "Effect Two";
const char program_3[] PROGMEM = "Effect Three";
// ...

const __FlashStringHelper* programNames[] = {
  (const __FlashStringHelper*) program_0,
  (const __FlashStringHelper*) program_1,
  (const __FlashStringHelper*) program_2,
  (const __FlashStringHelper*) program_3,
  // ...
};
void onProgramChange(uint8_t newProgram, uint8_t oldProgram) {
  lcd.clear();
  lcd.print(newProgram + 1);
  static uint8_t previousPreset = NO_PRESET;
  if (previousPreset != NO_PRESET) {
    digitalWrite(ledPins[previousPreset], LOW);
  }
  uint8_t preset = programToPreset(newProgram);
  if (preset != NO_PRESET) {
    lcd.setCursor(10, 0);
    lcd.print("Bank ");
    lcd.setCursor(15, 0);
    lcd.print(preset + 1);
    digitalWrite(ledPins[preset], HIGH);
  }
  previousPreset = preset;

  lcd.setCursor(0, 1);
  lcd.print(programNames[newProgram]);

  sendMIDIProgramChange(channel, newProgram);
}

Note that you have to add all 128 names, or else you'll have indexing errors.
I didn't post them all because it'd exceed the character limit, but here's a Python script to generate them all:

identifier = 'program'
defaultName = 'Effect'
numberOfStrings = 128

for i in range(numberOfStrings):
    print('const char {}_{}[] PROGMEM = "{} {}";'.format(identifier, i, defaultName, i))
    
print('\nconst __FlashStringHelper* {}Names[{}] = {{'.format(identifier, numberOfStrings))
for i in range(numberOfStrings):
    print('  (const __FlashStringHelper*) {}_{},'.format(identifier, i))

print("};")

Pieter

PieterP:
You really don't need a Mega if all you want to do is read buttons and drive LEDs. You can save a lot of pins by arranging your buttons in a matrix, and by using shift registers for your LEDs.
Do you need MIDI over USB? Or does your project require a 5-pin DIN connector?Correct. Your LCD uses the I²C bus (pins SDA and SCL).

I'm using MIDI 5pin connector, I guess I will try you options, What you mean by "arrangien buttons in a matrix" currently Im using 8 leds, eight buttons 2 pins for LCD, I will now add 2 more buttons.

thanks again, im really appreciate what you did!

Like so:


You don't need the diodes if you only need to register one button at a time. There's countless examples online on how to use it. You could find some inspiration here.

PieterP:
Like so:


You don't need the diodes if you only need to register one button at a time. There's countless examples online on how to use it. You could find some inspiration here.

}
is good idea But I already have my box with all the buttons and led done, I was trying to add or fix the idea of the full names on each preset but I got a code error, actually i have all the names writen there are only 0-99 not 0-127 so is a little more easy I have an error here

void onProgramChange(uint8_t newProgram, uint8_t oldProgram) {
  lcd.clear();
  lcd.print(newProgram + 1);
  static uint8_t previousPreset = NO_PRESET;
  if (previousPreset != NO_PRESET) {
    digitalWrite(ledPins[previousPreset], LOW);
  }
  uint8_t preset = programToPreset(newProgram);
  if (preset != NO_PRESET) {
    lcd.setCursor(10, 0);
    lcd.print("Bank ");
    lcd.setCursor(15, 0);
    lcd.print(preset +
              digitalWrite(ledPins[preset], HIGH);
  }
  previousPreset = preset;

  lcd.setCursor(0, 1);
  lcd.print(programNames[newProgram]);

  sendMIDIProgramChange(channel, newProgram);
}
lcd.print(preset +
              digitalWrite(ledPins[preset], HIGH);

What's this?

Please post or attach your full code, and post the full error message. If you only have the first 100 names, you have to set the maxProgram accordingly.

Pieter

PieterP:

lcd.print(preset +

digitalWrite(ledPins[preset], HIGH);



What's this?

Please post or attach your full code, and post the full error message. If you only have the first 100 names, you have to set the maxProgram accordingly.

Pieter

yes is wrong line now I see I jsut copy and paste, but here is the full code,

#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27, 16, 2); // lcd SDA 20, SCL 21

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);               // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

      if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

      previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

    const unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

class ProgramSelector
{
  public:
    ProgramSelector(uint8_t incrementPin, uint8_t decrementPin, uint8_t minProgram, uint8_t maxProgram)
      : incrementButton{incrementPin}, decrementButton{decrementPin},
        minProgram(minProgram), maxProgram(maxProgram), program(minProgram)
    {}
    void refresh() {
      uint8_t newProgram = program;
      if (incrementButton.isPressed())
        newProgram = program < maxProgram ? program + 1 : minProgram; // Increment program number or wrap around
      if (decrementButton.isPressed())
        newProgram = program == minProgram ? maxProgram : program - 1; // Decrement program number or wrap around
      if (newProgram != program) {
        if (onChangeFn != nullptr)
          onChangeFn(newProgram, program);
        program = newProgram;
      }
    }
    void onChange(void (*fn)(uint8_t newProgram, uint8_t program)) {
      onChangeFn = fn;
    }
    uint8_t getProgram() {
      return program;
    }
    void setProgram(uint8_t newProgram) {
      if (onChangeFn != nullptr)
        onChangeFn(newProgram, program);
      program = newProgram;
    }
  private:
    PushButton incrementButton, decrementButton;
    const uint8_t minProgram, maxProgram;
    uint8_t program;
    void (*onChangeFn)(uint8_t newProgram, uint8_t program) = nullptr;
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

// MIDI init


const uint8_t incrementPin = 11; // button Up
const uint8_t decrementPin = 12; // button Down

const uint8_t channel = 1; // MIDI channel 1

const uint8_t minProgram = 0;
const uint8_t maxProgram = 99;

const char program_0[] PROGMEM = "Effect Zero";
const char program_1[] PROGMEM = "Effect One";
const char program_2[] PROGMEM = "Effect Two";
const char program_4[] PROGMEM = "Effect Three";
const char program_5[] PROGMEM = "Effect Zero";

// ...

const __FlashStringHelper* programNames[] = {
  (const __FlashStringHelper*) program_0,
  (const __FlashStringHelper*) program_1,
  (const __FlashStringHelper*) program_2,
  (const __FlashStringHelper*) program_4,
  (const __FlashStringHelper*) program_5,
 
  // ...
};

ProgramSelector ps = {incrementPin, decrementPin, minProgram, maxProgram};

PushButton presetButtons[] = {
  {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10},  // ...
};
const size_t numberOfPresets = sizeof(presetButtons) / sizeof(presetButtons[0]);

const uint8_t presets[numberOfPresets] = {
  0,  1,  2,  3,  4,  5,  6,  7,  8,   // ...
};
const uint8_t ledPins[numberOfPresets] {
  A0,  A1,  A2,  A3,  A4,  A5,  A6,  A7,  A8,  // ...
};
const char* presetNames[] = {
  "Delay 500m",
  "EQ Blues",
  "Reverb 8v Down",
  "Reverb Noise G.",
  "Reverb Tremolo N",
  "Reverb + 8v 5^",
  "Reverb Flang. N.",
  "U2 Delay Triplets",
  
  // ...
};

/*--------------------------------------------------------------------------------------------------------------------------------*/

void refreshButtons() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presetButtons[i].isPressed())
      ps.setProgram(presets[i]);
}

const uint8_t NO_PRESET = -1;
uint8_t programToPreset(uint8_t program) {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    if (presets[i] == program)
      return i;
  return NO_PRESET;
}

void onProgramChange(uint8_t newProgram, uint8_t oldProgram) {
  lcd.clear();
  lcd.print(newProgram + 1);
  static uint8_t previousPreset = NO_PRESET;
  if (previousPreset != NO_PRESET) {
    digitalWrite(ledPins[previousPreset], LOW);
  }
  uint8_t preset = programToPreset(newProgram);
  if (preset != NO_PRESET) {
    lcd.setCursor(10, 0);
    lcd.print("Bank ");
    lcd.setCursor(15, 0);
    lcd.print(preset + 1);
    digitalWrite(ledPins[preset], HIGH);
  }
  previousPreset = preset;

  lcd.setCursor(0, 1);
  lcd.print(programNames[newProgram]);

  sendMIDIProgramChange(channel, newProgram);
}

void initializeLCD() {
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("   DIGITECH   ");
  lcd.setCursor(0, 1);
  lcd.print("Edge Control 3.1");
  delay(500);
}

void initializeMIDI() {
  Serial.begin(31250);
}

void initializeIO() {
  for (uint8_t i = 0; i < numberOfPresets; i++)
    pinMode(ledPins[i], OUTPUT);
}

void sendMIDIProgramChange(uint8_t channel, uint8_t program) {
  Serial.write(0xC0 | ((channel - 1) & 0xF));
  Serial.write(program & 0x7F);
}

identifier = 'program'
defaultName = 'Effect'
numberOfStrings = 100

for i in range(numberOfStrings):
    print('const char {}_{}[] PROGMEM = "{} {}";'.format(identifier, i, defaultName, i))
   
print('\nconst __FlashStringHelper* {}Names[{}] = {{'.format(identifier, numberOfStrings))
for i in range(numberOfStrings):
    print('  (const __FlashStringHelper*) {}_{},'.format(identifier, i))

print("};")



/*-------------------------------------------------------------------------------------------------------------------------------*/

void setup() {
    initializeMIDI();
    initializeLCD();
    initializeIO();
    ps.onChange(onProgramChange);

  }

void loop() {
  ps.refresh();
  refreshButtons();

  
}

I can't copy with full list names but here a link with the code

Please use the 'Attachments and other options' to attach code, not some insecure file sharing service.

Put your program names in the program_# variables. Delete the presetNames array, it is replaced by programNames.

That piece of Python code was meant to create the Arduino code. Run it using Python, don't include it in your Arduino sketch, the C/C++ compiler won't know what to do with it.

Here's what I meant (see attachment).
Edit: add static asserts to sketch in order to check array length.

program_selector_many_strings_progmem.ino (15.1 KB)