浏览代码

feat[bluetooth]: noble wrapper

tribf 4 年之前
父节点
当前提交
7f13c1b81b

+ 1 - 0
package.json

@@ -60,6 +60,7 @@
60 60
     "echarts": "^4.7.0",
61 61
     "element-ui": "^2.13.1",
62 62
     "koa": "^2.2.0",
63
+    "noble": "^1.9.1",
63 64
     "socket.io": "^2.3.0",
64 65
     "vue": "^2.5.16",
65 66
     "vue-echarts": "^5.0.0-beta.0",

+ 164 - 0
src/renderer/assets/heatmap_gradient.json

@@ -0,0 +1,164 @@
1
+{
2
+  "0.00000": "#0000E3",
3
+  "0.00392": "#0000E3",
4
+  "0.00784": "#0000E3",
5
+  "0.01176": "#0000E3",
6
+  "0.01569": "#0000E3",
7
+  "0.01961": "#0000E3",
8
+  "0.02353": "#0000E3",
9
+  "0.02745": "#0000E3",
10
+  "0.03137": "#0000E3",
11
+  "0.03529": "#0000E3",
12
+  "0.03922": "#0000E3",
13
+  "0.04314": "#0000E3",
14
+  "0.04706": "#0000E3",
15
+  "0.05098": "#0000E3",
16
+  "0.05490": "#0000E3",
17
+  "0.05882": "#0000E3",
18
+  "0.06275": "#0000E3",
19
+  "0.06667": "#0000E3",
20
+  "0.07059": "#0000E3",
21
+  "0.07451": "#0000E3",
22
+  "0.07843": "#0000E3",
23
+  "0.08235": "#0000E3",
24
+  "0.08627": "#0000E3",
25
+  "0.09020": "#0000E3",
26
+  "0.09412": "#0000E3",
27
+  "0.09804": "#0000E3",
28
+  "0.10196": "#0000E3",
29
+  "0.10588": "#0000E3",
30
+  "0.10980": "#0000E3",
31
+  "0.11373": "#0000E3",
32
+  "0.11765": "#0000E3",
33
+  "0.12157": "#0000E3",
34
+  "0.12549": "#0000E3",
35
+  "0.12941": "#0000E3",
36
+  "0.13333": "#0000E3",
37
+  "0.13725": "#0000E3",
38
+  "0.14118": "#0000E3",
39
+  "0.14510": "#0000E3",
40
+  "0.14902": "#0000E3",
41
+  "0.15294": "#0000E3",
42
+  "0.15686": "#0000E3",
43
+  "0.16078": "#0000E3",
44
+  "0.16471": "#0000E3",
45
+  "0.16863": "#0000E3",
46
+  "0.17255": "#0000E3",
47
+  "0.17647": "#0000E3",
48
+  "0.18039": "#0000E3",
49
+  "0.18431": "#0000E3",
50
+  "0.18824": "#0004FF",
51
+  "0.19216": "#0004FF",
52
+  "0.19608": "#0004FF",
53
+  "0.20000": "#0004FF",
54
+  "0.20392": "#0004FF",
55
+  "0.20784": "#0004FF",
56
+  "0.21176": "#0004FF",
57
+  "0.21569": "#0004FF",
58
+  "0.21961": "#0004FF",
59
+  "0.22353": "#0004FF",
60
+  "0.22745": "#0004FF",
61
+  "0.23137": "#0004FF",
62
+  "0.23529": "#0004FF",
63
+  "0.23922": "#0004FF",
64
+  "0.24314": "#0004FF",
65
+  "0.24706": "#0004FF",
66
+  "0.25098": "#0004FF",
67
+  "0.25490": "#0004FF",
68
+  "0.25882": "#0004FF",
69
+  "0.26275": "#0004FF",
70
+  "0.26667": "#0004FF",
71
+  "0.27059": "#0004FF",
72
+  "0.27451": "#0004FF",
73
+  "0.27843": "#0004FF",
74
+  "0.28235": "#0004FF",
75
+  "0.28627": "#0004FF",
76
+  "0.29020": "#0004FF",
77
+  "0.29412": "#0004FF",
78
+  "0.29804": "#0004FF",
79
+  "0.30196": "#0004FF",
80
+  "0.30588": "#0004FF",
81
+  "0.30980": "#0004FF",
82
+  "0.31373": "#0004FF",
83
+  "0.31765": "#0004FF",
84
+  "0.32157": "#0004FF",
85
+  "0.32549": "#0030FF",
86
+  "0.32941": "#0030FF",
87
+  "0.33333": "#0030FF",
88
+  "0.33725": "#0030FF",
89
+  "0.34118": "#0030FF",
90
+  "0.34510": "#0030FF",
91
+  "0.34902": "#0030FF",
92
+  "0.35294": "#0030FF",
93
+  "0.35686": "#0030FF",
94
+  "0.36078": "#0030FF",
95
+  "0.36471": "#0030FF",
96
+  "0.36863": "#0030FF",
97
+  "0.37255": "#0030FF",
98
+  "0.37647": "#0030FF",
99
+  "0.38039": "#0030FF",
100
+  "0.38431": "#0030FF",
101
+  "0.38824": "#0030FF",
102
+  "0.39216": "#0030FF",
103
+  "0.39608": "#0030FF",
104
+  ".40": "#007bff",
105
+  ".41": "#028aff",
106
+  ".42": "#0297ff",
107
+  ".43": "#04b9ff",
108
+  ".44": "#02ffe9",
109
+  ".45": "#00ffab",
110
+  ".46": "#00ff97",
111
+  ".47": "#00ff84",
112
+  ".48": "#00ff71",
113
+  ".49": "#00ff5d",
114
+  ".50": "#00ff4a",
115
+  ".51": "#00ff38",
116
+  ".52": "#00ff28",
117
+  ".53": "#00ff18",
118
+  ".54": "#00ff07",
119
+  ".55": "#09ff00",
120
+  ".56": "#19ff00",
121
+  ".57": "#29ff00",
122
+  ".58": "#39ff00",
123
+  ".59": "#4aff00",
124
+  ".60": "#5aff00",
125
+  ".61": "#6aff00",
126
+  ".62": "#7aff00",
127
+  ".63": "#8aff00",
128
+  ".64": "#9bff00",
129
+  ".65": "#abff00",
130
+  ".66": "#bbff00",
131
+  ".67": "#cbff00",
132
+  ".68": "#dcff00",
133
+  ".69": "#ecff00",
134
+  ".70": "#fcff00",
135
+  ".71": "#fffa00",
136
+  ".72": "#fff500",
137
+  ".73": "#fff000",
138
+  ".74": "#ffea00",
139
+  ".75": "#ffe500",
140
+  ".76": "#ffdf00",
141
+  ".77": "#ffda00",
142
+  ".78": "#ffd400",
143
+  ".79": "#ffcf00",
144
+  ".80": "#ffca00",
145
+  ".81": "#ffc400",
146
+  ".82": "#ffbf00",
147
+  ".83": "#ffb900",
148
+  ".84": "#ffb400",
149
+  ".85": "#ffaf00",
150
+  ".86": "#ffa900",
151
+  ".87": "#ffa400",
152
+  ".88": "#ff9e00",
153
+  ".89": "#ff9900",
154
+  ".90": "#ff9300",
155
+  ".91": "#ff8c00",
156
+  ".92": "#ff8400",
157
+  ".93": "#ff7d00",
158
+  ".94": "#ff7500",
159
+  ".95": "#ff6d00",
160
+  ".96": "#ff6600",
161
+  ".97": "#ff5e00",
162
+  ".98": "#fa5500",
163
+  ".99": "#ff4f00"
164
+}

+ 6 - 164
src/renderer/components/LandingPage.vue

@@ -35,6 +35,7 @@
35 35
     <el-button @click="resetDevice" :disabled="hasConnectCount == 0">复位</el-button>
36 36
     <el-button @click="toConfig" :disabled="hasConnectCount == 0">跳转至配置</el-button>
37 37
     <el-button @click="toBlueToothSend" :disabled="deviceStatus != 'deviceSuccess'" >跳转至蓝牙发送</el-button>
38
+    <el-button @click="toRealTimeBluetooth" >跳转至 实时蓝牙</el-button>
38 39
     <br/>
39 40
     <div style="width:540px;height:540px;position:relative;margin-top:30px" >
40 41
       <div ref="heatsample" style="width:540px;height:540px;position:absolute;top:0;left:0" />
@@ -87,180 +88,21 @@
87 88
       },
88 89
       toBlueToothSend() {
89 90
           this.$router.push('/blueTooth')
91
+      },
92
+      toRealTimeBluetooth() {
93
+        this.$router.push('/rt_ble')
90 94
       }
91 95
     },
92 96
     mounted() {
93 97
         this.$socket.emit('syncConnectCount','resetDevice')
94 98
         setTimeout(()=>{
99
+            var gradient = require('../assets/heatmap_gradient.json')
95 100
             var dst = this.$refs.heatsample;
96 101
             var heatmap = h337.create({
97 102
                 container: dst,
98 103
                 radius:6,
99 104
                 blur:0.7,
100
-                gradient:{
101
-                    "0.00000":"#0000E3",
102
-                    "0.00392":"#0000E3",
103
-                    "0.00784":"#0000E3",
104
-                    "0.01176":"#0000E3",
105
-                    "0.01569":"#0000E3",
106
-                    "0.01961":"#0000E3",
107
-                    "0.02353":"#0000E3",
108
-                    "0.02745":"#0000E3",
109
-                    "0.03137":"#0000E3",
110
-                    "0.03529":"#0000E3",
111
-                    "0.03922":"#0000E3",
112
-                    "0.04314":"#0000E3",
113
-                    "0.04706":"#0000E3",
114
-                    "0.05098":"#0000E3",
115
-                    "0.05490":"#0000E3",
116
-                    "0.05882":"#0000E3",
117
-                    "0.06275":"#0000E3",
118
-                    "0.06667":"#0000E3",
119
-                    "0.07059":"#0000E3",
120
-                    "0.07451":"#0000E3",
121
-                    "0.07843":"#0000E3",
122
-                    "0.08235":"#0000E3",
123
-                    "0.08627":"#0000E3",
124
-                    "0.09020":"#0000E3",
125
-                    "0.09412":"#0000E3",
126
-                    "0.09804":"#0000E3",
127
-                    "0.10196":"#0000E3",
128
-                    "0.10588":"#0000E3",
129
-                    "0.10980":"#0000E3",
130
-                    "0.11373":"#0000E3",
131
-                    "0.11765":"#0000E3",
132
-                    "0.12157":"#0000E3",
133
-                    "0.12549":"#0000E3",
134
-                    "0.12941":"#0000E3",
135
-                    "0.13333":"#0000E3",
136
-                    "0.13725":"#0000E3",
137
-                    "0.14118":"#0000E3",
138
-                    "0.14510":"#0000E3",
139
-                    "0.14902":"#0000E3",
140
-                    "0.15294":"#0000E3",
141
-                    "0.15686":"#0000E3",
142
-                    "0.16078":"#0000E3",
143
-                    "0.16471":"#0000E3",
144
-                    "0.16863":"#0000E3",
145
-                    "0.17255":"#0000E3",
146
-                    "0.17647":"#0000E3",
147
-                    "0.18039":"#0000E3",
148
-                    "0.18431":"#0000E3",
149
-                    "0.18824":"#0004FF",
150
-                    "0.19216":"#0004FF",
151
-                    "0.19608":"#0004FF",
152
-                    "0.20000":"#0004FF",
153
-                    "0.20392":"#0004FF",
154
-                    "0.20784":"#0004FF",
155
-                    "0.21176":"#0004FF",
156
-                    "0.21569":"#0004FF",
157
-                    "0.21961":"#0004FF",
158
-                    "0.22353":"#0004FF",
159
-                    "0.22745":"#0004FF",
160
-                    "0.23137":"#0004FF",
161
-                    "0.23529":"#0004FF",
162
-                    "0.23922":"#0004FF",
163
-                    "0.24314":"#0004FF",
164
-                    "0.24706":"#0004FF",
165
-                    "0.25098":"#0004FF",
166
-                    "0.25490":"#0004FF",
167
-                    "0.25882":"#0004FF",
168
-                    "0.26275":"#0004FF",
169
-                    "0.26667":"#0004FF",
170
-                    "0.27059":"#0004FF",
171
-                    "0.27451":"#0004FF",
172
-                    "0.27843":"#0004FF",
173
-                    "0.28235":"#0004FF",
174
-                    "0.28627":"#0004FF",
175
-                    "0.29020":"#0004FF",
176
-                    "0.29412":"#0004FF",
177
-                    "0.29804":"#0004FF",
178
-                    "0.30196":"#0004FF",
179
-                    "0.30588":"#0004FF",
180
-                    "0.30980":"#0004FF",
181
-                    "0.31373":"#0004FF",
182
-                    "0.31765":"#0004FF",
183
-                    "0.32157":"#0004FF",
184
-                    "0.32549":"#0030FF",
185
-                    "0.32941":"#0030FF",
186
-                    "0.33333":"#0030FF",
187
-                    "0.33725":"#0030FF",
188
-                    "0.34118":"#0030FF",
189
-                    "0.34510":"#0030FF",
190
-                    "0.34902":"#0030FF",
191
-                    "0.35294":"#0030FF",
192
-                    "0.35686":"#0030FF",
193
-                    "0.36078":"#0030FF",
194
-                    "0.36471":"#0030FF",
195
-                    "0.36863":"#0030FF",
196
-                    "0.37255":"#0030FF",
197
-                    "0.37647":"#0030FF",
198
-                    "0.38039":"#0030FF",
199
-                    "0.38431":"#0030FF",
200
-                    "0.38824":"#0030FF",
201
-                    "0.39216":"#0030FF",
202
-                    "0.39608":"#0030FF",
203
-                    '.40': '#007bff',
204
-                    '.41': '#028aff',
205
-                    '.42': '#0297ff',
206
-                    '.43': '#04b9ff',
207
-                    '.44': '#02ffe9',
208
-                    '.45': '#00ffab',
209
-                    '.46':'#00ff97',
210
-                    '.47': '#00ff84',
211
-                    '.48': '#00ff71',
212
-                    '.49': '#00ff5d',
213
-                    '.50': '#00ff4a',
214
-                    '.51': '#00ff38',
215
-                    '.52': '#00ff28',
216
-                    '.53': '#00ff18',
217
-                    '.54': '#00ff07',
218
-                    '.55': '#09ff00',
219
-                    '.56': '#19ff00',
220
-                    '.57': '#29ff00',
221
-                    '.58': '#39ff00',
222
-                    '.59': '#4aff00',
223
-                    '.60': '#5aff00',
224
-                    '.61': '#6aff00',
225
-                    '.62': '#7aff00',
226
-                    '.63': '#8aff00',
227
-                    '.64': '#9bff00',
228
-                    '.65': '#abff00',
229
-                    '.66': '#bbff00',
230
-                    '.67': '#cbff00',
231
-                    '.68': '#dcff00',
232
-                    '.69': '#ecff00',
233
-                    '.70': '#fcff00',
234
-                    '.71': '#fffa00',
235
-                    '.72': '#fff500',
236
-                    '.73': '#fff000',
237
-                    '.74': '#ffea00',
238
-                    '.75': '#ffe500',
239
-                    '.76': '#ffdf00',
240
-                    '.77': '#ffda00',
241
-                    '.78': '#ffd400',
242
-                    '.79': '#ffcf00',
243
-                    '.80': '#ffca00',
244
-                    '.81': '#ffc400',
245
-                    '.82': '#ffbf00',
246
-                    '.83': '#ffb900',
247
-                    '.84': '#ffb400',
248
-                    '.85': '#ffaf00',
249
-                    '.86': '#ffa900',
250
-                    '.87': '#ffa400',
251
-                    '.88': '#ff9e00',
252
-                    '.89': '#ff9900',
253
-                    '.90': '#ff9300',
254
-                    '.91': '#ff8c00',
255
-                    '.92': '#ff8400',
256
-                    '.93': '#ff7d00',
257
-                    '.94': '#ff7500',
258
-                    '.95': '#ff6d00',
259
-                    '.96': '#ff6600',
260
-                    '.97': '#ff5e00',
261
-                    '.98': '#fa5500',
262
-                    '.99': '#ff4f00',
263
-                }
105
+                gradient: gradient
264 106
             });
265 107
             this.h337 = heatmap;
266 108
         },1000)

+ 60 - 0
src/renderer/components/ble/rt_ble.vue

@@ -0,0 +1,60 @@
1
+<template>
2
+  <div class="bluetoothContent">
3
+
4
+    <el-button @click="toIndex">返回首页</el-button>
5
+    <el-button @click="search">搜索</el-button>
6
+    <el-button @click="connect">连接</el-button>
7
+    <el-button @click="send">发送</el-button>
8
+
9
+    <div>
10
+      <el-radio v-for="(item,index) in blueDeviceList" v-model="choseBlueDeviceId" :key="'device'+index"
11
+                :label="item.id">
12
+        {{item.localName}}
13
+      </el-radio>
14
+    </div>
15
+
16
+
17
+  </div>
18
+
19
+</template>
20
+
21
+<script>
22
+  import {mapGetters} from "vuex"
23
+
24
+  export default {
25
+    name: "rt_ble",
26
+    data() {
27
+      return {
28
+        choseBlueDeviceId: undefined
29
+      }
30
+    },
31
+    created() {
32
+      this.search()
33
+    },
34
+
35
+    methods: {
36
+      toIndex() {
37
+        this.$router.push('/')
38
+      },
39
+      search() {
40
+        this.$socket.emit('ble', {cmd: 'scan'})
41
+      },
42
+      connect() {
43
+        this.$socket.emit('ble', {cmd: 'connect', args: {id: this.choseBlueDeviceId}})
44
+      },
45
+      send() {
46
+        this.$socket.emit('ble', {cmd: 'send', args: {msg: '0123'}})
47
+      }
48
+    },
49
+    computed: {
50
+      ...mapGetters([
51
+        'blueDeviceList'
52
+      ])
53
+    },
54
+    watch: {}
55
+  }
56
+</script>
57
+
58
+<style scoped>
59
+
60
+</style>

+ 5 - 0
src/renderer/router/index.js

@@ -24,6 +24,11 @@ export default new Router({
24 24
         name: 'blueTooth',
25 25
         component: require('@/components/BlueTooth').default
26 26
     },
27
+    {
28
+        path: '/rt_ble',
29
+        name: 'rt_ble',
30
+        component: require('@/components/ble/rt_ble').default
31
+    },
27 32
 
28 33
   ]
29 34
 })

+ 15 - 2
src/renderer/store/modules/socket.js

@@ -1,11 +1,13 @@
1
-import Vue from 'vue'
2 1
 import func from '../../utils/FunctionSet'
2
+
3 3
 const state = {
4 4
     hasConnectCount:0,
5 5
     pressureData:[],
6 6
     blueToothDevice:'',
7 7
     deviceStatus:'',
8
-    blueSendData:''
8
+    blueSendData:'',
9
+
10
+    blueDeviceList: []
9 11
 }
10 12
 
11 13
 const actions = {
@@ -38,6 +40,15 @@ const mutations = {
38 40
     },
39 41
     SOCKET_sendBlueData(state,data) {
40 42
         state.blueSendData = func.buf2hex(data)
43
+    },
44
+    SOCKET_blueDeviceList(state, resp) {
45
+            console.log(resp)
46
+        if (resp.ok) {
47
+            state.blueDeviceList = resp.data;
48
+
49
+        } else {
50
+            console.log(resp)
51
+        }
41 52
     }
42 53
 }
43 54
 
@@ -47,6 +58,8 @@ const getters = {
47 58
     blueToothDevice:state=>state.blueToothDevice,
48 59
     deviceStatus:state=>state.deviceStatus,
49 60
     blueSendData:state=>state.blueSendData,
61
+
62
+    blueDeviceList:state=>state.blueDeviceList,
50 63
 }
51 64
 
52 65
 

+ 28 - 3
static/server/app.js

@@ -8,7 +8,11 @@ io = require('socket.io').listen(server);
8 8
 var sendProtocal = require('./protocal/sendProtocal')
9 9
 var command = require('./protocal/command')
10 10
 var commonFunction = require('./protocal/commonFunction')
11
-var blueTouth = require('./blueTouth')
11
+
12
+// var blueTouth = require('./blueTouth')
13
+
14
+const nobleWrapper = require('./ble/nobleWrapper')
15
+
12 16
 // socket 服务开启
13 17
 
14 18
 let socketEvent = null;
@@ -56,9 +60,9 @@ io.sockets.on('connection', function (socket) {
56 60
             console.log('已有初始连接' +connectCount +'个')
57 61
         }
58 62
         io.sockets.emit('connectCount', connectCount);
59
-        io.sockets.emit('setDeviceStatus', blueTouth.getBlueServerStatus());
63
+        // io.sockets.emit('setDeviceStatus', blueTouth.getBlueServerStatus());
60 64
         // 将io对象传递给blueTooth js (发送连接客户端已经断开客户端消息发送)
61
-        blueTouth.setIo(io)
65
+        // blueTouth.setIo(io)
62 66
     })
63 67
     // 发送获取采样频率指令
64 68
     socket.on('checkCaiyang',function (sendData) {
@@ -161,6 +165,27 @@ io.sockets.on('connection', function (socket) {
161 165
             sendData,'serverIp','get')
162 166
         blueTouth.sendData(data)
163 167
     })
168
+
169
+    socket.on('ble', function(request) {
170
+        if (request.cmd === 'scan') {
171
+            nobleWrapper.scan(function(resp) {
172
+                io.sockets.emit('blueDeviceList', resp)
173
+            })
174
+        } else if (request.cmd === 'connect') {
175
+            let on_data_cb  = function (buffer) {
176
+                console.log('Received: "' + buffer.length + '"');
177
+            }
178
+            let resp_cb =  function(resp) {
179
+                io.sockets.emit('blueConnect', resp)
180
+            }
181
+            nobleWrapper.connect(request.args.id, on_data_cb, resp_cb);
182
+        } else if (request.cmd === 'send') {
183
+            nobleWrapper.write(request.args.msg, function(resp) {
184
+                io.sockets.emit('blueSend', resp)
185
+            });
186
+        }
187
+    });
188
+
164 189
 })
165 190
 
166 191
 const frame_header_and_size_bytes = 1 + 2;

+ 155 - 0
static/server/ble/nobleWrapper.js

@@ -0,0 +1,155 @@
1
+const resp = require('../util/resp')
2
+const noble = require('noble');
3
+
4
+let inRange = {};
5
+let blueDeviceList = []
6
+let blueCharacteristics = []
7
+
8
+const ECHO_SERVICE_UUID = 'ec00';
9
+const ECHO_CHARACTERISTIC_UUID = 'ec0e';
10
+
11
+let connectedCharacteristic = null;
12
+let on_data_callback = null;
13
+
14
+noble.on('discover', function (peripheral) {
15
+  inRange[peripheral.id] = {
16
+    peripheral: peripheral
17
+  };
18
+
19
+  var blueDevice = {
20
+    'id': peripheral.id,
21
+    'localName': peripheral.advertisement.localName,
22
+    'serviceUuids': JSON.stringify(peripheral.advertisement.serviceUuids),
23
+    'address': peripheral.address,
24
+    'addressType': peripheral.addressType,
25
+    'connectable': peripheral.connectable,
26
+    'rssi': peripheral.rssi,
27
+  }
28
+
29
+  blueDeviceList.push(blueDevice)
30
+
31
+  explore(peripheral)
32
+});
33
+
34
+
35
+function explore(peripheral) {
36
+  peripheral.connect(error => {
37
+    peripheral.discoverServices([], function (error, services) {
38
+      for (let serviceIndex = 0; serviceIndex < services.length; serviceIndex++) {
39
+        (function (service) {
40
+          service.discoverCharacteristics([], function (error, characteristics) {
41
+            for (let charIndex = 0; charIndex < characteristics.length; charIndex++) {
42
+              let c = characteristics[charIndex];
43
+
44
+              let info = {
45
+                id: peripheral.id,
46
+                uuid: service.uuid,
47
+                name: service.name,
48
+                characteristic: {
49
+                  'uuid': c.uuid,
50
+                  'properties': c.properties
51
+                }
52
+              }
53
+
54
+              blueCharacteristics.push(info)
55
+            }
56
+          })
57
+        })(services[serviceIndex])
58
+
59
+      }
60
+
61
+      setTimeout(function () {
62
+        peripheral.disconnect()
63
+      }, 1000)
64
+    })
65
+  })
66
+}
67
+
68
+function merge_scan_result() {
69
+  for (let i = 0; i < blueDeviceList.length; i++) {
70
+    let device = blueDeviceList[i]
71
+    device.characteristics = []
72
+
73
+    for (let j = 0; j < blueCharacteristics.length; j++) {
74
+      let character = blueCharacteristics[j]
75
+      if (device.id === character.id) {
76
+        device.characteristics.push(character)
77
+      }
78
+    }
79
+  }
80
+
81
+  return blueDeviceList
82
+}
83
+
84
+function connectAndSetUp(peripheral) {
85
+  peripheral.connect(error => {
86
+    console.log('Connected to', peripheral.id);
87
+
88
+    // specify the services and characteristics to discover
89
+    const serviceUUIDs = [ECHO_SERVICE_UUID];
90
+    const characteristicUUIDs = [ECHO_CHARACTERISTIC_UUID];
91
+
92
+    peripheral.discoverSomeServicesAndCharacteristics(
93
+      serviceUUIDs,
94
+      characteristicUUIDs,
95
+      onServicesAndCharacteristicsDiscovered
96
+    );
97
+  });
98
+
99
+  peripheral.on('disconnect', () => console.log('disconnected'));
100
+}
101
+
102
+function onServicesAndCharacteristicsDiscovered(error, services, characteristics) {
103
+  console.log('Discovered services and characteristics');
104
+  const echoCharacteristic = characteristics[0];
105
+
106
+  // data callback receives notifications
107
+  echoCharacteristic.on('data', (data, isNotification) => {
108
+    if (on_data_callback) {
109
+      on_data_callback(data)
110
+    }
111
+  });
112
+
113
+  // subscribe to be notified whenever the peripheral update the characteristic
114
+  echoCharacteristic.subscribe(error => {
115
+    if (error) {
116
+      console.error('Error subscribing to echoCharacteristic');
117
+    } else {
118
+      console.log('Subscribed for echoCharacteristic notifications');
119
+    }
120
+  });
121
+
122
+  connectedCharacteristic = echoCharacteristic
123
+}
124
+
125
+module.exports = {
126
+  scan: function (resp_callback) {
127
+
128
+    try {
129
+      blueDeviceList = []
130
+      blueCharacteristics = []
131
+
132
+      setTimeout(() => {
133
+        noble.stopScanning()
134
+        resp_callback(resp.ok_resp(merge_scan_result()))
135
+      }, 2000);
136
+      noble.startScanning()
137
+    } catch (e) {
138
+      resp_callback(resp.fail_resp(e.message))
139
+    }
140
+  },
141
+  connect: function (id, on_data_cb, resp_callback) {
142
+    on_data_callback = on_data_cb;
143
+    connectAndSetUp(inRange[id].peripheral)
144
+  },
145
+  write: function (msg, resp_callback) {
146
+    try {
147
+      const buffer = new Buffer(msg, 'utf-8');
148
+      if (connectedCharacteristic) {
149
+        connectedCharacteristic.write(buffer);
150
+      }
151
+    } catch (e) {
152
+      resp_callback(resp.fail_resp(e.message))
153
+    }
154
+  }
155
+}

+ 9 - 0
static/server/util/resp.js

@@ -0,0 +1,9 @@
1
+module.exports = {
2
+  fail_resp: function fail_resp(msg) {
3
+    return {ok: false, msg: msg}
4
+  },
5
+
6
+  ok_resp: function ok_resp(data) {
7
+    return {ok: true, data: data}
8
+  }
9
+}