7. WIFI_SOUNDBOX 工程说明
7.1. wifi_soundbox工程简介
wifi_soundbox工程实现了通过WIFI、蓝牙实现多功能智能音箱的应用
WIFI智能音箱应用于连接蓝牙进行蓝牙播放或连接WIFI进行网络资源播放,也可进行FLASH、SD卡和U盘播放
该工程具有的功能有TWS、蓝牙EDR功能、BLE功能、音频播放功能、录音功能、配网功能和接入第三方SDK功能,具体为:
- 蓝牙功能:
蓝牙媒体音频
蓝牙通话
蓝牙串口SPP通信
- BLE功能:
数传ble
通用HID功能
主机多机BLE通讯
- 音频播放功能:
- 本地播放
FLASH播放
SD卡播放
U盘播放
网络播放
LINEIN播放
PC播放
蓝牙播放
tws
录音功能
- 配网功能
声波配网
BLE配网
AIRKISS配网
WSC配网
7.2. wifi_soundbox工程操作说明
查看快速入门文档中的开发准备说明、开发环境安装说明、SDK工程编译与下载和SDK应用开发说明
工程打开路径:
apps\wifi_soundbox\board\wl83\AC792N_WIFI_SOUNDBOX.cbp
打开工程后SDK中选择开发板对应的板级:
Headers/apps/wifi_soundbox/board/wl83/board_config.h
中选择对应的板级宏打开,如下图以打开7926A开源学习板级为例
//芯片型号
// #define CONFIG_BOARD_AC7925A
// #define CONFIG_BOARD_AC7925B
#define CONFIG_BOARD_AC7926A
// #define CONFIG_BOARD_AC7922C
在app_config.h中选择需要使用的功能和平台将其宏打开
进行编译烧录使用
- 按键操作说明:
- K1(KEY_POWER):短按进入语音监听模式;
长按进行电话操作,如接听/挂断/回拨(在连接了蓝牙设备前提下); 双击唤醒手机语音助手如siri(蓝牙连接情况下);
- K2(KEY_MODE): 短按切换下一个模式;
长按进入配网模式,通过微信小程序进行声波、ble等配网; 双击开关蓝牙连接; 三击开关tws蓝牙配对
- K3(KEY_DOWN): 短按减小音量;
长按上一首(在播放音乐前提下); 调低音调(linein_music/pc模式下);
- K4(KEY_UP): 短按增大音量;
长按下一首(在播放音乐前提下);调高音调(linein_music/pc模式下);
- K5(KEY_OK): 短按暂停/播放;
长按开关录音
按KEY_MODE切换下一模式时,会从local_music到recorder再回到local_music循环切换
// 可选择的模式列表
static const app_mode_table_t app_mode_table[] = {
#if TCFG_APP_BT_EN
{ "bt", APP_MSG_BT_BOTTOM, APP_MSG_TWS_TOP, APP_MODE_BT },
#endif
#if TCFG_APP_NET_MUSIC_EN
{ "net_music", APP_MSG_NET_MUSIC_BOTTOM, APP_MSG_NET_MUSIC_TOP, APP_MODE_NET },
#endif
#if TCFG_APP_MUSIC_EN
{ "local_music", APP_MSG_LOCAL_MUSIC_BOTTOM, APP_MSG_LOCAL_MUSIC_TOP, APP_MODE_LOCAL },
#endif
#if TCFG_APP_LINEIN_EN
{ "linein_music", APP_MSG_LINEIN_MUSIC_BOTTOM, APP_MSG_LINEIN_MUSIC_TOP, APP_MODE_LINEIN },
#endif
#if TCFG_APP_PC_EN
{ "pc_music", APP_MSG_PC_MUSIC_BOTTOM, APP_MSG_PC_MUSIC_TOP, APP_MODE_PC },
#endif
#if TCFG_APP_RECORD_EN
{ "recorder", APP_MSG_RECORDER_BOTTOM, APP_MSG_RECORDER_TOP, APP_MODE_RECORDER },
#endif
#if TCFG_LOCAL_TWS_ENABLE
{ "sink_music", APP_MSG_SINK_MUSIC_BOTTOM, APP_MSG_SINK_MUSIC_TOP, APP_MODE_SINK },
#endif
};
模式切换函数:
void app_mode_change_next(void)
{
struct intent it;
init_intent(&it);
app_mode_t curr_mode = get_current_app_mode();
if (curr_mode > APP_MODE_BT) {
it.action = ACTION_REPLACE;
}
do {
if (++curr_mode == APP_MODE_SINK || curr_mode < APP_MODE_LOCAL) { //设定模式从APP_MODE_LOCAL到recorder
curr_mode = APP_MODE_LOCAL;
}
for (int i = 0; i < ARRAY_SIZE(app_mode_table); ++i) {
if (curr_mode == app_mode_table[i].app_mode) {
it.name = app_mode_table[i].app_name;
//清掉上一个app的恢复断点
app_msg_handler(NULL, APP_MSG_STOP);
if (0 == start_app(&it)) {
return;
}
}
}
} while (1);
}
提醒:AD按键配置的电阻值需对应板卡电阻。如在board_develop_AC7926A.h中已配置
//*********************************************************************************//
// AD按键配置 //
//*********************************************************************************//
#define TCFG_ADKEY_ENABLE 1 //AD按键
#define TCFG_PRESS_LONG_KEY_POWERON_ENABLE TCFG_ADKEY_ENABLE //长按开关机功能
#define TCFG_ADKEY_INPUT_IO IO_PORTD_00
#define TCFG_ADKEY_INPUT_CHANNEL ADC_IO_CH_PD00
#define ADKEY_UPLOAD_R 22 //上拉电阻值
#define TCFG_ADC_LEVEL_09 0x3FF
#define TCFG_ADC_LEVEL_08 0x3FF
#define TCFG_ADC_LEVEL_07 0x3FF
#define TCFG_ADC_LEVEL_06 0x3FF
#define TCFG_ADC_LEVEL_05 0x3FF
#define TCFG_ADC_LEVEL_04 (ADC_VDDIO * 100 / (100 + ADKEY_UPLOAD_R))
#define TCFG_ADC_LEVEL_03 (ADC_VDDIO * 33 / (33 + ADKEY_UPLOAD_R))
#define TCFG_ADC_LEVEL_02 (ADC_VDDIO * 15 / (15 + ADKEY_UPLOAD_R))
#define TCFG_ADC_LEVEL_01 (ADC_VDDIO * 51 / (51 + ADKEY_UPLOAD_R * 10))
#define TCFG_ADC_LEVEL_00 (0)
7.3. wifi_soundbox工程代码流程重点讲解
1.app_mian.c:app_main()入口:通过play_tone_file_callback创建player播放器对象,注册播放提示音结束回调函数poweron_tone_play_end_callback并播放开机提示音,随后调用回调函数poweron_tone_play_end_callback进入蓝牙模式,若开机提示音播放失败直接调用poweron_tone_play_end_callback进入蓝牙模式。
static int poweron_tone_play_end_callback(void *priv, enum stream_event event)
{
if (event == STREAM_EVENT_STOP) {
app_mode_change(APP_MODE_BT);
}
return 0;
}
/*
* 应用程序主函数
*/
void app_main(void)
{
puts("------------- wifi_soundbox app main-------------\n");
#ifdef USE_LVGL_V8_UI_DEMO
int lvgl_main_task_init(void);
lvgl_main_task_init();
#endif
int ret = play_tone_file_callback(get_tone_files()->power_on, NULL, poweron_tone_play_end_callback);//播放开机提示音
if (ret) {
poweron_tone_play_end_callback(NULL, STREAM_EVENT_STOP);
}
}
- 2.各模式app,音箱工程可主要分为:local_music本地音乐模式、net_music网络音乐模式、linein_music线路输入模式、pc模式、tws模式、蓝牙模式、录音模式,每个模式都有注册对应的APP
如net_music.c:
static const struct application_operation net_music_ops = {
.state_machine = net_music_state_machine,
.event_handler = net_music_event_handler,
.msg_handler = net_music_msg_handler,
};
REGISTER_APPLICATION(net_music) = {
.name = "net_music",
.ops = &net_music_ops,
.state = APP_STA_DESTROY,
};
通过REGISTER_APPLICATION注册net_music app,每个app有各自的net_music_state_machine状态机、net_music_event_handler事件处理器、net_music_msg_handler消息处理器。
static int net_music_key_click(struct key_event *key)
{
int ret = FALSE;
switch (key->value) {
case KEY_OK:
app_send_message(APP_MSG_NET_MUSIC_PP, 1, 1);
break;
//...........
}
按键事件通过app_send_message通过消息队列向app_core任务发送消息,app_core会调用对应的net_music_msg_handler执行相应的操作。
通过REGISTER_APP_EVENT_HANDLER注册单个事件处理函数,如net_app_cfg.c:
REGISTER_APP_EVENT_HANDLER(net_cfg_key_event) = {
.event = SYS_KEY_EVENT,
.from = KEY_EVENT_FROM_KEY,
.handler = net_cfg_key_event_handler,
};
7.4. wifi_soundbox工程功能配置说明
- 1.音频播放部分:编解码流程可参考“音频部分”文档中“音频编码”和“音频解码”
1.1本地播放可选择FLASH、SD卡和U盘之间切换,通过扫描文件和搜索音频格式进行选择解码播放。
/*local_music.c*/ //设备切换 case APP_MSG_LOCAL_MUSIC_CHANGE_DEV: log_info("change play dev"); __this->mount_play_logo = NULL; // 获取下一个设备 u8 auto_next_dev = ((msg[0] == APP_MSG_LOCAL_MUSIC_AUTO_NEXT_DEV) ? 1 : 0); logo = music_player_get_dev_next(__this->player_hd, auto_next_dev); if (logo == NULL) { //若找不到下一个设备,不响应设备切换 break; } __this->suspend_flag = 0; log_info("next dev is %s", logo); //切换设备前先保存当前的设备断点信息(解码信息和文件信息) if (music_player_get_playing_breakpoint(__this->player_hd, __this->breakpoint, 1) == TRUE) { music_player_stop(__this->player_hd, 0); //先停止当前播放,防止下一步操作VM卡顿 breakpoint_vm_write(__this->breakpoint, dev_manager_get_logo(__this->player_hd->dev)); // 保存断点信息 } #if (MUSIC_DEVICE_TONE_EN) if (0 == music_device_tone_play(logo)) { //播放下一个设备的提示音 break; } #endif // 播放下一设备 if (TRUE == breakpoint_vm_read(__this->breakpoint, logo)) { //读取设备断点信息 err = music_player_play_by_breakpoint(__this->player_hd, logo, __this->breakpoint); //从断点处继续播放 } else { err = music_player_play_first_file(__this->player_hd, logo); //从第一个文件开始播放 } break;
/*local_player.c*/ //从断点处播放 int music_player_play_by_breakpoint(struct music_player *player_hd, const char *logo, breakpoint_t *bp) { u32 bp_flag = 1; if (bp == NULL) { return music_player_play_first_file(player_hd, logo); // 无断点则从头播放 } if (logo == NULL) { music_player_stop(player_hd, 0); if (dev_manager_online_check(player_hd->dev, 1) == 0) { return MUSIC_PLAYER_ERR_DEV_OFFLINE; // 设备离线错误 } } else { music_player_stop(player_hd, 1); // 停止当前播放 // 查找指定设备 player_hd->dev = dev_manager_find_spec(logo, 1); if (player_hd->dev == NULL) { return MUSIC_PLAYER_ERR_DEV_NOFOUND; } //验证断点有效性 if (strcmp(logo, "sdfile")) { bp_flag = 0; set_bp_info(bp->sclust, bp->fsize, &bp_flag); //断点若有效把bp_flag置1,注意后面要用put_bp_info释放资源 } //设备文件扫描扫盘,获取文件列表 player_hd->fsn = file_manager_scan_disk(player_hd->dev, NULL, scan_parm, cycle_mode, (scan_callback_t *)player_hd->parm.scan_cb); } //错误检查 if (player_hd->fsn == NULL) { put_bp_info(); return MUSIC_PLAYER_ERR_FSCAN; } if (!bp_flag) { //断点无效 put_bp_info(); return MUSIC_PLAYER_ERR_PARM; } //查找文件 player_hd->file = file_manager_select(player_hd->dev, player_hd->fsn, FSEL_BY_SCLUST, bp->sclust, (scan_callback_t *)player_hd->parm.scan_cb);//根据文件簇号查找断点文件 put_bp_info(); if (player_hd->file == NULL) { return MUSIC_PLAYER_ERR_FILE_NOFOUND; } //文件有效性验证 struct vfs_attr attr = {0}; fget_attrs(player_hd->file, &attr); if (bp->fsize != attr.fsize) { return MUSIC_PLAYER_ERR_PARM; } //启动解码器 int err = music_player_decode_start(player_hd, player_hd->file, &(bp->dbp)); if (err == MUSIC_PLAYER_SUCC) { //选定新设备播放成功后,需要激活当前设备 dev_manager_set_active(player_hd->dev); log_info("%s ok", __FUNCTION__); } return err; }
1.2网络播歌:可进行网络音频资源播放或使用DLNA功能:通过ai_server平台服务器进行DLNA投播,在同一局域网可通过手机音乐app的dlna功能在设备上投播。通过将网络文件数据缓存在网络缓存区后进行读取播放。
注意:代码首次下载到设备不会自动连接wifi,需长按KEY_MODE键进入配网模式,手机连接上wifi后通过杰理智能机器人微信小程序对设备进行配网(声波配网、蓝牙配网等),连接wifi后会将wifi信息保存,下次开机自动连接。
wifi_app_task.c:wifi初始化及wifi事件处理
static void wifi_app_task(void *priv) //主要是create wifi 线程的 { wifi_set_store_ssid_cnt(NETWORK_SSID_INFO_CNT); //设置WiFi存储的SSID数量 #ifdef CONFIG_DUER_SDK_ENABLE u8 airkiss_aes_key[16] = { 0x65, 0x31, 0x63, 0x33, 0x36, 0x31, 0x63, 0x63, 0x32, 0x39, 0x65, 0x34, 0x33, 0x66, 0x62, 0x38 }; wifi_set_airkiss_key(airkiss_aes_key); #endif #if 0 wifi_set_smp_cfg_scan_all_channel(1); wifi_set_smp_cfg_airkiss_recv_ssid(1); #endif wifi_set_sta_connect_timeout(CONNECT_TIMEOUT_SEC); //WiFi连接超时时间 wifi_set_event_callback(wifi_event_callback); //WiFi 事件回调 #if !IP_NAPT_EXT struct wifi_mode_info info; if (0 == wifi_get_mode_stored_info(&info)) { //若成功读取到保存的wifi数据信息 wifi_and_network_on(); //启用 WiFi 和网络功能 rf_coexistence_scene_enter(RF_COEXISTENCE_SCENE_WIFI_FAST_CONNECT, 5000); } #endif }
static int wifi_event_callback(void *network_ctx, enum WIFI_EVENT event) { int ret = 0; struct net_event net = {0}; switch (event) { case WIFI_EVENT_MODULE_INIT:// WiFi模块初始化完成 wifi_event_module_init(); break; case WIFI_EVENT_MODULE_START:// WiFi模块启动 log_info("network_user_callback->WIFI_EVENT_MODULE_START"); wifi_event_module_start(); break; .......... .......... case WIFI_EVENT_STA_CONNECT_SUCC:// 成功连接目标wifi log_info("network_user_callback->WIFI_STA_CONNECT_SUCC, ch=%d", wifi_get_channel()); ret = wifi_event_sta_connect_succ(); break; case WIFI_EVENT_STA_CONNECT_TIMEOUT_NOT_FOUND_SSID:// 超时未找到指定SSID log_info("network_user_callback->WIFI_STA_CONNECT_TIMEOUT_NOT_FOUND_SSID"); net.event = NET_CONNECT_TIMEOUT_NOT_FOUND_SSID; net_event_notify(NET_EVENT_FROM_WIFI, &net); break; case WIFI_EVENT_STA_CONNECT_ASSOCIAT_FAIL:// 关联失败 log_info("network_user_callback->WIFI_STA_CONNECT_ASSOCIAT_FAIL"); net.event = NET_CONNECT_ASSOCIAT_FAIL; net_event_notify(NET_EVENT_FROM_WIFI, &net); break; case WIFI_EVENT_STA_CONNECT_ASSOCIAT_TIMEOUT:// 关联超时 log_info("network_user_callback->WIFI_STA_CONNECT_ASSOCIAT_TIMEOUT"); break; case WIFI_EVENT_STA_NETWORK_STACK_DHCP_SUCC:// DHCP获取IP成功 log_info("network_user_callback->WIFI_EVENT_STA_NETWPRK_STACK_DHCP_SUCC"); wifi_event_sta_network_stack_dhcp_succ(); break; case WIFI_EVENT_STA_DISCONNECT: //断开连接 log_info("network_user_callback->WIFI_STA_DISCONNECT"); wifi_event_sta_disconnect(); break; case WIFI_EVENT_STA_NETWORK_STACK_DHCP_TIMEOUT:// DHCP超时 log_info("network_user_callback->WIFI_EVENT_STA_NETWPRK_STACK_DHCP_TIMEOUT"); break; .......... .......... case WIFI_EVENT_SMP_CFG_TIMEOUT: // 智能配网超时 log_info("network_user_callback->WIFI_EVENT_SMP_CFG_TIMEOUT"); net.event = NET_EVENT_SMP_CFG_TIMEOUT; net_event_notify(NET_EVENT_FROM_WIFI, &net); break; case WIFI_EVENT_SMP_CFG_COMPLETED:// 智能配网完成 log_info("network_user_callback->WIFI_EVENT_SMP_CFG_COMPLETED"); net.event = NET_SMP_CFG_COMPLETED; net_event_notify(NET_EVENT_FROM_WIFI, &net); break; .......... .......... case WIFI_EVENT_AP_ON_ASSOC:// 有客户端连接到本设备热点 wifi_event_ap_on_assoc(network_ctx); break; case WIFI_EVENT_AP_ON_DISCONNECTED:// 客户端从本设备热点断开 wifi_event_ap_on_disconnected(network_ctx); break; default: break; } return ret; }
net_app_cfg.c:wifi配网相关
static int net_smp_cfg_event_handler(void *evt) //接收wifi信息后启动wifi连接 { struct net_event *event = (struct net_event *)evt; switch (event->event) { case NET_EVENT_SMP_CFG_FINISH: log_info("NET_EVENT_SMP_CFG_FINISH"); if (is_in_config_network_state()) {// 检查当前处于“网络配置状态” play_tone_file(get_tone_files()->net_ssid_recv);// 1. 播放“接收SSID”提示音 config_network_stop();// 2. 停止配网模式(退出监听状态,避免重复接收) config_network_connect();// 3. 启动网络连接(使用接收的SSID和密码连接WiFi) #if TCFG_SMART_VOICE_ENABLE audio_smart_voice_detect_open(JL_KWS_COMMAND_KEYWORD); #endif } else {// 若未处于配置状态,仅播放提示音并连接网络 play_tone_file(get_tone_files()->net_ssid_recv); config_network_connect(); } __this->recv_ssid_flag = 1; __this->request_connect_flag = 1; break; default: break; } return FALSE; }
ai.c:连接ai云服务器相关
static int ai_net_event_handler(void *evt) { struct net_event *event = (struct net_event *)evt; switch (event->event) { case NET_EVENT_CONNECTED: log_info("AI_NET_EVENT_CONNECTED"); ai_server_connect(); // 连接AI云服务 break; case NET_EVENT_DISCONNECTED: log_info("AI_NET_EVENT_DISCONNECTED"); ai_server_disconnect(); // 断开AI服务 break; default: break; } return FALSE; }
net_download接口:
- 1.3tws模式:实现source端、sink端设备协同
在sdk_config.h中选择配对方式(按键发起配对、开机自动配对、测试盒/充电仓配对) 及声道选择:
/*sdk_config.h*/ #define TCFG_BT_TWS_PAIR_MODE CONFIG_TWS_PAIR_BY_CLICK // 配对方式 #define TCFG_BT_TWS_CHANNEL_SELECT CONFIG_TWS_MASTER_AS_LEFT // 声道选择 /*app_config.h*/ /* 配对方式选择 */ #define CONFIG_TWS_PAIR_BY_CLICK 0 //按键发起配对 #define CONFIG_TWS_PAIR_BY_AUTO 1 //开机自动配对 #define CONFIG_TWS_PAIR_BY_BOX 2 //测试盒/充电仓配对 /* 声道确定方式选择 */ #define CONFIG_TWS_MASTER_AS_LEFT 0 //主机作为左耳 #define CONFIG_TWS_MASTER_AS_RIGHT 1 //主机作为右耳 #define CONFIG_TWS_AS_LEFT 2 //固定左耳 #define CONFIG_TWS_AS_RIGHT 3 //固定右耳 #define CONFIG_TWS_START_PAIR_AS_LEFT 4 //双击发起配对的耳机做左耳 #define CONFIG_TWS_START_PAIR_AS_RIGHT 5 //双击发起配对的耳机做右耳 #define CONFIG_TWS_EXTERN_UP_AS_LEFT 6 //外部有上拉电阻作为左耳 #define CONFIG_TWS_EXTERN_DOWN_AS_LEFT 7 //外部有下拉电阻作为左耳 #define CONFIG_TWS_EXTERN_UP_AS_RIGHT 8 //外部有上拉电阻作为右耳 #define CONFIG_TWS_EXTERN_DOWN_AS_RIGHT 9 //外部有下拉电阻作为右耳 #define CONFIG_TWS_CHANNEL_SELECT_BY_BOX 10 //充电仓/测试盒决定左右耳
source/sink角色决策:
/*local_tws.c*/ static int bt_local_tws_cmd_handler(void *evt) { .............. .............. case CMD_TWS_CONNECT_MODE_REPORT: if (cmd[1] == APP_MODE_BT && !current_app_in_mode(APP_MODE_BT)) { //当前仅考虑了除蓝牙模式其他模式都为Source的情况 log_info("CMD_TWS_CONNECT_MODE_REPORT:BECOME TO SOURCE"); local_tws_become_to_source(get_current_app_mode()); // 本地成为 Source } else if (cmd[1] != APP_MODE_BT && current_app_in_mode(APP_MODE_BT)) { //对端是非蓝牙模式,但本地是蓝牙模式 log_info("CMD_TWS_CONNECT_MODE_REPORT: MODE REPORT"); local_tws_connect_mode_report(); //// 向对端报告本地模式 } else if (cmd[1] == APP_MODE_BT && current_app_in_mode(APP_MODE_BT)) { //双方都处于蓝牙模式 //都处于蓝牙模式不需要操作 log_info("Both in BT MODE"); } else { //两边都处于非蓝牙模式,有可能一边在音乐模式下播歌,另一边开机切到音乐模式才连上 bool local_play_status = FALSE; struct local_tws_mode_ops *ops; list_for_each_local_tws_ops(ops) { if (!strcmp(ops->name, __get_current_app()->name) && ops->get_play_status) { local_play_status = ops->get_play_status(); } } log_info("local_play_status:%d remote_play_status:%d", local_play_status, cmd[2]); // 根据播放状态决策 if (local_play_status == FALSE && cmd[2] == TRUE) { // 本地未播放,对端在播放 local_tws_connect_mode_report(); // 同步到对端(成为 Sink) } else { local_tws_become_to_source(get_current_app_mode()); // 本地成为 Source } } break; .............. .............. }
- 2.蓝牙播歌和蓝牙通话部分
2.1需在app_conifg中配置开启蓝牙功能宏(选择下图中需要宏打开),进入蓝牙模式自动打开蓝牙,蓝牙连接后可进行蓝牙播歌和通话功能
bt_mode_init()入口:调用bt_function_select_init()函数进行蓝牙功能初始化,包括蓝牙支持连接的个数、通话使用msbc或cvsd、电量更新的周期时间、回连搜索时间长度、回连时超时参数设置、简易配对参数等。btstack_init()初始化蓝牙协议栈。
调用REGISTER_APP_EVENT_HANDLER()函数分别注册HCI层事件和蓝牙连接事件回调函数,处理相应事件,包括设备查询完成、蓝牙连接建立完成、请求确认配对、蓝牙第一次连接、第二次连接、初始化完成、断开连接等。
//*********************************************************************************// // EDR和BLE配置 // //*********************************************************************************// #ifdef CONFIG_BT_ENABLE #define TCFG_BT_MODE BT_NORMAL #define BT_EMITTER_EN 1 #define BT_RECEIVER_EN 2 #define TCFG_POWER_ON_ENABLE_EMITTER 0 //开机自动打开发射器 #define TCFG_POWER_ON_ENABLE_BT 0 //开机自动打开经典蓝牙 #define TCFG_POWER_ON_ENABLE_BLE 0 //开机自动打开BLE #define TCFG_USER_BT_CLASSIC_ENABLE 1 //经典蓝牙功能 #define TCFG_USER_BLE_ENABLE 1 //BLE功能使能 #define TCFG_USER_EMITTER_ENABLE 0 //蓝牙发射功能 #define TCFG_BT_DUAL_CONN_ENABLE 1 //经典蓝牙支持同时连接2台设备 #endif
/*bt.c*/ static int bt_mode_init(void) { ......... ......... bt_function_select_init(); bredr_handle_register(); btstack_init(); //蓝牙栈初始化 ......... ......... } /*bt_event_func.c*/ static void bt_function_select_init(void) { /* set_bt_data_rate_acl_3mbs_mode(1); */ #if TCFG_BT_DUAL_CONN_ENABLE bt_set_user_ctrl_conn_num(2); //设置蓝牙支持连接的个数,主要用于控制控制可发现可连接和回连流程 #else bt_set_user_ctrl_conn_num(1); #endif #if (defined(TCFG_BT_SUPPORT_MSBC) && TCFG_BT_SUPPORT_MSBC) bt_set_support_msbc_flag(TCFG_BT_SUPPORT_MSBC); //配置通话使用16k的msbc还是8k的cvsd #endif #if (defined(TCFG_BT_SUPPORT_AAC) && TCFG_BT_SUPPORT_AAC) bt_set_support_aac_flag(TCFG_BT_SUPPORT_AAC); //配置协议栈使用支持AAC的信息 bt_set_aac_bitrate(TCFG_BT_AAC_BITRATE); #endif #if (defined(TCFG_BT_SUPPORT_LHDC) && TCFG_BT_SUPPORT_LHDC) bt_set_support_lhdc_flag(TCFG_BT_SUPPORT_LHDC); //配置协议栈使用支持LHDC的信息 #endif #if (defined(TCFG_BT_SUPPORT_LHDC_V5) && TCFG_BT_SUPPORT_LHDC_V5) bt_set_support_lhdc_v5_flag(TCFG_BT_SUPPORT_LHDC_V5); #endif #if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC) bt_set_support_ldac_flag(TCFG_BT_SUPPORT_LDAC); #endif #if TCFG_BT_DISPLAY_BAT_ENABLE bt_set_update_battery_time(60); //设置电量显示发送更新的周期时间 #else bt_set_update_battery_time(0); //为0表示关闭电量显示功能 #endif /*回连搜索时间长度设置,可使用该函数注册使用,ms单位,u16*/ bt_set_page_timeout_value(0); /*回连时超时参数设置。ms单位。做主机有效*/ bt_set_super_timeout_value(8000); #if TCFG_BT_DUAL_CONN_ENABLE bt_set_auto_conn_device_num(2); #endif #if TCFG_BT_VOL_SYNC_ENABLE vol_sys_tab_init(); #endif /* io_capabilities * 0: Display only 1: Display YesNo 2: KeyboardOnly 3: NoInputNoOutput * authentication_requirements: 0:not protect 1 :protect */ #if TCFG_BT_BQB_PROFILE_TEST_ENABLE bt_set_simple_pair_param(1, 0, 2); //设置简易配对参数 #else bt_set_simple_pair_param(3, 0, 2); #endif #if 0 /*测试盒连接获取参数需要的一些接口注册*/ bt_testbox_ex_info_get_handle_register(TESTBOX_INFO_VBAT_VALUE, get_vbat_value); bt_testbox_ex_info_get_handle_register(TESTBOX_INFO_VBAT_PERCENT, get_vbat_percent); bt_testbox_ex_info_get_handle_register(TESTBOX_INFO_BURN_CODE, sdfile_get_burn_code); bt_testbox_ex_info_get_handle_register(TESTBOX_INFO_SDK_VERSION, bt_get_sdk_ver_info); #endif bt_set_sbc_cap_bitpool(TCFG_BT_SBC_BITPOOL); #if TCFG_USER_BLE_ENABLE u8 tmp_ble_addr[6]; #if TCFG_BT_BLE_BREDR_SAME_ADDR memcpy(tmp_ble_addr, (void *)bt_get_mac_addr(), 6); #else bt_make_ble_address(tmp_ble_addr, (void *)bt_get_mac_addr()); #endif le_controller_set_mac((void *)tmp_ble_addr); log_info("-----edr + ble 's address-----"); put_buf((void *)bt_get_mac_addr(), 6); put_buf((void *)tmp_ble_addr, 6); #endif #if (TCFG_BT_MODE != BT_NORMAL) set_bt_enhanced_power_control(1); #endif #if (TCFG_BT_SUPPORT_PROFILE_PBAP==1) ////设置蓝牙设备类型 bt_change_hci_class_type(BD_CLASS_CAR_AUDIO); #else bt_change_hci_class_type(BD_CLASS_LOUDSPEAKER); #endif } static void bredr_handle_register(void) { #if TCFG_BT_SUPPORT_PROFILE_SPP #if APP_ONLINE_DEBUG online_spp_init(); #endif bt_spp_data_deal_handle_register(spp_data_handler); //支持串口功能的数据处理接口 #endif bt_fast_test_handle_register(bt_fast_test_api);//测试盒快速测试接口 #if TCFG_BT_VOL_SYNC_ENABLE bt_music_vol_change_handle_register(bt_set_music_device_volume, bt_get_phone_device_vol); #endif #if TCFG_BT_DISPLAY_BAT_ENABLE bt_get_battery_percent_handle_register(bt_get_battery_value); /*电量显示获取电量的接口*/ #endif //样机进入dut被测试仪器链接上回调 bt_dut_test_handle_register(bt_dut_api); //获取远端设备蓝牙名字回调 bt_read_remote_name_handle_register(bt_read_remote_name); #if TCFG_BT_MUSIC_INFO_ENABLE //获取歌曲信息回调 bt_music_info_handle_register(user_get_bt_music_info); #endif }
//hci层事件处理 static int bt_hci_event_handler(void *evt) { struct bt_event *bt = (struct bt_event *)evt; //对应原来的蓝牙连接上断开处理函数 ,bt->value=reason log_info("bt_hci_event_handler reason 0x%x 0x%x", bt->event, bt->value); switch (bt->event) { case HCI_EVENT_INQUIRY_COMPLETE: //设备查询完成 log_info("HCI_EVENT_INQUIRY_COMPLETE"); bt_hci_event_inquiry(bt); break; case HCI_EVENT_USER_CONFIRMATION_REQUEST: log_info("HCI_EVENT_USER_CONFIRMATION_REQUEST"); ///<可通过按键来确认是否配对 1:配对 0:取消 bt_send_pair(1); break; case HCI_EVENT_USER_PASSKEY_REQUEST: log_info("HCI_EVENT_USER_PASSKEY_REQUEST"); ///<可以开始输入6位passkey break; case HCI_EVENT_USER_PRESSKEY_NOTIFICATION: log_info("HCI_EVENT_USER_PRESSKEY_NOTIFICATION %x", bt->value); ///<可用于显示输入passkey位置 value 0:start 1:enrer 2:earse 3:clear 4:complete break; case HCI_EVENT_PIN_CODE_REQUEST: log_info("HCI_EVENT_PIN_CODE_REQUEST"); break; case HCI_EVENT_VENDOR_NO_RECONN_ADDR: log_info("HCI_EVENT_VENDOR_NO_RECONN_ADDR"); bt_hci_event_disconnect(bt); break; case HCI_EVENT_DISCONNECTION_COMPLETE: log_info("HCI_EVENT_DISCONNECTION_COMPLETE"); if (!__this->enable) { break; } if (bt->value == ERROR_CODE_CONNECTION_TIMEOUT) { bt_hci_event_connection_timeout(bt); } bt_hci_event_disconnect(bt); break; case BTSTACK_EVENT_HCI_CONNECTIONS_DELETE: case HCI_EVENT_CONNECTION_COMPLETE: log_info("HCI_EVENT_CONNECTION_COMPLETE"); switch (bt->value) { case ERROR_CODE_SUCCESS: log_info("ERROR_CODE_SUCCESS"); testbox_in_ear_detect_test_flag_set(0); break; case ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED: case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES: case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR: case ERROR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED: case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: case ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST: case ERROR_CODE_AUTHENTICATION_FAILURE: //case CUSTOM_BB_AUTO_CANCEL_PAGE: bt_hci_event_disconnect(bt); break; case ERROR_CODE_CONNECTION_TIMEOUT: log_info("ERROR_CODE_CONNECTION_TIMEOUT"); bt_hci_event_connection_timeout(bt); break; default: break; } break; default: break; } return FALSE; } /* * 对应原来的状态处理函数,连接,电话状态等 */ static int bt_connction_status_event_handler(void *evt) { struct bt_event *bt = (struct bt_event *)evt; u8 a2dp_vol_mac[6]; switch (bt->event) { case BT_STATUS_INIT_OK: /* 蓝牙初始化完成 */ log_info("BT_STATUS_INIT_OK"); bt_status_init_ok(); break; case BT_STATUS_SECOND_CONNECTED: bt_clear_current_poweron_memory_search_index(0); case BT_STATUS_FIRST_CONNECTED: log_info("BT_STATUS_CONNECTED"); #if TCFG_USER_TWS_ENABLE bt_tws_phone_connected(); #endif #if TCFG_BT_SUPPORT_PROFILE_MAP bt_cmd_prepare(USER_CTRL_MAP_READ_TIME, 0, NULL); #endif #if TCFG_BT_VOL_SYNC_ENABLE u8 *vol_addr = malloc(6); if (vol_addr) { memcpy(vol_addr, bt->args, 6); __this->vol_sync_timer = sys_timeout_add(vol_addr, bt_connect_vol_sync, 2000); } #endif break; case BT_STATUS_FIRST_DISCONNECT: case BT_STATUS_SECOND_DISCONNECT: log_info("BT_STATUS_DISCONNECT"); break; case BT_STATUS_CONN_A2DP_CH: log_info("BT_STATUS_CONN_A2DP_CH"); memcpy(a2dp_vol_mac, bt->args, 6); app_audio_bt_volume_save_mac(a2dp_vol_mac); break; case BT_STATUS_DISCON_A2DP_CH: log_info("BT_STATUS_DISCON_A2DP_CH"); break; case BT_STATUS_AVRCP_INCOME_OPID: #define AVC_VOLUME_UP 0x41 #define AVC_VOLUME_DOWN 0x42 #define AVC_PLAY 0x44 #define AVC_PAUSE 0x46 log_info("BT_STATUS_AVRCP_INCOME_OPID:0x%x", bt->value); if (bt->value == AVC_VOLUME_UP) { } else if (bt->value == AVC_VOLUME_DOWN) { } else if (bt->value == AVC_PLAY) { bt_music_player_time_timer_deal(1); } else if (bt->value == AVC_PAUSE) { bt_music_player_time_timer_deal(0); } break; case BT_STATUS_AVRCP_VOL_CHANGE: if (!__this->vol_sync_timer && #if TCFG_USER_TWS_ENABLE tws_api_get_role() != TWS_ROLE_SLAVE && #endif !a2dp_player_get_btaddr(a2dp_vol_mac)) { bt_set_music_device_volume(bt->value); } break; default: log_info("BT STATUS DEFAULT : 0x%x", bt->event); break; } return FALSE; }
- 3.设备配网及使用第三方平台
3.1配网:在app_config.h选择第三方平台,例如使用图灵平台,如下打开宏#define CONFIG_DUER_SDK_ENABLE //使用百度平台
当前仅可通过杰理平台配网,第三方平台需自行移植接通。
//==============网络配置.json--第三方平台相关配置==========/ #if TCFG_SERVER_ASSIGN_PROFILE #define CONFIG_SERVER_ASSIGN_PROFILE //第三方平台的profile由杰理服务器分配 #endif #if TCFG_PROFILE_UPDATE #define CONFIG_PROFILE_UPDATE //每次开机都从杰理服务器获取第三方平台的profile,不读flash #endif #if TCFG_HTTP_SERVER_ENABLE #define CONFIG_HTTP_SERVER_ENABLE //HTTP服务器 #endif #if TCFG_FTP_SERVER_ENABLE #define CONFIG_FTP_SERVER_ENABLE //FTP服务器 #endif #if TCFG_DLNA_SDK_ENABLE #define CONFIG_DLNA_SDK_ENABLE //打开DLNA音乐播放功能 #endif #if TCFG_AI_SERVER == TCFG_DUER_ENABLE #define CONFIG_DUER_SDK_ENABLE //使用百度平台 #elif TCFG_AI_SERVER == TCFG_AI_SERVER_DISABLE // #define CONFIG_TURING_SDK_ENABLE //使用图灵平台 // #define CONFIG_DUI_SDK_ENABLE //使用思必驰DUI平台 // #define CONFIG_ALI_SDK_ENABLE //使用天猫精灵平台 // #define CONFIG_TVS_SDK_ENABLE //使用腾讯云平台 // #define CONFIG_TELECOM_SDK_ENABLE //电信云平台 // #define CONFIG_JL_CLOUD_SDK_ENABLE //打开使用杰理云平台 /*************电信云平台配网方式选择*************/ #ifdef CONFIG_TELECOM_SDK_ENABLE #define CONFIG_APLINK_NET_CFG //电信AP配网。注意:1.电信AP配网不能与elink无感配网同时使能。2.wifi库需要支持ap #ifndef CONFIG_APLINK_NET_CFG #define CONFIG_ELINK_QLINK_NET_CFG //电信elink无感配网。注意:elink无感配网不能与AIRKISS配网同时使能。 #endif #define CONFIG_CTEI_DEVICE_ENABLE //电信非蜂窝类串码对接设备使用 //#define CONFIG_MC_DEVICE_ENABLE //电信mc接口类直连设备使用 #endif #endif
未选用第三方平台时,可使用杰理智能机器人配网工具进行配网
通过长按K2进入配网模式,使用声波、蓝牙可进行配置网络,配网时手机与选择配置的网络必须为同一网络。
可在app_config.h选择配网方式
//==============网络配置.json--配网相关配置==============/ #if TCFG_WIFI_ENABLE // #define CONFIG_AIRKISS_NET_CFG //AIRKISS配网 // #define CONFIG_WSC_NET_CFG //WSC配网 #endif #if TCFG_ACOUSTIC_COMMUNICATION_NODE_ENABLE #define CONFIG_ACOUSTIC_COMMUNICATION_ENABLE //声波配网 #endif
蓝牙配网须在app_config.h配置第三方协议,开启NET_CFG_EN:
#ifndef THIRD_PARTY_PROTOCOLS_SEL #define THIRD_PARTY_PROTOCOLS_SEL NET_CFG_EN #endif
对应功能实现:
/*multi_protocol_main.c*/ #if (THIRD_PARTY_PROTOCOLS_SEL & NET_CFG_EN) && TCFG_POWER_ON_ENABLE_BLE le_net_cfg_all_init(); #endif