Reputation: 2653
I've been playing around with CoreBluetooth a lot lately, and although I can connect to some devices, I never seem to be able to properly read the data (characteristic values).
Right now I am connecting with the Wahoo BT Heartrate monitor, and I am getting all the signals but I can't make the data into anything sensible. (Yes, I am aware there is an API, but I am trying to connect without it, to properly get something working with CoreBluetooth).
I have so far not been able to turn the NSData (characteristic.value) into anything sensible. If you have any suggestions on how to make sense of this data that would be very much apreciated.
Upvotes: 1
Views: 1511
Reputation: 1949
Below some code to fully parse all the HeartRate measurement characteristic data.
How to process the data depends on several things:
Here is the actual spec of the Heart_rate_measurement characteristic
// Instance method to get the heart rate BPM information
- (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error
{
// Get the BPM //
// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml //
// Convert the contents of the characteristic value to a data-object //
NSData *data = [characteristic value];
// Get the byte sequence of the data-object //
const uint8_t *reportData = [data bytes];
// Initialise the offset variable //
NSUInteger offset = 1;
// Initialise the bpm variable //
uint16_t bpm = 0;
// Next, obtain the first byte at index 0 in the array as defined by reportData[0] and mask out all but the 1st bit //
// The result returned will either be 0, which means that the 2nd bit is not set, or 1 if it is set //
// If the 2nd bit is not set, retrieve the BPM value at the second byte location at index 1 in the array //
if ((reportData[0] & 0x01) == 0) {
// Retrieve the BPM value for the Heart Rate Monitor
bpm = reportData[1];
offset = offset + 1; // Plus 1 byte //
}
else {
// If the second bit is set, retrieve the BPM value at second byte location at index 1 in the array and //
// convert this to a 16-bit value based on the host’s native byte order //
bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));
offset = offset + 2; // Plus 2 bytes //
}
NSLog(@"bpm: %i", bpm);
// Determine if EE data is present //
// If the 3rd bit of the first byte is 1 this means there is EE data //
// If so, increase offset with 2 bytes //
if ((reportData[0] & 0x03) == 1) {
offset = offset + 2; // Plus 2 bytes //
}
// Determine if RR-interval data is present //
// If the 4th bit of the first byte is 1 this means there is RR data //
if ((reportData[0] & 0x04) == 0)
{
NSLog(@"%@", @"Data are not present");
}
else
{
// The number of RR-interval values is total bytes left / 2 (size of uint16) //
NSUInteger length = [data length];
NSUInteger count = (length - offset)/2;
NSLog(@"RR count: %lu", (unsigned long)count);
for (int i = 0; i < count; i++) {
// The unit for RR interval is 1/1024 seconds //
uint16_t value = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[offset]));
value = ((double)value / 1024.0 ) * 1000.0;
offset = offset + 2; // Plus 2 bytes //
NSLog(@"RR value %lu: %u", (unsigned long)i, value);
}
}
}
Upvotes: 2
Reputation: 1552
Well, you should be implementing the Heart Rate Profile (see here), which uses the Heart Rate Service. If you look at the Heart Rate Service Specifications, you will see that the format of the Heart Rate Measurement Characteristic changes according to the flags set in the least significant octet of the data packet.
This means that you need to set up your code to handle dynamic packet sizes.
So your general process would be:
value
property and check it for:
Using NSData - especially with Core Bluetooth - takes some getting used to, but it's not that bad once you grasp the concept.
Good luck!
Upvotes: 1
Reputation: 26036
Well...
What you'll have to do, when you read the value for the characteric :
NSData *data = [characteritic value];
theTypeOfTheData value;
[data getByte:&value lenght:sizeof(value)];
But, theTypeOfTheData can be whatever has thought the developer of the device. So it could be UInt8, UInt16, or struct (with or without bitfield)... You'll have to get info by contacting the developer of the device or looking if there is some documentation.
For example, with my colleague, we use the the type of data that consumes the less space (because the device hasn't infinite memory) according to what is need.
Look into the descriptor of the characteristic. It may points out the type of data.
Upvotes: 0