Skip to content

Commit 5b3fa40

Browse files
committed
Parse cycling power cadence in CSC sensors
1 parent c1c2521 commit 5b3fa40

2 files changed

Lines changed: 78 additions & 54 deletions

File tree

src/devices/bluetooth.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3262,10 +3262,8 @@ void bluetooth::connectedAndDiscovered() {
32623262

32633263
connect(powerSensor, &stagesbike::debug, this, &bluetooth::debug);
32643264
connect(powerSensor, &bluetoothdevice::powerChanged, this->device(), &bluetoothdevice::powerSensor);
3265-
if (cscName.startsWith(QStringLiteral("Disabled"))) {
3266-
connect(powerSensor, &bluetoothdevice::cadenceChanged, this->device(),
3267-
&bluetoothdevice::cadenceSensor);
3268-
}
3265+
connect(powerSensor, &bluetoothdevice::cadenceChanged, this->device(),
3266+
&bluetoothdevice::cadenceSensor);
32693267
powerSensor->deviceDiscovered(b);
32703268
} else if (device() && device()->deviceType() == TREADMILL) {
32713269
powerSensorRun = new strydrunpowersensor(false, false, true);

src/devices/cscbike/cscbike.cpp

Lines changed: 76 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,6 @@ void cscbike::update() {
170170
QSettings settings;
171171
QString heartRateBeltName =
172172
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
173-
bool externalPowerSensorEnabled =
174-
!settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
175-
.toString()
176-
.startsWith(QStringLiteral("Disabled"));
177173

178174
if (!noVirtualDevice) {
179175
#ifdef Q_OS_ANDROID
@@ -187,24 +183,21 @@ void cscbike::update() {
187183
}
188184
}
189185

190-
if (!externalPowerSensorEnabled) {
191-
bool rogue_echo_bike =
192-
settings.value(QZSettings::rogue_echo_bike, QZSettings::default_rogue_echo_bike).toBool();
193-
194-
if (manualResistancePowerAdjustmentActive && jorotoBike) {
195-
m_watt = manualResistanceAdjustedWatts();
196-
} else if (manualResistancePowerAdjustmentActive && useCustomResistancePowerTable()) {
197-
m_watt = customResistanceAdjustedWatts(currentCadence().value(), manualResistanceTarget);
198-
} else if (rogue_echo_bike) {
199-
double rpm = currentCadence().value();
200-
m_watt = 0.000602337 * pow(rpm, 3.11762) + 32.6404;
186+
bool rogue_echo_bike = settings.value(QZSettings::rogue_echo_bike, QZSettings::default_rogue_echo_bike).toBool();
187+
188+
if (manualResistancePowerAdjustmentActive && jorotoBike) {
189+
m_watt = manualResistanceAdjustedWatts();
190+
} else if (manualResistancePowerAdjustmentActive && useCustomResistancePowerTable()) {
191+
m_watt = customResistanceAdjustedWatts(currentCadence().value(), manualResistanceTarget);
192+
} else if (rogue_echo_bike) {
193+
double rpm = currentCadence().value();
194+
m_watt = 0.000602337 * pow(rpm, 3.11762) + 32.6404;
195+
} else {
196+
// When cadence is zero, watts should be zero regardless of HR
197+
if (currentCadence().value() == 0) {
198+
m_watt = 0;
201199
} else {
202-
// When cadence is zero, watts should be zero regardless of HR
203-
if (currentCadence().value() == 0) {
204-
m_watt = 0;
205-
} else {
206-
m_watt = wattFromHR(false);
207-
}
200+
m_watt = wattFromHR(false);
208201
}
209202
}
210203
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
@@ -279,15 +272,6 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
279272
qDebug() << "characteristicChanged << " << characteristic.uuid() << newValue.toHex(' ') << newValue.length();
280273
Q_UNUSED(characteristic);
281274
QSettings settings;
282-
bool externalCadenceSensorEnabled =
283-
!settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
284-
.toString()
285-
.startsWith(QStringLiteral("Disabled"));
286-
bool externalPowerSensorEnabled =
287-
!settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
288-
.toString()
289-
.startsWith(QStringLiteral("Disabled"));
290-
bool useMachineCadence = !externalCadenceSensorEnabled && !externalPowerSensorEnabled;
291275
// QString heartRateBeltName = //unused QString
292276
// settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
293277

@@ -309,14 +293,47 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
309293
return;
310294
}
311295

312-
if (characteristic.uuid() != QBluetoothUuid((quint16)0x2A5B)) {
296+
bool cyclingPowerMeasurement = characteristic.uuid() == QBluetoothUuid::CyclingPowerMeasurement;
297+
if (characteristic.uuid() != QBluetoothUuid((quint16)0x2A5B) && !cyclingPowerMeasurement) {
313298
return;
314299
}
315300

316301
lastPacket = newValue;
317302

318-
bool CrankPresent = (newValue.at(0) & 0x02) == 0x02;
319-
bool WheelPresent = (newValue.at(0) & 0x01) == 0x01;
303+
bool CrankPresent = false;
304+
bool WheelPresent = false;
305+
if (cyclingPowerMeasurement) {
306+
if (newValue.length() < 4) {
307+
return;
308+
}
309+
310+
uint16_t flags = (((uint16_t)((uint8_t)newValue.at(1)) << 8) | (uint16_t)((uint8_t)newValue.at(0)));
311+
CrankPresent = (flags & 0x20) == 0x20;
312+
if (!CrankPresent) {
313+
return;
314+
}
315+
316+
uint8_t index = 4;
317+
if ((flags & 0x01) == 0x01) {
318+
index += 1; // pedal power balance
319+
}
320+
if ((flags & 0x04) == 0x04) {
321+
index += 2; // accumulated torque
322+
}
323+
if ((flags & 0x10) == 0x10) {
324+
index += 6; // wheel revolutions and wheel event time
325+
}
326+
if (newValue.length() < index + 4) {
327+
return;
328+
}
329+
330+
_CrankRevs = (((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
331+
_LastCrankEventTime =
332+
(((uint16_t)((uint8_t)newValue.at(index + 3)) << 8) | (uint16_t)((uint8_t)newValue.at(index + 2)));
333+
} else {
334+
CrankPresent = (newValue.at(0) & 0x02) == 0x02;
335+
WheelPresent = (newValue.at(0) & 0x01) == 0x01;
336+
}
320337
qDebug() << QStringLiteral("CrankPresent: ") << CrankPresent;
321338
qDebug() << QStringLiteral("WheelPresent: ") << WheelPresent;
322339

@@ -334,14 +351,18 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
334351
emit debug(QStringLiteral("Current Wheel Event Time: ") + QString::number(_LastWheelEventTime));
335352
index += 2;
336353
}
337-
if (CrankPresent) {
354+
if (CrankPresent && !cyclingPowerMeasurement) {
338355
_CrankRevs = (((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
339356
emit debug(QStringLiteral("Current Crank Revs: ") + QString::number(_CrankRevs));
340357
index += 2;
341358
_LastCrankEventTime =
342359
(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
343360
emit debug(QStringLiteral("Current Crank Event Time: ") + QString::number(_LastCrankEventTime));
344361
}
362+
if (cyclingPowerMeasurement) {
363+
emit debug(QStringLiteral("Current Crank Revs: ") + QString::number(_CrankRevs));
364+
emit debug(QStringLiteral("Current Crank Event Time: ") + QString::number(_LastCrankEventTime));
365+
}
345366

346367
// CSC Combo Sensor Fallback Logic
347368
//
@@ -367,12 +388,12 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
367388
LastCrankEventTime = _LastCrankEventTime;
368389
}
369390

370-
int16_t deltaT = LastCrankEventTime - oldLastCrankEventTime;
391+
int32_t deltaT = LastCrankEventTime - oldLastCrankEventTime;
371392
if (deltaT < 0) {
372-
deltaT = LastCrankEventTime + 65535 - oldLastCrankEventTime;
393+
deltaT = LastCrankEventTime + 65536 - oldLastCrankEventTime;
373394
}
374395

375-
if (useMachineCadence && CrankRevs != oldCrankRevs && deltaT) {
396+
if (CrankRevs != oldCrankRevs && deltaT) {
376397
double cadence = ((CrankRevs - oldCrankRevs) / deltaT) * 1024 * 60;
377398

378399
// Cadence Validation Logic
@@ -393,7 +414,7 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
393414
if ((cadence >= 0 && (cadence < 256 || _CrankRevs == 0) && CrankPresent) || (!CrankPresent && WheelPresent))
394415
Cadence = cadence;
395416
lastGoodCadence = now;
396-
} else if (useMachineCadence && lastGoodCadence.msecsTo(now) > 2000) {
417+
} else if (lastGoodCadence.msecsTo(now) > 2000) {
397418
Cadence = 0;
398419
}
399420
emit cadenceChanged(Cadence.value());
@@ -490,13 +511,14 @@ void cscbike::stateChanged(QLowEnergyService::ServiceState state) {
490511
emit debug(QStringLiteral("BTLE stateChanged ") + QString::fromLocal8Bit(metaEnum.valueToKey(state)));
491512

492513
QBluetoothUuid CyclingSpeedAndCadence(QBluetoothUuid::CyclingSpeedAndCadence);
514+
QBluetoothUuid CyclingPower(QBluetoothUuid::CyclingPower);
493515
QBluetoothUuid Battery(QBluetoothUuid::BatteryService);
494516
for (QLowEnergyService *s : qAsConst(gattCommunicationChannelService)) {
495517
qDebug() << QStringLiteral("stateChanged") << s->serviceUuid() << s->state();
496518
#ifdef Q_OS_WINDOWS
497-
qDebug() << "windows workaround, check only CyclingSpeedAndCadence ftms service"
498-
<< (s->serviceUuid() == CyclingSpeedAndCadence);
499-
if (s->serviceUuid() == CyclingSpeedAndCadence)
519+
qDebug() << "windows workaround, check only cycling sensor services"
520+
<< (s->serviceUuid() == CyclingSpeedAndCadence || s->serviceUuid() == CyclingPower);
521+
if (s->serviceUuid() == CyclingSpeedAndCadence || s->serviceUuid() == CyclingPower)
500522
#endif
501523
{
502524
if (s->state() != QLowEnergyService::ServiceDiscovered && s->state() != QLowEnergyService::InvalidService) {
@@ -511,13 +533,15 @@ void cscbike::stateChanged(QLowEnergyService::ServiceState state) {
511533
for (QLowEnergyService *s : qAsConst(gattCommunicationChannelService)) {
512534
if (s->state() == QLowEnergyService::ServiceDiscovered) {
513535

514-
if(s->serviceUuid() == CyclingSpeedAndCadence) {
515-
qDebug() << "CyclingSpeedAndCadence found";
536+
if (s->serviceUuid() == CyclingSpeedAndCadence || s->serviceUuid() == CyclingPower) {
537+
qDebug() << "Cycling cadence service found" << s->serviceUuid();
516538
cadenceService = s;
517539
}
518540

519-
if(s->serviceUuid() != CyclingSpeedAndCadence && s->serviceUuid() != Battery) {
520-
// No data from sensors and avatar won’t move in Zwift (even when data showed on first try) (Issue #2178)
541+
if (s->serviceUuid() != CyclingSpeedAndCadence && s->serviceUuid() != CyclingPower &&
542+
s->serviceUuid() != Battery) {
543+
// No data from sensors and avatar won’t move in Zwift (even when data showed on first try) (Issue
544+
// #2178)
521545
qDebug() << "avoid unwaned service";
522546
continue;
523547
}
@@ -536,8 +560,9 @@ void cscbike::stateChanged(QLowEnergyService::ServiceState state) {
536560

537561
auto characteristics_list = s->characteristics();
538562
for (const QLowEnergyCharacteristic &c : qAsConst(characteristics_list)) {
539-
if(c.uuid() == QBluetoothUuid((quint16)0x2A5B)) {
540-
qDebug() << "CyclingSpeedAndCadence char found";
563+
if (c.uuid() == QBluetoothUuid((quint16)0x2A5B) ||
564+
c.uuid() == QBluetoothUuid::CyclingPowerMeasurement) {
565+
qDebug() << "Cycling cadence characteristic found" << c.uuid();
541566
cadenceChar = c;
542567
}
543568
qDebug() << QStringLiteral("char uuid") << c.uuid() << QStringLiteral("handle") << c.handle() << QStringLiteral("properties") << c.properties();
@@ -663,9 +688,10 @@ void cscbike::serviceScanDone(void) {
663688
for (const QBluetoothUuid &s : qAsConst(services_list)) {
664689
#ifdef Q_OS_WINDOWS
665690
QBluetoothUuid CyclingSpeedAndCadence(QBluetoothUuid::CyclingSpeedAndCadence);
666-
qDebug() << "windows workaround, check only the CyclingSpeedAndCadence service" << s << CyclingSpeedAndCadence
667-
<< (s == CyclingSpeedAndCadence);
668-
if (s == CyclingSpeedAndCadence)
691+
QBluetoothUuid CyclingPower(QBluetoothUuid::CyclingPower);
692+
qDebug() << "windows workaround, check only cycling sensor services" << s << CyclingSpeedAndCadence
693+
<< CyclingPower << (s == CyclingSpeedAndCadence || s == CyclingPower);
694+
if (s == CyclingSpeedAndCadence || s == CyclingPower)
669695
#endif
670696
{
671697
gattCommunicationChannelService.append(m_control->createServiceObject(s));

0 commit comments

Comments
 (0)