4

我正在使用 Cordova 创建一个应用程序,该应用程序需要解释来自蓝牙 HR 监视器的数据(能够记录原始 RR 间隔,例如 Polar H7)。我正在使用cordova-plugin-ble-central

尽管在互联网上搜寻答案并多次阅读蓝牙心率服务特性规范,但我很难理解从显示器接收到的数据。

这是我每次收到数据时运行的函数:

onData: function(buffer) {
    console.log(buffer);

    // var data8 = new Uint8Array(buffer);

    var data16 = new Uint16Array(buffer);
    var rrIntervals = data.slice(1);
    for (i=0; i<rrIntervals.length; i++) {
        rrInterval = rrIntervals[i];
        heartRate.addReading(rrInterval); // process RR interval elsewhere
    }
},

当我记录缓冲区中接收到的数据时,以下内容输出到控制台: 控制台输出

我知道如何提取 RR 间隔(以黄色突出显示),但我并不真正理解其他值代表什么,因为用户可能正在连接不传输 RR 间隔等的其他监视器。

非常感谢对接收到的数据的含义以及如何解析它的快速简单的英语解释。例如,什么数字构成标志字段,以及如何将其转换为二进制以提取子字段(即检查是否存在 RR 间隔 - 我知道这是由标志字段中的第 5 位决定的。)

该插件还声明“原始数据作为 ArrayBuffer 从本机代码传递到成功回调”,但我不知道如何检查标志以确定来自特定 HR 监视器的数据是 8 位还是 16 位格式。下面是我从收到的数据创建 Uint8 和 Uint16 数组时的另一个控制台日志。我再次强调了心率和 RR 间隔,但我需要知道其他值代表什么以及如何正确解析它们。

带有 Uint8 和 Uint16 输出的控制台日志

整个代码如下:

var heartRateSpec = {
    service: '180d',
    measurement: '2a37'
};


var app = {
    initialize: function() {
        this.bindEvents();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        app.scan();
    },
    scan: function() {
        app.status("Scanning for Heart Rate Monitor");

        var foundHeartRateMonitor = false;

        function onScan(peripheral) {
            // this is demo code, assume there is only one heart rate monitor
            console.log("Found " + JSON.stringify(peripheral));
            foundHeartRateMonitor = true;

            ble.connect(peripheral.id, app.onConnect, app.onDisconnect);
        }

        function scanFailure(reason) {
            alert("BLE Scan Failed");
        }

        ble.scan([heartRateSpec.service], 5, onScan, scanFailure);

        setTimeout(function() {
            if (!foundHeartRateMonitor) {
                app.status("Did not find a heart rate monitor.");
            }
        }, 5000);
    },
    onConnect: function(peripheral) {
        app.status("Connected to " + peripheral.id);
        ble.startNotification(peripheral.id, heartRateSpec.service, heartRateSpec.measurement, app.onData, app.onError);
    },
    onDisconnect: function(reason) {
        alert("Disconnectedz " + reason);
        beatsPerMinute.innerHTML = "...";
        app.status("Disconnected");
    },
    onData: function(buffer) {
        var data = new Uint16Array(buffer);

        if (heartRate.hasStarted() == false) {
            heartRate.beginReading(Date.now());

        } else {
            var rrIntervals = data.slice(1);
            for (i=0; i<rrIntervals.length; i++) {
                rrInterval = rrIntervals[i];
                heartRate.addReading(rrInterval);
            }

        }

    },
    onError: function(reason) {
        alert("There was an error " + reason);
    },
    status: function(message) {
        console.log(message);
        statusDiv.innerHTML = message;
    }
};


app.initialize();

非常感谢您的任何帮助或建议。

4

1 回答 1

1

更新更深入的解释查看我写的关于这个主题的这篇文章

我已经想通了——这里有一个对遇到类似问题的人的快速解释:

传入 onData(buffer) 的数据只是二进制数据,因此无论我们将其转换为 Uint8Array 还是 Uint16Array ,它仍然表示相同的二进制数据。当然,Uint16 中的整数可能会更大,因为它们包含 16 位而不是 8 位。

flags 字段始终由第一个字节表示,因此我们可以通过将数据(作为缓冲区传入)转换为 Uint8Array 并访问该数组的第一个元素,该元素将是索引为 0 的元素。

然后我们可以使用按位运算检查各种位域。例如,蓝牙心率服务特性规范告诉我们,第五位表示读数是包含 RR 间隔 (1) 还是不包含任何 (0)。

下面我们可以看到第五位是二进制的数字 16:

128 64  32  16  8   4   2   1
0   0   0   1   0   0   0   0  

因此,如果读数包含 RR 间隔,则操作 16 & flag(其中 flag 是包含 flags 字段的字节)将返回 16(可以提升为 true),如果不包含则返回 0(提升为 false)。

于 2018-03-25T12:19:19.453 回答