基于Cannon小钢炮蓝牙通信添加服务与特性


实验目的

本文主要目的是添加服务与特性,在BLE Scanner中实现读,写,通知。

实验环境

  1. 软件: stm32cubemx ,keilMDK,BLE Scanner, WireShark
  2. 硬件:Cannon小钢炮
  3. 资料:BlueNRG-1、BlueNRG-2 BLE 栈 v2.x 编程指南

实验步骤

STM32CubeMx配置

软件设计

蓝牙协议栈

要实现蓝牙之间的通信,首先得知道蓝牙之间如何进行通信的。
一般而言,我们把某个协议的实现代码称为协议栈,BLE协议栈就是实现低功耗蓝牙协议的代码,理解和掌握BLE协议是实现BLE协议栈的前提。接下来对BLE协议进行简单介绍。
简单来说,BLE协议栈主要用来对应用数据进行层层封包,以生成一个满足BLE协议的空中数据包,也就是说,把应用数据包裹在一系列的帧头(header)和帧尾(tail)中。
蓝牙协议规定了两个层次的协议,分别为蓝牙核心协议(Bluetooth Core)和蓝牙应用层协议(Bluetooth Application)。而蓝牙核心协议(Bluetooth Core)又包含BLE Controller和BLE Host两部分。

BLE低功耗蓝牙核心协议层

通用访问配置文件层(GAP) :它定义了设备如何彼此发现,建立连接以及如何实现绑定,同时描述了设备如何成为广播者和观察者,实现无需连接的数据传输,并且定义了如何用不同类型的地址来实现隐私性和可解性。也就是说它定义了一个蓝
牙设备所需具备的基本要求。
通用属性配置文件层(GATT):GATT用来规范attribute中的数据内容,并运用group(分组)的概念对attribute进行分类管理。他定义了使用ATT协议的框架,被用于服务,特征,描述符发现,特征读写,写入,指示和通知。
属性协议层(ATT):简单来说,ATT层用来定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据。BLE协议栈中,开发者接触最多的就是ATT。BLE引入了attribute概念,用来描述一条一条的数据。Attribute除了定义数据,同时定义该数据可以使用的ATT命令,因此这一层被称为ATT层。
安全管理层(SM):当两个设备要在连接期间进行通信加密时,安全管理器使用配对流程。此流程允许通过交换身份信息来验证两个设备,交换信息是为了创建可作为受信任关系或(单个)安全连接基础的安全密钥。有一些方法用于执行配对过程。
逻辑链路控制及自适应协议层(L2CAP):支持更高层协议复用、数据包分割和重组操作以及服务信息质量的通知。
主机控制器接口(HCI):主机和控制器之间提供了一种通信方式。本文使用的SPI接口实现HOST与Control的连接。
链路层(LL):LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。LL层要做的事情非常多,比如具体选择哪程度 个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者ATT。
物理层(PHY):PHY层用来指定BLE所用的无线频段,调制解调方式和方法等。

添加服务与特性
  1. 打开keilMDK,找到gatt_db.c文件,添加服务与特性的UUID,定义服务与特性句柄
    #define COPY_USART1_SERVICE_UUID(uuid_struct)          COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x00,0x00,0x03,0x11,0xe1,0x9a,0xb4,0x00,0x02,0xa5,0xd5,0xc5,0x1b)
    #define COPY_SEND_AND_RECEIVE_CHAR_UUID(uuid_struct)   COPY_UUID_128(uuid_struct,0x01,0x00,0x00,0x00,0x00,0x01,0x11,0xe1,0x9a,0xb4,0x00,0x02,0xa5,0xd5,0xc5,0x1b)
    uint16_t UTServW2STHandle, SendandreceiveCharHadle;
  2. 要实现添加服务,首先得知道这几个函数:
    aci_gatt_add_serv功能是将服务添加到GATT服务器中,入口参数如下:
tBleStatus aci_gatt_add_serv(uint8_t service_uuid_type,   //服务类型UUID(16位或128位)
                 const uint8_t* service_uuid,             //基于UUID类型字段的16位或128位UUID
                 uint8_t service_type,                    //主要或辅助服务
                 uint8_t max_attr_records,                //添加到此服务的最大属性记录数(包括服务声明本身)
                 uint16_t *serviceHandle);                //务的句柄。将此服务添加到服务后,服务器将句柄分配给该服务。服务器也将此服务的句柄范围从serviceHandle分配给<serviceHandle + max_attr_records>。

aci_gatt_add_char功能是为服务添加特征。入口参数如下:

tBleStatus aci_gatt_add_char(uint16_t serviceHandle, //向其添加特征的服务的句柄。
                 uint8_t charUuidType,               //特征UUID的类型(16位或128位)。
                 const uint8_t* charUuid,            //16位或128位UUID
                 uint8_t charValueLen,              //特征值的最大长度
                 uint8_t charProperties,            //特征属性,
                 uint8_t secPermissions,            //添加的特征的安全权限
                 uint8_t gattEvtMask,               //位掩码,用于启用将由GATT服务器发送到应用程序的事件
                 uint8_t encryKeySize,              //此属性的最小加密密钥大小要求。有效范围:7到16。
                 uint8_t isVariable,                //如果属性具有可变长度值字段(1)或没有(0)。
                 uint16_t* charHandle);             //已添加特征的句柄。它是特征声明的句柄。

在gatt_db.c文件中,添加自己所写的服务与特性,在初始化过程中添加此函数,实现服务与特性的添加。代码如下:


tBleStatus Add_USARTServW2ST_Service(void)
{
  tBleStatus ret;
  int32_t NumberOfRecords=1;
  uint8_t uuid[16];

  COPY_USART1_SERVICE_UUID(uuid);
  BLUENRG_memcpy(&service_uuid.Service_UUID_128, uuid, 16);
  ret = aci_gatt_add_serv(UUID_TYPE_128, service_uuid.Service_UUID_128, PRIMARY_SERVICE,
                          1+3*NumberOfRecords, &UTServW2STHandle);

  if (ret != BLE_STATUS_SUCCESS) {
    goto fail;
  }

  COPY_SEND_AND_RECEIVE_CHAR_UUID(uuid);
  BLUENRG_memcpy(&char_uuid.Char_UUID_128, uuid, 16);
  ret =  aci_gatt_add_char(UTServW2STHandle, UUID_TYPE_128, char_uuid.Char_UUID_128,
                           2+4+2,
                           CHAR_PROP_NOTIFY|CHAR_PROP_READ|CHAR_PROP_WRITE,
                           ATTR_PERMISSION_NONE,
                           GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP|GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP|GATT_NOTIFY_ATTRIBUTE_WRITE,
                           16, 1, &SendandreceiveCharHadle);

  if (ret != BLE_STATUS_SUCCESS) {
    goto fail;
  }

  return BLE_STATUS_SUCCESS;

fail:
  return BLE_STATUS_ERROR;
}
  1. 在sensor.c文件中添加属性事件。user_notify函数功能作用是回调处理ACI事件。在 case EVT_VENDOR:语句中,是对属性进行判断。EVT_BLUE_GATT_READ_PERMIT_REQ是GATT读允许请求,EVT_BLUE_GATT_WRITE_PERMIT_REQ为GATT写允许请求,EVT_BLUE_GATT_ATTRIBUTE_MODIFIED为GATT通知。当客户端对服务器进行读,写,通知时,程序将进入此函数进行相关操作。

    case EVT_VENDOR:
     {
       evt_blue_aci *blue_evt = (void*)event_pckt->data;
       switch(blue_evt->ecode){
       case EVT_BLUE_GATT_ATTRIBUTE_MODIFIED:
        {
          evt_gatt_attr_modified_IDB05A1 *nt = (void*)blue_evt->data; 
          //uint8_t a = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);
          //aci_gatt_update_char_value_ext_IDB05A1(nt->conn_handle,nt->attr_handle,0x01,4,6,2,&a);
        }
        break;
       case EVT_BLUE_GATT_READ_PERMIT_REQ:
         {
           evt_gatt_read_permit_req *pr = (void*)blue_evt->data;
           Read_Request_CB(pr->attr_handle);
         }
         break;
       case EVT_BLUE_GATT_WRITE_PERMIT_REQ:
         {
            evt_gatt_write_permit_req *pw = (void*)blue_evt->data;
            Write_Request_CB(pw);
         }  
         break;
       }
    
     }
     break;
    }
  2. 对相应的属性做出响应。
    在EVT_BLUE_GATT_READ_PERMIT_REQ中,进入Read_Request_CB函数,添加的读属性进行操作,本文将0x1234发送给客户端。

else if(handle == SendandreceiveCharHadle + 1)
  {

     BlueMS_Sendandreceive_Update(0x12345678, 0x1234);
  }

BlueMS_Sendandreceive_Update函数功能为将服务器中的数据发送给客户端,代码如下:

  //更新数据
tBleStatus BlueMS_Sendandreceive_Update(int32_t press, int16_t temp)
{
  tBleStatus ret;
  uint8_t buff[2];
  HOST_TO_LE_16(buff, HAL_GetTick()>>3);

  //HOST_TO_LE_32(buff+2,press);
  HOST_TO_LE_16(buff,temp);

  ret = aci_gatt_update_char_value(UTServW2STHandle, SendandreceiveCharHadle,
                                   0, 2, buff);

  if (ret != BLE_STATUS_SUCCESS){
    PRINTF("Error while updating TEMP characteristic: 0x%04X\n",ret) ;
    return BLE_STATUS_ERROR ;
  }

  return BLE_STATUS_SUCCESS;
}

在EVT_BLUE_GATT_WRITE_PERMIT_REQ中,添加Write_Request_CB函数,客户端写数据时,程序将进入这里,服务器进行读取进行相关操作,本函数只是从客户端写入的数据放入dubf数组中,没有进行相关操作,代码如下:

 void Write_Request_CB(evt_gatt_write_permit_req *pw)
{
  tBleStatus ret;
  uint8_t dubf[4];
   if(connection_handle !=0)
  {
    ret = aci_gatt_write_response(pw->conn_handle,pw->attr_handle,0,0,pw->data_length,pw->data);
    if (ret != BLE_STATUS_SUCCESS)
    {
      PRINTF("aci_gatt_allow_read() failed: 0x%02x\r\n", ret);
    }
  }
  if(pw->attr_handle == AccGyroMagCharHandle + 1)
  {

  }
  else if (pw->attr_handle == EnvironmentalCharHandle + 1)
  {

  }
  else if(pw->attr_handle == SendandreceiveCharHadle + 1)
  {
     dubf[0] = pw->data[0];
     dubf[1] = pw->data[1];
     dubf[2] = pw->data[2];
     dubf[3] = pw->data[3];
  }
}

在通知属性中,当将新值写入启用通知的特性时,服务器将启动此操作。如果客户端已订阅有关该特征的通知,则在写入新值时会将新值推送到客户端。本文是将LED的状态通知给客户端。在app_bluenrg_ms.c下,在User_Process中,connected判断语句下添加以下两行代码:

uint8_t a = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);
BlueMS_LED_Update(a);

BlueMS_LED_Update函数作是更新客户端的值,代码如下:

tBleStatus BlueMS_LED_Update(int8_t led)
{
  tBleStatus ret;
  uint8_t buff[2];
  HOST_TO_LE_16(buff, HAL_GetTick()>>3);
  HOST_TO_LE_16(buff,led);

  ret = aci_gatt_update_char_value(UTServW2STHandle, SendandreceiveCharHadle,
                                   0, 2, buff);

  if (ret != BLE_STATUS_SUCCESS){
    PRINTF("Error while updating LED characteristic: 0x%04X\n",ret) ;
    return BLE_STATUS_ERROR ;
  }

  return BLE_STATUS_SUCCESS; 
}

至此,这个程序就实现了添加服务与特性,以及读写通知属性。

实验结果

本文使用BLE Scanner作为客户端。当BLE Scanner和Cannon小钢炮相连时,读取数据,写入数据,以及通知。
如图1-1所示,为wireshark抓到的包,客户端读取的数据,也就是服务器发送的数据。
图1-1读数据包
如图1-2所示,为wireshark抓到的写入数据包。
图1-2读数据包
如图1-3所示,为wireshark抓到的通知包。
图1-3读数据包


文章作者: 潘苏皖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 潘苏皖 !
评论
 上一篇
基于NUCLEO-WB55开发板对代码进行解析以及实现 基于NUCLEO-WB55开发板对代码进行解析以及实现
实验目的本文利用STM32CubeMx初始化配置STM32WB55,使用自己生成的顾客服务与特性实现蓝牙之间的读写通知,通过写控制LED2亮灭,读取LED1的状态,通知LED3(300ms闪烁)来实现本实验的目的。 实验环境 STM32Cu
2020-12-27
下一篇 
关于智能小车电机的选取 关于智能小车电机的选取
挑选智能小车的电机功以及功率的理解功是物理学中表示力对位移的累积的物理量。功率是指物体在单位时间内所做的功的多少,即功率是描述做功快慢的物理量。 直流伺服马达挑选直流伺服马达有以下条件 计算所需的最大功率 利用功率需求挑选合适的马达
2020-05-31
  目录