Adding more button press LED's to Control Surface code example

Thanks to PieterP turning me onto his Control Surface library yesterday, I've been able to streamline the code for my MCS. I have multiple buttons, pots and mux pots all working. I even have an LED indicator that turns off and on with a button press. I would love to add more LED's for other buttons, but I haven't been able to figure out how. I know I'm missing something completely obvious. This is the LED button press example code I used from the Control Surface library.

// Include the library
#include <Arduino_Helpers.h>
 
#include <AH/Hardware/Button.hpp>
 
// Create a Button object that reads a push button connected to pin 2:
Button pushbutton = {2};
 
// The pin with the LED connected:
const pin_t ledPin = LED_BUILTIN;
 
void setup() {
  pinMode(ledPin, OUTPUT);
  pushbutton.begin();
  // You can invert the input, for use with normally closed (NC) switches:
  // pushbutton.invert();
}
 
void loop() {
  static bool ledState = LOW;
  // Read the digital input, debounce the signal, and check the state of
  // the button:
  if (pushbutton.update() == Button::Falling) {
    ledState = !ledState; // Invert the state of the LED
    // Update the LED with the new state
    digitalWrite(ledPin, ledState ? HIGH : LOW);
  }
}

If it helps, here is the whole (in progress) Sketch I'm using, so far.

#include <Control_Surface.h> // Include the Control Surface library
 #include <Arduino_Helpers.h>
 
#include <AH/Hardware/Button.hpp>
// Create a Button object that reads a push button connected to pin 8:
Button pushbutton = {8};
 
// The pin with the LED connected:
const pin_t ledPin = 7;

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Instantiate an analog multiplexer
CD74HC4051 mux = {
  A0,       // Analog input pin
  {3, 4, 5} // Address pins S0, S1, S2
};


// Create an array of potentiometers that send out
// MIDI Control Change messages when you turn the
// potentiometers connected to the eight input pins of
// the multiplexer
CCPotentiometer volumePotentiometers[] = {
  {mux.pin(0), {MIDI_CC::Channel_Volume, CHANNEL_1}},
  {mux.pin(1), {MIDI_CC::Channel_Volume, CHANNEL_2}},
  //{mux.pin(2), {MIDI_CC::Channel_Volume, CHANNEL_3}},
 //{mux.pin(3), {MIDI_CC::Channel_Volume, CHANNEL_4}},
 // {mux.pin(4), {MIDI_CC::Channel_Volume, CHANNEL_5}},
  //{mux.pin(5), {MIDI_CC::Channel_Volume, CHANNEL_6}},
 // {mux.pin(6), {MIDI_CC::Channel_Volume, CHANNEL_7}},
 // {mux.pin(7), {MIDI_CC::Channel_Volume, CHANNEL_8}},
};
 
// Instantiate an array of CCPotentiometer objects
CCPotentiometer potentiometers[] = {
  //{A0,0x10},       // Analog pin connected to potentiometer 1
       // Controller number of the first potentiometer
  //{A1,0x11},       // Analog pin connected to potentiometer 2
        // Controller number of the second potentiometer
  {A2, 0x12}, // Etc.
  //{A3, 0x13},
  //{A4, 0x14},
  //{A5, 0x15},
};

// Instantiate a CCButton object
CCButton button [] = {
  {8,
  {MIDI_CC::General_Purpose_Controller_5, CHANNEL_1}},
  {9,
  {MIDI_CC::General_Purpose_Controller_5, CHANNEL_2}},
   };
 
void setup() {
  Control_Surface.begin(); // Initialize Control Surface
  pinMode(ledPin, OUTPUT);
  pushbutton.begin();
  // You can invert the input, for use with normally closed (NC) switches:
  // pushbutton.invert();
}
 
void loop() {
  Control_Surface.loop(); // Update the Control Surface
 {
  static bool ledState = LOW;
  // Read the digital input, debounce the signal, and check the state of
  // the button:
  if (pushbutton.update() == Button::Falling) {
    ledState = !ledState; // Invert the state of the LED
    // Update the LED with the new state
    digitalWrite(ledPin, ledState ? HIGH : LOW);
  }
}}

Do you understand how the Button example works? What parts are you having trouble with?

The Button class does nothing more than debouncing an input, it doesn't do anything related to MIDI.
It's not recommended to use both a Button and a CCButton on the same pin, since CCButton already contains a Button under the hood. You can use the CCButton::getButtonState() method, it returns the result of the latest call to Button::update():

#include <Control_Surface.h> // Include the Control Surface library

// The pin with the LED connected:
const pin_t ledPin = 7;

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Instantiate a CCButton object
CCButton button = {
  8,
  {MIDI_CC::General_Purpose_Controller_5, CHANNEL_1},
};

void setup() {
  Control_Surface.begin(); // Initialize Control Surface
  pinMode(ledPin, OUTPUT);
}

void loop() {
  Control_Surface.loop(); // Update the Control Surface
  static bool ledState = false;
  if (button.getButtonState() == Button::Falling) {
    ledState = !ledState; // Invert the state of the LED
    // Update the LED with the new state
    digitalWrite(ledPin, ledState ? HIGH : LOW);
  }
}

Pieter

Hi Pieter,
Thank you very much for responding. Not having any previous programming experience, it's all still a bit confusing for me as I work toward trying to understand it. I think I understand a fair bit of what you are saying and I am slightly following the concept of using CCButton::getButtonState() as a way to use the Button pin to activate the CCButton objective. I suppose if I can't figure that out, I could always mult the button input to a second pin and have one be the CCButton activator and the other would be for the LED, but that seems inelegant at best.

Ultimately, my goal is to create a midi controller with 8 faders, one master fader, 8 mute buttons and 8 solo buttons. At least 2 banks of data to extend the 8 faders across more DAW channels and have LED indicators for when each solo or mute button has been pressed and is active. I think Control Panel can help me achieve all of these objectives and my initial pass at the code can work without the banks or LEDs, but I'd like to get those integrated eventually.

I know this is such a noobish question, but just drilling down, I was trying with your Button example to just create a second button that would activate a second LED, and got frustrated that I couldn't figure that out. Would I define the second button as "pushbutton2" or something? Or is it something so simple that when I find out I'm going to facepalm myself into a coma?

wordsushi:
I am slightly following the concept of using CCButton::getButtonState() as a way to use the Button pin to activate the CCButton objective.

I'm not sure I understand what you mean here.

What I was trying to say is that the CCButton class already takes care of reading the state of your switch: internally CCButton relies on Button to do three main things: reading the input pin with your switch connected, debouncing it, and doing state change detection.
The Button::update() method reads the state of the input pin, and returns one of the four possible states of the switch: it can be pressed, released, falling or rising. Falling and rising are transitions between the former two states. CCButton uses the output of Button::update() to know when to send MIDI CC messages.

Since every CCButton object already has a Button object inside of it, there's no need to create a second Button object that reads from the same input pin. You can just use the Button that's inside of the CCButton object.
To get the state of the internal Button of a CCButton object, you use the CCButton::getButtonState() method (it returns either pressed, released, rising or falling, just like Button::update()). You can use this information to turn on or off your LED.

wordsushi:
I suppose if I can't figure that out, I could always mult the button input to a second pin and have one be the CCButton activator and the other would be for the LED, but that seems inelegant at best.

Why would you do that? It's not clear to me what problem that would solve.

wordsushi:
I know this is such a noobish question, but just drilling down, I was trying with your Button example to just create a second button that would activate a second LED, and got frustrated that I couldn't figure that out. Would I define the second button as "pushbutton2" or something? Or is it something so simple that when I find out I'm going to facepalm myself into a coma?

Each variable that you declare must have a unique name. You cannot have two variables with the same identifier in the same scope.
The clean approach here is to use an array, this is demonstrated in most examples, such as the Getting Started example.

wordsushi:
Ultimately, my goal is to create a midi controller with 8 faders, one master fader, 8 mute buttons and 8 solo buttons. At least 2 banks of data to extend the 8 faders across more DAW channels and have LED indicators for when each solo or mute button has been pressed and is active. I think Control Panel can help me achieve all of these objectives and my initial pass at the code can work without the banks or LEDs, but I'd like to get those integrated eventually.

The problem with LEDs that are updated by the switches is that they can get out of sync with the DAW. When you click the solo button using your mouse, the corresponding LED will not be updated.
The solution is to use the NoteValueLED class that listens for incoming MIDI data to determine whether the LED should be on or off. This is shown in the same Getting Started example.
This does require your DAW to send feedback to the Arduino. If your DAW does not support that, you're out of luck. Most DAWs support at least the Mackie Control Universal protocol, which should send feedback for things like solo and mute buttons.

To write any code for Arduino or the Control Surface library, you'll need some basic knowledge of C++. You have to know what variables are, how functions work, some basic knowledge about classes and methods, etc. That's not something you learn from reading example code alone, I'd recommend following an introductory C++ tutorial first. You could try learncpp.com, for example: https://www.learncpp.com/cpp-tutorial/introduction-to-variables/

Thanks again Pieter! You bring up a very good point with the LED's and the risk of them going out of sync with the DAW. So since a mute/solo status on a channel would be represented accurately on the DAW screen anyway, putting LEDs on the outboard controller really isn't as important a consideration overall, but rather an experiment I tried to figure out with my limited knowledge and will instead put aside for a later date after I better understand the coding necessary. Since Universal Audio's Console software does not support the MCU protocol I can back-burner the LED's for now.

Luckily, big thanks to your library, I do have enough working code in place to create an upgraded version of my first DIY midi controller which is running in my studio, and then at least build out the 8 fader controller with mute/solo buttons and then try to figure out how to implement Banks to extend it as I dig deeper into C++. I'll definitely check out that tutorial. I appreciate the link, and I want you to know I really, really appreciate you answering my truly annoying and uninformed noob questions. Thank you for pointing me in the right direction.

This illustrates quite well the problem I have with using this library. In terms of software it is quite advanced for a beginner to understand, but it offers the sweet seductive returns of being quick and easy. However when it comes to using it it is often a dead end for beginners because nothing is being learned and a beginner ends up feeling they are stupid.

There is nothing very difficult using “normal “ code to do this and offers the big bonus of actually teaching basic techniques.