SPP
SPP: Serial Port Profile(串口协议,允许设备之间通过模拟串口的方式进行无线数据传输。基于 RFCOMM 通信层,SPP 协议与传统的 RS-232 串口标准类似,因此非常适合低速、短距离的数据传输,如 Android 设备和传感器、微控制器之间的通信。SPP协议规定两种角色:
Device A (DevA) – This is the device that takes initiative to form a connection to another device.(发起连接的设备)
使用SDP发起请求,查询DevB的RFCOMM channel通道
能与对端设备进行安全认证
能通过查询到的RFCOMM channel与对端设备建立L2CAP(RFCOMM) RFCOMM(DLC)通道
收发数据
断开连接
Device B (DevB) – This is the device that waits for another device to take initiative to connect. (等待连接的设备)
注册SPP相关的UUID到SDP database里,DevA能够通过SDP查询到
能与对端设备进行安全认证
接收对端设备的连接请求
收发数据
断开连接
本文档主要是基于Sifli SDK,介绍对SPP的DevB基本功能支持。涉及文件如下:
bts2_app_interface
bts2_app_spp_s
SPP初始化
SPP初始化的函数:bt_spp_srv_init,SPP相关的状态、标志赋初始值,以及SPP uuid注册
//step1: 用户可以通过重写bt_spp_srv_add_uuid_list函数将自定义的SPP UUID注册到BR/EDR 的SDP database中
//step2:启用SPP profile时,SPP会将数据注册到SDP中
//注意: 目前最多支持自定义UUID数量为7个
void bt_spp_srv_add_uuid_list(void)
{
U8 uuid_128[] =
{
0x30, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
U8 uuid_32[] =
{
0x30, 0x01, 0x02, 0x03
};
U8 uuid_16[] =
{
0x30, 0x01
};
spp_add_uuid_list_node(uuid_128, sizeof(uuid_128), "aaaa");
spp_add_uuid_list_node(uuid_32, sizeof(uuid_32), "bbbb");
spp_add_uuid_list_node(uuid_16, sizeof(uuid_16), "cccc");
}
void bt_spp_srv_init(bts2_app_stru *bts2_app_data)
{
U8 i = 0;
bts2_app_data->select_device_id = 0;
bts2_app_data->select_srv_chnl = 0;
bts2_app_data->spp_srv_conn_nums = 0;
bts2_app_data->spp_srv_inst_ptr = &bts2_app_data->spp_srv_inst[0];
for (i = 0; i < CFG_MAX_ACL_CONN_NUM; i++)
{
bts2_app_data->spp_srv_inst[i].device_id = 0xff;
bd_set_empty(&bts2_app_data->spp_srv_inst[i].bd_addr);
bts2_app_data->spp_srv_inst[i].cur_link_mode = 0;
bts2_app_data->spp_srv_inst[i].exit_sniff_pending = FALSE;
bts2_app_data->spp_srv_inst[i].cod = 0;
bts2_app_data->spp_srv_inst[i].service_list = 0;
bts2_app_data->spp_srv_inst[i].spp_service_list = NULL;
}
bt_spp_srv_add_uuid_list();
}
void bt_spp_srv_start_enb(bts2_app_stru *bts2_app_data)
{
U8 i;
for (i = 0; i < SPP_SRV_MAX_CONN_NUM; i++)
{
spp_srv_enb_req(i, SPP_DEFAULT_NAME);
}
USER_TRACE(">> SPP enabled\n");
}
SPP数据收发功能
SPP作为DevB功能,只能接收对方发起的连接请求。
SPP断开设备接口为:
bts2_app_interface断开连接接口:bt_interface_dis_spp_by_addr_and_chl
bts2_app_spp_s断开接口:bt_spp_srv_disc_req
SPP连接状态回调event:
SPP连接成功:BT_NOTIFY_SPP_PROFILE_CONNECTED
SPP连接失败:BT_NOTIFY_SPP_PROFILE_DISCONNECTED
SPP收到连接事件:BT_NOTIFY_SPP_CONN_IND
SPP收到连接断开事件:BT_NOTIFY_SPP_DISC_IND
SPP数据收发相关的接口和事件(event):
数据发送:
bts2_app_interface数据发送接口:bt_interface_spp_send_data
bts2_app_spp_s数据发送接口:bt_spp_srv_sending_data_by_device_id_and_srv_chnl
数据发送成功event:BT_NOTIFY_SPP_DATA_CFM
数据接收:
bts2_app_interface数据接收成功回复接口:bt_interface_spp_srv_data_rsp
bts2_app_spp_s数据接收成功回复接口:spp_srv_data_rsp_ext
收到数据EVENT: BT_NOTIFY_SPP_DATA_IND
// step1: 接收对方设备连接时,如果只连接单个channel时,可以处理:BT_NOTIFY_SPP_PROFILE_CONNECTED 和 BT_NOTIFY_SPP_PROFILE_DISCONNECTED
// step2:接收对方多个channel连接时,需要处理的连接相关的event:BT_NOTIFY_SPP_CONN_IND 和 BT_NOTIFY_SPP_DISC_IND
//step1 和 step2 中两者的区别在于:step1 中的event只有对端设备的addr 和连接原因,step2中的event信息包含连接的UUID 和 service channel
// step3: 收到数据之后,需要回复bt_interface_spp_srv_data_rsp
// step4: 发送数据调用接口:bt_interface_spp_send_data
// step5: 数据发送成功之后会收到event:BT_NOTIFY_SPP_DATA_CFM
int bt_sifli_notify_spp_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
/*
typedef struct
{
bt_notify_device_mac_t mac; /// the bt device mac
uint8_t profile_type; /// BT_NOTIFY type id
uint8_t res; /// error code 为自定义类型
} bt_notify_profile_state_info_t;
*/
case BT_NOTIFY_SPP_PROFILE_CONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
break;
}
/*
typedef struct
{
bt_notify_device_mac_t mac; /// the bt device mac
uint8_t profile_type; /// BT_NOTIFY type id
uint8_t res; /// error code 为自定义类型
} bt_notify_profile_state_info_t;
*/
case BT_NOTIFY_SPP_PROFILE_DISCONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
break;
}
/*
/// spp connection information
typedef struct
{
/// the address of the connected device
bt_notify_device_mac_t mac;
/// the service channel of the connected device
uint8_t srv_chl;
/// uuid value
SPP_UUID uuid;
/// uuid length
uint8_t uuid_len;
/// the mtu size of the connected device
uint16_t mfs;
} bt_notify_spp_conn_ind_t;
*/
case BT_NOTIFY_SPP_CONN_IND:
{
bt_notify_spp_conn_ind_t *conn_ind = (bt_notify_spp_conn_ind_t *)data;
break;
}
// 收到的每个包都需要回复:bt_interface_spp_srv_data_rsp
// 回复之后才能收到下一包数据,以及对方设备更新credit发数据。
// 回复速度可以决定对方设备发包的速率
case BT_NOTIFY_SPP_DATA_IND:
{
bt_notify_spp_data_ind_t *data_info = (bt_notify_spp_data_ind_t *)data;
BTS2S_BD_ADDR bd_addr;
bt_addr_convert_to_bts((bd_addr_t *)data_info->mac.addr, &bd_addr);
bt_interface_spp_srv_data_rsp(&bd_addr, data_info->srv_chl);
break;
}
// bt_interface_spp_send_data(data, len, bd_addr, srv_chl);
// send data cfm event
case BT_NOTIFY_SPP_DATA_CFM:
{
bt_notify_spp_data_cfm_t *data_cfm = (bt_notify_spp_data_cfm_t *)data;
// Send another packet of data (bt_interface_spp_send_data)
// 发送数据时,除了数据本身之外,还需要带地址以及对应的srv_chl通道,这些信息包含在event:BT_NOTIFY_SPP_CONN_IND中
// 地址需要转换,eg:bt_addr_convert_to_bts((bd_addr_t *)data_info->mac.addr, &bd_addr);
// bt_interface_spp_send_data(data, len, bd_addr, srv_chl);
break;
}
/*
/// spp disconnection information
typedef struct
{
/// the address of the disconnected device
bt_notify_device_mac_t mac;
/// the service channel of the disconnected device
uint8_t srv_chl;
/// the uuid of the disconnected device
SPP_UUID uuid;
/// the uuid length of spp connection that sent the data
uint8_t uuid_len;
} bt_notify_spp_disc_ind_t;
*/
case BT_NOTIFY_SPP_DISC_IND:
{
bt_notify_spp_disc_ind_t *disc_ind = (bt_notify_spp_disc_ind_t *)data;
break;
}
default:
return -1;
}
return 0;
}
static int bt_sifli_notify_common_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
//bt功能开启成功,可以正常使用
case BT_NOTIFY_COMMON_BT_STACK_READY:
{
break;
}
//bt关闭成功
case BT_NOTIFY_COMMON_CLOSE_COMPLETE:
{
break;
}
// ACL 连接成功
case BT_NOTIFY_COMMON_ACL_CONNECTED:
{
bt_notify_device_acl_conn_info_t *acl_info = (bt_notify_device_acl_conn_info_t *) data;
//device acl connected
break;
}
// ACL 断开连接成功
case BT_NOTIFY_COMMON_ACL_DISCONNECTED:
{
bt_notify_device_base_info_t *device_info = (bt_notify_device_base_info_t *)data;
//device acl disconnected
break;
}
default:
return -1;
}
return 0;
}
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
int ret = -1;
switch (type)
{
case BT_NOTIFY_COMMON:
{
ret = bt_sifli_notify_common_event_hdl(event_id, data, data_len);
}
break;
case BT_NOTIFY_SPP:
{
bt_sifli_notify_spp_event_hdl(event_id, data, data_len);
}
break;
default:
break;
}
return 0;
}
int app_bt_notify_init(void)
{
bt_interface_register_bt_event_notify_callback(bt_notify_handle);
return 0;
}
INIT_ENV_EXPORT(app_bt_notify_init);
//register notify event handle function end