Bluetooth low energy, how to parse R-R Interval va

2019-02-15 23:56发布

问题:

My application is receiving information from smart heart device. Now i can see pulse value. Could you please help me to parse R-R Interval value? How can i check device support R-R Interval value or Not ?

Any advise from you

Thanks

回答1:

Have you checked the Bluetooth spec? The sample code below is in C#, but I think it shows the way to parse the data in each heart rate packet.

//first byte of heart rate record denotes flags
byte flags = heartRateRecord[0];

ushort offset = 1;

bool HRC2 = (flags & 1) == 1;
if (HRC2) //this means the BPM is un uint16
{
    short hr = BitConverter.ToInt16(heartRateRecord, offset);
    offset += 2;
}
else //BPM is uint8
{
    byte hr = heartRateRecord[offset];
    offset += 1;
}

//see if EE is available
//if so, pull 2 bytes
bool ee = (flags & (1 << 3)) != 0;
if (ee)
    offset += 2;

//see if RR is present
//if so, the number of RR values is total bytes left / 2 (size of uint16)
bool rr = (flags & (1 << 4)) != 0;
if (rr)
{
    int count = (heartRateRecord.Length - offset)/2;
    for (int i = 0; i < count; i++)
    {
        //each existence of these values means an R-Wave was already detected
        //the ushort means the time (1/1024 seconds) since last r-wave
        ushort value = BitConverter.ToUInt16(heartRateRecord, offset);

        double intervalLengthInSeconds = value/1024.0;
        offset += 2;
    }
}


回答2:

This post is a little old but a full answer has not been given. As I run into this post and it did help me at the end, I would like to share my final code. Hopefully it will help others.

The code provided by Daniel Judge is actually right, but as he already wrote, it is C#. HIs code is a bit better compared to what Simon M came up with at the end as the code of Daniel Judge takes into account there can be more than two RR-values within one message. Here is the actual spec of the Heart_rate_measurement characteristic

I have translated Daniel Judge his code to Objective-C:

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

        }

    }

}


回答3:

EDIT: this work for me, i get the correct rr values: In some cases you can find two values at the same time for rr.

- (void) updateWithHRMData:(NSData *)datas {

    const uint8_t *reportData = [datas bytes];

    uint16_t bpm = 0;
    uint16_t bpm2 = 0;

    if ((reportData[0] & 0x04) == 0)
    {
        NSLog(@"%@", @"Data are not present");
    }
    else
    {

        bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[2]));

        bpm2 = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[4]));

        if (bpm != 0 || bpm2 != 0) {

                NSLog(@"%u", bpm);

                if (bpm2 != 0) {
                    NSLog(@"%u", bpm2);
                }

        }

    }

}


回答4:

in @Brabbeldas solution i had to use a different flag to get rri values. but might depend on device used.

if ((reportData[0] & 0x10) == 0)

instead of

if ((reportData[0] & 0x04) == 0)


回答5:

Parse heart rate parameters in "C"

I uploaded the sample application to GitHub Heart-Rate-Bluegiga

void ble_evt_attclient_attribute_value(const struct ble_msg_attclient_attribute_value_evt_t *msg)
{
    if (msg->value.len < 2) {
        printf("Not enough fields in Heart Rate Measurement value");
        change_state(state_finish);
    }


    // Heart Rate Profile defined flags 
    const unsigned char HEART_RATE_VALUE_FORMAT = 0x01;
    const unsigned char ENERGY_EXPENDED_STATUS = 0x08;
    const unsigned char RR_INTERVAL = 0x10;


    unsigned char current_offset = 0;
    unsigned char flags = msg->value.data[current_offset];
    int is_heart_rate_value_size_long = ((flags & HEART_RATE_VALUE_FORMAT) != 0);
    int has_expended_energy = ((flags & ENERGY_EXPENDED_STATUS) != 0);
    int has_rr_intervals = ((flags & RR_INTERVAL) != 0);


    current_offset++;


    uint16 heart_rate_measurement_value = 0;


    if (is_heart_rate_value_size_long)
    {
        heart_rate_measurement_value = (uint16)((msg->value.data[current_offset + 1] << 8) +
            msg->value.data[current_offset]);
        current_offset += 2;
    }
    else
    {
        heart_rate_measurement_value = msg->value.data[current_offset];
        current_offset++;
    }


    printf("Heart rate measurment value: %d ", heart_rate_measurement_value);


    uint16 expended_energy_value = 0;


    if (has_expended_energy)
    {
        expended_energy_value = (uint16)((msg->value.data[current_offset + 1] << 8) +
            msg->value.data[current_offset]);
        current_offset += 2;


        printf(" Expended energy value: %d ", expended_energy_value);
    }


    uint16 rr_intervals[10] = {0};


    if (has_rr_intervals)
    {
        printf(" Rr intervals: ");


        int rr_intervals_count = (msg->value.len - current_offset) / 2;


        for (int i = 0; i < rr_intervals_count; i++)
        {
            int raw_rr_interval = (uint16)((msg->value.data[current_offset + 1] << 8) +
                msg->value.data[current_offset]);
            rr_intervals[i] = ((double)raw_rr_interval / 1024) * 1000;
            current_offset += 2;


            printf("%d ", rr_intervals[i]);
        }
        printf("\n");
    }
}