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:
sudo lsusb
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.
Connecting
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,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,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.