Making a simple button box for PC games/emulation

Hi all, I'm having issues and I hope someone can assist. I want to create a relatively simple bluetooth button box for use with emulation, specifically light gun games. All I want is it to have directional inputs plus approx 5 buttons to do basic stuff like provide credits, start, escape from the game, etc.

I am using a Lolin32 lite board which has onboard battery charging & BT. I got this code from a YouTube video and have altered it slightly. I know nothing about Arduino coding so I'm not sure what's going on. After uploading the sketch to the board, it connects to my PC via BT no problem, but when shorting out a respective pin, there is no output. I would've expected something to type when testing in notepad.

I have also tested the GPIO pins using a multimeter and they are turning high with 3.3v so it seems to be working. Any help would be greatly appreciated as I'm tearing my hair out (what's left of it, lol). Code is below, thanks.

However if there is a different code which will serve me better, I'd love to hear it.

#define USE_NIMBLE
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

#define GP_UP 32
#define GP_DOWN 33
#define GP_LEFT 25
#define GP_RIGHT 26
#define GP_COIN 13
#define GP_START 15
#define GP_EXTRA 2
#define GP_RETURN 0
#define GP_ESCAPE 4


bool keyStates[9] = {false, false, false, false, false, false, false, false, false};
int keyPins[9] = {GP_UP, GP_DOWN, GP_LEFT, GP_RIGHT, GP_COIN, GP_START, GP_EXTRA, GP_RETURN, GP_ESCAPE};
uint8_t keyCodes[9] = {'KEY_UP_ARROW', 'KEY_DOWN_ARROW', 'KEY_LEFT_ARROW', 'KEY_RIGHT_ARROW', '5', '1', 'KEY_SPACE', 'KEY_RETURN', 'KEY_ESC'};


void setup() {
  Serial.begin(115200);
  Serial.println("Code running...");
  setInputs();
  bleKeyboard.begin();
}

bool connectNotificationSent = false;

void loop() {
  int counter;
  if(bleKeyboard.isConnected()) {
    if (!connectNotificationSent) {
      Serial.println("Code connected...");
      connectNotificationSent = true;
    }
    for(counter = 0; counter < 9; counter ++){
      handleButton(counter);
    }
  }
}

void setInputs() {
  pinMode(GP_UP, INPUT_PULLUP);
  pinMode(GP_DOWN, INPUT_PULLUP);
  pinMode(GP_LEFT, INPUT_PULLUP);
  pinMode(GP_RIGHT, INPUT_PULLUP);
  pinMode(GP_COIN, INPUT_PULLUP);
  pinMode(GP_START, INPUT_PULLUP);
  pinMode(GP_EXTRA, INPUT_PULLUP);
  pinMode(GP_RETURN, INPUT_PULLUP);
  pinMode(GP_ESCAPE, INPUT_PULLUP);
}

void handleButton(int keyIndex){
  // handle the button press
  if (!digitalRead(keyPins[keyIndex])){
    // button pressed
    if (!keyStates[keyIndex]){
      // key not currently pressed
      keyStates[keyIndex] = true;
      bleKeyboard.press(keyCodes[keyIndex]);
    }
  }
  else {
    // button not pressed
    if (keyStates[keyIndex]){
      // key currently pressed
      keyStates[keyIndex] = false;
      bleKeyboard.release(keyCodes[keyIndex]);
    }
  }
}

Did it work properly before you altered it?

1 Like

Hi, thanks for the reply.

I did try that and no it didn't, as far as I know. I thought it might've been an issue with my board or it wasn't compatible, but other people claim it was working ok with the Lolin32. I also tried another PC and same thing.

It compiles and writes ok so I don't know.

HI @dreamcazman

welcome to the arduino-forum.

As soon as you leave the ready to use products-zone like ready to use USB/bluetooth-devices you have to take care of more details than just

"does the plug fit into the socket?"

And this requires to provide a pretty good amount of detail-information about:

  • your knowledge in electronics
  • how you wired things together
  • what exact hardware you were using

The code configures the button-input-pins as INPUT_PULLUP.

Did you connect your buttons between IO-pin and ground?

Did you check in the device-manager if your microcontroller shows up as HID-device?

The code you have posted seems to send keystrokes as soon as a button is pressed.
There seems to be no possability to make the device stop sending keystrokes.
This is a specialty about HID-keyboard-devices:
If your code will send always keystrokes you can find yourself in the situation that you want to modify your sourcecode but as the device is connected and sends keystrokes these keystrokes are typed into your sourcecode as soon as you put the cursor-focus into the IDE for typing.

So there should be a possibility to have your device connected to your computer for uploading a new code-version but the device is forced to not send any keystrokes.
This can be realised with another switch and code that makes sending keystrokes conditional to the off/on-position of this switch.

best regards Stefan

Buttons bounce, don't see no debouncing code??

~q

Why wouldn't it? The code can be fine but not for your board / wiring / random reason.

what do you mean by this specifically??
do you not have buttons, just shorting wires to ground??
that would bounce even worse..

~q

I am fairly proficient in electronics, not programming.
Still testing at the moment, will eventually be using momentary switches.

In Device Manager, it shows up under Bluetooth as 'ESP32 Keyboard', not as a HID device.

What code would I need for this?

Shorting out say GPIO 13 with ground using a wire (like a switch would do).

I am testing out a few other test sketches atm, I have enabled the monitor so I can see it doing something, just doesn't output to my PC. I'll check a few other things in Windows too.

something like this..

unsigned long debounceButtons[9];
int intervalDebounce = 50;

void handleButton(int keyIndex){
  // handle the button press
 if (millis()- debounceButtons[keyIndex]>=intervalDebounce){ 
  if (!digitalRead(keyPins[keyIndex])){
    // button pressed
    if (!keyStates[keyIndex]){
      debounceButtons[keyIndex]= millis();
      // key not currently pressed
      keyStates[keyIndex] = true;
      bleKeyboard.press(keyCodes[keyIndex]);
    }
  }
  else {
    // button not pressed
    if (keyStates[keyIndex]){
      debounceButtons[keyIndex]= millis();
      // key currently pressed
      keyStates[keyIndex] = false;
     bleKeyboard.release(keyCodes[keyIndex]);
    }
  }
}
}

untested sorry.. ~q

these are unusal words. Do you mean:

IOpin 13 is configured as INPUT_PULLUP
"If I connect the IO-Pin 13 with ground I would expect seeing a "5" printed into notepad
if notepad has the keyboard-focus"?

If I run the code and if I measure voltage between IO-pin13 and ground I measure 3.3V
If I connect a wire from io-pin 13 to ground I measure 0V on IO-pin 13

again: you should describe with more precision and more details what you did

best regards Stefan

I have found an alternative BLE keyboard code, flashed it and it appears to be working in it's default form. It also shows up as a HID device whereas the other code didn't.

I'll make some changes and will update accordingly, thanks all.

1 Like

Here is a demo-code that provides more details what is going on through using the onboard-LED of ESP32-nodeMCU-boards and printing to the serial monitor.

It uses the onboard-EN-button.
on ESP32-nodeMCU-Boards the EN-button is connected to GPIO-0.
If the EN-button is pressed the code sends characters and a press of the return-key.

// This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete

#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;

// on ESP32-nodeMCU-boards there is a button on the board
// that is connected to the GPIO-pin 0 (zero)
const byte onBoard_EN_Button = 0;

#define BLE_DEVICE_NAME "my-ESP32-BLE-Keyboard"

void setup() {
  pinMode(onBoard_EN_Button, INPUT_PULLUP);

  Serial.begin(115200);
  while (!Serial);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  Serial.println("You have to connect the BLE-device");
  Serial.print("with the name ");
  Serial.println(BLE_DEVICE_NAME);
  Serial.println("to your computer or tablet or smartphone");

  bleKeyboard.setName(BLE_DEVICE_NAME);
  bleKeyboard.begin();
  Serial.println("bleKeyboard.begin() done");
  Serial.println("waiting for bleKeyboard.isConnected()");
  Serial.println("blinking fast until bleKeyboard.isConnected()");

  while ( !bleKeyboard.isConnected() ) {
    unsigned long myTimer;
    yield();
    BlinkHeartBeatLED(OnBoard_LED, 100);
    if ( TimePeriodIsOver(myTimer, 500) ) {
      Serial.print(".");
    }
  }
  Serial.println();
  
  if (bleKeyboard.isConnected()) {
    Serial.println("bleKeyboard.isConnected()");
    Serial.println("blinking slower 1 Hz");
    Serial.println("press EN-Button (IO-Pin 0) to send keystrokes");
  }
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 500);

  if (digitalRead(onBoard_EN_Button) == LOW) {
    Serial.println("EN-Button presssed");
    Serial.println("delay(100) for cheap debouncing");
    delay(100);

    if (bleKeyboard.isConnected()) {
      Serial.println("send Hello world");
      bleKeyboard.print("Hello world");

      Serial.println("Sending Enter key...");
      bleKeyboard.write(KEY_RETURN);

      Serial.println("Waiting 3 seconds...");
      delay(3000);
      Serial.println("ready for new button-press");
    }
  }
}

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

printing to the serial monitor looks like this

07:31:57.750 -> Setup-Start
07:31:57.750 -> Code running comes from file 
07:31:57.750 -> F:\myData\Arduino\ESP32-BLE-Keyboad-Demo_002\ESP32-BLE-Keyboad-Demo_002.ino
07:31:57.750 ->   compiled Oct 14 2023 07:31:21
07:31:57.750 -> You have to connect the BLE-device
07:31:57.750 -> with the name my-ESP32-BLE-Keyboard
07:31:57.750 -> to your computer or tablet or smartphone
07:31:58.444 -> bleKeyboard.begin() done
07:31:58.444 -> waiting for bleKeyboard.isConnected()
07:31:58.444 -> blinking fast until bleKeyboard.isConnected()
07:31:58.444 -> ..
07:31:59.178 -> bleKeyboard.isConnected()
07:31:59.178 -> blinking slower 1 Hz
07:31:59.178 -> press EN-Button (IO-Pin 0) to send keystrokes
07:32:06.276 -> EN-Button presssed
07:32:06.276 -> delay(100) for cheap debouncing
07:32:06.349 -> send Hello world
07:32:06.384 -> Sending Enter key...
07:32:06.384 -> Waiting 3 seconds...
07:32:09.361 -> ready for new button-press
07:32:13.520 -> EN-Button presssed
07:32:13.520 -> delay(100) for cheap debouncing
07:32:13.591 -> send Hello world
07:32:13.628 -> Sending Enter key...
07:32:13.628 -> Waiting 3 seconds...
07:32:16.615 -> ready for new button-press

best regards Stefan

1 Like

Just a quick update, I managed to get it working after a lot of trial and error. I scrapped what I was using and reinstalled all the software again, effectively starting from scratch. I have no idea why it wasn't working previously.

This code now works, the special keys I was able to configure using some decimal values I found. :wink:

btw @qubits-us , your debounce code worked perfectly, thanks.

#define USE_NIMBLE
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

#define Up 26
#define Down 27
#define Left 14
#define Right 12
#define Coin 13
#define Start 15
#define Spacebar 2
#define Enter 0
#define Escape 4

bool keyStates[9] = {false, false, false, false, false, false, false, false, false};
int keyPins[9] = {Up, Down, Left, Right, Coin, Start, Spacebar, Enter, Escape};
uint8_t keyCodes[9] = {(218), (217), (216), (215), '5', '1', (180), (176), (177)};


void setup() {
  Serial.begin(115200);
  Serial.println("Code running...");
  setInputs();
  bleKeyboard.begin();
}

bool connectNotificationSent = false;

void loop() {
  int counter;
  if(bleKeyboard.isConnected()) {
    if (!connectNotificationSent) {
      Serial.println("Code connected...");
      connectNotificationSent = true;
    }
    for(counter = 0; counter < 9; counter ++){
      handleButton(counter);
    }
  }
}

void setInputs() {
    pinMode(Up, INPUT_PULLUP);
    pinMode(Down, INPUT_PULLUP);
    pinMode(Left, INPUT_PULLUP);
    pinMode(Right, INPUT_PULLUP);
    pinMode(Coin, INPUT_PULLUP);
    pinMode(Start, INPUT_PULLUP);
    pinMode(Spacebar, INPUT_PULLUP);
    pinMode(Enter, INPUT_PULLUP);
    pinMode(Escape, INPUT_PULLUP);
}

unsigned long debounceButtons[9];
int intervalDebounce = 50;

void handleButton(int keyIndex){
  // handle the button press
 if (millis()- debounceButtons[keyIndex]>=intervalDebounce){ 
  if (!digitalRead(keyPins[keyIndex])){
    // button pressed
    if (!keyStates[keyIndex]){
      debounceButtons[keyIndex]= millis();
      // key not currently pressed
      keyStates[keyIndex] = true;
      bleKeyboard.press(keyCodes[keyIndex]);
    }
  }
  else {
    // button not pressed
    if (keyStates[keyIndex]){
      debounceButtons[keyIndex]= millis();
      // key currently pressed
      keyStates[keyIndex] = false;
      bleKeyboard.release(keyCodes[keyIndex]);
    }
  }
}
}  
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.