-1

The background:


I bought a "dumb" 8-pin 4x4 keypad for use in a project. (This one: https://www.sparkfun.com/products/14881 It has a wire connected to each row, and a wire connected to each column. When you press a key, it connects a row pin and a column pin. You have to use GPIO outputs to send +5V down one row/column at a time, and then use inputs to look for a HIGH on a corresponding set of pins from the other dimension, which indicates a connection between a specific row/column pair.)

I found a framework, MultitapKeypad, that at first appeared perfect. It is a nice clean C++ object with a simple main method, getKey(), that returns a struct with information about the key the user pressed. It detects long presses and repeat presses, things I plan on using.

However, when I looked into it I found things were not as good on the inside. The getKey method uses a while (no key pressed) loop that loops forever. The author's solution to not locking up the Arduino any time you are waiting for a keypress is to provide a callback function that the library calls from the while loop.

The idea of turning a sketch's control flow completely upside down and having to do all your app's event handling from the callback of a keypad class just seems wrong. Then control flow from the app's main loop has to go through the keypad's callback when I'm waiting for keys, or through some other flow otherwise.

The framework also makes use of delay() in it's debouncing, another no-no for apps that manage multiple things at once.

I was therefore thinking of writing my own keypad handler.

My current project, a power controller for lights, chargers, and a humidity controlled warm air dryer will run all of those things simultaneously. It has a back-lit LCD for a display, and uses a motion sensor and photoresistor to turn lights on when it detects motion in low light.

It puts the LCD to sleep if it doesn't detect motion or any user input from it's various switches for an extended period.

When I integrate a keypad into the project I would also like to add keypresses to the list of things that keeps the display awake. I don't see an easy way to do that with the LCD library I was using.


My proposed design:

I was thinking of wiring the keypad with all of the row pins connected to a 9th pin with diodes, anodes pointed towards the 9th pin. I'd set that 9th pin up in INPUT_PULLUP mode and attach it to an interrupt, set to trigger on FALLING signal (or perhaps CHANGE. See below.) The row pins would all also be configured as INPUT_PULLUP. The column pins would all be set up as outputs, and driven LOW.

When the user pressed a key, it would connect one of the column pins to a row pin. That would pull that row pin's input LOW, and also pull the interrupt pin LOW, triggering the ISR (Interrupt service routine).

In the ISR I would use port registers to switch all the column pins to HIGH, and then toggle one column pin at a time back to LOW, looking for a row pin to also drop LOW. If one and only one column going LOW triggers one and only one row to go LOW, I know only one button is pressed, and know the row/column combo, and hence the key that was pressed. If multiple columns trigger a row, or more than one row drops to LOW, I know more than one key is pressed. (I could use that info to report multi-key presses if I need to, although >2 keys at a time gets messy fast.)

Once I have a key I could add the key code for that key to a simple ring buffer and set a "key ready" flag. (My ring buffer might contain structs with more than just a key code, like up/down events, time stamps, etc.)

The rest of my sketch could then query my keypad for "key ready" and read one or more keys as needed. I'd provide housekeeping functions for clearing the key buffer, a way to detect buffer overflows, etc.

I guess I'd actually want interrupts on both falling and rising state change, so I could detect key up events and track the duration of key presses.

I'd also need to add debouncing of some sort or another. I could use a simple RC filter on each input pin, but I don't have a lot of room left on my project breadboard and that would require 4 capacitors and 8 resistors.

From what I've read the millis() function is a little slow and best avoided in ISR routines. Is there a fast alternative to get the current clock value directly in an ISR? If so I could use software to debounce the keys.

Are there any obvious flaws in my plan? Does using an ISR like this sound reasonable? The Keypad actually doesn't short the row/column pins together. It uses variable resistances between the row/column combinations, I guess so that you can use a voltage divider and an analog input to figure out which key was pressed based on the measured voltage. The docs for the keypad say the resistance for a row-column pair varies from 10 to 625 ohms. I'm assuming that the key resistance is low enough that even with signal diodes in the circuit, the closed switch resistance should be enough to pull my interrupt pin and my row pins to LOW.

I suppose I could also tie all the columns and all the rows together, hook it to a voltage divider, and see if I could decode the key using an analog input. That would let me do all that decoding with a single pin.

Alternatively, are there other keypad frameworks that handle 3x4 and 4x4 keypads and offer things like repeated taps, long taps, and allow you to trigger events on ANY key press (So I could keep my LCD display awake.)

  • This is a proposed combined hardware/software solution. I haven’t built or written anything yet, so I don’t have any code to post. I’m hoping somebody with more experience than me can me about the relative merits of what I’m proposing. – Duncan C Mar 6 '19 at 2:35
  • Where did you read that the millis function is slow? It is very fast. What is wrong with the rest of your sketch? Does the loop function run 10 times of more per second? then you don't need interrupts. You could use a timer that generates an interrupt (maybe 20 times per second or more) and scan the whole keyboard in the interrupt. That makes it also easier to debounce. Perhaps it is possible to use a transistor that detect a press in the keyboard instead of so many diodes. Have you build a ringbuffer before? I think that ringbuffer code is in the arduino libraries, but I'm not sure. – Jot Mar 6 '19 at 6:56
  • I’m using 2 DHT22 temp/humidity sensors. The library for reading them stalls the event loop for ≈250ms per read, and I read both every 2 seconds, so the event loop is stalled 1/4 of the time, for 1/4 second each time. – Duncan C Mar 6 '19 at 11:28
  • 1
    Sounds like X-Y problem. You want to read a keypad with quick response to user input, but you have a blocking function, which prevents your code from running, forcing you to rely on interrupts or timers unnecessarily. Internet search for "DHT22 arduino non blocking" shows that there are other solutions that eliminate the block when reading the sensors. – jose can u c Mar 6 '19 at 15:54
  • 1
    I think the magic keywords are "non blocking" and/or "timer". eevblog.com/forum/beginners/4x4-keypad-arduino-non-blocking/… Here's a forum post that has example code for a keypad function similar to your structure, except that this person sets up Timer1 on the Arduino to fire on a regular basis, calling an ISR that scans the keys. It doesn't require an external interrupt or a pin change interrupt, only a timer interrupt. It scans fast enough for human interfacing (claim of 160uS to scan keypad?) – jose can u c Mar 6 '19 at 16:03
-1

enter image description hereI made a simple RF remote control using a very similar method with a 4x4 kepyad, with diodes connected together to trigger an interrupt and read a keypress. After any keypress, the interrupt woke the processor (unmodified 8 MHz Promini with Atmega328P), it used keypad.h library to detect the pressed key, sent that out via a short RF burst (433 MHz transmitter) using the library (8 years ago now, drawing a blank on the name) that became the Radiohead library (find it on github), then went back to sleep. Lasts about a month on 2000mAH (or so?) LiPo battery that is secured to the bottom of the board.

As I recall, I had to write the outputs opposite of their normal state before going to sleep mode for the interrupt to work correctly, then restore them before reading the keypad.h value. Then put them back in opposite state before going back into sleep mode. Works well, but I only transmit one key at a time. I don't have complete details here, can post them when I get home later. You can see a picture of the remote at the bottom of this site - sorry about the scrolling, I managed to overwrite my index.html file that had link/tabs to jump to the various areas that my son did up for me. http://www.crossroadsfencing.com/BobuinoRev17

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.