问题描述
本文已翻译,所以可能写得不好。
您好,我正在尝试使用 M5stack 来获取我的 Polar OH1+ 的加速度和心电图,但 notifyCallback 不起作用。
我在 Python 中发现了一些执行类似操作的代码,因此我按照相同的步骤建立了连接。
根据这个,我发现这是要遵循的程序。
- 读取pmd控制uuid的值
- 写入 pmd 控制 uuid
- 读取 pmd 数据 uuid
以下程序尝试使用 M5stack 实现这一点。
interval
完成后,串行监视器显示如下所示。
//===== header file & define & global variable =====
#include"BLEDevice.h"
boolean doConnect = false;
volatile boolean isConnected = false;
boolean doScan = false;
bleuUID pmd_serviceUUID ("FB005C80-02E7-F387-1CAD-8ACD2D8DF0C8");
bleuUID pmd_dataUUID ("FB005C82-02E7-F387-1CAD-8ACD2D8DF0C8");
bleuUID pmd_ctrlUUID ("FB005C81-02E7-F387-1CAD-8ACD2D8DF0C8");
BLEAdvertisedDevice* myDevice;
BLEClient* pClient;
String SensorName = "Polar OH1 87C4C425"; // SDから読み取る
//===========================================
//===== class & function ====================
class MyClientCallback: public BLEClientCallbacks{
void onConnect(BLEClient* pclient){ }
void ondisconnect(BLEClient* pclient){
isConnected = false;
Serial.println("ondisconnetct");
}
};
// BLEデバイスを検索する
class MyAdvertisedDeviceCallback: public BLEAdvertisedDeviceCallbacks{
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.println(advertisedDevice.toString().c_str());
// 指定デバイスなら接続する
if(SensorName.equals(advertisedDevice.getName().c_str())){
Serial.print("Connect BLE device : ");
Serial.println(advertisedDevice.toString().c_str());
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
}
}
};
void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,uint8_t* pData,size_t length,bool isnotify){
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
for (int i = 0; i <= length - 1; i++) {
Serial.print(String(*(pData + i),HEX));
Serial.print(" ");
}
Serial.println();
}
bool connectToServer(){
Serial.print("connection to : ");
Serial.println(myDevice->getAddress().toString().c_str());
pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback() );
pClient->connect(myDevice);
Serial.println(" - Created to server");
BLERemoteService* pRemoteService = pClient->getService(pmd_serviceUUID);
if (pRemoteService == nullptr) {
Serial.println("Failed to find our service UUID: ");
Serial.println(pmd_serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.print(" - Found service ( ");
Serial.print(pmd_serviceUUID.toString().c_str());
Serial.println(" )");
static BLERemoteCharacteristic* pControlCharacteristic;
pControlCharacteristic = pRemoteService->getCharacteristic(pmd_ctrlUUID);
if( pControlCharacteristic == nullptr ){
Serial.print("Failed to find out characteristic UUID : ");
Serial.println(pmd_ctrlUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println("characteristics");
std::map<uint16_t,BLERemoteCharacteristic*>* mapcharacteristics = pRemoteService->getcharacteristicsByHandle();
for (std::map<uint16_t,BLERemoteCharacteristic*>::iterator i = mapcharacteristics->begin(); i != mapcharacteristics->end(); ++i) {
Serial.print(" - characteristic UUID : ");
Serial.print(i->second->getUUID().toString().c_str());
Serial.print(" broadcast:");
Serial.print(i->second->canbroadcast()?'O':'X');
Serial.print(" Read:");
Serial.print(i->second->canRead()?'O':'X');
Serial.print(" Writenoresponse:");
Serial.print(i->second->canWritenoresponse()?'O':'X');
Serial.print(" Write:");
Serial.print(i->second->canWrite()?'O':'X');
Serial.print(" Notify:");
Serial.print(i->second->canNotify()?'O':'X');
Serial.print(" Indicate:");
Serial.print(i->second->canIndicate()?'O':'X');
Serial.println();
}
static BLERemoteCharacteristic* pDataCharacteristic;
pDataCharacteristic = pRemoteService->getCharacteristic(pmd_dataUUID);
if( pDataCharacteristic == nullptr ){
Serial.print("Failed to find out characteristic UUID : ");
Serial.println(pmd_dataUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.print(" - Add Notify ( ");
Serial.print(pmd_dataUUID.toString().c_str());
Serial.println(" )");
if(pDataCharacteristic->canNotify()){
std::string value = pControlCharacteristic->readValue();
uint8_t data[14] = {0x02,0x02,0x00,0x01,0xC8,0x10,0x08,};
pControlCharacteristic->writeValue(data,14,false);
Serial.println(" - Set value");
Serial.println(" - Can Notify");
pDataCharacteristic->registerForNotify(notifyCallback);
}
isConnected = true;
return true;
}
//===========================================
//===== setting =============================
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
static BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallback());
pBLEScan->setInterval(1349);
pBLEScan->setwindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5,false);
}
//===========================================
//===== main ================================
void loop(){
if(doConnect==true){
if(connectToServer()){
Serial.println("Now connected to BLE Server.");
}else{
Serial.println("faild to connect to the server.");
}
doConnect = false;
}
if( isConnected == false && doScan == true ) BLEDevice::getScan()->start(0);
delay(1000);
}
//===========================================
它甚至显示可以通知,但 notyfiCallback 不起作用。你能告诉我为什么它不起作用吗?
此外,写入 pmd 控件的字节序列应该基于此页面。
此外,这是在 ArduinoIDE 中将 ESP32 的 CoreDebugLebel 设置为 Debug 时串行监视器的输出。
Starting Arduino BLE Client application...
Name:,Address: 5a:f3:e5:97:72:be,manufacturer data: 060001092006319b0f7cab7c18b3ad1f11d4f6475cf638678bd51cf02d
Name:,Address: 33:20:7d:41:97:52,serviceUUID: 0000fd6f-0000-1000-8000-00805f9b34fb
Name:,Address: 11:27:f2:c5:92:98,manufacturer data: 0600010920029ac7ae5b723ad210a6450c28780429ca56a82bae79a076
Name: Polar OH1 87C4C425,Address: a0:9e:1a:87:c4:c4,manufacturer data: 6b00720851a77b02000000007a01053b005d,serviceUUID: 0000180d-0000-1000-8000-00805f9b34fb,serviceUUID: 0000feee-0000-1000-8000-00805f9b34fb
Connect BLE device : Name: Polar OH1 87C4C425,serviceUUID: 0000feee-0000-1000-8000-00805f9b34fb
connection to : a0:9e:1a:87:c4:c4
- Created client
- Created to server
- Found service ( fb005c80-02e7-f387-1cad-8acd2d8df0c8 )
characteristics
- characteristic UUID : fb005c81-02e7-f387-1cad-8acd2d8df0c8 broadcast:X Read:O Writenoresponse:X Write:O Notify:X Indicate:O
- characteristic UUID : fb005c82-02e7-f387-1cad-8acd2d8df0c8 broadcast:X Read:X Writenoresponse:X Write:X Notify:O Indicate:X
- Add Notify ( fb005c82-02e7-f387-1cad-8acd2d8df0c8 )
- Set value
- Can Notify
Now connected to BLE Server.
解决方法
您提供的 specification 显示了开始流式传输所需的正确消息流。您必须首先请求流媒体设置。
要请求 ppi 设置,只需将 0x01 0x03
发送到 P=MD 控制点。
在查看 polar-ble-sdk 的源代码时,我在 BlePMDClient.java 中发现了这一点(我在相关部分添加了注释):
public static class PpiData {
public static class PPSample {
public final int hr;
public final int ppInMs;
public final int ppErrorEstimate;
public final int blockerBit;
public final int skinContactStatus;
public final int skinContactSupported;
public PPSample(@NonNull byte[] data) {
// Convert data[0] to heart rate using a bitwise AND operation
// with 0xFF as long
hr = (int) ((long) data[0] & 0xFFL);
// Convert data[0] and data[1] to time between peaks in ms using
// BleUtils.convertArrayToUnsignedLong
ppInMs = (int) BleUtils.convertArrayToUnsignedLong(data,1,2);
// Same for the error estimate
ppErrorEstimate = (int) BleUtils.convertArrayToUnsignedLong(data,3,2);
// The last byte contains multiple flags that get read by
// bitshifting. First bit is the blockerBit,second the
// skinContactStatus,third skinContactSupported
blockerBit = data[5] & 0x01;
skinContactStatus = (data[5] & 0x02) >> 1;
skinContactSupported = (data[5] & 0x04) >> 2;
}
}
public final List<PPSample> ppSamples = new ArrayList<>();
public final long timeStamp;
public PpiData(@NonNull byte[] data,long timeStamp) {
int offset = 0;
this.timeStamp = timeStamp;
while (offset < data.length) {
final int finalOffset = offset;
ppSamples.add(new PPSample(Arrays.copyOfRange(data,finalOffset,finalOffset + 6)));
offset += 6;
}
}
}
现在我们知道如何转换数据帧的 6 个字节了,但是你收到的数据大多有 40 个字节,看起来像这样:
03 00 00 00 00 00 00 00 00 00 00 34 02 1e 00 07 00 f2 01 1e 00 07 00 05 03 1e 00 07 00 63 03 1e 00 06 00 8e 03 1e 00 06
从原始数据到不同数据帧的转换发生在同一文件的 Line 888 中。
此处提取接收数据的前 10 个字节:
PmdMeasurementType type = PmdMeasurementType.fromId(data[0]);
final long timeStamp = BleUtils.convertArrayToUnsignedLong(data,8);
final long frameType = BleUtils.convertArrayToUnsignedLong(data,9,1);
final byte[] content = new byte[data.length - 10];
System.arraycopy(data,10,content,content.length);
查看您收到的数据,我们现在知道以下几点:
类型 | 数据 | 说明 |
---|---|---|
PmdMeasurementType | 03 |
数据的类型,这里和请求的一样。 3 = PPI |
时间戳 | 00 00 00 00 00 00 00 00 |
时间戳,不知何故这里是 0 |
帧类型 | 00 |
后面的数据类型 |
内容 | 00 34 02 1e 00 07 00 f2 01 1e 00 07 00 05 03 1e 00 07 00 63 03 1e 00 06 00 8e 03 1e 00 06 |
其余的原始数据称为内容并进一步使用 |
由于我们正在查看 PPI 数据,因此适用以下切换案例:
case PPI:
if (frameType == 0) {
RxUtils.emitNext(ppiObservers,object -> object.onNext(new PpiData(content,timeStamp)));
} else {
BleLogger.w(TAG,"Unknown PPI frame type received");
}
break;
此处只允许零帧类型,这正是您收到的。您有时会收到 40 个字节,有时只收到 16 个。由于前 10 个不是实际数据的一部分,我们现在可以假设 40 个字节包含 5 个 PPI 测量值(5 * 6 个字节 = 30 个字节),而 16 个字节仅包含一个测量。
如果我们查看您收到的数据集中的第一个 PPI 测量值,我们现在可以阅读以下内容:
原始数据 | 类型 | 价值 |
---|---|---|
00 |
心率 | 0 |
34 02 |
峰峰值(毫秒) | 564 毫秒 |
1e 00 |
峰峰值误差估计 | 30 |
07 -> 00000111
|
错误位、皮肤接触状态位、皮肤接触状态支持位 | 错误:true,皮肤接触:true,支持皮肤接触:true |
由于您没有心率读数并且设置了错误位,我假设出现问题,可能您在测试时没有佩戴设备。