BLE工程——透明传输Profile编写(一个特征)
在之前的《BLE工程——透明传输Profile编写(两个特征)》一文中,讲述了透传模块的Profile文件。它包含了一个服务,及这个服务的两个特征,其中一个特征用来发送数据,另一个特征用来接收数据。这个Profile文件功能上虽然没有的任何问题,但是实际上却还不够专业,原因有二:一是它修改自官网例程中的的simpleGATTProfile文件,文件中的变量函数都保留着原先的命名;二是它用了两个特征,实际上一个特征足以,市面上的大多串口透传模块使用的Profile都只使用了一个特征的。所以下面就要重新编写只用一个特征实现的透传模块的Profile文件。
用一个特征实现的Profile文件的图示如下。这个特征实现收发一体的思路是:该特征同时具有可通知(Notify)和无响应写(write with out response)的权限。可通过向这个特征写数据来达到接收数据的功能,特征通过发送通知来实现发送数据的功能。
1、透传的方式通常是串口,所以新建两个文件,分别取名为:SeialNetProfile.c和SerialNetProfile.h。
2、打开SeialNetProfile.h文件,这个文件主要做一些相关的定义及相关函数的生命,如下:
(1)定义特征值的参数的索引值。
#define SERIALNETPROFILE_TRX 0
(2)定义回调函数类型。
typedef void (*serialNetChange_t)( uint8 paramID, uint8 len );
typedef struct
{
serialNetChange_t pfnSerialNetProfileChange;
} serialNetProfileCBs_t;
(3)声明函数。
extern bStatus_t SerialNetProfile_AddService( void );
extern bStatus_t SerialNetProfile_RegisterAppCBs( serialNetProfileCBs_t *appCallbacks );
extern bStatus_t SerialNetProfile_GetParameter( uint8 param, void *value, uint8 len );
3、打开SerialNetProfile.c文件,对文件进行编程。
4、添加头文件,如下:
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"
#include "gapbondmgr.h"
#include "SerialNetProfile.h"
5、定义服务及特征的UUID。
(1)定义透传Profile的服务及特征默认的UUID分别为0xFFE0、0xFFE1。
uint16 serialNetProfile_serv_uuid = 0xFFE0;
uint16 serialNetProfile_trx_uuid = 0xFFE1;
(2)用于保存服务特征UUID在属性中的结构。
uint8 serialNetProfileServUUID[ATT_BT_UUID_SIZE];
uint8 serialNetProfileTRxUUID[ATT_BT_UUID_SIZE];
6、定义服务特征相关的静态变量。
(1)定义回调函数。
static serialNetProfileCBs_t *serialNetProfile_AppCBs = NULL;
(2)定义服务属性。
static CONST gattAttrType_t serialNetProfileService = { ATT_BT_UUID_SIZE, serialNetProfileServUUID };
(3)定义特征的属性。
a.特征权限设置为:可通知、无响应写。
static uint8 serialNetProfileTRxProps = GATT_PROP_WRITE_NO_RSP | GATT_PROP_NOTIFY;
(这里的一定要是无响应的写。)
b.特征的特征值。
static uint8 serialNetProfileTRx[20] = { 0 };
c.特征的特征配置。
static gattCharCfg_t serialNetProfileTRxConfig[GATT_MAX_NUM_CONN];
d.特征的用户描述
static uint8 serialNetProfileTRxUserDesp[8] = "Tx&Rx\0";
7、定义Profile的属性。
#define SERVAPP_NUM_ATTR_SUPPORTED 5
gattAttribute_t serialNetProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
......
}
(1)定义服务的属性。
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID },
GATT_PERMIT_READ,
0,
(uint8 *)&serialNetProfileService
},
(2)定义特征的属性。
a.特征的声明。
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&serialNetProfileTRxProps
},
b.特征的特征值。
{
{ ATT_BT_UUID_SIZE, serialNetProfileTRxUUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
serialNetProfileTRx
},
c.特征的特征配置。
{
{ ATT_BT_UUID_SIZE, clientCharCfgUUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
(uint8 *)serialNetProfileTRxConfig
},
d.特征的用户描述。
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
serialNetProfileTRxUserDesp
},
8、定义Profile的服务的回调函数,
CONST gattServiceCBs_t serialNetProfileCBs =
{
NULL, /* Read callback function pointer */
SerialNetProfile_WriteAttrCB, /* Write callback function pointer */
NULL /* Authorization callback function pointer */
};
由于特征只有无响应写权限和可通知的权限,所以这里只要注册写回调函数。
9、编写注册服务的函数SeialNetProfile_AddSerive()如下:
bStatus_t SerialNetProfile_AddService( void )
{
uint8 status = SUCCESS;
/* Set service and characteristic UUID for attribute */
serialNetProfileServUUID[0] = LO_UINT16(serialNetProfile_serv_uuid);
serialNetProfileServUUID[1] = HI_UINT16(serialNetProfile_serv_uuid);
serialNetProfileTRxUUID[0] = LO_UINT16(serialNetProfile_trx_uuid);
serialNetProfileTRxUUID[1] = HI_UINT16(serialNetProfile_trx_uuid);
/* Initialize Client Characteristic Configuration attributes */
GATTServApp_InitCharCfg( INVALID_CONNHANDLE, serialNetProfileTRxConfig );
/* Register with Link DB to receive link status change callback */
VOID linkDB_Register( SerialNetProfile_HandleConnStatusCB );
/* Register GATT attribute list and CBs with GATT Server App */
status = GATTServApp_RegisterService( serialNetProfileAttrTbl,
GATT_NUM_ATTRS( serialNetProfileAttrTbl ),
&serialNetProfileCBs );
return ( status );
}
10、编写注册应用回调函数的函数SerialNetProfile_RegisterAppCBs(),如下:
bStatus_t SerialNetProfile_RegisterAppCBs( serialNetProfileCBs_t *appCallbacks )
{
if ( appCallbacks )
{
serialNetProfile_AppCBs = appCallbacks;
return ( SUCCESS );
}
else
{
return ( bleAlreadyInRequestedMode );
}
}
11、编写获取Profile参数的函数SerialNetProfile_GetParamer(),如下:
bStatus_t SerialNetProfile_GetParameter( uint8 param, void *value, uint8 len )
{
bStatus_t ret = SUCCESS;
switch ( param )
{
case SERIALNETPROFILE_TRX:
osal_memcpy(value, serialNetProfileTRx, len);
break;
default:
ret = INVALIDPARAMETER;
break;
}
return ( ret );
}
12、编写写特征属性的回调函数SerialNetProfile_WriteAttrCBs(),如下:
static bStatus_t SerialNetProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset )
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
/* If attribute permissions require authorization to write, return error */
if ( gattPermitAuthorWrite( pAttr->permissions ) )
{
/* Insufficient authorization */
return ( ATT_ERR_INSUFFICIENT_AUTHOR );
}
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
/* 16-bit UUID */
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
if (uuid == serialNetProfile_trx_uuid)
{
/* Write the value */
if ( status == SUCCESS )
{
uint8 *pCurValue = (uint8 *)pAttr->pValue;
osal_memcpy(pCurValue, pValue, len);
if( pAttr->pValue == serialNetProfileTRx )
{
notifyApp = SERIALNETPROFILE_TRX;
}
}
}
else if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
{
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue,
len,offset, GATT_CLIENT_CFG_NOTIFY );
}
else
{
status = ATT_ERR_ATTR_NOT_FOUND;
}
}
else
{
/* 128-bit UUID */
status = ATT_ERR_INVALID_HANDLE;
}
if ( (notifyApp != 0xFF ) && serialNetProfile_AppCBs &&
serialNetProfile_AppCBs->pfnSerialNetProfileChange )
{
serialNetProfile_AppCBs->pfnSerialNetProfileChange( notifyApp, len);
}
return ( status );
}
13、编写Profile连接状态改变处理函数SerialNetProfile_HandleConnStatus(),如下:
static void SerialNetProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
/* Make sure this is not loopback connection */
if ( connHandle != LOOPBACK_CONNHANDLE )
{
/* Reset Client Char Config if connection has dropped */
if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) ||
( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
( !linkDB_Up( connHandle ) ) ) )
{
GATTServApp_InitCharCfg( connHandle, serialNetProfileTRxConfig );
}
}
}
如上就是蓝牙透传所需的Profile文件。使用该Profile的蓝牙模块,连接手机后,会显示如下: