我正在使用示例CustomGATTSvc代码熟悉Movesense窗格上的GATT界面,并且在尝试向代码中添加更多服务时遇到了问题。我的项目完全需要具备以下能力:
所有这些都必须使用GATT界面来实现,因为我们希望在Cordova中开发移动应用程序,据我所知,该应用程序不支持Movesense库。
首先,我尝试将第二个服务添加到示例代码中已经存在的“健康温度计”服务中。我希望实现当前时间服务(与当前时间服务相关的所有内容都包含在#define CURRENT_TIME_SVC 中)-
#define THERMOMETER_SERV_UUID 0x1809 // Health Thermometer
#define CURRENT_TIME_SERV_UUID 0x1805 // Current Time Service
const uint16_t measCharUUID16 = 0x2A1C;
const uint16_t intervalCharUUID16 = 0x2A21;
const uint16_t healthThermometerSvcUUID16 = THERMOMETER_SERV_UUID; // Health Temperature probe
#ifdef CURRENT_TIME_SVC
const uint16_t timeCharUUID16 = 0x2A2C; //Random UUID for Current Time
const uint16_t timeSvcUUID16 = CURRENT_TIME_SERV_UUID; //Current Time
#endif
在configGattSvc()函数中,我如下配置服务和特征:
WB_RES::GattSvc customGattThermometerSvc;
WB_RES::GattChar characteristics[2];
WB_RES::GattChar &measChar = characteristics[0];
WB_RES::GattChar &intervalChar = characteristics[1];
//const uint16_t healthThermometerSvcUUID16 = 0x1809;
#ifdef CURRENT_TIME_SVC
WB_RES::GattSvc customGattTimeSvc;
WB_RES::GattChar characteristicsTime[1];
WB_RES::GattChar &timeChar = characteristicsTime[0];
//const uint16_t timeSvcUUID16 = 0x1805;
#endif
// Define the CMD characteristics
WB_RES::GattProperty measCharProp = WB_RES::GattProperty::INDICATE;
WB_RES::GattProperty intervalCharProps[2] = {WB_RES::GattProperty::READ, WB_RES::GattProperty::WRITE};
WB_RES::GattProperty timeCharProps[3] = {WB_RES::GattProperty::READ, WB_RES::GattProperty::WRITE, WB_RES::GattProperty::NOTIFY};
measChar.props = whiteboard::MakeArray<WB_RES::GattProperty>( &measCharProp, 1);
measChar.uuid = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&measCharUUID16), 2);
intervalChar.props = whiteboard::MakeArray<WB_RES::GattProperty>( intervalCharProps, 2);
intervalChar.uuid = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&intervalCharUUID16), 2);
intervalChar.initial_value = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&mMeasIntervalSecs), 2);
#ifdef CURRENT_TIME_SVC
timeChar.props = whiteboard::MakeArray<WB_RES::GattProperty>( timeCharProps, 3);
timeChar.uuid = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&timeCharUUID16), 2);
timeChar.initial_value = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&mTimeSecs), 2);
#endif
// Combine chars to service
customGattThermometerSvc.uuid = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&healthThermometerSvcUUID16), 2);
customGattThermometerSvc.chars = whiteboard::MakeArray<WB_RES::GattChar>(characteristics, 2);
// Create custom service
asyncPost(WB_RES::LOCAL::COMM_BLE_GATTSVC(), AsyncRequestOptions::Empty, customGattThermometerSvc);
#ifdef CURRENT_TIME_SVC
// Combine Time chars to service
customGattTimeSvc.uuid = whiteboard::MakeArray<uint8_t>( reinterpret_cast<const uint8_t*>(&timeSvcUUID16), 2);
customGattTimeSvc.chars = whiteboard::MakeArray<WB_RES::GattChar>(characteristicsTime, 1);
// Create custom service
asyncPost(WB_RES::LOCAL::COMM_BLE_GATTSVC(), AsyncRequestOptions::Empty, customGattTimeSvc);
#endif
在onGetResult中,我扩展了代码以合并“当前时间服务和特征”订阅,如下所示:
void CustomGATTSvcClient::onGetResult(whiteboard::RequestId requestId, whiteboard::ResourceId resourceId, whiteboard::Result resultCode, const whiteboard::Value& rResultData)
{
DEBUGLOG("CustomGATTSvcClient::onGetResult");
switch(resourceId.localResourceId)
{
case WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE::LID:
{
const WB_RES::GattSvc &svc = rResultData.convertTo<const WB_RES::GattSvc &>();
for (size_t i=0; i<svc.chars.size(); i++) {
const WB_RES::GattChar &c = svc.chars[i];
uint16_t uuid16 = *reinterpret_cast<const uint16_t*>(&(c.uuid[0]));
if(uuid16 == measCharUUID16)
mMeasCharHandle = c.handle.hasValue() ? c.handle.getValue() : 0;
else if(uuid16 == intervalCharUUID16)
mIntervalCharHandle = c.handle.hasValue() ? c.handle.getValue() : 0;
#ifdef CURRENT_TIME_SVC
else if(uuid16 == timeCharUUID16)
mTimeCharHandle = c.handle.hasValue() ? c.handle.getValue() : 0;
#endif
}
if (!mIntervalCharHandle || !mMeasCharHandle)
{
DEBUGLOG("ERROR: Not all chars were configured!");
return;
}
#ifdef CURRENT_TIME_SVC
if (!mTimeCharHandle)
{
DEBUGLOG("ERROR: Not all chars were configured!");
return;
}
#endif
char pathBuffer[32]= {'\0'};
snprintf(pathBuffer, sizeof(pathBuffer), "/Comm/Ble/GattSvc/%d/%d", mTemperatureSvcHandle, mIntervalCharHandle);
getResource(pathBuffer, mIntervalCharResource);
snprintf(pathBuffer, sizeof(pathBuffer), "/Comm/Ble/GattSvc/%d/%d", mTemperatureSvcHandle, mMeasCharHandle);
getResource(pathBuffer, mMeasCharResource);
#ifdef CURRENT_TIME_SVC
snprintf(pathBuffer, sizeof(pathBuffer), "/Comm/Ble/GattSvc/%d/%d", mTimeSvcHandle, mTimeCharHandle);
getResource(pathBuffer, mTimeCharResource);
#endif
// Subscribe to listen to intervalChar notifications (someone writes new value to intervalChar)
asyncSubscribe(mIntervalCharResource, AsyncRequestOptions::Empty);
// Subscribe to listen to measChar notifications (someone enables/disables the INDICATE characteristic)
asyncSubscribe(mMeasCharResource, AsyncRequestOptions::Empty);
#ifdef CURRENT_TIME_SVC
// Subscribe to listen to timeChar notifications (someone writes new value to timeChar)
asyncSubscribe(mTimeCharResource, AsyncRequestOptions::Empty);
#endif
}
break;
case WB_RES::LOCAL::MEAS_TEMP::LID:
{
// Temperature result or error
if (resultCode == whiteboard::HTTP_CODE_OK)
{
WB_RES::TemperatureValue value = rResultData.convertTo<WB_RES::TemperatureValue>();
float temperature = value.measurement;
// Convert K to C
temperature -= 273.15;
// Return data
//uint8_t buffer[5]; // 1 byte or flags, 4 for FLOAT "in Celsius" value
uint8_t buffer[5];
buffer[0]=0;
// convert normal float to IEEE-11073 "medical" FLOAT type into buffer
floatToFLOAT(temperature, &buffer[1]);
// Write the result to measChar. This results INDICATE to be triggered in GATT service
WB_RES::Characteristic newMeasCharValue;
newMeasCharValue.bytes = whiteboard::MakeArray<uint8_t>(buffer, sizeof(buffer));
asyncPut(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE_CHARHANDLE(), AsyncRequestOptions::Empty, mTemperatureSvcHandle,
mMeasCharHandle, newMeasCharValue);
}
}
break;
#ifdef CURRENT_TIME_SVC
case WB_RES::LOCAL::TIME::LID:
{
// Return with the RTC Time
if (resultCode == whiteboard::HTTP_CODE_OK)
{
WB_RES::DetailedTime value = rResultData.convertTo<WB_RES::DetailedTime>();
int64 tm = value.utcTime;
uint8_t buffer[2];
// Could have an endian issue here, will have to check once connection works
buffer[0] - (tm&0xFF00)>>8;
buffer[1] = tm &0xFF;
// Here we need to get the current time and then Put it back to the device connected
WB_RES::Characteristic newTimeCharValue;
newTimeCharValue.bytes = whiteboard::MakeArray<uint8_t>(buffer, sizeof(buffer));
asyncPut(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE_CHARHANDLE(), AsyncRequestOptions::Empty, mTimeSvcHandle,
mTimeCharHandle, newTimeCharValue);
}
}
break;
#endif
}
}
我在onGetResult中添加了Time Local Resource,因为我仍然不确定如何从Pod中“获取” RTC时间。如何访问/ Time资源?
onNotify已作如下修改:
void CustomGATTSvcClient::onNotify(whiteboard::ResourceId resourceId, const whiteboard::Value& value, const whiteboard::ParameterList& rParameters)
{
switch(resourceId.localResourceId)
{
case WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE_CHARHANDLE::LID:
{
WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE_CHARHANDLE::SUBSCRIBE::ParameterListRef parameterRef(rParameters);
if (parameterRef.getCharHandle() == mIntervalCharHandle)
{
const WB_RES::Characteristic &charValue = value.convertTo<const WB_RES::Characteristic &>();
uint16_t interval = *reinterpret_cast<const uint16_t*>(&charValue.bytes[0]);
DEBUGLOG(": mMeasCharResource: len: %d, new interval: %d", charValue.bytes.size(), interval);
// Update the interval
if (interval >= 1 && interval <= 65535)
mMeasIntervalSecs = interval;
// restart timer if exists
if (mMeasurementTimer != whiteboard::ID_INVALID_TIMER) {
stopTimer(mMeasurementTimer);
mMeasurementTimer = startTimer(mMeasIntervalSecs*1000, true);
}
}
else if (parameterRef.getCharHandle() == mMeasCharHandle)
{
const WB_RES::Characteristic &charValue = value.convertTo<const WB_RES::Characteristic &>();
bool bNotificationsEnabled = charValue.notifications.hasValue() ? charValue.notifications.getValue() : false;
DEBUGLOG(": mMeasCharHandle. bNotificationsEnabled: %d", bNotificationsEnabled);
// Start or stop the timer
if (mMeasurementTimer != whiteboard::ID_INVALID_TIMER)
{
stopTimer(mMeasurementTimer);
mMeasurementTimer = whiteboard::ID_INVALID_TIMER;
}
if (bNotificationsEnabled)
mMeasurementTimer = startTimer(mMeasIntervalSecs*1000, true);
}
#ifdef CURRENT_TIME_SVC
else if (parameterRef.getCharHandle() == mTimeCharHandle)
{
// Received Time information!
const WB_RES::Characteristic &charValue = value.convertTo<const WB_RES::Characteristic &>();
uint16_t tm = *reinterpret_cast<const uint16_t*>(&charValue.bytes[0]);
DEBUGLOG(": mMeasCharResource: len: %d, new interval: %d", charValue.bytes.size(), tm);
// Update the interval
mTimeSecs = tm;
}
#endif
}
break;
}
}
到目前为止,我认为代码应该正确并且可以正常工作,但是onPostResult的最后一小段代码存在问题:
void CustomGATTSvcClient::onPostResult(whiteboard::RequestId requestId, whiteboard::ResourceId resourceId, whiteboard::Result resultCode, const whiteboard::Value& rResultData)
{
DEBUGLOG("CustomGATTSvcClient::onPostResult: %d", resultCode);
if (resultCode == whiteboard::HTTP_CODE_CREATED) {
#if 1
// This is the code that I propose using when having more than one service but it doesn't seem to work.
const WB_RES::GattSvc &svc = rResultData.convertTo<const WB_RES::GattSvc &>();
uint16_t uuid16 = *reinterpret_cast<const uint16_t*>(&(svc.uuid[0]));
if(uuid16 == healthThermometerSvcUUID16) {
mTemperatureSvcHandle = (int32_t)rResultData.convertTo<uint16_t>();
DEBUGLOG("Custom Gatt service was created. handle: %d", mTemperatureSvcHandle);
asyncGet(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE(), AsyncRequestOptions::Empty, mTemperatureSvcHandle);
}
#ifdef CURRENT_TIME_SVC
else if(uuid16 == timeSvcUUID16) {
mTimeSvcHandle = (int32_t)rResultData.convertTo<uint16_t>();
DEBUGLOG("Custom Gatt service was created. handle: %d", mTimeSvcHandle);
asyncGet(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE(), AsyncRequestOptions::Empty, mTimeSvcHandle);
}
#endif
#else
// This is the code that does work with a single Service but is doesn't work as soon as I add a second service.
// Custom Gatt service was created
mTemperatureSvcHandle = (int32_t)rResultData.convertTo<uint16_t>();
DEBUGLOG("Custom Gatt service was created. handle: %d", mTemperatureSvcHandle);
// Request more info about created svc so we get the char handles
asyncGet(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE(), AsyncRequestOptions::Empty, mTemperatureSvcHandle);
#endif
}
}
您将看到我在此代码段中有一个#if 1 .. (code1) .. #else .. (code2) .. #endif 。我在第#if 1部分(code1)中编写的代码是与代码创建的两个GATT服务一起使用的。我认为由于有两个服务,因此必须进行测试(使用服务UUID)以确定要处理的服务。 #else (code2)之后的代码是来自刚刚使用运行状况临时服务的原始示例代码的结果。
当我使用(code1)进行编译时,一切都可以正常编译,但是我似乎无法订阅健康温度服务。当我切换为使用(code2)时,健康温度服务可以正常工作,并且可以毫无问题地订阅它。
要测试GATT界面,我正在使用Bluetility(蓝牙低功耗浏览器)。 https://github.com/jnross/Bluetility
我的问题如下:
任何人的帮助,都能为我指出实现最终目标的正确道路,我将不胜感激。预先感谢。
答案 0 :(得分:0)
我认为您在响应处理中类型不匹配。
void CustomGATTSvcClient::onPostResult(...)
{
if (resultCode == whiteboard::HTTP_CODE_CREATED) {
const WB_RES::GattSvc &svc = rResultData.convertTo<const WB_RES::GattSvc &>();
将代码为201(已创建)的/ Comm / Ble / GattSvc POST请求定义为GattSvcHandle,该请求类型为整数,而不是结构WB_RES :: GattSvc
因此,以下比较逻辑也会失败。
解决方案: 您可以在onPostResult中尝试比较逻辑吗?
if(mTemperatureSvcHandle == 0) {
mTemperatureSvcHandle = (int32_t)rResultData.convertTo<uint16_t>();
asyncGet(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE(), AsyncRequestOptions::Empty, mTemperatureSvcHandle);
}
else {
mTimeSvcHandle = (int32_t)rResultData.convertTo<uint16_t>();
asyncGet(WB_RES::LOCAL::COMM_BLE_GATTSVC_SVCHANDLE(), AsyncRequestOptions::Empty, mTimeSvcHandle);}
(此解决方案只是一种变通方法,它可以测试这是否解决了同时运行两个服务的问题,因为这并未考虑到帖子是异步的,最终您可能需要一些更优雅的方法来识别与postResult相关的服务)