It is now possible to use Bluetooth gamepads both in Arduino and CircuitPython projects.
This means that you can use your PlayStation (PS3, PS4, PS5), Nintendo (Wii, Wii U, Switch) and Xbox One S gamepads in your electronics project: control a robot, home-automation, video games, etc… everything controlled from your favorite gamepad.
The catch is that not every Arduino or CircuitPython boards are supported. In fact, only a few of them are supported.
For Arduino, the boards that have the NINA-W10x (ESP32) co-processor are supported, like:
- Arduino Nano RP2040 Connect (great board, get one if you haven’t already)
- Arduino Nano 33 IoT
- Arduino MKR WiFi 1010
- Arduino MKR VIDOR 4000 WiFi
- Arduino Uno WiFi Rev 2
- …and probably a few more. If it has the NINA-W10x co-processor, it is supported.
And similar for CircuitPython, the boards that have the AirLift (ESP32) co-processor are supported, like:
- Adafruit MatrixPortal M4 (great to create a video-game console)
- Adafruit Metro M4 Express AirLift
- Adafruit PyPortal
- Adafruit PyBadge
With the additional benefit that you can use any CircuitPython board by attaching the stand-alone AirLift module:
How does the co-processor work
Before describing how Bluepad32 works, it is better to first describe how WiFi works on NINA and AirLift co-processors (ESP32).
Both Arduino and CircuitPython use the co-processor mostly as WiFi modules. In order to use WiFi, you would use:
- WiFiNINA library, in Arduino
- ESP32SPI library, in CircuitPython
These two libraries (WiFiNINA and ESP32SPI) have the same functionality. In fact they are compatible. The only difference is that WiFININA is written in C++ , and ESP32SPI in Python.
The co-processor (ESP32) comes pre-flashed with the Arduino NINA firmware.
The ESP32 (A) connects to the internet using WiFi. And then sends the data back to the main processor (B). It uses a protocol that has some predefined messages like:
- Open HTTP connection and get data
- Enumerate SSID networks
- Connect to SSID network
How does Bluepad32 work
Now that we know how WiFi works, it is easier to explain how Bluepad32 works. Similar to WiFi, Bluepad32 has two main parts:
- Bluepad32 firmware
- Bluepad32 library
The gamepad connects to the ESP32 (A) using Bluetooth. And the ESP32 (A) sends the gamepad data to the main processor (B). It uses the same protocol used by the NINA firmware, but with some extended messages like:
- Get gamepad data
- Set rumble on gamepad
- Set player LEDs on gamepad
Bluepad32 firmware has the following features:
- Has Bluetooth gamepad support
- Runs on the co-processor (ESP32)
- Replaces the Arduino NINA firmware
- Uses the same NINA firmware protocol
- There are two variants of the firmware:
Bluepad32 library has the following features:
- Runs on the main processor
- Fetches that gamepad data from the co-processor
- There are two version of the library:
How to use it in Arduino
Bluepad32 library is part of the official Arduino library registry, so, you can install like any other Arduino library:
Arduino IDE -> Tools -> Manage Libraries -> Search for “bluepad32” and install.
And it comes already with an example that shows how to use it:
Arduino IDE -> File -> Examples -> Bluepad32 -> Gamepad
To flash the Bluepad32 firmware in your Arduino board, please follow these instructions:
How to use it in CircuitPython
Bluepad32 library is part of the official CircuitPython Community Library Bundle. You can install it like any other CircuitPython library. Install circup and then do:
$ circup install bluepad32
This example shows how to use Bluepad32 library for CircuitPython:
And to flash the Bluepad32 firmware in your CircuitPython board, please follow these instructions:
NINA vs AirLift vs ESP32
NINA-W10x are ESP32 modules. They are similar in functionality to the ESP32-WROOM-32 modules. Although the NINA modules are built by u-blox, and not by Espressif (do not confuse a ESP32 module with the ESP32 chip).
u-blox, in addition to the NINA-W10x modules, makes other modules that are not ESP32-based. But for the sake of simplicity, in this article, when we mention “NINA”, we talk about the NINA ESP32-based modules.
The NINA modules come pre-flashed with the Arduino NINA firmware, and in order to talk to them, you use the the WiFiNINA library.
Makes sense, it is consistent:
- Module name: NINA-W10x
- Firmware name: Arduino NINA-W10x firmware
- Library name: WiFiNINA
AirLift modules, on the other hand, are ESP32-WROOM-32 modules. I guess “AirLift” was created as a marketing name. But what is confusing is that they come pre-flashed with a fork of the Arduino NINA firmware, and the library name is called ESP32SPI.
It is confusing:
- Module name: AirLift
- Firmware name: Adafruit NINA-W10x firmware (fork of Arduino’s).
- Library name: ESP32SPI
(Adafruit, if you are reading, I’d rename them to “AirLift firmware”, and “AirLift library”).
Whether it is a NINA-W10x, an AirLift module or an ESP32-WROOM32, all of them have the ESP32 chip inside. And all of them can run the NINA firmware, or the Bluepad32 firmware, or mostly any other ESP32 firmware that are out there.
So, if all of them are ESP32-based, and if WiFiNINA and ESP32SPI library are compatible, why did Adafruit fork Arduino’s NINA firmware? So far, the only incompatibility that I found, is that the MOSI pin (from SPI) is different.
GPIO 12 is strapping pin, and at boot time it is used internally by the ESP32. It seems that it was causing some interference with some modules.
And just because of that change (different MOSI pins) I needed to create a “Bluepad32 firmware for NINA” and a “Bluepad32 firmware for AirLift”.
4 thoughts on “Bluepad32 support for Arduino and CircuitPython”
Hi Ricardo – I was very happy to find your BluePad32 library for a project I’m working on. Unfortunately I cannot get the GamePad example to compiler for an Arduino Uno WiFi Rev2 board. The problem seems to be in the call to functional (#include ) in the Bluepad32.h file. I am able to successfully compile when I target an MKR WiFi 1010 board so I suspect it has something to do with the fact that the MKR is an ARM based uC and the Uno is a AVR based uC.
Have you noticed this issue and any advice for how to get around it?
Any chance that you can file a bug including the full error log?
Thanks for the quick response. I just logged the issue. Thanks for looking at it!