Control Surface: Program Change

I'm working on a Midi foot controller using Control-Surface library (it's a great library!).
I need to use two buttons/switches to browse patches up and down, respectively.
So far I was able to send fixed program changes using diferent switches, but I need to use the same switch to browse Up and other switch to browse Down.
Thanks in advance.

Increment/decrement a variable based on the button presses and determine which patch to use based on that variable. If you add an array to the mix your code can be smaller and smarter.

Thanks koraks
I tried that but I'm missing something.

I use PCButton pcBtn2 = {8, {nroPatch+1, CHANNEL_1},};
but nroPatch is not increment its value.
I tried to place the sentence inside the void loop(), but in that section PCButton don't work.
Any help?

That line as such doesn't look right, with the curly braces. It also looks like a class instantiation which, if you have this line in your main loop, will create a new local instance of the PCButton class on each pass of the loop. That's not what you'd want. Can you post the full code?

Thanks for help koracks. Below, you'll see the full code. This code works great for activate/deactivate individual (using one switch for each CCButtonLateched) FXs on my guitar rack.
Also the code works great sending Program Change using PCButton, but I need to use the same physical switch to browse Up all the patches on the guitar rack. Of course, I'll use another switch to browse all the patches down.
The code:

#include <Control_Surface.h>

// demo en base a library Control Surface tomada de web: CC toggle on/off - #4 by slipstick
////////////////////////////

HardwareSerialMIDI_Interface midi {Serial, MIDI_BAUD};
//byte nroPatch=0x00;

// switches to activate/deactivate FX modules on Guitar rack
CCButtonLatched button1 = {
6, // pin number
{53, CHANNEL_1}// CC number and MIDI channel
};

CCButtonLatched button2 = {5,{52, CHANNEL_1}};
CCButtonLatched button3 = {4,{51, CHANNEL_1}};
CCButtonLatched button4 = {3,{54, CHANNEL_1}};

// switch to send Program Change (0x0B)
PCButton pcBtn2 = {8, {0x0B, CHANNEL_1},}; // nro de pin y de patch (en hexa)

void setup()
{
Control_Surface.begin();
}

void loop()
{
Control_Surface.loop();
}

Ok, well, I think what you want should be possible, however, the implementation of Control_Surface is so byzantine that I got entirely lost in trying to navigate its structure. I think I did see some promising functions in MIDIButtonLatched.hpp lines 53-61:

    /// Get the current state.
    bool getState() const { return state; }

    /// Set the state to the given value.
    /// Sends the appropriate MIDI event.
    void setState(bool state) {
        this->state = state;
        state ? sender.sendOn(address) : sender.sendOff(address);
    }

This suggest you could at least read the current status of a button in your own code, and also change the toggle state of a button from your code. Using this, you should be able to write a little function that monitors the state of the button; if it has changed to 'true' since the last time you read it (and it was 'false' previously), you could call the .setState() function twice; once to turn it off and once to turn it on again. This should trigger a MIDI event to be sent by Control_Surface to the host system.
I haven't tried it, and due to the complexity of Control_Surface I don't think I ever will; I think it's a very functional and convenient set of libraries, but due to its structure, it's also a bit of a case of "take it for what it is, but don't try to mess with it more than necessary". Could also be my rudimentary knowledge of C++ of course.

PCButton only sends a single program change message for a single patch.

If you want to cycle through a list of predefined patches, you can use the Program-Changer example. You can compose it with any selector you want, it sounds like the IncrementDecrementSelector suits your needs:

// Instantiate a program changer with 3 programs
ProgramChanger<3> programChanger = {
  {
    MIDI_PC::Acoustic_Grand_Piano, // list of programs
    MIDI_PC::Rock_Organ,
    MIDI_PC::Electric_Bass_Pick,
  },
  CHANNEL_1, // MIDI channel to use
};
 
// Instantiate a selector to control which one of the three programs is active
IncrementDecrementSelector<3> selector = {
    programChanger, // what to select
    {2, 3},         // push button pins (increment, decrement)
    Wrap::Wrap,     // wrap around if min/max program is reached
};

While the predefined classes are rather opaque, the library aims to provide the basic building blocks in a modular way. This allows you to easily take control yourself, to send your own MIDI messages, for example:

#include <Control_Surface.h>

USBDebugMIDI_Interface midi; // You can use any MIDI interface here

Button up_button   = 2; // define button pin numbers here
Button down_button = 3;

constexpr uint8_t min_program = 0;
constexpr uint8_t max_program = 8;

void setup() {
  Control_Surface.begin();
  up_button.begin();
  down_button.begin();
}

void loop() {
  Control_Surface.loop();
  static uint8_t program = min_program;
  if (program < max_program && up_button.update() == Button::Falling)
    Control_Surface.sendPC(++program);
  if (program > min_program && down_button.update() == Button::Falling)
    Control_Surface.sendPC(--program);
}

Hi koracks, hi PieterP
Thank you so much!
PieterP: this is really great! It works!

I'll research your library to learn how to use for other MIDI tasks.
Really thank you!

That's definitely the strong point of control surface. It makes the file structure a bit awkward to navigate, but with sufficient knowledge of its structure, it should be very flexible.

Hi PieterP,
My controller have 8 push buttons and I would like each one have a LED indicator that turn on/off when I press that button.
Could be possible to add LEDs indicator that turn on/off every time I press a button?
Can LED and its button share the same pin on the UNO board?
Thanks

What kind of messages should the buttons send?

Yes, an LED and its button can share the same pin, but this is not supported out of the box.

What kind of messages should the buttons send?
The buttons send MIDI CCs.

CCButtonLatched button1 = {3,{51, CHANNEL_1}}; // Pin,CC number and MIDI channel
CCButtonLatched button2 = {4,{52, CHANNEL_1}};
CCButtonLatched button3 = {5,{53, CHANNEL_1}};
CCButtonLatched button4 = {6,{54, CHANNEL_1}};
...
...

The LED (each LED for each button) would allow me to know when a FX module on my external rack is affected by the button pressed.
Thank you.

Does this answer your question? CCButtonLatched LED without ShiftRegister · Issue #241 · tttapa/Control-Surface · GitHub

Let me know if I'm right.
This code uses the same pin (#8) for four buttons...
CD74HC4067 mux = {
8, // Digital input pin
{2, 3, 4, 5} // Address pins S0, S1, S2, S3
};
.. and then assign pins where LEDs are connected:
const pin_t ledPins = { 10, 16, 14, 15 };

is this correct?

You don't have to use a multiplexer, just replace the pin numbers for the buttons:

CCButtonLatched footswitchControl[] = {
  { 3, { 51, CHANNEL_1 } },
  { 4, { 52, CHANNEL_1 } },
  { 5, { 53, CHANNEL_1 } },
  { 6, { 54, CHANNEL_1 } },
};

Great.
And can I use the same pins for the LEDs? I need 8 buttons and 8 LEDs.

Not with that code. I'll see if I can write an example tonight or tomorrow. In the meantime, keep in mind that you can use the pins A0-A5 as normal digital pins as well.

Edit: not much time to write and test code right now.

It would be great! really thank you!

You could try something like this:

#include <Control_Surface.h>  // Include the library
 
USBMIDI_Interface midi;  // Instantiate a MIDI Interface to use

class CCButtonLatchedWithLED : public MIDIOutputElement {
 public:
  CCButtonLatchedWithLED(pin_t pin, MIDIAddress address)
    : button(pin), pin(pin), address(address) {}
 
  // This method is called once by `Control_Surface.begin()`
  void begin() final override {
    button.begin();
  }
 
  // This method is called continuously by `Control_Surface.loop()`
  void update() final override {
    if (state) {
      pinMode(pin, INPUT_PULLUP); // Switch to input mode to read button
      delayMicroseconds(1);
    }
    AH::Button::State buttonstate = button.update(); // Read the button
    if (buttonstate == AH::Button::Falling) {        // If pressed
      state = !state;                                // Toggle the state
      Control_Surface.sendCC(address, state ? 127 : 0);
    }
    if (state) {
      digitalWrite(pin, LOW); // Switch to low and
      pinMode(pin, OUTPUT);   // output mode to drive the LED
    }
  }
 
 private:
  AH::Button button;
  pin_t pin;
  const MIDIAddress address;
  bool state;
};

CCButtonLatchedWithLED footswitchControl[] = {
  { 3, { 51, CHANNEL_1 } },
  { 4, { 52, CHANNEL_1 } },
  { 5, { 53, CHANNEL_1 } },
  { 6, { 54, CHANNEL_1 } },
};

void setup() {
  Control_Surface.begin();  // Initialize the Control Surface
}

void loop() {
  Control_Surface.loop();  // Update the Control Surface
}

Connections:

                Arduino pin
                     |
Ground --- button ---o--- LED --- resistor --- Vcc

Thank you really much.