1 | var tessel = require('tessel');
|
2 | var bgLib = require('bglib');
|
3 | bgLib.setPacketMode(1);
|
4 | var EventEmitter = require('events').EventEmitter;
|
5 | var util = require('util');
|
6 |
|
7 | var DEBUG = 0;
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var awaitingResponse = [];
|
16 |
|
17 |
|
18 |
|
19 | var controller;
|
20 | var TX_HANDLE=20;
|
21 |
|
22 | var characteristicHandles = [21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65];
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | function connect(hardware, next) {
|
32 | controller = new BluetoothController(hardware, next);
|
33 |
|
34 | return controller;
|
35 | }
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | function BluetoothController(hardware, next) {
|
45 | this.hardware = hardware;
|
46 | this.isAdvertising = false;
|
47 | this.messenger = new BluetoothMessenger(hardware);
|
48 | this.connected = false;
|
49 | var self = this;
|
50 | controller = self;
|
51 |
|
52 | this.verifyCommunication(function(err, response) {
|
53 | if (err) {
|
54 | console.log("ERROR: Could not establish comms with BLE...");
|
55 | console.log(err.message);
|
56 | next && next(err);
|
57 | }
|
58 |
|
59 | else {
|
60 |
|
61 | console.log("Comms established with BLE!");
|
62 | self.connected = true;
|
63 | self.emit('connected');
|
64 | next && next(null);
|
65 | }
|
66 | });
|
67 | }
|
68 |
|
69 | util.inherits(BluetoothController, EventEmitter);
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | BluetoothController.prototype.scanForPeripherals = function(next) {
|
81 | this.messenger.execute(bgLib.api.gapDiscover, [bgLib.GAPDiscoverMode.gap_discover_generic], next);
|
82 | }
|
83 |
|
84 | BluetoothController.prototype.stopScanning = function(next) {
|
85 | this.messenger.execute(bgLib.api.gapEndProcedure, next);
|
86 | }
|
87 |
|
88 | BluetoothController.prototype.connectToPeripheral = function(address, address_type, conn_interval_min, conn_interval_max, timeout, latency, next) {
|
89 | this.messenger.execute(bgLib.api.gapConnectDirect, [address, address_type, conn_interval_min, conn_interval_max, timeout, latency], next);
|
90 | }
|
91 |
|
92 | BluetoothController.prototype.disconnectFromPeripheral = function(handle, next) {
|
93 | this.messenger.execute(bgLib.api.connectionDisconnect, [handle], next);
|
94 | }
|
95 |
|
96 | BluetoothController.prototype.findInformation = function(connection, start, end, next) {
|
97 | this.messenger.execute(bgLib.api.attClientFindInformation, [connection, start, end], next);
|
98 | }
|
99 |
|
100 | BluetoothController.prototype.startAdvertising = function(next) {
|
101 | this.messenger.execute(bgLib.api.gapSetMode, [bgLib.GAPDiscoverableModes.gap_general_discoverable, bgLib.GAPConnectableMode.gap_directed_connectable], next);
|
102 | }
|
103 |
|
104 | BluetoothController.prototype.readLocalHandle = function(handle, next) {
|
105 | this.messenger.execute(bgLib.api.attributesRead, [handle, 0], next);
|
106 | }
|
107 |
|
108 | BluetoothController.prototype.readRemoteHandle = function(connection, handle, next) {
|
109 | this.messenger.execute(bgLib.api.attClientReadByHandle, [connection, handle], next);
|
110 | }
|
111 |
|
112 | BluetoothController.prototype.writeValue = function(handle, value, next) {
|
113 |
|
114 | if (!next) {
|
115 | handle = TX_HANDLE;
|
116 | }
|
117 | else {
|
118 | handle = characteristicHandles[handle];
|
119 | }
|
120 | this.messenger.execute(bgLib.api.attributesWrite, [handle, 0, value], next);
|
121 | }
|
122 |
|
123 | BluetoothController.prototype.verifyCommunication = function(next) {
|
124 | this.messenger.execute(bgLib.api.systemHello, next);
|
125 | }
|
126 |
|
127 | function BluetoothMessenger(hardware) {
|
128 | this.uart = hardware.UART({baudrate:9600});
|
129 |
|
130 | this.uart.on('data', this.parseIncomingPackets);
|
131 |
|
132 | this.commandPacketTimeoutDuration = 10000;
|
133 |
|
134 |
|
135 | this.wakePin = hardware.gpio(3);
|
136 | this.wakePin.output().low();
|
137 |
|
138 | this.wakePin.high();
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | BluetoothMessenger.prototype.wakeBLE = function(wakeBool) {
|
148 | tessel.sleep(10);
|
149 | this.wakePin.set(wakeBool);
|
150 | tessel.sleep(10);
|
151 | }
|
152 |
|
153 | BluetoothMessenger.prototype.execute = function(command, params, callback) {
|
154 |
|
155 |
|
156 | var self = this;
|
157 |
|
158 | if (!command || command == 'undefined') {
|
159 | callback && callback (new Error("Invalid API call."), null);
|
160 | return;
|
161 | }
|
162 |
|
163 |
|
164 |
|
165 | if (typeof params == 'function') {
|
166 | callback = params;
|
167 | params = [];
|
168 | }
|
169 |
|
170 | bgLib.getPacket(command, params, function(err, packet) {
|
171 |
|
172 | if (err) {
|
173 |
|
174 | throw err;
|
175 | }
|
176 |
|
177 | else {
|
178 |
|
179 | packet.getByteArray(function(bArray) {
|
180 |
|
181 |
|
182 | if (DEBUG) console.log("Byte Array to be sent: ", bArray);
|
183 |
|
184 |
|
185 | self.wakeBLE(1);
|
186 |
|
187 | controller.once('hardwareIOPortStatusChange', function() {
|
188 | if (DEBUG) console.log("Port Change. Sending Packet");
|
189 | self.sendBytes(packet, bArray, callback);
|
190 | });
|
191 |
|
192 | });
|
193 | }
|
194 | });
|
195 | }
|
196 |
|
197 | BluetoothMessenger.prototype.sendBytes = function(packet, bArray, callback) {
|
198 |
|
199 | var numSent = this.uart.write(bArray);
|
200 |
|
201 |
|
202 | this.wakeBLE(0);
|
203 |
|
204 |
|
205 |
|
206 | if (bArray.length != numSent) {
|
207 |
|
208 |
|
209 | callback && callback(new Error("Not all bytes were sent..."), null);
|
210 |
|
211 |
|
212 | } else {
|
213 |
|
214 |
|
215 | if (callback) {
|
216 |
|
217 | packet.callback = callback;
|
218 |
|
219 |
|
220 | var self = this;
|
221 | packet.timeout = setTimeout(function() {
|
222 | self.commandPacketTimeout(packet);
|
223 | }, this.commandPacketTimeoutDuration);
|
224 |
|
225 |
|
226 |
|
227 | awaitingResponse.push(packet);
|
228 | }
|
229 | }
|
230 | }
|
231 |
|
232 | BluetoothMessenger.prototype.commandPacketTimeout = function(commandPacket) {
|
233 |
|
234 | popMatchingResponsePacket(commandPacket, function(err, packet) {
|
235 |
|
236 | if (err) return console.log(err);
|
237 | if (packet) {
|
238 | try {
|
239 | return packet.callback(new Error("Packet Timeout..."));
|
240 | }
|
241 | catch (e) {
|
242 | console.log(e);
|
243 | }
|
244 | }
|
245 | })
|
246 | }
|
247 |
|
248 | var popMatchingResponsePacket = function(parsedPacket, callback) {
|
249 |
|
250 |
|
251 | var matchedPacket;
|
252 |
|
253 | for (var i in awaitingResponse) {
|
254 |
|
255 | var packet = awaitingResponse[i];
|
256 |
|
257 |
|
258 | if (packet.packetHeader.cClass == parsedPacket.packetHeader.cClass
|
259 | && packet.packetHeader.cID == parsedPacket.packetHeader.cID) {
|
260 |
|
261 |
|
262 | clearTimeout(packet.timeout);
|
263 |
|
264 |
|
265 | awaitingResponse.splice(i, 1);
|
266 |
|
267 |
|
268 | matchedPacket = packet;
|
269 | break;
|
270 | }
|
271 | }
|
272 |
|
273 | callback && callback(null, matchedPacket);
|
274 |
|
275 | return matchedPacket;
|
276 | }
|
277 |
|
278 |
|
279 |
|
280 | BluetoothMessenger.prototype.parseIncomingPackets = function(data) {
|
281 |
|
282 | if (DEBUG) console.log("Just received this data: ", data);
|
283 |
|
284 |
|
285 | var incomingPackets = bgLib.parseIncoming(data, function(err, parsedPackets) {
|
286 | if (DEBUG) console.log("And that turned into ", parsedPackets.length, "packets")
|
287 | if (err){
|
288 | console.log(err);
|
289 | return;
|
290 | }
|
291 |
|
292 | for (var i in parsedPackets) {
|
293 | var parsedPacket = parsedPackets[i];
|
294 | var cClass = parsedPacket.packet.packetHeader.cClass;
|
295 | var cID = parsedPacket.packet.packetHeader.cID;
|
296 |
|
297 |
|
298 |
|
299 | switch (parsedPacket.packet.packetHeader.mType & 0x80) {
|
300 |
|
301 | case 0x80:
|
302 | if (parsedPacket.response) {
|
303 | if (cClass == 0x07 && cID == 0x00) {
|
304 | controller.emit("hardwareIOPortStatusChange", parsedPacket.response);
|
305 | }
|
306 | else if (cClass == 0x06 && cID == 0x00) {
|
307 | controller.emit("discoveredPeripheral", parsedPacket.response);
|
308 | }
|
309 | else if (cClass == 0x00 && cID == 0x00) {
|
310 | controller.emit("booted");
|
311 | }
|
312 | else if (cClass == 0x03 && cID == 0x00) {
|
313 | controller.emit("connectionStatus", parsedPacket.response);
|
314 | }
|
315 | else if (cClass == 0x03 && cID == 0x04) {
|
316 | controller.emit('disconnectedPeripheral', parsedPacket.response);
|
317 | }
|
318 | else if (cClass == 0x04 && cID == 0x04) {
|
319 | controller.emit('foundInformation', parsedPacket.response);
|
320 | }
|
321 | else if (cClass == 0x04 && cID == 0x01) {
|
322 | controller.emit('completedProcedure', parsedPacket.response);
|
323 | }
|
324 | else if (cClass == 0x04 && cID == 0x05) {
|
325 | controller.emit('readValue', parsedPacket.response);
|
326 | }
|
327 | }
|
328 |
|
329 | break;
|
330 |
|
331 |
|
332 | case 0x00:
|
333 |
|
334 | popMatchingResponsePacket(parsedPacket.packet, function(err, packet) {
|
335 |
|
336 |
|
337 | if (packet && packet.callback) packet.callback(err, parsedPacket.response);
|
338 |
|
339 | });
|
340 | break;
|
341 | default:
|
342 | throw new Error("Malformed packet returned...");
|
343 | }
|
344 | }
|
345 | });
|
346 | }
|
347 |
|
348 |
|
349 |
|
350 |
|
351 | module.exports.connect = connect;
|
352 | module.exports.BluetoothController = BluetoothController;
|
353 | module.exports.Events = Events; |
\ | No newline at end of file |