Closed gordonel closed 3 years ago
I'm not much of a dev myself, but I'll be happy to help troubleshoot this if you tell me how
Can you post a screenshot showing the action to "open" the sensor via settings? The one's that I have are either on or off, but no further action (and even pairing is required).
And is this issue happening in the settings (sensor selection dialog) or while recording (i.e., no connection to sensor and thus no data)?
@dennisguse I'll record a screengrab once I have a minute.
The problem is that I have to open the sensor in another app for it to work at all. I can pair the sensor just fine in OpenTracks, but unless I send the "open" signal to it via IpSensorMan, it won't work
@dennisguse okay, here's the recording.
From what I gather, IpSensorMan runs some sort of a service that makes the sensor discoverable and allows many apps to access it simultaneously. What's weird is that I tried a lot of apps and IpSensorMan is the only one that can talk to the sensor and make the other apps see it. Normally, I'd just connect the sensor to my device and be done with it, but here, well, I have to dance with a tambourine to make it work, basically
i guess, the sensor needs some (non-standard) initial setup before it sends data (send a message to the sensor). Gadgetbridge is doing something similar to get HR data from some smartwatches (or you have to start a workout from the smartwatch even).
You may check with with either IpSensorMan or the manufacturer of this HR sensor. To be honest: it is probably not worth the time to implement this as you have a (not cool) working solution.
So normally, sensors just broadcast the data and OpenTracks doesn't do any set up/ping/whatever?
@gordonel Actually no.
After connecting and discovering service, we subscribe for updates on this specific characteristics by writing a specific value there. The characteristics is the "variable" (aka data) we would like to get informed about (different one per sensor type). One could also just get the data from characteristics (pull style), but that would be a waste of energy.
The responsible code is here: https://github.com/OpenTracksApp/OpenTracks/blob/9a7f24bddcbbce9ebe2e703c9c64350c9aed0a0f/src/main/java/de/dennisguse/opentracks/services/sensors/BluetoothConnectionManager.java#L91
And here are the addresses: https://github.com/OpenTracksApp/OpenTracks/blob/9a7f24bddcbbce9ebe2e703c9c64350c9aed0a0f/src/main/java/de/dennisguse/opentracks/util/BluetoothUtils.java#L40
Technically pull is possible, but it is not BLE style. And the effort within OpenTracks is considerable as we need to handle mulit-hreading for this.
And the issue might be that the sensor is not reporting any data without some specific treatment. Or only the subscription needs to be done differently.
... so pull might not even fix the problem.
Okay, so to stop speculating and figure out how to make OpenTracks work, I need to figure out why this HRM needs activation in the first place, I guess. I'll continue researching and present my findings
Okay. I think I figured it what IpSensorMan does using nRF Connect and got a bpm readout. The trouble is I can't find what's exactly missing in the code as it has all the UUID and settings needed to retrieve data. Do you see something I'm missing?
EDIT: I also found out that after I do this, OpenTracks starts seeing the sensor too, so I no longer have to use a proprietary app 🥳
I tried grabbing logs from the app via logcat and grepping them afterwards to search for bluetooth-specific lines and this is what I found. Let me know if you need more context, but what I did in this session was:
05-20 14:43:52.704 10595 10595 D BluetoothAdapter: isLeEnabled(): ON
05-20 14:43:52.707 10595 10620 D BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=6 mScannerId=0
05-20 14:43:53.111 10595 10595 D BluetoothLeSensorPreference: Found device PanoBike BLE HRM ScanResult{device=90:59:AF:1F:77:20, scanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=[0000180d-0000-1000-8000-00805f9b34fb, 0000180f-0000-1000-8000-00805f9b34fb], mServiceSolicitationUuids=[], mManufacturerSpecificData={}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=PanoBike BLE HRM], rssi=-65, timestampNanos=1094237274473, eventType=27, primaryPhy=1, secondaryPhy=0, advertisingSid=255, txPower=127, periodicAdvertisingInterval=0}
05-20 14:43:53.891 10595 10595 D BluetoothAdapter: isLeEnabled(): ON
05-20 14:43:57.181 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
05-20 14:43:57.181 10595 10595 I BluetoothRemoteSensorManager: Connecting to bluetooth address: 90:59:AF:1F:77:20
05-20 14:43:57.181 10595 10595 D BluetoothConnectionManager: Connecting to: 90:59:AF:1F:77:20
05-20 14:43:57.181 10595 10595 D BluetoothGatt: connect() - device: 90:59:AF:1F:77:20, auto: true
05-20 14:43:57.181 10595 10595 D BluetoothGatt: registerApp()
05-20 14:43:57.181 10595 10595 D BluetoothGatt: registerApp() - UUID=62315931-31b5-45c4-94bb-4bd42b7dbbc0
05-20 14:43:57.182 10595 10595 W BluetoothRemoteSensorManager: No Bluetooth address.
05-20 14:43:57.182 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
05-20 14:43:57.182 10595 10595 W BluetoothRemoteSensorManager: No Bluetooth address.
05-20 14:43:57.182 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
05-20 14:43:57.182 10595 10595 W BluetoothRemoteSensorManager: No Bluetooth address.
05-20 14:43:57.182 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
05-20 14:43:57.183 10595 10620 D BluetoothGatt: onClientRegistered() - status=0 clientIf=6
05-20 14:44:39.272 10595 10620 D BluetoothGatt: onClientConnectionState() - status=0 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:44:39.272 10595 10620 D BluetoothConnectionManager: Connected to sensor: 90:59:AF:1F:77:20
05-20 14:44:39.273 10595 10620 D BluetoothGatt: discoverServices() - device: 90:59:AF:1F:77:20
05-20 14:44:39.717 10595 10620 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=6 latency=0 timeout=500 status=0
05-20 14:44:40.021 10595 10620 D BluetoothGatt: onSearchComplete() = Device=90:59:AF:1F:77:20 Status=0
05-20 14:44:40.022 10595 10620 D BluetoothGatt: setCharacteristicNotification() - uuid: 00002a37-0000-1000-8000-00805f9b34fb enable: true
05-20 14:44:41.050 10595 10620 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=960 latency=0 timeout=600 status=0
05-20 14:44:50.095 10595 10620 D BluetoothGatt: onClientConnectionState() - status=8 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:44:50.095 10595 10620 D BluetoothConnectionManager: Disconnected from sensor: 90:59:AF:1F:77:20
05-20 14:45:36.472 10595 10620 D BluetoothGatt: onClientConnectionState() - status=0 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:45:36.472 10595 10620 D BluetoothConnectionManager: Connected to sensor: 90:59:AF:1F:77:20
05-20 14:45:36.472 10595 10620 D BluetoothGatt: discoverServices() - device: 90:59:AF:1F:77:20
05-20 14:45:36.912 10595 10620 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=6 latency=0 timeout=500 status=0
05-20 14:45:37.566 10595 10620 D BluetoothGatt: onSearchComplete() = Device=90:59:AF:1F:77:20 Status=0
05-20 14:45:37.566 10595 10620 D BluetoothGatt: setCharacteristicNotification() - uuid: 00002a37-0000-1000-8000-00805f9b34fb enable: true
05-20 14:45:38.649 10595 10620 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=960 latency=0 timeout=600 status=0
05-20 14:45:47.693 10595 10620 D BluetoothGatt: onClientConnectionState() - status=8 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:45:47.693 10595 10620 D BluetoothConnectionManager: Disconnected from sensor: 90:59:AF:1F:77:20
05-20 14:46:00.809 10595 10805 D BluetoothGatt: onClientConnectionState() - status=0 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:46:00.809 10595 10805 D BluetoothConnectionManager: Connected to sensor: 90:59:AF:1F:77:20
05-20 14:46:00.809 10595 10805 D BluetoothGatt: discoverServices() - device: 90:59:AF:1F:77:20
05-20 14:46:01.253 10595 10805 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=6 latency=0 timeout=500 status=0
05-20 14:46:01.587 10595 10805 D BluetoothGatt: onSearchComplete() = Device=90:59:AF:1F:77:20 Status=0
05-20 14:46:01.587 10595 10805 D BluetoothGatt: setCharacteristicNotification() - uuid: 00002a37-0000-1000-8000-00805f9b34fb enable: true
05-20 14:46:02.045 10595 10595 D BluetoothGatt: close()
05-20 14:46:02.045 10595 10595 D BluetoothGatt: unregisterApp() - mClientIf=6
05-20 14:46:02.046 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
05-20 14:46:02.046 10595 10595 W BluetoothConnectionManager: Cannot disconnect if not connected.
This is the important part:
05-20 14:45:37.566 10595 10620 D BluetoothGatt: setCharacteristicNotification() - uuid: 00002a37-0000-1000-8000-00805f9b34fb enable: true
05-20 14:45:38.649 10595 10620 D BluetoothGatt: onConnectionUpdated() - Device=90:59:AF:1F:77:20 interval=960 latency=0 timeout=600 status=0
05-20 14:45:47.693 10595 10620 D BluetoothGatt: onClientConnectionState() - status=8 clientIf=6 device=90:59:AF:1F:77:20
05-20 14:45:47.693 10595 10620 D BluetoothConnectionManager: Disconnected from sensor: 90:59:AF:1F:77:20
We are connected and try to write the "please notify" field of the characteristics and then we are disconnected after about 10s. This should not happen... do you have another Android device to test with? And Bluetooth snooping might reveal something.
No, but I can try bluetooth snooping. As a mitigation, would it be possible to bump the timeout to something like 360 seconds and log how much it actually takes? I'll try to do it on my own tomorrow 'cause I got to do some work, but if you can make a branch I could build off of faster, that would be greatly appreciated 🙏🏻
EDIT: just re-tried subscribing via nRF Connect and it doesn't take more than 3 seconds to get a reading
Couldn't find a hardcoded timeout anywhere here, but I did try on another Pixel and had no luck on it either
I should clarify that I'm using an F-Droid version, not PlayStore version. I'll try building on my own to see if there's something funny going on with the F-Droid build
UPD: built 3.18.1-debug from source, same thing, no luck
Did some bluetooth snooping, but couldn't find any HRM-related errors or anything. Here's the write request that should subscribe us
Frame 451: 14 bytes on wire (112 bits), 14 bytes captured (112 bits)
Encapsulation type: Bluetooth H4 with linux header (99)
Arrival Time: May 21, 2021 15:10:23.439703000 EEST
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1621599023.439703000 seconds
[Time delta from previous captured frame: 0.006631000 seconds]
[Time delta from previous displayed frame: 0.006631000 seconds]
[Time since reference or first frame: 244.770882000 seconds]
Frame Number: 451
Frame Length: 14 bytes (112 bits)
Capture Length: 14 bytes (112 bits)
[Frame is marked: False]
[Frame is ignored: False]
Point-to-Point Direction: Sent (0)
[Protocols in frame: bluetooth:hci_h4:bthci_acl:btl2cap:btatt]
Bluetooth
[Source: REDACTED]
[Destination: TexasIns_1f:77:20 (90:59:af:1f:77:20)]
Bluetooth HCI H4
[Direction: Sent (0x00)]
HCI Packet Type: ACL Data (0x02)
Bluetooth HCI ACL Packet
.... 0000 0000 0011 = Connection Handle: 0x003
..00 .... .... .... = PB Flag: First Non-automatically Flushable Packet (0)
00.. .... .... .... = BC Flag: Point-To-Point (0)
Data Total Length: 9
Data
[Connect in frame: 365]
[Disconnect in frame: 456]
[Source BD_ADDR: REDACTED]
[Source Device Name: REDACTED]
[Source Role: Unknown (0)]
[Destination BD_ADDR: TexasIns_1f:77:20 (90:59:af:1f:77:20)]
[Destination Device Name: PanoBike BLE HRM]
[Destination Role: Unknown (0)]
[Current Mode: Unknown (-1)]
Bluetooth L2CAP Protocol
Length: 5
CID: Attribute Protocol (0x0004)
Bluetooth Attribute Protocol
Opcode: Write Request (0x12)
0... .... = Authentication Signature: False
.0.. .... = Command: False
..01 0010 = Method: Write Request (0x12)
Handle: 0x0013 (Heart Rate: Heart Rate Measurement: Client Characteristic Configuration)
[Service UUID: Heart Rate (0x180d)]
[Characteristic UUID: Heart Rate Measurement (0x2a37)]
[UUID: Client Characteristic Configuration (0x2902)]
Characteristic Configuration Client: 0x0001, Notification
0000 0000 0000 00.. = Reseved: 0x0000
.... .... .... ..0. = Indication: False
.... .... .... ...1 = Notification: True
And here's a write response:
Frame 453: 10 bytes on wire (80 bits), 10 bytes captured (80 bits)
Encapsulation type: Bluetooth H4 with linux header (99)
Arrival Time: May 21, 2021 15:10:23.457462000 EEST
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1621599023.457462000 seconds
[Time delta from previous captured frame: 0.009205000 seconds]
[Time delta from previous displayed frame: 0.009205000 seconds]
[Time since reference or first frame: 244.788641000 seconds]
Frame Number: 453
Frame Length: 10 bytes (80 bits)
Capture Length: 10 bytes (80 bits)
[Frame is marked: False]
[Frame is ignored: False]
Point-to-Point Direction: Received (1)
[Protocols in frame: bluetooth:hci_h4:bthci_acl:btl2cap:btatt]
Bluetooth
[Source: TexasIns_1f:77:20 (90:59:af:1f:77:20)]
[Destination: REDACTED]
Bluetooth HCI H4
[Direction: Rcvd (0x01)]
HCI Packet Type: ACL Data (0x02)
Bluetooth HCI ACL Packet
.... 0000 0000 0011 = Connection Handle: 0x003
..10 .... .... .... = PB Flag: First Automatically Flushable Packet (2)
00.. .... .... .... = BC Flag: Point-To-Point (0)
Data Total Length: 5
Data
[Connect in frame: 365]
[Disconnect in frame: 456]
[Source BD_ADDR: TexasIns_1f:77:20 (90:59:af:1f:77:20)]
[Source Device Name: PanoBike BLE HRM]
[Source Role: Unknown (0)]
[Destination BD_ADDR: REDACTED]
[Destination Device Name: REDACTED]
[Destination Role: Unknown (0)]
[Current Mode: Unknown (-1)]
Bluetooth L2CAP Protocol
Length: 1
CID: Attribute Protocol (0x0004)
Bluetooth Attribute Protocol
Opcode: Write Response (0x13)
0... .... = Authentication Signature: False
.0.. .... = Command: False
..01 0011 = Method: Write Response (0x13)
[Handle: 0x0013 (Heart Rate: Heart Rate Measurement: Client Characteristic Configuration)]
[Service UUID: Heart Rate (0x180d)]
[Characteristic UUID: Heart Rate Measurement (0x2a37)]
[UUID: Client Characteristic Configuration (0x2902)]
[Request in Frame: 451]
LMK if you need other packets or the whole dump
@dennisguse I'm no expert, but nothing screams Error
here. That said, I'm unable to test or purchase another heart rate monitor due to country-wide heart rate monitor shortage that's driven by the current COVID craze. What are your thoughts on getting this issue back into potential bug
realm and what other troubleshooting should I try?
@gordonel I am also clueless (henice I did not yet reply yet). Everything looks okay... and then the sensor disconnects for no obvious reason. Using nRF or IpSensorMan it works. The last thing, we do is writing the characteristic and then boom aka nothing.
Next that you could try via OpenTracks:
Out of curiosity: could you try RunnerUp and FitoTrack (if I remember correctly they use a lib to connect to sensors)?
PS: how did you do the package snooping?
PPS: F-Droid and PlayStore built are identical except that the PlayStore builds are available earlier and you have to trust my computer :)
Put a lot of breakpoints into the BluetoothConnectionManager and see what happens. It might be that we are doing something too quickly.
I'd have to learn how to do that, so it might take some time
Out of curiosity: could you try RunnerUp and FitoTrack
RunnerUP connects to the sensor and shows the readout without problems. FitoTrack doesn't have a settings entry for sensors and crashes when I try to start a workout session
how did you do the package snooping?
Bluetooth HCI logs from dev options + wireshark
RunnnerUp might be a good hint (see AndroidBLEHRProvider). They do a descriptor read before trying to enable notifications.
I hacked something: https://github.com/OpenTracksApp/OpenTracks/tree/hr%23782 Can you give this a try?
RunnerUp code:
BluetoothGattDescriptor mHRMccc = mHRMcharac.getDescriptor(CCC);
if (mHRMccc == null) {
reportConnectFailed("CCC for HEART RATE MEASUREMENT charateristic not found!");
return;
}
if (!btGatt.readDescriptor(mHRMccc)) {
and they enable the notifications in the following callback
@Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor arg0, int status) {
BluetoothGattCharacteristic mHRMcharac = arg0.getCharacteristic();
if (!enableNotification(true, mHRMcharac)) {
reportConnectFailed("Failed to enable notification in onDescriptorRead");
}
}
@gordonel Any updates?
@gordonel Any updates?
Hey. Sorry for radio silence. Busy with work. This is on my plate, don't worry
@dennisguse I'm back. Sorry for the delay.
Nothing on your branch, unfortunately
@gordonel I meant this commit: https://github.com/OpenTracksApp/OpenTracks/commit/b22d555e5750edbb350098040dee468439ff55ef It is on this branch: https://github.com/OpenTracksApp/OpenTracks/tree/hr%23782
@gordonel I meant this commit: b22d555 It is on this branch: https://github.com/OpenTracksApp/OpenTracks/tree/hr%23782
Yes, I built from this commit
@gordonel Then I am (again) out of options/ideas. So, I guess you are on your own... It might be some kind of timing issue, and you might be able to analyze by putting some breakpoints into BluetoothConnectionManager.
@gordonel Then I am (again) out of options/ideas. So, I guess you are on your own... It might be some kind of timing issue, and you might be able to analyze by putting some breakpoints into BluetoothConnectionManager.
That's unfortunate. Please don't delete the branch. I'll try to work on top of it
@gordonel Please re-open, if you are starting to work on this again.
Describe the bug When Topeak PanoBike Heart Rate Monitor is paired to OpenTracks, it defaults to a "closed" sensor state, which prevents the sensor from transmitting any data. Apps like IpSensorMan can send the "Open" signal to the sensor, which eventually gets the sensor into a "tracking" state where it transmits data.
To Reproduce
Technical information