mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
47 Commits
Mobi-Rower
...
treadmill-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
482b136203 | ||
|
|
e91faf273d | ||
|
|
5c00f3d11a | ||
|
|
1be1f6fc8c | ||
|
|
ed0b112a00 | ||
|
|
cb4217319d | ||
|
|
5ac41ec1aa | ||
|
|
9c6f87804a | ||
|
|
80bae7db76 | ||
|
|
cd7365d47e | ||
|
|
a793f17dfb | ||
|
|
cbb2c8f35d | ||
|
|
6481ef7ae9 | ||
|
|
d6cf6773e5 | ||
|
|
eadb17bd44 | ||
|
|
cc80306337 | ||
|
|
e1b05612a7 | ||
|
|
d9034eff3f | ||
|
|
94f5c37667 | ||
|
|
c9b10026e0 | ||
|
|
d6937433da | ||
|
|
6a16672ab1 | ||
|
|
daa8dca053 | ||
|
|
21349a6e06 | ||
|
|
70dff0b78e | ||
|
|
7d88f9b211 | ||
|
|
7a58c776f9 | ||
|
|
4f3051671e | ||
|
|
ad4514a7e3 | ||
|
|
f8f8e6c7ba | ||
|
|
f195432020 | ||
|
|
283c1015f2 | ||
|
|
8f41a2c8d8 | ||
|
|
6aacfe3cb0 | ||
|
|
477f58cf03 | ||
|
|
9d2ef5822d | ||
|
|
8a56aab0ce | ||
|
|
5e93ea947d | ||
|
|
8539fe91ea | ||
|
|
4e81c60370 | ||
|
|
5aaa37ee02 | ||
|
|
0aa1284898 | ||
|
|
d7e6cf51e9 | ||
|
|
ab8fe4f4a6 | ||
|
|
5c344a5a98 | ||
|
|
963becad7c | ||
|
|
5dd9547121 |
32
.github/workflows/main.yml
vendored
32
.github/workflows/main.yml
vendored
@@ -1266,8 +1266,20 @@ jobs:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake wget
|
||||
qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools
|
||||
libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev
|
||||
qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev
|
||||
libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation gettext-base
|
||||
qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5
|
||||
qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
cd /tmp &&
|
||||
git clone https://github.com/WiringPi/WiringPi.git &&
|
||||
cd WiringPi &&
|
||||
./build debian &&
|
||||
mv debian-template/wiringpi_3.10_armhf.deb . &&
|
||||
apt install -y ./wiringpi_3.10_armhf.deb &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
@@ -1325,8 +1337,20 @@ jobs:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake wget
|
||||
qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools
|
||||
libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev
|
||||
qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev
|
||||
libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation
|
||||
qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 gettext-base
|
||||
qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
cd /tmp &&
|
||||
git clone https://github.com/WiringPi/WiringPi.git &&
|
||||
cd WiringPi &&
|
||||
./build debian &&
|
||||
mv debian-template/wiringpi_3.10_arm64.deb . &&
|
||||
apt install -y ./wiringpi_3.10_arm64.deb &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
|
||||
89
src/ConsoleReader.cpp
Normal file
89
src/ConsoleReader.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
//#if defined(Q_OS_LINUX)
|
||||
#if 1
|
||||
#include "ConsoleReader.h"
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static struct termios oldSettings;
|
||||
static struct termios newSettings;
|
||||
|
||||
/* Initialize new terminal i/o settings */
|
||||
void initTermios(int echo) {
|
||||
tcgetattr(0, &oldSettings); /* grab old terminal i/o settings */
|
||||
newSettings = oldSettings; /* make new settings same as old settings */
|
||||
newSettings.c_lflag &= ~ICANON; /* disable buffered i/o */
|
||||
newSettings.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
|
||||
tcsetattr(0, TCSANOW, &newSettings); /* use these new terminal i/o settings now */
|
||||
}
|
||||
|
||||
/* Restore old terminal i/o settings */
|
||||
void resetTermios(void) { tcsetattr(0, TCSANOW, &oldSettings); }
|
||||
|
||||
/* Read 1 character without echo */
|
||||
char getch(void) { return getchar(); }
|
||||
|
||||
ConsoleReader::ConsoleReader(bluetooth *bt) {
|
||||
bluetoothManager = bt;
|
||||
initTermios(0);
|
||||
}
|
||||
|
||||
ConsoleReader::~ConsoleReader() { resetTermios(); }
|
||||
|
||||
void ConsoleReader::run() {
|
||||
forever {
|
||||
char key = getch();
|
||||
qDebug() << "key pressed" << key;
|
||||
if (key == 'q') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double speed = ((treadmill *)bluetoothManager->device())->currentSpeed().value();
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(speed + 0.5);
|
||||
}
|
||||
} else if (key == 'w') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double speed = ((treadmill *)bluetoothManager->device())->currentSpeed().value();
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(speed - 0.5);
|
||||
}
|
||||
} else if (key == 'a') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double inclination = ((treadmill *)bluetoothManager->device())->currentInclination().value();
|
||||
((treadmill *)bluetoothManager->device())->changeInclination(inclination + 0.5, inclination + 0.5);
|
||||
}
|
||||
} else if (key == 's') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double inclination = ((treadmill *)bluetoothManager->device())->currentInclination().value();
|
||||
((treadmill *)bluetoothManager->device())->changeInclination(inclination - 0.5, inclination - 0.5);
|
||||
}
|
||||
} else if (key == '1') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(5);
|
||||
}
|
||||
} else if (key == '2') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double inclination = ((treadmill *)bluetoothManager->device())->currentInclination().value();
|
||||
((treadmill *)bluetoothManager->device())->changeInclination(inclination + 0.5, inclination + 0.5);
|
||||
}
|
||||
} else if (key == '3') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(10);
|
||||
}
|
||||
} else if (key == '4') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double speed = ((treadmill *)bluetoothManager->device())->currentSpeed().value();
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(speed - 0.5);
|
||||
}
|
||||
} else if (key == '5') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double inclination = ((treadmill *)bluetoothManager->device())->currentInclination().value();
|
||||
((treadmill *)bluetoothManager->device())->changeInclination(inclination - 0.5, inclination - 0.5);
|
||||
}
|
||||
} else if (key == '6') {
|
||||
if (bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
double speed = ((treadmill *)bluetoothManager->device())->currentSpeed().value();
|
||||
((treadmill *)bluetoothManager->device())->changeSpeed(speed + 0.5);
|
||||
}
|
||||
}
|
||||
emit KeyPressed(key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
21
src/ConsoleReader.h
Normal file
21
src/ConsoleReader.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CONSOLEREADER_H
|
||||
#define CONSOLEREADER_H
|
||||
|
||||
#include "bluetooth.h"
|
||||
#include <QThread>
|
||||
|
||||
class ConsoleReader : public QThread {
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void KeyPressed(char ch);
|
||||
|
||||
public:
|
||||
ConsoleReader(bluetooth *bt);
|
||||
~ConsoleReader();
|
||||
void run();
|
||||
|
||||
private:
|
||||
bluetooth *bluetoothManager;
|
||||
};
|
||||
|
||||
#endif /* CONSOLEREADER_H */
|
||||
@@ -450,6 +450,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
powerSensorName.startsWith(QStringLiteral("Disabled")) || power_as_bike || power_as_treadmill;
|
||||
bool eliteRizerFound = eliteRizerName.startsWith(QStringLiteral("Disabled"));
|
||||
bool eliteSterzoSmartFound = eliteSterzoSmartName.startsWith(QStringLiteral("Disabled"));
|
||||
bool gpio_treadmill = true; // TO FIX!! settings.value(QStringLiteral("gpio_treadmill"), false).toBool();
|
||||
bool fake_bike =
|
||||
settings.value(QZSettings::applewatch_fakedevice, QZSettings::default_applewatch_fakedevice).toBool();
|
||||
bool fakedevice_elliptical =
|
||||
@@ -686,6 +687,20 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(fakeBike);
|
||||
} else if (gpio_treadmill && !gpioTreadmill) {
|
||||
discoveryAgent->stop();
|
||||
gpioTreadmill = new gpiotreadmill(noWriteResistance, noHeartService);
|
||||
emit deviceConnected(b);
|
||||
connect(gpioTreadmill, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
connect(gpioTreadmill, &gpiotreadmill::inclinationChanged, this, &bluetooth::inclinationChanged);
|
||||
// connect(cscBike, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
// connect(this, SIGNAL(searchingStop()), gpioTreadmill, SLOT(searchingStop())); //NOTE: Commented due
|
||||
// to #358
|
||||
if (!discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(gpioTreadmill);
|
||||
} else if (fakedevice_elliptical && !fakeElliptical) {
|
||||
this->stopDiscovery();
|
||||
fakeElliptical = new fakeelliptical(noWriteResistance, noHeartService, false);
|
||||
@@ -3347,6 +3362,11 @@ void bluetooth::restart() {
|
||||
delete fakeBike;
|
||||
fakeBike = nullptr;
|
||||
}
|
||||
if (gpioTreadmill) {
|
||||
|
||||
delete gpioTreadmill;
|
||||
gpioTreadmill = nullptr;
|
||||
}
|
||||
if (fakeElliptical) {
|
||||
|
||||
delete fakeElliptical;
|
||||
@@ -3816,6 +3836,8 @@ bluetoothdevice *bluetooth::device() {
|
||||
return powerTreadmill;
|
||||
} else if (fakeBike) {
|
||||
return fakeBike;
|
||||
} else if (gpioTreadmill) {
|
||||
return gpioTreadmill;
|
||||
} else if (fakeElliptical) {
|
||||
return fakeElliptical;
|
||||
} else if (fakeRower) {
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "devices/discoveryoptions.h"
|
||||
#include "qzsettings.h"
|
||||
|
||||
#include "gpiotreadmill.h"
|
||||
|
||||
#include "devices/activiotreadmill/activiotreadmill.h"
|
||||
#include "devices/speraxtreadmill/speraxtreadmill.h"
|
||||
#include "devices/antbike/antbike.h"
|
||||
@@ -223,6 +225,7 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
nordictrackifitadbbike *nordictrackifitadbBike = nullptr;
|
||||
nordictrackifitadbelliptical *nordictrackifitadbElliptical = nullptr;
|
||||
octaneelliptical *octaneElliptical = nullptr;
|
||||
gpiotreadmill *gpioTreadmill = nullptr;
|
||||
octanetreadmill *octaneTreadmill = nullptr;
|
||||
pelotonbike *pelotonBike = nullptr;
|
||||
proformrower *proformRower = nullptr;
|
||||
|
||||
316
src/gpiotreadmill.cpp
Normal file
316
src/gpiotreadmill.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
#include "gpiotreadmill.h"
|
||||
#include "ios/lockscreen.h"
|
||||
#include "keepawakehelper.h"
|
||||
#include "virtualdevices/virtualtreadmill.h"
|
||||
#include <QBluetoothLocalDevice>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QMetaEnum>
|
||||
#include <QSettings>
|
||||
#include <chrono>
|
||||
#define Q_OS_RASPI 1
|
||||
#ifdef Q_OS_RASPI
|
||||
#include <wiringPi.h>
|
||||
#else
|
||||
#define OUTPUT 1
|
||||
void digitalWrite(int pin, int state) {
|
||||
qDebug() << QStringLiteral("switch pin ") + QString::number(pin) + QStringLiteral(" to ") + QString::number(state);
|
||||
}
|
||||
|
||||
void pinMode(int pin, int state) {
|
||||
qDebug() << QStringLiteral("init pin ") + QString::number(pin) + QStringLiteral(" to ") + QString::number(state);
|
||||
}
|
||||
|
||||
int wiringPiSetup() {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
||||
gpioWorkerThread::gpioWorkerThread(QObject *parent, QString name, uint8_t pinUp, uint8_t pinDown, double step, double currentValue, QSemaphore *semaphore): QThread(parent),
|
||||
name{name}, pinUp{pinUp}, pinDown{pinDown}, step{step}, currentValue{currentValue}, semaphore{semaphore}
|
||||
{
|
||||
pinMode(pinUp, OUTPUT);
|
||||
pinMode(pinDown, OUTPUT);
|
||||
digitalWrite(pinUp, 0);
|
||||
digitalWrite(pinDown, 0);
|
||||
}
|
||||
|
||||
void gpioWorkerThread::setRequestValue(double request)
|
||||
{
|
||||
this->requestValue = request;
|
||||
}
|
||||
|
||||
void gpioWorkerThread::run() {
|
||||
if (requestValue > currentValue) {
|
||||
while (requestValue > currentValue) {
|
||||
qDebug() << QStringLiteral("increasing ") + name + " from " + QString::number(currentValue) + " to " + QString::number(requestValue);
|
||||
semaphore->acquire();
|
||||
digitalWrite(pinUp, 1);
|
||||
QThread::msleep(GPIO_KEEP_MS);
|
||||
digitalWrite(pinUp, 0);
|
||||
QThread::msleep(GPIO_REBOUND_MS);
|
||||
semaphore->release();
|
||||
currentValue += step;
|
||||
if(QThread::currentThread()->isInterruptionRequested()) {
|
||||
qDebug() << "Interrupting set " + name;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (requestValue < currentValue) {
|
||||
qDebug() << QStringLiteral("decreasing ") + name + " from " + QString::number(currentValue) + " to " + QString::number(requestValue);
|
||||
semaphore->acquire();
|
||||
digitalWrite(pinDown, 1);
|
||||
QThread::msleep(GPIO_KEEP_MS);
|
||||
digitalWrite(pinDown, 0);
|
||||
QThread::msleep(GPIO_REBOUND_MS);
|
||||
semaphore->release();
|
||||
currentValue -= step;
|
||||
if(QThread::currentThread()->isInterruptionRequested()) {
|
||||
qDebug() << "Interrupting set " + name;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpiotreadmill::gpiotreadmill(uint32_t pollDeviceTime, bool noConsole, bool noHeartService, double forceInitSpeed,
|
||||
double forceInitInclination) {
|
||||
m_watt.setType(metric::METRIC_WATT);
|
||||
Speed.setType(metric::METRIC_SPEED);
|
||||
this->noConsole = noConsole;
|
||||
this->noHeartService = noHeartService;
|
||||
|
||||
if (wiringPiSetup() == -1) {
|
||||
qDebug() << QStringLiteral("wiringPiSetup ERROR!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pinMode(OUTPUT_START, OUTPUT);
|
||||
pinMode(OUTPUT_STOP, OUTPUT);
|
||||
digitalWrite(OUTPUT_START, 0);
|
||||
digitalWrite(OUTPUT_STOP, 0);
|
||||
|
||||
if (forceInitSpeed > 0) {
|
||||
lastSpeed = forceInitSpeed;
|
||||
}
|
||||
|
||||
if (forceInitInclination > 0) {
|
||||
lastInclination = forceInitInclination;
|
||||
}
|
||||
|
||||
semaphore = new QSemaphore(1);
|
||||
speedThread = new gpioWorkerThread(this, "speed", OUTPUT_SPEED_UP, OUTPUT_SPEED_DOWN, SPEED_STEP, forceInitSpeed, semaphore);
|
||||
inclineThread = new gpioWorkerThread(this, "incline", OUTPUT_INCLINE_UP, OUTPUT_INCLINE_DOWN, INCLINATION_STEP, forceInitInclination, semaphore);
|
||||
|
||||
refresh = new QTimer(this);
|
||||
initDone = false;
|
||||
connect(refresh, &QTimer::timeout, this, &gpiotreadmill::update);
|
||||
refresh->start(pollDeviceTime);
|
||||
}
|
||||
|
||||
gpiotreadmill::~gpiotreadmill() {
|
||||
speedThread->requestInterruption();
|
||||
speedThread->quit();
|
||||
speedThread->wait();
|
||||
delete speedThread;
|
||||
inclineThread->requestInterruption();
|
||||
inclineThread->quit();
|
||||
inclineThread->wait();
|
||||
delete inclineThread;
|
||||
delete semaphore;
|
||||
}
|
||||
|
||||
void gpiotreadmill::changeInclinationRequested(double grade, double percentage) {
|
||||
if (percentage < 0)
|
||||
percentage = 0;
|
||||
changeInclination(grade, percentage);
|
||||
}
|
||||
|
||||
void gpiotreadmill::forceSpeed(double requestSpeed) {
|
||||
qDebug() << QStringLiteral("gpiotreadmill.cpp: request set speed ") + QString::number(Speed.value()) + QStringLiteral(" to ") + QString::number(requestSpeed);
|
||||
if (speedThread->isRunning())
|
||||
{
|
||||
speedThread->requestInterruption();
|
||||
speedThread->quit();
|
||||
speedThread->wait();
|
||||
|
||||
}
|
||||
speedThread->setRequestValue(requestSpeed);
|
||||
speedThread->start();
|
||||
|
||||
Speed = requestSpeed; /* we are on the way to the requested speed */
|
||||
}
|
||||
|
||||
void gpiotreadmill::forceIncline(double requestIncline) {
|
||||
qDebug() << QStringLiteral("gpiotreadmill.cpp: request set Incline ") + QString::number(Inclination.value()) + QStringLiteral(" to ") + QString::number(requestIncline);
|
||||
|
||||
if (inclineThread->isRunning())
|
||||
{
|
||||
inclineThread->requestInterruption();
|
||||
inclineThread->quit();
|
||||
inclineThread->wait();
|
||||
|
||||
}
|
||||
inclineThread->setRequestValue(requestIncline);
|
||||
inclineThread->start();
|
||||
|
||||
Inclination = requestIncline; /* we are on the way to the requested incline */
|
||||
}
|
||||
|
||||
void gpiotreadmill::update() {
|
||||
|
||||
QSettings settings;
|
||||
// ******************************************* virtual treadmill init *************************************
|
||||
if (!firstInit && !virtualTreadMill && !virtualBike) {
|
||||
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
|
||||
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
|
||||
|
||||
emit connectedAndDiscovered();
|
||||
|
||||
if (virtual_device_enabled) {
|
||||
if (!virtual_device_force_bike) {
|
||||
debug("creating virtual treadmill interface...");
|
||||
virtualTreadMill = new virtualtreadmill(this, noHeartService);
|
||||
connect(virtualTreadMill, &virtualtreadmill::debug, this, &gpiotreadmill::debug);
|
||||
connect(virtualTreadMill, &virtualtreadmill::changeInclination, this,
|
||||
&gpiotreadmill::changeInclinationRequested);
|
||||
} else {
|
||||
debug("creating virtual bike interface...");
|
||||
virtualBike = new virtualbike(this);
|
||||
connect(virtualBike, &virtualbike::changeInclination, this, &gpiotreadmill::changeInclinationRequested);
|
||||
}
|
||||
firstInit = 1;
|
||||
}
|
||||
}
|
||||
// ********************************************************************************************************
|
||||
|
||||
// debug("Domyos Treadmill RSSI " + QString::number(bluetoothDevice.rssi()));
|
||||
|
||||
double heart = 0;
|
||||
QString heartRateBeltName =
|
||||
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (settings.value("ant_heart", false).toBool())
|
||||
Heart = (uint8_t)KeepAwakeHelper::heart();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
|
||||
|
||||
if (heart == 0) {
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
lockscreen h;
|
||||
long appleWatchHeartRate = h.heartRate();
|
||||
h.setKcal(KCal.value());
|
||||
h.setDistance(Distance.value());
|
||||
Heart = appleWatchHeartRate;
|
||||
debug("Current Heart from Apple Watch: " + QString::number(appleWatchHeartRate));
|
||||
#endif
|
||||
#endif
|
||||
} else
|
||||
|
||||
Heart = heart;
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstCharacteristicChanged) {
|
||||
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
|
||||
KCal +=
|
||||
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
|
||||
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
|
||||
200.0) /
|
||||
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
|
||||
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
|
||||
// kg * 3.5) / 200 ) / 60
|
||||
Distance += ((Speed.value() / (double)3600.0) /
|
||||
((double)1000.0 / (double)(lastTimeCharacteristicChanged.msecsTo(QDateTime::currentDateTime()))));
|
||||
lastTimeCharacteristicChanged = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
emit debug(QStringLiteral("Current speed: ") + QString::number(Speed.value()));
|
||||
emit debug(QStringLiteral("Current incline: ") + QString::number(Inclination.value()));
|
||||
emit debug(QStringLiteral("Current heart: ") + QString::number(Heart.value()));
|
||||
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
|
||||
emit debug(QStringLiteral("Current KCal from the machine: ") + QString::number(KCal.value()));
|
||||
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
|
||||
emit debug(QStringLiteral("Current Distance Calculated: ") + QString::number(Distance.value()));
|
||||
|
||||
firstCharacteristicChanged = false;
|
||||
|
||||
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
|
||||
|
||||
// updating the treadmill console every second
|
||||
if (sec1Update++ >= (1000 / refresh->interval())) {
|
||||
}
|
||||
|
||||
// byte 3 - 4 = elapsed time
|
||||
// byte 17 = inclination
|
||||
{
|
||||
if (requestSpeed != -1) {
|
||||
if (requestSpeed != currentSpeed().value() && requestSpeed >= 0 && requestSpeed <= 22) {
|
||||
emit debug(QStringLiteral("writing speed ") + QString::number(requestSpeed));
|
||||
|
||||
forceSpeed(requestSpeed);
|
||||
}
|
||||
requestSpeed = -1;
|
||||
}
|
||||
if (requestInclination != -1) {
|
||||
// only 0.5 steps ara avaiable
|
||||
requestInclination = qRound(requestInclination * 2.0) / 2.0;
|
||||
if (requestInclination != currentInclination().value() && requestInclination >= 0 &&
|
||||
requestInclination <= 15) {
|
||||
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
|
||||
|
||||
forceIncline(requestInclination);
|
||||
}
|
||||
requestInclination = -1;
|
||||
}
|
||||
if (requestStart != -1) {
|
||||
emit debug(QStringLiteral("starting..."));
|
||||
if (lastSpeed == 0.0) {
|
||||
|
||||
lastSpeed = 0.5;
|
||||
}
|
||||
digitalWrite(OUTPUT_START, 1);
|
||||
QThread::msleep(GPIO_KEEP_MS);
|
||||
digitalWrite(OUTPUT_START, 0);
|
||||
requestStart = -1;
|
||||
emit tapeStarted();
|
||||
}
|
||||
if (requestStop != -1) {
|
||||
emit debug(QStringLiteral("stopping..."));
|
||||
digitalWrite(OUTPUT_STOP, 1);
|
||||
QThread::msleep(GPIO_KEEP_MS);
|
||||
digitalWrite(OUTPUT_STOP, 0);
|
||||
requestStop = -1;
|
||||
}
|
||||
if (requestFanSpeed != -1) {
|
||||
emit debug(QStringLiteral("changing fan speed..."));
|
||||
|
||||
requestFanSpeed = -1;
|
||||
}
|
||||
if (requestIncreaseFan != -1) {
|
||||
emit debug(QStringLiteral("increasing fan speed..."));
|
||||
|
||||
requestIncreaseFan = -1;
|
||||
} else if (requestDecreaseFan != -1) {
|
||||
emit debug(QStringLiteral("decreasing fan speed..."));
|
||||
|
||||
requestDecreaseFan = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gpiotreadmill::connected() { return true; }
|
||||
|
||||
void *gpiotreadmill::VirtualTreadMill() { return virtualTreadMill; }
|
||||
|
||||
void *gpiotreadmill::VirtualDevice() { return VirtualTreadMill(); }
|
||||
|
||||
void gpiotreadmill::searchingStop() { searchStopped = true; }
|
||||
127
src/gpiotreadmill.h
Normal file
127
src/gpiotreadmill.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef GPIOTREADMILL_H
|
||||
#define GPIOTREADMILL_H
|
||||
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/qlowenergyadvertisingdata.h>
|
||||
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristic.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristicdata.h>
|
||||
|
||||
#include <QtBluetooth/qlowenergycontroller.h>
|
||||
#include <QtBluetooth/qlowenergydescriptordata.h>
|
||||
#include <QtBluetooth/qlowenergyservice.h>
|
||||
#include <QtBluetooth/qlowenergyservicedata.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#else
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#endif
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
|
||||
#include "devices/treadmill.h"
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include "virtualdevices/virtualtreadmill.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
class gpioWorkerThread : public QThread
|
||||
{
|
||||
public:
|
||||
explicit gpioWorkerThread(QObject *parent, QString name = "", uint8_t pinUp = 0, uint8_t pinDown = 0, double step = 0.0, double currentValue = 0.0, QSemaphore *semaphore = nullptr);
|
||||
void run();
|
||||
void setRequestValue(double request);
|
||||
private:
|
||||
QString name;
|
||||
double requestValue;
|
||||
double currentValue;
|
||||
uint8_t pinUp;
|
||||
uint8_t pinDown;
|
||||
double step;
|
||||
const uint16_t GPIO_KEEP_MS = 275;
|
||||
const uint16_t GPIO_REBOUND_MS = 175;
|
||||
QSemaphore *semaphore;
|
||||
};
|
||||
|
||||
|
||||
class gpiotreadmill : public treadmill {
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
gpiotreadmill(uint32_t poolDeviceTime = 200, bool noConsole = false, bool noHeartService = false,
|
||||
double forceInitSpeed = 1.0, double forceInitInclination = 0.0);
|
||||
~gpiotreadmill();
|
||||
bool connected();
|
||||
|
||||
void *VirtualTreadMill();
|
||||
void *VirtualDevice();
|
||||
|
||||
private:
|
||||
bool noConsole = false;
|
||||
bool noHeartService = false;
|
||||
uint32_t pollDeviceTime = 200;
|
||||
bool searchStopped = false;
|
||||
uint8_t sec1Update = 0;
|
||||
uint8_t firstInit = 0;
|
||||
QDateTime lastTimeCharacteristicChanged;
|
||||
bool firstCharacteristicChanged = true;
|
||||
|
||||
QTimer *refresh;
|
||||
virtualtreadmill *virtualTreadMill = nullptr;
|
||||
virtualbike *virtualBike = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
const uint8_t OUTPUT_SPEED_UP = 0;
|
||||
const uint8_t OUTPUT_SPEED_DOWN = 1;
|
||||
const uint8_t OUTPUT_INCLINE_UP = 2;
|
||||
const uint8_t OUTPUT_INCLINE_DOWN = 3;
|
||||
const uint8_t OUTPUT_START = 23;
|
||||
const uint8_t OUTPUT_STOP = 25;
|
||||
|
||||
const uint16_t GPIO_KEEP_MS = 275;
|
||||
//const uint16_t GPIO_REBOUND_MS = 200;
|
||||
|
||||
const double SPEED_STEP = 0.1;
|
||||
const double INCLINATION_STEP = 0.5;
|
||||
|
||||
void forceSpeed(double requestSpeed);
|
||||
void forceIncline(double requestIncline);
|
||||
gpioWorkerThread* speedThread;
|
||||
gpioWorkerThread* inclineThread;
|
||||
QSemaphore *semaphore; // my treadmill don't like it if the buttons will be pressed simultanly
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
void speedChanged(double speed);
|
||||
void packetReceived();
|
||||
|
||||
public slots:
|
||||
void searchingStop();
|
||||
|
||||
private slots:
|
||||
|
||||
void changeInclinationRequested(double grade, double percentage);
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif // GPIOTREADMILL_H
|
||||
10
src/main.cpp
10
src/main.cpp
@@ -4,6 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#ifdef Q_OS_LINUX
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include "ConsoleReader.h"
|
||||
#include <unistd.h> // getuid
|
||||
#include "EventHandler.h"
|
||||
#endif
|
||||
@@ -73,6 +74,7 @@ bool run_cadence_sensor = false;
|
||||
bool horizon_treadmill_7_8 = false;
|
||||
bool horizon_treadmill_force_ftms = false;
|
||||
bool nordictrack_10_treadmill = false;
|
||||
bool gpiotreadmill = false;
|
||||
bool proform_performance_300i_treadmill = false;
|
||||
bool reebok_fr30_treadmill = false;
|
||||
bool zwift_play = false;
|
||||
@@ -292,6 +294,8 @@ QCoreApplication *createApplication(int &argc, char *argv[]) {
|
||||
horizon_treadmill_force_ftms = true;
|
||||
if (!qstrcmp(argv[i], "-nordictrack-10-treadmill"))
|
||||
nordictrack_10_treadmill = true;
|
||||
if (!qstrcmp(argv[i], "-gpiotreadmill"))
|
||||
gpiotreadmill = true;
|
||||
if (!qstrcmp(argv[i], "-proform-perf-300i-treadmill"))
|
||||
proform_performance_300i_treadmill = true;
|
||||
if (!qstrcmp(argv[i], "-reebok_fr30_treadmill"))
|
||||
@@ -572,6 +576,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
else {
|
||||
settings.setValue(QStringLiteral("gpio_treadmill"), gpiotreadmill);
|
||||
settings.setValue(QZSettings::miles_unit, miles);
|
||||
settings.setValue(QZSettings::bluetooth_no_reconnection, bluetooth_no_reconnection);
|
||||
settings.setValue(QZSettings::bluetooth_relaxed, bluetooth_relaxed);
|
||||
@@ -848,6 +853,11 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
ConsoleReader *consoleReader = new ConsoleReader(&bl);
|
||||
consoleReader->start();
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
if (qobject_cast<QApplication *>(app.data())) {
|
||||
// start GUI version...
|
||||
|
||||
@@ -2,6 +2,7 @@ include(../defaults.pri)
|
||||
QT += bluetooth widgets xml positioning quick networkauth websockets texttospeech location multimedia
|
||||
QTPLUGIN += qavfmediaplayer
|
||||
QT+= charts core-private
|
||||
LIBS += -lwiringPi
|
||||
|
||||
qtHaveModule(httpserver) {
|
||||
QT += httpserver
|
||||
@@ -156,6 +157,8 @@ characteristics/characteristicnotifier2a5b.cpp \
|
||||
characteristics/characteristicnotifier2acc.cpp \
|
||||
characteristics/characteristicnotifier2acd.cpp \
|
||||
characteristics/characteristicnotifier2ad9.cpp \
|
||||
ConsoleReader.cpp \
|
||||
gpiotreadmill.cpp \
|
||||
characteristics/characteristicwriteprocessor.cpp \
|
||||
characteristics/characteristicwriteprocessore005.cpp \
|
||||
devices/computrainerbike/computrainerbike.cpp \
|
||||
@@ -453,6 +456,8 @@ characteristics/characteristicnotifier2ad9.h \
|
||||
characteristics/characteristicwriteprocessore005.h \
|
||||
devices/computrainerbike/computrainerbike.h \
|
||||
definitions.h \
|
||||
ConsoleReader.h \
|
||||
gpiotreadmill.h \
|
||||
devices/fakeelliptical/fakeelliptical.h \
|
||||
devices/faketreadmill/faketreadmill.h \
|
||||
devices/lifefitnesstreadmill/lifefitnesstreadmill.h \
|
||||
|
||||
@@ -33,6 +33,8 @@ win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../src/release/ -lqdom
|
||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../src/debug/ -lqdomyos-zwift
|
||||
else:unix: LIBS += -L$$OUT_PWD/../src/ -lqdomyos-zwift
|
||||
|
||||
LIBS += -lwiringPi
|
||||
|
||||
INCLUDEPATH += $$PWD/../src $$PWD/../src/devices
|
||||
DEPENDPATH += $$PWD/../src $$PWD/../src/devices
|
||||
|
||||
|
||||
Reference in New Issue
Block a user