KDocs 研发资料库
  • 手表穿戴技术应用文档入口汇总
  • JL707N穿戴客述问题FAQ
  • 彩屏仓 硬件开发指南 与 量产作业流程规范
  • 彩屏仓 AC98N和JL701烧录升级注意事项
  • JL707 硬件开发资料
  • JL707 手表硬件设计注意事项 总结
  • 穿戴生产烧录OTA和key文件添加使用说明
  • JL707N-入门到进阶开发指导文档
  • 彩屏仓982相关
  • 彩屏仓耳机介绍
  • 杰理701_707双屏demo介绍
  • 707电子吧唧开发资料
  • GPIO相关
  • UART串口相关
  • SPI相关
  • nandflash相关
  • norflash相关
  • IIC相关
  • PWM相关
  • SD卡相关
  • 定时器TIMER
  • 文件系统接口设计说明文档
  • 音视频录像开发流程
  • rdec编码器
  • 穿戴功耗数据标准
  • 手表蓝牙开发文档
  • 701蓝牙PAN联网demo
  • BLE开发使用说明
  • 701手表2.0.0版本SDK EQ在线调试和离线调试方法 V1.1
  • JL707 手表喇叭输出声音大小评估方法 V1.0
  • JL707 手表音频EQ调试方法 V1.0
  • AC707N 手表通话写卡 软件配置方法
  • 录音功能
  • 701手表2.X.X系列SDK调试EQ配置方法及音频调试技巧V1.1
  • 701手表104版本SDK EQ在线调试和离线调试方法 V1.1
  • mic2dac示例
  • P11 SensorHub相关
  • 手表升级操作文档
  • AC707NGPU
  • DBI模块介绍
  • GPU模块介绍
  • 701N屏幕驱动配置说明
  • 701N_UI编辑工具用户手册
  • 支付宝资料
  • 支付宝硬件接入资料
  • 支付宝乘车码接入资料
  • CAT1 接入文档
  • 701N_WATCH_CAT1开发说明
  • Findmy功能
  • music模式相关
  • 707电池曲线
  • 707N内置FLASH挂载FAT文件系统251112_v1.2
  • AC707N_本地视频播放demo(AVI-MJPEG-PCM)
  • 光波导屏幕调试
  • 耳机研发和生产流程 指南
  • 707-NandFlash 代码和UI资源独立烧录方案
  • 彩屏仓时间同步补丁
  • 彩屏仓抖音点赞移植
  • 707LVGLv8双屏开发
  • 707N蓝牙互传图传demo
  • AVIPCM数据流接入AIVOICE
  • Flash分区操作demo
  • 手表发射问题
  • 701_707 双备份升级DEMO
  • 707_乘车码刷码失败问题_共性

BLE主从模式

📎 原始文档:https://www.kdocs.cn/l/cc8HNmAlgJ9Y BLE主从模式

  1. BLE主从模式 BLE主从模式是指ble同时做主机和从机,但目前暂时只支持同时1主1从,即1个master,1个slave。不开启该模式则是默认只做从机,不具备主动去扫描连接其他设备的ble功能。
  2. sdk开启该功能 在对应的板级配置board_xxxx_demo_cfg.h文件中,将下图中的宏开启,即是开启了该功能。
    image
#define SMBOX_MULTI_BLE_EN                  1 //蓝牙BLE多连:1主1从

2.按键开启该模式

image 目前sdk的开启方式是按下按键切换,因为sdk流程中默认是会先跑单从机的初始化,所以目前做的方式是按键切换。公版sdk中是开启该功能后,key0按键单击即可切换。
如果想上电就开启该模式,也可参考以下修改:
image

void bt_ble_init(void)
{
    log_info("***** ble_init******\n");

    if (ble_init_flag) {
        return;
    }

#if TCFG_PAY_ALIOS_ENABLE
    upay_mode_enable = 0;
    upay_new_adv_enable = 0;

#if UPAY_ONE_PROFILE
    if (config_btctler_le_hw_nums < 2) {
        log_info("error:need add hw to adv new device!!!\n");
        ASSERT(0);
    }
#endif

#endif

    gap_device_name = bt_get_local_name();
    gap_device_name_len = strlen(gap_device_name);
    if (gap_device_name_len > BT_NAME_LEN_MAX) {
        gap_device_name_len = BT_NAME_LEN_MAX;
    }

    log_info("ble name(%d): %s \n", gap_device_name_len, gap_device_name);

    set_ble_work_state(BLE_ST_INIT_OK);
    bt_ble_init_do();

    ble_vendor_set_default_att_mtu(ATT_LOCAL_PAYLOAD_SIZE);
    bt_ble_rcsp_adv_enable();
    ble_module_enable(1);

    ble_init_flag = 1;
//添加该部分,直接切换一次
#if SMBOX_MULTI_BLE_EN
        app_change_key_is_press = 1;           //处于切换app的模式
        if (select_app1_or_app2) {
            ble_profile_again_init(0);//关闭app1的adv
            ble_app_disconnect();//断开app1的连接
        } else {
            smbox_multi_ble_module_enable(0);//断开app2的连接、关闭广播
        }

        select_app1_or_app2 = !select_app1_or_app2;
        log_info("SELECT_APP1_OR_APP2: %d\n", select_app1_or_app2);

        /* //重新注册函数 */
        //!!!!!!!需要注意是adv状态还是连接状态--只要是连接切换就需要在断链之后才会切换
        if (!smartbox_get_con_handle() && !mul_dev_get_conn_handle(0, SMBOX_MULTI_ROLE_SERVER) && !mul_dev_get_conn_handle(0, SMBOX_MULTI_ROLE_CLIENT)) {
            log_info("To_switch_app:%d\n", select_app1_or_app2);
            if (select_app1_or_app2) {
                ble_profile_again_init(1);
            } else {
                smbox_multi_ble_profile_init(1);
            }
        }
#endif
}

二、主模式扫描

  1. 匹配连接模式 目前sdk支持3中匹配方式,分别是指定地址创建连接、指定设备名称创建连接、匹配厂家标识创建连接。
    image

1.指定设备名称创建连接

//指定索索名字
static const u8 test_remoter_name1[] = "AC701N_watch_hzh";//

static const client_match_cfg_t match_dev01 = {
    .create_conn_mode = BIT(CLI_CREAT_BY_NAME),
    .compare_data_len = sizeof(test_remoter_name1) - 1 , //去结束符
    .compare_data = test_remoter_name1,
    .bonding_flag = 0,
};

2.指定地址创建连接

static const u8 mac1[6]={0x3A ,0x16,0x35 ,0x34,0x37, 0xD2};
static const client_match_cfg_t match_dev01 = {
    .create_conn_mode = BIT(CLI_CREAT_BY_ADDRESS),
    .compare_data_len = sizeof(mac1) , //去结束符
    .compare_data = mac1,
    .bonding_flag = 0,
};

3.匹配厂家标识创建连接

  1. 创建连接 除了采用1中的匹配的方式创建连接,也可以自定义发起连接。采用client_create_connect_api这个接口。mac地址和mac地址类型科才看2.3部分的广播数据部分中可以获取到。
/* --------------------------------------------------------------------------*/
/**
 * @brief 创建ble连接
 *
 * @param addr ble的mac地址
 * @param addr_type ble的mac地址类型,0--public address,1--random address
 * @param mode 连接方式
 *  1是采用有配对记录的发起最近1次连接的ble设备的连接,
 *  0是根据传入的地址连接
 *
 * @return 
 */
/* ----------------------------------------------------------------------------*/
static int client_create_connect_api(u8 *addr, u8 addr_type, u8 mode)
  1. 扫描到的广播数据 可以在GAP_EVENT_ADVERTISING_REPORT事件中,收到扫描到的广播回调。主要处理函数在client_report_adv_data中。如果还想看到更详细的分析,比如想看到广播的出来的ble的设备类型,16位的uuid之类的广播解析,可在resolve_adv_report这个函数中获取到更加详细的信息。
    imageimage

三、主模式通讯

  1. 主机扫描从机服务 连上ble后,如果是初次连接,就必须先扫描从机的profile,找到双发定义好通讯的handle。扫描的接口为client_profile_start。搜索到的回调是user_client_report_search_result函数,在该函数中可以看到从机profile中定义的各项服务,特征,以及其对应的handle。
static void client_profile_start(u16 conn_handle)
{
    set_ble_work_state(cur_dev_cid, BLE_ST_CONNECT);

    if (0 == client_config->security_en) {
        client_search_profile_start(conn_handle);
    }
}
//协议栈内部调用
void user_client_report_search_result(search_result_t *result_info)
{
    if (result_info == (void *) - 1) {
        log_info("client_report_search_result finish!!!\n");
        do_operate_search_handle();
        client_event_report(CLI_EVENT_SEARCH_PROFILE_COMPLETE, &client_con_handle[cur_dev_cid], 2);

        //搜索完profile,尝试开新设备scan
        /* ble_enable_new_dev_scan(); --碰一碰功能不需要继续打开*/
        return;
    }

    log_info("\n*** services, uuid16:%04x,index=%d ***\n", result_info->services.uuid16, result_info->service_index);
    log_info("{charactc, uuid16:%04x,index=%d,handle:%04x~%04x,value_handle=%04x}\n",
             result_info->characteristic.uuid16, result_info->characteristic_index,
             result_info->characteristic.start_handle, result_info->characteristic.end_handle,
             result_info->characteristic.value_handle
            );

    if (!result_info->services.uuid16) {
        log_info("######services_uuid128:");
        log_info_hexdump(result_info->services.uuid128, 16);
    }

    if (!result_info->characteristic.uuid16) {
        log_info("######charact_uuid128:");
        log_info_hexdump(result_info->characteristic.uuid128, 16);
    }

    check_target_uuid_match(result_info);
}
  1. 主机对从机的profile的handle的处理 搜索完成后,需要对handle进行对应的处理。比如需要对具有notify和indicate属性的character进行使能操作,从机发消息过来,主机才能收到。
//操作handle,完成 write ccc
static void do_operate_search_handle(void)
{
    u16 tmp_16;
    u16 i, cur_opt_type;
    opt_handle_t *opt_hdl_pt;

    log_info("opt_handle_used_cnt= %d\n", opt_handle_used_cnt);

    log_info("find target_handle:");
    log_info_hexdump(&target_handle[cur_dev_cid], sizeof(target_hdl_t));

    if (0 == opt_handle_used_cnt) {
        goto opt_end;
    }

    /* test_send_conn_update();//for test */

    for (i = 0; i < opt_handle_used_cnt; i++) {
        opt_hdl_pt = &opt_handle_table[i];
        log_info("do opt:service_uuid16:%04x,charactc_uuid16:%04x\n", opt_hdl_pt->search_uuid->services_uuid16, opt_hdl_pt->search_uuid->characteristic_uuid16);
        cur_opt_type = opt_hdl_pt->search_uuid->opt_type;
        switch ((u8)cur_opt_type) {
        case ATT_PROPERTY_READ:
            if (opt_hdl_pt->search_uuid->read_long_enable) {
                tmp_16  = 0x55A2;//fixed
                log_info("read_long:%04x\n", opt_hdl_pt->value_handle);
                client_operation_send(opt_hdl_pt->value_handle, (u8 *)&tmp_16, 2, ATT_OP_READ_LONG);
            } else {
                tmp_16  = 0x55A1;//fixed
                log_info("read:%04x\n", opt_hdl_pt->value_handle);
                client_operation_send(opt_hdl_pt->value_handle, (u8 *)&tmp_16, 2, ATT_OP_READ);
            }
            break;

        case ATT_PROPERTY_NOTIFY:
            tmp_16  = 0x01;//fixed
            log_info("write_ntf_ccc:%04x\n", opt_hdl_pt->value_handle);
            client_operation_send(opt_hdl_pt->value_handle + 1, &tmp_16, 2, ATT_OP_WRITE);
            if (opt_hdl_pt->search_uuid->read_report_reference) {
                tmp_16  = 0x55A1;//fixed
                log_info("read_report_reference:%04x\n", opt_hdl_pt->value_handle + 2);
                client_operation_send(opt_hdl_pt->value_handle + 2, (u8 *)&tmp_16, 2, ATT_OP_READ);
            }
            break;

        case ATT_PROPERTY_INDICATE:
            tmp_16  = 0x02;//fixed
            log_info("write_ind_ccc:%04x\n", opt_hdl_pt->value_handle);
            client_operation_send(opt_hdl_pt->value_handle + 1, &tmp_16, 2, ATT_OP_WRITE);
            break;

        default:
            break;
        }
    }

opt_end:
if(BLE_ST_SCAN != get_ble_work_state(cur_dev_cid))
{
    set_ble_work_state(cur_dev_cid, BLE_ST_SEARCH_COMPLETE);
}
   

}
  1. 主机与从机的通讯

1.主机对从机读写

主机可以通过client_operation_send接口想从机进行读写操作。接口介绍以及例子如下:  
/* --------------------------------------------------------------------------*/
/**
 * @brief 主机操作接口
 *
 * @param handle 对应的Characteristic的handle值
 * @param data 数据指针
 * @param len 数据长度
 * @param att_op_type handle的类型
 *  ATT_OP_READ 单次读
 *  ATT_OP_READ_LONG 多次读
 *  ATT_OP_WRITE 写发送(需要对方回答应包)
 *   ATT_OP_WRITE_WITHOUT_RESPOND 写发送(不需要对方回答应包)
 * @return 
 *    0:发送成功
 *    3:ble未连接或传入的handle有误
 *    1:发送buffer满了
 */
/* ----------------------------------------------------------------------------*/
static int client_operation_send(u16 handle, u8 *data, u16 len, u8 att_op_type)
{
    int ret = APP_BLE_NO_ERROR;
    if (!client_con_handle[cur_dev_cid]) {
        return APP_BLE_OPERATION_ERROR;
    }

    if (!handle) {
        log_info("handle is null\n");
        return APP_BLE_OPERATION_ERROR;
    }

    if (get_buffer_vaild_len(0) < len) {
        log_info("opt_buff_full!!!\n");
        return APP_BLE_BUFF_FULL;
    }

    ret = ble_op_multi_att_send_data(client_con_handle[cur_dev_cid], handle, data, len, att_op_type);
    if (ret == BLE_BUFFER_FULL) {
        ret = APP_BLE_BUFF_FULL;
    }
    return ret;
}
static int client_write_send(void *priv, u8 *data, u16 len)
{
    return client_operation_send(target_handle[cur_dev_cid].write_handle, data, len, ATT_OP_WRITE);
}
static int client_write_without_respond_send(void *priv, u8 *data, u16 len)
{
    return client_operation_send(target_handle[cur_dev_cid].write_no_respond, data, len, ATT_OP_WRITE_WITHOUT_RESPOND);
}
static int client_read_value_send(void *priv)
{
    u16 tmp_flag = 0x55A1;
    return client_operation_send(target_handle[cur_dev_cid].read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ);
}
static int client_read_long_value_send(void *priv)
{
    u16 tmp_flag = 0x55A2;
    return client_operation_send(target_handle[cur_dev_cid].read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ_LONG);
}

2.从机向主机发数据