Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
52 changes: 52 additions & 0 deletions examples/TestReceivingOutputReport/TestReceivingOutputReport.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Test receiving Output Report
* Here is a C# testing program for sending OutputReport from the host to the device
* https://github.com/Sab1e-GitHub/HIDOutputDemo_CSharp
*/
#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("");
}
}
}