Compare commits

...

2 Commits

Author SHA1 Message Date
Roberto Viola
b56ec844d5 Update virtualbike.cpp 2024-12-09 11:37:37 +01:00
Roberto Viola
0eab69fb20 Zwift Ride HID device (Issue #2886) 2024-12-09 10:51:06 +01:00
2 changed files with 105 additions and 0 deletions

View File

@@ -113,6 +113,8 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
}
}
services << QBluetoothUuid::HumanInterfaceDevice;
advertisingData.setServices(services);
//! [Advertising Data]
@@ -431,6 +433,38 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
serviceDataChanged.addCharacteristic(charData);
}
QLowEnergyCharacteristicData reportMapChar;
reportMapChar.setUuid(QBluetoothUuid::ReportMap);
reportMapChar.setValue(getHIDReportDescriptor());
reportMapChar.setProperties(QLowEnergyCharacteristic::Read);
// HID Information Characteristic
QLowEnergyCharacteristicData hidInfoChar;
hidInfoChar.setUuid(QBluetoothUuid::HIDInformation);
QByteArray hidInfo;
hidInfo.append((char)0x11); // HID version 1.1
hidInfo.append((char)0x01); // Country code
hidInfo.append((char)0x00); // Flags
hidInfoChar.setValue(hidInfo);
hidInfoChar.setProperties(QLowEnergyCharacteristic::Read);
// Report Characteristic
QLowEnergyCharacteristicData reportChar;
reportChar.setUuid(QBluetoothUuid::Report);
reportChar.setProperties(QLowEnergyCharacteristic::Notify | QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Write);
QByteArray descriptor;
descriptor.append((char)0x01);
descriptor.append((char)0x00);
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::ClientCharacteristicConfiguration, descriptor);
reportChar.addDescriptor(clientConfig);
// Configure service
serviceDataHID.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceDataHID.setUuid(QBluetoothUuid::HumanInterfaceDevice);
serviceDataHID.addCharacteristic(reportMapChar);
serviceDataHID.addCharacteristic(hidInfoChar);
serviceDataHID.addCharacteristic(reportChar);
//! [Start Advertising]
leController = QLowEnergyController::createPeripheral();
Q_ASSERT(leController);
@@ -473,6 +507,9 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
serviceHR = leController->addService(serviceDataHR);
}
QThread::msleep(100); // give time to Android to add the service async.ly
serviceHID = leController->addService(serviceDataHID);
if (!echelon && !ifit) {
if (!heart_only) {
if (!cadence && !power) {
@@ -482,6 +519,8 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
&virtualbike::characteristicChanged);
QObject::connect(serviceZwiftPlayBike, &QLowEnergyService::characteristicChanged, this,
&virtualbike::characteristicChanged);
QObject::connect(serviceHID, &QLowEnergyService::characteristicChanged, this,
&virtualbike::characteristicChanged);
} else {
QObject::connect(service, &QLowEnergyService::characteristicChanged, this,
&virtualbike::characteristicChanged);
@@ -1345,6 +1384,9 @@ void virtualbike::reconnect() {
if (!this->noHeartService || heart_only)
serviceHR = leController->addService(serviceDataHR);
QThread::msleep(100); // give time to Android to add the service async.ly
serviceHID = leController->addService(serviceDataHID);
#endif
QLowEnergyAdvertisingParameters pars;
@@ -1373,6 +1415,8 @@ void virtualbike::bikeProvider() {
uint16_t normalizeSpeed = (uint16_t)qRound(Bike->currentSpeed().value() * 100);
sendMouseReport(10, 0);
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
if (h) {

View File

@@ -43,6 +43,29 @@ class virtualbike : public virtualdevice {
return lastDirconFTMSFrameReceived;
}
// Function to send mouse input reports
void sendMouseReport(int8_t x, int8_t y, uint8_t buttons = 0, int8_t wheel = 0) {
if (!serviceHID || !hidEnabled || leController->state() != QLowEnergyController::ConnectedState) {
qDebug() << "HID service not available or not connected";
return;
}
QByteArray report;
report.append(buttons); // Button state
report.append(x); // X movement
report.append(y); // Y movement
report.append(wheel); // Wheel movement
// Find the report characteristic
QLowEnergyCharacteristic reportChar = serviceHID->characteristic(QBluetoothUuid(quint16(0x2A4D)));
if (!reportChar.isValid()) {
qDebug() << "HID report characteristic not found";
return;
}
writeCharacteristic(serviceHID, reportChar, report);
}
private:
QLowEnergyController *leController = nullptr;
QLowEnergyService *serviceHR = nullptr;
@@ -106,6 +129,44 @@ class virtualbike : public virtualdevice {
};
VarintResult decodeVarint(const QByteArray& bytes, int startIndex);
qint32 decodeSInt(const QByteArray& bytes);
QLowEnergyServiceData serviceDataHID;
QLowEnergyService* serviceHID = nullptr;
bool hidEnabled = false;
// HID report descriptor for mouse
QByteArray getHIDReportDescriptor() {
static const char reportDesc[] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
static_cast<char>(0xA1), 0x01, // Collection (Application)
0x09, 0x01, // Usage (Pointer)
static_cast<char>(0xA1), 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x03, // Usage Maximum (3)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
static_cast<char>(0x95), 0x03, // Report Count (3)
0x75, 0x01, // Report Size (1)
static_cast<char>(0x81), 0x02, // Input (Data, Variable, Absolute)
static_cast<char>(0x95), 0x01, // Report Count (1)
0x75, 0x05, // Report Size (5)
static_cast<char>(0x81), 0x01, // Input (Constant)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x38, // Usage (Wheel)
0x15, static_cast<char>(0x81), // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
static_cast<char>(0x95), 0x03, // Report Count (3)
static_cast<char>(0x81), 0x06, // Input (Data, Variable, Relative)
static_cast<char>(0xC0), // End Collection
static_cast<char>(0xC0) // End Collection
};
return QByteArray(reportDesc, sizeof(reportDesc));
}
signals:
void changeInclination(double grade, double percentage);