ESP32-BLE-Gamepad
ESP32-BLE-Gamepad copied to clipboard
is it possible to use bluetoothserial.h???
hello @lemmingDev , just straight as the issue does, im wondering on how to make BLE-Gamepad to work with simhub read message too. since im doing some f1 wheel project for my simrig. there are some sample from my code.
#include <Arduino.h>
#include "BluetoothSerial.h"
#include <FastLED.h>
#include <keypad.h>
#include <BleGamepad.h>
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
BleGamepad bleGamepad("F1Wheel", "mbohben", 100); // Shows how you can customise the device name, manufacturer name and initial battery level
//def
#define BUF_SIZE 64
#define REV_LIGHTS_COUNT 16
#define WS2812_PIN 33
#define ROWS 5
#define COLS 4
#define numOfButtons 4
//fastled
char simHubMessageBuf[BUF_SIZE];
BluetoothSerial spp;
CRGB leds[REV_LIGHTS_COUNT];
const byte ColorNone[3] = {0, 0, 0};
const byte ColorOrange[3] = {168, 80, 0};
const byte ColorBlue[3] = {0, 0, 168};
const byte ColorGreen[3] = {0, 168, 0};
const byte ColorYellow[3] = {168, 168, 0};
const byte ColorRed[3] = {168, 0, 0};
int spd;
int revs;
//keypad
uint8_t rowPins[ROWS] = {13, 12, 14, 27, 26}; // ESP32 pins used for rows --> adjust to suit --> Pinout on board: R1, R2, R3, R4
uint8_t colPins[COLS] = {15, 4, 16, 17}; // ESP32 pins used for columns --> adjust to suit --> Pinout on board: Q1, Q2, Q3, Q4
uint8_t keymap[ROWS][COLS] =
{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16},
{17,18,19,20}
};
Keypad customKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, ROWS, COLS);
//direct button
byte previousButtonStates[numOfButtons];
byte currentButtonStates[numOfButtons];
byte buttonPins[numOfButtons] = {23, 22, 1 , 3};
byte physicalButtons[numOfButtons] = {21, 22, 23, 24};
void setup()
{
BleGamepadConfiguration bleGamepadConfig;
bleGamepadConfig.setAutoReport(false);
bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD);
bleGamepadConfig.setButtonCount(24);
bleGamepadConfig.setHatSwitchCount(0);
bleGamepadConfig.setWhichSpecialButtons(false, false, false, false, false, false, false, false);
bleGamepadConfig.setWhichAxes(false, false, false, false, false, false, false, false);
bleGamepadConfig.setWhichSimulationControls(false, false, false, false, false);
Serial.begin(115200);
Serial.println("setup");
memset(simHubMessageBuf, 0x0, BUF_SIZE);
bleGamepad.begin(&bleGamepadConfig);
spp.begin();
FastLED.addLeds<NEOPIXEL, WS2812_PIN>(leds, REV_LIGHTS_COUNT);
for (byte currentPinIndex = 0; currentPinIndex < numOfButtons; currentPinIndex++)
{
pinMode(buttonPins[currentPinIndex], INPUT_PULLUP);
previousButtonStates[currentPinIndex] = HIGH;
currentButtonStates[currentPinIndex] = HIGH;
}
}
void loop()
{
if (spp.available() > 0) {
spp.readBytesUntil('{', simHubMessageBuf, BUF_SIZE);
int readCount = spp.readBytesUntil('}', simHubMessageBuf, BUF_SIZE);
simHubMessageBuf[min(readCount - 1, BUF_SIZE - 1)] = 0x0;
processMessage();
memset(simHubMessageBuf, 0x0, BUF_SIZE);
}
FastLED.show();
KeypadUpdate();
delay(10);
if (bleGamepad.isConnected())
{
for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++)
{
currentButtonStates[currentIndex] = digitalRead(buttonPins[currentIndex]);
if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex])
{
if (currentButtonStates[currentIndex] == LOW)
{
bleGamepad.press(physicalButtons[currentIndex]);
}
else
{
bleGamepad.release(physicalButtons[currentIndex]);
}
}
}
if (currentButtonStates != previousButtonStates)
{
for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++)
{
previousButtonStates[currentIndex] = currentButtonStates[currentIndex];
}
bleGamepad.sendReport();
}
delay(20);
}
}
void processMessage() {
char msgType = simHubMessageBuf[0];
switch (msgType) {
case 'R': {
sscanf(&simHubMessageBuf[1], "%d", &revs);
int numOfLightsToShow = round((revs / 100.0f) * REV_LIGHTS_COUNT);
for(int i=0; i < REV_LIGHTS_COUNT; i++) {
const byte *color = i < numOfLightsToShow ? ledColor(i) : ColorNone;
leds[i].setRGB(color[0], color[1], color[2]);
}
break;
}
case 'S':
sscanf(&simHubMessageBuf[1], "%d", &spd);
break;
}
Serial.print("Revs: ");
Serial.println(revs);
Serial.print("Speed: ");
Serial.println(spd);
}
const byte* ledColor(int index) {
switch(index) {
case 16:
return ColorBlue;
case 15:
case 14:
case 13:
case 12:
return ColorRed;
case 11:
case 10:
case 9:
case 8:
return ColorOrange;
case 7:
case 6:
case 5:
case 4:
return ColorYellow;
case 3:
case 2:
return ColorGreen;
default:
return ColorNone;
}
}
void KeypadUpdate()
{
customKeypad.getKeys();
for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list. //LIST_MAX is provided by the Keypad library and gives the number of buttons of the Keypad instance
{
if (customKeypad.key[i].stateChanged) // Only find keys that have changed state.
{
uint8_t keystate = customKeypad.key[i].kstate;
if (bleGamepad.isConnected())
{
if (keystate == PRESSED)
{
bleGamepad.press(customKeypad.key[i].kchar);
} // Press or release button based on the current state
if (keystate == RELEASED)
{
bleGamepad.release(customKeypad.key[i].kchar);
}
bleGamepad.sendReport(); // Send the HID report after values for all button states are updated, and at least one button state had changed
}
}
}
}
Hi
The current version of the library uses NimBLE, which unfortunately does not allow the simultaneous use of bluetoothserial
An older version of my library will probably be compatible There have been a few people ask. Perhaps I will create an up to date version of the library that uses the older/standard Bluetooth library that is compatible. I've got 2 weeks off in October and 5 weeks off in Dec/Jan. will attack it then, unless someone wants to beat me to it...
Hi
The current version of the library uses NimBLE, which unfortunately does not allow the simultaneous use of bluetoothserial
An older version of my library will probably be compatible There have been a few people ask. Perhaps I will create an up to date version of the library that uses the older/standard Bluetooth library that is compatible. I've got 2 weeks off in October and 5 weeks off in Dec/Jan. will attack it then, unless someone wants to beat me to it...
thanks for your fast response sir, will try to figure it out on the older version
So, got around to investing the time (turns out it was less than 1 hour) needed to get the current v5.1 version of the library ported back to the original BT stack instead of NimBLE
This should allow the use of BluetoothSerial.h, though no testing has ben done...
Let me know how you go ESP32-BLE-Gamepad.zip
So, did some testing and didn't work by default
Sees the timing to get both working at the same time is a bit special
What I will do is make it so Classic BT is built in to my library with the begin method called where it needs to be and then you can choose to enable it and send data
Did some quick testing along those lines and I was able to have the BLE Gamepad working at the same time as sending Classic BT Serial messages
Will need a little more development and testing, but it looks promising
Actually, given more testing, it seems to work fine as expected, except:
- You need to pair the BL Classic device first if pairing to both on Windows (connects perfectly to both again on powerup)(not sure about other devices)
- BLE and BT Classic both use the BLE name
- Other stuff I'm not aware of yet
Here is a working example of a single button pressed that turns an LED on, presses button 1 on the controller, and also sends a message over Serial BT when the pin is activated
#include <Arduino.h>
#include <Bounce2.h> // https://github.com/thomasfredericks/Bounce2
#include <BleGamepad.h> // https://github.com/lemmingDev/ESP32-BLE-Gamepad
#include "BluetoothSerial.h"
#define BOUNCE_WITH_PROMPT_DETECTION // Make button state changes available immediately
#define BUTTON_PIN 39
#define LED_PIN 14
Bounce debouncer = Bounce(); // Instantiate a Bounce object
BleGamepad bleGamepad; // Instantiate a BleGamepad object
BluetoothSerial SerialBT; // Instantiate a BluetoothSerial object
int currentButtonState = 1;
int previousButtonState = 1;
void setup()
{
SerialBT.begin(); // Begin the BT Classic serial port
bleGamepad.begin(); // Begin the BLE gamepad
pinMode(BUTTON_PIN, INPUT_PULLUP); // Setup the button with an internal pull-up
debouncer.attach(BUTTON_PIN); // After setting up the button, setup the Bounce instance :
debouncer.interval(5); // interval in ms
pinMode(LED_PIN, OUTPUT); // Setup the LED :
}
void loop()
{
if (bleGamepad.isConnected())
{
debouncer.update(); // Update the Bounce instance
int currentButtonState = debouncer.read(); // Get the updated value
// If there is a state change
if (currentButtonState != previousButtonState)
{
// Press/release gamepad button and turn on or off the LED as determined by the state
if (currentButtonState == LOW)
{
digitalWrite(LED_PIN, HIGH);
SerialBT.println("Button Pressed");
bleGamepad.press(BUTTON_1);
}
else
{
digitalWrite(LED_PIN, LOW);
SerialBT.println("Button Released");
bleGamepad.release(BUTTON_1);
}
}
// Set previous button state based on current button state
previousButtonState = currentButtonState;
}
}
Depending on the code above, so the device will start with 2 instance (bleGamepad and serialBT)?
Sorry replied via mobile phone, misspressed close issue button
Yeah - one of each
Here is some more code I just tested, where I connected the BT Classic to my phone, while the BLE gamepad was connected to the PC.
This sketch also has serial passthrough, where all data sent from BT Classic serial is output to USB serial on Arduino and vice versa
Worked perfectly. Everything I typed on my phone was sent to the Arduino serial, and everything I typed on Arduino serial was sent to the phone --> all with the BLE gamepad still functioning fine and sending notifications to the phone on button presses
#include <Arduino.h>
#include <Bounce2.h> // https://github.com/thomasfredericks/Bounce2
#include <BleGamepad.h> // https://github.com/lemmingDev/ESP32-BLE-Gamepad
#include "BluetoothSerial.h"
#define BOUNCE_WITH_PROMPT_DETECTION // Make button state changes available immediately
#define BUTTON_PIN 39
#define LED_PIN 14
Bounce debouncer = Bounce(); // Instantiate a Bounce object
BleGamepad bleGamepad; // Instantiate a BleGamepad object
BluetoothSerial SerialBT; // Instantiate a BluetoothSerial object
int currentButtonState = 1;
int previousButtonState = 1;
void setup()
{
SerialBT.begin(); // Begin the BT Classic serial port
bleGamepad.begin(); // Begin the BLE gamepad
Serial.begin(115200); // Begin USB serial port
pinMode(BUTTON_PIN, INPUT_PULLUP); // Setup the button with an internal pull-up
debouncer.attach(BUTTON_PIN); // After setting up the button, setup the Bounce instance :
debouncer.interval(5); // interval in ms
pinMode(LED_PIN, OUTPUT); // Setup the LED :
}
void loop()
{
if (bleGamepad.isConnected())
{
if (Serial.available())
{
SerialBT.write(Serial.read());
}
if (SerialBT.available())
{
Serial.write(SerialBT.read());
}
debouncer.update(); // Update the Bounce instance
int currentButtonState = debouncer.read(); // Get the updated value
// If there is a state change
if (currentButtonState != previousButtonState)
{
// Press/release gamepad button and turn on or off the LED as determined by the state
if (currentButtonState == LOW)
{
digitalWrite(LED_PIN, HIGH);
SerialBT.println("Button Pressed");
bleGamepad.press(BUTTON_1);
}
else
{
digitalWrite(LED_PIN, LOW);
SerialBT.println("Button Released");
bleGamepad.release(BUTTON_1);
}
}
// Set previous button state based on current button state
previousButtonState = currentButtonState;
}
}
I might even make this the default version of BLE stack so that everyone can use BT Classic, and have a seperate branch with NimBLE incase people want to have the benefit of reduced memory usage etc.
There have been a number of people asking for this over the past few years I think people creating custom controllers for sim racing etc have been wanting it for a while -> https://www.simhubdash.com/
I'll try it as soon as my work hour done, thank you very much for the response.
Played around with it some more.
When using both BLE and BT Classic, Android only sees the BT Classic and not the BLE Windows sees both and can connect to both, either, or on two different PCs, or BLE on PC and BT Classic on Android Also, to get the BLE Gamepad paired on PC, you need to reset the ESP32 and connect immediately
Once it's setup, it re-pairs automatically, so I'm not really too worried about trying to track down a fix at the moment
done rebuilding my code, will test it as soon as possible got a race to attend too this morning
I might even make this the default version of BLE stack so that everyone can use BT Classic, and have a seperate branch with NimBLE incase people want to have the benefit of reduced memory usage etc.
There have been a number of people asking for this over the past few years I think people creating custom controllers for sim racing etc have been wanting it for a while -> https://www.simhubdash.com/
You should check this one for sim racers. This is exactly what we want to do with your library. https://dev.to/nmwilk/simhub-without-the-wires-18ij
So, got around to investing the time (turns out it was less than 1 hour) needed to get the current v5.1 version of the library ported back to the original BT stack instead of NimBLE
This should allow the use of BluetoothSerial.h, though no testing has ben done...
Let me know how you go ESP32-BLE-Gamepad.zip
Hi, I tested this Version today and it works for me with classic BT and BLE. Do you plan to make the changes also in the main branch? Kind regards
Would it not be better to extended the library to use the Nordic UART Service (NUS)
(https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/libraries/bluetooth_services/services/nus.html#nordic-uart-service-nus)
Like the BLE-UART example in both nimBLE and blueDriod libraries.
Would need to add the services and TX/RX Characteristic with callbacks for the incoming data.