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

Thread: [photo needed] How to make a midi controller with 16 analog inputs using Teensy LC!

  1. #1

    [photo needed] How to make a midi controller with 16 analog inputs using Teensy LC!

    Hi everyone!
    Here's how to make a USB midi controller for dirt cheap!
    This controller has 16 analog inputs.

    what you'll need:

    1 Teensy LC ---> buy from pjrc ($11.65)
    1 CD74HC4067 breakout board ---> buy from sparkfun ($4.95)
    1 to 16 10k linear B potentiometers. ---> buy from ebay (5pc for $1.25)
    1 Breadboard -----> buy from ebay ($1.50)
    Wire....pennies per ft! buy lots because its always handy to have lots of.

    Step 1:
    Once you have all these items wire them up together like this...for simplicity sake only 3 potentiometers are shown but you can connect 16, just remember to connect any additional pots to +/- and the center pins of the potentiometers go to the "C" pins on the red breakout board.
    Click image for larger version. 

Name:	i63aWzf.jpg 
Views:	164 
Size:	97.6 KB 
ID:	11852


    Step 2:
    Install Arduino 1.8.4 (newer versions don't support teensy LC yet )
    Get it here ---> https://www.arduino.cc/en/Main/OldSoftwareReleases

    Step 3:
    Install the Teensy loader application
    Get it here ---> https://www.pjrc.com/teensy/loader.html

    Step 4:
    Plug your Teensy into your computer via usb...

    Step 5:
    Open Arduino 1.8.4 and click "tools" and then set the board type to Teensy LC
    Click image for larger version. 

Name:	mBqmiNm.png 
Views:	85 
Size:	34.3 KB 
ID:	11857


    Step 6:
    Click tools and also set the USB type to MIDI
    Click image for larger version. 

Name:	qLzFDnu.png 
Views:	84 
Size:	30.0 KB 
ID:	11853


    Step 7:
    Delete the "void setup" and "loop setup" code so you have a nice blank page in arduino.
    Click image for larger version. 

Name:	ViDcOmL.png 
Views:	70 
Size:	10.7 KB 
ID:	11854

    Step 8:
    Paste this code into the blank page
    //************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!
    const int muxTimeMin = 500; // minimum micro-seconds between MUX reads
    const int channel = 1; // MIDI channel
    const int MUX_PINS = 16; // number of MUX Channnels

    // define the CC ID numbers on which to send them..

    const int CCID[MUX_PINS] = {70,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36};


    //******VARIABLES***********
    // a data array and a lagged copy to tell when MIDI changes are required
    byte data[MUX_PINS];
    byte dataLag[MUX_PINS]; // when lag and new are not the same then update MIDI CC value
    byte i=0; // global index for MUX channel reads

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


    //****** TIMER VARIABLE *** change scale here!
    elapsedMicros mux1Updated; // switch to micros to run at speed and tune with muxTimeMin setting above
    //elapsedMillis mux1Updated; // switch to millis to troubleshoot

    //************INITIALIZE LIBRARY OBJECTS**************
    // initialize the ReponsiveAnalogRead objects
    ResponsiveAnalogRead analog[]{
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true},
    {pin_In_Mux1 ,true}
    };



    //************SETUP**************
    void setup() {
    //! don't forget to set for output!
    pinMode(pin_Out_S0, OUTPUT);
    pinMode(pin_Out_S1, OUTPUT);
    pinMode(pin_Out_S2, OUTPUT);
    pinMode(pin_Out_S3, OUTPUT);
    }

    //************LOOP**************
    void loop() {
    nextMUXpin();
    while (usbMIDI.read()) {
    // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
    }
    }


    //************MUX SECTION**************
    void nextMUXpin(){
    if (mux1Updated>muxTimeMin) {
    // 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);
    serialPringMIDIdata(); // use to troublshoot
    }
    }

    //reset timer
    mux1Updated = 0;
    //increment index
    i++;
    if (i>15) {i=0;}
    // 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));
    }
    }

    // **useful for debugging, comment out function call to run full speed
    void serialPringMIDIdata(){
    Serial.print(i,DEC);
    Serial.print(" :");
    Serial.print(HIGH && (i & B00000001),BIN);
    Serial.print(HIGH && (i & B00000010),BIN);
    Serial.print(HIGH && (i & B00000100),BIN);
    Serial.print(HIGH && (i & B00001000),BIN);
    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);
    }
    This code was made by the user oddson here at the Teensy forum (https://forum.pjrc.com) ...HERE is a link to the full thread if you're bored and wanna see how he developed it. I made some very small changes to the code he posted in order to make this code work with the wiring in my diagram above. He has mentioned that there is another verson of this code in the thread but I havn't had a chance to try it out yet.

    To set the CC values of the C0 pins on the red breakoutboard, change the values in the "const int CCID[MUX_PINS] = {70,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36}; " to whatever you want! left to right (70 to 36) are c0 to c15

    Step 9:
    Click "verify"
    Click image for larger version. 

Name:	UCf5PQJ.png 
Views:	81 
Size:	53.6 KB 
ID:	11855

    Wait a little while and allow the arduino software to verify and compile the code.

    Step 10:
    Once its done compiling, click "upload"
    Click image for larger version. 

Name:	twCl2Hg.png 
Views:	113 
Size:	58.0 KB 
ID:	11856


    Step 11:
    Plug your fancy new DIY midi controller into whatevers you want and play around with it!

    Note: I made this project to increase the number of analog inputs for a cool synth creation board called "Axoloti" you can check it out here: http://www.axoloti.com/ its awesome!

  2. #2
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    I believe it's preferable to power the MUX from Vin (5v) and ground it to the GND pin.

    The device is supposed to work better (lower on resistance, more linear output) with the higher supply voltage and it's return should not be to the analog ground (as it may introduce additional noise).

    FYI - the other version doesn't use the (excellent!) ResponsiveAnalogRead(RAR) library to keep the voltages stable enough to use for MIDI CC values, relying instead on simple 'dead-band' control where changes less than some threshold are ignored.

  3. #3
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    Can you provide example code with 2+ multiplexers? For example, use your example above for 16 analog inputs with one multiplexer and add a second multiplexer for 16 digital inputs, so on and so forth. Thanks for your help in advance. Cheers.

  4. #4
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    Let me also add doing the above example all on Midi channel 1.

  5. #5
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    Sorry I thought you were asking the OP to do this but now I realize that's my code so I guess you're asking for me to do this.

    I'm don't have the compiler handy ATM but here's my attempt from just a text editor (which means it almost certainly will contain an error or two):
    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!
    const int muxTimeMin = 500; // minimum micro-seconds between MUX reads 
    const int channel = 1; // MIDI channel
    const int MUX_PINS = 16; // number of MUX Channnels
    
    // define the CC ID numbers on which to send them..
    
    const int CCID1[MUX_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36};
    
    const int CCID2[MUX_PINS] = {41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56};
    
    
    //******VARIABLES***********
    
    // a data array and a lagged copy to tell when MIDI changes are required
    byte data1[MUX_PINS];
    byte data1Lag[MUX_PINS]; // when lag and new are not the same then update MIDI CC value
    
    //amd again for second MUX
    byte data2[MUX_PINS];
    byte data2Lag[MUX_PINS]; // ditto
    
    byte i=0; // global index for MUX channel reads
    
    //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 = A1;
    int pin_In_Mux2 = A2;
    
    
    //****** TIMER VARIABLE *** change scale here!
    elapsedMicros muxUpdated; // switch to micros to run at speed and tune with muxTimeMin setting above
    //elapsedMillis muxUpdated; // switch to millis to troubleshoot 
    
    //************INITIALIZE LIBRARY OBJECTS**************
    // initialize the ReponsiveAnalogRead objects
    ResponsiveAnalogRead analog1[]{
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true}
    }; 
    
    ResponsiveAnalogRead analog2[]{
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true}
    }; 
    
    
    //************SETUP**************
    void setup() {
      //! don't forget to set for output!
      pinMode(pin_Out_S0, OUTPUT);
      pinMode(pin_Out_S1, OUTPUT);
      pinMode(pin_Out_S2, OUTPUT);
      pinMode(pin_Out_S3, OUTPUT);
    }
    
    //************LOOP**************
    void loop() {
      nextMUXpin();
      while (usbMIDI.read()) {
         // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
      }
    }
    
    
    //************MUX SECTION**************
    void nextMUXpin(){  
      if (muxUpdated>muxTimeMin) {  
        // update the ResponsiveAnalogRead object every loop
    
    // read PIN i of MUX 1
        analog1[i].update(); 
        // if the repsonsive value has change, print out 'changed'
        if(analog1[i].hasChanged()) {
          data1[i] = analog1[i].getValue()>>3;
          if (data1[i] != data1Lag[i]){
            data1Lag[i] = data1[i];
            usbMIDI.sendControlChange(CCID1[i], data1[i], channel);
          }
        }  
    
    
    // read PIN i of MUX 2
        analog2[i].update(); 
        // if the repsonsive value has change, print out 'changed'
        if(analog2[i].hasChanged()) {
          data2[i] = analog2[i].getValue()>>3;
          if (data2[i] != data2Lag[i]){
            data2Lag[i] = data2[i];
            usbMIDI.sendControlChange(CCID2[i], data2[i], channel);
          }
        }
    
        //reset timer
        muxUpdated = 0; 
        //increment index
        i++;
        if (i>15)   {i=0;}      
        // 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));
      }
    }
    I'll compile and troubleshoot it when I get a chance or you can have a go at it.

    If there were enough MUX I'd switch to a two-dimensional array system loop through each MUX instead of having separate data structures for each.

  6. #6
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    Thank you very much for the code. It did compile successfully the first time. Now on to the hardwire portion...

  7. #7
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    Quote Originally Posted by oddson View Post
    I'll compile and troubleshoot it when I get a chance or you can have a go at it...
    Nice it compiled first time... I almost always miss terminating semi-colons or getting the braces wrong on control structures.

    But there is still (potentially) troubleshooting if there is a logical error in the coding I've added.

    I don't have the gear to do full testing and even to do a basic functionality test I have to fake the one MUX breakout board I have to look like two and to ground all the MUX but the input or two with pots on them.

    So I'll wait to hear whether it works or not before wiring it up.

  8. #8
    Junior Member
    Join Date
    Feb 2018
    Posts
    3
    I've just taken an interest in building a midi controller as I need one with a whole bunch of pots, so this thread caught my eye. I have extremely limited coding experience, so this will be super helpful. Cheers!

    However, I've been wondering a couple of things. Please bear with my newb-ishness:

    1. What code would be added to utilize the remaining analog inputs on the board, to bring the total number of potentiometers up to 27? I've been comparing this code to other, simpler (non-multiplexer using) projects and they're so different, I have no idea how to even start to go about it.

    2. On that note, other examples of code I've looked at seem to divide the readings from the pots by 8, in order to make it within the range that midi uses. I can't see where that's happening in this code, which is also confusing me.

    Again, I obviously have no idea what I'm doing regarding the coding side of things, so any help getting this going would be very much appreciated. Already a huge time saver having the code for 16 pots handed to me, so I feel perhaps a bit greedy asking to boost it up to 27.

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    17,014
    Quote Originally Posted by flerpyderp View Post
    2. On that note, other examples of code I've looked at seem to divide the readings from the pots by 8, in order to make it within the range that midi uses. I can't see where that's happening in this code, which is also confusing me.
    It's done here:

    Code:
          data1[i] = analog1[i].getValue()>>3;
    The ">>3" means to shift the bits by 3 positions, which is a very efficient way to divide by 8.

  10. #10
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    Quote Originally Posted by flerpyderp View Post
    1. What code would be added to utilize the remaining analog inputs on the board, to bring the total number of potentiometers up to 27? I've been comparing this code to other, simpler (non-multiplexer using) projects and they're so different, I have no idea how to even start to go about it.
    Many simple controllers use separate variables for each control and have long, but fairly easy to follow conditional logic structures testing each to look for new events to trigger midi.

    Once you get a lot of controls arrays of data and a common logic test structure keep the code tight and easy to write and maintain. It also makes it simpler to add features like mux support. But you need to understand indexed loops and data arrays.

    Add in having to control the mux itself and the code starts to look quite different from a simple controller that tests each pin with separate statements.

    27 pots could be done with two 16 channel using the code I posted above and just leave a few mux pins grounded so they don't generate noise signals. You could also use 1 mux and a bunch of pins directly.... but mixing mux'd and direct pins would need two different data structures or some clever way to merge them together. I could cludge together the non-clever version pretty quickly if you are in tested (but I can't test it as I'm away from my Teensy desk).

  11. #11
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    Oh... but if you're noob to this then please start with a couple simpler projects first before getting adding MUX into the mix.

    Your comments hint you have a clue about what's going on but it's very common for people to be too ambitious in their first midi project.

  12. #12
    Junior Member
    Join Date
    Feb 2018
    Posts
    3
    Quote Originally Posted by PaulStoffregen View Post
    It's done here:

    Code:
          data1[i] = analog1[i].getValue()>>3;
    The ">>3" means to shift the bits by 3 positions, which is a very efficient way to divide by 8.
    Thanks, wouldn't have figured this out just from scouring through the code repeatedly.

    oddson:

    Thanks for the information, and offer of a solution. I'm certainly interested.

    Haha, yep I'm aware of over ambition when it comes to new projects. This will in fact be my second midi controller after I finish my current project, for which I used a guide with provided code. I just altered it slightly to add some more buttons, but it was much easier for me to understand than the code found here. When I saw this thread I took interest because code was provided, and I could focus on the physical aspect of the build. Then I released how practical this controller would be for me with additional pots. And then, "how cool would it be if all the pots have LED lights activated when turned". At that point I have to say no, take a step back for now.

  13. #13
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    I'm happy to provide ready-to-run sketches for stock features.

    I have a standard structure that allows for easy extension of features
    https://forum.pjrc.com/threads/45376...ts-and-Buttons

    It's not as simple as some because it relies on arrays, loops and index variables but these give it flexibility.

    And the code in this thread, while still untested, should support 32 mux channels.

    But I'm not a free development service so if you want fancy features you'll have to learn a bit of coding.

    But also don't underestimate the physical build and getting your wiring correct. Especially with mux and all those wires.

  14. #14
    Junior Member
    Join Date
    Feb 2018
    Posts
    3
    I'm somewhat familiar with arrays and loops from doing a bit of learning (relatively simple) game development, but looking at code I didn't write personally can be a bit of a headspin still. I'll save that code from the thread and have a proper look through it until I understand it better.

    Yeah, the build is likely going to have some problems, which is why I'm starting with the smaller project (much less controls/wires) before the one in this thread. Have some soldering experience from building a guitar recently, but haven't soldered wires to a board before. Have watched plenty of youtube vids, but it looks like a delicate process that will take practice.

    Thanks again.

  15. #15
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    So, another question. Can you provide additional code that would incorporate three sixteen channel mux - two for analog )pot) input and one for digital(pushbutton) input? Thanks.

  16. #16
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    I believe the additions are something like these:

    Code:
    #include <Bounce.h>  // need the bounce library to stabilize signals from switch contacts
    
    ....
    
    const int pin_In_Mux3 = 4; // data pin of 'digital' MUX
    
    
    ...
    
    const BOUNCE_TIME = 15; // debounce time -- higher is more stable, lower more responsive
    
    ...
    
    const int note[MUX_PINS] = {48,49,50,54,52,53,54,55,56,57,58,59}; // note values for MUX3 (digital section)
    
    ...
    
    // initialize the bounce objects all to the same MUX data pin
    Bounce digital[] =   {
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME)
    }; 
    
    ...
    
    // read PIN i of MUX 3 (DIGITAL!)
      digital[i].update();
        if (digital1[i].fallingEdge()) {
          usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);  
        }
        // Note Off messages when each button is released
        if (digital1[i].risingEdge()) {
          usbMIDI.sendNoteOff(note[i], 0, channel);  
        }
      }
    I'll paste them into the sketch when I'm at the compiler next and see if it compiles but I'm not going to do much (if any) testing.

    (These are lifted almost 'as is' from my Pots and Buttons example code https://forum.pjrc.com/threads/45376...ts-and-Buttons)

  17. #17
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    You absolutely ROCK. Thank you very much. I should have this build done in a week or so and I will post here...

  18. #18
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    So the digital section is for noteOn / noteOff messages with on at contact and off at release. That's what you want?

  19. #19
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    Quote Originally Posted by oddson View Post
    So the digital section is for noteOn / noteOff messages with on at contact and off at release. That's what you want?
    Yes, Note On and Note Off.

  20. #20
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Vancouver Canada
    Posts
    856
    There were the usual number of missing semi-colon's and inconsistant variable names (digital or digital1 for the data array?) but it was pretty simple to get it to compile.

    Then I read through and reorganized the variables and constants a bit so hopefully you can figure out how to configure it.
    Code:
    //************LIBRARIES USED**************
    // include the ResponsiveAnalogRead library for analog smoothing
    #include <ResponsiveAnalogRead.h>
    #include <Bounce.h>  // need the bounce library to stabilize signals from switch contacts
    //usbMIDI.h library is added automatically when code is compiled as a MIDI device
    
    // ******CONSTANT VALUES******** 
    // customize code behaviour here!
    
    //**TIMING -- higher is more stable, lower more responsive
    const int muxTimeMin = 500; // minimum micro-seconds between MUX reads - 500 = half a millisecond!
    const int BOUNCE_TIME = 15; // max time before the physical contacts of switches settle. 
    
    //**HARDWARE
    const int MUX_PINS = 16; // number of MUX Channnels
    //mux control out pin assignement
    const int pin_Out_S0 = 0;
    const int pin_Out_S1 = 1;
    const int pin_Out_S2 = 2;
    const int pin_Out_S3 = 3;
    //data from mux pin assignement
    const int pin_In_Mux1 = A1;
    const int pin_In_Mux2 = A2;
    const int pin_In_Mux3 = 4; // data pin of 'digital' MUX
    
    //**MIDI 
    const int channel = 1; // MIDI channel
    const int CCID1[MUX_PINS] = {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36}; // CC (D1) values for analog MUX one
    const int CCID2[MUX_PINS] = {41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56}; // CC (D1) values for analog MUX two
    const int note[MUX_PINS] = {48,49,50,54,52,53,54,55,56,57,58,59}; // note values for MUX3 (digital section)
    const int ON_VELOCITY = 105; // note velocity for note on events
    
    
    //******VARIABLES***********
    
    // a data array and a lagged copy to tell when MIDI changes are required
    byte data1[MUX_PINS];
    byte data1Lag[MUX_PINS]; // when lag and new are not the same then update MIDI CC value
    
    //amd again for second MUX
    byte data2[MUX_PINS];
    byte data2Lag[MUX_PINS]; // ditto
    
    //third mux don't need memory!
    
    //NB - index variable i is incremented and rolled over manually inside the main loop so that on each pass one MUX pin is read.
    byte i=0; // global index for MUX channel reads 
    
    
    //****** TIMER VARIABLE *** change scale here!
    elapsedMicros muxUpdated; // switch to micros to run at speed and tune with muxTimeMin setting above
    //elapsedMillis muxUpdated; // switch to millis to troubleshoot 
    
    //************INITIALIZE LIBRARY OBJECTS**************
    // initialize the ReponsiveAnalogRead objects
    ResponsiveAnalogRead analog1[]{
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true},
      {pin_In_Mux1 ,true}
    }; 
    
    ResponsiveAnalogRead analog2[]{
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true},
      {pin_In_Mux2 ,true}
    }; 
    // initialize the bounce objects all to the same MUX data pin
    Bounce digital[] =   {
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME), 
      Bounce(pin_In_Mux3,BOUNCE_TIME)
    }; 
    
    //************SETUP**************
    void setup() {
      //! don't forget to set for output!
      pinMode(pin_Out_S0, OUTPUT);
      pinMode(pin_Out_S1, OUTPUT);
      pinMode(pin_Out_S2, OUTPUT);
      pinMode(pin_Out_S3, OUTPUT);
    }
    
    //************LOOP**************
    void loop() {
      nextMUXpin();
      while (usbMIDI.read()) {
         // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
      }
    }
    
    
    //************MUX SECTION**************
    void nextMUXpin(){  
      if (muxUpdated>muxTimeMin) {  
        // update the ResponsiveAnalogRead object every loop
    
    // read PIN i of MUX 1
        analog1[i].update(); 
        // if the repsonsive value has change, print out 'changed'
        if(analog1[i].hasChanged()) {
          data1[i] = analog1[i].getValue()>>3;
          if (data1[i] != data1Lag[i]){
            data1Lag[i] = data1[i];
            usbMIDI.sendControlChange(CCID1[i], data1[i], channel);
          }
        }  
    
    
    // read PIN i of MUX 2
        analog2[i].update(); 
        // if the repsonsive value has change, print out 'changed'
        if(analog2[i].hasChanged()) {
          data2[i] = analog2[i].getValue()>>3;
          if (data2[i] != data2Lag[i]){
            data2Lag[i] = data2[i];
            usbMIDI.sendControlChange(CCID2[i], data2[i], channel);
          }
        }
    
    // read PIN i of MUX 3 (DIGITAL!)
        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);  
          }
        }
        
        //reset timer
        muxUpdated = 0; 
        //increment index
        i++;
        if (i>15)   {i=0;}      
        // 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));
      }
    There is a very strong probability that errors remain. If we're lucky they are of the 'every-so-often' variety and not the 'nothing is working' type.

    I'm very limited in being able to test this so I make no promises should there be any crippling errors when you go to try it out; other than I'll do what I can with what time I can spare.

  21. #21
    Junior Member
    Join Date
    Feb 2018
    Posts
    8
    Everything compiled and thank you for creating both CC events and note on/note off. This will take me a couple of weeks to actually build. I will post the results because as a musician, this is a midi controller I would be very interested in building to control my DAW, i.e. Cantabile, Reaper, etc.

    Colin

  22. #22
    Administrator Paul's Avatar
    Join Date
    Oct 2012
    Posts
    283
    Any chance you could add a youtube video or at least a couple photos?

    We don't have a lot of requirements to show this on the home page & blog, but there must be at least a photo of the actual build.

Posting Permissions

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