1 | var tessel = require('tessel');
|
2 | var bglib = require('bglib');
|
3 | var uuid = require('./uuid');
|
4 | var attributes = require('./attributes.json');
|
5 | var events = require('events');
|
6 | var util = require('util');
|
7 |
|
8 |
|
9 | var DEBUG = 0;
|
10 |
|
11 | var __firstHandle = 0x0001;
|
12 | var __lastHandle = 0xFFFF;
|
13 |
|
14 | function Messenger(hardware) {
|
15 |
|
16 | this.bgLib = new bglib();
|
17 |
|
18 | this.bgLib.setPacketMode(1);
|
19 |
|
20 |
|
21 | this.uart = hardware.UART({baudrate:9600});
|
22 |
|
23 |
|
24 | this.uart.on('data', this.parseIncomingPackets.bind(this));
|
25 |
|
26 |
|
27 | this.resetPin = hardware.gpio(3);
|
28 |
|
29 |
|
30 | this.awaitingResponse = [];
|
31 |
|
32 |
|
33 | this.commandPacketTimeoutDuration = 60000;
|
34 |
|
35 |
|
36 |
|
37 | this.reset();
|
38 | }
|
39 |
|
40 | util.inherits(Messenger, events.EventEmitter);
|
41 |
|
42 | Messenger.prototype.connectTimeout = function() {
|
43 |
|
44 | setImmediate(function() {
|
45 |
|
46 | this.emit('error', new Error("Unable to connect to module."));
|
47 | }.bind(this));
|
48 | }
|
49 |
|
50 | Messenger.prototype.bootSequence = function(connectTimeout, callback) {
|
51 |
|
52 |
|
53 | this.verifyCommunication( function(err) {
|
54 |
|
55 |
|
56 | clearTimeout(connectTimeout);
|
57 |
|
58 | callback && callback(err);
|
59 |
|
60 | if (err) {
|
61 |
|
62 | setImmediate(function() {
|
63 | this.emit('error', err);
|
64 | }.bind(this));
|
65 | }
|
66 |
|
67 | else {
|
68 |
|
69 | setImmediate(function() {
|
70 | this.emit('ready');
|
71 | }.bind(this));
|
72 | }
|
73 | }.bind(this));
|
74 |
|
75 |
|
76 | this.removeAllListeners('booted');
|
77 | }
|
78 |
|
79 | Messenger.prototype.reset = function(callback) {
|
80 |
|
81 | var connectTimeout = setTimeout(this.connectTimeout.bind(this), 5000);
|
82 |
|
83 |
|
84 | this.once('booted', this.bootSequence.bind(this, connectTimeout, callback));
|
85 |
|
86 | this.resetPin.output().low();
|
87 | this.resetPin.high();
|
88 | }
|
89 |
|
90 | Messenger.prototype.verifyCommunication = function(callback) {
|
91 | this.execute(bglib.api.systemHello, callback);
|
92 | }
|
93 |
|
94 | Messenger.prototype.startScanning = function(callback) {
|
95 | this.execute(bglib.api.gapDiscover, [this.bgLib.GAPDiscoverMode.gap_discover_generic], callback);
|
96 | }
|
97 |
|
98 | Messenger.prototype.stopScanning = function(callback) {
|
99 | this.execute(bglib.api.gapEndProcedure, callback);
|
100 | }
|
101 |
|
102 | Messenger.prototype.connect = function(address, addressType, callback) {
|
103 |
|
104 |
|
105 | this.execute(bglib.api.gapConnectDirect, [address, address_type, 1000, 2000, 3200, 0], callback);
|
106 | }
|
107 | Messenger.prototype.disconnect = function(connection, callback) {
|
108 |
|
109 |
|
110 | this.execute(bglib.api.connectionDisconnect, [connection], callback);
|
111 | }
|
112 |
|
113 | Messenger.prototype.discoverServices = function(peripheral, callback) {
|
114 | this.execute(bglib.api.attClientReadByGroupType, [peripheral.connection, __firstHandle, __lastHandle, [0x00, 0x28]], callback);
|
115 | }
|
116 |
|
117 | Messenger.prototype.discoverAllAttributes = function(peripheral, callback) {
|
118 | this.execute(bglib.api.attClientFindInformation, [peripheral.connection, __firstHandle, __lastHandle], callback);
|
119 | }
|
120 |
|
121 | Messenger.prototype.discoverCharacteristicsInRangeForUUID = function(peripheral, startHandle, endHandle, uuid, callback) {
|
122 | this.execute(bglib.api.attClientReadByType, [peripheral.connection, startHandle, endHandle, uuid.toBuffer()], callback);
|
123 | }
|
124 |
|
125 | Messenger.prototype.discoverCharacteristicUUID = function(characteristic, callback) {
|
126 | this.execute(bglib.api.attClientFindInformation, [characteristic._peripheral.connection, characteristic.handle, characteristic.handle + 1], callback);
|
127 | }
|
128 |
|
129 | Messenger.prototype.discoverCharacteristicsInRange = function(peripheral, startHandle, endHandle, callback) {
|
130 | this.execute(bglib.api.attClientFindInformation, [peripheral.connection, startHandle, endHandle], callback);
|
131 | }
|
132 |
|
133 | Messenger.prototype.findHandle = function(peripheral, offset, callback) {
|
134 | this.execute(bglib.api.attClientFindInformation, [peripheral.connection, offset, offset+1], callback);
|
135 | }
|
136 |
|
137 | Messenger.prototype.readCharacteristicValue = function(characteristic, callback) {
|
138 | this.execute(bglib.api.attClientReadLong, [characteristic._peripheral.connection, characteristic.handle], callback);
|
139 | }
|
140 |
|
141 | Messenger.prototype.writeAttributeImmediately = function(attribute, buf, callback) {
|
142 | this.execute(bglib.api.attClientAttributeWrite, [attribute._peripheral.connection, attribute.handle, buf], callback);
|
143 | }
|
144 |
|
145 | Messenger.prototype.prepareWrite = function(attribute, buf, offset, callback) {
|
146 | this.execute(bglib.api.attClientPrepareWrite, [attribute._peripheral.connection, attribute.handle, offset, buf], callback);
|
147 | }
|
148 |
|
149 | Messenger.prototype.executeWrite = function(attribute, callback) {
|
150 | this.execute(bglib.api.attClientExecuteWrite, [attribute._peripheral.connection, 1], callback);
|
151 | }
|
152 |
|
153 | Messenger.prototype.cancelWrite = function(attribute, callback) {
|
154 | this.execute(bglib.api.attClientExecuteWrite, [attribute._peripheral.connection, 0], callback);
|
155 | }
|
156 |
|
157 | Messenger.prototype.readHandle = function(peripheral, attribute, callback) {
|
158 | this.execute(bglib.api.attClientReadLong, [peripheral.connection, attribute.handle], callback);
|
159 | }
|
160 |
|
161 | Messenger.prototype.confirmIndication = function(characteristic, callback) {
|
162 | this.execute(bglib.api.attClientIndicateConfirm, [characteristic._peripheral.connection], callback);
|
163 | }
|
164 |
|
165 | Messenger.prototype.updateRssi = function(peripheral, callback) {
|
166 | this.execute(bglib.api.connectionGetRSSI, [peripheral.connection], callback);
|
167 | }
|
168 |
|
169 | Messenger.prototype.getAddress = function(callback) {
|
170 | this.execute(bglib.api.systemAddressGet, [], callback);
|
171 | }
|
172 |
|
173 | Messenger.prototype.getMaxConnections = function(callback) {
|
174 | this.execute(bglib.api.systemGetConnections, [], callback);
|
175 | }
|
176 |
|
177 |
|
178 | Messenger.prototype.startAdvertising = function(callback) {
|
179 | this.execute(bglib.api.gapSetMode, [this.bgLib.GAPDiscoverableModes.gap_general_discoverable, this.bgLib.GAPConnectableMode.gap_directed_connectable], callback);
|
180 | }
|
181 |
|
182 | Messenger.prototype.stopAdvertising = function(callback) {
|
183 | this.execute(bglib.api.gapSetMode, [this.bgLib.GAPDiscoverableModes.gap_non_discoverable, this.bgLib.GAPConnectableMode.gap_non_connectable], callback);
|
184 | }
|
185 |
|
186 | Messenger.prototype.setAdvertisementData = function(advOrScan, data, callback) {
|
187 | this.execute(bglib.api.gapSetAdvData, [advOrScan, data], callback);
|
188 | }
|
189 |
|
190 | Messenger.prototype.writeLocalValue = function(handle, value, callback) {
|
191 | this.execute(bglib.api.attributesWrite, [handle, 0, value], callback);
|
192 | }
|
193 |
|
194 | Messenger.prototype.readLocalValue = function(handle, offset, callback) {
|
195 | this.execute(bglib.api.attributesRead, [handle, offset], callback);
|
196 | }
|
197 |
|
198 | Messenger.prototype.remoteWriteAck = function(connection, err, callback) {
|
199 | this.execute(bglib.api.attributesUserWriteResponse, [connection, err], callback);
|
200 | }
|
201 |
|
202 | Messenger.prototype.readADC = function(input, decimation, aref, callback) {
|
203 | this.execute(bglib.api.hwADCRead, [input, decimation, aref], callback);
|
204 | }
|
205 |
|
206 | Messenger.prototype.I2CSend = function(address, stopCondition, txbuf, callback) {
|
207 | this.execute(bglib.api.hwI2CWrite, [address, stopCondition, txbuf], callback);
|
208 | }
|
209 |
|
210 | Messenger.prototype.I2CRead = function(address, stopCondition, length, callback) {
|
211 | this.execute(bglib.api.hwI2CRead, [address, stopCondition, length], callback);
|
212 | }
|
213 |
|
214 | Messenger.prototype.readPin = function(port, pinMask, callback) {
|
215 | this.execute(bglib.api.hwIOPortRead, [port, pinMask], callback);
|
216 | }
|
217 |
|
218 | Messenger.prototype.writePin = function(port, pinMask, value, callback) {
|
219 | this.execute(bglib.api.hwIOPortWrite, [port, pinMask, value], callback);
|
220 | }
|
221 |
|
222 | Messenger.prototype.setPinDirections = function(port, pinMask, callback) {
|
223 | this.execute(bglib.api.hwIOPortConfigDirection, [port, pinMask], callback);
|
224 | }
|
225 |
|
226 | Messenger.prototype.watchPin = function(port, mask, edge, callback) {
|
227 | this.execute(bglib.api.hwIOPortConfigIRQ, [port, mask, edge], callback);
|
228 | }
|
229 |
|
230 | Messenger.prototype.setBondable = function(bondable, callback) {
|
231 | this.execute(bglib.api.smSetBondableMode, [bondable], callback);
|
232 | }
|
233 |
|
234 | Messenger.prototype.getBonds = function(callback) {
|
235 | this.execute(bglib.api.smGetBonds, [], callback);
|
236 | }
|
237 |
|
238 | Messenger.prototype.deleteBonds = function(peripheral, callback) {
|
239 | this.execute(bglib.api.smDeleteBonding, [peripheral.bondHandle], callback);
|
240 | }
|
241 |
|
242 | Messenger.prototype.startEncryption = function(peripheral, bond, callback) {
|
243 | this.execute(bglib.api.smEncryptStart, [peripheral.connection, bond], callback);
|
244 | }
|
245 |
|
246 | Messenger.prototype.enterPasskey = function(peripheral, passkey, callback) {
|
247 | this.execute(bglib.api.smPasskeyEntry, [peripheral.connection, passkey], callback);
|
248 | }
|
249 |
|
250 | Messenger.prototype.setOOBData = function(data, callback) {
|
251 | this.execute(bglib.api.smPasskeyEntry, [data], callback);
|
252 | }
|
253 |
|
254 | Messenger.prototype.setSecurityParameters = function(mitm, keysize, smpio, callback) {
|
255 | this.execute(bglib.api.smPasskeyEntry, [mitm, keysize, smpio], callback);
|
256 | }
|
257 |
|
258 |
|
259 | Messenger.prototype.discoverIncludedServices = function(peripheral, callback) {
|
260 | this.execute(bglib.api.attClientReadByGroupType, [peripheral.connection, 00001, 0xFFFF, [0x02, 0x28]], callback);
|
261 | }
|
262 |
|
263 |
|
264 |
|
265 |
|
266 | Messenger.prototype.execute = function(command, params, callback) {
|
267 |
|
268 | if (!command || command == 'undefined') {
|
269 | callback && callback (new Error("Invalid API call."), null);
|
270 | return;
|
271 | }
|
272 |
|
273 |
|
274 | if (typeof params == 'function') {
|
275 | callback = params;
|
276 | params = [];
|
277 | }
|
278 |
|
279 | var self = this;
|
280 |
|
281 |
|
282 | this.bgLib.getPacket(command, params, function(err, packet) {
|
283 |
|
284 | if (err) {
|
285 |
|
286 |
|
287 | return callback && callback(err);
|
288 | }
|
289 |
|
290 | else {
|
291 |
|
292 | packet.getByteArray(function(bArray) {
|
293 |
|
294 | if (DEBUG) {
|
295 | console.log("Byte Array to be sent: ", bArray);
|
296 | }
|
297 |
|
298 |
|
299 | self.sendBytes(packet, bArray, callback);
|
300 |
|
301 | });
|
302 | }
|
303 | });
|
304 | }
|
305 |
|
306 | Messenger.prototype.sendBytes = function(packet, bArray, callback) {
|
307 |
|
308 |
|
309 | var numSent = this.uart.write(bArray);
|
310 |
|
311 |
|
312 |
|
313 | if (bArray.length != numSent) {
|
314 |
|
315 |
|
316 | callback && callback(new Error("Not all bytes were sent..."), null);
|
317 |
|
318 |
|
319 | } else if (callback) {
|
320 |
|
321 |
|
322 | packet.callback = callback;
|
323 |
|
324 |
|
325 | var self = this;
|
326 | packet.timeout = setTimeout(function() {
|
327 | self.commandPacketTimeout.bind(self, packet)();
|
328 | }, this.commandPacketTimeoutDuration);
|
329 |
|
330 |
|
331 | this.awaitingResponse.push(packet);
|
332 | }
|
333 | }
|
334 |
|
335 | Messenger.prototype.commandPacketTimeout = function(commandPacket) {
|
336 |
|
337 | this.popMatchingResponsePacket(commandPacket, function timeoutPacketFetch(err, packet) {
|
338 |
|
339 | if (err) return console.log(err);
|
340 | if (packet) {
|
341 | try {
|
342 | return packet.callback && packet.callback(new Error("Packet Timeout..."));
|
343 | }
|
344 | catch (e) {
|
345 | console.log(e);
|
346 | }
|
347 | }
|
348 | })
|
349 | }
|
350 |
|
351 | Messenger.prototype.popMatchingResponsePacket = function(parsedPacket, callback) {
|
352 |
|
353 |
|
354 | var matchedPacket;
|
355 |
|
356 | for (var i = 0; i < this.awaitingResponse.length; i++) {
|
357 |
|
358 | var packet = this.awaitingResponse[i];
|
359 |
|
360 | if (packet && packet.cClass === parsedPacket.cClass
|
361 | && packet.cID === parsedPacket.cID) {
|
362 |
|
363 |
|
364 | clearTimeout(packet.timeout);
|
365 |
|
366 |
|
367 | this.awaitingResponse.splice(i, 1);
|
368 |
|
369 |
|
370 | matchedPacket = packet;
|
371 | break;
|
372 | }
|
373 | }
|
374 |
|
375 | callback && callback(null, matchedPacket);
|
376 |
|
377 | return matchedPacket;
|
378 | }
|
379 |
|
380 |
|
381 |
|
382 | Messenger.prototype.parseIncomingPackets = function(data) {
|
383 |
|
384 | if (DEBUG) console.log("Just received this data: ", data);
|
385 |
|
386 | var self = this;
|
387 |
|
388 |
|
389 | var incomingPackets = this.bgLib.parseIncoming(data, function callbackSeeker(err, parsedPackets) {
|
390 |
|
391 |
|
392 | if (err){
|
393 | console.warn(err);
|
394 | return;
|
395 | }
|
396 |
|
397 |
|
398 | for (var i = 0; i < parsedPackets.length; i++) {
|
399 | var parsedPacket = parsedPackets[i];
|
400 | var cClass = parsedPacket.packet.cClass;
|
401 | var cID = parsedPacket.packet.cID;
|
402 |
|
403 | if (DEBUG) console.log("Got this packet: ", parsedPacket);
|
404 |
|
405 |
|
406 |
|
407 | switch (parsedPacket.packet.mType & 0x80) {
|
408 |
|
409 | case 0x80:
|
410 | if (parsedPacket.response) {
|
411 | if (cClass === 0x00 && cID === 0x00) {
|
412 | self.emit("booted");
|
413 | }
|
414 | else if (cClass === 0x02 && cID === 0x00) {
|
415 | self.emit("remoteWrite", parsedPacket.response)
|
416 | }
|
417 | else if (cClass === 0x02 && cID === 0x02) {
|
418 | self.emit("remoteStatus", parsedPacket.response)
|
419 | }
|
420 | else if (cClass === 0x03 && cID === 0x00) {
|
421 | self.emit("connectionStatus", parsedPacket.response);
|
422 | }
|
423 | else if (cClass === 0x03 && cID === 0x04) {
|
424 | self.emit('disconnect', parsedPacket.response);
|
425 | }
|
426 | else if (cClass === 0x04 && cID === 0x00) {
|
427 | self.emit('remoteHandleRead', parsedPacket.response);
|
428 | }
|
429 | else if (cClass === 0x04 && cID === 0x01) {
|
430 | self.emit('completedProcedure', parsedPacket.response);
|
431 | }
|
432 | else if (cClass === 0x04 && cID === 0x02) {
|
433 | self.emit('groupFound', parsedPacket.response);
|
434 | }
|
435 | else if (cClass === 0x04 && cID === 0x04) {
|
436 | self.emit('findInformationFound', parsedPacket.response);
|
437 | }
|
438 | else if (cClass === 0x04 && cID === 0x05) {
|
439 | self.emit('attributeValue', parsedPacket.response);
|
440 | }
|
441 | else if (cClass === 0x05 && cID === 0x04) {
|
442 | self.emit('bondStatus', parsedPacket.response);
|
443 | }
|
444 | else if (cClass === 0x06 && cID === 0x00) {
|
445 | self.emit("discover", parsedPacket.response);
|
446 | }
|
447 | else if (cClass === 0x07 && cID === 0x00) {
|
448 | self.emit("portStatus", parsedPacket.response);
|
449 | }
|
450 | else if (cClass === 0x07 && cID === 0x02) {
|
451 | self.emit("ADCRead", parsedPacket.response);
|
452 | }
|
453 | }
|
454 | break;
|
455 |
|
456 |
|
457 | case 0x00:
|
458 |
|
459 | self.popMatchingResponsePacket(parsedPacket.packet, function responsePacketPop(err, packet) {
|
460 |
|
461 |
|
462 | if (!err && (packet && packet.result != 0)) {
|
463 |
|
464 | err = packet.result;
|
465 | }
|
466 |
|
467 |
|
468 | if (packet && packet.callback) packet.callback(err, parsedPacket.response);
|
469 |
|
470 | });
|
471 | break;
|
472 | default:
|
473 | console.warn("Malformed packet returned...");
|
474 | }
|
475 | }
|
476 | });
|
477 | }
|
478 |
|
479 | module.exports = Messenger;
|