midi controller

Status
Not open for further replies.

edrummer

Well-known member
I want to build a midi controller with 24 10k pots and 24 digital buttons, I have tried several that didn't work, then I found these two examples. I have tested these codes and each work but they are for only 1 button and 1 pot. My question is, how can I expand each to 24 and then combine them into one sketch?


[CODE/*
This is an example of the "Analog" class of the MIDI_controller library.
Connect a potentiometer to analog pin A0. This will be the MIDI channel volume of channel 1.
Map it in your DAW or DJ software.

Written by Pieter P, 08-09-2017
https://github.com/tttapa/MIDI_controller
*/

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

// Create a new instance of the class 'Analog', called 'potentiometer', on pin A0,
// that sends MIDI messages with controller 7 (channel volume) on channel 1
Analog potentiometer(A0, MIDI_CC::Channel_Volume, 1);

void setup() {}

void loop() {
// Refresh the MIDI controller (check whether the potentiometer's input has changed since last time, if so, send the new value over MIDI)
MIDI_Controller.refresh();
}][/CODE]



Code:
/*
This is an example of the "Digital" class of the MIDI_controller library.
Connect a push buttons to digital pin 2. Connect the other pin of the button to the ground, 
a pull-up resistor is not necessary, because the internal one will be used. 
This button will play MIDI note C4 when pressed.
Map it in your DAW or DJ software.

Written by tttapa, 08/09/2017
https://github.com/tttapa/MIDI_controller
*/

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

const uint8_t velocity = 0b1111111; // Maximum velocity (0b1111111 = 0x7F = 127)
const uint8_t C4 = 60;              // Note number 60 is defined as middle C in the MIDI specification

// Create a new instance of the class 'Digital', called 'button', on pin 2, that sends MIDI messages with note 'C4' (60) on channel 1, with velocity 127
Digital button(2, C4, 1, velocity);

void setup() {}

void loop() {
  // Refresh the button (check whether the button's state has changed since last time, if so, send it over MIDI)
  MIDI_Controller.refresh();
}
 
The midi controller library is hardly used by anyone here and it's use is not actually recommended. See the Using USB MIDI page for info on how to connect many pots & buttons. Also have a look at the "Many_Knobs_Buttons" example provided with the Teensyduino installation (Menu: File > Examples > Teensy > USB_MIDI).
 
Why is it not recommended? I tried the"Many_Knobs_Buttons" sketch, I couldn't get it to work. The pots just flicker and it doesn't say what value the pull down resistors should be for the digital pins. The 2 examples above work, I'm hoping someone can show me how adjust the codes for more buttons and pots.
 
it doesn't say what value the pull down resistors should be for the digital pins
That's because it configures each digital pin with an internal pullup resistor:
Code:
    pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
which means you don't need to add external resistors.

Which Teensy are you using to handle the 24 pots and 24 buttons?

Pete
 
Why is it not recommended? ...

Author supports the product on GitHub. I wouldn't say it's not recommended but I'm not sure what it brings you can't do without it just as easily.

https://github.com/tttapa/MIDI_controller/issues/87
Note here he cites 'help from the forum' which was me guessing what was wrong with his code and how to fix it.

...I tried the"Many_Knobs_Buttons" sketch, I couldn't get it to work....
You could have asked for help.

... The pots just flicker ...
Usually you have a pin configured but not tied to a stable voltage (ground, hot or anywhere in between but it cannot be 'floating').

If you mean you flicker between adjacent values then you can change the sensitivity to eliminate this. [Edit - forgot this is ResponsiveAnalogRead and I've not built the sensativitiy in yet but it's very rare to have pots so noisy that the three-bit reduction and ResponsiveAnalogRead with default settings won't sort it out.]

A decent MIDI monitor's output could tell me what your problem is, if you want to try. But it's almost always a bad connection to a pin or the maker not being aware that all analog pins configured must be connected to a stable voltage.


:)
 
Last edited:
Ah, there you are! The drum pad works great! I mean when I use the many buttons and knobs sketch with Ableton and map the pot to one of the parameters like volume, it just goes crazy flickering sporadically and the digital switches do nothing. I may be doing something wrong but the 2 examples in#1 work
 
So with the many buttons and knobs sketch there are 6 analog and 3 digital pins that are hot, the rest are grayed out. Just to test, I connected 3 pots to A0,A1,A2 and put 10k resistors to gnd. on the other 3. I have 2 buttons connected to digital pins 0 and 1. still flickering sporadically.

i
 
Again, the 2 examples in post #1 work fine without any extra components except that the pots don't max out soon enough, like there's an extra 10% left to turn but I think that can be adjusted.
 
Have you modified the Many_Buttons_Knobs code to use your specific analog and digital pins. If so, which ones are you using? Post the code.

Pete
 
I have not touched the code, please don't stray from the original post. I've tried the Many_Buttons_Knobs sketch every way I can, It doesn't work, the two in the first post do. I just need to know how to adjust the codes to use multiple pins.
 
I've tried the Many_Buttons_Knobs sketch every way I can, It doesn't work
I haven't a clue what you have tried with that sketch but obviously everything you've done is wrong.
Post a version of the sketch, explain what it is doing wrong and perhaps we can figure it out so that you can have multiple buttons and pots.

Pete
 
which sketch? I haven't touched the many buttons one and the others are posted in#1
Code:
[/* Use arrays to manage lists of knobs/pots and pushbuttons.

   By Leif Oddson
   https://forum.pjrc.com/threads/45376

   This more complex example demonstrates how to use arrays to
   manage a larger number of inputs, without duplicating your
   code for every signal.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
*/


//************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);  
    }
  }
}/CODE]
 
Let's get the digital pins working first. Modify the code in the loop function and just comment out the analog part:
Code:
//  getAnalogData();
That will stop it flooding the receiving end with messages about the pots changing value.
Now run the code (compile it with USB type "Midi" or "Serial + MIDI") and then touch a wire from pin zero to ground. When I do that, Midi-OX on Windows sees a note on event and a note off event when I remove the wire. It is sending note 60 as defined in the code. Pins 1 and 2 also send their specified note event.

Pete
 
Sorry if I got a little impatient, I changed boards a new 3.6 and re booted everything. I was able to get 3 pots to work with the Many_Buttons_Knobs sketch but not the buttons. There's hope I'm still trying
 
I don't know if this is what you mean, if not please post a version of the complete code that I can copy.



Code:
/* Use arrays to manage lists of knobs/pots and pushbuttons.

   By Leif Oddson
   https://forum.pjrc.com/threads/45376

   This more complex example demonstrates how to use arrays to
   manage a larger number of inputs, without duplicating your
   code for every signal.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
*/


//************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);  
    }
  }
}
 
OK without changing the code the buttons turn the parameter off but not on, for example when I map a button to the record function it will turn it off but I have to turn it on with the mouse. Also there is some chatter with the buttons some other functions are flickering
 
No. All you do is comment the one statement in the loop statement that I showed.
Code:
//  getAnalogData();
Here's the whole thing:
Code:
/* Use arrays to manage lists of knobs/pots and pushbuttons.

By Leif Oddson
https://forum.pjrc.com/threads/45376

This more complex example demonstrates how to use arrays to
manage a larger number of inputs, without duplicating your
code for every signal.

You must select MIDI from the "Tools > USB Type" menu

This example code is in the public domain.
*/


//************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()
{
  Serial.begin(9600);
  while(!Serial);
  Serial.println("Start");
  
// 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);
    }
  }
}

Pete
 
I went back to the buttons sketch, I have 2 buttons connected to pins 0 and 1, only the one button works. I tested the buttons there fine any suggestions? Also this code can 't compile for teensy 3.6

Code:
/*
  This is the code I used for my specific MIDI controller.
  I used an Arduino Leonardo with 4 faders on A0-A3, 8 potentiometers on A4-A11, a rotary encoder on pin 0&1, 4 toggle switches on pins 2, 3, 5 and 7, a toggle switch (for bank select) on pin 11, and an LED on pin 13.

  When bank select is 0, faders 1-4 are channel volumes 1-4, potSide 1-4 are pan/balance of channel 1-4, switches 1-4 are mute channel 5-8.
  When bank select is 1, faders 1-4 are channel volumes 5-8, potSide 1-4 are pan/balance of channel 5-8, switches 1-4 are mute channel 1-4.
  PotTop 1-4 are not in the bank, because I use them as effect or EQ parameters, and they are channel independent.
  Enc1 is used for scrolling.

  Note that I used a custom control mapping in my DAW.

  If you are using a Teensy, make sure you have the USB type set to MIDI.
  If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.


  Written by tttapa, 07-09-2017
  https://github.com/tttapa/MIDI_controller
*/
#define USE_ROTARY_ENCODER
#include <MIDI_Controller.h>

const uint8_t velocity = 0b01111111; // The velocity of the buttons (0b01111111 = 127 = 100%)
const unsigned int latchTime = 100;  // How long a note will be held on, in DigitalLatch mode (in milliseconds).

const int speedMultiply = 1; // If the jog wheels or other encoders are too slow in your software, increase this value
                             // (it will be multiplied with the actual speed of the encoder, as the name implies.) Default is 1.

//_____________________________________________________________________________________________________________________________________________________________________________________________

Analog faders[] = {
    {A0, MIDI_CC::Channel_Volume, 1}, // Create a new instance of class 'Analog' on pin A0, controller number 0x07 (channel volume), on MIDI channel 1.
    {A1, MIDI_CC::Channel_Volume, 2},
    {A2, MIDI_CC::Channel_Volume, 3},
    {A3, MIDI_CC::Channel_Volume, 4},
};

Analog knobsTop[] = {
    {A4, 0x10, 1}, // Create a new instance of class 'Analog' on pin A4, controller number 0x10 (General Purpose Controller 1), on MIDI channel 1.
    {A5, 0x11, 1},
    {A6, 0x12, 1},
    {A7, 0x13, 1},
};

Analog knobsSide[] = {
    {A8,  MIDI_CC::Pan, 1}, // Create a new instance of class 'Analog' called 'potSide1', on pin A8, controller number 0x0A (pan), on MIDI channel 1.
    {A9,  MIDI_CC::Pan, 2},
    {A10, MIDI_CC::Pan, 3},
    {A11, MIDI_CC::Pan, 4},
};

DigitalLatch switches[] = {
    {2, 0x10, 1, velocity, latchTime}, // Create a new instance of class 'DigitalLatch' on pin 0, note number 16 (mute) on MIDI channel 1, with a predefined latch time
    {3, 0x11, 1, velocity, latchTime},
    {5, 0x12, 1, velocity, latchTime},
    {7, 0x13, 1, velocity, latchTime},
};

RotaryEncoder enc = {1, 0, 0x2F, 1, speedMultiply, NORMAL_ENCODER, TWOS_COMPLEMENT}; // Create a new instance of class 'RotaryEncoder' called enc, on pins 1 and 0, controller number 0x2F, on MIDI channel 1, at normal speed, using a normal encoder (4 pulses per click/step), using the TWOS_COMPLEMENT sign option

Bank bank(4); // A bank with four channels

BankSelector bankselector(bank, 11, LED_BUILTIN, BankSelector::TOGGLE); // A bank selector with a single toggle switch on pin 11 and an LED for feedback on pin 13

//_____________________________________________________________________________________________________________________________________________________________________________________________

void setup()
{
    bank.add(faders, Bank::CHANGE_CHANNEL); // Add the control elements to the bank
    bank.add(knobsSide, Bank::CHANGE_CHANNEL);
    bank.add(switches, Bank::CHANGE_ADDRESS);
}

//_____________________________________________________________________________________________________________________________________________________________________________________________

void loop() // Refresh all inputs
{
    MIDI_Controller.refresh();
}
 
I still only get 1 to work. I'm mapping these to functions like record, is this because the cc value is wrong? Wrong note?
 
Status
Not open for further replies.
Back
Top