cagnulein / qdomyos-zwift

Zwift bridge for smart treadmills and bike/cyclette
https://www.qzfitness.com/
GNU General Public License v3.0
369 stars 109 forks source link

[REQ] SmartSpin2K & QZ integration #299

Closed marklogan17 closed 3 years ago

marklogan17 commented 3 years ago

Looking to integrate the two so we can remotely control resistance on bikes with no motorized resistance control. NRF screenshots attached. I have started a discussion on the SmartSpin2K github page asking for suggestions/thoughts. Screenshot_20210505-181402_nRF Connect Screenshot_20210505-181437_nRF Connect Screenshot_20210505-181420_nRF Connect Screenshot_20210505-181446_nRF Connect Screenshot_20210505-181352_nRF Connect Screenshot_20210505-181511_nRF Connect

cagnulein commented 3 years ago

Ok nice, it's a standard ftms. So let's talk about the implementation: i guess i should treat this as an accessory for every bike, not as a bike itself. Do you agree? So new settings to enable it and configure it

Il gio 6 mag 2021, 01:52 marklogan17 @.***> ha scritto:

Assigned #299 https://github.com/cagnulein/qdomyos-zwift/issues/299 to @cagnulein https://github.com/cagnulein.

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#event-4692070962, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWCYRVIDHJL2YDBVGSDTMHK4LANCNFSM44F6V43A .

marklogan17 commented 3 years ago

Yes I think its an accessory for every bike. My concern is what feedback SmartSpin will need from the bike to know when its changed the resistance? In other words, does SmartSpin need to be told the current resistance level on the bike so it knows when to stop turning the knob? I think the answer is 'no' but I don't know that :)

I can get reasonable logging on the web interface to SmartSpin so even though I don't have the motor I should be able to see it 'trying' to change the resistance if it receives the commands from QZ.

cagnulein commented 3 years ago

mmm interesting thought. But SmartSpin should works even with bike without bluetooth, so i don't think it needs a feedback. It just counts its motor steps.

Roberto Viola Software engineer and open source enthusiast http://robertoviola.cloud

Il giorno gio 6 mag 2021 alle ore 14:10 marklogan17 < @.***> ha scritto:

Yes I think its an accessory for every bike. My concern is what feedback SmartSpin will need from the bike to know when its changed the resistance? In other words, does SmartSpin need to be told the current resistance level on the bike so it knows when to stop turning the knob? I think the answer is 'no' but I don't know that :)

I can get reasonable logging on the web interface to SmartSpin so even though I don't have the motor I should be able to see it 'trying' to change the resistance if it receives the commands from QZ.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-833472117, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWBG6EHNA2HGEPOCC53TMKBLBANCNFSM44F6V43A .

marklogan17 commented 3 years ago

Yes that's what I'm hoping, but is that accurate for every resistance level? i.e. Does changing from 24 to 25 resistance have the same amount of turning as changing from 31 to 32 resistance? I 'hope' its consistent :)

cagnulein commented 3 years ago

it has to be! i don't want to write the smartspin firmware also :D

Roberto Viola Software engineer and open source enthusiast http://robertoviola.cloud

Il giorno gio 6 mag 2021 alle ore 14:19 marklogan17 < @.***> ha scritto:

Yes that's what I'm hoping, but is that accurate for every resistance level? i.e. Does changing from 24 to 25 resistance have the same amount of turning as changing from 31 to 32 resistance? I 'hope' its consistent :)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-833477451, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWE43XR3M3DXD5KOTR3TMKCNNANCNFSM44F6V43A .

cagnulein commented 3 years ago

give it a try @marklogan17 there is a new parameter called "FTMS accessory". If i did everything right, when zwift or peloton asks for a resistance change, i will send also to SpinBike2K :)

marklogan17 commented 3 years ago

Built it. It connected to the smartspin (going to have to ask them for a switch to turn on/off echelon support so smartspin doesn't grab connection to bike). I tried a few times but didn't get any messages to come across to the smart spin. Put Zwift setting to Erg mode just for the fun of it. Tried changing resistance on target resistance and regular resistance tiles. Turned on/off the 'auto follow resistance' at the top of the app. Some log files attached.

debug-Mon_May_10_13_36_01_2021.log debug-Mon_May_10_13_37_59_2021.log

kadaan commented 3 years ago

@cagnulein We only support two modes for FTMS Control at the moment: 0x05 and 0x11

cagnulein commented 3 years ago

@cagnulein We only support two modes for FTMS Control at the moment: 0x05 and 0x11

Yes i saw. I already fork your project and i would like to add also the resistance command to. What do you think?

In any case i think i can emulate it send a grade command proportionally to the resistance requested

kadaan commented 3 years ago

Fork and contribute would be preferred. I'd love to see if there is a way to collaborate for the benefit of all.

kadaan commented 3 years ago

"To the resistance required" will be difficult without first "homing" the knob to zero.

cagnulein commented 3 years ago

Fork and contribute would be preferred. I'd love to see if there is a way to collaborate for the benefit of all.

Yes of course, i will do a PR when i will have something ready :)

cagnulein commented 3 years ago

"To the resistance required" will be difficult without first "homing" the knob to zero.

It's not the same with ZWIFT also? I mean how did you homing with the current version? It's something requested to the user?

kadaan commented 3 years ago

With zwift, in sim mode, it controls the incline. The user can set the resistance via SS2Ks remote switches on the handlebars. All control from Zwift is relative to that. For ERG mode we track the target range and vary resistance to try to match that.

For Sim and ERG, you can just signal SS2K via Sim. If Zwift is signaling you with sim mode control you can pass it though. If Zwift is controlling with ERG, you have target and current power, so you calculate the needed change and send it as incline.

Target resistance mode is the only one that need homing.

We want to support homing, but need to determine the best way to do it. Different bike have different means of controlling resistance and I'm not sure that they will all trigger the stepper motors end stop. We only have a few bikes to test on, so it is a small sample set.

cagnulein commented 3 years ago

Understood. Maybe the simple way is to measure the torque. If the torque increases means that you reach the home point. Just a guess

Il mar 11 mag 2021, 07:11 Joel Baranick @.***> ha scritto:

With zwift, in sim mode, it controls the incline. The user can set the resistance via SS2Ks remote switches on the handlebars. All control from Zwift is relative to that. For ERG mode we track the target range and vary resistance to try to match that.

For Sim and ERG, you can just signal SS2K via Sim. If Zwift is signaling you with sim mode control you can pass it though. If Zwift is controlling with ERG, you have target and current power, so you calculate the needed change and send it as incline.

Target resistance mode is the only one that need homing.

We want to support homing, but need to determine the best way to do it. Different bike have different means of controlling resistance and I'm not sure that they will all trigger the stepper motors end stop. We only have a few bikes to test on, so it is a small sample set.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-837827869, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWBEEFRPYLG7DEMFND3TNC4B3ANCNFSM44F6V43A .

kadaan commented 3 years ago

Yeah. That is the plan, and it will work for bikes with mechanical control of the resistance (ie. IC4). Bike with a different type of control (ie. electronic) that will not work.

cagnulein commented 3 years ago

It seems that you already know better than me how the world works :D I guess manual homing is the safest choice so

Il mar 11 mag 2021, 07:18 Joel Baranick @.***> ha scritto:

Yeah. That is the plan, and it will work for bikes with mechanical control of the resistance (ie. IC4). Bike with a different type of control (ie. electronic) that will not work.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-837834701, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWHTYL3JUNVJ7EAXLVLTNC4ZZANCNFSM44F6V43A .

kadaan commented 3 years ago

Hehehe. Having no app makes it harder for us :P

kadaan commented 3 years ago

On a side note, I'm trying to build your iOs app and this keeps happening:

Undefined symbols for architecture arm64:
  "_QZ_EnableDiscoveryCharsAndDescripttors", referenced from:
      echelonconnectsport::echelonconnectsport(bool, bool, unsigned char, double) in echelonconnectsport.o
      fitplusbike::fitplusbike(bool, bool, unsigned char, double) in fitplusbike.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Any ideas?

cagnulein commented 3 years ago

On a side note, I'm trying to build your iOs app and this keeps happening:

Undefined symbols for architecture arm64:
  "_QZ_EnableDiscoveryCharsAndDescripttors", referenced from:
      echelonconnectsport::echelonconnectsport(bool, bool, unsigned char, double) in echelonconnectsport.o
      fitplusbike::fitplusbike(bool, bool, unsigned char, double) in fitplusbike.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Any ideas?

yes because you have to use these https://github.com/cagnulein/qdomyos-zwift/tree/master/qt-patches/ios/5.15.2

cagnulein commented 3 years ago

Hehehe. Having no app makes it harder for us :P

this is where QZ can help :) P.S. where are you based?

cagnulein commented 3 years ago

@marklogan17 could you try now pulling and building again?

kadaan commented 3 years ago

@cagnulein Washington State

I manually patched my QT. Was unclear if I had to do anything with the .mm file.

Seems like it builds, so I'm hoping that it's gonna run

cagnulein commented 3 years ago

@kadaan you can just use the binary library without touching the mm, this is why i added to the GIT repo (in order to help people to build QZ without rebuilding the QT)

kadaan commented 3 years ago

Where can I get the debug logs? The app based on master keeps crashing on me.

cagnulein commented 3 years ago

@kadaan if you have XCode attached i guess you should post a screenshot about it. We should create a new github issue in order to keep this clean

marklogan17 commented 3 years ago

Built and did a couple of quick tests. With the FTMS field populated but the smartspin turned off, QZ actually failed to connect to the bike. As soon as I put FTMS to disabled it connected. I then turned on the smart spin, went to the FTMS setting, searched/refreshed, picked smart spin. Now, I'm still not sure that it actually connected to the smart spin. SmartSpin connected to something just like it did yesterday but nothing came across when I hit +/- on the resistance tile.

This is the last log file, I think I did everything in here... debug-Tue_May_11_09_15_02_2021.log

cagnulein commented 3 years ago

ok we did some steps forward @marklogan17 . i guess you have now some issue about virtual device without QT patched. Try to disable the virtual device and retry please.

With the FTMS field populated but the smartspin turned off, QZ actually failed to connect to the bike

did you wait at least 10 seconds in this scenario? i have a timeout about this condition.

Also in the log i saw not the smartspin but it fails everytime, so i hope it's releated to the virtual bike setting

marklogan17 commented 3 years ago

Tried again. Disabled virtual device and restarted. With the smartspin OFF QZ did not connect to the bike - I waited about 15 or 20 seconds. Disabled FTMS and it connected right away. Started SmartSpin and I don't think I got a connection - tried a few things. Finally restarted the SmartSpin and it looked like it connected however again no control messages came across.
debug-Tue_May_11_09_42_50_2021.log

Next I tried QZ with the SmartSpin already running - had to be a little careful of timing because I didn't want the SmartSpin connecting to the Echelon directly. This time QZ started, connected to bike and again looked like it connected to SmartSpin but no data. debug-Tue_May_11_09_47_27_2021.log

cagnulein commented 3 years ago

it works! i mean not from your point of view, but in the log, in the 2nd log, i saw the connection between the bike and between the SS2K at the same time!

Now i have to fixed 2 bugs: 1) the first scenario that you described 2) the resistance issue (i already found the cause)

I will tell you when i will be ready to try again

cagnulein commented 3 years ago

@marklogan17 could you please pull, build and test again using the scenario 2? Now you should see some resistance chaning in the SS2K In the meanwhile i will try to fix scenario 1

cagnulein commented 3 years ago

also @marklogan17 i checked the first scenario log and it seems that the bike was connected (after 10 seconds timeout). I see it cleary in the logs:

Tue. May 11 09:43:13 2021 "Found new device: ECH-SPORT-081192 (EF:70:0D:EF:C1:35) \u001F:\u0000" Tue. May 11 09:43:23 2021 1620740603714 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::serviceDiscovered(const QBluetoothUuid &) "serviceDiscovered {00001800-0000-1000-8000-00805f9b34fb}"

exactly 10 seconds

and then i saw the metrics too

Tue. May 11 09:43:24 2021 1620740604146 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) " << f0 d1 09 00 00 00 00 00 00 00 00 00 ca" Tue. May 11 09:43:24 2021 1620740604148 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Local elapsed: 00:00:00" Tue. May 11 09:43:24 2021 1620740604149 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Speed: 0" Tue. May 11 09:43:24 2021 1620740604149 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Calculate Distance: 0" Tue. May 11 09:43:24 2021 1620740604150 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Cadence: 0" Tue. May 11 09:43:24 2021 1620740604151 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Distance: 0" Tue. May 11 09:43:24 2021 1620740604151 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current CrankRevs: 0" Tue. May 11 09:43:24 2021 1620740604152 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Last CrankEventTime: 0" Tue. May 11 09:43:24 2021 1620740604152 Debug: ../src/echelonconnectsport.cpp void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &, const QByteArray &) "Current Watt: 0"

marklogan17 commented 3 years ago

Alright, I think we're getting there. I saw the connection and the request for incline 15 come across. My bike resistance was 24, probably if I had changed it I would have had a different incline request come across. In fact I'll try that now and come back here and edit this comment. Anyway, successful log attached...

debug-Tue_May_11_10_43_40_2021.log

Edit: the incline request is 15.00 no matter what resistance I'm at on the bike and whether I hit the + or - resistance button.

Edit 2: I can confirm that QZ is connecting to the bike, just not displaying tiles, until it connects to the smartspin. That may be the best way to do it. Get everything connected and show the tiles when its ready to go...

cagnulein commented 3 years ago

i got the "15" error. try to lower resistance (lower than 15), it should change. anyway i'm pushing it :)

kadaan commented 3 years ago

Any reason to have a limit of 100?

kadaan commented 3 years ago

Also, it would be good to get the SS2K switches to work with QZ

cagnulein commented 3 years ago

Any reason to have a limit of 100?

just a workaround, i will read from the ftms in the next versions

cagnulein commented 3 years ago

Also, it would be good to get the SS2K switches to work with QZ

one thing at a time :)

marklogan17 commented 3 years ago

Also, it would be good to get the SS2K switches to work with QZ

I thought about this too but since QZ reads from the bike, changing the level by the shifters will get echoed to QZ via the bike. So it 'kinda' will...

cagnulein commented 3 years ago

@marklogan17 i also fixed the scenaro 1 with the last commit

kadaan commented 3 years ago

Also, it would be good to get the SS2K switches to work with QZ

I thought about this too but since QZ reads from the bike, changing the level by the shifters will get echoed to QZ via the bike. So it 'kinda' will...

Except AFAIK not all bikes expose resistance over FTMS

cagnulein commented 3 years ago

Except AFAIK not all bikes expose resistance over FTMS

for every bike i knew the max resistance level even if they did't expose it by bluetooth

marklogan17 commented 3 years ago

Also, it would be good to get the SS2K switches to work with QZ

I thought about this too but since QZ reads from the bike, changing the level by the shifters will get echoed to QZ via the bike. So it 'kinda' will...

Except AFAIK not all bikes expose resistance over FTMS

True. Some are proprietary (Echelon) and some have nothing. Are you thinking of some web api that apps could subscribe to to be notified on internal smartspin changes? You would almost need to disable the internal processing of the shifters if pushing control out to QZ or other apps.

kadaan commented 3 years ago

I doubt we would disable handling of shifts. Rather, we could expose shifts as a callback. Also, with bike calibration we should be a belt I expose resistance.

kadaan commented 3 years ago

That said, I think we should keep all options open ATM. There sure is a lot to be gained for people by an integration between SS2K and QZ.

cagnulein commented 3 years ago

You should expose shifts in the resistance attributes on ftms. What do you think?

Il mar 11 mag 2021, 17:50 Joel Baranick @.***> ha scritto:

That said, I think we should keep all options open ATM. There sure is a lot to be gained for people by an integration between SS2K and QZ.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-838721824, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWER2DDBTLPQ2GZH3OTTNFG23ANCNFSM44F6V43A .

marklogan17 commented 3 years ago

Great news! I got my build working - at least to the point where the motor turns. I can now use QZ to cause the motor to turn either direction by hitting the + or - button on the resistance tile. I'll try and get the build buttoned up tomorrow so that I can actually install it on the bike and tweak the parameters to get it turning the right amount to change resistance levels. Very exciting!

cagnulein commented 3 years ago

Can't wait to see it working on the bike!

Il mer 12 mag 2021, 02:14 marklogan17 @.***> ha scritto:

Great news! I got my build working - at least to the point where the motor turns. I can now use QZ to cause the motor to turn either direction by hitting the + or - button on the resistance tile. I'll try and get the build buttoned up tomorrow so that I can actually install it on the bike and tweak the parameters to get it turning the right amount to change resistance levels. Very exciting!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cagnulein/qdomyos-zwift/issues/299#issuecomment-839318575, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALYWGMOMBPHRHCKU5YYHDTNHB7NANCNFSM44F6V43A .

marklogan17 commented 3 years ago

I installed on the bike. I had some binding issues that I got sorted out enough to test. I set the # of steps to 750 which seems to equate to a level of resistance each time I hit a shifter button. When I use the +/- buttons on QZ they send an incline value that is equal to the next resistance level. So for example if I'm at resistance 22 and hit + it will send an incline of 23 if I hit '-' it sends an incline of 21. The first time you do that on start up you tend to get a huge resistance change. I went from 24 to 32 one time for example. I also had a huge shift down the one time. I'm assuming that's because SmartSpin is assuming some starting incline, probably zero, and when you immediately put '23' it spins a long way. Also 1 increase of incline doesn't seem to be necessarily equivalent to 1 increase of resistance in smartspin, it feels like it spins less. I don't have anything empirical to back that up.

So, in short, its working and its close to good enough. If we tweak the incline stuff a little we'll probably have something really workable. In the mean time, I'm continuing to figure out my binding issues. I'm also going to see if I can build the smartspin fork that was provided that has more info coming across the bluetooth.

kadaan commented 3 years ago

As of right now the method @cagnulein is using to change SS2K resistance is setting "incline". SS2K assumes you start at "incline" of zero, and all changes from that baseline. In practice this works correctly for Zwift, FulGaz, etc. in sim mode because the "incline" changes are directly related virtual changes in slope.

There is an incline multiplier that can impact how much the knob is turned and is applied to the data that @cagnulein is sending.

This sounds like it will "work" for now, but is not optimal. The shifters in SS2K change the baseline and all incline changes are relative to that. That way you can adjust the "feel" in realtime if things are too tough or too easy.

The real solution here is to add an SS2K BLE service that:

  1. Allows disabling of direct stepper control from within SS2K
  2. Disables FTMS, HRM, CPS BLE binding
  3. Notifies on shifter up and down press
  4. Enables direct stepper control

With these capabilities added, QZ could take over the BLE communication with bikes and apps, if desired, and it could drive SS2K's control of the knob.

For users operating without QZ, SS2K would retain standard functionality.