Basic Multiplexing Question

I have a 3x4 14 pin keypad. As intended, key 1 closes the circuit between pin 6 and pin 1 (common). Key 2 closes pin 10 and pin 1...So on and so forth.

I can get it to work, but it takes up twelve pins on my Arduino. Is there any way I can convert it to a multiplex setup?

I've been trying things for a couple days, but I think I can't get it to work without being able to separate the common.

Hi, if that is how the keypad is wired internally, and you cannot alter that, i don’t see a way to multiplex it. To reduce the number of Arduino pins needed, you could use shift registers. You would daisy-chain two 8-bit shift registers to connect up the 14 pins on the keypad. This would only need 3 or maybe 4 Arduino pins.

I don’t understand why there are 14 pins. One per key and a common would be 13. So what is the 14th pin for?

Paul

Fourteen pins does not add up. How about a weblink to the site from which you obtained it, and/ or a perfectly focussed photograph?


Yes, you can multiplex it to four pins You will need about 20 diodes. You will never be able to distinguish two or more buttons simultaneously pressed.


(Not the best diagram - will look for a better one later!)

Thank you guys for the replies. The more I thought about it last night, the more I started to come to the conclusion that it wouldn't be possible without altering the internal wiring.

As far as the keypad, pin 2 is unused. I'm not sure why they made it that way. Here is the link:

The blue and red Arduino keypads were a little ugly in my opinion, which is why I opted for the one above. Last night, I ordered this one that appears to be multiplexed: http://www.amazon.com/gp/product/B00HGAK2FK

I just started exploring Arduinos. I'm a software engineer, so that part has been easy. The hardware side, while super fun, has been a bit more challending. For my last project, I 3d printed an automated rubber band sentry gun. That was a blast, but everything was pretty much laid out. This project has been much more from scratch. I think I will try wiring up that diagram just for fun now.

madkins:
The more I thought about it last night, the more I started to come to the conclusion that it wouldn’t be possible without altering the internal wiring.

No, it is possible, Paul__B just showed a way. Actually that is a slightly odd example, as Paul__B commented. Those extra diodes on the right the connection marked "GS1(GS2) and the logic gates shown are not needed I think.

Another way is to use a single analog input, a 4K7 and 11 x 1K resistors. You will then get a different reading from analogRead() for every button. Combinations of buttons pressed at the same time will give strange readings. Is that a problem?

You would connect the common pin to the Arduino analog input, and also have a 4K7 pulling it up to 5V. Button 12’s pin would connect to ground. The other 11 resistors go between the pins for buttons 1 to 12 in a string.

Drawing1.jpg

PaulRB:
No, it is possible, Paul__B just showed a way. Actually that is a slightly odd example, as Paul__B commented. Those extra diodes on the right the connection marked "GS1(GS2) and the logic gates shown are not needed I think.

Another way is to use a single analog input, a 4K7 and 11 x 1K resistors. You will then get a different reading from analogRead() for every button. Combinations of buttons pressed at the same time will give strange readings. Is that a problem?

You would connect the common pin to the Arduino analog input, and also have a 4K7 pulling it up to 5V. Button 12's pin would connect to ground. The other 11 resistors go between the pins for buttons 1 to 12 in a string.

I really like that solution. I wired it up while waiting for my other keypad to come in. Now, I just need to figure out how to determine if any button is pressed because the analog channel is always sending a number. At rest, it oscillates quite a bit.

madkins:
I really like that solution. I wired it up while waiting for my other keypad to come in. Now, I just need to figure out how to determine if any button is pressed because the analog channel is always sending a number. At rest, it oscillates quite a bit.

The analogRead() should give 1023 or thereabouts when no button is pressed. The reading will always vary a little. Make a list of the approximate range of values you get when each button is pressed. Then pick a value mid way between each reading and the next.

The readings should be roughly like this, if you have wired it up correctly:

Button Value
1 0
2 306
3 327
4 350
5 378
6 410
7 449
8 495
9 552
10 624
11 717
12 843
None 1023

PaulRB:
The analogRead() should give 1023 or thereabouts when no button is pressed. The reading will always vary a little. Make a list of the approximate range of values you get when each button is pressed. Then pick a value mid way between each reading and the next.

The readings should be roughly like this, if you have wired it up correctly:

When a button is pressed, the values are pretty steady (+/- 1). However, when no button is pressed, the values cycles from 0 to 800ish and back down. I'll try to put together a fritzing diagram of how I have it wired.
How crucial is the 4k7 resistor? I don't have one, so at first I wired up a pot to as close as I could get. Now, I'm using four 1k and two 330 resistors.

madkins:
When a button is pressed, the values are pretty steady (+/- 1). However, when no button is pressed, the values cycles from 0 to 800ish and back down.

Sounds like something is not correctly connected. It should be above 1,000.

madkins:
How crucial is the 4k7 resistor? I don't have one, so at first I wired up a pot to as close as I could get. Now, I'm using four 1k and two 330 resistors.

Its crucial to have a resistor between the analog input and 5V, to prevent the "floating" that you are seeing. Its exact value is not critical, but a value around half of the total of all the 1K resistors would be ideal. For the values I worked out above, I calculated them assuming a 4K7. Other resistor values will give different readings, but it should still work ok.

Your readings are strange. If the 4K7 pull-up resistor was not connected properly, it might lead to the floating values you are getting. But if that is the problem, then when you press any button, you would get a reading of around zero, regardless of which button you pressed.

Try connecting your pot's wiper to the same analog input, with the other connectors to 5V and 0V. Do you get a steady range of values between 0 and 1023 as you turn the pot?

PaulRB:
Sounds like something is not correctly connected. It should be above 1,000.

Its crucial to have a resistor between the analog input and 5V, to prevent the “floating” that you are seeing. Its exact value is not critical, but a value around half of the total of all the 1K resistors would be ideal. For the values I worked out above, I calculated them assuming a 4K7. Other resistor values will give different readings, but it should still work ok.

Your readings are strange. If the 4K7 pull-up resistor was not connected properly, it might lead to the floating values you are getting. But if that is the problem, then when you press any button, you would get a reading of around zero, regardless of which button you pressed.

Try connecting your pot’s wiper to the same analog input, with the other connectors to 5V and 0V. Do you get a steady range of values between 0 and 1023 as you turn the pot?

I’m sure I’m doing something wrong and I REALLY appreciate you taking the time to help someone so new to this.
As far as the 800ish: I was not planning on using the pound character on the keypad, so I omitted it. I wired it in just now and I’m now seeing it oscillating from 0 to 1023.
I’ve attached my first ever Fritzing diagram which I presume will be followed by, “Oh, you shouldn’t have done it that way, dummy!” :slight_smile:

Edit: To answer your question: Yes, I just wired up the pot like you mentioned and it gives good, steady values as I turn it.

Here is my code, just in case:

void setup() {
  Serial.begin(9600);
}

void loop() {  
  Serial.println(analogRead(A0));
  delay(100);
}

madkins:
I've attached my first ever Fritzing diagram which I presume will be followed by, "Oh, you shouldn't have done it that way, dummy!" :slight_smile:

Ah, yes, that explains the readings you are seeing! When no buttons are pressed, A0 is not connected to either 5V or 0V, via resistors or otherwise. So it is "floating".

Take another look at my diagram and try again. You only need to move one resistor and a couple of wires.

PaulRB:
Ah, yes, that explains the readings you are seeing! When no buttons are pressed, A0 is not connected to either 5V or 0V, via resistors or otherwise. So it is “floating”.

Take another look at my diagram and try again. You only need to move one resistor and a couple of wires.

Thanks for making me work for it. Once I realized the entire right hand side of the diagram was isolated from ground unless a button is pushed, it made more sense. I reworked a couple of things and it is working perfectly now. I’ll attach my updated diagram.

madkins:
I'll attach my updated diagram.

Well done. I guess you won't need help with the sketch now, being a s/w person.

Oh, and get some more resistors. I find that 220/330R, 1K, 4K7 and 10K are the ones I use by far the most frequently, so don't bother with those "lucky bags" that contain loads of weird values that nobody else wants, you don't want them either!

I think I have the software side covered, thanks! I’ll post my code just in case it helps someone else. You’ll notice my values are inverse from yours…probably my wiring.

Thanks for the resistor tip. I have plenty of 330, 1k, and 10k with the kits I bought. I’ll pick up some 220 and 4k7 soon.

void chkKeypad() {  
  intKey = analogRead(KeyPin);  //Read analog pin
  if (intKey < 1020) {          //Is any key pressed?
    if (blnReady) {             //Has the keypad been at rest since the last key press?
      blnReady = false;         //Set ready state
      switch(intKey) {          //Determine key pressed based on analog value
        case 705 ... 750:        
        processNumber("1");
        break;
        
        case 683 ... 704:
        processNumber("2");
        break;

        case 658 ... 681:
        processNumber("3");
        break;
        
        case 627 ... 657:
        processNumber("4");
        break;

        case 592 ... 626:
        processNumber("5");
        break;

        case 549 ... 591:
        processNumber("6");
        break;

        case 497 ... 548:
        processNumber("7");
        break;

        case 433 ... 496:
        processNumber("8");
        break;

        case 351 ... 432:
        processNumber("9");
        break;

        case 242 ... 350:
        processNumber("*");
        break;

        case 90 ... 241:
        processNumber("0");
        break;        

        case 0 ... 89:
        processNumber("#");
        break;
      }
    }
  }
  else {
    blnReady = true;
  }       
}

If you have 330R don't bother buying 220R. Either are useful as current limiting resistors for leds, transistor bases etc.

Regarding your code, you will probably need to change it to process the keypress once and only once each time a key is pressed. Right now, you will get a continuous stream of "3" messages to be processed as long as the "3" key is held down. So you will need to spot when the analog value changes from 1023 to the value for "3", process the keypress once, then ignore the value until it returns to 1023.

This may then reveal the problem of "bouncing" where the mechanical switch contacts, which are spring-loaded, change several times before settling. If this happens, your code may still register two or more "3" presses where only one was intended. To "debounce" the switches, you need to introduce a short "dead-time" after a press is detected, during which any further changes are ignored. Use the millis() function for this. A 20ms dead-time will probably be enough.

PaulRB:
If you have 330R don't bother buying 220R. Either are useful as current limiting resistors for leds, transistor bases etc.

Regarding your code, you will probably need to change it to process the keypress once and only once each time a key is pressed. Right now, you will get a continuous stream of "3" messages to be processed as long as the "3" key is held down. So you will need to spot when the analog value changes from 1023 to the value for "3", process the keypress once, then ignore the value until it returns to 1023.

This may then reveal the problem of "bouncing" where the mechanical switch contacts, which are spring-loaded, change several times before settling. If this happens, your code may still register two or more "3" presses where only one was intended. To "debounce" the switches, you need to introduce a short "dead-time" after a press is detected, during which any further changes are ignored. Use the millis() function for this. A 20ms dead-time will probably be enough.

That is what the blnReady variable handles. If the analog value is greater than or equal to 1020, then no key is pressed and I set blnReady to true. When a key is pressed (A0 less than 1020), I set blnReady to false and process the number. No other number can be processed until the key is released and blnReady gets set to true again.

madkins:
That is what the blnReady variable handles.

Ah, yes, sorry I missed what that was doing. To de-bounce, you could do something like this:

void chkKeypad() {  
  intKey = analogRead(KeyPin);  //Read analog pin
  if (intKey < 1020) {          //Is any key pressed?
    if (blnReady) {             //Has the keypad been at rest since the last key press?
      blnReady = false;         //Set ready state
      ulBusy = millis();         //Record time of keypress
      switch(intKey) {          //Determine key pressed based on analog value
        case 705 ... 750:        
        processNumber("1");
        break;
        
        case 683 ... 704:
        processNumber("2");
        break;

        case 658 ... 681:
        processNumber("3");
        break;
        
        case 627 ... 657:
        processNumber("4");
        break;

        case 592 ... 626:
        processNumber("5");
        break;

        case 549 ... 591:
        processNumber("6");
        break;

        case 497 ... 548:
        processNumber("7");
        break;

        case 433 ... 496:
        processNumber("8");
        break;

        case 351 ... 432:
        processNumber("9");
        break;

        case 242 ... 350:
        processNumber("*");
        break;

        case 90 ... 241:
        processNumber("0");
        break;        

        case 0 ... 89:
        processNumber("#");
        break;
      }
    }
  }
  else if (millis() - ulBusy > 20) { // Debounce keypress
    blnReady = true;
  }       
}

PaulRB:
Ah, yes, sorry I missed what that was doing. To de-bounce, you could do something like this:

Very cool. I hadn’t noticed any bounce because I still had a delay(50) in my main loop from testing. I will switch to that because I read you shouldn’t really use delay in a production app.

Prototyping went well, so I’m starting to wire up the final version. I’ll attach a picture. Now I just have to wire up my LCD screen, servo, and design and 3d print the housing for everything.

Once again, thanks for the help. I just wanted to post a quick video of the (near) final result: - YouTube