Change MIDI channel using buttons

Hey
I have a setup that uses 7 different potentiometers that send MIDI CC data as shown

//DEFINE DIRECTLY CONNECTED POTENTIOMETERS*********************
//Pot (Pin Number, Command, CC Control, Channel Number)
//Command parameter is for future use

Pot PO1(A1, 0, 1, 1); // PULSEWIDTH
Pot PO2(A2, 0, 2, 1); // ENVELOPE
Pot PO3(A3, 0, 3, 1); // PITCHSWEEP
Pot PO4(A4, 0, 4, 1); // PITCHBEND RANGE
Pot PO5(A5, 0, 5, 1); //LOAD PRESET
Pot PO6(A6, 0, 10, 1); //PAN
Pot PO7(A7, 0, 64, 1); //SUSTAIN

now i want to add 5 buttons to the digital pins on the arduino that change the MIDI channel on each of the pots from 1-5 as the pots use the same CC values and functions but across 5 channels

Im using this example for the buttons
//DEFINE DIRECTLY CONNECTED BUTTONS****************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
//** Command parameter 0=NOTE 1=CC 2=Toggle CC **

Button BU1(17, 1, 70, 1, 5 );
Button BU2(16, 1, 70, 2, 5 );
Button BU3(15, 1, 70, 3, 5 );
Button BU4(20, 1, 71, 1, 5 );
Button BU5(19, 1, 71, 2, 5 );

but i dont want to send CC, note or toggle, I just want it to change the channel of the pots MIDI channel?
How would i start to do that?

1 Like

Can you post your entire sketch in code tags? Somewhere in the code you must be
specifying the channel, and that needs to be made variable. The buttons controlling
this are not MIDI buttons, they are just configuration for the MIDI Pots.

#include <MIDI.h>
#include "Controller.h"
#define NUMBER_OF_MODES 5    //Right now there are 5 modes, Might be more in the future

/*************************************************************
  MIDI CONTROLLER

  THIS IS THE ONE!!!! 

  by Notes and Volts
  www.notesandvolts.com

  Version 1.2 **Arduino UNO ONLY!**
 *************************************************************/

MIDI_CREATE_DEFAULT_INSTANCE();
 
//************************************************************
//***SET THE NUMBER OF CONTROLS USED**************************
//************************************************************
//---How many buttons are connected directly to pins?---------
byte NUMBER_BUTTONS = 5;
//---How many potentiometers are connected directly to pins?--
byte NUMBER_POTS = 7;
//---How many buttons are connected to a multiplexer?---------
byte NUMBER_MUX_BUTTONS = 0;
//---How many potentiometers are connected to a multiplexer?--
byte NUMBER_MUX_POTS = 0;
//************************************************************

//***ANY MULTIPLEXERS? (74HC4067)************************************
//MUX address pins must be connected to Arduino UNO pins 2,3,4,5
//A0 = PIN2, A1 = PIN3, A2 = PIN4, A3 = PIN5
//*******************************************************************
//Mux NAME (OUTPUT PIN, , How Many Mux Pins?(8 or 16) , Is It Analog?);


//Mux M1(10, 16, false); //Digital multiplexer on Arduino pin 10
//Mux M2(A5, 8, true); //Analog multiplexer on Arduino analog pin A0
//*******************************************************************


//***DEFINE DIRECTLY CONNECTED POTENTIOMETERS************************
//Pot (Pin Number, Command, CC Control, Channel Number)
//**Command parameter is for future use**

Pot PO1(A1, 0, 1, 1); // PULSEWIDTH
Pot PO2(A2, 0, 2, 1); // ENVELOPE
Pot PO3(A3, 0, 3, 1); // PITCHSWEEP
Pot PO4(A4, 0, 4, 1); // PITCHBEND RANGE
Pot PO5(A5, 0, 5, 1); //LOAD PRESET
Pot PO6(A6, 0, 10, 1);  //PAN
Pot PO7(A7, 0, 64, 1);  //SUSTAIN


//*******************************************************************
//Add pots used to array below like this->  Pot *POTS[] {&PO1, &PO2, &PO3, &PO4, &PO5, &PO6};
Pot *POTS[] {&PO1,&PO2,&PO3,&PO4,&PO5,&PO6,&PO7};
//*******************************************************************


//***DEFINE DIRECTLY CONNECTED BUTTONS*******************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
//** Command parameter 0=NOTE  1=CC  2=Toggle CC **

Button BU1(17, 1, 70, 1, 5 );
Button BU2(16, 1, 70, 2, 5 );
Button BU3(15, 1, 70, 3, 5 );
Button BU4(20, 1, 71, 1, 5 );
Button BU5(19, 1, 71, 2, 5 );
Button BU6(18, 1, 71, 3, 5 );
Button BU7(21, 1, 85, 1, 5 );
Button BU8(21, 1, 85, 2, 5 );
Button BU9(21, 1, 85, 3, 5 );
Button BU10(21, 1, 85, 4, 5 );


//Button BU2(3, 0, 61, 1, 5 );
//Button BU3(4, 0, 62, 1, 5 );
//Button BU4(5, 0, 63, 1, 5 );
//Button BU5(6, 0, 64, 1, 5 );
//Button BU6(7, 0, 65, 1, 5 );
//Button BU7(8, 1, 64, 1, 5 );
//Button BU8(9, 2, 64, 1, 5 );
//*******************************************************************
//Add buttons used to array below like this->  Button *BUTTONS[] {&BU1, &BU2, &BU3, &BU4, &BU5, &BU6, &BU7, &BU8};
Button *BUTTONS[] {&BU1,&BU2, &BU3, &BU4, &BU5, &BU6, &BU7,&BU8,&BU9,&BU10};
//*******************************************************************


//***DEFINE BUTTONS CONNECTED TO MULTIPLEXER*************************
//Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
//** Command parameter 0=NOTE  1=CC  2=Toggle CC **

//Button MBU1(M1, 0, 0, 70, 1, 5);
//Button MBU2(M1, 1, 1, 71, 1, 5);
//Button MBU3(M1, 2, 2, 72, 1, 5);
//Button MBU4(M1, 3, 0, 73, 1, 5);
//Button MBU5(M1, 4, 0, 74, 1, 5);
//Button MBU6(M1, 5, 0, 75, 1, 5);
//Button MBU7(M1, 6, 0, 76, 1, 5);
//Button MBU8(M1, 7, 0, 77, 1, 5);
//Button MBU9(M1, 8, 0, 78, 1, 5);
//Button MBU10(M1, 9, 0, 79, 1, 5);
//Button MBU11(M1, 10, 0, 80, 1, 5);
//Button MBU12(M1, 11, 0, 81, 1, 5);
//Button MBU13(M1, 12, 0, 82, 1, 5);
//Button MBU14(M1, 13, 0, 83, 1, 5);
//Button MBU15(M1, 14, 0, 84, 1, 5);
//Button MBU16(M1, 15, 0, 85, 1, 5);
//*******************************************************************
////Add multiplexed buttons used to array below like this->  Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6.....};
Button *MUXBUTTONS[] {};

//*******************************************************************


//***DEFINE POTENTIOMETERS CONNECTED TO MULTIPLEXER*******************
//Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
//**Command parameter is for future use**

//Pot MPO1(M2, 0, 0, 1, 1);
//Pot MPO2(M2, 1, 0, 7, 1);
//Pot MPO3(M2, 2, 0, 50, 1);
//Pot MPO4(M2, 3, 0, 55, 2);
//Pot MPO5(M2, 4, 0, 50, 1);
//Pot MPO6(M2, 5, 0, 55, 2);
//Pot MPO7(M2, 6, 0, 50, 1);
//Pot MPO8(M2, 7, 0, 55, 2);
//Pot MPO9(M2, 8, 0, 50, 1);
//Pot MPO10(M2, 9, 0, 55, 2);
//Pot MPO11(M2, 10, 0, 50, 1);
//Pot MPO12(M2, 11, 0, 55, 2);
//Pot MPO13(M2, 12, 0, 50, 1);
//Pot MPO14(M2, 13, 0, 55, 2);
//Pot MPO15(M2, 14, 0, 50, 1);
//Pot MPO16(M2, 15, 0, 55, 2);
//*******************************************************************
//Add multiplexed pots used to array below like this->  Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6.....};
Pot *MUXPOTS[] {};
//*******************************************************************
 
 

int midiByte = 0 ;
void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI);
  Serial.begin(31250) ;



}


void loop() {

  //  if (MIDI.read())
  {
//    MIDI.send(MIDI.getType(),
///              MIDI.getData1(),
//              MIDI.getData2(),
//              MIDI.getChannel());
  }
if (Serial.available() > 0) {
    midiByte = Serial.read() ;
    Serial.write(midiByte) ;
  }


  if (NUMBER_BUTTONS != 0) updateButtons();
  if (NUMBER_POTS != 0) updatePots();
  if (NUMBER_MUX_BUTTONS != 0) updateMuxButtons();
  if (NUMBER_MUX_POTS != 0) updateMuxPots();

}


//*****************************************************************
void updateButtons() {

  // Cycle through Button array
  for (int i = 0; i < NUMBER_BUTTONS; i = i + 1) {
    byte message = BUTTONS[i]->getValue();

    //  Button is pressed
    if (message == 0) {
      switch (BUTTONS[i]->Bcommand) {
        case 0: //Note
          MIDI.sendNoteOn(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
          break;
        case 1: //CC
          MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
          break;
        case 2: //Toggle
          if (BUTTONS[i]->Btoggle == 0) {
            MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
            BUTTONS[i]->Btoggle = 1;
          }
          else if (BUTTONS[i]->Btoggle == 1) {
            MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
            BUTTONS[i]->Btoggle = 0;
          }
          break;
      }
    }

    //  Button is not pressed
    if (message == 1) {
      switch (BUTTONS[i]->Bcommand) {
        case 0:
          MIDI.sendNoteOff(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
          break;
        case 1:
          MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
          break;
      }
    }
  }
}
//*******************************************************************
void updateMuxButtons() {

  // Cycle through Mux Button array
  for (int i = 0; i < NUMBER_MUX_BUTTONS; i = i + 1) {

    MUXBUTTONS[i]->muxUpdate();
    byte message = MUXBUTTONS[i]->getValue();

    //  Button is pressed
    if (message == 0) {
      switch (MUXBUTTONS[i]->Bcommand) {
        case 0: //Note
          MIDI.sendNoteOn(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
          break;
        case 1: //CC
          MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
          break;
        case 2: //Toggle
          if (MUXBUTTONS[i]->Btoggle == 0) {
            MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
            MUXBUTTONS[i]->Btoggle = 1;
          }
          else if (MUXBUTTONS[i]->Btoggle == 1) {
            MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 127, MUXBUTTONS[i]->Bchannel);
            MUXBUTTONS[i]->Btoggle = 0;
          }
          break;
      }
    }
    //  Button is not pressed
    if (message == 1) {
      switch (MUXBUTTONS[i]->Bcommand) {
        case 0:
          MIDI.sendNoteOff(MUXBUTTONS[i]->Bvalue, 0, MUXBUTTONS[i]->Bchannel);
          break;
        case 1:
          MIDI.sendControlChange(MUXBUTTONS[i]->Bvalue, 0, MUXBUTTONS[i]->Bchannel);
          break;
      }
    }
  }
}
//***********************************************************************
void updatePots() {
  for (int i = 0; i < NUMBER_POTS; i = i + 1) {
    byte potmessage = POTS[i]->getValue();
    if (potmessage != 255) MIDI.sendControlChange(POTS[i]->Pcontrol, potmessage, POTS[i]->Pchannel);
  }
}
//***********************************************************************
void updateMuxPots() {
  for (int i = 0; i < NUMBER_MUX_POTS; i = i + 1) {
    MUXPOTS[i]->muxUpdate();
    byte potmessage = MUXPOTS[i]->getValue();
    if (potmessage != 255) MIDI.sendControlChange(MUXPOTS[i]->Pcontrol, potmessage, MUXPOTS[i]->Pchannel);
  }
}

ignore the buttons section, not using them at the moment, just didnt // them out

So you need to update the POTS[i]->Pchannel values when the channel buttons are operated - and you'll need code to read those buttons regularly enough not to miss a press.

thanks man!!!

can I use this example from
https://tttapa.github.io/Control-Surface-doc/Doxygen/d0/d74/Bank_8ino-example.html

edited to have 5 banks and 1 track per bank?
so each bank represents a MIDI channel change for each of the 7 pots?


#include <Control_Surface.h>

//FIVE BANKS WITH ONE TRACK PER BANK
Bank<5> bank(1);
//

IncrementDecrementSelector<5> selector = {
  bank, //Bank to manage
  {2, 3}, //push button pins (increment,decrement)
  Wrap::Wrap, //wrap around

//Pots 
};
Bankable::CCPotentiometer Potentiometer1 = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A1, //analog pin
  {MIDI_CC:1, CHANNEL_1},//address

};
Bankable::CCPotentiometer Potentiometer2  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A2, //analog pin
  {MIDI_CC:2, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer3  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A3, //analog pin
  {MIDI_CC:3, CHANNEL_1},//address
};

Bankable::CCPotentiometer Potentiometer4  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A4, //analog pin
  {MIDI_CC:4, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer5  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A5, //analog pin
  {MIDI_CC:5, CHANNEL_1},//address
};

Bankable::CCPotentiometer Potentiometer6  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A6, //analog pin
  {MIDI_CC:10, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer7  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A7, //analog pin
  {MIDI_CC:64, CHANNEL_1},//address
};

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

MIDI_CC:1 is invalid syntax. If you want MIDI continuous controller #1, just use 1, not MIDI_CC:1.

Please use code tags/fences when posting code. Select your code and press the </> button in the editor, or surround your code with triple back ticks:

```
// your code here
```

Pieter

I want to send MIDI cc message 1
The original example has {MIDICC::Channel_Volume , CHANNEL_1}
but I didnt want.to send channel volume I want to send CC1?

Please see the Getting Started guide and the MIDI Addresses documentation.

This any better, thanks for the help!

#include <Control_Surface.h>
#include <MIDI.h>
#include <MIDI_Interfaces/SerialMIDI_Interface.hpp>
//FIVE BANKS WITH ONE TRACK PER BANK
Bank<5> bank(1);
//

IncrementDecrementSelector<5> selector = {
  bank, //Bank to manage
  {2, 3}, //push button pins (increment,decrement)
  Wrap::Wrap, //wrap around

//Pots 
};
Bankable::CCPotentiometer Potentiometer1 = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A1, //analog pin
  {1, CHANNEL_1},//address

};
Bankable::CCPotentiometer Potentiometer2  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A2, //analog pin
  {2, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer3  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A3, //analog pin
  {3, CHANNEL_1},//address
};

Bankable::CCPotentiometer Potentiometer4  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A4, //analog pin
  {4, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer5  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A5, //analog pin
  {5, CHANNEL_1},//address
};

Bankable::CCPotentiometer Potentiometer6  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A6, //analog pin
  {10, CHANNEL_1},//address
};
Bankable::CCPotentiometer Potentiometer7  = {
  {bank,BankType::CHANGE_CHANNEL}, //bank configuration
  A6, //analog pin
  {64, CHANNEL_1},//address
};
int midiByte = 0 ;
void setup() {
  Control_Surface.begin(); // Initialize Control Surface
  Serial.begin(31250) ;

  }
 
void loop() {
  Control_Surface.loop(); // Update the Control Surface
    //  if (MIDI.read())
//    MIDI.send(MIDI.getType(),
///              MIDI.getData1(),
//              MIDI.getData2(),
//              MIDI.getChannel());
if (Serial.available() > 0) {
    midiByte = Serial.read() ;
    Serial.write(midiByte) ;
}
}

You forgot to define a MIDI interface for Control Surface to use.
I wouldn't recommend using the MIDI.h library and Control Surface on the same serial port. You shouldn't need MIDI.h at all in 99% of cases.

If you want to loop back the MIDI data you receive, you can use MIDI pipes:

#include <Control_Surface.h>

HardwareSerialMIDI_Interface midi = Serial;
MIDI_PipeFactory<3> pipes;

/* Place your banks, selectors and potentiometers here */

void setup() {
  Control_Surface >> pipes >> midi; // Connect Control Surface output to the MIDI interface
  Control_Surface << pipes << midi; // Connect Control Surface input to MIDI interface
  midi >> pipes >> midi;            // Route input from the MIDI interface back to its output
  Control_Surface.begin();
}
void loop() {
  Control_Surface.loop();
}
1 Like

Thanks very much!! Very useful!
I'll test this out in the morning on the bread board :wink:
It is just 5 pin din that I need to use at the moment as I dont want to have to hook it up via USB to use it when finished
Purely for hardware :wink:

This officially works beautifully!!! Now I need to work out how to light an LED for the channel I'm.on , so midi channel 1-5 with an led for each and I'm golden!!
Officially hooked on making code from scratch :wink: and not just been lazy!!
Looking at this section of the MIDI control stuff
https://tttapa.github.io/Control-Surface-doc/Doxygen/d9/dee/classProgramChangeSelectorLEDs.html

That is simple, I assume you have some buttons that change the channel. When these are pressed you change a variable that defines the channel. When you do that you just call a function that you write and pass over the channel number. That function turns off all the LEDs and turns on the one corresponding to your selected channel.

You probably want to use the IncrementDecrementSelectorLEDs class. It has the same signature as the IncrementDecrementSelector you have in your code right now, apart from the extra ledPins argument that allows you to specify a list of 5 pins for your LEDs. Connect the LEDs between these pins and ground, don't forget the current limiting series resistors.

For example:

IncrementDecrementSelector<5> selector = {
  bank,            // Bank to manage
  {2, 3},          // Push button pins (increment,decrement)
  {4, 5, 6, 7, 8}, // LED pins, one for each channel/bank
  Wrap::Wrap,      // Wrap around at the edges of the range
};

Legendary status achieved!! Thanks loads!!

struggling to understand the selectorLEDs
ive got

IncrementDecrementSelector<5> selector = {
SelectorLEDsCallback<5>>{
  selectable,
  bank, //Bank to manage
  {4, 5, 6, 7, 8}, //LED pins, one for each channel/bank
  {2, 3}, //push button pins (increment,decrement)
  Wrap::Wrap, //wrap around
};

if I use the code

IncrementDecrementSelector<5> selector = {
  bank,            // Bank to manage
  {2, 3},          // Push button pins (increment,decrement)
  {4, 5, 6, 7, 8}, // LED pins, one for each channel/bank
  Wrap::Wrap,      // Wrap around at the edges of the range
};

I am unable to compile
sorry

Ah, my bad, that was a copy-paste error, it should be IncrementDecrementSelectorLEDs, of course, not IncrementDecrementSelector.

Slick!! Works like a charm
I've added a bankable pitchbend and cc buttons also!

Are you the guy that's wrote the library and examples by any chance Pieter P?