找回密码
 立即注册

使用微信账号登录

只需一步,快速开始

QCC300x学习笔记:自定义HFP AT command

2020-5-15 10:48| 发布者: csdn| 查看: 1899| 评论: 0|来自: CSDN

摘要: 1. 引言 最近在做一个蓝牙发射器+接收器的项目,客户提了几个定制需求: 接收器能同时连接一个蓝牙发射器和

1. 引言

最近在做一个蓝牙发射器+接收器的项目,客户提了几个定制需求:

  • 接收器能同时连接一个蓝牙发射器和一个普通手机
  • 有两个按键,按键一呼叫蓝牙发射器,按键二呼叫手机
  • 蓝牙发射器和手机与接收器的连接次序不固定

一般地,在确定连接次序的前提下,按键一对应事件[Initiate voice dial 1],可呼叫第一个连接的设备,按键二对应事件[Initiate voice dial 2],可呼叫第二个连接的设备。若不确定连接次序,可能第一个连接的设备是手机,这就导致按键一无法呼叫蓝牙发射机。

为了让按键一总能呼叫蓝牙发射器,有必要在发射器和接收器之间建立匹配校验机制。当发射器连接上任何设备后,其发送匹配校验请求,如对方返回正确的匹配响应,即彼此都知晓对方是匹配的设备类型,接收器可以指定按键一关联此设备,从而不用考虑连接次序,总能呼叫到发射器。

匹配校验消息的传输通道可以是HFP、GATT、SPP等协议。本项目没有GATT、SPP协议的使用场景,用既有的HFP协议传输自定义的AT command是一个省力又可靠的解决方案。

2. 什么是HFP AT command

AT command是HFP协议的一部分,用于在AG(Audio Gateway)和HF(Hands-Free Unit)两者之间通过RFCOMM通道传输控制信令。

在这里插入图片描述

在这里插入图片描述

举一个简单的例子来说明AT command的用途。某些场景下,AG的回声消除和噪声抑制功能需要被关闭,此时HF会发送特定的AT command给AG,流程图如下:
在这里插入图片描述

可以看到HF发送AT+NREC=0给AG,AG根据自身情况返回OK或ERROR给HF。这里HFP给出了关于AT command的几个约定:

  • HF发送给AG的command,要有“AT+”,0代表关闭,1代表使能
  • NREC是HFP协议原生支持的命令
  • AG发送给HFP的command可以没有“AT+”

参考HFP V1.7全文,没有找到可用于匹配校验的AT command,于是需要我们自定义一个AT command来完成匹配校验。

相类似的做法可参考苹果设备的AT+BVRA命令,可在手机端(AG)发起与耳机端(HF)的SCO连接:

The accessory should expect the following command sequence:
· The iOS device sends a +BVRA:1 event to the accessory.
· The iOS device launches a Siri session and creates a SCO connection for the
audio.
· When the Siri session is finished, the iOS device sends a +BVRA:0 result code
to the accessory.
· The iOS device disconnects the SCO connection.

在这里插入图片描述

3. 自定义AT command

3.1. 自定义AT command交互时序图

3.2. AG端程序示例

VM层发消息给AGHFP lib:

void aghfp_device_role_ind(bool role)
{
    uint16 index = 0;
    aghfpInstance *inst = theSource->aghfp_data.inst;
        
    if (inst != NULL)
    {
        for_all_aghfp_instance(index)
        {
            if (aghfp_is_connected(inst->aghfp_state))
            {
                /* send voice recognition to remote side */
                AghfpDeviceRoleSet(theSource->aghfp_data.aghfp, role);
            }
            inst++;
        }
    }
}

void AghfpDeviceRoleSet(AGHFP *aghfp, bool role)
{
	MAKE_AGHFP_MESSAGE(AGHFP_INTERNAL_DEVICE_ROLE_SET);
	message->role = role;
	MessageSend(&aghfp->task, AGHFP_INTERNAL_DEVICE_ROLE_SET, message);
}

AGHFP lib调用aghfpSendAtCmd发送自定义AT command:

void aghfpHandleDeviceRoleSet(AGHFP *aghfp, bool role)
{
	/* Send AT cmd to HF */
	const char AgMessage[] = "+DERL: 1";
	const char HfMessage[] = "+DERL: 0";
	if (role)
	{
		aghfpSendAtCmd(aghfp, AgMessage);
	}
	else
	{
		aghfpSendAtCmd(aghfp, HfMessage);
	}

	/* Send confirm to app */
	aghfpSendCommonCfmMessageToApp(AGHFP_DEVICE_ROLE_SET_CFM, aghfp, aghfp_success);
}

3.3. HF端程序示例

HF在收到AT command时,会尝试用默认支持的AT command去匹配,如匹配不上,会把AT command的字符串用AGHFP_UNRECOGNISED_AT_CMD_IND消息发送给VM来解析。

case HFP_UNRECOGNISED_AT_CMD_IND:    {
        sinkHandleUnrecognisedATCmd( (HFP_UNRECOGNISED_AT_CMD_IND_T*)message ) ;
    }
    break ;

我们在sinkHandleUnrecognisedATCmd中插入匹配校验处理函数。

AT_DEBUG(("AT command = %s\n", pData));
    sinkAtCommandsCheckDeviceRole(ind, pData); // 匹配校验处理函数

    sinkAtCommandsCheckAndProcessProductionTestCommands(ind, pData);

当匹配成功后,调用HfpAtCmdRequest函数,返回预定的AT command “+DERL: 0"”。

static const char * const device_role_ag_string	            = "+DERL: 1";
static const char * const device_role_ag_string_res_success = "+DERL: 0";

static void sinkAtCommandsCheckDeviceRole(HFP_UNRECOGNISED_AT_CMD_IND_T *ind,
                                                            const char * const command_string)
{
    if(strncmp(command_string, device_role_ag_string, strlen(device_role_ag_string)) == 0)
    {
        AT_DEBUG(("Handle Device Role Ag\n"));
        
        /* Send the success response AT command */
        AT_DEBUG(("Response %s\n", device_role_ag_string_res_success));
        HfpAtCmdRequest(ind->priority, device_role_ag_string_res_success);
    }
}

在获取到的HFP_UNRECOGNISED_AT_CMD_IND_T句柄中,可以得到其对应的hfp_link_priority:

typedef struct
{
    /*! The priority of the link. */
    hfp_link_priority   priority;
    /*! The number of bytes pointed to by data.*/
    uint16    size_data;
    /*! The data that could not be parsed. The client should not attempt to
      free this pointer, the memory will be freed when the message is
      destroyed. If the client needs access to this data after the message has
      been destroyed it is the client's responsibility to copy it. */
    uint8    data[1];
} HFP_UNRECOGNISED_AT_CMD_IND_T;

/*!
    @brief Link priority is used to identify different links to
    AG devices using the order in which the devices were connected.
*/
typedef enum
{
    /*! Invalid Link. */
    hfp_invalid_link,
    /*! The link that was connected first. */
    hfp_primary_link,
    /*! The link that was connected second. */
    hfp_secondary_link
} hfp_link_priority;

有了priority,可以直接调用sinkInitiateVoiceDial(priority)向指定的设备发起呼叫。

4. 总结

自定义AT command的方法适用于仅需少量数据交互的场景。使用时注意不要影响到HFP协议自带的AT command。

5. 参考资料

  • SIG: HFP V1.7.0
  • QUALCOMM: CS-330103-UG (Audio Sink Application Custom AT Commands User Guide)
  • APPLE: BluetoothDesignGuideLines

来源:https://blog.csdn.net/wzz4420381/article/details/104284279
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

路过

雷人

握手

鲜花

鸡蛋

最新评论

小黑屋|手机版|我爱蓝牙网 - 52Bluetooth

GMT+8, 2024-5-5 15:41 , Processed in 0.365938 second(s), 32 queries , Gzip On, MemCached On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

返回顶部