From d7f57057808eb32a56004db2e5456632644203cb Mon Sep 17 00:00:00 2001 From: "C.Lee Taylor" Date: Sun, 27 Apr 2025 18:04:16 +0200 Subject: [PATCH 1/4] Init HID Feature Report --- BleFeatureReport.cpp | 38 +++++++++++++++++ BleFeatureReport.h | 27 ++++++++++++ BleGamepad.cpp | 84 +++++++++++++++++++++++++++++++++++++ BleGamepad.h | 8 ++++ BleGamepadConfiguration.cpp | 8 +++- BleGamepadConfiguration.h | 12 ++++-- 6 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 BleFeatureReport.cpp create mode 100644 BleFeatureReport.h diff --git a/BleFeatureReport.cpp b/BleFeatureReport.cpp new file mode 100644 index 0000000..f1fb6f1 --- /dev/null +++ b/BleFeatureReport.cpp @@ -0,0 +1,38 @@ +#include "BleFeatureReport.h" + +BleFeatureReceiver::BleFeatureReceiver(uint16_t featureReportLength) +{ + this->featureReportLength = featureReportLength; + featureBuffer = new uint8_t[featureReportLength]; +} + +BleFeatureReceiver::~BleFeatureReceiver() +{ + // Release memory + if (featureBuffer) + { + delete[] featureBuffer; + } +} + +void BleFeatureReceiver::onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) +{ + // 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)featureReportLength); i++) + { + featureBuffer[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(); + + featureFlag = true; +} \ No newline at end of file diff --git a/BleFeatureReport.h b/BleFeatureReport.h new file mode 100644 index 0000000..f16db1d --- /dev/null +++ b/BleFeatureReport.h @@ -0,0 +1,27 @@ +#ifndef BLE_FEATURE_REPORT_H +#define BLE_FEATURE_REPORT_H +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "nimconfig.h" +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include +#include "NimBLECharacteristic.h" +#include "NimBLEConnInfo.h" + +class BleFeatureReceiver : public NimBLECharacteristicCallbacks +{ +public: + BleFeatureReceiver(uint16_t featureReportLength); + ~BleFeatureReceiver(); + //void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; + void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; + bool featureFlag = false; + uint16_t featureReportLength; + uint8_t *featureBuffer; +}; + +#endif // CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // CONFIG_BT_ENABLED +#endif // BLE_FEATURE_REPORT_H diff --git a/BleGamepad.cpp b/BleGamepad.cpp index f26f27d..8c04a91 100644 --- a/BleGamepad.cpp +++ b/BleGamepad.cpp @@ -85,8 +85,12 @@ BleGamepad::BleGamepad(std::string deviceName, std::string deviceManufacturer, u hidReportDescriptorSize = 0; hidReportSize = 0; numOfButtonBytes = 0; + enableOutputReport = false; outputReportLength = 64; + enableFeatureReport = false; + featureReportLength = 64; + nusInitialized = false; } @@ -101,6 +105,8 @@ void BleGamepad::begin(BleGamepadConfiguration *config) enableOutputReport = configuration.getEnableOutputReport(); outputReportLength = configuration.getOutputReportLength(); + enableFeatureReport = configuration.getEnableFeatureReport(); + featureReportLength = configuration.getFeatureReportLength(); uint8_t buttonPaddingBits = 8 - (configuration.getButtonCount() % 8); if (buttonPaddingBits == 8) @@ -738,6 +744,53 @@ void BleGamepad::begin(BleGamepadConfiguration *config) tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; } + if (configuration.getEnableFeatureReport()) + { + // 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.getFeatureReportLength() <= 0xFF) + { + // Report Count (0~255 bytes) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getFeatureReportLength(); + } + else + { + // Report Count (0~65535 bytes) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x96; + tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getFeatureReportLength()); + tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getFeatureReportLength()); + } + + // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xB1; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + } + // END_COLLECTION (Application) tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; @@ -1645,6 +1698,29 @@ uint8_t* BleGamepad::getOutputBuffer() return nullptr; } +bool BleGamepad::isFeatureReceived() +{ + if (enableFeatureReport && featureReceiver) + { + if (this->featureReceiver->featureFlag) + { + this->featureReceiver->featureFlag = false; // Clear Flag + return true; + } + } + return false; +} + +uint8_t* BleGamepad::getFeatureBuffer() +{ + if (enableFeatureReport && featureReceiver) + { + memcpy(featureBackupBuffer, featureReceiver->featureBuffer, featureReportLength); // Creating a backup to avoid buffer being overwritten while processing data + return featureBackupBuffer; + } + return nullptr; +} + bool BleGamepad::deleteAllBonds(bool resetBoard) { bool success = false; @@ -1996,6 +2072,14 @@ void BleGamepad::taskServer(void *pvParameter) BleGamepadInstance->outputGamepad->setCallbacks(BleGamepadInstance->outputReceiver); } + if (BleGamepadInstance->enableFeatureReport) + { + BleGamepadInstance->featureGamepad = BleGamepadInstance->hid->getFeatureReport(BleGamepadInstance->configuration.getHidReportId()); + BleGamepadInstance->featureReceiver = new BleFeatureReceiver(BleGamepadInstance->featureReportLength); + BleGamepadInstance->featureBackupBuffer = new uint8_t[BleGamepadInstance->featureReportLength]; + BleGamepadInstance->featureGamepad->setCallbacks(BleGamepadInstance->featureReceiver); + } + BleGamepadInstance->hid->setManufacturer(BleGamepadInstance->deviceManufacturer); NimBLEService *pService = pServer->getServiceByUUID(SERVICE_UUID_DEVICE_INFORMATION); diff --git a/BleGamepad.h b/BleGamepad.h index 5f1377c..a6c406f 100644 --- a/BleGamepad.h +++ b/BleGamepad.h @@ -11,6 +11,7 @@ #include "NimBLECharacteristic.h" #include "BleGamepadConfiguration.h" #include "BleOutputReceiver.h" +#include "BleFeatureReport.h" #include "BleNUS.h" class BleGamepad @@ -24,6 +25,8 @@ class BleGamepad uint8_t numOfButtonBytes; bool enableOutputReport; uint16_t outputReportLength; + bool enableFeatureReport; + uint16_t featureReportLength; uint8_t _buttons[16]; // 8 bits x 16 bytes = 128 bits --> 128 button max uint8_t _specialButtons; int16_t _x; @@ -57,15 +60,18 @@ class BleGamepad BleConnectionStatus *connectionStatus; BleOutputReceiver *outputReceiver; + BleFeatureReceiver *featureReceiver; NimBLEServer *pServer; BleNUS* nus; NimBLEHIDDevice *hid; NimBLECharacteristic *inputGamepad; NimBLECharacteristic *outputGamepad; + NimBLECharacteristic *featureGamepad; NimBLECharacteristic *pCharacteristic_Power_State; uint8_t *outputBackupBuffer; + uint8_t *featureBackupBuffer; void rawAction(uint8_t msg[], char msgSize); static void taskServer(void *pvParameter); @@ -143,6 +149,8 @@ class BleGamepad bool delayAdvertising; bool isOutputReceived(); uint8_t* getOutputBuffer(); + bool isFeatureReceived(); + uint8_t* getFeatureBuffer(); bool deleteBond(bool resetBoard = false); bool deleteAllBonds(bool resetBoard = false); bool enterPairingMode(); diff --git a/BleGamepadConfiguration.cpp b/BleGamepadConfiguration.cpp index 9b9aa20..450cf46 100644 --- a/BleGamepadConfiguration.cpp +++ b/BleGamepadConfiguration.cpp @@ -25,9 +25,11 @@ BleGamepadConfiguration::BleGamepadConfiguration() : _controllerType(CONTROLLER_ _firmwareRevision("0.7.4"), _hardwareRevision("1.0.0"), _enableOutputReport(false), + _enableFeatureReport(false), _enableNordicUARTService(false), _outputReportLength(64), - _transmitPowerLevel(9) + _featureReportLength(64), + _transmitPowerLevel(9) { } @@ -132,8 +134,10 @@ char *BleGamepadConfiguration::getSerialNumber(){ return _serialNumber; } char *BleGamepadConfiguration::getFirmwareRevision(){ return _firmwareRevision; } char *BleGamepadConfiguration::getHardwareRevision(){ return _hardwareRevision; } bool BleGamepadConfiguration::getEnableOutputReport(){ return _enableOutputReport; } +bool BleGamepadConfiguration::getEnableFeatureReport(){ return _enableFeatureReport; } bool BleGamepadConfiguration::getEnableNordicUARTService(){ return _enableNordicUARTService; } uint16_t BleGamepadConfiguration::getOutputReportLength(){ return _outputReportLength; } +uint16_t BleGamepadConfiguration::getFeatureReportLength(){ return _featureReportLength; } int8_t BleGamepadConfiguration::getTXPowerLevel(){ return _transmitPowerLevel; } // Returns the power level that was set as the server started void BleGamepadConfiguration::setWhichSpecialButtons(bool start, bool select, bool menu, bool home, bool back, bool volumeInc, bool volumeDec, bool volumeMute) @@ -212,6 +216,8 @@ void BleGamepadConfiguration::setSerialNumber(char *value) { _serialNumber = val void BleGamepadConfiguration::setFirmwareRevision(char *value) { _firmwareRevision = value; } void BleGamepadConfiguration::setHardwareRevision(char *value) { _hardwareRevision = value; } void BleGamepadConfiguration::setEnableOutputReport(bool value) { _enableOutputReport = value; } +void BleGamepadConfiguration::setEnableFeatureReport(bool value) { _enableFeatureReport = value; } void BleGamepadConfiguration::setEnableNordicUARTService(bool value) { _enableNordicUARTService = value; } void BleGamepadConfiguration::setOutputReportLength(uint16_t value) { _outputReportLength = value; } +void BleGamepadConfiguration::setFeatureReportLength(uint16_t value) { _featureReportLength = value; } void BleGamepadConfiguration::setTXPowerLevel(int8_t value) { _transmitPowerLevel = value; } diff --git a/BleGamepadConfiguration.h b/BleGamepadConfiguration.h index 600cc87..85a5cd4 100644 --- a/BleGamepadConfiguration.h +++ b/BleGamepadConfiguration.h @@ -238,9 +238,11 @@ class BleGamepadConfiguration char *_firmwareRevision; char *_hardwareRevision; bool _enableOutputReport; + bool _enableFeatureReport; bool _enableNordicUARTService; uint16_t _outputReportLength; - int8_t _transmitPowerLevel; + uint16_t _featureReportLength; + int8_t _transmitPowerLevel; public: @@ -297,9 +299,11 @@ class BleGamepadConfiguration char *getFirmwareRevision(); char *getHardwareRevision(); bool getEnableOutputReport(); + bool getEnableFeatureReport(); bool getEnableNordicUARTService(); uint16_t getOutputReportLength(); - int8_t getTXPowerLevel(); + uint16_t getFeatureReportLength(); + int8_t getTXPowerLevel(); void setControllerType(uint8_t controllerType); void setAutoReport(bool value); @@ -347,9 +351,11 @@ class BleGamepadConfiguration void setFirmwareRevision(char *value); void setHardwareRevision(char *value); void setEnableOutputReport(bool value); + void setEnableFeatureReport(bool value); void setEnableNordicUARTService(bool value); void setOutputReportLength(uint16_t value); - void setTXPowerLevel(int8_t value); + void setFeatureReportLength(uint16_t value); + void setTXPowerLevel(int8_t value); }; #endif From e8b43555f41973a4f7cf2e519292d14f4dd633d3 Mon Sep 17 00:00:00 2001 From: "C.Lee Taylor" Date: Sun, 27 Apr 2025 19:07:25 +0200 Subject: [PATCH 2/4] Remove warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] --- BleGamepadConfiguration.cpp | 10 +++++----- BleGamepadConfiguration.h | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/BleGamepadConfiguration.cpp b/BleGamepadConfiguration.cpp index 450cf46..31b2a94 100644 --- a/BleGamepadConfiguration.cpp +++ b/BleGamepadConfiguration.cpp @@ -128,11 +128,11 @@ bool BleGamepadConfiguration::getIncludeSteering() { return _whichSimulationCont const bool *BleGamepadConfiguration::getWhichSimulationControls() const { return _whichSimulationControls; } bool BleGamepadConfiguration::getIncludeGyroscope() { return _includeGyroscope; } bool BleGamepadConfiguration::getIncludeAccelerometer() { return _includeAccelerometer; } -char *BleGamepadConfiguration::getModelNumber(){ return _modelNumber; } -char *BleGamepadConfiguration::getSoftwareRevision(){ return _softwareRevision; } -char *BleGamepadConfiguration::getSerialNumber(){ return _serialNumber; } -char *BleGamepadConfiguration::getFirmwareRevision(){ return _firmwareRevision; } -char *BleGamepadConfiguration::getHardwareRevision(){ return _hardwareRevision; } +const char *BleGamepadConfiguration::getModelNumber(){ return _modelNumber; } +const char *BleGamepadConfiguration::getSoftwareRevision(){ return _softwareRevision; } +const char *BleGamepadConfiguration::getSerialNumber(){ return _serialNumber; } +const char *BleGamepadConfiguration::getFirmwareRevision(){ return _firmwareRevision; } +const char *BleGamepadConfiguration::getHardwareRevision(){ return _hardwareRevision; } bool BleGamepadConfiguration::getEnableOutputReport(){ return _enableOutputReport; } bool BleGamepadConfiguration::getEnableFeatureReport(){ return _enableFeatureReport; } bool BleGamepadConfiguration::getEnableNordicUARTService(){ return _enableNordicUARTService; } diff --git a/BleGamepadConfiguration.h b/BleGamepadConfiguration.h index 85a5cd4..59d1806 100644 --- a/BleGamepadConfiguration.h +++ b/BleGamepadConfiguration.h @@ -232,11 +232,11 @@ class BleGamepadConfiguration int16_t _simulationMax; int16_t _motionMin; int16_t _motionMax; - char *_modelNumber; - char *_softwareRevision; - char *_serialNumber; - char *_firmwareRevision; - char *_hardwareRevision; + const char *_modelNumber; + const char *_softwareRevision; + const char *_serialNumber; + const char *_firmwareRevision; + const char *_hardwareRevision; bool _enableOutputReport; bool _enableFeatureReport; bool _enableNordicUARTService; @@ -293,11 +293,11 @@ class BleGamepadConfiguration int16_t getSimulationMax(); int16_t getMotionMin(); int16_t getMotionMax(); - char *getModelNumber(); - char *getSoftwareRevision(); - char *getSerialNumber(); - char *getFirmwareRevision(); - char *getHardwareRevision(); + const char *getModelNumber(); + const char *getSoftwareRevision(); + const char *getSerialNumber(); + const char *getFirmwareRevision(); + const char *getHardwareRevision(); bool getEnableOutputReport(); bool getEnableFeatureReport(); bool getEnableNordicUARTService(); From 84c1b9ce10e7134c37b1b38ddfdb99ffdb7ef5b6 Mon Sep 17 00:00:00 2001 From: "C.Lee Taylor" Date: Sun, 27 Apr 2025 20:11:39 +0200 Subject: [PATCH 3/4] Add feature report onRead --- BleFeatureReport.cpp | 8 ++++++++ BleFeatureReport.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/BleFeatureReport.cpp b/BleFeatureReport.cpp index f1fb6f1..70c14f9 100644 --- a/BleFeatureReport.cpp +++ b/BleFeatureReport.cpp @@ -15,6 +15,14 @@ BleFeatureReceiver::~BleFeatureReceiver() } } +void BleFeatureReceiver::onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) +{ + // Set data for the host + pCharacteristic->setValue(featureBuffer, featureReportLength); + + featureFlag = true; +} + void BleFeatureReceiver::onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) { // Retrieve data sent from the host diff --git a/BleFeatureReport.h b/BleFeatureReport.h index f16db1d..e57590b 100644 --- a/BleFeatureReport.h +++ b/BleFeatureReport.h @@ -15,7 +15,7 @@ class BleFeatureReceiver : public NimBLECharacteristicCallbacks public: BleFeatureReceiver(uint16_t featureReportLength); ~BleFeatureReceiver(); - //void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; + void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; bool featureFlag = false; uint16_t featureReportLength; From 47c166f81e075721b24f563f2b201d864f2fc504 Mon Sep 17 00:00:00 2001 From: "C.Lee Taylor" Date: Wed, 30 Jul 2025 16:45:56 +0200 Subject: [PATCH 4/4] check for data change --- BleFeatureReport.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/BleFeatureReport.cpp b/BleFeatureReport.cpp index 70c14f9..67a3b8b 100644 --- a/BleFeatureReport.cpp +++ b/BleFeatureReport.cpp @@ -28,12 +28,27 @@ void BleFeatureReceiver::onWrite(NimBLECharacteristic *pCharacteristic, NimBLECo // Retrieve data sent from the host std::string value = pCharacteristic->getValue(); + if (value.length() <= featureReportLength) { + // Check if data has changed + if (memcmp(value.c_str(), featureBuffer, value.length()) != 0) { + memcpy(featureBuffer, value.c_str(), value.length()); + // If there's a callback defined, call it + //if (_onFeatureReportReceived) { + // _onFeatureReportReceived((const uint8_t*)value.c_str(), value.length()); + //} + } + } + + /* + // Store the received data in the buffer for (int i = 0; i < std::min(value.length(), (size_t)featureReportLength); i++) { featureBuffer[i] = (uint8_t)value[i]; } + */ + // Testing // Serial.println("Received data from host:"); // for (size_t i = 0; i < value.length(); i++) {