mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
22 Commits
fixing_tra
...
nordictrac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ef84e5798 | ||
|
|
ff541371e1 | ||
|
|
29789ea659 | ||
|
|
234a10bcab | ||
|
|
938c5673e8 | ||
|
|
f9d62ee02c | ||
|
|
a85ed1910f | ||
|
|
1ca1a63aa3 | ||
|
|
34312be8f4 | ||
|
|
60729e6b52 | ||
|
|
e48af4e864 | ||
|
|
e990a27f65 | ||
|
|
4c88304be0 | ||
|
|
4c1d39d68c | ||
|
|
efc92f72b2 | ||
|
|
424e21a8fa | ||
|
|
51bc2ffb48 | ||
|
|
7cfd1ba93b | ||
|
|
c01e5ccb14 | ||
|
|
dd495f1a76 | ||
|
|
78e3a33596 | ||
|
|
77a9fc718a |
@@ -29,6 +29,9 @@
|
||||
<usb-device vendor-id="17A4" product-id="0002" />
|
||||
<usb-device vendor-id="17A4" product-id="0001" />
|
||||
|
||||
<!-- ICON Fitness, Nordictrack treadmill -->
|
||||
<usb-device vendor-id="213c" product-id="0002" />
|
||||
|
||||
<!-- CDC driver -->
|
||||
<usb-device vendor-id="9025" /> <!-- 0x2341 / ......: Arduino -->
|
||||
<usb-device vendor-id="5824" product-id="1155" /> <!-- 0x16C0 / 0x0483: Teensyduino -->
|
||||
|
||||
66
src/android/src/NordictrackUSBHID.java
Normal file
66
src/android/src/NordictrackUSBHID.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.util.Log;
|
||||
|
||||
public class NordictrackUSBHID {
|
||||
|
||||
static HidBridge hidBridge;
|
||||
static byte[] receiveData;
|
||||
static int lastReadLen = 0;
|
||||
|
||||
public static void open(Context context) {
|
||||
Log.d("QZ","NordictrackUSBHID open");
|
||||
hidBridge = new HidBridge(context, 0x0002, 0x213c);
|
||||
boolean ret = hidBridge.OpenDevice();
|
||||
Log.d("QZ","hidBridge.OpenDevice " + ret);
|
||||
hidBridge.StartReadingThread();
|
||||
Log.d("QZ","hidBridge.StartReadingThread");
|
||||
}
|
||||
|
||||
public static void write (byte[] bytes) {
|
||||
Log.d("QZ","NordictrackUSBHID writing " + new String(bytes, StandardCharsets.ISO_8859_1));
|
||||
hidBridge.WriteData(bytes);
|
||||
}
|
||||
|
||||
public static int readLen() {
|
||||
return lastReadLen;
|
||||
}
|
||||
|
||||
public static byte[] read() {
|
||||
if(hidBridge.IsThereAnyReceivedData()) {
|
||||
receiveData = hidBridge.GetReceivedDataFromQueue();
|
||||
lastReadLen = receiveData.length;
|
||||
Log.d("QZ","NordictrackUSBHID reading " + lastReadLen + new String(receiveData, StandardCharsets.ISO_8859_1));
|
||||
return receiveData;
|
||||
} else {
|
||||
Log.d("QZ","NordictrackUSBHID empty data");
|
||||
lastReadLen = 0;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,10 @@ bluetooth::bluetooth(bool logs, const QString &deviceName, bool noWriteResistanc
|
||||
connectedAndDiscovered();
|
||||
return;
|
||||
#endif
|
||||
nordictrackusbTreadmill = new nordictrackusbtreadmill(false, false, false);
|
||||
this->signalBluetoothDeviceConnected(nordictrackusbTreadmill);
|
||||
connectedAndDiscovered();
|
||||
return;
|
||||
|
||||
if (!startDiscovery) {
|
||||
this->discoveryAgent = nullptr;
|
||||
@@ -3171,6 +3175,11 @@ void bluetooth::restart() {
|
||||
delete proformWifiTreadmill;
|
||||
proformWifiTreadmill = nullptr;
|
||||
}
|
||||
if (nordictrackusbTreadmill) {
|
||||
|
||||
delete nordictrackusbTreadmill;
|
||||
nordictrackusbTreadmill = nullptr;
|
||||
}
|
||||
if (nordictrackifitadbTreadmill) {
|
||||
|
||||
delete nordictrackifitadbTreadmill;
|
||||
@@ -3649,6 +3658,8 @@ bluetoothdevice *bluetooth::device() {
|
||||
return proformTelnetBike;
|
||||
} else if (proformWifiTreadmill) {
|
||||
return proformWifiTreadmill;
|
||||
} else if (nordictrackusbTreadmill) {
|
||||
return nordictrackusbTreadmill;
|
||||
} else if (antBike) {
|
||||
return antBike;
|
||||
} else if (nordictrackifitadbTreadmill) {
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#include "devices/faketreadmill/faketreadmill.h"
|
||||
#include "devices/fitmetria_fanfit/fitmetria_fanfit.h"
|
||||
#include "devices/fitplusbike/fitplusbike.h"
|
||||
|
||||
#include "nordictrackusbtreadmill.h"
|
||||
#include "devices/fitshowtreadmill/fitshowtreadmill.h"
|
||||
#include "devices/flywheelbike/flywheelbike.h"
|
||||
#include "devices/ftmsbike/ftmsbike.h"
|
||||
@@ -210,6 +210,7 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
flywheelbike *flywheelBike = nullptr;
|
||||
nordictrackelliptical *nordictrackElliptical = nullptr;
|
||||
nordictrackifitadbtreadmill *nordictrackifitadbTreadmill = nullptr;
|
||||
nordictrackusbtreadmill *nordictrackusbTreadmill = nullptr;
|
||||
nordictrackifitadbbike *nordictrackifitadbBike = nullptr;
|
||||
nordictrackifitadbelliptical *nordictrackifitadbElliptical = nullptr;
|
||||
octaneelliptical *octaneElliptical = nullptr;
|
||||
|
||||
@@ -263,8 +263,8 @@ QCoreApplication *createApplication(int &argc, char *argv[]) {
|
||||
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
|
||||
|
||||
QSettings settings;
|
||||
static bool logdebug = settings.value(QZSettings::log_debug, QZSettings::default_log_debug).toBool();
|
||||
#if defined(Q_OS_LINUX) // Linux OS does not read settings file for now
|
||||
static bool logdebug = true; // settings.value(QZSettings::log_debug, QZSettings::default_log_debug).toBool();
|
||||
#if defined(Q_OS_LINUX) // Linux OS does not read settings file for now
|
||||
if ((logs == false && !forceQml) || (logdebug == false && forceQml))
|
||||
#else
|
||||
if (logdebug == false)
|
||||
|
||||
377
src/nordictrackusbtreadmill.cpp
Normal file
377
src/nordictrackusbtreadmill.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
#include "nordictrackusbtreadmill.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
nordictrackusbtreadmill::nordictrackusbtreadmill(bool noWriteResistance, bool noHeartService, bool noVirtualDevice) {
|
||||
m_watt.setType(metric::METRIC_WATT);
|
||||
Speed.setType(metric::METRIC_SPEED);
|
||||
refresh = new QTimer(this);
|
||||
this->noWriteResistance = noWriteResistance;
|
||||
this->noHeartService = noHeartService;
|
||||
this->noVirtualDevice = noVirtualDevice;
|
||||
initDone = false;
|
||||
connect(refresh, &QTimer::timeout, this, &nordictrackusbtreadmill::update);
|
||||
refresh->start(200ms);
|
||||
nordictrackusbtreadmillThread *t = new nordictrackusbtreadmillThread();
|
||||
t->start();
|
||||
}
|
||||
|
||||
nordictrackusbtreadmillThread::nordictrackusbtreadmillThread() {}
|
||||
|
||||
void nordictrackusbtreadmillThread::run() {
|
||||
QSettings settings;
|
||||
/*devicePort =
|
||||
settings.value(QZSettings::computrainer_serialport, QZSettings::default_computrainer_serialport).toString();*/
|
||||
|
||||
openPort();
|
||||
int countError = 0;
|
||||
while (1) {
|
||||
|
||||
uint8_t command[64];
|
||||
memset(command, 0xFF, sizeof(command));
|
||||
if(countError == 0)
|
||||
rawWrite(command, sizeof(command));
|
||||
static uint8_t rx[100];
|
||||
memset(rx, 0x00, sizeof(rx));
|
||||
rawRead(rx, 100);
|
||||
qDebug() << " << " << QByteArray::fromRawData((const char *)rx, 64).toHex(' ');
|
||||
QThread::msleep(50);
|
||||
if(countError++>10)
|
||||
countError = 0;
|
||||
}
|
||||
closePort();
|
||||
}
|
||||
|
||||
int nordictrackusbtreadmillThread::closePort() {
|
||||
#ifdef WIN32
|
||||
return (int)!CloseHandle(devicePort);
|
||||
#else
|
||||
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
|
||||
return close(devicePort);
|
||||
#endif
|
||||
}
|
||||
|
||||
int nordictrackusbtreadmillThread::openPort() {
|
||||
#ifdef Q_OS_ANDROID
|
||||
QAndroidJniObject::callStaticMethod<void>("org/cagnulen/qdomyoszwift/NordictrackUSBHID", "open",
|
||||
"(Landroid/content/Context;)V", QtAndroid::androidContext().object());
|
||||
#elif !defined(WIN32)
|
||||
|
||||
// LINUX AND MAC USES TERMIO / IOCTL / STDIO
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
int ldisc = TTYDISC;
|
||||
#else
|
||||
int ldisc = N_TTY; // LINUX
|
||||
#endif
|
||||
|
||||
if ((devicePort = open(deviceFilename.toLatin1(), O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1)
|
||||
return errno;
|
||||
|
||||
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
|
||||
|
||||
if (ioctl(devicePort, TIOCSETD, &ldisc) == -1)
|
||||
return errno;
|
||||
|
||||
// get current settings for the port
|
||||
tcgetattr(devicePort, &deviceSettings);
|
||||
|
||||
// set raw mode i.e. ignbrk, brkint, parmrk, istrip, inlcr, igncr, icrnl, ixon
|
||||
// noopost, cs8, noecho, noechonl, noicanon, noisig, noiexn
|
||||
cfmakeraw(&deviceSettings);
|
||||
cfsetspeed(&deviceSettings, B2400);
|
||||
|
||||
// further attributes
|
||||
deviceSettings.c_iflag &=
|
||||
~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ICANON | ISTRIP | IXON | IXOFF | IXANY);
|
||||
deviceSettings.c_iflag |= IGNPAR;
|
||||
deviceSettings.c_cflag &= (~CSIZE & ~CSTOPB);
|
||||
deviceSettings.c_oflag = 0;
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
deviceSettings.c_cflag &= (~CCTS_OFLOW & ~CRTS_IFLOW); // no hardware flow control
|
||||
deviceSettings.c_cflag |= (CS8 | CLOCAL | CREAD | HUPCL);
|
||||
#else
|
||||
deviceSettings.c_cflag &= (~CRTSCTS); // no hardware flow control
|
||||
deviceSettings.c_cflag |= (CS8 | CLOCAL | CREAD | HUPCL);
|
||||
#endif
|
||||
deviceSettings.c_lflag = 0;
|
||||
deviceSettings.c_cc[VSTART] = 0x11;
|
||||
deviceSettings.c_cc[VSTOP] = 0x13;
|
||||
deviceSettings.c_cc[VEOF] = 0x20;
|
||||
deviceSettings.c_cc[VMIN] = 0;
|
||||
deviceSettings.c_cc[VTIME] = 0;
|
||||
|
||||
// set those attributes
|
||||
if (tcsetattr(devicePort, TCSANOW, &deviceSettings) == -1)
|
||||
return errno;
|
||||
tcgetattr(devicePort, &deviceSettings);
|
||||
|
||||
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
|
||||
#else
|
||||
// WINDOWS USES SET/GETCOMMSTATE AND READ/WRITEFILE
|
||||
|
||||
COMMTIMEOUTS timeouts; // timeout settings on serial ports
|
||||
|
||||
// if deviceFilename references a port above COM9
|
||||
// then we need to open "\\.\COMX" not "COMX"
|
||||
QString portSpec;
|
||||
int portnum = deviceFilename.midRef(3).toString().toInt();
|
||||
if (portnum < 10)
|
||||
portSpec = deviceFilename;
|
||||
else
|
||||
portSpec = "\\\\.\\" + deviceFilename;
|
||||
wchar_t deviceFilenameW[32]; // \\.\COM32 needs 9 characters, 32 should be enough?
|
||||
MultiByteToWideChar(CP_ACP, 0, portSpec.toLatin1(), -1, (LPWSTR)deviceFilenameW, sizeof(deviceFilenameW));
|
||||
|
||||
// win32 commport API
|
||||
devicePort = CreateFile(deviceFilenameW, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (devicePort == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
if (GetCommState(devicePort, &deviceSettings) == false)
|
||||
return -1;
|
||||
|
||||
// so we've opened the comm port lets set it up for
|
||||
deviceSettings.BaudRate = CBR_2400;
|
||||
deviceSettings.fParity = NOPARITY;
|
||||
deviceSettings.ByteSize = 8;
|
||||
deviceSettings.StopBits = ONESTOPBIT;
|
||||
deviceSettings.XonChar = 11;
|
||||
deviceSettings.XoffChar = 13;
|
||||
deviceSettings.EofChar = 0x0;
|
||||
deviceSettings.ErrorChar = 0x0;
|
||||
deviceSettings.EvtChar = 0x0;
|
||||
deviceSettings.fBinary = true;
|
||||
deviceSettings.fOutX = 0;
|
||||
deviceSettings.fInX = 0;
|
||||
deviceSettings.XonLim = 0;
|
||||
deviceSettings.XoffLim = 0;
|
||||
deviceSettings.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
deviceSettings.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
deviceSettings.fOutxCtsFlow = FALSE; // TRUE;
|
||||
|
||||
if (SetCommState(devicePort, &deviceSettings) == false) {
|
||||
CloseHandle(devicePort);
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeouts.ReadIntervalTimeout = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = 1000;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 50;
|
||||
timeouts.WriteTotalTimeoutConstant = 2000;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(devicePort, &timeouts);
|
||||
|
||||
#endif
|
||||
|
||||
// success
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nordictrackusbtreadmillThread::rawWrite(uint8_t *bytes, int size) // unix!!
|
||||
{
|
||||
qDebug() << size << QByteArray((const char *)bytes, size).toHex(' ');
|
||||
|
||||
int rc = 0;
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
|
||||
QAndroidJniEnvironment env;
|
||||
jbyteArray d = env->NewByteArray(size);
|
||||
jbyte *b = env->GetByteArrayElements(d, 0);
|
||||
for (int i = 0; i < size; i++)
|
||||
b[i] = bytes[i];
|
||||
env->SetByteArrayRegion(d, 0, size, b);
|
||||
QAndroidJniObject::callStaticMethod<void>("org/cagnulen/qdomyoszwift/NordictrackUSBHID", "write", "([B)V", d);
|
||||
#elif defined(WIN32)
|
||||
DWORD cBytes;
|
||||
rc = WriteFile(devicePort, bytes, size, &cBytes, NULL);
|
||||
if (!rc)
|
||||
return -1;
|
||||
return rc;
|
||||
|
||||
#else
|
||||
int ibytes;
|
||||
ioctl(devicePort, FIONREAD, &ibytes);
|
||||
|
||||
// timeouts are less critical for writing, since vols are low
|
||||
rc = write(devicePort, bytes, size);
|
||||
|
||||
// but it is good to avoid buffer overflow since the
|
||||
// computrainer microcontroller has almost no RAM
|
||||
if (rc != -1)
|
||||
tcdrain(devicePort); // wait till its gone.
|
||||
|
||||
ioctl(devicePort, FIONREAD, &ibytes);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nordictrackusbtreadmillThread::rawRead(uint8_t bytes[], int size) {
|
||||
int rc = 0;
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
int64_t start = QDateTime::currentMSecsSinceEpoch();
|
||||
jint len = 0;
|
||||
|
||||
do {
|
||||
QAndroidJniEnvironment env;
|
||||
QAndroidJniObject dd =
|
||||
QAndroidJniObject::callStaticObjectMethod("org/cagnulen/qdomyoszwift/NordictrackUSBHID", "read", "()[B");
|
||||
len =
|
||||
QAndroidJniObject::callStaticMethod<jint>("org/cagnulen/qdomyoszwift/NordictrackUSBHID", "readLen", "()I");
|
||||
if (len > 0) {
|
||||
jbyteArray d = dd.object<jbyteArray>();
|
||||
jbyte *b = env->GetByteArrayElements(d, 0);
|
||||
for (int i = 0; i < len; i++) {
|
||||
bytes[i] = b[i];
|
||||
}
|
||||
qDebug() << len << QByteArray((const char *)b, len).toHex(' ');
|
||||
}
|
||||
} while (len == 0 && start + 2000 > QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
return len;
|
||||
#elif defined(WIN32)
|
||||
Q_UNUSED(size);
|
||||
// Readfile deals with timeouts and readyread issues
|
||||
DWORD cBytes;
|
||||
rc = ReadFile(devicePort, bytes, 7, &cBytes, NULL);
|
||||
if (rc)
|
||||
return (int)cBytes;
|
||||
else
|
||||
return (-1);
|
||||
|
||||
#else
|
||||
|
||||
int timeout = 0, i = 0;
|
||||
uint8_t byte;
|
||||
|
||||
// read one byte at a time sleeping when no data ready
|
||||
// until we timeout waiting then return error
|
||||
for (i = 0; i < size; i++) {
|
||||
timeout = 0;
|
||||
rc = 0;
|
||||
while (rc == 0 && timeout < CT_READTIMEOUT) {
|
||||
rc = read(devicePort, &byte, 1);
|
||||
if (rc == -1)
|
||||
return -1; // error!
|
||||
else if (rc == 0) {
|
||||
msleep(50); // sleep for 1/20th of a second
|
||||
timeout += 50;
|
||||
} else {
|
||||
bytes[i] = byte;
|
||||
}
|
||||
}
|
||||
if (timeout >= CT_READTIMEOUT)
|
||||
return -1; // we timed out!
|
||||
}
|
||||
|
||||
qDebug() << i << QString::fromLocal8Bit((const char *)bytes, i);
|
||||
|
||||
return i;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void nordictrackusbtreadmill::update() {
|
||||
QSettings settings;
|
||||
QString heartRateBeltName =
|
||||
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
|
||||
|
||||
// update_metrics(false, watts());
|
||||
|
||||
Distance += ((Speed.value() / (double)3600.0) /
|
||||
((double)1000.0 / (double)(lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime()))));
|
||||
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
|
||||
|
||||
// ******************************************* virtual bike/rower init *************************************
|
||||
if (!firstStateChanged && !this->hasVirtualDevice()
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
&& !h
|
||||
#endif
|
||||
#endif
|
||||
) {
|
||||
QSettings settings;
|
||||
bool virtual_device_enabled =
|
||||
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
|
||||
bool virtual_device_rower =
|
||||
settings.value(QZSettings::virtual_device_rower, QZSettings::default_virtual_device_rower).toBool();
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
bool cadence =
|
||||
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && !virtual_device_rower) {
|
||||
qDebug() << "ios_peloton_workaround activated!";
|
||||
h = new lockscreen();
|
||||
h->virtualbike_ios();
|
||||
} else
|
||||
#endif
|
||||
#endif
|
||||
if (virtual_device_enabled) {
|
||||
if (!virtual_device_rower) {
|
||||
qDebug() << QStringLiteral("creating virtual bike interface...");
|
||||
auto virtualBike = new virtualbike(this, noWriteResistance, noHeartService);
|
||||
// connect(virtualBike,&virtualbike::debug ,this,&echelonrower::debug);
|
||||
this->setVirtualDevice(virtualBike, VIRTUAL_DEVICE_MODE::PRIMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!firstStateChanged)
|
||||
emit connectedAndDiscovered();
|
||||
firstStateChanged = 1;
|
||||
// ********************************************************************************************************
|
||||
|
||||
if (!noVirtualDevice) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
|
||||
Heart = (uint8_t)KeepAwakeHelper::heart();
|
||||
debug("Current Heart: " + QString::number(Heart.value()));
|
||||
}
|
||||
#endif
|
||||
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
|
||||
update_hr_from_external();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
bool cadence =
|
||||
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
if (Heart.value()) {
|
||||
static double lastKcal = 0;
|
||||
if (KCal.value() < 0) // if the user pressed stop, the KCAL resets the accumulator
|
||||
lastKcal = abs(KCal.value());
|
||||
KCal = metric::calculateKCalfromHR(Heart.average(), elapsed.value()) + lastKcal;
|
||||
}*/
|
||||
}
|
||||
|
||||
void nordictrackusbtreadmill::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
|
||||
const QByteArray &newValue) {
|
||||
QByteArray b = newValue;
|
||||
qDebug() << "routing FTMS packet to the bike from virtualbike" << characteristic.uuid() << newValue.toHex(' ');
|
||||
}
|
||||
|
||||
bool nordictrackusbtreadmill::connected() { return true; }
|
||||
|
||||
void nordictrackusbtreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit debug(QStringLiteral("Found new device: ") + device.name() + " (" + device.address().toString() + ')');
|
||||
}
|
||||
|
||||
void nordictrackusbtreadmill::newPacket(QByteArray p) {}
|
||||
154
src/nordictrackusbtreadmill.h
Normal file
154
src/nordictrackusbtreadmill.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#ifndef NORDICTRACKUSBTREADMILL_H
|
||||
#define NORDICTRACKUSBTREADMILL_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 <QString>
|
||||
|
||||
#include "treadmill.h"
|
||||
#include "virtualbike.h"
|
||||
#include "virtualtreadmill.h"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windef.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winbase.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h> // unix!!
|
||||
#include <unistd.h> // unix!!
|
||||
#ifndef N_TTY // for OpenBSD, this is a hack XXX
|
||||
#define N_TTY 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "keepawakehelper.h"
|
||||
#include <QAndroidJniObject>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
/* read timeouts in microseconds */
|
||||
#define CT_READTIMEOUT 1000
|
||||
#define CT_WRITETIMEOUT 2000
|
||||
|
||||
class nordictrackusbtreadmillThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit nordictrackusbtreadmillThread();
|
||||
|
||||
void run();
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
// Utility and BG Thread functions
|
||||
int openPort();
|
||||
int closePort();
|
||||
|
||||
// Mutex for controlling accessing private data
|
||||
QMutex pvars;
|
||||
|
||||
// device port
|
||||
QString deviceFilename;
|
||||
#ifdef WIN32
|
||||
HANDLE devicePort; // file descriptor for reading from com3
|
||||
DCB deviceSettings; // serial port settings baud rate et al
|
||||
#else
|
||||
int devicePort; // unix!!
|
||||
struct termios deviceSettings; // unix!!
|
||||
#endif
|
||||
// raw device utils
|
||||
int rawWrite(uint8_t *bytes, int size); // unix!!
|
||||
int rawRead(uint8_t *bytes, int size); // unix!!
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QList<jbyte> bufRX;
|
||||
bool cleanFrame = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
class nordictrackusbtreadmill : public treadmill {
|
||||
Q_OBJECT
|
||||
public:
|
||||
nordictrackusbtreadmill(bool noWriteResistance, bool noHeartService, bool noVirtualDevice);
|
||||
bool connected() override;
|
||||
|
||||
private:
|
||||
QTimer *refresh;
|
||||
|
||||
uint8_t sec1Update = 0;
|
||||
QByteArray lastPacket;
|
||||
QDateTime lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
|
||||
QDateTime lastGoodCadence = QDateTime::currentDateTime();
|
||||
uint8_t firstStateChanged = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
bool noWriteResistance = false;
|
||||
bool noHeartService = false;
|
||||
bool noVirtualDevice = false;
|
||||
|
||||
uint16_t oldLastCrankEventTime = 0;
|
||||
uint16_t oldCrankRevs = 0;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
|
||||
private slots:
|
||||
void update();
|
||||
void newPacket(QByteArray p);
|
||||
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
};
|
||||
|
||||
#endif // NORDICTRACKUSBTREADMILL_H
|
||||
@@ -80,6 +80,7 @@ SOURCES += \
|
||||
$$PWD/devices/cycleopsphantombike/cycleopsphantombike.cpp \
|
||||
$$PWD/devices/deeruntreadmill/deerruntreadmill.cpp \
|
||||
$$PWD/devices/focustreadmill/focustreadmill.cpp \
|
||||
$$PWD/nordictrackusbtreadmill.cpp \
|
||||
$$PWD/devices/jumprope.cpp \
|
||||
$$PWD/devices/kineticinroadbike/SmartControl.cpp \
|
||||
$$PWD/devices/kineticinroadbike/kineticinroadbike.cpp \
|
||||
@@ -346,6 +347,7 @@ HEADERS += \
|
||||
$$PWD/devices/sportstechelliptical/sportstechelliptical.h \
|
||||
$$PWD/devices/sramAXSController/sramAXSController.h \
|
||||
$$PWD/devices/technogymbike/technogymbike.h \
|
||||
$$PWD/nordictrackusbtreadmill.h \
|
||||
$$PWD/devices/trxappgateusbelliptical/trxappgateusbelliptical.h \
|
||||
$$PWD/devices/trxappgateusbrower/trxappgateusbrower.h \
|
||||
$$PWD/ergtable.h \
|
||||
@@ -831,6 +833,7 @@ DISTFILES += \
|
||||
$$PWD/android/libs/zaplibrary-debug.aar \
|
||||
$$PWD/android/res/xml/device_filter.xml \
|
||||
$$PWD/android/src/BleAdvertiser.java \
|
||||
$$PWD/android/src/NordictrackUSBHID.java \
|
||||
$$PWD/android/src/CSafeRowerUSBHID.java \
|
||||
$$PWD/android/src/ContentHelper.java \
|
||||
$$PWD/android/src/Garmin.java \
|
||||
@@ -839,6 +842,7 @@ DISTFILES += \
|
||||
$$PWD/android/src/LocationHelper.java \
|
||||
$$PWD/android/src/MediaButtonReceiver.java \
|
||||
$$PWD/android/src/MediaProjection.java \
|
||||
$$PWD/android/src/NordictrackUSBHID.java \
|
||||
$$PWD/android/src/NotificationUtils.java \
|
||||
$$PWD/android/src/ScreenCaptureService.java \
|
||||
$$PWD/android/src/Shortcuts.java \
|
||||
|
||||
Reference in New Issue
Block a user