Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is it possible to use bluetoothserial.h??? #111

Open
mbohben opened this issue Jul 2, 2022 · 15 comments
Open

is it possible to use bluetoothserial.h??? #111

mbohben opened this issue Jul 2, 2022 · 15 comments

Comments

@mbohben
Copy link

mbohben commented Jul 2, 2022

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
            }
        }
    }
}
@lemmingDev
Copy link
Owner

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...

@mbohben
Copy link
Author

mbohben commented Jul 3, 2022

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

@lemmingDev
Copy link
Owner

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

@lemmingDev
Copy link
Owner

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

@lemmingDev
Copy link
Owner

Actually, given more testing, it seems to work fine as expected, except:

  1. 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)
  2. BLE and BT Classic both use the BLE name
  3. 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;
    }
}

@mbohben mbohben closed this as completed Sep 22, 2022
@mbohben mbohben reopened this Sep 22, 2022
@mbohben
Copy link
Author

mbohben commented Sep 22, 2022

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

@lemmingDev
Copy link
Owner

lemmingDev commented Sep 22, 2022

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;
    }
}

This was referenced Sep 22, 2022
@lemmingDev
Copy link
Owner

lemmingDev commented Sep 22, 2022

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/

@mbohben
Copy link
Author

mbohben commented Sep 22, 2022

I'll try it as soon as my work hour done, thank you very much for the response.

@lemmingDev
Copy link
Owner

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

@mbohben
Copy link
Author

mbohben commented Sep 23, 2022

done rebuilding my code, will test it as soon as possible got a race to attend too this morning

@sefatunckanat
Copy link

sefatunckanat commented Oct 27, 2022

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

@twinking92
Copy link

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

@LeeNX
Copy link

LeeNX commented Oct 29, 2023

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.

@LeeNX
Copy link

LeeNX commented Nov 23, 2024

Finally got around to drawing up a sketch - #195 (comment)

This worked in my testing between ESP32-C3 connected to my Android phone and my Mac connected via serial for debugging and monitoring, where I could send and receive data between BLE connected devices.

Worked pretty well and I think adding a screen where you can pop up notices or other things on the gamepad. Just not sure how or what you might be doing with the host OS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants