tttapa / MIDI_controller

This is a library for creating a MIDI controller using an Arduino or Teensy board.
GNU General Public License v3.0
404 stars 69 forks source link

Conect 2 or more buttons to a single led #97

Open sincos5 opened 4 years ago

sincos5 commented 4 years ago

I dont know how to do it without making a mess with my signals.

tttapa commented 4 years ago

The MIDI Controller library doesn't support this, Control Surface does.

What do you want the LED to do?

sincos5 commented 4 years ago

Just turn on and off whenever a button is pressed

tttapa commented 4 years ago

You could do something like this:

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

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

using namespace MIDI_Notes;

// Instantiate an array of NoteButton objects
Array<NoteButton, 3> buttons = {{
  // Push button on pin 5, note C4 (middle C) on MIDI channel 1
  {5, {note(C, 4), CHANNEL_1}},
  // Push button on pin 6, note D4 on MIDI channel 1
  {6, {note(D, 4), CHANNEL_1}},
  // etc.
  {7, {note(E, 4), CHANNEL_1}},
}};

constexpr pin_t ledPin = LED_BUILTIN;

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

/// Return true if the predicate evaluates to true for at least one of the elements of the iterable
template<class Iterable, class Predicate> bool any_of(const Iterable &iterable, const Predicate &pred) {
  for (auto iterator = iterable.begin(); iterator != iterable.end(); ++iterator)
    if (pred(*iterator))
      return true;
  return false;
}

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

  // If any of the push buttons is pressed
  bool pressed = any_of(buttons, [](const NoteButton &button) { 
    return button.getButtonState() == Button::Pressed;
  });
  // Turn on the LED
  digitalWrite(ledPin, pressed);
}

I don't have any hardware to test it at the moment, but it seems to compile without problems.

sincos5 commented 4 years ago

Bussy week, today was the day i had to try this! Works grate, just one last doubt, i didnt understood how this part of the code is working could you explain to me, or sendme a link where i can find the explenation please?

/// Return true if the predicate evaluates to true for at least one of the elements of the iterable
template<class Iterable, class Predicate> bool any_of(const Iterable &iterable, const Predicate &pred) {
  for (auto iterator = iterable.begin(); iterator != iterable.end(); ++iterator)
    if (pred(*iterator))
      return true;
   return false;
}

**im sory i dont know how to comment with code

tttapa commented 4 years ago

You can use the following to format the code:

```cpp
// Your code here
```

The any_of function takes in an array or other collection, and a function (predicate). It loops over the array, and calls the function on each element. If the function returns true for all elements, any_of returns true. If there's at least one element for which the function returns false, any_of returns false as well.

It is roughly based on the standard library std::any_of function: https://en.cppreference.com/w/cpp/algorithm/all_any_none_of

panakronic commented 4 years ago

Hi there,

I am trying to connect digital 16 buttons to a single led and have the led turn on and off whenever any of the 16 button is pressed. I have tried adding control_surface mentioned above but is having hard time getting it to work.

I'm not expert in coding, just wondering if anyone can help me out a bit? Thank you in advanced. My code below is what I have so far with no control surface added:

Mega-Control-Control-Final.zip


#include <MIDI_Controller.h> // Include the library

// Create a two new instances of the class 'Analog', on pins A0 and A1,
// that send MIDI messages with controller 7 (channel volume) on channels 1 and 2
Analog potentiometer1(A0, MIDI_CC::Channel_Volume, 1);
Analog potentiometer2(A1, MIDI_CC::Channel_Volume, 2); // uncomment to use in sketch
Analog potentiometer3(A2, MIDI_CC::Sound_Controller_3, 3);
Analog potentiometer4(A3, MIDI_CC::Sound_Controller_4, 4); // uncomment to use in sketch
Analog potentiometer5(A4, MIDI_CC::Sound_Controller_5, 5);
Analog potentiometer6(A5, MIDI_CC::Sound_Controller_6, 6);
Analog potentiometer7(A6, MIDI_CC::Effects_1, 7);
Analog potentiometer8(A7, MIDI_CC::Effects_2, 8);
Analog potentiometer9(A8, MIDI_CC::Effects_3, 9);
Analog potentiometer10(A9, MIDI_CC::Effects_4, 10);
Analog potentiometer11(A10, MIDI_CC::Effect_Control_1, 11);
Analog potentiometer12(A11, MIDI_CC::Effect_Control_2, 12);

// Create a two new instances of the class 'Digital', on pins 2 and 3,
// that send MIDI messages with note numbers 0x10 and 0x11 on MIDI channel 1
Digital muteButton_A(13, 0x14, 1);
Digital muteButton_B(12, 0x15, 1);
Digital muteButton_C(11, 0x16, 1);
Digital muteButton_D(10, 0x17, 1);

Digital muteButton_E(9, 0x18, 1);
Digital muteButton_F(8, 0x19, 1);
Digital muteButton_G(7, 0x1A, 1);
Digital muteButton_H(6, 0x1B, 1);

Digital muteButton_I(5, 0x1C, 1);
Digital muteButton_J(4, 0x1D, 1);
Digital muteButton_K(3, 0x1E, 1);
Digital muteButton_L(2, 0x1F, 1);

Digital muteButton_M(14, 0x20, 1);
Digital muteButton_N(15, 0x21, 1);
Digital muteButton_O(16, 0x22, 1);
Digital muteButton_P(17, 0x23, 1);

Digital muteButton_Q (18, 0x33, 2);
Digital muteButton_R (19, 0x34, 2);

// Create a new bank that has two tracks per bank
Bank bank(16);

// Create a new bank selector that changes the bank setting of the bank we just created
// It has pushbuttons connected to pins 11 and 12 that increment or decrement the bank setting,
// and 4 LEDs to pins 4, 5, 6 and 7 that display the current bank setting.
BankSelector bankSelector(bank, { 21, 20 }, {46, 48, 50, 52 } );

/* Alternatively, you can use arrays for the pin numbers:

   const pin_t buttonPins[] = { 11, 12 };
   const pin_t ledPins[] = { 4, 5, 6, 7 };

   BankSelector bankSelector(bank, buttonPins, ledPins);
*/

/*_______________________________________________________________________________________________________________________________________*/

void setup() {
  // Add the created objects to the bank
  bank.add(potentiometer1, Bank::CHANGE_CHANNEL); // When the bank setting is changed, change the channel of the potentiometer
  bank.add(potentiometer2, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer3, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer4, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer5, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer6, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer7, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer8, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer9, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer10, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer11, Bank::CHANGE_CHANNEL);
  bank.add(potentiometer12, Bank::CHANGE_CHANNEL);

  bank.add(muteButton_A, Bank::CHANGE_ADDRESS); // When the bank setting is changed, change the address (note number) of the mute button
  bank.add(muteButton_B, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_C, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_D, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_E, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_F, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_G, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_H, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_I, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_J, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_K, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_L, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_M, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_N, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_O, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_P, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_Q, Bank::CHANGE_ADDRESS);
  bank.add(muteButton_R, Bank::CHANGE_ADDRESS);
}

/*_______________________________________________________________________________________________________________________________________*/

void loop() {
  // Refresh the MIDI controller (check whether the inputs have changed since last time, if so, send the new value over MIDI)
  // It also refreshes the bank selector
  MIDI_Controller.refresh();
}
tttapa commented 4 years ago

I added an example to Control Surface. See https://tttapa.github.io/Control-Surface-doc/Doxygen/de/d26/MIDI_controller-97_8ino-example.html

I haven't tested it, since I don't have an Arduino Mega, but it compiles fine.

panakronic commented 4 years ago

Hi,

First, I want to start by thanking you for the tutorial on connecting 2 or more buttons to a single LED.

I have tried the script you have provided and for the past two days and I am still running one problem. Maybe I have missed something...

I was able to upload the script to Arduino Mega and it did compile successfully. However, after this process, my controller no longer respond, ie sending out midi messages, and the LEDs on the bank select no longer works either.

My first thought was that the new script maybe did not include the MIDI_Controller.h and therefore controller stopped working.

When I tried to add MIDI_Controller.h to the new script you have provided, conflicts happened.

For another try, I tried to add the new script and library Control_Surface.h to the old script I had, and same error happens. The error log is very long, but I can provide to you if it is helpful.

Again, thank you for taking your time to help me and I am grateful. I am wondering if you are able to continue helping me with this issue? I have literally searched as much online, reading through tons of relevant instructions and tutorials and find no straight forward solution. You are the closest person I know that can possibly to get the LED to work to the buttons.

Once again, thank you.

Respect,

Steve https://www.underdogmodshop.com/

On Tue, Dec 10, 2019 at 1:41 PM tttapa notifications@github.com wrote:

I added an example to Control Surface. See https://tttapa.github.io/Control-Surface-doc/Doxygen/de/d26/MIDI_controller-97_8ino-example.html

I haven't tested it, since I don't have an Arduino Mega, but it compiles fine.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/tttapa/MIDI_controller/issues/97?email_source=notifications&email_token=ANEHUW44AMLTKGPGHTXOY6DQYAEKFA5CNFSM4JFJCR3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGRBVQY#issuecomment-564271811, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANEHUW73BHD7KIAT7EC4PRTQYAEKFANCNFSM4JFJCR3A .

tttapa commented 4 years ago

It looks like I forgot to add a MIDI interface to the example I posted earlier, my apologies.

It should be fixed now.

panakronic commented 4 years ago

Hi,

I just ran this sketch and this is the error: Arduino: 1.8.9 (Windows Store 1.8.21.0) (Windows 10), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

C:\Users\panak\Documents\Arduino\libraries\Control-Surface-master\src\Control_Surface\Control_Surface_Class.cpp: In member function 'onRealtimeMessage':

C:\Users\panak\Documents\Arduino\libraries\Control-Surface-master\src\Control_Surface\Control_Surface_Class.cpp:148:1: internal compiler error: Segmentation fault

}

tttapa commented 4 years ago

Well, that's unfortunate, it's a bug in the compiler.
You could try installing the Arduino IDE from the Arduino website, not from the Windows store.

panakronic commented 4 years ago

This is what I though too. I will try reinstall Arduino IDE. I will for sure get back to you on the progress. Once again, thank you!!! :)

tttapa commented 4 years ago

It seems to work on Linux using the Arduino IDE 1.8.9 and AVR Core version 1.8.1.

panakronic commented 4 years ago

Hi tttapa,

It's working!!!

You were absolutely correct with the Arduino IDE error, all I did was update the Arduino IDE to the newest version and problem solved.

See it work here: https://youtu.be/o1qB8_DQy-U

I have made some modification on the potentiometer part from change_channel to change_address. Here is my final script:

/**
 * <https://github.com/tttapa/MIDI_controller/issues/97#issuecomment-564247602>
 * 
 * Many bankable potentiometers and buttons. Bank selector displays bank setting
 * using LEDs.  
 * If any of the buttons is pressed, the LED on pin 22 is turned on.
 * 
 * @boards  Mega
 */

#include <Control_Surface.h>

#include <AH/STL/algorithm> // std::any_of

USBMIDI_Interface midi; // MIDI Interface to use

Bank<4> bank(16);

// Create a new bank selector that changes the bank setting of the bank we just
// created.
// It has push buttons connected to pins 21 and 20 that increment or decrement
// the bank setting, and 4 LEDs to pins 46, 48, 50, 52 that display the current
// bank setting.
IncrementDecrementSelectorLEDs<4> bankSelector = {
  bank,
  {21, 20},         // button pins
  {46, 48, 50, 52}, // LED pins
};

using namespace MIDI_CC;
Bankable::CCPotentiometer potentiometers[] = {
  {bank,  A0, {0x46, CHANNEL_2}},
  {bank,  A1, {0x47, CHANNEL_2}},
  {bank,  A2, {0x48, CHANNEL_2}},
  {bank,  A3, {0x49, CHANNEL_2}},
  {bank,  A4, {0x4A, CHANNEL_2}},
  {bank,  A5, {0x4B, CHANNEL_2}},
  {bank,  A6, {0x4C, CHANNEL_2}},
  {bank,  A7, {0x4D, CHANNEL_2}},
  {bank,  A8, {0x4E, CHANNEL_2}},
  {bank,  A9, {0x4F, CHANNEL_2}},
  {bank,  A10, {0x50, CHANNEL_2}},
  {bank,  A11, {0x51, CHANNEL_2}},
};

Bankable::NoteButton muteButtons[] = {
  {bank, 13, 0x14},
  {bank, 12, 0x15},
  {bank, 11, 0x16},
  {bank, 10, 0x17},
  {bank, 9, 0x18},
  {bank, 8, 0x19},
  {bank, 7, 0x1A},
  {bank, 6, 0x1B},
  {bank, 5, 0x1C},
  {bank, 4, 0x1D},
  {bank, 3, 0x1E},
  {bank, 2, 0x1F},
  {bank, 14, 0x20},
  {bank, 15, 0x21},
  {bank, 16, 0x22},
  {bank, 17, 0x23},
  {bank, 18, {0x33, CHANNEL_2}},
  {bank, 19, {0x34, CHANNEL_2}},

  {bank, 49, {0x35, CHANNEL_2}},
  {bank, 51, {0x36, CHANNEL_2}},
  {bank, 53, {0x37, CHANNEL_2}},

};

constexpr pin_t ledPin = 22;

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //

void setup() {
  Control_Surface.begin();
  pinMode(ledPin, OUTPUT);
}

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //

void loop() {
  Control_Surface.loop();

  // Function that checks if a given button is pressed
  auto checkButtonPressed = [](const Bankable::NoteButton &button) {
    return button.getButtonState() == Button::Pressed;
  };
  // If any of the push buttons is pressed
  bool pressed = std::any_of(std::begin(muteButtons), std::end(muteButtons),
                             checkButtonPressed);
  // Turn on the LED
  digitalWrite(ledPin, pressed);
}
tttapa commented 4 years ago

Glad to hear!

panakronic commented 4 years ago

Hi

As I am looking at the examples from the Control_Surface on Github, I cannot find an example on how to implement a rotary encoder like the one on MIDI_controller. I am just wondering if you are able to point me a direction to tutorial as what I can do if I wish to add a rotary encoder?

Thank you so much

On Fri, Dec 13, 2019 at 1:39 PM tttapa notifications@github.com wrote:

Glad to hear!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/tttapa/MIDI_controller/issues/97?email_source=notifications&email_token=ANEHUWYSR63HDEDQZVBZO33QYP6KHA5CNFSM4JFJCR3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEG3KMRI#issuecomment-565618245, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANEHUW5FKVODERZIVAGZ6ELQYP6KHANCNFSM4JFJCR3A .

tttapa commented 4 years ago

You can use the CCRotaryEncoder class.

There's no example yet, but you can find the arguments on the documentation page.

#include <Encoder.h> // Include the Encoder library.
// This must be done before the Control Surface library.
#include <Control_Surface.h> // Include the Control Surface library

CCRotaryEncoder enc = {{2, 3}, {MCU::V_POT_1, CHANNEL_1}};
panakronic commented 3 years ago

Hi there,

I have a question regarding "Connecting 2 or more buttons to a single LED"

I am trying to use matrix buttons alongside regular buttons. Is there a way to make the matrix buttons to work with constexpr pin_t ledPin = 10; ?

This is what I have so far:


/**
 * @example Ex.11.Button-Matrix.ino
 *
 * This is an example of the ButtonMatrix class of the Control_Surface library.
 *
 * Connect a 4 × 3 matrix of buttons with the rows to pins 2, 3, 4 and 5,
 * and the columns to pins 6, 7 and 8.  
 * Pull-up resistors are not necessary, because the internal ones will be used.
 *
 * If you want to be able to press multiple buttons at once, add a diode
 * in series with each button, as shown in the following schematic:
 *
 * @image html Button-matrix.svg
 *
 * The note numbers are specified in the `addresses` array.  
 * Map accordingly in your DAW or DJ software.
 *
 * Written by tttapa, 24/09/2017  
 * https://github.com/tttapa/Control-Surface
*/

#include <Control_Surface.h>
 // Custom callback to handle output for a selector.
class MySelectorCallback {
  public:
    // Constructor
    MySelectorCallback(pin_t redLED, pin_t greenLED, pin_t blueLED)
      : redLED(redLED), greenLED(greenLED), blueLED(blueLED) {}

    // Begin function is called once by Control Surface.
    // Use it to initialize everything.
    void begin() {
      pinMode(redLED, OUTPUT);
      pinMode(greenLED, OUTPUT);
      pinMode(blueLED, OUTPUT);
      show(0);
    }

    // Update function is called continuously by Control Surface.
    // Use it to implement things like fading, blinking ...
    void update() {}

    // Update function with arguments is called when the setting
    // changes.
    // Use it to update the LEDs.
    void update(setting_t oldSetting, setting_t newSetting) {
      (void) oldSetting; // unused in this example
      show(newSetting);
    }

  private:
    // Show the color of the given setting.
    void show(setting_t setting) {
      uint8_t color = getColor(setting);
      digitalWrite(redLED, color & 0b001 ? HIGH : LOW);
      digitalWrite(greenLED, color & 0b010 ? HIGH : LOW);
      digitalWrite(blueLED, color & 0b100 ? HIGH : LOW);
    }

    // Convert the given setting to a 3-bit RGB color value.
    static uint8_t getColor(setting_t setting) {
      switch (setting) {
        case 0: return 0b011;
        case 1: return 0b010;
        case 2: return 0b101;
        case 3: return 0b110;
        default: return 0b001;
      }
    }

  private:
    // Member variables to remember the pin numbers of the LEDs.
    pin_t redLED, greenLED, blueLED;
};

#include <AH/STL/algorithm> // std::any_of

USBMIDI_Interface midi;
Bank<4> bank = {16}; // 4 banks, 6 addresse per banks

  GenericIncrementDecrementSelector<4, MySelectorCallback> selector = {
  bank,         // bank to manage
  {11, 12, 13}, // red, green, blue LED pins 
                // (this is the MySelectorCallback constructor defined above)
  {8, 9},       // incr/decr button pins
  Wrap::Wrap,   // wrap around when reaching setting 6
};

AddressMatrix<4, 4> notes = {{
  {1, 2, 3, 4},
  {5, 6, 7, 8},
  {9, 10, 11, 12},
  {13, 14, 15, 16}
}};

Bankable::NoteButtonMatrix<4, 4> buttons = {
  bank,  
  {14, 15, 16, 17},    // row pins (outputs, driven low-Z low !)
  {18, 19, 20, 21}, // column pins (inputs, hi-Z)
 notes,     // address matrix
  CHANNEL_1, //
};

using namespace MIDI_CC;
Bankable::CCPotentiometer potentiometers[] = {
{bank,  A0, {0x46, CHANNEL_2}},

}; 
Bankable::NoteButton muteButtons[] = {

  {bank, 23, 0x20},
  {bank, 25, 0x21},

};

constexpr pin_t ledPin = 10;

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //

void setup() {
  Control_Surface.begin();
  pinMode(ledPin, OUTPUT);
}

// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //

void loop() {
  Control_Surface.loop();

  // Function that checks if a given button is pressed
  auto checkButtonPressed = [](const Bankable::NoteButton &button) {
    return button.getButtonState() == Button::Pressed;
  };
  // If any of the push buttons is pressed
  bool pressed = std::any_of(std::begin(muteButtons), std::end(muteButtons),
                             checkButtonPressed);

  // Turn on the LED
  digitalWrite(ledPin, pressed);
}
tttapa commented 3 years ago

I am trying to use matrix buttons alongside regular buttons. Is there a way to make the matrix buttons to work with constexpr pin_t ledPin = 10; ?

I'm not sure what you mean by that. A button matrix reads buttons, what does it have to do with a ledPin constant?

If you want to use matrix buttons as "regular" buttons, you have two options: inherit from the ButtonMatrix class, or implement the ExtendedIOElement interface for a scanning matrix.