Skip to content

Commit

Permalink
Add Output Report Function
Browse files Browse the repository at this point in the history
Added Output Report function and provided examples.
  • Loading branch information
Sab1e-GitHub committed Nov 20, 2024
1 parent 11ee6e9 commit cdf05b3
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 1 deletion.
70 changes: 70 additions & 0 deletions BleGamepad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<BleGamepad *>(pvParameter);
Expand All @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions BleGamepad.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "NimBLEHIDDevice.h"
#include "NimBLECharacteristic.h"
#include "BleGamepadConfiguration.h"
#include "BleOutputReceiver.h"

class BleGamepad
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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){};
Expand Down
7 changes: 6 additions & 1 deletion BleGamepadConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ BleGamepadConfiguration::BleGamepadConfiguration() : _controllerType(CONTROLLER_
_serialNumber("0123456789"),
_firmwareRevision("0.5.5"),
_hardwareRevision("1.0.0")
_enableOutputReport(false),
_outputReportLength(64)
{
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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; }
6 changes: 6 additions & 0 deletions BleGamepadConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ class BleGamepadConfiguration
char *_serialNumber;
char *_firmwareRevision;
char *_hardwareRevision;
bool _enableOutputReport;
uint16_t _outputReportLength;

public:
BleGamepadConfiguration();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
38 changes: 38 additions & 0 deletions BleOutputReceiver.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
25 changes: 25 additions & 0 deletions BleOutputReceiver.h
Original file line number Diff line number Diff line change
@@ -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 <NimBLEServer.h>
#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
50 changes: 50 additions & 0 deletions examples/TestReceivingOutputReport/TestReceivingOutputReport.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Test receiving Output Report
*/
#include <BleGamepad.h>

#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("");
}
}
}

0 comments on commit cdf05b3

Please sign in to comment.