【APOLLO BLE】设备作为从机时的连接参数请求

1. 前言

在阿波罗的项目中,当设备作为从机时,在主设备连接后一段设定时间后发起连接参数的请求,如果请求成功且自身接收主机设定参数,则认为请求成功,如果请求返回失败或主机设定的参数自身不接受,则重新请求。
接下来我们先熟悉低功耗蓝牙的连接参数有哪些以及他们的作用,然后看看APOLLO作为从机时是如何使用它的。

2. 连接参数

关于本节更加详细请进入 BLE连接参数详解

2.1 Connection interval

1.25ms为单位,取值范围为 6~3200。不同系统及版本的手机对连接参数的要求不同,在连接的时候需要注意。

2.2 Slave latency

取值范围为 0~499,同时有效连接间隔<=16s。有效连接间隔公式: Effective Connection Interval = (Connection Interval) × (1 + [Slave Latency])。

2.3 Supervisor timeout

单位为10ms,取值范围为10~3200。需满足以下公式:Supervision Timeout > (1 +slaveLatency)* (connectionInterval)

2.4 作用

连接间隔有最大最小值范围,目的是为了能让主机自由选择一个它认为合适的值,可以把min和max设置成相同值,以设置主机固定在该Connection interval。
设置为范围的好处是:主机有更多的自主性,可以根据具体的需求选择连接间隔。比如说有很多数据需要传输时,主机会选择较小的连接间隔,以快速传递数据;如果比较空闲,则会选择较大的连接间隔,以节省功耗。

3. 连接参数请求流程

连接成功后启动连接请求定时器,当定时器到期后发起请求,请求结果中判断是否有效。

  1. appSlaveProcConnOpen
  2. appConnUpdateTimerStart
  3. appSlaveConnUpdateTimeout
  4. appSlaveConnUpdate

3.1 连接参数结构体

/*! \brief Configurable parameters for connection parameter update */
typedef struct
{
  wsfTimerTicks_t idlePeriod;                  /*!< \brief 连接成功后 idlePeriod ms 发起连接参数请求*/
  uint16_t    connIntervalMin;                 /*!< \brief 连接最小间隔 in 1.25ms units */
  uint16_t    connIntervalMax;                 /*!< \brief 连接最大间隔 in 1.25ms units */
  uint16_t    connLatency;                     /*!< \brief 连接延迟 */
  uint16_t    supTimeout;                      /*!< \brief Supervision timeout in 10ms units */
  uint8_t     maxAttempts;                     /*!< \brief 如果请求失败或参数不符合预期,尝试重新请求的次数 */
} appUpdateCfg_t;

3.2 连接成功后指定时间发起连接参数请求

static void appSlaveProcConnOpen(dmEvt_t *pMsg, appConnCb_t *pCb)
{
  /* store connection ID */
  pCb->connId = (dmConnId_t) pMsg->hdr.param;

  /* check if we should do connection parameter update */
  if ((pAppUpdateCfg->idlePeriod != 0) &&
      ((pMsg->connOpen.connInterval < pAppUpdateCfg->connIntervalMin) ||
       (pMsg->connOpen.connInterval > pAppUpdateCfg->connIntervalMax) ||
       (pMsg->connOpen.connLatency != pAppUpdateCfg->connLatency) ||
       (pMsg->connOpen.supTimeout != pAppUpdateCfg->supTimeout)))
  {
    pCb->connWasIdle = FALSE;
    pCb->attempts = 0;
    appConnUpdateTimerStart(pCb->connId);//开启连接参数请求的定时器,当定时器到期时发起请求。
  }
}

3.3 定时器到期后发起连接请求

void appSlaveProcMsg(wsfMsgHdr_t *pMsg)
{
  appConnCb_t *pCb;

  /* look up app connection control block from DM connection ID */
  pCb = &appConnCb[pMsg->param - 1];

  switch(pMsg->event)
  {
    case APP_CONN_UPDATE_TIMEOUT_IND:
      /* Check state to determine if read remote features is needed */
      if (pCb->updateState == APP_CU_STATE_UPDATING)
      {
        appSlaveConnUpdateTimeout(pMsg, pCb);
      }
      else
      {
        pCb->updateState = APP_CU_STATE_WAIT_FEATURES;
      }
      break;

    default:
      break;
  }
}

static void appSlaveConnUpdateTimeout(wsfMsgHdr_t *pMsg, appConnCb_t *pCb)
{
  hciConnSpec_t connSpec;
  bool_t        idle;

  /* Change state to updating */
  pCb->updateState = APP_CU_STATE_UPDATING;

  /* check if connection is idle */
  idle = (DmConnCheckIdle(pCb->connId) == 0);

  /* if connection is idle and was also idle on last check */
  if (idle && pCb->connWasIdle)
  {
    /* do update */
    pCb->attempts++;
    connSpec.connIntervalMin = pAppUpdateCfg->connIntervalMin;
    connSpec.connIntervalMax = pAppUpdateCfg->connIntervalMax;
    connSpec.connLatency = pAppUpdateCfg->connLatency;
    connSpec.supTimeout = pAppUpdateCfg->supTimeout;
    connSpec.minCeLen = 0;
    connSpec.maxCeLen = 0xffff;

    DmConnUpdate(pCb->connId, &connSpec);
  }
  else
  {
    pCb->connWasIdle = idle;
    appConnUpdateTimerStart(pCb->connId);
  }
}

3.4 请求结果返回以及失败的话重新请求

static void appSlaveConnUpdate(dmEvt_t *pMsg, appConnCb_t *pCb)
{
  if (pAppUpdateCfg->idlePeriod != 0)
  {
    /* if successful */
    if (pMsg->hdr.status == HCI_SUCCESS)
    {

      // appSlaveConnUpdate can be called as well 
      // when master itself initiate a connection parameter update.
      // so here we need to check if the connection parameter
      // meets slave's requirement, if not, we should not stop
      // the timer.

      // only check connection interval for now
      if ((pAppUpdateCfg->connIntervalMin <= ((hciLeConnUpdateCmplEvt_t*)pMsg)->connInterval)
        &&(pAppUpdateCfg->connIntervalMax >= ((hciLeConnUpdateCmplEvt_t*)pMsg)->connInterval))
      {
        appConnUpdateTimerStop(pCb);
      }
      else {
        /* start timer and try again */
        appConnUpdateTimerStart(pCb->connId);
      }

    }
    /* else if update failed but not pending and still attempting to do update */
    else if ((pMsg->hdr.status != HCI_ERR_CMD_DISALLOWED) && (pCb->attempts < pAppUpdateCfg->maxAttempts))
    {
      /* start timer and try again */
      appConnUpdateTimerStart(pCb->connId);
    }
  }
}

4. 总结

连接参数是由主机端确定,但是从机端可以发起更新连接参数请求。在建立连接之后,从机可以要求主机更新连接参数,即只要连接建立了,随时可以再次更新连接参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强人电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值