Skip to content

Commit

Permalink
Support Nordictrack S25 treadmill #1933
Browse files Browse the repository at this point in the history
  • Loading branch information
cagnulein committed Jan 3, 2024
1 parent 4ba070e commit 9065ef0
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 8 deletions.
147 changes: 142 additions & 5 deletions src/proformtreadmill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ void proformtreadmill::forceIncline(double incline) {
settings.value(QZSettings::proform_treadmill_se, QZSettings::default_proform_treadmill_se).toBool();
bool norditrack_s25i_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25i_treadmill).toBool();
bool norditrack_s25_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25_treadmill).toBool();
bool proform_treadmill_z1300i =
settings.value(QZSettings::proform_treadmill_z1300i, QZSettings::default_proform_treadmill_z1300i).toBool();
bool nordictrack_s20_treadmill = settings.value(QZSettings::nordictrack_s20_treadmill,
Expand All @@ -97,7 +99,7 @@ void proformtreadmill::forceIncline(double incline) {

if (norditrack_s25i_treadmill) {
write[14] = write[11] + write[12] + 0x11;
} else if (proform_treadmill_8_0 || proform_treadmill_9_0 || proform_treadmill_se || proform_treadmill_z1300i || proform_treadmill_l6_0s) {
} else if (proform_treadmill_8_0 || proform_treadmill_9_0 || proform_treadmill_se || proform_treadmill_z1300i || proform_treadmill_l6_0s || norditrack_s25_treadmill) {
write[14] = write[11] + write[12] + 0x12;
} else if (!nordictrack_t65s_treadmill && !nordictrack_s30_treadmill && !nordictrack_s20_treadmill && !nordictrack_t65s_83_treadmill) {
for (uint8_t i = 0; i < 7; i++) {
Expand Down Expand Up @@ -132,6 +134,8 @@ void proformtreadmill::forceSpeed(double speed) {
.toBool();
bool norditrack_s25i_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25i_treadmill).toBool();
bool norditrack_s25_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25_treadmill).toBool();
bool proform_treadmill_z1300i =
settings.value(QZSettings::proform_treadmill_z1300i, QZSettings::default_proform_treadmill_z1300i).toBool();
bool nordictrack_s20_treadmill = settings.value(QZSettings::nordictrack_s20_treadmill,
Expand All @@ -148,7 +152,7 @@ void proformtreadmill::forceSpeed(double speed) {
if (norditrack_s25i_treadmill) {
write[14] = write[11] + write[12] + 0x11;
} else if (proform_treadmill_8_0 || proform_treadmill_9_0 || proform_treadmill_se || proform_treadmill_cadence_lt ||
proform_treadmill_z1300i || proform_treadmill_l6_0s) {
proform_treadmill_z1300i || proform_treadmill_l6_0s || norditrack_s25_treadmill) {
write[14] = write[11] + write[12] + 0x11;
} else if (!nordictrack_t65s_treadmill && !nordictrack_s30_treadmill && !nordictrack_s20_treadmill && !nordictrack_t65s_83_treadmill) {
for (uint8_t i = 0; i < 7; i++) {
Expand Down Expand Up @@ -205,6 +209,9 @@ void proformtreadmill::update() {
bool norditrack_s25i_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25i_treadmill)
.toBool();
bool norditrack_s25_treadmill =
settings.value(QZSettings::norditrack_s25_treadmill, QZSettings::default_norditrack_s25_treadmill)
.toBool();
bool nordictrack_incline_trainer_x7i =
settings
.value(QZSettings::nordictrack_incline_trainer_x7i, QZSettings::default_nordictrack_incline_trainer_x7i)
Expand Down Expand Up @@ -1268,6 +1275,68 @@ void proformtreadmill::update() {
if (counterPoll > 5) {
counterPoll = 0;
}
} else if (norditrack_s25_treadmill) {
uint8_t noOpData1[] = {0xfe, 0x02, 0x19, 0x03};
uint8_t noOpData2[] = {0x00, 0x12, 0x02, 0x04, 0x02, 0x15, 0x04, 0x15, 0x02, 0x00, 0x0f, 0x80, 0x0a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t noOpData3[] = {0xff, 0x07, 0x00, 0x00, 0x00, 0x85, 0x00, 0x10, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t noOpData4[] = {0xfe, 0x02, 0x14, 0x03};
uint8_t noOpData5[] = {0x00, 0x12, 0x02, 0x04, 0x02, 0x10, 0x04, 0x10, 0x02, 0x00, 0x0a, 0x1b, 0x94, 0x30, 0x00, 0x00, 0x40, 0x50, 0x00, 0x80};
uint8_t noOpData6[] = {0xff, 0x02, 0x18, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

switch (counterPoll) {
case 0:
writeCharacteristic(noOpData1, sizeof(noOpData1), QStringLiteral("noOp"));
break;
case 1:
writeCharacteristic(noOpData2, sizeof(noOpData2), QStringLiteral("noOp"));
break;
case 2:
writeCharacteristic(noOpData3, sizeof(noOpData3), QStringLiteral("noOp"));
break;
case 3:
writeCharacteristic(noOpData4, sizeof(noOpData4), QStringLiteral("noOp"), false, true);
break;
case 4:
writeCharacteristic(noOpData5, sizeof(noOpData5), QStringLiteral("noOp"));
break;
case 5:
writeCharacteristic(noOpData6, sizeof(noOpData6), QStringLiteral("noOp"), false, true);
if (requestInclination != -100) {
if (requestInclination < 0)
requestInclination = 0;
if (requestInclination != currentInclination().value() && requestInclination >= 0 &&
requestInclination <= 15) {
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
forceIncline(requestInclination);
}
requestInclination = -100;
}
if (requestSpeed != -1) {
if (requestSpeed != currentSpeed().value() && requestSpeed >= 0 && requestSpeed <= 22) {
emit debug(QStringLiteral("writing speed ") + QString::number(requestSpeed));
forceSpeed(requestSpeed);
}
requestSpeed = -1;
}
if (requestStart != -1) {
emit debug(QStringLiteral("starting..."));

// btinit();

requestStart = -1;
emit tapeStarted();
}
if (requestStop != -1) {
emit debug(QStringLiteral("stopping..."));
// writeCharacteristic(initDataF0C800B8, sizeof(initDataF0C800B8), "stop tape");
requestStop = -1;
}
break;
}
counterPoll++;
if (counterPoll > 5) {
counterPoll = 0;
}
} else {
uint8_t noOpData1[] = {0xfe, 0x02, 0x19, 0x03};
uint8_t noOpData2[] = {0x00, 0x12, 0x02, 0x04, 0x02, 0x15, 0x07, 0x15, 0x02, 0x00,
Expand Down Expand Up @@ -1381,7 +1450,6 @@ void proformtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
settings.value(QZSettings::proform_treadmill_z1300i, QZSettings::default_proform_treadmill_z1300i).toBool();
bool nordictrack_s20_treadmill = settings.value(QZSettings::nordictrack_s20_treadmill,
QZSettings::default_nordictrack_s20_treadmill).toBool();
bool proform_treadmill_l6_0s = settings.value(QZSettings::proform_treadmill_l6_0s, QZSettings::default_proform_treadmill_l6_0s).toBool();

double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();

Expand Down Expand Up @@ -1483,6 +1551,8 @@ void proformtreadmill::btinit() {
.toBool();
bool norditrack_s25i_treadmill =
settings.value(QZSettings::norditrack_s25i_treadmill, QZSettings::default_norditrack_s25i_treadmill).toBool();
bool norditrack_s25_treadmill =
settings.value(QZSettings::norditrack_s25_treadmill, QZSettings::default_norditrack_s25_treadmill).toBool();
bool nordictrack_t65s_83_treadmill =
settings.value(QZSettings::nordictrack_t65s_83_treadmill, QZSettings::default_nordictrack_t65s_83_treadmill)
.toBool();
Expand Down Expand Up @@ -1878,6 +1948,73 @@ void proformtreadmill::btinit() {
QThread::msleep(sleepms);
writeCharacteristic(noOpData6, sizeof(noOpData6), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
} else if (norditrack_s25_treadmill) {
uint8_t initData1[] = {0xfe, 0x02, 0x08, 0x02};
uint8_t initData2[] = {0xff, 0x08, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x81, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData3[] = {0xfe, 0x02, 0x08, 0x02};
uint8_t initData4[] = {0xff, 0x08, 0x02, 0x04, 0x02, 0x04, 0x04, 0x04, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData5[] = {0xfe, 0x02, 0x08, 0x02};
uint8_t initData6[] = {0xff, 0x08, 0x02, 0x04, 0x02, 0x04, 0x04, 0x04, 0x88, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData7[] = {0xfe, 0x02, 0x0a, 0x02};
uint8_t initData8[] = {0xff, 0x0a, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x82, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9[] = {0xfe, 0x02, 0x0a, 0x02};
uint8_t initData10[] = {0xff, 0x0a, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x84, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData11[] = {0xfe, 0x02, 0x08, 0x02};
uint8_t initData12[] = {0xff, 0x08, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x95, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData13[] = {0xfe, 0x02, 0x2c, 0x04};
uint8_t initData14[] = {0x00, 0x12, 0x02, 0x04, 0x02, 0x28, 0x04, 0x28, 0x90, 0x07, 0x01, 0x38, 0xac, 0x12, 0x8e, 0xfc, 0x78, 0xee, 0x6a, 0xd0};
uint8_t initData15[] = {0x01, 0x12, 0x54, 0xda, 0x56, 0xd4, 0x70, 0xf6, 0x62, 0xe8, 0x9c, 0x02, 0xbe, 0x2c, 0xc8, 0x7e, 0x1a, 0x80, 0x24, 0xca};
uint8_t initData16[] = {0xff, 0x08, 0x66, 0x04, 0xe0, 0x98, 0x02, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData17[] = {0xfe, 0x02, 0x19, 0x03};
uint8_t initData18[] = {0x00, 0x12, 0x02, 0x04, 0x02, 0x15, 0x04, 0x15, 0x02, 0x00, 0x0f, 0x00, 0x10, 0x00, 0xd8, 0x1c, 0x48, 0x00, 0x00, 0xe0};
uint8_t initData19[] = {0xff, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData2, sizeof(initData2), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData4, sizeof(initData4), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData5, sizeof(initData5), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData6, sizeof(initData6), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData5, sizeof(initData5), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData7, sizeof(initData7), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData8, sizeof(initData8), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData9, sizeof(initData9), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData10, sizeof(initData10), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData11, sizeof(initData11), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData12, sizeof(initData12), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData13, sizeof(initData13), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData14, sizeof(initData14), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData15, sizeof(initData15), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData16, sizeof(initData16), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData17, sizeof(initData17), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData18, sizeof(initData18), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
writeCharacteristic(initData19, sizeof(initData19), QStringLiteral("init"), false, false);
QThread::msleep(sleepms);
} else if (norditrack_s25i_treadmill) {
uint8_t initData1[] = {0xfe, 0x02, 0x08, 0x02};
uint8_t initData2[] = {0xff, 0x08, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x81, 0x87,
Expand Down Expand Up @@ -2921,14 +3058,14 @@ void proformtreadmill::stateChanged(QLowEnergyService::ServiceState state) {
.toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
emit debug("creating virtual treadmill interface...");
auto virtualTreadmill = new virtualtreadmill(this, noHeartService);
connect(virtualTreadmill, &virtualtreadmill::debug, this, &proformtreadmill::debug);
connect(virtualTreadmill, &virtualtreadmill::changeInclination, this,
&proformtreadmill::changeInclinationRequested);
this->setVirtualDevice(virtualTreadmill, VIRTUAL_DEVICE_MODE::PRIMARY);
} else {
debug("creating virtual bike interface...");
emit debug("creating virtual bike interface...");
auto virtualBike = new virtualbike(this);
connect(virtualBike, &virtualbike::changeInclination, this,
&proformtreadmill::changeInclinationRequested);
Expand Down
5 changes: 3 additions & 2 deletions src/qzsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,9 @@ const QString QZSettings::default_zwift_username = QStringLiteral("");
const QString QZSettings::zwift_password = QStringLiteral("zwift_password");
const QString QZSettings::default_zwift_password = QStringLiteral("");
const QString QZSettings::garmin_bluetooth_compatibility = QStringLiteral("garmin_bluetooth_compatibility");
const QString QZSettings::norditrack_s25_treadmill = QStringLiteral("norditrack_s25_treadmill");

const uint32_t allSettingsCount = 582;
const uint32_t allSettingsCount = 583;

QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
Expand Down Expand Up @@ -1285,7 +1286,7 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::zwift_username, QZSettings::default_zwift_username},
{QZSettings::zwift_password, QZSettings::default_zwift_password},
{QZSettings::garmin_bluetooth_compatibility, QZSettings::default_garmin_bluetooth_compatibility},

{QZSettings::norditrack_s25_treadmill, QZSettings::default_norditrack_s25_treadmill},
};

void QZSettings::qDebugAllSettings(bool showDefaults) {
Expand Down
2 changes: 2 additions & 0 deletions src/qzsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,8 @@ class QZSettings {
static const QString garmin_bluetooth_compatibility;
static constexpr bool default_garmin_bluetooth_compatibility = false;

static const QString norditrack_s25_treadmill;
static constexpr int default_norditrack_s25_treadmill = false;

/**
* @brief Write the QSettings values using the constants from this namespace.
Expand Down
17 changes: 16 additions & 1 deletion src/settings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ import QtQuick.Dialogs 1.0

// from version 2.16.31
property bool garmin_bluetooth_compatibility: false
property bool norditrack_s25_treadmill: false
}

function paddingZeros(text, limit) {
Expand Down Expand Up @@ -5531,7 +5532,21 @@ import QtQuick.Dialogs 1.0
textColor: Material.color(Material.Yellow)
color: Material.backgroundColor
accordionContent: ColumnLayout {
spacing: 0
spacing: 0
SwitchDelegate {
text: qsTr("Nordictrack S25")
spacing: 0
bottomPadding: 0
topPadding: 0
rightPadding: 0
leftPadding: 0
clip: false
checked: settings.norditrack_s25_treadmill
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
onClicked: { settings.norditrack_s25_treadmill = checked; window.settings_restart_to_apply = true; }
}

SwitchDelegate {
id: nordictrackS25iDelegate
text: qsTr("Nordictrack S25i")
Expand Down

0 comments on commit 9065ef0

Please sign in to comment.