注:(本文基于我自己定义的一个服务TEMProfile,但适用其他服务)

1.特征值是什么

  一个蓝牙协议栈中,包含了多个服务,一个服务里又包含了多个特征值,每个特征值都有其相关的一些信息。

  我们与蓝牙进行通信的时候,就是通过读写这些特征值,来获得数据。

2.特征值的属性

  一个特征值里面基本需要的变量是——

  1.UUID码  

  2.权限属性 :基本就是 可读、可写、可通知这些了。(通知是表示允许数据主动发送)

  3.内容

  4.描述:这个特征值的名称

3.属性表

  一个服务里,所有的特征值中的每个变量都有相应的属性,所有的属性都放在一个数组中,这个数组称之为属性表

  

  一个变量的属性表包含四个内容,

  1.type   2.permission   3.handle   4.pValue

  

  属性表其实就是定义了一个 gattAttribute_t类型的数组。

  需要注意的是,属性表中,除了特征值的属性,第一个还要添加服务的属性

 

1   //TEMProfile Service
2   {
3     {ATT_BT_UUID_SIZE,primaryServiceUUID},    //type
4     GATT_PERMIT_READ,                         //permissions
5     0,                                        //handle
6     (uint8*)&TEMProfileService                //pValue
7   },

4.增添一个新的特征值

  (1)Define出配置属性的数值,用以填写配置属性。

 1 // Profile Parameters
 2 #define TEMPROFILE_CHAR1                      0
 3 #define TEMPROFILE_CHAR2                      1
 4
 5 // Simple Profile Service UUID
 6 #define TEMPROFILE_SERV_UUID                  0xFF00
 7
 8 // Key Pressed UUID
 9 #define TEMPROFILE_CHAR1_UUID                 0xFF01
10 #define TEMPROFILE_CHAR2_UUID                 0xFF02
11
12 // Simple Keys Profile Services bit fields
13 #define TEMPROFILE_SERVICE                    0x00000001
14
15 // Length of Characteristic 2 in bytes
16 #define TEMPROFILE_CHAR2_LEN                  12

  其中UUID号有特定的范围,应避免与其他服务UUID冲突。

  这里增添了两个特征值,特征值2是数组型的,所以需要定义一个长度TEMPROFILE_CHAR2_LEN。

  

 (2)定义每个特征值的属性变量(以特征值2为例)

 

1 static  uint8 TEMProfileChar2Prop = GATT_PROP_READ ;
2 // TEM Profile char2 Value
3 static  uint8 TEMProfileChar2[TEMPROFILE_CHAR2_LEN] = {0};
4 // TEM Profile char2 Description
5 static  uint8 TEMProfileChar2Desp[6]="Data\0";

  配置权限,内容,描述。

  

 (3)由于属性表中的Value属性比较特殊,需要将其UUID号定义出来。具体原因暂时不是很理解。

1 CONST uint8 TEMProfilechar2UUID[ATT_BT_UUID_SIZE]=
2 {
3   LO_UINT16(TEMPROFILE_CHAR2_UUID),HI_UINT16(TEMPROFILE_CHAR2_UUID)
4 };

  

 (4)填写属性表

 1 static gattAttribute_t  TEMProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED]=
 2 {
 3   //TEMProfile Service
 4   {
 5     {ATT_BT_UUID_SIZE,primaryServiceUUID},   //type
 6     GATT_PERMIT_READ,                        //permissions
 7     0,                                       //handle
 8     (uint8*)&TEMProfileService              //pValue
 9   },
10
11   //char 1 Declaration
12   {
13     {ATT_BT_UUID_SIZE,characterUUID},
14     GATT_PERMIT_READ,
15     0,
16     &TEMProfileChar1Prop
17   },
18
19   //char 1 Value
20   {
21     {ATT_BT_UUID_SIZE,TEMProfilechar1UUID},   // !! Attribue Value UUID need definition
22     GATT_PERMIT_READ  | GATT_PERMIT_WRITE,
23     0,
24     &TEMProfileChar1
25   },
26
27   //char 1 Description
28    {
29     {ATT_BT_UUID_SIZE,charUserDescUUID},
30     GATT_PERMIT_READ,
31     0,
32     TEMProfileChar1Desp
33   },
34
35   //char 2 Declaration
36   {
37     {ATT_BT_UUID_SIZE,characterUUID},
38     GATT_PERMIT_READ,
39     0,
40     &TEMProfileChar2Prop
41   },
42
43   //char 2 Value
44   {
45     {ATT_BT_UUID_SIZE,TEMProfilechar2UUID},   // !! Attribue Value UUID need definition
46     GATT_PERMIT_READ,
47     0,
48     TEMProfileChar2
49   },
50
51   //char 2 Description
52     {
53     {ATT_BT_UUID_SIZE,charUserDescUUID},
54     GATT_PERMIT_READ,
55     0,
56     TEMProfileChar2Desp
57     },
58
59 };

  注意:  这里每个属性都有一个权限属性(如GATT_PERMIT_READ),之前定义特征值时也有一个权限变量(如GATT_PROP_WRITE) 两者作用对象不一样。

       可以这样理解,每个特征值都是一个大宝箱,里面还有许多个小宝箱,要打开他们需要不同的钥匙。

  至此,一个特征值的基本定义和声明就已经做完了。但我们需要使用这个特征值,所以要在调用到特征值的函数中,添加上它。

(5)修改Get_Parameter函数和Set_Parameter函数、ReadAttrCB函数、WriteAttrCB函数

  在服务中,基本是通过这四个函数对特征值进行读写。后两个是回调函数。

  在TEMProfile_SetParameter()函数中,新增一个case。

 1 bStatus_t TEMProfile_SetParameter(  uint8 param, uint8 len,  void *value)
 2 {
 3   bStatus_t ret = SUCCESS;
 4   switch  ( param )
 5   {
 6     case TEMPROFILE_CHAR1 :
 7       if( len == sizeof(uint8)  )
 8       {
 9         TEMProfileChar1 = *((uint8*)value);
10       }
11       else
12       {
13         ret = bleInvalidRange;
14       }
15       break;
16
17     case TEMPROFILE_CHAR2 :
18       if( len == TEMPROFILE_CHAR2_LEN )
19       {
20          VOID osal_memcpy( TEMProfileChar2, value, TEMPROFILE_CHAR2_LEN );
21       }
22       else
23       {
24         ret = bleInvalidRange;
25       }
26       break;
27
28     default  :
29       ret = INVALIDPARAMETER;
30       break;
31   }
32
33   return ret;
34
35  }

  这是一个设置特征值内容的函数,参数param是特征值,len是内容的长度,value是新内容的地址。

  以特征值2为例,先判断新内容的长度是否符合原先特征值定义的内容长度。如果一致,则将新内容填写进入特征值的内容TEMProfileChar2。

   

  TEMProfile_GetParameter()函数同理

 1 bStatus_t TEMProfile_GetParameter(  uint8 param,  void  *value)
 2 {
 3   bStatus_t ret = SUCCESS;
 4   switch  ( param )
 5   {
 6   case TEMPROFILE_CHAR1 :
 7     *((uint8*)value)  = TEMProfileChar1;
 8     break;
 9
10   case TEMPROFILE_CHAR2 :
11     VOID osal_memcpy( value, TEMProfileChar2, TEMPROFILE_CHAR2_LEN );
12     break;
13
14   default:
15     ret = INVALIDPARAMETER;
16     break;
17   }
18
19   return  (ret);
20 }

然后是TEMProfile_WriteAttrCB()函数

 1 static  bStatus_t TEMProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
 2                                        uint8 *pValue, uint8 len, uint16 offset )
 3 {
 4    bStatus_t status  = SUCCESS;
 5    uint8 notifyApp = 0xFF;
 6
 7    if  ( gattPermitAuthorWrite( pAttr->permissions ) )
 8    {
 9       return ( ATT_ERR_INSUFFICIENT_AUTHOR );
10    }
11
12    if ( pAttr->type.len == ATT_BT_UUID_SIZE )
13    {
14       uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
15       switch  (uuid)
16       {
17          case TEMPROFILE_CHAR1_UUID:
18
19            if( offset  ==  0 )
20            {
21                if( len !=  1 )
22                {
23                   status = ATT_ERR_INVALID_VALUE_SIZE;
24                }
25            }
26            else
27            {
28              status = ATT_ERR_ATTR_NOT_LONG;
29            }
30
31
32             if ( status == SUCCESS )
33             {
34               uint8 *pCurValue = (uint8 *)pAttr->pValue;
35               *pCurValue = pValue[0];
36               notifyApp = TEMPROFILE_CHAR1;
37             }
38
39          break;
40
41          default:
42             status = ATT_ERR_ATTR_NOT_FOUND;
43          break;
44        }
45     }
46     else
47     {
48       status = ATT_ERR_INVALID_HANDLE;
49     }
50
51   if ( (notifyApp != 0xFF ) && TEMProfile_AppCBs && TEMProfile_AppCBs->pfnTEMProfileChange )
52   {
53     TEMProfile_AppCBs->pfnTEMProfileChange( notifyApp );
54   }
55
56   return ( status );
57 }

以及ReadAttrCb函数

 1 static  uint8 TEMProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
 2                                     uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
 3 {
 4   bStatus_t status  = SUCCESS;
 5
 6   if( gattPermitAuthorRead( pAttr->permissions))
 7   {
 8     return  (ATT_ERR_INSUFFICIENT_AUTHOR);
 9   }
10
11   if( offset  > 0)
12   {
13     return  (ATT_ERR_ATTR_NOT_LONG);
14   }
15
16   if ( pAttr->type.len == ATT_BT_UUID_SIZE )
17   {
18     uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
19     switch( uuid )
20     {
21       //must have read permisson
22     case TEMPROFILE_CHAR1_UUID:
23         *pLen =1;
24         pValue[0] = *pAttr->pValue;
25         break;
26
27     case TEMPROFILE_CHAR2_UUID:
28         *pLen = TEMPROFILE_CHAR2_LEN;
29         VOID osal_memcpy( pValue, pAttr->pValue, TEMPROFILE_CHAR2_LEN );
30         break;
31
32     default:
33         *pLen = 0;
34         status=ATT_ERR_ATTR_NOT_FOUND;
35         break;
36     }
37   }
38   else
39   {
40     *pLen = 0;
41     status=ATT_ERR_INVALID_HANDLE;
42   }
43
44     return  (status);
45
46 }

至此服务的特征值已经修改完,接下来需要去应用层进行设置。

(6)在SimpleBLEPeripheral_Init()函数中,初始化特征值。

1   uint8 TEMProfile_Char1Vaule=1;
2   uint8 TEMProfile_Char2Value[TEMPROFILE_CHAR2_LEN]="2017.03.11\0";
3   TEMProfile_SetParameter(  TEMPROFILE_CHAR1, sizeof(uint8),  &TEMProfile_Char1Vaule );
4   TEMProfile_SetParameter(  TEMPROFILE_CHAR2, TEMPROFILE_CHAR2_LEN,  TEMProfile_Char2Value );

(7)回调函数simpleProfileChangeCB( )中增添特征值。

  该函数是当特征值改变时,即会被调用。

  本例中,当特征值改变时,LCD上的数据也会随之改变。

 1 static void simpleProfileChangeCB( uint8 paramID )
 2 {
 3   uint8 newValue;
 4
 5   switch( paramID )
 6   {
 7     case SIMPLEPROFILE_CHAR1:
 8       SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, &newValue );
 9
10       #if (defined HAL_LCD) && (HAL_LCD == TRUE)
11         HalLcdWriteStringValue( "Char 1:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );
12       #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)
13
14       break;
15
16     case SIMPLEPROFILE_CHAR3:
17       SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR3, &newValue );
18
19       #if (defined HAL_LCD) && (HAL_LCD == TRUE)
20         HalLcdWriteStringValue( "Char 3:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );
21       #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)
22
23       break;
24
25     default:
26       // should not reach here!
27       break;
28   }
29 }

至此,特征值的新增即完成了。

APP中已可以发现这两个特征值。

转载于:https://www.cnblogs.com/asam/p/6535374.html

BLE4.0教程四 新增特征值(CC2541)相关推荐

  1. IoT:BLE4.0教程一 蓝牙协议连接过程与广播分析

    IoT:BLE4.0教程一 蓝牙协议连接过程与广播分析 1.蓝牙简介 什么是蓝牙4.0 蓝牙无线技术是使用范围最广泛的全球短距离无线标准之一,蓝牙4.0版本涵盖了三种蓝牙技术,即传统蓝牙.高速蓝牙和低 ...

  2. RxJava2.0教程(四)

    RxJava2.0教程(四) 上一节里我们学习了只使用Observable如何去解决上下游流速不均衡的问题, 之所以学习这个是因为Observable还是有很多它使用的场景, 有些朋友自从听说了Flo ...

  3. BLE4.0教程一 蓝牙协议连接过程与广播分析

    1.蓝牙简介 什么是蓝牙4.0 蓝牙无线技术是使用范围最广泛的全球短距离无线标准之一,蓝牙4.0版本涵盖了三种蓝牙技术,即传统蓝牙.高速蓝牙和低功耗蓝牙技术,将三种规范合而为一.它继承了蓝牙技术在无线 ...

  4. CC2541蓝牙BLE4.0主从透传工程

    .前言 小弟初来乍到,这是小弟的第一篇博客,暂时还在上学,没有什么工作经验,本篇博客主要记录我在这几天学习BLE协议协议栈的一点心得体会,并用一个主从透传的实验来记录过程,如有错误之处,还望各位大佬多 ...

  5. iOS蓝牙BLE4.0通信功能

    概述 iOS蓝牙BLE4.0通信功能,最近刚学的苹果,为了实现蓝牙门锁的项目,找了一天学习了下蓝牙的原理,亲手测试了一次蓝牙的通信功能,结果成功了,那么就把我学习的东西分享一下. 详细 代码下载:ht ...

  6. Flask从入门到做出一个博客的大型教程(四)

    Flask从入门到做出一个博客的大型教程(四) 在开始之前,先来看下项目的整体结构. flask ├── app │ ├── forms.py │ ├── __init__.py │ ├── mode ...

  7. 零基础快速入门SpringBoot2.0教程 (二)

    一.SpringBoot2.x使用Dev-tool热部署简介:介绍什么是热部署,使用springboot结合dev-tool工具,快速加载启动应用官方地址:https://docs.spring.io ...

  8. excel计算机不准确,Excel小教程四十一:关于Excel计算不准确,我们应该这样解决!...

    原标题:Excel小教程四十一:关于Excel计算不准确,我们应该这样解决! 我们有时候在用excel进行计算的时候,会遇到Excel计算不准确的的时候! 别怕,先听小雅为您一一道来! 5.1-5.2 ...

  9. docker 打包镜像_Spring Boot2 系列教程(四十一)部署 Spring Boot 到远程 Docker 容器

    不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...

最新文章

  1. oracle字符界面安装,怎么解决oracle在linux 纯字符界面安装有关问题
  2. 在CentOS 6.3 64bit上安装最新版tsar并监控ATS 5.3
  3. UA MATH575B 数值分析下 统计物理的随机模拟方法4
  4. CVE-2015-3636(pingpong root) android内核 UAF漏洞分析
  5. javascript - dom
  6. LCA(最近公共祖先)
  7. C# RSA在服务上使用出现拒绝方法错误的解决方法
  8. 身份证阅读器在人事管理中的運用
  9. 面试前的准备和注意事项(非常详细)
  10. mysql订单表和订单详情表_订单详情表,与,订单表 怎么做?
  11. opencv remap matlab,C++ Opencv remap()重映射函数详解及使用示例
  12. c c++ 信息服务查询服务器
  13. 搜索衬线字体和无衬线字体的区别
  14. TeamPlan小组工作计划管理系统想法
  15. 烛光晚餐矢量图(编号:82204)_日常生活_矢量人物_矢量素材
  16. CSDN-Adobe Flex/AIR专区——http://flex.csdn.net/ 很多资料
  17. Android Ringtone 自定义铃声介绍
  18. XML的基本概念和Android下的使用
  19. 基于Java实现的GRE(美国研究生入学考试)学习系统
  20. 图片上传到阿里云OSS存储

热门文章

  1. 深挖前端 JavaScript 知识点 —— 史上最全面、最详细的 Cookie 总结
  2. 初级Web前端工程师是什么?初级Web前端工程师的技术体系有哪些?
  3. 7个前端新手常见误区,千万要避开!
  4. 魔术方法 python_python所有的魔术方法
  5. 编程利器 Beyond Compare 的使用
  6. 2021年第一篇原创——Spring核心初探~
  7. 【Keras】Win10系统 + Anaconda+TensorFlow+Keras 环境搭建教程
  8. linux系统下修改某个文件夹下所有的文件权限
  9. 利用Adorner制作用于图像裁切的选择框
  10. SugarNMS分布式网络运维部署攻略