Introduction: Programmable Keypad
In this project I'll show how to make relatively simple and inexpensive programmable keypad for mapping your most used keyboard shortcuts, applications and more.
This keypad is detected as keyboard in all major OS, no additional drivers needed.
Supplies
- Rotary encoder.
- Lots of (depends on your needs) push buttons.
- Arduino Pro Micro, Arduino Leonardo or any other dev board with Atmega32U4 MCU.
- Wire, solder, soldering iron, etc.
- (Optional) Some heavy base for keeping keypad from sliding, I'm using old 3.5" HDD
Step 1: Electrical Circuit
I chose to use Arduino Pro Micro dev board with Atmega32U4 MCU which has 18 usable digital pins.
The pins to connect push buttons and rotary encoder was selected with no particular order in mind, but some things should be noted:
- All digital read capable pins have internal pull-ups which allows to mitigate external pull-down resistors. Of course code should be updated accordingly, because it expects state of the pin go from low to high when push buttons are pressed.
- In encoder.h library example noted that best performance of the encoder is reached when both pins connected to MCU interrupt capable pins. Most of analog pins of Atmega32U4 doesn't have interrupt capability.
- Exact pull-down resistor values doesn't matter much, anything from 1 kΩ to 100 kΩ will work fine. Larger resistance values allows smaller power dissipation but results in slower pin response to voltage changes. Just choose whatever value resistors you have the most.
- Mechanical encoders aren't most reliable things because of contact wear and bouncing. That's why a good debouncing solution is needed. My chosen capacitor values and delay times in code probably won't provide best results for you. So a little bit of experimentation is needed. Or switch to something like optical encoder, but its price is significantly higher.
Step 2: Assembly
I wanted to make keypad as clean looking as possible, so I soldered all components on the back of prototype board. I figured that keypad would be more ergonomic if it was used elevated at small angle. That's why I soldered Arduino Pro Micro on separate board and connected all digital pins with wire to push buttons. Its more convenient to connect USB cable that way.
I found old 3.5" HDD to use as a base for keypad, its quite heavy and prevents board form sliding across desk when operating (anti-slip pads helps too). Also it has convenient 3 mm screw holes in which I screwed brass standoffs and fixed the board at slight angle.
Step 3: Programming
Code is written with Arduino IDE. You will need to install 2 libraries:
- Encoder by Paul Stoffregen
- Keyboard by Arduino
To compile for Atmega32U4 you also need to install Arduino Pro Micro board file, Sparkfun has great tutorial how to do that.
One thing to note in advance is to be careful not to leave "keys pressed" in your code. This happened to me and MCU was constantly spamming some key press combination. The only way I'm aware how to fix this is to re-burn boot-loader to MCU. If you will end up like me, you can follow this guide to burn boot-loader, you will need another arduino board to use as a programmer.
In main loop MCU first reads each push button state, if state change from LOW to HIGH is detected, function keyboard_shortcut(i) is executed. Variable i is an id of the pressed button, total push button number is defined by ALL_BUTTONS (in my case 15). When executed, keyboard_shortcut(i) sends CTRL+SHIFT and then a letter which is mapped to button id: 1->A, 2->B, 3->C etc. Some combinations like CTRL+SHIFT+N is omitted because it's already used in Windows10 by default (in this case to create a new folder). Here is the list of all default Windows shortcuts. After short delay MCU sends signal to release all keys and function exits back to main loop.
After all buttons are checked, MCU checks if rotary encoder position changed and if it does, keyboard_shortcut(i) is executed with unique id.
Encoder button press inverts encoderButtonFlag boolean variable. When encoder is rotated different shortcut is sent to PC, depending on rotation direction and encoderButtonFlag value.
If debugFlag set to 1 debug messages are sent via UART to serial monitor.
Attachments
Step 4: Configuring Shortcuts
What each shortcut does is up too you, we all have different preferences. I will provide what shortcuts I configured for myself as an example. I'm using Linux Mint 19.3 with xfce4 desktop manager, so my examples mainly involves bash scripts, but I will show some basic examples for Windows10 too.
In the first picture you can see which scripts I mapped to which shortcuts. It's done from xfce settings menu, process for this is straight forward. You can find these scripts in my GitHub repository
Smaller 6 push buttons in the bottom is for starting applications like web browser or file manager, some of these applications is called from start_only_one_app.sh script, which gets all started applications names and search for application you want to start. If application window already exists it gets focused, else new instance of an application is started.
Other scripts:
- 2nd_display_control.sh - switches second monitor ON/OFF.
- moon_lamp.sh - switches my Moon Lamp ON/OFF.
- pc_load.sh - creates notification bubble with current CPU and GPU usage and temperatures.
- shutdown.sh - initializes PC shutdown with 1 minute delay and creates notification bubble in which remaining time is displayed.
- spec_vpn.sh - connects to specific OpenVPN server or if connection already exists, disconnects from server.
- shortcut_controll.sh - takes command (plus, minus, tab, close) as argument, detects what window is now focused and if specific application is found active executes control action. For example to open new tab in sublime text editor default shortcut is "CTRL+N" and in xfce terminal - "CTRL+T", so this script allows to open new tab in sublime and terminal with the same push button.
First function of the rotary encoder is to control volume, second function is to control active window zoom via shortcut_controll.sh.
For Windows OS you can map shortcuts to applications via program properties window as shown in the second picture. For anything else you will want to use AutoHotkey. It's automation scripting language for Windows.
Some simple examples of AHK syntax:
;Volume control
^+t::
Send {Volume_Up}
return
^+v:: Send {Volume_Down}
return
;Close active window
^+h::
WinGetTitle, Title, A
PostMessage, 0x112, 0xF060,,, %Title%
return
;Shutdown PC
^+b::
Run shutdown /s
Step 5: Improvements
Some possible improvements:
- Better quality push buttons.
- Prototype PCB flexes quite a lot when buttons are pressed.
- RGB lighting to change color depending on which function rotary encoder is set to.
- More buttons (use IO expander IC).
- Better rotary encoder (or better debouncing solution).