一、 什么是特征值
特征值就是BLE協議棧向外提供的一個數據接口,藍牙之間的數據傳輸終落實在特征值上。在BLE協議棧的GATT層中封裝了若干服務(service),而在每一個服務中又有若干特征值(characters),特征值可以是任意類型的數據。藍牙之間的數據傳輸靠協議棧提供的write和read函數,而這兩個函數就是在操作特征值
二、UUID
UUID就是通用唯一識別碼。在藍牙協議棧中可能會有多個服務,每個服務會有多個特征值,而這些服務或者特征值都有一個唯一的ID,這樣就可以區分了。這個UUID是其他設備設置藍牙服務和特征值的唯一方法。
三、增加特征值
在BLE協議棧中,GATT層定義了特征值和服務。下面就以SimpleBLEPeripheral為例,增加一個特征值。在simpleGATTprofile.c中,已經有定義好的特征值,參考已有的特征值就可以順利添加自己的特征值
1) 修改頭文件simpleGATTprofile.h
頭文件中定義了特征值的UUID,以及長度和默認值
//特征值UUID
#define SIMPLEPROFILE_CHAR5_UUID 0xFFF5
//特征值長度
#define SIMPLEPROFILE_CHAR5_LEN 5
2)添加特征值相關變量
包括特征值的讀寫權限、變量名、展現給用戶的名字
//特征值初始化
// 特征值屬性,讀或者寫
static uint8 simpleProfileChar6Props = GATT_PROP_READ | GATT_PROP_WRITE;
// 值,可以向其寫入數據,也可以讀出數據。這里是一個字符數組
static uint8 simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN] = {0};
// 用戶描述,展現給用戶的名字
static uint8 simpleProfileChar6UserDesp[17] = "Characteristic 6\0";
//提取uuid,uuid定義在頭文件中
CONST uint8 simpleProfilechar5UUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(SIMPLEPROFILE_CHAR5_UUID), HI_UINT16(SIMPLEPROFILE_CHAR5_UUID)
};
3)將特征值加入屬性表
特征值由服務統一管理,所有的特征值都會在一個服務的屬性表中呈現出來,每增加一個特征值,它的相關變量就要在添加到屬性表中
//特征值初始化
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
// Simple Profile Service
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
GATT_PERMIT_READ, /* permissions */
0, /* handle */
(uint8 *)&simpleProfileService /* pValue */
},
// Characteristic 1 Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&simpleProfileChar1Props
},
// Characteristic Value 1
{
{ ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
GATT_PERMIT_AUTHEN_READ | GATT_PERMIT_AUTHEN_WRITE,
0,
&simpleProfileChar1
},
// Characteristic 1 User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
simpleProfileChar1UserDesp
},
。。。。。
// Characteristic 5 Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&simpleProfileChar5Props
},
// Characteristic Value 5
{
{ ATT_BT_UUID_SIZE, simpleProfilechar5UUID },
GATT_PERMIT_AUTHEN_READ| GATT_PERMIT_AUTHEN_WRITE,
0,
simpleProfileChar5
},
// Characteristic 5 User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
simpleProfileChar5UserDesp
},
4)修改屬性表的長度
每增加一個特征值,屬性表的長度也會增加,因此要修改屬性表的長度。在文件的一開始就聲明了屬性表的長度
#define SERVAPP_NUM_ATTR_SUPPORTED 24
5)修改SimpleProfile_SetParamete和SimpleProfile_GetParamete函數
這是操作特征值的兩個函數,set函數可以用來初始化特征值,get函數可以用來提取特征值。一般我們定義的特征值都是uint8類型的數組,因此無論set還是get,都可以使用copy函數來完成,同時要注意實際的長度
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )
{
bStatus_t ret = SUCCESS;
switch ( param )
{
。。。。。。。
case SIMPLEPROFILE_CHAR5:
if ( len <= SIMPLEPROFILE_CHAR5_LEN )
{
//將value值復制到特征值5,同時注意長度
VOID osal_memcpy( simpleProfileChar5, value, len );
}
else
{
ret = bleInvalidRange;
}
break;
default:
ret = INVALIDPARAMETER;
break;
}
return ( ret );
}
bStatus_t SimpleProfile_GetParameter( uint8 param, void *value )
{
bStatus_t ret = SUCCESS;
switch ( param )
{
。。。。。。
case SIMPLEPROFILE_CHAR5:
//將特征值5復制到value
VOID osal_memcpy( value, simpleProfileChar5, osal_strlen(simpleProfileChar5));
break;
default:
ret = INVALIDPARAMETER;
break;
}
return ( ret );
}
6)修改simpleProfile_ReadAttrCB和simpleProfile_WriteAttrCB函數
上面已經有了set和get函數,可以實現對特征值的讀寫,那么這里為何又來了一對read和write呢???set和get是用來本地讀寫特征值的,而read和write則是網絡上的讀寫。什么意思呢,當藍牙網絡的另一端想要讀取特征值的時候,協議棧就會自動回調這個read函數,然后將讀取的結果傳輸的網絡的另一端。當然,寫操作也是一樣的。
static uint8 simpleProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
{
bStatus_t status = SUCCESS;
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch ( uuid )
{
。。。。。。
case SIMPLEPROFILE_CHAR5_UUID:
*pLen = osal_strlen(pAttr->pValue);
VOID osal_memcpy( pValue, pAttr->pValue, osal_strlen(pAttr->pValue) );
break;
default:
// Should never get here! (characteristics 3 and 4 do not have read permissions)
*pLen = 0;
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
}
return ( status );
}
static bStatus_t simpleProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 len, uint16 offset )
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch ( uuid )
{
。。。。。。
case SIMPLEPROFILE_CHAR5_UUID:
//Validate the value 檢測輸入數據是否合法
// Make sure it's not a blob oper
if ( offset == 0 ) //是第一字節
{
if ( len >= SIMPLEPROFILE_CHAR6_LEN )
{
status = ATT_ERR_INVALID_VALUE_SIZE;
} //若輸入長度不對,status為
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;//不是第一字節
}
if ( status == SUCCESS )
{
//清空緩沖區
osal_memset(pAttr->pValue, '\0', SIMPLEPROFILE_CHAR6_LEN) ;
//復制
VOID osal_memcpy( pAttr->pValue, pValue, len);
notifyApp = SIMPLEPROFILE_CHAR6;
}
break;
default:
// Should never get here! (characteristics 2 and 4 do not have write permissions)
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
}
7)修改simpleBLEPeripheral.c
A、在SimpleBLEPeripheral_Init函數中可以使用set方法對特征值做初始化操作
SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, 5, charValue5 );
B、修改simpleProfileChangeCB
當特征值被網絡的另一端修改之后,協議棧會回調這個函數,通知當前特征值發生變化
static void simpleProfileChangeCB( uint8 paramID )
{
uint8 newValue;
uint8 *val;
switch( paramID )
{
。。。。。。
case SIMPLEPROFILE_CHAR5:
val = osal_msg_allocate(15);
//提取特征值,注意這里使用的是get方式
SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR5, val );
break;
default:
// should not reach here!
break;
}
SerialPrint(val);
}
四、測試
如何驗證特征值已經被成功的添加呢,基于上面的例子,我們需要一個BLE主機設備來讀取特征值。建議使用手機來完成測試,因為你很難確保你的主機代碼是正確的。從網絡上下載BLE調試軟件,可以輕松的操作特征值。