Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Zwift Hub FTMS #7

Closed
Berg0162 opened this issue Dec 9, 2022 · 113 comments
Closed

Test Zwift Hub FTMS #7

Berg0162 opened this issue Dec 9, 2022 · 113 comments

Comments

@Berg0162
Copy link
Owner

Berg0162 commented Dec 9, 2022

    Hello Jorghen,

Here is the first result with FTMS_Client_ v022

I don't know if it's good?

Next test when I receive the dongle for the Pc

Friendship,
Joel

[CFG ] SoftDevice's RAM requires: 0x20002C78
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
[BLE ] BLE_GAP_EVT_ADV_REPORT : Conn Handle = 65535
[BLE ] BLE_GAP_EVT_ADV_REPORT : Conn Handle = 65535
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000000681 F8:9C:FC:53:5E:49 -60 09-02-16-18-26-18-18-18-0A-18
[BLE ] BLE_GAP_EVT_CONNECTED : Conn Handle = 0
[GAP ] MAC = F8:9C:FC:53:5E:49, Type = 1, Resolved = 0
[GAP ] Conn Interval = 20.00 ms, Latency = 0, Supervisor Timeout = 2000 ms
[BLE ] BLE_GAP_EVT_DISCONNECTED : Conn Handle = 0
[GAP ] Disconnect Reason: CONN_FAILED_TO_BE_ESTABLISHED
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [] MAC Address: F8:9C:FC:53:5E:49
Now checking mandatory Client Services and Characteristics!
Discovering Client Cycling Power (CP) Service ... [DISC ] [SVC] Handle start = 1
bool BLEDiscovery::_discoverService(uint16_t, BLEClientService&, uint16_t): 79: verify failed, error = BLE_ERROR_INVALID_CONN_HANDLE
Not Found!
Disconnecting since Client Cyling Power Service is mandatory!
Client Disconnected, reason = 0x3E

Restart the Feather nRF52 Client for a new run! <<<
Couldn't enable notify for Client CP Measurement Characteristic.
Couldn't enable indicate for Client CP Control Point Characteristic.
Couldn't enable notify for Client CSC Measurement Characteristic.
Couldn't enable notify for Client FTM Training Status Characteristic.
FTMS (trainer) is controlled by another Client (Training App)!
Client (Central) is Up and Running!

BSP Library : 1.3.0
Bootloader : s140 6.1.1
Serial No : C2BA380FAD3CBBC2

--------- SoftDevice Config ---------
Max UUID128 : 10
ATTR Table Size : 4096
Service Changed : 1
Central Connect Setting

  • Max MTU : 23
  • Event Length : 3
  • HVN Queue Size : 1
  • WrCmd Queue Size: 1

--------- BLE Settings ---------
Name : ItsyBitsy nRF52840 Express
Max Connections : Peripheral = 0, Central = 1
Address : E2:AA:F7:E9:06:E0 (Static)
TX Power : 0 dBm
Conn Intervals : min = 20.00 ms, max = 30.00 ms
Conn Timeout : 2000 ms
Central Paired Devices:

Originally posted by @le-joebar in #5 (comment)

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 9, 2022

Dear Joel,
Please lets have this thread separated from Elite Direto thread!

Are you 100% sure that the Zwift Hub is not connected to another client application (i.c. Zwift)?
It looks like your Zwift Hub is advertising FTMS but is NOT exposing the FTMS Service when it is connected. That means it is controlled by another Client, since you do not have (a) BLE(dongle) that must be over ANT+. Or do you have an other setup?

--> Shut down your PC, remove the ANT+ dongle and restart the PC.
--> Please set the Debug level in the Arduoino IDE to RELEASE (level 0)!!
Like the instructions say:

  • Requirements: Running FTMS trainer and Feather nRF52 board
    1. Start the trainer and do NOT connect it with other devices
    1. Upload and Run this code on the Feather-nRF52

Good luck!
Jörgen.

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 9, 2022

Dear Joel,
Please have a look at this tool for your smartphone to help you inspecting and testing BLE devices:
nRF Connect by Nordic
You can perfectly inspect the Zwift Hub trainer and check for the presence of BLE services like CPS, CSC, FTMS and others. You can easily connect to the trainer and test all those different Characteristics.... and their behaviour. It should be part of your toolbox I.C.E.

I have uploaded a new version for the Client (v023) that is more robust when error states occur. It will not result in an other outcome of your previous test!
Regards,
Jörgen.

@le-joebar
Copy link

Hello Jorghen,

I redid the test with Ant+ disconnected and Bluethoot gsm switched off.

Here is the result with FTMS_Client_ v022:

Now I think it's good !

Joel

PS : I'm waiting for the dongle, maybe tomorrow

Feather nRF52 Client/Central: CPS, CSC and FTMS
----------------- Version 02.2 ------------------
Initialise the Bluefruit nRF52 module: Client (Central)
[CFG ] SoftDevice's RAM requires: 0x20002C78
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000016641 F8:9C:FC:53:5E:49 -68 09-02-16-18-26-18-18-18-0A-18
[BLE ] BLE_GAP_EVT_CONNECTED : Conn Handle = 0
[GAP ] MAC = F8:9C:FC:53:5E:49, Type = 1, Resolved = 0
[GAP ] Conn Interval = 20.00 ms, Latency = 0, Supervisor Timeout = 2000 ms
[BLE ] BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST : Conn Handle = 0
[GAP ] ATT MTU is changed to 23
[BLE ] BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST : Conn Handle = 0
[GAP ] Data Length Request is (tx, rx) octets = (251, 251), (tx, rx) time = (2120, 2120) us
[BLE ] BLE_GAP_EVT_DATA_LENGTH_UPDATE : Conn Handle = 0
[GAP ] Data Length is (tx, rx) octets = (27, 27), (tx, rx) time = (1370, 1370) us
[BLE ] BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP : Conn Handle = 0
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking mandatory Client Services and Characteristics!
Discovering Client Cycling Power (CP) Service ... [DISC ] [SVC] Handle start = 1
[BLE ] BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP : Conn Handle = 0
[DISC ] [SVC] Service Count: 1
[DISC ] [SVC] Found 0x1818, Handle start = 41, end = 48

Found it! CPS Max Payload: 20 Data Length: 27
Discovering Client CP Measurement characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A63, handle = 43

[DISC ] [DESC] Handle start = 44, end = 48
[BLE ] BLE_GATTC_EVT_DESC_DISC_RSP : Conn Handle = 0
[DISC ] [DESC] Descriptor Count: 5
[DISC ] [DESC] Descriptor 0: uuid = 0x2902, handle = 44
[DISC ] [DESC] Descriptor 1: uuid = 0x2803, handle = 45
[DISC ] [DESC] Descriptor 2: uuid = 0x2A65, handle = 46
[DISC ] [DESC] Descriptor 3: uuid = 0x2803, handle = 47
[DISC ] [DESC] Descriptor 4: uuid = 0x2A5D, handle = 48
[DISC ] Found CCCD: handle = 44
Found it!
Discovering Client CP Control Point characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Handle start = 49, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 0
[DISC ] [CHR] Gatt Status = 0x0101
Not Found! NOT Mandatory!
Discovering Client CP Feature characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A65, handle = 46

Found it!
[BLE ] BLE_GATTC_EVT_READ_RSP : Conn Handle = 0
-> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
Wheel revolution data supported
Crank revolution data supported
Discovering Client CP Sensor Location characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A5D, handle = 48

Found it!
-> Client Reads CP Location Sensor: Loc#: 0 Other
Discovering Cycling Speed and Cadence (CSC) Service ... [DISC ] [SVC] Handle start = 1
bool BLEDiscovery::_discoverService(uint16_t, BLEClientService&, uint16_t): 79: verify failed, error = NRF_ERROR_BUSY
Not Found and disconnecting!
CSC Service is mandatory!
[BLE ] BLE_GATTC_EVT_READ_RSP : Conn Handle = 0
[BLE ] BLE_GAP_EVT_DISCONNECTED : Conn Handle = 0
[GAP ] Disconnect Reason: LOCAL_HOST_TERMINATED_CONNECTION
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Couldn't enable notify for Client CP Measurement Characteristic.
Couldn't enable indicate for Client CP Control Point Characteristic.
Couldn't enable notify for Client CSC Measurement Characteristic.
Couldn't enable notify for Client FTM Training Status Characteristic.
FTMS (trainer) is controlled by another Client (Training App)!
Client (Central) is Up and Running!

BSP Library : 1.3.0
Bootloader : s140 6.1.1
Serial No : C2BA380FAD3CBBC2

--------- SoftDevice Config ---------
Max UUID128 : 10
ATTR Table Size : 4096
Service Changed : 1
Central Connect Setting

  • Max MTU : 23
  • Event Length : 3
  • HVN Queue Size : 1
  • WrCmd Queue Size: 1

--------- BLE Settings ---------
Name : ItsyBitsy nRF52840 Express
Max Connections : Peripheral = 0, Central = 1
Address : E2:AA:F7:E9:06:E0 (Static)
TX Power : 0 dBm
Conn Intervals : min = 20.00 ms, max = 30.00 ms
Conn Timeout : 2000 ms
Central Paired Devices:

@le-joebar
Copy link

Test with FTMS_Client_v023 :

Feather nRF52 Client/Central: CPS, CSC and FTMS
----------------- Version 02.3 ------------------
Initialise the Bluefruit nRF52 module: Client (Central)
[CFG ] SoftDevice's RAM requires: 0x20002C78
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
[BLE ] BLE_GAP_EVT_ADV_REPORT : Conn Handle = 65535
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000013719 F8:9C:FC:53:5E:49 -66 09-02-16-18-26-18-18-18-0A-18
[BLE ] BLE_GAP_EVT_CONNECTED : Conn Handle = 0
[GAP ] MAC = F8:9C:FC:53:5E:49, Type = 1, Resolved = 0
[GAP ] Conn Interval = 20.00 ms, Latency = 0, Supervisor Timeout = 2000 ms
[BLE ] BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST : Conn Handle = 0
[GAP ] ATT MTU is changed to 23
[BLE ] BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST : Conn Handle = 0
[GAP ] Data Length Request is (tx, rx) octets = (251, 251), (tx, rx) time = (2120, 2120) us
[BLE ] BLE_GAP_EVT_DATA_LENGTH_UPDATE : Conn Handle = 0
[GAP ] Data Length is (tx, rx) octets = (27, 27), (tx, rx) time = (1370, 1370) us
[BLE ] BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP : Conn Handle = 0
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking mandatory Client Services and Characteristics!
Discovering Client Cycling Power (CP) Service ... [DISC ] [SVC] Handle start = 1
[BLE ] BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP : Conn Handle = 0
[DISC ] [SVC] Service Count: 1
[DISC ] [SVC] Found 0x1818, Handle start = 41, end = 48

Found it! CPS Max Payload: 20 Data Length: 27
Discovering Client CP Measurement characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A63, handle = 43

[DISC ] [DESC] Handle start = 44, end = 48
[BLE ] BLE_GATTC_EVT_DESC_DISC_RSP : Conn Handle = 0
[DISC ] [DESC] Descriptor Count: 5
[DISC ] [DESC] Descriptor 0: uuid = 0x2902, handle = 44
[DISC ] [DESC] Descriptor 1: uuid = 0x2803, handle = 45
[DISC ] [DESC] Descriptor 2: uuid = 0x2A65, handle = 46
[DISC ] [DESC] Descriptor 3: uuid = 0x2803, handle = 47
[DISC ] [DESC] Descriptor 4: uuid = 0x2A5D, handle = 48
[DISC ] Found CCCD: handle = 44
Found it!
Discovering Client CP Control Point characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Handle start = 49, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 0
[DISC ] [CHR] Gatt Status = 0x0101
Not Found! NOT Mandatory!
Discovering Client CP Feature characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A65, handle = 46

Found it!
[BLE ] BLE_GATTC_EVT_READ_RSP : Conn Handle = 0
-> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
Wheel revolution data supported
Crank revolution data supported
Discovering Client CP Sensor Location characteristic ... [DISC ] [CHR] Handle start = 41, end = 48
[BLE ] BLE_GATTC_EVT_CHAR_DISC_RSP : Conn Handle = 0
[DISC ] [CHR] Characteristic Count: 3
[DISC ] [CHR] Found 0x2A5D, handle = 48

Found it!
-> Client Reads CP Location Sensor: Loc#: 0 Other
Discovering Cycling Speed and Cadence (CSC) Service ... [DISC ] [SVC] Handle start = 1
bool BLEDiscovery::_discoverService(uint16_t, BLEClientService&, uint16_t): 79: verify failed, error = NRF_ERROR_BUSY
Not Found and disconnecting!
CSC Service is mandatory!
[BLE ] BLE_GATTC_EVT_READ_RSP : Conn Handle = 0
[BLE ] BLE_GAP_EVT_DISCONNECTED : Conn Handle = 0
[GAP ] Disconnect Reason: LOCAL_HOST_TERMINATED_CONNECTION
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Stopped!
BSP Library : 1.3.0
Bootloader : s140 6.1.1
Serial No : C2BA380FAD3CBBC2

--------- SoftDevice Config ---------
Max UUID128 : 10
ATTR Table Size : 4096
Service Changed : 1
Central Connect Setting

  • Max MTU : 23
  • Event Length : 3
  • HVN Queue Size : 1
  • WrCmd Queue Size: 1

--------- BLE Settings ---------
Name : ItsyBitsy nRF52840 Express
Max Connections : Peripheral = 0, Central = 1
Address : E2:AA:F7:E9:06:E0 (Static)
TX Power : 0 dBm
Conn Intervals : min = 20.00 ms, max = 30.00 ms
Conn Timeout : 2000 ms
Central Paired Devices:

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 9, 2022

Dear Joel,

  1. I have asked you before --> Please set the Debug level in the Arduino IDE to RELEASE (level 0) !! You did NOT do this...for a particular reason? Please realize that more elaborate debug levels cost processor capacity and might lead to BLE timing errors.... See above: Discovering the CSC service an error occurs: error = NRF_ERROR_BUSY ---> Busy? Doing what? Does this error also occur when you set Release level to zero at compile time ?
  2. --------- BLE Settings --------- Name : ItsyBitsy nRF52840 Express
    Did you have a particular reason why you bought this board and not the Feather nRF52840 Express that the Simcline projects use? I have no experience with the ItsyBitsy at all and nobody I know ever used it in this project....so I simply do not know if there are critical differences, that lead to unexpected results during operation and/or debugging in this stage or later.
  3. The results are not very different from previous runs, the client detects a trainer [Zwift Hub] that advertises with CPS, CSC and FTMS... However when connected the discover actions (in the latest runs) for a mandatory service or characteristic fail at CSC. That is different from the first runs! The first runs failed when FTM was NOT detected! Does your Zwift hub not expose CSC? Is that typical for this type? Are there different types of Zwift Hubs with different specs.?
  4. Please use nRF Connect to scan your Zwift hub when it is powered --> advertising -> connect -> detail the different services and characteristics that the hub is exposing... In previous issues (see Elite Direto) you can see how people made screen shots of screens of the nRF Connect tool and how instructive these images are!
  5. What other devices (Treadmill(s), trainers, smartphone(s) et cetera) with BLE and/or ANT+ are present in the room that can disturb a smooth connection process.
    Best wishes,
    Jörgen.

@Berg0162
Copy link
Owner Author

Dear Joel,
After a night sleep.... I have made some changes in the Client software and uploaded version v024.

  1. No longer is CSC a MANDATORY service, it is very well possible that Zwift Hub does not has it implemented... sofar we do not know! May be "nRF Connect" is giving soon better insight in what services are exposed exactly!
  2. I have changed the order of the connect sequence (discover a service or characteristic). The sequence now starts with more general services that all BLE devices (must) have, only thereafter we check for FTMS... That is mandatory so if it fails -> end of exercise! If all goes well CPS (Mandatory) and CSC (Not Mandatory) are checked...
    I hope this will give more decisive information!
    Good luck,
    Jörgen.

@le-joebar
Copy link

le-joebar commented Dec 10, 2022

Dear Jörghen,

I ordered today Adafruit Feather nRF52840 Express
image
but there are many sites out of stock!
that's why I ordered the ItsyBitsy nRF52840 Express

Here is the first result with version V024.
At first I turn on the trainer and watch the results.
Here they are :

FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000016166 F8:9C:FC:53:5E:49 -64 09-02-16-18-26-18-18-18-0A-18
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking all Client Services and Characteristics!
If Mandatory Services Fail --> the Client will disconnect!
First checking Generic Access and Device Information Services and Characteristics!
Found Client Generic Access
-> Client Reads Device Name: [Zwift Hub]
-> Client Reads Appearance: [0]
Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
Disconnecting since Client FTM Service is mandatory!
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Stopped!

And here is the second if just after turning on I pedal to wake up the services.

This is the second result if i'm turn

Feather nRF52 Client/Central: CPS, CSC and FTMS
----------------- Version 02.4 ------------------
Initialise the Bluefruit nRF52 module: Client (Central)
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000001764 F8:9C:FC:53:5E:49 -70 09-02-16-18-26-18-18-18-0A-18
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking all Client Services and Characteristics!
If Mandatory Services Fail --> the Client will disconnect!
First checking Generic Access and Device Information Services and Characteristics!
Found Client Generic Access
-> Client Reads Device Name: [Zwift Hub]
-> Client Reads Appearance: [1152]
Found Client Device Information
-> Client Reads Manufacturer: [Zwift]
-> Client Reads Model Number: [06]
-> Client Reads Serial Number: [06-F89CFC535E49]
Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
Discovering Client FTM Feature Characteristic ... Found it!
-> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
Discovering Client FTM Training Status Characteristic ... Not Found!
Disconnecting since Client FTM Characteristic is mandatory!
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Stopped!

Here with the V23:

Feather nRF52 Client/Central: CPS, CSC and FTMS
----------------- Version 02.3 ------------------
Initialise the Bluefruit nRF52 module: Client (Central)
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000001160 F8:9C:FC:53:5E:49 -69 09-02-16-18-26-18-18-18-0A-18
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking mandatory Client Services and Characteristics!
Discovering Client Cycling Power (CP) Service ... Found it! CPS Max Payload: 20 Data Length: 27
Discovering Client CP Measurement characteristic ... Found it!
Discovering Client CP Control Point characteristic ... Not Found! NOT Mandatory!
Discovering Client CP Feature characteristic ... Found it!
-> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
Wheel revolution data supported
Crank revolution data supported
Discovering Client CP Sensor Location characteristic ... Found it!
-> Client Reads CP Location Sensor: Loc#: 0 Other
Discovering Cycling Speed and Cadence (CSC) Service ... Not Found and disconnecting!
CSC Service is mandatory!
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Stopped!

Here are the screenshots with nrf connect:

Screenshot_20221210-132431_nRF Connect
Screenshot_20221210-132214_nRF Connect
Screenshot_20221210-132222_nRF Connect
Screenshot_20221210-132412_nRF Connect

Sincerely,

Joel

@le-joebar
Copy link

le-joebar commented Dec 10, 2022

I don't know if this can't help you? :
Conect nrf debug mode log

nRF Connect, 2022-12-10
Zwift Hub (F8:9C:FC:53:5E:49)
V 16:01:11.439 Connecting to F8:9C:FC:53:5E:49...
D 16:01:11.439 gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, preferred PHY = LE 1M)
D 16:01:11.697 [Callback] Connection state changed with status: 0 and new state: CONNECTED (2)
I 16:01:11.697 Connected to F8:9C:FC:53:5E:49
V 16:01:11.702 Discovering services...
D 16:01:11.702 gatt.discoverServices()
D 16:01:11.728 [Broadcast] Action received: android.bluetooth.device.action.ACL_CONNECTED
I 16:01:12.541 Connection parameters updated (interval: 7.5ms, latency: 0, timeout: 5000ms)
D 16:01:15.576 [Callback] Services discovered with status: 0
I 16:01:15.577 Services discovered
V 16:01:15.615 Generic Access (0x1800)

  • Device Name [R W] (0x2A00)
  • Appearance [R] (0x2A01)
  • Peripheral Preferred Connection Parameters [R] (0x2A04)
  • Central Address Resolution [R] (0x2AA6)
    Generic Attribute (0x1801)
  • Service Changed [I] (0x2A05)
    Client Characteristic Configuration (0x2902)
    Fitness Machine (0x1826)
  • Indoor Bike Data [N] (0x2AD2)
    Client Characteristic Configuration (0x2902)
  • Fitness Machine Feature [R] (0x2ACC)
  • Supported Resistance Level Range [R] (0x2AD6)
  • Supported Power Range [R] (0x2AD8)
  • Fitness Machine Status [N] (0x2ADA)
    Client Characteristic Configuration (0x2902)
  • Fitness Machine Control Point [I W] (0x2AD9)
    Client Characteristic Configuration (0x2902)
    Cycling Speed and Cadence (0x1816)
  • CSC Measurement [N] (0x2A5B)
    Client Characteristic Configuration (0x2902)
  • CSC Feature [R] (0x2A5C)
  • Sensor Location [R] (0x2A5D)
  • SC Control Point [I W] (0x2A55)
    Client Characteristic Configuration (0x2902)
    Cycling Power (0x1818)
  • Cycling Power Measurement [N] (0x2A63)
    Client Characteristic Configuration (0x2902)
  • Cycling Power Feature [R] (0x2A65)
  • Sensor Location [R] (0x2A5D)
    Secure DFU Service (0xFE59)
  • Buttonless DFU [I W] (8ec90003-f315-4f60-9fb8-838830daea50)
    Client Characteristic Configuration (0x2902)
    Heart Rate (0x180D)
  • Heart Rate Measurement [N] (0x2A37)
    Client Characteristic Configuration (0x2902)
  • Body Sensor Location [R] (0x2A38)
    Unknown Service (c4630001-003f-4cec-8994-e489b04d857f)
  • Unknown Characteristic [R] (c4632b03-003f-4cec-8994-e489b04d857f)
  • Unknown Characteristic [N R] (c4632b05-003f-4cec-8994-e489b04d857f)
    Client Characteristic Configuration (0x2902)
    Characteristic User Description (0x2901)
  • Unknown Characteristic [I W] (c4632b04-003f-4cec-8994-e489b04d857f)
    Client Characteristic Configuration (0x2902)
    Device Information (0x180A)
  • Manufacturer Name String [R] (0x2A29)
  • Model Number String [R] (0x2A24)
  • Serial Number String [R] (0x2A25)
  • Hardware Revision String [R] (0x2A27)
  • Firmware Revision String [R] (0x2A26)
    D 16:01:15.616 gatt.setCharacteristicNotification(00002a05-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.621 gatt.setCharacteristicNotification(00002ad2-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.624 gatt.setCharacteristicNotification(00002ada-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.627 gatt.setCharacteristicNotification(00002ad9-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.630 gatt.setCharacteristicNotification(00002a5b-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.632 gatt.setCharacteristicNotification(00002a55-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.635 gatt.setCharacteristicNotification(00002a63-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.637 gatt.setCharacteristicNotification(00002a37-0000-1000-8000-00805f9b34fb, true)
    D 16:01:15.640 gatt.setCharacteristicNotification(c4632b05-003f-4cec-8994-e489b04d857f, true)
    D 16:01:15.643 gatt.setCharacteristicNotification(c4632b04-003f-4cec-8994-e489b04d857f, true)
    I 16:01:15.766 Connection parameters updated (interval: 47.5ms, latency: 0, timeout: 5000ms)
    I 16:01:18.099 Connection parameters updated (interval: 142.5ms, latency: 0, timeout: 4000ms)
    I 16:01:51.051 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 01-94-00-00-00-B7-28
    A 16:01:51.051 "Wheel rev: 148,
    Last wheel event time: 10423 ms" received
    I 16:01:52.035 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 01-96-00-00-00-E6-2D
    A 16:01:52.035 "Wheel rev: 150,
    Last wheel event time: 11750 ms" received
    I 16:01:53.032 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 01-98-00-00-00-79-32
    A 16:01:53.032 "Wheel rev: 152,
    Last wheel event time: 12921 ms" received
    I 16:01:54.032 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 01-99-00-00-00-C8-34
    A 16:01:54.032 "Wheel rev: 153,
    Last wheel event time: 13512 ms" received
    I 16:01:54.887 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 01-9B-00-00-00-71-39
    A 16:01:54.887 "Wheel rev: 155,
    Last wheel event time: 14705 ms" received
    I 16:01:56.023 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 03-9D-00-00-00-2F-3E-3C-00-FA-3A
    A 16:01:56.023 "Wheel rev: 157,
    Last wheel event time: 15919 ms,
    Crank rev: 60,
    Last crank event time: 15098 ms" received
    I 16:01:56.883 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 03-9E-00-00-00-88-40-3D-00-C5-3F
    A 16:01:56.883 "Wheel rev: 158,
    Last wheel event time: 16520 ms,
    Crank rev: 61,
    Last crank event time: 16325 ms" received
    I 16:01:57.878 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 03-A0-00-00-00-30-45-3E-00-2F-44
    A 16:01:57.878 "Wheel rev: 160,
    Last wheel event time: 17712 ms,
    Crank rev: 62,
    Last crank event time: 17455 ms" received
    I 16:01:59.017 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 03-A2-00-00-00-B1-49-3F-00-7A-48
    A 16:01:59.017 "Wheel rev: 162,
    Last wheel event time: 18865 ms,
    Crank rev: 63,
    Last crank event time: 18554 ms" received
    I 16:02:00.017 Notification received from 00002a5b-0000-1000-8000-00805f9b34fb, value: (0x) 03-A4-00-00-00-18-4E-40-00-b-0000-1000-8000-00805f9b34fb, value: (0x) 03-B8-00-00-00-9E-A4-49-00-

@le-joebar
Copy link

I found this program:
https://github.com/arduino-libraries/ArduinoBLE/blob/master/examples/Central/PeripheralExplorer/PeripheralExplorer.ino

for ESP32 BLE of which I did the test and here is the result of the services :

ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13864
load:0x40080400,len:3608
entry 0x400805f0
Bluetooth�� Low Energy Central - Peripheral Explorer
Found 64:6c:58:a5:76:71 '' fe9f
Found 60:1e:b5:71:b8:93 '' fe9f
Found f8:9c:fc:53:5e:49 'Zwift Hub' 1816
Connecting ...
Connected
Discovering attributes ...
Attributes discovered

Device name: Zwift Hub
Appearance: 0x0

Service 1800
Characteristic 2a00, properties 0xA, value 0x5A7769667420487562
Descriptor 2803, value 0x020500012A
Descriptor 2a01, value 0x8004
Characteristic 2a01, properties 0x2, value 0x8004
Descriptor 2803, value 0x020700042A
Descriptor 2a04, value 0x4800780000009001
Characteristic 2a04, properties 0x2, value 0x4800780000009001
Descriptor 2803, value 0x020900A62A
Descriptor 2aa6, value 0x01
Characteristic 2aa6, properties 0x2, value 0x01
Service 1801
Characteristic 2a05, properties 0x20
Descriptor 2902, value 0x0000
Service 1826
Characteristic 2ad2, properties 0x10
Descriptor 2902, value 0x0000
Descriptor 2803, value 0x021300CC2A
Descriptor 2acc, value 0x874400000CE00000
Characteristic 2acc, properties 0x2, value 0x874400000CE00000
Descriptor 2803, value 0x021500D62A
Descriptor 2ad6, value 0x000064000100
Characteristic 2ad6, properties 0x2, value 0x000064000100
Descriptor 2803, value 0x021700D82A
Descriptor 2ad8, value 0x0000E8030100
Characteristic 2ad8, properties 0x2, value 0x0000E8030100
Descriptor 2803, value 0x101900DA2A
Descriptor 2ada, value 0x
Characteristic 2ada, properties 0x10
Descriptor 2902, value 0x0000
Descriptor 2803, value 0x281C00D92A
Descriptor 2ad9, value 0x
Characteristic 2ad9, properties 0x28
Descriptor 2902, value 0x0000
Service 1816
Characteristic 2a5b, properties 0x10
Descriptor 2902, value 0x0000
Descriptor 2803, value 0x0223005C2A
Descriptor 2a5c, value 0x0300
Characteristic 2a5c, properties 0x2, value 0x0300
Descriptor 2803, value 0x0225005D2A
Descriptor 2a5d, value 0x0C
Characteristic 2a5d, properties 0x2, value 0x0C
Descriptor 2803, value 0x282700552A
Descriptor 2a55, value 0x
Characteristic 2a55, properties 0x28
Descriptor 2902, value 0x0000
Service 1818
Characteristic 2a63, properties 0x10
Descriptor 2902, value 0x0000
Descriptor 2803, value 0x022E00652A
Descriptor 2a65, value 0x0C000000
Characteristic 2a65, properties 0x2, value 0x0C000000
Descriptor 2803, value 0x0230005D2A
Descriptor 2a5d, value 0x0C
Characteristic 2a5d, properties 0x2, value 0x0C
Service fe59
Characteristic 8ec90003-f315-4f60-9fb8-838830daea50, properties 0x28
Descriptor 2902, value 0x0000
Service 180d
Characteristic 2a37, properties 0x10
Descriptor 2902, value 0x0000
Descriptor 2803, value 0x023A00382A
Descriptor 2a38, value 0x01
Characteristic 2a38, properties 0x2, value 0x01
Service c4630001-003f-4cec-8994-e489b04d857f
Characteristic c4632b03-003f-4cec-8994-e489b04d857f, properties 0x2, value 0xFF0F0000
Descriptor 2803, value 0x123F007F854DB089E49489EC4C3F00052B63C4
Descriptor 857f, value 0x0000000000000000000000000000000000000000
Descriptor 4cec, value 0x
Descriptor 470a, value 0x
Characteristic c4632b05-003f-4cec-8994-e489b04d857f, properties 0x12, value 0x0000000000000000000000000000000000000000
Descriptor 2902, value 0x0000
Descriptor 2901, value 0x537461747573
Descriptor 2803, value 0x2843007F854DB089E49489EC4C3F00042B63C4
Descriptor 857f, value 0x
Descriptor 4cec, value 0x
Descriptor 470a, value 0x
Characteristic c4632b04-003f-4cec-8994-e489b04d857f, properties 0x28
Descriptor 2902, value 0x0000
Service 180a
Characteristic 2a29, properties 0x2, value 0x5A77696674
Descriptor 2803, value 0x024900242A
Descriptor 2a24, value 0x3036
Characteristic 2a24, properties 0x2, value 0x3036
Descriptor 2803, value 0x024B00252A
Descriptor 2a25, value 0x30362D463839434643353335453439
Characteristic 2a25, properties 0x2, value 0x30362D463839434643353335453439
Descriptor 2803, value 0x024D00272A
Descriptor 2a27, value 0x31
Characteristic 2a27, properties 0x2, value 0x31
Descriptor 2803, value 0x024F00262A
Descriptor 2a26, value 0x332E39
Characteristic 2a26, properties 0x2, value 0x332E39

Disconnecting ...
Disconnected

@Berg0162
Copy link
Owner Author

Dear Joel,
Only this Sunday morning I had time to look into your post.....
Wow! Super! That is a wealth of information!
This is more than one could have asked for, what a pity we did not start this way....
I have to study it all carefully and that will take some time!

One thing is immediately striking at my first scan:

And here is the second if just after turning on I pedal to wake up the services.

I have only met trainers that are awake all the time, may be after 20 minutes go to sleep, but never within seconds/minutes...
CPS (1818), CSC (1816) and FTMS (1826) are all present, NO DOUBT! I have to check if all the Chars are exposed and if there is a discrepancy in what the Client is instructed to be Mandatory and what the Zwift Hub is programmed with !!!
This explains definitely the behavior in all of the runs.
I will be back soon!
Thanks again, this will bring the solution!
Regards,
Jörgen.

@Berg0162
Copy link
Owner Author

Let's give Client_FTMS version v025 a try!
I have changed several Service Characteristics to NOT Mandatory, hopefully this is now fully in accordance with the Zwift Hub settings....! (This is quite different from FTMS trainers I have met so far, but no reason for worries!)
Procedure is changed: run Feather code first and then start and wake up your trainer (we know now this is critical!)

    1. Upload and Run this code on the Feather-nRF52
    1. Start the Serial Monitor to catch verbose debugging and data info
    1. Power ON and Wake UP trainer -> do NOT connect with other devices

Fingers crossed!

@le-joebar
Copy link

Dear jörghen,

Here are the results, You should know that the first attempt was a failure. The Zwift Hub is very temperamental to wake up.
On the second I had to hurry to wake him up or else I don't know what happened. But he woke up at just the right time!

Unfortunately I didn't copy everything because the serial monitor had already been closed!!!!

I'm trying to reproduce but I can't!!!

Feather nRF52 Client/Central: CPS, CSC and FTMS
----------------- Version 02.5 ------------------
Initialise the Bluefruit nRF52 module: Client (Central)
FTMS and Chars 'initialized'
CPS and Chars 'initialized'
CSCS and Chars 'initialized'
GA and Chars 'initialized'
DIS and Chars 'initialized'
Start Scanning for CPS, CSC and FTMS!
Found advertising Peripheral with FTMS service!, see the Raw Data packet:
Timestamp MAC Address Rssi Data
000021991 F8:9C:FC:53:5E:49 -69 09-02-16-18-26-18-18-18-0A-18
Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
Now checking all Client Services and Characteristics!
If Mandatory Services Fail --> the Client will disconnect!
First checking Generic Access and Device Information Services and Characteristics!
Found Client Generic Access
-> Client Reads Device Name: [Zwift Hub]
-> Client Reads Appearance: [0]
Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
Disconnecting since Client FTM Service is mandatory!
Client Disconnected, reason = 0x16

Restart the Feather nRF52 Client for a new run! <<<
Stopped!

15:54:24.551 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
15:54:26.523 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
15:54:26.643 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:26.643 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
15:54:28.645 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
15:54:28.694 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:28.694 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
15:54:30.682 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
15:54:30.915 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:30.915 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
15:54:32.764 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
15:54:32.888 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:32.888 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
15:54:34.867 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
15:54:35.131 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:35.131 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
15:54:36.980 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
15:54:37.072 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:37.072 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
15:54:39.043 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
15:54:39.168 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:39.168 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
15:54:41.168 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
15:54:41.261 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:41.261 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
15:54:43.265 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
15:54:43.310 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:43.310 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
15:54:45.302 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
15:54:45.361 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:45.382 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]
15:54:47.333 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 B8 FF 28 33 00 ]
15:54:47.519 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:47.519 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 B8 FF 28 33 ]
15:54:49.443 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
15:54:49.506 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:49.552 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
15:54:51.551 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A2 00 28 33 00 ]
15:54:51.627 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:51.627 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A2 00 28 33 ]
15:54:53.625 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 01 28 33 00 ]
15:54:53.658 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:53.658 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 01 28 33 ]
15:54:55.690 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 4F 01 28 33 00 ]
15:54:55.768 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:55.768 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 4F 01 28 33 ]
15:54:57.765 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 01 28 33 00 ]
15:54:58.198 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:58.198 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 01 28 33 ]
15:54:59.858 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 57 FF 28 33 00 ]
15:54:59.915 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:54:59.915 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 57 FF 28 33 ]
15:55:01.902 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 08 00 00 00 00 00 00 00 ]
15:55:02.041 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 08 03 ]
15:55:04.002 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 01 00 00 00 00 00 00 00 ]
15:55:04.081 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 01 01 ]
15:55:04.129 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
15:55:06.110 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
15:55:06.186 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
15:55:08.190 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
15:55:08.236 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
15:55:10.225 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
15:55:10.537 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
15:55:10.537 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
15:55:12.340 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
15:55:12.466 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
15:55:12.511 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]

@le-joebar
Copy link

le-joebar commented Dec 11, 2022

Now I managed to get the full serial but I don't know how I woke up the Zwift hub.

I can't reproduce every time!!!!

16:35:25.068 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
16:35:25.068 -> ----------------- Version 02.5 ------------------
16:35:25.068 -> Initialise the Bluefruit nRF52 module: Client (Central)
16:35:25.071 -> FTMS and Chars 'initialized'
16:35:25.071 -> CPS and Chars 'initialized'
16:35:25.071 -> CSCS and Chars 'initialized'
16:35:25.071 -> GA and Chars 'initialized'
16:35:25.071 -> DIS and Chars 'initialized'
16:35:25.071 -> Start Scanning for CPS, CSC and FTMS!
16:35:25.071 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
16:35:25.071 -> Timestamp MAC Address Rssi Data
16:35:25.071 -> 000000717 F8:9C:FC:53:5E:49 -64 09-02-16-18-26-18-18-18-0A-18
16:35:25.071 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
16:35:25.071 -> Now checking all Client Services and Characteristics!
16:35:25.071 -> If Mandatory Services Fail --> the Client will disconnect!
16:35:25.071 -> First checking Generic Access and Device Information Services and Characteristics!
16:35:25.229 -> Found Client Generic Access
16:35:25.229 -> -> Client Reads Device Name: [Zwift Hub]
16:35:25.231 -> -> Client Reads Appearance: [1152]
16:35:25.231 -> Found Client Device Information
16:35:25.242 -> -> Client Reads Manufacturer: [Zwift]
16:35:25.242 -> -> Client Reads Model Number: [06]
16:35:25.378 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:35:25.378 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
16:35:25.425 -> Discovering Client FTM Feature Characteristic ... Found it!
16:35:25.832 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
16:35:25.832 -> Discovering Client FTM Control Point Characteristic ... Found it!
16:35:26.374 -> Discovering Client FTM Status Characteristic ... Found it!
16:35:26.374 -> Discovering Client FTM Training Status Characteristic ... Ready to receive Client FTM Control Point Response Messages
16:35:26.409 -> Ready to receive Client FTM Status values
16:35:26.409 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
16:35:26.409 -> >>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.
16:35:26.409 -> >>> Couldn't enable notify for Client CP Measurement Characteristic.
16:35:26.409 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
16:35:26.409 -> >>> Couldn't enable notify for Client CSC Measurement Characteristic.
16:35:26.409 -> Client (Central) is Up and Running!
16:35:26.595 -> Not Found! Not Mandatory
16:35:26.595 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
16:35:26.750 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:35:26.750 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
16:35:27.058 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:35:27.058 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
16:35:27.291 -> Discovering Client Cycling Power (CP) Service ... Found it! CPS Max Payload: 20 Data Length: 27
16:35:27.383 -> Discovering Client CP Measurement characteristic ... Found it!
16:35:27.554 -> Discovering Client CP Control Point characteristic ... Not Found! NOT Mandatory!
16:35:27.816 -> Discovering Client CP Feature characteristic ... Found it!
16:35:27.987 -> -> Client Reads Raw CP Feature bytes: [4] [ 00 00 00 00 ]
16:35:27.987 -> Discovering Client CP Sensor Location characteristic ... NOT Found! NOT Mandatory!
16:35:27.987 -> Discovering Cycling Speed and Cadence (CSC) Service ... Not Found! CSC Service is Not Mandatory!
16:35:28.391 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
16:35:28.513 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
16:35:30.519 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
16:35:30.597 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
16:35:32.596 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
16:35:32.673 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
16:35:32.673 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
16:35:34.658 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
16:35:34.734 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:34.769 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
16:35:36.711 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
16:35:36.789 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:36.789 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
16:35:38.772 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
16:35:39.099 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:39.099 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
16:35:40.880 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
16:35:40.958 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:40.958 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
16:35:42.973 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
16:35:43.066 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:43.066 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
16:35:45.077 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
16:35:45.154 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:45.154 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
16:35:47.144 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
16:35:47.470 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:47.470 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
16:35:49.244 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
16:35:49.415 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:49.415 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
16:35:51.357 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
16:35:51.481 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:51.481 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
16:35:53.451 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
16:35:53.523 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:53.523 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
16:35:55.537 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
16:35:55.583 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:55.583 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
16:35:57.562 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
16:35:57.700 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:57.700 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
16:35:59.685 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
16:35:59.809 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:35:59.809 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
16:36:01.790 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
16:36:01.931 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:36:01.931 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
16:36:03.871 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
16:36:03.948 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
16:36:03.949 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 11, 2022 via email

@le-joebar
Copy link

At the first test I turned the pedals with my hand and I would say yes, I think it was decreasing! On the second test I did not pay attention!

@Berg0162
Copy link
Owner Author

Dear Joel,
It looks like a successful run now! Feather and Trainer are connected, services/characteristics were detected or not. Focus was until now on establishing the connection, but what I am still missing is the response of the trainer to the resistance simulation parameters that the client is regularly sending...and how these data are decoded by the Feather..... This is critical information!

  1. Move the trainer pedals and notice/feel changes in resistance...
    The Client sends Resistance Parameters to the Trainer that coincide
    with the first 5 minutes of the Zwift Volcano Circuit!
  2. Inspect the info presented by Serial Monitor.....

This is what I miss in the output: -> Client Rec'd Raw FTM Indoor Bike Data: [##] [#####################]
Have a careful look at the complete debug output file that I have uploaded and notice the comment (physical experience) at the start of the tester's output file! If you want to be sure communication is working flawless you have to do the pedaling on the bike, Zwift Hub is clearly not doing it by itself! Or it is simply NOT working.... How do we know?
See Output
Good luck!
Jörgen.

@le-joebar
Copy link

Hello Jorghen,

Here are the results after several connection attempts I tried several ways to connect and wake up the services.

I have never managed to do it again 2 times in a row!!!

So I don't know how to make a successful connection!!!

But we have successful results!! And I really felt the different resistances.

20:01:34.256 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
20:01:34.256 -> ----------------- Version 02.5 ------------------
20:01:34.256 -> Initialise the Bluefruit nRF52 module: Client (Central)
20:01:34.348 -> FTMS and Chars 'initialized'
20:01:34.348 -> CPS and Chars 'initialized'
20:01:34.348 -> CSCS and Chars 'initialized'
20:01:34.348 -> GA and Chars 'initialized'
20:01:34.348 -> DIS and Chars 'initialized'
20:01:34.348 -> Start Scanning for CPS, CSC and FTMS!
20:01:35.186 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
20:01:35.186 -> Timestamp MAC Address Rssi Data
20:01:35.186 -> 000002583 F8:9C:FC:53:5E:49 -69 09-02-16-18-26-18-18-18-0A-18
20:01:35.277 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
20:01:35.277 -> Now checking all Client Services and Characteristics!
20:01:35.277 -> If Mandatory Services Fail --> the Client will disconnect!
20:01:35.277 -> First checking Generic Access and Device Information Services and Characteristics!
20:01:35.494 -> Found Client Generic Access
20:01:35.494 -> -> Client Reads Device Name: [Zwift Hub]
20:01:35.815 -> -> Client Reads Appearance: [1152]
20:01:35.815 -> Found Client Device Information
20:01:36.219 -> -> Client Reads Manufacturer: [Zwift]
20:01:36.219 -> -> Client Reads Model Number: [06]
20:01:36.389 -> -> Client Reads Serial Number: [06-F89CFC535E49]
20:01:36.389 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
20:01:36.467 -> Discovering Client FTM Feature Characteristic ... Found it!
20:01:36.653 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
20:01:36.653 -> Discovering Client FTM Control Point Characteristic ... Not Found!
20:01:36.653 -> Disconnecting since Client FTM Control Point Characteristic is mandatory!
20:01:36.699 -> Client Disconnected, reason = 0x16
20:01:36.699 -> >>> Restart the Feather nRF52 Client for a new run! <<<
20:01:37.689 -> Stopped!

20:02:38.186 -> FTMS and Chars 'initialized'
20:02:38.186 -> CPS and Chars 'initialized'
20:02:38.186 -> CSCS and Chars 'initialized'
20:02:38.186 -> GA and Chars 'initialized'
20:02:38.186 -> DIS and Chars 'initialized'
20:02:38.186 -> Start Scanning for CPS, CSC and FTMS!
20:02:38.186 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
20:02:38.186 -> Timestamp MAC Address Rssi Data
20:02:38.186 -> 000001612 F8:9C:FC:53:5E:49 -67 09-02-16-18-26-18-18-18-0A-18
20:02:38.418 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [] MAC Address: F8:9C:FC:53:5E:49
20:02:38.418 -> Now checking all Client Services and Characteristics!
20:02:38.418 -> If Mandatory Services Fail --> the Client will disconnect!
20:02:38.418 -> First checking Generic Access and Device Information Services and Characteristics!
20:02:38.418 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
20:02:38.418 -> Disconnecting since Client FTM Service is mandatory!
20:02:38.418 -> Client Disconnected, reason = 0x3E
20:02:38.418 -> >>> Restart the Feather nRF52 Client for a new run! <<<
20:02:39.988 -> Stopped!

20:03:28.928 -> FTMS and Chars 'initialized'
20:03:28.928 -> CPS and Chars 'initialized'
20:03:28.928 -> CSCS and Chars 'initialized'
20:03:28.928 -> GA and Chars 'initialized'
20:03:28.928 -> DIS and Chars 'initialized'
20:03:28.928 -> Start Scanning for CPS, CSC and FTMS!
20:03:28.928 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
20:03:28.928 -> Timestamp MAC Address Rssi Data
20:03:28.928 -> 000001315 F8:9C:FC:53:5E:49 -67 09-02-16-18-26-18-18-18-0A-18
20:03:28.943 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [] MAC Address: F8:9C:FC:53:5E:49
20:03:28.943 -> Now checking all Client Services and Characteristics!
20:03:28.943 -> If Mandatory Services Fail --> the Client will disconnect!
20:03:28.943 -> First checking Generic Access and Device Information Services and Characteristics!
20:03:28.943 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
20:03:28.943 -> Disconnecting since Client FTM Service is mandatory!
20:03:28.943 -> Client Disconnected, reason = 0x3E
20:03:28.943 -> >>> Restart the Feather nRF52 Client for a new run! <<<
20:03:30.705 -> Stopped!

20:15:52.131 -> ----------------- Version 02.5 ------------------
20:15:52.131 -> Initialise the Bluefruit nRF52 module: Client (Central)
20:15:52.135 -> FTMS and Chars 'initialized'
20:15:52.135 -> CPS and Chars 'initialized'
20:15:52.135 -> CSCS and Chars 'initialized'
20:15:52.135 -> GA and Chars 'initialized'
20:15:52.135 -> DIS and Chars 'initialized'
20:15:52.135 -> Start Scanning for CPS, CSC and FTMS!
20:16:41.655 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
20:16:41.655 -> Timestamp MAC Address Rssi Data
20:16:41.655 -> 000052631 F8:9C:FC:53:5E:49 -63 09-02-16-18-26-18-18-18-0A-18
20:16:41.870 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
20:16:41.870 -> Now checking all Client Services and Characteristics!
20:16:41.870 -> If Mandatory Services Fail --> the Client will disconnect!
20:16:41.870 -> First checking Generic Access and Device Information Services and Characteristics!
20:16:42.087 -> Found Client Generic Access
20:16:42.087 -> -> Client Reads Device Name: [Zwift Hub]
20:16:42.351 -> -> Client Reads Appearance: [1152]
20:16:42.351 -> Found Client Device Information
20:16:42.816 -> -> Client Reads Manufacturer: [Zwift]
20:16:42.816 -> -> Client Reads Model Number: [06]
20:16:42.986 -> -> Client Reads Serial Number: [06-F89CFC535E49]
20:16:42.987 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
20:16:43.112 -> Discovering Client FTM Feature Characteristic ... Found it!
20:16:43.249 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
20:16:43.249 -> Discovering Client FTM Control Point Characteristic ... Found it!
20:16:43.974 -> Discovering Client FTM Status Characteristic ... Found it!
20:16:43.974 -> Discovering Client FTM Training Status Characteristic ... Ready to receive Client FTM Control Point Response Messages
20:16:44.310 -> Ready to receive Client FTM Status values
20:16:44.310 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
20:16:44.310 -> >>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.
20:16:44.310 -> >>> Couldn't enable notify for Client CP Measurement Characteristic.
20:16:44.310 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
20:16:44.310 -> >>> Couldn't enable notify for Client CSC Measurement Characteristic.
20:16:44.310 -> Client (Central) is Up and Running!
20:16:44.346 -> Not Found! Not Mandatory
20:16:44.346 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
20:16:44.516 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
20:16:44.516 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
20:16:44.870 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
20:16:44.870 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
20:16:45.056 -> Discovering Client Cycling Power (CP) Service ... Found it! CPS Max Payload: 20 Data Length: 27
20:16:45.149 -> Discovering Client CP Measurement characteristic ... Found it!
20:16:45.334 -> Discovering Client CP Control Point characteristic ... Not Found! NOT Mandatory!
20:16:45.569 -> Discovering Client CP Feature characteristic ... Found it!
20:16:45.752 -> -> Client Reads Raw CP Feature bytes: [4] [ 00 00 00 00 ]
20:16:45.752 -> Discovering Client CP Sensor Location characteristic ... NOT Found! NOT Mandatory!
20:16:45.752 -> Discovering Cycling Speed and Cadence (CSC) Service ... Not Found! CSC Service is Not Mandatory!
20:16:46.309 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:16:46.401 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:16:48.356 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:16:48.450 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:16:50.443 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
20:16:50.475 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
20:16:50.475 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
20:16:52.514 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
20:16:52.546 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:16:52.546 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
20:16:54.573 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
20:16:54.757 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:16:54.803 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
20:16:56.652 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:16:56.839 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:16:56.839 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:16:58.772 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
20:16:58.943 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:16:58.943 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
20:17:00.869 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
20:17:00.961 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:00.961 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
20:17:02.957 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
20:17:03.052 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:03.052 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
20:17:05.040 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
20:17:05.072 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:05.072 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
20:17:07.112 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
20:17:07.342 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:07.342 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
20:17:09.211 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
20:17:09.289 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:09.289 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
20:17:11.266 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:17:11.360 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:11.360 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:17:13.362 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
20:17:13.473 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:13.473 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
20:17:15.452 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
20:17:15.545 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:15.545 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
20:17:17.539 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
20:17:17.571 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:17.571 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
20:17:19.604 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
20:17:19.715 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:19.715 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
20:17:21.694 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
20:17:21.834 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:21.834 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
20:17:23.799 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
20:17:24.109 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:24.109 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
20:17:25.913 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
20:17:26.007 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:26.007 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
20:17:28.006 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
20:17:28.053 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:28.053 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
20:17:30.047 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
20:17:30.111 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:30.111 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
20:17:32.153 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
20:17:32.230 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:32.230 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]
20:17:34.207 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 B8 FF 28 33 00 ]
20:17:34.377 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:34.377 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 B8 FF 28 33 ]
20:17:36.308 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:17:36.433 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:36.433 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:17:38.431 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A2 00 28 33 00 ]
20:17:38.510 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:38.510 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A2 00 28 33 ]
20:17:40.534 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 01 28 33 00 ]
20:17:40.643 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:40.643 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 01 28 33 ]
20:17:42.606 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 4F 01 28 33 00 ]
20:17:42.682 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:42.682 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 4F 01 28 33 ]
20:17:44.682 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 01 28 33 00 ]
20:17:44.852 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:44.852 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 01 28 33 ]
20:17:46.768 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 57 FF 28 33 00 ]
20:17:46.845 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:46.845 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 57 FF 28 33 ]
20:17:48.847 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 08 00 00 00 00 00 00 00 ]
20:17:49.112 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 08 03 ]
20:17:50.939 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 01 00 00 00 00 00 00 00 ]
20:17:51.063 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 01 01 ]
20:17:51.063 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
20:17:53.041 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:17:53.088 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:17:55.102 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:17:55.195 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:17:57.185 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
20:17:57.510 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
20:17:57.510 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
20:17:59.284 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
20:17:59.331 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:17:59.331 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
20:18:01.351 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
20:18:01.567 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:01.567 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
20:18:03.450 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:18:03.528 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:03.528 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:18:05.512 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
20:18:05.590 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:05.590 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
20:18:07.627 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
20:18:07.673 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:07.673 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
20:18:09.683 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
20:18:09.730 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:09.730 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
20:18:11.708 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
20:18:11.802 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:11.802 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
20:18:13.746 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
20:18:14.072 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:14.072 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
20:18:15.867 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
20:18:15.945 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:15.945 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
20:18:17.918 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:18:18.089 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:18.089 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:18:20.050 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
20:18:20.096 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:20.096 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
20:18:22.115 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
20:18:22.207 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:22.207 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
20:18:24.202 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
20:18:24.249 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:24.249 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
20:18:26.259 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
20:18:26.508 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:26.508 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
20:18:28.349 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
20:18:28.551 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:28.551 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
20:18:30.463 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
20:18:30.604 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:30.604 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
20:18:32.565 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
20:18:32.643 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:32.643 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
20:18:34.646 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
20:18:34.678 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:34.678 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
20:18:36.693 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
20:18:36.769 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:36.769 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
20:18:38.750 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
20:18:39.060 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:39.060 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]
20:18:40.868 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 B8 FF 28 33 00 ]
20:18:41.054 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:41.054 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 B8 FF 28 33 ]
20:18:42.968 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:18:43.187 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:43.187 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:18:45.072 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A2 00 28 33 00 ]
20:18:45.149 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:45.149 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A2 00 28 33 ]
20:18:47.124 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 01 28 33 00 ]
20:18:47.248 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:47.248 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 01 28 33 ]
20:18:49.238 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 4F 01 28 33 00 ]
20:18:49.332 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:49.332 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 4F 01 28 33 ]
20:18:51.311 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 01 28 33 00 ]
20:18:51.452 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:51.452 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 01 28 33 ]
20:18:53.427 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 57 FF 28 33 00 ]
20:18:53.613 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:18:53.613 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 57 FF 28 33 ]
20:18:55.519 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 08 00 00 00 00 00 00 00 ]
20:18:55.550 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 08 03 ]
20:18:57.571 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 01 00 00 00 00 00 00 00 ]
20:18:57.681 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 01 01 ]
20:18:57.681 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
20:18:59.669 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:18:59.747 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:19:01.734 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:19:01.827 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:19:03.840 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
20:19:04.105 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
20:19:04.105 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
20:19:05.947 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
20:19:06.116 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:06.116 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
20:19:08.042 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
20:19:08.165 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:08.165 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
20:19:10.145 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:19:10.208 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:10.253 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:19:12.243 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
20:19:12.321 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:12.321 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
20:19:14.328 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
20:19:14.406 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:14.406 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
20:19:16.397 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
20:19:16.429 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:16.429 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
20:19:18.464 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
20:19:18.541 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:18.541 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
20:19:20.534 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
20:19:20.783 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:20.783 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
20:19:22.653 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
20:19:22.775 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:22.775 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
20:19:24.753 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:19:24.831 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:24.831 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:19:26.832 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
20:19:26.971 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:26.971 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
20:19:28.926 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
20:19:29.097 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:29.097 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
20:19:31.013 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
20:19:31.106 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:31.106 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
20:19:33.127 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
20:19:33.329 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:33.329 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
20:19:35.219 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
20:19:35.265 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:35.265 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
20:19:37.278 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
20:19:37.356 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:37.356 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
20:19:39.354 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
20:19:39.433 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:39.433 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
20:19:41.425 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
20:19:41.457 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:41.457 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
20:19:43.492 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
20:19:43.723 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:43.723 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
20:19:45.581 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
20:19:45.750 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:45.750 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]
20:19:47.678 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 B8 FF 28 33 00 ]
20:19:47.818 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:47.818 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 B8 FF 28 33 ]
20:19:49.788 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:19:49.865 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:49.865 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:19:51.859 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A2 00 28 33 00 ]
20:19:51.950 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:51.950 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A2 00 28 33 ]
20:19:53.940 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 01 28 33 00 ]
20:19:54.033 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:54.033 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 01 28 33 ]
20:19:56.027 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 4F 01 28 33 00 ]
20:19:56.462 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:56.462 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 4F 01 28 33 ]
20:19:58.105 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 01 28 33 00 ]
20:19:58.275 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:19:58.322 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 01 28 33 ]
20:20:00.213 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 57 FF 28 33 00 ]
20:20:00.290 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:00.337 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 57 FF 28 33 ]
20:20:02.267 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 08 00 00 00 00 00 00 00 ]
20:20:02.360 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 08 03 ]
20:20:04.371 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 01 00 00 00 00 00 00 00 ]
20:20:04.417 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 01 01 ]
20:20:04.417 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
20:20:06.425 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:20:06.457 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:20:08.481 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
20:20:08.527 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
20:20:10.546 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
20:20:10.624 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
20:20:10.624 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
20:20:12.620 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
20:20:12.698 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:12.698 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
20:20:14.712 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
20:20:14.806 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:14.806 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
20:20:16.785 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:20:16.862 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:16.862 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:20:18.871 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
20:20:18.980 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:18.980 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
20:20:20.978 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
20:20:21.070 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:21.070 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
20:20:23.027 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
20:20:23.396 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:23.396 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
20:20:25.145 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
20:20:25.223 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:25.223 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
20:20:27.225 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
20:20:27.365 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:27.365 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
20:20:29.329 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
20:20:29.376 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:29.376 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
20:20:31.379 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
20:20:31.469 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:31.469 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
20:20:33.446 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
20:20:33.569 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:33.569 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
20:20:35.559 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
20:20:35.591 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:35.591 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
20:20:37.619 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
20:20:37.852 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:37.852 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
20:20:39.714 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
20:20:39.977 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:39.977 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
20:20:41.827 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
20:20:41.966 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:41.966 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
20:20:43.925 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
20:20:43.973 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:43.973 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
20:20:45.950 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
20:20:46.091 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:46.091 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
20:20:48.091 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
20:20:48.138 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:48.138 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
20:20:50.141 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
20:20:50.188 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:50.188 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
20:20:52.205 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
20:20:52.513 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
20:20:52.513 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]

Thanks,

Joel

@Berg0162
Copy link
Owner Author

Dear Joel,
This is different from how the Elite Direto is responding, but that seems not to be a problem since you experience changing resistance.
I have uploaded a new version that hopefully accounts for the slightly different timing of the itsybitsy.. (???) and should avoid message cluttering during connection phase.
Furthermore I have introduced a Request for Machine Control in an early stage of the connection procedure, may be this helps waking up your trainer....
Best wishes,
Jörgen.

@le-joebar
Copy link

Hello Jorghen,

I ordered the Feather but it hasn't arrived yet.

I had also started modifying the FTMS_Client_V25 to wait for feedback from everyone's services.

So I'm going to stop and try V.26 tonight.
I'll get back to you with the results.

I received the Bluethoot BLE dongle and tested the FTMS_Server_V.22.
It works so my key is good for the future.

Friendship
Joel,

@le-joebar
Copy link

I looked at your version V.26
I saw you change the time from 2.5 seconds to 5.
Personally I was looking at a different solution which consisted of:
Example:
if ( client_FitnessMachine_Service.discover(conn_handle) )
If false to restart the complete request and this for all the services which must answer!
I don't know if this is the right solution but its a lot of work.
if ( client_FitnessMachine_Service.discover(conn_handle) )
If False to restart the complete request and this for all the services that must respond!
I don't know if this is the right solution but its a lot of work.

@le-joebar
Copy link

Hello Jörghen,

Here are the results:
Unfortunately V0.26 does not work better than the others.
Here is the result of V0.25 modified by me.

And the code below

18:07:13.207 -> GA and Chars 'initialized'
18:07:13.207 -> DIS and Chars 'initialized'
18:07:13.207 -> Start Scanning for CPS, CSC and FTMS!
18:07:13.207 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
18:07:13.207 -> Timestamp MAC Address Rssi Data
18:07:13.207 -> 000001374 F8:9C:FC:53:5E:49 -62 09-02-16-18-26-18-18-18-0A-18
18:07:13.207 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
18:07:13.207 -> Now checking all Client Services and Characteristics!
18:07:13.207 -> If Mandatory Services Fail --> the Client will disconnect!
18:07:13.207 -> First checking Generic Access and Device Information Services and Characteristics!
18:07:13.461 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
18:07:13.461 -> Discovering Client FTM Feature Characteristic ... Discovering Client FTM Control Point Characteristic ... Found it!
18:07:14.047 -> Discovering Client FTM Status Characteristic ... Found it!
18:07:14.601 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
18:07:14.864 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
18:07:15.220 -> Ready to receive Client FTM Control Point Response Messages
18:07:15.267 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
18:07:15.267 -> Discovering Client FTM Supported Power Range Characteristic ... Ready to receive Client FTM Status values
18:07:15.267 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
18:07:15.267 -> >>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.
18:07:15.267 -> >>> Couldn't enable notify for Client CP Measurement Characteristic.
18:07:15.267 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
18:07:15.267 -> >>> Couldn't enable notify for Client CSC Measurement Characteristic.
18:07:15.267 -> Client (Central) is Up and Running!
18:07:15.392 -> Found it!
18:07:15.484 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 00 00 00 00 ]
18:07:15.484 -> Discovering Client FTM Indoor Bike Data Characteristic ... Not Found! Not Mandatory
18:07:15.484 -> Discovering Client Cycling Power (CP) Service ... Not Found!
18:07:15.484 -> Disconnecting since Client Cyling Power Service is mandatory!
18:07:15.531 -> Client Disconnected, reason = 0x16
18:07:15.531 -> >>> Restart the Feather nRF52 Client for a new run! <<<
18:07:26.723 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
18:07:26.723 -> ----------------- Version 02.5 ------------------
18:07:26.723 -> Initialise the Bluefruit nRF52 module: Client (Central)
18:07:26.724 -> FTMS and Chars 'initialized'
18:07:26.724 -> CPS and Chars 'initialized'
18:07:26.724 -> CSCS and Chars 'initialized'
18:07:26.724 -> GA and Chars 'initialized'
18:07:26.724 -> DIS and Chars 'initialized'
18:07:26.724 -> Start Scanning for CPS, CSC and FTMS!
18:07:26.724 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
18:07:26.724 -> Timestamp MAC Address Rssi Data
18:07:26.724 -> 000001192 F8:9C:FC:53:5E:49 -62 09-02-16-18-26-18-18-18-0A-18
18:07:26.743 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
18:07:26.743 -> Now checking all Client Services and Characteristics!
18:07:26.743 -> If Mandatory Services Fail --> the Client will disconnect!
18:07:26.743 -> First checking Generic Access and Device Information Services and Characteristics!
18:07:26.747 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
18:07:26.747 -> Discovering Client FTM Feature Characteristic ... Discovering Client FTM Control Point Characteristic ... Found it!
18:07:27.137 -> Discovering Client FTM Status Characteristic ... Found it!
18:07:27.555 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
18:07:27.912 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
18:07:28.129 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
18:07:28.129 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
18:07:28.346 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
18:07:28.346 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
18:07:28.639 -> Discovering Client Cycling Power (CP) Service ... Ready to receive Client FTM Control Point Response Messages
18:07:28.731 -> Found it! CPS Max Payload: 20 Data Length: 27
18:07:28.731 -> Discovering Client CP Measurement characteristic ... Ready to receive Client FTM Status values
18:07:28.731 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
18:07:28.809 -> Ready to receive Client FTM Indoor Bike Data values
18:07:28.809 -> Found it!
18:07:28.809 -> Discovering Client CP Control Point characteristic ... Ready to receive Client CP Measurement values
18:07:28.809 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
18:07:28.809 -> >>> Couldn't enable notify for Client CSC Measurement Characteristic.
18:07:28.809 -> Client (Central) is Up and Running!
18:07:28.932 -> Not Found! NOT Mandatory!
18:07:28.932 -> Discovering Client CP Feature characteristic ... Found it!
18:07:29.446 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
18:07:29.446 -> Wheel revolution data supported
18:07:29.446 -> Crank revolution data supported
18:07:29.446 -> Discovering Client CP Sensor Location characteristic ... Found it!
18:07:29.646 -> -> Client Reads CP Location Sensor: Loc#: 0 Other
18:07:29.646 -> Discovering Cycling Speed and Cadence (CSC) Service ... Not Found! CSC Service is Not Mandatory!
18:07:29.646 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:29.646 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:30.031 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:30.031 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:30.819 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:07:30.864 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:07:31.036 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:31.036 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:32.006 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:32.006 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:32.870 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:07:32.916 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:07:33.195 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:33.195 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:34.074 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:34.074 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:34.925 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
18:07:35.266 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 00 00 00 00 14 00 00 00 00 ]
18:07:35.266 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:07:35.313 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
18:07:35.313 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
18:07:36.008 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:36.008 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:37.011 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:37.011 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:37.011 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
18:07:37.213 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:37.213 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
18:07:37.972 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:38.018 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:38.984 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:38.984 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:39.110 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
18:07:39.188 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:39.188 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
18:07:39.989 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:39.989 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:41.147 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:41.147 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:41.193 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:07:41.286 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:41.286 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:07:41.978 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 ]
18:07:41.978 -> Speed: 0 KPH Instantaneous Cadence: 0 RPM Average Cadence: 0 RPM Total Distance: 0 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:07:43.072 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 09 05 88 00 00 00 00 00 03 00 00 14 00 78 00 0F 00 00 ]
18:07:43.072 -> Speed: 12 KPH Instantaneous Cadence: 68 RPM Average Cadence: 0 RPM Total Distance: 196608 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 120 Watt Heart Rate: 15 HBM
18:07:43.289 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
18:07:43.321 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:43.321 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
18:07:44.015 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A3 06 1A 01 00 00 00 00 07 00 00 14 00 D5 00 25 00 00 ]
18:07:44.015 -> Speed: 16 KPH Instantaneous Cadence: 141 RPM Average Cadence: 0 RPM Total Distance: 458752 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 213 Watt Heart Rate: 37 HBM
18:07:45.252 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 B5 06 8F 01 00 00 00 00 0B 00 00 14 00 91 00 2F 00 00 ]
18:07:45.252 -> Speed: 17 KPH Instantaneous Cadence: 199 RPM Average Cadence: 0 RPM Total Distance: 720896 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 145 Watt Heart Rate: 47 HBM
18:07:45.344 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
18:07:45.390 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:45.390 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
18:07:46.008 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 15 07 EF 01 36 00 04 00 0F 00 00 14 00 BE 00 3C 00 00 ]
18:07:46.008 -> Speed: 18 KPH Instantaneous Cadence: 247 RPM Average Cadence: 27 RPM Total Distance: 983044 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 190 Watt Heart Rate: 60 HBM
18:07:46.999 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 B4 07 5D 02 56 00 0B 00 14 00 00 14 00 C5 00 48 00 00 ]
18:07:46.999 -> Speed: 19 KPH Instantaneous Cadence: 302 RPM Average Cadence: 43 RPM Total Distance: 1310731 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 197 Watt Heart Rate: 72 HBM
18:07:47.416 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
18:07:47.667 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:47.667 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
18:07:48.011 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 15 08 BA 02 66 00 12 00 19 00 00 14 00 A4 00 4F 00 00 ]
18:07:48.011 -> Speed: 20 KPH Instantaneous Cadence: 349 RPM Average Cadence: 51 RPM Total Distance: 1638418 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 164 Watt Heart Rate: 79 HBM
18:07:49.075 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 6C 08 09 03 70 00 19 00 1E 00 00 14 00 9D 00 54 00 00 ]
18:07:49.075 -> Speed: 21 KPH Instantaneous Cadence: 388 RPM Average Cadence: 56 RPM Total Distance: 1966105 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 157 Watt Heart Rate: 84 HBM
18:07:49.490 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
18:07:49.568 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:49.568 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
18:07:50.015 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 C7 08 67 03 76 00 1F 00 24 00 00 14 00 99 00 59 00 00 ]
18:07:50.015 -> Speed: 22 KPH Instantaneous Cadence: 435 RPM Average Cadence: 59 RPM Total Distance: 2359327 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 153 Watt Heart Rate: 89 HBM
18:07:51.031 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 4D 09 B8 03 7A 00 25 00 2A 00 00 14 00 8E 00 5C 00 00 ]
18:07:51.031 -> Speed: 23 KPH Instantaneous Cadence: 476 RPM Average Cadence: 61 RPM Total Distance: 2752549 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 142 Watt Heart Rate: 92 HBM
18:07:51.570 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
18:07:51.849 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:51.849 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
18:07:51.973 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 70 09 00 04 80 00 2A 00 30 00 00 14 00 89 00 5F 00 00 ]
18:07:51.973 -> Speed: 24 KPH Instantaneous Cadence: 512 RPM Average Cadence: 64 RPM Total Distance: 3145770 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 137 Watt Heart Rate: 95 HBM
18:07:52.992 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 5E 09 40 04 86 00 2F 00 36 00 00 14 00 85 00 61 00 00 ]
18:07:52.992 -> Speed: 23 KPH Instantaneous Cadence: 544 RPM Average Cadence: 67 RPM Total Distance: 3538991 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 133 Watt Heart Rate: 97 HBM
18:07:53.670 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
18:07:53.748 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:53.748 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
18:07:53.978 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 65 09 7A 04 88 00 34 00 3C 00 00 14 00 78 00 62 00 00 ]
18:07:53.978 -> Speed: 24 KPH Instantaneous Cadence: 573 RPM Average Cadence: 68 RPM Total Distance: 3932212 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 120 Watt Heart Rate: 98 HBM
18:07:54.980 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 87 09 AD 04 8A 00 38 00 42 00 00 14 00 70 00 63 00 00 ]
18:07:54.980 -> Speed: 24 KPH Instantaneous Cadence: 598 RPM Average Cadence: 69 RPM Total Distance: 4325432 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 112 Watt Heart Rate: 99 HBM
18:07:55.749 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:07:55.886 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:55.886 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:07:56.009 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 5E 09 DC 04 8A 00 3C 00 48 00 00 14 00 5D 00 62 00 00 ]
18:07:56.009 -> Speed: 23 KPH Instantaneous Cadence: 622 RPM Average Cadence: 69 RPM Total Distance: 4718652 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 93 Watt Heart Rate: 98 HBM
18:07:57.015 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A8 09 06 05 8C 00 40 00 4E 00 00 14 00 70 00 63 00 00 ]
18:07:57.015 -> Speed: 24 KPH Instantaneous Cadence: 643 RPM Average Cadence: 70 RPM Total Distance: 5111872 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 112 Watt Heart Rate: 99 HBM
18:07:57.849 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
18:07:57.927 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:57.927 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
18:07:57.974 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 0A 3D 05 8C 00 43 00 55 00 00 14 00 74 00 64 00 00 ]
18:07:57.974 -> Speed: 25 KPH Instantaneous Cadence: 670 RPM Average Cadence: 70 RPM Total Distance: 5570627 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 116 Watt Heart Rate: 100 HBM
18:07:59.038 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 D4 09 60 05 90 00 46 00 5B 00 00 14 00 7D 00 65 00 00 ]
18:07:59.038 -> Speed: 25 KPH Instantaneous Cadence: 688 RPM Average Cadence: 72 RPM Total Distance: 5963846 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 125 Watt Heart Rate: 101 HBM
18:07:59.950 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
18:07:59.997 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:07:59.997 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
18:07:59.997 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 23 0A 8E 05 92 00 49 00 62 00 00 14 00 75 00 65 00 00 ]
18:07:59.997 -> Speed: 25 KPH Instantaneous Cadence: 711 RPM Average Cadence: 73 RPM Total Distance: 6422601 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 117 Watt Heart Rate: 101 HBM
18:08:01.066 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 0D 0A B9 05 92 00 4C 00 69 00 00 14 00 6B 00 66 00 00 ]
18:08:01.066 -> Speed: 25 KPH Instantaneous Cadence: 732 RPM Average Cadence: 73 RPM Total Distance: 6881356 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 107 Watt Heart Rate: 102 HBM
18:08:01.990 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 4F 0A E1 05 94 00 4E 00 70 00 00 14 00 6C 00 66 00 00 ]
18:08:01.990 -> Speed: 26 KPH Instantaneous Cadence: 752 RPM Average Cadence: 74 RPM Total Distance: 7340110 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 108 Watt Heart Rate: 102 HBM
18:08:01.990 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
18:08:02.113 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:02.113 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
18:08:02.993 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 28 0A 06 06 94 00 51 00 77 00 00 14 00 63 00 66 00 00 ]
18:08:02.993 -> Speed: 26 KPH Instantaneous Cadence: 771 RPM Average Cadence: 74 RPM Total Distance: 7798865 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 99 Watt Heart Rate: 102 HBM
18:08:04.087 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
18:08:04.211 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 62 0A 28 06 98 00 53 00 7E 00 00 14 00 5A 00 65 00 00 ]
18:08:04.211 -> Speed: 26 KPH Instantaneous Cadence: 788 RPM Average Cadence: 76 RPM Total Distance: 8257619 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 90 Watt Heart Rate: 101 HBM
18:08:04.303 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:04.303 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
18:08:04.996 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 2E 0A 48 06 96 00 56 00 85 00 00 14 00 51 00 65 00 00 ]
18:08:04.996 -> Speed: 26 KPH Instantaneous Cadence: 804 RPM Average Cadence: 75 RPM Total Distance: 8716374 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 81 Watt Heart Rate: 101 HBM
18:08:06.088 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 AF 0A 66 06 96 00 58 00 8C 00 00 14 00 52 00 64 00 00 ]
18:08:06.088 -> Speed: 27 KPH Instantaneous Cadence: 819 RPM Average Cadence: 75 RPM Total Distance: 9175128 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 82 Watt Heart Rate: 100 HBM
18:08:06.181 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
18:08:06.303 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:06.303 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
18:08:06.995 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 9B 0A 83 06 A0 00 5A 00 93 00 00 14 00 64 00 64 00 00 ]
18:08:06.995 -> Speed: 27 KPH Instantaneous Cadence: 833 RPM Average Cadence: 80 RPM Total Distance: 9633882 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 100 Watt Heart Rate: 100 HBM
18:08:08.181 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 CA 0A 9D 06 A6 00 5C 00 9A 00 00 14 00 61 00 64 00 00 ]
18:08:08.182 -> Speed: 27 KPH Instantaneous Cadence: 846 RPM Average Cadence: 83 RPM Total Distance: 10092636 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 97 Watt Heart Rate: 100 HBM
18:08:08.274 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
18:08:08.412 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:08.412 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
18:08:08.999 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 0D 0B B6 06 A6 00 5E 00 A1 00 00 14 00 5E 00 64 00 00 ]
18:08:08.999 -> Speed: 28 KPH Instantaneous Cadence: 859 RPM Average Cadence: 83 RPM Total Distance: 10551390 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 94 Watt Heart Rate: 100 HBM
18:08:10.090 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 0E 0B CD 06 A2 00 60 00 A8 00 00 14 00 51 00 63 00 00 ]
18:08:10.138 -> Speed: 28 KPH Instantaneous Cadence: 870 RPM Average Cadence: 81 RPM Total Distance: 11010144 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 81 Watt Heart Rate: 99 HBM
18:08:10.373 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
18:08:10.451 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:10.451 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
18:08:11.013 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 39 0B E4 06 A6 00 62 00 AF 00 00 14 00 58 00 63 00 00 ]
18:08:11.013 -> Speed: 28 KPH Instantaneous Cadence: 882 RPM Average Cadence: 83 RPM Total Distance: 11468898 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 88 Watt Heart Rate: 99 HBM
18:08:11.978 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 6C 0B 02 07 A6 00 64 00 B7 00 00 14 00 50 00 62 00 00 ]
18:08:11.978 -> Speed: 29 KPH Instantaneous Cadence: 897 RPM Average Cadence: 83 RPM Total Distance: 11993188 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 80 Watt Heart Rate: 98 HBM
18:08:12.432 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
18:08:12.668 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:12.668 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
18:08:12.992 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 70 0B 20 07 AA 00 66 00 BF 00 00 14 00 4D 00 62 00 00 ]
18:08:12.993 -> Speed: 29 KPH Instantaneous Cadence: 912 RPM Average Cadence: 85 RPM Total Distance: 12517478 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 77 Watt Heart Rate: 98 HBM
18:08:13.995 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 B7 0B 3B 07 AE 00 68 00 C7 00 00 14 00 50 00 61 00 00 ]
18:08:13.995 -> Speed: 29 KPH Instantaneous Cadence: 925 RPM Average Cadence: 87 RPM Total Distance: 13041768 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 80 Watt Heart Rate: 97 HBM
18:08:14.550 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 6F FF 28 33 00 ]
18:08:14.768 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:14.768 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 6F FF 28 33 ]
18:08:14.984 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A3 0B 56 07 B2 00 6A 00 CF 00 00 14 00 4C 00 61 00 00 ]
18:08:14.984 -> Speed: 29 KPH Instantaneous Cadence: 939 RPM Average Cadence: 89 RPM Total Distance: 13566058 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 76 Watt Heart Rate: 97 HBM
18:08:16.182 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 81 0B 6F 07 BA 00 6B 00 D7 00 00 14 00 39 00 60 00 00 ]
18:08:16.182 -> Speed: 29 KPH Instantaneous Cadence: 951 RPM Average Cadence: 93 RPM Total Distance: 14090347 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 57 Watt Heart Rate: 96 HBM
18:08:16.631 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 8A FF 28 33 00 ]
18:08:16.709 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:16.709 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 8A FF 28 33 ]
18:08:17.003 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 9F 0B 86 07 BC 00 6D 00 DF 00 00 14 00 42 00 5F 00 00 ]
18:08:17.003 -> Speed: 29 KPH Instantaneous Cadence: 963 RPM Average Cadence: 94 RPM Total Distance: 14614637 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 66 Watt Heart Rate: 95 HBM
18:08:18.191 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A0 0B 9D 07 BE 00 6F 00 E7 00 00 14 00 41 00 5E 00 00 ]
18:08:18.191 -> Speed: 29 KPH Instantaneous Cadence: 974 RPM Average Cadence: 95 RPM Total Distance: 15138927 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 65 Watt Heart Rate: 94 HBM
18:08:18.671 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 B8 FF 28 33 00 ]
18:08:18.794 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:18.794 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 B8 FF 28 33 ]
18:08:18.980 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 C3 0B B3 07 B6 00 71 00 EF 00 00 14 00 4E 00 5E 00 00 ]
18:08:18.980 -> Speed: 30 KPH Instantaneous Cadence: 985 RPM Average Cadence: 91 RPM Total Distance: 15663217 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 78 Watt Heart Rate: 94 HBM
18:08:20.077 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 99 0B C7 07 B2 00 72 00 F7 00 00 14 00 46 00 5D 00 00 ]
18:08:20.077 -> Speed: 29 KPH Instantaneous Cadence: 995 RPM Average Cadence: 89 RPM Total Distance: 16187506 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 70 Watt Heart Rate: 93 HBM
18:08:20.803 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:08:20.835 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:20.835 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:08:20.973 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 89 0B DB 07 B2 00 74 00 FF 00 00 14 00 47 00 5D 00 00 ]
18:08:20.973 -> Speed: 29 KPH Instantaneous Cadence: 1005 RPM Average Cadence: 89 RPM Total Distance: 16711796 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 71 Watt Heart Rate: 93 HBM
18:08:22.098 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 48 0B E6 07 AE 00 75 00 06 01 00 14 00 47 00 5C 00 00 ]
18:08:22.098 -> Speed: 28 KPH Instantaneous Cadence: 1011 RPM Average Cadence: 87 RPM Total Distance: 393333 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 71 Watt Heart Rate: 92 HBM
18:08:22.867 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A2 00 28 33 00 ]
18:08:22.913 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:22.913 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A2 00 28 33 ]
18:08:23.039 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 42 0B F1 07 B0 00 76 00 0D 01 00 14 00 59 00 5C 00 00 ]
18:08:23.039 -> Speed: 28 KPH Instantaneous Cadence: 1016 RPM Average Cadence: 88 RPM Total Distance: 852086 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 89 Watt Heart Rate: 92 HBM
18:08:24.006 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 0E 0B FC 07 AA 00 77 00 14 01 00 14 00 5F 00 5C 00 00 ]
18:08:24.006 -> Speed: 28 KPH Instantaneous Cadence: 1022 RPM Average Cadence: 85 RPM Total Distance: 1310839 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 95 Watt Heart Rate: 92 HBM
18:08:24.918 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 01 28 33 00 ]
18:08:25.012 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:25.012 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 01 28 33 ]
18:08:25.012 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 C9 0A 05 08 96 00 78 00 1B 01 00 14 00 70 00 5D 00 00 ]
18:08:25.012 -> Speed: 27 KPH Instantaneous Cadence: 1026 RPM Average Cadence: 75 RPM Total Distance: 1769592 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 112 Watt Heart Rate: 93 HBM
18:08:26.030 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 62 0A 0F 08 9C 00 78 00 22 01 00 14 00 95 00 5E 00 00 ]
18:08:26.030 -> Speed: 26 KPH Instantaneous Cadence: 1031 RPM Average Cadence: 78 RPM Total Distance: 2228344 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 149 Watt Heart Rate: 94 HBM
18:08:27.014 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 9F 0A 18 08 A0 00 79 00 29 01 00 14 00 A9 00 5F 00 00 ]
18:08:27.014 -> Speed: 27 KPH Instantaneous Cadence: 1036 RPM Average Cadence: 80 RPM Total Distance: 2687097 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 169 Watt Heart Rate: 95 HBM
18:08:27.014 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 4F 01 28 33 00 ]
18:08:27.107 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:27.107 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 4F 01 28 33 ]
18:08:27.989 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 FE 09 21 08 9C 00 7A 00 30 01 00 14 00 C0 00 61 00 00 ]
18:08:27.989 -> Speed: 25 KPH Instantaneous Cadence: 1040 RPM Average Cadence: 78 RPM Total Distance: 3145850 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 192 Watt Heart Rate: 97 HBM
18:08:28.992 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 02 0A 2A 08 98 00 7A 00 37 01 00 14 00 C9 00 63 00 00 ]
18:08:28.992 -> Speed: 25 KPH Instantaneous Cadence: 1045 RPM Average Cadence: 76 RPM Total Distance: 3604602 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 201 Watt Heart Rate: 99 HBM
18:08:29.114 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 01 28 33 00 ]
18:08:29.346 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:29.346 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 01 28 33 ]
18:08:29.995 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A3 09 2B 08 94 00 7B 00 3D 01 00 14 00 CD 00 65 00 00 ]
18:08:29.995 -> Speed: 24 KPH Instantaneous Cadence: 1045 RPM Average Cadence: 74 RPM Total Distance: 3997819 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 205 Watt Heart Rate: 101 HBM
18:08:30.983 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 6B 09 2D 08 92 00 7B 00 43 01 00 14 00 D0 00 67 00 00 ]
18:08:30.983 -> Speed: 24 KPH Instantaneous Cadence: 1046 RPM Average Cadence: 73 RPM Total Distance: 4391035 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 208 Watt Heart Rate: 103 HBM
18:08:31.216 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 57 FF 28 33 00 ]
18:08:31.309 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:31.309 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 57 FF 28 33 ]
18:08:32.004 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 FE 09 34 08 8E 00 7C 00 4A 01 00 14 00 C7 00 68 00 00 ]
18:08:32.004 -> Speed: 25 KPH Instantaneous Cadence: 1050 RPM Average Cadence: 71 RPM Total Distance: 4849788 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 199 Watt Heart Rate: 104 HBM
18:08:33.234 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 3C 0A 3C 08 8E 00 7C 00 51 01 00 14 00 D2 00 6A 00 00 ]
18:08:33.234 -> Speed: 26 KPH Instantaneous Cadence: 1054 RPM Average Cadence: 71 RPM Total Distance: 5308540 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 210 Watt Heart Rate: 106 HBM
18:08:33.310 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 08 00 00 00 00 00 00 00 ]
18:08:33.388 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 08 03 ]
18:08:33.974 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 A0 0A 43 08 92 00 7C 00 58 01 00 14 00 B2 00 6B 00 00 ]
18:08:33.974 -> Speed: 27 KPH Instantaneous Cadence: 1057 RPM Average Cadence: 73 RPM Total Distance: 5767292 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 178 Watt Heart Rate: 107 HBM
18:08:34.984 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 EE 0A 4A 08 96 00 7D 00 5F 01 00 14 00 6F 00 6C 00 00 ]
18:08:34.984 -> Speed: 27 KPH Instantaneous Cadence: 1061 RPM Average Cadence: 75 RPM Total Distance: 6226045 m Resistance Level: 1 Instantaneous Power: 20 Watt Average Power: 111 Watt Heart Rate: 108 HBM
18:08:35.386 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 01 00 00 00 00 00 00 00 ]
18:08:35.479 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 01 01 ]
18:08:35.479 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
18:08:35.989 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 44 0A 9C 00 14 00 3A 00 00 ]
18:08:35.989 -> Speed: 26 KPH Instantaneous Cadence: 78 RPM Resistance Level: 20 Instantaneous Power: 58 Watt Heart Rate: 0 HBM
18:08:36.992 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 AC 09 00 00 14 00 00 00 00 ]
18:08:36.992 -> Speed: 24 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:08:37.486 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:08:37.579 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:08:37.997 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 18 09 00 00 14 00 00 00 00 ]
18:08:37.997 -> Speed: 23 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:08:39.290 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 8A 08 00 00 14 00 00 00 00 ]
18:08:39.290 -> Speed: 21 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:08:39.584 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:08:39.833 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:08:39.956 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 FC 07 00 00 14 00 00 00 00 ]
18:08:39.956 -> Speed: 20 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:08:40.991 -> -> Client Rec'd Raw FTM Indoor Bike Data: [11] [ 64 02 74 07 00 00 14 00 00 00 00 ]
18:08:40.991 -> Speed: 19 KPH Instantaneous Cadence: 0 RPM Resistance Level: 20 Instantaneous Power: 0 Watt Heart Rate: 0 HBM
18:08:41.684 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
18:08:41.793 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
18:08:41.793 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
18:08:42.024 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 ED 06 AC 05 00 00 00 00 04 00 00 14 00 00 00 00 00 00 ]
18:08:42.024 -> Speed: 17 KPH Instantaneous Cadence: 726 RPM Average Cadence: 0 RPM Total Distance: 262144 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:43.125 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 6A 06 AB 05 00 00 00 00 08 00 00 14 00 00 00 00 00 00 ]
18:08:43.125 -> Speed: 16 KPH Instantaneous Cadence: 725 RPM Average Cadence: 0 RPM Total Distance: 524288 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:43.770 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
18:08:43.863 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:43.863 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
18:08:43.987 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 E9 05 AB 05 00 00 00 00 0C 00 00 14 00 00 00 00 00 00 ]
18:08:43.987 -> Speed: 15 KPH Instantaneous Cadence: 725 RPM Average Cadence: 0 RPM Total Distance: 786432 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:44.988 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 6C 05 51 05 00 00 00 00 0F 00 00 14 00 00 00 00 00 00 ]
18:08:44.988 -> Speed: 13 KPH Instantaneous Cadence: 680 RPM Average Cadence: 0 RPM Total Distance: 983040 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:45.858 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
18:08:45.936 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:45.936 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
18:08:45.982 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 F0 04 1A 05 00 00 00 00 12 00 00 14 00 00 00 00 00 00 ]
18:08:45.982 -> Speed: 12 KPH Instantaneous Cadence: 653 RPM Average Cadence: 0 RPM Total Distance: 1179648 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:47.261 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 7B 04 F6 04 00 00 00 00 15 00 00 14 00 00 00 00 00 00 ]
18:08:47.261 -> Speed: 11 KPH Instantaneous Cadence: 635 RPM Average Cadence: 0 RPM Total Distance: 1376256 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:47.940 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:08:47.972 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 07 04 A8 04 00 00 00 00 17 00 00 14 00 00 00 00 00 00 ]
18:08:47.972 -> Speed: 10 KPH Instantaneous Cadence: 596 RPM Average Cadence: 0 RPM Total Distance: 1507328 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:48.003 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:48.003 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:08:49.051 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 98 03 6E 04 00 00 00 00 19 00 00 14 00 00 00 00 00 00 ]
18:08:49.096 -> Speed: 9 KPH Instantaneous Cadence: 567 RPM Average Cadence: 0 RPM Total Distance: 1638400 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:50.008 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 2B 03 40 04 00 00 00 00 1B 00 00 14 00 00 00 00 00 00 ]
18:08:50.008 -> Speed: 8 KPH Instantaneous Cadence: 544 RPM Average Cadence: 0 RPM Total Distance: 1769472 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:50.008 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
18:08:50.132 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:50.132 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
18:08:51.171 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 C2 02 F8 03 00 00 00 00 1C 00 00 14 00 00 00 00 00 00 ]
18:08:51.171 -> Speed: 7 KPH Instantaneous Cadence: 508 RPM Average Cadence: 0 RPM Total Distance: 1835008 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:52.014 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 5C 02 BC 03 00 00 00 00 1D 00 00 14 00 00 00 00 00 00 ]
18:08:52.014 -> Speed: 6 KPH Instantaneous Cadence: 478 RPM Average Cadence: 0 RPM Total Distance: 1900544 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:52.133 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
18:08:52.169 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:52.169 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
18:08:52.969 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 FC 01 8B 03 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:52.969 -> Speed: 5 KPH Instantaneous Cadence: 453 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:54.032 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 45 03 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:54.032 -> Speed: 0 KPH Instantaneous Cadence: 418 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:54.172 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
18:08:54.375 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:54.375 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 00 28 33 ]
18:08:55.011 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 09 03 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:55.056 -> Speed: 0 KPH Instantaneous Cadence: 388 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:55.994 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 D6 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:55.994 -> Speed: 0 KPH Instantaneous Cadence: 363 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:56.274 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 23 00 28 33 00 ]
18:08:56.366 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:56.366 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 00 28 33 ]
18:08:57.103 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 A8 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:57.103 -> Speed: 0 KPH Instantaneous Cadence: 340 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:58.006 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 80 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:58.006 -> Speed: 0 KPH Instantaneous Cadence: 320 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:58.393 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 1C 00 28 33 00 ]
18:08:58.486 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:08:58.486 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 00 28 33 ]
18:08:59.043 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 5D 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:59.043 -> Speed: 0 KPH Instantaneous Cadence: 302 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:08:59.970 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 3D 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:08:59.970 -> Speed: 0 KPH Instantaneous Cadence: 286 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:00.484 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 0F 00 28 33 00 ]
18:09:00.562 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:00.562 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 0F 00 28 33 ]
18:09:00.982 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 20 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:00.982 -> Speed: 0 KPH Instantaneous Cadence: 272 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:02.154 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 06 02 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:02.154 -> Speed: 0 KPH Instantaneous Cadence: 259 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:02.545 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:09:02.671 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:02.671 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:09:03.077 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 EF 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:03.077 -> Speed: 0 KPH Instantaneous Cadence: 247 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:04.037 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 D9 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:04.037 -> Speed: 0 KPH Instantaneous Cadence: 236 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:04.670 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 F5 FF 28 33 00 ]
18:09:04.993 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 C5 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:04.993 -> Speed: 0 KPH Instantaneous Cadence: 226 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:05.039 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:05.039 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 F5 FF 28 33 ]
18:09:05.976 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 B3 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:05.976 -> Speed: 0 KPH Instantaneous Cadence: 217 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:06.769 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 E4 FF 28 33 00 ]
18:09:06.848 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:06.848 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 E4 FF 28 33 ]
18:09:06.988 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 A3 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:06.988 -> Speed: 0 KPH Instantaneous Cadence: 209 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:07.992 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 93 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:07.992 -> Speed: 0 KPH Instantaneous Cadence: 201 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:08.857 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 CF FF 28 33 00 ]
18:09:08.980 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 85 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:08.980 -> Speed: 0 KPH Instantaneous Cadence: 194 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:09.026 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:09.026 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CF FF 28 33 ]
18:09:10.279 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 77 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:10.279 -> Speed: 0 KPH Instantaneous Cadence: 187 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:10.944 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 A8 FF 28 33 00 ]
18:09:10.990 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 6B 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:10.990 -> Speed: 0 KPH Instantaneous Cadence: 181 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:11.037 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:11.037 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 A8 FF 28 33 ]
18:09:12.001 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 5F 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:12.001 -> Speed: 0 KPH Instantaneous Cadence: 175 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:12.989 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 54 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:12.989 -> Speed: 0 KPH Instantaneous Cadence: 170 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:13.025 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 84 FF 28 33 00 ]
18:09:13.104 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:13.104 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 FF 28 33 ]
18:09:13.972 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 4A 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:13.972 -> Speed: 0 KPH Instantaneous Cadence: 165 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:14.978 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 40 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:14.978 -> Speed: 0 KPH Instantaneous Cadence: 160 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:15.116 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 67 FF 28 33 00 ]
18:09:15.194 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:15.194 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 67 FF 28 33 ]
18:09:16.125 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 37 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:16.125 -> Speed: 0 KPH Instantaneous Cadence: 155 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:17.013 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 2E 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:17.013 -> Speed: 0 KPH Instantaneous Cadence: 151 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:17.184 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5D FF 28 33 00 ]
18:09:17.263 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:17.263 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5D FF 28 33 ]
18:09:18.010 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 26 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:18.010 -> Speed: 0 KPH Instantaneous Cadence: 147 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:19.015 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 1E 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]
18:09:19.015 -> Speed: 0 KPH Instantaneous Cadence: 143 RPM Average Cadence: 0 RPM Total Distance: 1966080 m Resistance Level: 0 Instantaneous Power: 20 Watt Average Power: 0 Watt Heart Rate: 0 HBM
18:09:19.248 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 5E FF 28 33 00 ]
18:09:19.372 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:09:19.372 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 5E FF 28 33 ]
18:09:20.050 -> -> Client Rec'd Raw FTM Indoor Bike Data: [20] [ FE 02 00 00 17 01 00 00 00 00 1E 00 00 14 00 00 00 00 00 00 ]

// If FTM Feature is not found, disconnect, resume scanning, and return
while (!( client_FTM_Feature_Chr.discover() ))
{
if ( client_FTM_Feature_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read FTM Feature Data
client_FTM_Feature_Chr.read(client_FTM_Feature_Data, 8);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Feature bytes: [8] [ ");
for (int i = 0; i < sizeof(client_FTM_Feature_Data); i++) {
Serial.printf("%02X ", client_FTM_Feature_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Disconnecting since Client FTM Feature Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
Bluefruit.Connection(conn_handle);
Serial.print("Discovering Client FTM Feature not decouvert ... ");
// return;
}
}

As you can see in the second test it does not find the speed and cadence values.

Probably I should put more "While".

What is weird is the value of the heart rate because the sensor is not connected (battery removed)

18:26:23.470 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
18:26:23.470 -> ----------------- Version 02.5 ------------------
18:26:23.470 -> Initialise the Bluefruit nRF52 module: Client (Central)
18:26:23.718 -> FTMS and Chars 'initialized'
18:26:23.718 -> CPS and Chars 'initialized'
18:26:23.718 -> CSCS and Chars 'initialized'
18:26:23.718 -> GA and Chars 'initialized'
18:26:23.718 -> DIS and Chars 'initialized'
18:26:23.718 -> Start Scanning for CPS, CSC and FTMS!
18:26:23.720 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
18:26:23.720 -> Timestamp MAC Address Rssi Data
18:26:23.720 -> 000001552 F8:9C:FC:53:5E:49 -61 09-02-16-18-26-18-18-18-0A-18
18:26:23.720 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
18:26:23.720 -> Now checking all Client Services and Characteristics!
18:26:23.720 -> If Mandatory Services Fail --> the Client will disconnect!
18:26:23.720 -> First checking Generic Access and Device Information Services and Characteristics!
18:26:24.243 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
18:26:24.243 -> Discovering Client FTM Feature Characteristic ... Discovering Client FTM Control Point Characteristic ... Found it!
18:26:24.252 -> Discovering Client FTM Status Characteristic ... Found it!
18:26:24.544 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
18:26:24.823 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
18:26:25.225 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
18:26:25.225 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
18:26:25.457 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
18:26:25.457 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
18:26:25.781 -> Discovering Client Cycling Power (CP) Service ... Ready to receive Client FTM Control Point Response Messages
18:26:25.827 -> Found it! CPS Max Payload: 20 Data Length: 27
18:26:25.827 -> Discovering Client CP Measurement characteristic ... Ready to receive Client FTM Status values
18:26:25.827 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
18:26:25.906 -> >>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.
18:26:25.906 -> >>> Couldn't enable notify for Client CP Measurement Characteristic.
18:26:25.906 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
18:26:25.906 -> >>> Couldn't enable notify for Client CSC Measurement Characteristic.
18:26:25.906 -> Client (Central) is Up and Running!
18:26:26.045 -> Found it!
18:26:26.045 -> Discovering Client CP Control Point characteristic ... Not Found! NOT Mandatory!
18:26:26.541 -> Discovering Client CP Feature characteristic ... Found it!
18:26:26.726 -> -> Client Reads Raw CP Feature bytes: [4] [ 00 00 00 00 ]
18:26:26.726 -> Discovering Client CP Sensor Location characteristic ... NOT Found! NOT Mandatory!
18:26:26.726 -> Discovering Cycling Speed and Cadence (CSC) Service ... Not Found! CSC Service is Not Mandatory!
18:26:27.906 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:26:27.970 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:26:29.983 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
18:26:30.029 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 00 01 ]
18:26:32.045 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
18:26:32.122 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 07 01 ]
18:26:32.122 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
18:26:34.127 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
18:26:34.174 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:26:34.174 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
18:26:36.191 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
18:26:36.237 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:26:36.237 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 09 00 28 33 ]
18:26:38.240 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]
18:26:38.317 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:26:38.317 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 00 00 28 33 ]
18:26:40.324 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 14 00 28 33 00 ]
18:26:40.402 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:26:40.402 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 00 28 33 ]
18:26:42.383 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 00 28 33 00 ]
18:26:42.508 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]
18:26:42.508 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 00 28 33 ]
18:26:44.510 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 25 00 28 33 00 ]
18:26:44.588 -> -> Client Rec'd Raw FTM Control Point Response Data: [ 80 11 01 ]

@Berg0162
Copy link
Owner Author

Dear Joel,
I am puzzled about how and what you have changed in the code version 025. I see a while loop that waits for ever until the FTM_Feature_Char is discovered: while (!( client_FTM_Feature_Chr.discover() )) { et cetera }
FTM_Feature was never a problem....??? Did you put while loops in all discover calls...? For what purpose?
Whatever you did: one of the runs now shows -> Client Rec'd Raw FTM Indoor Bike Data: et cetera
That never ever happened before ...... ????
So please detail more and better what you have changed otherwise I am only guessing and that does not help progress...
Greetings,
Jörgen.

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 15, 2022

Dear Joel,
Still I would like to know more precisely what you have altered in the code of 025... that issue is still open!
Your code snippet is only relevant when you think a timing problem prevents the discovery of the relevant Char's..... (?)
Meanwhile I guessed that (along the same line of reasoning) enabling of the Char's with Notify or Indicate (at the Zwift Hub) might go wrong due to a timing problem...(ItsyBitsy? or Hub?). So I have inserted a timed check for notify/indicate result that gives the polling process two (2) seconds for response....
I wonder if this version 027 will bring us closer to a solution... (You still need to wake up your trainer and ride the trainer!!)

Do not take the IBD decoded data too serious, decoding the IBD data is very complicated and I would not be surprised that I understood it in a wrong way.... although it works fine with Elite Direto specifications! This is not critical for the final result, after all the MITM does not have to understand/decode everything, only what it is interested in...

By the way the FTMS_Zwift_Bridge code v023 can be tested with your setup.... (Notice that you have to pedal as well! Read the instructions)

Best wishes,
Jörgen.

@le-joebar
Copy link

le-joebar commented Dec 15, 2022

Hello Jorghen,

To answer the question should I always wake him up?

No with the modification I made I just have to turn on the trainer.
No need to pedal.
So the problem was not a problem of waking up but probably of receiving "char"!

Here is the V.025 that I had to modify.
There are 3 "While" that I had added.

I will test your V0.27.

Sincerely,
Joel

/*********************************************************************
This is programming code for the nRF52 based Bluefruit BLE boards

The code uses heavily the Adafruit supplied Bluefruit BLE libraries !!
Adafruit invests time and resources providing open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

MIT license, check LICENSE for more information
All text must be included in any redistribution
*********************************************************************/

/*

  • This Feather-nRF52840 tested code scans for the CPS, CSC and FTMS
  • that the trainer is advertising, it tries to connect and then
  • enables .....
  • Requirements: FTMS trainer and Feather nRF52 board
    1. Upload and Run this code on the Feather-nRF52
    1. Start the Serial Monitor to catch verbose debugging and data info
    1. Power ON and Wake UP trainer -> do NOT connect with other devices
    1. Trainer and Feather should successfully pair or disconnect...
    1. Keep the Serial Monitor visible on top of all windows
    1. Move the trainer pedals and notice/feel changes in resistance...
  • The Client sends Resistance Parameters to the Trainer that coincide 
    
  • with the first 5 minutes of the Zwift Volcano Circuit!
    
    1. Inspect the info presented by Serial Monitor.....

*/

// -------------------------------------------------------------------------------------------
// COMPILER DIRECTIVE to allow/suppress SERIAL.PRINT messages that help debugging...
// Uncomment to activate
#define DEBUG
//#define DEBUG_CP_MEASUREMENT
//#define DEBUG_CSC_MEASUREMENT
// --------------------------------------------------------------------------------------------

#include <bluefruit.h>

const uint8_t MAX_PAYLOAD = 20; // Max 20 byte data size for single packet BLE transfer
uint16_t client_Connection_Handle = BLE_CONN_HANDLE_INVALID;

/* Generic Access
#define UUID16_SVC_GENERIC_ACCESS 0x1800
#define UUID16_CHR_DEVICE_NAME 0x2A00
#define UUID16_CHR_APPEARANCE 0x2A01
#define UUID16_CHR_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS 0x2A04
#define UUID16_CHR_CENTRAL_ADDRESS_RESOLUTION 0x2AA6
*/
BLEClientService client_GenericAccess_Service(UUID16_SVC_GENERIC_ACCESS);
BLEClientCharacteristic client_GA_Appearance_Chr(UUID16_CHR_APPEARANCE); // Read
uint16_t client_GA_Appearance_Value = 0;
BLEClientCharacteristic client_GA_DeviceName_Chr(UUID16_CHR_DEVICE_NAME);// Read, Write
unsigned char client_GA_DeviceName_Data[MAX_PAYLOAD] = {};

/* Cycling Power Service

  • CP Service: 0x1818
  • CP Characteristic: 0x2A63 (Measurement)
  • CP Characteristic: 0x2A65 (Feature)
  • CP Characteristic: 0x2A5D (Location)
  • CP Characteristic: 0x2A66 (Control Point)
    */
    BLEClientService client_CyclingPower_Service(UUID16_SVC_CYCLING_POWER);
    BLEClientCharacteristic client_CP_Measurement_Chr(UUID16_CHR_CYCLING_POWER_MEASUREMENT); // Notify, Read
    BLEClientCharacteristic client_CP_Feature_Chr(UUID16_CHR_CYCLING_POWER_FEATURE); // Read
    uint32_t client_CP_Feature_Flags = 0;
    BLEClientCharacteristic client_CP_Location_Chr(UUID16_CHR_SENSOR_LOCATION); // Read
    uint8_t client_CP_Location_Value = 0; // UINT8
    BLEClientCharacteristic client_CP_ControlPoint_Chr(UUID16_CHR_CYCLING_POWER_CONTROL_POINT); // Indicate, Write

const char* client_CP_Feature_Str[] = {
"Pedal power balance supported",
"Accumulated torque supported",
"Wheel revolution data supported",
"Crank revolution data supported",
"Extreme magnitudes supported",
"Extreme angles supported",
"Top/bottom dead angle supported",
"Accumulated energy supported",
"Offset compensation indicator supported",
"Offset compensation supported",
"Cycling power measurement characteristic content masking supported",
"Multiple sensor locations supported",
"Crank length adj. supported",
"Chain length adj. supported",
"Chain weight adj. supported",
"Span length adj. supported",
"Sensor measurement context",
"Instantaineous measurement direction supported",
"Factory calibrated date supported",
"Enhanced offset compensation supported" };

const char* client_Sensor_Location_Str[] = { "Other", "Top of shoe", "In shoe", "Hip", "Front wheel", "Left crank", "Right crank", "Left pedal",
"Right pedal", "Front hub", "Rear dropout", "Chainstay", "Rear wheel", "Rear hub", "Chest", "Spider", "Chain ring"};

/*

  • Cycling Speed and Cadence Service
  • CSC Service: 0x1816
  • CSC Measurement Characteristic: 0x2A5B
  • CSC Feature Characteristic: 0x2A5C
    /
    /
    CSC Control Point Characteristic:0x2A55 ---> not implemented
    // CSC Control Point op codes
    #define SC_CP_OP_SET_CUMULATIVE_VALUE 1
    #define SC_CP_OP_START_SENSOR_CALIBRATION 2
    #define SC_CP_OP_UPDATE_SENSOR_LOCATION 3
    #define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4
    #define SC_CP_OP_RESPONSE 16
    // CSC Control Point response values
    #define SC_CP_RESPONSE_SUCCESS 1
    #define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2
    #define SC_CP_RESPONSE_INVALID_PARAM 3
    #define SC_CP_RESPONSE_OP_FAILED 4
    */

/CSC Measurement flags/
#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01
#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02

/* CSC Feature flags */
#define CSC_FEATURE_WHEEL_REV_DATA 0x01
#define CSC_FEATURE_CRANK_REV_DATA 0x02
#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04

const char* client_CSC_Feature_Str[] = {"Wheel rev supported", "Crank rev supported", "Multiple locations supported"};

BLEClientService client_CyclingSpeedCadence_Service(UUID16_SVC_CYCLING_SPEED_AND_CADENCE);
BLEClientCharacteristic client_CSC_Measurement_Chr(UUID16_CHR_CSC_MEASUREMENT); // Notify, Read
BLEClientCharacteristic client_CSC_Feature_Chr(UUID16_CHR_CSC_FEATURE); // Read
const uint8_t CSC_FEATURE_FIXED_DATALEN = 2; // UINT16
uint16_t client_CSC_Feature_Flags = 0;
BLEClientCharacteristic client_CSC_Location_Chr(UUID16_CHR_SENSOR_LOCATION); // Read
uint8_t client_CSC_Location_Value = 0;

/* Client Service Device Information
#define UUID16_SVC_DEVICE_INFORMATION 0x180A
#define UUID16_CHR_MODEL_NUMBER_STRING 0x2A24
#define UUID16_CHR_SERIAL_NUMBER_STRING 0x2A25
#define UUID16_CHR_FIRMWARE_REVISION_STRING 0x2A26
#define UUID16_CHR_HARDWARE_REVISION_STRING 0x2A27
#define UUID16_CHR_SOFTWARE_REVISION_STRING 0x2A28
#define UUID16_CHR_MANUFACTURER_NAME_STRING 0x2A29
*/
BLEClientService client_DIS_Service(UUID16_SVC_DEVICE_INFORMATION);
BLEClientCharacteristic client_DIS_ManufacturerName_Chr(UUID16_CHR_MANUFACTURER_NAME_STRING); // Read
char client_DIS_Manufacturer_Str[MAX_PAYLOAD] = {};
BLEClientCharacteristic client_DIS_ModelNumber_Chr(UUID16_CHR_MODEL_NUMBER_STRING); // Read
char client_DIS_ModelNumber_Str[MAX_PAYLOAD] = {};
BLEClientCharacteristic client_DIS_SerialNumber_Chr(UUID16_CHR_SERIAL_NUMBER_STRING); // Read
char client_DIS_SerialNumber_Str[MAX_PAYLOAD] = {};

/*

  • Fitness Machine Service, uuid 0x1826 or 00001826-0000-1000-8000-00805F9B34FB
    #define UUID16_SVC_FITNESS_MACHINE 0x1826
    #define UUID16_CHR_FITNESS_MACHINE_FEATURE 0x2ACC
    #define UUID16_CHR_INDOOR_BIKE_DATA 0x2AD2
    #define UUID16_CHR_TRAINING_STATUS 0x2AD3
    #define UUID16_CHR_SUPPORTED_SPEED_RANGE 0x2AD4
    #define UUID16_CHR_SUPPORTED_INCLINATION_RANGE 0x2AD5
    #define UUID16_CHR_SUPPORTED_RESISTANCE_LEVEL_RANGE 0x2AD6
    #define UUID16_CHR_SUPPORTED_HEART_RATE_RANGE 0x2AD7
    #define UUID16_CHR_SUPPORTED_POWER_RANGE 0x2AD8
    #define UUID16_CHR_FITNESS_MACHINE_CONTROL_POINT 0x2AD9
    #define UUID16_CHR_FITNESS_MACHINE_STATUS 0x2ADA
    */
    BLEClientService client_FitnessMachine_Service(UUID16_SVC_FITNESS_MACHINE); // FTM Service
    // Service characteristics exposed by FTM Service
    BLEClientCharacteristic client_FTM_Feature_Chr(UUID16_CHR_FITNESS_MACHINE_FEATURE); // Fitness Machine Feature, mandatory, read
    const uint8_t FTM_FEATURE_FIXED_DATALEN = 8;
    uint8_t client_FTM_Feature_Data[FTM_FEATURE_FIXED_DATALEN];
    BLEClientCharacteristic client_FTM_IndoorBikeData_Chr(UUID16_CHR_INDOOR_BIKE_DATA); // Indoor Bike Data, optional, notify
    BLEClientCharacteristic client_FTM_TrainingStatus_Chr(UUID16_CHR_TRAINING_STATUS); // Training Status, optional, read & notify
    BLEClientCharacteristic client_FTM_SupportedResistanceLevelRange_Chr(UUID16_CHR_SUPPORTED_RESISTANCE_LEVEL_RANGE); // Supported Resistance Level, read, optional
    const uint8_t FTM_SRLR_FIXED_DATALEN = 6;
    uint8_t client_FTM_SupportedResistanceLevelRange_Data[FTM_SRLR_FIXED_DATALEN];
    BLEClientCharacteristic client_FTM_SupportedPowerRange_Chr(UUID16_CHR_SUPPORTED_POWER_RANGE); // Supported Power Levels, read, optional
    const uint8_t FTM_SPR_FIXED_DATALEN = 6;
    uint8_t client_FTM_SupportedPowerRange_Data[FTM_SPR_FIXED_DATALEN];
    BLEClientCharacteristic client_FTM_ControlPoint_Chr(UUID16_CHR_FITNESS_MACHINE_CONTROL_POINT); // Fitness Machine Control Point, optional, write & indicate
    BLEClientCharacteristic client_FTM_Status_Chr(UUID16_CHR_FITNESS_MACHINE_STATUS); // Fitness Machine Status, mandatory, notify

#define TIME_SPAN 2500 // Time span (delay) in millis 1000 = 1 second
#define CONTROL_POINT_TIME_SPAN 2000 // Time span for sending Control Point data
unsigned long TimeInterval = 0;

// TEST DATA set -----------------------------------------------------
// Test set (Zwift Volcano Circuit) with Control and 2D Resistance data to be sent to Control Point
uint8_t ControlPointMessageCount = 0;
const uint8_t ControlPointData[32][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // Request Control
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // Request Control
{0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // Start or Resume
{0x11,0x00,0x00,0x22,0x01,0x28,0x33,0x00}, // Set Indoor Bike Simulation Parameters..
{0x11,0x00,0x00,0x09,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x00,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x14,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x22,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x25,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x23,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x1C,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x0F,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x00,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0xF5,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0xE4,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0xCF,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0xA8,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x84,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x67,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x5D,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x5E,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x6F,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x8A,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0xB8,0xFF,0x28,0x33,0x00},
{0x11,0x00,0x00,0x00,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0xA2,0x00,0x28,0x33,0x00},
{0x11,0x00,0x00,0x09,0x01,0x28,0x33,0x00},
{0x11,0x00,0x00,0x4F,0x01,0x28,0x33,0x00},
{0x11,0x00,0x00,0x67,0x01,0x28,0x33,0x00},
{0x11,0x00,0x00,0x57,0xFF,0x28,0x33,0x00},
{0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // Stop or Pause
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}};// Reset
// TEST DATA set ---------------------------------------------------

void setup()
{
#ifdef DEBUG
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb

Serial.println(" Feather nRF52 Client/Central: CPS, CSC and FTMS");
Serial.println("----------------- Version 02.5 ------------------");
Serial.println("Initialise the Bluefruit nRF52 module: Client (Central)");
#endif
// Initialize Bluefruit module
// begin (Peripheral = 0, Central = 1)
Bluefruit.begin(0, 1);

Setup_Client_FTMS();
Setup_Client_CPS();
Setup_Client_CSC();
Setup_Client_DIS();
Client_Start_Scanning();

while (Bluefruit.Scanner.isRunning()) { // do nothing else but scanning....
}
TimeInterval = millis() + TIME_SPAN; // ADD just enough delay
// wait enough time or go on when Client/Central is connected and all set!
while( TimeInterval > millis() ) {
}
// Scanning is finished! Are we connected?
// If NO connection is established -> Stop!
if( !(Bluefruit.connected()) ) {
#ifdef DEBUG
Serial.println("Stopped!");

#endif
return;
}
// Try to enable Client (Trainer) CPS, CSC and FTMS data streams...
Client_Enable_Notify_Indicate();
if( !(Bluefruit.connected()) ) { // Are we meanwhile disconnected?
#ifdef DEBUG
Serial.println("Stopped!");

#endif
return;
}
// After successful enable notify/indicate of all mandatory Char's continue!
#ifdef DEBUG
Serial.println("Client (Central) is Up and Running!");
#endif
// Set first timing for sending first set of Control Point Data
TimeInterval = millis() + CONTROL_POINT_TIME_SPAN;
}

void Setup_Client_FTMS(void)
{
// Initialize client FTM Service
client_FitnessMachine_Service.begin();
while( !(client_FitnessMachine_Service.begin()) ) {
Serial.println("Wait for FitnessMachine_Service.begin ");
}
// Initialize client FTM Feature characteristic
client_FTM_Feature_Chr.begin();

// Initialize client FTM Training Status characteristic
client_FTM_TrainingStatus_Chr.setNotifyCallback(client_FTM_TrainingStatus_Notify_callback);

client_FTM_TrainingStatus_Chr.begin();

// Initialize client FTM Supported Power Range characteristic
client_FTM_SupportedPowerRange_Chr.begin();

// Initialize client FTM Supported Resistance Level Range characteristic
client_FTM_SupportedResistanceLevelRange_Chr.begin();

// Initialize client FTM Indoor Bike Data characteristic
client_FTM_IndoorBikeData_Chr.setNotifyCallback(client_FTM_IndoorBikeData_Notify_callback);
client_FTM_IndoorBikeData_Chr.begin();

// Initialize client FTM Control Point characteristic
// For receiving Control Point Responses
client_FTM_ControlPoint_Chr.setIndicateCallback(client_FTM_ControlPoint_Indicate_callback);
client_FTM_ControlPoint_Chr.begin();

// Initialize client FTM Status characteristic
client_FTM_Status_Chr.setNotifyCallback(client_FTM_Status_Notify_callback);
client_FTM_Status_Chr.begin();
#ifdef DEBUG
Serial.println("FTMS and Chars 'initialized'");
#endif
}

void Setup_Client_CPS(void)
{
// Initialize CPS client
client_CyclingPower_Service.begin();

// Initialize CP Feature characteristics of client_CyclingPower_Service.
client_CP_Feature_Chr.begin();

// Initialize CP sensor location characteristics of client_CyclingPower_Service.
client_CP_Location_Chr.begin();

// set up callback for receiving measurement
client_CP_Measurement_Chr.setNotifyCallback(client_CP_Measurement_Chr_notify_callback);
client_CP_Measurement_Chr.begin();

// Initialize Control Point and set up Indicate callback for receiving responses (indicate!)
client_CP_ControlPoint_Chr.setIndicateCallback(client_CP_ControlPoint_Chr_indicate_callback);
client_CP_ControlPoint_Chr.begin();
#ifdef DEBUG
Serial.println("CPS and Chars 'initialized'");
#endif
}

void Setup_Client_CSC(void)
{
// Initialize CSC client
client_CyclingSpeedCadence_Service.begin();

// Initialize client characteristics of CSC.
client_CSC_Location_Chr.begin();

// Initialize CSC Feature characteristics of client_CSC.
client_CSC_Feature_Chr.begin();

// set up callback for receiving measurement
client_CSC_Measurement_Chr.setNotifyCallback(client_CSC_Measurement_Chr_notify_callback);
client_CSC_Measurement_Chr.begin();
#ifdef DEBUG
Serial.println("CSCS and Chars 'initialized'");
#endif
}

void Setup_Client_DIS(void)
{
// Initialize client Generic Access Service
client_GenericAccess_Service.begin();
// Initialize some characteristics of the Generic Access Service.
client_GA_DeviceName_Chr.begin();
client_GA_Appearance_Chr.begin();
#ifdef DEBUG
Serial.println("GA and Chars 'initialized'");
#endif
// Initialize client Device Information Service
client_DIS_Service.begin();
// Initialize some characteristics of the Device Information Service.
client_DIS_ManufacturerName_Chr.begin();
client_DIS_ModelNumber_Chr.begin();
client_DIS_SerialNumber_Chr.begin();
#ifdef DEBUG
Serial.println("DIS and Chars 'initialized'");
#endif
}

void loop()
{

if ( Bluefruit.connected() )
{
// If time is there, send test values of Indoor Bike Simulation Parameters to
// the Trainer's FTM Control Point to drive the FTM...
if(millis() > TimeInterval)
{
if(ControlPointMessageCount > 31) { ControlPointMessageCount = 0; } // start all over again!
uint8_t CPData[8] = {};
#ifdef DEBUG
Serial.print("Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ ");
#endif
// Transfer multidimensional test data to CPData buffer
for (int i = 0; i < sizeof(CPData); i++) {
CPData[i] = ControlPointData[ControlPointMessageCount][i];
#ifdef DEBUG
Serial.printf("%02X ", CPData[i], HEX);
#endif
}
#ifdef DEBUG
Serial.println("] ");
#endif
// client_FTM_ControlPoint_Chr.write(CPData, 8);
client_FTM_ControlPoint_Chr.write_resp(CPData, 8);
TimeInterval = millis() + CONTROL_POINT_TIME_SPAN;
ControlPointMessageCount++;
} // TimeInterval
} // Bluefruit connected
} // end loop

void Client_Start_Scanning(void)
{
/* Start Central Scanning

    • Enable auto scan if disconnected
    • Interval = 100 ms, window = 80 ms
    • Don't use active scan
    • Filter only accept CP service
    • Start(timeout) with timeout = 0 will scan forever (until connected)
      */

// Client/Central Callbacks defined
Bluefruit.Central.setDisconnectCallback(client_disconnect_callback);
Bluefruit.Central.setConnectCallback(client_connect_callback);

Bluefruit.Scanner.setRxCallback(client_scan_callback);
Bluefruit.Scanner.restartOnDisconnect(false); // default -> true !!! in test stage we do not want to RESTART -> false
Bluefruit.Scanner.filterRssi(-70); // original value of -80 , we want to scan only nearby peripherals, so get close to your device !!
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
// only invoke callback if one of these services are advertised --> check for FTMS only later
Bluefruit.Scanner.filterUuid(UUID16_SVC_CYCLING_POWER, UUID16_SVC_CYCLING_SPEED_AND_CADENCE, UUID16_SVC_FITNESS_MACHINE);
Bluefruit.Scanner.useActiveScan(true);
Bluefruit.Scanner.start(0); // 0 = Don't stop scanning or n = after n/100 seconds
#ifdef DEBUG
Serial.println("Start Scanning for CPS, CSC and FTMS!");
delay(100); // To show print message !
#endif
}

void Client_Enable_Notify_Indicate(void)
{
// Reaching here means we are ready to go, let's enable Chars with Indicate/Notify
// ------------------------- Enable FTMS Notify and Indicate ---------------------------------------
if ( client_FTM_ControlPoint_Chr.enableIndicate() ) { // MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Control Point Response Messages");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable indicate for Client FTM Control Point Characteristic.");
Serial.println("FTMS (trainer) is controlled by another Client (Training App)!");
#endif
Bluefruit.disconnect(client_Connection_Handle);
return;
}

if ( client_FTM_Status_Chr.enableNotify() ) { // MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Status values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Status Characteristic.");
Serial.println("FTMS (trainer) is controlled by another Client (Training App)!");
#endif
Bluefruit.disconnect(client_Connection_Handle);
return;
}

if ( client_FTM_TrainingStatus_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Training Status values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Training Status Characteristic.");
#endif
}

if ( client_FTM_IndoorBikeData_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Indoor Bike Data values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.");
#endif
}
// ------------------------- Enable FTMS Notify and Indicate ---------------------------------------
// --------------------- Enable CP and CSC Notify and Indicate ------------------------------------
if ( client_CP_Measurement_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CP Measurement values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client CP Measurement Characteristic.");
#endif
}

if ( client_CP_ControlPoint_Chr.enableIndicate() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CP Control Point Responses");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable indicate for Client CP Control Point Characteristic.");
#endif
}

if ( client_CSC_Measurement_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CSC Measurement values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client CSC Measurement Characteristic.");
#endif
}
// ------------------------- Enable CP and CSC Notify and Indicate --------------------------------
}

/**

  • Hooked callback that triggered when a value is sent
  • @param chr Pointer client characteristic that event occurred,
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_FTM_TrainingStatus_Notify_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG
    uint8_t SDataLen = (uint8_t)len;
    uint8_t SDataBuf[SDataLen] = {};
    // Transfer first the contents of data to buffer (array of chars)
    Serial.printf(" -> Client Rec'd Raw FTM Training Status Data: [%d] [ ", len);
    for (int i = 0; i < sizeof(SDataBuf); i++) {
    SDataBuf[i] = *data++;
    Serial.printf("%02X ", SDataBuf[i], HEX);
    }
    Serial.println("] ");
    #endif
    }

/**

  • Hooked callback that triggered when a value is sent
  • @param chr Pointer client characteristic that event occurred,
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_FTM_IndoorBikeData_Notify_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG
    // testing
    ParseIndoorBikeData(data, len);
    #endif
    }

#ifdef DEBUG
void ParseIndoorBikeData(uint8_t* data, uint16_t len)
{
// ---> IBD Buffer Data Length depends on data flagged to be present !!!!
uint8_t IBDDataLen = (uint8_t)len;
uint8_t IBDDataBuf[IBDDataLen] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw FTM Indoor Bike Data: [%d] [ ", len);
for (int i = 0; i < sizeof(IBDDataBuf); i++) {
IBDDataBuf[i] = *data++;
Serial.printf("%02X ", IBDDataBuf[i], HEX);
}
Serial.println("]");
uint8_t offset = 0;
uint16_t flags = 0;
memcpy(&flags, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
if ((flags & 1) != 0) {
// More Data
Serial.print("More Data!");
}
// if ((flags & 2) != 0) { // (flags & 2) --> true or false Average Speed is always(?) there (tested with Elite Direto XR)
// Average Speed 0.01
uint16_t sim_Speed = 0;
memcpy(&sim_Speed, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Speed: %d KPH", (sim_Speed/100));
offset += 2; // UINT16
// }
if ((flags & 4) != 0) {
// Instantaneous Cadence 0.5
uint16_t inst_Cadence = 0;
memcpy(&inst_Cadence, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Instantaneous Cadence: %d RPM", (inst_Cadence/2));
offset += 2; // UINT16
}
if ((flags & 8) != 0) {
// Average Cadence 0.5
uint16_t av_Cadence = 0;
memcpy(&av_Cadence, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Average Cadence: %d RPM", (av_Cadence/2));
offset += 2; // UINT16
}
if ((flags & 16) != 0) {
// Total Distance 1
// Little endian format, transfer 24 bit to 32 bit variable
uint32_t tot_Distance = 0;
memcpy(&tot_Distance, &IBDDataBuf[offset], 3); // Transfer buffer fields to variable
Serial.printf(" Total Distance: %d m", tot_Distance);
offset += 3; // UINT24 16 + 8
}
if ((flags & 32) != 0) {
// Resistance Level 1
uint16_t res_Level = 0;
memcpy(&res_Level, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Resistance Level: %d ", res_Level);
offset += 2; // UINT16
}
if ((flags & 64) != 0) {
// Instantaneous Power 1
uint16_t inst_Power = 0;
memcpy(&inst_Power, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Instantaneous Power: %d Watt", inst_Power);
offset += 2; // UINT16
}
if ((flags & 128) != 0) {
// Average Power 1
uint16_t av_Power = 0;
memcpy(&av_Power, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Average Power: %d Watt", av_Power);
offset += 2; // UINT16
}
if ((flags & 256) != 0) {
// Expended Energy -> UINT16 UINT16 UINT8
// Total Energy UINT16 1
uint16_t tot_Energy = 0;
memcpy(&tot_Energy, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Tot. Energy: %d kCal", tot_Energy);
offset += 2; // UINT16
// Energy per hour UINT16 1
uint16_t Energy_hr = 0;
memcpy(&Energy_hr, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Energy/hr: %d kCal/hr", Energy_hr);
offset += 2; // UINT16
// Energy per minute UINT8 1
uint8_t Energy_pm = 0;
memcpy(&Energy_pm, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Energy/m: %d kCal/m", Energy_pm);
offset += 1; // UINT8
}
if ((flags & 512) != 0) {
// Heart Rate 1
uint8_t Heart_Rate = 0;
memcpy(&Heart_Rate, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Heart Rate: %d HBM", Heart_Rate);
offset += 1; // UINT8
}
if ((flags & 1024) != 0) {
// Metabolic Equivalent 0.1
uint8_t Mets = 0;
memcpy(&Mets, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Metabolic Equivalent: %d ", Mets/10);
offset += 1; // UINT8
}
if ((flags & 2048) != 0) {
// Elapsed Time 1
uint16_t elap_time = 0;
memcpy(&elap_time, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Elapsed time: %d s", elap_time);
offset += 2; // UINT16
}
if ((flags & 4096) != 0) {
// Remaining Time 1
uint16_t rem_time = 0;
memcpy(&rem_time, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Remaining time: %d s", rem_time);
offset += 2; // UINT16
}
Serial.println();
}
#endif

/**

  • Hooked callback that triggered when a value is sent
  • @param chr Pointer client characteristic that event occurred!
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_FTM_ControlPoint_Indicate_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG
    uint8_t RespBufferLen = (uint8_t)len;
    uint8_t RespBuffer[RespBufferLen] = {}; // It is max 6 bytes long
    // Transfer first the contents of data to buffer (array of chars)
    Serial.print(" -> Client Rec'd Raw FTM Control Point Response Data: [ ");
    for (int i = 0; i < sizeof(RespBuffer); i++) {
    RespBuffer[i] = *data++;
    Serial.printf("%02X ", RespBuffer[i], HEX);
    }
    Serial.println("]");
    #endif
    }

/**

  • Hooked callback that triggered when a value is sent
  • @param chr Pointer client characteristic that event occurred,
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_FTM_Status_Notify_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG
    uint8_t SDataLen = (uint8_t)len;
    uint8_t SDataBuf[SDataLen] = {};
    // Transfer first the contents of data to buffer (array of chars)
    Serial.printf(" -> Client Rec'd Raw FTM Machine Status Data: [%d] [ ", len);
    for (int i = 0; i < sizeof(SDataBuf); i++) {
    SDataBuf[i] = *data++;
    Serial.printf("%02X ", SDataBuf[i], HEX);
    }
    Serial.println("] ");
    #endif
    }

// Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}

// Find certain uuid in the data of the received advertising packet
bool checkForUuidPresent(const uint16_t uuid, const uint8_t* reportData, uint8_t reportDataLen)
{
// Enter uuid in printed format like 0x1826 for UUID16_SVC_FITNESS_MACHINE
// uuid is internally stored in Little Endian
for (int i = 0; i < (reportDataLen); i++) { // step 1: never miss out a position!
if( memcmp(&uuid, (reportData+i), 2) == 0) {
return true;
}
}
return false;
}

/**

  • Callback invoked when scanner pick up an advertising data
  • @param report Structural advertising data
    /
    void client_scan_callback(ble_gap_evt_adv_report_t
    report)
    {
    // Since we configure the scanner with filterUuid(CPS, CSC, FTMS)
    // scan_callback is invoked for devices with usually CPS service advertised.
    // However, we only do business with FTMS enabled Trainer types so check
    // for UUID16_SVC_FITNESS_MACHINE to be present, if not --> keep scanning!
    if (!checkForUuidPresent(UUID16_SVC_FITNESS_MACHINE, report->data.p_data, report->data.len)) {
    return; // Keep scanning for FTMS trainer !!
    }
    #ifdef DEBUG
    Serial.println("Found advertising Peripheral with FTMS service!, see the Raw Data packet:");
    Serial.println(F("Timestamp MAC Address Rssi Data"));
    Serial.printf("%09d ", millis());
    // MAC is in little endian --> print reverse
    Serial.printBufferReverse(report->peer_addr.addr, 6, ':');
    Serial.print(F(" "));
    Serial.print(report->rssi);
    Serial.print(F(" "));
    Serial.printBuffer(report->data.p_data, report->data.len, '-');
    Serial.println();
    #endif
    Bluefruit.Central.connect(report);
    }

#ifdef DEBUG
void PrintPeerAddress(uint8_t addr[6])
{
for (int i = 1; i < 6; i++) {
// Display byte by byte in HEX reverse: little Endian
Serial.printf("%02X:",addr[(6-i)], HEX);
}
Serial.printf("%02X ",addr[0], HEX);
}
#endif

/**

  • Callback invoked when an connection is established
  • @param conn_handle
    /
    void client_connect_callback(uint16_t conn_handle)
    {
    char peripheral_name[MAX_PAYLOAD] = { 0 };
    uint8_t peripheral_addr[6] = {0};
    // Get the reference to current connection
    BLEConnection
    connection = Bluefruit.Connection(conn_handle);
    connection->getPeerName(peripheral_name, sizeof(peripheral_name));
    ble_gap_addr_t peer_address = connection->getPeerAddr();
    memcpy(peripheral_addr, peer_address.addr, 6);
    #ifdef DEBUG
    Serial.printf("Feather nRF52 (Central) connected to Trainer (Peripheral) device: [%s] MAC Address: ", peripheral_name);
    PrintPeerAddress(peripheral_addr);
    Serial.println();
    #endif
    client_Connection_Handle = conn_handle;
    #ifdef DEBUG
    Serial.println("Now checking all Client Services and Characteristics!");
    Serial.println("If Mandatory Services Fail --> the Client will disconnect!");
    #endif
    // ---------------------------- GA and DIS SERVICE ------------------------------------------
    #ifdef DEBUG
    Serial.println("First checking Generic Access and Device Information Services and Characteristics!");
    #endif
    // If Generic Access is not found then go on.... NOT FATAL !

while (!(client_GenericAccess_Service.discover(conn_handle))) {
if ( client_GenericAccess_Service.discover(conn_handle) ) {
#ifdef DEBUG
Serial.print(F("Found Client Generic Access\n"));
#endif
if ( client_GA_DeviceName_Chr.discover() ) {
client_GA_DeviceName_Chr.read(client_GA_DeviceName_Data, sizeof(client_GA_DeviceName_Data));
#ifdef DEBUG
Serial.printf(" -> Client Reads Device Name: [%s]\n", client_GA_DeviceName_Data);
#endif
}
if ( client_GA_Appearance_Chr.discover() ) {
client_GA_Appearance_Value = client_GA_Appearance_Chr.read16();
#ifdef DEBUG
Serial.printf(" -> Client Reads Appearance: [%d]\n", client_GA_Appearance_Value);
#endif
}
} // GA
}
while (!( client_DIS_Service.discover(conn_handle) )){
// If DIS is not found then go on.... NOT FATAL !
if ( client_DIS_Service.discover(conn_handle) ) {
#ifdef DEBUG
Serial.print(F("Found Client Device Information\n"));
#endif
// 1
if ( client_DIS_ManufacturerName_Chr.discover() ) {
// read and print out Manufacturer
if ( client_DIS_ManufacturerName_Chr.read(client_DIS_Manufacturer_Str, sizeof(client_DIS_Manufacturer_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Manufacturer: [%s]\n", client_DIS_Manufacturer_Str);
#endif
}
} // 1
// 2
if ( client_DIS_ModelNumber_Chr.discover() ) {
// read and print out Model Number
if ( client_DIS_ModelNumber_Chr.read(client_DIS_ModelNumber_Str, sizeof(client_DIS_ModelNumber_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Model Number: [%s]\n", client_DIS_ModelNumber_Str);
#endif
}
} // 2
// 3
if ( client_DIS_SerialNumber_Chr.discover() ) {
// read and print out Serial Number
if ( client_DIS_SerialNumber_Chr.read(client_DIS_SerialNumber_Str, sizeof(client_DIS_SerialNumber_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Serial Number: [%s]\n", client_DIS_SerialNumber_Str);
#endif
}
} // 3
} // DIS
}

// ---------------------------- END GA and DIS SERVICE ------------------------------------------------

// -----------------------------FTM SERVICE ------------------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Mandatory Client Fitness Machine (FTM) Service ... ");
#endif
// If FTM is not found, disconnect, resume scanning, and return
if ( client_FitnessMachine_Service.discover(conn_handle) )
{
#ifdef DEBUG
BLEConnection* conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("FTMS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Service is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Feature Characteristic ... ");
#endif
// If FTM Feature is not found, disconnect, resume scanning, and return
while (!( client_FTM_Feature_Chr.discover() ))
{
if ( client_FTM_Feature_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read FTM Feature Data
client_FTM_Feature_Chr.read(client_FTM_Feature_Data, 8);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Feature bytes: [8] [ ");
for (int i = 0; i < sizeof(client_FTM_Feature_Data); i++) {
Serial.printf("%02X ", client_FTM_Feature_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Disconnecting since Client FTM Feature Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
Bluefruit.Connection(conn_handle);
Serial.print("Discovering Client FTM Feature not decouvert ... ");
// return;
}
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Control Point Characteristic ... ");
#endif
// If FTM Control Point is not found, disconnect, resume scanning, and return
if ( client_FTM_ControlPoint_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Control Point Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Status Characteristic ... ");
#endif
// If FTM Status is not found, disconnect, resume scanning, and return
if ( client_FTM_Status_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Status Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Training Status Characteristic ... ");
#endif
// FTM Training Status is NOT MANDATORY
if ( client_FTM_TrainingStatus_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory");
#endif
}
/*
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Supported Resistance Level Range Characteristic ... ");
#endif
// FTM SupportedResistanceLevelRange is not mandatory!
if ( client_FTM_SupportedResistanceLevelRange_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read Supported Resistance Level Range Data
client_FTM_SupportedResistanceLevelRange_Chr.read(client_FTM_SupportedResistanceLevelRange_Data, 6);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ ");
for (int i = 0; i < sizeof(client_FTM_SupportedResistanceLevelRange_Data); i++) {
Serial.printf("%02X ", client_FTM_SupportedResistanceLevelRange_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT mandatory!");
#endif
}
/*
Serial.println("Disconnecting since Client FTM Supported Resistance Level Range Characteristic is mandatory!");
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Supported Power Range Characteristic ... ");
#endif
// FTM SupportedPowerRange is not mandatory!
if ( client_FTM_SupportedPowerRange_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read Supported Resistance Level Range values
client_FTM_SupportedPowerRange_Chr.read(client_FTM_SupportedPowerRange_Data, 6);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Supported Power Range bytes: [6] [ ");
for (int i = 0; i < sizeof(client_FTM_SupportedPowerRange_Data); i++) {
Serial.printf("%02X ", client_FTM_SupportedPowerRange_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT mandatory!");
#endif
}
/*
#ifdef DEBUG
Serial.println("Disconnecting since Client FTM Supported Power Range Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Indoor Bike Data Characteristic ... ");
#endif
// FTM Indoor Bike Data is not mandatory
if ( client_FTM_IndoorBikeData_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory");
#endif
}
/*
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Indoor Bike Data Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
/
// ---------------------------- End FTM SERVICE ---------------------------------------------
// ---------------------------- CP SERVICE --------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Client Cycling Power (CP) Service ... ");
#endif
// If CPS is not found, disconnect, resume scanning, and return
if ( client_CyclingPower_Service.discover(conn_handle) )
{
#ifdef DEBUG
BLEConnection
conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("CPS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client Cyling Power Service is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client CP Measurement characteristic ... ");
#endif
if ( !client_CP_Measurement_Chr.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client CP Measurement Characteristic is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
} else {
#ifdef DEBUG
Serial.println("Found it!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Control Point characteristic ... ");
#endif
if ( client_CP_ControlPoint_Chr.discover() )
{
// CP Control Point chr is not mandatory
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Feature characteristic ... ");
#endif
if ( client_CP_Feature_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Configure the Cycle Power Feature characteristic
// Properties = Read
// Min Len = 1
// Max Len = 32
// B0:3 = UINT8 - Cycling Power Feature (MANDATORY)
// b0 = Pedal power balance supported; 0 = false, 1 = true
// b1 = Accumulated torque supported; 0 = false, 1 = true
// b2 = Wheel revolution data supported; 0 = false, 1 = true
// b3 = Crank revolution data supported; 0 = false, 1 = true
// b4 = Extreme magnatudes supported; 0 = false, 1 = true
// b5 = Extreme angles supported; 0 = false, 1 = true
// b6 = Top/bottom dead angle supported; 0 = false, 1 = true
// b7 = Accumulated energy supported; 0 = false, 1 = true
// b8 = Offset compensation indicator supported; 0 = false, 1 = true
// b9 = Offset compensation supported; 0 = false, 1 = true
// b10 = Cycling power measurement characteristic content masking supported; 0 = false, 1 = true
// b11 = Multiple sensor locations supported; 0 = false, 1 = true
// b12 = Crank length adj. supported; 0 = false, 1 = true
// b13 = Chain length adj. supported; 0 = false, 1 = true
// b14 = Chain weight adj. supported; 0 = false, 1 = true
// b15 = Span length adj. supported; 0 = false, 1 = true
// b16 = Sensor measurement context; 0 = force, 1 = torque
// b17 = Instantaineous measurement direction supported; 0 = false, 1 = true
// b18 = Factory calibrated date supported; 0 = false, 1 = true
// b19 = Enhanced offset compensation supported; 0 = false, 1 = true
// b20:21 = Distribtue system support; 0 = legacy, 1 = not supported, 2 = supported, 3 = RFU
// b22:32 = Reserved

// Read 32-bit client_CP_Feature_Chr value
client_CP_Feature_Flags = client_CP_Feature_Chr.read32();
#ifdef DEBUG
const uint8_t CPFC_FIXED_DATALEN = 4;
uint8_t cpfcData[CPFC_FIXED_DATALEN] = {(uint8_t)(client_CP_Feature_Flags & 0xff), (uint8_t)(client_CP_Feature_Flags >> 8),
(uint8_t)(client_CP_Feature_Flags >> 16), (uint8_t)(client_CP_Feature_Flags >> 24)};
Serial.print(" -> Client Reads Raw CP Feature bytes: [4] [ ");
for (int i = 0; i < CPFC_FIXED_DATALEN; i++) {
if ( i <= sizeof(cpfcData)) {
Serial.printf("%02X ", cpfcData[i], HEX);
}
}
Serial.println("] ");
for (int i = 0; i < sizeof(client_CP_Feature_Str); i++) {
if ( client_CP_Feature_Flags & (1 << i) )
{
Serial.println(client_CP_Feature_Str[i]);
}
}
#endif
} else {
#ifdef DEBUG
Serial.println("NOT Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Sensor Location characteristic ... ");
#endif
if ( client_CP_Location_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// The Sensor Location characteristic
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0:1 = UINT8 - Sensor Location

// Read 8-bit client CP sensor location value
client_CP_Location_Value = client_CP_Location_Chr.read8();   

#ifdef DEBUG
Serial.print(" -> Client Reads CP Location Sensor: ");
Serial.printf("Loc#: %d %s\n", client_CP_Location_Value, client_Sensor_Location_Str[client_CP_Location_Value]);
#endif
} else {
#ifdef DEBUG
Serial.println("NOT Found! NOT Mandatory!");
#endif
}
// ---------------------------- END CP SERVICE ------------------------------------------------
// ---------------------------- CSC SERVICE --------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Cycling Speed and Cadence (CSC) Service ... ");
#endif
if ( client_CyclingSpeedCadence_Service.discover(conn_handle) ) // UUID16_SVC_CYCLING_SPEED_AND_CADENCE
{
#ifdef DEBUG
BLEConnection* conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("CSCS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! CSC Service is Not Mandatory!");
#endif
return; // NO CSC -> end of client_connect_callback !!
}
/*
// Is Mandatory
#ifdef DEBUG
Serial.println("Not Found and disconnecting!");
Serial.println("CSC Service is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
*/

// Test for client CSC Characteristics when the client CSC Service is existing
#ifdef DEBUG
Serial.print("Discovering Client CSC Measurement CHR ... ");
#endif
if ( client_CSC_Measurement_Chr.discover() ) // UUID16_CHR_CSC_MEASUREMENT
{
#ifdef DEBUG
Serial.println("Found it! ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory!");
#endif
}
/*
// Is Mandatory
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client CSC Measurement CHR is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client CSC Location CHR ... ");
#endif
if ( client_CSC_Location_Chr.discover() ) // UUID16_CHR_SENSOR_LOCATION
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read 16-bit client CSC sensor location value
client_CSC_Location_Value = client_CSC_Location_Chr.read8();
#ifdef DEBUG
Serial.print(" -> Client Reads CSC Location Sensor: ");
Serial.printf("Loc#: %d %s\n", client_CSC_Location_Value, client_Sensor_Location_Str[client_CSC_Location_Value]);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CSC Feature CHR ... ");
#endif
if ( client_CSC_Feature_Chr.discover() ) // UUID16_CHR_CSC_FEATURE
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read sensor CSC Feature value in 16 bit
client_CSC_Feature_Flags = client_CSC_Feature_Chr.read16();
#ifdef DEBUG
uint8_t cscfcData[CSC_FEATURE_FIXED_DATALEN] = { (uint8_t)(client_CSC_Feature_Flags & 0xff), (uint8_t)(client_CSC_Feature_Flags >> 8) }; // Little Endian Representation
Serial.printf(" -> Client Reads Raw CSC Feature bytes: [2] [ ");
for (int i = 0; i < sizeof(cscfcData); i++) {
Serial.printf("%02X ", cscfcData[i], HEX);
}
Serial.println("] ");
for (int i = 0; i < sizeof(client_CSC_Feature_Str); i++) {
if ( (client_CSC_Feature_Flags & (1 << i)) != 0 )
{
Serial.println(client_CSC_Feature_Str[i]);
}
}
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}
// ---------------------------- END CSC SERVICE ---------------------------------------------
} // End client_connect_callback

/**

  • Callback invoked when a connection is dropped
  • @param conn_handle
  • @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
    */
    void client_disconnect_callback(uint16_t conn_handle, uint8_t reason)
    {
    client_Connection_Handle = BLE_CONN_HANDLE_INVALID;
    #ifdef DEBUG
    Serial.print("Client Disconnected, reason = 0x"); Serial.println(reason, HEX);
    Serial.println(">>> Restart the Feather nRF52 Client for a new run! <<<");
    #endif
    }

/**

  • Hooked callback that triggered when a measurement value is sent from peripheral
  • @param chr Pointer client characteristic that even occurred,
  •          in this example it should be cpmc
    
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_CP_Measurement_Chr_notify_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG_CP_MEASUREMENT
    uint8_t buffer[len]= {}; /*{ (uint8_t)(cpmcDef & 0xff), (uint8_t)(cpmcDef >> 8), // flags
    (uint8_t)(powerOut & 0xff), (uint8_t)(powerOut >> 8), // inst. power
    0, // bal
    0, 0, // torque
    0, 0, 0, 0, // cum. rev
    0, 0, // wheel time
    0, 0, // cum. crank
    0, 0, // crank time
    0, 0, // max force
    0, 0, // min force
    0, 0, // max tor
    0, 0, // min tor
    0, 0, // max ang
    0, 0, // min ang
    0, 0, // tdc
    0, 0, // bdc
    0, 0 }; // total energy
    */
    // Transfer first the contents of data to buffer (array of chars)
    Serial.printf(" -> Client Rec'd Raw CP Data: [%d] [ ", len);
    for (int i = 0; i < sizeof(buffer); i++) {
    if ( i <= sizeof(buffer)) {
    buffer[i] = *data++;
    Serial.printf("%02X ", buffer[i], HEX);
    }
    }
    Serial.print("] ");

uint8_t offset = 0;
// Get flags field
uint16_t flags = 0;
memcpy(&flags, &buffer[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
// Get Instantaneous Power values UINT16
uint16_t PowerValue = 0;
memcpy(&PowerValue, &buffer[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
Serial.printf("Instantaneous Power: %4d\n", PowerValue);
// Get the other CP measurement values
if ((flags & 1) != 0) {
// Power Balance Present
Serial.print(" --> Pedal Power Balance!");
}
if ((flags & 2) != 0) {
// Accumulated Torque
Serial.println(" --> Accumulated Torque!");
}
// etcetera...
#endif
} // End cpmc_notify_callback

/**

  • Hooked callback that triggered when a measurement value is sent from peripheral
  • @param chr Pointer client characteristic that even occurred,
  •          in this example it should be client_CP_ControlPoint_Chr
    
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_CP_ControlPoint_Chr_indicate_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    /*
    static const unsigned MAX_CONTROL_BYTES = 5;
    const uint8_t RESPONSE_SUCCESS = 1;
    const uint8_t RESPONSE_OP_CODE_NOT_SUPPORTED = 2;
    const uint8_t RESPONSE_INVALID_PARAMETER = 3;
    const uint8_t RESPONSE_OPERATION_FAILED = 4;
    const uint8_t OPCODE_RESPONSE_CODE = 32;
    Len = 3
    cpcpData[0] = Response Code = 0x20 // 32 Decimal
    cpcpData[1] = OpCode
    cpcpData[2] = responseValue
    Len = 5
    cpcpData[3] = (uint8_t)(responseParameter & 0xFF);
    cpcpData[4] = (uint8_t)(responseParameter >> 8);
    */

// Handle the response message

#ifdef DEBUG
uint8_t cpcpDataLen = (uint8_t)len;
uint8_t cpcpData[cpcpDataLen]= {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw CP Control Point Data: [%d] [ ", len);
for (int i = 0; i < sizeof(cpcpData); i++) {
cpcpData[i] = *data++;
Serial.printf("%02X ", cpcpData[i], HEX);
}
Serial.print("] ");
#endif
}

/**

  • Hooked callback that triggered when a measurement value is sent from peripheral
  • @param chr Pointer client characteristic that even occurred,
  •          in this example it should be cpmc
    
  • @param data Pointer to received data
  • @param len Length of received data
    /
    void client_CSC_Measurement_Chr_notify_callback(BLEClientCharacteristic
    chr, uint8_t* data, uint16_t len)
    {
    #ifdef DEBUG_CSC_MEASUREMENT
    uint8_t buffer[len]= {};
    // Transfer first the contents of data to buffer (array of chars)
    Serial.printf(" -> Client Rec'd Raw CSC Data: [%d] [ ", len);
    for (int i = 0; i < len; i++) {
    if ( i <= sizeof(buffer)) {
    buffer[i] = data++;
    Serial.printf("%02X ", buffer[i], HEX);
    }
    }
    Serial.print("] ");
    uint8_t offset = 0;
    // we define the offset that is to be used when reading the next field
    // Size of variables (e.g. 2 (16) or 4 bytes (32)) are constants in BluetoothGattCharacteristic
    // these represent the values you can find in the "Value Fields" table in the "Format" column
    // Read the Flags field at buffer[0]
    uint8_t flags = buffer[offset];
    offset += 1; // UINT8
    // we have to check the flags' nth bit to see if C1 field exists
    if ((flags & 1) != 0) {
    uint32_t cum_wheel_rev = 0;
    memcpy(&cum_wheel_rev, &buffer[offset], 4);
    offset += 4; // UINT32
    uint16_t last_wheel_event = 0;
    memcpy(&last_wheel_event, &buffer[offset], 2);
    offset += 2; // UINT16
    Serial.printf(" Cum. wheel rev.: %d Last wheel event: %d ", cum_wheel_rev, last_wheel_event);
    /
    Calculation of speed at the Collector can be derived from the wheel circumference and
  • data in two successive measurements. The Collector calculation can be performed as
  • shown below:
  • Speed = (Difference in two successive Cumulative Wheel Revolution values * Wheel Circumference)
  •     / (Difference in two successive Last Wheel Event Time values)
    

/
}
// we have to check the flags' nth bit to see if C2 field exists
if ((flags & 2) != 0) {
uint16_t cum_cranks = 0;
memcpy(&cum_cranks, &buffer[offset], 2);
offset += 2; // UINT16
uint16_t last_crank_event = 0;
memcpy(&last_crank_event, &buffer[offset], 2);
offset += 2; // UINT16
Serial.printf(" Cum cranks: %d Last crank event: %d", cum_cranks, last_crank_event);
/
Calculation of cadence at the Collector can be derived from data in two successive

  • measurements. The Collector calculation can be performed as shown below:
  • Cadence = (Difference in two successive Cumulative Crank Revolution values)
  •       / (Difference in two successive Last Crank Event Time values)        
    

*/
}
// etcetera...
Serial.println();
#endif
}

@le-joebar
Copy link

Dear Jörghen,

Here is the disappointing result.

The problem is still the same no feedback with V0.27

Let's not lose heart!

123456789101112131415161718192021
/*********************************************************************
This is programming code for the nRF52 based Bluefruit BLE boards

The code uses heavily the Adafruit supplied Bluefruit BLE libraries !!
Adafruit invests time and resources providing open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

MIT license, check LICENSE for more information
All text must be included in any redistribution

Message (Enter to send message to 'Adafruit ItsyBitsy nRF52840 Express' on 'COM11')
New Line
115200 baud
16:59:01.080 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
16:59:01.080 -> ----------------- Version 02.7 ------------------
16:59:01.080 -> Initialise the Bluefruit nRF52 module: Client (Central)
16:59:01.205 -> FTMS and Chars 'initialized'
16:59:01.205 -> CPS and Chars 'initialized'
16:59:01.205 -> CSCS and Chars 'initialized'
16:59:01.205 -> GA and Chars 'initialized'
16:59:01.205 -> DIS and Chars 'initialized'
16:59:01.205 -> Start Scanning for CPS, CSC and FTMS!
16:59:01.434 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
16:59:01.434 -> Timestamp MAC Address Rssi Data
16:59:01.434 -> 000091471 F8:9C:FC:53:5E:49 -59 09-02-16-18-26-18-18-18-0A-18
16:59:01.575 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
16:59:01.575 -> Now checking all Client Services and Characteristics!
16:59:01.575 -> If Mandatory Services Fail --> the Client will disconnect!
16:59:01.575 -> First checking Generic Access and Device Information Services and Characteristics!
16:59:01.668 -> Found Client Generic Access
16:59:01.808 -> -> Client Reads Device Name: [Zwift Hub]
16:59:02.042 -> -> Client Reads Appearance: [0]
16:59:02.042 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
16:59:02.042 -> Disconnecting since Client FTM Service is mandatory!
16:59:02.121 -> Client Disconnected, reason = 0x16
16:59:02.121 -> >>> Restart the Feather nRF52 Client for a new run! <<<
16:59:12.257 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
16:59:12.257 -> ----------------- Version 02.7 ------------------
16:59:12.257 -> Initialise the Bluefruit nRF52 module: Client (Central)
16:59:12.257 -> FTMS and Chars 'initialized'
16:59:12.257 -> CPS and Chars 'initialized'
16:59:12.257 -> CSCS and Chars 'initialized'
16:59:12.257 -> GA and Chars 'initialized'
16:59:12.257 -> DIS and Chars 'initialized'
16:59:12.257 -> Start Scanning for CPS, CSC and FTMS!
16:59:12.258 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
16:59:12.258 -> Timestamp MAC Address Rssi Data
16:59:12.258 -> 000001100 F8:9C:FC:53:5E:49 -55 09-02-16-18-26-18-18-18-0A-18
16:59:12.258 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
16:59:12.258 -> Now checking all Client Services and Characteristics!
16:59:12.258 -> If Mandatory Services Fail --> the Client will disconnect!
16:59:12.258 -> First checking Generic Access and Device Information Services and Characteristics!
16:59:12.266 -> Found Client Generic Access
16:59:12.266 -> -> Client Reads Device Name: [Zwift Hub]
16:59:12.266 -> -> Client Reads Appearance: [1152]
16:59:12.266 -> Found Client Device Information
16:59:12.266 -> -> Client Reads Manufacturer: [Zwift]

@le-joebar
Copy link

Here is the result of FTMS_Zwift_Bridge_v023:

17:15:10.800 -> Feather nRF52840 MITM supporting: CPS, CSC and FTMS
17:15:10.800 -> ------------------ Version 02.3 ---------------------
17:15:10.804 -> FTM Service and Chars are 'initialized'
17:15:10.804 -> CP Service and Chars are 'initialized'
17:15:10.804 -> CSC Service and Chars are 'initialized'
17:15:10.804 -> Generic Access Service and Chars are 'initialized'
17:15:10.804 -> Device Information Service and Chars are 'initialized'
17:15:10.804 -> Start Client-side Scanning for CPS, CSC and FTMS!
17:15:10.805 -> Found advertising Peripheral with FTMS enabled! See Raw data packet:
17:15:10.805 -> Timestamp Addr Rssi Data
17:15:10.805 -> 000001606 F8:9C:FC:53:5E:49 -55 09-02-16-18-26-18-18-18-0A-18
17:15:10.805 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:15:10.805 -> Now checking all mandatory Client Services and Characteristics!
17:15:10.805 -> If Mandatory Services Fail --> the Client will disconnect!
17:15:10.805 -> First checking Generic Access and Device Information Services and Characteristics!
17:15:10.828 -> Found Client Generic Access
17:15:10.828 -> -> Client Reads Device Name: [Zwift Hub]
17:15:10.828 -> -> Client Reads Appearance: [1152]
17:15:10.828 -> Found Client Device Information
17:15:10.828 -> -> Client Reads Manufacturer: [Zwift]
17:15:10.829 -> -> Client Reads Model Number: [06]
17:15:10.920 -> -> Client Reads Serial Number: [06-F89CFC535E49]
17:15:10.920 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
17:15:11.008 -> Discovering Client FTM Feature Characteristic ... Found it!
17:15:11.124 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
17:15:11.124 -> Discovering Client FTM Control Point Characteristic ... Found it!
17:15:11.703 -> Discovering Client FTM Status Characteristic ... Found it!
17:15:11.984 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:15:12.285 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
17:15:12.402 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
17:15:12.402 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
17:15:12.742 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 00 00 00 00 ]
17:15:12.742 -> Discovering Client FTM Indoor Bike Data Characteristic ... Not Found! Not Mandatory
17:15:12.742 -> Discovering Client Cycling Power (CP) Service ... Not Found!
17:15:12.742 -> Disconnecting since Client Cyling Power Service is mandatory!
17:15:12.834 -> Client disconnected from Peripheral Device: [Zwift Hub], reason: [16]
17:15:27.560 -> Feather nRF52840 MITM supporting: CPS, CSC and FTMS
17:15:27.560 -> ------------------ Version 02.3 ---------------------
17:15:27.822 -> FTM Service and Chars are 'initialized'
17:15:27.822 -> CP Service and Chars are 'initialized'
17:15:27.822 -> CSC Service and Chars are 'initialized'
17:15:27.822 -> Generic Access Service and Chars are 'initialized'
17:15:27.822 -> Device Information Service and Chars are 'initialized'
17:15:27.822 -> Start Client-side Scanning for CPS, CSC and FTMS!
17:15:27.836 -> Found advertising Peripheral with FTMS enabled! See Raw data packet:
17:15:27.836 -> Timestamp Addr Rssi Data
17:15:27.836 -> 000001270 F8:9C:FC:53:5E:49 -60 09-02-16-18-26-18-18-18-0A-18
17:15:27.836 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:15:27.836 -> Now checking all mandatory Client Services and Characteristics!
17:15:27.836 -> If Mandatory Services Fail --> the Client will disconnect!
17:15:27.836 -> First checking Generic Access and Device Information Services and Characteristics!

@le-joebar
Copy link

I'll see if with the "While" in the "Bridge V23" version if it's better

The cadence is OK but I don't have the Watts!?

20221215_181851

Here is the modified code and the results :

/*********************************************************************
This is programming code for the nRF52 based Bluefruit BLE boards

The code uses heavily the Adafruit supplied Bluefruit BLE libraries !!
Adafruit invests time and resources providing open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

    MIT license, check LICENSE for more information
    All text must be included in any redistribution

*********************************************************************/

/* -----------------------------------------------------------------------------------------------------
This code should work with all indoor cycling trainers that fully support,
Fitness Machine Service, Cycling Power Service and Cycling Speed & Cadence Service

The code links a BLE Server (a peripheral to Zwift) and a BLE Client (a central to the Trainer) with a bridge
in between, the Feather nRF52 being man-in-the-middle (MITM).
The nRF52-bridge can control, filter and alter the bi-directional interchanged data!
The client-side (central) scans and connects with the trainer relevant services: FTMS, CPS and CSC. It collects
all cyling data of the services and passes these on to the server-side....
The client-side supplies the indoor trainer with target and resistance control data.
The server-side (peripheral) advertises and enables connection with cycling apps like Zwift and collects the app's
control commands, target and resistance data. It passes these on to the client-side....
The server-side supplies the app with the generated cycling data in return.

The client plus server (MITM) are transparent to the indoor trainer as well as to the training app Zwift or alike!

Requirements: Zwift app or alike, Feather nRF52 board and a FTMS/CPS/CSC supporting indoor trainer
1) Upload and Run this code on the Feather nRF52
2) Start the Serial Monitor to catch debugging info
3) Start/Power On the indoor trainer
4) Feather nRF52 and trainer (with <name>) will pair as reported in the output
5) Start Zwift on your computer or tablet and wait....
6) Search on the Zwift pairing screens for the Feather nRF52 a.k.a. "Sim <name>"
7) Pair: Power, Cadence and Controllable one after another with "Sim <name>"
8) Optionally one can pair as well devices for heartrate and/or steering (Sterzo)
9) Start the default Zwift ride or any ride you wish
  1. Make Serial Monitor output window visible on top of the Zwift window
  2. Hop on the bike: do the work and feel resistance change with the road
  3. Inspect the info presented by Serial Monitor.....
Your trainer's device <name> is modified by the bridge to "Sim <name>", to allow for a clear distinction
between the bridge (simulating your trainer) and your original trainer, when advertising the trainer's services!
You will notice this only when connecting to Zwift on the pairing screens! Notice: Zwift extends device names with
additional numbers for identification!

*/

// -------------------------------------------------------------------------------------------
// COMPILER DIRECTIVE to allow/suppress SERIAL.PRINT messages that help debugging...
// Uncomment to activate
#define DEBUG
// Restrict activating one or more of the following DEBUG directives --> process intensive
// Have caused spurious side effects like a loss of quality of service handling!!
//#define DEBUG_CP_MEASUREMENT
//#define DEBUG_CSC_MEASUREMENT
//#define DEBUG_FTM_INDOORBIKEDATA
// --------------------------------------------------------------------------------------------

#include <bluefruit.h>

const uint8_t MAX_PAYLOAD = 20; // Max 20 byte data size for single packet BLE transfer

// Struct containing Device info to administer dis/connected devices
typedef struct
{
uint8_t PeerAddress[6];
char PeerName[MAX_PAYLOAD];
uint16_t conn_handle;
bool IsConnected;
} Device_info_t;
// -----------------------------------------------------------------
// Your hardware MAC/DEVICE ADDRESSES
// Laptop/Desktop Device Address that runs Zwift, in printed format: [00:01:02:03:04:05]
// Little Endian: in reversed order !!!!
#define LAPTOPADDRESS {0x18,0x52,0x53,0x22,0x11,0x58}
// Trainer FTMS enabled Device Address, in printed format: [00:01:02:03:04:05]
// Little Endian: in reversed order !!!!
#define TRAINERADDRESS {0x49,0x5E,0x53,0xFC,0x9C,0xF8}

// -----------------------------------------------------------------
// Initialize connectable device registration
Device_info_t Trainer = {TRAINERADDRESS, {0x00}, BLE_CONN_HANDLE_INVALID, false};
Device_info_t Laptop = { LAPTOPADDRESS, {0x00}, BLE_CONN_HANDLE_INVALID, false};
Device_info_t Smartphone = { {0x00}, {0x00}, BLE_CONN_HANDLE_INVALID, false};
// ----------------------------------------------------------------------------------

/* Generic Access
#define UUID16_SVC_GENERIC_ACCESS 0x1800
#define UUID16_CHR_DEVICE_NAME 0x2A00
#define UUID16_CHR_APPEARANCE 0x2A01
#define UUID16_CHR_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS 0x2A04 ---> not implemented
#define UUID16_CHR_CENTRAL_ADDRESS_RESOLUTION 0x2AA6 ---> not implemented
*/
BLEClientService client_GenericAccess_Service(UUID16_SVC_GENERIC_ACCESS); // Optional
BLEClientCharacteristic client_GA_Appearance_Chr(UUID16_CHR_APPEARANCE); // Read
uint16_t client_GA_Appearance_Value = 0;
BLEClientCharacteristic client_GA_DeviceName_Chr(UUID16_CHR_DEVICE_NAME); // Read, Write
unsigned char client_GA_DeviceName_Data[MAX_PAYLOAD] = {};

/* Cycling Power Service
CP Service: 0x1818
CP Characteristic: 0x2A63 (Measurement)
CP Characteristic: 0x2A65 (Feature)
CP Characteristic: 0x2A5D (Location)
CP Characteristic: 0x2A66 (Control Point)
/
BLEClientService client_CyclingPower_Service(UUID16_SVC_CYCLING_POWER); // Mandatory
BLEClientCharacteristic client_CP_Measurement_Chr(UUID16_CHR_CYCLING_POWER_MEASUREMENT); // Notify, Read, Mandatory
BLEClientCharacteristic client_CP_Feature_Chr(UUID16_CHR_CYCLING_POWER_FEATURE); // Read, optional
uint32_t client_CP_Feature_Flags = 0;
const uint8_t CP_FEATURE_DATALEN = 4; // Set MaxLen to 4
const char
client_CP_Feature_Str[] = {
"Pedal power balance supported", "Accumulated torque supported", "Wheel revolution data supported", "Crank revolution data supported",
"Extreme magnitudes supported", "Extreme angles supported", "Top/bottom dead angle supported", "Accumulated energy supported",
"Offset compensation indicator supported", "Offset compensation supported", "Cycling power measurement characteristic content masking supported",
"Multiple sensor locations supported", "Crank length adj. supported", "Chain length adj. supported", "Chain weight adj. supported",
"Span length adj. supported", "Sensor measurement context", "Instantaineous measurement direction supported", "Factory calibrated date supported",
"Enhanced offset compensation supported"
};
BLEClientCharacteristic client_CP_Location_Chr(UUID16_CHR_SENSOR_LOCATION); // Read, optional
uint8_t client_CP_Location_Value = 0; // UINT8
const char* client_Sensor_Location_Str[] = { "Other", "Top of shoe", "In shoe", "Hip", "Front wheel", "Left crank", "Right crank", "Left pedal",
"Right pedal", "Front hub", "Rear dropout", "Chainstay", "Rear wheel", "Rear hub", "Chest", "Spider", "Chain ring"
};
BLEClientCharacteristic client_CP_ControlPoint_Chr(UUID16_CHR_CYCLING_POWER_CONTROL_POINT); // Indicate, Write, optional
const uint16_t CP_CONTROL_POINT_DATALEN = 5;

/*
Cycling Speed and Cadence Service
CSC Service: 0x1816
CSC Measurement Characteristic: 0x2A5B
CSC Feature Characteristic: 0x2A5C
CSC Location Characteristic: 0x2A5D
CSC Control Point Characteristic:0x2A55 ---> not implemented
/
BLEClientService client_CyclingSpeedCadence_Service(UUID16_SVC_CYCLING_SPEED_AND_CADENCE); // Mandatory
BLEClientCharacteristic client_CSC_Measurement_Chr(UUID16_CHR_CSC_MEASUREMENT); // Notify, Read, Mandatory
BLEClientCharacteristic client_CSC_Feature_Chr(UUID16_CHR_CSC_FEATURE); // Read, optional
const uint8_t CSC_FEATURE_FIXED_DATALEN = 2; // UINT16
uint16_t client_CSC_Feature_Flags = 0;
const char
client_CSC_Feature_Str[] = {"Wheel rev supported", "Crank rev supported", "Multiple locations supported"};
BLEClientCharacteristic client_CSC_Location_Chr(UUID16_CHR_SENSOR_LOCATION); // Read, optional
uint8_t client_CSC_Location_Value = 0;
// Shared with CPS --> client_Sensor_Location_Str[]

/* Service Device Information
#define UUID16_SVC_DEVICE_INFORMATION 0x180A
#define UUID16_CHR_MODEL_NUMBER_STRING 0x2A24
#define UUID16_CHR_SERIAL_NUMBER_STRING 0x2A25
#define UUID16_CHR_FIRMWARE_REVISION_STRING 0x2A26 ---> not implemented
#define UUID16_CHR_HARDWARE_REVISION_STRING 0x2A27 ---> not implemented
#define UUID16_CHR_SOFTWARE_REVISION_STRING 0x2A28 ---> not implemented
#define UUID16_CHR_MANUFACTURER_NAME_STRING 0x2A29
*/
BLEClientService client_DIS_Service(UUID16_SVC_DEVICE_INFORMATION); // Optional
BLEClientCharacteristic client_DIS_ManufacturerName_Chr(UUID16_CHR_MANUFACTURER_NAME_STRING); // Read
char client_DIS_Manufacturer_Str[MAX_PAYLOAD] = {};
BLEClientCharacteristic client_DIS_ModelNumber_Chr(UUID16_CHR_MODEL_NUMBER_STRING); // Read
char client_DIS_ModelNumber_Str[MAX_PAYLOAD] = {};
BLEClientCharacteristic client_DIS_SerialNumber_Chr(UUID16_CHR_SERIAL_NUMBER_STRING); // Read
char client_DIS_SerialNumber_Str[MAX_PAYLOAD] = {};

/* Fitness Machine Service
#define UUID16_SVC_FITNESS_MACHINE 0x1826
#define UUID16_CHR_FITNESS_MACHINE_FEATURE 0x2ACC
#define UUID16_CHR_INDOOR_BIKE_DATA 0x2AD2
#define UUID16_CHR_TRAINING_STATUS 0x2AD3
#define UUID16_CHR_SUPPORTED_SPEED_RANGE 0x2AD4 ---> not implemented
#define UUID16_CHR_SUPPORTED_INCLINATION_RANGE 0x2AD5 ---> not implemented
#define UUID16_CHR_SUPPORTED_RESISTANCE_LEVEL_RANGE 0x2AD6
#define UUID16_CHR_SUPPORTED_HEART_RATE_RANGE 0x2AD7 ---> not implemented
#define UUID16_CHR_SUPPORTED_POWER_RANGE 0x2AD8
#define UUID16_CHR_FITNESS_MACHINE_CONTROL_POINT 0x2AD9
#define UUID16_CHR_FITNESS_MACHINE_STATUS 0x2ADA
*/
BLEClientService client_FitnessMachine_Service(UUID16_SVC_FITNESS_MACHINE); // Mandatory
// Service characteristics exposed by FTM Service
BLEClientCharacteristic client_FTM_Feature_Chr(UUID16_CHR_FITNESS_MACHINE_FEATURE); // Mandatory, Read
const uint8_t FTM_FEATURE_FIXED_DATALEN = 8;
uint8_t client_FTM_Feature_Data[FTM_FEATURE_FIXED_DATALEN];
BLEClientCharacteristic client_FTM_IndoorBikeData_Chr(UUID16_CHR_INDOOR_BIKE_DATA); // Optional, Notify
BLEClientCharacteristic client_FTM_TrainingStatus_Chr(UUID16_CHR_TRAINING_STATUS); // Optional, Read & Notify
BLEClientCharacteristic client_FTM_SupportedResistanceLevelRange_Chr(UUID16_CHR_SUPPORTED_RESISTANCE_LEVEL_RANGE); // Mandatory, Read
const uint8_t FTM_SRLR_FIXED_DATALEN = 6;
uint8_t client_FTM_SupportedResistanceLevelRange_Data[FTM_SRLR_FIXED_DATALEN];
BLEClientCharacteristic client_FTM_SupportedPowerRange_Chr(UUID16_CHR_SUPPORTED_POWER_RANGE); // Mandatory, Read
const uint8_t FTM_SPR_FIXED_DATALEN = 6;
uint8_t client_FTM_SupportedPowerRange_Data[FTM_SPR_FIXED_DATALEN];
BLEClientCharacteristic client_FTM_ControlPoint_Chr(UUID16_CHR_FITNESS_MACHINE_CONTROL_POINT); // Mandatory, Write & Indicate
BLEClientCharacteristic client_FTM_Status_Chr(UUID16_CHR_FITNESS_MACHINE_STATUS); // Mandatory, Notify

// ------------------------------------ START of SERVER DEFINITIONS -----------------------------------------------------
/* Cycling Speed and Cadence Service */
BLEService server_CyclingSpeedCadence_Service = BLEService(UUID16_SVC_CYCLING_SPEED_AND_CADENCE);
BLECharacteristic server_CSC_Measurement_Chr = BLECharacteristic(UUID16_CHR_CSC_MEASUREMENT); // Notify, Read
BLECharacteristic server_CSC_Feature_Chr = BLECharacteristic(UUID16_CHR_CSC_FEATURE); // Read
BLECharacteristic server_CSC_Location_Chr = BLECharacteristic(UUID16_CHR_SENSOR_LOCATION); // Read

/* Cycling Power Service */
BLEService server_CylingPower_Service = BLEService(UUID16_SVC_CYCLING_POWER);
BLECharacteristic server_CP_Measurement_Chr = BLECharacteristic(UUID16_CHR_CYCLING_POWER_MEASUREMENT); // Notify, Read
BLECharacteristic server_CP_Feature_Chr = BLECharacteristic(UUID16_CHR_CYCLING_POWER_FEATURE); // Read
BLECharacteristic server_CP_Location_Chr = BLECharacteristic(UUID16_CHR_SENSOR_LOCATION); // Read
BLECharacteristic server_CP_ControlPoint_Chr = BLECharacteristic(UUID16_CHR_CYCLING_POWER_CONTROL_POINT); // Indicate, Write

/* Fitness Machine Service */
BLEService server_FitnessMachine_Service = BLEService(UUID16_SVC_FITNESS_MACHINE);
BLECharacteristic server_FTM_Feature_Chr = BLECharacteristic(UUID16_CHR_FITNESS_MACHINE_FEATURE); // Read
BLECharacteristic server_FTM_IndoorBikeData_Chr = BLECharacteristic(UUID16_CHR_INDOOR_BIKE_DATA); // Notify
BLECharacteristic server_FTM_TrainingStatus_Chr = BLECharacteristic(UUID16_CHR_TRAINING_STATUS); // Notify, Read
const uint8_t FTM_TRAINING_STATUS_FIXED_DATALEN = 2; // Fixed len
BLECharacteristic server_FTM_SupportedResistanceLevelRange_Chr = BLECharacteristic(UUID16_CHR_SUPPORTED_RESISTANCE_LEVEL_RANGE); // Read
BLECharacteristic server_FTM_SupportedPowerRange_Chr = BLECharacteristic(UUID16_CHR_SUPPORTED_POWER_RANGE); // Read
BLECharacteristic server_FTM_ControlPoint_Chr = BLECharacteristic(UUID16_CHR_FITNESS_MACHINE_CONTROL_POINT); // Write & Indicate
BLECharacteristic server_FTM_Status_Chr = BLECharacteristic(UUID16_CHR_FITNESS_MACHINE_STATUS); // Notify
const uint8_t FTM_STATUS_DATALEN = 7; // Max Len was: [3] --> Notice that with de Elite Direto the size is: [7] !!

/* Device Information Service helper class instance */
BLEDis server_bledis; // Read
unsigned char FirmwareRevStr[] = "0.0.0";
unsigned char HardwareRevStr[] = "0.0.0";
unsigned char SoftwareRevStr[] = "0.0.0";

/* NORDIC UART SERVICE a.k.a. NUS
NUS Service: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
NUS RXD : 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
NUS TXD : 6E400003-B5A3-F393-E0A9-E50E24DCCA9E
*/
const uint8_t UUID_NUS_SERVICE[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, 0x6E};
const uint8_t UUID_NUS_CHR_RXD[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E};
const uint8_t UUID_NUS_CHR_TXD[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E};
BLEService server_NordicUart_Service = BLEService(UUID_NUS_SERVICE);
BLECharacteristic server_NUS_RXD_Chr = BLECharacteristic(UUID_NUS_CHR_RXD); // Read (Receiving Data)
BLECharacteristic server_NUS_TXD_Chr = BLECharacteristic(UUID_NUS_CHR_TXD); // Notify (Sending Data)

// ----------------------------- The Fitness Machine Control Point data type structure --------------------------------
// ---------------------------- Decoding is done in: server_FTM_ControlPoint_Chr_callback ------------------------------
const uint8_t ftmcpRequestControl = 0x00;
const uint8_t ftmcpReset = 0x01;
const uint8_t ftmcpSetTargetSpeed = 0x02;
const uint8_t ftmcpSetTargetInclination = 0x03;
const uint8_t ftmcpSetTargetResistanceLevel = 0x04;
const uint8_t ftmcpSetTargetPower = 0x05;
const uint8_t ftmcpSetTargetHeartRate = 0x06;
const uint8_t ftmcpStartOrResume = 0x07;
const uint8_t ftmcpStopOrPause = 0x08;
const uint8_t ftmcpSetTargetedExpendedEngery = 0x09;
const uint8_t ftmcpSetTargetedNumberOfSteps = 0x0A;
const uint8_t ftmcpSetTargetedNumberOfStrided = 0x0B;
const uint8_t ftmcpSetTargetedDistance = 0x0C;
const uint8_t ftmcpSetTargetedTrainingTime = 0x0D;
const uint8_t ftmcpSetTargetedTimeInTwoHeartRateZones = 0x0E;
const uint8_t ftmcpSetTargetedTimeInThreeHeartRateZones = 0x0F;
const uint8_t ftmcpSetTargetedTimeInFiveHeartRateZones = 0x10;
const uint8_t ftmcpSetIndoorBikeSimulationParameters = 0x11;
const uint8_t ftmcpSetWheelCircumference = 0x12;
const uint8_t ftmcpSetSpinDownControl = 0x13;
const uint8_t ftmcpSetTargetedCadence = 0x14;

const uint8_t FTM_CONTROL_POINT_DATALEN = 19; // Control point consists of 1 opcode (byte) and maximum 18 bytes as parameters
// This ftmcp_data_t structure represents the control point data. The first octet represents the opcode of the request
// followed by a parameter array of maximum 18 octects
typedef struct attribute( ( packed ) )
{
uint8_t OPCODE;
uint8_t OCTETS[(FTM_CONTROL_POINT_DATALEN - 1)];
} ftmcp_data_t;
typedef union // The union type automatically maps the bytes member array to the ftmcp_data_t structure member values
{
ftmcp_data_t values;
uint8_t bytes[FTM_CONTROL_POINT_DATALEN];
} ftmcp_data_ut;
// Fitness Machine Control Point Data variable
ftmcp_data_ut server_FTM_Control_Point_Data;
// ----------------------- end of server_FTM_ControlPoint_Chr_callback definitions ----------------------------

// -------------------------------------- END OF SERVER DEFINITIONS -------------------------------------------

// --------------------------------------------------------------------------------
// Global Server variables for decoding of INDOOR BIKE RESISTANCE PARAMETERS
// --------------------------------------------------------------------------------
float wind_speed = 0; // meters per second, resolution 0.001
float grade = 0; // percentage, resolution 0.01
float crr = 0; // Coefficient of rolling resistance, resolution 0.0001
float cw = 0; // Wind resistance Kg/m, resolution 0.01;
// --------------------------------------------------------------------------------

#define TIME_SPAN 5000 // Time span in millis 1000 = 1 second
unsigned long TimeInterval = 0;

void setup()
{
#ifdef DEBUG
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println("Feather nRF52840 MITM supporting: CPS, CSC and FTMS");
Serial.println("------------------ Version 02.3 ---------------------");
#endif
// Initialize Bluefruit with maximum connections as Peripheral = 1, Central = 1
Bluefruit.begin(1, 1);
Setup_Client_FTMS();
Setup_Client_CPS();
Setup_Client_CSC();
Setup_Client_DIS();
Client_Start_Scanning();

while (Bluefruit.Scanner.isRunning()) { // do nothing else but scanning....
yield();
}
// wait enough time or go on when Client/Central is connected and all set!
TimeInterval = millis() + TIME_SPAN; // ADD just enough delay
while ( (millis() < TimeInterval) || (!Trainer.IsConnected) ) {
yield();
}

// Configure and Start the Device Information Service
#ifdef DEBUG
Serial.println("Configuring the Server Device Information Service");
#endif
server_setupDIS();
// Setup the Cycle Power Service, Speed & Cadence Service and FTMS
#ifdef DEBUG
Serial.println("Configuring the Server Cycle Power Service");
#endif
server_setupCPS();
#ifdef DEBUG
Serial.println("Configuring the Server Cadence and Speed Service");
#endif
server_setupCSC();
#ifdef DEBUG
Serial.println("Configuring the Server Fitness Machine Service");
#endif
server_setupFTMS();
#ifdef DEBUG
Serial.println("Configuring the Server NUS Service");
#endif
server_setupNUS();
// Setup and start advertising
#ifdef DEBUG
Serial.println("Setting up the Server-side advertising payload(s)");
#endif
server_startADV();
#ifdef DEBUG
Serial.println("Server-side is CPS, CSC and FTMS advertising!");
#endif
while (Bluefruit.Advertising.isRunning()) { // ONLY advertise!
yield();
}
TimeInterval = millis() + TIME_SPAN; // ADD just enough DELAY
// wait enough time or go on when Server/Peripheral is connected and set!
while ( (millis() < TimeInterval) || (!Laptop.IsConnected) ) {
yield();
}
#ifdef DEBUG
Serial.println("Client- and Server-side are Up and Running!");
#endif
}

// ---------------------- CLIENT SIDE FUNCTIONS -------------------------
void Setup_Client_FTMS(void)
{
// Initialize client FTM Service
client_FitnessMachine_Service.begin();

// Initialize client FTM Feature characteristic
client_FTM_Feature_Chr.begin();

// Initialize client FTM Training Status characteristic
client_FTM_TrainingStatus_Chr.setNotifyCallback(client_FTM_TrainingStatus_Notify_callback);
client_FTM_TrainingStatus_Chr.begin();

// Initialize client FTM Supported Power Range characteristic
client_FTM_SupportedPowerRange_Chr.begin();

// Initialize client FTM Supported Resistance Level Range characteristic
client_FTM_SupportedResistanceLevelRange_Chr.begin();

// Initialize client FTM Indoor Bike Data characteristic
client_FTM_IndoorBikeData_Chr.setNotifyCallback(client_FTM_IndoorBikeData_Notify_callback);
client_FTM_IndoorBikeData_Chr.begin();

// Initialize client FTM Control Point characteristic
// For receiving Control Point Responses
client_FTM_ControlPoint_Chr.setIndicateCallback(client_FTM_ControlPoint_Indicate_callback);
client_FTM_ControlPoint_Chr.begin();

// Initialize client FTM Status characteristic
client_FTM_Status_Chr.setNotifyCallback(client_FTM_Status_Notify_callback);
client_FTM_Status_Chr.begin();
#ifdef DEBUG
Serial.println("FTM Service and Chars are 'initialized'");
#endif
}

void Setup_Client_CPS(void)
{
// Initialize CPS client
client_CyclingPower_Service.begin();

// Initialize CP Feature characteristics of client_CyclingPower_Service.
client_CP_Feature_Chr.begin();

// Initialize CP sensor location characteristics of client_CyclingPower_Service.
client_CP_Location_Chr.begin();

// set up callback for receiving measurement
client_CP_Measurement_Chr.setNotifyCallback(client_CP_Measurement_Chr_notify_callback);
client_CP_Measurement_Chr.begin();

// Initialize Control Point and set up Indicate callback for receiving responses (indicate!)
client_CP_ControlPoint_Chr.setIndicateCallback(client_CP_ControlPoint_Chr_indicate_callback);
client_CP_ControlPoint_Chr.begin();
#ifdef DEBUG
Serial.println("CP Service and Chars are 'initialized'");
#endif
}

void Setup_Client_CSC(void)
{
// Initialize CSC client
client_CyclingSpeedCadence_Service.begin();

// Initialize client characteristics of CSC.
client_CSC_Location_Chr.begin();

// Initialize CSC Feature characteristics of client_CSC.
client_CSC_Feature_Chr.begin();

// set up callback for receiving measurement
client_CSC_Measurement_Chr.setNotifyCallback(client_CSC_Measurement_Chr_notify_callback);
client_CSC_Measurement_Chr.begin();
#ifdef DEBUG
Serial.println("CSC Service and Chars are 'initialized'");
#endif
}

void Setup_Client_DIS(void)
{
// Initialize client Generic Access Service
client_GenericAccess_Service.begin();
// Initialize some characteristics of the Generic Access Service.
client_GA_DeviceName_Chr.begin();
client_GA_Appearance_Chr.begin();
#ifdef DEBUG
Serial.println("Generic Access Service and Chars are 'initialized'");
#endif
// Initialize client Device Information Service
client_DIS_Service.begin();
// Initialize some characteristics of the Device Information Service.
client_DIS_ManufacturerName_Chr.begin();
client_DIS_ModelNumber_Chr.begin();
client_DIS_SerialNumber_Chr.begin();
#ifdef DEBUG
Serial.println("Device Information Service and Chars are 'initialized'");
#endif
}

void loop()
{
} // end loop

void Client_Start_Scanning(void)
{
/* Start Central Scanning
- Enable auto scan if disconnected
- Interval = 100 ms, window = 80 ms
- Don't use active scan
- Filter only accept CP service
- Start(timeout) with timeout = 0 will scan forever (until connected)
*/
// Client/Central Callbacks defined
Bluefruit.Central.setDisconnectCallback(client_disconnect_callback);
Bluefruit.Central.setConnectCallback(client_connect_callback);
Bluefruit.Scanner.setRxCallback(client_scan_callback);
Bluefruit.Scanner.restartOnDisconnect(false); // default is true !!! this stage we do not want to RESTART
Bluefruit.Scanner.filterRssi(-70); // original value of -80 , we want to scan only nearby peripherals, so get close to your device !!
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
// Invoke callback if CPS or CSC or FTMS is advertised
Bluefruit.Scanner.filterUuid(UUID16_SVC_CYCLING_POWER, UUID16_SVC_CYCLING_SPEED_AND_CADENCE, UUID16_SVC_FITNESS_MACHINE);
Bluefruit.Scanner.useActiveScan(true);
Bluefruit.Scanner.start(0); // 500 // 0 = Don't stop scanning or n = after n/100 seconds
#ifdef DEBUG
Serial.println("Start Client-side Scanning for CPS, CSC and FTMS!");
delay(100); // To show print message !
#endif
}

void Client_Enable_Notify_Indicate(void)
{
// --------------------- Enable CP and CSC Notify and Indicate ------------------------------------
// Reaching here means we are ready to go, let's enable notification on measurement chr
// ------------------------- Enable FTMS Notify and Indicate ---------------------------------------
if ( client_FTM_ControlPoint_Chr.enableIndicate() ) { // MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Control Point Response Messages");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable indicate for Client FTM Control Point Characteristic.");
Serial.println("FTMS (trainer) is controlled by another Client (Training App)!");
#endif
Bluefruit.disconnect(Trainer.conn_handle);
return;
}

if ( client_FTM_Status_Chr.enableNotify() ) { // MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Status values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Status Characteristic.");
Serial.println("FTMS (trainer) is controlled by another Client (Training App)!");
#endif
Bluefruit.disconnect(Trainer.conn_handle);
return;
}

if ( client_FTM_TrainingStatus_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Training Status values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Training Status Characteristic.");
#endif
}

if ( client_FTM_IndoorBikeData_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client FTM Indoor Bike Data values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.");
#endif
}
// ------------------------- Enable FTMS Notify and Indicate ---------------------------------------
// --------------------- Enable CP and CSC Notify and Indicate ------------------------------------
if ( client_CP_Measurement_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CP Measurement values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client CP Measurement Characteristic.");
#endif
}

if ( client_CP_ControlPoint_Chr.enableIndicate() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CP Control Point Responses");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable indicate for Client CP Control Point Characteristic.");
#endif
}

if ( client_CSC_Measurement_Chr.enableNotify() ) { // NOT MANDATORY
#ifdef DEBUG
Serial.println("Ready to receive Client CSC Measurement values");
#endif
} else {
#ifdef DEBUG
Serial.println(">>> Couldn't enable notify for Client CSC Measurement Characteristic.");
#endif
}
// ------------------------- Enable CP and CSC Notify and Indicate --------------------------------

} // ------------------------- Enable FTMS Notify and Indicate ---------------------------------------

/**
Hooked callback that triggered when a status value is sent
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_FTM_TrainingStatus_Notify_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Client Training Status data is tranferred to the Server
// NO TREATMENT OF COMMAND !!!
server_FTM_TrainingStatus_Chr.notify(data, len); // Just pass on and process later!

#ifdef DEBUG
uint8_t SDataLen = (uint8_t)len;
uint8_t SDataBuf[SDataLen] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw FTM Training Status Data: [%d] [ ", len);
for (int i = 0; i < sizeof(SDataBuf); i++) {
SDataBuf[i] = *data++;
Serial.printf("%02X ", SDataBuf[i], HEX);
}
Serial.println("] ");
#endif
}

/**
Hooked callback that is triggered when an IBD value is sent
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_FTM_IndoorBikeData_Notify_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Client IBD data is tranferred to the Server
// NO TREATMENT OF COMMAND !!!
if (server_FTM_IndoorBikeData_Chr.notifyEnabled(Laptop.conn_handle)) {
server_FTM_IndoorBikeData_Chr.notify(data, len); // Just pass on and process later!
}
#ifdef DEBUG_FTM_INDOORBIKEDATA
// Only when DEBUG_FTM_INDOORBIKEDATA is defined IDBData will be parsed and printed!
ParseIndoorBikeData(data, len);
#endif
}

#ifdef DEBUG_FTM_INDOORBIKEDATA
void ParseIndoorBikeData(uint8_t* data, uint16_t len)
{
// ---> IBD Buffer Data Length depends on data flagged to be present !!!!
uint8_t IBDDataLen = (uint8_t)len;
uint8_t IBDDataBuf[IBDDataLen] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw FTM IBD Data: [%d] [ ", len);
for (int i = 0; i < sizeof(IBDDataBuf); i++) {
IBDDataBuf[i] = *data++;
Serial.printf("%02X ", IBDDataBuf[i], HEX);
}
Serial.print("] ");

uint8_t offset = 0;
uint16_t flags = 0;
memcpy(&flags, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
if ((flags & 1) != 0) {
// More Data
Serial.print(" More Data!");
}
// if ((flags & 2) != 0) { // (flags & 2) --> true or false Average Speed is always(?) there (tested with Elite Direto XR)
// Average Speed 0.01
uint16_t sim_Speed = 0;
memcpy(&sim_Speed, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Speed: %d KPH", (sim_Speed / 100));
offset += 2; // UINT16
// }
if ((flags & 4) != 0) {
// Instantaneous Cadence 0.5
uint16_t inst_Cadence = 0;
memcpy(&inst_Cadence, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Instantaneous Cadence: %d RPM", (inst_Cadence / 2));
offset += 2; // UINT16
}
if ((flags & 8) != 0) {
// Average Cadence 0.5
uint16_t av_Cadence = 0;
memcpy(&av_Cadence, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Average Cadence: %d RPM", (av_Cadence / 2));
offset += 2; // UINT16
}
if ((flags & 16) != 0) {
// Total Distance 1
uint32_t tot_Distance = 0;
memcpy(&tot_Distance, &IBDDataBuf[offset], 3); // Transfer buffer fields to variable
Serial.printf(" Total Distance: %d m", tot_Distance);
offset += 3; // UINT24
}
if ((flags & 32) != 0) {
// Resistance Level 1
uint16_t res_Level = 0;
memcpy(&res_Level, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Resistance Level: %d ", res_Level);
offset += 2; // UINT16
}
if ((flags & 64) != 0) {
// Instantaneous Power 1
uint16_t inst_Power = 0;
memcpy(&inst_Power, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Instantaneous Power: %d Watt", inst_Power);
offset += 2; // UINT16
}
if ((flags & 128) != 0) {
// Average Power 1
uint16_t av_Power = 0;
memcpy(&av_Power, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Average Power: %d Watt", av_Power);
offset += 2; // UINT16
}
if ((flags & 256) != 0) {
// Expended Energy -> UINT16 UINT16 UINT8
// Total Energy UINT16 1
uint16_t tot_Energy = 0;
memcpy(&tot_Energy, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Tot. Energy: %d kCal", tot_Energy);
offset += 2; // UINT16
// Energy per hour UINT16 1
uint16_t Energy_hr = 0;
memcpy(&Energy_hr, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Energy/hr: %d kCal/hr", Energy_hr);
offset += 2; // UINT16
// Energy per minute UINT8 1
uint8_t Energy_pm = 0;
memcpy(&Energy_pm, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Energy/m: %d kCal/m", Energy_pm);
offset += 1; // UINT8
}
if ((flags & 512) != 0) {
// Heart Rate 1
uint8_t Heart_Rate = 0;
memcpy(&Heart_Rate, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Heart Rate: %d HBM", Heart_Rate);
offset += 1; // UINT8
}
if ((flags & 1024) != 0) {
// Metabolic Equivalent 0.1
uint8_t Mets = 0;
memcpy(&Mets, &IBDDataBuf[offset], 1); // Transfer buffer fields to variable
Serial.printf(" Metabolic Equivalent: %d ", Mets / 10);
offset += 1; // UINT8
}
if ((flags & 2048) != 0) {
// Elapsed Time 1
uint16_t elap_time = 0;
memcpy(&elap_time, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Elapsed time: %d s", elap_time);
offset += 2; // UINT16
}
if ((flags & 4096) != 0) {
// Remaining Time 1
uint16_t rem_time = 0;
memcpy(&rem_time, &IBDDataBuf[offset], 2); // Transfer buffer fields to variable
Serial.printf(" Remaining time: %d s", rem_time);
offset += 2; // UINT16
}
Serial.println();
}
#endif

/**
Hooked callback that triggered when a value is sent
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_FTM_ControlPoint_Indicate_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// The receipt of Control Point settings is acknowledged by the trainer: handle it
// Send Client's Response message to the Server
// NO TREATMENT OF COMMAND !!!
server_FTM_ControlPoint_Chr.indicate(data, len); // Just pass on and process later!

#ifdef DEBUG
uint8_t RespBufferLen = (uint8_t)len;
uint8_t RespBuffer[RespBufferLen] = {}; // It is max 6 bytes long
// Transfer first the contents of data to buffer (array of chars)
Serial.print(" -> Client Rec'd Control Point Response: [ ");
for (int i = 0; i < sizeof(RespBuffer); i++) {
RespBuffer[i] = *data++;
Serial.printf("%02X ", RespBuffer[i], HEX);
}
Serial.println("] ");
#endif
}

/**
Hooked callback that triggered when a value is sent
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_FTM_Status_Notify_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Client's Machine Status data is tranferred to the Server
// NO TREATMENT OF COMMAND !!!
server_FTM_Status_Chr.notify(data, len); // Just pass on and process later!

#ifdef DEBUG
uint8_t SDataLen = (uint8_t)len;
uint8_t SDataBuf[SDataLen] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw FTM Machine Status Data: [%d] [ ", len);
for (int i = 0; i < sizeof(SDataBuf); i++) {
SDataBuf[i] = *data++;
Serial.printf("%02X ", SDataBuf[i], HEX);
}
Serial.println("] ");
#endif
}

// Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}

// Find certain uuid in the data of the received advertising packet
bool checkForUuidPresent(const uint16_t uuid, const uint8_t* reportData, uint8_t reportDataLen)
{
// Enter uuid in printed format like 0x1826 for UUID16_SVC_FITNESS_MACHINE
// uuid is internally stored in Little Endian
for (int i = 0; i < (reportDataLen); i++) { // step 1: never miss out a position!
if ( memcmp(&uuid, (reportData + i), 2) == 0) {
return true;
}
}
return false;
}

/**
Callback invoked when scanner pick up an advertising data
@param report Structural advertising data
/
void client_scan_callback(ble_gap_evt_adv_report_t
report)
{
// Since we configure the scanner with filterUuid(CPS, CSC, FTMS)
// scan_callback is invoked for devices with usually CPS service advertised.
// However, we only do business with FTMS enabled Trainer types so check
// for UUID16_SVC_FITNESS_MACHINE to be present, if not --> keep scanning!
uint8_t Device_Addr[6] = {0};
if (!checkForUuidPresent(UUID16_SVC_FITNESS_MACHINE, report->data.p_data, report->data.len)) {
return; // Keep scanning for FTMS trainer !!
}
memcpy(Device_Addr, report->peer_addr.addr, 6);
if ( !(memcmp(Device_Addr, Trainer.PeerAddress, 6) == 0) ) {
return; // Keep scanning for the required trainer !!
}
// Connect to device only with required services AND device address
if (Bluefruit.Scanner.isRunning()) {
Bluefruit.Scanner.stop();
}
#ifdef DEBUG
Serial.println("Found advertising Peripheral with FTMS enabled! See Raw data packet:");
Serial.println(F("Timestamp Addr Rssi Data"));
Serial.printf("%09d ", millis());
// MAC is in little endian --> print reverse
Serial.printBufferReverse(report->peer_addr.addr, 6, ':');
Serial.print(F(" "));
Serial.print(report->rssi);
Serial.print(F(" "));
Serial.printBuffer(report->data.p_data, report->data.len, '-');
Serial.println();
#endif
Bluefruit.Central.connect(report);
}

#ifdef DEBUG
void PrintPeerAddress(uint8_t addr[6])
{
for (int i = 1; i < 6; i++) {
// Display byte by byte in HEX reverse: little Endian
Serial.printf("%02X:", addr[(6 - i)], HEX);
}
Serial.printf("%02X ", addr[0], HEX);
}
#endif

/**
Callback invoked when a connection is established
@param conn_handle
/
void client_connect_callback(uint16_t conn_handle)
{
Trainer.conn_handle = conn_handle;
// Get the reference to current connection
BLEConnection
connection = Bluefruit.Connection(conn_handle);
connection->getPeerName(Trainer.PeerName, sizeof(Trainer.PeerName));
ble_gap_addr_t PeerAddr = connection->getPeerAddr(); // Fill BLE Gap struct
memcpy(Trainer.PeerAddress, PeerAddr.addr, 6); // Copy Peer Address from ble_gap_addr_t struct
#ifdef DEBUG
Serial.printf("Feather nRF52 (Central) connected to Trainer (Peripheral) device: [%s] MAC Address: ", Trainer.PeerName);
PrintPeerAddress(Trainer.PeerAddress);
Serial.println();
Serial.println("Now checking all mandatory Client Services and Characteristics!");
Serial.println("If Mandatory Services Fail --> the Client will disconnect!");
#endif
// ---------------------------- GA and DIS SERVICE ------------------------------------------
#ifdef DEBUG
Serial.println("First checking Generic Access and Device Information Services and Characteristics!");
#endif
// If Generic Access is not found then go on.... NOT FATAL !
while (!(client_GenericAccess_Service.discover(conn_handle))) {
if ( client_GenericAccess_Service.discover(conn_handle) ) {
#ifdef DEBUG
Serial.print(F("Found Client Generic Access\n"));
#endif
if ( client_GA_DeviceName_Chr.discover() ) {
client_GA_DeviceName_Chr.read(client_GA_DeviceName_Data, sizeof(client_GA_DeviceName_Data));
#ifdef DEBUG
Serial.printf(" -> Client Reads Device Name: [%s]\n", client_GA_DeviceName_Data);
#endif
}
if ( client_GA_Appearance_Chr.discover() ) {
client_GA_Appearance_Value = client_GA_Appearance_Chr.read16();
#ifdef DEBUG
Serial.printf(" -> Client Reads Appearance: [%d]\n", client_GA_Appearance_Value);
#endif
}
} // GA
}
// If DIS is not found then go on.... NOT FATAL !

while (!( client_DIS_Service.discover(conn_handle) )){
if ( client_DIS_Service.discover(conn_handle) ) {
#ifdef DEBUG
Serial.print(F("Found Client Device Information\n"));
#endif
// 1
if ( client_DIS_ManufacturerName_Chr.discover() ) {
// read and print out Manufacturer
if ( client_DIS_ManufacturerName_Chr.read(client_DIS_Manufacturer_Str, sizeof(client_DIS_Manufacturer_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Manufacturer: [%s]\n", client_DIS_Manufacturer_Str);
#endif
}
} // 1
// 2
if ( client_DIS_ModelNumber_Chr.discover() ) {
// read and print out Model Number
if ( client_DIS_ModelNumber_Chr.read(client_DIS_ModelNumber_Str, sizeof(client_DIS_ModelNumber_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Model Number: [%s]\n", client_DIS_ModelNumber_Str);
#endif
}
} // 2
// 3
if ( client_DIS_SerialNumber_Chr.discover() ) {
// read and print out Serial Number
if ( client_DIS_SerialNumber_Chr.read(client_DIS_SerialNumber_Str, sizeof(client_DIS_SerialNumber_Str)) ) {
#ifdef DEBUG
Serial.printf(" -> Client Reads Serial Number: [%s]\n", client_DIS_SerialNumber_Str);
#endif
}
} // 3
}
} // DIS
// ---------------------------- END GA and DIS SERVICE ------------------------------------------------

// -----------------------------FTM SERVICE ------------------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Mandatory Client Fitness Machine (FTM) Service ... ");
#endif
// If FTM is not found, disconnect, resume scanning, and return
if ( client_FitnessMachine_Service.discover(conn_handle) )
{
#ifdef DEBUG
BLEConnection* conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("FTMS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Service is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Feature Characteristic ... ");
#endif
// If FTM Feature is not found, disconnect, resume scanning, and return
while (!( client_FTM_Feature_Chr.discover() ))
{
if ( client_FTM_Feature_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read FTM Feature Data
client_FTM_Feature_Chr.read(client_FTM_Feature_Data, 8);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Feature bytes: [8] [ ");
for (int i = 0; i < sizeof(client_FTM_Feature_Data); i++) {
Serial.printf("%02X ", client_FTM_Feature_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Disconnecting since Client FTM Feature Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
Bluefruit.Connection(conn_handle);
Serial.print("Discovering Client FTM Feature not decouvert ... ");
// return;
}
}
#ifdef DEBUG
Serial.print("Discovering Client FTM Control Point Characteristic ... ");
#endif
// If FTM Control Point is not found, disconnect, resume scanning, and return
if ( client_FTM_ControlPoint_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Send a Request Control of Machine, OpCode == 0, no values!
const uint8_t RCM[1] = {0};
client_FTM_ControlPoint_Chr.write_resp(RCM, 1);
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Control Point Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
Bluefruit.Connection(conn_handle);
Serial.print("Discovering Client FTM Feature not decouvert ... ");
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Status Characteristic ... ");
#endif
// If FTM Status is not found, disconnect, resume scanning, and return
if ( client_FTM_Status_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Status Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
}

#ifdef DEBUG
Serial.print("Discovering Client FTM Training Status Characteristic ... ");
#endif
// FTM Training Status is NOT MANDATORY
if ( client_FTM_TrainingStatus_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory");
#endif
}
/*
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Supported Resistance Level Range Characteristic ... ");
#endif
// FTM SupportedResistanceLevelRange is not mandatory!
if ( client_FTM_SupportedResistanceLevelRange_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read Supported Resistance Level Range Data
client_FTM_SupportedResistanceLevelRange_Chr.read(client_FTM_SupportedResistanceLevelRange_Data, 6);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ ");
for (int i = 0; i < sizeof(client_FTM_SupportedResistanceLevelRange_Data); i++) {
Serial.printf("%02X ", client_FTM_SupportedResistanceLevelRange_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT mandatory!");
#endif
}
/*
Serial.println("Disconnecting since Client FTM Supported Resistance Level Range Characteristic is mandatory!");
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Supported Power Range Characteristic ... ");
#endif
// FTM SupportedPowerRange is not mandatory!
if ( client_FTM_SupportedPowerRange_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read Supported Resistance Level Range values
client_FTM_SupportedPowerRange_Chr.read(client_FTM_SupportedPowerRange_Data, 6);
#ifdef DEBUG
Serial.print(" -> Client Reads Raw FTM Supported Power Range bytes: [6] [ ");
for (int i = 0; i < sizeof(client_FTM_SupportedPowerRange_Data); i++) {
Serial.printf("%02X ", client_FTM_SupportedPowerRange_Data[i], HEX);
} // for
Serial.println("] ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT mandatory!");
#endif
}
/*
#ifdef DEBUG
Serial.println("Disconnecting since Client FTM Supported Power Range Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client FTM Indoor Bike Data Characteristic ... ");
#endif
// FTM Indoor Bike Data is not mandatory
if ( client_FTM_IndoorBikeData_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory");
#endif
}
/*
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client FTM Indoor Bike Data Characteristic is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
return;
/
// ---------------------------- End FTM SERVICE ---------------------------------------------
// ---------------------------- CP SERVICE --------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Client Cycling Power (CP) Service ... ");
#endif
// If CPS is not found, disconnect, resume scanning, and return
while (!( client_CyclingPower_Service.discover(conn_handle) ))
{
if ( client_CyclingPower_Service.discover(conn_handle) )
{
#ifdef DEBUG
BLEConnection
conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("CPS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client Cyling Power Service is mandatory!");
#endif
// MANDATORY so disconnect since we couldn't find the service
Bluefruit.disconnect(conn_handle);
Bluefruit.Connection(conn_handle);
Serial.print("Discovering Cycling Power (CP) Service not decouvert ... ");
return;
}
}
#ifdef DEBUG
Serial.print("Discovering Client CP Measurement characteristic ... ");
#endif

if ( !client_CP_Measurement_Chr.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client CP Measurement Characteristic is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
} else {
#ifdef DEBUG
Serial.println("Found it!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Control Point characteristic ... ");
#endif
if ( client_CP_ControlPoint_Chr.discover() )
{
// CP Control Point chr is not mandatory
#ifdef DEBUG
Serial.println("Found it!");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Feature characteristic ... ");
#endif
if ( client_CP_Feature_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Configure the Cycle Power Feature characteristic
// Properties = Read
// Min Len = 1
// Max Len = 32
// B0:3 = UINT8 - Cycling Power Feature (MANDATORY)
// b0 = Pedal power balance supported; 0 = false, 1 = true
// b1 = Accumulated torque supported; 0 = false, 1 = true
// b2 = Wheel revolution data supported; 0 = false, 1 = true
// b3 = Crank revolution data supported; 0 = false, 1 = true
// b4 = Extreme magnatudes supported; 0 = false, 1 = true
// b5 = Extreme angles supported; 0 = false, 1 = true
// b6 = Top/bottom dead angle supported; 0 = false, 1 = true
// b7 = Accumulated energy supported; 0 = false, 1 = true
// b8 = Offset compensation indicator supported; 0 = false, 1 = true
// b9 = Offset compensation supported; 0 = false, 1 = true
// b10 = Cycling power measurement characteristic content masking supported; 0 = false, 1 = true
// b11 = Multiple sensor locations supported; 0 = false, 1 = true
// b12 = Crank length adj. supported; 0 = false, 1 = true
// b13 = Chain length adj. supported; 0 = false, 1 = true
// b14 = Chain weight adj. supported; 0 = false, 1 = true
// b15 = Span length adj. supported; 0 = false, 1 = true
// b16 = Sensor measurement context; 0 = force, 1 = torque
// b17 = Instantaineous measurement direction supported; 0 = false, 1 = true
// b18 = Factory calibrated date supported; 0 = false, 1 = true
// b19 = Enhanced offset compensation supported; 0 = false, 1 = true
// b20:21 = Distribtue system support; 0 = legacy, 1 = not supported, 2 = supported, 3 = RFU
// b22:32 = Reserved

// Read 32-bit client_CP_Feature_Chr value
client_CP_Feature_Flags = client_CP_Feature_Chr.read32();
#ifdef DEBUG
const uint8_t CPFC_FIXED_DATALEN = 4;
uint8_t cpfcData[CPFC_FIXED_DATALEN] = {(uint8_t)(client_CP_Feature_Flags & 0xff), (uint8_t)(client_CP_Feature_Flags >> 8),
(uint8_t)(client_CP_Feature_Flags >> 16), (uint8_t)(client_CP_Feature_Flags >> 24)};
Serial.print(" -> Client Reads Raw CP Feature bytes: [4] [ ");
for (int i = 0; i < CPFC_FIXED_DATALEN; i++) {
if ( i <= sizeof(cpfcData)) {
Serial.printf("%02X ", cpfcData[i], HEX);
}
}
Serial.println("] ");
for (int i = 0; i < sizeof(client_CP_Feature_Str); i++) {
if ( client_CP_Feature_Flags & (1 << i) )
{
Serial.println(client_CP_Feature_Str[i]);
}
}
#endif
} else {
#ifdef DEBUG
Serial.println("NOT Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CP Sensor Location characteristic ... ");
#endif
if ( client_CP_Location_Chr.discover() )
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// The Sensor Location characteristic
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0:1 = UINT8 - Sensor Location

// Read 8-bit client CP sensor location value
client_CP_Location_Value = client_CP_Location_Chr.read8();   

#ifdef DEBUG
Serial.print(" -> Client Reads CP Location Sensor: ");
Serial.printf("Loc#: %d %s\n", client_CP_Location_Value, client_Sensor_Location_Str[client_CP_Location_Value]);
#endif
} else {
#ifdef DEBUG
Serial.println("NOT Found! NOT Mandatory!");
#endif
}
// ---------------------------- END CP SERVICE ------------------------------------------------
// ---------------------------- CSC SERVICE --------------------------------------------------
#ifdef DEBUG
Serial.print("Discovering Cycling Speed and Cadence (CSC) Service ... ");
#endif
if ( client_CyclingSpeedCadence_Service.discover(conn_handle) ) // UUID16_SVC_CYCLING_SPEED_AND_CADENCE
{
#ifdef DEBUG
BLEConnection* conn = Bluefruit.Connection(conn_handle);
uint16_t max_payload = conn->getMtu()-3;
uint16_t data_length = conn->getDataLength();
Serial.print("Found it! ");
Serial.printf("CSCS Max Payload: %d Data Length: %d\n", max_payload, data_length);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! CSC Service is Not Mandatory!");
#endif
return; // NO CSC -> end of client_connect_callback !!
}
/*
// Is Mandatory
#ifdef DEBUG
Serial.println("Not Found and disconnecting!");
Serial.println("CSC Service is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
*/

// Test for client CSC Characteristics when the client CSC Service is existing
#ifdef DEBUG
Serial.print("Discovering Client CSC Measurement CHR ... ");
#endif
if ( client_CSC_Measurement_Chr.discover() ) // UUID16_CHR_CSC_MEASUREMENT
{
#ifdef DEBUG
Serial.println("Found it! ");
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! Not Mandatory!");
#endif
}
/*
// Is Mandatory
#ifdef DEBUG
Serial.println("Not Found!");
Serial.println("Disconnecting since Client CSC Measurement CHR is mandatory!");
#endif
Bluefruit.disconnect(conn_handle);
return;
*/

#ifdef DEBUG
Serial.print("Discovering Client CSC Location CHR ... ");
#endif
if ( client_CSC_Location_Chr.discover() ) // UUID16_CHR_SENSOR_LOCATION
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read 16-bit client CSC sensor location value
client_CSC_Location_Value = client_CSC_Location_Chr.read8();
#ifdef DEBUG
Serial.print(" -> Client Reads CSC Location Sensor: ");
Serial.printf("Loc#: %d %s\n", client_CSC_Location_Value, client_Sensor_Location_Str[client_CSC_Location_Value]);
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}

#ifdef DEBUG
Serial.print("Discovering Client CSC Feature CHR ... ");
#endif
if ( client_CSC_Feature_Chr.discover() ) // UUID16_CHR_CSC_FEATURE
{
#ifdef DEBUG
Serial.println("Found it!");
#endif
// Read sensor CSC Feature value in 16 bit
client_CSC_Feature_Flags = client_CSC_Feature_Chr.read16();
#ifdef DEBUG
uint8_t cscfcData[CSC_FEATURE_FIXED_DATALEN] = { (uint8_t)(client_CSC_Feature_Flags & 0xff), (uint8_t)(client_CSC_Feature_Flags >> 8) }; // Little Endian Representation
Serial.printf(" -> Client Reads Raw CSC Feature bytes: [2] [ ");
for (int i = 0; i < sizeof(cscfcData); i++) {
Serial.printf("%02X ", cscfcData[i], HEX);
}
Serial.println("] ");
for (int i = 0; i < sizeof(client_CSC_Feature_Str); i++) {
if ( (client_CSC_Feature_Flags & (1 << i)) != 0 )
{
Serial.println(client_CSC_Feature_Str[i]);
}
}
#endif
} else {
#ifdef DEBUG
Serial.println("Not Found! NOT Mandatory!");
#endif
}
// ---------------------------- END CSC SERVICE ---------------------------------------------
// Only now set true after ALL Mandatory Services and Char's have been discovered !!!
Trainer.IsConnected = true;
} // End client_connect_callback

/**
Callback invoked when a connection is dropped
@param conn_handle
@param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void client_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
#ifdef DEBUG
Serial.printf("Client disconnected from Peripheral Device: [%s], reason: [%02X]\n", Trainer.PeerName, reason, HEX);
#endif
Trainer.conn_handle = BLE_CONN_HANDLE_INVALID;
Trainer.IsConnected = false;
// Force server to disconnect!
if (Laptop.conn_handle != BLE_CONN_HANDLE_INVALID) {
Bluefruit.disconnect(Laptop.conn_handle);
}
}

/**
Hooked callback that triggered when a measurement value is sent from peripheral
@param chr Pointer client characteristic
in this example it should be cpmc
@param data Pointer to received data
@param len Length of received data
/
void client_CP_Measurement_Chr_notify_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Client CP Measurement data is tranferred to the Server (Zwift)
// NO TREATMENT OF RESPONSE !!!!!
if (server_CP_Measurement_Chr.notifyEnabled(Laptop.conn_handle)) {
server_CP_Measurement_Chr.notify(data, len); // Just pass on and process later!
}
#ifdef DEBUG_CP_MEASUREMENT
uint8_t buffer[len] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw CP Data: [%d] [ ", len);
for (int i = 0; i < sizeof(buffer); i++) {
if ( i <= sizeof(buffer)) {
buffer[i] = *data++;
Serial.printf("%02X ", buffer[i], HEX);
}
}
Serial.print("] ");
uint8_t offset = 0;
// Get flags field
uint16_t flags = 0;
memcpy(&flags, &buffer[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
// Get Instantaneous Power values UINT16
uint16_t PowerValue = 0;
memcpy(&PowerValue, &buffer[offset], 2); // Transfer buffer fields to variable
offset += 2; // UINT16
Serial.printf("Instantaneous Power: %4d\n", PowerValue);
// Get the other CP measurement values
if ((flags & 1) != 0) {
// Power Balance Present
Serial.print(" --> Pedal Power Balance!");
}
if ((flags & 2) != 0) {
// Accumulated Torque
Serial.println(" --> Accumulated Torque!");
}
// etcetera...
#endif
} // End cpmc_notify_callback

/**
Hooked callback that triggered when a response value is sent from peripheral
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_CP_ControlPoint_Chr_indicate_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Send Client's response message to the Server (Zwift)
// NO TREATMENT OF RESPONSE !!!!!
server_CP_ControlPoint_Chr.indicate(data, len); // Just pass on and process later!

#ifdef DEBUG
uint8_t cpcpDataLen = (uint8_t)len;
uint8_t cpcpData[cpcpDataLen] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw CP Control Point Data: [%d] [ ", len);
for (int i = 0; i < sizeof(cpcpData); i++) {
cpcpData[i] = *data++;
Serial.printf("%02X ", cpcpData[i], HEX);
}
Serial.print("] ");
#endif
}

/**
Hooked callback that triggered when a measurement value is sent from peripheral
@param chr Pointer client characteristic
@param data Pointer to received data
@param len Length of received data
/
void client_CSC_Measurement_Chr_notify_callback(BLEClientCharacteristic
chr, uint8_t* data, uint16_t len)
{
// Client CSC Measurement data is transferred to the Server (Zwift)
// NO TREATMENT OF RESPONSE !!!!!
if (server_CSC_Measurement_Chr.notifyEnabled(Laptop.conn_handle)) {
server_CSC_Measurement_Chr.notify(data, len); // Just pass on and process later!
}
#ifdef DEBUG_CSC_MEASUREMENT
uint8_t buffer[len] = {};
// Transfer first the contents of data to buffer (array of chars)
Serial.printf(" -> Client Rec'd Raw CSC Data: [%d] [ ", len);
for (int i = 0; i < sizeof(buffer); i++) {
if ( i <= sizeof(buffer)) {
buffer[i] = *data++;
Serial.printf("%02X ", buffer[i], HEX);
}
}
Serial.print("] ");
uint8_t offset = 0;
// we define the offset that is to be used when reading the next field
// Size of variables (e.g. 2 (16) or 4 bytes (32)) are constants in BluetoothGattCharacteristic
// these represent the values you can find in the "Value Fields" table in the "Format" column
// Read the Flags field at buffer[0]
uint8_t flags = buffer[offset];
offset += 1; // UINT8
// we have to check the flags' nth bit to see if C1 field exists
if ((flags & 1) != 0) {
uint32_t cum_wheel_rev = 0;
memcpy(&cum_wheel_rev, &buffer[offset], 4);
offset += 4; // UINT32
uint16_t last_wheel_event = 0;
memcpy(&last_wheel_event, &buffer[offset], 2);
offset += 2; // UINT16
Serial.printf(" Cum. wheel rev.: %d Last wheel event: %d ", cum_wheel_rev, last_wheel_event);
}
// we have to check the flags' nth bit to see if C2 field exists
if ((flags & 2) != 0) {
uint16_t cum_cranks = 0;
memcpy(&cum_cranks, &buffer[offset], 2);
offset += 2; // UINT16
uint16_t last_crank_event = 0;
memcpy(&last_crank_event, &buffer[offset], 2);
offset += 2; // UINT16
Serial.printf(" Cum cranks: %d Last crank event: %d", cum_cranks, last_crank_event);
}
Serial.println();
#endif
}
// ---------------------- END of CLIENT SIDE FUNCTIONS -------------------------
// ---------------------- START of SERVER SIDE FUNCTIONS -------------------------

void server_setupNUS(void)
{
server_NordicUart_Service.begin();
// Add NUS TXD Characteristic
server_NUS_TXD_Chr.setProperties(CHR_PROPS_NOTIFY); // Type "notify"
server_NUS_TXD_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, NO writeAccess
server_NUS_TXD_Chr.setMaxLen(MAX_PAYLOAD); // To be on the safe side!
server_NUS_TXD_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_NUS_TXD_Chr.begin();

// Add NUS RXD Characteristic
server_NUS_RXD_Chr.setProperties(CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP); // Write with No response !!
server_NUS_RXD_Chr.setPermission(SECMODE_NO_ACCESS, SECMODE_OPEN);
server_NUS_RXD_Chr.setMaxLen(MAX_PAYLOAD); // Maxlen
server_NUS_RXD_Chr.setWriteCallback(server_NUS_RXD_Chr_callback);
server_NUS_RXD_Chr.begin();
}

void server_NUS_RXD_Chr_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len)
{
// Read data received over NUS RXD from Mobile Phone
#ifdef DEBUG
uint8_t NusRxdDataLen = (uint8_t)len; // Get the actual length of data bytes and type cast to (uint8_t)
char NusRxdData[MAX_PAYLOAD + 1]; // Data is all ASCII !
memset(NusRxdData, 0, MAX_PAYLOAD); // set to zero
if (NusRxdDataLen > MAX_PAYLOAD) {
NusRxdDataLen = MAX_PAYLOAD; // Check for limit
}
memcpy(NusRxdData, data, NusRxdDataLen); // Transfer data to char array
// Display the raw packet data in actual length
Serial.printf(" -> Server NUS RXD Data [%d][%s]\n", NusRxdDataLen, NusRxdData);
#endif
}

void Construct_Dev_Name(void)
{
const char prefix[] = {'S', 'i', 'm', ' '}; // #4 Chars
size_t len = strlen((const char*)client_GA_DeviceName_Data); // Len of null terminated char array
int MaxLen = (MAX_PAYLOAD - sizeof(prefix) - 1); // 1 less for null terminating char
if (len > MaxLen) {
len = MaxLen;
}
int pos = MaxLen;
if (len > 0) {
// pfound points to the first occurence of " " (blank space char)
char pfound = strstr((const char)client_GA_DeviceName_Data, " ");
if (pfound != NULL) {
pos = int(pfound - (char*)client_GA_DeviceName_Data); // Convert to position in DevName
}
}
if ( pos > MaxLen ) {
pos = MaxLen; // Stay within char array allocated memory!
}
memmove(&client_GA_DeviceName_Data[sizeof(prefix)], &client_GA_DeviceName_Data, pos); // Make space: shift to the right
memcpy(&client_GA_DeviceName_Data, &prefix, sizeof(prefix)); // Insert prefix at begin of DevName
client_GA_DeviceName_Data[(pos + sizeof(prefix))] = 0; // Make null terminated char array at new position, skip rest!
}

void server_startADV(void)
{
// Setup and start advertising
// Supported tx_power values depending on mcu:
// - nRF52832: -40dBm, -20dBm, -16dBm, -12dBm, -8dBm, -4dBm, 0dBm, +3dBm and +4dBm.
// - nRF52840: -40dBm, -20dBm, -16dBm, -12dBm, -8dBm, -4dBm, 0dBm, +2dBm, +3dBm, +4dBm, +5dBm, +6dBm, +7dBm and +8dBm.
Bluefruit.setTxPower(4); // See above for supported values: +4dBm
// Set blink rate in advertising mode
Bluefruit.setConnLedInterval(250);

Construct_Dev_Name();

#ifdef DEBUG
Serial.printf("Setting Server Device Name to: [%s]\n", client_GA_DeviceName_Data);
#endif
Bluefruit.setName((const char*)client_GA_DeviceName_Data);
if (Bluefruit.setAppearance(client_GA_Appearance_Value))
{
#ifdef DEBUG
Serial.printf("Setting Server Appearance to: [%d]\n", client_GA_Appearance_Value);
#endif
}
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(server_connect_callback);
Bluefruit.Periph.setDisconnectCallback(server_disconnect_callback);

// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();

// Test include only FTMS Service as defined above
Bluefruit.Advertising.addService(server_CylingPower_Service);
Bluefruit.Advertising.addService(server_CyclingSpeedCadence_Service);
Bluefruit.Advertising.addService(server_FitnessMachine_Service);
// No need to advertise NUS, Companion App detects it anyway!

// Include Bluefruit.Advertising.addName and 128-bit uuid(s) result in a packet space problem!!!
// Use secondary Scan Response packet (optional)
// if there is no room for 'Name' in Advertising packet
Bluefruit.ScanResponse.addName();

/* Start Advertising
- Enable auto advertising Yes/NO if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
*/
Bluefruit.Advertising.restartOnDisconnect(true); // false --> at test stage we do NOT want to auto RESTART
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
}

void server_setupDIS(void)
{
// Configure and Start the Device Information Service
server_bledis.setManufacturer((const char*)client_DIS_Manufacturer_Str);
server_bledis.setModel((const char*)client_DIS_ModelNumber_Str);
// Notice that the Firmware Revision string is default set to
// the value of the Feather-nRF52 Board being used!
server_bledis.setFirmwareRev((const char*)FirmwareRevStr);
server_bledis.setSerialNum((const char*)client_DIS_SerialNumber_Str);
server_bledis.setHardwareRev((const char*)HardwareRevStr);
server_bledis.setSoftwareRev((const char*)SoftwareRevStr);
server_bledis.begin();
}

void server_setupFTMS(void)
{
server_FitnessMachine_Service.begin(); //

// Fitness Machine Feature, mandatory, read
server_FTM_Feature_Chr.setProperties(CHR_PROPS_READ);
server_FTM_Feature_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_FTM_Feature_Chr.setFixedLen(FTM_FEATURE_FIXED_DATALEN);
server_FTM_Feature_Chr.begin();
server_FTM_Feature_Chr.write(client_FTM_Feature_Data, FTM_FEATURE_FIXED_DATALEN);

// Indoor Bike Data, optional, notify
server_FTM_IndoorBikeData_Chr.setProperties(CHR_PROPS_NOTIFY); // because type "notify"
server_FTM_IndoorBikeData_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, writeAccess
server_FTM_IndoorBikeData_Chr.setMaxLen(MAX_PAYLOAD); // To be on the safe side, when many features are set!
server_FTM_IndoorBikeData_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_FTM_IndoorBikeData_Chr.begin();

// Training Status, optional, read & notify
server_FTM_TrainingStatus_Chr.setProperties(CHR_PROPS_NOTIFY); // because type is "notify"
server_FTM_TrainingStatus_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, writeAccess
server_FTM_TrainingStatus_Chr.setFixedLen(FTM_TRAINING_STATUS_FIXED_DATALEN);
server_FTM_TrainingStatus_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_FTM_TrainingStatus_Chr.begin();

// Supported Resistance Level Range, read, optional
server_FTM_SupportedResistanceLevelRange_Chr.setProperties(CHR_PROPS_READ);
server_FTM_SupportedResistanceLevelRange_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_FTM_SupportedResistanceLevelRange_Chr.setFixedLen(FTM_SRLR_FIXED_DATALEN);
server_FTM_SupportedResistanceLevelRange_Chr.begin();
server_FTM_SupportedResistanceLevelRange_Chr.write(client_FTM_SupportedResistanceLevelRange_Data, FTM_SRLR_FIXED_DATALEN);

// Supported Power Range, read, optional
server_FTM_SupportedPowerRange_Chr.setProperties(CHR_PROPS_READ);
server_FTM_SupportedPowerRange_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_FTM_SupportedPowerRange_Chr.setFixedLen(FTM_SPR_FIXED_DATALEN);
server_FTM_SupportedPowerRange_Chr.begin();
server_FTM_SupportedPowerRange_Chr.write(client_FTM_SupportedPowerRange_Data, FTM_SPR_FIXED_DATALEN);

// Fitness Machine Control Point, optional, write
server_FTM_ControlPoint_Chr.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_WRITE); // CHR_PROPS_READ // | CHR_PROPS_WRITE_WO_RESP); // Write with No response !!
server_FTM_ControlPoint_Chr.setPermission(SECMODE_OPEN, SECMODE_OPEN); // readAccess, writeAccess DO NOT SET: SECMODE_NO_ACCESS !
server_FTM_ControlPoint_Chr.setMaxLen(FTM_CONTROL_POINT_DATALEN); // Maxlen of Client written data: (1) OpCode and (FTM_CONTROL_POINT_DATALEN-1) OCTETS
server_FTM_ControlPoint_Chr.setWriteCallback(server_FTM_ControlPoint_Chr_callback);
server_FTM_ControlPoint_Chr.begin();

// Fitness Machine Status, mandatory, notify BLENotify,
server_FTM_Status_Chr.setProperties(CHR_PROPS_NOTIFY); // because type is "notify"
server_FTM_Status_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, writeAccess
server_FTM_Status_Chr.setMaxLen(FTM_STATUS_DATALEN);
server_FTM_Status_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_FTM_Status_Chr.begin();
}

void server_FTM_ControlPoint_Chr_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len)
{
// Server FTM Control Point data is tranferred to the Client
// NO TREATMENT OF COMMAND !!!
client_FTM_ControlPoint_Chr.write_resp(data, len); // Just pass on and process later!
// Seems to cause hickups in response stream
// client_FTM_ControlPoint_Chr.write(data, len); // Just pass on and process later!

uint8_t ftmcpDataLen = (uint8_t)len;
memset(server_FTM_Control_Point_Data.bytes, 0, sizeof(server_FTM_Control_Point_Data.bytes));
// Transfer the contents of data to server_FTM_Control_Point_Data.bytes
for (int i = 0; i < ftmcpDataLen; i++) {
server_FTM_Control_Point_Data.bytes[i] = data++;
}
/
Decodes an incoming Fitness Machine Control Point request */
#ifdef DEBUG
Serial.printf(" -> Server Rec'd Raw FTM Control Point Data [len: %d] ", ftmcpDataLen);
Serial.printf("[OpCode: %02X] [Values: ", server_FTM_Control_Point_Data.values.OPCODE, HEX);
for (int i = 0; i < ftmcpDataLen; i++) {
Serial.printf("%02X ", server_FTM_Control_Point_Data.values.OCTETS[i], HEX);
}
Serial.println("]");
#endif
switch (server_FTM_Control_Point_Data.values.OPCODE) {
case ftmcpRequestControl: {
#ifdef DEBUG
Serial.println("Request Control of Machine!");
#endif
break;
}
case ftmcpStartOrResume: {
#ifdef DEBUG
Serial.println("Start or Resume Machine!");
#endif
break;
}
case ftmcpStopOrPause: {
#ifdef DEBUG
Serial.println("Stop or Pause Machine, Parameter: Stop!");
#endif
break;
}
case ftmcpSetIndoorBikeSimulationParameters: {
// Short is 16 bit signed, so the windspeed is converted from two bytes to signed value. Highest bit is sign bit
short ws = (server_FTM_Control_Point_Data.values.OCTETS[0] << 8) + server_FTM_Control_Point_Data.values.OCTETS[1];
wind_speed = ws / 1000.0;
// Short is 16 bit signed, so a negative grade is correctly converted from two bytes to signed value. Highest bit is sign bit
short gr = (server_FTM_Control_Point_Data.values.OCTETS[3] << 8) + server_FTM_Control_Point_Data.values.OCTETS[2];
grade = gr / 100.0;
crr = server_FTM_Control_Point_Data.values.OCTETS[4] / 10000.0;
cw = server_FTM_Control_Point_Data.values.OCTETS[5] / 100.0;
#ifdef DEBUG // Remember, if debugging with Zwift, that these values are divided by 2 if in Zwift 50% settings!
Serial.print("Set Indoor Bike Simulation Parameters --> ");
Serial.print("Wind speed (1000): "); Serial.print(wind_speed);
Serial.print(" | Grade (100): "); Serial.print(grade);
Serial.print(" | Crr (10000): "); Serial.print(crr);
Serial.print(" | Cw (100): "); Serial.println(cw);
#endif
break;
}
case ftmcpReset: {
#ifdef DEBUG
Serial.println("Reset Machine!");
#endif
break;
}
case ftmcpSetTargetResistanceLevel:
case ftmcpSetTargetSpeed:
case ftmcpSetTargetInclination:
case ftmcpSetTargetPower:
case ftmcpSetTargetHeartRate:
case ftmcpSetTargetedExpendedEngery:
case ftmcpSetTargetedNumberOfSteps:
case ftmcpSetTargetedNumberOfStrided:
case ftmcpSetTargetedDistance:
case ftmcpSetTargetedTrainingTime:
case ftmcpSetTargetedTimeInTwoHeartRateZones:
case ftmcpSetTargetedTimeInThreeHeartRateZones:
case ftmcpSetTargetedTimeInFiveHeartRateZones:
case ftmcpSetWheelCircumference:
case ftmcpSetSpinDownControl:
case ftmcpSetTargetedCadence:
{
#ifdef DEBUG
Serial.println("Unresolved OpCode!");
#endif
break;
}
} // switch
}

void server_setupCPS(void)
{
// Configure the Cycling Power service
server_CylingPower_Service.begin();

server_CP_Measurement_Chr.setProperties(CHR_PROPS_NOTIFY); // type is "notify"
server_CP_Measurement_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, writeAccess
server_CP_Measurement_Chr.setMaxLen(MAX_PAYLOAD); // Will work in most cases!
server_CP_Measurement_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_CP_Measurement_Chr.begin();

server_CP_ControlPoint_Chr.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_WRITE); // Indicate and Write !!
server_CP_ControlPoint_Chr.setPermission(SECMODE_OPEN, SECMODE_OPEN); // readAccess, writeAccess DO NOT SET: SECMODE_NO_ACCESS !
server_CP_ControlPoint_Chr.setMaxLen(CP_CONTROL_POINT_DATALEN); // The charactersitic's data set varies in length
server_CP_ControlPoint_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_CP_ControlPoint_Chr.begin();
server_CP_ControlPoint_Chr.setWriteCallback(server_CP_ControlPoint_Chr_callback); // Respond to events with "Write with Response" !!

server_CP_Feature_Chr.setProperties(CHR_PROPS_READ);
server_CP_Feature_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_CP_Feature_Chr.setMaxLen(CP_FEATURE_DATALEN);
server_CP_Feature_Chr.begin();
server_CP_Feature_Chr.write32(client_CP_Feature_Flags);

server_CP_Location_Chr.setProperties(CHR_PROPS_READ);
server_CP_Location_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_CP_Location_Chr.setFixedLen(1); // UINT8
server_CP_Location_Chr.begin();
server_CP_Location_Chr.write8(client_CP_Location_Value); // Set the characteristic
}

void server_setupCSC()
{
// Configure the Cadence and Speed service
server_CyclingSpeedCadence_Service.begin();

server_CSC_Measurement_Chr.setProperties(CHR_PROPS_NOTIFY); // because type "notify"
server_CSC_Measurement_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // readAccess, writeAccess
server_CSC_Measurement_Chr.setMaxLen(MAX_PAYLOAD);
server_CSC_Measurement_Chr.setCccdWriteCallback(server_cccd_callback); // Optionally capture CCCD updates
server_CSC_Measurement_Chr.begin();

// Set the CSC Feature characteristic
server_CSC_Feature_Chr.setProperties(CHR_PROPS_READ);
server_CSC_Feature_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_CSC_Feature_Chr.setFixedLen(CSC_FEATURE_FIXED_DATALEN);
server_CSC_Feature_Chr.begin();
server_CSC_Feature_Chr.write16(client_CSC_Feature_Flags);

// Configure the Sensor Location characteristic
server_CSC_Location_Chr.setProperties(CHR_PROPS_READ);
server_CSC_Location_Chr.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
server_CSC_Location_Chr.setFixedLen(1);
server_CSC_Location_Chr.begin();
server_CSC_Location_Chr.write8(client_CSC_Location_Value); // Set the characteristic
}

void server_CP_ControlPoint_Chr_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len)
{
// Server CP Control Point data from Zwift is transferred to the Trainer
// NO TREATMENT OF COMMAND !!!
client_CP_ControlPoint_Chr.write_resp(data, len); // Just pass on and process later!
// Not sure this is the right choice!
// client_CP_ControlPoint_Chr.write(data, len); // Just pass on and process later!

#ifdef DEBUG
uint8_t cpcpDataLen = (uint8_t)len; // Get the actual length of data bytes and type cast to (uint8_t)
uint8_t cpcpData[cpcpDataLen];
memset(cpcpData, 0, cpcpDataLen); // set to zero
// Display the raw request packet actual length
Serial.printf(" -> Server CP Control Point Data [%d] [ ", cpcpDataLen);
// Transfer the contents of data to cpcpData
for (int i = 0; i < cpcpDataLen; i++) {
if ( i <= sizeof(cpcpData)) {
cpcpData[i] = *data++;
// Display the raw request packet byte by byte in HEX
Serial.printf("%02X ", cpcpData[i], HEX);
}
}
Serial.println(" ] ");
#endif
}

void server_connect_callback(uint16_t conn_handle)
{
char Peer_Name[MAX_PAYLOAD] = {0};
uint8_t Peer_Addr[6] = {0};
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
connection->getPeerName(Peer_Name, sizeof(Peer_Name));
ble_gap_addr_t peer_address = connection->getPeerAddr();
memcpy(Peer_Addr, peer_address.addr, 6);
#ifdef DEBUG
Serial.printf("Feather nRF52 (Peripheral) connected to Central device: [%s] MAC Address: ", Peer_Name);
PrintPeerAddress(Peer_Addr);
Serial.println();
#endif
// Who has been exactly connected?
// [1] Laptop is connecting
if (memcmp(Peer_Addr, Laptop.PeerAddress, 6) == 0 ) { // Check Laptop MAC address
// Laptop/PC is connecting !
memcpy(Laptop.PeerName, Peer_Name, sizeof(Peer_Name));
Laptop.conn_handle = conn_handle;
Laptop.IsConnected = true;
// MITM Server is connected with Central (Laptop/Smartphone) and ONLY
// now enable the peripheral (trainer) to stream CPS, CSC and FTMS data...
if(Trainer.IsConnected) { Client_Enable_Notify_Indicate(); }
#ifdef DEBUG
Serial.println("Waiting for Central (Zwift) to set CCCD Notify/Indicate (enable) and start....");
#endif
return; // We are done here!
}
// [2] Smartphone is connecting
memcpy(Smartphone.PeerName, Peer_Name, sizeof(Peer_Name));
Smartphone.conn_handle = conn_handle;
Smartphone.IsConnected = true;
memcpy(Smartphone.PeerAddress, Peer_Addr, 6);
#ifdef DEBUG
Serial.println("Waiting for Central (Phone) to set NUS Txd 'Notify' and start....");
#endif
}

/*
Callback invoked when a connection is dropped
@param conn_handle connection where this event happens
@param reason is a BLE_HCI_STATUS_CODE
*/
void server_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
if (Laptop.conn_handle == conn_handle ) { // Laptop/Desktop is disconnected
Laptop.conn_handle = BLE_CONN_HANDLE_INVALID;
Laptop.IsConnected = false;
#ifdef DEBUG
Serial.printf("Server disconnected from Central (Laptop): [%s], reason: [%02X]\n", Laptop.PeerName, reason, HEX);
#endif
}
if (Smartphone.conn_handle == conn_handle ) { // Smartphone is disconnected
Smartphone.conn_handle = BLE_CONN_HANDLE_INVALID;
Smartphone.IsConnected = false;
#ifdef DEBUG
Serial.printf("Server disconnected from Central (Phone): [%s], reason: [%02X]\n", Smartphone.PeerName, reason, HEX);
#endif
}
Bluefruit.Advertising.start(0);
}

void server_cccd_callback(uint16_t conn_handle, BLECharacteristic* chr, uint16_t cccd_value)
{
// When changed, display the Notify/Indicate Status for all characteristics
#ifdef DEBUG
Serial.printf("Central Device Updated CCCD to: [%d] --> ", cccd_value);
#endif
// Check the characteristic UUID this CCCD callback is associated with,
// in case this handler is used for multiple CCCD records.
if (chr->uuid == server_CP_Measurement_Chr.uuid) {
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server CP: Measurement 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server CP: Measurement 'Notify' disabled");
#endif
}
}

if (chr->uuid == server_CP_ControlPoint_Chr.uuid) {
if (chr->indicateEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server CP: ControlPoint 'Indicate' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server CP: ControlPoint 'Indicate' disabled");
#endif
}
}

if (chr->uuid == server_CSC_Measurement_Chr.uuid) {
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server CSC: Measurement 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server CSC: Measurement 'Notify' disabled");
#endif
}
}

if (chr->uuid == server_FTM_IndoorBikeData_Chr.uuid) {
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server FTM: IndoorBikeData 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server FTM: IndoorBikeData 'Notify' disabled");
#endif
}
}

if (chr->uuid == server_FTM_TrainingStatus_Chr.uuid) { // Zwift is NOT interested at all !!!
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server FTM: TrainingStatus 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server FTM: TrainingStatus 'Notify' disabled");
#endif
}
}

if (chr->uuid == server_FTM_Status_Chr.uuid) {
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server FTM: Status 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server FTM: Status 'Notify' disabled");
#endif
}
}

if (chr->uuid == server_FTM_ControlPoint_Chr.uuid) {
if (chr->indicateEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server FTM: ControlPoint 'Indicate' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server FTM: ControlPoint 'Indicate' disabled");
#endif
}
}

if (chr->uuid == server_NUS_TXD_Chr.uuid) {
if (chr->notifyEnabled(conn_handle)) {
#ifdef DEBUG
Serial.print("Server NUS: TXD 'Notify' enabled");
#endif
} else {
#ifdef DEBUG
Serial.print("Server NUS: TXD 'Notify' disabled");
#endif
}
}

#ifdef DEBUG
Serial.println();
#endif
} // end Server CCCD callback
// ---------------------- END of SERVER SIDE FUNCTIONS -------------------------

Result :

18:15:39.969 -> Feather nRF52840 MITM supporting: CPS, CSC and FTMS
18:15:39.969 -> ------------------ Version 02.3 ---------------------
18:15:39.969 -> FTM Service and Chars are 'initialized'
18:15:39.969 -> CP Service and Chars are 'initialized'
18:15:39.969 -> CSC Service and Chars are 'initialized'
18:15:39.969 -> Generic Access Service and Chars are 'initialized'
18:15:39.969 -> Device Information Service and Chars are 'initialized'
18:15:39.969 -> Start Client-side Scanning for CPS, CSC and FTMS!
18:15:39.969 -> Found advertising Peripheral with FTMS enabled! See Raw data packet:
18:15:39.969 -> Timestamp Addr Rssi Data
18:15:39.969 -> 000001090 F8:9C:FC:53:5E:49 -60 09-02-16-18-26-18-18-18-0A-18
18:15:39.969 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
18:15:39.969 -> Now checking all mandatory Client Services and Characteristics!
18:15:39.969 -> If Mandatory Services Fail --> the Client will disconnect!
18:15:39.969 -> First checking Generic Access and Device Information Services and Characteristics!
18:15:39.969 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
18:15:39.969 -> Discovering Client FTM Feature Characteristic ... Discovering Client FTM Control Point Characteristic ... Found it!
18:15:40.064 -> Discovering Client FTM Status Characteristic ... Found it!
18:15:40.391 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
18:15:40.672 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
18:15:40.827 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
18:15:40.827 -> Discovering Client FTM Supported Power Range Characteristic ... Found it!
18:15:41.088 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
18:15:41.088 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
18:15:41.368 -> Discovering Client Cycling Power (CP) Service ... Discovering Client CP Measurement characteristic ... Found it!
18:15:41.724 -> Discovering Client CP Control Point characteristic ... Not Found! NOT Mandatory!
18:15:42.005 -> Discovering Client CP Feature characteristic ... Found it!
18:15:42.175 -> -> Client Reads Raw CP Feature bytes: [4] [ 00 00 00 00 ]
18:15:42.175 -> Discovering Client CP Sensor Location characteristic ... NOT Found! NOT Mandatory!
18:15:42.175 -> Discovering Cycling Speed and Cadence (CSC) Service ... Found it! CSCS Max Payload: 20 Data Length: 27
18:15:42.346 -> Discovering Client CSC Measurement CHR ... Found it!
18:15:42.532 -> Discovering Client CSC Location CHR ... Found it!
18:15:42.718 -> -> Client Reads CSC Location Sensor: Loc#: 0 Other
18:15:42.718 -> Discovering Client CSC Feature CHR ... Not Found! NOT Mandatory!
18:15:44.315 -> Configuring the Server Device Information Service
18:15:44.315 -> Configuring the Server Cycle Power Service
18:15:44.315 -> Configuring the Server Cadence and Speed Service
18:15:44.315 -> Configuring the Server Fitness Machine Service
18:15:44.315 -> Configuring the Server NUS Service
18:15:44.315 -> Setting up the Server-side advertising payload(s)
18:15:44.315 -> Setting Server Device Name to: [Sim ]
18:15:44.315 -> Setting Server Appearance to: [0]
18:15:44.315 -> Server-side is CPS, CSC and FTMS advertising!
18:17:45.853 -> Feather nRF52 (Peripheral) connected to Central device: [DESKTOP-1CC9OQN] MAC Address: 58:11:22:53:52:18
18:17:45.853 -> Ready to receive Client FTM Control Point Response Messages
18:17:45.900 -> Ready to receive Client FTM Status values
18:17:45.900 -> >>> Couldn't enable notify for Client FTM Training Status Characteristic.
18:17:46.024 -> >>> Couldn't enable notify for Client FTM Indoor Bike Data Characteristic.
18:17:46.024 -> Ready to receive Client CP Measurement values
18:17:46.024 -> >>> Couldn't enable indicate for Client CP Control Point Characteristic.
18:17:46.057 -> Ready to receive Client CSC Measurement values
18:17:46.057 -> Waiting for Central (Zwift) to set CCCD Notify/Indicate (enable) and start....
18:17:50.388 -> Client- and Server-side are Up and Running!
18:17:51.217 -> Central Device Updated CCCD to: [1] --> Server CP: Measurement 'Notify' enabled
18:17:51.639 -> Central Device Updated CCCD to: [1] --> Server CSC: Measurement 'Notify' enabled
18:17:52.343 -> Central Device Updated CCCD to: [1] --> Server FTM: IndoorBikeData 'Notify' enabled
18:17:53.126 -> Central Device Updated CCCD to: [1] --> Server FTM: Status 'Notify' enabled
18:19:20.062 -> -> Server Rec'd Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
18:19:20.062 -> Request Control of Machine!
18:19:20.235 -> -> Client Rec'd Control Point Response: [ 80 00 01 ]
18:19:20.360 -> -> Server Rec'd Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
18:19:20.360 -> Reset Machine!
18:19:20.360 -> -> Server Rec'd Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
18:19:20.360 -> Request Control of Machine!
18:19:20.626 -> -> Client Rec'd Control Point Response: [ 80 01 01 ]
18:19:20.626 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 01 ]
18:19:25.543 -> -> Server Rec'd Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
18:19:25.543 -> Request Control of Machine!
18:19:25.589 -> -> Client Rec'd Control Point Response: [ 80 00 01 ]
18:19:25.730 -> -> Server Rec'd Raw FTM Control Point Data [len: 1] [OpCode: 07] [Values: 00 ]
18:19:25.730 -> Start or Resume Machine!
18:19:25.869 -> -> Client Rec'd Control Point Response: [ 80 07 01 ]
18:19:25.869 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [ 04 ]
18:19:26.804 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 CC 00 28 33 00 ]
18:19:26.804 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.04 | Crr (10000): 0.00 | Cw (100): 0.51
18:19:26.945 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:19:26.945 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 CC 00 28 33 ]
18:19:55.908 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 84 00 28 33 00 ]
18:19:55.908 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 1.32 | Crr (10000): 0.00 | Cw (100): 0.51
18:19:56.293 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:19:56.293 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 84 00 28 33 ]
18:19:57.253 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 04 00 28 33 00 ]
18:19:57.253 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 0.04 | Crr (10000): 0.00 | Cw (100): 0.51
18:19:57.458 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:19:57.458 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 04 00 28 33 ]
18:19:58.366 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 2D 01 28 33 00 ]
18:19:58.366 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 3.01 | Crr (10000): 0.00 | Cw (100): 0.51
18:19:58.538 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:19:58.538 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 2D 01 28 33 ]
18:19:59.475 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 2C 01 28 33 00 ]
18:19:59.475 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 3.00 | Crr (10000): 0.00 | Cw (100): 0.51
18:19:59.616 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:19:59.616 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 2C 01 28 33 ]
18:20:01.176 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 2B 01 28 33 00 ]
18:20:01.176 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.99 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:01.362 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:01.362 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 2B 01 28 33 ]
18:20:04.460 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 2A 01 28 33 00 ]
18:20:04.460 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.98 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:04.664 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:04.664 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 2A 01 28 33 ]
18:20:06.262 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 29 01 28 33 00 ]
18:20:06.262 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.97 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:06.434 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:06.434 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 29 01 28 33 ]
18:20:08.944 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 28 01 28 33 00 ]
18:20:08.944 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.96 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:09.069 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:09.069 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 28 01 28 33 ]
18:20:11.448 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 27 01 28 33 00 ]
18:20:11.448 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.95 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:11.494 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:11.494 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 27 01 28 33 ]
18:20:13.321 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 26 01 28 33 00 ]
18:20:13.321 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.94 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:13.477 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:13.477 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 26 01 28 33 ]
18:20:15.857 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 25 01 28 33 00 ]
18:20:15.857 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.93 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:16.183 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:16.183 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 25 01 28 33 ]
18:20:17.808 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 24 01 28 33 00 ]
18:20:17.808 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.92 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:18.059 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:18.059 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 24 01 28 33 ]
18:20:18.953 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 23 01 28 33 00 ]
18:20:18.953 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.91 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:19.139 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:19.139 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 23 01 28 33 ]
18:20:21.451 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 22 01 28 33 00 ]
18:20:21.451 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.90 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:21.670 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:21.670 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 22 01 28 33 ]
18:20:22.628 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 21 01 28 33 00 ]
18:20:22.628 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.89 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:22.722 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:22.722 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 21 01 28 33 ]
18:20:24.290 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 20 01 28 33 00 ]
18:20:24.290 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.88 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:24.370 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:24.370 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 20 01 28 33 ]
18:20:25.946 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1F 01 28 33 00 ]
18:20:25.946 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.87 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:26.165 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:26.165 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1F 01 28 33 ]
18:20:26.994 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1E 01 28 33 00 ]
18:20:26.994 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.86 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:27.150 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:27.150 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1E 01 28 33 ]
18:20:28.998 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1D 01 28 33 00 ]
18:20:28.998 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.85 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:29.170 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:29.170 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1D 01 28 33 ]
18:20:29.985 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1C 01 28 33 00 ]
18:20:29.985 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.84 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:30.311 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:30.311 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1C 01 28 33 ]
18:20:31.250 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1B 01 28 33 00 ]
18:20:31.250 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.83 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:31.328 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
8:20:31.328 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1B 01 28 33 ]
18:20:32.141 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 1A 01 28 33 00 ]
18:20:32.141 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.82 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:32.234 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:32.234 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 1A 01 28 33 ]
18:20:33.843 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 19 01 28 33 00 ]
18:20:33.843 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.81 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:33.984 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:33.984 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 19 01 28 33 ]
18:20:34.948 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 18 01 28 33 00 ]
18:20:34.948 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.80 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:35.200 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:35.200 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 18 01 28 33 ]
18:20:36.098 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 17 01 28 33 00 ]
18:20:36.098 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.79 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:36.331 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:36.331 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 17 01 28 33 ]
18:20:37.287 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 16 01 28 33 00 ]
18:20:37.287 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.78 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:37.507 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:37.507 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 16 01 28 33 ]
18:20:38.335 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 14 01 28 33 00 ]
18:20:38.335 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.76 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:38.569 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]
18:20:38.569 -> -> Client Rec'd Raw FTM Machine Status Data: [7] [ 12 00 00 14 01 28 33 ]
18:20:39.430 -> -> Server Rec'd Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 13 01 28 33 00 ]
18:20:39.430 -> Set Indoor Bike Simulation Parameters --> Wind speed (1000): 0.00 | Grade (100): 2.75 | Crr (10000): 0.00 | Cw (100): 0.51
18:20:39.525 -> -> Client Rec'd Control Point Response: [ 80 11 01 ]

@Berg0162
Copy link
Owner Author

Berg0162 commented Dec 15, 2022 via email

@Berg0162
Copy link
Owner Author

Dear Joel,
With respect to FTMS_Zwift_Bridge:
You inserted several While loops (in client_connect_callback): No doubt that you also have seen that these have negative (side-)effects, GA- and DIS readings are missed -> No manufacturer name, model number, et cetera. --> consequence later:
empty variables:

18:15:44.315 -> Setting Server Device Name to: [Sim ]
18:15:44.315 -> Setting Server Appearance to: [0]

I do not see for example the trainer send IndoorBikeData to Zwift as a response to the resistance parameter settings..... nor a different output as we have seen before...
My conclusion is:
I am sorry but I do not see any improvement in the output/behavior, with some While loops, on the contrary.
Next time just run the original uploaded FTMS Zwift Bridge version 023.

I am still missing the test of the latest uploaded FTMS_Client v027 (please without you inserting extra while loops).

Keep faith, somehow we will tackle the Hub connection problem!
Beste wishes,
Jörgen.

@le-joebar
Copy link

Dear Jörghen,

Here is the disappointing result.

The problem is still the same no feedback with V0.27

Let's not lose heart!

123456789101112131415161718192021
/*********************************************************************
This is programming code for the nRF52 based Bluefruit BLE boards

The code uses heavily the Adafruit supplied Bluefruit BLE libraries !!
Adafruit invests time and resources providing open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

MIT license, check LICENSE for more information
All text must be included in any redistribution

Message (Enter to send message to 'Adafruit ItsyBitsy nRF52840 Express' on 'COM11')
New Line
115200 baud
16:59:01.080 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
16:59:01.080 -> ----------------- Version 02.7 ------------------
16:59:01.080 -> Initialise the Bluefruit nRF52 module: Client (Central)
16:59:01.205 -> FTMS and Chars 'initialized'
16:59:01.205 -> CPS and Chars 'initialized'
16:59:01.205 -> CSCS and Chars 'initialized'
16:59:01.205 -> GA and Chars 'initialized'
16:59:01.205 -> DIS and Chars 'initialized'
16:59:01.205 -> Start Scanning for CPS, CSC and FTMS!
16:59:01.434 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
16:59:01.434 -> Timestamp MAC Address Rssi Data
16:59:01.434 -> 000091471 F8:9C:FC:53:5E:49 -59 09-02-16-18-26-18-18-18-0A-18
16:59:01.575 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
16:59:01.575 -> Now checking all Client Services and Characteristics!
16:59:01.575 -> If Mandatory Services Fail --> the Client will disconnect!
16:59:01.575 -> First checking Generic Access and Device Information Services and Characteristics!
16:59:01.668 -> Found Client Generic Access
16:59:01.808 -> -> Client Reads Device Name: [Zwift Hub]
16:59:02.042 -> -> Client Reads Appearance: [0]
16:59:02.042 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Not Found!
16:59:02.042 -> Disconnecting since Client FTM Service is mandatory!
16:59:02.121 -> Client Disconnected, reason = 0x16
16:59:02.121 -> >>> Restart the Feather nRF52 Client for a new run! <<<
16:59:12.257 -> Feather nRF52 Client/Central: CPS, CSC and FTMS
16:59:12.257 -> ----------------- Version 02.7 ------------------
16:59:12.257 -> Initialise the Bluefruit nRF52 module: Client (Central)
16:59:12.257 -> FTMS and Chars 'initialized'
16:59:12.257 -> CPS and Chars 'initialized'
16:59:12.257 -> CSCS and Chars 'initialized'
16:59:12.257 -> GA and Chars 'initialized'
16:59:12.257 -> DIS and Chars 'initialized'
16:59:12.257 -> Start Scanning for CPS, CSC and FTMS!
16:59:12.258 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
16:59:12.258 -> Timestamp MAC Address Rssi Data
16:59:12.258 -> 000001100 F8:9C:FC:53:5E:49 -55 09-02-16-18-26-18-18-18-0A-18
16:59:12.258 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
16:59:12.258 -> Now checking all Client Services and Characteristics!
16:59:12.258 -> If Mandatory Services Fail --> the Client will disconnect!
16:59:12.258 -> First checking Generic Access and Device Information Services and Characteristics!
16:59:12.266 -> Found Client Generic Access
16:59:12.266 -> -> Client Reads Device Name: [Zwift Hub]
16:59:12.266 -> -> Client Reads Appearance: [1152]
16:59:12.266 -> Found Client Device Information
16:59:12.266 -> -> Client Reads Manufacturer: [Zwift]

@Berg0162
Copy link
Owner Author

Berg0162 commented Mar 4, 2023 via email

@le-joebar
Copy link

Hello Jorgen,

Despite many tests with different values, the best is +- 115 mms but not at all stable.

A lot of : 17:40:18.954 -> Client Disconnected, reason = 0x16

17:20:34.084 -> Feather nRF52 Client/Central: CPS, CSC, HBM and FTMS
17:20:34.084 -> ------------------- Version 03.0 --------------------
17:20:34.084 -> Processor: Feather nRF52840
17:20:34.084 -> Initialise the Bluefruit nRF52 module: Client (Central)
17:20:34.688 -> FTMS and Chars 'initialized'
17:20:34.688 -> CPS and Chars 'initialized'
17:20:34.688 -> CSCS and Chars 'initialized'
17:20:34.688 -> GA and Chars 'initialized'
17:20:34.689 -> DIS and Chars 'initialized'
17:20:34.689 -> HRMS and Chars 'initialized'
17:20:34.689 -> Start Scanning for CPS, CSC and FTMS!
17:20:34.689 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
17:20:34.689 -> Timestamp MAC Address Rssi Data
17:20:34.689 -> 000000615 F8:9C:FC:53:5E:49 -45 09-02-16-18-26-18-18-18-0A-18
17:20:34.690 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:20:34.690 -> >>> Request NEW Connection Parameters! Interval: 115 ms Latency: 0 Supervision Timeout: 4000 ms
17:20:34.690 -> Now checking all Client Services and Characteristics!
17:20:34.690 -> If Mandatory Services Fail --> the Client will disconnect!
17:20:34.690 -> Checking Generic Access and Device Information!
17:20:34.690 -> Start BLEDiscovery... does it help ?
17:20:34.894 -> Found Client Generic Access
17:20:35.476 -> -> Client Reads Device Name: []
17:20:35.848 -> -> Client Reads Appearance: [0]
17:20:35.943 -> Not Found Client Device Information!
17:20:35.943 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Enable Notify/Indicate of relevant Peripheral (Trainer) Characteristics
17:20:36.435 -> Found it! FTMS Max Payload: 20 Data Length: 27
17:20:36.436 -> Discovering Client FTM Control Point Characteristic ... Found it!
17:20:37.225 -> Discovering Client FTM Status Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:20:38.740 -> Found it!
17:20:38.740 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
17:20:39.417 -> Discovering Client FTM Feature Characteristic ... Found it!
17:20:40.095 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
17:20:40.095 -> - Fitness Machine Features:
17:20:40.095 -> - Target Setting Features:
17:20:40.095 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:20:40.218 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Not Found! NOT mandatory!
17:20:40.297 -> Discovering Client FTM Supported Power Range Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:20:40.421 -> Not Found! NOT mandatory!
17:20:40.421 -> Discovering Client Cycling Power (CP) Service ... Not Found!
17:20:40.560 -> Disconnecting since Client Cyling Power Service is mandatory!
17:20:42.504 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
17:20:44.616 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
17:20:46.726 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]
17:20:48.820 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 00 00 28 33 00 ]

17:24:50.978 -> Feather nRF52 Client/Central: CPS, CSC, HBM and FTMS
17:24:50.978 -> ------------------- Version 03.0 --------------------
17:24:50.978 -> Processor: Feather nRF52840
17:24:50.978 -> Initialise the Bluefruit nRF52 module: Client (Central)
17:24:51.630 -> FTMS and Chars 'initialized'
17:24:51.630 -> CPS and Chars 'initialized'
17:24:51.630 -> CSCS and Chars 'initialized'
17:24:51.630 -> GA and Chars 'initialized'
17:24:51.630 -> DIS and Chars 'initialized'
17:24:51.630 -> HRMS and Chars 'initialized'
17:24:51.630 -> Start Scanning for CPS, CSC and FTMS!
17:24:51.631 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
17:24:51.631 -> Timestamp MAC Address Rssi Data
17:24:51.631 -> 000000654 F8:9C:FC:53:5E:49 -45 09-02-16-18-26-18-18-18-0A-18
17:24:51.631 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:24:51.631 -> >>> Request NEW Connection Parameters! Interval: 118 ms Latency: 0 Supervision Timeout: 4000 ms
17:24:51.631 -> Now checking all Client Services and Characteristics!
17:24:51.631 -> If Mandatory Services Fail --> the Client will disconnect!
17:24:51.631 -> Checking Generic Access and Device Information!
17:24:51.631 -> Start BLEDiscovery... does it help ?
17:24:52.440 -> Found Client Generic Access
17:24:52.458 -> -> Client Reads Device Name: []
17:24:52.977 -> -> Client Reads Appearance: [0]
17:24:53.103 -> Not Found Client Device Information!
17:24:53.103 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
17:24:53.366 -> Discovering Client FTM Control Point Characteristic ... Enable Notify/Indicate of relevant Peripheral (Trainer) Characteristics
17:24:54.698 -> Found it!
17:24:54.698 -> Discovering Client FTM Status Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:24:55.504 -> Found it!
17:24:55.504 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
17:24:56.107 -> Discovering Client FTM Feature Characteristic ... Found it!
17:24:56.571 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
17:24:56.571 -> - Fitness Machine Features:
17:24:56.571 -> - Target Setting Features:
17:24:56.571 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:24:56.695 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Found it!
17:24:57.281 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 00 00 00 00 ]
17:24:57.281 -> Discovering Client FTM Supported Power Range Characteristic ... Not Found! NOT mandatory!
17:24:57.404 -> Discovering Client Cycling Power (CP) Service ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:24:57.563 -> Not Found!
17:24:57.563 -> Disconnecting since Client Cyling Power Service is mandatory!
17:24:59.403 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
17:25:01.511 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
17:25:03.615 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]

17:37:01.110 -> Feather nRF52 Client/Central: CPS, CSC, HBM and FTMS
17:37:01.110 -> ------------------- Version 03.0 --------------------
17:37:01.110 -> Processor: Feather nRF52840
17:37:01.110 -> Initialise the Bluefruit nRF52 module: Client (Central)
17:37:01.735 -> FTMS and Chars 'initialized'
17:37:01.735 -> CPS and Chars 'initialized'
17:37:01.735 -> CSCS and Chars 'initialized'
17:37:01.735 -> GA and Chars 'initialized'
17:37:01.735 -> DIS and Chars 'initialized'
17:37:01.735 -> HRMS and Chars 'initialized'
17:37:01.735 -> Start Scanning for CPS, CSC and FTMS!
17:37:01.735 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
17:37:01.735 -> Timestamp MAC Address Rssi Data
17:37:01.735 -> 000000620 F8:9C:FC:53:5E:49 -45 09-02-16-18-26-18-18-18-0A-18
17:37:02.454 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:37:02.454 -> >>> Request NEW Connection Parameters! Interval: 118 ms Latency: 0 Supervision Timeout: 4000 ms
17:37:02.454 -> Now checking all Client Services and Characteristics!
17:37:02.454 -> If Mandatory Services Fail --> the Client will disconnect!
17:37:02.454 -> Checking Generic Access and Device Information!
17:37:02.454 -> Start BLEDiscovery... does it help ?
17:37:02.483 -> Found Client Generic Access
17:37:02.483 -> -> Client Reads Device Name: []
17:37:02.781 -> -> Client Reads Appearance: [0]
17:37:02.921 -> Not Found Client Device Information!
17:37:02.921 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Found it! FTMS Max Payload: 20 Data Length: 27
17:37:03.417 -> Discovering Client FTM Control Point Characteristic ... Enable Notify/Indicate of relevant Peripheral (Trainer) Characteristics
17:37:04.715 -> Found it!
17:37:04.715 -> Discovering Client FTM Status Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:37:05.671 -> Found it!
17:37:05.671 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
17:37:06.740 -> Discovering Client FTM Feature Characteristic ... Found it!
17:37:07.158 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
17:37:07.158 -> - Fitness Machine Features:
17:37:07.158 -> - Target Setting Features:
17:37:07.158 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:37:07.295 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Not Found! NOT mandatory!
17:37:07.435 -> Discovering Client FTM Supported Power Range Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:37:07.933 -> Found it!
17:37:08.027 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 00 00 00 00 ]
17:37:08.027 -> Discovering Client Cycling Power (CP) Service ... Not Found!
17:37:08.150 -> Disconnecting since Client Cyling Power Service is mandatory!
17:37:09.620 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
17:37:11.721 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]

17:38:03.799 -> Feather nRF52 Client/Central: CPS, CSC, HBM and FTMS
17:38:03.799 -> ------------------- Version 03.0 --------------------
17:38:03.799 -> Processor: Feather nRF52840
17:38:03.799 -> Initialise the Bluefruit nRF52 module: Client (Central)
17:38:04.313 -> FTMS and Chars 'initialized'
17:38:04.313 -> CPS and Chars 'initialized'
17:38:04.313 -> CSCS and Chars 'initialized'
17:38:04.313 -> GA and Chars 'initialized'
17:38:04.313 -> DIS and Chars 'initialized'
17:38:04.313 -> HRMS and Chars 'initialized'
17:38:04.313 -> Start Scanning for CPS, CSC and FTMS!
17:38:04.313 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
17:38:04.313 -> Timestamp MAC Address Rssi Data
17:38:04.313 -> 000001318 F8:9C:FC:53:5E:49 -45 09-02-16-18-26-18-18-18-0A-18
17:38:04.313 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:38:04.313 -> >>> Request NEW Connection Parameters! Interval: 118 ms Latency: 0 Supervision Timeout: 4000 ms
17:38:04.313 -> Now checking all Client Services and Characteristics!
17:38:04.313 -> If Mandatory Services Fail --> the Client will disconnect!
17:38:04.313 -> Checking Generic Access and Device Information!
17:38:04.313 -> Start BLEDiscovery... does it help ?
17:38:04.631 -> Found Client Generic Access
17:38:05.417 -> -> Client Reads Device Name: []
17:38:06.283 -> -> Client Reads Appearance: [0]
17:38:06.361 -> Not Found Client Device Information!
17:38:06.361 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Enable Notify/Indicate of relevant Peripheral (Trainer) Characteristics
17:38:06.638 -> Found it! FTMS Max Payload: 20 Data Length: 27
17:38:06.638 -> Discovering Client FTM Control Point Characteristic ... Found it!
17:38:07.599 -> Discovering Client FTM Status Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:38:08.656 -> Found it!
17:38:08.656 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
17:38:09.257 -> Discovering Client FTM Feature Characteristic ... Found it!
17:38:09.708 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
17:38:09.709 -> - Fitness Machine Features:
17:38:09.709 -> - Target Setting Features:
17:38:09.709 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:38:09.847 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Not Found! NOT mandatory!
17:38:09.939 -> Discovering Client FTM Supported Power Range Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:38:10.574 -> Found it!
17:38:10.667 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 00 00 00 00 ]
17:38:10.667 -> Discovering Client Cycling Power (CP) Service ... Not Found!
17:38:10.792 -> Disconnecting since Client Cyling Power Service is mandatory!
17:38:12.486 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
17:38:14.609 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
17:38:16.705 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]

17:41:48.576 -> Feather nRF52 Client/Central: CPS, CSC, HBM and FTMS
17:41:48.576 -> ------------------- Version 03.0 --------------------
17:41:48.576 -> Processor: Feather nRF52840
17:41:48.576 -> Initialise the Bluefruit nRF52 module: Client (Central)
17:41:49.072 -> FTMS and Chars 'initialized'
17:41:49.072 -> CPS and Chars 'initialized'
17:41:49.072 -> CSCS and Chars 'initialized'
17:41:49.072 -> GA and Chars 'initialized'
17:41:49.072 -> DIS and Chars 'initialized'
17:41:49.072 -> HRMS and Chars 'initialized'
17:41:49.072 -> Start Scanning for CPS, CSC and FTMS!
17:41:49.072 -> Found advertising Peripheral with FTMS service!, see the Raw Data packet:
17:41:49.072 -> Timestamp MAC Address Rssi Data
17:41:49.072 -> 000001397 F8:9C:FC:53:5E:49 -46 09-02-16-18-26-18-18-18-0A-18
17:41:49.103 -> Feather nRF52 (Central) connected to Trainer (Peripheral) device: [Zwift Hub] MAC Address: F8:9C:FC:53:5E:49
17:41:49.103 -> >>> Request NEW Connection Parameters! Interval: 118 ms Latency: 0 Supervision Timeout: 4000 ms
17:41:49.103 -> Now checking all Client Services and Characteristics!
17:41:49.103 -> If Mandatory Services Fail --> the Client will disconnect!
17:41:49.103 -> Checking Generic Access and Device Information!
17:41:49.103 -> Start BLEDiscovery... does it help ?
17:41:49.890 -> Found Client Generic Access
17:41:50.800 -> -> Client Reads Device Name: []
17:41:51.172 -> -> Client Reads Appearance: [0]
17:41:51.296 -> Not Found Client Device Information!
17:41:51.296 -> Discovering Mandatory Client Fitness Machine (FTM) Service ... Enable Notify/Indicate of relevant Peripheral (Trainer) Characteristics
17:41:51.884 -> Found it! FTMS Max Payload: 20 Data Length: 27
17:41:51.884 -> Discovering Client FTM Control Point Characteristic ... Found it!
17:41:52.719 -> Discovering Client FTM Status Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:41:53.926 -> Found it!
17:41:53.926 -> Discovering Client FTM Indoor Bike Data Characteristic ... Found it!
17:41:54.515 -> Discovering Client FTM Feature Characteristic ... Found it!
17:41:55.337 -> -> Client Reads Raw FTM Feature bytes: [8] [ 00 00 00 00 00 00 00 00 ]
17:41:55.337 -> - Fitness Machine Features:
17:41:55.337 -> - Target Setting Features:
17:41:55.337 -> Discovering Client FTM Training Status Characteristic ... Not Found! Not Mandatory
17:41:55.431 -> Discovering Client FTM Supported Resistance Level Range Characteristic ... Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 00 00 00 00 00 00 00 00 ]
17:41:55.554 -> Not Found! NOT mandatory!
17:41:55.554 -> Discovering Client FTM Supported Power Range Characteristic ... Not Found! NOT mandatory!
17:41:55.678 -> Discovering Client Cycling Power (CP) Service ... Not Found!
17:41:55.802 -> Disconnecting since Client Cyling Power Service is mandatory!
17:41:57.652 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 07 00 00 00 00 00 00 00 ]
17:41:59.756 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 22 01 28 33 00 ]
17:42:01.855 -> Client sends Indoor Bike Simulation Parameters to Trainer's FTM Control Point: [ 11 00 00 09 00 28 33 00 ]

@Berg0162
Copy link
Owner Author

Berg0162 commented Mar 7, 2023

Dear Joel,
That is NOT really the jackpot... 👎
If you still have the courage let's try just some small alterations...

    poll_delay = ConnInterval; // Synchronize SVC/CHR Discovery calls with Connection Interval

Modify --> Comment out the line

    //poll_delay = ConnInterval; // Synchronize SVC/CHR Discovery calls with Connection Interval

Try this with the connInterval value that was "most" successful around (92) 115 ms. If this new situation is improving the success rate, vary the connInterval value (from 72 and up) within the Zwift Hub Preferred Connection interval parameters

// - Zwift Hub will NOT pass 100%-error-free with a value of poll_delay lower than 250 ms !!
unsigned long poll_delay = 0;   // Unit is milliseconds
//---------------------------------------------------------------------------------------------------

You could give poll_delay in the above code line an arbitrary value of 50 ms....

Best wishes,
Jörgen.

@le-joebar
Copy link

Hello Jorgen,

I think you made Jackpot with the ESP32!!!!!

I had no connection failure.

Here is the result :

12:04:50.863 -> ets Jun 8 2016 00:22:57
12:04:50.863 ->
12:04:50.863 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
12:04:50.863 -> configsip: 0, SPIWP:0xee
12:04:50.863 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
12:04:50.863 -> mode:DIO, clock div:1
12:04:50.863 -> load:0x3fff0030,len:1344
12:04:50.863 -> load:0x40078000,len:13864
12:04:50.863 -> load:0x40080400,len:3608
12:04:50.863 -> entry 0x400805f0
12:04:51.096 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
12:04:51.096 -> ------------------ Version 01.0 --------------------
12:04:51.345 -> Configuring the default Generic Access Service
12:04:51.345 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
12:04:51.345 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
12:04:51.345 -> Configuring the Server Nordic Uart Service
12:04:51.345 -> Configuring the Server Device Information Service
12:04:51.377 -> Configuring the Server Cycle Power Service
12:04:51.377 -> Configuring the Server Cadence and Speed Service
12:04:51.377 -> Configuring the Server Fitness Machine Service
12:04:51.377 -> Configuring the Server Heart Rate Service
12:04:51.377 -> Setting up the Server advertising payload(s)
12:04:51.377 -> Setting Appearance in Advertised data to [1152]
12:04:51.377 -> Server is advertising: CPS, CSC and FTMS
12:04:51.377 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
12:04:53.065 -> Found advertising Peripheral with FTMS enabled! See data:
12:04:53.101 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
12:04:53.101 -> Service Data:
12:04:53.101 -> UUID: 0x1826, Data: �
12:04:53.141 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
12:04:53.141 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
12:04:53.141 -> Now checking all Client Services and Characteristics!
12:04:53.141 -> If Mandatory Services Fail --> the Client will disconnect!
12:04:53.544 -> Client Generic Access: Found!
12:04:53.824 -> -> Client Reads Device Name: [Zwift Hub]
12:04:54.193 -> -> Client Reads Appearance: [1152]
12:04:54.271 -> Client Device Information Service: Found!
12:04:54.641 -> -> Client Reads Manufacturer Name: [Zwift]
12:04:55.000 -> -> Client Reads Model Number: [06]
12:04:55.373 -> -> Client Reads Serial Number: [06-F89CFC535E49]
12:04:55.636 -> Client_CyclingPower_Service: Found!
12:04:55.822 -> Client_CP_Measurement_Chr: Found!
12:04:56.377 -> Client_CP_Feature_Chr: Found!
12:04:56.456 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
12:04:56.456 -> Wheel revolution data supported
12:04:56.501 -> Crank revolution data supported
12:04:56.779 -> Client_CP_Location_Chr: Found!
12:04:56.857 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
12:04:57.088 -> Client_CyclingSpeedCadence_Service: Found!
12:04:57.336 -> Client_CSC_Measurement_Chr: Found!
12:04:57.893 -> Client_CSC_Feature_Chr: Found!
12:04:57.986 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
12:04:57.986 -> Wheel rev supported
12:04:57.986 -> Crank rev supported
12:04:58.232 -> Client_CSC_Location_Chr: Found!
12:04:58.421 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
12:04:58.823 -> Client_FitnessMachine_Service: Found!
12:04:59.135 -> Client_FTM_Feature_Chr: Found!
12:04:59.281 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
12:04:59.281 -> - Fitness Machine Features:
12:04:59.281 -> Average Speed Supported
12:04:59.281 -> Cadence Supported
12:04:59.281 -> Total Distance Supported
12:04:59.281 -> Resistance Level Supported
12:04:59.281 -> Heart Rate Measurement Supported
12:04:59.281 -> Power Measurement Supported
12:04:59.281 -> - Target Setting Features:
12:04:59.281 -> Resistance Target Setting Supported
12:04:59.281 -> Power Target Setting Supported
12:04:59.281 -> Indoor Bike Simulation Parameters Supported
12:04:59.320 -> Wheel Circumference Configuration Supported
12:04:59.320 -> Spin Down Control Supported
12:04:59.584 -> Client_FTM_IndoorBikeData_Chr: Found!
12:05:00.519 -> Client_FTM_TrainingStatus_Chr: Not Found!
12:05:00.831 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
12:05:00.924 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
12:05:01.285 -> Client_FTM_SupportedPowerRange_Chr: Found!
12:05:01.373 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
12:05:01.637 -> Client_FTM_ControlPoint_Chr: Found!
12:05:02.432 -> Client_FTM_Status_Chr: Found!
12:05:03.135 -> Client_HeartRate_Service: Found!
12:05:03.384 -> Client_HR_Measurement_Chr: Found!
12:05:04.038 -> Client_HR_Location_Chr: Found!
12:05:04.132 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
12:06:01.821 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
12:06:01.821 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
12:06:01.821 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
12:06:02.277 -> MTU updated: 255 for connection ID: 1
12:06:02.542 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
12:06:06.951 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
12:06:08.054 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
12:06:09.257 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
12:06:09.885 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
12:06:10.432 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
12:06:11.276 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
12:08:24.282 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
12:08:24.282 -> Request Control of Machine!
12:08:24.513 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
12:08:24.652 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
12:08:24.652 -> Reset Machine!
12:08:24.715 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 01 01 ]
12:08:24.761 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 01 ]
12:08:24.761 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
12:08:24.761 -> Request Control of Machine!
12:08:24.993 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
12:08:25.071 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 07] [Values: 00 ]
12:08:25.071 -> Start or Resume Machine!
12:08:25.195 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 07 01 ]
12:08:25.242 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 04 ]
12:08:26.114 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 E6 00 28 33 00 ]
12:08:26.160 -> Set Indoor Bike Simulation Parameters!
12:08:26.160 -> Wind speed (1000): 0.00 | Grade (100): 2.30 | Crr (10000): 0.00 | Cw (100): 0.51
12:08:26.283 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:08:26.283 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 E6 00 28 33 ]
12:10:02.871 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 CC 00 28 33 00 ]
12:10:02.871 -> Set Indoor Bike Simulation Parameters!
12:10:02.871 -> Wind speed (1000): 0.00 | Grade (100): 2.04 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:03.026 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:03.026 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 CC 00 28 33 ]
12:10:03.853 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 9A 00 28 33 00 ]
12:10:03.899 -> Set Indoor Bike Simulation Parameters!
12:10:03.899 -> Wind speed (1000): 0.00 | Grade (100): 1.54 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:04.223 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:04.223 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 9A 00 28 33 ]
12:10:05.269 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 90 00 28 33 00 ]
12:10:05.269 -> Set Indoor Bike Simulation Parameters!
12:10:05.269 -> Wind speed (1000): 0.00 | Grade (100): 1.44 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:05.392 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:05.438 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 90 00 28 33 ]
12:10:05.957 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 80 00 28 33 00 ]
12:10:06.001 -> Set Indoor Bike Simulation Parameters!
12:10:06.001 -> Wind speed (1000): 0.00 | Grade (100): 1.28 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:06.140 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:06.140 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 80 00 28 33 ]
12:10:07.012 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 75 00 28 33 00 ]
12:10:07.012 -> Set Indoor Bike Simulation Parameters!
12:10:07.012 -> Wind speed (1000): 0.00 | Grade (100): 1.17 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:07.230 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:07.230 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 75 00 28 33 ]
12:10:07.995 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 6D 00 28 33 00 ]
12:10:08.041 -> Set Indoor Bike Simulation Parameters!
12:10:08.041 -> Wind speed (1000): 0.00 | Grade (100): 1.09 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:08.304 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:08.304 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 6D 00 28 33 ]
12:10:09.162 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 64 00 28 33 00 ]
12:10:09.162 -> Set Indoor Bike Simulation Parameters!
12:10:09.162 -> Wind speed (1000): 0.00 | Grade (100): 1.00 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:09.396 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:09.396 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 64 00 28 33 ]
12:10:10.281 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 5B 00 28 33 00 ]
12:10:10.281 -> Set Indoor Bike Simulation Parameters!
12:10:10.281 -> Wind speed (1000): 0.00 | Grade (100): 0.91 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:10.468 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:10.468 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 5B 00 28 33 ]
12:10:11.264 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 55 00 28 33 00 ]
12:10:11.264 -> Set Indoor Bike Simulation Parameters!
12:10:11.264 -> Wind speed (1000): 0.00 | Grade (100): 0.85 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:11.435 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:11.435 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 55 00 28 33 ]
12:10:12.294 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 50 00 28 33 00 ]
12:10:12.294 -> Set Indoor Bike Simulation Parameters!
12:10:12.294 -> Wind speed (1000): 0.00 | Grade (100): 0.80 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:12.510 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:12.510 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 50 00 28 33 ]
12:10:13.588 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 4B 00 28 33 00 ]
12:10:13.588 -> Set Indoor Bike Simulation Parameters!
12:10:13.588 -> Wind speed (1000): 0.00 | Grade (100): 0.75 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:13.710 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:13.710 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 4B 00 28 33 ]
12:10:14.568 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 49 00 28 33 00 ]
12:10:14.568 -> Set Indoor Bike Simulation Parameters!
12:10:14.568 -> Wind speed (1000): 0.00 | Grade (100): 0.73 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:14.757 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:14.803 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 49 00 28 33 ]
12:10:15.642 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 46 00 28 33 00 ]
12:10:15.642 -> Set Indoor Bike Simulation Parameters!
12:10:15.642 -> Wind speed (1000): 0.00 | Grade (100): 0.70 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:15.722 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:15.722 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 46 00 28 33 ]
12:10:18.167 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 48 00 28 33 00 ]
12:10:18.167 -> Set Indoor Bike Simulation Parameters!
12:10:18.167 -> Wind speed (1000): 0.00 | Grade (100): 0.72 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:18.356 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:18.401 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 48 00 28 33 ]
12:10:19.216 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 4B 00 28 33 00 ]
12:10:19.262 -> Set Indoor Bike Simulation Parameters!
12:10:19.262 -> Wind speed (1000): 0.00 | Grade (100): 0.75 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:19.586 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:19.586 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 4B 00 28 33 ]
12:10:20.394 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 51 00 28 33 00 ]
12:10:20.394 -> Set Indoor Bike Simulation Parameters!
12:10:20.394 -> Wind speed (1000): 0.00 | Grade (100): 0.81 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:20.519 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:20.519 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 51 00 28 33 ]
12:10:21.468 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 58 00 28 33 00 ]
12:10:21.468 -> Set Indoor Bike Simulation Parameters!
12:10:21.468 -> Wind speed (1000): 0.00 | Grade (100): 0.88 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:21.593 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:21.640 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 58 00 28 33 ]
12:10:22.541 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 61 00 28 33 00 ]
12:10:22.541 -> Set Indoor Bike Simulation Parameters!
12:10:22.541 -> Wind speed (1000): 0.00 | Grade (100): 0.97 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:22.712 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:22.712 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 61 00 28 33 ]
12:10:23.505 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 6B 00 28 33 00 ]
12:10:23.505 -> Set Indoor Bike Simulation Parameters!
12:10:23.505 -> Wind speed (1000): 0.00 | Grade (100): 1.07 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:23.677 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:23.677 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 6B 00 28 33 ]
12:10:24.489 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 74 00 28 33 00 ]
12:10:24.535 -> Set Indoor Bike Simulation Parameters!
12:10:24.535 -> Wind speed (1000): 0.00 | Grade (100): 1.16 | Crr (10000): 0.00 | Cw (100): 0.51
12:10:24.752 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 11 01 ]
12:10:24.752 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 74 00 28 33 ]
12:10:25.579 -> --> Raw FTM Control Point Data [len: 7] [OpCode: 11] [Values: 00 00 7A 00 28 33 00 ]
12:10:25.579 -> Set Indoor Bike Simulation Parameters!
12:05:01.373 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]

image

image

@le-joebar
Copy link

Jorgen,

I'm trying to compile the: esp32_FTMS_Simcline_v010

at line 513 I must correct like this #include "Lifter.h"

I will make the electronic connections to test everything actively.

@Berg0162
Copy link
Owner Author

Dear Joel,
I am very glad that the ESP32 code is working so well with your Zwift Hub... I assume that ESP32_Client is doing as well as FTMS_Zwift_Bridge...??

Lifter is a class that should reside in Arduino/libraries/, if that is the case is should be addressed as #include <Lifter.h> . In other cases when it resides in the same directory as the Arduino.ino file is should be addressed as #include "Lifter.h".

Have a nice weekend,
Jörgen.

@le-joebar
Copy link

Yes the ESP32_Client work perfectly !

@le-joebar
Copy link

le-joebar commented Mar 20, 2023

Hello Jorgen,

I can't manage to properly adjust the values ​​for the inclination of the bike.

My bike is 96cm between the 2 wheels.

The axle of the rear wheel is 42 cm from the ground.

For the front wheel to be at the same height, the value of the VL6180X must be at 27 cm, i.e.:

Lifter.cpp
_TargetPosition = 270; // Choose the safe value of a flat road

But I don't think this is taken into account!!!!

The maximum high and Min low stroke

#define MINPOSITION 170 // VL6180X highest value top microswitch activated to mechanically stop operation
#define MAXPOSITION 370 // VL6180X lowest value bottom microswitch activated to mechanically stop operation

Despite the values ​​that I changed in the program after the start of the system and the self-test the wheel stood at 35cm from the ground.

Can you help me to adjust the values?

PS: Do you believe that with the dual core ESP32 it would be possible to add the fan speed ??? lol

Friendship ,
Joel.

@Berg0162
Copy link
Owner Author

Positions

Does this help you?

@Berg0162
Copy link
Owner Author

Dear Joel,
Did you use the test programs in the Arduino directory?
These were added (among other things) to help one solve finding correct Top/Bottom positioning values.....
I have added an updated version derived from the Test_Manual..etcetera and Test_Auto..etcetera.... All in one and taking into account the development board for correct pin allocation. These are the tools you can do it with!
Please notice that the VL6180X is NOT calibrated (!) and NOT measuring in millimeters, at first glance it looks like but it is NOT! However, the Simcline code does take care as long as you enter the correct Min and Max, Top and Bottom values.
Best wishes,
Jörgen.

@Berg0162
Copy link
Owner Author

For the front wheel to be at the same height, the value of the VL6180X must be at 27 cm, i.e.:

Lifter.cpp
_TargetPosition = 270; // Choose the safe value of a flat road

But I don't think this is taken into account!!!!

The private class variables are defined in Lifter Class and set to an initial value

// Private variables for position control
  _IsBrakeOn = true;
  _IsMovingUp = false;
  _IsMovingDown = false;
  _TargetPosition = 400; // Choose the safe value of a flat road

Class methods are defined that allow setting new values, like:

void Lifter::SetTargetPosition(int16_t Tpos)
{
  _TargetPosition = Tpos;
}

It is safer to start with the Brakes on (true) and Moving-Up and -Down being false !!!
The value of 400 is arbitrary (but safer than 0 or 32000) and chosen in case the casual class user is not using the method lift.setTargetPosition(###) before Up/DownControl is activated... 400 would then mean: lift to something close to flat road position... That is a lot safer than lift until you reached the value of zero... If you study carefully the Simcline code you would see that ALWAYS first lift.SetTargetPosition(###) is called before UpDownMovement is called!
So to make a long story short: the initial value of 400 has really NOTHING to do with your specific horizontal wheel position and changing it will have no effect!

The settings in the Simcline code have an one on one relationship with the Simcline construction and how the wheel axle position is measured, between the top of the Aluminium Extruded Profile and the gliding Axle Cylinder mount. The photos, that you have sent me, suggest (but I am not sure) that with your setup you are having the same alignment. In that case, setting values for MINPOSITION and MAXPOSITION should bring you close to a working setup...
Did you check carefully that the VL6180X is measuring correct values over the whole range of axle movement? Alignment of the laser (with respect to its target) is very critical... I did not see a shiny reflection plate (target mirror) on your axle mount, do you have good response when you reach the maximal distance value of 30 cm?
Keep trying!
Jörgen.

@le-joebar
Copy link

Hello Jorgen,

I followed your instructions and tested with "esp32-nRF52_Simcline_Diagnostics_Test.ino"
After some mechanical adjustment everything is perfect !!! ;)

With the ESP 32 dual core don't you think it's possible to add the fans?.

Last thing the "Simcline_v2_4_4.apk" does not work on my Samsung android A71 I have this message :

image

Joel

@Berg0162
Copy link
Owner Author

Berg0162 commented Mar 25, 2023 via email

@le-joebar
Copy link

Hello Jorgen,
I haven't spent the last few days adjusting the wheel height and now it's perfect.
I had done the editing with an ES 32 WROOM that I had in stock and I had a lot of untimely reboots!

Then its this calm and sometimes works without problem!

I thought it was because of the card.
So I ordered the Adafruit ESP32 FEATHER V2 but it's exactly the same.

I'll send you copies of the results.

On the Adafruit I did not put the Oled there the VL but it does not affect the result.

There is also this I don't know if it has to do? with adafruit and Wroom

16:27:00.700 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x1, 0x0}
16:27:00.700 -> E (15) esp_littlefs: mount failed, (-84)
16:27:00.744 -> E (19) esp_littlefs: Failed to initialize LittleFS

ESP32-WROOM

16:27:00.441 -> ets Jun 8 2016 00:22:57
16:27:00.441 ->
16:27:00.441 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:27:00.441 -> configsip: 0, SPIWP:0xee
16:27:00.441 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:27:00.483 -> mode:DIO, clock div:1
16:27:00.483 -> load:0x3fff0030,len:1344
16:27:00.483 -> load:0x40078000,len:13864
16:27:00.483 -> load:0x40080400,len:3608
16:27:00.483 -> entry 0x400805f0
16:27:00.700 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:27:00.700 -> -------------------- Version 01.1 ----------------------
16:27:00.700 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x1, 0x0}
16:27:00.700 -> E (15) esp_littlefs: mount failed, (-84)
16:27:00.744 -> E (19) esp_littlefs: Failed to initialize LittleFS
16:27:00.744 -> SSD1306 OLED display is running...
16:27:10.284 -> otor Control Task Created and Active!
16:27:10.284 -> Simcline Basic Motor Funtions are working!!
16:27:10.550 -> Configuring the default Generic Access Service
16:27:10.550 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:27:10.550 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:27:10.582 -> Configuring the Server Nordic Uart Service
16:27:10.582 -> Configuring the Server Device Information Service
16:27:10.582 -> Configuring the Server Cycle Power Service
16:27:10.582 -> Configuring the Server Cadence and Speed Service
16:27:10.582 -> Configuring the Server Fitness Machine Service
16:27:10.582 -> Configuring the Server Heart Rate Service
16:27:10.582 -> Setting up the Server advertising payload(s)
16:27:10.627 -> Setting Appearance in Advertised data to [1152]
16:27:10.627 -> Server is advertising: CPS, CSC and FTMS
16:27:10.627 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:27:10.706 -> Found advertising Peripheral with FTMS enabled! See data:
16:27:10.706 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:27:10.752 -> Service Data:
16:27:10.752 -> UUID: 0x1826, Data: �
16:27:10.784 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:27:10.784 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:27:10.830 -> Now checking all Client Services and Characteristics!
16:27:10.830 -> If Mandatory Services Fail --> the Client will disconnect!
16:27:11.158 -> Client Generic Access: Found!
16:27:11.611 -> -> Client Reads Device Name: [Zwift Hub]
16:27:12.191 -> -> Client Reads Appearance: [1152]
16:27:12.284 -> Client Device Information Service: Found!
16:27:12.886 -> -> Client Reads Manufacturer Name: [Zwift]
16:27:13.365 -> -> Client Reads Model Number: [06]
16:27:13.902 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:27:14.409 -> Client_CyclingPower_Service: Found!
16:27:14.688 -> Client_CP_Measurement_Chr: Found!
16:27:15.655 -> Client_CP_Feature_Chr: Found!
16:27:15.902 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:27:15.902 -> Wheel revolution data supported
16:27:15.902 -> Crank revolution data supported
16:27:16.303 -> Client_CP_Location_Chr: Found!
16:27:16.442 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:27:16.736 -> Client_CyclingSpeedCadence_Service: Found!
16:27:17.045 -> Client_CSC_Measurement_Chr: Found!
16:27:17.906 -> Client_CSC_Feature_Chr: Found!
16:27:18.047 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:27:18.047 -> Wheel rev supported
16:27:18.047 -> Crank rev supported
16:27:18.356 -> Client_CSC_Location_Chr: Found!
16:27:18.587 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:27:18.847 -> Client_FitnessMachine_Service: Found!
16:27:19.065 -> Client_FTM_Feature_Chr: Found!
16:27:19.235 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:27:19.235 -> - Fitness Machine Features:
16:27:19.235 -> Average Speed Supported
16:27:19.235 -> Cadence Supported
16:27:19.235 -> Total Distance Supported
16:27:19.235 -> Resistance Level Supported
16:27:19.235 -> Heart Rate Measurement Supported
16:27:19.235 -> Power Measurement Supported
16:27:19.281 -> - Target Setting Features:
16:27:19.281 -> Resistance Target Setting Supported
16:27:19.281 -> Power Target Setting Supported
16:27:19.281 -> Indoor Bike Simulation Parameters Supported
16:27:19.281 -> Wheel Circumference Configuration Supported
16:27:19.281 -> Spin Down Control Supported
16:27:19.560 -> Client_FTM_IndoorBikeData_Chr: Found!
16:27:20.908 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:27:21.138 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:27:21.217 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:27:21.600 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:27:21.787 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:27:22.202 -> Client_FTM_ControlPoint_Chr: Found!
16:27:22.892 -> Client_FTM_Status_Chr: Found!
16:27:23.753 -> Client_HeartRate_Service: Found!
16:27:23.985 -> Client_HR_Measurement_Chr: Found!
16:27:24.907 -> Client_HR_Location_Chr: Found!
16:27:25.138 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:32:36.029 -> erver Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:32:36.029 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:32:36.029 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:32:36.587 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:32:37.243 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:32:41.215 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:32:42.355 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:32:43.381 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:32:44.102 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:32:44.631 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:32:45.503 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:33:13.496 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:33:13.496 -> Request Control of Machine!
16:33:14.381 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:33:14.521 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:33:14.521 -> Reset Machine!
16:33:14.646 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:33:14.646 -> Request Control of Machine!
16:33:14.851 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:33:14.851 -> Memory dump at 0x400d2808: 3f6508a8 00f01dc2 81004136
16:33:14.851 -> Core 1 register dump:
16:33:14.851 -> PC : 0x400d280d PS : 0x00060030 A0 : 0x00000000 A1 : 0x3ffd20c0
16:33:14.851 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:33:14.851 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d280d A9 : 0x3ffd20a0
16:33:14.883 -> A10 : 0x3ffd2ad4 A11 : 0x00000001 A12 : 0x0000000b A13 : 0x3ffd215c
16:33:14.883 -> A14 : 0x00000000 A15 : 0x3ffc3fbc SAR : 0x00000009 EXCCAUSE: 0x00000000
16:33:14.883 -> EXCVADDR: 0x00000000 LBEG : 0x4009044c LEND : 0x40090462 LCOUNT : 0x00000000
16:33:14.883 ->
16:33:14.883 ->
16:33:14.883 -> Backtrace:0x400d280a:0x3ffd20c0
16:33:14.915 ->
16:33:14.915 ->
16:33:14.915 ->
16:33:14.915 ->
16:33:14.915 -> ELF file SHA256: 0000000000000000
16:33:14.915 ->
16:33:14.915 -> Rebooting...
16:33:14.915 -> ets Jun 8 2016 00:22:57
16:33:14.915 ->
16:33:14.915 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:33:14.915 -> configsip: 0, SPIWP:0xee
16:33:14.915 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:33:14.915 -> mode:DIO, clock div:1
16:33:14.915 -> load:0x3fff0030,len:1344
16:33:14.915 -> load:0x40078000,len:13864
16:33:14.915 -> load:0x40080400,len:3608
16:33:14.915 -> entry 0x400805f0
16:33:15.164 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:33:15.164 -> -------------------- Version 01.1 ----------------------
16:33:15.164 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x1, 0x0}
16:33:15.164 -> E (15) esp_littlefs: mount failed, (-84)
16:33:15.210 -> E (19) esp_littlefs: Failed to initialize LittleFS
16:33:15.210 -> SSD1306 OLED display is running...
16:33:24.709 -> Motor Control Task Created and Active!
16:33:24.709 -> Simcline Basic Motor Funtions are working!!
16:33:25.019 -> Configuring the default Generic Access Service
16:33:25.019 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:33:25.019 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:33:25.019 -> Configuring the Server Nordic Uart Service
16:33:25.019 -> Configuring the Server Device Information Service
16:33:25.051 -> Configuring the Server Cycle Power Service
16:33:25.051 -> Configuring the Server Cadence and Speed Service
16:33:25.051 -> Configuring the Server Fitness Machine Service
16:33:25.051 -> Configuring the Server Heart Rate Service
16:33:25.051 -> Setting up the Server advertising payload(s)
16:33:25.051 -> Setting Appearance in Advertised data to [1152]
16:33:25.051 -> Server is advertising: CPS, CSC and FTMS
16:33:25.089 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:33:25.126 -> Found advertising Peripheral with FTMS enabled! See data:
16:33:25.126 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:33:25.126 -> Service Data:
16:33:25.126 -> UUID: 0x1826, Data: �
16:33:25.191 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:33:25.191 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:33:25.237 -> Now checking all Client Services and Characteristics!
16:33:25.237 -> If Mandatory Services Fail --> the Client will disconnect!
16:33:25.595 -> Client Generic Access: Found!
16:33:26.067 -> -> Client Reads Device Name: [Zwift Hub]
16:33:26.476 -> -> Client Reads Appearance: [1152]
16:33:26.662 -> Client Device Information Service: Found!
16:33:27.207 -> -> Client Reads Manufacturer Name: [Zwift]
16:33:27.766 -> -> Client Reads Model Number: [06]
16:33:28.126 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:33:28.377 -> Client_CyclingPower_Service: Found!
16:33:28.894 -> Client_CP_Measurement_Chr: Found!
16:33:29.033 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:33:29.033 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:33:29.033 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:33:29.171 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:33:29.843 -> Client_CP_Feature_Chr: Found!
16:33:30.060 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:33:30.060 -> Wheel revolution data supported
16:33:30.060 -> Crank revolution data supported
16:33:30.324 -> Client_CP_Location_Chr: Found!
16:33:30.513 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:33:30.794 -> Client_CyclingSpeedCadence_Service: Found!
16:33:31.107 -> Client_CSC_Measurement_Chr: Found!
16:33:31.964 -> Client_CSC_Feature_Chr: Found!
16:33:32.104 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:33:32.104 -> Wheel rev supported
16:33:32.104 -> Crank rev supported
16:33:32.402 -> Client_CSC_Location_Chr: Found!
16:33:32.528 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:33:33.164 -> Client_FitnessMachine_Service: Found!
16:33:33.413 -> Client_FTM_Feature_Chr: Found!
16:33:33.633 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:33:33.633 -> - Fitness Machine Features:
16:33:33.665 -> Average Speed Supported
16:33:33.665 -> Cadence Supported
16:33:33.665 -> Total Distance Supported
16:33:33.665 -> Resistance Level Supported
16:33:33.665 -> Heart Rate Measurement Supported
16:33:33.665 -> Power Measurement Supported
16:33:33.665 -> - Target Setting Features:
16:33:33.665 -> Resistance Target Setting Supported
16:33:33.665 -> Power Target Setting Supported
16:33:33.665 -> Indoor Bike Simulation Parameters Supported
16:33:33.665 -> Wheel Circumference Configuration Supported
16:33:33.665 -> Spin Down Control Supported
16:33:33.787 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:33:33.959 -> Client_FTM_IndoorBikeData_Chr: Found!
16:33:34.910 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:33:35.563 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:33:35.909 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:33:36.003 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:33:36.130 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:33:36.471 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:33:36.517 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:33:36.686 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:33:36.966 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:33:37.012 -> Client_FTM_ControlPoint_Chr: Found!
16:33:37.888 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:33:38.013 -> Client_FTM_Status_Chr: Found!
16:33:38.903 -> Client_HeartRate_Service: Found!
16:33:39.417 -> Client_HR_Measurement_Chr: Found!
16:33:40.195 -> Client_HR_Location_Chr: Found!
16:33:40.412 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:33:40.833 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:33:40.833 -> Request Control of Machine!
16:33:41.999 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:33:43.060 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:33:45.858 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:33:45.858 -> Request Control of Machine!
16:33:45.984 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:33:46.153 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:33:46.153 -> Reset Machine!
16:33:46.278 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:33:46.278 -> Request Control of Machine!
16:33:46.403 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:33:46.403 -> Memory dump at 0x400d2808: 3f6508a8 00f01dc2 81004136
16:33:46.403 -> Core 1 register dump:
16:33:46.403 -> PC : 0x400d280d PS : 0x00060830 A0 : 0x00000000 A1 : 0x3ffd2060
16:33:46.403 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:33:46.435 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d280d A9 : 0x3ffd2040
16:33:46.435 -> A10 : 0x3ffd2a78 A11 : 0x00000001 A12 : 0x0000000b A13 : 0x3ffd2100
16:33:46.435 -> A14 : 0x00000000 A15 : 0x3ffc3fbc SAR : 0x00000009 EXCCAUSE: 0x00000000
16:33:46.435 -> EXCVADDR: 0x00000000 LBEG : 0x4009044c LEND : 0x40090462 LCOUNT : 0x00000000
16:33:46.467 ->
16:33:46.467 ->
16:33:46.467 -> Backtrace:0x400d280a:0x3ffd2060
16:33:46.467 ->
16:33:46.467 ->
16:33:46.467 ->
16:33:46.467 ->
16:33:46.467 -> ELF file SHA256: 0000000000000000
16:33:46.467 ->
16:33:46.467 -> Rebooting...
16:33:46.467 -> ets Jun 8 2016 00:22:57
16:33:46.467 ->
16:33:46.467 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:33:46.467 -> configsip: 0, SPIWP:0xee
16:33:46.467 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:33:46.467 -> mode:DIO, clock div:1
16:33:46.467 -> load:0x3fff0030,len:1344
16:33:46.467 -> load:0x40078000,len:13864
16:33:46.509 -> load:0x40080400,len:3608
16:33:46.509 -> entry 0x400805f0
16:33:46.729 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:33:46.729 -> -------------------- Version 01.1 ----------------------
16:33:46.729 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x1, 0x0}
16:33:46.729 -> E (15) esp_littlefs: mount failed, (-84)
16:33:46.729 -> E (19) esp_littlefs: Failed to initialize LittleFS
16:33:46.729 -> SSD1306 OLED display is running...
16:33:56.266 -> Motor Control Task Created and Active!
16:33:56.312 -> Simcline Basic Motor Funtions are working!!
16:33:56.577 -> Configuring the default Generic Access Service
16:33:56.577 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:33:56.609 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:33:56.609 -> Configuring the Server Nordic Uart Service
16:33:56.609 -> Configuring the Server Device Information Service
16:33:56.609 -> Configuring the Server Cycle Power Service
16:33:56.609 -> Configuring the Server Cadence and Speed Service
16:33:56.609 -> Configuring the Server Fitness Machine Service
16:33:56.609 -> Configuring the Server Heart Rate Service
16:33:56.642 -> Setting up the Server advertising payload(s)
16:33:56.642 -> Setting Appearance in Advertised data to [1152]
16:33:56.642 -> Server is advertising: CPS, CSC and FTMS
16:33:56.642 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:33:56.684 -> Found advertising Peripheral with FTMS enabled! See data:
16:33:56.684 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:33:56.684 -> Service Data:
16:33:56.684 -> UUID: 0x1826, Data: �
16:33:56.761 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:33:56.761 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:33:56.808 -> Now checking all Client Services and Characteristics!
16:33:56.808 -> If Mandatory Services Fail --> the Client will disconnect!
16:33:57.231 -> Client Generic Access: Found!
16:33:57.885 -> -> Client Reads Device Name: [Zwift Hub]
16:33:58.275 -> -> Client Reads Appearance: [1152]
16:33:58.400 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:33:58.400 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:33:58.400 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:33:58.400 -> Client Device Information Service: Found!
16:33:58.446 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:33:58.834 -> -> Client Reads Manufacturer Name: [Zwift]
16:33:59.285 -> -> Client Reads Model Number: [06]
16:33:59.831 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:34:00.141 -> Client_CyclingPower_Service: Found!
16:34:00.499 -> Client_CP_Measurement_Chr: Found!
16:34:01.293 -> Client_CP_Feature_Chr: Found!
16:34:01.386 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:34:01.386 -> Wheel revolution data supported
16:34:01.386 -> Crank revolution data supported
16:34:01.667 -> Client_CP_Location_Chr: Found!
16:34:01.839 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:34:02.073 -> Client_CyclingSpeedCadence_Service: Found!
16:34:02.338 -> Client_CSC_Measurement_Chr: Found!
16:34:02.902 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:34:03.134 -> Client_CSC_Feature_Chr: Found!
16:34:03.353 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:34:03.353 -> Wheel rev supported
16:34:03.399 -> Crank rev supported
16:34:03.648 -> Client_CSC_Location_Chr: Found!
16:34:03.788 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:34:04.021 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:34:04.099 -> Client_FitnessMachine_Service: Found!
16:34:04.380 -> Client_FTM_Feature_Chr: Found!
16:34:04.503 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:34:04.534 -> - Fitness Machine Features:
16:34:04.534 -> Average Speed Supported
16:34:04.534 -> Cadence Supported
16:34:04.534 -> Total Distance Supported
16:34:04.534 -> Resistance Level Supported
16:34:04.534 -> Heart Rate Measurement Supported
16:34:04.534 -> Power Measurement Supported
16:34:04.534 -> - Target Setting Features:
16:34:04.534 -> Resistance Target Setting Supported
16:34:04.534 -> Power Target Setting Supported
16:34:04.534 -> Indoor Bike Simulation Parameters Supported
16:34:04.534 -> Wheel Circumference Configuration Supported
16:34:04.579 -> Spin Down Control Supported
16:34:04.843 -> Client_FTM_IndoorBikeData_Chr: Found!
16:34:04.969 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:34:05.576 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:34:06.079 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:34:06.122 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:34:06.482 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:34:06.621 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:34:06.839 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:34:06.839 -> Request Control of Machine!
16:34:06.934 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:34:07.122 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:34:07.170 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:34:07.529 -> Client_FTM_ControlPoint_Chr: Found!
16:34:08.277 -> Client_FTM_Status_Chr: Found!
16:34:09.022 -> Client_HeartRate_Service: Found!
16:34:09.492 -> Client_HR_Measurement_Chr: Found!
16:34:10.130 -> Client_HR_Location_Chr: Found!
16:34:10.256 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:34:11.221 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:34:11.689 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:34:11.689 -> Request Control of Machine!
16:34:11.782 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:34:11.876 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:34:11.876 -> Reset Machine!
16:34:11.955 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 01 01 ]
16:34:12.001 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:34:12.001 -> Request Control of Machine!
16:34:12.032 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:34:12.032 -> Memory dump at 0x400d2808: 3f6508a8 00f01dc2 81004136
16:34:12.032 -> Core 1 register dump:
16:34:12.032 -> PC : 0x400d280d PS : 0x00060b30 A0 : 0x00000000 A1 : 0x3ffd2060
16:34:12.032 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:34:12.065 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d280d A9 : 0x3ffd2040
16:34:12.065 -> A10 : 0x3ffd2a78 A11 : 0x00000001 A12 : 0x0000000b A13 : 0x3ffd2100
16:34:12.065 -> A14 : 0x00000000 A15 : 0x3ffc3fbc SAR : 0x00000009 EXCCAUSE: 0x00000000
16:34:12.065 -> EXCVADDR: 0x00000000 LBEG : 0x4009044c LEND : 0x40090462 LCOUNT : 0x00000000
16:34:12.065 ->
16:34:12.065 ->
16:34:12.065 -> Backtrace:0x400d280a:0x3ffd2060
16:34:12.096 ->
16:34:12.096 ->
16:34:12.096 ->
16:34:12.096 ->
16:34:12.096 -> ELF file SHA256: 0000000000000000
16:34:12.096 ->
16:34:12.096 -> Rebooting...
16:34:12.096 -> ets Jun 8 2016 00:22:57
16:34:12.096 ->
16:34:12.096 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:34:12.096 -> configsip: 0, SPIWP:0xee
16:34:12.096 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:34:12.096 -> mode:DIO, clock div:1
16:34:12.096 -> load:0x3fff0030,len:1344
16:34:12.096 -> load:0x40078000,len:13864
16:34:12.096 -> load:0x40080400,len:3608
16:34:12.141 -> entry 0x400805f0
16:34:12.359 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:34:12.359 -> -------------------- Version 01.1 ----------------------
16:34:12.359 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x1, 0x0}
16:34:12.359 -> E (15) esp_littlefs: mount failed, (-84)
16:34:12.359 -> E (19) esp_littlefs: Failed to initialize LittleFS
16:34:12.359 -> SSD1306 OLED display is running...
16:34:22.350 -> otor Control Task Created and Active!
16:34:22.395 -> Simcline Basic Motor Funtions are working!!
16:34:22.658 -> Configuring the default Generic Access Service
16:34:22.658 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:34:22.690 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:34:22.690 -> Configuring the Server Nordic Uart Service
16:34:22.690 -> Configuring the Server Device Information Service
16:34:22.690 -> Configuring the Server Cycle Power Service
16:34:22.690 -> Configuring the Server Cadence and Speed Service
16:34:22.690 -> Configuring the Server Fitness Machine Service
16:34:22.690 -> Configuring the Server Heart Rate Service
16:34:22.724 -> Setting up the Server advertising payload(s)
16:34:22.724 -> Setting Appearance in Advertised data to [1152]
16:34:22.724 -> Server is advertising: CPS, CSC and FTMS
16:34:22.724 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:34:22.724 -> Found advertising Peripheral with FTMS enabled! See data:
16:34:22.765 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:34:22.765 -> Service Data:
16:34:22.765 -> UUID: 0x1826, Data: �
16:34:22.843 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:34:22.843 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:34:22.889 -> Now checking all Client Services and Characteristics!
16:34:22.889 -> If Mandatory Services Fail --> the Client will disconnect!

ADAFRUIT ESP32 Feather V2

16:39:39.343 -> ets Jul 29 2019 12:21:46
16:39:39.343 ->
16:39:39.343 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:39:39.385 -> configsip: 271414342, SPIWP:0xee
16:39:39.385 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:39:39.385 -> mode:DIO, clock div:1
16:39:39.385 -> load:0x3fff0030,len:1184
16:39:39.385 -> load:0x40078000,len:13160
16:39:39.385 -> load:0x40080400,len:3036
16:39:39.385 -> entry 0x400805e4
16:39:39.882 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:39:39.882 -> -------------------- Version 01.1 ----------------------
16:39:39.882 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
16:39:39.882 -> E (253) esp_littlefs: mount failed, (-84)
16:39:39.882 -> E (257) esp_littlefs: Failed to initialize LittleFS
16:39:39.925 -> SSD1306 OLED display is running...
16:40:10.516 -> Simcline >> ERROR << Basic Motor Funtions are NOT working!!
16:40:10.820 -> Configuring the default Generic Access Service
16:40:10.820 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:40:10.820 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:40:10.820 -> Configuring the Server Nordic Uart Service
16:40:10.820 -> Configuring the Server Device Information Service
16:40:10.820 -> Configuring the Server Cycle Power Service
16:40:10.856 -> Configuring the Server Cadence and Speed Service
16:40:10.856 -> Configuring the Server Fitness Machine Service
16:40:10.856 -> Configuring the Server Heart Rate Service
16:40:10.856 -> Setting up the Server advertising payload(s)
16:40:10.856 -> Setting Appearance in Advertised data to [1152]
16:40:10.856 -> Server is advertising: CPS, CSC and FTMS
16:40:10.856 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:40:11.164 -> Found advertising Peripheral with FTMS enabled! See data:
16:40:11.164 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:40:11.164 -> Service Data:
16:40:11.164 -> UUID: 0x1826, Data: �
16:40:11.211 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:40:11.211 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:40:11.211 -> Now checking all Client Services and Characteristics!
16:40:11.211 -> If Mandatory Services Fail --> the Client will disconnect!
16:40:11.658 -> Client Generic Access: Found!
16:40:12.185 -> -> Client Reads Device Name: [Zwift Hub]
16:40:12.694 -> -> Client Reads Appearance: [1152]
16:40:12.868 -> Client Device Information Service: Found!
16:40:13.358 -> -> Client Reads Manufacturer Name: [Zwift]
16:40:13.849 -> -> Client Reads Model Number: [06]
16:40:14.388 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:40:14.711 -> Client_CyclingPower_Service: Found!
16:40:14.896 -> Client_CP_Measurement_Chr: Found!
16:40:15.637 -> Client_CP_Feature_Chr: Found!
16:40:15.807 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:40:15.807 -> Wheel revolution data supported
16:40:15.808 -> Crank revolution data supported
16:40:16.040 -> Client_CP_Location_Chr: Found!
16:40:16.163 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:40:16.502 -> Client_CyclingSpeedCadence_Service: Found!
16:40:16.949 -> Client_CSC_Measurement_Chr: Found!
16:40:17.641 -> Client_CSC_Feature_Chr: Found!
16:40:17.766 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:40:17.811 -> Wheel rev supported
16:40:17.812 -> Crank rev supported
16:40:18.091 -> Client_CSC_Location_Chr: Found!
16:40:18.261 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:40:18.444 -> Client_FitnessMachine_Service: Found!
16:40:18.709 -> Client_FTM_Feature_Chr: Found!
16:40:18.837 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:40:18.837 -> - Fitness Machine Features:
16:40:18.837 -> Average Speed Supported
16:40:18.837 -> Cadence Supported
16:40:18.837 -> Total Distance Supported
16:40:18.837 -> Resistance Level Supported
16:40:18.880 -> Heart Rate Measurement Supported
16:40:18.880 -> Power Measurement Supported
16:40:18.880 -> - Target Setting Features:
16:40:18.880 -> Resistance Target Setting Supported
16:40:18.880 -> Power Target Setting Supported
16:40:18.880 -> Indoor Bike Simulation Parameters Supported
16:40:18.880 -> Wheel Circumference Configuration Supported
16:40:18.880 -> Spin Down Control Supported
16:40:19.142 -> Client_FTM_IndoorBikeData_Chr: Found!
16:40:20.050 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:40:20.405 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:40:20.498 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:40:20.808 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:40:20.900 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:40:21.118 -> Client_FTM_ControlPoint_Chr: Found!
16:40:21.991 -> Client_FTM_Status_Chr: Found!
16:40:22.593 -> Client_HeartRate_Service: Found!
16:40:22.963 -> Client_HR_Measurement_Chr: Found!
16:40:23.549 -> Client_HR_Location_Chr: Found!
16:40:23.735 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:41:28.693 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:41:28.693 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:41:28.693 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:41:29.209 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:41:29.958 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:41:33.806 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:41:35.004 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:41:35.959 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:41:36.644 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:41:37.086 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:41:37.959 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:42:04.405 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:04.405 -> Request Control of Machine!
16:42:04.965 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:42:05.090 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:42:05.090 -> Reset Machine!
16:42:05.169 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:05.213 -> Request Control of Machine!
16:42:05.329 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:42:05.329 -> Memory dump at 0x400d2944: 7fa508a8 00f01dc3 81004136
16:42:05.329 -> Core 1 register dump:
16:42:05.329 -> PC : 0x400d2949 PS : 0x00060430 A0 : 0x00000000 A1 : 0x3ffd1010
16:42:05.329 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:42:05.362 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d2949 A9 : 0x3ffd0ff0
16:42:05.362 -> A10 : 0x3ffd1a24 A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd10ac
16:42:05.362 -> A14 : 0x00000000 A15 : 0x3ffc3ffc SAR : 0x00000005 EXCCAUSE: 0x00000000
16:42:05.362 -> EXCVADDR: 0x00000000 LBEG : 0x400917e4 LEND : 0x40091800 LCOUNT : 0x00000000
16:42:05.399 ->
16:42:05.399 ->
16:42:05.399 -> Backtrace:0x400d2946:0x3ffd1010
16:42:05.399 ->
16:42:05.399 ->
16:42:05.399 ->
16:42:05.399 ->
16:42:05.399 -> ELF file SHA256: 0000000000000000
16:42:05.399 ->
16:42:05.399 -> Rebooting...
16:42:05.399 -> ets Jul 29 2019 12:21:46
16:42:05.399 ->
16:42:05.399 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:42:05.399 -> configsip: 271414342, SPIWP:0xee
16:42:05.399 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:42:05.399 -> mode:DIO, clock div:1
16:42:05.399 -> load:0x3fff0030,len:1184
16:42:05.399 -> load:0x40078000,len:13160
16:42:05.399 -> load:0x40080400,len:3036
16:42:05.399 -> entry 0x400805e4
16:42:05.904 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:42:05.904 -> -------------------- Version 01.1 ----------------------
16:42:05.904 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
16:42:05.946 -> E (253) esp_littlefs: mount failed, (-84)
16:42:05.946 -> E (257) esp_littlefs: Failed to initialize LittleFS
16:42:05.946 -> SSD1306 OLED display is running...
16:42:36.583 -> Simcline >> ERROR << Basic Motor Funtions are NOT working!!
16:42:36.845 -> Configuring the default Generic Access Service
16:42:36.845 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:42:36.845 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:42:36.845 -> Configuring the Server Nordic Uart Service
16:42:36.845 -> Configuring the Server Device Information Service
16:42:36.845 -> Configuring the Server Cycle Power Service
16:42:36.891 -> Configuring the Server Cadence and Speed Service
16:42:36.891 -> Configuring the Server Fitness Machine Service
16:42:36.891 -> Configuring the Server Heart Rate Service
16:42:36.891 -> Setting up the Server advertising payload(s)
16:42:36.891 -> Setting Appearance in Advertised data to [1152]
16:42:36.891 -> Server is advertising: CPS, CSC and FTMS
16:42:36.891 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:42:36.937 -> Found advertising Peripheral with FTMS enabled! See data:
16:42:36.937 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:42:36.937 -> Service Data:
16:42:36.937 -> UUID: 0x1826, Data: �
16:42:37.019 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:42:37.019 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:42:37.019 -> Now checking all Client Services and Characteristics!
16:42:37.061 -> If Mandatory Services Fail --> the Client will disconnect!
16:42:37.421 -> Client Generic Access: Found!
16:42:37.926 -> -> Client Reads Device Name: [Zwift Hub]
16:42:38.460 -> -> Client Reads Appearance: [1152]
16:42:38.584 -> Client Device Information Service: Found!
16:42:38.662 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:42:38.662 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:42:38.662 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:42:38.662 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:42:39.019 -> -> Client Reads Manufacturer Name: [Zwift]
16:42:39.428 -> -> Client Reads Model Number: [06]
16:42:39.989 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:42:40.535 -> Client_CyclingPower_Service: Found!
16:42:40.800 -> Client_CP_Measurement_Chr: Found!
16:42:41.733 -> Client_CP_Feature_Chr: Found!
16:42:41.810 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:42:41.810 -> Wheel revolution data supported
16:42:41.810 -> Crank revolution data supported
16:42:42.107 -> Client_CP_Location_Chr: Found!
16:42:42.263 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:42:42.635 -> Client_CyclingSpeedCadence_Service: Found!
16:42:42.918 -> Client_CSC_Measurement_Chr: Found!
16:42:43.247 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:42:44.120 -> Client_CSC_Feature_Chr: Found!
16:42:44.213 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:42:44.213 -> Wheel rev supported
16:42:44.213 -> Crank rev supported
16:42:44.339 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:42:44.683 -> Client_CSC_Location_Chr: Found!
16:42:44.871 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:42:45.280 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:42:45.373 -> Client_FitnessMachine_Service: Found!
16:42:45.683 -> Client_FTM_Feature_Chr: Found!
16:42:45.906 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:42:45.906 -> - Fitness Machine Features:
16:42:45.906 -> Average Speed Supported
16:42:45.906 -> Cadence Supported
16:42:45.948 -> Total Distance Supported
16:42:45.948 -> Resistance Level Supported
16:42:45.948 -> Heart Rate Measurement Supported
16:42:45.948 -> Power Measurement Supported
16:42:45.948 -> - Target Setting Features:
16:42:45.948 -> Resistance Target Setting Supported
16:42:45.948 -> Power Target Setting Supported
16:42:45.948 -> Indoor Bike Simulation Parameters Supported
16:42:45.948 -> Wheel Circumference Configuration Supported
16:42:45.948 -> Spin Down Control Supported
16:42:45.948 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:42:46.294 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:46.294 -> Request Control of Machine!
16:42:46.294 -> Client_FTM_IndoorBikeData_Chr: Found!
16:42:46.556 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:42:47.384 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:42:47.664 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:42:48.085 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:42:48.179 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:42:48.630 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:42:48.723 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:42:49.146 -> Client_FTM_ControlPoint_Chr: Found!
16:42:49.868 -> Client_FTM_Status_Chr: Found!
16:42:50.884 -> Client_HeartRate_Service: Found!
16:42:51.165 -> Client_HR_Measurement_Chr: Found!
16:42:51.338 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:51.338 -> Request Control of Machine!
16:42:52.228 -> Client_HR_Location_Chr: Found!
16:42:52.307 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:42:53.464 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:42:54.732 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:42:56.378 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:56.378 -> Request Control of Machine!
16:42:56.534 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:42:56.675 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:42:56.675 -> Reset Machine!
16:42:56.802 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:42:56.802 -> Request Control of Machine!
16:42:56.802 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:42:56.835 -> Memory dump at 0x400d2944: 7fa508a8 00f01dc3 81004136
16:42:56.835 -> Core 1 register dump:
16:42:56.835 -> PC : 0x400d2949 PS : 0x00060230 A0 : 0x00000000 A1 : 0x3ffd0db0
16:42:56.835 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:42:56.835 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d2949 A9 : 0x3ffd0d90
16:42:56.835 -> A10 : 0x3ffd17c4 A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd0e4c
16:42:56.867 -> A14 : 0x00000000 A15 : 0x3ffc3ffc SAR : 0x00000004 EXCCAUSE: 0x00000000
16:42:56.867 -> EXCVADDR: 0x00000000 LBEG : 0x400917e4 LEND : 0x40091800 LCOUNT : 0x00000000
16:42:56.867 ->
16:42:56.867 ->
16:42:56.867 -> Backtrace:0x400d2946:0x3ffd0db0
16:42:56.867 ->
16:42:56.867 ->
16:42:56.867 ->
16:42:56.867 ->
16:42:56.867 -> ELF file SHA256: 0000000000000000
16:42:56.867 ->
16:42:56.867 -> Rebooting...
16:42:56.906 -> ets Jul 29 2019 12:21:46
16:42:56.906 ->
16:42:56.906 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:42:56.906 -> configsip: 271414342, SPIWP:0xee
16:42:56.906 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:42:56.906 -> mode:DIO, clock div:1
16:42:56.906 -> load:0x3fff0030,len:1184
16:42:56.906 -> load:0x40078000,len:13160
16:42:56.906 -> load:0x40080400,len:3036
16:42:56.906 -> entry 0x400805e4
16:42:57.425 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:42:57.425 -> -------------------- Version 01.1 ----------------------
16:42:57.425 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
16:42:57.425 -> E (253) esp_littlefs: mount failed, (-84)
16:42:57.425 -> E (257) esp_littlefs: Failed to initialize LittleFS
16:42:57.425 -> SSD1306 OLED display is running...
16:43:28.075 -> Simcline >> ERROR << Basic Motor Funtions are NOT working!!
16:43:28.329 -> Configuring the default Generic Access Service
16:43:28.329 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:43:28.329 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:43:28.329 -> Configuring the Server Nordic Uart Service
16:43:28.329 -> Configuring the Server Device Information Service
16:43:28.362 -> Configuring the Server Cycle Power Service
16:43:28.362 -> Configuring the Server Cadence and Speed Service
16:43:28.362 -> Configuring the Server Fitness Machine Service
16:43:28.362 -> Configuring the Server Heart Rate Service
16:43:28.362 -> Setting up the Server advertising payload(s)
16:43:28.362 -> Setting Appearance in Advertised data to [1152]
16:43:28.362 -> Server is advertising: CPS, CSC and FTMS
16:43:28.362 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:43:28.394 -> Found advertising Peripheral with FTMS enabled! See data:
16:43:28.394 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:43:28.394 -> Service Data:
16:43:28.394 -> UUID: 0x1826, Data: �
16:43:28.464 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:43:28.464 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:43:28.464 -> Now checking all Client Services and Characteristics!
16:43:28.464 -> If Mandatory Services Fail --> the Client will disconnect!
16:43:29.010 -> Client Generic Access: Found!
16:43:29.602 -> -> Client Reads Device Name: [Zwift Hub]
16:43:29.634 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:43:29.634 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:43:29.680 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:43:29.680 -> Central (MyLaptop/Zwift) updated MTU to: [255] for connection ID: 1
16:43:30.168 -> -> Client Reads Appearance: [1152]
16:43:30.262 -> Client Device Information Service: Found!
16:43:30.761 -> -> Client Reads Manufacturer Name: [Zwift]
16:43:31.448 -> -> Client Reads Model Number: [06]
16:43:32.055 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:43:32.351 -> Client_CyclingPower_Service: Found!
16:43:32.760 -> Client_CP_Measurement_Chr: Found!
16:43:33.599 -> Client_CP_Feature_Chr: Found!
16:43:33.790 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:43:33.790 -> Wheel revolution data supported
16:43:33.835 -> Crank revolution data supported
16:43:34.164 -> Client_CP_Location_Chr: Found!
16:43:34.304 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:43:34.350 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:43:34.722 -> Client_CyclingSpeedCadence_Service: Found!
16:43:35.114 -> Client_CSC_Measurement_Chr: Found!
16:43:35.519 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:43:36.161 -> Client_CSC_Feature_Chr: Found!
16:43:36.380 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:43:36.380 -> Wheel rev supported
16:43:36.380 -> Crank rev supported
16:43:36.536 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:43:36.773 -> Client_CSC_Location_Chr: Found!
16:43:36.866 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:43:37.148 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:43:37.272 -> Client_FitnessMachine_Service: Found!
16:43:37.639 -> Client_FTM_Feature_Chr: Found!
16:43:37.680 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:43:37.680 -> Request Control of Machine!
16:43:37.796 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:43:37.796 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:43:37.796 -> - Fitness Machine Features:
16:43:37.796 -> Average Speed Supported
16:43:37.796 -> Cadence Supported
16:43:37.836 -> Total Distance Supported
16:43:37.836 -> Resistance Level Supported
16:43:37.836 -> Heart Rate Measurement Supported
16:43:37.836 -> Power Measurement Supported
16:43:37.836 -> - Target Setting Features:
16:43:37.836 -> Resistance Target Setting Supported
16:43:37.836 -> Power Target Setting Supported
16:43:37.836 -> Indoor Bike Simulation Parameters Supported
16:43:37.836 -> Wheel Circumference Configuration Supported
16:43:37.836 -> Spin Down Control Supported
16:43:38.273 -> Client_FTM_IndoorBikeData_Chr: Found!
16:43:38.694 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:43:39.847 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:43:40.223 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:43:40.362 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:43:40.816 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:43:40.989 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:43:41.414 -> Client_FTM_ControlPoint_Chr: Found!
16:43:42.271 -> Client_FTM_Status_Chr: Found!
16:43:42.696 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:43:42.696 -> Request Control of Machine!
16:43:43.213 -> Client_HeartRate_Service: Found!
16:43:43.523 -> Client_HR_Measurement_Chr: Found!
16:43:44.872 -> Client_HR_Location_Chr: Found!
16:43:44.998 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:43:46.153 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:43:47.415 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:43:47.744 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:43:47.744 -> Request Control of Machine!
16:43:47.916 -> -> Client Rec'd Raw FTM Control Point Response Data: [0] [3] [ 80 00 01 ]
16:43:47.994 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 01] [Values: 00 ]
16:43:47.994 -> Reset Machine!
16:43:48.118 -> --> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:43:48.118 -> Request Control of Machine!
16:43:48.151 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:43:48.151 -> Memory dump at 0x400d2944: 7fa508a8 00f01dc3 81004136
16:43:48.151 -> Core 1 register dump:
16:43:48.151 -> PC : 0x400d2949 PS : 0x00060030 A0 : 0x00000000 A1 : 0x3ffd0dc0
16:43:48.185 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:43:48.185 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d2949 A9 : 0x3ffd0da0
16:43:48.185 -> A10 : 0x3ffd17cc A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd0e54
16:43:48.185 -> A14 : 0x00000000 A15 : 0x3ffc3ffc SAR : 0x00000004 EXCCAUSE: 0x00000000
16:43:48.222 -> EXCVADDR: 0x00000000 LBEG : 0x400917e4 LEND : 0x40091800 LCOUNT : 0x00000000
16:43:48.222 ->
16:43:48.222 ->
16:43:48.222 -> Backtrace:0x400d2946:0x3ffd0dc0
16:43:48.222 ->
16:43:48.222 ->
16:43:48.222 ->
16:43:48.222 ->
16:43:48.222 -> ELF file SHA256: 0000000000000000
16:43:48.222 ->
16:43:48.222 -> Rebooting...
16:43:48.222 -> ets Jul 29 2019 12:21:46
16:43:48.222 ->
16:43:48.222 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:43:48.222 -> configsip: 271414342, SPIWP:0xee
16:43:48.222 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:43:48.222 -> mode:DIO, clock div:1
16:43:48.258 -> load:0x3fff0030,len:1184
16:43:48.258 -> load:0x40078000,len:13160
16:43:48.258 -> load:0x40080400,len:3036
16:43:48.258 -> entry 0x400805e4
16:43:48.759 -> ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS
16:43:48.759 -> -------------------- Version 01.1 ----------------------
16:43:48.759 -> ./components/esp_littlefs/src/littlefs/lfs.c:1229:error: Corrupted dir pair at {0x0, 0x1}
16:43:48.759 -> E (253) esp_littlefs: mount failed, (-84)
16:43:48.759 -> E (257) esp_littlefs: Failed to initialize LittleFS
16:43:48.759 -> SSD1306 OLED display is running...
16:44:19.401 -> Simcline >> ERROR << Basic Motor Funtions are NOT working!!
16:44:19.666 -> Configuring the default Generic Access Service
16:44:19.666 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:44:19.666 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:44:19.701 -> Configuring the Server Nordic Uart Service

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 5, 2023

Dear Joel,
Let's handle the error message of LittleFS (Little File System) first! Have a close look at this part in the code:

  DEBUG_PRINTLN("ESP32 NimBLE SIMCLINE supporting: CPS, CSC, HBM and FTMS");
  DEBUG_PRINTLN("-------------------- Version 01.1 ----------------------");
  // LittleFS start the Littlefilesystem lib and see if we have persistent data ----
  // This opens LittleFS with a root subdirectory /littlefs/
  LittleFS.begin();
  // WARNING --------- Uncomment only when strictly necessary!!! ---------
  // Uncomment only the very first time the Simcline code is executed!!!
  /* This creates LittleFS with a root subdirectory /littlefs/
  LittleFS.format();
  DEBUG_PRINTLN("Wipe out all persistent data, including any file(s)....");
  */
  // Get or set (first time only) the values of relevant and crucial variables
  // to persistence, with the Companion App the user can set these on the fly!
  // Get or set the values of aRGVmax, aRGVmin, GradeChangeFactor in PRSdata.
  if (!getPRSdata()) {
    setPRSdata();
  }
  // LittleFS------------------------------------------------------------------------

The LittleFS error messages make it quite clear that you did NOT allow LittleFS (the first time you run the code) to create (LittleFS.format) a virtual FS-partition on the ESP32 development board that you run the Simcline code on...
(when you change boards you have to repeat this!) Every time, after startup, the Simcline code tries to open the FS-partition, read a file (getPRSdata) with persistent variable values.... and it will fail consequently! ---> You can solve this yourself!

By the way is the Companion App working after you have allowed Bluetooth to access your location?
Notice that you can test the working of Phone-ESP32 with esp32_FTMS_Server_v010 by connecting your phone when the server is running! (NO FS-PARTION IS CREATED RUNNING THE SERVER CODE!)
Best wishes,
Jörgen.

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 5, 2023

Welcome to the world of ESP32:

Guru Meditation Error: Core 1 panic'ed (Illegal Instruction). Exception was unhandled.

This message covers a plethora of potential causes.....

Then its this calm and sometimes works without problem!

Although you have sent 5 times the same (finally) fatal output, as I understand it: --> it will run sometimes without any problem!
Question: Correct?

This points to a critical timing problem, one of the processes on Core #1 is timed out and I guess it has to do with handling of server_FTM_ControlPoint_Chr , looking at the debug output, the callback handling is active most probably when the error occurs.
Question: You never had this error when running esp32_FTMS_Zwift_Bridge_v010 with DEBUG activated?

You have DEBUG activated with the following parts defined:

#ifdef DEBUG
//  Restrict activating one or more of the following DEBUG directives --> process intensive 
//  The overhead can lead to spurious side effects and a loss of quality of service handling!!
//#define DEBUG_HBM               // If defined allows for parsing and decoding the Heart Beat Data
//#define DEBUG_CP_MEASUREMENT    // If defined allows for parsing and decoding the Cycling Power Data
//#define DEBUG_CSC_MEASUREMENT   // If defined allows for parsing and decoding the Cycling Speed and Cadence Data
//#define DEBUG_FTM_INDOORBIKEDATA// If defined allows for parsing the Indoor Bike Data
#ifdef DEBUG_FTM_INDOORBIKEDATA
//#define DEBUG_DECODE_IBD        // If defined allows for decoding the Indoor Bike Data
#endif
//#define DEBUG_FTM_TRAININGSTATUS// If defined allows for parsing the Training Status Data
//#define DEBUG_FTM_STATUS        // If defined allows for parsing the Machine Status Data
#define DEBUG_FTM_CONTROLPOINT_RESPONSE     // If defined allows for parsing the Data
#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA  // If defined allows for parsing and decoding Data
#endif

With my experimental setup that is never causing problems but I have Arduino IDE Serial Monitor running at 115200 baud... So printing to serial monitor is fast....so less prone to timing errors!
Question: What is your baud rate setting of Arduino IDE Serial Monitor?

My suggestion is to try the following steps first:

  1. Comment out
//#define DEBUG_FTM_CONTROLPOINT_RESPONSE     // If defined allows for parsing the Data
//#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA  // If defined allows for parsing and decoding Data
  1. Switch Arduino IDE: Tools -> Core Debug Level: "Error"
  2. Compile, Upload and Run again, with Serial Monitor active for output

@le-joebar
Copy link

le-joebar commented Apr 6, 2023

Hello Jorgen,

For the connection with Companion I will deal with it later.
LittleFS is fixed.

Here are the results with esp32_FTMS_Zwift_Bridge_v010

change as follows:

//#define DEBUG_FTM_CONTROLPOINT_RESPONSE // If defined allows for parsing the Data
//#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA // If defined allows for parsing and decoding Data

Core Debug Level: "Error"

Baud Rate 115200

I also put the Zwift images because it is each time from the same page that the reboot begins.

The restart happens sometimes 1 or 2 times but as you can see in the 2nd attempt the restart lasted 4 minutes !!

As soon as we arrive at this:

16:07:52.948 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 01 ]
16:07:53.040 -> Request Control of Machine!
16:07:53.396 -> Start or Resume Machine!
16:07:53.568 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 04 ]
16:07:54.468 -> Set Indoor Bike Simulation Parameters!
16:07:54.468 -> Wind speed (1000): 0.00 | Rank (100): -0.58 | Crr(10000): 0.00 | Cw(100): 0.51
16:07:54.718 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 C6 FF 28 33 ]

So it's stable

Be careful because when there is this:

16:07:53.568 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 04 ]
16:07:54.468 -> Set Indoor Bike Simulation Parameters!
16:07:54.468 -> Wind speed (1000): 0.00 | Rank (100): -0.58 | Crr(10000): 0.00 | Cw(100): 0.51
16:07:54.718 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 C6 FF 28 33 ]

I'm still on the page seen above and not busy riding!!

Sincerely,

Joel

Now the result :

image

15:35:18.498 -> ets Jul 29 2019 12:21:46
15:35:18.498 ->
15:35:18.498 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
15:35:18.498 -> configsip: 271414342, SPIWP:0xee
15:35:18.498 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
15:35:18.498 -> mode:DIO, clock div:1
15:35:18.539 -> load:0x3fff0030,len:1184
15:35:18.539 -> load:0x40078000,len:13160
15:35:18.539 -> load:0x40080400,len:3036
15:35:18.539 -> entry 0x400805e4
15:35:19.006 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
15:35:19.006 -> ------------------ Version 01.0 --------------------
15:35:19.309 -> Configuring the default Generic Access Service
15:35:19.309 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
15:35:19.309 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
15:35:19.309 -> Configuring the Server Nordic Uart Service
15:35:19.309 -> Configuring the Server Device Information Service
15:35:19.342 -> Configuring the Server Cycle Power Service
15:35:19.342 -> Configuring the Server Cadence and Speed Service
15:35:19.342 -> Configuring the Server Fitness Machine Service
15:35:19.342 -> Configuring the Server Heart Rate Service
15:35:19.342 -> Setting up the Server advertising payload(s)
15:35:19.342 -> Setting Appearance in Advertised data to [1152]
15:35:19.342 -> Server is advertising: CPS, CSC and FTMS
15:35:19.342 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
15:35:22.234 -> Found advertising Peripheral with FTMS enabled! See data:
15:35:22.234 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
15:35:22.234 -> Service Data:
15:35:22.234 -> UUID: 0x1826, Data: �
15:35:22.312 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
15:35:22.312 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
15:35:22.312 -> Now checking all Client Services and Characteristics!
15:35:22.312 -> If Mandatory Services Fail --> the Client will disconnect!
15:35:22.733 -> Client Generic Access: Found!
15:35:23.293 -> -> Client Reads Device Name: [Zwift Hub]
15:35:23.931 -> -> Client Reads Appearance: [1152]
15:35:24.100 -> Client Device Information Service: Found!
15:35:24.427 -> -> Client Reads Manufacturer Name: [Zwift]
15:35:24.893 -> -> Client Reads Model Number: [06]
15:35:25.483 -> -> Client Reads Serial Number: [06-F89CFC535E49]
15:35:25.745 -> Client_CyclingPower_Service: Found!
15:35:26.103 -> Client_CP_Measurement_Chr: Found!
15:35:26.850 -> Client_CP_Feature_Chr: Found!
15:35:26.944 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
15:35:26.944 -> Wheel revolution data supported
15:35:26.944 -> Crank revolution data supported
15:35:27.253 -> Client_CP_Location_Chr: Found!
15:35:27.456 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
15:35:27.662 -> Client_CyclingSpeedCadence_Service: Found!
15:35:27.972 -> Client_CSC_Measurement_Chr: Found!
15:35:28.829 -> Client_CSC_Feature_Chr: Found!
15:35:29.095 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
15:35:29.095 -> Wheel rev supported
15:35:29.095 -> Crank rev supported
15:35:29.593 -> Client_CSC_Location_Chr: Found!
15:35:29.687 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
15:35:29.999 -> Client_FitnessMachine_Service: Found!
15:35:30.247 -> Client_FTM_Feature_Chr: Found!
15:35:30.324 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
15:35:30.324 -> - Fitness Machine Features:
15:35:30.324 -> Average Speed Supported
15:35:30.370 -> Cadence Supported
15:35:30.370 -> Total Distance Supported
15:35:30.370 -> Resistance Level Supported
15:35:30.370 -> Heart Rate Measurement Supported
15:35:30.370 -> Power Measurement Supported
15:35:30.370 -> - Target Setting Features:
15:35:30.370 -> Resistance Target Setting Supported
15:35:30.370 -> Power Target Setting Supported
15:35:30.370 -> Indoor Bike Simulation Parameters Supported
15:35:30.370 -> Wheel Circumference Configuration Supported
15:35:30.370 -> Spin Down Control Supported
15:35:30.696 -> Client_FTM_IndoorBikeData_Chr: Found!
15:35:32.030 -> Client_FTM_TrainingStatus_Chr: Not Found!
15:35:32.293 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
15:35:32.433 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
15:35:32.854 -> Client_FTM_SupportedPowerRange_Chr: Found!
15:35:33.150 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
15:35:33.428 -> Client_FTM_ControlPoint_Chr: Found!
15:35:34.302 -> Client_FTM_Status_Chr: Found!
15:35:35.183 -> Client_HeartRate_Service: Found!
15:35:35.445 -> Client_HR_Measurement_Chr: Found!
15:35:36.185 -> Client_HR_Location_Chr: Found!
15:35:36.354 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest

image

15:39:53.528 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
15:39:53.571 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
15:39:53.571 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
15:39:53.571 -> MTU updated: 255 for connection ID: 1
15:39:54.957 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
15:39:58.214 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
15:39:59.336 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
15:40:00.302 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
15:40:00.912 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
15:40:01.376 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
15:40:02.232 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]

image

15:43:04.454 -> Request Control of Machine!
15:43:04.730 -> Reset Machine!
15:43:04.858 -> Request Control of Machine!
15:43:04.891 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
15:43:04.891 -> Memory dump at 0x400d20f0: b26508a8 00f01dc3 81004136
15:43:04.891 -> Core 1 register dump:
15:43:04.891 -> PC : 0x400d20f5 PS : 0x00060430 A0 : 0x00000000 A1 : 0x3ffd03f0
15:43:04.891 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
15:43:04.925 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d20f5 A9 : 0x3ffd03d0
15:43:04.925 -> A10 : 0x3ffd0dfc A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd0484
15:43:04.925 -> A14 : 0x00000000 A15 : 0x3ffc3d3c SAR : 0x00000002 EXCCAUSE: 0x00000000
15:43:04.925 -> EXCVADDR: 0x00000000 LBEG : 0x400913f4 LEND : 0x40091410 LCOUNT : 0x00000000
15:43:04.962 ->
15:43:04.962 ->
15:43:04.962 -> Backtrace:0x400d20f2:0x3ffd03f0
15:43:04.962 ->
15:43:04.962 ->
15:43:04.962 ->
15:43:04.962 ->
15:43:04.962 -> ELF file SHA256: 0000000000000000
15:43:04.962 ->
15:43:04.962 -> Rebooting...
15:43:04.962 -> ets Jul 29 2019 12:21:46
15:43:04.962 ->
15:43:04.962 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
15:43:04.962 -> configsip: 271414342, SPIWP:0xee
15:43:04.962 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
15:43:04.962 -> mode:DIO, clock div:1
15:43:04.962 -> load:0x3fff0030,len:1184
15:43:04.962 -> load:0x40078000,len:13160
15:43:04.962 -> load:0x40080400,len:3036
15:43:04.962 -> entry 0x400805e4
15:43:05.463 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
15:43:05.463 -> ------------------ Version 01.0 --------------------
15:43:05.728 -> Configuring the default Generic Access Service
15:43:05.728 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
15:43:05.728 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
15:43:05.728 -> Configuring the Server Nordic Uart Service
15:43:05.728 -> Configuring the Server Device Information Service
15:43:05.761 -> Configuring the Server Cycle Power Service
15:43:05.761 -> Configuring the Server Cadence and Speed Service
15:43:05.761 -> Configuring the Server Fitness Machine Service
15:43:05.761 -> Configuring the Server Heart Rate Service
15:43:05.761 -> Setting up the Server advertising payload(s)
15:43:05.761 -> Setting Appearance in Advertised data to [1152]
15:43:05.761 -> Server is advertising: CPS, CSC and FTMS
15:43:05.761 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
15:43:09.853 -> Found advertising Peripheral with FTMS enabled! See data:
15:43:09.853 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
15:43:09.853 -> Service Data:
15:43:09.853 -> UUID: 0x1826, Data: �
15:43:10.021 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
15:43:10.021 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
15:43:10.021 -> Now checking all Client Services and Characteristics!
15:43:10.021 -> If Mandatory Services Fail --> the Client will disconnect!
15:43:10.348 -> Client Generic Access: Found!
15:43:10.907 -> -> Client Reads Device Name: [Zwift Hub]
15:43:11.312 -> -> Client Reads Appearance: [1152]
15:43:11.438 -> Client Device Information Service: Found!
15:43:11.858 -> -> Client Reads Manufacturer Name: [Zwift]
15:43:12.402 -> -> Client Reads Model Number: [06]
15:43:13.058 -> -> Client Reads Serial Number: [06-F89CFC535E49]
15:43:13.323 -> Client_CyclingPower_Service: Found!
15:43:13.509 -> Client_CP_Measurement_Chr: Found!
15:43:14.198 -> Client_CP_Feature_Chr: Found!
15:43:14.368 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
15:43:14.368 -> Wheel revolution data supported
15:43:14.368 -> Crank revolution data supported
15:43:14.600 -> Client_CP_Location_Chr: Found!
15:43:14.724 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
15:43:15.037 -> Client_CyclingSpeedCadence_Service: Found!
15:43:15.457 -> Client_CSC_Measurement_Chr: Found!
15:43:16.095 -> Client_CSC_Feature_Chr: Found!
15:43:16.266 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
15:43:16.266 -> Wheel rev supported
15:43:16.266 -> Crank rev supported
15:43:16.608 -> Client_CSC_Location_Chr: Found!
15:43:16.702 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
15:43:17.015 -> Client_FitnessMachine_Service: Found!
15:43:17.278 -> Client_FTM_Feature_Chr: Found!
15:43:17.435 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
15:43:17.468 -> - Fitness Machine Features:
15:43:17.468 -> Average Speed Supported
15:43:17.468 -> Cadence Supported
15:43:17.468 -> Total Distance Supported
15:43:17.468 -> Resistance Level Supported
15:43:17.468 -> Heart Rate Measurement Supported
5:43:17.468 -> Power Measurement Supported
15:43:17.468 -> - Target Setting Features:
15:43:17.468 -> Resistance Target Setting Supported
15:43:17.468 -> Power Target Setting Supported
15:43:17.468 -> Indoor Bike Simulation Parameters Supported
15:43:17.468 -> Wheel Circumference Configuration Supported
15:43:17.514 -> Spin Down Control Supported
15:43:17.637 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
15:43:17.637 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
15:43:17.637 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
15:43:17.637 -> MTU updated: 255 for connection ID: 1
15:43:17.716 -> Client_FTM_IndoorBikeData_Chr: Found!
15:43:19.008 -> Client_FTM_TrainingStatus_Chr: Not Found!
15:43:19.416 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
15:43:19.724 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
15:43:20.007 -> Client_FTM_SupportedPowerRange_Chr: Found!
15:43:20.182 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
15:43:20.569 -> Client_FTM_ControlPoint_Chr: Found!
15:43:21.300 -> Client_FTM_Status_Chr: Found!
15:43:22.021 -> Client_HeartRate_Service: Found!
15:43:22.237 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
15:43:22.376 -> Client_HR_Measurement_Chr: Found!
15:43:23.014 -> Client_HR_Location_Chr: Found!
15:43:23.263 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
15:43:23.310 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
15:43:23.964 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
15:43:24.275 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
15:43:24.777 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
15:43:24.856 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
15:43:25.357 -> Request Control of Machine!
15:43:25.468 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
15:43:25.930 -> Reset Machine!
15:43:26.134 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 01 ]
15:43:26.352 -> Request Control of Machine!
15:43:26.616 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
15:43:26.695 -> Start or Resume Machine!
15:43:26.912 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 04 ]
15:43:27.856 -> Set Indoor Bike Simulation Parameters!
15:43:27.856 -> Wind speed (1000): 0.00 | Grade (100): -0.58 | Crr (10000): 0.00 | Cw (100): 0.51
15:43:28.169 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 C6 FF 28 33 ]

Second test

image

15:57:55.669 -> ets Jul 29 2019 12:21:46
15:57:55.716 ->
15:57:55.716 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
15:57:55.716 -> configsip: 271414342, SPIWP:0xee
15:57:55.716 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
15:57:55.716 -> mode:DIO, clock div:1
15:57:55.716 -> load:0x3fff0030,len:1184
15:57:55.716 -> load:0x40078000,len:13160
15:57:55.716 -> load:0x40080400,len:3036
15:57:55.716 -> entry 0x400805e4
15:57:56.214 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
15:57:56.214 -> ------------------ Version 01.0 --------------------
15:57:56.478 -> Configuring the default Generic Access Service
15:57:56.478 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
15:57:56.478 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
15:57:56.478 -> Configuring the Server Nordic Uart Service
15:57:56.478 -> Configuring the Server Device Information Service
15:57:56.511 -> Configuring the Server Cycle Power Service
15:57:56.511 -> Configuring the Server Cadence and Speed Service
15:57:56.511 -> Configuring the Server Fitness Machine Service
15:57:56.511 -> Configuring the Server Heart Rate Service
15:57:56.511 -> Setting up the Server advertising payload(s)
15:57:56.511 -> Setting Appearance in Advertised data to [1152]
15:57:56.511 -> Server is advertising: CPS, CSC and FTMS
15:57:56.511 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
15:57:56.547 -> Found advertising Peripheral with FTMS enabled! See data:
15:57:56.547 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
15:57:56.585 -> Service Data:
15:57:56.585 -> UUID: 0x1826, Data: �
15:57:56.678 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
15:57:56.678 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
15:57:56.678 -> Now checking all Client Services and Characteristics!
15:57:56.678 -> If Mandatory Services Fail --> the Client will disconnect!
15:57:57.270 -> Client Generic Access: Found!
15:57:57.660 -> -> Client Reads Device Name: [Zwift Hub]
15:57:58.203 -> -> Client Reads Appearance: [1152]
15:57:58.315 -> Client Device Information Service: Found!
15:57:59.000 -> -> Client Reads Manufacturer Name: [Zwift]
15:57:59.516 -> -> Client Reads Model Number: [06]
15:57:59.907 -> -> Client Reads Serial Number: [06-F89CFC535E49]
15:58:00.311 -> Client_CyclingPower_Service: Found!
15:58:00.530 -> Client_CP_Measurement_Chr: Found!
15:58:01.261 -> Client_CP_Feature_Chr: Found!
15:58:01.465 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
15:58:01.465 -> Wheel revolution data supported
15:58:01.465 -> Crank revolution data supported
15:58:01.761 -> Client_CP_Location_Chr: Found!
15:58:01.853 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
15:58:02.166 -> Client_CyclingSpeedCadence_Service: Found!
15:58:02.480 -> Client_CSC_Measurement_Chr: Found!
15:58:03.176 -> Client_CSC_Feature_Chr: Found!
15:58:03.364 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
15:58:03.364 -> Wheel rev supported
15:58:03.364 -> Crank rev supported
15:58:03.598 -> Client_CSC_Location_Chr: Found!
15:58:03.770 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
15:58:04.096 -> Client_FitnessMachine_Service: Found!
15:58:04.517 -> Client_FTM_Feature_Chr: Found!
15:58:04.829 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
15:58:04.866 -> - Fitness Machine Features:
15:58:04.866 -> Average Speed Supported
15:58:04.866 -> Cadence Supported
15:58:04.866 -> Total Distance Supported
15:58:04.866 -> Resistance Level Supported
15:58:04.866 -> Heart Rate Measurement Supported
15:58:04.866 -> Power Measurement Supported
15:58:04.866 -> - Target Setting Features:
15:58:04.866 -> Resistance Target Setting Supported
15:58:04.866 -> Power Target Setting Supported
15:58:04.866 -> Indoor Bike Simulation Parameters Supported
15:58:04.907 -> Wheel Circumference Configuration Supported
15:58:04.907 -> Spin Down Control Supported
15:58:05.168 -> Client_FTM_IndoorBikeData_Chr: Found!
15:58:06.270 -> Client_FTM_TrainingStatus_Chr: Not Found!
15:58:06.548 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
15:58:06.721 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
15:58:07.095 -> Client_FTM_SupportedPowerRange_Chr: Found!
15:58:07.423 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
15:58:07.751 -> Client_FTM_ControlPoint_Chr: Found!
15:58:08.451 -> Client_FTM_Status_Chr: Found!
15:58:09.562 -> Client_HeartRate_Service: Found!
15:58:09.796 -> Client_HR_Measurement_Chr: Found!
15:58:10.712 -> Client_HR_Location_Chr: Found!
15:58:10.964 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest

image

15:59:53.270 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
15:59:53.270 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
15:59:53.270 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
15:59:53.270 -> MTU updated: 255 for connection ID: 1
15:59:54.501 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
15:59:57.848 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
15:59:59.033 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
15:59:59.983 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:00:00.575 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:00:01.088 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:00:01.931 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]

image

16:02:26.530 -> Request Control of Machine!
16:02:27.169 -> Reset Machine!
16:02:27.303 -> Request Control of Machine!
16:02:27.337 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:02:27.337 -> Memory dump at 0x400d20f0: b26508a8 00f01dc3 81004136
16:02:27.337 -> Core 1 register dump:
16:02:27.337 -> PC : 0x400d20f5 PS : 0x00060630 A0 : 0x00000000 A1 : 0x3ffd0140
16:02:27.339 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:02:27.370 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d20f5 A9 : 0x3ffd0120
16:02:27.370 -> A10 : 0x3ffd0b54 A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd01dc
16:02:27.370 -> A14 : 0x00000000 A15 : 0x3ffc3d3c SAR : 0x00000001 EXCCAUSE: 0x00000000
16:02:27.370 -> EXCVADDR: 0x00000000 LBEG : 0x400913f4 LEND : 0x40091410 LCOUNT : 0x00000000
16:02:27.416 ->
16:02:27.416 ->
16:02:27.416 -> Backtrace:0x400d20f2:0x3ffd0140
16:02:27.416 ->
16:02:27.416 ->
16:02:27.416 ->
16:02:27.416 ->
16:02:27.416 -> ELF file SHA256: 0000000000000000
16:02:27.416 ->
16:02:27.416 -> Rebooting...
16:02:27.416 -> ets Jul 29 2019 12:21:46
16:02:27.416 ->
16:02:27.416 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:02:27.416 -> configsip: 271414342, SPIWP:0xee
16:02:27.416 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:02:27.416 -> mode:DIO, clock div:1
16:02:27.416 -> load:0x3fff0030,len:1184
16:02:27.416 -> load:0x40078000,len:13160
16:02:27.416 -> load:0x40080400,len:3036
16:02:27.416 -> entry 0x400805e4
16:02:27.916 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
16:02:27.916 -> ------------------ Version 01.0 --------------------
16:02:28.188 -> Configuring the default Generic Access Service
16:02:28.188 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:02:28.188 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:02:28.188 -> Configuring the Server Nordic Uart Service
16:02:28.188 -> Configuring the Server Device Information Service
16:02:28.188 -> Configuring the Server Cycle Power Service
16:02:28.229 -> Configuring the Server Cadence and Speed Service
16:02:28.229 -> Configuring the Server Fitness Machine Service
16:02:28.229 -> Configuring the Server Heart Rate Service
16:02:28.229 -> Setting up the Server advertising payload(s)
16:02:28.229 -> Setting Appearance in Advertised data to [1152]
16:02:28.229 -> Server is advertising: CPS, CSC and FTMS
16:02:28.229 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:02:32.404 -> Found advertising Peripheral with FTMS enabled! See data:
16:02:32.404 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:02:32.404 -> Service Data:
16:02:32.404 -> UUID: 0x1826, Data: �
16:02:32.527 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:02:32.527 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:02:32.528 -> Now checking all Client Services and Characteristics!
16:02:32.528 -> If Mandatory Services Fail --> the Client will disconnect!
16:02:32.858 -> Client Generic Access: Found!
16:02:33.467 -> -> Client Reads Device Name: [Zwift Hub]
16:02:34.077 -> -> Client Reads Appearance: [1152]
16:02:34.203 -> Client Device Information Service: Found!
16:02:34.701 -> -> Client Reads Manufacturer Name: [Zwift]
16:02:35.264 -> -> Client Reads Model Number: [06]
16:02:35.921 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:02:36.154 -> Client_CyclingPower_Service: Found!
16:02:36.465 -> Client_CP_Measurement_Chr: Found!
16:02:37.227 -> Client_CP_Feature_Chr: Found!
16:02:37.368 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:02:37.368 -> Wheel revolution data supported
16:02:37.368 -> Crank revolution data supported
16:02:37.772 -> Client_CP_Location_Chr: Found!
16:02:38.006 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:02:38.146 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:02:38.146 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:02:38.146 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:02:38.285 -> MTU updated: 255 for connection ID: 1
16:02:38.319 -> Client_CyclingSpeedCadence_Service: Found!

...

16:02:46.672 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:02:47.050 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:02:47.563 -> Request Control of Machine!
16:02:47.594 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:02:48.550 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:02:52.678 -> Request Control of Machine!
16:02:52.912 -> Reset Machine!
16:02:53.038 -> Request Control of Machine!
16:02:53.152 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:02:53.152 -> Memory dump at 0x400d20f0: b26508a8 00f01dc3 81004136
16:02:53.152 -> Core 1 register dump:
16:02:53.152 -> PC : 0x400d20f5 PS : 0x00060430 A0 : 0x00000000 A1 : 0x3ffd03d0
16:02:53.152 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:02:53.152 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d20f5 A9 : 0x3ffd03b0
16:02:53.183 -> A10 : 0x3ffd0de4 A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd046c
16:02:53.183 -> A14 : 0x00000000 A15 : 0x3ffc3d3c SAR : 0x00000002 EXCCAUSE: 0x00000000
16:02:53.183 -> EXCVADDR: 0x00000000 LBEG : 0x400913f4 LEND : 0x40091410 LCOUNT : 0x00000000
16:02:53.183 ->
16:02:53.183 ->
16:02:53.183 -> Backtrace:0x400d20f2:0x3ffd03d0
16:02:53.183 ->
16:02:53.183 ->
16:02:53.183 ->
16:02:53.183 ->
16:02:53.183 -> ELF file SHA256: 0000000000000000
16:02:53.183 ->
16:02:53.183 -> Rebooting...
16:02:53.226 -> ets Jul 29 2019 12:21:46
16:02:53.226 ->
16:02:53.226 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:02:53.226 -> configsip: 271414342, SPIWP:0xee
16:02:53.226 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:02:53.226 -> mode:DIO, clock div:1
16:02:53.226 -> load:0x3fff0030,len:1184
16:02:53.226 -> load:0x40078000,len:13160
16:02:53.226 -> load:0x40080400,len:3036
16:02:53.226 -> entry 0x400805e4
16:02:53.726 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
16:02:53.726 -> ------------------ Version 01.0 --------------------
16:02:53.994 -> Configuring the default Generic Access Service
16:02:53.994 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:02:53.994 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]

....

16:03:11.966 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:03:12.405 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:03:12.810 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:03:12.933 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:03:13.622 -> Request Control of Machine!
16:03:13.886 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:03:13.982 -> Reset Machine!
16:03:14.106 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:03:14.106 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 01 ]
16:03:14.232 -> Request Control of Machine!
16:03:14.361 -> Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
16:03:14.361 -> Memory dump at 0x400d20f0: b26508a8 00f01dc3 81004136
16:03:14.361 -> Core 1 register dump:
16:03:14.361 -> PC : 0x400d20f5 PS : 0x00060d30 A0 : 0x00000000 A1 : 0x3ffd0460
16:03:14.394 -> A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
16:03:14.394 -> A6 : 0x00000000 A7 : 0x00000000 A8 : 0x800d20f5 A9 : 0x3ffd0440
16:03:14.394 -> A10 : 0x3ffd0e70 A11 : 0x00000001 A12 : 0x0000000a A13 : 0x3ffd04f8
16:03:14.394 -> A14 : 0x00000000 A15 : 0x3ffc3d3c SAR : 0x00000002 EXCCAUSE: 0x00000000
16:03:14.425 -> EXCVADDR: 0x00000000 LBEG : 0x400913f4 LEND : 0x40091410 LCOUNT : 0x00000000
16:03:14.425 ->
16:03:14.425 ->
16:03:14.425 -> Backtrace:0x400d20f2:0x3ffd0460
16:03:14.425 ->
16:03:14.425 ->
16:03:14.425 ->
16:03:14.425 ->
16:03:14.425 -> ELF file SHA256: 0000000000000000
16:03:14.425 ->
16:03:14.425 -> Rebooting...
16:03:14.425 -> ets Jul 29 2019 12:21:46
16:03:14.425 ->
16:03:14.425 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:03:14.425 -> configsip: 271414342, SPIWP:0xee
16:03:14.425 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:03:14.464 -> mode:DIO, clock div:1
16:03:14.464 -> load:0x3fff0030,len:1184
16:03:14.464 -> load:0x40078000,len:13160
16:03:14.464 -> load:0x40080400,len:3036
16:03:14.464 -> entry 0x400805e4
16:03:14.932 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
16:03:14.932 -> ------------------ Version 01.0 --------------------
16:03:15.197 -> Configuring the default Generic Access Service

.....

16:07:32.643 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
16:07:32.643 -> ------------------ Version 01.0 --------------------
16:07:32.910 -> Configuring the default Generic Access Service
16:07:32.910 -> Successfully Set Generic Access Appearance Chr value to: [1152] Generic Cycling
16:07:32.910 -> Successfully Set Generic Access Device Name Chr value to: [Sim ESP32]
16:07:32.944 -> Configuring the Server Nordic Uart Service
16:07:32.944 -> Configuring the Server Device Information Service
16:07:32.944 -> Configuring the Server Cycle Power Service
16:07:32.944 -> Configuring the Server Cadence and Speed Service
16:07:32.944 -> Configuring the Server Fitness Machine Service
16:07:32.944 -> Configuring the Server Heart Rate Service
16:07:32.944 -> Setting up the Server advertising payload(s)
16:07:32.944 -> Setting Appearance in Advertised data to [1152]
16:07:32.944 -> Server is advertising: CPS, CSC and FTMS
16:07:32.984 -> Client Starts Scanning for Server Device with CPS, CSC and FTMS!
16:07:34.699 -> Found advertising Peripheral with FTMS enabled! See data:
16:07:34.699 -> Name: Zwift Hub, Address: f8:9c:fc:53:5e:49, appearance: 1152, manufacturer data: 4a09065e49, serviceUUID: 0x1816
16:07:34.742 -> Service Data:
16:07:34.742 -> UUID: 0x1826, Data: �
16:07:34.778 -> Client Connection Parameters -> Interval: [40] Latency: [0] Supervision Timeout: [256]
16:07:34.819 -> ESP32 Client connected to Server device with Name: [Zwift Hub] MAC Address: [F8:9C:FC:53:5E:49] Handle: [0] MTU: [244]
16:07:34.819 -> Now checking all Client Services and Characteristics!
16:07:34.819 -> If Mandatory Services Fail --> the Client will disconnect!
16:07:35.179 -> Client Generic Access: Found!
16:07:35.738 -> -> Client Reads Device Name: [Zwift Hub]
16:07:36.607 -> -> Client Reads Appearance: [1152]
16:07:36.794 -> Client Device Information Service: Found!
16:07:37.248 -> -> Client Reads Manufacturer Name: [Zwift]
16:07:37.760 -> -> Client Reads Model Number: [06]
16:07:38.148 -> -> Client Reads Serial Number: [06-F89CFC535E49]
16:07:38.459 -> Client_CyclingPower_Service: Found!
16:07:38.771 -> Client_CP_Measurement_Chr: Found!
16:07:39.550 -> Client_CP_Feature_Chr: Found!
16:07:39.675 -> -> Client Reads Raw CP Feature bytes: [4] [ 0C 00 00 00 ]
16:07:39.675 -> Wheel revolution data supported
16:07:39.721 -> Crank revolution data supported
16:07:39.939 -> Client_CP_Location_Chr: Found!
16:07:40.019 -> -> Client Reads CP Location Sensor: Loc#: 12 Rear wheel
16:07:40.436 -> Client_CyclingSpeedCadence_Service: Found!
16:07:40.702 -> Client_CSC_Measurement_Chr: Found!
16:07:41.358 -> Client_CSC_Feature_Chr: Found!
16:07:41.451 -> -> Client Reads Raw CSC Feature bytes: [2] [ 03 00 ]
16:07:41.451 -> Wheel rev supported
16:07:41.451 -> Crank rev supported
16:07:41.686 -> Client_CSC_Location_Chr: Found!
16:07:41.857 -> -> Client Reads CSC Location Sensor: Loc#: 12 Rear wheel
16:07:42.137 -> Client_FitnessMachine_Service: Found!
16:07:42.402 -> Client_FTM_Feature_Chr: Found!
16:07:42.480 -> -> Client Reads Raw FTM Feature bytes: [8] [ 87 44 00 00 0C E0 00 00 ]
16:07:42.480 -> - Fitness Machine Features:
16:07:42.480 -> Average Speed Supported
16:07:42.480 -> Cadence Supported
16:07:42.526 -> Total Distance Supported
16:07:42.526 -> Resistance Level Supported
16:07:42.526 -> Heart Rate Measurement Supported
16:07:42.526 -> Power Measurement Supported
16:07:42.526 -> - Target Setting Features:
16:07:42.526 -> Resistance Target Setting Supported
16:07:42.526 -> Power Target Setting Supported
16:07:42.526 -> Indoor Bike Simulation Parameters Supported
16:07:42.526 -> Wheel Circumference Configuration Supported
16:07:42.526 -> Spin Down Control Supported
16:07:42.903 -> Client_FTM_IndoorBikeData_Chr: Found!
16:07:43.575 -> Server Connection Parameters -> Interval: [48] Latency: [0] Supervision Timeout: [960]
16:07:43.575 -> ESP32 Server connected to Client device with MAC Address: [58:11:22:53:52:18] Conn Handle: [1]
16:07:43.575 -> Central (MyLaptop/Zwift) has to set CP/CSC/FTMS CCCD Notify/Indicate (enable) and start....
16:07:43.617 -> MTU updated: 255 for connection ID: 1
16:07:43.990 -> Client_FTM_TrainingStatus_Chr: Not Found!
16:07:44.364 -> Client_FTM_SupportedResistanceLevelRange_Chr: Found!
16:07:44.520 -> -> Client Reads Raw FTM Supported Resistance Level Range bytes: [6] [ 00 00 64 00 01 00 ]
16:07:44.863 -> Client_FTM_SupportedPowerRange_Chr: Found!
16:07:44.957 -> -> Client Reads Raw FTM Supported Power Range bytes: [6] [ 00 00 E8 03 01 00 ]
16:07:45.409 -> Client_FTM_ControlPoint_Chr: Found!
16:07:46.309 -> Client_FTM_Status_Chr: Found!
16:07:47.510 -> Client_HeartRate_Service: Found!
16:07:47.806 -> Client_HR_Measurement_Chr: Found!
16:07:48.226 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a63]
16:07:49.007 -> Client_HR_Location_Chr: Found!
16:07:49.211 -> -> Client Reads HR Location Sensor: Loc#: 1 Chest
16:07:49.537 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a5b]
16:07:50.408 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:07:50.502 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ad2]
16:07:51.185 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:07:51.405 -> All Client (Trainer) Characteristics are Notify/Indicate Enabled!
16:07:51.718 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:07:52.542 -> Request Control of Machine!
16:07:52.775 -> Reset Machine!
16:07:52.901 -> Central Updated CCCD --> Notify Enabled for Char: [0x2a37]
16:07:52.948 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 01 ]
16:07:53.040 -> Request Control of Machine!
16:07:53.396 -> Start or Resume Machine!
16:07:53.568 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [1] [ 04 ]
16:07:54.468 -> Set Indoor Bike Simulation Parameters!
16:07:54.468 -> Wind speed (1000): 0.00 | Grade (100): -0.58 | Crr (10000): 0.00 | Cw (100): 0.51
16:07:54.718 -> -> Client Rec'd Raw FTM Machine Status Data: [1] [7] [ 12 00 00 C6 FF 28 33 ]

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 6, 2023

Dear Joel,
Again, I am not able to reproduce this fatal error but I have a suggestion, locate this task-function in the MITM/Simcline code :

void TaskWriteWithResponse(void *parameter) {
  // Just pass on and process later! 
  if( !pRemote_FTM_ControlPoint_Chr->writeValue(ftmcpData, true) ) { // true -> WithResponse (fatal if trainer is not responding: Guru paniced!!)
      pClient_FTMS->disconnect();
      DEBUG_PRINTLN(">>> Error: NOT responding to FTM Control Point -> Write Value!");
  }
  vTaskDelete(TaskWriteWithResponseHandle);
};

After you have changed true in false like below:

->writeValue(ftmcpData, true)) {

in

->writeValue(ftmcpData, false)) }

Compile and upload the code again and run....

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 7, 2023 via email

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 8, 2023

Other suggestions:
Focus on esp32_FTMS_Zwift_Bridge_v010
Experiment with the load balance (Tools ) "Events run on Core #" and "Arduino runs on Core #" and see how it influences the result...
Have a nice weekend!

@le-joebar
Copy link

Hello Jorgen,

Should I do like this?

change as follows:

//#define DEBUG_FTM_CONTROLPOINT_RESPONSE // If defined allows for parsing the Data
//#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA // If defined allows for parsing and decoding Data

Core Debug Level: "Error"

Baud Rate 115200

and in addition:
->writeValue(ftmcpData, false)) }

Set Arduino IDE debug level to "debug"

and test?

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 8, 2023

Dear Joel,
set the ->writeValue(ftmcpData, false)) } to its original value: true (Write with response)
Other settings are ok! Do not hesitate to experiment with the load balance!

@le-joebar
Copy link

Here is :

so esp32_FTMS_Zwift_Bridge_V010 modify as follows:

//#define DEBUG_FTM_CONTROLPOINT_RESPONSE // If defined, allows to analyze the data
//#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA // If defined, allows to analyze and decode the data

Baud rate 115200

Main debug level: "Debug"

16:05:52.931 -> D NimBLEClient: Got Client event
16:05:52.931 -> D NimBLEClient: Notify Recieved for handle: 43
16:05:52.931 -> D NimBLEClient: checking service 0x180a for handle: 43
16:05:52.931 -> D NimBLEClient: checking service 0x1818 for handle: 43
16:05:52.931 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:52.964 -> Descriptor: uuid: 0x2902, handle: 44
16:05:52.964 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:52.964 -> Descriptor: uuid: 0x2902, handle: 44
16:05:52.964 -> D NimBLECharacteristic: >> setValue: length=4, data=00000000, characteristic UUID=0x2a63
16:05:52.964 -> D NimBLECharacteristic: << setValue
16:05:52.964 -> D NimBLECharacteristic: >> notify: length: 4
16:05:53.009 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:53.009 -> D NimBLEServer: >> handleGapEvent:
16:05:53.009 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:53.009 -> D NimBLECharacteristic: << notify
16:05:53.900 -> D NimBLEClient: Got Client event
16:05:53.900 -> D NimBLEClient: Notify Recieved for handle: 16
16:05:53.933 -> D NimBLEClient: checking service 0x180a for handle: 16
16:05:53.933 -> D NimBLEClient: checking service 0x1818 for handle: 16
16:05:53.933 -> D NimBLEClient: checking service 0x1816 for handle: 16
16:05:53.933 -> D NimBLEClient: checking service 0x1826 for handle: 16
16:05:53.933 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:53.967 -> Descriptor: uuid: 0x2902, handle: 17
16:05:53.967 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:53.967 -> Descriptor: uuid: 0x2902, handle: 17
16:05:53.967 -> D NimBLECharacteristic: >> setValue: length=11, data=6402000000001400000000, characteristic UUID=0x2ad2
16:05:53.967 -> D NimBLECharacteristic: << setValue
16:05:54.000 -> D NimBLECharacteristic: >> notify: length: 11
16:05:54.000 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:54.000 -> D NimBLEServer: >> handleGapEvent:
16:05:54.000 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:54.000 -> D NimBLECharacteristic: << notify
16:05:54.000 -> D NimBLEClient: Got Client event
16:05:54.000 -> D NimBLEClient: Notify Recieved for handle: 43
16:05:54.000 -> D NimBLEClient: checking service 0x180a for handle: 43
16:05:54.000 -> D NimBLEClient: checking service 0x1818 for handle: 43
16:05:54.033 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:54.033 -> Descriptor: uuid: 0x2902, handle: 44
16:05:54.033 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:54.033 -> Descriptor: uuid: 0x2902, handle: 44
16:05:54.033 -> D NimBLECharacteristic: >> setValue: length=4, data=00000000, characteristic UUID=0x2a63
16:05:54.071 -> D NimBLECharacteristic: << setValue
16:05:54.071 -> D NimBLECharacteristic: >> notify: length: 4
16:05:54.071 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:54.071 -> D NimBLEServer: >> handleGapEvent:
16:05:54.071 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:54.071 -> D NimBLECharacteristic: << notify
16:05:54.888 -> D NimBLEClient: Got Client event
16:05:54.888 -> D NimBLEClient: Notify Recieved for handle: 16
16:05:54.888 -> D NimBLEClient: checking service 0x180a for handle: 16
16:05:54.888 -> D NimBLEClient: checking service 0x1818 for handle: 16
16:05:54.888 -> D NimBLEClient: checking service 0x1816 for handle: 16
16:05:54.888 -> D NimBLEClient: checking service 0x1826 for handle: 16
16:05:54.888 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:54.921 -> Descriptor: uuid: 0x2902, handle: 17
16:05:54.921 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:54.921 -> Descriptor: uuid: 0x2902, handle: 17
16:05:54.921 -> D NimBLECharacteristic: >> setValue: length=11, data=6402000000001400000000, characteristic UUID=0x2ad2
16:05:54.954 -> D NimBLECharacteristic: << setValue
16:05:54.954 -> D NimBLECharacteristic: >> notify: length: 11
16:05:54.954 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:54.954 -> D NimBLEServer: >> handleGapEvent:
16:05:54.954 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:54.954 -> D NimBLECharacteristic: << notify
16:05:54.954 -> D NimBLEClient: Got Client event
16:05:54.954 -> D NimBLEClient: Notify Recieved for handle: 43
16:05:54.954 -> D NimBLEClient: checking service 0x180a for handle: 43
16:05:54.988 -> D NimBLEClient: checking service 0x1818 for handle: 43
16:05:54.988 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:54.988 -> Descriptor: uuid: 0x2902, handle: 44
16:05:54.988 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:55.023 -> Descriptor: uuid: 0x2902, handle: 44
16:05:55.023 -> D NimBLECharacteristic: >> setValue: length=4, data=00000000, characteristic UUID=0x2a63
16:05:55.023 -> D NimBLECharacteristic: << setValue
16:05:55.023 -> D NimBLECharacteristic: >> notify: length: 4
16:05:55.023 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:55.023 -> D NimBLEServer: >> handleGapEvent:
16:05:55.023 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:55.023 -> D NimBLECharacteristic: << notify
16:05:55.842 -> D NimBLEClient: Got Client event
16:05:55.842 -> D NimBLEClient: Notify Recieved for handle: 16
16:05:55.842 -> D NimBLEClient: checking service 0x180a for handle: 16
16:05:55.842 -> D NimBLEClient: checking service 0x1818 for handle: 16
16:05:55.842 -> D NimBLEClient: checking service 0x1816 for handle: 16
16:05:55.842 -> D NimBLEClient: checking service 0x1826 for handle: 16
16:05:55.876 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:55.876 -> Descriptor: uuid: 0x2902, handle: 17
16:05:55.876 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:05:55.876 -> Descriptor: uuid: 0x2902, handle: 17
16:05:55.876 -> D NimBLECharacteristic: >> setValue: length=11, data=6402000000001400000000, characteristic UUID=0x2ad2
16:05:55.909 -> D NimBLECharacteristic: << setValue
16:05:55.909 -> D NimBLECharacteristic: >> notify: length: 11
16:05:55.909 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:55.909 -> D NimBLEServer: >> handleGapEvent:
16:05:55.909 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:55.909 -> D NimBLECharacteristic: << notify
16:05:55.909 -> D NimBLEClient: Got Client event
16:05:55.942 -> D NimBLEClient: Notify Recieved for handle: 43
16:05:55.942 -> D NimBLEClient: checking service 0x180a for handle: 43
16:05:55.942 -> D NimBLEClient: checking service 0x1818 for handle: 43
16:05:55.942 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:55.942 -> Descriptor: uuid: 0x2902, handle: 44
16:05:55.942 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:05:55.975 -> Descriptor: uuid: 0x2902, handle: 44
16:05:55.975 -> D NimBLECharacteristic: >> setValue: length=4, data=00000000, characteristic UUID=0x2a63
16:05:55.975 -> D NimBLECharacteristic: << setValue
16:05:55.975 -> D NimBLECharacteristic: >> notify: length: 4
16:05:55.975 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:55.975 -> D NimBLEServer: >> handleGapEvent:
16:05:55.975 -> D NimBLECharacteristicCallbacks: onStatus: default
16:05:56.010 -> D NimBLECharacteristic: << notify
16:05:56.167 -> D NimBLECharacteristic: Characteristic 0x2ad9 Write event
16:05:56.167 -> D NimBLECharacteristic: >> setValue: length=1, data=00, characteristic UUID=0x2ad9
16:05:56.167 -> D NimBLECharacteristic: << setValue
16:05:56.167 -> ReqD NimBLERemoteCharacteristic: >> writeValue(), length: 1
16:05:56.167 -> uest Control of Machine!
16:05:56.167 -> D NimBLECharacteristicCallbacks: onWrite: default
16:05:56.325 -> I NimBLERemoteCharacteristic: Write complete; status=0 conn_handle=0
16:05:56.325 -> D NimBLERemoteCharacteristic: << writeValue, rc: 0
16:05:56.325 -> D NimBLEClient: Got Client event
16:05:56.325 -> D NimBLEClient: Notify Recieved for handle: 28
16:05:56.325 -> D NimBLEClient: checking service 0x180a for handle: 28
16:05:56.325 -> D NimBLEClient: checking service 0x1818 for handle: 28
16:05:56.325 -> D NimBLEClient: checking service 0x1816 for handle: 28
16:05:56.357 -> D NimBLEClient: checking service 0x1826 for handle: 28
16:05:56.357 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad9, handle: 28 0x001c, props: 0x28
16:05:56.357 -> Descriptor: uuid: 0x2902, handle: 29
16:05:56.357 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad9, handle: 28 0x001c, props: 0x28
16:05:56.389 -> Descriptor: uuid: 0x2902, handle: 29
16:05:56.389 -> D NimBLECharacteristic: >> setValue: length=3, data=800001, characteristic UUID=0x2ad9
16:05:56.389 -> D NimBLECharacteristic: << setValue
16:05:56.389 -> D NimBLECharacteristic: >> notify: length: 3
16:05:56.389 -> D NimBLECharacteristicCallbacks: onNotify: default
16:05:56.389 -> Guru Meditation Error: Core 0 panic'ed (Unhandled debug exception).
16:05:56.422 -> Debug exception reason: Stack canary watchpoint triggered (CP Indicate)
16:05:56.422 -> Core 0 register dump:
16:05:56.422 -> PC : 0x40096c93 PS : 0x00060f36 A0 : 0x80094a2d A1 : 0x3ffcfe10
16:05:56.422 -> A2 : 0x3ffce144 A3 : 0xb33fffff A4 : 0x0000abab A5 : 0x00060f23
16:05:56.422 -> A6 : 0x00060f20 A7 : 0x0000cdcd A8 : 0xb33fffff A9 : 0xffffffff
16:05:56.456 -> A10 : 0x00000003 A11 : 0x00060f23 A12 : 0x00060f20 A13 : 0x00000024
16:05:56.456 -> A14 : 0x007ce144 A15 : 0x003fffff SAR : 0x00000008 EXCCAUSE: 0x00000001
16:05:56.456 -> EXCVADDR: 0x00000000 LBEG : 0x40091b11 LEND : 0x40091b21 LCOUNT : 0xfffffffc
16:05:56.456 ->
16:05:56.456 ->
16:05:56.456 -> Backtrace:0x40096c90:0x3ffcfe100x40094a2a:0x3ffcfe50 0x40094af4:0x3ffcfe90 0x40085fbd:0x3ffcfeb0 0x400860c9:0x3ffcfee0 0x400f8636:0x3ffcff00 0x400f78db:0x3ffcff20 0x400f7549:0x3ffcff40 0x40091856:0x3ffcff60 0x40090b0d:0x3ffcff80 0x40090b9c:0x3ffcffa0 0x400910e7:0x3ffcffd0 0x4012d60a:0x3ffd0000 0x40125ec6:0x3ffd0020 0x4011b176:0x3ffd0330 0x400da801:0x3ffd0380 0x400df27d:0x3ffd03f0 0x400df2c7:0x3ffd0410 0x400e0917:0x3ffd0430 0x400e211b:0x3ffd0490 0x400d6481:0x3ffd04b0 0x400d64dd:0x3ffd0540 0x400d64ed:0x3ffd0560 0x400d2361:0x3ffd0580
16:05:56.521 ->
16:05:56.521 ->
16:05:56.521 ->
16:05:56.521 ->
16:05:56.521 -> ELF file SHA256: 0000000000000000
16:05:56.521 ->
16:05:56.521 -> Rebooting...
16:05:56.521 -> ets Jul 29 2019 12:21:46
16:05:56.521 ->

@le-joebar
Copy link

I don't know what to do anymore because it's really a funny problem !!

What if I thought of delivering my trainer to you by post?

Belgium to Netherlands 11 euro!

What do you think about it ?

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 8, 2023

Dear Joel,
I have been asking around if someone knows someone that has a Zwift Hub... So I have been thinking in the same direction... I find it so annoying for you (and me) that we are confronted with these "funny" (really?) problems... I would say very confronting and bad problems.... Ik woon dichtbij Utrecht (centraal NL), bij welke grote stad woon jij?

Debug exception reason: Stack canary watchpoint triggered (CP Indicate) See

So we have a stack problem, when TaskWriteWithResponse is called from server_FTM_ControlPoint_Chr_callback.
I was expecting a timeout problem not a stack problem!
I suggest that you make a simple change in the code again, locate the following code snippet:

 // write with response !!! writeValue(string, bool response = false);
  if(Trainer.IsConnected) { 
    xTaskCreate(&TaskWriteWithResponse, "Write w Response", 2048, (void *)NULL, 1, &TaskWriteWithResponseHandle); 
  } 
  memset(server_FTM_Control_Point_Data.bytes, 0, sizeof(server_FTM_Control_Point_Data.bytes));

Change the "stack depth" value from 2048 in 4096 ; we just double it and see if that is helping us out.....

Lets add some extra debug messages (uncomment these defines again, to activate):

#define DEBUG_FTM_CONTROLPOINT_RESPONSE // If defined, allows to analyze the data
#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA // If defined, allows to analyze and decode the data

Run the program and fingers crossed!

@le-joebar
Copy link

Hallo Jorgen,
Ik kom uit Brussel maar woon nu in Charleroi.
Het is 60 km verder naar het zuiden.
Met de auto van Charleroi naar Utrecht is het 2 u 26 min (233,7 km) via E19 en A27.

I will try your suggestion tomorrow!

Vriendelijk

Joel

@le-joebar
Copy link

Hello Jorgen,

Here are the results with esp32_FTMS_Zwift_Bridge_v010

change as follows:

#define DEBUG_FTM_CONTROLPOINT_RESPONSE // If defined, allows to analyze the data
#define DEBUG_FTM_CONTROLPOINT_OPCODE_DATA // If defined, allows to analyze and decode the data

++++

if(Trainer.IsConnected) {
xTaskCreate(&TaskWriteWithResponse, "Write w Response", 4096, (void *)NULL, 1, &TaskWriteWithResponseHandle);
}
memset(server_FTM_Control_Point_Data.bytes, 0, sizeof(server_FTM_Control_Point_Data.bytes));

+++++

->writeValue(ftmcpData, false)) } ???????????????????????????????

Main debug level: "Debug"

Baud rate 115200

:

6:29:55.383 -> D NimBLECharacteristicCallbacks: onStatus: default
16:29:55.419 -> D NimBLECharacteristic: << notify
16:29:55.419 -> D NimBLEServer: >> handleGapEvent:
16:29:55.419 -> I NimBLEServer: subscribe event; attr_handle=62, subscribed: false
16:29:55.419 -> I NimBLECharacteristic: New subscribe value for conn: 1 val: 2
16:29:55.419 -> Central Updated CCCD --> Indicate Enabled for Char: [0x2ad9]
16:29:55.594 -> D NimBLEClient: Got Client event
16:29:55.594 -> D NimBLEClient: Peer requesting to update connection parameters
16:29:55.594 -> D NimBLEClient: MinInterval: 72, MaxInterval: 120, Latency: 0, Timeout: 400
16:29:55.594 -> D NimBLEClientCallbacks: onConnParamsUpdateRequest: default
16:29:55.637 -> D NimBLEClient: Accepted peer params
16:29:55.946 -> D NimBLEServer: >> handleGapEvent:
16:29:55.946 -> I NimBLEServer: subscribe event; attr_handle=49, subscribed: true
16:29:55.946 -> I NimBLECharacteristic: New subscribe value for conn: 1 val: 1
16:29:55.946 -> Central Updated CCCD --> Notify Enabled for Char: [0x2ada]
16:29:56.024 -> D NimBLEClient: Got Client event
16:29:56.024 -> I NimBLEClient: Connection parameters updated.
16:29:56.070 -> D NimBLECharacteristic: Characteristic 0x2ada Read event
16:29:56.070 -> D NimBLECharacteristicCallbacks: onRead: default
16:29:56.070 -> D NimBLECharacteristicCallbacks: onRead: default
16:29:56.141 -> D NimBLEClient: Got Client event
16:29:56.141 -> D NimBLEClient: Notify Recieved for handle: 16
16:29:56.141 -> D NimBLEClient: checking service 0x180a for handle: 16
16:29:56.141 -> D NimBLEClient: checking service 0x1818 for handle: 16
16:29:56.141 -> D NimBLEClient: checking service 0x1816 for handle: 16
16:29:56.141 -> D NimBLEClient: checking service 0x1826 for handle: 16
16:29:56.141 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:29:56.174 -> Descriptor: uuid: 0x2902, handle: 17
16:29:56.174 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad2, handle: 16 0x0010, props: 0x10
16:29:56.174 -> Descriptor: uuid: 0x2902, handle: 17
16:29:56.174 -> D NimBLECharacteristic: >> setValue: length=11, data=6402000000001400000000, characteristic UUID=0x2ad2
16:29:56.174 -> D NimBLECharacteristic: << setValue
16:29:56.209 -> D NimBLECharacteristic: >> notify: length: 11
16:29:56.209 -> D NimBLECharacteristicCallbacks: onNotify: default
16:29:56.209 -> D NimBLEServer: >> handleGapEvent:
16:29:56.209 -> D NimBLECharacteristicCallbacks: onStatus: default
16:29:56.209 -> D NimBLECharacteristic: << notify
16:29:56.369 -> D NimBLEClient: Got Client event
16:29:56.369 -> D NimBLEClient: Notify Recieved for handle: 43
16:29:56.369 -> D NimBLEClient: checking service 0x180a for handle: 43
16:29:56.369 -> D NimBLEClient: checking service 0x1818 for handle: 43
16:29:56.369 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:29:56.403 -> Descriptor: uuid: 0x2902, handle: 44
16:29:56.403 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a63, handle: 43 0x002b, props: 0x10
16:29:56.403 -> Descriptor: uuid: 0x2902, handle: 44
16:29:56.403 -> D NimBLECharacteristic: >> setValue: length=4, data=00000000, characteristic UUID=0x2a63
16:29:56.403 -> D NimBLECharacteristic: << setValue
16:29:56.436 -> D NimBLECharacteristic: >> notify: length: 4
16:29:56.436 -> D NimBLECharacteristicCallbacks: onNotify: default
16:29:56.436 -> D NimBLEServer: >> handleGapEvent:
16:29:56.436 -> D NimBLECharacteristicCallbacks: onStatus: default
16:29:56.436 -> D NimBLECharacteristic: << notify
16:29:56.436 -> D NimBLECharacteristic: Characteristic 0x2ad9 Write event
16:29:56.436 -> D NimBLECharacteristic: >> setValue: length=1, data=00, characteristic UUID=0x2ad9
16:29:56.436 -> D NimBLECharacteristic: << setValue
16:29:56.473 -> -->D NimBLERemoteCharacteristic: >> writeValue(), length: 1
16:29:56.473 -> Raw FTM Control Point Data [len: 1] [OpCode: 00] [Values: 00 ]
16:29:56.473 -> Request Control of Machine!
16:29:56.473 -> D NimBLECharacteristicCallbacks: onWrite: default
16:29:56.741 -> I NimBLERemoteCharacteristic: Write complete; status=0 conn_handle=0
16:29:56.741 -> D NimBLERemoteCharacteristic: << writeValue, rc: 0
16:29:56.741 -> D NimBLEClient: Got Client event
16:29:56.741 -> D NimBLEClient: Notify Recieved for handle: 28
16:29:56.741 -> D NimBLEClient: checking service 0x180a for handle: 28
16:29:56.741 -> D NimBLEClient: checking service 0x1818 for handle: 28
16:29:56.741 -> D NimBLEClient: checking service 0x1816 for handle: 28
16:29:56.741 -> D NimBLEClient: checking service 0x1826 for handle: 28
16:29:56.774 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2ad9, handle: 28 0x001c, props: 0x28
16:29:56.774 -> Descriptor: uuid: 0x2902, handle: 29
16:29:56.774 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2ad9, handle: 28 0x001c, props: 0x28
16:29:56.774 -> Descriptor: uuid: 0x2902, handle: 29
16:29:56.774 -> D NimBLECharacteristic: >> setValue: length=3, data=800001, characteristic UUID=0x2ad9
16:29:56.808 -> D NimBLECharacteristic: << setValue
16:29:56.808 -> D NimBLECharacteristic: >> notify: length: 3
16:29:56.808 -> D NimBLECharacteristicCallbacks: onNotify: default
16:29:56.808 -> Guru Meditation Error: Core 0 panic'ed (Unhandled debug exception).
16:29:56.808 -> Debug exception reason: Stack canary watchpoint triggered (CP Indicate)
16:29:56.808 -> Core 0 register dump:
16:29:56.808 -> PC : 0x40096c93 PS : 0x00060d36 A0 : 0x80094a2d A1 : 0x3ffcfdf0
16:29:56.841 -> A2 : 0x3ffce144 A3 : 0xb33fffff A4 : 0x0000abab A5 : 0x00060d23
16:29:56.841 -> A6 : 0x00060d20 A7 : 0x0000cdcd A8 : 0xb33fffff A9 : 0xffffffff
16:29:56.841 -> A10 : 0x00000003 A11 : 0x00060d23 A12 : 0x00060d20 A13 : 0x00000024
16:29:56.841 -> A14 : 0x007ce144 A15 : 0x003fffff SAR : 0x00000008 EXCCAUSE: 0x00000001
16:29:56.874 -> EXCVADDR: 0x00000000 LBEG : 0x40091b11 LEND : 0x40091b21 LCOUNT : 0xfffffffc
16:29:56.874 ->
16:29:56.874 ->
16:29:56.874 -> Backtrace:0x40096c90:0x3ffcfdf00x40094a2a:0x3ffcfe30 0x40094af4:0x3ffcfe70 0x40085fbd:0x3ffcfe90 0x400860c9:0x3ffcfec0 0x400f872a:0x3ffcfee0 0x400f79cf:0x3ffcff00 0x400f763d:0x3ffcff20 0x40091856:0x3ffcff40 0x40090b0d:0x3ffcff60 0x40090b9c:0x3ffcff80 0x400910e7:0x3ffcffb0 0x4012d6fe:0x3ffcffe0 0x40125fba:0x3ffd0000 0x4011b26a:0x3ffd0310 0x400da8f5:0x3ffd0360 0x400df371:0x3ffd03d0 0x400df3bb:0x3ffd03f0 0x400e0a0b:0x3ffd0410 0x400e220f:0x3ffd0470 0x400d6575:0x3ffd0490 0x400d65d1:0x3ffd0520 0x400d65e1:0x3ffd0540 0x400d2371:0x3ffd0560
16:29:56.907 ->
16:29:56.907 ->
16:29:56.907 ->
16:29:56.907 ->
16:29:56.907 -> ELF file SHA256: 0000000000000000
16:29:56.907 ->
16:29:56.907 -> Rebooting...
16:29:56.907 -> ets Jul 29 2019 12:21:46
16:29:56.952 ->
16:29:56.952 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:29:56.952 -> configsip: 271414342, SPIWP:0xee
16:29:56.952 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:29:56.952 -> mode:DIO, clock div:1
16:29:56.952 -> load:0x3fff0030,len:1184
16:29:56.952 -> load:0x40078000,len:13160
16:29:56.952 -> load:0x40080400,len:3036
16:29:56.952 -> entry 0x400805e4
16:29:57.169 -> [ 4][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
16:29:57.435 -> [ 229][I][esp32-hal-psram.c:96] psramInit(): PSRAM enabled
16:29:57.435 -> ESP32 NimBLE MITM supporting: CPS, CSC, HBM and FTMS
16:29:57.435 -> ------------------ Version 01.0 --------------------
16:29:57.689 -> I NimBLEDevice: BLE Host Task Started

@le-joebar
Copy link

It's really and only when you're on this page!! What info does Zwift send to the trainer that it doesn't when it's on the previous page?

image

And after he rebooted the trainer properly resumes the connection with Zwift but as he is on this page he reboots !!

Once you have chosen your course then it's good time that it does not reboot! sometimes 20 minutes sometimes more sometimes less!

@Berg0162
Copy link
Owner Author

Berg0162 commented Apr 10, 2023

Stuur even een antwoord via e-mail naar mijn email dan gaan we zo verder...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants