๐Ÿ”„ GSPro BLE Controller: Upgraded from ESP8266 to ESP32

GSPro BLE Controller: Upgraded from ESP8266 to ESP32

After building my first GSPro controller using ESP8266, I decided to upgrade to ESP32 โ€” and it was absolutely worth it. This post walks through the upgrade, button mapping, BLE setup, and everything you need to know to make it work smoothly.

๐Ÿ‘‰ If you missed the original ESP8266 version, check it out here:
๐Ÿ”— GSPro DIY Controller with ESP8266 (Legacy Version)


GSPro Controller DIY

โœ… Why I Switched from ESP8266 to ESP32

While the ESP8266 worked well for a basic controller, it had limitations:

  • โŒ No built-in Bluetooth HID support
  • โŒ Fewer GPIOs (button inputs were tight)
  • โŒ No native BLE keyboard libraries

By switching to ESP32, I gained:

  • โœ… Bluetooth HID (BLE Keyboard) functionality โ€” no USB dongle needed
  • โœ… More GPIOs to handle buttons + joystick cleanly
  • โœ… Strong community support and robust libraries

๐ŸŽฎ Final Key Mapping

I mapped the joystick and buttons to cover all major in-game functions. Hereโ€™s my setup:

๐ŸŽฏ Joystick (for Aim)

DirectionKey SentGPIO
Upโ†‘ arrow14
Downโ†“ arrow27
Leftโ† arrow13
Rightโ†’ arrow12

๐ŸŸก Buttons

FunctionKeyGPIO
Reset Aima18
MulliganCtrl+m17
Club Upk26
Club Downi25
Tee Leftc32
Tee Rightv16
Flyovero33
Putter Selectu15
Target Viewj21

๐Ÿงฐ BLE Keyboard Setup with Arduino

To enable Bluetooth HID (keyboard emulation), I followed these steps:

  1. Installed Arduino ESP32 board package
    • Downgraded to version 2.0.11 for BLE compatibility
  2. Installed ESP32 BLE Keyboard library by T-vK
  3. Selected board: Tools > Board > ESP32 Dev Module
  4. Assigned all GPIOs using INPUT_PULLUP to prevent ghost presses
  5. Used Serial.println() for live debugging while testing

โœ… Now, once the ESP32 boots and pairs via Bluetooth, it shows up as โ€œGSPro Controllerโ€ and sends key inputs directly to GSPro โ€” no extra software needed.


๐Ÿงช Troubleshooting Notes

  • If your controller spams keys on boot, check for floating GPIOs (especially GPIO2)
  • If ESP32 doesnโ€™t show up on COM port, replace the USB cable (many are charge-only!)
  • Use the BOOT button if uploads hang on โ€œConnectingโ€ฆโ€

๐Ÿง  Final GSPro Controller Code for ESP32 (BLE Keyboard)

#include <BleKeyboard.h>

BleKeyboard bleKeyboard("GSPro Controller", "ESP32 Inc.", 100);

// Joystick (Aim) GPIOs
#define JOY_UP     14
#define JOY_DOWN   27
#define JOY_RIGHT  12
#define JOY_LEFT   13

// Button GPIOs
#define BTN_RESET_AIM  18  // a
#define BTN_MULLIGAN   17  // ctrl+m
#define BTN_CLUB_UP    26  // k
#define BTN_CLUB_DOWN  25  // i
#define BTN_TEE_LEFT   32  // c
#define BTN_TEE_RIGHT  16  // v
#define BTN_FLYOVER    33  // o
#define BTN_PUTTER     15  // u
#define BTN_TARGET     21  // j

bool lastState[14];

void setupButton(int pin, int index) {
  pinMode(pin, INPUT_PULLUP);
  lastState[index] = HIGH;
}

void handlePress(int pin, int index, uint8_t key, const char* label) {
  int state = digitalRead(pin);
  if (lastState[index] == HIGH && state == LOW) {
    Serial.print("Pressed: ");
    Serial.println(label);
    bleKeyboard.write(key);
  }
  lastState[index] = state;
}

void setup() {
  Serial.begin(115200);
  bleKeyboard.begin();

  setupButton(JOY_UP, 0);
  setupButton(JOY_DOWN, 1);
  setupButton(JOY_LEFT, 2);
  setupButton(JOY_RIGHT, 3);
  setupButton(BTN_RESET_AIM, 4);
  setupButton(BTN_MULLIGAN, 5);
  setupButton(BTN_CLUB_UP, 6);
  setupButton(BTN_CLUB_DOWN, 7);
  setupButton(BTN_TEE_LEFT, 8);
  setupButton(BTN_TEE_RIGHT, 9);
  setupButton(BTN_FLYOVER, 10);
  setupButton(BTN_PUTTER, 11);
  setupButton(BTN_TARGET, 12);
}

void loop() {
  if (!bleKeyboard.isConnected()) return;

  // Joystick
  handlePress(JOY_UP, 0, KEY_UP_ARROW, "Aim UP");
  handlePress(JOY_DOWN, 1, KEY_DOWN_ARROW, "Aim DOWN");
  handlePress(JOY_LEFT, 2, KEY_LEFT_ARROW, "Aim LEFT");
  handlePress(JOY_RIGHT, 3, KEY_RIGHT_ARROW, "Aim RIGHT");

  // Buttons
  handlePress(BTN_RESET_AIM, 4, 'a', "Reset Aim");
  handlePress(BTN_CLUB_UP, 6, 'k', "Club Up");
  handlePress(BTN_CLUB_DOWN, 7, 'i', "Club Down");
  handlePress(BTN_TEE_LEFT, 8, 'c', "Tee Left");
  handlePress(BTN_TEE_RIGHT, 9, 'v', "Tee Right");
  handlePress(BTN_FLYOVER, 10, 'o', "Flyover");
  handlePress(BTN_PUTTER, 11, 'u', "Putter");
  handlePress(BTN_TARGET, 12, 'j', "Target View");

  // Mulligan = Ctrl + M
  int currentM = digitalRead(BTN_MULLIGAN);
  if (lastState[5] == HIGH && currentM == LOW) {
    Serial.println("Pressed: Mulligan (Ctrl+M)");
    bleKeyboard.press(KEY_LEFT_CTRL);
    bleKeyboard.press('m');
    delay(100);
    bleKeyboard.releaseAll();
  }
  lastState[5] = currentM;

  delay(10);  // debounce
}

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *