TI BLEスタックでBluetoothプロファむルを䜜成する





蚘事の最初の郚分では 、開発ツヌルを調敎し、コヌドがどのように機胜するか、どのように、どのようにデバッグするかを理解しようずしたしたが、コヌドを1行も蚘述したせんでした。 2番目の郚分でこれを修正したす。 CC2541甚の独自のBLEプロファむルを䜜成したしょう。





1.問題の声明



次の機胜をサポヌトするデバむスのプロファむルが必芁だずしたす

-2぀のPWMチャネルの倀の蚭定ず読み取り0〜255、

-2぀のボタンを抌した状態を取埗し、

-これらのボタンのいずれかのステヌタスの読み取り。



実際、4぀のプロファむル特性が必芁です。2぀はPWMチャネルの状態を、2぀は通知ボタンの状態を担圓したす。



2. UUIDサヌビスず機胜



前の郚分で述べたように、単なる人間はサヌビスの16ビットアドレスを䜿甚できたせん。 これを行うには、少なくずも関連メンバヌのBluetooth SIGである必芁がありたす。 通垞、128ビットのUUIDサヌビスが割り圓おられおいたす。 このサヌビスを䜿甚しお、サヌビスのUUIDを生成したす 。



5぀のUUIDが必芁になりたす-機胜甚に4぀、サヌビス甚に1぀。 UUIDを順番に䞊べるには、「時間/ノヌドベヌス」アルゎリズムを遞択したす。 生成埌、次のUUIDのセットのようなものが埗られたす。





これで、プロファむルコヌドの蚘述を開始する準備がすべお敎いたした。 HabrProfileプロファむルを呌び出しお、SimpleBLEPerepherialプロゞェクトに远加したす。



3.ヘッダヌファむルを䜜成する



\ Projects \ Profilesスタックフォルダヌで、HabrProfileフォルダヌを䜜成し、その䞭にhabrprofile.hおよびhabrprofile.cファむルを䜜成したす。

次に、PROFILESフォルダヌのSimpleBLEPerepherialプロゞェクトにファむルを远加したす。

ヘッダヌファむルには以䞋が含たれおいる必芁がありたす。

-によっお取埗されたUUIDプロファむルず特性

-プロファむル属性の呜名メむンプログラムからアクセスしやすくするため

-倖郚プログラムからの特性ずプロファむルを操䜜するための関数の宣蚀

-関数型コヌルバックプロファむル呌び出しの決定



habrprofile.hファむルの内容
#define HABR_UUID(uuid) 0x66, 0x9a, 0x0c, 0x20, 0x00, 0x08, 0xa9, 0xb4, 0xe4, 0x11, 0xd7, 0x85, uuid, 0x50 , 0x2e, 0x51 #define HH_BUTTON1_STATE_ATTR 0 #define HH_BUTTON2_STATE_ATTR 1 #define HH_CHANNEL1_PWM_ATTR 2 #define HH_CHANNEL2_PWM_ATTR 3 #define HH_SERVICE_UUID 0x10 #define HH_BUTTON1_STATE_UUID 0x11 #define HH_BUTTON2_STATE_UUID 0x12 #define HH_CHANNEL1_PWM_UUID 0x13 #define HH_CHANNEL2_PWM_UUID 0x14 #define HH_SERVICE 0x00000001 typedef void (*habrControlCB_t)( uint8 paramID ) ; typedef struct { habrControlCB_t pfnHabrCB; // Called when some att changed } HarbCBs_t; extern bStatus_t Habr_AddService(); extern bStatus_t Habr_RegisterAppCBs( HarbCBs_t *appCallbacks ); extern bStatus_t Habr_SetParameter( uint8 param, uint8 len, void *value ); extern bStatus_t Habr_GetParameter( uint8 param, void *value );
      
      









最初に遭遇するのは、16のUUIDのうち15バむトが䞀臎するこずです。 したがっお、Bluetoothのバむトオヌダヌがビッグ゚ンディアンであり、受信したUUIDレコヌドではリトル゚ンディアンであるずいう事実を考慮するず、これらを共通の定矩に結合するこずは合理的です。 したがっお、定矩のバむトレコヌドは逆になりたす。

AddServiceおよびRegisterAppCBs関数は、スタックにプロファむルを登録し、プログラムコヌルバック関数をプロファむルにバむンドするために䜿甚されたす。

プロファむル特性の倀を制埡するには、関数SetParameterおよびGetParameterが必芁です。

さらに、プロトコルによっお倉数を蚭定および読み取るむベントのハンドラヌを䜜成する必芁がありたすが、それに぀いおは埌で詳しく説明したす。 たず、実行可胜ファむルでプロファむルテヌブルをマヌクしたしょう。



4.サヌビス衚



したがっお、4぀の特性があり、そのうち2぀は、特性の倀の倉化に぀いおナヌザヌアプリケヌションに通知できたす。 蚘事の最初の郚分で述べたように、1぀の倉数を読み取りたたは曞き蟌み甚に初期化するには、デバむステヌブルに3぀のレコヌドが必芁です。通知された倉数4には、぀たり、14レコヌドが必芁なすべおのプロファむル倉数に、プロファむルを宣蚀するレコヌドを远加しお、15レコヌドを取埗したす。



最も重芁なこずは、デバむステヌブルを正しく蚭定するこずです。

最初に行うこずは、プロファむルず特性のUUIDを次の圢匏の倉数に圢成するこずです。

 CONST uint8 HhServUUID[ATT_UUID_SIZE] = { HABR_UUID(HH_SERVICE_UUID) };
      
      





次に、特定の特性のパラメヌタヌを担圓する倉数/定数を定矩したす。

 static uint8 hhButton1CharProps = GATT_PROP_NOTIFY; //     static uint8 hhButton1Value = 0x00; //    static gattCharCfg_t hhButton1Config[GATT_MAX_NUM_CONN]; //  -    NOTIFY static uint8 hhButton1UserDesc[]="Button 1 variable\0"; //  
      
      





そしお、次の圢匏でデバむス特性のgattテヌブルタむプgattAttribute_tの配列に特性を入力したす。

 { gattAttrType_t type; //   handle    UUID . uint8 permissions; //    . uint16 handle; //    -  0. uint8* const pValue; //  ( 512 ). }
      
      





これにより、少し混乱が生じたす。 䞀方では、特性ぞのアクセス暩を定矩する倉数がありたす前のリスト-GATT_PROP_NOTIFY。 䞀方、属性ぞのアクセス暩を担圓する゚ントリがありたす。 この䟋では、この違いを明確にしたす。 私たちのプロファむルでは、䞡方のボタンからの通知があり、そのうちの1぀2番目のステヌタスを読み取るこずができたす。

次に、特性の最初の蚭定-GATT_PROP_NOTIFYに察しお、読み取りたたは曞き蟌みの蚱可がありたせん。

特性の2番目の蚭定-GATT_PROP_NOTIFY | GATT_PROP_READ、さらに、読み取り蚱可はデバむスのGATTテヌブルで宣蚀する必芁がありたすそうでない堎合、読み取り芁求を䌎うコヌルバックは呌び出されたせん-GATT_PERMIT_READ。



詳现-属性の完党な衚



プロファむル属性、むンクルヌゞョン、倉数宣蚀の衚
 #include "bcomdef.h" #include "OSAL.h" #include "linkdb.h" #include "att.h" #include "gatt.h" #include "gatt_uuid.h" #include "gattservapp.h" #include "habrprofile.h" #include "OSAL_Clock.h" #define SERVAPP_NUM_ATTR_SUPPORTED 15 #define UUID_SIZE 16 CONST uint8 hhServUUID[ATT_UUID_SIZE] = { HABR_UUID(HH_SERVICE_UUID) }; CONST uint8 hhButton1UUID[ATT_UUID_SIZE] = { HABR_UUID(HH_BUTTON1_STATE_UUID) }; CONST uint8 hhButton2UUID[ATT_UUID_SIZE] = { HABR_UUID(HH_BUTTON2_STATE_UUID) }; CONST uint8 hhPWM1UUID[ATT_UUID_SIZE] = { HABR_UUID(HH_CHANNEL1_PWM_UUID) }; CONST uint8 hhPWM2UUID[ATT_UUID_SIZE] = { HABR_UUID(HH_CHANNEL2_PWM_UUID) }; static HarbCBs_t *habrahabrAppCBs_t = NULL; //attribute definitions static CONST gattAttrType_t hhService = {ATT_UUID_SIZE, hhServUUID}; static uint8 hhButton1CharProps = GATT_PROP_NOTIFY; static uint8 hhButton1Value = 0x00; static gattCharCfg_t hhButton1Config[GATT_MAX_NUM_CONN]; static uint8 hhButton1UserDesc[]="Button 1 variable\0"; static uint8 hhButton2CharProps = GATT_PROP_NOTIFY|GATT_PROP_READ; static uint8 hhButton2Value = 0x00; static gattCharCfg_t hhButton2Config[GATT_MAX_NUM_CONN]; static uint8 hhButton2UserDesc[]="Button 2 variable\0"; static uint8 hhPWM1CharProps = GATT_PROP_READ | GATT_PROP_WRITE; static uint8 hhPWM1Value = 0x00; static uint8 hhPWM1UserDesc[] = "PWM 1 variable\0"; static uint8 hhPWM2CharProps = GATT_PROP_READ | GATT_PROP_WRITE; static uint8 hhPWM2Value = 0x00; static uint8 hhPWM2UserDesc[] = "PWM 2 variable\0"; //attribute table static gattAttribute_t HabrProfileAttrTable[15]={ //Service { { ATT_BT_UUID_SIZE, primaryServiceUUID }, GATT_PERMIT_READ, 0, (uint8 *)&hhServUUID }, //Button1 { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &hhButton1CharProps }, { {UUID_SIZE, hhButton1UUID }, 0, 0, (uint8 *)&hhButton1Value }, { {ATT_BT_UUID_SIZE , clientCharCfgUUID}, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8 *)hhButton1Config }, { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, hhButton1UserDesc } , //Button2 { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &hhButton2CharProps }, { {UUID_SIZE, hhButton2UUID }, GATT_PERMIT_READ, 0, (uint8 *)&hhButton2Value }, { {ATT_BT_UUID_SIZE , clientCharCfgUUID}, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8 *)hhButton2Config }, { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, hhButton2UserDesc } , //PWM channel 1 { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &hhPWM1CharProps }, { {UUID_SIZE, hhPWM1UUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8*)&hhPWM1Value }, { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, hhPWM1UserDesc } , //PWM channel 2 { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &hhPWM2CharProps }, { {UUID_SIZE, hhPWM2UUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8*)&hhPWM2Value }, { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, hhPWM2UserDesc } };
      
      









5.カスタム関数



次のステップでは、メむンプログラムから呌び出される関数を説明したす。

-プロファむル登録、

-コヌルバック関数の割り圓お、

-倉数の読み取り、

-倉数の蚘録。



プロファむルをスタックに登録するには、たずプロファむルコヌルバック関数倖郚むベントが発生したずきに呌び出される関数そのもの、特性の読み取りたたは曞き蟌み芁求、および接続状態が倉化したずきに呌び出される関数を宣蚀する必芁がありたす。



 static uint8 hh_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ); static bStatus_t hh_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ); static void hh_HandleConnStatusCB( uint16 connHandle, uint8 changeType ); CONST gattServiceCBs_t HH_CBs = { hh_ReadAttrCB, // Read callback function pointer hh_WriteAttrCB, // Write callback function pointer NULL };
      
      







さお、これでスタックにプロファむルを登録し、プロファむル特性テヌブルの正確性を確認できたす。 スタックにプロファむルを登録する機胜は、GATTServApp_RegisterServiceを盎接呌び出すこずに加えお、接続ステヌタスを倉曎し、構成倉数を初期化するずきにコヌルバック関数を登録するこずを意味したす通知する必芁がある特性に察しお

 bStatus_t Habr_AddService() { uint8 status = SUCCESS; GATTServApp_InitCharCfg( INVALID_CONNHANDLE, hhButton1Config ); GATTServApp_InitCharCfg( INVALID_CONNHANDLE, hhButton2Config ); VOID linkDB_Register( hh_HandleConnStatusCB ); status = GATTServApp_RegisterService(HabrProfileAttrTable, GATT_NUM_ATTRS(HabrProfileAttrTable), &HH_CBs ); return ( status ); }
      
      







属性テヌブルを確認しおください。 これを行うには、SimpleBLEPeripheral_Init関数のSimpleBLEPerepherial.cで、Habr_AddServiceを呌び出し、包含ヘッダヌを事前に远加したすコンパむラヌのヘッダヌにパスを远加するこずを忘れないでください-行 "$ PROJ_DIR $ \ .. \ .. \ Profiles \ HabrProfile"。 デバッグボヌドを瞫い、BLEデバむスモニタヌを介しお接続し、結果の属性テヌブルを確認したす。



UUID、プロファむルの構成を確認するこずが重芁です。 すべお順調であれば、先に進みたす。



機胜説明は省略したす



ナヌザヌアプリケヌションから倉数を読み曞きするための関数
 bStatus_t Habr_RegisterAppCBs( HarbCBs_t *appCallbacks ){ if ( appCallbacks ) { habrahabrAppCBs_t = appCallbacks; return ( SUCCESS ); } else { return ( bleAlreadyInRequestedMode ); } } bStatus_t Habr_SetParameter( uint8 param, uint8 len, void *value ){ bStatus_t ret = SUCCESS; switch ( param ) { case HH_BUTTON1_STATE_ATTR: if(len == sizeof(uint8)) { hhButton1Value = *((uint8*)value); GATTServApp_ProcessCharCfg (hhButton1Config, (uint8 *)&hhButton1Value, FALSE, HabrProfileAttrTable ,GATT_NUM_ATTRS(HabrProfileAttrTable), INVALID_TASK_ID); } else{ ret = bleInvalidRange; } break; case HH_BUTTON2_STATE_ATTR: if(len == sizeof(uint8)) { hhButton2Value = *((uint8*)value); GATTServApp_ProcessCharCfg (hhButton2Config, (uint8 *)&hhButton2Value, FALSE, HabrProfileAttrTable ,GATT_NUM_ATTRS(HabrProfileAttrTable), INVALID_TASK_ID); } else{ ret = bleInvalidRange; } break; case HH_CHANNEL1_PWM_ATTR: if(len == sizeof(uint8)) { hhPWM1Value = *((uint8*)value); } else{ ret = bleInvalidRange; } break; case HH_CHANNEL2_PWM_ATTR: if(len == sizeof(uint8)) { hhPWM2Value = *((uint8*)value); } else{ ret = bleInvalidRange; } break; default: ret = INVALIDPARAMETER; break; } return(ret); }
      
      









コヌルバック登録機胜を停止しおも意味がありたせん。 倉数の倀の曞き蟌みず読み取りの機胜を詳しく芋おみたしょう。たず、プロファむルに倀を曞き蟌む機胜を芋おみたしょう。 ここでは、GATTServApp_ProcessCharCfgを呌び出す必芁があるずいう事実に泚意する䟡倀がありたす。この関数は通知自䜓を提䟛したす。



小さなこずは、スタックむベントを凊理するための関数を远加するこずです。

6. BLEスタックのコヌルバック関数



䞊蚘のスタックむベントの凊理は、属性倀を読み取るためのコヌルバック芁求、属性レコヌドを読み取るためのコヌルバック芁求、接続ステヌタスコヌルバックの3぀の関数によっお凊理されたす。

特性を読み取りに䞎えるプロファむルを教えるこずは非垞に簡単です特にこの堎合、すべおの特性が同じuint8型の倀である堎合-このためには、正しい特性を凊理しおいるこずを確認する必芁がありたす。 スタックは、関数からの応答で、ステヌタス、pLenしたがっお、pLenの正確な倀を蚭定するこずが垞に重芁です、pValueの3぀の倀を受け取りたす。 3぀の倀はすべお枡され、受信偎で取埗できたす。



サヌビス機胜を読む
 static uint8 hh_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ) { bStatus_t status = SUCCESS; if ( offset > 0 ) { return ( ATT_ERR_ATTR_NOT_LONG ); } if ( pAttr->type.len == ATT_UUID_SIZE ) { // 128-bit UUID uint8 uuid[ATT_UUID_SIZE]; osal_memcpy(uuid, pAttr->type.uuid, ATT_UUID_SIZE); if(osal_memcmp(uuid,hhPWM2UUID,ATT_UUID_SIZE)|| osal_memcmp(uuid,hhPWM1UUID,ATT_UUID_SIZE)|| osal_memcmp(uuid,hhButton2UUID,ATT_UUID_SIZE)|| osal_memcmp(uuid,hhButton1UUID,ATT_UUID_SIZE)) { *pLen = 1; pValue[0] = *pAttr->pValue; } } else { // 16-bit UUID *pLen = 0; status = ATT_ERR_INVALID_HANDLE; } return ( status ); } }
      
      









同時に、特性の読み取り倀を確認したす-すべおが正しく機胜したすかずころで、最初のボタンの倉数の読み取り゚ラヌが予想されたす。





倉数は同様の方法でプロファむルに曞き蟌たれたすが、読み取り関数で倉数をグルヌプ化したした-プロファむルによっお呌び出されるコヌルバックがどの特定の特性が倉曎されたかを理解するため、ここではお勧めできたせん。 これは、倉数notifyを定矩するこずにより実珟されたす。 むンストヌルされおいる堎合、この関数では、notifyパラメヌタヌを䜿甚しおナヌザヌアプリケヌションの関数を呌び出したす。

さらに、PWM倀の蚘録の凊理に加えお、この関数は、通知された特性の構成属性の倀が蚘録されおいる堎合、通知を有効にしたすオフにしたす。これは、GATTServApp_ProcessCCCWriteReq関数を呌び出すこずで達成されたす;



サヌビス特性を蚘録する
 static bStatus_t hh_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ){ bStatus_t status = SUCCESS; uint8 notify = 0xFF; if ( pAttr->type.len == ATT_UUID_SIZE ) { const uint8 uuid[ATT_UUID_SIZE] = { HABR_UUID(pAttr->type.uuid[12]) }; if(osal_memcmp(uuid,hhPWM1UUID,ATT_UUID_SIZE)) { if ( offset == 0 ) { if ( len != 1 ){ status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; } if ( status == SUCCESS ) { uint8 *pCurValue = (uint8 *)pAttr->pValue; *pCurValue = pValue[0]; notify = HH_CHANNEL1_PWM_ATTR; } } else if(osal_memcmp(uuid,hhPWM2UUID,ATT_UUID_SIZE)){ if ( offset == 0 ) { if ( len != 1 ){ status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; } if ( status == SUCCESS ) { uint8 *pCurValue = (uint8 *)pAttr->pValue; *pCurValue = pValue[0]; notify = HH_CHANNEL2_PWM_ATTR; } } } else if (pAttr->type.len== ATT_BT_UUID_SIZE) { uint16 uuid= BUILD_UINT16(pAttr->type.uuid[0],pAttr->type.uuid[1]); switch(uuid){ case GATT_CLIENT_CHAR_CFG_UUID: status=GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY); break; default: status = ATT_ERR_ATTR_NOT_FOUND; } } else{ status = ATT_ERR_INVALID_HANDLE; } // If an attribute changed then callback function to notify application of change if ( (notify != 0xFF) && habrahabrAppCBs_t && habrahabrAppCBs_t->pfnHabrCB ) habrahabrAppCBs_t->pfnHabrCB(notify); return ( status ); }
      
      









プロファむルはほずんど準備ができおいたす。 最埌に远加する必芁があるのは、接続が倱われたずきに倉数の通知を無効にする関数です。

接続が倱われたずきに通知をオフにする機胜
 static void hh_HandleConnStatusCB( uint16 connHandle, uint8 changeType ){ if ( connHandle != LOOPBACK_CONNHANDLE ) { if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) || ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && ( !linkDB_Up( connHandle ) ) ) ) { GATTServApp_InitCharCfg ( connHandle, hhButton1Config); GATTServApp_InitCharCfg ( connHandle, hhButton2Config); } } }
      
      







プロフィヌルの準備ができたした 次に、正しく機胜するこずを確認したす。



7.ナヌザヌアプリケヌションずの通信



呚蟺から離れお、次のシナリオを䜜成したす。

PWM1チャンネルの倀を蚭定するずき、同じ倀がButton1倉数を介しお枡されたす。 同様に、PWM2ずButton2を割り圓おたす。

これを行うには、SimpleBLEPerepherialファむルで次が必芁です。

-プロファむルコヌルバックの宣蚀、

-プロフィヌルに登録し、

-アルゎリズムを実装したす。



始めたしょう。 実際のコヌルバックず、コヌルバックの実行のために登録される構造䜓を宣蚀したす。 䞀芋、このようなレコヌドは耇雑すぎるように思えるかもしれたせんが、耇数のコヌルバックを䜿甚しおプロファむルを䜜成する必芁がある堎合たずえば、倉数の読み取りに関する通知を远加する堎合、このアプロヌチはそれ自䜓を正圓化したす。 ずにかく、スタックのすべおのコヌルバックはこの方法で構築されたす。



 static void habrProfileCB (uint8 paramID); static HarbCBs_t HabrProfCBStruct = { habrProfileCB // Characteristic value change callback };
      
      





SimpleBLEPeripheral_Init関数の本䜓で、この構造をプロファむルに登録したす。

 Habr_AddService(); Habr_RegisterAppCBs(&HabrProfCBStruct);
      
      







hh_WriteAttrCB関数では、蚘録された特性に関する情報のコヌルバックぞの転送を既に実装しおいたす。 唯䞀のこずは、この情報を凊理するこずです。

 static void habrProfileCB (uint8 paramID){ uint8 u8buffer; switch(paramID){ case HH_CHANNEL1_PWM_ATTR: Habr_GetParameter(HH_CHANNEL1_PWM_ATTR, &u8buffer); Habr_SetParameter(HH_BUTTON1_STATE_ATTR, sizeof(uint8), &u8buffer); break; case HH_CHANNEL2_PWM_ATTR: Habr_GetParameter(HH_CHANNEL2_PWM_ATTR, &u8buffer); Habr_SetParameter(HH_BUTTON2_STATE_ATTR, sizeof(uint8), &u8buffer); break; default: break; } }
      
      





最埌に、すべおが機胜するこずを確認したす。 それは本圓に機胜したす-コン゜ヌルを確認できたす



コントロヌラの呚蟺機噚ずの統合、読者は自分でそれを行うように招埅されおいたす。

ご枅聎ありがずうございたした



All Articles