CC2640R2F之配对绑定与解除绑定篇

蓝牙协议栈:simplelink_cc2640r2_sdk_1_40_00_45

IAR版本:IAR for ARM 8.11.3

开发板:CC2640R2F

蓝牙版本:BLE4.2

配对模式:PasscodeEntry 密码输入模式

主机端仅具有GAPBOND_IO_CAP_KEYBOARD_ONLY按键能力,从机端仅具有显示能力GAPBOND_IO_CAP_DISPLAY_ONLY。

从机端:

如果用手机做主机,开发板做从机,开发板显示密码,手机端输入密码,如果密码一直则配对成功。从机端使用协议栈自带例程simple_peropheral即可实现与手机的配对绑定,但此过程需要修改配对模式为uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;从机端初始化配对请求模式,否则主机端是不能收到配对过程中的输入密码请求的。

在此过程需要注意:手机大多是双模蓝牙,而手机配置界面大多是BDR蓝牙(非低功耗蓝牙),所以大多手机配置界面的自带蓝牙是不能与开发板进行配对绑定的,需要使用手机app,安卓建议nRF Connect,IOS端建议使用lightblue。

主机端

两个开发板一主一从,开发板作为从机实现与手机的配对与绑定后,从机端程序便可实现与另一开发板的配对绑定,我是基于simple_central例程修改主机端代码的。

初始化配置,在simple_central.c的static voidSimpleBLECentral_init(void)函数中,配置配对模式的初始化以及回调注册:

    uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;// 配置主机端初始化配对请求;
    uint8_t bonding = TRUE;//配对完成后,进行绑定
    uint8_t mitm = TRUE;//  使用authenticated pairing
    uint8_t ioCap = GAPBOND_IO_CAP_KEYBOARD_ONLY;
    GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
    GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);  
    GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
    GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);

    GAPBondMgr_Register(&SimpleBLECentral_bondCB);
接下来编写回调函数:
static gapBondCBs_t SimpleBLECentral_bondCB =
{
 (pfnPasscodeCB_t)SimpleBLECentral_passcodeCB, // Passcode callback
  SimpleBLECentral_pairStateCB                // Pairing / Bonding state Callback
};

static void SimpleBLECentral_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                        uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison)
{
  gapPasskeyNeededEvent_t *pData;
  
  // Allocate space for the passcode event.
  if ((pData = ICall_malloc(sizeof(gapPasskeyNeededEvent_t))))
  {
    memcpy(pData->deviceAddr, deviceAddr, B_ADDR_LEN);
    pData->connectionHandle = connHandle;    
    pData->uiInputs = uiInputs;
    pData->uiOutputs = uiOutputs;
    pData->numComparison = numComparison;
    
    // Enqueue the event.
    SimpleBLECentral_enqueueMsg(SBC_PASSCODE_NEEDED_EVT, 0, (uint8_t *) pData);
  }
}

static void SimpleBLECentral_pairStateCB(uint16_t connHandle, uint8_t state,
                                         uint8_t status)
{
  uint8_t *pData;

 // Allocate space for the event data.
  if ((pData = ICall_malloc(sizeof(uint8_t))))
  {
    *pData = status;

    // Queue the event.
    SimpleBLECentral_enqueueMsg(SBC_PAIRING_STATE_EVT, state, pData);
  }
}
在static_processAppMsg(sbcEvt_t *pMsg)函数中添加对于SBC_PASSCODE_NEEDED_EVT和SBC_PAIRING_STATE_EVT两个事件的处理,代码如下
static void SimpleBLECentral_processAppMsg(sbcEvt_t *pMsg)
{
  switch (pMsg->hdr.event)
  {
    case SBC_STATE_CHANGE_EVT:
      SimpleBLECentral_processStackMsg((ICall_Hdr *)pMsg->pData);

      // Free the stack message
      ICall_freeMsg(pMsg->pData);
      break;

    case SBC_KEY_CHANGE_EVT:
      SimpleBLECentral_handleKeys(0, pMsg->hdr.state);
      break;

    case SBC_RSSI_READ_EVT:
      {
        readRssi_t *pRssi = (readRssi_t *)pMsg->pData;

        // If link is up and RSSI reads active
        if (pRssi->connHandle != GAP_CONNHANDLE_ALL &&
            linkDB_Up(pRssi->connHandle))
        {
          // Restart timer
          Util_restartClock(pRssi->pClock, pRssi->period);

          // Read RSSI
          VOID HCI_ReadRssiCmd(pRssi->connHandle);
        }
      }
      break;

    // Pairing event
    case SBC_PAIRING_STATE_EVT:
      {
        SimpleBLECentral_processPairState(pMsg->hdr.state, *pMsg->pData);

        ICall_free(pMsg->pData);
        break;
      }

    // Passcode event
    case SBC_PASSCODE_NEEDED_EVT:
      {
        SimpleBLECentral_processPasscode(connHandle,(gapPasskeyNeededEvent_t *)pMsg->pData);
        
        ICall_free(pMsg->pData);
        break;
      }

    default:
      // Do nothing.
      break;
  }
}

static void SimpleBLECentral_processPasscode(uint16_t connectionHandle, gapPasskeyNeededEvent_t *pData)
{
  if (pData->uiInputs) // if we are to enter passkey
  {
#if STATIC_PASSCODE
    passcode = 123456;    //此处使用的是静态密码,不需要主机端手动输入,只要默认和从机端一致,就可实现配对
    GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);  //发送密码响应
#else
    // user will enter passcode
    waiting_for_passcode = TRUE;  //此处由主机端手动输入从机端显示的配对密码
    passcode_connHandle = connectionHandle;
#endif      
    Display_print0(dispHandle, 4, 0, "Enter Passcode:");
    Display_print1(dispHandle, 5, 0, "%d", passcode);      
  }
  else if (pData->uiOutputs) // if we are to display passkey
  {
#if STATIC_PASSCODE
    passcode = 123456;
#else
    // Create random passcode
    passcode = Util_GetTRNG();
    passcode %= 1000000;
#endif      
    Display_print1(dispHandle, 4, 0, "Passcode: %d", passcode);
    // Send passcode response
    GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);   
  }
}

static void SimpleBLECentral_processPairState(uint8_t state, uint8_t status)
{
  if (state == GAPBOND_PAIRING_STATE_STARTED)
  {
    Display_print0(dispHandle, 2, 0, "Pairing started");
  }
  else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
  {
    if (status == SUCCESS)
    {
      Display_print0(dispHandle, 2, 0, "Pairing success");
    }
    else
    {
      Display_print1(dispHandle, 2, 0, "Pairing fail: %d", status);
    }
  }
  else if (state == GAPBOND_PAIRING_STATE_BONDED)
  {
    if (status == SUCCESS)
    {
      Display_print0(dispHandle, 2, 0, "Bonding success");
    }
  }
  else if (state == GAPBOND_PAIRING_STATE_BOND_SAVED)
  {
    if (status == SUCCESS)
    {
      Display_print0(dispHandle, 2, 0, "Bond save success");
    }
    else
    {
      Display_print1(dispHandle, 2, 0, "Bond save failed: %d", status);
    }
  }
}
至此,整个配对绑定过程,需要开发人员编写的代码便完成了,其余都是由底层实现,关于底层实现 BLE4.0配对绑定过程的底层剖析这篇文章讲解很详细,有需要的朋友可以自行查阅。
绑定之后便存在解绑过程,经实际测试,单独在主机端解除绑定,在下次连接时会进行重新配对绑定,若单独在从机端解除绑定,主机连接从机会一直提示连接失败。
解除绑定是在连接断开后才执行,解绑有两种方式:一种是解除所有绑定设备 :
GAPBondMgr_SetParameter(GAPBOND_ERASE_ALLBONDS, 0, NULL);
此模式调用这个函数,便可实现,不再赘述。
另一种是只解除单个设备绑定:
GAPBondMgr_SetParameter(GAPBOND_ERASE_SINGLEBOND, B_ADDR_LEN + 1, DevAddr); 
解除单个设备绑定过程由于对参数含义不清楚,实现此功能颇费周折,在此详细说下各个参数的含义。
参数1:GAPBONG_ERASE_SINGLEBONG这个参数很容易理解,解除单个设备绑定;
参数2:B_ADDR_LEN+1表示要解绑设备的地址长度加1,此处为何加1呢?详见参数3的说明;
参数3:此参数是由要解绑设备的地址类型和地址共同组合的一个数组,所以要在地址长度上加1,此外连接完成后的设备地址是由低字节向高字节的,而此处是需要待解绑地址的字节由高到低,所以需要反向字节,具体操作如下:
uint8_t idx=0;
idx = GAPBondMgr_ResolveAddr(pEvent->linkCmpl.devAddrType, pEvent->linkCmpl.devAddr, NULL);//找到待解绑设备的索引
//此处pEvent->linkCmpl.devAddrType是待解绑设备的地址类型,pEvent->linkCmpl.devAddr是待解绑地址,这是连接完成后的地址,
Display_print1(dispHandle, 3, 0, "del idx: %d", idx);
if(idx < GAP_BONDINGS_MAX)//在bond manager中找到地址
{
 DevAddr[0] = pEvent->linkCmpl.devAddrType; //此处将待解绑地址的地址类型复制在DevAddr的最低字节;
 memcpy(lsdevaddr, pEvent->linkCmpl.devAddr, B_ADDR_LEN); //此处将待解绑地址复制到lsdevaddr中
//反向字节
VOID osal_revmemcpy(&DevAddr[1],lsdevaddr,B_ADDR_LEN);//将lsdevaddr的顺序颠倒放置在DevAddr的1~B_ADDR_LEN+1字节中
}
//最后将得到的字节传输至单个解绑的第三个参数
GAPBondMgr_SetParameter(GAPBOND_ERASE_SINGLEBOND, B_ADDR_LEN + 1, DevAddr); 

你可能感兴趣的:(CC2640R2F之配对绑定与解除绑定篇)