From cdf05b3fccdee7fb5546fb3b0262f6c91e1b0efa Mon Sep 17 00:00:00 2001 From: Sab1e <2085474394@qq.com> Date: Wed, 20 Nov 2024 21:53:22 +0800 Subject: [PATCH] Add Output Report Function Added Output Report function and provided examples. --- BleGamepad.cpp | 70 +++++++++++++++++++ BleGamepad.h | 8 +++ BleGamepadConfiguration.cpp | 7 +- BleGamepadConfiguration.h | 6 ++ BleOutputReceiver.cpp | 38 ++++++++++ BleOutputReceiver.h | 25 +++++++ .../TestReceivingOutputReport.ino | 50 +++++++++++++ 7 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 BleOutputReceiver.cpp create mode 100644 BleOutputReceiver.h create mode 100644 examples/TestReceivingOutputReport/TestReceivingOutputReport.ino diff --git a/BleGamepad.cpp b/BleGamepad.cpp index 4df4796..380dfe3 100644 --- a/BleGamepad.cpp +++ b/BleGamepad.cpp @@ -47,6 +47,8 @@ std::string serialNumber; std::string firmwareRevision; std::string hardwareRevision; +bool enableOutputReport = false; +uint16_t outputReportLength = 64; BleGamepad::BleGamepad(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel) : _buttons(), _specialButtons(0), _x(0), @@ -94,6 +96,8 @@ void BleGamepad::begin(BleGamepadConfiguration *config) pid = configuration.getPid(); guidVersion = configuration.getGuidVersion(); + enableOutputReport = configuration.getEnableOutputReport(); + outputReportLength = configuration.getOutputReportLength(); uint8_t high = highByte(vid); uint8_t low = lowByte(vid); @@ -568,6 +572,49 @@ void BleGamepad::begin(BleGamepadConfiguration *config) tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; } + if (configuration.getEnableOutputReport()){ + // Usage Page (Vendor Defined 0xFF00) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x06; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; + + // Usage (Vendor Usage 0x01) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // Usage (0x01) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // Logical Minimum (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // Logical Maximum (255) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // Report Size (8 bits) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; + + if(configuration.getOutputReportLength()<=0xFF){ + // Report Count (0~255 bytes) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getOutputReportLength(); + }else{ + // Report Count (0~65535 bytes) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x96; + tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getOutputReportLength()); + tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getOutputReportLength()); + } + + // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x91; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + } + // END_COLLECTION (Application) tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; @@ -1357,6 +1404,22 @@ void BleGamepad::setBatteryLevel(uint8_t level) } } +bool BleGamepad::isOutputReceived(){ + if(enableOutputReport && outputReceiver){ + if(this->outputReceiver->outputFlag){ + this->outputReceiver->outputFlag=false; // Clear Flag + return true; + } + } + return false; +} +uint8_t* BleGamepad::getOutputBuffer(){ + if(enableOutputReport && outputReceiver){ + memcpy(outputBackupBuffer, outputReceiver->outputBuffer, outputReportLength); // Creating a backup to avoid buffer being overwritten while processing data + return outputBackupBuffer; + } + return nullptr; +} void BleGamepad::taskServer(void *pvParameter) { BleGamepad *BleGamepadInstance = (BleGamepad *)pvParameter; // static_cast(pvParameter); @@ -1375,6 +1438,13 @@ void BleGamepad::taskServer(void *pvParameter) BleGamepadInstance->inputGamepad = BleGamepadInstance->hid->inputReport(BleGamepadInstance->configuration.getHidReportId()); // <-- input REPORTID from report map BleGamepadInstance->connectionStatus->inputGamepad = BleGamepadInstance->inputGamepad; + if (enableOutputReport){ + BleGamepadInstance->outputGamepad = BleGamepadInstance->hid->outputReport(BleGamepadInstance->configuration.getHidReportId()); + BleGamepadInstance->outputReceiver = new BleOutputReceiver(outputReportLength); + BleGamepadInstance->outputBackupBuffer = new uint8_t[outputReportLength]; + BleGamepadInstance->outputGamepad->setCallbacks(BleGamepadInstance->outputReceiver); + } + BleGamepadInstance->hid->manufacturer()->setValue(BleGamepadInstance->deviceManufacturer); NimBLEService *pService = pServer->getServiceByUUID(SERVICE_UUID_DEVICE_INFORMATION); diff --git a/BleGamepad.h b/BleGamepad.h index ab17740..9beaaa7 100644 --- a/BleGamepad.h +++ b/BleGamepad.h @@ -10,6 +10,7 @@ #include "NimBLEHIDDevice.h" #include "NimBLECharacteristic.h" #include "BleGamepadConfiguration.h" +#include "BleOutputReceiver.h" class BleGamepad { @@ -38,8 +39,13 @@ class BleGamepad BleConnectionStatus *connectionStatus; + BleOutputReceiver *outputReceiver; + NimBLEHIDDevice *hid; NimBLECharacteristic *inputGamepad; + NimBLECharacteristic *outputGamepad; + + uint8_t *outputBackupBuffer; void rawAction(uint8_t msg[], char msgSize); static void taskServer(void *pvParameter); @@ -105,6 +111,8 @@ class BleGamepad uint8_t batteryLevel; std::string deviceManufacturer; std::string deviceName; + bool isOutputReceived(); + uint8_t* getOutputBuffer(); protected: virtual void onStarted(NimBLEServer *pServer){}; diff --git a/BleGamepadConfiguration.cpp b/BleGamepadConfiguration.cpp index 87f50ee..390d42b 100644 --- a/BleGamepadConfiguration.cpp +++ b/BleGamepadConfiguration.cpp @@ -20,6 +20,8 @@ BleGamepadConfiguration::BleGamepadConfiguration() : _controllerType(CONTROLLER_ _serialNumber("0123456789"), _firmwareRevision("0.5.5"), _hardwareRevision("1.0.0") + _enableOutputReport(false), + _outputReportLength(64) { } @@ -119,7 +121,8 @@ char *BleGamepadConfiguration::getSoftwareRevision(){ return _softwareRevision; char *BleGamepadConfiguration::getSerialNumber(){ return _serialNumber; } char *BleGamepadConfiguration::getFirmwareRevision(){ return _firmwareRevision; } char *BleGamepadConfiguration::getHardwareRevision(){ return _hardwareRevision; } - +bool BleGamepadConfiguration::getEnableOutputReport(){ return _enableOutputReport; } +uint16_t BleGamepadConfiguration::getOutputReportLength(){ return _outputReportLength; } void BleGamepadConfiguration::setWhichSpecialButtons(bool start, bool select, bool menu, bool home, bool back, bool volumeInc, bool volumeDec, bool volumeMute) { _whichSpecialButtons[START_BUTTON] = start; @@ -191,3 +194,5 @@ void BleGamepadConfiguration::setSoftwareRevision(char *value) { _softwareRevisi void BleGamepadConfiguration::setSerialNumber(char *value) { _serialNumber = value; } void BleGamepadConfiguration::setFirmwareRevision(char *value) { _firmwareRevision = value; } void BleGamepadConfiguration::setHardwareRevision(char *value) { _hardwareRevision = value; } +void BleGamepadConfiguration::setEnableOutputReport(bool value) { _enableOutputReport = value; } +void BleGamepadConfiguration::setOutputReportLength(uint16_t value) { _outputReportLength = value; } \ No newline at end of file diff --git a/BleGamepadConfiguration.h b/BleGamepadConfiguration.h index 598543f..557c837 100644 --- a/BleGamepadConfiguration.h +++ b/BleGamepadConfiguration.h @@ -222,6 +222,8 @@ class BleGamepadConfiguration char *_serialNumber; char *_firmwareRevision; char *_hardwareRevision; + bool _enableOutputReport; + uint16_t _outputReportLength; public: BleGamepadConfiguration(); @@ -272,6 +274,8 @@ class BleGamepadConfiguration char *getSerialNumber(); char *getFirmwareRevision(); char *getHardwareRevision(); + bool getEnableOutputReport(); + uint16_t getOutputReportLength(); void setControllerType(uint8_t controllerType); void setAutoReport(bool value); @@ -314,6 +318,8 @@ class BleGamepadConfiguration void setSerialNumber(char *value); void setFirmwareRevision(char *value); void setHardwareRevision(char *value); + void setEnableOutputReport(bool value); + void setOutputReportLength(uint16_t value); }; #endif diff --git a/BleOutputReceiver.cpp b/BleOutputReceiver.cpp new file mode 100644 index 0000000..269e2c5 --- /dev/null +++ b/BleOutputReceiver.cpp @@ -0,0 +1,38 @@ +#include "BleOutputReceiver.h" + +BleOutputReceiver::BleOutputReceiver(uint16_t outputReportLength) +{ + this->outputReportLength = outputReportLength; + outputBuffer = new uint8_t[outputReportLength]; +} + +BleOutputReceiver::~BleOutputReceiver() +{ + // Release memory + if (outputBuffer) + { + delete[] outputBuffer; + } +} + +void BleOutputReceiver::onWrite(NimBLECharacteristic *pCharacteristic) +{ + // Retrieve data sent from the host + std::string value = pCharacteristic->getValue(); + + // Store the received data in the buffer + for (int i = 0; i < std::min(value.length(), (size_t)outputReportLength); i++) + { + outputBuffer[i] = (uint8_t)value[i]; + } + + // Testing + // Serial.println("Received data from host:"); + // for (size_t i = 0; i < value.length(); i++) { + // Serial.print((uint8_t)value[i], HEX); + // Serial.print(" "); + // } + // Serial.println(); + + outputFlag = true; +} \ No newline at end of file diff --git a/BleOutputReceiver.h b/BleOutputReceiver.h new file mode 100644 index 0000000..237303d --- /dev/null +++ b/BleOutputReceiver.h @@ -0,0 +1,25 @@ +#ifndef BLE_OUTPUT_RECEIVER_H +#define BLE_OUTPUT_RECEIVER_H +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "nimconfig.h" +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include +#include "NimBLECharacteristic.h" + +class BleOutputReceiver : public NimBLECharacteristicCallbacks +{ +public: + BleOutputReceiver(uint16_t outputReportLength); + ~BleOutputReceiver(); + void onWrite(NimBLECharacteristic *pCharacteristic) override; + bool outputFlag = false; + uint16_t outputReportLength; + uint8_t *outputBuffer; +}; + +#endif // CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // CONFIG_BT_ENABLED +#endif // BLE_OUTPUT_RECEIVER_H diff --git a/examples/TestReceivingOutputReport/TestReceivingOutputReport.ino b/examples/TestReceivingOutputReport/TestReceivingOutputReport.ino new file mode 100644 index 0000000..0ea8bc9 --- /dev/null +++ b/examples/TestReceivingOutputReport/TestReceivingOutputReport.ino @@ -0,0 +1,50 @@ +/* + * Test receiving Output Report + */ +#include + +#define numOfButtons 16 + +BleGamepad bleGamepad; +BleGamepadConfiguration bleGamepadConfig; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + bleGamepadConfig.setAutoReport(false); + bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS + + bleGamepadConfig.setEnableOutputReport(true); // (Necessary) Enable Output Report. Default is false. + bleGamepadConfig.setOutputReportLength(128); // (Optional) Set Report Length 128(Bytes). The default value is 64 bytes. + // Do not set the OutputReportLength too large, otherwise it will be truncated. For example, if the hexadecimal value of 10000 in decimal is 0x2710, it will be truncated to 0x710. + + bleGamepadConfig.setHidReportId(0x05); // (Optional) Set ReportID to 0x05. + //When you send data from the upper computer to ESP32, you must send the ReportID in the first byte! The default ReportID is 3. + + bleGamepadConfig.setButtonCount(numOfButtons); + + bleGamepadConfig.setAxesMin(0x0000); // 0 --> 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 + + // Try NOT to modify VID, otherwise it may cause the host to be unable to send output reports to the device. + bleGamepadConfig.setVid(0x3412); + // You can freely set the PID + bleGamepadConfig.setPid(0x0100); + bleGamepad.begin(&bleGamepadConfig); + + // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again +} + +void loop() { + if (bleGamepad.isConnected()) { + if (bleGamepad.isOutputReceived()) { + uint8_t* buffer = bleGamepad.getOutputBuffer(); + Serial.print("Receive: "); + for (int i = 0; i < 64; i++) { + Serial.printf("0x%X ",buffer[i]); // Print data from buffer + } + Serial.println(""); + } + } +}