Git repository: make a wireless steer, buttonbox or shifter with esp32 for little money

During my christmas holiday I have made software for a wireless wheel with paddles and buttons using a (cheap) esp32. If you made a wired solution with an arduino, maybe this software and the esp32 can replace this.

Features:
-batterywarning (i use 3 AAA batteries)
-standby mode
-bluetooth ble
-range: 30 meter (i dont know why you should need it but its a feature ;-) )
-40 hrs with the 3 aaa batteries (in standby mode this is of course a lot longer)

it's on: https://github.com/beastdjw/bluetoothGameControllerESP32
If you got any questions/remarks let me know.

Its work in progress...
 
Awesome. Will get it modified to my config and see how it works. IIRC I commented out the inputs that need pullups and used some of the encoder 3 and 4 inputs for for additional buttons. The .INO shows interrupts for buttons 1-16 although you have defined 23 buttons. Is 16 all you can do or should I add statements for all my button inputs?
 
Last edited:
Upvote 0
In windows you see 25 buttons available, this matches 25 gpio pins. In total this is 4 encoders (2 gpio pins each -> 8 buttons in windows) and 17 (25-8) buttons. Of course you can change this, but you can not add buttons. At my esp32 25 gpios is the limitation. So i.e. you can configure 2 encoders (2x2 gpios) and 21 buttons. (if you have the same esp32 as I have). To disable encoders or buttons you simply can comment out the line with the attachinterrupt for the particular gpio you want to disable.
 
Upvote 0
Yep. Correct branch. Seems to work OK. I just expected to get a button press per rotary encoder detent. Also cannot get it to wake from sleep. Powering with USB vs. battery input, could that be the reason? Once it sleeps, no button presses will wake it, only a reboot.
 
Upvote 0
Your expectation is right, each click of the rotary encoder should lead to a button press.
I really got no clue why its not working at your situation. What you can do is to do a "Serial.println" at every change on your rotary encoder in the interrupt routine. So you can see if thats working.

This line takes care to get out of standby:

//Configure pin as ext0 wake up source for HIGH logic level to activate esp32 out of sleep (standby)
esp_sleep_enable_ext0_wakeup(BUTTON1, LOW); esp_sleep_enable_ext0_wakeup(BUTTON1, LOW);

Dont just change Button1 to another button, cause not every gpio works with this option.
 
Upvote 0
The encoder issue I was having was that the rotary.h library can work in either full or half step mode. switched from full step and now it presses every other detent. Wonder if it could be re-written to work in 1/4 step mode so there would be a press for every detent of the encoder.
 
Upvote 0
Something I've done in the .ino has made my encoders stop working. Edited and got rid of Rotary 1 and 4 to use those inputs for buttons. Not a hardware issue since your encoders work if I flash your stock .ino file.
 

Attachments

  • mainSteerBlueTootButtonsEncoders.txt
    12.5 KB · Views: 140
Upvote 0
My first entry is a bit misleading. The changes for the battery update were done within NimBLE 1.4.1. This version is a dependency for BLE32gamepad...
Coming from a quite old version of ble32gamepad, I had to change my code. They added a configuration api. Anything different from the defaults needs to be configured using bleGamepadConfig.
 
Upvote 0
Finally got my button box project going. However Im having trouble compiling the code?

Stops on this row.
bleGamepad.begin(25, 0, false, false, false, false, false, false, false, false, false, false, false, false, false);



Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: "ESP32-WROOM-DA Module, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 921600, Core 1, Core 1, None, Disabled"


G:\Files\ESP32\mainSteerBlueTootButtonsEncoders\mainSteerBlueTootButtonsEncoders.ino: In function 'void setup()':

mainSteerBlueTootButtonsEncoders:170:116: error: no matching function for call to 'BleGamepad::begin(int, int, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool)'

bleGamepad.begin(25, 0, false, false, false, false, false, false, false, false, false, false, false, false, false);

^

In file included from G:\Files\ESP32\mainSteerBlueTootButtonsEncoders\mainSteerBlueTootButtonsEncoders.ino:1:

C:\Users\laffe\Documents\Arduino\libraries\ESP32-BLE-Gamepad/BleGamepad.h:50:10: note: candidate: 'void BleGamepad::begin(BleGamepadConfiguration*)'

void begin(BleGamepadConfiguration *config = new BleGamepadConfiguration());

^~~~~

C:\Users\laffe\Documents\Arduino\libraries\ESP32-BLE-Gamepad/BleGamepad.h:50:10: note: candidate expects 1 argument, 15 provided

exit status 1

no matching function for call to 'BleGamepad::begin(int, int, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool)'



This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
Nimble arduino 1.4.1
lemmingDev/ESP32-BLE-Gamepad 5.3
those I have added to my library
 
Upvote 0
Configuration of the gamepad is now done by an extra object.
Here is an example taken from DrivingControllerTest.ino
C-like:
BleGamepad bleGamepad("BLE Driving Controller", "lemmingDev", 100);

void setup()
{
    Serial.begin(115200);
    Serial.println("Starting BLE work!");

    // Setup controller with 10 buttons, accelerator, brake and steering
    BleGamepadConfiguration bleGamepadConfig;
    bleGamepadConfig.setAutoReport(false);
    bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS
    bleGamepadConfig.setButtonCount(numOfButtons);
    bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2);      // Can also be done per-axis individually. All are true by default
    bleGamepadConfig.setWhichSimulationControls(enableRudder, enableThrottle, enableAccelerator, enableBrake, enableSteering); // Can also be done per-control individually. All are false by default
    bleGamepadConfig.setHatSwitchCount(numOfHatSwitches);                                                                      // 1 by default
    // Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default
    bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal
    bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal
   
    bleGamepad.begin(&bleGamepadConfig);
    // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again

    // Set accelerator and brake to min
    bleGamepad.setAccelerator(-32767);
    bleGamepad.setBrake(-32767);

    // Set steering to center
    bleGamepad.setSteering(0);
}
 
Last edited:
Upvote 0
If I get your code right you wan't to have a gamepad with 25 buttons and no hat switches, sliders an so on.
In the setup function you need to add something like this:
Code:
BleGamepadConfiguration bleGamepadConfig;
bleGamepadConfig.setButtonCount(25);

bleGamepad.begin(&bleGamepadConfig);
 
Upvote 0
Hi, Beast.
I really noob at this coding matters.
Can you help me with the coding.
I already make my button box using this code (from wirelessgamehub.ino).
it works great, but the battery life is so poor, and I realize its so different with your code. It doesn't included the deep sleep mode and the wake up feature.
so which line should I write to make a better battery life?
sorry for my english.
TIA.. cheers!

the code is bellow
#include <ESP32Encoder.h> // https://github.com/madhephaestus/ESP32Encoder/
#include <Keypad.h> // https://github.com/Chris--A/Keypad
#include <BleGamepad.h> // https://github.com/MagnusThome/ESP32-BLE-Gamepad

BleGamepad bleGamepad("WirelessGameHub", "breeze", 100);

////////////////////// BUTTON MATRIX //////////////////////
#define ROWS 5
#define COLS 4
uint8_t rowPins[ROWS] = {13, 14, 15, 16, 33};
uint8_t colPins[COLS] = {17, 18, 19, 21};
uint8_t keymap[ROWS][COLS] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
{12,13,14,15},
{24,25,26,27}
};
Keypad customKeypad = Keypad( makeKeymap(keymap), rowPins, colPins, ROWS, COLS);

//////////// ROTARY ENCODERS (WITH PUSH SWITCHES) ////////////
#define MAXENC 4
uint8_t uppPin[MAXENC] = {22, 25, 27, 04};
uint8_t dwnPin[MAXENC] = {23, 26, 32, 05};
uint8_t encoderUpp[MAXENC] = {16,18,20,22};
uint8_t encoderDwn[MAXENC] = {17,19,21,23};
ESP32Encoder encoder[MAXENC];
unsigned long holdoff[MAXENC] = {0,0,0,0};
int32_t prevenccntr[MAXENC] = {0,0,0,0};
bool prevprs[MAXENC] = {0,0,0,0};
#define HOLDOFFTIME 150





////////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
for (uint8_t i=0; i<MAXENC; i++) {
encoder.clearCount();
encoder.attachSingleEdge(dwnPin, uppPin);
}
customKeypad.addEventListener(keypadEvent);
customKeypad.setHoldTime(1);
bleGamepad.begin();
Serial.println("Booted!");
}


////////////////////////////////////////////////////////////////////////////////////////
void loop() {
unsigned long now = millis();

// -- ROTARY ENCODERS : ROTATION -- //
for (uint8_t i=0; i<MAXENC; i++) {
int32_t cntr = encoder.getCount();
if (cntr!=prevenccntr) {
if (!holdoff) {
if (cntr>prevenccntr) { sendKey(encoderUpp); }
if (cntr<prevenccntr) { sendKey(encoderDwn); }
holdoff = now;
if (holdoff==0) holdoff = 1; // SAFEGUARD WRAP AROUND OF millis() (WHICH IS TO 0) SINCE holdoff==0 HAS A SPECIAL MEANING ABOVE
}
else if (now-holdoff > HOLDOFFTIME) {
prevenccntr = encoder.getCount();
holdoff = 0;
}
}

// -- ROTARY ENCODERS : PUSH SWITCH -- //
}
customKeypad.getKey(); // READ BUTTON MATRIX (EVENT CALLBACK SETUP)
//delay(10);

}



////////////////////////////////////////////////////////////////////////////////////////
void keypadEvent(KeypadEvent key){
uint8_t keystate = customKeypad.getState();
if (keystate==PRESSED) { pressKey(key); }
if (keystate==RELEASED) { releaseKey(key); }
}

////////////////////////////////////////////////////////////////////////////////////////
void sendKey(uint8_t key) {
uint32_t gamepadbutton = pow(2,key); // CONVERT TO THE BINARY MAPPING GAMEPAD KEYS USE
Serial.print("pulse\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.press(gamepadbutton);
delay(150);
bleGamepad.release(gamepadbutton);
}
}
void pressKey(uint8_t key) {
uint32_t gamepadbutton = pow(2,key); // CONVERT TO THE BINARY MAPPING GAMEPAD KEYS USE
Serial.print("hold\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.press(gamepadbutton);
}
}
void releaseKey(uint8_t key) {
uint32_t gamepadbutton = pow(2,key); // CONVERT TO THE BINARY MAPPING GAMEPAD KEYS USE
Serial.print("release\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.release(gamepadbutton);
}
}


////////////////////////////////////////////////////////////////////////////////////////
 
Upvote 0
Back
Top