Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: Example code for MIDI controllers with Pots and Buttons

  1. #1
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093

    Example code for MIDI controllers with Pots and Buttons

    Here is my code for a simple MIDI controller with both potentiometers and buttons using ResponsiveAnalogRead and BOUNCE libraries to deal with signal stability.

    I hope it will be helpful for those looking to implement basic USB-MIDI controller projects and don't want to write their own code as yet.

    I'm looking for suggestions and a general vetting for stupidity before pointing noobs at it...

    Code:
    //************LIBRARIES USED**************
    // include the ResponsiveAnalogRead library for analog smoothing
    #include <ResponsiveAnalogRead.h>
    // include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle
    #include <Bounce.h> 
    //usbMIDI.h library is added automatically when code is compiled as a MIDI device
    
    // ******CONSTANT VALUES******** 
    // customize code behaviour here!
    const int channel = 1; // MIDI channel
    const int A_PINS = 6; // number of Analog PINS
    const int D_PINS = 3; // number of Digital PINS
    const int ON_VELOCITY = 99; // note-one velocity sent from buttons (should be 65 to  127)
    
    // define the pins you want to use and the CC ID numbers on which to send them..
    const int ANALOG_PINS[A_PINS] = {A0,A1,A2,A3,A4,A5};
    const int CCID[A_PINS] = {21,22,23,24,25,26};
    
    // define the pins and notes for digital events
    const int DIGITAL_PINS[D_PINS] = {0,1,2};
    const int note[D_PINS] = {60,61,62};
    const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
    const boolean toggled = true;
    
    
    //******VARIABLES***********
    // a data array and a lagged copy to tell when MIDI changes are required
    byte data[A_PINS];
    byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value
    
    
    //************INITIALIZE LIBRARY OBJECTS**************
    // not sure if there is a better way... some way run a setup loop on global array??
    // use comment tags to comment out unused portions of array definitions
    
    // initialize the ReponsiveAnalogRead objects
    ResponsiveAnalogRead analog[]{
      {ANALOG_PINS[0],true},
      {ANALOG_PINS[1],true},
      {ANALOG_PINS[2],true},
      {ANALOG_PINS[3],true},
      {ANALOG_PINS[4],true},
      {ANALOG_PINS[5],true}/*,
      {ANALOG_PINS[6],true},
      {ANALOG_PINS[7],true},*/
    }; 
    
    // initialize the bounce objects 
    Bounce digital[] =   {
      Bounce(DIGITAL_PINS[0],BOUNCE_TIME), 
      Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
      Bounce(DIGITAL_PINS[2], BOUNCE_TIME)/*,
      Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
      Bounce(DIGITAL_PINS[4], BOUNCE_TIME),
      Bounce(DIGITAL_PINS[5], BOUNCE_TIME),
      Bounce(DIGITAL_PINS[6], BOUNCE_TIME),
      Bounce(DIGITAL_PINS[7], BOUNCE_TIME),*/
    }; 
    
    //************SETUP**************
    void setup() {
    // loop to configure input pins and internal pullup resisters for digital section
      for (int i=0;i<D_PINS;i++){
        pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
      }
    }
    
    //************LOOP**************
    void loop() {
      getAnalogData();
      getDigitalData();
      while (usbMIDI.read()) {
         // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
      }
    }
    
    
    //************ANALOG SECTION**************
    void getAnalogData(){  
      for (int i=0;i<A_PINS;i++){
        // update the ResponsiveAnalogRead object every loop
        analog[i].update(); 
        // if the repsonsive value has change, print out 'changed'
        if(analog[i].hasChanged()) {
          data[i] = analog[i].getValue()>>3;
          if (data[i] != dataLag[i]){
            dataLag[i] = data[i];
            usbMIDI.sendControlChange(CCID[i], data[i], channel);
          }
        }
      }
    }
    
    
    
    //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
      digital[i].update();
        if (digital[i].fallingEdge()) {
          usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);  
        }
        // Note Off messages when each button is released
        if (digital[i].risingEdge()) {
          usbMIDI.sendNoteOff(note[i], 0, channel);  
        }
      }
    }
    Last edited by oddson; 07-15-2017 at 08:30 PM.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,075
    Would it be ok to include this in the examples that ship with Teensyduino?

    I'd like to give you credit in the comments. How best to do that? "oddson"?

    The other examples have "This example code is in the public domain". Is that ok? Or would you prefer it to have an open source license?

  3. #3
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    PD is fine... 'oddson' or "Leif Oddson" if you want to credit.

    Is there a better way to initialize an arbitrary number of BOUNCE or RAR objects that the comment trick in my code?

  4. #4
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Hello! Thanks for sharing this @oddson, it's the best example I have found for my needs.
    I have made a few mofications to suit my needs however I think the way I coded things is a bit inneficient and I was wondering if i could get some help.


    My objective is to have an LED per Button, every time a button is pressed its corresponding LED turns On and stays On until another button is pressed and the rest turns Off, basically a Ableton Live Scene trigger where LED's On is just a visual indicatior of what Scene is active.

    Adding the LED info
    Code:
    const int D_LEDS = 2; // How many Pins for LEDS 
    const int DIGITAL_LEDS[D_LEDS] = {11,12}; // Numbers of the LED Pins
    Digital Section:
    Code:
    //************DIGITAL SECTION**************
    //Modificaions : Changed the order of fallingEdge and risingEdge because note off was trigeering the note instead of note on.
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
      digital[i].update();
        if (digital[i].risingEdge()) {
          usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);
          digitalWrite(DIGITAL_LEDS[0], LOW); // TURNS OFF DIGITAL_LEDS 1 - PIN 11
          digitalWrite(DIGITAL_LEDS[1], LOW); // TURNS OFF DIGITAL_LEDS 2 - PIN 12
        }
        // Note Off messages when each button is released
        if (digital[i].fallingEdge()) {
          usbMIDI.sendNoteOff(note[i], 0, channel);
          digitalWrite(DIGITAL_LEDS[i], HIGH); // TURN ON THE PRESSED BUTTON EQUIVALENT LED WHEN BUTTON IS RELEASED.
          Serial.print(" Note OFF ");
        }
      }
    }
    It works fine but as i add more LED's using "DIGITAL_LEDS[number], LOW" a bunch of times seems inneficint, is there a better way to go about this?

  5. #5
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    If there is always one, and only one mode active you can store it as a byte/integer variable.

    (This may be easier than checking a pin output when you try use the mode info... what do you use it for?)

    Just have the falling edge turn off the old and turn on the new and set the mode to the new index. But for a small number of modes you could just set all to low before setting the new value.

    Re swapping rising/falling... are you using active low switching?

  6. #6
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Oh man, I am a big noob, I just started learning 2 days ago, tbh i dont know most of what you're asking.

    Quote Originally Posted by oddson View Post
    If there is always one, and only one mode active you can store it as a byte/integer variable.
    (This may be easier than checking a pin output when you try use the mode info... what do you use it for?)
    Yes, only one active at a time.
    I'll do some research on byte/integer variables to learn what it's about and how it works.
    I think i left it like that by mistake after testing some stuff and forgot about it.


    Quote Originally Posted by oddson View Post
    Just have the falling edge turn off the old and turn on the new and set the mode to the new index. But for a small number of modes you could just set all to low before setting the new value.
    So bacasically this:
    void getDigitalData(){
    for (int i=0;i<D_PINS;i++){
    digital[i].update();
    if (digital[i].risingEdge()) {
    usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);
    }
    // Note Off messages when each button is released
    if (digital[i].fallingEdge()) {
    usbMIDI.sendNoteOff(note[i], 0, channel);
    digitalWrite(11, LOW); // turn off list
    digitalWrite(12, LOW); // turn off list
    digitalWrite(DIGITAL_LEDS[i], HIGH);
    }
    }
    }
    ?
    I wont ever use many Leds so i guess that works.


    Quote Originally Posted by oddson View Post
    Re swapping rising/falling... are you using active low switching?
    I have no idea, if i am is that bad? How do i check if I am? Sorry for the dumb question.

  7. #7
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    See https://www.pjrc.com/teensy/td_digital.html for active low explanation.

  8. #8
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Quote Originally Posted by oddson View Post
    See https://www.pjrc.com/teensy/td_digital.html for active low explanation.
    Thanks Sir, i appreciate your time and pointing me in the right direction.
    Time for me to do some reading.

  9. #9
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    Set

    digitalWrite(DIGITAL_LEDS[i], HIGH );

    on falling edge.

    Set the previous high to low

    digitalWrite(DIGITAL_LEDS[lastHigh], LOW);

    Record the current pin as last high

    lastHigh = i ;

    So you can turn it off on the next.

    If I'm following you

  10. #10
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    Actually set new to HIGH after turning old LOW to avoid turning LED off if selected twice in sequence.

  11. #11
    Junior Member
    Join Date
    Apr 2019
    Posts
    2
    This is a my second venture into the world of Teensy midi controllers. First time I used a Teensy 3.1 to control and repurpose and old AVIOM mixer with 20 buttons and an added 16x2 LCD. I built that about two years ago and it runs solid all the time. This time I am building a Midi Foot Controller to control an Amp, Ableton Live, and a Helix. This code is working well for the main control, I am using 8 Potentiometers and 10 buttons. This leads me to my question; I would like to add banks to this code but am not quite sure how to go about implementing the banks in the code. I would like to have different values for midi notes or CC messages selected with each bank select. Right now I am planning on four banks with an indicator LEDs to show what bank I am on. Could some one point me in the right direction?

    This is what I want to do but not sure how or where I need to modify code....

    - One momentary switch (push button) and multiple LEDs

    Pressing the button increments the bankSetting number and turns on the respective LED,
    When starting the program, bankSetting 1 is selected and LED 1 is on,
    When the button is pressed, bankSetting 2 is selected, LED 1 turns off and LED 2 turns on,
    When the button is pressed again, bankSetting 3 is selected, LED 2 turns off and LED 3 turns on.
    When the last bankSetting is selected, and the button is pressed,
    bankSetting 1 is selected, the last LED turns off and LED 1 turns on.

    Thanks

  12. #12
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    CCID and note arrays can be extended to two dimensions with a dimension for the bank...

    https://forum.pjrc.com/threads/43263...ons-(usb-midi)

  13. #13
    Junior Member
    Join Date
    Apr 2019
    Posts
    2
    Thanks Oddson I will take a look and study the info in the link.

  14. #14
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,093
    I'm happy to help if you need it... subject to limited availability ATM.

    The idea is there is a matrix of note values and they are looked up by the current value of the indicator variable 'i' within the 'for' loop (as before) as well as the current mode or 'bank' value.

    Other than that you just need to initialize it with your desired values and then have a button or two for bank selection. If one you increment the bank and reset to zero when needed.

    If you have a lot of banks you'll want an indicator at some point... a seven or 12 segment LED works for a small number of banks... but then you'll want to be able to configure without a recompile... and to save values in eeprom.... and it never ends.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •