Controlling Your Robot: USB HID/Wireless Keyboards
In this guide we’ll learn about remote-controlling robots with a USB HID device of your choice. Preferably something with buttons like a Rii mini keyboard or a tiny remote control.
Of all the different ways to control a robot, using a USB device is probably one of the easiest methods. Not only do you probably already have one handy but it’s one of the more simple things to get up and running.
Installing The Software
First, you’re going to need some way of polling a HID device from Python. I’ve seen some creative attempts at this by reading one key at a time from the terminal. Don’t do this. It’s slow, unwieldy not at all fun to control.
The trick is to grab a little library called PyUSB. Before installing it, you'll need to download libusb-dev.
sudo apt-get install libusb-dev
Then go ahead and grab PyUSB from GitHub, and install as follows:
git clone https://github.com/walac/pyusb cd pyusb sudo python setup.py install
Finding Your Keyboard
Next you’ll need a USB keyboard, USB presenter remote or any other HID device that pretends to be a keyboard.
Warning! If you're working directly on your Pi you'll need a separate keyboard to type on! Once Python gloms onto a keyboard, you wont be able to use it for anything else until your script is finished.
To read your keyboard in Python you’ll need to know its Vendor ID and Product ID. These are easy to find by plugging it into your Pi and running:
Sometimes your device will be the blank line. You might see a number like 1997:2433. The first part is the Vendor ID, and the second part is the Product ID. 1997 is Rii's Vendor ID so if you have a Rii keyboard you should look for it.
If you see multiple blank entries using lsusb, or have trouble identifying your device in the list then you can try:
sudo lsusb -v
This will give you a very verbose output, which you’ll have to scroll through and locate your device. It can be a bit tricky to find the relevant line, but you only have to do this once.
Once you’ve got the Vendor and Device IDs you're going to need the Python code to plug these into. The first chunk of code we’ll use will help us see exactly what output each keypress on your device results in, this way we can start to map the controls we want to use.
To get connected to your USB device, you'll need something along the lines of this:
Note: The Vendor ID and Product ID are hexadecimal numbers, this means you should prefix them with 0x like below.
import usb.core import usb.util import time USB_IF = 0 # Interface USB_TIMEOUT = 5 # Timeout in MS USB_VENDOR = 0x1997 # Rii USB_PRODUCT = 0x2433 # Mini Wireless Keyboard dev = usb.core.find(idVendor=USB_VENDOR, idProduct=USB_PRODUCT) endpoint = dev[(0,0)] if dev.is_kernel_driver_active(USB_IF) is True: dev.detach_kernel_driver(USB_IF) usb.util.claim_interface(dev, USB_IF) while True: control = None try: control = dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, USB_TIMEOUT) print control except: pass time.sleep(0.01) # Let CTRL+C actually exit
If everything works as expected then this code, when run as root, will show you what happens when you press some keys on your USB HID device.
Run the code, press some keys and see what happens.
If it fails, try double-checking your USB Device and Vendor ID, and make sure you're running as root.
Controlling A Robot
If you've successfully run the code and smushed some keys then you'll notice that the input is read into an array, a keyboard buffer if you will.
The dev.read call will success when a key is pressed or released, and the updated buffer will be received containing only the keys which are currently pressed.
Because it's an array, it can be checked against in a remarkably simple way:
if my_key in control: # My key is pressed my_key_pressed = True else: # My key is released my_key_pressed = False
If you run a motor forward when my_key_pressed == True, and stop it when my_key_pressed == False then you've got a simple control interface. This is great for driving a left tank track forward.
Finding The Right Keys
Now that you've got a basic example that shows you which number corresponds to a keypress you should press the keys you want to use for your robot and write them down. You'll need them for the values of
KEY_LEFT etc in a moment.
The Finished Code
Below you'll find a more complete example to expand upon, it's what I use to drive my tracked robot, which uses an Explorer HAT Pro and LEGO Technic Power Functions&right; motors.
Give this code a whirl, substituting your Vendor ID, Product ID and key bindings as necessary:
# !/usr/bin/env python import usb.core import usb.util import explorerhat import time explorerhat.light.red.on() USB_VENDOR = 0x1997 # Rii USB_PRODUCT = 0x2433 # Mini Wireless Keyboard USB_IF = 0 # Interface USB_TIMEOUT = 5 # Timeout in MS BTN_LEFT = 80 BTN_RIGHT = 79 BTN_DOWN = 81 BTN_UP = 82 BTN_STOP = 44 # Space BTN_EXIT = 41 # ESC dev = usb.core.find(idVendor=USB_VENDOR, idProduct=USB_PRODUCT) endpoint = dev[(0,0)] if dev.is_kernel_driver_active(USB_IF) is True: dev.detach_kernel_driver(USB_IF) usb.util.claim_interface(dev, USB_IF) explorerhat.light.red.off() explorerhat.light.green.on() while True: control = None try: control = dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, USB_TIMEOUT) print(control) except: pass if control != None: if BTN_DOWN in control: explorerhat.motor.backwards() if BTN_UP in control: explorerhat.motor.forwards() if BTN_LEFT in control: explorerhat.motor.two.forwards() explorerhat.motor.one.backwards() if BTN_RIGHT in control: explorerhat.motor.two.backwards() explorerhat.motor.one.forwards() if BTN_STOP in control: explorerhat.motor.stop() if BTN_EXIT in control: exit() time.sleep(0.02)
Once you've got this example up and running, it should be straight-forward to modify it for your needs.
Things To Try
You might want to experiment with giving each motor its own pair of keys, so you can drive them forwards/backwards independently. This is great for tank control. You might also want to check both the LEFT/RIGHT keys and the UP/DOWN keys together, and make your robot move forwards/backwards and turn at the same time for more fluid control. Whatever you do, these basics should get you off to a good start.
Search above to find more great tutorials and guides.