mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-19 07:50:44 +01:00
Compare commits
21 Commits
0.3.0
...
startup_se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff354fd20d | ||
|
|
d21f92727e | ||
|
|
632991e58e | ||
|
|
21ec2a890e | ||
|
|
b434d1f1e6 | ||
|
|
373eb3fbf9 | ||
|
|
acfdff7b5c | ||
|
|
38a41451f3 | ||
|
|
99fd62c8ec | ||
|
|
eff5d1d2f3 | ||
|
|
22b6de14f1 | ||
|
|
483cc45643 | ||
|
|
3d152b903a | ||
|
|
1974dd26ee | ||
|
|
78f81261d9 | ||
|
|
d542aa819b | ||
|
|
cddd74e0d2 | ||
|
|
9d66348ddb | ||
|
|
62cae2c015 | ||
|
|
54579d06c5 | ||
|
|
ee7b8c4752 |
BIN
docs/HRP_V10.pdf
Normal file
BIN
docs/HRP_V10.pdf
Normal file
Binary file not shown.
BIN
docs/HRS_SPEC_V10.pdf
Normal file
BIN
docs/HRS_SPEC_V10.pdf
Normal file
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
#include "domyostreadmill.h"
|
||||
#include "virtualtreadmill.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDateTime>
|
||||
#include <QMetaEnum>
|
||||
|
||||
// set speed and incline to 0
|
||||
uint8_t initData1[] = { 0xf0, 0xc8, 0x01, 0xb9 };
|
||||
@@ -17,9 +19,31 @@ uint8_t initDataStart2[] = { 0xf0, 0xcb, 0x01, 0x00, 0x00, 0x02, 0xff, 0xff, 0xf
|
||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00 };
|
||||
uint8_t initDataStart3[] = { 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xb6 };
|
||||
uint8_t initDataStart4[] = { 0xf0, 0xc8, 0x00, 0xb8 };
|
||||
uint8_t initDataStart5[] = { 0xf0, 0xcb, 0x03, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x02,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00 };
|
||||
uint8_t initDataStart6[] = { 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc1 };
|
||||
uint8_t initDataStart5[] = { 0xf0, 0xc8, 0x01, 0xb9 };
|
||||
uint8_t initDataStart6[] =
|
||||
{
|
||||
0xf0, 0xad, 0xff, 0xff, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
uint8_t initDataStart7[] = { 0xff, 0xff, 0x95 };
|
||||
uint8_t initDataStart8[] =
|
||||
{
|
||||
0xf0, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
uint8_t initDataStart9[] = { 0xff, 0xff, 0x8b };
|
||||
uint8_t initDataStart10[] =
|
||||
{
|
||||
0xf0, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x03, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
uint8_t initDataStart11[] = { 0xff, 0xff, 0x8a };
|
||||
uint8_t initDataStart12[] =
|
||||
{
|
||||
0xf0, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x04, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
uint8_t initDataStart13[] = { 0xff, 0xff, 0x9e };
|
||||
#endif
|
||||
|
||||
// main startup sequence
|
||||
@@ -65,18 +89,24 @@ QLowEnergyCharacteristic gattNotifyCharacteristic;
|
||||
QBluetoothDeviceDiscoveryAgent *discoveryAgent;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
extern volatile double currentSpeed;
|
||||
extern volatile double currentIncline;
|
||||
extern volatile double currentHeart;
|
||||
extern volatile uint8_t currentHeart;
|
||||
extern volatile double requestSpeed;
|
||||
extern volatile double requestIncline;
|
||||
extern volatile int8_t requestStart;
|
||||
extern volatile int8_t requestStop;
|
||||
|
||||
QFile* debugCommsLog;
|
||||
|
||||
domyostreadmill::domyostreadmill()
|
||||
{
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
|
||||
QTimer* refresh = new QTimer(this);
|
||||
debugCommsLog = new QFile("debug-" + QDateTime::currentDateTime().toString() + ".log");
|
||||
debugCommsLog->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||
|
||||
initDone = false;
|
||||
|
||||
@@ -92,19 +122,38 @@ domyostreadmill::domyostreadmill()
|
||||
refresh->start(200);
|
||||
}
|
||||
|
||||
void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestIncline, uint16_t elapsed)
|
||||
void domyostreadmill::debug(QString text)
|
||||
{
|
||||
QString debug = QDateTime::currentDateTime().toString() + text + '\n';
|
||||
debugCommsLog->write(debug.toLocal8Bit());
|
||||
qDebug() << debug;
|
||||
}
|
||||
|
||||
void domyostreadmill::writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log)
|
||||
{
|
||||
QEventLoop loop;
|
||||
connect(gattCommunicationChannelService, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)data, data_len));
|
||||
|
||||
if(!disable_log)
|
||||
debug(" >> " + QByteArray((const char*)data, data_len).toHex(' ') + " // " + info);
|
||||
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
void domyostreadmill::updateDisplay(uint16_t elapsed)
|
||||
{
|
||||
uint8_t writeIncline[] = {0xf0, 0xcb, 0x03, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x02,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00,
|
||||
(uint8_t)(requestSpeed * 10), 0x01, 0xff, 0xff, 0xff, 0xff, 0x00};
|
||||
0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00};
|
||||
|
||||
writeIncline[3] = (elapsed >> 8) & 0xFF; // high byte for elapsed time (in seconds)
|
||||
writeIncline[4] = (elapsed & 0xFF); // low byte for elasped time (in seconds)
|
||||
|
||||
writeIncline[12] = currentHeart;
|
||||
|
||||
writeIncline[16] = (uint8_t)(requestIncline * 10);
|
||||
|
||||
for(uint8_t i=0; i<sizeof(writeIncline)-1; i++)
|
||||
{
|
||||
//qDebug() << QString::number(writeIncline[i], 16);
|
||||
@@ -113,65 +162,85 @@ void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestInc
|
||||
|
||||
//qDebug() << "writeIncline crc" << QString::number(writeIncline[26], 16);
|
||||
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)writeIncline, sizeof(writeIncline)));
|
||||
|
||||
writeCharacteristic(writeIncline, 20, "updateDisplay speed=" + QString::number(requestSpeed) + " incline=" + QString::number(requestIncline) + " elapsed=" + QString::number(elapsed) );
|
||||
writeCharacteristic(&writeIncline[20], sizeof (writeIncline) - 20, "updateDisplay speed=" + QString::number(requestSpeed) + " incline=" + QString::number(requestIncline) + " elapsed=" + QString::number(elapsed) );
|
||||
}
|
||||
|
||||
void domyostreadmill::update()
|
||||
void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestIncline)
|
||||
{
|
||||
static uint8_t first = 0;
|
||||
static virtualtreadmill* v;
|
||||
uint8_t writeIncline[] = {0xf0, 0xad, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00};
|
||||
|
||||
writeIncline[4] = ((uint16_t)(requestSpeed*10) >> 8) & 0xFF;
|
||||
writeIncline[5] = ((uint16_t)(requestSpeed*10) & 0xFF);
|
||||
|
||||
writeIncline[13] = ((uint16_t)(requestIncline*10) >> 8) & 0xFF;
|
||||
writeIncline[14] = ((uint16_t)(requestIncline*10) & 0xFF);
|
||||
|
||||
for(uint8_t i=0; i<sizeof(writeIncline)-1; i++)
|
||||
{
|
||||
//qDebug() << QString::number(writeIncline[i], 16);
|
||||
writeIncline[22] += writeIncline[i]; // the last byte is a sort of a checksum
|
||||
}
|
||||
|
||||
//qDebug() << "writeIncline crc" << QString::number(writeIncline[26], 16);
|
||||
|
||||
|
||||
writeCharacteristic(writeIncline, 20, "forceSpeedOrIncline speed=" + QString::number(requestSpeed) + " incline=" + QString::number(requestIncline));
|
||||
writeCharacteristic(&writeIncline[20], sizeof (writeIncline) - 20, "forceSpeedOrIncline speed=" + QString::number(requestSpeed) + " incline=" + QString::number(requestIncline));
|
||||
}
|
||||
|
||||
|
||||
void domyostreadmill::update()
|
||||
{
|
||||
static uint32_t counter = 0;
|
||||
Q_UNUSED(v);
|
||||
|
||||
//qDebug() << treadmill.isValid() << m_control->state() << gattCommunicationChannelService << gattWriteCharacteristic.isValid() << gattNotifyCharacteristic.isValid() << initDone;
|
||||
if(treadmill.isValid() &&
|
||||
(m_control->state() == QLowEnergyController::ConnectedState || m_control->state() == QLowEnergyController::DiscoveredState) &&
|
||||
|
||||
if(initRequest)
|
||||
{
|
||||
initRequest = false;
|
||||
btinit();
|
||||
}
|
||||
else if(treadmill.isValid() &&
|
||||
m_control->state() == QLowEnergyController::DiscoveredState &&
|
||||
gattCommunicationChannelService &&
|
||||
gattWriteCharacteristic.isValid() &&
|
||||
gattNotifyCharacteristic.isValid() &&
|
||||
initDone)
|
||||
{
|
||||
counter++;
|
||||
if(!first)
|
||||
{
|
||||
qDebug() << "creating virtual treadmill interface...";
|
||||
v = new virtualtreadmill();
|
||||
}
|
||||
first = 1;
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)noOpData, sizeof(noOpData)));
|
||||
if(currentSpeed > 0.0)
|
||||
counter++;
|
||||
|
||||
writeCharacteristic(noOpData, sizeof(noOpData), "noOp", true);
|
||||
|
||||
// byte 3 - 4 = elapsed time
|
||||
// byte 17 = inclination
|
||||
|
||||
if(requestSpeed != -1)
|
||||
{
|
||||
qDebug() << "writing speed" << requestSpeed;
|
||||
forceSpeedOrIncline(requestSpeed, currentIncline, counter/5);
|
||||
debug("writing speed " + QString::number(requestSpeed));
|
||||
forceSpeedOrIncline(requestSpeed, currentIncline);
|
||||
requestSpeed = -1;
|
||||
}
|
||||
if(requestIncline != -1)
|
||||
{
|
||||
qDebug() << "writing incline" << requestIncline;
|
||||
forceSpeedOrIncline(currentSpeed, requestIncline, counter/5);
|
||||
debug("writing incline " + QString::number(requestIncline));
|
||||
forceSpeedOrIncline(currentSpeed, requestIncline);
|
||||
requestIncline = -1;
|
||||
}
|
||||
if(requestStart != -1)
|
||||
{
|
||||
qDebug() << "starting...(TODO)";
|
||||
debug("starting...");
|
||||
btinit();
|
||||
requestStart = -1;
|
||||
}
|
||||
if(requestStop != -1)
|
||||
{
|
||||
qDebug() << "stopping...";
|
||||
/*gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initData1, sizeof(initData1)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initData2, sizeof(initData2)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart, sizeof(initDataStart)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart2, sizeof(initDataStart2)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart3, sizeof(initDataStart3)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart4, sizeof(initDataStart4)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart5, sizeof(initDataStart5)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart6, sizeof(initDataStart6)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart7, sizeof(initDataStart7)));*/
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataF0C800B8, sizeof(initDataF0C800B8)));
|
||||
debug("stopping...");
|
||||
writeCharacteristic(initDataF0C800B8, sizeof(initDataF0C800B8), "stop tape");
|
||||
requestStop = -1;
|
||||
}
|
||||
}
|
||||
@@ -179,7 +248,7 @@ void domyostreadmill::update()
|
||||
|
||||
void domyostreadmill::serviceDiscovered(const QBluetoothUuid &gatt)
|
||||
{
|
||||
qDebug() << "serviceDiscovered" << gatt;
|
||||
debug("serviceDiscovered " + gatt.toString());
|
||||
}
|
||||
|
||||
static QByteArray lastPacket;
|
||||
@@ -188,6 +257,8 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
|
||||
//qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
|
||||
Q_UNUSED(characteristic);
|
||||
|
||||
debug(" << " + newValue.toHex(' '));
|
||||
|
||||
if (lastPacket.length() && lastPacket == newValue)
|
||||
return;
|
||||
|
||||
@@ -195,9 +266,14 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
|
||||
if (newValue.length() != 26)
|
||||
return;
|
||||
|
||||
if (newValue.at(22) == 0x07)
|
||||
if (newValue.at(22) == 0x06)
|
||||
{
|
||||
qDebug() << "STOP PRESSED!";
|
||||
debug("start button pressed!");
|
||||
requestStart = 1;
|
||||
}
|
||||
else if (newValue.at(22) == 0x07)
|
||||
{
|
||||
debug("stop button pressed!");
|
||||
requestStop = 1;
|
||||
}
|
||||
|
||||
@@ -206,18 +282,15 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
|
||||
|
||||
double speed = GetSpeedFromPacket(newValue);
|
||||
double incline = GetInclinationFromPacket(newValue);
|
||||
//var isStartPressed = GetIsStartPressedFromPacket(currentPacket);
|
||||
//var isStopPressed = GetIsStopPressedFromPacket(currentPacket);
|
||||
|
||||
#if DEBUG
|
||||
Debug.WriteLine(args.CharacteristicValue.ToArray().HexDump());
|
||||
#endif
|
||||
|
||||
currentHeart = newValue.at(18);
|
||||
|
||||
qDebug() << "Current speed: " << speed;
|
||||
qDebug() << "Current incline: " << incline;
|
||||
qDebug() << "Current heart:" << currentHeart;
|
||||
debug("Current speed: " + QString::number(speed));
|
||||
debug("Current incline: " + QString::number(incline));
|
||||
debug("Current heart: " + QString::number(currentHeart));
|
||||
|
||||
if(m_control->error() != QLowEnergyController::NoError)
|
||||
qDebug() << "QLowEnergyController ERROR!!" << m_control->errorString();
|
||||
|
||||
currentSpeed = speed;
|
||||
currentIncline = incline;
|
||||
@@ -233,65 +306,112 @@ double domyostreadmill::GetSpeedFromPacket(QByteArray packet)
|
||||
double domyostreadmill::GetInclinationFromPacket(QByteArray packet)
|
||||
{
|
||||
uint16_t convertedData = (packet.at(2) << 8) | packet.at(3);
|
||||
qDebug() << convertedData;
|
||||
double data = ((double)convertedData - 1000.0f) / 10.0f;
|
||||
if (data < 0) return 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
void domyostreadmill::btinit()
|
||||
{
|
||||
writeCharacteristic(initData1, sizeof(initData1), "init");
|
||||
writeCharacteristic(initData2, sizeof(initData2), "init");
|
||||
writeCharacteristic(initDataStart, sizeof(initDataStart), "init");
|
||||
writeCharacteristic(initDataStart2, sizeof(initDataStart2), "init");
|
||||
writeCharacteristic(initDataStart3, sizeof(initDataStart3), "init");
|
||||
writeCharacteristic(initDataStart4, sizeof(initDataStart4), "init");
|
||||
writeCharacteristic(initDataStart5, sizeof(initDataStart5), "init");
|
||||
writeCharacteristic(initDataStart6, sizeof(initDataStart6), "init");
|
||||
writeCharacteristic(initDataStart7, sizeof(initDataStart7), "init");
|
||||
writeCharacteristic(initDataStart8, sizeof(initDataStart8), "init");
|
||||
writeCharacteristic(initDataStart9, sizeof(initDataStart9), "init");
|
||||
writeCharacteristic(initDataStart10, sizeof(initDataStart10), "init");
|
||||
writeCharacteristic(initDataStart11, sizeof(initDataStart11), "init");
|
||||
writeCharacteristic(initDataStart12, sizeof(initDataStart12), "init");
|
||||
writeCharacteristic(initDataStart13, sizeof(initDataStart13), "init");
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state)
|
||||
{
|
||||
qDebug() << "stateChanged" << state;
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceState>();
|
||||
debug("BTLE stateChanged " + QString::fromLocal8Bit(metaEnum.valueToKey(state)));
|
||||
|
||||
if(state == QLowEnergyService::ServiceDiscovered)
|
||||
{
|
||||
//qDebug() << gattCommunicationChannelService->characteristics();
|
||||
|
||||
gattWriteCharacteristic = gattCommunicationChannelService->characteristic(_gattWriteCharacteristicId);
|
||||
gattNotifyCharacteristic = gattCommunicationChannelService->characteristic(_gattNotifyCharacteristicId);
|
||||
Q_ASSERT(gattWriteCharacteristic.isValid());
|
||||
Q_ASSERT(gattNotifyCharacteristic.isValid());
|
||||
|
||||
// establish hook into notifications
|
||||
connect(gattCommunicationChannelService, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
|
||||
this, SLOT(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
|
||||
connect(gattCommunicationChannelService, SIGNAL(characteristicWritten(const QLowEnergyCharacteristic, const QByteArray)),
|
||||
this, SLOT(characteristicWritten(const QLowEnergyCharacteristic, const QByteArray)));
|
||||
connect(gattCommunicationChannelService, SIGNAL(error(QLowEnergyService::ServiceError)),
|
||||
this, SLOT(errorService(QLowEnergyService::ServiceError)));
|
||||
connect(gattCommunicationChannelService, SIGNAL(descriptorWritten(const QLowEnergyDescriptor, const QByteArray)), this,
|
||||
SLOT(descriptorWritten(const QLowEnergyDescriptor, const QByteArray)));
|
||||
|
||||
// ******************************************* virtual treadmill init *************************************
|
||||
static uint8_t first = 0;
|
||||
static virtualtreadmill* v;
|
||||
Q_UNUSED(v);
|
||||
if(!first)
|
||||
{
|
||||
debug("creating virtual treadmill interface...");
|
||||
v = new virtualtreadmill();
|
||||
}
|
||||
first = 1;
|
||||
// ********************************************************************************************************
|
||||
|
||||
// await _gattNotifyCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
|
||||
QByteArray descriptor;
|
||||
descriptor.append((char)0x01);
|
||||
descriptor.append((char)0x00);
|
||||
gattCommunicationChannelService->writeDescriptor(gattNotifyCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor);
|
||||
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initData1, sizeof(initData1)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initData2, sizeof(initData2)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart, sizeof(initDataStart)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart2, sizeof(initDataStart2)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart3, sizeof(initDataStart3)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart4, sizeof(initDataStart4)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart5, sizeof(initDataStart5)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart6, sizeof(initDataStart6)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart7, sizeof(initDataStart7)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart8, sizeof(initDataStart8)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart9, sizeof(initDataStart9)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart10, sizeof(initDataStart10)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart11, sizeof(initDataStart11)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart12, sizeof(initDataStart12)));
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)initDataStart13, sizeof(initDataStart13)));
|
||||
|
||||
initDone = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void domyostreadmill::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)
|
||||
{
|
||||
debug("descriptorWritten " + descriptor.name() + " " + newValue.toHex(' '));
|
||||
|
||||
initRequest = true;
|
||||
}
|
||||
|
||||
void domyostreadmill::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
|
||||
{
|
||||
Q_UNUSED(characteristic);
|
||||
debug("characteristicWritten " + newValue.toHex(' '));
|
||||
}
|
||||
|
||||
void domyostreadmill::serviceScanDone(void)
|
||||
{
|
||||
qDebug() << "serviceScanDone";
|
||||
debug("serviceScanDone");
|
||||
|
||||
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
|
||||
connect(gattCommunicationChannelService, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), this, SLOT(stateChanged(QLowEnergyService::ServiceState)));
|
||||
gattCommunicationChannelService->discoverDetails();
|
||||
}
|
||||
|
||||
void domyostreadmill::errorService(QLowEnergyService::ServiceError err)
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceError>();
|
||||
debug("domyostreadmill::errorService" + QString::fromLocal8Bit(metaEnum.valueToKey(err)) + m_control->errorString());
|
||||
}
|
||||
|
||||
void domyostreadmill::error(QLowEnergyController::Error err)
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyController::Error>();
|
||||
debug("domyostreadmill::error" + QString::fromLocal8Bit(metaEnum.valueToKey(err)) + m_control->errorString());
|
||||
}
|
||||
|
||||
void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device)
|
||||
{
|
||||
qDebug() << "Found new device:" << device.name() << '(' << device.address().toString() << ')';
|
||||
debug("Found new device: " + device.name() + " (" + device.address().toString() + ')');
|
||||
if(device.name().startsWith("Domyos"))
|
||||
{
|
||||
discoveryAgent->stop();
|
||||
@@ -301,22 +421,24 @@ void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device)
|
||||
this, SLOT(serviceDiscovered(const QBluetoothUuid &)));
|
||||
connect(m_control, SIGNAL(discoveryFinished()),
|
||||
this, SLOT(serviceScanDone()));
|
||||
connect(m_control, SIGNAL(error(QLowEnergyController::Error)),
|
||||
this, SLOT(error(QLowEnergyController::Error)));
|
||||
|
||||
connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
|
||||
this, [this](QLowEnergyController::Error error) {
|
||||
Q_UNUSED(error);
|
||||
Q_UNUSED(this);
|
||||
qDebug() << "Cannot connect to remote device.";
|
||||
debug("Cannot connect to remote device.");
|
||||
exit(1);
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::connected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
qDebug() << "Controller connected. Search services...";
|
||||
debug("Controller connected. Search services...");
|
||||
m_control->discoverServices();
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
qDebug() << "LowEnergy controller disconnected";
|
||||
debug("LowEnergy controller disconnected");
|
||||
exit(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -34,17 +34,25 @@ public:
|
||||
private:
|
||||
double GetSpeedFromPacket(QByteArray packet);
|
||||
double GetInclinationFromPacket(QByteArray packet);
|
||||
void forceSpeedOrIncline(double requestSpeed, double requestIncline, uint16_t elapsed);
|
||||
void forceSpeedOrIncline(double requestSpeed, double requestIncline);
|
||||
void updateDisplay(uint16_t elapsed);
|
||||
void btinit();
|
||||
void writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log=false);
|
||||
void debug(QString text);
|
||||
|
||||
private slots:
|
||||
|
||||
void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue);
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
void update();
|
||||
void error(QLowEnergyController::Error err);
|
||||
void errorService(QLowEnergyService::ServiceError);
|
||||
};
|
||||
|
||||
#endif // DOMYOSTREADMILL_H
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
volatile double currentSpeed = 0;
|
||||
volatile double currentIncline = 0;
|
||||
volatile double currentHeart = 0;
|
||||
volatile uint8_t currentHeart = 0;
|
||||
volatile double requestSpeed = -1;
|
||||
volatile double requestIncline = -1;
|
||||
volatile int8_t requestStart = -1;
|
||||
@@ -15,7 +15,10 @@ virtualtreadmill::virtualtreadmill()
|
||||
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
|
||||
advertisingData.setIncludePowerLevel(true);
|
||||
advertisingData.setLocalName("DomyosBridge");
|
||||
advertisingData.setServices(QList<QBluetoothUuid>() << (QBluetoothUuid::ServiceClassUuid)0x1826); //FitnessMachineServiceUuid
|
||||
QList<QBluetoothUuid> services;
|
||||
services << ((QBluetoothUuid::ServiceClassUuid)0x1826); //FitnessMachineServiceUuid
|
||||
services << QBluetoothUuid::HeartRate;
|
||||
advertisingData.setServices(services);
|
||||
//! [Advertising Data]
|
||||
|
||||
//! [Service Data]
|
||||
@@ -57,9 +60,24 @@ virtualtreadmill::virtualtreadmill()
|
||||
serviceData.addCharacteristic(charData3);
|
||||
//! [Service Data]
|
||||
|
||||
QLowEnergyCharacteristicData charDataHR;
|
||||
charDataHR.setUuid(QBluetoothUuid::HeartRateMeasurement);
|
||||
charDataHR.setValue(QByteArray(2, 0));
|
||||
charDataHR.setProperties(QLowEnergyCharacteristic::Notify);
|
||||
const QLowEnergyDescriptorData clientConfigHR(QBluetoothUuid::ClientCharacteristicConfiguration,
|
||||
QByteArray(2, 0));
|
||||
charDataHR.addDescriptor(clientConfigHR);
|
||||
|
||||
QLowEnergyServiceData serviceDataHR;
|
||||
serviceDataHR.setType(QLowEnergyServiceData::ServiceTypePrimary);
|
||||
serviceDataHR.setUuid(QBluetoothUuid::HeartRate);
|
||||
serviceDataHR.addCharacteristic(charDataHR);
|
||||
|
||||
//! [Start Advertising]
|
||||
leController = QLowEnergyController::createPeripheral();
|
||||
Q_ASSERT(leController);
|
||||
service = leController->addService(serviceData);
|
||||
serviceHR = leController->addService(serviceDataHR);
|
||||
|
||||
QObject::connect(service, SIGNAL(characteristicChanged(const QLowEnergyCharacteristic, const QByteArray)), this, SLOT(characteristicChanged(const QLowEnergyCharacteristic, const QByteArray)));
|
||||
|
||||
@@ -131,7 +149,7 @@ void virtualtreadmill::treadmillProvider()
|
||||
{
|
||||
QByteArray value;
|
||||
value.append(0x08); // Inclination avaiable
|
||||
value.append(0x11); // Heart rate and Force on Belt and Power Output present avaiable
|
||||
value.append((char)0x00);
|
||||
|
||||
uint16_t normalizeSpeed = (uint16_t)qRound(currentSpeed * 100);
|
||||
char a = (normalizeSpeed >> 8) & 0XFF;
|
||||
@@ -159,11 +177,28 @@ void virtualtreadmill::treadmillProvider()
|
||||
|
||||
value.append(rampBytes); //ramp angle
|
||||
|
||||
value.append(char(currentHeart)); // heart current
|
||||
QLowEnergyCharacteristic characteristic
|
||||
= service->characteristic((QBluetoothUuid::CharacteristicType)0x2ACD); //TreadmillDataCharacteristicUuid
|
||||
Q_ASSERT(characteristic.isValid());
|
||||
service->writeCharacteristic(characteristic, value); // Potentially causes notification.
|
||||
|
||||
//characteristic
|
||||
// = service->characteristic((QBluetoothUuid::CharacteristicType)0x2AD9); // Fitness Machine Control Point
|
||||
//Q_ASSERT(characteristic.isValid());
|
||||
//service->readCharacteristic(characteristic);
|
||||
|
||||
QByteArray valueHR;
|
||||
valueHR.append(char(0)); // Flags that specify the format of the value.
|
||||
valueHR.append(char(currentHeart)); // Actual value.
|
||||
QLowEnergyCharacteristic characteristicHR
|
||||
= serviceHR->characteristic(QBluetoothUuid::HeartRateMeasurement);
|
||||
Q_ASSERT(characteristicHR.isValid());
|
||||
serviceHR->writeCharacteristic(characteristicHR, valueHR); // Potentially causes notification.
|
||||
}
|
||||
|
||||
uint16_t virtualtreadmill::watts()
|
||||
{
|
||||
// calc Watts ref. https://alancouzens.com/blog/Run_Power.html
|
||||
value.append(0xFF); // Force on Belt Field (autocalculated)
|
||||
value.append(0x7F);
|
||||
|
||||
uint16_t watts=0;
|
||||
if(currentSpeed > 0)
|
||||
@@ -176,21 +211,5 @@ void virtualtreadmill::treadmillProvider()
|
||||
double vwatts=((9.8*weight) * (currentIncline/100));
|
||||
watts=hwatts+vwatts;
|
||||
}
|
||||
a = (watts >> 8) & 0XFF;
|
||||
b = watts & 0XFF;
|
||||
QByteArray wattsBytes;
|
||||
wattsBytes.append(b);
|
||||
wattsBytes.append(a);
|
||||
|
||||
value.append(wattsBytes);
|
||||
|
||||
QLowEnergyCharacteristic characteristic
|
||||
= service->characteristic((QBluetoothUuid::CharacteristicType)0x2ACD); //TreadmillDataCharacteristicUuid
|
||||
Q_ASSERT(characteristic.isValid());
|
||||
service->writeCharacteristic(characteristic, value); // Potentially causes notification.
|
||||
|
||||
//characteristic
|
||||
// = service->characteristic((QBluetoothUuid::CharacteristicType)0x2AD9); // Fitness Machine Control Point
|
||||
//Q_ASSERT(characteristic.isValid());
|
||||
//service->readCharacteristic(characteristic);
|
||||
return watts;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,11 @@ public:
|
||||
private:
|
||||
QLowEnergyController* leController;
|
||||
QLowEnergyService* service;
|
||||
QLowEnergyService* serviceHR;
|
||||
QLowEnergyAdvertisingData advertisingData;
|
||||
QLowEnergyServiceData serviceData;
|
||||
QTimer treadmillTimer;
|
||||
uint16_t watts();
|
||||
|
||||
private slots:
|
||||
void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
|
||||
Reference in New Issue
Block a user