Question

Im having trouble understanding bytes and uint8_t values.

I am using the sample project created by apple that reads data from a Bluetooth 4.0 heart rate monitor via the heart rate service protocol. THe sample project gives out heart rate data as below:

- (void) updateWithHRMData:(NSData *)data 
{
const uint8_t *reportData = [data bytes];
uint16_t bpm = 0;

if ((reportData[0] & 0x01) == 0) 
{
    /* uint8 bpm */
    bpm = reportData[1];
} 
else 
{
    /* uint16 bpm */
    bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));
}

I am assuming that (reportData[0] & 0x01) returns the first data bit in the data array reportData but I dont know how to access the second, (reportData[0] & 0x02) doesn't work like I thought it would. Ideally I would like to check all the data in reportData[0] and then based on that grab the rr interval data in reportData[4] or [5] dependant on where it is stored and iterate through it to get each value as I believe there can be multiple values stored there.

a newbie question I know but Im having trouble finding the answer, or indeed the search terms to establish the answer.

Était-ce utile?

La solution

When you do reportData[0] you are getting the first byte (at index 0). When you combine that value with reportData[0] & 0x02, you are masking out all but the 2nd bit. This result will either be 0 (if bit 2 is not set) or it will be 2 (if the 2nd bit is set).

if ((reportData[0] & 0x02) == 0) {
    // bit 2 of first byte is not set
} else {
    // bit 2 of first byte is set
}

If you want to check all 8 bits then you could do:

uint8_t byte = reportData[0];
for (int i = 0; i < 8; i++) {
    int mask = 1 << i;
    if ((byte & mask) == 0) {
        bit i is not set
    } else {
        bit i is set
    }
}

Update: To extract a value that spans two bits you do something like this:

uint8_t mask = 0x01 | 0x02; // Good for value stored in the first two bits
uint8_t value = byte & mask; // value now has just value from first two bits

If the value to extract is in higher bits then there is an extra step:

uint8_t mask = 0x02 | 0x04; // Good for value in 2nd and 3rd bits
uint8_t value = (byte & mask) >> 1; // need to shift value to convert to regular integer

Autres conseils

Check this post for a discussion of the sample code. The post also links to the Bluetooth spec which should help you understand why the endianess check is being performed (basically it's Apple ensuring maximum portability). Basically, the first byte is a bit field describing the format of the HRV data and the presence/absence of EE and RR interval data. So:

reportData[0] & 0x03

tells you if EE data are present (1 = yes, 0 = no), and

reportData[0] & 0x04

tells you if RR interval data are present (1 = yes, 0 = no)

You can then get RR interval data with

uint16_t rrinterval;
rrinterval = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[idx]));

where idx is determined by the presence/absence tests you performed. I am assuming that the offsets are not fixed BTW as that's what you are indicating (i.e., dynamic offsets based on presence/absence) -- I'm not familiar with the BT spec. If the format is fixed, in this case the RR data would be at offset 7.

Part of the original question has not been answered yet. Rory also wants to know how to parse all the RR-interval data, as there can be multiple values within in one message (I have seen up to three). The RR-interval data is not always located within the same bytes. It depends on several things:

  • is the BPM written into a single byte or two?
  • is there EE data present?
  • calculate the number of RR-interval values

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);

        }

    }

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top