MicroPython is the new kid on the block for programming the LEGO MINDSTORMS EV3 Intelligent Brick. It’s lightning fast because it’s so lightweight. Python 3 takes up to 20 seconds to start and is a little overkill for just driving a robot around. MicroPython starts in mere seconds. I will assume you are set up with VS Code, ev3dev and MicroPython.
EV3 MicroPython program for using the bluetooth gampad
Let’s start with the complete program for reading the PS3 gamepad stick positions in MicroPython. Below I will and dissect how it works below it. You can just go to VS Code, connect to your EV3 brick and paste this code in the main.py file. Press F5 to run it. If the gamepad is connected you will be able to steer a simple tank-like robot.
#!/usr/bin/env pybricks-micropython
from pybricks import ev3brick as brick
from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor,
InfraredSensor, UltrasonicSensor, GyroSensor)
from pybricks.parameters import (Port, Stop, Direction, Button, Color,
SoundFile, ImageFile, Align)
from pybricks.tools import print, wait, StopWatch
from pybricks.robotics import DriveBase
import struct
# Declare motors
left_motor = Motor(Port.B)
right_motor = Motor(Port.C)
# Initialize variables.
# Assuming sticks are in the middle when starting.
right_stick_x = 124
right_stick_y = 124
# A helper function for converting stick values (0 - 255)
# to more usable numbers (-100 - 100)
def scale(val, src, dst):
"""
Scale the given value from the scale of src to the scale of dst.
val: float or int
src: tuple
dst: tuple
example: print(scale(99, (0.0, 99.0), (-1.0, +1.0)))
"""
return (float(val - src[0]) / (src[1] - src[0])) * (dst[1] - dst[0]) + dst[0]
# Find the PS3 Gamepad:
# /dev/input/event3 is the usual file handler for the gamepad.
# look at contents of /proc/bus/input/devices if it doesn't work.
infile_path = "/dev/input/event3"
# open file in binary mode
in_file = open(infile_path, "rb")
# Read from the file
# long int, long int, unsigned short, unsigned short, unsigned int
FORMAT = 'llHHI'
EVENT_SIZE = struct.calcsize(FORMAT)
event = in_file.read(EVENT_SIZE)
while event:
(tv_sec, tv_usec, ev_type, code, value) = struct.unpack(FORMAT, event)
if ev_type == 3 and code == 3:
right_stick_x = value
if ev_type == 3 and code == 4:
right_stick_y = value
# Scale stick positions to -100,100
forward = scale(right_stick_y, (0,255), (100,-100))
left = scale(right_stick_x, (0,255), (100,-100))
# Set motor voltages. If we're steering left, the left motor
# must run backwards so it has a -left component
# It has a forward component for going forward too.
left_motor.dc(forward - left)
right_motor.dc(forward + left)
# Finally, read another event
event = in_file.read(EVENT_SIZE)
in_file.close()
Connecting more sticks and buttons
If you want to connect more buttons and sticks to motor functions, just listen for different event types and codes in the main while loop. Have those events trigger different function on your robots. Below is an overview of all event types and codes. Derek Segesdy was so kind to fix the errors in my overview and share his results.
How the PS3 gamepad program works in MicroPython
Device input without evdev
In python 3 there’s a handy library called evdev which connects to the Linux device events system. There’s no such thing in MicroPython. We’re lucky however that Debian Linux uses a file based access system for usb devices. So we can just read a file and decode the bytes inside with python struct.
After connecting the PS3 gamepad, you could open an SSH terminal to your EV3 brick and check which file handler is for your gamepad. On my brick it looked like this:
robot@ev3dev:~$ cat /proc/bus/input/devices
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="LEGO MINDSTORMS EV3 Speaker"
P: Phys=
S: Sysfs=/devices/platform/sound/input/input0
U: Uniq=
H: Handlers=kbd event0
[...]
I: Bus=0005 Vendor=054c Product=0268 Version=8100
N: Name="PLAYSTATION(R)3 Controller"
P: Phys=a0:e6:f8:60:30:81
S: Sysfs=/devices/platform/soc@1c00000/serial8250.2/tty/ttyS2/hci0/hci0:1/0005:054C:0268.0001/input/input2
U: Uniq=00:06:f7:89:3f:81
H: Handlers=event3
B: PROP=0
B: EV=20001b
B: KEY=f 0 0 0 0 0 0 0 7fdb0000 0 0 0 0 0 0 0 0 0
B: ABS=3f
B: MSC=10
B: FF=1 7030000 0 0
Near the end of the file you can see the file handler for the playstation controller. It contains a few bits and bytes with encoded event values. I use python struct to unpack those bytes and make some sense of them.
Controlling the robot from the main loop without Threading
In my regular python 3 code I used threading to have a neat 60Hz motor control loop. The MicroPython program on the other hand waits for an event from the gamepad to pop up. These events come fairly often, so you get the feeling of real-time. However it is probably not suited for a closed control loop. Such loop would be needed with a rack gear and 4-link steering mechanism. In MicroPython there are coroutines and asyncio. They are tough to program with for beginners. So that’s stuff for another article.
Great Article, but what is the secret of connecting a PS4 controller. I managed to do a few months back, but cannot seem to repeat my success as I write this.
Tried this code out with a PS4 directly connected and all hell broke loss. Wheels spinning at maximum power, no controls on the PS4 controller.
I have no PS4 controller, so I’m afraid I can’t help you. You could use the micropython script from my other article and print the codes from the PS4 controller. Maybe you can figure out the differences?
go to line 40 and change /event3 to /event4 because it is ps4
OK, got the Sony PS4 Dual Shock paired, [You need to press playstation and sharing button and then go look on the brick for the connection] but still looking at pandemonium! There are three event areas in the devices. One for the touchpad, one for the motion and one for the controller. None of them work with this script.
robot@ev3dev:/proc/bus/input$ more devices
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name=”LEGO MINDSTORMS EV3 Speaker”
P: Phys=
S: Sysfs=/devices/platform/sound/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=40001
B: SND=6
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name=”EV3 Brick Buttons”
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio_keys/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=1680 0 0 10004000
I: Bus=0005 Vendor=054c Product=09cc Version=8100
N: Name=”Wireless Controller Touchpad”
P: Phys=40:bd:32:3e:56:97
S: Sysfs=/devices/platform/soc@1c00000/serial8250.2/tty/ttyS2/hci0/hci0:1/0005:054C:09CC.0002/input/input6
U: Uniq=dc:af:68:a5:ee:6e
H: Handlers=event2
B: PROP=5
B: EV=b
B: KEY=2420 0 10000 0 0 0 0 0 0 0 0
B: ABS=2608000 0
I: Bus=0005 Vendor=054c Product=09cc Version=8100
N: Name=”Wireless Controller Motion Sensors”
P: Phys=40:bd:32:3e:56:97
S: Sysfs=/devices/platform/soc@1c00000/serial8250.2/tty/ttyS2/hci0/hci0:1/0005:054C:09CC.0002/input/input7
U: Uniq=dc:af:68:a5:ee:6e
H: Handlers=event3
B: PROP=40
B: EV=19
B: ABS=3f
B: MSC=20
I: Bus=0005 Vendor=054c Product=09cc Version=8100
N: Name=”Wireless Controller”
P: Phys=40:bd:32:3e:56:97
S: Sysfs=/devices/platform/soc@1c00000/serial8250.2/tty/ttyS2/hci0/hci0:1/0005:054C:09CC.0002/input/input5
U: Uniq=dc:af:68:a5:ee:6e
H: Handlers=event4
B: PROP=0
B: EV=20001b
B: KEY=7fdb0000 0 0 0 0 0 0 0 0 0
B: ABS=3003f
B: MSC=10
B: FF=1 7030000 0 0
Yes. Now you have to figure out which codes and which values correspond to which buttons and stick movements.
Change the program and add
print( ev_type, code, value)
after line 52. See what the PS4 pad spits out.
Ok,
Managed to extract these values?
Reading event4 Left/Right Stick Center/Forward/Back/Left/Right many events
RSC ev_type 3 code 4 returns 128/129 {centre}
RSF ev_type 3 code 4 returns 0 {forward}
RSB ev_type 3 code 4 returns 255 {back}
RSC ev_type 3 code 3 returns 123/122 {centre}
RSL ev_type 3 code 3 returns 0 {left}
RSR ev_type 3 code 3 returns 255 {right}
LSC ev_type 3 code 0 returns 130/141
LSL ev_type 3 code 0 returns 0
LSR ev_type 3 code 0 returns 255
LSC ev_type 3 code 1 returns 128/129
LSF ev_type 3 code 1 returns 0
LSB evtype 3 code 1 returns 255
Bon,
So this code produces forward/backward using the right stick.
if ev_type == 3 and code == 1:
right_stick_x = value
if ev_type == 3 and code == 4:
right_stick_y = value
Your cpython version works better 🙂
Sorry and finally this gives me all for directions, but it isn’t very clean.
if ev_type == 3 and code == 3:
right_stick_x = value
if ev_type == 3 and code == 1:
right_stick_x = value
if ev_type == 3 and code == 0:
right_stick_y = value
if ev_type == 3 and code == 4:
right_stick_y = value
So it works? Shouldn’t it be more like this?
if ev_type == 3 and code == 3:
LEFT_stick_x = value
if ev_type == 3 and code == 1:
right_stick_x = value
if ev_type == 3 and code == 0:
LEFT_stick_y = value
if ev_type == 3 and code == 4:
right_stick_y = value
Hi, thanks I got my 8BitDo working with this code also.
One thing is how to I tell motor to just rotate 45 deg left and right ?
right = scale(rPad_x, (0, 255), (100, -100))
left = scale(rPad_x, (0, 255), (100, -100))
main_motor.dc(right – left).
main_motor.dc(right + left)
So if I trun the right pad to at the end of the pad 0 motor only goes 45 deg
Set a motor target of 45. Something like: steer_motor.dc=(45-steer_motor.angle())
I received the following error when trying to execute the command:
“Starting: brickrun –directory=”/home/robot/PS3_Controller_Redux” “/home/robot/PS3_Controller_Redux/main.py”
Started.
———-
Starting remote process failed: Failed to execute child process “/home/robot/PS3_Controller_Redux/main.py” (Permission denied)
———-
Exited with error code 1.”
Thoughts? I tried running the operation through VS with the EV3 plugged in.
No idea. Are you using the official image from LEGO education?
Does anybody have an idea of how to make it both joysticks?
How do you make it connect to the medium motor?
Hi Anton,
I followed your instruction to connect a PS3 controler and it seems to works fine. But I dont no what to do with this kind of error to controle a small tank program, and I tried several things but the messages still pops up:
Traceback (most recent call last):
File “/home/robot/ROBOT_EDUCATOR/main.py”, line 16, in
NameError: name ‘ultrasonicSensor’ isn’t defined
———-
Exited with error code 1.
You can delete that line, I think. Or spell it with a capital U
Hey Anton I watched the video and have read over the code but I want to ask how I can make to additional motors move as well to create a type of crane action I would like to use the X button and O button if possible or the R2 and L2. Any advice on how I can figure out what codes i should add.
I’m getting so many questions about this, I’ll dedicate an article to this situation specifically. The short answer is: check for the buttons event type, then find the codes for the buttons you want to use and wait for a value == 0 event to happen. Value == 0 happens when a button has been pressed and is then released. Or you could turn the crane motors on at value==1 and off again at value==0.
Hi, Anton.
I have 8BitDo controller and it has been long time since dev ev3 so can you remind me how do I get the the events to terminal?
cat /dev/input/event2 I get the events but is show the data )��a)��_a)�� >)��_ format ( binary ? ) So how do I get the format human readable format, thanks 🙂
Use ‘struct’ like in my program.
What I did was evtest /dev/input/event(x) 🙂
Hi Anton! Please help us to resolve issue with our robot. We built the model, setuped ev3dev and wrote a program for pybricks-micropython. Here is our code:
https://docs.google.com/document/d/1eBznQh_vOO6us4md_-566pYvyTLIZjVqB27igrYp6ds/edit?usp=sharing
We are novice in python, so do not judge strictly.
We faced with slow performance and delays when use gamepad mostly after LF (Line Follower) thread.
It looks like a button on the gamepad is stuck and robot is moving some time after button was released.
I think the root cause could be in our program (“while event:” block) or my be we need to select python 3 and not pybricks-micropython?
Any help would be appreciated!
Many thanks!
I have no idea. But I know threading does not work well in pybricks. A tight loop with a state machine is better.
Could you share an example. It would be much appreciated your help. We just in the very first our steps in roboticks )
Thank you!!!