There are many solutions when it comes to remote controlling a Raspberry Pi robot. The approach which I have taken here is to use an Xbox-esque generic USB game controller, specifically the EasySMX ESM-9101. This controller does have to be put into its Android-compatible mode in order to work with the Pi, due to the default being a Windows-only, Xinput-based system with support for controller rumble.
I will be using PiBorg’s fantastic gamepad library to interface with the controller. If you are planning on using a video game console controller I would recommend using the approxeng.input
library instead, as those controllers are a little bit more fiddly due to their proprietary nature. approxeng.input
provides a higher-level API which seems to abstract over some of those nuances. Note that, however, you can only use it with the list of supported controllers. Mine is not on that list.
Using a controller
So, let’s get started. The first thing we need to do is to download PiBorg’s gamepad library on our Raspberry Pi. Rather than having a typical pip
-based install process, PiBorg’s library just requires you to git clone
it.
cd ~
mkdir tools
cd tools
git clone https://github.com/piborg/Gamepad
Now let’s copy the required files into our project directory:
cd Gamepad
cp Controllers.py ~/path/to/project/directory
cp Gamepad.py ~/path/to/project/directory
Now we need to create a mapping between controller codes and buttons. Open up a new Python file called Controller.py
. We will be putting our button and axis mappings in there. To discover the button and axis mappings, run python3 Gamepad.py
and leave the device name blank. Try fiddling with your game controller. You should see the code for the button you are pressing or the axis you are changing.
My controller mappings will be for the aforementioned EasySMX wireless controller. You can base yours off this template.
# Controller.py
from Gamepad import Gamepad
class Controller(Gamepad):
def __init__(self, joystickNumber = 0):
Gamepad.__init__(self, joystickNumber)
# Replace both the button and axis codes
# with the ones for your controller. RT
# and LT stand for the left and right
# triggers, RS and LS stand for the
# left and right analogue sticks and
# LB and RB stand for the left and
# right bumpers.
self.buttonNames = {
7 : "RT",
5 : "RB",
6 : "LT",
4 : "LB",
10 : "LS_BTN",
11 : "RS_BTN",
0 : "Y",
1 : "B",
3 : "X",
2 : "A",
8 : "BACK",
9 : "START"
}
self.axisNames = {
1 : "LS_Y",
0 : "LS_X",
3 : "RS_Y",
2 : "RS_X"
}
self._setupReverseMaps()
Now we can create an example remote control program. It doesn’t actually do anything except log values to the console, but it serves as a good, simple example program:
# remote_control.py
from time import sleep
# This is the custom controller we created earlier
from Controller import Controller
# PiBorg's Gamepad library
import Gamepad
# Gamepad settings
gamepadType = Controller
exitControl = "BACK"
left_speed_control = "LS_Y"
right_speed_control = "RS_Y"
def main():
left_speed: float = 0
right_speed: float = 0
# Wait for a connection
if not Gamepad.available():
print("=== Please connect your gamepad... ===")
while not Gamepad.available():
sleep(1.0)
gamepad = gamepadType()
print("=== Gamepad connected ===")
# Handle joystick updates one at a time
while gamepad.isConnected():
# Wait for the next event
eventType, control, value = gamepad.getNextEvent()
# Print information about the event
print(eventType)
print(control)
print(value)
# Determine the event type
if eventType == "BUTTON":
# Button changed
if control == exitControl:
# Exit button (event on press)
if value:
print("=== Exiting ===")
break
elif eventType == "AXIS":
# Joystick changed
# On my particular controller I had
# to inverse the axis values. This
# may be different for yours.
if control == left_speed_control:
left_speed = value * -1
elif control == right_speed_control:
right_speed = value * -1
print("Left speed: " + str(left_speed))
print("Right speed: " + str(right_speed))
if __name__ == "__main__":
main()
Try running it with python3 remote_control.py
(or whatever you called your program). The speed readouts should increase as you manipulate the analogue controls. Note that this assumes a two-stick controller. If yours has one or no analogue controls then it will be far less ergonomic and easy to handle.
Conclusion
It is surprisingly easy to get a remote-controlled Raspberry Pi up and running these days. There are many high-level abstractions over raw filesystem APIs and other lower-level abstractions now that deal specifically with gamepads. Again, check out approxeng.input
if you are lucky enough to have a supported controller.