BLE notification not appearing for FTMS BLE

Trying to implement FTMS BLE communication, I have come to a dead end.

Not sure whether there are particularities about FTMS or if it due to some special case my indoor trainer uses (like the 0xFFF0 service seen below).

So far, I have implemented an app that communicates with another piece of BLE equipment. Notifications appear as expected.

But, when trying to communicate with my trainer, only reads and writes succeed (actually even the CCCD writes end up in the corresponding handler, so I think they work too.

What I did to try to sort this out was to try the same thing with the NordicRF (nRF Connect) app, which basically shows the exact same behaviour.

As a last resort I also tried to connect using the trainer supplied "iConsole" app (which is garbage), while paying attention to the nRF app.

Behold! Notifications do appear (log appended at the end).

So, obviously there is a way of getting this to work.

Anybody have an idea what to try?

Note that I have also tried a bunch of other apps on Google Play. Most of which connect to the trainer but none get any data (probably no notifications)

iConsole, zwift companion and RGT seem to know how to do this but I don't know how to intercept the traffic.

Could it be that the MTU is too small to carry the notification data? Something should still appear, right?

Thanks for any help

/Ola

nRF Connect, 2022-01-09
iConsole+0203 (CB:CC:E1:B0:A2:8C)
D    22:11:51.034    [Broadcast] Action received: android.bluetooth.device.action.ACL_CONNECTED
V    22:11:51.850    Connecting to CB:CC:E1:B0:A2:8C...
D    22:11:51.850    gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, opportunistic = true, preferred PHY = LE 1M) (hidden)
D    22:11:51.913    [Callback] Connection state changed with status: 0 and new state: CONNECTED (2)
I    22:11:51.913    Connected to CB:CC:E1:B0:A2:8C
V    22:11:51.927    Discovering services...
D    22:11:51.927    gatt.discoverServices()
D    22:11:51.960    [Callback] Services discovered with status: 0
I    22:11:51.960    Services discovered
V    22:11:51.988    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)
Unknown Service (0000fff0-0000-1000-8000-00805f9b34fb)
- Unknown Characteristic [W WNR] (0000fff2-0000-1000-8000-00805f9b34fb)
- Unknown Characteristic [N] (0000fff1-0000-1000-8000-00805f9b34fb)
   Client Characteristic Configuration (0x2902)
Fitness Machine (0x1826)
- Fitness Machine Feature [R] (0x2ACC)
- Supported Speed Range [R] (0x2AD4)
- Supported Inclination Range [R] (0x2AD5)
- Supported Resistance Level Range [R] (0x2AD6)
- Supported Heart Rate Range [R] (0x2AD7)
- Supported Power Range [R] (0x2AD8)
- Indoor Bike Data [N W] (0x2AD2)
   Client Characteristic Configuration (0x2902)
- Training Status [N R W] (0x2AD3)
   Client Characteristic Configuration (0x2902)
- Fitness Machine Control Point [N W] (0x2AD9)
   Client Characteristic Configuration (0x2902)
- Fitness Machine Status [N W] (0x2ADA)
   Client Characteristic Configuration (0x2902)
Device Information (0x180A)
- Manufacturer Name String [R] (0x2A29)
- Model Number String [R] (0x2A24)
- Hardware Revision String [R] (0x2A27)
- Firmware Revision String [R] (0x2A26)
Secure DFU Service (0xFE59)
- Buttonless DFU [I W] (8ec90003-f315-4f60-9fb8-838830daea50)
   Client Characteristic Configuration (0x2902)
D    22:11:51.989    gatt.setCharacteristicNotification(00002a05-0000-1000-800000805f9b34fb, true)
D    22:11:51.991    gatt.setCharacteristicNotification(0000fff1-0000-1000-8000-00805f9b34fb, true)
D    22:11:51.992    gatt.setCharacteristicNotification(00002ad2-0000-1000-8000-00805f9b34fb, true)
D    22:11:51.993    gatt.setCharacteristicNotification(00002ad3-0000-1000-8000-00805f9b34fb, true)
D    22:11:51.993    gatt.setCharacteristicNotification(00002ad9-0000-1000-8000-00805f9b34fb, true)
D    22:11:51.995    gatt.setCharacteristicNotification(00002ada-0000-1000-8000-00805f9b34fb, true)
I    22:11:52.825    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:11:52.825    "(0x) F0-B0-01-01-A2" received
I    22:11:53.303    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:11:53.303    "(0x) F0-B1-01-01-21-C4" received
I    22:11:54.233    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:11:54.233    "(0x) F0-B0-01-01-A2" received
I    22:11:54.713    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:11:54.713    "(0x) F0-B1-01-01-21-C4" received
I    22:11:55.734    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:11:55.734    "(0x) F0-B0-01-01-A2" received
I    22:11:56.304    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:11:56.304    "(0x) F0-B1-01-01-21-C4" received
I    22:11:57.323    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:11:57.323    "(0x) F0-B0-01-01-A2" received
I    22:11:57.713    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:11:57.713    "(0x) F0-B1-01-01-21-C4" received
I    22:11:58.825    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:11:58.825    "(0x) F0-B0-01-01-A2" received
I    22:11:59.212    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:11:59.212    "(0x) F0-B1-01-01-21-C4" received
I    22:12:00.322    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:12:00.322    "(0x) F0-B0-01-01-A2" received
I    22:12:00.802    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21-C4
A    22:12:00.802    "(0x) F0-B1-01-01-21-C4" received
I    22:12:01.703    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B0-01-01-A2
A    22:12:01.703    "(0x) F0-B0-01-01-A2" received
I    22:12:02.302    Notification received from 0000fff1-0000-1000-8000-00805f9b34fb, value: (0x) F0-B1-01-01-21

  • Actually, this line got me thinking.

    With CIQ, it doesn't discover services or characteristics, so every thing your try to use needs to be registered in the profile in the CIQ app.

    If you try to read a characteristic, does that work, and notify fails?

  • This particular characteristic is not possible to Read. It only supports the Notification method if I understand correctly.

    I did manage to read other Readable characteristics though.

  • What is the code you are using to turn on notify on the CIQ side?

  • Basically what I picked up from the samples:

        function onConnectedStateChanged( device, state )
        {
            if (state == Ble.CONNECTION_STATE_CONNECTED)
            {
                System.println("BleDelegate.onConnectedStateChanged");
                var service = device.getService(FITNESS_MACHINE_SERVICE ); // 0x1826

                var characteristic = service.getCharacteristic(BIKE_DATA_CHARACTERISTIC); // 0x2ad2
                var cccd = characteristic.getDescriptor(Ble.cccdUuid());

                cccd.requestWrite([0x01, 0x00]b);

           }

      }

    ...

    And then I end up in the onDescriptorWrite event, telling me the write supposedly went well.

    When exchangin 0x1826/0x2ad2 with service/characteristic ID of my HRM, I get notifications from it, telling me there is nothing wrong with my code.

  • Just to make sure, with the characteristic you're trying to turn on notify for, it's in the profile you have configured on the CIQ side, correct?  It's got to be defined there or your app won't see it.

    Here's the profile stuff I use with a heart monitor:  It's pretty much the whole profile manager

    class HrPM {
        var HR_SERVICE = Ble.longToUuid(0x0000180D00001000L, 0x800000805F9B34FBL);
    	var HR_CHAR    = Ble.longToUuid(0x00002a3700001000L, 0x800000805F9B34FBL);    
    
        private var hrProfileDef = {
            :uuid => HR_SERVICE,
            :characteristics => [{
                :uuid => HR_CHAR,
                :descriptors => [Ble.cccdUuid()]
            }]
        };
           
    
        function registerProfiles() {
    		try {      
            	Ble.registerProfile( hrProfileDef );
    		} catch(e) {
    			System.println("e="+e.getErrorMessage());
    		}
        }
    }

  • Yes correct. The profile definition uses the FITNESS_MACHINE_SERVICE/BIKE_DATA_CHARACTERISTIC and when changing to HRM, I update the defines. Thanks for clearing that out.

    My full profile definition:
        private const fitnessProfileDef =
        {
            :uuid => FITNESS_MACHINE_SERVICE,                
            :characteristics => [
            {
                :uuid => BIKE_DATA_CHARACTERISTIC,:descriptors => [Ble.cccdUuid()]
            },
            {
                :uuid => FITNESS_MACHINE_FEATURE_CHARACTERISTIC                
                
            },
            {
                :uuid => BIKE_CONTROL_POINT                
                
            }]
        };

  • Add :descriptors after the other :uuid's and give it a try.

  • No difference in result, alas. I did not think it mattered since I hadn't gotten to those characteristics yet.

    I am thinking the problem might be related to there just not being any notifications to send at the moment.

    Maybe I need to request something first? I tried pedalling for a bit but that didn't affect anything. Maybe I need to write something else to a characteristic:

    The nRF log seems to indicate that the "top" characteristic is "W"ritable, in addition to "N"otifiable:
    - Indoor Bike Data [N W] (0x2AD2)

    Maybe I need to write something to kick it into action?

  • FTMS spec says:
    When the Indoor Bike Data characteristic is configured for notification via the Client Characteristic Configuration descriptor and training-related data is available, this characteristic shall be notified.

  • Once you have notifications enabled, do you have to do something on the bike or by way of writing a characteristic to actually "start training", as which time it will start doing the notifies?