Skip to content
Open
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
61 changes: 61 additions & 0 deletions BleFeatureReport.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#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::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
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++) {
// Serial.print((uint8_t)value[i], HEX);
// Serial.print(" ");
// }
// Serial.println();

featureFlag = true;
}
27 changes: 27 additions & 0 deletions BleFeatureReport.h
Original file line number Diff line number Diff line change
@@ -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 <NimBLEServer.h>
#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
84 changes: 84 additions & 0 deletions BleGamepad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,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;
}

Expand All @@ -105,6 +109,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)
Expand Down Expand Up @@ -742,6 +748,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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2047,6 +2123,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);
Expand Down
8 changes: 8 additions & 0 deletions BleGamepad.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "NimBLECharacteristic.h"
#include "BleGamepadConfiguration.h"
#include "BleOutputReceiver.h"
#include "BleFeatureReport.h"
#include "BleNUS.h"

// Debug enabled, disabled by default
Expand All @@ -29,6 +30,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;
Expand Down Expand Up @@ -62,15 +65,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);
Expand Down Expand Up @@ -148,6 +154,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();
Expand Down
6 changes: 6 additions & 0 deletions BleGamepadConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ BleGamepadConfiguration::BleGamepadConfiguration() : _controllerType(CONTROLLER_
_firmwareRevision("0.7.4"),
_hardwareRevision("1.0.0"),
_enableOutputReport(false),
_enableFeatureReport(false),
_enableNordicUARTService(false),
_outputReportLength(64),
_featureReportLength(64),
_transmitPowerLevel(9)
{
}
Expand Down Expand Up @@ -132,8 +134,10 @@ 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; }
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)
Expand Down Expand Up @@ -212,6 +216,8 @@ void BleGamepadConfiguration::setSerialNumber(const char *value) { _serialNumber
void BleGamepadConfiguration::setFirmwareRevision(const char *value) { _firmwareRevision = value; }
void BleGamepadConfiguration::setHardwareRevision(const 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; }
7 changes: 6 additions & 1 deletion BleGamepadConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ class BleGamepadConfiguration
const char *_firmwareRevision;
const char *_hardwareRevision;
bool _enableOutputReport;
bool _enableFeatureReport;
bool _enableNordicUARTService;
uint16_t _outputReportLength;
uint16_t _featureReportLength;
int8_t _transmitPowerLevel;


public:
BleGamepadConfiguration();
Expand Down Expand Up @@ -297,8 +298,10 @@ class BleGamepadConfiguration
const char *getFirmwareRevision();
const char *getHardwareRevision();
bool getEnableOutputReport();
bool getEnableFeatureReport();
bool getEnableNordicUARTService();
uint16_t getOutputReportLength();
uint16_t getFeatureReportLength();
int8_t getTXPowerLevel();

void setControllerType(uint8_t controllerType);
Expand Down Expand Up @@ -347,8 +350,10 @@ class BleGamepadConfiguration
void setFirmwareRevision(const char *value);
void setHardwareRevision(const char *value);
void setEnableOutputReport(bool value);
void setEnableFeatureReport(bool value);
void setEnableNordicUARTService(bool value);
void setOutputReportLength(uint16_t value);
void setFeatureReportLength(uint16_t value);
void setTXPowerLevel(int8_t value);
};

Expand Down
38 changes: 38 additions & 0 deletions examples/TestFeatureReports/FeatureReport-example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import asyncio
from bleak import BleakClient, BleakScanner

FEATURE_UUID = "INSERT-YOUR-FEATURE-REPORT-CHAR-UUID-HERE"

async def main():
print("Scanning for ESP32 Gamepad...")
devices = await BleakScanner.discover()

esp = None
for d in devices:
if "ESP32 Gamepad" in d.name:
esp = d
break

if not esp:
print("ESP32 Gamepad not found.")
return

print(f"Connecting to {esp.address}...")
async with BleakClient(esp.address) as client:
if not client.is_connected:
print("Failed to connect.")
return

print("Reading feature report...")
data = await client.read_gatt_char(FEATURE_UUID)
print("Initial Feature Report:", list(data))

print("Writing new feature report...")
new_values = bytes([0xAA, 0xBB, 0xCC])
await client.write_gatt_char(FEATURE_UUID, new_values)

print("Reading feature report again...")
data2 = await client.read_gatt_char(FEATURE_UUID)
print("Updated Feature Report:", list(data2))

asyncio.run(main())
Loading