The new LEGO hubs support gamepads from the official LEGO software but only with Scratch/Word Blocks and via a PC in streaming mode. When you need lag-less remote control and precise motor timing, this type of connection is just excruciatingly slow. This article shares how we connected a PS4 controller to a Robot Inventor and SPIKE Prime hub without lag.
Technical background information on Bluetooth and gamepads
You might ask: why did LEGO only allow the gamepad via a PC in streaming mode? Doesn’t it have Bluetooth? The problem is that there’s Bluetooth and Bluetooth. More specifically, there is Bluetooth Classic (BTC, BR/EDR) and Bluetooth Low Energy (or BLE).
Most gamepads use Bluetooth classic. And the LEGO hub has a chip that can do Bluetooth Classic (BTC). But there’s a catch: the hub runs an implementation of Micropython and has a firmware Bluetooth stack that limits BTC. The stack only runs the RFCOMM protocol – a wireless serial port – and you can only use that protocol to access the Micropython REPL. In all likelihood, LEGO will never change this setup because it is core to the communication with the LEGO apps. The bright side is that the Bluetooth Low Energy (BLE) protocol is accessible for us hackers. In other articles, I have shown how to use that to connect to an Android app or to connect to other LEGO hubs. Alas, most gamepads don’t support BLE. Long story short: the only way to work around this limitation is with some extra hardware.
Setup for connecting a PS4 gamepad to a Spike or Robot Inventor hub
To work around these Bluetooth limitations, we used a simple ESP32 chip. Any ESP32 board will work, but we have developed a dedicated LEGO-compatible version: the LMS-ESP32. The LMS-ESP32 chip runs firmware using the BluePad32 library that connects to any gamepad in pairing mode and captures the data. We then connected a LEGO PoweredUp or Wedo wire to the LMS-ESP32 board that feeds the gamepad data into the LEGO hub. The beauty of this setup is that the wire also powers up the ESP32 from the LEGO battery.
WiFi Python LMS-ESP32 board for MINDSTORMS (LMS-ESP32-v1.0)
This all-new MicroPython expansion board for LEGO MINDSTORMS Robot Inventor, Spike Prime, and EV3 is even more potent than the last Wifi Board. You can easily connect this LMS-ESP32-v1.0 board to your LEGO robot and use it as an interface to a limitless range of third-party electronics. You can now connect any proto…
If you have an ESP32 laying around, you can use that too. The most popular board is the M5 stick. In that case, use the wiring from the table below.
|SPIKE / MINDSTORMS Hub||ESP32|
|1 – M-||nc|
|2 – M+||nc|
|3 – GND||GND|
|4 – 3v3||Vin|
|5 – Tx||rx_pin=18|
|6 – Rx||tx_pin=19|
Apart from the chip, you need a Bluetooth gamepad. PS3 will not work, and neither will the old XBOX 360. But Nintendo Switch, PS4, PS5, Xbox One work.
Finally, this project requires some coding. So we’ll assume you have git installed and python3 with pip. Some basic Python knowledge will go a long way too.
Step by step guide for connecting the gamepad directly to Spike or Robot Inventor
Time needed: 15 minutes
- First, we will flash the modified Bluepad firmware on the ESP32.
To do so, Connect the LMS-ESP32 to your PC with a USB cable.
- Flash the firmware with the web-based firmware flasher
BluePad32 for LMS micropython projectsfrom the LMS_ESP Installer. Follow the steps that are shown on the screen. Choose the correct serial port. On my mac, it is usually
/dev/cu.usbserial-143220. You can use this web-based installer to revert back to MicroPython firmware.
Skip to step 6. Steps 3 to 5 show the manual flashing process.
- Install the esptool.py
pip install esptoolin a terminal window. The full esptool guide is here.
- Get our firmware for the LMS-ESP32
Clone the repository with
git clone https://github.com/antonvh/generic-lms-esp32-uart-fw
- Now flash the firmware onto the ESP32
In the command below replace the location of the port with the port on your computer. On my mac, it is usually
/dev/cu.usbserial-143220. The full command is this:
esptool.py -p /dev/cu.usbserial-143220 -b 460800 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_freq 40m --flash_size detect 0x10000 build/app-template.bin 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin
- Connect the flashed LMS-ESP32 to your Robot Inventor or Spike Prime hub
Our kit comes with a wire and you don’t have to worry about the connection. If you build your own, check the wiring table above.
- Connect the hub to your PC or Mac
Open the LEGO software and connect your hub. You can use Bluetooth or a USB wire. I prefer USB.
- Create a new Python program.
Press code, click new, and choose ‘python’
- Install mpy-robot-tools
Copy the installer script from GitHub and paste it into the Python program you just created. Note that GitHub has a handy copy button in the top-right of the script!
Run the pasted program once. It can look like the program freezes, but it takes a long time to download and run. You can check the console for output. The console is at the bottom of the screen of your LEGO app.
- Reboot the hub
Once the program has run, turn the hub off and on again. Ensure that the LEGO software does not ‘update’ the hub. If it does, cancel and disconnect.
- Run the test program
Clear the contents of the python file you used to install mpy-robot-tools and paste the gamepad test program to test the connection to the LMS-ESP32 board.
- Pair the gamepad with the LMS-ESP32 chip.
On the PS4 controller, you have to hold down the ‘Share’ and PS buttons for a few seconds. As soon as the gamepad is paired, the output should start showing the stick and button positions.
- Now write your program!
Explore the bluepad examples on GitHub and be sure to ask questions on Facebook and Patreon.
Which commands are available?
The Bluepad32 firmware for LMS-ESP is based on the marvelous BluePad32 project. We extended this project with the support for the UartRemote/SerialTalk commands that can be accessed from Lego Spike MicroPyhton. After initialization of the SerialTalk library using this code
from projects.mpy_robot_tools.serialtalk import SerialTalk from projects.mpy_robot_tools.mshub import MSHubSerial ur = SerialTalk( MSHubSerial('D'), timeout=20)
The following UartRemote/SerialTalk commands are available:
Checks whether a Gamepad is connected. Returns 1 when connected
Returns the status of the Gamepad with 6 parameters: (
Sets LEDs on the Gamepad (some gamepads support 4 LEDs) to the binary value
Initiates the rumble motor in the Gamepad with the given force and duration.
Returns the addresses of connected I2C devices. Note: returns a byte array.
len bytes from I2C device (connected to the Grove port) at address
Sets led number
led_nr to color
(red,green,blue). Use led_show to display the LEDs.
Shows current led configuration.
Initiates NeoPixel with
number_leds LEDs on Pin
pin. By default the number if LEDS is 64 on pin is 12.
Sets servo number
servo_nr to position
pos. Mapping is servo 1, 2, 3, and 4 on pins 21, 22, 23, and 25. Currently, only servo1 is supported.
What’s next after connecting the gamepad
We haven’t gotten around to fully documenting all possibilities of the firmware we developed. We would appreciate some help there! In the meantime, you can look at the code of the central program file to figure out what call you can try. Also, check out the possibilities in mpy-robot-tools by scanning through the code here.
If you enjoy this guide, be sure to subscribe on Youtube. You can also check out Patreon for access to more building instructions. Patreon supporters get priority answers when using my guides. Share your work and let us know what you’ve made!