Tuesday, November 29, 2016

GPIO client for the Raspberry Pi Bluetooth Keyboard Emulator


Controlling a Windows PC From the Raspberry Pi GPIO Bus


In this post I'm going to talk about controlling the Bluetooth keyboard emulator I discussed previously to send a key to the host based on the status of a GPIO pin.  This will enable a Raspberry Pi to control a PC using simple GPIO inputs.

Once you have this set up, the sky's the limit for what you can achieve.  Anything you can do on the PC using the keyboard you can also control from the Pi.

For this demo I am going to show how you use the Pi and the Bluetooth keyboard emulator to create an alternative input for a PC game.

Bluetooth Keyboard Emulator

This demo assumes that you have successfully setup the Bluetooth Keyboard emulator in my previous post.

The emulator is written to run as a D-Bus service,  which means that once it is set up we can create different clients that consume this service without having to modify the original server code.  This is a separation of concerns that hopefully makes the client code easier to maintain and understand.

Install Dependencies

The GPIO keyboard client uses the RPi.GPIO python module to interact with the GPIO pins.  It is installed by default in most Raspbian distributions, but just in case it isn't you can install it with the following command

> sudo apt-get install python-rpi.gpio


Obtain Source Code  

If you haven't already cloned a copy of the github repository for this blog, you really should go back and read the previous article

If you have already cloned a copy, then change into the BlogCode directory and get the latest version

> git pull origin master 


Review Source Code

Change into the  BlogCode/btk_gpio folder and use a python editor such as Idle to view the gpio_btkclient.py file

> idle btk_gpio_client &

This program  acts as a dbus client for the Bluetooth Keyboard Emulator.

It uses the RPi.GPIO  library to monitor a single pin on the GPIO bus, then updates a keyboard state structure and sends that structure to the emulator to generate a key event,

The main loop monitors the state of the input pin and generates key up and key down events accordingly,

At the top of the class are some constants that can be modified to change how the program behaves:

  • PIN sets the physical pin number. of the GPIO pin to monitor.  By default this is pin # 37, which corresponds to GPIO 26
  • KEYCODE sets the USB scan code of the key to send. This is set to 'W'  by default, which is the forward key for many games.
  • REPEAT_KEY controls whether the program will send a single key stroke or multiple keystrokes per switch press
  • MIN_KEY_TIME sets the minimum delay between sending a key down event and a key up event. This seems to be a least 0.001 seconds for a keystroke to register
  • REPEAT_KEY_DELAY  sets the delay between sending repeated keys.  This may need to be tuned for different applications 

Connect a switch to the GPIO


To test the demo, connect a switch between a ground pin and the pin defined in the PIN constant.  If using the default pin 39, then pin 40 can be used as a convenient ground

In my setup I am using a breadboard and an adafruit T-Cobbler to connect a micro switch.





Run the Emulator Server


Follow the steps from the previous article to run the Bluetooth Keyboard Emulator server

If all goes well it should look something like this






Run the Client


Open a new terminal window and change into t BlogCode\btk_gpio directory,
Use the following command to run the client

> sudo python gpio_btkclient.py


Control a PC Program


Run a program that you want to control with the switch and give it the mouse focus.

If all goes well, when you press the switch the client will display the switch events and the PC program will receive key events from your Raspberry Pi!






Have Fun!


This is obviously just scratching the surface of what you can achieve.  You can extend this beyond simple switches to control a PC using any sensor you can read through the GPIO

This method could also be easily extended to use sensors on the I2C  bus to control a PC.


Demo


Here is a video showing how this setup can can be used to control Minecraft on the PC.








6 comments:

  1. I Have a functional version of an arcade using GPIO and rpi but it hasn't enough power to emulate all games, adding this script maybe can I built a mobile bt arcade keyboard for PC, i'll let you know how it works

    ReplyDelete
  2. Hello, I found your project very useful, have you planned some way to pair devices without making ssh session, like press GPIO5 + GPIO6 to launch bluetoothctl?

    my next part of the project is to make it work togethe in headless mode, any advice is welcome

    ReplyDelete
  3. Hmm. you should only need to pair device once, so this shouldn't be necessary. Even so, using a pin to pair is only one option. You should be able to set things up so that the pin isn't needed. See https://stackoverflow.com/questions/12888589/linux-command-line-howto-accept-pairing-for-bluetooth-device-without-pin for examples

    ReplyDelete
  4. i get an error when run
    Key Down │[CHG] Device 08:DF:1F:99:F3:41 RSSI: -69
    Traceback (most recent call last): │[CHG] Device 48:CB:84:1B:D5:43 RSSI: -74
    File "gpio_btkclient.py", line 168, in │[CHG] Device D2:15:09:06:AC:92 RSSI: -84
    dc.event_loop() │[CHG] Device E1:97:E0:53:F0:9A RSSI: -92
    File "gpio_btkclient.py", line 144, in event_loop │[CHG] Device C6:68:35:FB:55:E2 RSSI: -79
    self.send_key_down() │[CHG] Device DB:4E:D4:95:52:51 RSSI: -78
    File "gpio_btkclient.py", line 88, in send_key_down │[CHG] Device E4:42:7E:0A:4A:FE RSSI: -82
    self.send_key_state() │[CHG] Device F4:5C:89:8B:C0:CC RSSI: -55
    File "gpio_btkclient.py", line 80, in send_key_state │[CHG] Device 34:DE:1A:82:E1:45 RSSI: -59
    self.iface.send_keys(int(bin_str,2),self.state[4:10] ) │[CHG] Device 48:CB:84:1B:D5:43 RSSI: -61
    File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 70, in __call__ │[CHG] Device E5:20:06:9D:D2:21 RSSI: -94
    return self._proxy_method(*args, **keywords) │[CHG] Device 42:48:F7:52:5D:AD RSSI: -72
    File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 145, in __call__ │[CHG] Device 18:B4:30:57:D1:6E RSSI: -60
    **keywords) │[CHG] Device ED:B3:92:C2:4B:0E RSSI: -71
    File "/usr/lib/python2.7/dist-packages/dbus/connection.py", line 651, in call_blocking │[CHG] Device F4:5C:89:8B:C0:CC RSSI: -73
    message, timeout) │[CHG] Device E1:97:E0:53:F0:9A RSSI: -76
    dbus.exceptions.DBusException: org.freedesktop.DBus.Python.bluetooth.btcommon.BluetoothError: Traceback (m│[CHG] Device C6:34:F2:AF:AE:33 RSSI: -62
    ost recent call last): │[CHG] Device E4:42:7E:0A:4A:FE RSSI: -92
    File "/usr/lib/python2.7/dist-packages/dbus/service.py", line 707, in _message_cb │[CHG] Device DB:4E:D4:95:52:51 RSSI: -70
    retval = candidate_method(self, *args, **keywords) │[CHG] Device 42:2E:1B:2B:47:D8 RSSI: -58
    File "btk_server.py", line 216, in send_keys │[CHG] Device 48:CB:84:1B:D5:43 RSSI: -72
    self.device.send_string(cmd_str); │[CHG] Device 42:48:F7:52:5D:AD RSSI: -88
    File "btk_server.py", line 177, in send_string │[CHG] Device 18:B4:30:57:D1:6E RSSI: -78
    self.cinterrupt.send(message) │[CHG] Device E5:20:06:9D:D2:21 RSSI: -82
    File "", line 5, in send │[CHG] Device F4:5C:89:8B:C0:CC RSSI: -57
    BluetoothError: (107, 'Transport endpoint is not connected')`
    `

    ReplyDelete
  5. I get the same error as Thanh Le. Can someone help with this?

    ReplyDelete
  6. Is it possible to get this to work with multiple GPIO pins?

    ReplyDelete