Developing a CIQ BLE Client for Treadmill and Fitness Equipment

I want to run on my smart treadmill and get EXACT speed and grade information via BLE.  Previously, I created an Arduino ESP-32 solution running as a BLE server that runs on the treadmill.  This server works well with Zwift etc, so I know it works.  Now the plan is to create a CLIENT on my fenix-5.  Eventually, I want to save and thereafter load my treadmill runs into Strava.  In the far away future, I would like to control my treadmill from the fenix-5 (using watch buttons to speed-up, tilt up etc) .  In the far,far,far away future, I would like to automatically adjust treadmill speed and grade based on complicated pre-planned-workouts (intervals, hill repeat etc).

REFERENCE INFO

GATT SERVICES

Plan to read data from:

  • Fitness Machine Service UUID: 0x1826
    • Fitness Machine Feature Characteristic 0x2acc 
    • Treadmill Data Characteristic 0x2acd
      • 2902 Enable Notify Descriptor 0x2902
        • This is required so client gets more than one data point.
    • Supported Speed Range Characteristic 0x2ad4
    • Supported Incline Range Characteristic 0x2ad5
  • Keep Alive UUID

Long Form "standard" UUIDs take the form of 

xxxxxxxx-0000-1000-8000-00805F9B34FB
where xxxxxxxx is the zero-padded short-form uuid (see above)
E.G. Fitness Machine Service long UUID would be 00001826-0000-1000-8000-00805F9B34FB

  • In the API, ActivityRecording.CreateSession has an option called 

    :nativeNum (Toybox::Lang::Number) — 

    If this Field can be treated equivalently to a Field that is included in the FIT SDK use this to indicate the Field Number that is specified by the FIT Profile.

    Could this be used to replace the native Speed with the Treadmill's speed??

  • Garmin connect ignores that.  Some 3rd party sites do use it but off hand, I don't recall which.

  • Is there some sort of delegate that gets called upon completion of a Toybox.ActivityRecording.Session.save() operation?  How can our applications know if an activity was saved (or not)?  The regular applications (like Swim) are able to bring-up a "Done" screen after saving.  How do they do this?

  • I just have the app display a different screen after the save() is called. and allow "back" to exit the app at that time.

  • In reading documentation and examples, i can only see profiles defined with a single service. Is it possible to define a profile with more than one service in CIQ? For example if i wanted to have 4 services in one profile. Since there is a limit of 3 profiles, it cannot be done with 1 profile per service. Documentation for registerProfile seems to suggest it is expecting a single service UUID. And then :characteristics.

    • profile (Toybox::Lang::Dictionary)

      Profile Definition. Defines the expected Profile UUID, Profile Characteristics and Characteristic Descriptors. Cannot be null.

  • That’s what I get too, but you can have characteristics that would otherwise belong to a different service included within your single service.   E.G.  I can get the battery level characteristic from my running cadence sensor. Or a Heart rate characteristic from my treadmill.  

  • You can have up to 3 services.  You just make 3 different profiles.  If you try more than 3, it will throw an exception.

    Here's what I have in my version of the nordic thingy52 app where I access some of the services other than environmental.  

        function registerProfiles() {
    try {      
            Ble.registerProfile( _envProfileDef );
            Ble.registerProfile( _uiProfileDef );     
            //Ble.registerProfile( _motProfileDef );
            //Ble.registerProfile( _spkProfileDef );        
            Ble.registerProfile( _batProfileDef );
    } catch(e) {
    System.println("e="+e.getErrorMessage());
    }
        }

    From the API doc:

    Registers a Bluetooth Profile Definition

    Call this function to define all of the Profiles that will be used in the application. Only registered characteristics and descriptors will be available when performing GATT operations

    When the operation is completed BleDelegate#onProfileRegister() will be called on the registered delegate with the UUID and a Status

    Registration can fail if too many profiles are registered, the current limit is 3.

  • This is why I believe Garmin got their terminology a little mixed up-- by definition, a profile should have multiple services.  Garmin's documentation is a little confusing when they say a maximum of three profiles.  What they should say is a maximum of three allowable services.  This makes their terminology mesh a little better with other developers.  . Here's how the rest of the universe views profiles and services.

  • Hi Chirs, it's interesting, how is it now? And why not try Ant+?

    Well I'm just having the same problem, difference is mine is a rower machine, and I went the Ant+ way.

    What I have done is, using a raspberry pi 0, with a ant+ usb dongle (less than $10, the cheapest will work, they are all the same chip), the raspberry Pi reads the rower headunit from the usb, and broadcast the data as a Ant+ FE signal, so I get the exact distance and power.

    The same as you, to get the data displayed and recorded, I have to make a "Client" on my watch (Fenix 3), it turns out to be a DataField that receives the Ant+ FE signal, and displays all the informations received. And I just submitted it into the store today to get all the charts...

    I found your thread because now I want to add BLE support to it, the bluetooth on Pi 0W is capable to do so, so I can use it on Zwift (actually I don't use zwift, it's just to make my little product "complete").

    In my opinion, the Ant+ way is much much easier than BLE, ant+ protocol is something really simple, encode and decode is just a one single function with a few if-else, it needs some time to understand the protocol, but it's really simple and elegant, and it's well supported by the watch.

    If you goes this way, it could save you a lot of time without getting into the hardware dev kit things... But that's the fun part, isn't it?

  • Hi Jim, amazing guide. Thank you! Pray

    Is there an update for the new s132_7.0.1 (dongle) or for the nRF52 DK?

    P.S.: How were these firmware files generated? (connectivity_1.0.0_usb_with_s132_5.1.0.hex and connectivity_2.0.1_115k2_with_s132_5.0.hex)