自定义BLE数据发送函数,就是将数据发送、数据发送前的检查、以及conn_handle查询等封装在一起,脱离SDK中的相关回调函数,在程序任意位置实现发送数据功能。
BLE的数据发送函数定义在apps\common\third_party_profile\jieli\gatt_common\le_gatt_common.c文件里。
int ble_comm_att_send_data(u16 conn_handle, u16 att_handle, u8 *data, u16 len, att_op_type_e op_type)
{
gatt_op_ret_e ret = GATT_OP_RET_SUCESS;
u16 tmp_16;
if (!conn_handle) {
return GATT_CMD_PARAM_ERROR;
}
switch (op_type) {
case ATT_OP_READ:
tmp_16 = 0x55A1;//fixed
ret = ble_op_multi_att_send_data(conn_handle, att_handle, &tmp_16, 2, op_type);
break;
case ATT_OP_READ_LONG:
tmp_16 = 0x55A2;//fixed
ret = ble_op_multi_att_send_data(conn_handle, att_handle, &tmp_16, 2, op_type);
break;
default:
ret = ble_op_multi_att_send_data(conn_handle, att_handle, data, len, op_type);
break;
}
if (ret) {
const char *err_string;
int error_id = (int)0 - (int)GATT_CMD_RET_BUSY + (int)ret;
if (error_id >= 0 && error_id < sizeof(gatt_op_error_string) / sizeof(char *)) {
err_string = gatt_op_error_string[error_id];
} else {
err_string = "UNKNOW GATT_ERROR";
}
log_error("att_send_fail: %d!!!,%s", ret, err_string);
log_error("param:%04x, %04x, %02x,len= %d", conn_handle, att_handle, op_type, len);
/* put_buf(data,len); */
}
return ret;
}
该数据发送函数出现在apps\spp_and_le\examples\trans_data\ble_trans.c文件下的write_callback 回调函数里。写入回调函数如下,注意,无关代码已经省略。
在手机或其他设备(Client角色)发送数据到AC63时,会进入注册过的回调函数trans_att_write_callback( );SDK例程中在该回调函数里执行了自动收发测试代码。
static int trans_att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size)
{
int result = 0;
u16 tmp16;
u16 handle = att_handle;
switch (handle) {
//...此处省略1万字
case ATT_CHARACTERISTIC_ae01_01_VALUE_HANDLE:
log_info("\n-ae01_rx(%d):", buffer_size);
put_buf(buffer, buffer_size);
//收发测试,自动发送收到的数据;for test
if (ble_comm_att_check_send(connection_handle, buffer_size) &&
ble_gatt_server_characteristic_ccc_get(trans_con_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE)) {
log_info("-loop send1\n");
ble_comm_att_send_data(connection_handle, ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, buffer, buffer_size, ATT_OP_AUTO_READ_CCC);
}
break;
//...此处省略1万字
default:
break;
}
return 0;
}
自动收发测试代码里使用ble_comm_att_check_send( )函数检查发送缓存够不够用,同时使用ble_gatt_server_characteristic_ccc_get( )函数检查特征的NOTIFY属性是否得到了Client端的许可。如果任意一个条件不满足,均不会发送数据。
为了避免内存溢出,导致不可预见的异常发生,必须检查数组长度是否超出了可用缓存大小。
/********************************************************************/
/*!
* \brief 检查预备发送的数据包长度是否能填入
/*******************************************************/
bool ble_comm_att_check_send(u16 conn_handle, u16 pre_send_len)
{
if (ble_comm_cbuffer_vaild_len(conn_handle) >= pre_send_len) {
return true;
} else {
return false;
}
}
/******************************************************************/
/*!
* \brief 获取配置的cbuffer 可以填入数据长度
*/
/**************************************************************/
u32 ble_comm_cbuffer_vaild_len(u16 conn_handle)
{
u32 vaild_len = 0;
ble_op_multi_att_get_remain(conn_handle, &vaild_len);
return vaild_len;
}
NOTIFY属性是需要在建立连接后,由手机端写入1许可通知的。该函数用于检查是否得到Client许可。
/*************************************************************************************************/
/*!
* \brief 获取 notify or indicate 通知使能值
*
* \param [in]
*
* \return return
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION
*
* \note
*/
/*************************************************************************************************/
u16 ble_gatt_server_characteristic_ccc_get(u16 conn_handle, u16 att_ccc_handle)
{
return multi_att_get_ccc_config(conn_handle, att_ccc_handle);
}
该函数返回值为连接的句柄。如果当前没有连接,则返回0。index是句柄在数组里的序号,默认从0开始。如果只有一个连接,则index为0。role是连接状态下的角色,分为server和client两种。
/*************************************************************************************************/
/*!
- \brief 获取index对应的连接 handle
*/
/*************************************************************************************************/
u16 ble_comm_dev_get_handle(u8 index, u8 role)
{
u16 *group_handle;
u8 count;
if (GATT_ROLE_SERVER == role) {
group_handle = gatt_server_conn_handle;
count = SUPPORT_MAX_GATT_SERVER;
} else {
group_handle = gatt_client_conn_handle;
count = SUPPORT_MAX_GATT_CLIENT;
}
if (index < count) {
return group_handle[index];
} else {
return 0;
}
}
/*****************************************************************************/
/*!
- \brief user defined ble data send fun
- u8* sdata point of data to be send
- u8 len the size of data to be send
*/
/***************************************************************************/
void my_ble_send_data(u8* sdata,u8 len)
{
//先查明当前连接的conn_handle
u16 connection_handle = ble_comm_dev_get_handle(0, GATT_ROLE_SERVER);
printf("connection_handle: %04x\n",connection_handle);
if(connection_handle==0)//无连接
{
return;
}
//检查预备发送的数据包长度是否能填入,并且获取 notify or indicate 通知使能值
if (ble_comm_att_check_send(connection_handle, len) &&
ble_gatt_server_characteristic_ccc_get(connection_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE))
{
printf("permit send...\n");
//使用notify方式发送
ble_comm_att_send_data(connection_handle, ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, sdata, len, ATT_OP_NOTIFY);
}
}
自定义BLE数据发送函数是将原来SDK中分散的几个函数集合到一起,完成独立的数据发送功能。其中 ble_comm_att_send_data( )函数是最主要的,其他几个是辅助该函数完成任务的。
BLE发送函数里,有个需要注意的地方是ATT的handle,这个值是由BLEprofile里的服务特征表里的数值决定的,如果该值不一致,数据发送会失败。
BLE的profile文件在:apps\spp_and_le\examples\trans_data\ble_trans_profile.h。服务特征表如下:
static const uint8_t trans_profile_data[] = {
//
//
// 0x0001 PRIMARY_SERVICE 1800
//
//
0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18,
/* CHARACTERISTIC, 2a00, READ | WRITE | DYNAMIC, */
// 0x0002 CHARACTERISTIC 2a00 READ | WRITE | DYNAMIC
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x0a, 0x03, 0x00, 0x00, 0x2a,
// 0x0003 VALUE 2a00 READ | WRITE | DYNAMIC
0x08, 0x00, 0x0a, 0x01, 0x03, 0x00, 0x00, 0x2a,
//
//
// 0x0004 PRIMARY_SERVICE 1801
//
//
0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x01, 0x18,
/* CHARACTERISTIC, 2a05, INDICATE, */
// 0x0005 CHARACTERISTIC 2a05 INDICATE
0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x20, 0x06, 0x00, 0x05, 0x2a,
// 0x0006 VALUE 2a05 INDICATE
0x08, 0x00, 0x20, 0x00, 0x06, 0x00, 0x05, 0x2a,
// 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION
0x0a, 0x00, 0x0a, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00,
//
//
// 0x0008 PRIMARY_SERVICE ae30
//
//
0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x30, 0xae,
/* CHARACTERISTIC, ae01, WRITE_WITHOUT_RESPONSE | DYNAMIC, */
// 0x0009 CHARACTERISTIC ae01 WRITE_WITHOUT_RESPONSE | DYNAMIC
0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x04, 0x0a, 0x00, 0x31, 0xae,
// 0x000a VALUE ae01 WRITE_WITHOUT_RESPONSE | DYNAMIC
0x08, 0x00, 0x04, 0x01, 0x0a, 0x00, 0x31, 0xae,
/* CHARACTERISTIC, ae02, NOTIFY, */
// 0x000b CHARACTERISTIC ae02 NOTIFY
0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x10, 0x0c, 0x00, 0x32, 0xae,
// 0x000c VALUE ae02 NOTIFY
0x08, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x32, 0xae,
// 0x000d CLIENT_CHARACTERISTIC_CONFIGURATION
0x0a, 0x00, 0x0a, 0x01, 0x0d, 0x00, 0x02, 0x29, 0x00, 0x00,
//...此处省略1万字
// END
0x00, 0x00,
};