From 5774e725a7b66fc7c368ae20bdb14f99360b5c4f Mon Sep 17 00:00:00 2001 From: cagnulein Date: Fri, 2 Oct 2020 16:38:07 +0200 Subject: [PATCH 01/45] started adding a gui --- src/domyostreadmill.cpp | 4 +- src/main.cpp | 29 +++-- src/mainwindow.cpp | 102 +++++++++++++++ src/mainwindow.h | 33 +++++ src/mainwindow.ui | 278 ++++++++++++++++++++++++++++++++++++++++ src/qdomyos-zwift.pro | 10 +- src/trainprogram.cpp | 6 + src/trainprogram.h | 23 ++++ 8 files changed, 474 insertions(+), 11 deletions(-) create mode 100644 src/mainwindow.cpp create mode 100644 src/mainwindow.h create mode 100644 src/mainwindow.ui create mode 100644 src/trainprogram.cpp create mode 100644 src/trainprogram.h diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 48c624031..af7694757 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -1,6 +1,6 @@ #include "domyostreadmill.h" #include "virtualtreadmill.h" - +#include "trainprogram.h" // set speed and incline to 0 uint8_t initData1[] = { 0xf0, 0xc8, 0x01, 0xb9 }; @@ -74,6 +74,8 @@ extern volatile double requestIncline; extern volatile int8_t requestStart; extern volatile int8_t requestStop; +trainprogram* trainProgram = 0; + domyostreadmill::domyostreadmill() { QTimer* refresh = new QTimer(this); diff --git a/src/main.cpp b/src/main.cpp index 98db0c1df..c84b1cd2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,34 @@ -#include +#include #include "virtualtreadmill.h" #include "domyostreadmill.h" +#include "mainwindow.h" + +QCoreApplication* createApplication(int &argc, char *argv[]) +{ + for (int i = 1; i < argc; ++i) { + if (!qstrcmp(argv[i], "-no-gui")) + return new QCoreApplication(argc, argv); + } + return new QApplication(argc, argv); +} int main(int argc, char *argv[]) { - //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); -#ifndef Q_OS_ANDROID - QCoreApplication app(argc, argv); -#else - QGuiApplication app(argc, argv); -#endif + QScopedPointer app(createApplication(argc, argv)); //virtualtreadmill* V = new virtualtreadmill(); domyostreadmill* D = new domyostreadmill(); + if (qobject_cast(app.data())) { + // start GUI version... + MainWindow* W = new MainWindow(); + W->show(); + } else { + // start non-GUI version... + } + //Q_UNUSED(V); Q_UNUSED(D); - return app.exec(); + return app->exec(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 000000000..9519f99c0 --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,102 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +extern volatile double currentSpeed; +extern volatile double currentIncline; +extern volatile uint8_t currentHeart; +extern volatile double requestSpeed; +extern volatile double requestIncline; +extern volatile int8_t requestStart; +extern volatile int8_t requestStop; +extern trainprogram* trainProgram; + +MainWindow::MainWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &MainWindow::update); + timer->start(1000); + + update(); +} + +void MainWindow::update() +{ + ui->speed->setText(QString::number(currentSpeed)); + ui->inclination->setText(QString::number(currentIncline)); + ui->heartrate->setText(QString::number(currentHeart)); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_tableWidget_cellChanged(int row, int column) +{ + static bool editing = false; + + if(editing) return; + if(column == 0) + { + switch(ui->tableWidget->currentItem()->text().length()) + { + case 4: + ui->tableWidget->currentItem()->setText("00:0" + ui->tableWidget->currentItem()->text()); + break; + case 5: + ui->tableWidget->currentItem()->setText("00:" + ui->tableWidget->currentItem()->text()); + break; + case 7: + ui->tableWidget->currentItem()->setText("0" + ui->tableWidget->currentItem()->text()); + break; + } + QString fmt = "hh:mm:ss"; + QTime dt = QTime::fromString(ui->tableWidget->currentItem()->text()); + QString timeStr = dt.toString("hh:mm:ss"); + ui->tableWidget->currentItem()->setText(timeStr); + } + + if(row + 1 == ui->tableWidget->rowCount() && ui->tableWidget->currentItem()->text().length() ) + { + editing = true; + ui->tableWidget->insertRow(row + 1); + ui->tableWidget->setItem(row + 1, 0, new QTableWidgetItem("00:00:00")); + ui->tableWidget->setItem(row + 1, 1, new QTableWidgetItem("10")); + ui->tableWidget->setItem(row + 1, 2, new QTableWidgetItem("0")); + ui->tableWidget->setItem(row + 1, 3, new QTableWidgetItem("")); + ui->tableWidget->item(row + 1, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 3)->setCheckState(Qt::CheckState::Checked); + editing = false; + } + + QList rows; + for(int i = 0; i < ui->tableWidget->rowCount(); i++) + { + if(!ui->tableWidget->item(i, 0)->text().contains("00:00:00")) + { + trainrow t; + t.duration = QTime::fromString(ui->tableWidget->item(i, 0)->text(), "hh:mm:ss"); + t.speed = ui->tableWidget->item(i, 1)->text().toFloat(); + t.inclination = ui->tableWidget->item(i, 2)->text().toFloat(); + t.forcespeed = ui->tableWidget->item(i, 3)->checkState() == Qt::CheckState::Checked; + rows.append(t); + } + else + { + break; + } + if(trainProgram) delete trainProgram; + trainProgram = new trainprogram(rows); + } +} + +void MainWindow::on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) +{ + +} diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 000000000..308b136fc --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,33 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QDialog +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + QTimer *timer; + +private slots: + void update(); + void on_tableWidget_cellChanged(int row, int column); + void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); +}; + +#endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui new file mode 100644 index 000000000..714249bef --- /dev/null +++ b/src/mainwindow.ui @@ -0,0 +1,278 @@ + + + MainWindow + + + + 0 + 0 + 640 + 480 + + + + qDoymos-Zwift + + + + + + + 0 + 0 + + + + + 0 + 80 + + + + Treadmill Status + + + + + 10 + 30 + 185 + 42 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Speed (Km/h) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + 220 + 30 + 185 + 42 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Inclination (degrees) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + 420 + 30 + 185 + 42 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Heart rate (bpm) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + + + 0 + 0 + + + + Train me! + + + + + + + 0 + 0 + + + + true + + + true + + + 130 + + + + 1 + + + + + Durantion (s) + + + + + Speed (km/h) + + + + + Inclination (degrees) + + + + + Force Speed + + + + + 00:10:00 + + + AlignCenter + + + + + 10 + + + AlignCenter + + + + + 0 + + + AlignCenter + + + + + + + + AlignCenter + + + Checked + + + ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled + + + + + + + + + + + + diff --git a/src/qdomyos-zwift.pro b/src/qdomyos-zwift.pro index aebb1d44a..01d74ed21 100644 --- a/src/qdomyos-zwift.pro +++ b/src/qdomyos-zwift.pro @@ -1,5 +1,4 @@ -QT -= gui -QT += bluetooth +QT += bluetooth widgets CONFIG += c++11 console CONFIG -= app_bundle @@ -18,6 +17,8 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ domyostreadmill.cpp \ main.cpp \ + mainwindow.cpp \ + trainprogram.cpp \ virtualtreadmill.cpp # Default rules for deployment. @@ -27,4 +28,9 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin HEADERS += \ domyostreadmill.h \ + mainwindow.h \ + trainprogram.h \ virtualtreadmill.h + +FORMS += \ + mainwindow.ui diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp new file mode 100644 index 000000000..a5ec26a1f --- /dev/null +++ b/src/trainprogram.cpp @@ -0,0 +1,6 @@ +#include "trainprogram.h" + +trainprogram::trainprogram(QList rows) +{ + this->rows = rows; +} diff --git a/src/trainprogram.h b/src/trainprogram.h new file mode 100644 index 000000000..16cd783fa --- /dev/null +++ b/src/trainprogram.h @@ -0,0 +1,23 @@ +#ifndef TRAINPROGRAM_H +#define TRAINPROGRAM_H +#include + +class trainrow +{ +public: + QTime duration; + double speed; + double inclination; + bool forcespeed; +}; + +class trainprogram +{ +public: + trainprogram(QList); + +private: + QList rows; +}; + +#endif // TRAINPROGRAM_H From 22b6de14f197a1a3a64299cb26165bf46828b55c Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 08:56:00 +0200 Subject: [PATCH 02/45] characteristicWritten and error added with mutex --- src/domyostreadmill.cpp | 23 ++++++++++++++++++++++- src/domyostreadmill.h | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 6b6a5dfa2..d537cb7bb 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -88,6 +88,8 @@ QLowEnergyCharacteristic gattWriteCharacteristic; QLowEnergyCharacteristic gattNotifyCharacteristic; QBluetoothDeviceDiscoveryAgent *discoveryAgent; +QMutex characteristicMutex; + bool initDone = false; extern volatile double currentSpeed; @@ -129,6 +131,7 @@ void domyostreadmill::debug(QString text) void domyostreadmill::writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log) { + characteristicMutex.lock(); gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)data, data_len)); if(!disable_log) debug(" >> " + QByteArray((const char*)data, data_len).toHex(' ') + " // " + info); @@ -318,6 +321,10 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) // 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))); // await _gattNotifyCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); QByteArray descriptor; @@ -332,6 +339,13 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) } } +void domyostreadmill::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) +{ + Q_UNUSED(characteristic); + debug("characteristicWritten " + newValue.toHex(' ')); + characteristicMutex.unlock(); +} + void domyostreadmill::serviceScanDone(void) { debug("serviceScanDone"); @@ -341,9 +355,16 @@ void domyostreadmill::serviceScanDone(void) gattCommunicationChannelService->discoverDetails(); } +void domyostreadmill::errorService(QLowEnergyService::ServiceError err) +{ + QMetaEnum metaEnum = QMetaEnum::fromType(); + debug("domyostreadmill::errorService" + QString::fromLocal8Bit(metaEnum.valueToKey(err)) + m_control->errorString()); +} + void domyostreadmill::error(QLowEnergyController::Error err) { - qDebug() << "domyostreadmill::error" << err << m_control->errorString(); + QMetaEnum metaEnum = QMetaEnum::fromType(); + debug("domyostreadmill::error" + QString::fromLocal8Bit(metaEnum.valueToKey(err)) + m_control->errorString()); } void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index bf05d1fb0..1cc4a564f 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -42,6 +42,7 @@ class domyostreadmill : QObject private slots: void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue); + void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue); void stateChanged(QLowEnergyService::ServiceState state); void serviceDiscovered(const QBluetoothUuid &gatt); @@ -49,6 +50,7 @@ private slots: void deviceDiscovered(const QBluetoothDeviceInfo &device); void update(); void error(QLowEnergyController::Error err); + void errorService(QLowEnergyService::ServiceError); }; #endif // DOMYOSTREADMILL_H From eff5d1d2f39f0b61d1968a1d4bf6f1603689994d Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 10:31:05 +0200 Subject: [PATCH 03/45] elapsed timer should count only when tape is moving --- src/domyostreadmill.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index d537cb7bb..69418f734 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -177,7 +177,9 @@ void domyostreadmill::update() gattNotifyCharacteristic.isValid() && initDone) { - counter++; + if(currentSpeed > 0.0) + counter++; + if(!first) { debug("creating virtual treadmill interface..."); From 4712d5780a8737d27fe06ca5d2c66c2414d487fe Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 14:19:09 +0200 Subject: [PATCH 04/45] adding ability to save and load training program (not finished yet) --- src/mainwindow.cpp | 76 +++++++++++++++++++++++++++++++++++--------- src/mainwindow.h | 5 +++ src/mainwindow.ui | 30 ++++++++++++++++- src/trainprogram.cpp | 44 +++++++++++++++++++++++++ src/trainprogram.h | 4 ++- 5 files changed, 142 insertions(+), 17 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9519f99c0..533f36c5a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include extern volatile double currentSpeed; extern volatile double currentIncline; @@ -35,10 +36,23 @@ MainWindow::~MainWindow() delete ui; } -void MainWindow::on_tableWidget_cellChanged(int row, int column) +void MainWindow::addEmptyRow(int row) { - static bool editing = false; + editing = true; + ui->tableWidget->insertRow(row + 1); + ui->tableWidget->setItem(row + 1, 0, new QTableWidgetItem("00:00:00")); + ui->tableWidget->setItem(row + 1, 1, new QTableWidgetItem("10")); + ui->tableWidget->setItem(row + 1, 2, new QTableWidgetItem("0")); + ui->tableWidget->setItem(row + 1, 3, new QTableWidgetItem("")); + ui->tableWidget->item(row + 1, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row + 1, 3)->setCheckState(Qt::CheckState::Checked); + editing = false; +} +void MainWindow::on_tableWidget_cellChanged(int row, int column) +{ if(editing) return; if(column == 0) { @@ -61,19 +75,7 @@ void MainWindow::on_tableWidget_cellChanged(int row, int column) } if(row + 1 == ui->tableWidget->rowCount() && ui->tableWidget->currentItem()->text().length() ) - { - editing = true; - ui->tableWidget->insertRow(row + 1); - ui->tableWidget->setItem(row + 1, 0, new QTableWidgetItem("00:00:00")); - ui->tableWidget->setItem(row + 1, 1, new QTableWidgetItem("10")); - ui->tableWidget->setItem(row + 1, 2, new QTableWidgetItem("0")); - ui->tableWidget->setItem(row + 1, 3, new QTableWidgetItem("")); - ui->tableWidget->item(row + 1, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 3)->setCheckState(Qt::CheckState::Checked); - editing = false; - } + addEmptyRow(row); QList rows; for(int i = 0; i < ui->tableWidget->rowCount(); i++) @@ -100,3 +102,47 @@ void MainWindow::on_tableWidget_currentItemChanged(QTableWidgetItem *current, QT { } + +void MainWindow::on_save_clicked() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), + "train.xml", + tr("Train Program (*.xml)")); + if(!fileName.isEmpty() && trainProgram) + trainProgram->save(fileName); +} + +void MainWindow::on_load_clicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), + "train.xml", + tr("Train Program (*.xml)")); + if(!fileName.isEmpty()) + { + if(trainProgram) + delete trainProgram; + trainProgram = trainprogram::load(fileName); + int countRow = 0; + foreach(trainrow row, trainProgram->rows) + { + if(ui->tableWidget->rowCount() <= countRow) + addEmptyRow(countRow); + + QTableWidgetItem* i; + editing = true; + i = ui->tableWidget->takeItem(countRow, 0); + i->setText(row.duration.toString("hh:mm:ss")); + ui->tableWidget->setItem(countRow, 0, i); + i = ui->tableWidget->takeItem(countRow, 1); + i->setText(QString::number(row.speed)); + ui->tableWidget->setItem(countRow, 1, i); + i->setText(QString::number(row.inclination)); + ui->tableWidget->setItem(countRow, 2, i); + i->setCheckState(row.forcespeed?Qt::CheckState::Checked:Qt::CheckState::Unchecked); + ui->tableWidget->setItem(countRow, 3, i); + editing = false; + + countRow++; + } + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 308b136fc..d4a3e45a1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -21,6 +21,9 @@ class MainWindow : public QDialog ~MainWindow(); private: + void addEmptyRow(int row); + bool editing = false; + Ui::MainWindow *ui; QTimer *timer; @@ -28,6 +31,8 @@ private slots: void update(); void on_tableWidget_cellChanged(int row, int column); void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); + void on_save_clicked(); + void on_load_clicked(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 714249bef..e70fde050 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 640 - 480 + 484 @@ -182,6 +182,9 @@ 0 + + Qt::LeftToRight + Train me! @@ -271,6 +274,31 @@ + + + + + + &Reset + + + + + + + &Load + + + + + + + &Save + + + + + diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index a5ec26a1f..ef4032199 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -1,6 +1,50 @@ #include "trainprogram.h" +#include +#include trainprogram::trainprogram(QList rows) { this->rows = rows; } + +void trainprogram::save(QString filename) +{ + QFile output(filename); + output.open(QIODevice::WriteOnly); + QXmlStreamWriter stream(&output); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + foreach (trainrow row, rows) { + stream.writeStartElement("row"); + stream.writeAttribute("duration", row.duration.toString()); + stream.writeAttribute("speed", QString::number(row.speed)); + stream.writeAttribute("inclination", QString::number(row.inclination)); + stream.writeAttribute("forcespeed", row.forcespeed?"1":"0"); + stream.writeEndElement(); // bookmark + } + stream.writeEndDocument(); +} + +trainprogram* trainprogram::load(QString filename) +{ + QList list; + QFile input(filename); + input.open(QIODevice::ReadOnly); + QXmlStreamReader stream(&input); + while(!stream.atEnd()) + { + stream.readNext(); + trainrow row; + QXmlStreamAttributes atts = stream.attributes(); + if(atts.length()) + { + row.duration = QTime::fromString(atts.value("duration").toString(), "hh:mm:ss"); + row.speed = atts.value("speed").toDouble(); + row.inclination = atts.value("inclination").toDouble(); + row.forcespeed = atts.value("forcespeed").toInt()?true:false ; + list.append(row); + } + } + trainprogram *t = new trainprogram(list); + return t; +} diff --git a/src/trainprogram.h b/src/trainprogram.h index 16cd783fa..4ccd1edf6 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -15,9 +15,11 @@ class trainprogram { public: trainprogram(QList); + void save(QString filename); + static trainprogram* load(QString filename); -private: QList rows; +private: }; #endif // TRAINPROGRAM_H From 99fd62c8ec22659a67de9675b4d2ce82adc63287 Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 14:21:53 +0200 Subject: [PATCH 05/45] converted mutex to eventloop --- src/domyostreadmill.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 69418f734..9ad599a74 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -88,8 +88,6 @@ QLowEnergyCharacteristic gattWriteCharacteristic; QLowEnergyCharacteristic gattNotifyCharacteristic; QBluetoothDeviceDiscoveryAgent *discoveryAgent; -QMutex characteristicMutex; - bool initDone = false; extern volatile double currentSpeed; @@ -131,10 +129,16 @@ void domyostreadmill::debug(QString text) void domyostreadmill::writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log) { - characteristicMutex.lock(); + 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); + debug(" >> " + QByteArray((const char*)data, data_len).toHex(' ') + " // " + info); + + loop.exec(); } void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestIncline, uint16_t elapsed) @@ -345,7 +349,6 @@ void domyostreadmill::characteristicWritten(const QLowEnergyCharacteristic &char { Q_UNUSED(characteristic); debug("characteristicWritten " + newValue.toHex(' ')); - characteristicMutex.unlock(); } void domyostreadmill::serviceScanDone(void) From 33a478b1ae287b1a097ef04aed24cb63ba708f4c Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 15:19:38 +0200 Subject: [PATCH 06/45] load, save and reset added and tested --- src/mainwindow.cpp | 60 ++++++++++++++++++++++++++++++++++++---------- src/mainwindow.h | 3 ++- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 533f36c5a..25133ff26 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -36,18 +36,19 @@ MainWindow::~MainWindow() delete ui; } -void MainWindow::addEmptyRow(int row) +void MainWindow::addEmptyRow() { + int row = ui->tableWidget->rowCount(); editing = true; - ui->tableWidget->insertRow(row + 1); - ui->tableWidget->setItem(row + 1, 0, new QTableWidgetItem("00:00:00")); - ui->tableWidget->setItem(row + 1, 1, new QTableWidgetItem("10")); - ui->tableWidget->setItem(row + 1, 2, new QTableWidgetItem("0")); - ui->tableWidget->setItem(row + 1, 3, new QTableWidgetItem("")); - ui->tableWidget->item(row + 1, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - ui->tableWidget->item(row + 1, 3)->setCheckState(Qt::CheckState::Checked); + ui->tableWidget->insertRow(row); + ui->tableWidget->setItem(row, 0, new QTableWidgetItem("00:00:00")); + ui->tableWidget->setItem(row, 1, new QTableWidgetItem("10")); + ui->tableWidget->setItem(row, 2, new QTableWidgetItem("0")); + ui->tableWidget->setItem(row, 3, new QTableWidgetItem("")); + ui->tableWidget->item(row, 0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row, 1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row, 2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + ui->tableWidget->item(row, 3)->setCheckState(Qt::CheckState::Checked); editing = false; } @@ -75,7 +76,7 @@ void MainWindow::on_tableWidget_cellChanged(int row, int column) } if(row + 1 == ui->tableWidget->rowCount() && ui->tableWidget->currentItem()->text().length() ) - addEmptyRow(row); + addEmptyRow(); QList rows; for(int i = 0; i < ui->tableWidget->rowCount(); i++) @@ -126,23 +127,58 @@ void MainWindow::on_load_clicked() foreach(trainrow row, trainProgram->rows) { if(ui->tableWidget->rowCount() <= countRow) - addEmptyRow(countRow); + addEmptyRow(); QTableWidgetItem* i; editing = true; i = ui->tableWidget->takeItem(countRow, 0); i->setText(row.duration.toString("hh:mm:ss")); ui->tableWidget->setItem(countRow, 0, i); + i = ui->tableWidget->takeItem(countRow, 1); i->setText(QString::number(row.speed)); ui->tableWidget->setItem(countRow, 1, i); + + i = ui->tableWidget->takeItem(countRow, 2); i->setText(QString::number(row.inclination)); ui->tableWidget->setItem(countRow, 2, i); + + i = ui->tableWidget->takeItem(countRow, 3); i->setCheckState(row.forcespeed?Qt::CheckState::Checked:Qt::CheckState::Unchecked); ui->tableWidget->setItem(countRow, 3, i); + editing = false; countRow++; } } } + +void MainWindow::on_reset_clicked() +{ + int countRow = 0; + foreach(trainrow row, trainProgram->rows) + { + QTableWidgetItem* i; + editing = true; + i = ui->tableWidget->takeItem(countRow, 0); + i->setText("00:00:00"); + ui->tableWidget->setItem(countRow, 0, i); + + i = ui->tableWidget->takeItem(countRow, 1); + i->setText("0"); + ui->tableWidget->setItem(countRow, 1, i); + + i = ui->tableWidget->takeItem(countRow, 2); + i->setText("0"); + ui->tableWidget->setItem(countRow, 2, i); + + i = ui->tableWidget->takeItem(countRow, 3); + i->setCheckState(row.forcespeed?Qt::CheckState::Checked:Qt::CheckState::Unchecked); + ui->tableWidget->setItem(countRow, 3, i); + + editing = false; + + countRow++; + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index d4a3e45a1..2e7688857 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -21,7 +21,7 @@ class MainWindow : public QDialog ~MainWindow(); private: - void addEmptyRow(int row); + void addEmptyRow(); bool editing = false; Ui::MainWindow *ui; @@ -33,6 +33,7 @@ private slots: void on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); void on_save_clicked(); void on_load_clicked(); + void on_reset_clicked(); }; #endif // MAINWINDOW_H From 4944f6d48dd11a4fbd8502f4ae0bc9f14a87ccc4 Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 16:35:36 +0200 Subject: [PATCH 07/45] scheduler added --- src/domyostreadmill.cpp | 11 +++---- src/domyostreadmill.h | 2 ++ src/mainwindow.cpp | 16 +++++++++++ src/mainwindow.h | 2 ++ src/mainwindow.ui | 32 +++++++++++++++++++++ src/trainprogram.cpp | 63 +++++++++++++++++++++++++++++++++++++++++ src/trainprogram.h | 11 ++++++- 7 files changed, 131 insertions(+), 6 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index af7694757..b2b42cbaf 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -78,7 +78,7 @@ trainprogram* trainProgram = 0; domyostreadmill::domyostreadmill() { - QTimer* refresh = new QTimer(this); + refresh = new QTimer(this); initDone = false; @@ -122,7 +122,7 @@ void domyostreadmill::update() { static uint8_t first = 0; static virtualtreadmill* v; - static uint32_t counter = 0; + Q_UNUSED(v); //qDebug() << treadmill.isValid() << m_control->state() << gattCommunicationChannelService << gattWriteCharacteristic.isValid() << gattNotifyCharacteristic.isValid() << initDone; if(treadmill.isValid() && @@ -132,7 +132,8 @@ void domyostreadmill::update() gattNotifyCharacteristic.isValid() && initDone) { - counter++; + trainProgram->scheduler(refresh->interval()); + if(!first) { qDebug() << "creating virtual treadmill interface..."; @@ -147,13 +148,13 @@ void domyostreadmill::update() if(requestSpeed != -1) { qDebug() << "writing speed" << requestSpeed; - forceSpeedOrIncline(requestSpeed, currentIncline, counter/5); + forceSpeedOrIncline(requestSpeed, currentIncline, trainProgram->elapsed); requestSpeed = -1; } if(requestIncline != -1) { qDebug() << "writing incline" << requestIncline; - forceSpeedOrIncline(currentSpeed, requestIncline, counter/5); + forceSpeedOrIncline(currentSpeed, requestIncline, trainProgram->elapsed); requestIncline = -1; } if(requestStart != -1) diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index e7033c36e..0ab9f964d 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -37,6 +37,8 @@ class domyostreadmill : QObject void forceSpeedOrIncline(double requestSpeed, double requestIncline, uint16_t elapsed); void btinit(); + QTimer* refresh; + private slots: void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 25133ff26..9e6911753 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -156,6 +156,8 @@ void MainWindow::on_load_clicked() void MainWindow::on_reset_clicked() { + if(currentSpeed > 0) return; + int countRow = 0; foreach(trainrow row, trainProgram->rows) { @@ -181,4 +183,18 @@ void MainWindow::on_reset_clicked() countRow++; } + + if(trainProgram) delete trainProgram; + trainProgram = new trainprogram(QList()); +} + +void MainWindow::on_stop_clicked() +{ + requestStop = 1; +} + +void MainWindow::on_start_clicked() +{ + trainProgram->restart(); + requestStart = 1; } diff --git a/src/mainwindow.h b/src/mainwindow.h index 2e7688857..5cf863d1c 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -34,6 +34,8 @@ private slots: void on_save_clicked(); void on_load_clicked(); void on_reset_clicked(); + void on_stop_clicked(); + void on_start_clicked(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index e70fde050..947b2753d 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -299,6 +299,38 @@ + + + + + + + 9 + 75 + true + + + + Start + + + + + + + + 9 + 75 + true + + + + S&top + + + + + diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index ef4032199..b021941f9 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -2,11 +2,74 @@ #include #include +extern volatile double requestSpeed; +extern volatile double requestIncline; +extern volatile int8_t requestStart; +extern volatile int8_t requestStop; + trainprogram::trainprogram(QList rows) { this->rows = rows; } +void trainprogram::scheduler(int tick) +{ + // entry point + if(ticks == 0 && currentStep == 0) + { + if(rows[0].forcespeed && rows[0].speed) + { + requestSpeed = rows[0].speed; + } + requestIncline = rows[0].inclination; + } + + ticks++; + elapsed = ticks / (1000 / tick); + ticksCurrentRow++; + elapsedCurrentRow = ticksCurrentRow / (1000 / tick); + + uint32_t currentRowLen = rows[currentStep].duration.second() + + (rows[currentStep].duration.minute() * 60) + + (rows[currentStep].duration.hour() * 3600); + + uint32_t nextRowLen = 0; + + if(rows.count() > currentStep + 1) + nextRowLen = rows[currentStep + 1].duration.second() + + (rows[currentStep + 1].duration.minute() * 60) + + (rows[currentStep + 1].duration.hour() * 3600); + + if(elapsedCurrentRow >= currentRowLen && currentRowLen) + { + if(nextRowLen) + { + currentStep++; + ticksCurrentRow = 0; + elapsedCurrentRow = 0; + if(rows[currentStep].forcespeed && rows[currentStep].speed) + { + requestSpeed = rows[currentStep].speed; + } + requestIncline = rows[currentStep].inclination; + } + else + { + requestStop = 1; + restart(); + } + } +} + +void trainprogram::restart() +{ + ticks = 0; + ticksCurrentRow = 0; + elapsed = 0; + elapsedCurrentRow = 0; + currentStep = 0; +} + void trainprogram::save(QString filename) { QFile output(filename); diff --git a/src/trainprogram.h b/src/trainprogram.h index 4ccd1edf6..6d0c1d1fe 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -19,7 +19,16 @@ class trainprogram static trainprogram* load(QString filename); QList rows; -private: + uint32_t elapsed = 0; + + void restart(); + void scheduler(int tick); + +private: + uint32_t ticks = 0; + uint16_t currentStep = 0; + uint32_t ticksCurrentRow = 0; + uint32_t elapsedCurrentRow = 0; }; #endif // TRAINPROGRAM_H From c2c5b7746f4df753761c39ee1b99d2a46812d0bb Mon Sep 17 00:00:00 2001 From: cagnulein Date: Thu, 8 Oct 2020 17:21:16 +0200 Subject: [PATCH 08/45] treadmill class created deleting all the external variables --- src/domyostreadmill.cpp | 38 +++++++++++++++----------------------- src/domyostreadmill.h | 4 +++- src/qdomyos-zwift.pro | 2 ++ src/treadmill.cpp | 14 ++++++++++++++ src/treadmill.h | 29 +++++++++++++++++++++++++++++ src/virtualtreadmill.cpp | 36 ++++++++++++++++-------------------- src/virtualtreadmill.h | 6 +++++- 7 files changed, 84 insertions(+), 45 deletions(-) create mode 100644 src/treadmill.cpp create mode 100644 src/treadmill.h diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 39c9593f0..8a51e284f 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -59,7 +59,7 @@ QBluetoothUuid _gattCommunicationChannelServiceId((QString)"49535343-fe7d-4ae5-8 QBluetoothUuid _gattWriteCharacteristicId((QString)"49535343-8841-43f4-a8d4-ecbe34729bb3"); QBluetoothUuid _gattNotifyCharacteristicId((QString)"49535343-1e4d-4bd9-ba61-23c647249616"); -QBluetoothDeviceInfo treadmill; +QBluetoothDeviceInfo bttreadmill; QLowEnergyController* m_control = 0; QLowEnergyService* gattCommunicationChannelService = 0; QLowEnergyCharacteristic gattWriteCharacteristic; @@ -68,14 +68,6 @@ QBluetoothDeviceDiscoveryAgent *discoveryAgent; bool initDone = false; -extern volatile double currentSpeed; -extern volatile double currentIncline; -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() @@ -121,7 +113,7 @@ void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestInc 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[12] = currentHeart(); writeIncline[16] = (uint8_t)(requestIncline * 10); @@ -143,7 +135,7 @@ 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() && + if(bttreadmill.isValid() && (m_control->state() == QLowEnergyController::ConnectedState || m_control->state() == QLowEnergyController::DiscoveredState) && gattCommunicationChannelService && gattWriteCharacteristic.isValid() && @@ -154,7 +146,7 @@ void domyostreadmill::update() if(!first) { debug("creating virtual treadmill interface..."); - v = new virtualtreadmill(); + v = new virtualtreadmill(this); } first = 1; writeCharacteristic(noOpData, sizeof(noOpData), "noOp", true); @@ -165,14 +157,14 @@ void domyostreadmill::update() if(requestSpeed != -1) { debug("writing speed " + QString::number(requestSpeed)); - forceSpeedOrIncline(requestSpeed, currentIncline, counter/5); + forceSpeedOrIncline(requestSpeed, Inclination, counter/5); requestSpeed = -1; } - if(requestIncline != -1) + if(requestInclination != -1) { - debug("writing incline " + QString::number(requestIncline)); - forceSpeedOrIncline(currentSpeed, requestIncline, counter/5); - requestIncline = -1; + debug("writing incline " + QString::number(requestInclination)); + forceSpeedOrIncline(currentSpeed(), requestInclination, counter/5); + requestInclination = -1; } if(requestStart != -1) { @@ -232,14 +224,14 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char Debug.WriteLine(args.CharacteristicValue.ToArray().HexDump()); #endif - currentHeart = newValue.at(18); + Heart = newValue.at(18); debug("Current speed: " + QString::number(speed)); debug("Current incline: " + QString::number(incline)); - debug("Current heart: " + QString::number(currentHeart)); + debug("Current heart: " + QString::number(Heart)); - currentSpeed = speed; - currentIncline = incline; + Speed = speed; + Inclination = incline; } double domyostreadmill::GetSpeedFromPacket(QByteArray packet) @@ -320,8 +312,8 @@ void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) if(device.name().startsWith("Domyos")) { discoveryAgent->stop(); - treadmill = device; - m_control = QLowEnergyController::createCentral(treadmill, this); + bttreadmill = device; + m_control = QLowEnergyController::createCentral(bttreadmill, this); connect(m_control, SIGNAL(serviceDiscovered(const QBluetoothUuid &)), this, SLOT(serviceDiscovered(const QBluetoothUuid &))); connect(m_control, SIGNAL(discoveryFinished()), diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 6aacbf32d..a46a691af 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -25,7 +25,9 @@ #include -class domyostreadmill : QObject +#include "treadmill.h" + +class domyostreadmill : public treadmill { Q_OBJECT public: diff --git a/src/qdomyos-zwift.pro b/src/qdomyos-zwift.pro index aebb1d44a..1b9be6940 100644 --- a/src/qdomyos-zwift.pro +++ b/src/qdomyos-zwift.pro @@ -18,6 +18,7 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ domyostreadmill.cpp \ main.cpp \ + treadmill.cpp \ virtualtreadmill.cpp # Default rules for deployment. @@ -27,4 +28,5 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin HEADERS += \ domyostreadmill.h \ + treadmill.h \ virtualtreadmill.h diff --git a/src/treadmill.cpp b/src/treadmill.cpp new file mode 100644 index 000000000..99f2e5eb9 --- /dev/null +++ b/src/treadmill.cpp @@ -0,0 +1,14 @@ +#include "treadmill.h" + +treadmill::treadmill() +{ + +} + +void treadmill::start(){ requestStart = 1; } +void treadmill::stop(){ requestStop = 1; } +void treadmill::changeSpeed(double speed){ requestSpeed = speed;} +void treadmill::changeInclination(double inclination){ requestInclination = inclination; } +unsigned char treadmill::currentHeart(){ return Heart; } +double treadmill::currentSpeed(){ return Speed; } +double treadmill::currentInclination(){ return Inclination; } diff --git a/src/treadmill.h b/src/treadmill.h new file mode 100644 index 000000000..9fbe53945 --- /dev/null +++ b/src/treadmill.h @@ -0,0 +1,29 @@ +#ifndef TREADMILL_H +#define TREADMILL_H +#include + +class treadmill:public QObject +{ + Q_OBJECT + +public: + treadmill(); + virtual void start(); + virtual void stop(); + virtual void changeSpeed(double speed); + virtual void changeInclination(double inclination); + virtual unsigned char currentHeart(); + virtual double currentSpeed(); + virtual double currentInclination(); + +protected: + double Speed = 0; + double Inclination = 0; + uint8_t Heart = 0; + double requestSpeed = -1; + double requestInclination = -1; + int8_t requestStart = -1; + int8_t requestStop = -1; +}; + +#endif // TREADMILL_H diff --git a/src/virtualtreadmill.cpp b/src/virtualtreadmill.cpp index d47919546..0bc5e70bf 100644 --- a/src/virtualtreadmill.cpp +++ b/src/virtualtreadmill.cpp @@ -1,16 +1,10 @@ #include "virtualtreadmill.h" #include -volatile double currentSpeed = 0; -volatile double currentIncline = 0; -volatile uint8_t currentHeart = 0; -volatile double requestSpeed = -1; -volatile double requestIncline = -1; -volatile int8_t requestStart = -1; -volatile int8_t requestStop = -1; - -virtualtreadmill::virtualtreadmill() +virtualtreadmill::virtualtreadmill(treadmill* t) { + treadMill = t; + //! [Advertising Data] advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral); advertisingData.setIncludePowerLevel(true); @@ -108,7 +102,8 @@ void virtualtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha b = newValue.at(2); uint16_t uspeed = a + (((uint16_t)b) << 8); - requestSpeed = (double)uspeed / 100.0; + double requestSpeed = (double)uspeed / 100.0; + treadMill->changeSpeed(requestSpeed); qDebug() << "new requested speed" << requestSpeed; } else if ((char)newValue.at(0)== 0x03) // Set Target Inclination @@ -117,19 +112,20 @@ void virtualtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha b = newValue.at(2); int16_t sincline = a + (((int16_t)b) << 8); - requestIncline = (double)sincline / 10.0; + double requestIncline = (double)sincline / 10.0; if(requestIncline < 0) requestIncline = 0; + treadMill->changeInclination(requestIncline); qDebug() << "new requested incline" << requestIncline; } else if ((char)newValue.at(0)== 0x07) // Start request { - requestStart = 1; + treadMill->start(); qDebug() << "request to start"; } else if ((char)newValue.at(0)== 0x08) // Stop request { - requestStop = 1; + treadMill->stop(); qDebug() << "request to stop"; } break; @@ -150,19 +146,19 @@ void virtualtreadmill::treadmillProvider() value.append(0x08); // Inclination avaiable value.append((char)0x00); - uint16_t normalizeSpeed = (uint16_t)qRound(currentSpeed * 100); + uint16_t normalizeSpeed = (uint16_t)qRound(treadMill->currentSpeed() * 100); char a = (normalizeSpeed >> 8) & 0XFF; char b = normalizeSpeed & 0XFF; QByteArray speedBytes; speedBytes.append(b); speedBytes.append(a); - uint16_t normalizeIncline = (uint32_t)qRound(currentIncline * 10); + uint16_t normalizeIncline = (uint32_t)qRound(treadMill->currentInclination() * 10); a = (normalizeIncline >> 8) & 0XFF; b = normalizeIncline & 0XFF; QByteArray inclineBytes; inclineBytes.append(b); inclineBytes.append(a); - double ramp = qRadiansToDegrees(qAtan(currentIncline/100)); + double ramp = qRadiansToDegrees(qAtan(treadMill->currentInclination()/100)); int16_t normalizeRamp = (int32_t)qRound(ramp * 10); a = (normalizeRamp >> 8) & 0XFF; b = normalizeRamp & 0XFF; @@ -188,7 +184,7 @@ void virtualtreadmill::treadmillProvider() QByteArray valueHR; valueHR.append(char(0)); // Flags that specify the format of the value. - valueHR.append(char(currentHeart)); // Actual value. + valueHR.append(char(treadMill->currentHeart())); // Actual value. QLowEnergyCharacteristic characteristicHR = serviceHR->characteristic(QBluetoothUuid::HeartRateMeasurement); Q_ASSERT(characteristicHR.isValid()); @@ -200,14 +196,14 @@ uint16_t virtualtreadmill::watts() // calc Watts ref. https://alancouzens.com/blog/Run_Power.html uint16_t watts=0; - if(currentSpeed > 0) + if(treadMill->currentSpeed() > 0) { double weight=75.0; // TODO: config need - double pace=60/currentSpeed; + double pace=60/treadMill->currentSpeed(); double VO2R=210.0/pace; double VO2A=(VO2R*weight)/1000.0; double hwatts=75*VO2A; - double vwatts=((9.8*weight) * (currentIncline/100)); + double vwatts=((9.8*weight) * (treadMill->currentInclination()/100)); watts=hwatts+vwatts; } return watts; diff --git a/src/virtualtreadmill.h b/src/virtualtreadmill.h index 6641f2238..7b03c3622 100644 --- a/src/virtualtreadmill.h +++ b/src/virtualtreadmill.h @@ -22,11 +22,13 @@ #include #include +#include "treadmill.h" + class virtualtreadmill: QObject { Q_OBJECT public: - virtualtreadmill(); + virtualtreadmill(treadmill* t); private: QLowEnergyController* leController; @@ -37,6 +39,8 @@ class virtualtreadmill: QObject QTimer treadmillTimer; uint16_t watts(); + treadmill* treadMill; + private slots: void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue); void treadmillProvider(); From 38a41451f35980c8c382c76c80b22124a153e303 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 08:05:46 +0200 Subject: [PATCH 09/45] descriptorWritten added --- src/domyostreadmill.cpp | 15 ++++++++++----- src/domyostreadmill.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 9ad599a74..a713b9396 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -310,6 +310,8 @@ void domyostreadmill::btinit() writeCharacteristic(initDataStart11, sizeof(initDataStart11), "init"); writeCharacteristic(initDataStart12, sizeof(initDataStart12), "init"); writeCharacteristic(initDataStart13, sizeof(initDataStart13), "init"); + + initDone = true; } void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) @@ -331,18 +333,21 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) 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))); - // await _gattNotifyCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); QByteArray descriptor; descriptor.append((char)0x01); descriptor.append((char)0x00); gattCommunicationChannelService->writeDescriptor(gattNotifyCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor); + } +} - btinit(); - - initDone = true; +void domyostreadmill::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue) +{ + debug("descriptorWritten " + descriptor.name() + " " + newValue.toHex(' ')); - } + btinit(); } void domyostreadmill::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 1cc4a564f..5c1b307d0 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -43,6 +43,7 @@ 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); From acfdff7b5c2b36930d0c3c15af39fd689574cc17 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 08:06:32 +0200 Subject: [PATCH 10/45] added timestamp to log filename --- src/domyostreadmill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index a713b9396..ee7140fe7 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -103,7 +103,7 @@ QFile* debugCommsLog; domyostreadmill::domyostreadmill() { QTimer* refresh = new QTimer(this); - debugCommsLog = new QFile("debug.log"); + debugCommsLog = new QFile("debug-" + QDateTime::currentDateTime().toString() + ".log"); debugCommsLog->open(QIODevice::WriteOnly | QIODevice::Unbuffered); initDone = false; From 373eb3fbf970c544e52b2a52f9d5a291ef6bfdbb Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 09:34:38 +0200 Subject: [PATCH 11/45] virtualtreadmill created when the services are fully discovered --- src/domyostreadmill.cpp | 24 +++++++++++++++--------- src/virtualtreadmill.cpp | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index ee7140fe7..07a809526 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -168,9 +168,7 @@ void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestInc } void domyostreadmill::update() -{ - static uint8_t first = 0; - static virtualtreadmill* v; +{ static uint32_t counter = 0; Q_UNUSED(v); //qDebug() << treadmill.isValid() << m_control->state() << gattCommunicationChannelService << gattWriteCharacteristic.isValid() << gattNotifyCharacteristic.isValid() << initDone; @@ -184,12 +182,6 @@ void domyostreadmill::update() if(currentSpeed > 0.0) counter++; - if(!first) - { - debug("creating virtual treadmill interface..."); - v = new virtualtreadmill(); - } - first = 1; writeCharacteristic(noOpData, sizeof(noOpData), "noOp", true); // byte 3 - 4 = elapsed time @@ -325,6 +317,8 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) 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)), @@ -336,6 +330,18 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) 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; + + if(!first) + { + debug("creating virtual treadmill interface..."); + v = new virtualtreadmill(); + } + first = 1; + // ******************************************************************************************************** + QByteArray descriptor; descriptor.append((char)0x01); descriptor.append((char)0x00); diff --git a/src/virtualtreadmill.cpp b/src/virtualtreadmill.cpp index d47919546..576779dbd 100644 --- a/src/virtualtreadmill.cpp +++ b/src/virtualtreadmill.cpp @@ -75,6 +75,7 @@ virtualtreadmill::virtualtreadmill() //! [Start Advertising] leController = QLowEnergyController::createPeripheral(); + Q_ASSERT(leController); service = leController->addService(serviceData); serviceHR = leController->addService(serviceDataHR); From b434d1f1e63753c511dd17589bfefab83356edc9 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 09:48:18 +0200 Subject: [PATCH 12/45] fix typo --- src/domyostreadmill.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 07a809526..9048d7269 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -170,7 +170,7 @@ void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestInc 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::DiscoveredState && @@ -333,7 +333,7 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) // ******************************************* virtual treadmill init ************************************* static uint8_t first = 0; static virtualtreadmill* v; - + Q_UNUSED(v); if(!first) { debug("creating virtual treadmill interface..."); From 21ec2a890e764535112bcd879f5a82193a4915b9 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 10:19:19 +0200 Subject: [PATCH 13/45] cleaned useless comments --- src/domyostreadmill.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 9048d7269..b3c758755 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -250,12 +250,6 @@ 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); From 3ba0219ce42b5bad98b4abbb2fa9682bc62554dd Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 10:36:27 +0200 Subject: [PATCH 14/45] added watt visualization --- src/domyostreadmill.cpp | 5 +-- src/domyostreadmill.h | 4 +++ src/main.cpp | 2 +- src/mainwindow.cpp | 14 +++++--- src/mainwindow.h | 5 ++- src/mainwindow.ui | 78 +++++++++++++++++++++++++++++++++++++--- src/virtualtreadmill.cpp | 3 +- src/virtualtreadmill.h | 6 ++-- 8 files changed, 97 insertions(+), 20 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index b2b42cbaf..adac1c599 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -1,5 +1,4 @@ #include "domyostreadmill.h" -#include "virtualtreadmill.h" #include "trainprogram.h" // set speed and incline to 0 @@ -121,9 +120,7 @@ void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestInc void domyostreadmill::update() { static uint8_t first = 0; - static virtualtreadmill* v; - 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) && @@ -137,7 +134,7 @@ void domyostreadmill::update() if(!first) { qDebug() << "creating virtual treadmill interface..."; - v = new virtualtreadmill(); + virtualTreadMill = new virtualtreadmill(); } first = 1; gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, QByteArray::fromRawData((const char*)noOpData, sizeof(noOpData))); diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 0ab9f964d..007626535 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -25,11 +25,15 @@ #include +#include "virtualtreadmill.h" + + class domyostreadmill : QObject { Q_OBJECT public: domyostreadmill(); + virtualtreadmill* virtualTreadMill = 0; private: double GetSpeedFromPacket(QByteArray packet); diff --git a/src/main.cpp b/src/main.cpp index c84b1cd2e..3583c2ce8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) if (qobject_cast(app.data())) { // start GUI version... - MainWindow* W = new MainWindow(); + MainWindow* W = new MainWindow(D); W->show(); } else { // start non-GUI version... diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9e6911753..3cc93fdc5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -11,12 +11,13 @@ extern volatile int8_t requestStart; extern volatile int8_t requestStop; extern trainprogram* trainProgram; -MainWindow::MainWindow(QWidget *parent) : - QDialog(parent), +MainWindow::MainWindow(domyostreadmill* treadmill) : + QDialog(nullptr), ui(new Ui::MainWindow) -{ +{ ui->setupUi(this); + this->treadmill = treadmill; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::update); timer->start(1000); @@ -29,6 +30,10 @@ void MainWindow::update() ui->speed->setText(QString::number(currentSpeed)); ui->inclination->setText(QString::number(currentIncline)); ui->heartrate->setText(QString::number(currentHeart)); + if(treadmill->virtualTreadMill) + ui->watt->setText(QString::number(treadmill->virtualTreadMill->watts(ui->weight->text().toFloat()))); + else + ui->watt->setText("0"); } MainWindow::~MainWindow() @@ -101,7 +106,8 @@ void MainWindow::on_tableWidget_cellChanged(int row, int column) void MainWindow::on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) { - + Q_UNUSED(current); + Q_UNUSED(previous); } void MainWindow::on_save_clicked() diff --git a/src/mainwindow.h b/src/mainwindow.h index 5cf863d1c..aeff103c2 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -7,6 +7,7 @@ #include #include #include +#include "domyostreadmill.h" namespace Ui { class MainWindow; @@ -17,7 +18,7 @@ class MainWindow : public QDialog Q_OBJECT public: - explicit MainWindow(QWidget *parent = nullptr); + explicit MainWindow(domyostreadmill* treadmill); ~MainWindow(); private: @@ -27,6 +28,8 @@ class MainWindow : public QDialog Ui::MainWindow *ui; QTimer *timer; + domyostreadmill* treadmill; + private slots: void update(); void on_tableWidget_cellChanged(int row, int column); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 947b2753d..48ed68a12 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -36,7 +36,7 @@ 10 30 - 185 + 131 42 @@ -81,9 +81,9 @@ - 220 + 150 30 - 185 + 161 42 @@ -128,9 +128,9 @@ - 420 + 320 30 - 185 + 161 42 @@ -172,6 +172,53 @@ + + + + 510 + 30 + 101 + 42 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Watt + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + @@ -274,6 +321,27 @@ + + + + + + Player Weight (kg): + + + + + + + 70.0 + + + Qt::AlignCenter + + + + + diff --git a/src/virtualtreadmill.cpp b/src/virtualtreadmill.cpp index d47919546..97608be60 100644 --- a/src/virtualtreadmill.cpp +++ b/src/virtualtreadmill.cpp @@ -195,14 +195,13 @@ void virtualtreadmill::treadmillProvider() serviceHR->writeCharacteristic(characteristicHR, valueHR); // Potentially causes notification. } -uint16_t virtualtreadmill::watts() +uint16_t virtualtreadmill::watts(double weight) { // calc Watts ref. https://alancouzens.com/blog/Run_Power.html uint16_t watts=0; if(currentSpeed > 0) { - double weight=75.0; // TODO: config need double pace=60/currentSpeed; double VO2R=210.0/pace; double VO2A=(VO2R*weight)/1000.0; diff --git a/src/virtualtreadmill.h b/src/virtualtreadmill.h index 6641f2238..dae0ad2c4 100644 --- a/src/virtualtreadmill.h +++ b/src/virtualtreadmill.h @@ -26,7 +26,8 @@ class virtualtreadmill: QObject { Q_OBJECT public: - virtualtreadmill(); + virtualtreadmill(); + uint16_t watts(double weight=75.0); private: QLowEnergyController* leController; @@ -34,8 +35,7 @@ class virtualtreadmill: QObject QLowEnergyService* serviceHR; QLowEnergyAdvertisingData advertisingData; QLowEnergyServiceData serviceData; - QTimer treadmillTimer; - uint16_t watts(); + QTimer treadmillTimer; private slots: void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue); From 632991e58e39c29e56efc9290639a6086221c57c Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 14:39:34 +0200 Subject: [PATCH 15/45] bluez logging added --- src/domyostreadmill.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index b3c758755..87d0491e0 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -102,6 +102,7 @@ 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); From d21f92727ed92283b67aa9e46d6a3dffa01abd78 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 9 Oct 2020 16:39:37 +0200 Subject: [PATCH 16/45] domyos init outside btle events --- src/domyostreadmill.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 87d0491e0..37582738a 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -89,6 +89,7 @@ QLowEnergyCharacteristic gattNotifyCharacteristic; QBluetoothDeviceDiscoveryAgent *discoveryAgent; bool initDone = false; +bool initRequest = false; extern volatile double currentSpeed; extern volatile double currentIncline; @@ -173,7 +174,13 @@ void domyostreadmill::update() static uint32_t counter = 0; //qDebug() << treadmill.isValid() << m_control->state() << gattCommunicationChannelService << gattWriteCharacteristic.isValid() << gattNotifyCharacteristic.isValid() << initDone; - if(treadmill.isValid() && + + if(initRequest) + { + initRequest = false; + btinit(); + } + else if(treadmill.isValid() && m_control->state() == QLowEnergyController::DiscoveredState && gattCommunicationChannelService && gattWriteCharacteristic.isValid() && @@ -348,7 +355,7 @@ void domyostreadmill::descriptorWritten(const QLowEnergyDescriptor &descriptor, { debug("descriptorWritten " + descriptor.name() + " " + newValue.toHex(' ')); - btinit(); + initRequest = true; } void domyostreadmill::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) From ff354fd20d496f4f530be786fb43a628e7f05761 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sat, 10 Oct 2020 09:39:04 +0200 Subject: [PATCH 17/45] speed and inclination writing finally works! --- src/domyostreadmill.cpp | 40 ++++++++++++++++++++++++++++++++-------- src/domyostreadmill.h | 3 ++- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 37582738a..848264d6c 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -143,19 +143,17 @@ void domyostreadmill::writeCharacteristic(uint8_t* data, uint8_t data_len, QStri loop.exec(); } -void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestIncline, uint16_t elapsed) +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 Date: Sat, 10 Oct 2020 13:47:28 +0200 Subject: [PATCH 18/45] fixed build issues --- src/domyostreadmill.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index df19304bf..4ddf1b9ee 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -158,8 +158,8 @@ void domyostreadmill::updateDisplay(uint16_t elapsed) //qDebug() << "writeIncline crc" << QString::number(writeIncline[26], 16); - 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) ); + writeCharacteristic(writeIncline, 20, "updateDisplay elapsed=" + QString::number(elapsed) ); + writeCharacteristic(&writeIncline[20], sizeof (writeIncline) - 20, "updateDisplay elapsed=" + QString::number(elapsed) ); } void domyostreadmill::forceSpeedOrIncline(double requestSpeed, double requestIncline) @@ -197,14 +197,14 @@ void domyostreadmill::update() initRequest = false; btinit(); } - else if(treadmill.isValid() && + else if(bttreadmill.isValid() && m_control->state() == QLowEnergyController::DiscoveredState && gattCommunicationChannelService && gattWriteCharacteristic.isValid() && gattNotifyCharacteristic.isValid() && initDone) { - if(currentSpeed > 0.0) + if(currentSpeed() > 0.0) trainProgram->scheduler(refresh->interval()); writeCharacteristic(noOpData, sizeof(noOpData), "noOp", true); @@ -220,7 +220,7 @@ void domyostreadmill::update() } if(requestInclination != -1) { - debug("writing incline " + QString::number(requestIncline)); + debug("writing incline " + QString::number(requestInclination)); forceSpeedOrIncline(currentSpeed(), requestInclination); requestInclination = -1; } @@ -356,7 +356,7 @@ void domyostreadmill::stateChanged(QLowEnergyService::ServiceState state) if(!first) { debug("creating virtual treadmill interface..."); - v = new virtualtreadmill(); + v = new virtualtreadmill(this); } first = 1; // ******************************************************************************************************** From 1212bc83f8c7c227b87a002e99e2caa1d7f3701b Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 09:45:17 +0200 Subject: [PATCH 19/45] restart bt connection to domyos treadmill --- src/domyostreadmill.cpp | 21 +++++++++++++++++++-- src/domyostreadmill.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 4ddf1b9ee..876435182 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -89,6 +89,7 @@ QLowEnergyCharacteristic gattWriteCharacteristic; QLowEnergyCharacteristic gattNotifyCharacteristic; QBluetoothDeviceDiscoveryAgent *discoveryAgent; +bool restart = false; bool initDone = false; bool initRequest = false; @@ -197,6 +198,10 @@ void domyostreadmill::update() initRequest = false; btinit(); } + else if(restart) + { + startDiscover(); + } else if(bttreadmill.isValid() && m_control->state() == QLowEnergyController::DiscoveredState && gattCommunicationChannelService && @@ -422,7 +427,7 @@ void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) Q_UNUSED(error); Q_UNUSED(this); debug("Cannot connect to remote device."); - exit(1); + restart = true; }); connect(m_control, &QLowEnergyController::connected, this, [this]() { Q_UNUSED(this); @@ -432,7 +437,7 @@ void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) connect(m_control, &QLowEnergyController::disconnected, this, [this]() { Q_UNUSED(this); debug("LowEnergy controller disconnected"); - exit(2); + restart = true; }); // Connect @@ -440,3 +445,15 @@ void domyostreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) return; } } + +void domyostreadmill::startDiscover() +{ + initDone = false; + initRequest = false; + if(m_control) + delete m_control; + if(gattCommunicationChannelService) + delete gattCommunicationChannelService; + discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); + restart = false; +} diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 5e174d2dd..334b8904d 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -43,6 +43,7 @@ class domyostreadmill : public treadmill void btinit(); void writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log=false); void debug(QString text); + void startDiscover(); QTimer* refresh; From 5c723375d71eea092f3bcc957ee611c5fe9343c9 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 10:34:59 +0200 Subject: [PATCH 20/45] trainprogram start added --- src/trainprogram.cpp | 19 ++++++++++++------- src/trainprogram.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index bb67a184e..0ff273fcc 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -9,8 +9,17 @@ trainprogram::trainprogram(QList rows) void trainprogram::scheduler(int tick) { + Q_ASSERT(tick); + + ticks++; + elapsed = ticks / (1000 / tick); + ticksCurrentRow++; + elapsedCurrentRow = ticksCurrentRow / (1000 / tick); + + if(rows.count() == 0 || started == false) return; + // entry point - if(ticks == 0 && currentStep == 0) + if(ticks == 1 && currentStep == 0) { if(rows[0].forcespeed && rows[0].speed) { @@ -19,11 +28,6 @@ void trainprogram::scheduler(int tick) emit changeInclination(rows[0].inclination); } - ticks++; - elapsed = ticks / (1000 / tick); - ticksCurrentRow++; - elapsedCurrentRow = ticksCurrentRow / (1000 / tick); - uint32_t currentRowLen = rows[currentStep].duration.second() + (rows[currentStep].duration.minute() * 60) + (rows[currentStep].duration.hour() * 3600); @@ -50,8 +54,8 @@ void trainprogram::scheduler(int tick) } else { + started = false; emit stop(); - restart(); } } } @@ -63,6 +67,7 @@ void trainprogram::restart() elapsed = 0; elapsedCurrentRow = 0; currentStep = 0; + started = true; } void trainprogram::save(QString filename) diff --git a/src/trainprogram.h b/src/trainprogram.h index cdd26f823..58916e279 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -34,6 +34,7 @@ class trainprogram: public QObject void changeInclination(double inclination); private: + bool started = false; uint32_t ticks = 0; uint16_t currentStep = 0; uint32_t ticksCurrentRow = 0; From 4625bccad3aa7eceeffb64806607cb4da8d1802f Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 14:26:56 +0200 Subject: [PATCH 21/45] no bluetooth dongle support added --- src/domyostreadmill.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 876435182..1d9671067 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -4,6 +4,7 @@ #include #include #include +#include // set speed and incline to 0 uint8_t initData1[] = { 0xf0, 0xc8, 0x01, 0xb9 }; @@ -106,13 +107,20 @@ domyostreadmill::domyostreadmill() initDone = false; - // Create a discovery agent and connect to its signals - discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); - connect(discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), - this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); + if(!QBluetoothLocalDevice::allDevices().count()) + { + debug("no bluetooth dongle found!"); + } + else + { + // Create a discovery agent and connect to its signals + discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); + connect(discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), + this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); - // Start a discovery - discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); + // Start a discovery + discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); + } connect(refresh, SIGNAL(timeout()), this, SLOT(update())); refresh->start(200); From 8945063f30fff0032ce6adcb19fce7d8b3c09e71 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 14:27:35 +0200 Subject: [PATCH 22/45] support for future arguments to the executable --- src/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3583c2ce8..98575c6b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,11 +5,16 @@ QCoreApplication* createApplication(int &argc, char *argv[]) { + bool nogui = false; for (int i = 1; i < argc; ++i) { if (!qstrcmp(argv[i], "-no-gui")) - return new QCoreApplication(argc, argv); + nogui = true; } - return new QApplication(argc, argv); + + if(nogui) + return new QCoreApplication(argc, argv); + else + return new QApplication(argc, argv); } int main(int argc, char *argv[]) From d068526e55eef48b6503beb4116b0065867f8236 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 14:27:56 +0200 Subject: [PATCH 23/45] save program xml issue fixed --- src/trainprogram.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index 0ff273fcc..51c157289 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -77,14 +77,16 @@ void trainprogram::save(QString filename) QXmlStreamWriter stream(&output); stream.setAutoFormatting(true); stream.writeStartDocument(); + stream.writeStartElement("rows"); foreach (trainrow row, rows) { stream.writeStartElement("row"); stream.writeAttribute("duration", row.duration.toString()); stream.writeAttribute("speed", QString::number(row.speed)); stream.writeAttribute("inclination", QString::number(row.inclination)); stream.writeAttribute("forcespeed", row.forcespeed?"1":"0"); - stream.writeEndElement(); // bookmark + stream.writeEndElement(); } + stream.writeEndElement(); stream.writeEndDocument(); } From 73a0bd7c653688bfd5ee4974cdfba0b066b6096d Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 14:39:06 +0200 Subject: [PATCH 24/45] fix seg fault without a train program --- src/domyostreadmill.cpp | 11 ++++++----- src/treadmill.h | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 1d9671067..44fb2acdc 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -96,8 +96,6 @@ bool initRequest = false; QFile* debugCommsLog; -trainprogram* trainProgram = 0; - domyostreadmill::domyostreadmill() { QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); @@ -217,9 +215,12 @@ void domyostreadmill::update() gattNotifyCharacteristic.isValid() && initDone) { - if(currentSpeed() > 0.0) - trainProgram->scheduler(refresh->interval()); - + if(currentSpeed() > 0.0) + { + elapsed += ((double)refresh->interval() / 1000.0); + if(trainProgram) + trainProgram->scheduler(refresh->interval()); + } writeCharacteristic(noOpData, sizeof(noOpData), "noOp", true); // byte 3 - 4 = elapsed time diff --git a/src/treadmill.h b/src/treadmill.h index 0d7475679..b3f303dbb 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -19,6 +19,7 @@ class treadmill:public QObject trainprogram* trainProgram = 0; protected: + double elapsed = 0; double Speed = 0; double Inclination = 0; uint8_t Heart = 0; From 787b9aa2c25a04e54e82fda318c0be31a65cc2ce Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 14:44:41 +0200 Subject: [PATCH 25/45] trainprogram debug lines added --- src/trainprogram.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index 51c157289..52c775587 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -23,8 +23,10 @@ void trainprogram::scheduler(int tick) { if(rows[0].forcespeed && rows[0].speed) { + qDebug() << "trainprogram change speed" + QString::number(rows[0].speed); emit changeSpeed(rows[0].speed); } + qDebug() << "trainprogram change inclination" + QString::number(rows[0].inclination); emit changeInclination(rows[0].inclination); } @@ -39,6 +41,8 @@ void trainprogram::scheduler(int tick) (rows[currentStep + 1].duration.minute() * 60) + (rows[currentStep + 1].duration.hour() * 3600); + qDebug() << "trainprogram elapsed current row" + QString::number(elapsedCurrentRow) + "current row len" + QString::number(currentRowLen); + if(elapsedCurrentRow >= currentRowLen && currentRowLen) { if(nextRowLen) @@ -48,12 +52,15 @@ void trainprogram::scheduler(int tick) elapsedCurrentRow = 0; if(rows[currentStep].forcespeed && rows[currentStep].speed) { + qDebug() << "trainprogram change speed" + QString::number(rows[currentStep].speed); emit changeSpeed(rows[currentStep].speed); } + qDebug() << "trainprogram change inclination" + QString::number(rows[currentStep].inclination); emit changeInclination(rows[currentStep].inclination); } else { + qDebug() << "trainprogram ends!"; started = false; emit stop(); } From b73092bd8ffd961f6945aaf13a8829129c5a63ba Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 15:28:02 +0200 Subject: [PATCH 26/45] fix treadmill slots --- src/mainwindow.cpp | 13 ++++++++++--- src/mainwindow.h | 1 + src/treadmill.h | 10 ++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 873574208..22ac15008 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -94,16 +94,21 @@ void MainWindow::on_tableWidget_cellChanged(int row, int column) } } -void MainWindow::createTrainProgram(QList rows) +void MainWindow::trainProgramSignals() { - if(treadmill->trainProgram) delete treadmill->trainProgram; - treadmill->trainProgram = new trainprogram(rows); connect(treadmill->trainProgram, SIGNAL(start()), treadmill, SLOT(start())); connect(treadmill->trainProgram, SIGNAL(stop()), treadmill, SLOT(stop())); connect(treadmill->trainProgram, SIGNAL(changeSpeed(double)), treadmill, SLOT(changeSpeed(double))); connect(treadmill->trainProgram, SIGNAL(changeInclination(double)), treadmill, SLOT(changeInclination(double))); } +void MainWindow::createTrainProgram(QList rows) +{ + if(treadmill->trainProgram) delete treadmill->trainProgram; + treadmill->trainProgram = new trainprogram(rows); + trainProgramSignals(); +} + void MainWindow::on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) { Q_UNUSED(current); @@ -157,6 +162,8 @@ void MainWindow::on_load_clicked() countRow++; } + + trainProgramSignals(); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 9b75e45a9..a092650b1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -24,6 +24,7 @@ class MainWindow : public QDialog private: void addEmptyRow(); void createTrainProgram(QList rows); + void trainProgramSignals(); bool editing = false; Ui::MainWindow *ui; diff --git a/src/treadmill.h b/src/treadmill.h index b3f303dbb..7659b053b 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -9,15 +9,17 @@ class treadmill:public QObject public: treadmill(); - virtual void start(); - virtual void stop(); - virtual void changeSpeed(double speed); - virtual void changeInclination(double inclination); virtual unsigned char currentHeart(); virtual double currentSpeed(); virtual double currentInclination(); trainprogram* trainProgram = 0; +public slots: + virtual void start(); + virtual void stop(); + virtual void changeSpeed(double speed); + virtual void changeInclination(double inclination); + protected: double elapsed = 0; double Speed = 0; From 259b53e8e0a151b76a5158c31d50d1be95a4a35c Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 15:48:56 +0200 Subject: [PATCH 27/45] change speed and inclination only if needed --- src/domyostreadmill.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 44fb2acdc..f7fd3b0a0 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -228,14 +228,20 @@ void domyostreadmill::update() if(requestSpeed != -1) { - debug("writing speed " + QString::number(requestSpeed)); - forceSpeedOrIncline(requestSpeed, Inclination); + if(requestSpeed != currentSpeed()) + { + debug("writing speed " + QString::number(requestSpeed)); + forceSpeedOrIncline(requestSpeed, Inclination); + } requestSpeed = -1; } if(requestInclination != -1) { - debug("writing incline " + QString::number(requestInclination)); - forceSpeedOrIncline(currentSpeed(), requestInclination); + if(requestInclination != currentInclination()) + { + debug("writing incline " + QString::number(requestInclination)); + forceSpeedOrIncline(currentSpeed(), requestInclination); + } requestInclination = -1; } if(requestStart != -1) From 3c7cb254e6cf73bf61feb99cfc6aa27d0dc412db Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 15:59:40 +0200 Subject: [PATCH 28/45] fixed changing speed and inclination at the same time --- src/domyostreadmill.cpp | 19 +++++++++++++++---- src/mainwindow.cpp | 1 + src/trainprogram.cpp | 11 +++++++---- src/trainprogram.h | 1 + src/treadmill.cpp | 1 + src/treadmill.h | 1 + 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index f7fd3b0a0..25b6cf346 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -225,22 +225,33 @@ void domyostreadmill::update() // byte 3 - 4 = elapsed time // byte 17 = inclination - if(requestSpeed != -1) { if(requestSpeed != currentSpeed()) { debug("writing speed " + QString::number(requestSpeed)); - forceSpeedOrIncline(requestSpeed, Inclination); + double inc = Inclination; + if(requestInclination != -1) + { + inc = requestInclination; + requestInclination = -1; + } + forceSpeedOrIncline(requestSpeed, inc); } requestSpeed = -1; } if(requestInclination != -1) { if(requestInclination != currentInclination()) - { + { debug("writing incline " + QString::number(requestInclination)); - forceSpeedOrIncline(currentSpeed(), requestInclination); + double speed = currentSpeed(); + if(requestSpeed != 1) + { + speed = requestSpeed; + requestSpeed = -1; + } + forceSpeedOrIncline(speed, requestInclination); } requestInclination = -1; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 22ac15008..84b87052f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -100,6 +100,7 @@ void MainWindow::trainProgramSignals() connect(treadmill->trainProgram, SIGNAL(stop()), treadmill, SLOT(stop())); connect(treadmill->trainProgram, SIGNAL(changeSpeed(double)), treadmill, SLOT(changeSpeed(double))); connect(treadmill->trainProgram, SIGNAL(changeInclination(double)), treadmill, SLOT(changeInclination(double))); + connect(treadmill->trainProgram, SIGNAL(changeSpeedAndInclination(double, double)), treadmill, SLOT(changeSpeedAndInclination(double, double))); } void MainWindow::createTrainProgram(QList rows) diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index 52c775587..dd85ebcb3 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -24,10 +24,13 @@ void trainprogram::scheduler(int tick) if(rows[0].forcespeed && rows[0].speed) { qDebug() << "trainprogram change speed" + QString::number(rows[0].speed); - emit changeSpeed(rows[0].speed); + emit changeSpeedAndInclination(rows[0].speed, rows[0].inclination); + } + else + { + qDebug() << "trainprogram change inclination" + QString::number(rows[0].inclination); + emit changeInclination(rows[0].inclination); } - qDebug() << "trainprogram change inclination" + QString::number(rows[0].inclination); - emit changeInclination(rows[0].inclination); } uint32_t currentRowLen = rows[currentStep].duration.second() + @@ -53,7 +56,7 @@ void trainprogram::scheduler(int tick) if(rows[currentStep].forcespeed && rows[currentStep].speed) { qDebug() << "trainprogram change speed" + QString::number(rows[currentStep].speed); - emit changeSpeed(rows[currentStep].speed); + emit changeSpeedAndInclination(rows[currentStep].speed, rows[currentStep].inclination); } qDebug() << "trainprogram change inclination" + QString::number(rows[currentStep].inclination); emit changeInclination(rows[currentStep].inclination); diff --git a/src/trainprogram.h b/src/trainprogram.h index 58916e279..89f49e1a6 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -32,6 +32,7 @@ class trainprogram: public QObject void stop(); void changeSpeed(double speed); void changeInclination(double inclination); + void changeSpeedAndInclination(double speed, double inclination); private: bool started = false; diff --git a/src/treadmill.cpp b/src/treadmill.cpp index 99f2e5eb9..30c2cf652 100644 --- a/src/treadmill.cpp +++ b/src/treadmill.cpp @@ -9,6 +9,7 @@ void treadmill::start(){ requestStart = 1; } void treadmill::stop(){ requestStop = 1; } void treadmill::changeSpeed(double speed){ requestSpeed = speed;} void treadmill::changeInclination(double inclination){ requestInclination = inclination; } +void treadmill::changeSpeedAndInclination(double speed, double inclination){ requestSpeed = speed; requestInclination = inclination;} unsigned char treadmill::currentHeart(){ return Heart; } double treadmill::currentSpeed(){ return Speed; } double treadmill::currentInclination(){ return Inclination; } diff --git a/src/treadmill.h b/src/treadmill.h index 7659b053b..f569cf1cf 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -19,6 +19,7 @@ public slots: virtual void stop(); virtual void changeSpeed(double speed); virtual void changeInclination(double inclination); + virtual void changeSpeedAndInclination(double speed, double inclination); protected: double elapsed = 0; From d58db4100f5d94f234b2fb30e60d3da80990c766 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Sun, 11 Oct 2020 16:15:08 +0200 Subject: [PATCH 29/45] fix typo on requestSpeed check --- src/domyostreadmill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 25b6cf346..cff675f94 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -246,7 +246,7 @@ void domyostreadmill::update() { debug("writing incline " + QString::number(requestInclination)); double speed = currentSpeed(); - if(requestSpeed != 1) + if(requestSpeed != -1) { speed = requestSpeed; requestSpeed = -1; From d7ac459a3dabf5115b3289c2f6277349a9dbc069 Mon Sep 17 00:00:00 2001 From: cagnulein Date: Mon, 12 Oct 2020 08:24:19 +0200 Subject: [PATCH 30/45] fan speed buttons managed Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 20 ++++++++++++++++++++ src/treadmill.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index cff675f94..c7ea316f6 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -267,6 +267,16 @@ void domyostreadmill::update() writeCharacteristic(initDataF0C800B8, sizeof(initDataF0C800B8), "stop tape"); requestStop = -1; } + if(requestIncreaseFan != -1) + { + debug("increasing fan speed TODO..."); + requestIncreaseFan = -1; + } + else if(requestDecreaseFan != -1) + { + debug("decreasing fan speed TODO..."); + requestDecreaseFan = -1; + } } } @@ -300,6 +310,16 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char debug("stop button pressed!"); requestStop = 1; } + else if (newValue.at(22) == 0x0b) + { + debug("increase speed fan pressed!"); + requestIncreaseFan = 1; + } + else if (newValue.at(22) == 0x0a) + { + debug("decrease speed fan pressed!"); + requestDecreaseFan = 1; + } /*if ((uint8_t)newValue.at(1) != 0xbc && newValue.at(2) != 0x04) // intense run, these are the bytes for the inclination and speed status return;*/ diff --git a/src/treadmill.h b/src/treadmill.h index f569cf1cf..ec8faf49a 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -30,6 +30,8 @@ public slots: double requestInclination = -1; int8_t requestStart = -1; int8_t requestStop = -1; + int8_t requestIncreaseFan = -1; + int8_t requestDecreaseFan = -1; }; #endif // TREADMILL_H From e21ad70ea99b2fa22991db1fa86df4a2dd253352 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 08:55:26 +0200 Subject: [PATCH 31/45] getting kcal and distance from the treadmill (tests need) Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 19 +++++++++++++++++++ src/domyostreadmill.h | 2 ++ src/treadmill.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index c7ea316f6..660aa4332 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -326,18 +326,24 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char double speed = GetSpeedFromPacket(newValue); double incline = GetInclinationFromPacket(newValue); + double kcal = GetKcalFromPacket(newValue); + double distance = GetDistanceFromPacket(newValue); Heart = newValue.at(18); debug("Current speed: " + QString::number(speed)); debug("Current incline: " + QString::number(incline)); debug("Current heart: " + QString::number(Heart)); + debug("Current KCal: " + QString::number(kcal)); + debug("Current Distance: " + QString::number(distance)); if(m_control->error() != QLowEnergyController::NoError) qDebug() << "QLowEnergyController ERROR!!" << m_control->errorString(); Speed = speed; Inclination = incline; + KCal = kcal; + Distance = distance; } double domyostreadmill::GetSpeedFromPacket(QByteArray packet) @@ -347,6 +353,19 @@ double domyostreadmill::GetSpeedFromPacket(QByteArray packet) return data; } +double domyostreadmill::GetKcalFromPacket(QByteArray packet) +{ + uint16_t convertedData = (packet.at(10) << 8) | packet.at(11); + return (double)convertedData; +} + +double domyostreadmill::GetDistanceFromPacket(QByteArray packet) +{ + uint16_t convertedData = (packet.at(12) << 8) | packet.at(13); + double data = ((double)convertedData) / 10.0f; + return data; +} + double domyostreadmill::GetInclinationFromPacket(QByteArray packet) { uint16_t convertedData = (packet.at(2) << 8) | packet.at(3); diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 334b8904d..f883773e9 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -38,6 +38,8 @@ class domyostreadmill : public treadmill private: double GetSpeedFromPacket(QByteArray packet); double GetInclinationFromPacket(QByteArray packet); + double GetKcalFromPacket(QByteArray packet); + double GetDistanceFromPacket(QByteArray packet); void forceSpeedOrIncline(double requestSpeed, double requestIncline); void updateDisplay(uint16_t elapsed); void btinit(); diff --git a/src/treadmill.h b/src/treadmill.h index ec8faf49a..a6bafc290 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -25,6 +25,8 @@ public slots: double elapsed = 0; double Speed = 0; double Inclination = 0; + double KCal = 0; + double Distance = 0; uint8_t Heart = 0; double requestSpeed = -1; double requestInclination = -1; From 92cd9baea36994b58e7d40ecbbc3a36477fca96d Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 10:25:57 +0200 Subject: [PATCH 32/45] updated installation from source steps --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e160026e..be30ab7a2 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,19 @@ Zwift bridge for Domyos treadmills ![First Success](docs/first_success.jpg) -### Installation +### Installation from source + +$ sudo apt upgrade && sudo apt update # this is very important on raspberry pi: you need the bluetooth firmware updated! + +$ sudo apt install git libqt5bluetooth5 libqt5widgets5 $ git clone https://github.com/cagnulein/qdomyos-zwift.git -$ sudo apt upgrade && sudo apt update # this is very important on raspberry pi: you need the bluetooth firmware updated! +$ cd src -$ sudo apt install libqt5bluetooth5 +$ qmake -$ sudo hciconfig hci0 leadv 0 +$ make -j4 $ sudo ./qdomyos-zwift From 9f9000427fca7e3c5f368145df1f63da8c396e39 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 10:44:56 +0200 Subject: [PATCH 33/45] auto start tape on connect disabled Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 48 ++++++++--------------------------------- src/domyostreadmill.h | 2 +- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 660aa4332..3d3eba7ba 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -15,39 +15,6 @@ uint8_t noOpData[] = { 0xf0, 0xac, 0x9c }; // stop tape uint8_t initDataF0C800B8[] = { 0xf0, 0xc8, 0x00, 0xb8 }; -#if 0 -uint8_t initDataStart[] = { 0xf0, 0xc8, 0x00, 0xb8 }; -uint8_t initDataStart2[] = { 0xf0, 0xcb, 0x01, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, - 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, 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 uint8_t initDataStart[] = { 0xf0, 0xa3, 0x93 }; uint8_t initDataStart2[] = { 0xf0, 0xa4, 0x94 }; @@ -202,7 +169,7 @@ void domyostreadmill::update() if(initRequest) { initRequest = false; - btinit(); + btinit(false); } else if(restart) { @@ -258,7 +225,7 @@ void domyostreadmill::update() if(requestStart != -1) { debug("starting..."); - btinit(); + btinit(true); requestStart = -1; } if(requestStop != -1) @@ -374,7 +341,7 @@ double domyostreadmill::GetInclinationFromPacket(QByteArray packet) return data; } -void domyostreadmill::btinit() +void domyostreadmill::btinit(bool startTape) { writeCharacteristic(initData1, sizeof(initData1), "init"); writeCharacteristic(initData2, sizeof(initData2), "init"); @@ -388,9 +355,12 @@ void domyostreadmill::btinit() 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"); + if(startTape) + { + writeCharacteristic(initDataStart11, sizeof(initDataStart11), "init"); + writeCharacteristic(initDataStart12, sizeof(initDataStart12), "init"); + writeCharacteristic(initDataStart13, sizeof(initDataStart13), "init"); + } initDone = true; } diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index f883773e9..a0d6c4687 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -42,7 +42,7 @@ class domyostreadmill : public treadmill double GetDistanceFromPacket(QByteArray packet); void forceSpeedOrIncline(double requestSpeed, double requestIncline); void updateDisplay(uint16_t elapsed); - void btinit(); + void btinit(bool startTape); void writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log=false); void debug(QString text); void startDiscover(); From 338b19f664bc073a23ca5f5df3abc90e6071723b Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 11:03:42 +0200 Subject: [PATCH 34/45] added the ability to enable/disable the train program; start button on the console starts the program also is valid and enabled Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 1 + src/mainwindow.cpp | 8 ++++++++ src/mainwindow.h | 1 + src/mainwindow.ui | 6 ++++++ src/trainprogram.cpp | 7 ++++++- src/trainprogram.h | 4 ++++ src/treadmill.h | 3 +++ 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 3d3eba7ba..52198c5b4 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -227,6 +227,7 @@ void domyostreadmill::update() debug("starting..."); btinit(true); requestStart = -1; + emit tapeStarted(); } if(requestStop != -1) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 84b87052f..fb18b6a7d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -101,6 +101,7 @@ void MainWindow::trainProgramSignals() connect(treadmill->trainProgram, SIGNAL(changeSpeed(double)), treadmill, SLOT(changeSpeed(double))); connect(treadmill->trainProgram, SIGNAL(changeInclination(double)), treadmill, SLOT(changeInclination(double))); connect(treadmill->trainProgram, SIGNAL(changeSpeedAndInclination(double, double)), treadmill, SLOT(changeSpeedAndInclination(double, double))); + connect(treadmill, SIGNAL(tapeStarted()), treadmill->trainProgram, SLOT(onTapeStarted())); } void MainWindow::createTrainProgram(QList rows) @@ -211,3 +212,10 @@ void MainWindow::on_start_clicked() treadmill->trainProgram->restart(); treadmill->start(); } + +void MainWindow::on_groupBox_2_clicked() +{ + if(!treadmill->trainProgram) + createTrainProgram(QList()); + treadmill->trainProgram->enabled = ui->groupBox_2->isChecked(); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index a092650b1..45db49276 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -41,6 +41,7 @@ private slots: void on_reset_clicked(); void on_stop_clicked(); void on_start_clicked(); + void on_groupBox_2_clicked(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 48ed68a12..497807563 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -235,6 +235,12 @@ Train me! + + true + + + false + diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index dd85ebcb3..f18e22432 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -16,7 +16,7 @@ void trainprogram::scheduler(int tick) ticksCurrentRow++; elapsedCurrentRow = ticksCurrentRow / (1000 / tick); - if(rows.count() == 0 || started == false) return; + if(rows.count() == 0 || started == false || enabled == false) return; // entry point if(ticks == 1 && currentStep == 0) @@ -70,6 +70,11 @@ void trainprogram::scheduler(int tick) } } +void trainprogram::onTapeStarted() +{ + started = true; +} + void trainprogram::restart() { ticks = 0; diff --git a/src/trainprogram.h b/src/trainprogram.h index 89f49e1a6..f4c9c5037 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -23,10 +23,14 @@ class trainprogram: public QObject QList rows; uint32_t elapsed = 0; + bool enabled = true; void restart(); void scheduler(int tick); +public slots: + void onTapeStarted(); + signals: void start(); void stop(); diff --git a/src/treadmill.h b/src/treadmill.h index a6bafc290..cc98eccff 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -21,6 +21,9 @@ public slots: virtual void changeInclination(double inclination); virtual void changeSpeedAndInclination(double speed, double inclination); +signals: + void tapeStarted(); + protected: double elapsed = 0; double Speed = 0; From bf40c460a5a60e0fb6dd96186f9f5c34b119c9bd Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 11:39:58 +0200 Subject: [PATCH 35/45] added currentRowElapsedTime, totalElapsedTime and Duration of the train program on UI Signed-off-by: Roberto Viola --- src/mainwindow.cpp | 14 +- src/mainwindow.ui | 525 +++++++++++++++++++++++-------------------- src/trainprogram.cpp | 19 ++ src/trainprogram.h | 3 + 4 files changed, 312 insertions(+), 249 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fb18b6a7d..cf3cd447b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -25,6 +25,16 @@ void MainWindow::update() ui->watt->setText(QString::number(treadmill->virtualTreadMill->watts(ui->weight->text().toFloat()))); else ui->watt->setText("0"); + + if(treadmill) + { + if(treadmill->trainProgram) + { + ui->trainProgramElapsedTime->setText(treadmill->trainProgram->totalElapsedTime().toString("hh:mm:ss")); + ui->trainProgramCurrentRowElapsedTime->setText(treadmill->trainProgram->currentRowElapsedTime().toString("hh:mm:ss")); + ui->trainProgramDuration->setText(treadmill->trainProgram->duration().toString("hh:mm:ss")); + } + } } MainWindow::~MainWindow() @@ -108,7 +118,9 @@ void MainWindow::createTrainProgram(QList rows) { if(treadmill->trainProgram) delete treadmill->trainProgram; treadmill->trainProgram = new trainprogram(rows); - trainProgramSignals(); + if(rows.length() == 0) + addEmptyRow(); + trainProgramSignals(); } void MainWindow::on_tableWidget_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 497807563..1c165cadf 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -17,7 +17,7 @@ - + 0 0 @@ -31,190 +31,181 @@ Treadmill Status - + 10 - 30 - 131 - 42 + 19 + 611 + 61 - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - - Speed (Km/h) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - 150 - 30 - 161 - 42 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - - Inclination (degrees) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - 320 - 30 - 161 - 42 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - + + + QLayout::SetDefaultConstraint + - - - Heart rate (bpm) - + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Speed (Km/h) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Inclination (degrees) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + - - - - - - 510 - 30 - 101 - 42 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - Watt - + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Heart rate (bpm) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + + Watt + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + @@ -243,86 +234,124 @@ - - - - 0 - 0 - - - - true - - - true - - - 130 - - - - 1 - - - - - Durantion (s) - - - - - Speed (km/h) - - - - - Inclination (degrees) - - - - - Force Speed - - - - - 00:10:00 - - - AlignCenter - + + + + + + 0 + 0 + + + + true + + + true + + + 130 + + + + Durantion (s) + + + + + Speed (km/h) + + + + + Inclination (degrees) + + + + + Force Speed + + + - - - 10 - - - AlignCenter - - - - - 0 - - - AlignCenter - - - - - - - - AlignCenter - - - Checked - - - ItemIsSelectable|ItemIsUserCheckable|ItemIsEnabled - + + + + + + + + Total Elapsed Time: + + + + + + + 00:00:00 + + + 8 + + + Qt::AlignCenter + + + true + + + + + + + Current Row Elapsed Time: + + + + + + + 00:00:00 + + + 8 + + + Qt::AlignCenter + + + true + + + + + + + Program Duration: + + + + + + + 00:00:00 + + + 8 + + + Qt::AlignCenter + + + true + + + + + + - + diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index f18e22432..0a123fe15 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -128,3 +128,22 @@ trainprogram* trainprogram::load(QString filename) trainprogram *tr = new trainprogram(list); return tr; } + +QTime trainprogram::totalElapsedTime() +{ + return QTime(0,0,elapsed); +} + +QTime trainprogram::currentRowElapsedTime() +{ + return QTime(0,0,elapsedCurrentRow); +} + +QTime trainprogram::duration() +{ + QTime total(0,0,0,0); + foreach (trainrow row, rows) { + total = total.addSecs((row.duration.hour() * 3600) + (row.duration.minute() * 60) + row.duration.second()); + } + return total; +} diff --git a/src/trainprogram.h b/src/trainprogram.h index f4c9c5037..c8304f8bd 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -20,6 +20,9 @@ class trainprogram: public QObject trainprogram(QList); void save(QString filename); static trainprogram* load(QString filename); + QTime totalElapsedTime(); + QTime currentRowElapsedTime(); + QTime duration(); QList rows; uint32_t elapsed = 0; From ea57069f336d54517d685148dcc64e18c3551142 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 11:51:05 +0200 Subject: [PATCH 36/45] train program total distance added to UI Signed-off-by: Roberto Viola --- src/mainwindow.cpp | 8 ++++++++ src/mainwindow.ui | 24 ++++++++++++++++++++++++ src/trainprogram.cpp | 16 ++++++++++++++++ src/trainprogram.h | 1 + 4 files changed, 49 insertions(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index cf3cd447b..14091df02 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -33,6 +33,14 @@ void MainWindow::update() ui->trainProgramElapsedTime->setText(treadmill->trainProgram->totalElapsedTime().toString("hh:mm:ss")); ui->trainProgramCurrentRowElapsedTime->setText(treadmill->trainProgram->currentRowElapsedTime().toString("hh:mm:ss")); ui->trainProgramDuration->setText(treadmill->trainProgram->duration().toString("hh:mm:ss")); + + double distance = treadmill->trainProgram->totalDistance(); + if(distance > 0) + { + ui->trainProgramTotalDistance->setText(QString::number(distance)); + } + else + ui->trainProgramTotalDistance->setText("N/A"); } } } diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 1c165cadf..6db2321fe 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -351,6 +351,30 @@ + + + + + + Total Distance (km): + + + + + + + 0.0 + + + Qt::AlignCenter + + + true + + + + + diff --git a/src/trainprogram.cpp b/src/trainprogram.cpp index 0a123fe15..1d213035b 100644 --- a/src/trainprogram.cpp +++ b/src/trainprogram.cpp @@ -147,3 +147,19 @@ QTime trainprogram::duration() } return total; } + +double trainprogram::totalDistance() +{ + double distance = 0; + foreach (trainrow row, rows) { + if(row.duration.hour() || row.duration.minute() || row.duration.second()) + { + if(!row.forcespeed) + { + return -1; + } + distance += ((row.duration.hour() * 3600) + (row.duration.minute() * 60) + row.duration.second()) * (row.speed / 3600); + } + } + return distance; +} diff --git a/src/trainprogram.h b/src/trainprogram.h index c8304f8bd..b8b083d72 100644 --- a/src/trainprogram.h +++ b/src/trainprogram.h @@ -23,6 +23,7 @@ class trainprogram: public QObject QTime totalElapsedTime(); QTime currentRowElapsedTime(); QTime duration(); + double totalDistance(); QList rows; uint32_t elapsed = 0; From c89c381177d093ade8ca73dae889fdf74becfea1 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 12:09:40 +0200 Subject: [PATCH 37/45] odometer added on UI Signed-off-by: Roberto Viola --- src/mainwindow.cpp | 2 + src/mainwindow.ui | 367 ++++++++++++++++++++++++--------------------- src/treadmill.cpp | 1 + src/treadmill.h | 1 + 4 files changed, 200 insertions(+), 171 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 14091df02..81ec1632c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -28,6 +28,8 @@ void MainWindow::update() if(treadmill) { + ui->odometer->setText(QString::number(treadmill->odometer())); + if(treadmill->trainProgram) { ui->trainProgramElapsedTime->setText(treadmill->trainProgram->totalElapsedTime().toString("hh:mm:ss")); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 6db2321fe..36c8ab8ac 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 640 - 484 + 663 + 509 @@ -17,7 +17,7 @@ - + 0 0 @@ -25,188 +25,213 @@ 0 - 80 + 0 Treadmill Status - + 10 - 19 + 10 611 - 61 + 80 - - - QLayout::SetDefaultConstraint - + - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 + + + QLayout::SetDefaultConstraint - - - - - Speed (Km/h) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - - Inclination (degrees) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - - Heart rate (bpm) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Speed (Km/h) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Inclination (degrees) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Heart rate (bpm) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Watt + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + - - - QFrame::StyledPanel - - - QFrame::Raised - - - 0 - - - - - - Watt - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - + + + + + Odometer (km): + + + + + + + 0.0 + + + Qt::AlignCenter + + + + @@ -215,7 +240,7 @@ - + 0 0 diff --git a/src/treadmill.cpp b/src/treadmill.cpp index 30c2cf652..2d2954588 100644 --- a/src/treadmill.cpp +++ b/src/treadmill.cpp @@ -13,3 +13,4 @@ void treadmill::changeSpeedAndInclination(double speed, double inclination){ req unsigned char treadmill::currentHeart(){ return Heart; } double treadmill::currentSpeed(){ return Speed; } double treadmill::currentInclination(){ return Inclination; } +double treadmill::odometer(){ return Distance; } diff --git a/src/treadmill.h b/src/treadmill.h index cc98eccff..943361438 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -13,6 +13,7 @@ class treadmill:public QObject virtual double currentSpeed(); virtual double currentInclination(); trainprogram* trainProgram = 0; + virtual double odometer(); public slots: virtual void start(); From 3a45935617599890308faf3595e9cc3402fed05c Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 12:12:20 +0200 Subject: [PATCH 38/45] UI layout fixed Signed-off-by: Roberto Viola --- src/mainwindow.ui | 408 +++++++++++++++++++++++----------------------- 1 file changed, 201 insertions(+), 207 deletions(-) diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 36c8ab8ac..e002de53a 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -17,7 +17,7 @@ - + 0 0 @@ -25,222 +25,216 @@ 0 - 0 + 120 Treadmill Status - - - - 10 - 10 - 611 - 80 - - - - - - - QLayout::SetDefaultConstraint - - - - - QFrame::NoFrame - - - QFrame::Raised - - - 0 - - - - - - Speed (Km/h) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - 0 - - - - - - Inclination (degrees) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - 0 - - - - - - Heart rate (bpm) - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - 0 - - - - - - Watt - - - - - - - - 75 - true - - - - 0 - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - - Odometer (km): - - - - - - - 0.0 - - - Qt::AlignCenter - - - - - - - + + + + + + + QLayout::SetDefaultConstraint + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Speed (Km/h) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Inclination (degrees) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Heart rate (bpm) + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + Watt + + + + + + + + 75 + true + + + + 0 + + + Qt::AlignCenter + + + true + + + + + + + + + + + + + + Odometer (km): + + + + + + + 0.0 + + + Qt::AlignCenter + + + + + + + + - + 0 0 From 81ac8909c852a412d822efceff07abe59d136719 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 12:23:27 +0200 Subject: [PATCH 39/45] elevationGain added to UI Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 4 +++- src/mainwindow.cpp | 1 + src/mainwindow.ui | 20 ++++++++++++++++++++ src/treadmill.cpp | 1 + src/treadmill.h | 2 ++ 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 52198c5b4..aad938098 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -245,6 +245,8 @@ void domyostreadmill::update() debug("decreasing fan speed TODO..."); requestDecreaseFan = -1; } + + elevationAcc += (currentSpeed() / 3600.0) * 1000 * (currentInclination() / 100) * (refresh->interval() / 1000); } } @@ -311,7 +313,7 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char Speed = speed; Inclination = incline; KCal = kcal; - Distance = distance; + Distance = distance; } double domyostreadmill::GetSpeedFromPacket(QByteArray packet) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 81ec1632c..14ab853eb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -29,6 +29,7 @@ void MainWindow::update() if(treadmill) { ui->odometer->setText(QString::number(treadmill->odometer())); + ui->elevationGain->setText(QString::number(treadmill->elevationGain())); if(treadmill->trainProgram) { diff --git a/src/mainwindow.ui b/src/mainwindow.ui index e002de53a..4d0e456b5 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -224,6 +224,26 @@ + + + + Elevation Gain (meters): + + + + + + + 0 + + + Qt::AlignCenter + + + true + + + diff --git a/src/treadmill.cpp b/src/treadmill.cpp index 2d2954588..deac0a253 100644 --- a/src/treadmill.cpp +++ b/src/treadmill.cpp @@ -14,3 +14,4 @@ unsigned char treadmill::currentHeart(){ return Heart; } double treadmill::currentSpeed(){ return Speed; } double treadmill::currentInclination(){ return Inclination; } double treadmill::odometer(){ return Distance; } +double treadmill::elevationGain(){ return elevationAcc; } diff --git a/src/treadmill.h b/src/treadmill.h index 943361438..7bad7edc3 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -14,6 +14,7 @@ class treadmill:public QObject virtual double currentInclination(); trainprogram* trainProgram = 0; virtual double odometer(); + virtual double elevationGain(); public slots: virtual void start(); @@ -26,6 +27,7 @@ public slots: void tapeStarted(); protected: + double elevationAcc = 0; double elapsed = 0; double Speed = 0; double Inclination = 0; From ee1c3e011823e8554e3696a61c44b1a10931166d Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 15:00:02 +0200 Subject: [PATCH 40/45] connectivity icon on UI added Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 7 ++ src/domyostreadmill.h | 1 + src/icons.qrc | 6 ++ src/icons/bluetooth-icon.png | Bin 0 -> 36830 bytes src/icons/zwift-on.png | Bin 0 -> 4123 bytes src/mainwindow.cpp | 21 ++++++ src/mainwindow.ui | 140 ++++++++++++++++++++++++++++++++++- src/qdomyos-zwift.pro | 3 + src/virtualtreadmill.cpp | 7 ++ src/virtualtreadmill.h | 1 + 10 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/icons.qrc create mode 100644 src/icons/bluetooth-icon.png create mode 100644 src/icons/zwift-on.png diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index aad938098..9919d71fa 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -495,3 +495,10 @@ void domyostreadmill::startDiscover() discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); restart = false; } + +bool domyostreadmill::connected() +{ + if(!m_control) + return false; + return m_control->state() == QLowEnergyController::DiscoveredState; +} diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index a0d6c4687..211ebe296 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -34,6 +34,7 @@ class domyostreadmill : public treadmill public: domyostreadmill(); virtualtreadmill* virtualTreadMill = 0; + bool connected(); private: double GetSpeedFromPacket(QByteArray packet); diff --git a/src/icons.qrc b/src/icons.qrc new file mode 100644 index 000000000..faf57ada8 --- /dev/null +++ b/src/icons.qrc @@ -0,0 +1,6 @@ + + + icons/bluetooth-icon.png + icons/zwift-on.png + + diff --git a/src/icons/bluetooth-icon.png b/src/icons/bluetooth-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3d0b109db02d9b1d7a99e11d4b71e63596cc531 GIT binary patch literal 36830 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
  • |+z2^sVK*hG@Fv7Jq zH>RWBoKye>Fn3lB^QPUhQW>c8x4rKZ!T)`b^R{>|anfB`X`9p)&M0=3bkJS0cT&zd z3QF~9N#L%k5>FYBAAIuhfEcQA>UIJW=w+ZkFg)N=J0g>W{tvLtYNNP^3pw0u9u;mH z52)#-w23N5K^WG#^vneA+BO9Q1W;HPIs7+OFhFA0q_{v`G=>OD!RDaDdbd^lI2=-7 z?AW%-x-tP2tNr8_^1HZ}b7w~VRN0W`9}F6& zia|hH^;N;O_4vx1ml**-Zn2XL(&x6aArk9;)Tp<|>``V~o?%_ui&s!?+GQWR+Sb3f zV>W>OgywH@k-x1@oE#uY7fzwD6iA{Hro};UOJNL#l6t$L=@s@4*neXUlP`Jsoh#T9 zb|As>f&qCaa+X(zY7qyDy(q1?NhtMipXx`)eoD?OiK6&Qo%UpjVt&I9;dUO-q(}!B zF+?^Ej@hit6l9VnlLccwb|CMxDJk*SpA!!k-V5DRD^<{DvIHfhSO3bMJYd-U0O}g_ zB?!u}(~gSYdb@}&-=+%oWV8nSs2gG%YoprD-ZUQy=hlP=KvOZ^s9LwNpP55 zlWt`dBpcMqYiL=+zSg=pGrCT)A&^XVcm!m>x=h$IA`On`Gnz_gN}LaX+~%8-k5esR+Rer2%@bAV_A%Okd}ZqSh^V` z<-WP^=p}g8#=I`QBB`7|@gwK2IEC!JwUy|*!z(J%%c0q<;n9kq2JqwRf@q|Vb}K6* zR+RGHX$TH5Yd$0GC4_w5DW7C=NF@@m=gpL;& zOr#JRz%0Ukkek;Im}Yfhnzelutpfx)It-P(S;+rBcgKDcuMajhTRts(Oi0ba-r|)Z zRf#dm{2L+P7}*Ylh#M@N22-Dp#H9Motb1vf&`*1SFz9`pj=#_r8Io5elY~h*3s`233zyuc|^TDG7%P(_vbm#kgo_+=O+kh`1@2Qjp z0IuS;i7+g^9j0)7O*e$L0VFi*n1sT!%;{3_btR9mWU^&ZGMJ`S*L#;GQU2L5dKnBM z@U=$L>3RygfQBn%Sp$1cXIX1;7afFS9>*1zZRhpV*S{SZ0cTV?8*#2Fz8LGtkle#E z_j~!(CytMij1Z4~m&DGO0d>y5hrmIBJ6}k3wY21_AR6GvjddjnU}^UsRT_!4>jH)R z@)tI+xF?82O6S!eGRd$V;h0Q*8>?IMrbmo%cDmkza z#x{k;2z^S6mkzUpJ-$N-%s3aa%fPk9 z;2?%}=3)Ei)GuPHm_XAQ1VC^M7Aq3<7) zi30jbm>|N-TU(~`y=3Ay0@%FHU|REPOX*@;!P{#+e-WaX;v7<49UWR(IY=B>Wm^c6 z0@}s80%1?#^q><^ujY}iV>ti-6UFZCntqt2TUY-pV8r;BoB3Pj=-+W8ktEKuw z5POx!Zru8iChOud84r3U6^DHKdHtC1%xO)_A48A<4u$8O_Do?*(_+b2lBQ*?VO*ng zrgh(m)dVPQagVOQ1A+9??Aj)VGJGmD8-lM;@@r`V@naSpX~y|#=vLyJ*1Z-ElH}?- z9FB^Ml0R9jYkHNw;ykdL(nHkI}E|`tf8i{P6_F{hF*T-zF?hNb|#W) z_)L9?*!NAE&UK54T|7IupOE$X!IyYhoGIW{FDDXCPO!SQ%wERcg2({eZ{ z?I2i4nhc`YmqMd;atj`oIxo$(1w!ge=6cU>*aWrF+?$QQKg0d3qvXu*WSVxp3^IVI z&kRsV@Mqxb(No)|o@x#H?~s3ASe@g@Cr`X0rO!TP9JP0zty;*5%fd_@2GRt2HUwP; z6vfRU=MY_$d|4nydi!e~&inZkR;@Fz9C8Xy!)CR5AQ}&zvv9;Zl~q(S5aS z!vSYfruxXTpS!l!rGukr&^A;gK1{Cb*QrgFk(Aro~e6e@V6$M7~)t&QB(R zQh%NJgS@$fU2pzwtdP;e0s_~Iqkqld&`){6Fe2y!CX=_Ydrx32eznatk+Pv{A-ExP zP5dx7>2WzhKl$wtdNpeFZw4y(Lovkd9C?XNHIKM44XOm%Uj@T6w~ES6l)+B?6?gfM z`HqEe+GNMwbiP`UA6Gx?&;Jb2u@y^Et|>dXJ+eOR6@O!gbK|Erel!lI{Y)re37m>Y zl@I`6Q=AYSLH_gYpcm#R+?%m|s4v&jxoP&Mz6lkK35Ad#g%i(sZL%|o zL41U+M4<_e9|#+diFmuLHF>6YUA*m&`}F&1aRdJ0o31%unvC;r!oNCu-DycW|3}kz z$5Z+K|KDdg$X=Oo?3o$a4zgvF5snbO6|z$}_AIhO#<9vOyHIlMjBJv!$tc;`e6K!_ z-|sIyJbE1WIp@Bw>v}!sK_x$XOsgDX^A}4Er-7xxEd!ZA+vY&&5x}UUa3Gi921}z{ zJU2ux_UFs}C&l(e=#XvwUsgdm^A8dkPuvjJH_!zbbR;VDBu@BB@UvF8Op~>T3<}V; z>#GjU4^atujL)}SwhEr!@ISQ*0xZ^|qbNJe5V0}R)ax0qs83jq_%DJ`OG=<+*Ykl#iz5Y8JGyK6X{1M7tBN?GA@C&{~5Fn zY@fOXF8LLk+ zLY6+b7U{Mq{y~5($%g&@L3r-}A;g%GchSY|VJoA-Tn*134A@wbN21<{pND{#ltW0B zH(l|$R(nc%D{u!I$d*Gqg2MmN=yF~isvWAT%-Rq5&^jkhkLIIk5k|Z{iyX3aM#3c6 zXr|tZ{P!2mbjDNa!&`0{e6EqDn8D_FwoC$Bu-=Sm&|LQzt?r+;#1m8sBj}FGjp0%ty=agPZnOq zWRQ|kxi>VPi;#EJds_H6vJ1iQH5;O1|6qn;0H$y~W&8FwUh&)C(T?0CM<#NlzCzzD z6GJHdRAYMmlJDmpS54j4;#vDOZJ096YSk;lB}tX?2eYDTEe!Y zOLpilA@vV_&O*|#lX8@OU*M87gsXQ==>1uI`9*3GFq@cH^UZN5D$*h}RByf^Eh!W7 zyth{JBbnKeH8?|-r@ZNmnQP;|_K$f~*`*2wq#AQeAhpqZIwQ&8uGvGm!0(lX9jp}a z^v>PN%`(E7UU+PBhoYD}pTdXa5y?iOE#TRW7BC|+kZu+de?x}pPz~~d>FSN{#=TDL zq`NC@0=E#}coO~#4)BQjE{>{PNQ}&<1P%iRwa;4RV*X`|-)D?CIClCgX^=)2B41WN z1pC71>v%u#m_Kk3zJlUug3#o&GPXE;FQ&M7?QPA`_93S~6bti%EXY??bd?))7dKnz z@Owv?`%(ledlc*_Y|qZ{dAQJ@Hb=Fj7n6=7&j*erKC-8U(vOExLscKErd#+R0R+th zLT|6G6|*bg3?~E;L?b8>!I$X-YB_O(9}k3}+uc(*Yf;zsJ`#nA*U~Qs4rFmc2Yw2eD7W!YMlm}!B4fx4nIY|Fk&J}q z5E<$2$29oaqxB`+f6X?-xfhxCF8!0;hsOJMJ%@~_OZyHSn5K_(m>xAWi{$7ZZzM(m zLVUns0cwXKcnmn1w_L+}cQKI&aKM1gnO*M*1bpP$z`D+&_5}L!0Rlty_;5Zth)eP5)2Fd!?3ippXlv3jQ3K8hJ_K_S z1Mz#-H{m+SFnvt=uZCA-kXh|oUQ0G zMx!Ks{CbtQreo0O;8;v$9T!%0KCqsU&2T26ifP!s-8vPFej zn|H&&DbZF$?M=m}&>X2OKe#M|Z256<+T%}GXnu)i`kFq}>DdBaan!PRg;gzI?gG4H z9gYL+riPjlghjjs0G{u+8fS^tHG;|bNX0tcegt_$d50ySG*$3Tv@+y4+%uj$OwFB7 zU1Hliu0%jQKL_6(M=-NyFRLnN9nL+ z<=p~;tF$;ZG+~rf<&LjFn%!&N#jn0>bCGtYRzdPV6aGrDP0G=YH1-w3#i!9oqbWN};5eYu`VQoBbR}WvaILfO}yVNcAwkV`Pp#yncD3;?Pqm-8 z-KELu{C6|ag^|YfRyURS^OCWa`R}f~E<~?Yd>TUh_n&)9+{*R7Lme(kvsf55ji-g``jOPd>C=!q?y>FK zyx$05NupI-CIo)|(=jGtCgGBhI8PYJMC%)w(cZsJv86un!>x*M4y(&LM5E0*1VS0) zGU$n>>)meE$rw{V=SfOPNx?mFE<0m#u&6$UQ4BpR8avY~#+v$j%TccSI435iq(|<^vD(7ECVAUbsQD>5f41>M zq-?vVnkn!KQNUHMW?V5<;N*G;V3D*c*5_p^YucAh`ndL9O8xxz(J&M7xr*65zD&6A zj_P&>?A1C3@Jz8N2(X>O^Z)3@->WZx(J^mh^V5ktJ%kg8s|F!i*Xx{}0wkhZ+t3Nu z6K_k*o^gcI+`YfdAH}1sqf9IPKC9#Cb;GWSXR`{&8)c_cyZnKp#jIJAHxvRv7M9d3 zNJU4!54&AqcRYbrP4hRMc)E<@oa14AmXsyajcYfVkeho~Jev2@Z!sZLLt|v-j&@fl z$f$$k72F&HXmr9kWti((pz8|R3F3YjCI>Llp`PZcc8MJ`wdA#nrKO`%EP|~{yg6l^ zjGsYbb&+o`k&j?+9nUF{SKc;+8W7h=6jN=oR(;8ZZV>n1-8XOX2_MXKqz&-8b3NTv z%AAGV4WW5Qo~xVL{mM;vLU=}}e&pR(#9ImxOz-mBAMRRNRmd}J0z zN4Z9|$*W6$FSY1S`0bc-gs-#q6i&zwS};Bo(N#?==}<<``58R(YP!94ZXI#9i`1VY zJq;gVE~5CX1J=ou4%P7+565m-e%y)M_?N`A=-+Z;Tl55ZI9?gdpz{RMN@Ra(F2q?e zLWS{yRVg-jQt)5fQ`odr!uzn!VT{vCrX(uhIoyza?(aCr98>1wB8D<}Dw{JgJ@ojBQs7uHKo2J48O69B5E7M-Kffwr*9G9O;%>iG z76#W#)bMh7Fqx7_pSl{345M@Xx6jQ4Q=kb$Pl8+0REoA29bxUUY1CXi4swEyM!!cc zK@q)Ii&^~f+2P>w^i-4R&0_0|DcpXLx;pGnH+X9r;6D`K*+iTR&RAC>h$(Nd1Dj!P z1Z*~7k;0}*A45EV!08hrfWZ03D{lIX0k1a3MJy|W8hgJ!wNdwwqzA@4^4JUqE!y|1 zTSq>>;jfuQu2QqJNNN(B6Vxng6?30gdJ`f%60RPWcaFVI%|m2Py{dS@ywbyZA-1H$@%xD{<`NZO-h zF?5Zfj+=IilfkO1tLxg@=5UeFU%ouGySo#mu&diW@k7~F+i&@~poo*CaT~+kaGV_Z z)qTW&7J*{ROlj{XM%FF$MwBwqU4}6#HQU~I#B7^hu8DC*Jp*9w|3t}g-|0MsGh2Px zx*N}?_dbx6DBw?Ialc3p42)4fIcg$-L;Z zED(nIThRV`{t?jd&4wCNZ&mU`_{iDN+lvH~%k#eCE=)bqqw(MBK&VEB<`dN;A|k9K zw{93%nL+9pd1Mx@3W1BWOLs;5OPoZK{7?mz zTj{V|!JsyrCz1f+Fjy0V{Pwk8o#<&8gt0gy61;>rkJ9HQIc+GK8V1UXYGB`dv&AQ3 zbpzcr^OKzAma6>4Zkv2D7uwm`+3z;}H444(P#G+}0AjvoPW=|hsT+4mlcer`cvcV7 z(;WfPMt4e|Kqj}84Y`O^Ep_Z;w=g1Yo>Fb(Z%&mflxbL7mNr^kj(4=h$&>GVA3TgT z;uo861)~L&VgvpIsbYSkmX5QQ1Y-fn4rF z_}d(q27doz0*hhz{Cri@rYh0J%C~Rdg;L=rP$I&@tduJ$P<}C4%+sinRz8V35pZ7q zvhJ9ckVl7zJm$%IFjE)6v8Xk-MzMd-i1H6HH8_!7Uk#HyC5ZE_e2$c1aa#OZ zI`RupfLq>8Ki0r2$;!boMHn)u1Qx2$_f<3W2U8~(8++4GNjA&0+t^`yE7vv00emN1 zfRKp0jBki|#~I0ua|CW1?rNF27_dy_&kv#M#=m}jy``9#jRV@kgZ~hLIXG@FZ|{w3 zk3#1=6_$+9l~>tHmOfS|Yn1d4jpUtmVhx)+s$V3_TbhaU;@+!-+})d&ve48usF`5! zD!q!Joj!*kN|jzxCM~w`4$_#>s1MTb@=4$#U#%L~?YZwXi>SGH#VCeam->bJQ0Z9) z*>}q=QlzAC6E;HsgVNK|NXB~>yq1-=^qCvTEGJDn zH9j3^st?2cXEFQr{($z;1-$pdC174DijF}K#{lxdbaiz#Rhc}y5}2zH{zKr1+|@-- z4!!yhF$Dr7)98UTr2t}rG(Swsar3QQ{?06>6d;u>rx})?e@jBzfmKQsb46W`l?1Iu zTox}7OoG*VF9E?LL=t>V0~%z}L^4$aRQvvr{-2 z6UZPfDEU%_@co}J*2y0R3o?^)gr4f!&`{&t3SRS2AeQ}3|1{}K1=Fb#mgGkJOcbDW zy3IoMwHU{gm%ly_4jKvz2r${+zD=$GX;n?jCmn#3KCmQ>{{?dnU)xg%0#|Ql&Ih8w z9zu}ldsd6A=S<^^z|{C4s)J&c6bS{=$|T@$$`*On%Qhk#{W>E9CJgq8i2v4yyA0o* zk^6lrI?&=hraq=$ z-q4T&g2gJilad080&WLIm#UNoEqShZj+*Q_#E_hv9A#p63hIN~B#a<|PNjecaa?u> zto^^ND}HMEDj-K_iGGHF@WwgIo%}-*(4|PB?2QB`w>20HP;(6ExtnVrk!LL}C0v1V z3jWvZHbyput(k6KdH&x+FC!|6U_KIC3)@E$N6a6Jz0ZKzYaQmwo_OAsxb$XQ_mfJ} zDO2OC2Jb~MEI4}e&%BU%yR%xJxzA{PucoT%809*ESELktuRe9vYe7>!Wn$S+;fK3F z%kMG=;(uWgO-?HGJE26`t_X07w#<>41G?<{Abx7wx*nD`;P3hFWvXd_>Bg6T@<5kh zg09Pg-R-{(U;7mwTt#Ck%f)0#u!ujQg@(X|_TzwytAG>Xp8VRiKT-D=%Gh~qee0dQ zL4<=DeLu-1_@}{%r=ySS8IZ%0qTnD&y{Iy0FCm0$5Xt)Seqh!-_e;(VW#mLrVF5E5 zJDk0iCNNykalRP&;j{bjvX5q~F^%7705RUG5}|wCs%B6JGt5n0J;+ zQ@GIge*cUzDVpsDxaElJ#aX*EMQEwrx^W!tUI)808d_X3{@>UAten?Z01AKQGc_9Xbu65eqMW7A_x4@6 zZNYR#|Elam*js3fd!^-F^6DpDxg?Z+DYF7Q33B^ zEJ2{lTxXc-;$cnpIO8X|(fC=LuMMar9t~-9i1-5?@@=~LE4cO&3U*mfg5T2T=aaZ8 z+}18IGz=Gs9EKu@*@F0lo-2Yhh);Xxr0tv8jX&Q3x#Wexc*U>!u%dhg?Qo5(s8t!b zsYxMapmerkHKOly6=sdP?KwH)Bqo2_6>v`f@rS!bFFP5PdY@PL??JNnCbtZ3K7Iwq z&A#KMU;=#D=)9 z(GX@2i|@C*efdj%{-E28xkcm(|BnH$g2e4b`UN{_eWP*=8eKlvAs22HGAFjy`(MKk z|GqG#_;vSZ+S#ju%)*&DXrDD&dH9pPVXf!NAs++x%l_pk$@ngJt0fb>#R9he zaFUC4xlCjqivv_S;&I>?HY47Zr@+>%AyyOF0Q8Fz&Ui9$QKbK?vf|L%O zzZC?ZL;hY}P0GQ=P2v6_Z+ug@f62ho0OaH(nIuYLaS;*oqCO`K5P&=mJ;_szM26j@ z6$bxQO6$~0fMEc0Nz& zr%^1X8qm&y7@ZfgcVs1i8(ih?4t$D8p!3Na^u3Hr@c;OtGtqbmxmR3wj1xd>_WlR!hptSj=u(i8GW74VH>NVIUat_-}C-lb;G9i z2GdXzASe^}@GdXhw!xFr2rHeptUi?DSEq0g5@#cjabj3{$9$$-PH z`65Ax(lYxeG+vCs^5OT{*~8fPv$2VfGFDUd^{hv`#DuAKj}vC~sfWI-k4m@{IZ`$e z@4VEvYzQef0Ui?MZBa%Yq9|+Dhnl9Lz@$@XRtp31N;G=ULl>lzLrtLO)Py{~h^p+2 z-kpU!G?jeePamq>`&p`(K__t}JdiJ(=3husCCrTv0VC!2OiuLdu&2!R+fz#=ehbcC z6y1(N{O$4fI$dIZ##50^PiM)g#Q|L)=~9%|!sr}1fgYb#xmn_RMfGcpQPU54W|CUi z;$Cv7-IHyN=%0a*kr8evbbNwk7bKRJu;aeJaXlS#YX1aCMyx9H8jRjyRKQ)sHw)?$P8tew6yT<0N z$$xmTu<7%(Ql0Xis8NqcWM}FtMEEt!3JdwIndZk=nuOti!4`c+k0dqsy#~&58lof% ze#B`~e36`%2HMz1^Xj)LOSQ;IGMXvI>e8++uHv7LzusL%`?wU|uN?v-+6{ojpJ zwM}QjAE!0yiJVv(G%A$eP+|dd(z>EAWt6!%PKH% zPk-+(BaoUI4{-)B;CsMNdL;w(C_4N6eFR8G8qde$=M{z9qJ=DjP&)(QgcY<>-A3%r zQD#QJcV2Kw;q*(TfPlogjQLvgjO;~@h#3BXN|}fmuM4^L-BW9ILb|wW+6uWrmiz<- zkpYuI!+oAI&A2c)G*?ClM=%f(nR^KPz-R#x$k`wgKx=+kQ6qX@Yr%Z6yM!-`CofW$ z2V@2|B&*WX1y<;zFSV{qU4m+4@WL)UwwF&sPrVdQpNWzU?6l234D@&@e<$)oX8tXRV<3pUSdb5gdgOOZj? zdjF;$+=32C#&zN(^!Hnrd#1*g|I*z_6sSBkyQ<>z^T3&g{eAW%-5)tRG{r~8!A#MN zQ4z5}lp2W^k3+?^A3ZwKCWp`z>AIxhB?il1&maZ{26Z5bt*NPT;=aN)4nk{B8Pgdr zSV?1zmaE&otbcf}eM2`BSGBH?Gw}S=2zHT<$xbnKdPk;r%hdZP|HI&P#?3o&!`+9) z+5Y+0sY|=ToCZm$#%>>=^Qr7kh5N`H!!-=+>~u%46ciL$0g87a3`@2aNUKhHIvS}H zS7oHS31?D1g{XtAHwg$rN+DMPlY!!(R~Y_nMzjE{3h92e(~9QdA63hs3m~nw2#KOw z88z@ofiOC3V}Ve~+qarpI63d1hiowPC9K5?lU+kbEL&KZp*sP0f2FO5i!Fm2)AYEv z!$;tIy1DNYx2Il#m7dqjv8Sqn&DYOq@GstWWw-j?7JoUewba@}gKugI46wDG z-U9Yfw?xQ77IyY7)lc!LD_mSt^9!>)Ki(hBChy=#LzzHFJ>tJ1x{^v``lHb?KKPh} zTX!>$y4@>uan#KHVp>G(w9+s+u~+tj;y3blrvZVTT+wzmuX)X`cHn%H)HM(ELx`*J zWB=zv*2}@Q{)4E*Sx|c`q)z5JuAi7+P_~Cp=&k>ZR48)%7vj{06EwRS%#6ghNajU1dj@{o*w$vyqa6P=^zGnH}+bh+0QA||y-HY^e zN-Anp!oMj0yE;rmWjFA+wZs&TIA1w_^=14m40gmuLbK7#Og33w1iU>ASJLQs$dhc* z_tR@$J(4p9Dx&flzZ<)OK?xs=so7rqZ*b-3Ob+`R2xCi`krVox|6%MFfLEGJN?egT zNRXTU%7n{k>|Y-OK=e#$L(vQYAlARkBy^qxFT)_~`K?N@8qFuUI1{3GcHaJy_j0Da z85Tbxf71p0aUpT-P+|v9lL6I^_4s|k=`ZmI;vZYKS@kxXRZT*h%RXz**qnyVcZP)f zqqxM;2(8{Hze*2YynN|4I5@}=+6bwdhJx}D_IiM@)0>)vhLYETW{Q8{Kys*BY3ILy zwJUi!ckvC^wBsBod`B?^yyDotCJIjK$>50IM9wu;TFU>a&c-%v&!U~sfd&hU>9T2} zj}2}e4_|_VG1D)xTMF*?6-`-|OimAC^`e~AP*b%oiGGsQC2?&OAj0Gof;g3S*MgGe z$Y|!8f(`~~w&f~>1D>)Gt;!!e6#Heha@YQ{rmGwlS}jF%ZKN(h40-s{~P(9{puilM%kcy?ViDZTqHCSamJ%e zvjl52Tu2};%$Pc_#CY3QBgVs4*!WtB&Q!vW8!ul;+?#TfjOy9AnPq46_sZ;)xBJhT z-(T!1b&}5|h^wp~f017C7?{o4y&f`q5Y#?TXg1*}mv+{iVbCWP{2oU4xu+*KL-ygF zCSm;v}(QfAopB?BQ6_WXPjnn?D&E8`D_#0$GaJ{1?|>P)EcxLA`&{ zFd@G_)LdpwSnjT%!+)1hh(Lq-0ly7E?|h@@)V8r_emst8qBVL}K zaSg@E2#%7Ke$?V`5#%SpCLNi;`%4N7MWW+C=`ho8BnWKoCRG(-dsTxVyV7Y7G zf$ZilgG*EVcMClDem{os>{-re`C8G?p=^aN0NPNFcS9u{d+AO1JeQ?Pjs=> zPDP}0Pkg2g4Ku)IWcCbpAwF+vk?7sLUe0@cl8Y+JC?cGb++>XA&dy^7S52|uTM$3_ z&0via(ijXF3e38uKl~ot(JNPKRVqIC#YbMSqOi9nM;m6gfqyDDzk&?784Qr3r~jgr z_G~a7PX${;O2W~Rm>07>IBNalHRXZpzr&QEj&IRKVZ-fCo<5? zzWK|?t>!qFr`%XX&~2{9-mESUWf0uLlZUpB_^7I6&w5Y1rKi&OF>^(oX!YKm)GMj> zejyUnZvl^8X~!@JJz2ny!7yxlX}9b^+zGS|qo~KA)dV}oPk609EKi5*m-ZeLm7vsD zx83+Cr&^SQN&~;LDj2^W-k|4eUcE{mcP6FuIxpuGRd?JV-%yz-TT;p4+F@_v6P@J{ zIpc=I%_0o?!ia@ETF%~m$debhfPjE|j*p7HYK7xoAF05n6qt_L*hOLO8|1CN6K1 z#}x`Hg1diznw?u(Sw}WCHF2F;K)}^X^I4_9m3j*p<)5?hw|x42J%yh-Dr((0_UEfn zrSccC_1Edk5{0=E^hrJmv|)$OjZ_Vee}>UDtPaN)uroT>-%cLqO|HnDAg(_M*nd0! zom;0-@KwT((H{dW;$Tq0PmEmfK_1oCtsn)LR) zgDUy9b7{9V*4&EX%Ex;UXEvhejX3Yu&K^>7Cw@@_^q6tpi+t9k#94#Z|XBwwY>BK{Z9JP*>OAL?@%fos@IrNhduoeR^+hj;RS z{N{1ZeAJ)|kBF5UC?FxuICaRuRdRSn0N(5U(pCQ;y&&Nd<>(w7&57(fQf=>r$B#LB zjcvQ#&)HWDJp!=WzqtoJ5*H+n)JYgx7|7 z&txKqTpv6b*B~mD!P?r|*6D~FDH0)=mDXz)k$g^u3COj!WC``;Pbw>&rN=K@eiztd zhm85LoITy_aYG$T{sO;-_mH(~$coAKbUKoHlO2tdadvpH+Qga|X^u6mM)|q{p@ZgK z8V`pB@OWaVi6vr*VhXYNf8}u*Y6$n)PqvJVJg$U25N)YWs`lCDl_$1#<cTKq=HLmVh|7E}H~F!T?pXQ2{j#fzT`1D#wH$eCCKkWs41J<^ z-f;OXM8`%-r~k8hvS-V&9=5x?tA%0fK(mPFwuQp})OHXLjg1w>lbbvs#KpyBh)?0R z$vI<6dpu|~O2krPZbi|>6osl98;78U)?JqR-@PoDo49^rt#;k|eV>gB7>loIpbI=z8%Yo)jQmH{4r%eI| za7k@IZ)+mJYkBY9J$(}0D%mJn5rpO40Rgp(Yq~!_cXZzpD=*+F-%P1^<377YB|9tg zl%$sVw`3HT4|WIq&1Xbk$>m}Eh?=O}AdFewx!2a+L%}Hy4UH3z-#RIs(0j+mvkDok zg9B%3XqbE`FooUFC7t;3gKdSLA72H!E6k$kugT!(uRY@=`U1t1y3jjJ?wW9TG*?i* zaN1{ij^0c^O}dTBjFg2QqcmycWsfN2Vcm&e+hk$vS~ZAwoFtjVjv=Qr>Lf~b49-{E zKg)$=aJzShDw(lXRaJR*fW8|{-{$o{!a_pHm`j(4s{qztwVzqW)>{ae*n6q}BW0^2 zE-OCECmMu8m|f`e=bcEiih0vYhLa1}`B0y8r&S~OnZARXmag#f-d47#FXp6g+#;U(n%LE+^>aA9dTx3bXkLfB%e7`c8V8xNZhodN+FTk4dO}|R!=6`K4KaTSD~B9Y4z#!50Z;qFA|Y~P4^TDtVol`Etkyf ztBxDO?8xyXj}|WnAZp>NdLkYwq$qznu0%0jt3K(1NLSj`E{1WfdRVNiKpIMqh5i8n zG$(b`Qer`v2q`;Qi$CL@-F(j@r}-Wkm(D9Vb;0FCsdS&^z6zS=`HO{kF0(V9im<}t z?5n?DhKR4T+In@(&ZlP2C$9f0ue@G<$aC{gBAnC|#WO>#YY1 zzsBC}1=?y;s|SRg9k1*D#6~72w0iHMF&OouEm^x16%xBClP+|8&|qr4td52MulsU# z2JhK_ZcMTsYW|Gox)#2^xaYTY+rEl{@~gQy-pFNENXveQ?pa5U2}kJb@1Z>^ik=Bs zU&19C;o70sn93>1o$;W}3J$0K1gD{F}P9o5+j?T!9jI z&(sUMiR@ja{-v)+n}-D?C!2|l1tj4G#{=HZdh4?xj12`OnST!n*ur?j)>lgMN`c#@ zEwK^r^YnvXw5O=dh)m~vgDV*zVKj&g1%G!hoXp&idAUX76@LimdY`sR;0pwp|8;4} zd(0>{8;`y={JP(}DuK1Lvr`0{0=*yZ_dZfHVNW4*=V;(zyt#b9A4x>*3xe7eO`51@ ze|Np$`Nly{vN75#J!HB+-Ut42HVRTo-l=i9f21)u8QI-*?rYJWsHh&*K$xTZoUkNb zmKaZmqvU@Ei$!QtzM>#;#vdOCzHp?3{woBVp|!E`bNgrBj# z7P7Ybb9kZu=;v(9OG?2#(rX#_ir9rJ{#jh6SB2U{#rcFD%gKS(h(Aml-)zTi!}mK_$p3tr>lE~CdPx!14e z&|q#ZN+0F;!H1@ufaw-Bg1QYHOyem1Z=Zrb^wl76O#(y+PCAgVWkM28H00Y49y@Sz z%retE`0<$8NT753a}Nn(mwxg}JPdn;XB{P`;p(pOdr^UO#g=|mj2n8AoM_9BWgmAL zB+Yo1Ir$JiL-*3|?qCR`N3i6!N(#NNdsPD!-#Iegu}k;ge}$TFa#5g&lm0Vd6@LU2 zVWOjy_~K&TxVBJmD$Js63i^lfv8^CVr&hby^`O0mMOK z`C|)F>r(5;Z1sI+Z}_t-X;Gf=z`UB&&7DUdw;$uJWZ&v{yPX;*oBwqcYjf4iYn52E zbZ;@{$K=J!LVYkjSHvA6j6mxGD9GdnH-?X?$-jzIKCr`>&oK)ww}$;kui@I2#8n$| z`nG^CCeDJZXX+w~8~2~+jG8A>`Pen?+7k4ebOo8MwGZGFw!xIG&06KFYMVuTxO8AH5*IrPnOzD6js_~ew{tMR#+Qnx1Nv4*jhc9WtWZRT6(s=>kkJD zro#Ep16b{6ue`l^rML$Nl+{*Xfp4?rg4#`@F|g7rpQ4)$*OJXo7*S+`ZTs z??b0A&W{Ih=3lPRn{n*^JkUw8$=p*;w#Z4`%+0M4E^l5HE;v1nEGi+ciL*QAO|I)@ zXGu;~I9Jh9lv(?BttbEbIV4v0T(Mg}Nj{qBBYoq5x+@|WcsT5cw?I$qAseo&teW2- zJ7!LrSsj18K-{P-IhhyJVdbLm9XZj{8*Ufp>^OUy`bzVcs z43zqo-SY@sS87uo>gx>%Dl`g;`t~$)x%uE`2o)tw&~q!l9(6+tu#UXpaf<_oj)-8} zJux-OiCrn}>v z?rolxa=omv87#TMVV10HG-gv!@PL|OLo4~m`%6K0?wlhQy~#c)aViiORSwrB%011* zmMFh{yhAwI)ud5lrB=nrLbD)9lvVulYY2$>^*fF@4b3_9EKY7IZ;sR1_`uY&*q{G*LyZXa_+2K->z=LoDI6mORD1G28s*UenTJi1r zZ>eNZCmi+YybfI+9#C6~;+gUkI->`NLUuT$G>w9u$%lF#6dFKld)nn%fz5RP<41u~aSxY? z1BPlEOBkA=0UXenP)cQ(D+5muDC*5bx!^=(D`Z~As{^R@Zrw((48*`<_E^Z$}c|BfB$tuqfg^|7)@;z zI_Ebj-H)ZGDwd92XU z+gJrhWAS6sZW9>=`eCm!Grfa9pz4#C9J}#%`BdDDYKkJ`jJpnhgpp*qv&Ds3n~j}- zA&Uz8t()GL?cYL_BN0E<@>xiEV_Wx0%UcE1M+X}1LbddQ7adEvZ<7;5It!k;@7(>f zjpA4{JpM2cKZ%G|Lz*=`f1*B;{n)p9eB{Nx{GLEir?kg3!!0HB_avtckK`xkIS^3qa?B_!IJTvfe*!u2qFwoG-y=}<0$M6cHCrykszCR9XYrX&5F z(&2beYExZtK1A`u9{>!UzvOX~!g!1JOtI@2D`;3X^Y*T<$Ul$$1hP+=O=n75o^uF= zSczGs-P$LxW=DlI{$aM=&*Cp%_cfdtxvuCaWVeq~cFTS;CH3mCC|Z{Ck?Oh zkG)}V-e)!*K7Y;Q#w}Pm6J@Nva9NQh$7HKdYxCvaq2aV^{6W9+-f+%%g%PV`7iF2) zmx7I}K~zif^b{GOEYl%1=;b&Hbobtr^;(L-W>Mj*tB32s&TrO6oMdNZwMN-aj3;Am zb8{yC){-f$FKa^MSL}eucb*hiHF{<-1CI(<{47C;h4Yl=eSWlVgRNu6ZOdw=j*}Z( z)bd!J#qyLl7E}!M3dvU=f_>LMz_m61>f&X#Z)t*gZZr(O;9Y!ZCTc1F&SNz#&@(kv zv5=x&{pM)qV{`Ap&-#IW5xEN~+hDZ~kmTRA3ET_5V^@?? zu0hpVrt%HlYCV@# zn*jlA_*E*ptdut@H=C`OUp!7EWTCy{+I=s!!5D!T0ANB{MOt1noy zGtqv1F3uAO=||vO#+}EX60TT?1`p=sYz$<|KIFN_z4V5gVv_o+g5+H_;}O-GXU)$J zG95R)a zH#a{3waSOe8pc0hBE?O2zk%jJEwxU1VVHuko<0duxoS_9l!qnKS}2?REJT3cA>B8! zRP1WfngC$J!%M}!%pV`?l+82uPY}Dz`BDZxjGb9XryoC?Og+;1mhvh{`0>c705``= z3pRi$sPTmkC@VV~EJU2Pd8lMw^uO<38Du*OUwN+)*ixwx5Z_p5qwn6O86a_k65F0+ z(AFwJ=c@kJv8#-tojRl@R2Yi`AWe(R&e!9LNW$rXb1v+)-ya>2BJN?-JF@9-IpKiD@5 zRy{RM;d{!(yIA^Bv6Yi`{TH*`!%yD#J_1u%7n-0vz@#*6-yvUgv&hlPvA0hwe5_9S!vvtQV*alI_R2@wn@N~S|DWkf*sXFzdw>s#dE1eC;F;mc&o~Y-RS5S4E z!B5T;CGVoXM+F?wDiIt&M(hU<;NxBUS1Nzc)m8jsVC`-2MfN#TQ{fHp4GpsC{DJqt zH#t0cFhrX`H|_APn54or<5)6)yUmDrv}-;;Op$WpMcdysfO!?jePz@-HW35m^l^(; zv*0(k!{zQ8-Tji4)>@Z%l2KYwq3^(_l79ZGE1_7Im4)TUqzkqM#&2Y2_tt|LZP-!x zd{E`$R+e#?D5Vz{5|Ykf z#vW2f$BW9yd+hrX-Z7TaAasff6`HYUc`akov4;`SU@T)swv>G>OEZS|_Q&tK=DL0} z&o$TYcR%;@Joobb^ewzekKwX6y`W>-(%o~{+ClI|Kj^t@@?!93Qp=7;X&pkyguVqG zYeNZ^(9tUvNx2XQxy;4EhU76K6r%9r2T1OONu_S6gHIzi@*Q)(k*2>io0heuoQ`H? zpn&#cp4|e5b=Qky;X>8k^ms$lXNnoRjW+8K7O?^=sXa!zCdeN$+L^nF9sPgp3u!cn z(Z|MKQ$VhWJJwuVJM>HD0N-#Mi1R!G#=$DJL}F_PA79XAcq)(m;!fN0s6HZ*NUl#% zW{#kAu9lq-28x-VwNt%ELOH&8n3g7Ca^8*D8iTGX_VTTuLj-=9TC2H&qC#Qvs zIKD`zov6OZjwzO6mC;tCLCl>iOrY_xn@6i7)~CS_O7w&V1Ue4vSLN8vkM5! zO+PxRI`A|r*)s;)60p7U<+{n{}Skstb}vj z?~VeTG}tvMg)#j4+vbae!GE{e0XF;r;w_1UE(W{K8!>KVSm9wpNgi}AZI=`mTOhFW z&rNcUjhr3*_XlO!64CvpK&TGz`e^pHyj=d#jABifS)kHdu!@e3j+W~cQYEZE_W#9$$3FjH zcxdPtxbyQfkwJgA2Tqiflvw6(%DzWsD$Z|D^`}PQmp1Lz$|BsgH;qTLCJdA?Q z-0IL=uJLIjGL#4y8B-CfvW?avZesL)&Y8 zMhX%A=gl48`Dd3lH7SgUnwFH7YKN-Ty7waD)Dk9TgHGI?!&$sAqS`}W8^Q{YZ**-t zXrFfl8^v5-SX>nGc&>BtVmp0nO2Mo-!6R?^!M5JZxws}QGb5$x!le?D7YB=BP>V&t zAKmB*K@gr|deWjqY2I}xZw$N4tW)?k-xjSsI^xeZ^EbfvPA;NoYP%R9&>>7jO z=d85SKQM3~`GJK;&r(wtl}9$}ixiWf-RYPEU>#RI>xwH}<9fNnX}8krJZK6T zy?cY#UVr?7Dg3PSL}YuJCG8d76RUZ8Fw+l@PB7lEugj5fm`>am9dfXNLn@wyWIWG^hjowLWW-*y- z`h!>HEd}&JI=pXqSjv2rH9oFjxghx%X}H2^C@)v1Cb6&FG5sb(7Av)4aR!X$4fd+WVRTq`nw2&H-1JH_xSI8%Uc9`XS%3 z5Be-cE#H9zIIrK}{=te{MwbYv6sMyMVORZK-`)OZ z*bj84Z1X;M6-8^wJM|XmXp7@V=3oLk{nU5-6I>1E1h_Uf(@i z-Owrum?$FJ%%v$ROURvAT6$H$7b*CxEOL;aI#y9pp+XjVC~L5cSpvWAzkRsk!{10CePA;q^TBeIs83|+lE NmZr8Q6=>}J{{wmNoMZq1 literal 0 HcmV?d00001 diff --git a/src/icons/zwift-on.png b/src/icons/zwift-on.png new file mode 100644 index 0000000000000000000000000000000000000000..3201504319b8bf07ca0720bbc4d9135b7f082400 GIT binary patch literal 4123 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"N/A"); } + + if(treadmill->connected()) + { + ui->connectionToTreadmill->setEnabled(true); + if(treadmill->virtualTreadMill) + { + if(treadmill->virtualTreadMill->connected()) + ui->connectionToZwift->setEnabled(true); + else + ui->connectionToZwift->setEnabled(false); + } + else + ui->connectionToZwift->setEnabled(false); + } + else + ui->connectionToTreadmill->setEnabled(false); + } + else + { + ui->connectionToTreadmill->setEnabled(false); + ui->connectionToZwift->setEnabled(false); } } diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 4d0e456b5..c7740b81b 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -14,6 +14,71 @@ qDoymos-Zwift + + + + + 0 + 100 + + + + Connection Status + + + + + + + + false + + + + 0 + 0 + + + + Treadmill Connection Status + + + + :/icons/icons/bluetooth-icon.png:/icons/icons/bluetooth-icon.png + + + + 48 + 48 + + + + + + + + false + + + Zwift Connection Status + + + + :/icons/icons/zwift-on.png:/icons/icons/zwift-on.png + + + + 48 + 48 + + + + + + + + + @@ -25,7 +90,7 @@ 0 - 120 + 0 @@ -51,6 +116,21 @@ 0 + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -60,6 +140,12 @@ + + + 0 + 0 + + 75 @@ -92,6 +178,18 @@ 0 + + 0 + + + 0 + + + 0 + + + 0 + @@ -101,6 +199,12 @@ + + + 0 + 0 + + 75 @@ -133,6 +237,18 @@ 0 + + 0 + + + 0 + + + 0 + + + 0 + @@ -142,6 +258,12 @@ + + + 0 + 0 + + 75 @@ -174,6 +296,18 @@ 0 + + 0 + + + 0 + + + 0 + + + 0 + @@ -499,6 +633,8 @@ - + + + diff --git a/src/qdomyos-zwift.pro b/src/qdomyos-zwift.pro index 419b7f92b..3a2828161 100644 --- a/src/qdomyos-zwift.pro +++ b/src/qdomyos-zwift.pro @@ -36,3 +36,6 @@ HEADERS += \ FORMS += \ mainwindow.ui + +RESOURCES += \ + icons.qrc diff --git a/src/virtualtreadmill.cpp b/src/virtualtreadmill.cpp index 10464c205..5a22d78ae 100644 --- a/src/virtualtreadmill.cpp +++ b/src/virtualtreadmill.cpp @@ -208,3 +208,10 @@ uint16_t virtualtreadmill::watts(double weight) } return watts; } + +bool virtualtreadmill::connected() +{ + if(!leController) + return false; + return leController->state() == QLowEnergyController::ConnectedState; +} diff --git a/src/virtualtreadmill.h b/src/virtualtreadmill.h index f3374ce2d..593197435 100644 --- a/src/virtualtreadmill.h +++ b/src/virtualtreadmill.h @@ -30,6 +30,7 @@ class virtualtreadmill: QObject public: virtualtreadmill(treadmill* t); uint16_t watts(double weight=75.0); + bool connected(); private: QLowEnergyController* leController; From 9f6a4de4ac08eb9f21200034733eee8fa05d45b2 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 15:06:20 +0200 Subject: [PATCH 41/45] ui screenshot added --- README.md | 15 +++++++++++++++ docs/ui.png | Bin 0 -> 19569 bytes 2 files changed, 15 insertions(+) create mode 100644 docs/ui.png diff --git a/README.md b/README.md index be30ab7a2..4801c313e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,15 @@ Zwift bridge for Domyos treadmills Buy Me A Coffee +![UI](docs/ui.png) + +### Features + +1. Domyos compatible +2. Zwift compatible +3. Create, load and save train programs +4. Measure distance, elevation gain and watts + ![First Success](docs/first_success.jpg) ### Installation from source @@ -25,6 +34,12 @@ $ sudo ./qdomyos-zwift Raspberry PI 0W and Domyos Intense Run +### No gui version + +run as + +$ sudo ./qdomyos-zwift -no-gui + ### Reference https://github.com/ProH4Ck/treadmill-bridge diff --git a/docs/ui.png b/docs/ui.png new file mode 100644 index 0000000000000000000000000000000000000000..9498c025e3b716cb0dc8163d858672ff09954b4e GIT binary patch literal 19569 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 literal 0 HcmV?d00001 From bbeaa5ec95c7c41f664521409000f9169b01a650 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 15:12:34 +0200 Subject: [PATCH 42/45] calories added to UI Signed-off-by: Roberto Viola --- src/mainwindow.cpp | 1 + src/mainwindow.ui | 20 ++++++++++++++++++++ src/treadmill.cpp | 1 + src/treadmill.h | 1 + 4 files changed, 23 insertions(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 427f0f856..b9a37f154 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -30,6 +30,7 @@ void MainWindow::update() { ui->odometer->setText(QString::number(treadmill->odometer())); ui->elevationGain->setText(QString::number(treadmill->elevationGain())); + ui->calories->setText(QString::number(treadmill->calories())); if(treadmill->trainProgram) { diff --git a/src/mainwindow.ui b/src/mainwindow.ui index c7740b81b..a7d2e594c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -378,6 +378,26 @@ + + + + Calories (kcal): + + + + + + + 0 + + + Qt::AlignCenter + + + true + + + diff --git a/src/treadmill.cpp b/src/treadmill.cpp index deac0a253..625043ed6 100644 --- a/src/treadmill.cpp +++ b/src/treadmill.cpp @@ -15,3 +15,4 @@ double treadmill::currentSpeed(){ return Speed; } double treadmill::currentInclination(){ return Inclination; } double treadmill::odometer(){ return Distance; } double treadmill::elevationGain(){ return elevationAcc; } +double treadmill::calories(){ return KCal; } diff --git a/src/treadmill.h b/src/treadmill.h index 7bad7edc3..2572a8b87 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -15,6 +15,7 @@ class treadmill:public QObject trainprogram* trainProgram = 0; virtual double odometer(); virtual double elevationGain(); + virtual dobule calories(); public slots: virtual void start(); From 1d23ac4b81d99e8fb53ef2af6c4a95ae8a1dc687 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 15:13:42 +0200 Subject: [PATCH 43/45] fix typo Signed-off-by: Roberto Viola --- src/treadmill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/treadmill.h b/src/treadmill.h index 2572a8b87..50b9af008 100644 --- a/src/treadmill.h +++ b/src/treadmill.h @@ -15,7 +15,7 @@ class treadmill:public QObject trainprogram* trainProgram = 0; virtual double odometer(); virtual double elevationGain(); - virtual dobule calories(); + virtual double calories(); public slots: virtual void start(); From c21e337bddf57ffcd5a7f0257446a3c0d9c74e43 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 15:32:49 +0200 Subject: [PATCH 44/45] train program example added --- train-programs-examples/calorie-barbara.xml | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 train-programs-examples/calorie-barbara.xml diff --git a/train-programs-examples/calorie-barbara.xml b/train-programs-examples/calorie-barbara.xml new file mode 100644 index 000000000..52c6aa9bb --- /dev/null +++ b/train-programs-examples/calorie-barbara.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + From cd1c10a09015b45f09c89b98c8b4650617c21bd4 Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Mon, 12 Oct 2020 16:06:07 +0200 Subject: [PATCH 45/45] -no-log added Signed-off-by: Roberto Viola --- src/domyostreadmill.cpp | 18 ++++++++++++------ src/domyostreadmill.h | 2 +- src/main.cpp | 7 ++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/domyostreadmill.cpp b/src/domyostreadmill.cpp index 9919d71fa..c65f084eb 100644 --- a/src/domyostreadmill.cpp +++ b/src/domyostreadmill.cpp @@ -61,14 +61,17 @@ bool restart = false; bool initDone = false; bool initRequest = false; -QFile* debugCommsLog; +QFile* debugCommsLog = 0; -domyostreadmill::domyostreadmill() +domyostreadmill::domyostreadmill(bool logs) { QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); refresh = new QTimer(this); - debugCommsLog = new QFile("debug-" + QDateTime::currentDateTime().toString() + ".log"); - debugCommsLog->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + if(logs) + { + debugCommsLog = new QFile("debug-" + QDateTime::currentDateTime().toString() + ".log"); + debugCommsLog->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } initDone = false; @@ -94,8 +97,11 @@ domyostreadmill::domyostreadmill() void domyostreadmill::debug(QString text) { QString debug = QDateTime::currentDateTime().toString() + text + '\n'; - debugCommsLog->write(debug.toLocal8Bit()); - qDebug() << debug; + if(debugCommsLog) + { + debugCommsLog->write(debug.toLocal8Bit()); + qDebug() << debug; + } } void domyostreadmill::writeCharacteristic(uint8_t* data, uint8_t data_len, QString info, bool disable_log) diff --git a/src/domyostreadmill.h b/src/domyostreadmill.h index 211ebe296..67c158b76 100644 --- a/src/domyostreadmill.h +++ b/src/domyostreadmill.h @@ -32,7 +32,7 @@ class domyostreadmill : public treadmill { Q_OBJECT public: - domyostreadmill(); + domyostreadmill(bool logs = true); virtualtreadmill* virtualTreadMill = 0; bool connected(); diff --git a/src/main.cpp b/src/main.cpp index 98575c6b0..87c2211c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,12 +3,17 @@ #include "domyostreadmill.h" #include "mainwindow.h" +bool nologs = false; + QCoreApplication* createApplication(int &argc, char *argv[]) { bool nogui = false; + for (int i = 1; i < argc; ++i) { if (!qstrcmp(argv[i], "-no-gui")) nogui = true; + if (!qstrcmp(argv[i], "-no-log")) + nologs = true; } if(nogui) @@ -22,7 +27,7 @@ int main(int argc, char *argv[]) QScopedPointer app(createApplication(argc, argv)); //virtualtreadmill* V = new virtualtreadmill(); - domyostreadmill* D = new domyostreadmill(); + domyostreadmill* D = new domyostreadmill(!nologs); if (qobject_cast(app.data())) { // start GUI version...