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) / (src - src)) * (dst - dst) + dst # 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.