打开官方例程

打开工程下的ble_simple_peripheral例程,路径如下:fr8000\examples\none_evm\ble_simple_peripheral\keil,官方例程下载请参考富芮坤进行OTA升级进行下载

使能notify功能

打开simple_gatt_service.c文件,找到simple_profile_att_table函数,使能相关服务, 在调用服务时要注意相应序号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const gatt_attribute_t simple_profile_att_table[SP_IDX_NB] =
{
// Simple gatt Service Declaration
/*[SP_IDX_SERVICE]*/[0] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_PRIMARY_SERVICE_UUID) }, /* UUID */
GATT_PROP_READ, /* Permissions */
UUID_SIZE_2, /* Max size of the value */ /* Service UUID size in service declaration */
(uint8_t*)sp_svc_uuid, /* Value of the attribute */ /* Service UUID value in service declaration */
},

// Characteristic 1 Declaration
/*[SP_IDX_CHAR1_DECLARATION]*/[1] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_CHARACTER_UUID) }, /* UUID */
GATT_PROP_READ, /* Permissions */
0, /* Max size of the value */
NULL, /* Value of the attribute */
},
// Characteristic 1 Value
/*[SP_IDX_CHAR1_VALUE]*/[2] = {
{ UUID_SIZE_16, SP_CHAR1_TX_UUID }, /* UUID */
GATT_PROP_READ | GATT_PROP_NOTI, /* Permissions */
SP_CHAR1_VALUE_LEN, /* Max size of the value */
NULL, /* Value of the attribute */ /* Can assign a buffer here, or can be assigned in the application by user */
},

// Characteristic 4 client characteristic configuration
/*[SP_IDX_CHAR1_CFG]*/[3] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_CLIENT_CHAR_CFG_UUID) }, /* UUID */
GATT_PROP_READ | GATT_PROP_WRITE, /* Permissions */
2, /* Max size of the value */
NULL, /* Value of the attribute */ /* Can assign a buffer here, or can be assigned in the application by user */
},

// Characteristic 1 User Description
/*[SP_IDX_CHAR1_USER_DESCRIPTION]*/[4] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_CHAR_USER_DESC_UUID) }, /* UUID */
GATT_PROP_READ, /* Permissions */
SP_CHAR1_DESC_LEN, /* Max size of the value */
(uint8_t *)sp_char1_desc, /* Value of the attribute */
},


// Characteristic 2 Declaration
/*[SP_IDX_CHAR2_DECLARATION]*/[5] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_CHARACTER_UUID) }, /* UUID */
GATT_PROP_READ, /* Permissions */
0, /* Max size of the value */
NULL, /* Value of the attribute */
},
// Characteristic 2 Value
/*[SP_IDX_CHAR2_VALUE]*/[6] = {
{ UUID_SIZE_16, SP_CHAR2_RX_UUID }, /* UUID */
GATT_PROP_READ | GATT_PROP_WRITE, /* Permissions */
SP_CHAR2_VALUE_LEN, /* Max size of the value */
NULL, /* Value of the attribute */ /* Can assign a buffer here, or can be assigned in the application by user */
},
// Characteristic 2 User Description
/*[SP_IDX_CHAR2_USER_DESCRIPTION]*/[7] = {
{ UUID_SIZE_2, UUID16_ARR(GATT_CHAR_USER_DESC_UUID) }, /* UUID */
GATT_PROP_READ, /* Permissions */
SP_CHAR2_DESC_LEN, /* Max size of the value */
(uint8_t *)sp_char2_desc, /* Value of the attribute */
},
};

打开ble_simple_peripheral.c文件,找到app_gap_evt_cb函数,在GAP_EVT_SLAVE_CONNECT事件下作出如下更改,使能notify:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
uint8_t sp_conidx; //蓝牙连接号condix
extern uint8_t sp_svc_id; //服务号
void app_gap_evt_cb(gap_event_t *p_event)
{
switch(p_event->type)
{
case GAP_EVT_ADV_END: //广播结束
{
LOG_INFO(app_tag, "adv_end,status:0x%02x\r\n",p_event->param.adv_end.status);
}
break;

case GAP_EVT_ALL_SVC_ADDED: //所有的service都添加完毕
{
LOG_INFO(app_tag, "All service added\r\n");
sp_start_adv();
}
break;

case GAP_EVT_SLAVE_CONNECT: //作为slave链接建立
{
sp_conidx = p_event->param.slave_connect.conidx;
co_printf("slave[%d],connect, link_num:%d\r\n",p_event->param.slave_connect.conidx,gap_get_connect_num());
gatt_mtu_exchange_req(p_event->param.slave_connect.conidx);
gap_conn_param_update(p_event->param.slave_connect.conidx,6,6,0,500);
LOG_INFO(app_tag, "slave[%d],connect. link_num:%d\r\n",p_event->param.slave_connect.conidx,gap_get_connect_num());
}
break;

case GAP_EVT_DISCONNECT: //链接断开 ,可能是master或slave
{
LOG_INFO(app_tag, "Link[%d] disconnect,reason:0x%02X\r\n",p_event->param.disconnect.conidx
,p_event->param.disconnect.reason);
gap_start_advertising(0);
}
break;

case GAP_EVT_LINK_PARAM_REJECT: //链接参数更新被拒绝
LOG_INFO(app_tag, "Link[%d]param reject,status:0x%02x\r\n"
,p_event->param.link_reject.conidx,p_event->param.link_reject.status);
break;

case GAP_EVT_LINK_PARAM_UPDATE: //链接参数更新成功
LOG_INFO(app_tag, "Link[%d]param update,interval:%d,latency:%d,timeout:%d\r\n",p_event->param.link_update.conidx
,p_event->param.link_update.con_interval,p_event->param.link_update.con_latency,p_event->param.link_update.sup_to);
break;

case GAP_EVT_PEER_FEATURE: //收到对端的feature特性回复
LOG_INFO(app_tag, "peer[%d] feats ind\r\n",p_event->param.peer_feature.conidx);
break;

case GAP_EVT_MTU:
LOG_INFO(app_tag, "mtu update,conidx=%d,mtu=%d\r\n"
,p_event->param.mtu.conidx,p_event->param.mtu.value);
break;

case GAP_EVT_LINK_RSSI:
LOG_INFO(app_tag, "link rssi %d\r\n",p_event->param.link_rssi);
break;

case GAP_SEC_EVT_SLAVE_ENCRYPT:
LOG_INFO(app_tag, "slave[%d]_encrypted\r\n",p_event->param.slave_encrypt_conidx);
break;

default:
break;
}
}

使用如下一段程序即可实现notify上报数据功能

1
2
3
4
5
6
7
8
9
gatt_ntf_t ntf_att;
ntf_att.att_idx = 2;
ntf_att.conidx = sp_conidx;
ntf_att.svc_id = sp_svc_id;
uint8_t show_string[30]={0};
sprintf((char *)show_string,"temperature: %f",(float)10);
ntf_att.p_data = (uint8_t *)show_string;
ntf_att.data_len = sizeof(show_string);
gatt_notification(ntf_att);

处理数据接收

打开simple_gatt_service.c文件,找到sp_gatt_msg_handler函数,在GATTC_MSG_READ_REQ与GATTC_MSG_WRITE_REQ事件下做出如下更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
static uint16_t sp_gatt_msg_handler(gatt_msg_t *p_msg)
{
switch(p_msg->msg_evt)
{
case GATTC_MSG_READ_REQ:
{
if(p_msg->att_idx == SP_IDX_CHAR1_VALUE)
{
memcpy(p_msg->param.msg.p_msg_data, "CHAR1_VALUE", strlen("CHAR1_VALUE"));
return strlen("CHAR1_VALUE");
}
else if(p_msg->att_idx == SP_IDX_CHAR2_VALUE)
{
memcpy(p_msg->param.msg.p_msg_data, "CHAR2_VALUE", strlen("CHAR2_VALUE"));
return strlen("CHAR2_VALUE");
}
else if(p_msg->att_idx == SP_IDX_CHAR4_CFG)
{
memcpy(p_msg->param.msg.p_msg_data, sp_char4_ccc, 2);
return 2;
}
else if(p_msg->att_idx == SP_IDX_CHAR5_VALUE)
{
memcpy(p_msg->param.msg.p_msg_data, "CHAR5_VALUE", strlen("CHAR5_VALUE"));
return strlen("CHAR5_VALUE");
}
}
break;

case GATTC_MSG_WRITE_REQ:
{
if (p_msg->att_idx == SP_IDX_CHAR1_VALUE)
{
co_printf("char1_recv:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
}
else if (p_msg->att_idx == SP_IDX_CHAR2_VALUE)
{
co_printf("char2_recv:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
if(p_msg->param.msg.p_msg_data[0] == 0x77 && p_msg->param.msg.p_msg_data[1]==0x77)
{
gatt_ntf_t ntf_att;
ntf_att.att_idx = 2;
ntf_att.conidx = sp_conidx;
ntf_att.svc_id = sp_svc_id;
uint8_t show_string[30]={0};
sprintf((char *)show_string,"tem: %d",1000);
co_printf("%s",show_string);
ntf_att.p_data = (uint8_t *)show_string;
ntf_att.data_len = sizeof(show_string);
gatt_notification(ntf_att);
}
}
else if (p_msg->att_idx == SP_IDX_CHAR3_VALUE)
{
co_printf("char3_recv:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
}
else if (p_msg->att_idx == SP_IDX_CHAR5_VALUE)
{
co_printf("char5_recv:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
}
else if (p_msg->att_idx == SP_IDX_CHAR1_CFG)
{
co_printf("char1_ntf_enable:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
if(p_msg->param.msg.p_msg_data[0] & 0x1)
ntf_char1_enable = 1;
if(ntf_char1_enable)
ntf_data(p_msg->conn_idx,SP_IDX_CHAR1_VALUE,(uint8_t *)"char1_ntf_data",strlen("char1_ntf_data"));
}
else if (p_msg->att_idx == SP_IDX_CHAR4_CFG)
{
co_printf("char4_ntf_enable:");
show_reg(p_msg->param.msg.p_msg_data,p_msg->param.msg.msg_len,1);
memcpy(sp_char4_ccc,p_msg->param.msg.p_msg_data,2);
}
}
break;
case GATTC_MSG_LINK_CREATE:
co_printf("link_created\r\n");
break;
case GATTC_MSG_LINK_LOST:
co_printf("link_lost\r\n");
ntf_char1_enable = 0;
break;
default:
break;
}
return p_msg->param.msg.msg_len;
}

蓝牙数据的发送与接收:
   在GATTC_MSG_READ_REQ事件中,可对发送数据进行处理;
   在GATTC_MSG_WRITE_REQ事件中,可对接收数据进行处理;
如上,即可完成蓝牙数据接收与发送功能的实现。