The GPIO keypad input

We have seen how we can monitor inputs on the GPIO to launch applications and control the Raspberry Pi; however, sometimes we need to control third-party programs. Using the uInput library, we can emulate key presses from a keyboard (or even mouse movement) to control any program, using our own custom hardware.

For more information about using uInput, visit http://tjjr.fi/sw/python-uinput/.

Getting ready

Perform the following steps to install uInput:

  1. First, we need to download uInput.

    You will need to download the uInput Python library from Github (~50 KB) using the following commands:

    wget https://github.com/tuomasjjrasanen/python-uinput/archive/master.zip
    unzip master.zip
    

    The library will unzip to a directory called python-uinput-master.

    Once completed, you can remove the ZIP file using the following command:

    rm master.zip
    
  2. Install the required packages (if you have installed them already, the apt-get command will ignore them) using the following commands:
    sudo apt-get install python3-setuptools python3-dev
    sudo apt-get install libudev-dev
    
  3. Compile and install uInput using the following commands:
    cd python-uinput-master
    sudo python3 setup.py install
    
  4. Finally, we load the new uinput kernel module using the following command:
    sudo modprobe uinput
    

    To ensure it is loaded on startup, we can add uinput to the modules file using the following command:

    sudo nano /etc/modules
    

    Put uinput on a new line in the file and save it (Ctrl + X,Y).

  5. Create the following circuit using the following equipment:
    • Breadboard (half-sized or larger)
    • 7 x Dupont female to male patch wires
    • Six push buttons
    • 6 x 470-ohm resistors
    • Breadboarding wire (solid core)
    Getting ready

    GPIO keypad circuit layout

    The keypad circuit can also be built into a permanent circuit by soldering the components into a Vero prototype board (also known as a stripboard), as shown in the following image:

    Getting ready

    GPIO keypad Pi hardware module

    Note

    This circuit is available as a solder-yourself kit from PiHardware.com.

  6. Connect the circuit to the Raspberry Pi GPIO pins as follows:
     

    Button

    GPIO pin

    GND

     

    6

    v

    B_DOWN

    22

    <

    B_LEFT

    18

    ^

    B_UP

    15

    >

    B_RIGHT

    13

    1

    B_1

    11

    2

    B_2

    7

How to do it…

Create a gpiokeys.py script as follows:

#!/usr/bin/python3
#gpiokeys.py
import time
import RPi.GPIO as GPIO
import uinput

#HARDWARE SETUP
# GPIO
# 2[==G=====<=V==]26[=======]40
# 1[===2=1>^=====]25[=======]39
B_DOWN  = 22    #V
B_LEFT  = 18   #<
B_UP    = 15   #^
B_RIGHT = 13   #>
B_1  = 11   #1
B_2  = 7   #2

DEBUG=True
BTN = [B_UP,B_DOWN,B_LEFT,B_RIGHT,B_1,B_2]
MSG = ["UP","DOWN","LEFT","RIGHT","1","2"]

#Setup the DPad module pins and pull-ups
def dpad_setup():
  #Set up the wiring
  GPIO.setmode(GPIO.BOARD)
  # Setup BTN Ports as INPUTS
  for val in BTN:
    # set up GPIO input with pull-up control
    #(pull_up_down can be:
    #    PUD_OFF, PUD_UP or PUD_DOWN, default PUD_OFF)
    GPIO.setup(val, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def main():
  #Setup uinput
  events = (uinput.KEY_UP,uinput.KEY_DOWN,uinput.KEY_LEFT,
           uinput.KEY_RIGHT,uinput.KEY_ENTER,uinput.KEY_ENTER)
  device = uinput.Device(events)
  time.sleep(2) # seconds
  dpad_setup()
  print("DPad Ready!")

  btn_state=[False,False,False,False,False,False]
  key_state=[False,False,False,False,False,False]
  while True:
    #Catch all the buttons pressed before pressing the related keys
    for idx, val in enumerate(BTN):
      if GPIO.input(val) == False:
        btn_state[idx]=True
      else:
        btn_state[idx]=False

    #Perform the button presses/releases (but only change state once)
    for idx, val in enumerate(btn_state):
      if val == True and key_state[idx] == False:
        if DEBUG:print (str(val) + ":" + MSG[idx])
        device.emit(events[idx], 1) # Press.
        key_state[idx]=True
      elif val == False and key_state[idx] == True:
        if DEBUG:print (str(val) + ":!" + MSG[idx])
        device.emit(events[idx], 0) # Release.
        key_state[idx]=False

    time.sleep(.1)
    
try:
  main()
finally:
  GPIO.cleanup()
#End

How it works…

First, we import uinput and define the wiring of the keypad buttons. For each of the buttons in BTN, we enable them as inputs with internal pull-ups enabled.

Next, we set up uinput, defining the keys we want to emulate and adding them to the uinput.Device() function. We wait a few seconds to allow uinput to initialize, set the initial button and key states, and start our main loop.

The main loop is split into two sections: the first part checks through the buttons and records the states in btn_state, and the second part compares the btn_state with the current key_state array. This way, we can detect a change in btn_state and call device.emit() to toggle the state of the key.

To allow us to run this script in the background, we can run it with & as shown in the following command:

sudo python3 gpiokeys.py &

Note

The & character allows the command to run in the background, so we can continue with the command line to run other programs. You can use fg to bring it back to the foreground, or %1, %2, and so on if you have several commands running. Use jobs to get a list.

You can even put a process/program on hold to get to the command prompt by pressing Ctrl + Z and then resume it with bg (which will let it run in the background).

You can test the keys using the game created in the Creating an overhead scrolling game recipe in Chapter 4, Creating Games and Graphics, which you can now control using your GPIO directional pad. Don't forget that if you are connecting to the Raspberry Pi remotely, any key presses will only be active on the locally connected screen.

There's more…

We can do more using uinput to provide hardware control for other programs, including those that require mouse input.

Generating other key combinations

You can create several different key mappings in your file to support different programs. For instance, the events_z80 key mapping would be useful for a Spectrum Emulator such as fuze (browse to http://raspi.tv/2012/how-to-install-fuse-zx-spectrum-emulator-on-raspberry-pi for details). The events_omx key mappings are suitable for controlling video played through the OMX Player using the following command:

omxplayer filename.mp4

You can get a list of keys supported by omxplayer by using the -k parameter.

Replace the line that defines the events list with a new key mapping, and select different ones by assigning them to events using the following code:

events_dpad = (uinput.KEY_UP,uinput.KEY_DOWN,uinput.KEY_LEFT,
              uinput.KEY_RIGHT,uinput.KEY_ENTER,uinput.KEY_ENTER)
events_z80 = (uinput.KEY_Q,uinput.KEY_A,uinput.KEY_O,
             uinput.KEY_P,uinput.KEY_M,uinput.KEY_ENTER)
events_omx = (uinput.KEY_EQUAL,uinput.KEY_MINUS,uinput.KEY_LEFT,
             uinput.KEY_RIGHT,uinput.KEY_P,uinput.KEY_Q)

You can find all the KEY definitions in the input.h file; you can view it using the less command (press Q to exit) as shown in the following command:

less /usr/include/linux/input.h

Emulating mouse events

The uinput library can emulate mouse and joystick events as well as keyboard presses. To use the buttons to simulate a mouse, we can adjust the script to use mouse events (as well as defining mousemove to set the step size of the movement) using the following code:

MSG = ["M_UP","M_DOWN","M_LEFT","M_RIGHT","1","Enter"]
events_mouse=(uinput.REL_Y,uinput.REL_Y, uinput.REL_X,
             uinput.REL_X,uinput.BTN_LEFT,uinput.BTN_RIGHT)
mousemove=1

We also need to modify the button handling to provide continuous movement, as we don't need to keep track of the state of the keys for the mouse. To do so, use the following code:

    #Perform the button presses/releases
    #(but only change state once)
    for idx, val in enumerate(btn_state):
      if MSG[idx] == "M_UP" or MSG[idx] == "M_LEFT":
        state = -mousemove
      else:
        state = mousemove
      if val == True:
        device.emit(events[idx], state) # Press.
      elif val == False:
        device.emit(events[idx], 0) # Release.

    time.sleep(0.01)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.66.13