DIY Midi-Controller, need help with 4021 and 4051

Hi!

I've got an Arduino Duemilanove since Monday last week. My aim was to build an Midi-Controller with the Arduino, it should look like the one on the drawing below.
The problem is, i haven't worked with the 4021 and 4051 yet, i hope someone here has done that already. A little help was to read "little-scales" blog little-scale: MIDI With Two 4021s and Arduino because he has done some similar midi stuff with the arduino right now. I've attached the schematic, its based on the schematics i've found on his blog. But i don't know if the ic's are connected correctly.
Sorry for my bad english, i hope you could understand what im writing here :wink:

Notes:
Red - 58mm Fader (4,7k linear) and 1k Potentiometers (linear)
Green - Joysticks with integrated button (original in Xbox360 Controllers) http://www.conrad.de/goto.php?artikel=425664
Yellow - simple Buttons

  • you look right, there are 2 Crossfaders ::slight_smile:


High Res: http://www.klingt.net/arduino/diy-midi-controller.jpg


High Res: http://www.klingt.net/arduino/schematic_arduino-midi-shield.png

Looks OK, however, on the switch inputs I would use 10K pull up resistors and switch down to ground, there is better noise immunity that way.

With Pullup-resistor it should look like this? But when i use pullups then the inputs will read an high signal when the switch isn't pressed, so i have to use them inversed, am i right?

Yes, that's correct!

so i have to use them inversed,

Why not do at the start:-
#define PRESSED LOW
#define RELEASED HIGH

Then your code will look better and you won't have to think about inverting the logic.

If you don't like that then just invert the signal as you clock it in from the shift register.


High-Res Version: http://www.klingt.net/arduino/schematic_arduino-midi-shield_new.png

I've updated the schematic, all buttons are now using pull-up resistors. Today i would like to test a small setup with one 4021. It works with the Code from the ShiftIn Tutorial http://arduino.cc/en/Tutorial/ShftIn11 but when i press the button number 7 (connected to D7 of 4021 shift-register) one bit is lost (or shifted away, dont know).
When i use pull-up resitors, how could i invert the input signal?
.

Here is the code from the Tutorial (it's almost the same, but i used different input and output pins).

//**************************************************************//
//  Name    : shiftIn Example 1.1                              //
//  Author  : Carlyn Maw                                        //
//  Date    : 25 Jan, 2007                                      //
//  Version : 1.0                                               //
//  Notes   : Code for using a CD4021B Shift Register          //
//          :                                                   //
//****************************************************************

//define where your pins are
int latchPin = 5;
int dataPin = 6;
int clockPin = 7;

//Define variables to hold the data 
//for shift register.
//starting with a non-zero numbers can help
//troubleshoot
byte switchVar1 = 72;  //01001000

void setup() {
  //start serial
  Serial.begin(9600);

  //define pin modes
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT); 
  pinMode(dataPin, INPUT);

}

void loop() {

  //Pulse the latch pin:
  //set it to 1 to collect parallel data
  digitalWrite(latchPin,1);
  //set it to 1 to collect parallel data, wait
  delayMicroseconds(20);
  //set it to 0 to transmit data serially  
  digitalWrite(latchPin,0);

  //while the shift register is in serial mode
  //collect each shift register into a byte
  //the register attached to the chip comes in first 
  switchVar1 = shiftIn(dataPin, clockPin);

  //Print out the results.
  //leading 0's at the top of the byte 
  //(7, 6, 5, etc) will be dropped before 
  //the first pin that has a high input
  //reading  
  Serial.println(switchVar1, BIN);

//white space
Serial.println("-------------------");
//delay so all these print satements can keep up. 
delay(500);

}

//------------------------------------------------end main loop

////// ----------------------------------------shiftIn function
///// just needs the location of the data pin and the clock pin
///// it returns a byte with each bit in the byte corresponding
///// to a pin on the shift register. leftBit 7 = Pin 7 / Bit 0= Pin 0

byte shiftIn(int myDataPin, int myClockPin) { 
  int i;
  int temp = 0;
  int pinState;
  byte myDataIn = 0;

  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, INPUT);
//we will be holding the clock pin high 8 times (0,..,7) at the
//end of each time through the for loop

//at the begining of each loop when we set the clock low, it will
//be doing the necessary low to high drop to cause the shift
//register's DataPin to change state based on the value
//of the next bit in its serial information flow.
//The register transmits the information about the pins from pin 7 to pin 0
//so that is why our function counts down
  for (i=7; i>=0; i--)
  {
    digitalWrite(myClockPin, 0);
    delayMicroseconds(0.2);
    temp = digitalRead(myDataPin);
    if (temp) {
      pinState = 1;
      //set the bit to 0 no matter what
      myDataIn = myDataIn | (1 << i);
    }
    else {
      //turn it off -- only necessary for debuging
     //print statement since myDataIn starts as 0
      pinState = 0;
    }

    //Debuging print statements
    //Serial.print(pinState);
    //Serial.print("     ");
    //Serial.println (dataIn, BIN);

    digitalWrite(myClockPin, 1);

  }
  //debuging print statements whitespace
  //Serial.println();
  //Serial.println(myDataIn, BIN);
  return myDataIn;
}

Very interesting project Klingt.
Keep us updated as you go on!
What about latency? Isn't there a problem with latency with so many buttons and pots?
ciao
g

All my orders have arrived, i must layout and manufactre the pcb's at work. This week im on a seminar, but till end of the next week the electronic part should be finished.

@ g.andreini:

I hope there are'nt any latency issues. I don't have a breadboard, so i could'nt make an test setup with all the buttons and pots.

greets "klingt.net"

I've bought all the stuff for the project but in the last weeks i was stressed because of the final examination of my apprenticeship. The next problem is, that i cant etch circuit boards at work in the next time. Sorry, but i will finish the project as soon as i can.

Hello, could you already finish the Arduino Midi Controller? I'm very interested in the code (buttons to 4021 to midiout?) – you made any experiences?

Best!
Tobias

Hi!

I'm back again. The project has slept to long, now it must be finished :wink:
Based on that (little-scale: Arduino MIDI out example) code the arduino sends every loop a midi cc message. My Idea is to store the analog values of every loop iteration in an array and compare them with their last state, if there is a difference the arduino should send a message. That's the behaviour of the most (or all) midi controllers. But how could i do that? If someone knows, let me know, i will try it myself in the mean time.

/*
PROJEKT: MIDI-Controller
Juni 2010
code is based on "16 pots to MIDI CC by Sebastian Tomczak"
*/

// command bytes
byte status_byte = 176; /*   cc on channel 1         >>> http://www.planetoftunes.com/sequence/statdata.html
                             176 = 1011 0000         >>> http://www.midimountain.com/midi/midi_status.htm
                                   cc   ch 1         >>> http://www.midi.org/techspecs/midimessages.php                                                        */
byte cc_number = 0;     // value for control number

// init transfer and operation values
byte data_send = 0;

// init looping and working values
int workport = 0;               //  working value for outgoing port
int workan   = 0;               //  working value for analog input
byte analogtemp = 0;             //  temporary, for comparing
byte analoginput[3] = {0,0,0};   //  will be needed to store and compare analog values

// setup
void setup() {
  Serial.begin(31250);  // midi baud rate
  pinMode(2, OUTPUT);   // pins 0 & 1 are reserved for RX and TX
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
}

// main program begins here
void loop() {
  for (int i = 0; i <= 2; i++)                     // loop the following code X times and increase i about 1 every loop
    {
    cc_number = 20+i;
    workport = i*4;                                // ignore bits 1 and 2 of output Pins f.e. 3*4=12 = 11100
    PORTD = workport;                              // see >>> http://www.arduino.cc/en/Reference/PortManipulation
    analogtemp = analogRead(workan)/8;             // read analog pin into variable with 10 bit -> 7 bit | 0-1023 (11 1111 1111) / 8 >>> 128 (1000 0000)
    if (analogtemp != analoginput[i])
       {
         analoginput[i] = analogtemp; 
         Serial.print(status_byte);                // send status byte
         Serial.print(cc_number);                  // send CC number
         Serial.print(analoginput[i]);             // send data value    
       }
    // loop back...
    }
    // loop back...
}

Works very well. Now its time to do some work with shift registers.

Hello All, hello klingt.net,

This midi Project looks pretty useful. There is not much activity curiously.

I wished to know does someone has some experience to share, especially working with chained 4021s. Btw, there is a small mistake in the schematics of the midi controller, regarding the pins 3/11-Inputs and outputs.

Hope that this project is not dead.
Greetings

Hello Electron Libre,

i'm sorry but i have to say that this project is dead. Actually i'm studying computer engineering, because of that i've no time to work on the project.

I wish you a happy X-mas.

Maybe a dumb question...

Is it necessary to have pull-up resistors on the multiplexer inputs (instead of leaving them floating), if the input pin on the Arduino has it's built-in pull-up resistor set?

Hi lqbert,
Yes, it is necessary to put Pull-up or pull-down resistors. If they stay floating, they acts like Antennas, picking all around. On my setup I'm using pull-downs only. Once I got a SMD resistor disordered by mistake, and the switch attached on it started to send HIGH when I approached my hand. This also leads to HF oscillations.

The values of the Res is not critical at all; everything from 5k to 22k fits. It is a common practice to use inexpensive SMD Res Briges :slight_smile: (if you use Surface parts, of course)

And his is my code, working and, let's say completed version. Explications in the next post ...

/*  DEXTROLER Midi Truc, version 01, 28.12.2010, Rennes-France par xDEx - Victor Proda /// xdex@free.fr
    Merci a Makari - Gael Signol pour son Grooooos coup de main!
    License GNU
    
REFS (par ordre de parution):
1.  http://little-scale.blogspot.com/2007/11/pots-and-switches-to-midi-data.html
    Digital and Analog Data to MIDI Output by Sebastian Tomczak

2.  midi_cc_test.pde by Benjamin Eckel
    http://blog.datasingularity.com/?p=34
    
3.  http://www.arduino.cc/en/Tutorial/ShiftIn
    Parallel to Serial Shifting-In with a CD4021BE
    
4.  http://www.arduino.cc/playground/Learning/4051
    Analog Multiplexer/Demultiplexer - 4051
    
*/

#define e 1        // delta machin truc

byte status_byte = 176;
// control change sur channel 1

byte cc_on_byte = 127;
// data to send if button is pressed

byte cc_off_byte = 0;
// data to send if button is depressed

// set controller numbers for the pots
byte ana_cc_0[] = {30, 31, 38, 39, 34, 35, 42, 43};     // EQ 1-8
byte ana_cc_1[] = {32, 33, 40, 41, 36, 37, 44, 45};      // EQ 8-16
byte ana_cc_2[] = {54, 53, 56, 51, 55, 52, 57, 50};      // FILTERS AND FX
byte ana_cc_3[] = {20, 0, 22, 0, 21, 0, 23, 0};              // VOLUME FADERS


byte data_ana_cc_0[] = {
      255, 255, 255, 255, 255, 255, 255, 255};

byte data_ana_cc_1[] = {
      255, 255, 255, 255, 255, 255, 255, 255};

byte data_ana_cc_2[] = {
      255, 255, 255, 255, 255, 255, 255, 255};

byte data_ana_cc_3[] = {
      255, 255, 255, 255, 255, 255, 255, 255};

// set controller numbers for the switches
byte dig_cc_0[] = {00, 04, 8, 12, 01, 05, 9, 13};      // BOUTONS BLOCK 1
byte dig_cc_1[] = {02, 06, 10, 14, 03, 07, 11, 15};      // BOUTONS BLOCK 2
byte dig_cc_2[] = {60, 61, 62, 63, 64, 65, 66, 67};      // BOUTONS CUE ET FX
byte dig_cc_3[] = {70, 71, 72, 73, 74, 75, 76, 77};      // BOUTONS PONTES

// plein de Zeros pour les switches
byte data_dig_cc_0[] = {0, 0, 0, 0, 0, 0, 0, 0};
byte data_dig_cc_1[] = {0, 0, 0, 0, 0, 0, 0, 0};
byte data_dig_cc_2[] = {0, 0, 0, 0, 0, 0, 0, 0};
byte data_dig_cc_3[] = {0, 0, 0, 0, 0, 0, 0, 0};

//Data entrees analogiques 
byte data0 = 0;
byte data1 = 0;
byte data2 = 0;
byte data3 = 0;

//Les Ports E/S
int latchPin = 5; // set the latch pin
int clockPin = 7; // set the clock pin
int dataPin_0 = 6;// set the data in pin 1
int dataPin_1 = 8;// set the data in pin 2
int dataPin_2 = 9;// set the data in pin 3
//int dataPin_2 = 10;// set the data in pin 4, UNUSED

byte digital_data = 0;

int state_0 = 0;
int state_1 = 0;
int state_2 = 0;
int state_3 = 0;

//Position Centrale pour le potarrrrrr (en DEC, et seulement tou qui sont traites)
byte finalpotar_1 = 108;

//variables a potar - def de la position Zero pour chaque pot, 0 (zero pour ne pas traiter)
byte vpotana_cc_0[] = {63, 57, 63, 56, 60, 57, 61, 58}; // EQ 1-8
byte vpotana_cc_1[] = {60, 0, 56, 0, 53, 0, 54, 0};      // EQ 8-16
byte vpotana_cc_2[] = {0, 0, 0, 0, 0, 0, 0, 0};              // FILTERS AND FX
byte vpotana_cc_3[] = {0, 0, 0, 0, 0, 0, 0, 0};              // VOLUME FADERS

//Important pour les calculs a virgulle flotante ...
float potartmp=0.00;

//Ze Setup Truc ...
void setup() {
      Serial.begin(31250);       // SERIAL MIDI
      //Serial.begin(28800);       //SERIAL CONSOLE DEBUG
      //Serial.begin(9200);       //SERIAL CONSOLE DEBUG
      
      pinMode(2,OUTPUT);
      pinMode(3,OUTPUT);
      pinMode(4,OUTPUT);

      pinMode(latchPin,OUTPUT);
      pinMode(clockPin,OUTPUT);
      pinMode(dataPin_0,INPUT);
      pinMode(dataPin_1,INPUT);
      pinMode(dataPin_2,INPUT);
      
        //Si vous le dites ...
      digitalWrite(latchPin,HIGH);
      digitalWrite(clockPin,HIGH);
}

//Debut de l'initiation dans la Magie Noire ...
void loop() {
      delayMicroseconds(20);
      digitalWrite(latchPin,0);

      byte myDataIn = 0;
      for (int j=7; j>=0; j--)
      {
            digitalWrite(clockPin, 0);
            delayMicroseconds(20);

/////// INPUT 01 NUMERIQUE      
            state_0 = digitalRead(dataPin_0);
            if (state_0) {
                  if(data_dig_cc_0[j] != 1) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_0[j]);
                        Serial.print(cc_on_byte);
                        data_dig_cc_0[j] = 1;
                  }
            }
            else {
                  if(data_dig_cc_0[j] != 0) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_0[j]);
                        Serial.print(cc_off_byte);
                        data_dig_cc_0[j] = 0;
                  }
            }
            
/////// INPUT 02 NUMERIQUE
            digitalWrite(clockPin, 0);
            delayMicroseconds(20);            
            
            state_1 = digitalRead(dataPin_1);
            if (state_1) {
                  if(data_dig_cc_1[j] != 1) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_1[j]);
                        Serial.print(cc_on_byte);
                        data_dig_cc_1[j] = 1;
                  }
            }
            else {
                  if(data_dig_cc_1[j] != 0) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_1[j]);
                        Serial.print(cc_off_byte);
                        data_dig_cc_1[j] = 0;
                  }
            }
/////// INPUT 03 NUMERIQUE
            digitalWrite(clockPin, 0);
            delayMicroseconds(20);                  
            
            state_2 = digitalRead(dataPin_2);
            if (state_2) {
                  if(data_dig_cc_2[j] != 1) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_2[j]);
                        Serial.print(cc_on_byte);
                        data_dig_cc_2[j] = 1;
                  }
            }
            else {
                  if(data_dig_cc_2[j] != 0) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_2[j]);
                        Serial.print(cc_off_byte);
                        data_dig_cc_2[j] = 0;
                  }
            }            
            
/////// INPUT 04 NUMERIQUE
/*             digitalWrite(clockPin, 0);
            delayMicroseconds(20);      

            state_3 = digitalRead(dataPin_3);
            if (state_3) {
                  if(data_dig_cc_3[j] != 1) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_3[j]);
                        Serial.print(cc_on_byte);
                        data_dig_cc_3[j] = 1;
                  }
            }
            else {
                  if(data_dig_cc_3[j] != 0) {
                        Serial.print(status_byte);
                        Serial.print(dig_cc_3[j]);
                        Serial.print(cc_off_byte);
                        data_dig_cc_3[j] = 0;
                  }
            }            
 */            
/////// Personne ne sait a quoi ca peut bien servir ...            
            digitalWrite(clockPin, 1);
      }
      digitalWrite(latchPin, 1);

      
      
      
/////// ANALOG INPUT 1
       for(byte i = 0; i <= 7; i++) {
            PORTD = i * 4; 
            data0 = analogRead(0) /4; //On divise par 4, et non par 8 pour passer de 1023 a 127, par la suite, on redivise le resultat en 2
            if(abs(data_ana_cc_0[i] - data0) > e) {
                  sendMidi(ana_cc_0[i], data0,vpotana_cc_0[i]);
                  data_ana_cc_0[i] = data0;
            } 
      }
/////// ANALOG INPUT 2      
      for(byte i = 0; i <= 7; i++) {  
            PORTD = i * 4;  
            data1 = (analogRead(1)) / 4;
            if(abs(data_ana_cc_1[i] - data1) > e) {
                  sendMidi(ana_cc_1[i], data1,vpotana_cc_1[i]);
                  data_ana_cc_1[i] = data1;
            }
      }
/////// ANALOG INPUT 3      
      for(byte i = 0; i <= 7; i++) {  
            PORTD = i * 4;  
            data2 = analogRead(2) /4;
            if(abs(data_ana_cc_2[i] - data2) > e) {
                  sendMidi(ana_cc_2[i], data2,vpotana_cc_2[i]);
                  data_ana_cc_2[i] = data2;
            }
      }
/////// ANALOG INPUT 4      
      for(byte i = 0; i <= 7; i++) {  
            PORTD = i * 4;  
            data3 = analogRead(3) /4;
            if(abs(data_ana_cc_3[i] - data3) > e) {
                  sendMidi(ana_cc_3[i], data3,vpotana_cc_3[i]);
                  data_ana_cc_3[i] = data3;
            }
      }
}
//La chose de Gael pour le potards ... Merciiiiii Gaeeeeeeeel !!!!!!!!!!!
void sendMidi(byte controlNum, byte val, byte vpotana) {
  if(vpotana != 0){
    if(val <= 2*vpotana){
      val=floor(val*((float)finalpotar_1/(float)vpotana));
    }
    else{
      potartmp=ceil(2*(float)finalpotar_1+(val-2*(float)vpotana)*((127-(float)finalpotar_1)/(127-(float)vpotana)));
      val=potartmp;
    }
    if(abs(val - 2*finalpotar_1) <= 2){
      val=2*finalpotar_1;
    }
  }
  
  Serial.print(0xB0, BYTE);
  Serial.print(controlNum, BYTE);
  //Chose tres importante !!!
  Serial.print(val /2, BYTE );
}
//Uffff, c'est fini ...

So ...
Excuse my French, but all the comments are may appear strange to most of you. It stays !

  1. The Schematic:
    Same as the first version posted on this thread, with the following mods and electronic observations: I use only Shift Down Resistors, 10k 1% SMD parts, and there is no oscillations or any perturbation even if a SMPS unshielded transformer is approached close to the board. The Res are placed close to the 4021s, and not on the switches. I simply trust a ground than an unfiltered VCC (4.45 to 5.23V !!!) from a USB port... For this reason I simply swapped out the USB as power Supply for an Filtered low weight SMPS 1A/9V. The Midi Serial data joins my PC by a Edirol UM-1X midi controller. No extra latency is added using this technique.

Using an external VCC and the integrated 7805 on the Arduino board gives me a perfectly clean 5.03V as VREF for my pots, and this greatly contributes for removing of any pot oscillations.

I also added some 16V 470uf caps in every section - pots, buttons and switches.

Aaaahhh... my controller!!! It fits in a very old Behringer DJ Mixer, 4 chans + a master. Check this link : http://www.behringer.com/EN/Products/DJX700.aspx

The fx section is unused. Almost all of the pots was dead, ams was replaced by different linear and log pots from other mixers - old Gemini 656 and a Numark one. It was a great mess to convert those INV EXPonential pots to something linear with a center point. The code solved most of the problems.

So, we have 48 analog and 48 digital switches. The next main difference in my schematic - each 4021 uses a digital IN on the board and no cascading. The code worked fine, but there ware some electrical inter-modulation on the oscilloscope, addind an extra to the 20us delay solved it at most, but I prefer very verbose midi protocol.

The power supply again : add a couple of extra filtering caps in the circuit and A 100nF FILTERING CAP AS TO EACH 4021 AND 4051, AS CLOSE AS POSSIBLE TO THE VDD AND THE GND PLANE! This is a golden rule for CMOS, else, strange things may happen.

The main problem was some -+ random oscillations from the pots in a position, different from the borders - GND and VCC. I firstly considered this as a USB voltage variation, so I filtered all of the Divided Voltage in the Pots with a 100nf caps. On the oscilloscope all seamed clan, but the oscillation persist. So the solution resides in the code : the 1023 input id divided by 4, an abs looks for 1 increments (all internal math resides on 256 and no 128!) and at the end everything is divided by 2. So no pair/unpair mismatch. Look at the code for more details :slight_smile:

There is a section to set the pot center position, 108 in my case. 108, because I use the EQ3 module in Ableton Live, and the +0.02 db Gain is on 108 :slight_smile: You can use 63 or 64, as you wish. The complicated floating math will (hopefully) fix your center position. If you use Log pots, converting them to a pseudo linear using this hint :

Set the log pot at its physical midpoint. Take resistance measurements from the wiper to the endpoints of the pot, call them R1 and R2. Get a
resistor with a value approximately equal to the absolute value of R1R2/(R2-R1). Connect this resistor between the wiper and the end of
the pot where you measured the higher resistance, you now have a pot with a resistance centered at its physical midpoint.

Next Step : make a Max device in Ableton to send back do the board the Chan Level, and drive the level led bars with a sort of DAC

Thanks again to the creator of this thread ans all the contributors.
Enjoy