Problems with buttons through multiplexer to shiftregister {updated}

Hi!

I'm making a midi controller and got some problems. The multiplexed potentiometers that I'm using with multiplexers are working great, now I'm trying to connect 16 buttons through kind of the same code but with some edits. The hardware is ok I think, so if anyone could check my code for errors I would be happy! It's compiling but I'm pretty new to this so I probably have some errors anyway..

First of all I need to get the multiplexer working for the buttons. Right now with this shortened code I get no messages at all from the device. If I change the code a bit I can get some midi messages, but it's some kind of a loop that is not correct and the buttons doesn't change anything if I press them. So it's probably something more basic.

When the multiplexer is reading the buttons I will go further to send signals to corresponding leds. But that's for the next step.

I'm using a Teensy LC for this project.

All help is much appreciated!

Diagram/schematic for the hardware here!

// Teensy Monster V1
// teensymonster.cc
// Code v1.0
// last updated April, 2014
// license __ http://opensource.org/licenses/MIT
// Edit: Imbecillen 2018






// ======================================================================================
// SETTINGS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//CHANNEL_____________________________________________
int channelNumber = 1; //each controller should have a unique channel number between 1 and 10



//DEBOUNCE BUTTONS___________________________________
//debounce is a measurement of the time in which a pushbutton is unresponsive after it is pressed
//this is important to prevent unwanted double clicks
uint32_t pbBounce = 150; //150 millisecond debounce duration - you may want to change this value depending on the mechanics of your pushbuttons



//MULTIPLEXER READING_________________________________
//CD4067BE - http://www.ti.com/lit/ds/symlink/cd4067b.pdf
//'1' for multiplexer I/O you want to send signal, else enter '0'

//MULTIPLEXER 5, DIGITAL FOR PUSHBUTTONS______________
int toReadDigitalMux5[16] = {
  //IC pin number are written below
  1, 1, 1, 1, //0-3
  1, 1, 1, 1, //4-7
  1, 1, 1, 1, //8-11
  1, 1, 1, 1 //12-15
};



//PUSHBUTTON MODE______________________________________
//there are a few different modes in which you may wish for your pushbutton to behave
//'1' - standard mode, when pushbutton is engaged note is turned on, when pushbutton is released, note is turned off
//'2' - on mode, note is only turned on with each click
//'3' - off mode, note is only turned off with each click
//'4' - toggle mode, note is switched between on and off with each click
int pushbuttonMode[32] = {
  //Pin number are written below
  4, 4, 4, 4, //0-3
  4, 4, 4, 4, //4-7
  4, 4, 4, 4, //8-11
  4, 4, 4, 4 //12-15 (NOTE: comma after last row/number for shift function to continue down under)

};






// ======================================================================================
// VARIABLES AND FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


//MULTIPLEXER CONTROL SIGNAL____________________________
int s0 = 1; //control pin A
int s1 = 2; //control pin B
int s2 = 3; //control pin C
int s3 = 4; //control pin D



//MULTUPLEXER INHIBITOR PIN_____________________________
int INH_pin = 9; 



//MULTIPLEXER NR 5 _____________________________________
//DIGITAL IN, SIGNAL PIN FOR BUTTONS
//(16 buttons in total)
int SIG_pin5 = 6; //digital mux read pin for muxButtons



//MULTIPLEXER NR 5, DIGITAL IN FOR PUSH BUTTONS_________
long timeHit[16]; //16*2 = 32 for shift button code
boolean buttonState[16]; //array with stored state: if the button was last turned on or off
boolean tempDigitalRead5 = 0;//array to hold previously read values
int controlPin5[] = {s0, s1, s2, s3}; //set contol pins in array
//control array
int muxChannel5[16][4] = {{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 1, 0}, 
{1, 0, 1, 0}, {0, 1, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {1, 1, 0, 1}, 
{0, 0, 1, 1}, {1, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}};
//function to read mux
int readMux5(int channel5) {
  //loop through the four control pins
  for (int i = 0; i < 4; i ++) {
    //turn on/off the appropriate control pins according to what channel we are trying to read
    digitalWrite(controlPin5[i], muxChannel5[channel5][i]);
  }
 delay(20);
  //read the value of the pin
  int val = digitalRead(SIG_pin5);
  //return the value
  return val;
}






// ======================================================================================
// SETUP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void setup() {

//AREF_______________________________________________
  // Voltage reference for analog input, MUST BE BEFORE analogRead! 
  analogReference(EXTERNAL);

  
//MULTIPLEXER SETUP _________________________________
  //set multiplexers reading pin
  pinMode(SIG_pin5, INPUT_PULLUP); 

  //set inhibit pin to output and turn off
  pinMode(INH_pin, OUTPUT);
  digitalWrite(INH_pin, LOW); 
  
  //set our control pins to output
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  pinMode(s3, OUTPUT);
  
  //turn all control pins off (for now)
  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

} // SETUP END






// ======================================================================================
// LOOP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void loop() {
  


//MULTIPLEXER 5
//DIGITAL IN FOR BUTTONS ________________________________
  for (int i = 0; i < 16; i++) { //loop through 16 mux channels
      if (toReadDigitalMux5[i] == 1) { //we read the choosen mux channel input
        tempDigitalRead5 = readMux5(i);
        if (pushbuttonMode[i] == 1 && tempDigitalRead5 != buttonState[i]) { //___NORMAL MODE (1)
          delay(20); //just a delay for noise to ensure push button was actually hit
          if (readMux5(i) == tempDigitalRead5) { //check if pushbutton is still the same
            if (tempDigitalRead5 == LOW) { //button pressed, turn note on
              midiSend('p', 1, i); //call note on/off function
            } else { //button released
              midiSend('p', 0, i);
            }
            buttonState[i] = tempDigitalRead5; //update the state (on or off)
          }
        } else { //___ALL OTHER MODES (2,3,4)
          if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce) { //check bounce time
            if (pushbuttonMode[i] == 2) { //mode 2 - only note on
              midiSend('p', 1, i);
            } else if (pushbuttonMode[i] == 3) { //mode 3 - only note off
              midiSend('p', 0, i);
            } else { //mode 4 - toggle
              if (buttonState[i] == 1) { //on->off
                midiSend('p', 0, i);
                buttonState[i] = 0;
              } else { //off->on
                midiSend('p', 1, i);
                buttonState[i] = 1;
              }
            }
            timeHit[i] = millis();
          }
        }
      }
    }

    
    
} // LOOP END



// ======================================================================================
// COMMUNICATION FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// http://www.pjrc.com/teensy/td_midi.html

//FUNCTIONS TO SEND MIDI______________________________ 
void midiSend(char type, int val, int pin) {
  switch (type) {

    case 'p': //--------------- PUSHBUTTON
        if (val == 1) {
          usbMIDI.sendNoteOn(pin, 127, channelNumber); //!!!
        } else {
          usbMIDI.sendNoteOff(pin, 127, channelNumber); //!!!
        }
      break;
    }
  }

Imbecillen:
I'm making a midi controller and got some problems. The multiplexed potentiometers are working great, now I'm trying to connect 16 buttons through a mux but something is wrong I guess. The hardware is ok I think,

You may induce folks to respond by posting an as-built, non-fritzing schematic and your sketch enclosed in code tags - the < /> icon above the smilies.

dougp:
You may induce folks to respond by posting an as-built, non-fritzing schematic and your sketch enclosed in code tags - the < /> icon above the smilies.

Thanks! The code did reach maximum of letters in the post, therefore my github link. I will work on a better presentation, stay tuned!

The problem is solved for now btw, but I will keep the thread open because there will be more questions soon...

The usual way is to distill the code down to just enough to exhibit the problem.

dougp:
The usual way is to distill the code down to just enough to exhibit the problem.

Now I got both a schematic and a distilled code in the thread start. Hope someone can find any errors! :slight_smile:

There are two problems with this line, one is syntactical, one is logical - as in 'your thinking process' :

   if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce) { //check bounce time

Read through it very carefully and see if you can discover what's wrong. It may be illustrative to add some Serial.print statements to display variables/conditions as the program runs.

dougp:
There are two problems with this line, one is syntactical, one is logical - as in 'your thinking process' :

   if (readMux5(i) == LOW && (millis() - timeHit[i]) > pbBounce) { //check bounce time

Read through it very carefully and see if you can discover what's wrong. It may be illustrative to add some Serial.print statements to display variables/conditions as the program runs.

I found a couple of things that may be wrong but I also need to think a bit further on the actual problem. Can one be that the timeHit assigns millis() as an operator, and then writes it wrong on the same line (the one you posted)? The timeHit = millis(); feels weird in a sense. It can't be correct?
One may be that bpBounce uses uint32_t and compares it with the long from the timeHit. It should be unsigned long right?
I don't know if one or the other is syntactical or logical, but I can't seem to find any syntax errors due to missing signs or misspelling. So the big question, am I right in any way?
Thanks a lot for your answer, keeping my head working but giving a hint about the problem!

Imbecillen:
I don't know if one or the other is syntactical or logical, but I can't seem to find any syntax errors due to missing signs or misspelling. So the big question, am I right in any way?

Apologies, my mistake. Brain f**t. Upon a closer, more intent read there do *not * appear to be any errors in that line. I'll be excusing myself from this thread.

dougp:
Apologies, my mistake. Brain f**t. Upon a closer, more intent read there do *not * appear to be any errors in that line. I'll be excusing myself from this thread.

Actually, there was no problem as you said. I changed the timeHit variable to unsigned long and then uploaded the sketch. But my problem was probably in the hardware anyway, I had missed a ground wire on the breadboard, haha! :smiley: The buttons work great now. Next step is the leds, I will take a look at it and come back if necessary..

Thanks for your support anyway!

I've finally solved the LEDs correspond to the right button presses, thanks to you guys on this forum!

Now I've got two issues left.

  1. My leds are flickering when they are off. Some current go through and they flicker very subtle but noticeable. What can this be about? Is it something with the reading speed of the multiplexer or shift register maybe? Or is it some hardware related issue?

  2. The first number in every new row in my toggleState array sends multiple messages and got some weird things going on. Button 1, 5, 9 and 13. I don't think it's floating and due to the fact that it's every forth button I guess it's something with the code and reading of the multiplexer (probably the controlPin reading and for loop). Anyone got any solution? I got another sketch working great without double sending, so its something going on here.

In fact I got some of the issue to disappear when adding the delayMicroseconds (20) in the for loop of digitalReadMux1. The problem still occur on button 1 and 9 though. When pressing button one, 9 is also sending message, and vice versa. I've tried to change the delayMicroseconds amount but no difference.

Can I do anything else?

Whole sketch on Github
The important parts:

//DIGITAL MULTIPLEXER 1, FOR PUSHBUTTONS______________
int toReadDigitalMux1[16] = {
  //IC pin number are written below
  1, 1, 1, 1, //0-3      button 1-4
  1, 1, 1, 1, //4-7      button 5-8
  1, 1, 1, 1, //8-11     button 9-12
  1, 1, 1, 1 //12-15     button 13-16
};

//PUSHBUTTON MODE______________________________________
//there are a few different modes in which you may wish for your pushbutton to behave
//'1' - standard mode, when pushbutton is engaged note is turned on, when pushbutton is released, note is turned off
//'2' - on mode, note is only turned on with each click
//'3' - off mode, note is only turned off with each click
//'4' - toggle mode, note is switched between on and off with each click
int pushbuttonMode1[16] = {
  //Pin number are written below
  4, 4, 4, 4, //0-3
  4, 4, 4, 4, //4-7
  4, 4, 4, 4, //8-11
  4, 4, 4, 4 //12-15 (NOTE: comma after last row/number for shift function to continue down under)
};



//DIGITAL IN MULTIPLEXER 1________________________________
unsigned long timeHit[16];
boolean buttonState[numOfBytes * 8]; //array with stored state
boolean toggleState[numOfBytes * 8]; //array with stored state
boolean tempDigitalRead1 = 0;//array to hold previously read values
int digitalControlPin1[] = {s0, s1, s2, s3}; //set contol pins in array
//control array
int digitalMuxChannel1[16][4] = {{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 1, 0},
  {1, 0, 1, 0}, {0, 1, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {1, 1, 0, 1},
  {0, 0, 1, 1}, {1, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}
};
//function to read mux
int digitalReadMux1(int digitalChannel1) {
  //loop through the four control pins
  for (int i = 0; i < 4; i ++) {
    delayMicroseconds (20); // solved half of the problem with double messages sent
    //turn on/off the appropriate control pins according to what channel we are trying to read
    digitalWrite(digitalControlPin1[i], digitalMuxChannel1[digitalChannel1][i]);
  }
  //read the value of the pin
  int val = digitalRead(digitalSignalMux1);
  //return the value
  return val;
}




void loop() {

//DIGITAL MULTIPLEXER 1 ________________________________
  for (int i = 0; i < 16; i++)   //loop through 16 mux channels
  {
    if (toReadDigitalMux1[i] == 1)   //we read the choosen mux channel input
    {
      tempDigitalRead1 = digitalReadMux1(i);


      // Look for a state change and debounce
      if (tempDigitalRead1 != buttonState[i]  && millis() - timeHit[i] > 20UL)
      {
        buttonState[i] = tempDigitalRead1; //update the state (LOW=PRESSED, HIGH=RELEASED)
        timeHit[i] = millis();


        if (pushbuttonMode1[i] == 1)   //___NORMAL MODE (1)
        {
          midiSend('p', tempDigitalRead1 == LOW, i); // note on/off when button is PRESSED/RELEASED
        }
        else if (pushbuttonMode1[i] == 2)   //mode 2 - only note on
        {
          if (tempDigitalRead1 == LOW)  // Send note on when button is PRESSED (Active LOW)
            midiSend('p', 1, i);
        }
        else if (pushbuttonMode1[i] == 3)     //mode 3 - only note off
        {
          if (tempDigitalRead1 == LOW)  // Send note off when button is PRESSED (Active LOW)
            midiSend('p', 0, i);
        }
        else     //mode 4 - toggle
        {
          if (tempDigitalRead1 == HIGH)   // Toggle on button RELEASE  (Active LOW)
          {
            toggleState[i] = !toggleState[i];
            midiSend('p', toggleState[i], i);
          }
        }
      }
    }
  }



  // //DIGITAL OUT SHIFT REGISTER FOR LEDS_______________________
  for (byte i = numOfBytes; i > 0; i--) { //get bytes from array
    for (byte j = 8; j > 0; j--) {    //send information
      if (pushbuttonMode1[i] == 1) {
        digitalWrite(dataPin, buttonState[((i - 1) * 8) + (j - 1)]);      // mode 1
        digitalWrite(clockPin, HIGH);  // clock the output into the 595
        digitalWrite(clockPin, LOW);
      } else {
        digitalWrite(dataPin, toggleState[((i - 1) * 8) + (j - 1)]);      // mode 4
        digitalWrite(clockPin, HIGH);  // clock the output into the 595
        digitalWrite(clockPin, LOW);
      }
      digitalWrite(latchPin, 1);
      digitalWrite(latchPin, 0);
    }
  }
} // Loop end

Again real schematic please. The link in post #1 goes to the git hub page but it says there is an error. Please post all images here on this site, read the how to use this forum sticky post for how.

Grumpy_Mike:
Again real schematic please. The link in post #1 goes to the git hub page but it says there is an error. Please post all images here on this site, read the how to use this forum sticky post for how.

Sorry! It works for me, but here's a new schematic anyway that should be up to date as well! I also attached the whole sketch with the post so it gets right.

I promise to follow the rules from now on :wink:

slowtech_2.3.1.ino (14.3 KB)

Slowtech_2.3.pdf (259 KB)

OK I am on a mobile device so it will not read .ino files so I can’t look at the code.
However from that schematic I notice:-

You have no pull up resistor on the digital multiplex pin, the internal pull up resistor is not strong enough I would use a 4K7 pull up. If this does not solve the problem with the buttons then put a pull up on each individual input to the multiplexer as well, sometimes they don’t like the inputs floating.

You also have no pull up resistors on the I2C line. I would check the voltage on that LCD module’s lines, with no other connections, and see if it is being pulled up to 5V. If it is that means there is some sort of pull up already and so you need a bi-directional level shifter on both lines.

Grumpy_Mike:
OK I am on a mobile device so it will not read .ino files so I can’t look at the code.
However from that schematic I notice:-

You have no pull up resistor on the digital multiplex pin, the internal pull up resistor is not strong enough I would use a 4K7 pull up. If this does not solve the problem with the buttons then put a pull up on each individual input to the multiplexer as well, sometimes they don’t like the inputs floating.

You also have no pull up resistors on the I2C line. I would check the voltage on that LCD module’s lines, with no other connections, and see if it is being pulled up to 5V. If it is that means there is some sort of pull up already and so you need a bi-directional level shifter on both lines.

I've discussed the thing with pullup on another forum and I was advised that I should be able to use the internal_pullup on the digital multiplexer signalpin. But as you say, this may not be enough? I tried once before with a 220ohm resistor but no difference, will try with a 4k7! Good with some options about what to do, I will experiment!

Regarding the display. I measured now and the GND to VCC is 3.33v, the GND to SCL is 2.2v with some flux. Should I try to pull it up to 5v or am I ok as it is? It works great so it should be no problem. Due to the datasheet it will handle 3-5v. Or is it possible it's affecting the buttons as well?

Should I try to pull it up to 5v

No it needs pulling up to 3V3.

Due to the datasheet it will handle 3-5v.

But the Teensy won't.

I tried once before with a 220ohm resistor but no difference

That should have done it. I would add some decoupling capacitors, 0.1uF ceramic, across the power and ground of each multiplexer and then try the pull ups on the input.

Grumpy_Mike:
No it needs pulling up to 3V3.
But the Teensy won't.
That should have done it. I would add some decoupling capacitors, 0.1uF ceramic, across the power and ground of each multiplexer and then try the pull ups on the input.

Aaah, now I understand. I also read a guide about I2C and got it. Both SDA och SCL are 0v without pullup, so I set a 4k7 resistor on each output to pull it up to 3V3. Is 100mA enough, or should I go for the other 3V3 pin without limit? Most of the current is on the VCC right, so 100mA is ok to just pull the SDA/SCL up?

Also, should I go for 3V3 or 5V on the VCC, maybe doesn't matter? My current measure is 3.3v because I've soldered it wrong, I noticed now. :slight_smile: It's supposed be 5v due to the schematic, but it seem to work with 3V3 as well.

I will try the capacitors, thanks a lot!

Is 100mA enough,

More than enough.

Normally with 3V3 I2C I would use 3K3 pull up, some would even go for 1K8. But for short distances you are fine with 4K7.

If it is happily running on 3V3 then stick to that but correct the diagram.