Teensy LC + CD74HC4051 multiplex Midi problem...

Hi everyone!

I'm working on a project right now and I need a bunch of analog inputs added to my Teensy LC and then I need it to output Midi signals (its a midi controller)
In the end I'll eventually need about 15 potentiometers and I'm trying to connect them all to my Teensy LC using this CD74HC4051 breakout board:
09056-01.jpg
CD74HC4051 (multiplexer breakout board)
51u9BvH6eKL._SY355_.jpg
TEENSY LC (coolest little dude)

I've been searching for days (seriously dozens of hours at this point) and found like dozens of other threads and tutorials on almost the exact same matter and all the threads and tutorials I've read through are dead ends (either questions never get answered, or the OP never follows up with complete code or status of project or the tutorial is for Arduino only..here's some examples..

https://forum.pjrc.com/threads/2879...ultiplexer-with-MIDI?highlight=multiplex+midi
https://forum.pjrc.com/threads/4535...h-insane-number-of-sensors?highlight=74HC4051
https://forum.pjrc.com/threads/26057-motorized-faders-via-midi-usb?highlight=74HC4051
https://forum.pjrc.com/threads/31797-Teensy-FSR-based-MIDI-controller?highlight=74HC4051
https://forum.pjrc.com/threads/35221-DIY-MIDI-74HC4051-74HCT4051-Question?highlight=74HC4051
https://forum.pjrc.com/threads/3424...ler-with-16-Potentiometers?highlight=74HC4051
http://www.instructables.com/id/Sugarcube-MIDI-Controller/
https://www.youtube.com/watch?v=DXhxdsGREsU&t=621s
https://www.youtube.com/watch?v=NmxoBdEJG28


Anyways I just wanna get this over with and I never want anyone else to have to do this much digging. It's driving me insane at this point because I'm not a programmer at all, nor am I the brightest bulb in the bag of bulbs.....that being said I'm determined to get this done!

I've boiled my problem down to ONE potentiometer attached to the multiplexer then to the Teensy LC to eliminate confusion.

GOAL:

  • The pot should control MIDI cc 74 (also known as 4A in hex) on MIDI channel 1.
  • When the pot is turned clockwise/counter-clockwise the MIDI Data2 value should increase/decrease
  • When the pot is not being turned I want that Data2 value to just stay where it was turned to....


I've stitched together code from different parts of different tutorials that didn't work at all but made some sense and miraculously my code kinda works!

When I plug in the Teensy, Midi note 4A registers right where it should on the channel I want it to. Right now it seems to be increasing it and decreasing it a gazillion times a second regardless of weather or not I'm turning the Pot...when I do turn the pot it seems to change the value up and down franticly a gazillion times a second too..
Here is the diagram of my project.

teensy diagram.png

Here is my code. It doesnt seem to work properly but If anyone here can help me get it to work then I promise that I'll follow up and update this thread with the final working code for the greater good....I should note that this is the first code I've ever written and have no clue what most of it means but I understand the logic behind it I guess.

/* USB MIDI AnalogControlChange Example

You must select MIDI from the "Tools > USB Type" menu
http://www.pjrc.com/teensy/td_midi.html

This example code is in the public domain.
*/

#include <Bounce.h>
//mapping of mux to teensy digital pins
int pin_Out_S0 = 0;
int pin_Out_S1 = 1;
int pin_Out_S2 = 2;
int pin_Out_S3 = 3;
int pin_In_Mux1 = A0;
//the state of the mux channels(initialized to zero)
int Mux1_State[16] = {0};

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI continuous controller for each analog input
const int controllerA0 = 74; // 74 = test cc74


void setup() {
pinMode(pin_Out_S0, OUTPUT);
pinMode(pin_Out_S1, OUTPUT);
pinMode(pin_Out_S2, OUTPUT);
pinMode(pin_Out_S3, OUTPUT);
Serial.begin(9600);
}



// store previously sent values, to detect changes
int previousA0 = -1;


elapsedMillis msec = 0;

void loop() {
//mux code
{
updateMux1();
for(int i = 0; i < 16; i ++) {
if(i == 15) {
Serial.println(Mux1_State);
} else {
Serial.print(Mux1_State);
Serial.print(",");
}
}
}

// only check the analog inputs 50 times per second,
// to prevent a flood of MIDI messages
if (msec >= 20) {
msec = 0;
int n0 = analogRead(A0) / 16;

// only transmit MIDI messages if analog input changed
if (n0 != previousA0) {
usbMIDI.sendControlChange(controllerA0, n0, channel);
previousA0 = n0;
}

}

// MIDI Controllers should discard incoming MIDI messages.
// http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
while (usbMIDI.read()) {
// ignore incoming messages
}
}

void updateMux1 () {
for (int i = 0; i < 16; i++){
digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
Mux1_State = analogRead(pin_In_Mux1);
}
}





I'm crossing my fingers and just hoping some good Samaritan will lend a hand because I seriously have no business coding anything...

edit: changed a number
 
Last edited:
Code:
// only check the analog inputs 50 times per second,
// to prevent a flood of MIDI messages
if (msec >= 20) {
msec = 0;
int n0 = analogRead(A0) / 16;
This part is just reading the analog pin directly following the loop that reads all the mux values... but it will be on pin 15 not pin 0.

You need something that reads from the Mux1_State array.

The serial.print stuff looks like it should work but it's likely pumping a lot of text as there's nothing limiting the speed. Put a delay(100) in the loop to slow the output down.

I'd recommend tying the unused mux inputs to ground while testing as otherwise you will get noise readings from stray induced voltages.

Skip the midi part until you understand the mux code using serial.print
https://www.pjrc.com/teensy/td_serial.html
 
Last edited:
I've switched the Potentiometer's data jumper to pin 15 and things have improved!
I'm no longer being flooded with gazillions of output per second and understand whats happening now....not sure how to fix it though.

As of right now, when the Pot is turned all the way counter-clockwise I get a value of 7F (good)
If turned all the way to the right I get a value of 00 (good)
If the pot is anywhere inbetween, the Data2 value jumps up and down by a value of 1.

I tried grounding all the other pins as you suggested but it did not resolve the "up/down by 1" issue.

My guess is because its an analog Pot, the value can never Truly be an exact number because it will always be at rest between two values (it's 360 degrees...hard to put into words)

I need to add some sort of code that just picks one of the values and sticks with it rather than switching back and forward between the two...if anyone knows how to write that code then the problem will be solved.
 
Last edited:
You've taken on two non-trivial tasks at once...

Building an analog midi control from a potentiometer as a voltage divider AND multiplexing signals.

Each on their own isn't too bad but together it makes things more complex than is needed.

With pots as voltage dividers stability and resolution of the output is a major issue and can include hardware issues that add noise making stability a bigger challenge.

There is a library called ResponsiveAnalogRead() that stabilizes analog values by adding filtering and hysteresis-based control so that minor fluctuations are ignored.

You can do your own version (it's not really difficult) but you're likely better off using this for now.

Here's my suggested starting code for a controller with buttons and pots:
https://forum.pjrc.com/threads/45376-Example-code-for-MIDI-controllers-with-Pots-and-Buttons

I strongly recommend you try building controllers without a MUX first. - even if it's just bread boarding

...afterwards:

If you want to add a MUX you will need to interject some code to replace the analog.update with something like the code in the updateMux1 part of your Frankencode to populate a matrix with the raw analog readings:
Code:
     digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
     digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
     digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
     digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
     analog.update[pin_In_Mux1] ;

The bitwise and operator (&) is extracting the appropriate bit-state from the index variable to get the correct pattern on MUX control lines
Then the value is read and recorded.

The Mux1_State matrix assignment line is replaced with analog.update[] which will read the Teensy analog pin set by the digital write commands in the preceding lines.

You also need some of the setup code too:

Code:
//mapping of mux to teensy digital pins
 int pin_Out_S0 = 0;
 int pin_Out_S1 = 1;
 int pin_Out_S2 = 2;
 int pin_Out_S3 = 3;
 int pin_In_Mux1 = A0;

I'll have a go at getting code that compiles in the next day or so (unless you manage before me) but I don't have a mux handy to test the code so that will be up to you.


PS... grounding the other mux inputs only matters once you're outputting all the values. If you try to read a pin that is not connected to any sink or source of current then the voltage is free to float around and it will inundate the controller with many wild signals that cannot be tamed by ResponsiveAnalogRead or anything else.

https://arduino.stackexchange.com/questions/19967/analog-pins-are-sensing-values
 
Last edited:
Nice! Thanks for the reply (again)
I'm glad you brought up the controller without a Multiplex project/tutorial - I did indeed go through that step first before taking on this multiplex task and managed to run through a couple tutorials that used different code (I think) with no issues whatsoever (they were all straight forward and simple)

I hadn't considered voltage to be a factor but now that you bring it up - it makes total sense.

I think I know how to add the library to the project but I'm not exactly sure how I can take advantage of it (don't know what code I should write :S )

Any help is appreciated, thanks again for taking the time to help!
 
Last edited:
I'll see if I have a few minutes tonight to try my hand on what I think should work... but as I said I'm not likely to be able to test the MUX part... but if I can get it most of the way there you should be able to debug my code for/with me. :)
 
Amazing! That's super kind of you!
If you post the code here I will definitely test it out and post the results tonight when I get home (about 5 hours from now)
 
Hmm... was a little optimistic... there is only 1 analog pin now so the array has to be populated differently than I'm use too... it's still doable but it might take a few hours instead of 20 min.
 
Have you thought about a 3.5 and skipping the MUX?

The issue is the method .hasChanged won't work if there is only one pin so I'll have to track changes myself...

Unless there are other major surprises it won't be too hard but I'm unlikely to finish it tonight as I'm on by third beer.
 
Hehe yeah I have. Theyre a bit too expensive, and I'm predicting that I'll have to make a bunch of these next year for projects that require various numbers of pots (4-32) so I should learn this while I still can.

I understand what your saying though. Difficult to work on a problem if you don't have the hardware infront of you
 
Well I found an 8 channel mux and some trimmers that I'd started to set up on a proto-board but never got around to soldering.

So I should be able mimic the same behavior with three channel selector pins. If I can get it to work with that it should be trivial to add pin_Out_S3 back in.

I got the code to compile but it's very, VERY! likely there are still bugs.

We can try adding serial.print lines and slowing down the code to see what's not working tomorrow but I don't want to be charged for coding while impaired so I'll have call it a day.

EDIT -- See posts 26 and 29 for code that actually works... 26 uses ResponsiveAnalogRead while 29 uses dead-band hysteresis for 'smoothing' output.


Code:
//************LIBRARIES USED**************
// include the ResponsiveAnalogRead library for analog smoothing
#include <ResponsiveAnalogRead.h>
//usbMIDI.h library is added automatically when code is compiled as a MIDI device

//mapping of mux to teensy digital pins
int pin_Out_S0 = 0;
int pin_Out_S1 = 1;
int pin_Out_S2 = 2;
int pin_Out_S3 = 3;
int pin_In_Mux1 = A0;
//the state of the mux channels(initialized to zero)
int Mux1_State[16] = {0};
int Mux1_State_Lagged[16] = {0};
 
// ******CONSTANT VALUES******** 
// customize code behaviour here!
const int channel = 1; // MIDI channel
const int A_PINS = 16; // number of Analog PINS (these are now MUX 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 CCID[A_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36}; // set your own CC values for the 16 MUX pins

// 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**************

// initialize the ReponsiveAnalogRead objects
ResponsiveAnalogRead analog(pin_In_Mux1, true);


//************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(); // comment out while there are no buttons on the project
  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
     digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
     digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
     digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
     digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
     analog.update();
     Mux1_State[i] = analog.getValue();
    // if the repsonsive value has change, print out 'changed'
    if(Mux1_State[i]!=Mux1_State_Lagged[i]) {
      data[i] = Mux1_State[i]>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        usbMIDI.sendControlChange(CCID[i], data[i], channel);
      }
    }
  }
}
 
Last edited:
Sweeet. I'm gunna try this out and update tomorrow if I'm not too hung over (Beer, gumbo, Rick and Morty with the roomie on my end tonight so I'll definitely screw this up at least once)
 
yikes ...double posts... the second one has some corrections already... there are bound to be more
 
Three things might help a bit.

You can add capacitors to lower the AC impedance, which might give you cleaner measurements. But these capacitors must be on the pot side of the mux, between each signal to ground. Do not connect a capacitor to the mux output, because that will only slow things down and tend to make each pot interfere with the others. Capacitors also slow things down on the input side of the mux, so you don't want to push this to extremes (slower than a human can turn the knob). Also, ceramic type capacitors work better than electrolytic or tantalum ones. Values between 0.01uF to 0.47uF are most effective.

If your pots are a high value, like over 10K, try using a lower resistance pot. They will draw more power, so obviously you don't want to push this to extremes. Below 5K is probably diminishing returns. But going from 100K to 10K pots usually makes a pretty noticeable improvement.

While it's best to reduce the noise as much as possible by improving the hardware, the ResponsiveAnalogRead library can really help you clean up a small amount of noise that just won't go away by any reasonable (cheap) hardware improvements.
 
...oh forgot the mux needs time to switch... may need to add a timer to limit speed and the code above isn't set up to add this properly... it might work to just add delay but it will prevent the main loop from running at speed and so would slow other functions.


edit... MUX should update one pot per main loop cycle and only when sufficient time is lasped

...something like this (tested output for logical consistency but I cannot test with MUX for the moment)
Code:
//************LIBRARIES USED**************
// include the ResponsiveAnalogRead library for analog smoothing
#include <ResponsiveAnalogRead.h>
//usbMIDI.h library is added automatically when code is compiled as a MIDI device

 
// ******CONSTANT VALUES******** 
// customize code behaviour here!
[COLOR="#FF0000"]const int muxTimeMin = 100; // minimum micro-seconds between MUX reads[/COLOR]
const int channel = 1; // MIDI channel
const int A_PINS = 16; // number of Analog PINS (these are now MUX pins)

// define the CC ID numbers on which to send them..
const int CCID[A_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36}; // set your own CC values for the 16 MUX pins



//******VARIABLES***********
//mapping of mux to teensy digital pins
int pin_Out_S0 = 0;
int pin_Out_S1 = 1;
int pin_Out_S2 = 2;
int pin_Out_S3 = 3;
int pin_In_Mux1 = A0;
//the state of the mux channels(initialized to zero) 
int Mux1_State[16] = {0};
//...and their lagged values to test for change in the absence of .hasChanged()
int Mux1_State_Lagged[16] = {0};
[COLOR="#FF0000"]elapsedMicros mux1Updated;
//elapsedMillis mux1Updated; // switch to millis to troubleshoot [/COLOR]
byte i = 0; // index for mux loop 
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; 


//************INITIALIZE LIBRARY OBJECTS**************

// initialize the ReponsiveAnalogRead object 
ResponsiveAnalogRead analog(pin_In_Mux1, true);


//************SETUP**************
void setup() {
// 

}

//************LOOP**************
void loop() {
  getMUX1Data();
  // other work here... mux is called once in each loop rather than updating all in every loop
  while (usbMIDI.read()) {
     // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
  }
}


//************MUX1 SECTION**************
void getMUX1Data(){  
  if (mux1Updated>muxTimeMin) {
    // update the ResponsiveAnalogRead object every loop
     analog.update();
     Mux1_State[i] = analog.getValue();

    if(Mux1_State[i] != Mux1_State_Lagged[i]) {
      Mux1_State_Lagged[i] = Mux1_State[i];
      data[i] = Mux1_State[i]>>3;
      if (data[i] != dataLag[i]){ 
        dataLag[i] = data[i];
        usbMIDI.sendControlChange(CCID[i], data[i], channel);
[COLOR="#0000FF"]
        Serial.print("MUX_PIN: ");
        Serial.print(i,DEC); 
        Serial.print(" CC: ");
        Serial.print(CCID[i],DEC); 
        Serial.print(" DATA HEX: ");
        Serial.println(data[i],HEX); [/COLOR]
      }
    } //moved this below in error... this is where it goes
    // set mux control pins for next pass
    // done here to give MUX time to switch at least equal to the value in 'muxTimeMin'
    digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
    digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
    digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
    digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
    //reset timer
    mux1Updated = 0; 
    //increment index
    i++;
    if (i>15)   {i=0;}
   //} // wrongly thought close to changed reading should go here
  }
}

It occurs to me some of the instability you would have seen when you had mux 15 reading data was the speed at which the loop was running means the voltage at the Teensy pin has not had sufficient time to stabilize and so the readings vary much more than in the least significant bit.

The serial.print stuff should help you sort this... it wasn't clear if you were actually using the serial monitor to troubleshoot your code... it's much easier to use than hoping to figure out what's going wrong from the MIDI messages (well to a point and depending on your MIDI software that's receiving).


You need to understand how to adjust the timing. Change to elapsedMillis instead of ...Micros and alter the value muxTimeMin to get it to a speed that is human readable on the serial monitor if you are getting too many MIDI values (for example) because of the electrical issues Paul has mentioned.

https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

Once you have it working with the MUX in slow motion you should be able to switch back to micros and adjust the value downwards and see if/where instability in the values comes back.
 
Last edited:
Here's a line-by-line explanation... should help you troubleshoot now and extend the code later.


Includes the library used to smooth analog values
#include <ResponsiveAnalogRead.h>

These next set of lines define constant values so you don't have to look for them in the code to update (especially when used in multiple locations)

The first sets the minimum time between switching to the next MUX reading (meant to be in micro seconds for the MUX but can be set with millis if human scale is needed (see below)
const int muxTimeMin = 100;

Next the MIDI channel you wish to send on (can be set as variable by hardware later with minimal code changes -- other than the stuff that sets the channel)
const int channel = 1;

Here's where you adjust the number of pins on the MUX
const int A_PINS = 16;

Here is where you set CC values. This too can be turned into a variable array but an interface for selecting CC values is a complex undertaking. I prefer to set these externally using midi sysex messages.
const int CCID[A_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36};

These lines assign the control pins used to control the MUX
int pin_Out_S0 = 0;
int pin_Out_S2 = 2;
int pin_Out_S3 = 3;

This line sets the pin used for reading MUX analog values
int pin_In_Mux1 = A0;

*EDIT* - these are not required with RAR objects as they hold the current values and report changes through the hasChanged() so the technique from my non-MUX code example is much closer to what we need to do than the if-changed test using lagged value arrays.
These define arrays for the mux reading and a lagged value from the last time they were updates. This second array is needed because we cannot use the '.hasChanged()' method when we are cycling through different MUX pins and so we must track changes on each mux channel separately
int Mux1_State[16] = {0};
int Mux1_State_Lagged[16] = {0};

This defines a value of the special type supported by internal Teensy libraries (nothing to include) that are incremented to give the number of ellasped time units (micros here) since the value was last reset.
Switching this to elapsedMillis allows you to define human time scales for events and watch the output in the serial monitor or a midi monitor.
elapsedMicros mux1Updated;

Here we define the index variable used to build a loop-like structure. It's declared as a global because it has to persist across multiple calls to the MUX update function
byte i = 0;

Here we store the calculated data bytes in two arrays -- again the second is a lagged value from the last update and again it's used to tell if updating is needed
The midi data is lower resolution so it may not update just because the smoothed data value has.
byte data[A_PINS];
byte dataLag[A_PINS];

Here we initialize ResponsiveAnalogRead call
*EDIT* - this is wrong... should be an array of objects with the same pin. This replaces the need for arrays to hold the direct readings and getting the hasChanged() method back means we don't need lagged values either!
ResponsiveAnalogRead analog(pin_In_Mux1, true);
The Boolean parameter is to set 'sleep' functionality. Here's what the read.me says about it:
// make a ResponsiveAnalogRead object, pass in the pin, and either true or false depending on if you want sleep enabled
// enabling sleep will cause values to take less time to stop changing and potentially stop changing more abruptly,
// where as disabling sleep will cause values to ease into their correct position smoothly and with slightly greater

Here is the main loop:
Code:
void loop() {
  getMUX1Data();
  while (usbMIDI.read()) {}
}
Right now it only calls the function that scans for the next MUX data value and clears any received midi messages without action.
It turns out your controller will crash if the MIDI event queue isn't cleared so its strongly recommended to include a read command in the main loop.
The empty braces are where any midi reading code would go... like reading sysex messages from your host.

Here's our main function call to get MUX data. -- the void keyword tells the compiler there is no data returned to the main loop by this function
void getMUX1Data(){

Here we check if the time since last update is greater than the lower limit we set above
if (mux1Updated>muxTimeMin) {

Here we ask the library to update the ResponsiveAnalogRead variable 'analog'
analog.update();
...and here we ask for the updated value explicitly to record it in its data array
Mux1_State = analog.getValue();

Now we check if it's different from last time... if not we return to the main loop...
if(Mux1_State != Mux1_State_Lagged) {
...but if so we need to update the lagged value for next time...
Mux1_State_Lagged = Mux1_State;

*EDIT* - If we roll our own Hysteresis we don't need these MIDI data arrays and if we use RAR objects then we can use this technique to limit messages to MIDI-resolution-level changes. So you would only use one or the other of these two methods of tracking changes to the MIDI notes not both (which I do in the roll-your-own-smoothing version in post 22).
Now we write a reduced resolution value to a data array storing what we are actually sending via MIDI messages
The '>>' is called the bitshift-right operator which moves all the bits rightward while filling the left-most bits with zeros.
Each shift is like dividing by two ... so '>>3' effectively means 'divide by eight' - or reduce the bit resolution by 3 bits.
data = Mux1_State>>3;

Now we do a similar check to see if the MIDI data value itself will be different.
Because we're dropping from 10 bits to 7 bits many (most) updates to the ResponsiveAnalogRead value will not result in new MIDI values.
if (data != dataLag){

...again... if we update then we have to write to the lagged array so the next comparison will work
dataLag = data;

Finally... we send MIDI... I assume you're familiar with this....
usbMIDI.sendControlChange(CCID, data, channel);

Then there are a bunch of serial.print commands to give you feedback via the Arduino serial monitor window
Serial.print("MUX_PIN: ");
Serial.print(i,DEC);
Serial.print(" CC: ");
Serial.print(CCID,DEC);
Serial.print(" DATA HEX: ");
Serial.println(data,HEX);

Back outside the MIDI messaging block -- whether the MIDI has changed or not we have updated the ResponsiveAnalogRead so it's time to move to prepare for the next MUX pin read by updating the control pin values...
digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
digitalWrite(pin_Out_S3, HIGH && (i & B00001000));

Now the muxUpdated value needs to be zeroed so the code is not called again until out minimum time limit has been observed
mux1Updated = 0;

Then the (global) index value is incremented and wrapped back to zero when needed
i++;
if (i>15) {i=0;}
 
Last edited:
UPDATE:These are the steps I had taken before oddson's post at 2:38, which I am going to go over meticulously to see what I can learn...Im at work right now tho.

I've tried a couple different things today and got a couple different results.

USING MY CODE:
experiment#1. I added the ResponsiveAnalogRead.h library to my project by clicking project, add library....weather or not this helped I can't say for sure but it's in there now...the data2 value still jumped up and down by 1 a few times per second.

experiment#2 I then tried re-grounding pins 0-14 with some thick wire rather than the jumpers I was using before and it made a HUUUGE difference! after making this change, when I turn the pot I can clearly see a the data2 value increase and decrease as desired. When I set the pot to a certain value and let go of it, the value still jitters up and down by a value of 1 but per/minute it's like %95 less frequent.

tonight I will rewire as much as possible using the thick wire to see how much of the jitter problem goes away.

experiment #3 I also tried adding a few different capacitors all within the range suggested by Paul Stroffregen but it did not seem to improve things (I added them between the mux and the pot on the + line first, then on the data line itself...no noticeable difference to the jitter (thank goodness, because the prospect of having to add 16 capacitors would have been a huge pain in the ass)

experiment#4 with 95% of the jitter gone, I added a pot to mux pin 14, added the line of code "const int controllerA0 = 75;" to try to assign it to midi cc 75.. then tried it out..... turning the pot for mux pin14...no results....turning pot for mux pin 15 still worked nicely though!

USING ODDSUNs CODE
I tried using Oddsun's code and was flooded with a lot of output when the pot wasn't fully turned clockwise or counterclockwise.

it looked like there were parameter changes being detected multiple times per second when the pot was not being touched...turning clockwise or counterclockwise stopped the torrent of output though....

With pots attached to both pins 15 and 14 I did however notice that their values seemed to change when manipulated so I assigned them to two CCS that I know corespond with left and right outputs on my synth annnnd was confronted with insane sounding static and the oscillators (that I knew worked using my code) were basically just static noise....



CURRENT STATUS:
my code: 95% of the data2 jitter is gone so far. but I don't understand how to add additional pots to the project.

ODDSUNs code: lots of parameter changes being detected, shitload of static somehow..
 
Last edited:
You really have to slow my code down to 200 milliseconds or more and watch the serial messages print one-by-one and see if it is even working correctly with the MUX.

It's likely easier to see the output on the serial monitor than to decipher messages on a MIDI monitor program.

(It is still not clear to me if you are using the serial print output on your own code or even if you're still using the code from your first post.)

For my code to work you need to have the mux pins grounded (or hot or somehow attached to stable voltages in between) or the floating voltages on the open pins will make troubleshooting impossible.

It's very possible (likely even) that there are mistakes in my code but I think it's near to a working version if it's attached to stable hardware and the MUX timing issue is carefully resolved.

I'll likely try to make an eight-channel MUX version that I can test with hardware I have on hand but it won't be for at least a few days. But I'm happy to help you troubleshoot the code on your setup (provided I'm not too far off the mark with what I've posted above).



FYI - In your experiments it's likely stability was best with a pot on MUX14 because the voltage difference between it and MUX15 would likely be less and therefore the reading at MUX15 will have stabilized more by the time you read it.

It's a symptom of your code not waiting for the MUX to switch and the voltage at A0 to stabilize before reading the output.

If I'm correct then code that polls the MUX at regular time intervals should be able to get fairly stable output (i.e. varies by less than the three bit's we're dropping) by limiting the speed at which it accesses the MUX but still get acceptable responsiveness with the MIDI output.


Try adding a delay(); command to your code between between setting the control pins and reading the value your loop you should get much more stable (thought perhaps less responsive) output.

Code:
digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
delay(1);
Mux1_State[i] = analogRead(pin_In_Mux1);

While this may work it's not the best approach, especially once you start adding other features as a simple delay stops anything from happening where my code allows for other processing while waiting for the MUX to switch and input voltage at the Teensy to stabilize.

I'd try just delay(1); to start as the we should be talking about fairly short amounts of time.
 
It occurs to me now that ResponsiveAnalogRead() may be incompatible with MUX'd signals as it cannot use the history of the signal to smooth the values without leaving it an inordinate amount of time on each new mux pin to re-stabilize on the new values.

(It's the same thing in software that capacitors after the mux cause - essentially a low-pass filter is slowing the responsiveness between sudden value changes that you don't want to smooth.)

I'll see about a version of my code with basic dead-band hysteresis instead... it's not really very difficult.

EDIT - really not difficult. Because I already had to track changes with a lagged array I just had to alter the test-for-change line to add the dead-band threshold.

abs(Mux1_State - Mux1_State_Lagged) > dbt


dbt is how many bits out the reading has to be from the previous to be considered different enough to recalculate. If the absolute difference between the readings is greater than the threshold value the new reading becomes the old and we (usually) send a new MIDI.

The test-for-MIDI-change thing could be removed as if the threshold is 8 or greater it will recalculate the MIDI every time anyway but I've left it in for now.

Still not completely 'tested' but here you go (timescale is set in millis in this code)

Edit this code doesn't initialize the control pins. ...see code at post 29

Code:
// ******CONSTANT VALUES******** 
// customize code behaviour here!
const int muxTimeMin = 1; // minimum micro-seconds between MUX reads
const int channel = 1; // MIDI channel
const int A_PINS = 16; // number of Analog PINS (these are now MUX 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 CC ID numbers on which to send them..
const int CCID[A_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36}; // set your own CC values for the 16 MUX pins

[COLOR="#FF0000"]const int dbt = 8;[/COLOR] // a threshold value for how big the dead-band is before recalc

//******VARIABLES***********
//mapping of mux to teensy digital pins
int pin_Out_S0 = 0;
int pin_Out_S1 = 1;
int pin_Out_S2 = 2;
int pin_Out_S3 = 3;
int pin_In_Mux1 = A0;
//the state of the mux channels(initialized to zero)
int Mux1_State[16] = {0};
int Mux1_State_Lagged[16] = {0};
//elapsedMicros mux1Updated;
elapsedMillis mux1Updated; // switch to millis to troubleshoot 
byte i = 0; // index for mux loop
// 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


void setup(){}//need this even if you don't use it

//************LOOP**************
void loop() {
  getMUX1Data();
  // other work here... mux is called once in each loop rather than updating all in every loop
  while (usbMIDI.read()) {
     // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
  }
}


//************MUX1 SECTION**************
void getMUX1Data(){  
  if (mux1Updated>muxTimeMin) {

     Mux1_State[i] = analogRead(pin_In_Mux1);

    if([COLOR="#FF0000"]abs(Mux1_State[i] - Mux1_State_Lagged[i]) > dbt [/COLOR]) {
      Mux1_State_Lagged[i] = Mux1_State[i];
      data[i] = Mux1_State[i]>>3;
      if (data[i] != dataLag[i]){ 
        dataLag[i] = data[i];
        usbMIDI.sendControlChange(CCID[i], data[i], channel);

        Serial.print("MUX_PIN: ");
        Serial.print(i,DEC); 
        Serial.print(" CC: ");
        Serial.print(CCID[i],DEC); 
        Serial.print(" DATA HEX: ");
        Serial.println(data[i],HEX); 
      }
    }
    // set mux control pins for next pass
    digitalWrite(pin_Out_S0, HIGH && (i & B00000001));
    digitalWrite(pin_Out_S1, HIGH && (i & B00000010));
    digitalWrite(pin_Out_S2, HIGH && (i & B00000100));
    digitalWrite(pin_Out_S3, HIGH && (i & B00001000));
    //reset timer
    mux1Updated = 0; 
    //increment index
    i++;
    if (i>15)   {i=0;}

  }
}
 
Last edited:
responsiveAnalogRead (short: RAR) should be fine as long as it's used properly. If you feed every value into the same RAR object, it won't work for the reason you stated. Instead, create one RAR object for ech channel and use them in turn:
  • create an array of n RAR objects: RAR_array[n]
  • every object of this array must use the same analog pin, since the all read from the same pin
  • for each channel i:
    • switch MUX to channel i
    • after switchung to MUX channel i, wait for the MUX output voltage to settle
    • call RAR_array::update()
      [*]profit!

 
disclaimer: I didn't test that suggestion. If the RAR library is trying to be smart about duplicate analog pins, we have to use RAR::update(value) which allows to use values obtained from anywhere, not just the ADC. Looks useful for values from SPI or I2C devices (anything, really). In the RAR readme we're somwhere between "Using your own ADC" and "Smoothing multiple inputs" (see https://github.com/dxinteractive/ResponsiveAnalogRead/blob/master/README.md)
 
Back
Top