『uni-app, Mini Program』The Whole Process of Bluetooth Connection, Reading and Writing Data

  • Dev tool: HBuilder X 3.4.7.20220422
  • uni-app + Vue3
  • Running as an Android app (same idea for iOS and mini programs)

The idea

The logic of sending and receiving data over Bluetooth is a tiny bit different from the AJAX network requests we’re used to.
The biggest difference is this: receiving data over Bluetooth isn’t all that stable. Compared with network requests, Bluetooth drops packets far more easily.
In day-to-day development, whether an AJAX request succeeds or fails, the browser will basically always give you an answer. But judging by the APIs uni-app provides, receiving data over Bluetooth feels much more “asynchronous”.

The rough idea

The rough idea of using Bluetooth to transfer data is as follows:

  1. Initialize: turn on the Bluetooth module
  2. Scan: detect nearby devices
  3. Connect: find the target device and connect to it
  4. Listen: turn on the listening feature to receive data sent over by other devices
  5. Send commands: whether you’re sending data or reading data, you can think of both as sending a command outwards

The initialization stage

Before using Bluetooth you have to initialize the Bluetooth module — this is the very, very first thing you have to do!

Calling the uni.openBluetoothAdapter API initializes the Bluetooth module. All other
Bluetooth-related APIs must be used after uni.openBluetoothAdapter has been called. Otherwise the API returns an error ( errCode=10000 ).

Author: 德育处主任
Link: https://juejin.cn/post/7093171532318375950
Source: 稀土掘金
Copyright belongs to the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please cite the source.

Code example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<view>
<button @click="initBlue">初始化蓝牙</button>
</view>
</template>

<script setup>

// [1] Initialize Bluetooth
function initBlue() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
}
</script>


If Bluetooth is turned on on your phone, after tapping the button on the page the console will print the following

1
{"errMsg":"openBluetoothAdapter:ok"}

If Bluetooth isn’t turned on on the phone, you’ll get the following

1
{"errMsg":"openBluetoothAdapter:fail not available","code":10001}

10001 means the current Bluetooth adapter is unavailable.

If your console can print {“errMsg”:”openBluetoothAdapter:ok”}, that proves step one already succeeded.

Next you can start scanning for nearby Bluetooth devices.

Scanning for nearby devices

This step needs 2 APIs working together. So it can be broken down into the following 2 steps:

  • Turn on scanning: uni.startBluetoothDevicesDiscovery
  • Listen for newly found devices: uni.onBluetoothDeviceFound

When you’re building Bluetooth features, the logic is more like push notifications, which is why “start scanning” and “listen for new devices” are two separate operations.

uni.startBluetoothDevicesDiscovery lets the device start scanning for nearby Bluetooth devices, but this method eats a fair amount of system resources, so it’s recommended to call uni.stopBluetoothDevicesDiscovery to stop scanning once you’re connected to a device.

uni.startBluetoothDevicesDiscovery accepts an object, and that object takes a few parameters, but as a beginner we only care about success and fail. If the hardware guy on your project gave you the service uuid, you can pass it in services as well. For the other parameters you can check the introduction in the official docs.

After using uni.startBluetoothDevicesDiscovery (start scanning), you can listen with uni.onBluetoothDeviceFound, which takes a callback function.

Code example

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
<template>
<view>
<scroll-view
scroll-y
class="box"
>
<view class="item" v-for="item in blueDeviceList">
<view>
<text>id: {{ item.deviceId }}</text>
</view>
<view>
<text>name: {{ item.name }}</text>
</view>
</view>
</scroll-view>

<button @click="initBlue">初始化蓝牙</button>

<button @click="discovery">搜索附近蓝牙设备</button>

</view>
</template>

<script setup>
import { ref } from 'vue'

// The list of Bluetooth devices found
const blueDeviceList = ref([])

// [1] Initialize Bluetooth
function initBlue() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
}

// [2] Start scanning for nearby devices
function discovery() {
uni.startBluetoothDevicesDiscovery({
success(res) {
console.log('开始搜索')

// Turn on the listen callback
uni.onBluetoothDeviceFound(found)
},
fail(err) {
console.log('搜索失败')
console.error(err)
}
})
}

// [3] This method fires whenever a new device is found
function found(res) {
console.log(res)
blueDeviceList.value.push(res.devices[0])
}
</script>

<style>
.box {
width: 100%;
height: 400rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}
</style>

The logic of the code above is: if turning on “scan for nearby devices” succeeds, then turn on “listen for the event of finding a new device”.

The devices found come back with the following data:

1
2
3
4
5
6
7
8
9
10
11
{
"devices": [{
"deviceId": "B4:10:7B:C4:83:14",
"name": "蓝牙设备名",
"RSSI": -58,
"localName": "",
"advertisServiceUUIDs": ["0000FFF0-0000-1000-8000-00805F9B34FB"],
"advertisData": {}
}]
}

Every time a new device is detected I push it into the Bluetooth device list (blueDeviceList), and finally render that list’s data onto the page.

Connecting to the target device

Connecting to the target device only takes 1 API. But according to the docs, after connecting we also need to turn off the “scan for nearby devices” feature — which makes perfect sense: you already found it, so keeping on scanning is just wasting resources.

The flow is as follows:

  • Get the device ID: grab the device ID from the uni.onBluetoothDeviceFound callback
  • Connect to the device: connect using the device ID with uni.createBLEConnection
  • Stop scanning: uni.stopBluetoothDevicesDiscovery

I add a click event to every Bluetooth result found, which sends a connection request to the target device.
My device is named leihou, so I tapped that one.

Code example

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
116
117
118
119
120
121
<template>
<view>
<scroll-view
scroll-y
class="box"
>
<view class="item" v-for="item in blueDeviceList" @click="connect(item)">
<view>
<text>id: {{ item.deviceId }}</text>
</view>
<view>
<text>name: {{ item.name }}</text>
</view>
</view>
</scroll-view>

<button @click="initBlue">初始化蓝牙</button>

<button @click="discovery">搜索附近蓝牙设备</button>

</view>
</template>

<script setup>
import { ref } from 'vue'

// The list of Bluetooth devices found
const blueDeviceList = ref([])

// [1] Initialize Bluetooth
function initBlue() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
}

// [2] Start scanning for nearby devices
function discovery() {
uni.startBluetoothDevicesDiscovery({
success(res) {
console.log('开始搜索')
// Turn on the listen callback
uni.onBluetoothDeviceFound(found)
},
fail(err) {
console.log('搜索失败')
console.error(err)
}
})
}

// [3] This method fires whenever a new device is found
function found(res) {
console.log(res)
blueDeviceList.value.push(res.devices[0])
}

// The id of the Bluetooth device
const deviceId = ref('')

// [4] Connect to the device
function connect(data) {
console.log(data)

deviceId.value = data.deviceId

uni.createBLEConnection({
deviceId: deviceId.value,
success(res) {
console.log('连接成功')
console.log(res)
// Stop scanning
stopDiscovery()
},
fail(err) {
console.log('连接失败')
console.error(err)
}
})
}

// [5] Stop scanning
function stopDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success(res) {
console.log('停止成功')
console.log(res)
},
fail(err) {
console.log('停止失败')
console.error(err)
}
})
}
</script>

<style>
.box {
width: 100%;
height: 400rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}
</style>

Once connected, the console prints

1
{"errMsg":"createBLEConnection:ok"}

Right after a successful connection, call uni.stopBluetoothDevicesDiscovery to stop scanning for other nearby devices. When it stops successfully you’ll see

1
{"errMsg":"stopBluetoothDevicesDiscovery:ok"}

Listening

After connecting to the device, the first thing you do is turn on the data listening feature. That’s the only way to receive the information the device calls back to you after you send read/write commands.

To turn on listening you first need to know which services the Bluetooth device offers, then use a service to get its characteristics, and the characteristics tell you which one is readable and which one is writable. Finally you listen for messages on the characteristic.

The steps are:

  • Get the Bluetooth device services: uni.getBLEDeviceServices
  • Get the characteristics: uni.getBLEDeviceCharacteristics
  • Turn on message listening: uni.notifyBLECharacteristicValueChange
  • Receive the data coming in from the listener: uni.onBLECharacteristicValueChange

Normally the hardware guy will tell you upfront which service and which characteristic the Bluetooth device uses.

For example, this device’s Bluetooth service is: 0000FFE0-0000-1000-8000-00805F9B34FB

And the characteristic is: 0000FFE1-0000-1000-8000-00805F9B34FB

Step one, get the Bluetooth services
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
<template>
<view>
<!-- Omitted: the code from the previous step -->
<button @click="getServices">获取蓝牙服务</button>
</view>
</template>

<script setup>
import { ref } from 'vue'

// Omitted: the code from the previous step...

// [6] Get the services
function getServices() {
uni.getBLEDeviceServices({
deviceId: deviceId.value, // device ID, obtained in step [4] above
success(res) {
console.log(res)
},
fail(err) {
console.error(err)
}
})
}
</script>

Now tap the button and you’ll get all the services of the connected device.

My device has the following services. The service uuids you get at work won’t be the same as mine, and the number of them may differ too.

As you can see, the result I got contains the service 0000FFE0-0000-1000-8000-00805F9B34FB.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"services": [{
"uuid": "00001800-0000-1000-8000-00805F9B34FB",
"isPrimary": true
}, {
"uuid": "00001801-0000-1000-8000-00805F9B34FB",
"isPrimary": true
}, {
"uuid": "0000180A-0000-1000-8000-00805F9B34FB",
"isPrimary": true
}, {
"uuid": "0000FFF0-0000-1000-8000-00805F9B34FB",
"isPrimary": true
}, {
"uuid": "0000FFE0-0000-1000-8000-00805F9B34FB",
"isPrimary": true
}],
"errMsg": "getBLEDeviceServices:ok"
}

Step two, get the characteristics of a given service

To get the characteristics you need to pass in the device ID and the service ID.

In the two steps above I got the device ID as B4:10:7B:C4:83:14 and the service ID as 0000FFE0-0000-1000-8000-00805F9B34FB.

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
<template>
<view>
<!-- Omitted: the code from the previous steps -->
<button @click="getCharacteristics">获取特征值</button>
</view>
</template>

<script setup>
import { ref } from 'vue'

// Omitted: the code from the previous steps

// [7] Get the characteristics
function getCharacteristics() {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId.value, // device ID, obtained in [4]
serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // service UUID, obtainable in [6]
success(res) {
console.log(res)
},
fail(err) {
console.error(err)
}
})
}
</script>

Finally it prints successfully

1
2
3
4
5
6
7
8
9
10
11
12
{
"characteristics": [{
"uuid": "0000FFE1-0000-1000-8000-00805F9B34FB",
"properties": {
"read": true,
"write": true,
"notify": true,
"indicate": false
}
}],
"errMsg": "getBLEDeviceCharacteristics:ok"
}

The characteristics field holds all the characteristics of that service. My device’s service only has 1 characteristic, and read, write and notify are all true.

Your device may have more than one characteristic, and which one to listen on is something you have to work out with the hardware guy (usually the hardware guy just tells you which one to listen to).

Steps three and four, turn on message listening and receive the data coming from the listener

With the device ID, service ID and characteristic already in hand, you can turn on the corresponding listening.

Use uni.notifyBLECharacteristicValueChange to turn on message listening;

And uni.onBLECharacteristicValueChange fires for the messages that come in.

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
<template>
<view>
<!-- Omitted: the code from the previous steps -->
<button @click="notify">开启消息监听</button>
</view>
</template>

<script setup>
import { ref } from 'vue'

// Omitted: the code from the previous steps

// [8] Turn on message listening
function notify() {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId.value, // device ID, obtained in [4]
serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // service UUID, obtainable in [6]
characteristicId: '0000FFE1-0000-1000-8000-00805F9B34FB', // characteristic, obtainable in [7]
success(res) {
console.log(res)

// The method that receives messages
listenValueChange()
},
fail(err) {
console.error(err)
}
})
}

// Example of converting an ArrayBuffer to a hex string
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}

// Convert hex content into a string we can actually read
function hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
var len = rawStr.length;
if (len % 2 !== 0) {
alert("存在非法字符!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16);
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
}

// [9] Listen for message changes
function listenValueChange() {
uni.onBLECharacteristicValueChange(res => {
// The result
console.log(res)

// The result contains a value field, and that value is of type ArrayBuffer, so you can't see it with the naked eye in the console — you have to convert it to hex
let resHex = ab2hex(res.value)
console.log(resHex)

// Finally convert the hex to ASCII and you can see the actual result
let result = hexCharCodeToStr(resHex)
console.log(result)
})
}
</script>

The listenValueChange method is what receives the messages the device sends over.

In the example above, the result of res is

1
2
3
4
5
6
7
{
"deviceId": "B4:10:7B:C4:83:14",
"serviceId": "0000FFE0-0000-1000-8000-00805F9B34FB",
"characteristicId": "0000FFE1-0000-1000-8000-00805F9B34FB",
"value": {}
}

Whatever the device sends over ends up in the value field, but since that field is of type ArrayBuffer you can’t see it with the naked eye in the console. So we use the ab2hex method to turn the value into hex, and finally use hexCharCodeToStr to turn the hex into ASCII.

I send a string over from the device: leihou

The data the app side receives, after conversion to hex: 6c6569686f75

And after converting that hex to ASCII: leihou

Sending commands

Finally, the last step.

Looking at the Bluetooth APIs that uni-app and WeChat Mini Programs provide, there are only 2 methods for sending commands:

  • uni.writeBLECharacteristicValue: write binary data to a Bluetooth Low Energy device characteristic.
  • uni.readBLECharacteristicValue: read the binary data value of a Bluetooth Low Energy device characteristic.

Here we need to get one concept straight. This section is about “sending commands”, which means sending commands from your app or mini program to other Bluetooth devices, and those commands come in 2 flavours: one where you want to send some data to the Bluetooth device, and another where you’re asking the Bluetooth device to send you some information.

uni.writeBLECharacteristicValue

We need to discuss these two cases separately, and we’ll start with uni.writeBLECharacteristicValue.

From the docs you can see that uni.writeBLECharacteristicValue can send some data to a Bluetooth device, but the value being sent has to be converted to an ArrayBuffer.

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
<template>
<view>
<!-- Omitted: the code from the previous steps -->
<button @click="send">发送数据</button>
</view>
</template>

<script setup>
import { ref } from 'vue'

// Omitted: the code from the previous steps

// [10] Send data
function send() {
// Send a 0x00 hex value to the Bluetooth device

let msg = 'hello'

const buffer = new ArrayBuffer(msg.length)
const dataView = new DataView(buffer)
// dataView.setUint8(0, 0)

for (var i = 0; i < msg.length; i++) {
dataView.setUint8(i, msg.charAt(i).charCodeAt())
}

uni.writeBLECharacteristicValue({
deviceId: deviceId.value, // device ID, obtained in [4]
serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // service UUID, obtainable in [6]
characteristicId: '0000FFE1-0000-1000-8000-00805F9B34FB', // characteristic, obtainable in [7]
value: buffer,
success(res) {
console.log(res)
},
fail(err) {
console.error(err)
}
})
}
</script>

At this point, if uni.writeBLECharacteristicValue goes down the success path, it proves you successfully sent the data out — but that doesn’t mean the device necessarily received it.

Usually when the device gets the information you sent, it returns a message to you, and that callback message fires in uni.onBLECharacteristicValueChange, i.e. step [9] over there. But that’s controlled by the Bluetooth device side — as the front-end guy, if they leave you on read, there’s nothing you can do about it.

uni.readBLECharacteristicValue

In the “Listening” section we used uni.getBLEDeviceCharacteristics to get the device characteristics, and my device’s characteristic supports read, so I can use uni.readBLECharacteristicValue to send a “read” command to the Bluetooth device. Then in uni.onBLECharacteristicValueChange you can receive the data the device sends over.

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
<template>
<view>
<!-- Omitted: the code from the previous steps -->
<button @click="read">读取数据</button>
</view>
</template>

<script setup>
import { ref } from 'vue'

// Omitted: the code from the previous steps

// [11] Read data
function read() {
uni.readBLECharacteristicValue({
deviceId: deviceId.value,
serviceId: serviceId.value,
characteristicId: characteristicId.value,
success(res) {
console.log('读取指令发送成功')
console.log(res)
},
fail(err) {
console.log('读取指令发送失败')
console.error(err)
}
})
}
</script>

Sending a command to the device the “read” way doesn’t require passing any extra value.

At this point my device returns 00

That data is set on the hardware side.

In daily work, the main use of uni.readBLECharacteristicValue is reading data, though it isn’t used all that often.

The scenario I ran into at work was: the Bluetooth device exposes a few interfaces, and the data sent over is fairly large — sending images to the app side, for example. I’d first use uni.writeBLECharacteristicValue to tell the device which interface’s data I need now, then use uni.readBLECharacteristicValue to send the read request, and if the data volume is large I have to call uni.readBLECharacteristicValue repeatedly to read it. In the example above, for instance, the first read returns 00, the second read returns 01 …

One last reminder: uni.readBLECharacteristicValue only takes care of sending the read request, and its success and fail only tell you whether the action of sending the request this time succeeded — whether the Bluetooth device on the other end received that command is something you don’t know.

In the end you have to use uni.getBLEDeviceCharacteristics to listen for the data the device sends over.

Full code

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
<template>
<view>
<scroll-view
scroll-y
class="box"
>
<view class="item" v-for="item in blueDeviceList" @click="connect(item)">
<view>
<text>id: {{ item.deviceId }}</text>
</view>
<view>
<text>name: {{ item.name }}</text>
</view>
</view>
</scroll-view>

<button @click="initBlue">1 初始化蓝牙</button>

<button @click="discovery">2 搜索附近蓝牙设备</button>

<button @click="getServices">3 获取蓝牙服务</button>

<button @click="getCharacteristics">4 获取特征值</button>

<button @click="notify">5 开启消息监听</button>

<button @click="send">6 发送数据</button>

<button @click="read">7 读取数据</button>

<view class="msg_x">
<view class="msg_txt">
监听到的内容:{{ message }}
</view>
<view class="msg_hex">
监听到的内容(十六进制):{{ messageHex }}
</view>
</view>

</view>
</template>

<script setup>
import { ref } from 'vue'

// The list of Bluetooth devices found
const blueDeviceList = ref([])

// [1] Initialize Bluetooth
function initBlue() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
}

// [2] Start scanning for nearby devices
function discovery() {
uni.startBluetoothDevicesDiscovery({
success(res) {
console.log('开始搜索')
// Turn on the listen callback
uni.onBluetoothDeviceFound(found)
},
fail(err) {
console.log('搜索失败')
console.error(err)
}
})
}

// [3] This method fires whenever a new device is found
function found(res) {
console.log(res)
blueDeviceList.value.push(res.devices[0])
}

// The id of the Bluetooth device
const deviceId = ref('')

// [4] Connect to the device
function connect(data) {
console.log(data)

deviceId.value = data.deviceId // save the device ID we got

uni.createBLEConnection({
deviceId: deviceId.value,
success(res) {
console.log('连接成功')
console.log(res)
// Stop scanning
stopDiscovery()
uni.showToast({
title: '连接成功'
})
},
fail(err) {
console.log('连接失败')
console.error(err)
uni.showToast({
title: '连接成功',
icon: 'error'
})
}
})
}

// [5] Stop scanning
function stopDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success(res) {
console.log('停止成功')
console.log(res)
},
fail(err) {
console.log('停止失败')
console.error(err)
}
})
}

// [6] Get the services
function getServices() {
// If it's an automatic connection, it's recommended to run uni.getBLEDeviceServices 1 second later with setTimeout
uni.getBLEDeviceServices({
deviceId: deviceId.value,
success(res) {
console.log(res) // you can check in res whether the service the hardware guy gave you is there
uni.showToast({
title: '获取服务成功'
})
},
fail(err) {
console.error(err)
uni.showToast({
title: '获取服务失败',
icon: 'error'
})
}
})
}

// The service id provided by the hardware; during development you need to ask the hardware guy for it
const serviceId = ref('0000FFE0-0000-1000-8000-00805F9B34FB')

// [7] Get the characteristics
function getCharacteristics() {
// If it's an automatic connection, it's recommended to run uni.getBLEDeviceCharacteristics 1 second later with setTimeout
uni.getBLEDeviceCharacteristics({
deviceId: deviceId.value,
serviceId: serviceId.value,
success(res) {
console.log(res) // here you can check whether the characteristic supports read/write and so on; the characteristic also has to be requested from the hardware guy in advance
uni.showToast({
title: '获取特征值成功'
})
},
fail(err) {
console.error(err)
uni.showToast({
title: '获取特征值失败',
icon: 'error'
})
}
})
}

const characteristicId = ref('0000FFE1-0000-1000-8000-00805F9B34FB')

// [8] Turn on message listening
function notify() {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId.value, // device id
serviceId: serviceId.value, // listen on the given service
characteristicId: characteristicId.value, // listen on the corresponding characteristic
success(res) {
console.log(res)
listenValueChange()
uni.showToast({
title: '已开启监听'
})
},
fail(err) {
console.error(err)
uni.showToast({
title: '监听失败',
icon: 'error'
})
}
})
}

// Example of converting an ArrayBuffer to a hex string
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}

// Convert hex content into a string we can actually read
function hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
var len = rawStr.length;
if (len % 2 !== 0) {
alert("存在非法字符!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16);
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
}

// The content that was listened to
const message = ref('')
const messageHex = ref('') // hex

// [9] Listen for message changes
function listenValueChange() {
uni.onBLECharacteristicValueChange(res => {
console.log(res)
let resHex = ab2hex(res.value)
console.log(resHex)
messageHex.value = resHex
let result = hexCharCodeToStr(resHex)
console.log(String(result))
message.value = String(result)
})
}

// [10] Send data
function send() {
// Send a 0x00 hex value to the Bluetooth device
let msg = 'hello'

const buffer = new ArrayBuffer(msg.length)
const dataView = new DataView(buffer)
// dataView.setUint8(0, 0)

for (var i = 0; i < msg.length; i++) {
dataView.setUint8(i, msg.charAt(i).charCodeAt())
}

uni.writeBLECharacteristicValue({
deviceId: deviceId.value,
serviceId: serviceId.value,
characteristicId: characteristicId.value,
value: buffer,
success(res) {
console.log('writeBLECharacteristicValue success', res.errMsg)
uni.showToast({
title: 'write指令发送成功'
})
},
fail(err) {
console.error(err)
uni.showToast({
title: 'write指令发送失败',
icon: 'error'
})
}
})
}

// [11] Read data
function read() {
uni.readBLECharacteristicValue({
deviceId: deviceId.value,
serviceId: serviceId.value,
characteristicId: characteristicId.value,
success(res) {
console.log(res)
uni.showToast({
title: 'read指令发送成功'
})
},
fail(err) {
console.error(err)
uni.showToast({
title: 'read指令发送失败',
icon: 'error'
})
}
})
}
</script>

<style>
.box {
width: 98%;
height: 400rpx;
box-sizing: border-box;
margin: 0 auto 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}

.msg_x {
border: 2px solid seagreen;
width: 98%;
margin: 10rpx auto;
box-sizing: border-box;
padding: 20rpx;
}

.msg_x .msg_txt {
margin-bottom: 20rpx;
}
</style>

Author: 德育处主任
Link: https://juejin.cn/post/7093171532318375950
Source: 稀土掘金
Copyright belongs to the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please cite the source.