蓝牙相关
📎 原始文档:https://www.kdocs.cn/l/cq7C8uzFydQS 蓝牙相关
蓝牙ID3
- 把以下变量置1
const u8 more_avctp_cmd_support = 1;
//检查以下宏是否定义
#define TCFG_DEC_ID3_V1_ENABLE ENABLE
#define TCFG_DEC_ID3_V2_ENABLE ENABLE
- void bredr_handle_register() 添加获取歌曲信息的回调
////获取歌曲信息回调
bt_music_info_handle_register(user_get_bt_music_info);
- 添加歌曲信息的处理函数(如果SDK已实现该函数,使用默认就可以)
/*----------------------------------------------------------------------------*/
/**@brief 蓝牙歌词信息获取回调
@param
@return
@note
const u8 more_avctp_cmd_support = 1;置上1
需要在void bredr_handle_register()注册回调函数
要动态获取播放时间的,可以发送USER_CTRL_AVCTP_OPID_GET_PLAY_TIME命令就可以了
要半秒或者1秒获取就做个定时发这个命令
*/
/*----------------------------------------------------------------------------*/
void user_get_bt_music_info(u8 type, u32 time, u8 *info, u16 len)
{
//profile define type: 1-title 2-artist name 3-album names 4-track number 5-total number of tracks 6-genre 7-playing time
//JL define 0x10-total time , 0x11 current play position
u8 min, sec;
printf("type %d\n", type );
if ((info != NULL) && (len != 0)) {
printf(" %s \n", info);
}
if (time != 0) {
min = time / 1000 / 60;
sec = time / 1000 - (min * 60);
printf(" time %d %d\n ", min, sec);
}
}
打印如下
蓝牙空闲状态时间设置(可优化连接速度)
- 减少空间状态时间,可以减少蓝牙连接,TWS配对,无线测试盒连接的时间。但是相对功耗会高
//普通SDKe
set_idle_period_slot(1600);
//可视化SDK 使用以下函数
void dut_idle_run_slot(u16 slot);
SDK 默认值为 1600(时间实际为 1600*0.625ms), 建议修改为 200。 该值最小为 100, 注意不能使用低功耗适用于音箱对功耗要求不高的产品使用。
2. 蓝牙名修改
- 普通模式下蓝牙名(默认使用配置文件的蓝牙名)
const char *bt_get_local_name()
{
return (const char *)(bt_cfg.edr_name);
}
- 独立自拍模式下的蓝牙名设置(该函数需要自行写入代码)
const char *bt_get_hid_name()
{
return "JL_HID_DEFAULT";
}
- 注意:如果打开了TWS功能,需要修改HID名字需要修改以下内容
需要自行添加判断
- 动态修改蓝牙名
extern void hci_vendor_update_name(void);
int bt_modify_name(u8 *new_name)
{
u8 new_len = strlen(new_name);
if (new_len >= LOCAL_NAME_LEN) {
new_name[LOCAL_NAME_LEN - 1] = 0;
}
if (strcmp(new_name, bt_cfg.edr_name)) {
syscfg_write(CFG_BT_NAME, new_name, LOCAL_NAME_LEN);
memcpy(bt_cfg.edr_name, new_name, LOCAL_NAME_LEN);
hci_vendor_update_name();
log_info("mdy_name sucess\\n");
return 1;
}
return 0;
}
- BLE名字修改 搜索程序上 HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME字样,找到数据部分的传参就是实际的蓝牙名。根据实际需要修改指针指向的内容。注意:包数据最长只有31字节每个包都是 31 字节,数据包中分为有效数据(significant)和无效数据(non-significant)两部分。 有效数据部分 包含若干个广播数据单元,组成是: 长度 Len ,表示这个 AD Structure 的长度(除去 len本身 1) 类型 AD Type 标记这段广播数据代表什么, 比如设备名, uuid 等。 数据 AD data 无效数据部分 广播包的长度必须是 31 个 byte,如果有效数据部分不到 31 自己,剩下的就用 0 补全。这部分的数据是无效的。 蓝牙名 数据类型 HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME 所以需要隐藏BLE名称在蓝牙搜索列表中显示的方式

- A2DP延时
- 音箱版本SDK
const int CONFIG_A2DP_DELAY_TIME = 200;
- 耳机SDK版本

备注:调小延迟会影响蓝牙距离,要严格测试确认。
4. A2DP 开关
/*----------------------------------------------------------------------------*/
/**@brief 蓝牙 开关a2dp
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void bredr_a2dp_open_and_close()
{
if (get_curr_channel_state() & A2DP_CH) {
puts("start to disconnect a2dp ");
user_send_cmd_prepare(USER_CTRL_DISCONN_A2DP, 0, NULL);
} else {
puts("start to connect a2dp ");
user_send_cmd_prepare(USER_CTRL_CONN_A2DP, 0, NULL);
}
}
- AAC、SBC格式
- 开启AAC格式支持 打开下面的宏定义
#define TCFG_BT_SUPPORT_AAC 1 //AAC格式支持
注意点:
- AC696音箱版本SDK还需要打开(1.5.0SDK及以上才支持AAC) SDK\apps\soundbox\log_config\lib_media_config.c
const int config_aac_dec_use_malloc = 1;
A2DP解码去除开头杂音数据,添加淡入效果 对比696_1.5.0_SDK的此文件修改,能解决某些AAC切SBC格式存在杂音问题
【金山文档】 audio_dec_bt
https://kdocs.cn/l/cvoRvEMih9Se
此处需要修改
SBC、AAC码率
SBC码率修改接口(不同SDK,接口可能会不同,可以通过模糊搜索”bitpool“定位): AC700X为例:

SBC是一种 可设置Bitpool的编解码,Bitpool设置范围在2~53之间,但在通用的A2DP协议(不考虑V1.2的Extension)中,对于联合立体声,Bitpool可用范围在35~53之间,因此可用码率在229kbps~345kbps之间,但要注意,这并不是可变码率。具体的Bitpool数值和码率的运算可以使用如下网站的工具:
https://link.zhihu.com/?target=https%3A//btcodecs.valdikss.org.ru/sbc-bitrate-calculator/
2、AAC码率修改接口(不同SDK,接口可能会不同,可以通过模糊搜索”bitrate“定位):
AC700X为例:
扩展:
1、sbc是通用的最基本的解码方式,蓝牙耳机都支持,支持44khz/16bit的音频,最高码率是328kbps,延时大约220ms,所以音质一般。
2、aac是苹果产品通用的解码方式,跟sbc差不多,支持44khz/16bit的音频,最高码率512kbps,延时大约100ms,音质略好于sbc。
3、aptx是高通的专利,支持48khz/16bit的音频,最高码率352kbps,延时约40ms,音质好于sbc,但相比sbc提升并不大。
4、ldac是真正的高音质解码,索尼出品,支持96khz/24bit的音频,最高码率达990kbps,接近无损解码。
5、lhac(hwa)也是接近无损解码,华为主推,支持96khz/24bit的音频,最高码率900kbps,可以媲美ldac
以flac格式的无损音乐为例,FLAC 24bit/96kHz典型压缩编码码率在2350kbps左右,我们网上下载的flac无损音质一般是44khz/16bit的,码率大约990kbps,网上下载的wav无损音乐一般也是44khz/16bit的,码率大约1400kbps。所以高品质的无损音乐体积是比较大的
- HID 独立模式(独立拍照模式) apps\soundbox\task_manager\bt\bt.c 添加对应消息
case KEY_CHANGE_MODE:
if(__this->hid_mode){
__this->hid_mode = 0;
extern int app_task_switch_check(u8 app_task);
if(app_task_switch_check(APP_MUSIC_TASK)||\
app_task_switch_check(APP_LINEIN_TASK)\
){
ret = false;
}else{
user_change_profile_mode(__this->hid_mode);///HID设备名称, 默认蓝牙名称追加_HID
}
}else{
__this->hid_mode = 1;
user_change_profile_mode(__this->hid_mode);///HID设备名称, 默认蓝牙名称追加_HID
//如果要修改名称, 可以重写重写这个函数,上面有重写例子
//const char *__attribute__((weak)) bt_get_hid_name()
}
break;
- 音量同步
- 连接使用默认音量
| AC芯片型号 | SDK | 修改方法 | 补丁文件 |
|---|---|---|---|
| AC692N | AC692x_SDK_release_V2.6.3 | 1. 外部定义下以下代码: |
extern void __set_bt_music_default_vol(u8 vol);
__set_bt_music_default_vol(50); /*1-127*/
| •AC692 2.6.3SDK
替换下面链接的库文件
【金山文档】 AC692x_SDK_release_V2.6.3_改音量同步 | | AC695N | AC695N_soundbox_sdk_release_3.1.0以及以前SDK | 1.配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
2. 替换库文件 | AC695N_soundbox_sdk_release_3.1.0:
【金山文档】 btstack-695-v310-调整音量同步-C
btstack-695-v310-调整音量同步-C.zip
btstack-695-v310-20240301.zip(支持首次连接使用默认音量,
部分测试仪不支持delay_report,需要关掉:u8 a2dp_support_delay_report = 0;)
AC695N_soundbox_sdk_release_3.0.6:
【金山文档】 btstack-695-v306-改同步默认音量
btstack-695-v306-改同步默认音量.zip
AC695N_soundbox_sdk_release_3.0.1:
btstack-695-v301-修改音量同步.zip
以前SDK版本联系我们改库
需要map功能:
【金山文档】 310版本
https://kdocs.cn/l/cmySs38X8CAR
| | AC696N | ac696n_soundbox_sdk_v1.5.0以上 | 1. 配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
| 无需替换文件 | | AC696N | ac696n_soundbox_sdk_v1.5.0及以前SDK
ac696n_earphone_v0.2.0--2M_
| 1. 配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
2. 替换库文件 | ac696n_soundbox_sdk_v1.3.3&ac696n_soundbox_sdk_v1.5.0:【金山文档】 ac696n_soundbox_sdk_v1.5.0_改音量同步
ac696n_soundbox_sdk_v1.2.2:【金山文档】 AC696N_SOUNDBOX_SDK_1.2.2-修改默认音量配置
ac696n_soundbox_sdk_v1.1.1:【金山文档】 btstack-696_v111加同步默认音量
ac696n_earphone_v0.2.0:btstack_2M_flash-696-v0.2.0修好同步默认音量: | | AC697N | AC897N_AD697N_earphone_release_V2.3.0(包含)以上 | 1.配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
| 无需替换文件 | | AC697N | AC897N_AD697N_earphone_release_V2.3.0以下SDK | 1.配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
2.替换库文件 | AC897N_AD697N_earphone_release_V2.2.5:【金山文档】 697_SDk225_默认音量同步值 | | AC698N | ad698n_earphone_release_v1.6.0及以上 | 1. 配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
| 无需替换文件 | | AC698N | ad698n_earphone_release_v1.6.0以下 | 1. 配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
2. 替换库文件 | ad698n_earphone_release_v1.3.3:【金山文档】 btstack-698_v1.3.3修改音量同步
ad698n_earphone_release_v1.2.5:【金山文档】 ad698n_earphone_release_v1.2.5 支持默认音量同步值修改的库 | | 新系列(AC700N、AC701N...) | | 参考以下修改:
1.配合外面重新定义一个变量
u8 need_default_volume = 127*8/10; 为初始化80%。
| |
- 首次连接使用默认音量
AC697X/700n/701n/708n/709n/710n
配合外面重新定义u8 need_default_volume = 127*8/10; 为初始化80%。
外面程序定义u8 sync_default_volume_every_time = 0 ; //0是首次连接时使用默认音量即(need_default_volume) 1是每次都使用默认音量
特殊版本:
- AC897N_AD697N_earphone_release_V2.2.5 替换下面链接的库文件
【金山文档】 SDK225 修改首次连接音量同步值,之后按记忆
https://kdocs.cn/l/cdToz8mQqt0X - AC897N_AD697N_earphone_release_V2.3.0 替换下面链接的库文件
【金山文档】 SDK230 修改首次连接音量同步值,之后按记忆
btstack-697-v230-20250325.zip
AC696X
- ac696n_soundbox_sdk_v1.7.0 【金山文档】 SDK170 修改首次连接音量同步值,之后按记忆
btstack-696-v170-音量支持首次连接固定音量-20251009.zip
- 自定义音量同步下,手机每一个对应的音量大小
上图是单独调节模拟音量的,如果需要更加细致的音量调节,需要调节数字音量,找到app_audio控制音量处修改:
static unsigned short user_combined_vol_list[31][2] = {
{ 0, 0}, //0: None
{30, 65}, // 1:-48.00 db
{30, 92}, // 2:-45.00 db
{30, 130}, // 3:-42.00 db
{30, 184}, // 4:-39.00 db
{30, 260}, // 5:-36.00 db
{30, 367}, // 6:-33.00 db
{30, 518}, // 7:-30.00 db
{30, 732}, // 8:-27.00 db
{30, 1034}, // 9:-24.00 db
{30, 1460}, // 10:-21.00 db
{30, 2063}, // 11:-18.00 db
{30, 2914}, // 12:-15.00 db
{30, 4115}, // 13:-12.00 db
{30, 5813}, // 14:-9.00 db
{30, 8211}, // 15:-6.00 db
{30, 9213}, // 16:-5.00 db
{30, 9600}, // 17:-4.64 db
{30, 10003}, // 18:-4.29 db
{30, 10423}, // 19:-3.93 db
{30, 10860}, // 20:-3.57 db
{30, 11316}, // 21:-3.21 db
{30, 11791}, // 22:-2.86 db
{30, 12286}, // 23:-2.50 db
{30, 12802}, // 24:-2.14 db
{30, 13339}, // 25:-1.79 db
{30, 13899}, // 26:-1.43 db
{30, 14483}, // 27:-1.07 db
{30, 15091}, // 28:-0.71 db
{30, 15724}, // 29:-0.36 db
{30, 16384}, // 30:0.00 db
};
static int audio_vol_set(u8 gain_l, u8 gain_r, u8 gain_rl, u8 gain_rr, u8 fade)
{
#if (AUDIO_VOL_MANUAL)
return 0;
#endif
#if (AUDIO_OUTPUT_WAY == AUDIO_OUTPUT_WAY_FM)
return 0;
#endif
#if (AUDIO_OUTPUT_WAY == AUDIO_OUTPUT_WAY_IIS)
extern void *iis_digvol_last;
if (iis_digvol_last) {
audio_dig_vol_set(iis_digvol_last, AUDIO_DIG_VOL_ALL_CH, gain_l);
}
#endif
#if (TCFG_APP_FM_EMITTER_EN)
extern void *fmtx_digvol_last;
audio_dig_vol_set(fmtx_digvol_last, AUDIO_DIG_VOL_ALL_CH, gain_l);
#endif
#if (AUDIO_OUTPUT_WAY == AUDIO_OUTPUT_WAY_BT)
bt_emitter_set_vol(gain_l);
#endif
/* r_printf("dac_fade_begin:0x%x\n", __this->fade_timer); */
local_irq_disable();
__this->fade_gain_l = gain_l;
__this->fade_gain_r = gain_r;
__this->fade_gain_rl = gain_rl;
__this->fade_gain_rr = gain_rr;
#if AUDIO_OUTPUT_INCLUDE_DAC
#if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_MONO_L)
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(0), gain_l, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(0), gain_l ? DEFAULT_DIGTAL_VOLUME : 0, fade);
#elif (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_MONO_R)
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(1), gain_r, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(0), gain_r ? DEFAULT_DIGTAL_VOLUME : 0, fade);
#else
//将原先此处固定数字音量然后设置模拟音量的方式 改为 同时更改数字和模拟音量,注意TCFG_AUDIO_DAC_CONNECT_MODE的定义
u16 analog_vol_l = user_combined_vol_list[gain_l][0];
u16 analog_vol_r = user_combined_vol_list[gain_r][0];
u16 digital_vol_l = user_combined_vol_list[gain_l][1];
u16 digital_vol_r = user_combined_vol_list[gain_r][1];
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(0), analog_vol_l, fade);
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(1), analog_vol_r, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(0), digital_vol_l, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(1), digital_vol_r, fade);
#endif
#if ((TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_FRONT_LR_REAR_LR) \
|| (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_DUAL_LR_DIFF))
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(2), gain_rl, fade);
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(3), gain_rr, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(2), gain_rl ? DEFAULT_DIGTAL_VOLUME : 0, fade);
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(3), gain_rr ? DEFAULT_DIGTAL_VOLUME : 0, fade);
#endif
#endif // AUDIO_OUTPUT_INCLUDE_DAC
local_irq_enable();
return 0;
}
固定通话音量

首次连接同步通话音量



696v160 支持同步音量的手机连接时设置默认50%音量,如果不是音量同步的手机则设置最大音量。 btstack-696-v160修改音量同步细节.rar
同步样机音量到手机端

获取电话本功能
使用指令
user_send_cmd_prepare(USER_CTRL_PBAP_READ_ALL,0,NULL);
回调函数
//此处拿数据,结束标志:type == 255
void phonebook_packet_handler(u8 type, const u8 *name, const u8 *number, const u8 *date)
{
static u16 number_cnt = 0;
printf("NO.%d:", number_cnt);
number_cnt++;
printf("type:%d ", type);
if (type == 0xff) {
number_cnt = 0;
}
if (name) {
printf(" NAME:%s ", name);
}
if (number) {
printf("number:%s ", number);
}
if (date) {
printf("date:%s ", date);
}
putchar('\n');
}
【金山文档】 蓝牙电话本
蓝牙电话本.otl
- 三方通话 关键词:三方通话
用途:当手机支持三方通话时,实现结束当前通话接听另一个打入的电话或者保留当前通话并接听另一个打入的电话的效果
user_send_cmd_prepare(USER_CTRL_HFP_THREE_WAY_ANSWER1, 0, NULL);
user_send_cmd_prepare(USER_CTRL_HFP_THREE_WAY_ANSWER2, 0, NULL);
注意点:
- 如果无效果注意此变量有无开启,开启后才有效
2.本功能单纯是让手机执行这两个功能,具体是否支持三方通话以及三方通话的实际效果只与手机有关。
3.挂断当前通话切回另一个,需要同时发USER_CTRL_HFP_THREE_WAY_ANSWER1和USER_CTRL_HFP_THREE_WAY_ANSWER2,只发answer1会停在保留界面。 - 蓝牙功率 接口函数
/* --------------------------------------------------------------------------*/
/**
* @brief ble_set_fix_pwr
*
* @param fix (0~max)
* 动态调整BLE的发射功率
*/
/* ----------------------------------------------------------------------------*/
void ble_set_fix_pwr(u8 fix);
/* --------------------------------------------------------------------------*/
/**
* @brief bredr_set_fix_pwr
*
* @param fix (0~max)
* 动态调整EDR的发射功率
*/
/* ----------------------------------------------------------------------------*/
void bredr_set_fix_pwr(u8 fix);
//pwr:普通状态pg_pwr:发起连接请求 iq_pwr:发现周围蓝牙设备 ble_pwr:BLE
void bt_max_pwr_set(u8 pwr, u8 pg_pwr, u8 iq_pwr, u8 ble_pwr);
- 蓝牙rssi信号强度获取
BREDR 获取已连接设备的rssi
BLE 获取已连接设备的rssi
12. 清除连接记录(恢复出厂设置)
1、 在蓝牙模式下非连接状态下执行:
清除手机配对信息: user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);
清除 TWS 配对信息: bt_tws_remove_pairs();
2、 如果是手机和 TWS 连接的状态:
先断开手机连接: user_send_cmd_prepare(USER_CTRL_DISCONNECTION_HCI, 0, NULL);
断开后清除手机配对信息:
case BT_STATUS_FIRST_DISCONNECT:
case BT_STATUS_SECOND_DISCONNECT:
user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);
清除 TWS 配对信息: tws_api_remove_pairs();
3、 非蓝牙模式下:
清除 TWS 配对信息:
清除手机配对信息:先记录要清除配对信息的标志,进入蓝牙模式后再调用
user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);
来清除手机配对信息。

例如在idle模式下清除配对信息:
进入蓝牙模式后执行清除:
- 频偏
AC708N,AC710N程序上频偏修改:
使用以下函数进行频偏修改,可同时修改edr和ble,调用位置如图:
extern void bt_osc_offset_ext_save(s32 offset);
bt_osc_offset_ext_save(-55);//参数跟bt_osc_offset_set规则一致,特别注意此函数需要全擦才能清除

AC700X &701&703&(706音箱 )程序上频偏修改:
extern void bt_osc_offset_set(s32 offset); 该函数要在power_reset_source_dump();后调用。参数使用说明:如果测试盒测试到未修改频偏前数值是+20 ,那么就填bt_osc_offset_set(20); 如果测试到是-20 那么就填bt_osc_offset_set(-20);
带ble的需要使用此函数,建议在app_main调用;
extern void bt_osc_offset_ext_save(s32 offset);
bt_osc_offset_ext_save(-55);//参数跟bt_osc_offset_set规则一致,特别注意此函数需要全擦才能清除

AC698X 程序上频偏修改:
void bt_osc_offset_ext_updata(s32 offset);在下图的地方调用,
参数使用说明:如果测试盒测试到未修改频偏前数值是+20 ,那么就填bt_osc_offset_ext_updata(20); 如果测试到是-20 那么就填bt_osc_offset_ext_updata(-20);
AC696 音箱程序上频偏修改:
默认值修改:
动态修改:
使用函数:bt_osc_offset_ext_save(s32 offset)
使用方法: 该函数要在power_reset_source_dump();后调用。参数使用说明:如果测试盒测试到未修改频偏前数值是+20 ,那么就填bt_osc_offset_ext_save(20); 如果测试到是-20 那么就填bt_osc_offset_ext_save(-20);
AC696 耳机程序上频偏修改:

- 蓝牙版本号
- 蓝牙测试仪连接蓝牙样机读取到的蓝牙版本号可以通过以下接口修改: (蓝牙5.3就设置0x0c) btcontroller_modules.h:


- AC690X 蓝牙版本号修改
/*AC690蓝牙BQB版本设置
这里的0x08改为0x0a,对应的就是5.1的。
0x08 4.2
0x09 5.0
0x0a 5.1
0x0b 5.2
0x0c 5.3
0x0d 5.4
AC690 系列修改蓝牙版本号,由于 SDK 中默认把蓝牙的版本写为 2.1,在外层可以进行修改。ac690x 系列程序修改蓝牙版本信息修改在 bt_configs_user.c 文件里面添加下面代码
*/
struct lmp_deviceni
{
u8 *name;
u8 pin_code[16];
u8 addr[6];
u8 version[5];
};
extern struct lmp_device local;
void patch_version()
{
local.version[0]=0x08;
local.version[1]=0xd6;
local.version[2]=0x05;
}
在bt_app_cfg 函数开头调用 patch_version 函数如下!
【金山文档】 bluetooth_lib-690-emitter_v107-BQB-修改5.3
https://kdocs.cn/l/cnEkA0rXnc45
【金山文档】 bluetooth_lib-690-v2012v4-BQB-修改5.3
https://kdocs.cn/l/cqWEp330X0sl
2. AC692X 蓝牙版本号修改 在上层 APP 程序加入如下修改,把蓝牙版本修改为 5.0 版本。
extern u8 *get_device_version(void);
u8 *lmp_pal_version = get_device_version();
*lmp_pal_version = 0x09;
【金山文档】 bluetooth_lib-br21-v2.6.3-BQB-修改5.3
https://kdocs.cn/l/ck7wl55B6q0S
3. AC696X/AC695X/AC697X/AC700X 蓝牙版本号修改 蓝牙版本修改(蓝牙5.3就设置0x0c,蓝牙5.4就设置0x0d):
4. AC702X 蓝牙版本号修改 蓝牙版本修改(蓝牙5.3就设置0x0c,蓝牙5.4就设置0x0d):
5. 蓝牙6.0修改 蓝牙core 6.0修改说明.pdf
- 蓝牙设备类型(蓝牙HCI属性)修改 http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
例如如下应用设置:
SDK程序端接口
#define BD_CLASS_LOUDSPEAKER 0x240414
extern void __change_hci_class_type(u32 class);
///例如:
__change_hci_class_type(BD_CLASS_LOUDSPEAKER);


备注:AC702N flash版本SDK的void __change_hci_class_type(u32 class)函数没有引出。需要按以下重定义处理:
extern struct _stack_config rom_stack_configs_app;
void __change_hci_class_type(u32 class)
{
struct _stack_config *user_stack_configs = &rom_stack_configs_app;
user_stack_configs->hci_dev_class = class;
}
调用位置一样是在蓝牙初始化配置函数中调用:

- BT HID
AC700X键盘简单应用
添加hid_user.h,hid_user.c以及standard_hid.h文件到对应目录 【金山文档】 AC700X_BTHID文件
https://kdocs.cn/l/cjw2RPKaP93G
\apps\common\third_party_profile\jieli\hid_user.h
\apps\common\third_party_profile\jieli\hid_user.c
\apps\common\include\standard_hid.hHID描述符,如需修改参考官方标准HID协议文档
const u8 use_hid_descriptor[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x85, 0x01, // Report ID (1)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x05, // Usage Maximum (Kana)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xA4, 0x00, // Logical Maximum (164)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x28, // Usage Minimum (0x28)
0x2A, 0x28, 0x00, // Usage Maximum (0x28)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x15, 0x01, // Logical Minimum (1)
0x26, 0xFC, 0x03, // Logical Maximum (1020)
0x19, 0x01, // Usage Minimum (Consumer Control)
0x2A, 0xFC, 0x03, // Usage Maximum (0x03FC)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
- 使用例子
struct user_hid_consumer_cmd {
//Bluetooth HID Protocol Message Header Octet
u8 HIDP_Hdr;
//Bluetooth HID Boot Reports
u8 report_id;
u8 button;
} _GNU_PACKED_;
static struct user_hid_consumer_cmd u_consumer = {
.HIDP_Hdr = 0xA1,
.report_id = 0x02,
.button = 0,
};
// consumer key
#define CONSUMER_MENU 0x01
#define CONSUMER_MENU_ESCAPE 0x02
#define CONSUMER_AC_HOME 0x04
void hid_consumer_send_test(u8 menu)
{
y_printf("hid_consumer_send_test menu = %d", menu);
if (menu == 1) {
u_consumer.button = CONSUMER_MENU;
}
if (menu == 2) {
u_consumer.button = CONSUMER_MENU_ESCAPE;
}
if (menu == 3) {
u_consumer.button = CONSUMER_AC_HOME;
}
put_buf((u8 *)&u_consumer, sizeof(u_consumer));
user_data_write_sub((u8 *)&u_consumer, sizeof(u_consumer));
u_consumer.button = 0x00;
user_data_write_sub((u8 *)&u_consumer, sizeof(u_consumer));
}
u8 sdp_make_hid_service_data[0x200];
static void user_hid_sdp_init(const u8 *hid_descriptor, u16 size)
{
int real_size;
real_size = sdp_create_diy_hid_service(sdp_make_hid_service_data, sizeof(sdp_make_hid_service_data), hid_descriptor, size);
printf("dy_hid_service(%d):", real_size);
}
void user_hid_descriptor_init(void)
{
__change_hci_class_type(BD_CLASS_KEYBOARD); /*键盘图标,需要更换手机显示图标的可以自己改成以前的值*/
user_hid_init(user_hid_interrupt_handler);
user_hid_sdp_init(use_hid_descriptor, sizeof(use_hid_descriptor));
}
//用户修改成自定义的描述符说明
//1、在void bredr_handle_register();中调用user_hid_descriptor_init;
//2、user_hid_sdp_init换成自己的表
//3、文件上方HID_CHANGE_DESCRIPTOR定义为1
//4、在bt_profile_config.c文件中extern sdp_make_hid_service_data数组,
// 把sdp_hid_service_data替换为sdp_make_hid_service_data.
//5、把const u8 hid_conn_depend_on_dev_company的值置0;
#define USER_HID_MUSIC_PP 0x00CD //播放暂停
#define USER_HID_MUSIC_NEXT 0x00B5 //下一首
#define USER_HID_MUSIC_PREV 0x00B6 //上一首
#define USER_HID_VOL_UP 0x00E9 //音量+
#define USER_HID_VOL_DOWN 0x00EA //音量-
#define USER_HID_FAST_FORWARD 0x00B3 //快进
#define USER_HID_REWIND 0x00B4 //快退
#define USER_HID_IOS_HOME 0x0040 //IOS HOME键
switch (key_event) {
case KEY_HID_TEST_1:
log_info("KEY_HID_TEST_1");
edr_hid_key_deal_test(USER_HID_MUSIC_PP);
break;
case KEY_HID_TEST_2:
log_info("KEY_HID_TEST_2");
edr_hid_key_deal_test(USER_HID_MUSIC_NEXT);
break;
case KEY_HID_TEST_3:
log_info("KEY_HID_TEST_3");
edr_hid_key_deal_test(USER_HID_MUSIC_PREV);
break;
case KEY_HID_TEST_4:
log_info("KEY_HID_TEST_4");
edr_hid_key_deal_test(USER_HID_VOL_UP);
break;
case KEY_HID_TEST_5:
log_info("KEY_HID_TEST_5");
edr_hid_key_deal_test(USER_HID_VOL_DOWN);
break;
case KEY_HID_TEST_6:
log_info("KEY_HID_TEST_6");
edr_hid_key_deal_test(USER_HID_FAST_FORWARD);
break;
case KEY_HID_TEST_7:
log_info("KEY_HID_TEST_7");
edr_hid_key_deal_test(USER_HID_REWIND);
break;
case KEY_HID_TEST_8:
log_info("KEY_HID_TEST_8");
// edr_hid_long_key_deal_test(USER_HID_IOS_HOME);
break;
}
- 蓝牙发射器 蓝牙发射器(bt_emitter)相关.otl
- TWS 主从机相关 【金山文档】 TWS配对常见应用
TWS配对常见应用.otl - 蓝牙TWS自定义消息同步功能 该功能用于TWS连接后的消息交互处理
- 注册消息回调函数 使用以下函数注册
示例:
#define USER_TWS_FUNC_ID_USER_INFO_SYNC TWS_FUNC_ID('U', 'S', 'E', 'R') //自定义ID号
//回调函数
static void bt_tws_user_info_sync(void *_data, u16 len, bool rx)
{
if (rx/*&& len==2*/) {
u8 *data = (u8 *)_data;
printf(">>>> tws sync DATA0:%d DATA1:%d\n",data[0],data[1]);
switch(data[0]){
case USER_TWS_VOL_SYNC:
break;
default:
printf("unknow tws user sync\n");
break;
}
}
}
//注册TWS同步信息
REGISTER_TWS_FUNC_STUB(app_led_mode_sync_stub) = //注册 app_led_mode_sync_stub 类
{
.func_id = USER_TWS_FUNC_ID_USER_INFO_SYNC,//自定义ID号
.func = bt_tws_user_info_sync,//回调函数
};
- 发送自定义ID的消息 使用以下函数:
示例:
//消息枚举表
//实例发送
user_bt_tws_sync_msg_send(USER_TWS_VOL_SYNC,(u8)data);
开关主从自动切换接口:

通话时切换左右耳的mic 通话状态下才能调用!!!!!!!!
mic_switch_to_left(); //用左耳的mic
mic_switch_to_right(); //用右耳的mic
代码如下
#define MIC_SWITCH_DELAY_MS (1000)
extern void test_esco_role_switch(unsigned char);
static int rs_timer = 0;
void mic_switch(void *p)
{
rs_timer = 0;
test_esco_role_switch(1);
}
void mic_switch_to_left(void)
{
if(rs_timer)
{
g_printf("cancle prev mic switch\n");
sys_timeout_del(rs_timer);
rs_timer = 0;
}
if(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED){
if(tws_api_get_role() == TWS_ROLE_MASTER && tws_api_get_local_channel() == 'R'){
g_printf("mic_switch_to_left\n");
rs_timer = sys_timeout_add(NULL, mic_switch, MIC_SWITCH_DELAY_MS);
}
}
}
void mic_switch_to_right(void)
{
if(rs_timer)
{
g_printf("cancle prev mic switch\n");
sys_timeout_del(rs_timer);
rs_timer = 0;
}
if(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED){
if(tws_api_get_role() == TWS_ROLE_MASTER && tws_api_get_local_channel() == 'L'){
g_printf("mic_switch_to_right\n");
rs_timer = sys_timeout_add(NULL, mic_switch, MIC_SWITCH_DELAY_MS);
}
}
}打开TWS功能,如何实现不可发现可连接状态AC695/AC696
//把程序上调用以下函数的接口,的name传参改为 NULL,可以实现不可发现可连接状态
int tws_open_tws_wait_pair(u16 code, const char *name);
int tws_open_phone_wait_pair(u16 code, const char *name);
蓝牙电话本 【金山文档】 蓝牙电话本
蓝牙电话本.otlPINCODE
修改方法
- AC695&AC696 音箱SDK
//开启pincode功能
extern void __set_simple_pair_flag(u8 flag);
//添加位置bt_function_select_init下一行或者在bt_function_select_init中的最后一行
bt_function_select_init();
__set_simple_pair_flag(0);
//修改pincode默认值
const char *bt_get_pin_code()
{
return "0250";
}
可视化SDK:
//开启pincode功能
void bt_set_simple_pair_flag(bool flag);
//添加位置bt_function_select_init下一行或者在bt_function_select_init中的最后一行
bt_function_select_init();
bt_set_simple_pair_flag(0);
//修改pincode默认值
const char *bt_get_pin_code()
{
return "0250";
}
注意:
必须在协议栈配置bt_function_select_init接口中设置

- 开启DUT模式
extern void bredr_set_dut_enble(u8 en, u8 phone); //en:开启 phone:手机是否能链接
bredr_set_dut_enble(1, 0);
- 回连 多个设备 如果需要支持上电回连多个设置需要把以下变量置
const u8 more_addr_reconnect_support = 1;
//设置上电回连设备的数量最多9台设备(1~9)
__set_auto_conn_device_num(9);
- MAP方式获取安卓手机时间到样机 关键词:MAP、同步时间、安卓系统
发布的SDK中有profile HFP协议,支持小机端获取手机的时间信息,但是HFP协议不能够获取安卓手机的时间,所以需要额外添加补丁支持profile MAP协议,通过获取安卓手机的通讯录获取时间。通常的应用在时钟音箱,用于连接蓝牙后的时间同步更新。
以下是各版本的补丁链接,下载后请仔细阅读文档内容。
【金山文档】 安卓手机同步时间补丁
安卓手机同步时间补丁.otl
耳机SDK获取手机时间:
- 定义以下宏:
#define TCFG_BT_SUPPORT_MAP 0x1
- 在earphone.c加以下代码:
#if TCFG_BT_SUPPORT_MAP
#define PROFILE_CMD_TRY_AGAIN_LATER -1004
void bt_get_time_date()
{
int error = bt_cmd_prepare(USER_CTRL_HFP_GET_PHONE_DATE_TIME, 0, NULL);
log_info(">>>>>error = %d\n", error);
if (error == PROFILE_CMD_TRY_AGAIN_LATER) {
sys_timeout_add(NULL, bt_get_time_date, 100);
}
}
void phone_date_and_time_feedback(u8 *data, u16 len)
{
log_info("time:%s ", data);
}
void map_get_time_data(char *time, int status)
{
if (status == 0) {
log_info("time:%s ", time);
} else {
log_info(">>>map get fail\n");
sys_timeout_add(NULL, bt_get_time_date, 100);
}
}
#endif
- 对应蓝牙事件bt_connction_status_event_handler里添加流程:
case BT_STATUS_RECONN_OR_CONN:
log_info("++++++++ BT_STATUS_RECONN_OR_CONN +++++++++ \n");
bt_cmd_prepare(USER_CTRL_MAP_READ_TIME, 0, NULL);
break;
- 获取到时间: 必须同意才能获取的到手机时间(ios不用)

5.目前支持的SDK:
可视化SDK: AC700N-demo 2.0.2 (patch 08)、AC701N 1.0.0 patch_05.31及以后
非可视化SDK: AC897N_AD697N_earphone_release_V2.4.1 、ad698n_earphone_release_v1.7.2、AC700N_earphone_release_V1.3.8、JL701n_earphone_release_v1.6.1
24. 样机连接、断开高级音频(SQ) 注意点:如果手机主动断开,样机是不能通过命令进行连接的
eg. 695 310SDK
/*----------------------------------------------------------------------------*/
/**@brief 蓝牙 开关a2dp
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void bredr_a2dp_open_and_close()
{
if (get_curr_channel_state() & A2DP_CH) {
puts("start to disconnect a2dp ");
user_send_cmd_prepare(USER_CTRL_DISCONN_A2DP, 0, NULL);
} else {
puts("start to connect a2dp ");
user_send_cmd_prepare(USER_CTRL_CONN_A2DP, 0, NULL);
}
}
- 广播音箱 先仔细阅读随SDK发布的:广播音箱开发文档,目前广播音箱通信是单向的
- 广播音箱配对 建议使用demo板级(不要用1tn板级),使能TCFG_BROADCAST_ENABLE
按键消息:KEY_BROADCAST_SW
备注:广播音响大部分连接不上,连接慢的情况都是开启了n个从机却没有主机导致
//按键消息执行的函数
int app_broadcast_switch(void)
{
int temp_broadcast_hdl;
if (!app_bt_hdl.init_ok) {
return -1;
}
//判断是否目前广播支持的模式
if ((app_get_curr_task() == APP_BT_TASK) &&
(get_call_status() != BT_CALL_HANGUP)) {
return -1;
}
if ((app_get_curr_task() == APP_FM_TASK) ||
(app_get_curr_task() == APP_PC_TASK) /*||
(app_get_curr_task() == APP_MUSIC_TASK)*/) {
return -1;
}
if (broadcast_hdl) { //判断是否已经开启广播,是则关闭广播
log_info("broadcast_close");
#if defined(RCSP_MODE) && RCSP_MODE
ble_module_enable(1);
#endif
tone_play_with_callback_by_name(tone_table[IDEX_TONE_BROADCAST_CLOSE], 1, broadcast_tone_play_end_callback, (void *)IDEX_TONE_BROADCAST_CLOSE);
broadcast_close(broadcast_hdl);
broadcast_hdl = 0;
broadcast_status = APP_BROADCAST_STATUS_STOP;
} else {
log_info("broadcast_open");
#if defined(RCSP_MODE) && RCSP_MODE
ble_module_enable(0);
#endif
tone_play_with_callback_by_name(tone_table[IDEX_TONE_BROADCAST_OPEN], 1, broadcast_tone_play_end_callback, (void *)IDEX_TONE_BROADCAST_OPEN);
//重要部分,判断开启广播后是作为广播端,还是接收端,大部分连接不上,连接慢的情况都是开启了n个从机却没有主机导致
if (is_broadcast_as_transmitter()) {
temp_broadcast_hdl = broadcast_transmitter();
} else {
temp_broadcast_hdl = broadcast_receiver();
}
if (temp_broadcast_hdl < -1) {
broadcast_hdl = 0;
} else if (temp_broadcast_hdl >= 0) {
broadcast_hdl = temp_broadcast_hdl;
broadcast_status = APP_BROADCAST_STATUS_START;
}
}
return 0;
}
程序开启广播后判断作为主机或者从机(默认程序,在弄清楚整个流程前不建议改)
广播音箱140之后的版本支持固定主从;
//蓝牙模式下,只有连接手机并且正在播歌的机器才作为广播端,否则是接收端
//公版1.2.0_sdk如果作为主机的话,需要在开启广播前,本次开机连接过手机,播放过一次音频
if ((app_get_curr_task() == APP_BT_TASK) &&
(get_bt_connect_status() != BT_STATUS_WAITINT_CONN)) {
/* if (get_a2dp_decoder_status()) { */
/* if (get_a2dp_start_flag()) { */
if ((a2dp_get_status() == BT_MUSIC_STATUS_STARTING) ||
get_a2dp_decoder_status() ||
get_a2dp_start_flag()) {
return true;
} else {
return false;
}
}
//linein模式默认也是播放音频的设备为广播端
#if TCFG_LINEIN_ENABLE
if (app_get_curr_task() == APP_LINEIN_TASK) {
if (linein_get_status() || config_broadcast_as_master) {
return true;
} else {
return false;
}
}
#endif
//音乐模式默认也是播放音频的设备作为广播端
if (app_get_curr_task() == APP_MUSIC_TASK) {
if ((music_player_get_play_status() == FILE_DEC_STATUS_PLAY) || config_broadcast_as_master) {
return true;
} else {
return false;
}
}
播放暂停歌曲广播切换逻辑
模式切换广播切换逻辑
2. 广播音箱基本函数功能以及使用
//宏定义:
#define BROADCAST_DATA_SYNC_EN 1 //广播状态同步,同步消息位置,可修改自定义发送消息
#define BROADCAST_TX_LOCAL_DEC_EN 1 //广播音频发送端是否解码发送的音频
#define BROADCAST_RECEIVER_CLOSE_EDR_CONN 0//广播从机关闭经典蓝牙连接
#define BROADCAST_FIXED_ROLE 0 //固定广播角色, 配0不固定角色,配1做发送端,配2做接收端
#define BROADCAST_TX_CHANNEL_SEPARATION 0 //发送端音频声道分离使能,要求发送端为双声道,接收端为单声道并且码率为发送端一半
#define BROADCAST_AUTO_SWITCH_ROLE 0 //播放和暂停自动切换发射和接收状态
//按键消息:
KEY_BROADCAST_SW //广播音箱开启关闭消息,广播开启分主从机,主机广播音频,从机接收音频
KEY_WIRELESS_AUTH_ENTER //广播配对,主机广播配对码,需要退出消息;从机接收配对码后自动退出;
KEY_WIRELESS_AUTH_EXIT //广播配对退出
//基本函数:
is_broadcast_as_transmitter //广播音箱主从机判断,可自行修改判断条件
live_audio_mode_setup(LIVE_AUX_CAPTURE_MODE, 48000, &wireless_aux_mode_ops);
//广播模式配置:wireless_aux_mode_ops:广播开启以及数据流capture配置
广播音箱声道配置

广播音箱1tn介绍(做无线麦使用) 先仔细阅读此文档
【金山文档】 低延时1tN无线Linein音频传输功能说明
低延时1tN无线Linein音频传输功能说明.pdf
配对逻辑:
此处以TX举例:
配对码生成的位置在蓝牙底层库文件内,每次配对生成新的配对码固定配对码
1.1.2.1_sdk
如果不希望生成新配对码,修改方法:
- 替换库文件 【金山文档】 btstack_701_1.2.1_配对补丁
https://kdocs.cn/l/cv36MxUOnrzr - 修改配对流程,修改为如果已经存在配对码则不生成新的配对码 修改位置:

extern void select_old_pri_ch(uint32_t old_pri_ch);
u32 pair_code = 0;
int ret = syscfg_read(VM_WIRELESS_PAIR_CODE, &pair_code, sizeof(u32));
printf("select_old_pri_chaaa == %x ret == %d\n",pair_code,ret);
if (ret > 0 && pair_code != 0 && pair_code != 0xffffffff) {
select_old_pri_ch(pair_code);
printf("select_old_pri_ch == %x\n",pair_code);
}
使用配对码位置:
2.1.3.0_sdk修改
【金山文档】 广播固定配对码修改
https://kdocs.cn/l/chGolp8SWgAj
替换所有文件后,配对前会先判断VM区是否有配对码存在,有则固定配对码
2. jl701n_soundbox_release_v1.3.01TN应用增加LLNS降噪算法 jl701n_soundbox_release_v1.3.01TN应用增加LLNS降噪算法
3. 实现AC701N 1TN形态自动配对需求实现 【金山文档】 jl701n_soundbox_release_v1.2.1_1TN_自动配对功能实现方式_20230904
jl701n_soundbox_release_v1.2.1_1TN_自动配对功能实现方式_20230904.rar
- 广播音箱音量同步以及自定义消息(TX->RX) 1.2.1得修改此处

开启广播状态同步后,默认是做音量同步
广播端:
调用此消息即可,但一直会在广播
void user_status_sync()
{
char data[6] = "11111"; //测试使用,无任何格式意义
wireless_dev_status_sync("big_tx", data, 6, NULL);//使用此函数会一直广播数据
}
接收端:
//广播数据接收端
printf("broadcast_rx_padv_data_callback == %s\n",buf);
u8 test_buf[6];
memcpy(test_buf, buf, 6);
put_buf(test_buf,6);
if(test_buf[0] == '1'){
printf("dddddddddddddddddddddddd\n");
return;
}
1.4.2版本sdk ,做1TN应用自定义同步数据
TX和RX都要打开广播状态同步宏使能
发送自定义数据的方式可以自行参考音量同步的做法和接收解析方式:
6. 广播音箱配对功能实现
- 通过修改配对码方式,适用于130以后SDK:
步骤:
1.主机调用:KEY_WIRELESS_AUTH_ENTER发送配对码
2.从机调用:KEY_WIRELESS_AUTH_ENTER接收配对码
3.主机调用:KEY_WIRELESS_AUTH_EXIT退出配对
4.从机可以直接调用:KEY_BROADCAST_SW开启广播从机;
5.主机链接手机播放歌曲后,调用KEY_BROADCAST_SW开启广播主机;
6.链接成功
配对码等会有打印的,进入配对状态以及最后广播使用的配对码都是可以修改的,可以根据打印的信息去跟下
最后使用到的位置,但配对码不能随意写,比如不能直接:0x11111111,需要符合底层的规定,建议直接
使用主机多次调用:KEY_WIRELESS_AUTH_ENTER,生成多个不同的配对码自行保存,用于配对码修改
- RX获取当前接收包的miss和err数 【金山文档】 btstack_701_1.3.0_get_miss
https://kdocs.cn/l/coe9W8msBwdU

【金山文档 | WPS云文档】 btstack_701_142_sdk_get_miss
https://kdocs.cn/l/cbiehioBk7GG
【金山文档 | WPS云文档】 btstack_701_sdk141_sdk_get_miss
https://kdocs.cn/l/ccmxSCojXGgW

//报错解决
const char *bt_get_ble_name()
{
printf("bt_cfg.ble_name == %s\n",bt_cfg.ble_name);
return (const char *)(bt_cfg.ble_name);
}
//参考使用方式
u32 rx_buf[3] = {0};
extern void get_big_rx_condition(void *rx_info);
get_big_rx_condition(rx_buf);
printf("total:%d Miss:%d Err:%d\n",rx_buf[0],rx_buf[1],rx_buf[2]);
- AURACAST(杰理开发者平台SDK) 关于芯片选型,请优先仔细看此表格!!!
补充:AC210N同样支持标准auracast功能,资源较JL709N少,降本方案可考虑;
建议:通用的le_audio应用采用JL7094芯片去做;
JL7094开发参考文档:JL7094系列-入门到进阶开发指导.otl
私有的不需要与其他厂家手机等连接的,可考虑JL701N系列;
以下是代码端配置项:
功能开启,使能le_audio应用,角色固定和不固定看实际项目需求,其他项建议不修改:
BIS参数配置,不了解配置项意义以及影响的情况下,不建议做修改,按照每个版本默认的配置进行使用,不用修改得跟下图一致;
代码使用接口说明:
APP_MSG_LE_BROADCAST_SW //程序参考使用消息,用于广播开关切换使用
app_auracast_switch(); //广播开关实际调用到的接口
app_auracast_open(); //广播开启api,未初始化前不能调用,详情见实际代码调用方式
is_auracast_as_source();
/*广播开机角色判断,可根据需求自行修改,比如程序默认是蓝牙播放歌曲情况下是主机,也可自行修改为比如链接手机的就是主机而不需要播放歌曲;大多数le-audio无法连接的问题都是开启了多个从机,但没有主机导致的,请先确定主从机问题正常后(开启广播会有log),还出现主从机无法连接的问题再反馈*/
app_auracast_sink_close();//广播接收关闭,详情见实际代码调用方式
app_auracast_source_close();//广播发送关闭,详情见实际代码调用方式
对外测试方式:
JBL GO 4:GO4开启广播后,使用杰理平台打开从机后就能接受到GO 4端广播音频;(杰理平台做主机的话需要发JBL定制化的数据,可能存在法律风险,默认不支持杰理平台做主机去链接JBL从机)
三星 s23等支持auracast功能手机,此处以三星s23举例,在设置->蓝牙->auracast广播音频(蓝牙设置右上角三个点)->配置无密码广播;(无法打开auracast的话进开发者选项先打开le_audio功能)
加PA配置 以下这些系列IC的RX和TX口是固定的,硬件对正确后,软件调用使能函数bt_set_rxtx_status_enable(1)即可:
include_lib\btctrler\btcontroller_modules.h
/* --------------------------------------------------------------------------*/
/**
* @brief bt_set_rxtx_status_enable
*
* @param en
*
*
TX RX
AI800x PA13 PA12
AC692x PA13 PA12
AC693x PA8 PA9
AC695x PA9 PA10
AC696x PC1 PC2 //! 代码上的注释是PA9/PA10有误,以这个为准
AC694x PB1 PB2
AC697x PC2 PC3
AC631x PA7 PA8
*/
/* ----------------------------------------------------------------------------*/
void bt_set_rxtx_status_enable(u8 en);
JL700X、JL701N、JL703N TX和RX口 可以映射到其他IO:
bt_rf_PA_control_io_remap(IO_PORTB_06,IO_PORTB_07); //697/701N 有其他芯片可能没有这个 txen->PB6
bt_set_rxtx_status_io(u32 tx_pin, u32 rx_pin);//br28(701) 可视化sdk 用这个api替代上述接口
bt_set_rxtx_status_io(IO_PORTB_06,IO_PORTB_07); //700N
bt_set_rxtx_status_enable(1);//695,696等
硬件:
28. 获取手机产商 case BT_STATUS_PHONE_MANUFACTURER:
log_info("BT_STATUS_PHONE_MANUFACTURER:%d\n", bt->value);
app_var.remote_dev_company = bt->value;
HFP 通话协议
设置要保留hfp做电量显示,但不要蓝牙通话(参考设置) AC695X :
对应接口:
调用接口函数:
2. HFP TWS耳机 每次来电都在耳机端出声音,接听电话之后,从耳机出声音 关键字:通话 默认 【金山文档】 HFP TWS耳机 每次来电都在耳机端出声音的,接听电话之后,从耳机出声音
HFP TWS耳机 每次来电都在耳机端出声音的,接听电话之后,从耳机出声音.otl
3.通话时按键切换手机的输入输出方式为蓝牙或者手机
user_send_cmd_prepare(USER_CTRL_SCO_LINK, 0, NULL);
4.设置仅保留HFP做电量显示,通话时固定输入输出方式为手机,避免苹果手机HFP通话时,BT断连以及可以切换到耳机输出 问题
const int CONFIG_BTCTLER_FUN = EDR_LMP_SUPPORT_ESCO; // 使能EDR_LMP_SUPPORT_ESCO

- 获取设备蓝牙名
- 把以下变量置1
const int CONFIG_LMP_NAME_REQ_ENABLE = 1;
- void bredr_handle_register() 添加获取信息的回调
read_remote_name_handle_register(user_remote_name_hdl);
- 自定义user_remote_name_hdl函数
static void user_remote_name_hdl(u8 status, u8 *addr, u8 *name)
{
printf("%s\n",__func__);
put_buf(addr ,6);
printf("name = %s\n",name);
}
- 发命令获取设备蓝牙名
user_send_cmd_prepare(USER_CTRL_READ_REMOTE_NAME, 6, user_addr);
//user_addr为设备蓝牙MAC
- 蓝牙库更新
AC690:
兼容性更新蓝牙库,版本:ac69_sdk_v2012_p4_opp,ac69_emitter_sdk_v107
【金山文档】 bluetooth_lib
https://kdocs.cn/l/cdU59qM1poJN
发射器使用这版:
bluetooth_lib-690发射器-20230228.zip
AC695:
兼容性修改,版本:AC695N_soundbox_sdk_release_3.1.0
emitter_btlib_V311_20240313-1418.zip
修改以下文件:
#if TCFG_BLE_BRIDGE_EDR_ENALBE
const int config_le_sm_sub_sc_enable = 1; /*SC加密模式使能,need config_le_sm_support_enable = 1*/ const int config_le_sm_sub_sc_bridge_edr_enable = 1; /*SC加密模式下,ios app ble配对快连edr,need config_le_sm_sub_sc_enable = 1*/
#else
const int config_le_sm_sub_sc_enable = 0; /**/
const int config_le_sm_sub_sc_bridge_edr_enable = 0; /**/
#endif
const int config_btstask_use_malloc = 1;
u8 a2dp_support_delay_report =0;
小度音箱库文件更新:
AC696:修复多次唤醒断连问题
【金山文档】 dueros_stack-696v170 修复SPP拼包问题,支持配置是否支持电量显示
dueros_stack-696-v170-20250523.zip
// 电量显示 不支持:0, 对耳:2, 对耳 + 盒子:3, 单机:4
void dma_change_battery_structure(u8 flag); //默认返回0
注:公版小度默认不开启电量显示回调。
AC695::修复多次唤醒断连问题
【金山文档】 dueros_stack-695-v310-修复spp拼包问题
dueros_stack-695-v310-修复spp拼包问题.zip
JL701N :修复多次唤醒断连问题
dueros_stack-AC701v142 修改SPP拼包问题.zip
- 修改PNP的PID和VID 要修改的是下图中的内容
在apps\common\config\bt_profile_config.c添加数组
//定义的产品信息,for test
#define PNP_VID_SOURCE 0x02
#define PNP_VID 0x05ac //0x05d6
#define PNP_PID 0x022C //
#define PNP_PID_VERSION 0x011b //1.1.11
static const u8 sdp_pnp_service_data_hid[] = {
0x36, 0x00, 0x34, 0x09, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x0A, 0x09, 0x00, 0x01, 0x36, 0x00,
0x03, 0x19, 0x12, 0x00, 0x09, 0x02, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x01, 0x09, PNP_VID >> 8, PNP_VID & 0xFF,
0x09, 0x02, 0x02, 0x09, PNP_PID >> 8, PNP_PID & 0xFF, 0x09, 0x02, 0x03, 0x09, PNP_PID_VERSION >> 8, PNP_PID_VERSION & 0xFF, 0x09, 0x02, 0x04, 0x28,
0x01, 0x09, 0x02, 0x05, 0x09, 0x09, PNP_VID_SOURCE >> 8, PNP_VID_SOURCE & 0xFF, 0x00, 0x00
};
替换公版数组
FAQ
1.手机主动取消配对后,耳机不删除手机信息

2.蓝牙上电蓝牙回连时间修改
#define TIMEOUT_CONN_TIME 60 //超时断开之后回连的时间s
#define POWERON_AUTO_CONN_TIME 12 //开机去回连的时间
3.蓝牙打开1托2功能后,两台手机播放音乐按播放暂停进行切换操作注意事项
1. 目前设计不支持太快切换的,要有足够的数据包进行计数。故意测试打断功能时,两个手机操作应10秒以上。
- SDK提供数据包的阈值设置
/*高级音频打断检测数据包阈值设置*/
extern void __set_a2dp_sound_detect_counter(u8 sound_come, u8 sound_go);//默认 sound_come:70 sound_go:30 (单位都是数据包的个数)
//第一个参数是检测到有能量数据包的值,计数满了之后会进行打断,第二个参数就检测没有能量数据包的包数,计数满了之后会释放自己
4.蓝牙通话 输出方式选择:(选择不对可能通过算法不起作用)

f aec_param->output_way = 1; //默认DAC有声音的方案,默认填0
5. 设置蓝牙加密的level
////设置蓝牙加密的level
//io_capabilities ; /*0: Display only 1: Display YesNo 2: KeyboardOnly 3: NoInputNoOutput*/
//authentication_requirements: 0:not protect 1 :protect
__set_simple_pair_param(1, 0, 1);
6. 发送自定义AT指令示例
AC700N系列旧SDK(非可视化工具)
- lib_btctrler_config.c使能自定义AT命令功能

u8 user_at_cmd_send_support = 1;
- 定义自定义AT命令头和命令尾

#define USER_AT_CMD_HEAD "AT+USER="
#define USER_AT_CMD_TAIL "\r\0"
- 定义发送命令内容 参考示例如下:

- 发送自定义AT命令 这样去发送

sprintf(at_buf, "%s%d%s",USER_AT_CMD_HEAD, USER_AT_CMD_CALL_REJECT, USER_AT_CMD_TAIL);
user_send_at_cmd_prepare(at_buf, strlen(at_buf));
- 接收AT指令
int at_cmd_analysis_by_user(u8 *packet, u16 size)
{
//puts(packet);
return 0;
}
可视化版本
//发送
bt_cmd_prepare(USER_CTRL_HFP_SEND_USER_AT_CMD,len,data_ptr);
//接收
int at_cmd_analysis_by_user(u8 *addr, u8 *packet, u16 size)
{
return 0;
}
7.配置最大记忆的设备记录,默认是10(范围:0-10)
可视化SDK
void bt_set_max_remote_device_number(u8 number);
非可视化SDK
void set_bt_remote_device_number(u8 remote_device_number);
本文档为保密信息,未经授权,请勿转发或告知第三方。非常感谢!
This electronic documentation and any attachments are confidential and may be legally privileged or otherwise protected from disclosure. If you are not the intended recipient, please do not disclose the contents to anyone。
Thank you for your cooperation!
解析权归杰理所有!
The analysis right belongs to JieLi Technology Co., Ltd.
8.可视化SDK接收AT指令
int at_cmd_analysis_by_user(u8 *addr, u8 *packet, u16 size)
{
puts(packet);
printf("lllllllllllsize == %d\n",size);
return 0;
}
9. 蓝牙后台开启时,其他模式下播放音频,手机启动 a2dp 播放不返回蓝牙模式
方法一:后台丢包检测直接丢包。
测试 SDK :701 le audio v2.0.0 patch 02.14
修改接口(只贴出修改部分,接口内其他内容已省略):
void bt_background_resume(void) // 后台返回需要关闭能量检测重新打开 a2dp 链路
static void a2dp_slience_detect(void *_detect) // 后台能量检测直接丢包
#if TCFG_BACKGROUND_WITHOUT_EDR_CONNECT
void *devices[2];
if (btstack_get_conn_devices(devices, 2) < 1) { //无设备连接才打开自动关机
sys_auto_shut_down_enable();
}
#else
void *devices[2] = { 0 }; // 防止野指针
u8 conn_num = btstack_get_conn_devices(devices, 2);
if (conn_num < 1) {
sys_auto_shut_down_enable();
} else {
for (int i = 0; i < conn_num; i++) {
if (!devices[i]) {
continue;
}
u8* addr = btstack_get_device_mac_addr(devices[i]);
if (addr) {
log_info("conn addr:");
put_buf(addr, 6);
extern u8 app_get_a2dp_play_status(void);
if (app_get_a2dp_play_status()) {
set_g_play_addr(addr);
#if !TCFG_USER_TWS_ENABLE
a2dp_play_close(addr);
#else
tws_a2dp_player_close(addr);
#endif
}
} else {
log_info("get addr fail");
}
}
}
#endif
static void a2dp_slience_detect(void *_detect)
{
/*
* 不检测,一直丢包
*/
#if ((TCFG_A2DP_PREEMPTED_ENABLE == 0) && (TCFG_BT_DUAL_CONN_ENABLE))
#if (TCFG_BT_BACKGROUND_ENABLE)
#if !TCFG_USER_A2DP_PREEMPTED_RETURN // TCFG_USER_A2DP_PREEMPTED_RETURN = 1
if (bt_background_active() == true)
#else
if (bt_background_active() == false)
#endif
#endif
{
seqn += 10;
if ((seqn & 0xffff) == 0) {
seqn = 1;
}
a2dp_media_free_packet(detect->file, packet);
break;
}
#endif
}
方法二:断开 A2DP 链路
// 后台返回时重新连接
void bt_background_resume(void)
{
if (g_bt_hdl.background.backmode == BACKGROUND_GOBACK_WITH_MODE_SWITCH) {
if (tws_api_get_role() == TWS_ROLE_MASTER) {
bt_cmd_prepare(USER_CTRL_AUTO_CONN_A2DP, 0, NULL);
}
}
}
// 后台挂起时断开
void bt_background_suspend()
{
if (tws_api_get_role() == TWS_ROLE_MASTER) {
bt_cmd_prepare(USER_CTRL_DISCONN_A2DP, 0, NULL);
}
}
// 后台运行时手机断开连接时会自动连接 A2DP , 马上断开
static int bt_background_btstack_event_filter(struct bt_event *event)
{
case BT_STATUS_CONN_A2DP_CH:
if (tws_api_get_role() == TWS_ROLE_MASTER) {
bt_cmd_prepare_for_addr(event->args, USER_CTRL_DISCONN_A2DP, 0, NULL);
}
break;
}