2.6. DEMO_EDR工程说明

概述

本工程展示了使用蓝牙EDR进行播歌和通话测试的使用示例。

  • 1.实现蓝牙EDR播歌中暂停播放、上下曲、音量增减等功能。

  • 2.实现蓝牙EDR通话中接听和挂断电话,音量增减等功能。

  • 3.实现蓝牙发射功能。

  • 4.实现spp数据传输功能。

具体的示例工程代码详见 apps/demo/demo_edr

2.6.1. 工程配置说明

apps/demo/demo_edr/app_config

#define CONFIG_POWER_ON_ENABLE_BT                 1     //开机自动打开经典蓝牙
#define TCFG_USER_BT_CLASSIC_ENABLE               1     //经典蓝牙功能
#define TCFG_USER_BLE_ENABLE                      0     //BLE功能使能
#define TCFG_USER_EMITTER_ENABLE                  1     //蓝牙发射功能
  • 修改蓝牙初始名称user_cfg.c:

  • 可修改bt_get_local_name()中edr_name:JL-AC79XX。

  • 在bt_get_pin_code()函数修改设备返回的pin code。

  • 如有特殊需求,可在bt_get_mac_addr()函数修改设备的蓝牙mac地址。

const char *bt_get_local_name(void)
{
#ifndef CONFIG_RF_TEST_ENABLE
    const u8 *mac_addr;
    mac_addr = bt_get_mac_addr();
    sprintf(edr_name, "JL-AC79XX-%02X%02X", mac_addr[4], mac_addr[5]);//修改edr_name
#endif
    return edr_name;
}

const char *bt_get_pin_code(void)
{
    return pincode;
}

const u8 *bt_get_mac_addr(void)
{
    static u8 mac_addr[6];

#if defined CONFIG_WIFI_ENABLE && !defined CONFIG_RF_TEST_ENABLE
    wifi_get_mac(mac_addr);
    if (bytecmp(mac_addr, 0, 6)) {
        return mac_addr;
    } else
#endif
    {
        if (syscfg_read(CFG_BT_MAC_ADDR, mac_addr, 6) == 6) {
            return mac_addr;
        }

        u8 flash_uid[16];
        memcpy(flash_uid, get_norflash_uuid(), 16);
        do {
            u32 crc32 = rand32()^CRC32(flash_uid, sizeof(flash_uid));
            u16 crc16 = rand32()^CRC16(flash_uid, sizeof(flash_uid));
            memcpy(mac_addr, &crc32, sizeof(crc32));
            memcpy(&mac_addr[4], &crc16, sizeof(crc16));
        } while (!bytecmp(mac_addr, 0, 6));
        //此处用户可自行修改为本地生成mac地址的算法
        mac_addr[0] &= ~((1 << 0) | (1 << 1));
        syscfg_write(CFG_BT_MAC_ADDR, mac_addr, 6);
        return mac_addr;
    }
}

2.6.2. 操作说明:

按键功能:查看bt_music.c中bt_music_key_event_handler()

  • 1.播歌模式

A ) 暂停或播放:KEY_OK按键。
B ) 音量增大:短按KEY_VOLUME_INC按键。
C ) 音量减小:短按KEY_VOLUME_DEC按键。
D ) 播放上一首:长按KEY_VOLUME_DEC按键。
E ) 播放下一首:长按KEY_VOLUME_INC按键。

  • 2.通话模式

A ) 接听或挂断电话:KEY_OK按键。
B ) 音量增大:短按KEY_VOLUME_INC按键。
C ) 音量减小:短按KEY_VOLUME_DEC按键。

  • 3.发射模式

长按KEY_MODE按键切换到蓝牙发射模式,进行设备搜索并根据过滤规则连接指定设备。

  • 4.通过spp数传app进行开发板与手机之间的数据传输。发送间隔2ms,sent_len长度660,速率最快150Kbytes。底层限制了每次发送的最大长度,当sent_len超过660时受限制。

  • 5.关闭或打开蓝牙,短按KEY_MODE按键。切换蓝牙到发射或者接收模式,长按KEY_MODE按键。

2.6.3. 代码流程

  • 1.app_main(),调用bt_ble_module_init()进行蓝牙BR协议栈初始化

//app_main.c
 /*
 * 应用程序主函数
 */
 void app_main()
 {
     puts("------------- demo_edr app main-------------\n");

     extern void bt_ble_module_init(void);
     bt_ble_module_init();
 }
  • 2.bt_ble_module_init()

A ) 调用bt_function_select_init()函数进行经典蓝牙功能配置,包括音量是否同步、是否连接自动播放、是否定时更新电量、回连搜索超时、蓝牙加密等级和蓝牙设备类型等功能。
B ) 调用bredr_handle_register()函数进行协议栈回调函数注册,包括SPP数传数据回调、蓝牙音量同步接口回调、电量显示获取电量的接口、被测试盒链接上进入快速测试回调、样机进入dut被测试仪器链接上回调、获取远端设备蓝牙名字回调、获取歌曲信息回调、发射器设置回调等。
C ) 调用btstack_init()函数进行蓝牙协议栈初始化。
D ) 调用sys_auto_sniff_controle()函数进行自动进入sniff低功耗模式,空闲5s之后进入sniff模式。
E ) bt_connction_status_event_handler()函数负责处理协议栈连接、加密、媒体播放、通话等各种状态的回调。
F ) bt_hci_event_handler()函数负责处理协议栈各种HCI事件的回调。

void bt_ble_module_init(void)
{
    bt_function_select_init();
    bredr_handle_register();
    btstack_init();
}

static void bt_function_select_init(void)
{
    __this->media_volume = INIT_VOLUME_VALUE;
    __this->call_volume = INIT_VOLUME_VALUE;

    __set_a2dp_auto_play_flag(1); /* 连接高级音频后自动播放歌曲 */
    __set_support_msbc_flag(1);
#if TCFG_BT_SUPPORT_AAC
    __set_support_aac_flag(1);
#endif
#if BT_SUPPORT_DISPLAY_BAT
    __bt_set_update_battery_time(60);
#else
    __bt_set_update_battery_time(0);
#endif
    __set_page_timeout_value(8000); /*回连搜索时间长度设置,可使用该函数注册使用,ms单位,u16*/
    __set_super_timeout_value(8000); /*回连时超时参数设置。ms单位。做主机有效*/

    __set_simple_pair_flag(1); //是否打开简易配对功能,打开后不需要输入pincode
    ////设置蓝牙加密的level
    //io_capabilities ; /*0: Display only 1: Display YesNo 2: KeyboardOnly 3: NoInputNoOutput*/
    //authentication_requirements: 0:not protect  1 :protect
    __set_simple_pair_param(3, 0, 2);

#if (USER_SUPPORT_PROFILE_PBAP==1)
    ////设置蓝牙设备类型
    __change_hci_class_type(BD_CLASS_CAR_AUDIO);
#endif

#if (TCFG_BT_SNIFF_ENABLE == 0)
    void lmp_set_sniff_disable(void);
    lmp_set_sniff_disable();
#endif
}

static void bredr_handle_register(void)
{
#if USER_SUPPORT_PROFILE_SPP
    extern void user_spp_data_handler(u8 packet_type, u16 ch, u8 * packet, u16 size);
    spp_data_deal_handle_register(user_spp_data_handler);
#endif

#if BT_SUPPORT_MUSIC_VOL_SYNC
    ///蓝牙音乐和通话音量同步
    music_vol_change_handle_register(bt_set_music_device_volume, phone_get_device_vol);
#endif
#if BT_SUPPORT_DISPLAY_BAT
    ///电量显示获取电量的接口
    get_battery_value_register(bt_get_battery_value);   /*电量显示获取电量的接口*/
#endif
    ///被测试盒链接上进入快速测试回调
    /* bt_fast_test_handle_register(bt_fast_test_api); */

    ///样机进入dut被测试仪器链接上回调
    bt_dut_test_handle_register(bt_dut_api);

    ///获取远端设备蓝牙名字回调
    read_remote_name_handle_register(bt_read_remote_name);

    ////获取歌曲信息回调
    /* bt_music_info_handle_register(user_get_bt_music_info); */

#if TCFG_USER_EMITTER_ENABLE
    ////发射器设置回调等
    inquiry_result_handle_register(emitter_search_result);
#endif
}
  • 3.transport_spp_init()函数,初始化spp传输初始化

//bt_music.c
static int bt_connction_status_event_handler(struct bt_event *bt)
{
    log_i("-----------------------bt_connction_status_event_handler %d\n", bt->event);

    switch (bt->event) {
    case BT_STATUS_INIT_OK:
        /*
        * 蓝牙初始化完成
        */
        log_i("===================BT_STATUS_INIT_OK\n");
#if SPP_TRANS_DATA_EN
        extern void transport_spp_init(void);
        transport_spp_init();
#endif
        bt_connection_enable();
        break;
  • 4.蓝牙发射功能(bt_emitter.c):蓝牙发射搜索结果回调处理,根据过滤规则可以做名字过滤、地址过滤、自定义过滤和无限制规则,也可以保存搜索到的所有设备再选择一个进行连接。

提供按键切换发射器或者接收器功能:2:发射 1:接收 0: 关闭。
发射器设置回调包括设备名字name、设备名字长度len、设备类型class和设备信号强度rssi等。
//bt_music.c的bt_music_key_event_handler()长按KEY_MODE切换发射器和接收器
case KEY_MODE:
#if TCFG_USER_EMITTER_ENABLE
            //发射器或者接收器切换
            if (__this->enable) {
                if (__this->emitter) {
                    bt_emitter_disable();
                } else {
                    key_event_disable();
                    bt_connection_disable();
                    sys_timeout_add(NULL, bt_emitter_enable, 1000);
                }
            }
#endif
            break;

//bt_emitter.c
#define  SEARCH_BD_ADDR_LIMITED 0  //搜索蓝牙地址过滤
#define  SEARCH_BD_NAME_LIMITED 1  //搜索蓝牙名称过滤
#define  SEARCH_CUSTOM_LIMITED  2  //自定义搜索
#define  SEARCH_NULL_LIMITED    3  //没有指定限制,则搜到什么就连接什么

#define SEARCH_LIMITED_MODE  SEARCH_BD_NAME_LIMITED

//地址过滤
#if (SEARCH_LIMITED_MODE == SEARCH_BD_ADDR_LIMITED)
static const u8 bd_addr_filt[][6] = {
    {0x8E, 0xA7, 0xCA, 0x0A, 0x5E, 0xC8}, /*S10_H*/
    {0xA7, 0xDD, 0x05, 0xDD, 0x1F, 0x00}, /*ST-001*/
    {0xE9, 0x73, 0x13, 0xC0, 0x1F, 0x00}, /*HBS 730*/
    {0x38, 0x7C, 0x78, 0x1C, 0xFC, 0x02}, /*Bluetooth*/
};

/*----------------------------------------------------------------------------*/
/**@brief    蓝牙发射搜索通过地址过滤
@param    无
@return   无
@note
*/
/*----------------------------------------------------------------------------*/
static u8 search_bd_addr_filt(const u8 *addr)
{
    log_i("bd_addr:");
    put_buf(addr, 6);

    for (u8 i = 0; i < (sizeof(bd_addr_filt) / sizeof(bd_addr_filt[0])); i++) {
        if (memcmp(addr, bd_addr_filt[i], 6) == 0) {
            /* printf("bd_addr match:%d\n", i); */
            return TRUE;
        }
    }
    /*log_i("bd_addr not match\n"); */
    return FALSE;
}
#endif

//名称过滤
#if (SEARCH_LIMITED_MODE == SEARCH_BD_NAME_LIMITED)
#if 0
static const u8 bd_name_filt[][32] = {
    "BeMine",
    "EDIFIER CSR8635",/*CSR*/
    "JL-BT-SDK",/*Realtek*/
    "I7-TWS",/*ZKLX*/
    "TWS-i7",/*ZKLX*/
    "I9",/*ZKLX*/
    "小米小钢炮蓝牙音箱",/*XiaoMi*/
    "小米蓝牙音箱",/*XiaoMi*/
    "XMFHZ02",/*XiaoMi*/
    "JBL GO 2",
    "i7mini",/*JL tws AC690x*/
    "S08U",
    "AI8006B_TWS00",
    "S046",/*BK*/
    "AirPods",
    "CSD-TWS-01",
    "AC692X_wh",
    "JBL GO 2",
    "JBL Flip 4",
    "BT Speaker",
    "CSC608",
    "QCY-QY19",
    "Newmine",
    "HT1+",
    "S-35",
    "T12-JL",
    "Redmi AirDots_R",
    "Redmi AirDots_L",
    "AC69_Bluetooth",
    "FlyPods 3",
    "MNS",
    "Jam Heavy Metal",
    "Bluedio",
    "HR-686",
    "BT MUSIC",
    "BW-USB-DONGLE",
    "S530",
    "XPDQ7",
    "MICGEEK Q9S",
    "S10_H",
    "S10",/*JL AC690x*/
    "S11",/*JL AC460x*/
    "HBS-730",
    "SPORT-S9",
    "Q5",
    "IAEB25",
    "T5-JL",
    "MS-808",
    "LG HBS-730",
    "NG-BT07"
};
#else
static const u8 bd_name_filt[][30] = {
    "JL-AC79XX-AF0B",
    "JL-AC79XX-AAFF",
};
#endif

/*----------------------------------------------------------------------------*/
/**@brief    蓝牙发射搜索通过名字过滤
@param    无
@return   无
@note
*/
/*----------------------------------------------------------------------------*/
u8 search_bd_name_filt(const char *data, u8 len, u32 dev_class, char rssi)
{
    char bd_name[64] = {0};

    if ((len > (sizeof(bd_name))) || (len == 0)) {
        return FALSE;
    }

    memcpy(bd_name, data, len);
    log_i("name:%s,len:%d,class %x ,rssi %d\n", bd_name, len, dev_class, rssi);

    for (u8 i = 0; i < (sizeof(bd_name_filt) / sizeof(bd_name_filt[0])); i++) {
        if (memcmp(data, bd_name_filt[i], len) == 0) {
            log_i("\n*****find dev ok******\n");
            return TRUE;
        }
    }

    return FALSE;
}
#endif

/*----------------------------------------------------------------------------*/
/**@brief    蓝牙发射搜索结果回调处理
@param    name : 设备名字
            name_len: 设备名字长度
            addr:   设备地址
            dev_class: 设备类型
            rssi:   设备信号强度
@return   无
@note
            蓝牙设备搜索结果,可以做名字/地址过滤,也可以保存搜到的所有设备
            在选择一个进行连接,获取其他你想要的操作。
            返回TRUE,表示搜到指定的想要的设备,搜索结束,直接连接当前设备
            返回FALSE,则继续搜索,直到搜索完成或者超时
*/
/*----------------------------------------------------------------------------*/
u8 emitter_search_result(char *name, u8 name_len, u8 *addr, u32 dev_class, char rssi)
{
    if (emitter_or_receiver != BT_EMITTER_EN) {
        return 0;
    }

#if (SEARCH_LIMITED_MODE == SEARCH_BD_NAME_LIMITED)
    if (name == NULL) {
        struct inquiry_noname_remote *remote = zalloc(sizeof(struct inquiry_noname_remote));
        remote->match = 0;
        remote->class = dev_class;
        remote->rssi = rssi;
        memcpy(remote->addr, addr, 6);
        os_mutex_pend(&mutex, 0);
        list_add_tail(&remote->entry, &inquiry_noname_list);
        if (read_name_start == 0) {
            read_name_start = 1;
            user_send_cmd_prepare(USER_CTRL_READ_REMOTE_NAME, 6, addr);
        }
        os_mutex_post(&mutex);
    }
#endif

#if (SEARCH_LIMITED_MODE == SEARCH_BD_NAME_LIMITED)
    return search_bd_name_filt(name, name_len, dev_class, rssi);
#endif

#if (SEARCH_LIMITED_MODE == SEARCH_BD_ADDR_LIMITED)
    return search_bd_addr_filt(addr);
#endif

#if (SEARCH_LIMITED_MODE == SEARCH_CUSTOM_LIMITED)
    /*以下为搜索结果自定义处理*/
    char bt_name[63] = {0};
    u8 len;
    if (name_len == 0) {
        log_i("No_eir\n");
    } else {
        len = (name_len > 63) ? 63 : name_len;
        /* display bd_name */
        memcpy(bt_name, name, len);
        log_i("name:%s,len:%d,class %x ,rssi %d\n", bt_name, name_len, dev_class, rssi);
    }

    /* display bd_addr */
    put_buf(addr, 6);

    /* You can connect the specified bd_addr by below api      */
    //user_send_cmd_prepare(USER_CTRL_START_CONNEC_VIA_ADDR,6,addr);

    return FALSE;
#endif

#if (SEARCH_LIMITED_MODE == SEARCH_NULL_LIMITED)
    /*没有指定限制,则搜到什么就连接什么*/
    return TRUE;
#endif
}