当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 >
BLE添加特征值
时间:2018-08-16作者:华清远见

一、 什么是特征值

特征值就是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调试软件,可以轻松的操作特征值。


发表评论

全国咨询电话:400-611-6270,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(3337544669), 徐老师QQ(1462495461), 刘老师 QQ(3108687497)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2018 华清远见教育集团 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号