BlueZ Gatt使用中的一些问题记录

Bluez GATT使用注意

发送线程同步问题

原版Bluez中,GATT消息的发送并没有任何的同步机制。多条消息发送时的同步是依赖于上层dbus的消息机制完成的。所以当直接调用bluez底层api时,会有死锁的风险。

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
static void *bluetooth_message_send_thread(void *args)
{
bluetooth_message_s message = {0};

memcpy(&message, (bluetooth_message_s *)args, sizeof(bluetooth_message_s));

if (_wifi_data.server != NULL)
bt_gatt_server_send_notification(_wifi_data.server->gatt, _wifi_data.server->hr_msrmt_handle, (uint8_t *)message.messageData, (uint16_t)message.messageLen);

sleep(2);
}

int32_t hlgatt_server_notify_write(uint8_t *notifyInfo, size_t notifyLen)
{
int ret = -1;
bluetooth_message_s message = {0};

message.messageLen = notifyLen;
if ((message.messageLen > 0) && (message.messageLen <= sizeof(message.messageData)))
{
memmove(message.messageData, notifyInfo, message.messageLen);
ret = threadpool_add_job(bluetooth_message_send_thread, (void *)&message, "bluetooth_message_send_thread"); // 受谭工驱动封装原因,此处使用线程发送,避免阻塞
sleep(1);// 等待线程拷贝完成数据
}

return ret;
}

大概现象是当遇到较长的协议时,比如app查询wifi信息,期间如果触发其他消息或者触发notify主动上报,会导致蓝牙停止工作,不响应任何消息,60s超时后断开退出。重新初始化后可以恢复。

为了避免这个问题,后续的gatt消息发送改为了阻塞式,不考虑线程调度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

int32_t hlgatt_server_send(uint8_t *value, size_t length, bool indicate)
{
if (_wifi_data.server == NULL || value == NULL)
return -1;

if (indicate) {
if (!bt_gatt_server_send_indication(_wifi_data.server->gatt, _wifi_data.server->hr_msrmt_handle, value, length, send_cb, NULL, NULL))
return -1;
} else {
if (!bt_gatt_server_send_notification(_wifi_data.server->gatt, _wifi_data.server->hr_msrmt_handle, value, length, false))
return -1;
}

return 0;
}

两种方式底层调用的都是bt_att_send方法。

发送数据量过大问题

发送数据量过大时会导致传输较慢,或者MTU太小传输超时等等问题。需要控制消息内容的大小。

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* @brief 获取WIFI扫描结果
* @param results: 结果获取数组
* @param results: 结果获取数组
* @return EXIT_SUCCESS or EXIT_FAILURE
* @author Antong
* @date 2022.11.15
*/
int get_wifi_scan_results(wifiInfoList_s results[], int maxNum)
{
uint8_t i, j;
char *ssid = NULL;
char *rssi = NULL;
uint8_t count = 0;
uint8_t tryCnt = 0;
int resultSize = 0;
char readBuff[256] = {0};
wifiInfoList_s temp = {0};
bt_binding_params_s *ptx = get_bt_binidng_params_ctx();

while (1)
{
if (access(AP_SCAN_RESULT_PATH, F_OK) == 0)
{
break;
}

// 如果没有开启wifi扫描, 那么打开wifi扫描
if (ptx->wifiScan_status == false)
{
ptx->wifiScan_status = true;
threadpool_add_job(scan_wifi_in_environment, NULL, "scan_wifi_in_environment");
}

// 重试五次, 还不行就退出
if (tryCnt++ > 5)
{
return 0;
}

HL_APP_LOG_ERR(BLE_MODULE, "file:[%s] does not exist!", AP_SCAN_RESULT_PATH);
sleep(1);
}

FILE *fp = fopen(AP_SCAN_RESULT_PATH, "r");
if (fp == NULL)
{
HL_APP_LOG_ERR(BLE_MODULE, "can't open file:[%s]", AP_SCAN_RESULT_PATH);
return -1;
}

// 循环读取结果文件,写入结构体
while (fgets(readBuff, sizeof(readBuff), fp) != NULL)
{
ssid = strtok(readBuff, "\t");
strtok(NULL, "\t");
rssi = strtok(NULL, "\t");
strtok(NULL, "\t");
if (rssi != NULL)
{
ssid = strtok(NULL, "\t");
ssid[strcspn(ssid, "\r\n")] = '\0';

// 剔除 空SSID和含有中文SSID
if ((strlen(ssid) == 0) || strlen(ssid) > SSID_LEN || (strchr(ssid, '\\') != NULL))
continue;

// 检查是否已经存在相同的SSID
int isDuplicate = 0;
for (i = 0; i < resultSize; i++)
{
if (strcmp(results[i].ssid, ssid) == 0)
{
isDuplicate = 1;
break;
}
}

// 如果不是重复的SSID,则添加到结果数组
if (!isDuplicate)
{
results[resultSize].rssi = atoi(rssi);
strncpy(results[resultSize].ssid, ssid, sizeof(results[resultSize].ssid));

if (resultSize++ >= (maxNum - 1)) // maxNum是个数, resultSize从0开始, (maxNum - 1)消除差异
{
break;
}
}
}
}

// 进行排序,信号最强的在最前边
for (i = 0; i < (resultSize - 1); i++)
{
count = 0;

for (j = 0; j < (resultSize - 1 - i); j++)
{
if (results[j].rssi < results[j + 1].rssi)
{
temp = results[j];
results[j] = results[j + 1];
results[j + 1] = temp;
count = 1;
}
}

if (count == 0)
break;
}

fclose(fp);
return resultSize;
}

在扫描wifi列表时做了去重操作,避免相同ssid不同频段的wifi重复记录。能有效减小这条协议的长度。