可视化工具一拖二tws+dongle-耳机与dongle之间实现通信控制
📎 原始文档:https://www.kdocs.cn/l/cllhw3CXKQte 可视化工具一拖二tws+dongle-耳机与dongle之间实现通信控制
700n1T2+tws+dongleSDK实现耳机与dongle设备之间互相传递信息并加以控制的功能,是通过蓝牙HFP+AT指令来实现。当下以耳机端控制dongle设备清除 dongle设备与耳机设备的一对一快连码信息为例(同理,dongle端设备要想传递信息或者控制命令给耳机则反过来添加修改即可),参考以下修改:
一、耳机端
以下案例是将AT命令与耳机端按键触发相结合,通过耳机端按键触发消息,执行动作发送对应的控制指令给dongle。
#define HFP_AT_CMD_HEAD "AT+USER="
#define HFP_AT_CMD_TAIL "\r\0"
#define HFP_AT_CMD_01 "01"
#define HFP_AT_CMD_02 "02"
#define HFP_AT_CMD_03 "03"
#define HFP_AT_CMD_04 "04"

u8 user_at_cmd_send_support = 1;
void user_hfp_atcmd_send_handler(void *atcmd){
u8 bt_addr[6];
int ret = syscfg_read(CFG_USER_DONGLE_ADDR, bt_addr, 6);
if(ret == sizeof(bt_addr) && strcmp(bt_addr,0)){
u8 addr[6];
u16 data_len = strlen(HFP_AT_CMD_HEAD)+strlen(HFP_AT_CMD_TAIL)+strlen(atcmd)+1;
u8 *send_data = (u8 *)malloc(data_len);
memset(send_data, 0, (data_len));
sprintf(send_data, "AT+USER=%s\r\0", atcmd);
printf("--send_data:[%s],datalen:[%d]--\n",send_data,strlen(send_data));
user_send_cmd_prepare_for_addr(bt_addr, USER_CTRL_HFP_SEND_USER_AT_CMD, strlen(send_data), send_data);
printf("--hfp_atcmd_send_completed--\n");
}
}
以上添加指令发送接口的定义,下方为结合按键控制,key_msg_handler.c,向dongle发送指令并清除自身的一对一快连信息和dongle设备地址。
case APP_MSG_REMOVE_QUICK_ADDR:
// 案例1:清除耳机端与dongle一起生成的一对一快连码和dongle设备的地址
printf("--REMOVE_QUICK_ADDR--\n");
u8 addr[6];
user_hfp_atcmd_send_handler(HFP_AT_CMD_01);
memset(addr, 0, 6);
syscfg_write(CFG_USER_DONGLE_ADDR, addr, 6);
syscfg_write(CFG_USER_EDR_QUICK_ADDR, addr, 6);
printf("--REMOVE_QUICK_ADDR_COM--\n");
break;
二、dongle端

//指令信息映射表的结构体,可放在dongle-SDK adapter_odev_bt.h中
struct atcmd_remap_table {
u8 *at_cmd;
u8 remap_cmd;
};
static const struct atcmd_remap_table cmd_remap_table[] = {
[0] = {
.at_cmd = "01",
.remap_cmd = 1
},
[1] = {
.at_cmd = "02",
.remap_cmd = 2
},
[2] = {
.at_cmd = "03",
.remap_cmd = 3
},
[3] = {
.at_cmd = "04",
.remap_cmd = 4
},
{ .at_cmd = 0xff }
};

int user_hfp_atcmd_remap(char *packet, const struct atcmd_remap_table *table)
{
u8 recv_data[3];
for (u8 i=0; i < 2; i++){
recv_data[i] = *(((u8 *)packet)+8+i);
}
printf("recv_cmd=%s\n",recv_data);
for (u8 i = 0; table[i].at_cmd != 0xff; i++) {
if (!strcmp(recv_data,table[i].at_cmd)) {
return table[i].remap_cmd;
}
}
return 0;
}

u8 user_at_cmd_send_support = 1;
extern int ag_at_cmd_analysis_by_user(char *packet, u16 size);
int ag_at_cmd_analysis_by_user(char *packet, u16 size)
{
printf("-ag_at_cmd_analysis_by_user-datasize:%d\n",size);
put_buf(packet, size);
const struct atcmd_remap_table *table = cmd_remap_table;
int remap_cmd = user_hfp_atcmd_remap(packet, table);
switch (remap_cmd)
{
case 1:
// 案例1:清除dongle端与耳机一起生成的一对一快连码
u8 quick_addr[6];
int ret = syscfg_read(CFG_USER_EDR_QUICK_ADDR, quick_addr, 6);
if(ret == sizeof(quick_addr) && strcmp(quick_addr,0)){
memset(quick_addr, 0, 6);
syscfg_write(CFG_USER_EDR_QUICK_ADDR, quick_addr, 6);
}
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
return 0;
}
三、控制dongle对手机应用接听/挂断/唤醒Siri的操作
知识:AVCTP协议描述了蓝牙设备间Audio/Video的控制信号交换的格式和机制,它是一个总体的协议,具体的控制信息由其指定的协议(如AVRCP)实现,AVCTP本身只指定控制command和response的总体的格式。
3.1、蓝牙常用音频控制指令
当前以耳机经常用于控制手机等蓝牙设备音频播放的AVCTP指令来举例:
案例:耳机控制已连接蓝牙设备音频的播放和暂停,即发送USER_CTRL_AVCTP_OPID_PLAY指令给蓝牙设备,蓝牙设备收到该指令再做下一步处理。
dongle设备也可以接收并响应耳机发过来的一些音频控制指令,可响应的音频指令如下:[无法更改]
当前已有的,接收到的音频控制指令响应接口如下:[无法更改]
3.2、dongle类设备音频指令转HID指令
dongle设备接收到音频指令后无法直接控制手机,需要将音频控制指令进一步转为USB的HID控制指令,JL_dongleSDK接口如上截图: adapter_avctp_key_handler(USB_AUDIO_PP);
USB-HID for audio的指令有如下: 所以可以看到上方的音频控制指令响应接口,都是将蓝牙音频控制指令转为USB-HID指令。
3.3、接听/挂断/唤醒Siri的HID指令
通过上面知道控制音频播放/暂停的HID指令是USB_AUDIO_PP,dongle设备是通过USB(type-c)与手机进行连接,这种连接方式其实就类似于一根type-C有线耳机。所以我们可以模仿type-C有线耳机通过按键转HID指令的方法控制手机端的接听/挂断/唤醒Siri。
例如:type-C有线耳机上的按键单击一次,即控制手机的播放与暂停,实际上就是单击一次,发送了一次USB_AUDIO_PP的HID指令。
type-C有线耳机控制某安卓手机进行挂断电话的操作是通话时长按按键2s,则实际上就是先发送了USB_AUDIO_PP的指令,延迟2s后,再发送结束符。【需要注意的是:这个延时时间是和手机型号有关的,不同的手机型号响应的时间不同,最好通过一根type-c耳机去测试验证】
所以,组合新的不同的HID指令后(接听/挂断/唤醒Siri等),再集合第一二点,耳机控制控制dongle的框架,即可实现耳机控制dongle的接听/挂断/唤醒Siri了。