KW36 - 長い特性値の書き込みと読み取り はじめに
Bluetooth LEのMTU(Maximum Transmission Unit)は、リモートデバイスに、ローカルがそのようなチャネルで処理できる最大バイト数を示す情報パラメータです。たとえば、KW36のATT_MTUは247バイトに固定されています。一部のアプリケーションでは、GATT データベースで長い特性を定義する必要があり、特性の長さがクライアントとサーバーの Bluetooth LE デバイスによってネゴシエートされた MTU を超える場合があります。このシナリオでは、Bluetooth LE 仕様で、対象の特性を書き込んだり読み取ったりする手順が定義されています。要約すると、それはで構成されています 特定のコマンドを使用して、同じ特性値に対して複数の書き込みと読み取りを実行します。「長特性値の書込み」手順では、これらのコマンドはATT_PREPARE_WRITE_REQおよびATT_EXECUTE_WRITE_REQされます。「長い特性値を読み取る」手順の場合、これらのコマンドはATT_READ_REQされ、ATT_READ_BLOB_REQされます。このドキュメントでは、クライアントデバイスとサーバーデバイスの観点から、長い特性値を書き込んだり読み取ったりする方法の例を紹介します。
特性値を書き込んだり読み取ったりする API
特性値の書込み
GattClient_WriteCharacteristicValue API は、書き込み操作を実行するために使用されます。これは、GATT クライアント デバイスによって実装されます。
次の表では、入力パラメータについて説明します。
入力パラメータ
形容
deviceId_t deviceId
ピア デバイスのデバイス ID。
gattCharacteristic_t * p特性
gattCharacteristic 構造体型へのポインター。この構造体には、"value.handle" の特性値の有効なハンドルが含まれている必要があります畑。書き込む特性値のハンドルは、通常、サービスディスカバリ手順の後に取得されます。
uint16_t値の長さ
この値は、aValue が指す配列の長さを示します。
const uint8_t * aValue
GATT データベースに書き込まれる値を含む配列へのポインター。
レスポンスのないbool_t
true の場合、アプリケーションが "応答なしの書き込み" を実行すること、つまり、コマンドがATT_WRITE_CMDまたはATT_SIGNED_WRITE_CMDされるタイミングを意味します。
bool_t signedWrite
withoutResponse と signedWrite の両方が true の場合、コマンドは ATT_SIGNED_WRITE_CMD になります。withoutResponse が false の場合、このパラメータは無視されます。
bool_t doReliableLongCharWrites
アプリケーションが長い特性値を書き込む必要がある場合は、このフィールドを true に設定する必要があります。
const uint8_t * aCsrk
withoutResponse と signedWrite の両方が true の場合、このポインターにはデータに署名するための CSRK が含まれている必要があります。それ以外の場合、このパラメータは無視されます。
特性値の読込
GattClient_ReadCharacteristicValue API は、読み取り操作を実行するために使用されます。これは、GATT クライアント デバイスによって実装されます。
次の表では、入力パラメータについて説明します。
入力パラメータ
形容
deviceId_t deviceId
ピア デバイスのデバイス ID。
gattCharacteristic_t * pIoCharacteristic
gattCharacteristic 構造体型へのポインター。この構造体には、"value.handle" の特性値の有効なハンドルが含まれている必要があります畑。書き込む特性値のハンドルは、通常、サービスディスカバリ手順の後に取得されます。同様に、"value.paValue"この構造体のフィールドは、ピアから読み取られた特性値を含む配列を指す必要があります。
unit16_t maxReadBytes
読み込む特性値の長さ。
この API は長い特性を処理するため、特別なパラメーターや構成について心配する必要はありません。
次のセクションでは、長い特性の書き込み方法と読み取り方法の機能的な例を示します。この例は、温度コレクターと温度センサー SDK の例に基づいています。この例では、GATT データベースでカスタム・サービスを作成する方法と、その特性を検出する方法も示しています。
Bluetooth LEサーバー(温度センサー)
gatt_uuid128.h の変更
この例で使用する「カスタムサービス」の 128 ビット UUID を定義します。次のコードを追加します。
/* Custom service */
UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01)
UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)
gatt_db.h の変更
この例では、私たちのサービスには1つの特性しかなく、書き込みまたは読み取りが可能で、可変長は400バイトに制限されています(KW36のATT_MTUは247バイトであるため、この長さでは、長い書き込みと読み取りが保証されます)。次のコードを追加します。
PRIMARY_SERVICE_UUID128(service_custom, uuid_service_custom)
CHARACTERISTIC_UUID128(char_custom, uuid_char_custom, (gGattCharPropWrite_c | gGattCharPropRead_c))
VALUE_UUID128_VARLEN(value_custom, uuid_char_custom, (gPermissionFlagWritable_c | gPermissionFlagReadable_c), 400, 1)
app_preinclude.h の変更
長い特性の書き込みと読み取りに関する最も重要な考慮事項の 1 つは、これに必要なメモリ割り当てです。現在の "AppPoolsDetails_c" 設定 ("_block_size_" と "_number_of_blocks_") をインクリメントする必要があります。「_block_size_」が4バイトに揃えられていることを確認してください。アプリケーションで機能する構成を見つけたら、MKW3xA/KW3xZアプリケーションノートのメモリプールオプティマイザーの手順に従って、メモリリソースを無駄にしない最適な構成を見つけてください。この例では、「AppPoolsDetails_c」を次のように構成します。
/* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/
#define AppPoolsDetails_c \
_block_size_ 264 _number_of_blocks_ 8 _eol_
Bluetooth LEクライアント(温度コレクター)
gatt_uuid128.h の変更
この例で使用する「カスタムサービス」の 128 ビット UUID を定義します。次のコードを追加します。
/* Custom service */
UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01)
UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)
temperature_collector.c の変更点
1. 「プライベートタイプ定義」セクションで次の変数を定義します。
typedef struct customServiceConfig_tag
{
uint16_t hService;
uint16_t hCharacteristic;
} customServiceConfig_t;
typedef struct appCustomInfo_tag
{
tmcConfig_t tempClientConfig;
customServiceConfig_t customServiceClientConfig;
}appCustomInfo_t;
typedef enum
{
mCustomServiceWrite = 0,
mCustomServiceRead
}customServiceState_t;
2. 「プライベートメモリ宣言」セクションで、400バイトの配列を2つ追加し、1つは送信用、もう1つはサーバーからデータを受信します。
/* Dummy array for custom service */
uint8_t mWriteDummyArray[400];
uint8_t mReadDummyArray[400];
3. 「プライベート関数プロトタイプ」セクションで新しい関数を定義し、特性値を書き込んだり読み取ったりします。
static void BleApp_SendReceiveCustomService (customServiceState_t state);
4.「BleApp_Config」関数を見つけ、ここに次のコードを追加して、「mWriteDummyArray」に既知のパターンを埋めてから、カスタム特性を書き込みます。
static void BleApp_Config(void)
{
uint16_t fill_pattern;
/* Fill pattern to write long characteristic */
for (fill_pattern = 0; fill_pattern<400; fill_pattern++)
{
mWriteDummyArray[fill_pattern] = (uint8_t)fill_pattern;
}
/* Configure as GAP Central */
BleConnManager_GapCommonConfig();
...
...
}
5.「BleApp_StoreServiceHandles」機能を見つけます。この関数を変更して、カスタム・サービスをサービス・ディスカバリー・プロシージャーに含めます。これは、カスタム特性のハンドルが GattClient_WriteCharacteristicValue API と GattClient_ReadCharacteristicValue API によって使用されるため、そのハンドルを保存するためです。
static void BleApp_StoreServiceHandles
(
gattService_t *pService
)
{
uint8_t i,j;
if ((pService->uuidType == gBleUuidType128_c) &&
FLib_MemCmp(pService->uuid.uuid128, uuid_service_temperature, 16))
{
/* Found Temperature Service */
mPeerInformation.customInfo.tempClientConfig.hService = pService->startHandle;
for (i = 0; i < pService->cNumCharacteristics; i++)
{
if ((pService->aCharacteristics[i].value.uuidType == gBleUuidType16_c) &&
(pService->aCharacteristics[i].value.uuid.uuid16 == gBleSig_Temperature_d))
{
/* Found Temperature Char */
mPeerInformation.customInfo.tempClientConfig.hTemperature = pService->aCharacteristics[i].value.handle;
for (j = 0; j < pService->aCharacteristics[i].cNumDescriptors; j++)
{
if (pService->aCharacteristics[i].aDescriptors[j].uuidType == gBleUuidType16_c)
{
switch (pService->aCharacteristics[i].aDescriptors[j].uuid.uuid16)
{
/* Found Temperature Char Presentation Format Descriptor */
case gBleSig_CharPresFormatDescriptor_d:
{
mPeerInformation.customInfo.tempClientConfig.hTempDesc = pService->aCharacteristics[i].aDescriptors[j].handle;
break;
}
/* Found Temperature Char CCCD */
case gBleSig_CCCD_d:
{
mPeerInformation.customInfo.tempClientConfig.hTempCccd = pService->aCharacteristics[i].aDescriptors[j].handle;
break;
}
default:
; /* No action required */
break;
}
}
}
}
}
}
else if ((pService->uuidType == gBleUuidType128_c) &&
FLib_MemCmp(pService->uuid.uuid128, uuid_service_custom, 16))
{
/* Found Custom Service */
mPeerInformation.customInfo.customServiceClientConfig.hService = pService->startHandle;
if (pService->cNumCharacteristics > 0U &&
pService->aCharacteristics != NULL)
{
/* Found Custom Characteristic */
mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic = pService->aCharacteristics[0].value.handle;
}
}
else
{
;
}
}
6.以下に示すように「BleApp_SendReceiveCustomService」を開発します。この関数は、長い操作を使用してカスタム特性値を書き込んだり読み込んだりするために使用されます。この関数に注目して、 GattClient_WriteCharacteristicValue and GattClient_ReadCharacteristicValue APIを使用して長い特性値を書き込んだり読み取ったりする例を次に示します。「特性」構造体は、最後のAPIを使用する前に入力され、カスタム特性のハンドルとピアから読み取られた値を受け取る宛先アドレスが含まれていることに注意してください。 GattClient_WriteCharacteristicValue を使用した長い書き込みを許可するには、「doReliableLongCharWrites」フィールドをTRUEにする必要がある ことに注意してください 。
static void BleApp_SendReceiveCustomService (customServiceState_t state)
{
bleResult_t bleResult;
gattCharacteristic_t characteristic;
/* Verify if there is a valid peer */
if (gInvalidDeviceId_c != mPeerInformation.deviceId)
{
/* Fill the characteristic struct with a read destiny and the custom service handle */
characteristic.value.handle = mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic;
characteristic.value.paValue = &mReadDummyArray[0];
/* Try to write the custom characteristic value */
if(mCustomServiceWrite == state)
{
bleResult = GattClient_WriteCharacteristicValue(mPeerInformation.deviceId,
&characteristic,
(uint16_t)400,
&mWriteDummyArray[0],
FALSE, FALSE, TRUE, NULL);
/* An error occurred while trying to write the custom characteristic value, disconnect */
if(gBleSuccess_c != bleResult)
{
(void)Gap_Disconnect(mPeerInformation.deviceId);
}
}
/* Try to read the custom characteristic value */
else
{
bleResult = GattClient_ReadCharacteristicValue(mPeerInformation.deviceId,
&characteristic,
(uint16_t)400);
/* An error occurred while trying to read the custom characteristic value, disconnect */
if(gBleSuccess_c != bleResult)
{
(void)Gap_Disconnect(mPeerInformation.deviceId);
}
}
}
}
7. 「BleApp_GattClientCallback」を以下のように変更します。この関数では、入力パラメータ「state」に応じて特性を書き込んだり読み込んだりする「BleApp_SendReceiveCustomService」を実装しています。この例の想定される動作は、まず、mWriteDummyArray に含まれる 400 バイトのパターンをカスタム特性値に書き込み、その直後に温度サービスの特性記述子 (gGattProcWriteCharacteristicDescriptor_c イベントのこのコールバックによって示されます) を書き込むことです。書き込みが正常に実行されると、このコールバックでは「gGattProcWriteCharacteristicValue_c」イベントで示されるため、ここで関数を実行して特性値を読み取ることができます。次に、読み取りが完了すると「gGattProcReadCharacteristicValue_c」イベントがトリガーされ、ここでは、書き込まれた値とGATTサーバーから読み取られた値を比較し、両方が同じ場合は、緑色のRGB LEDが点灯し、長い特性が書き込まれ、正常に読み取られたことを示します。
static void BleApp_GattClientCallback(
deviceId_t serverDeviceId,
gattProcedureType_t procedureType,
gattProcedureResult_t procedureResult,
bleResult_t error
)
{
if (procedureResult == gGattProcError_c)
{
attErrorCode_t attError = (attErrorCode_t)(uint8_t)(error);
if (attError == gAttErrCodeInsufficientEncryption_c ||
attError == gAttErrCodeInsufficientAuthorization_c ||
attError == gAttErrCodeInsufficientAuthentication_c)
{
/* Start Pairing Procedure */
(void)Gap_Pair(serverDeviceId, &gPairingParameters);
}
BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcError_c);
}
else
{
if (procedureResult == gGattProcSuccess_c)
{
switch(procedureType)
{
case gGattProcReadCharacteristicDescriptor_c:
{
if (mpCharProcBuffer != NULL)
{
/* Store the value of the descriptor */
BleApp_StoreDescValues(mpCharProcBuffer);
}
break;
}
case gGattProcWriteCharacteristicDescriptor_c:
{
/* Try to write to the custom service */
BleApp_SendReceiveCustomService(mCustomServiceWrite);
}
break;
case gGattProcWriteCharacteristicValue_c:
{
/* If write to the custom service was completed, try to read the custom service */
BleApp_SendReceiveCustomService(mCustomServiceRead);
}
break;
case gGattProcReadCharacteristicValue_c:
{
/* If read to the custom service was completed, compare write and read buffers */
if(FLib_MemCmp(&mWriteDummyArray[0], &mReadDummyArray[0], 400))
{
Led3On();
}
else
{
(void)Gap_Disconnect(mPeerInformation.deviceId);
}
}
break;
default:
{
; /* No action required */
break;
}
}
BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcComplete_c);
}
}
/* Signal Service Discovery Module */
BleServDisc_SignalGattClientEvent(serverDeviceId, procedureType, procedureResult, error);
}
app_preinclude.h の変更
長い特性の書き込みと読み取りに関する最も重要な考慮事項の 1 つは、これに必要なメモリ割り当てです。現在の "AppPoolsDetails_c" 設定 ("_block_size_" と "_number_of_blocks_") をインクリメントする必要があります。「_block_size_」が4バイトに揃えられていることを確認してください。プールの現在の構成がアプリケーション要件を満たさない場合、「GattClient_WriteCharacteristicValue」または「GattClient_ReadCharacteristicValue 」の戻り値が「gBleOutOfMemory_c」ではなく「」である場合に知ることができますgBleSuccess_c」(その場合、デバイスは「temperature_collector.cの変更」のステップ6のコードに従ってピアから切断されます)。アプリケーションで機能する構成を見つけたら、 MKW3xA/KW3xZアプリケーションノートのメモリプールオプティマイザーの手順に従って、メモリリソースを無駄にしない最適な構成を見つけてください。この例では、「AppPoolsDetails_c」を次のように構成します。
/* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/
#define AppPoolsDetails_c \
_block_size_ 112 _number_of_blocks_ 6 _eol_ \
_block_size_ 256 _number_of_blocks_ 3 _eol_ \
_block_size_ 280 _number_of_blocks_ 2 _eol_ \
_block_size_ 432 _number_of_blocks_ 1 _eol_
このトピックに関するご質問はお気軽にお問い合わせください。 BLEソフトウェア kW KW35 |36
查看全文