ESP32 Bluetooth Gamepad

A bluetooth Gamepad with an analog joystick

Public Chat
Similar projects worth following
I really liked the gamepad designed by Billiam, (, but since I have a gaming laptop, I didn't want to have to deal with the cable. So I decided to replace the Teensy with an ESP32, and make it bluetooth.

This keypad enumerates as a bluetooth HID device using the Keyboard and Gamepad usages. It advertises itself as a gamepad device, but it also emits standard keyboard keypresses.

I've designed a custom circuit board that includes an ESP32, LDO voltage regulator, and a Li-Ion battery charger with thermal protection. If you decide to make one for yourself, you are responsible for your batteries. I make no guarantees as to the performance or function of this circuit. That said, the design is largely mimicking the layout found in the datasheet, with power path management more-or-less copied from a Microchip Technology app note on the subject. From my admittedly limited testing, everything appears to be fully functional.

  • 1 × ESP32-WROOM-32E
  • 1 × MIC37303YME-TR 3A Low Voltage LDO Regulator
  • 1 × LTC1730ES8 Battery Management ICs

  • MCU Weirdness

    tyrigi02/19/2021 at 19:10 0 comments

    With the V4 board and the resulting GPIO scramble to accommodate an SD card, I started having an issue. The top two rows were behaving weirdly. On boot, the keypad would report all keys in the top two rows pressed. When I actually pressed a key in that row, all other keys in the row would be recognized as released. When I released the pressed key, the keypad would report the key as being released, wait a small increment, then show the whole row as pressed again. I did some digging in the ESP datasheet, and turns out the pins I'm using for those rows are also used for mode selection during boot.

    Long story short: those two pins have internal hardware that causes a positive charge to accumulate on row 0's pin, and a negative charge to accumulate on row 1's pin. It's weak enough to take a few tens of ms to sway the digital state of the pin, but strong enough to persist through multiple reads of the pin's digital state. The fix, crude as it is, is to "slap" the pins high and low, then switch back to an input state just before taking a reading. I could probably fix this with a hardware change, but I'm frankly tired of doing board revisions, and I'd have to do some weird stuff to avoid holding the pins in a weird state during boot which would change some options I don't want changed. Like internal flash voltage level.

  • V4 PCB

    tyrigi02/19/2021 at 05:26 0 comments

    Version 4 of the PCB has arrived and been assembled. I switched around what GPIO is being used for what so that I could add an SD card to the spi interface, but now keypad scanning isn't working properly. The top two rows recognize all keys in the row as being pressed if any key in the row is pressed. Not sure yet if this is just a software thing, or if I'm using GPIO pins that have different pull-up resistor criteria, since those rows are ones that are using different pins now. I may have to do another board version with external pull-up resistors.

    Since the code is misbehaving, I'm taking the opportunity to clean stuff up and add more comments. Doesn't do anything to the code, and it makes it easier for anyone else to load it up and figure out what I'm doing. I've had to reverse-engineer weird code too many times. The less of that in the world, the better so far as I'm concerned.

  • Catching up to current state

    tyrigi02/01/2021 at 02:05 0 comments

    I'm starting this log about 80% of the way through the project, so I'll attempt to re-create the timeline faithfully.

    First off, I started by building Billiam's Sherbet Keypad ( I won't go into much in the way of details, since he's done a great write-up of his own. I liked it, and it worked well, but I wanted a wireless version. So I decided to add an ESP32. I chose this chip since A) I knew it had bluetooth, and B) I plan on using this chip for other projects in the future. Plus it's really cheap and capable. So I started with a dead-bug style breakout, wired up the keypad to some GPIO, and got cracking. 

    Problem #1
    The keypad uses the Teensy USB HID arduino libraries to enumerate as a keyboard and generic joystick. The problem is, that I'm working in PlatformIO with Visual Studio Code, and I'm using a different chip with a different architecture. The biggest issue is that, apart from the Teensyduino libraries, it appears as though no-one has ever made a library that enables a device to enumerate as both a keyboard and joystick. Understandable, since the two don't normally play together super great, but with the addition of Steam's controller stuff, a good number of games can seamlessly switch between the two inputs. If you don't mind some icons flickering, it works really well.  The lack of library meant I'd have to make my own. Fortunately, there are already a number of libraries for the ESP32 that enable it to enumerate as a keyboard and as a gamepad over bluetooth. Just not at the same time. I'm using elements from these two libraries:

    Smashing these two libraries into one isn't as easy as copy-paste though. Fortunately, at least for HID, bluetooth just acts like a wireless bridge. Both ends of the wireless bridge are actually using USB HID, so I can use the USB documents for information on the enumeration process. There's a pretty dense and unhelpful description of the tables in this document:

    For a bit more comprehensible explanation, I found these pages:

    I was then able to dig into the Teensyduino libraries, and slowly start to make sense of what I was seeing by comparing with the two ESP32 libraries I linked above. Over time, I managed to get what I thought was a functionally correct USB HID descriptor put together, but I just couldn't seem to make it work. Then I found a site that parses a USB HID descriptor from a list of hex numbers:

    It's an old page and looks like it's been abandoned since 2016, but it still works just fine. I was able to use this to figure out a couple of issues by comparing the interpreted output against what I was expecting, and I actually got something functional!

    Problem #2

    The keypad was now enumerating as a gamepad, and successfully emitting keypresses, button presses, and joystick position values. But, when I went back into Steam and tried to use it with a game, I ran into an issue. Steam was showing two XBox compatible controllers. I thought I might have accidentally left something plugged into my computer, but no. And when I launched a game and tried using the gamepad, I got erratic outputs. Nothing was working correctly. Somehow, there was a second channel enumerated and interfering with the keypress and joystick commands. Eventually, after several hours of google-fu, I found a hint: XBox One controllers used to exhibit the same issue, but Steam had since introduced a patch. I thought on this for a while, and eventually came up with an idea. One of the differences between the 360 and One eras...

    Read more »

View all 3 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates