Skip to content

Commit faa85ad

Browse files
committedJul 22, 2018
added topic /ble/read/DEVICE for scanning services and characteristics
1 parent c69314e commit faa85ad

File tree

3 files changed

+132
-27
lines changed

3 files changed

+132
-27
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ You can also connect to a device:
172172

173173
* `/ble/write/DEVICE/SERVICE/CHARACTERISTIC` connects and writes to the charactertistic
174174
* `/ble/read/DEVICE/SERVICE/CHARACTERISTIC` connects and reads from the charactertistic
175+
* `/ble/read/DEVICE` connects and reads an array of services and charactertistics
175176
* `/ble/notify/DEVICE/SERVICE/CHARACTERISTIC` connects and starts notifications on the characteristic, which
176177
send data back on `/ble/data/DEVICE/SERVICE/CHARACTERISTIC`
177178
* `/ble/ping/DEVICE` connects, or maintains a connection to the device, and sends `/ble/pong/DEVICE` on success

‎lib/connect.js

+117-21
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ function Connection(device, callback) {
3636

3737
log(connection.name+": Connecting...");
3838
device.connect(function (error) {
39-
if (error) {
39+
if (error) {
4040
log(connection.name+": Error Connecting: "+error.toString());
4141
connection.device = undefined;
4242
connection.close();
4343
callback("Error Connecting: "+error.toString());
4444
} else {
4545
log("Connected.");
4646
callback(null, connection);
47-
}
47+
}
4848
});
4949
}
5050
Connection.prototype.getCharacteristic = function(serviceUUID, characteristicUUID, callback) {
@@ -61,8 +61,8 @@ Connection.prototype.getCharacteristic = function(serviceUUID, characteristicUUI
6161
};
6262
log(connection.name+": found characteristic: " + matchedCharacteristic.uuid);
6363
callback(null, matchedCharacteristic);
64-
} else {
65-
callback("Characteristic "+characteristicUUID+" not found");
64+
} else {
65+
callback("Characteristic "+characteristicUUID+" not found");
6666
}
6767
});
6868
}
@@ -74,7 +74,7 @@ Connection.prototype.getCharacteristic = function(serviceUUID, characteristicUUI
7474

7575
log(connection.name+": Getting Service...");
7676
var timeout = setTimeout(function() {
77-
timeout = undefined;
77+
timeout = undefined;
7878
log(connection.name+": Timed out getting services.");
7979
callback("Timed out getting services.");
8080
}, 4000);
@@ -91,21 +91,75 @@ Connection.prototype.getCharacteristic = function(serviceUUID, characteristicUUI
9191
getCharacteristicFromService(matchedService);
9292
} else {
9393
if (timeout) clearTimeout(timeout);
94-
callback("Service "+serviceUUID+" not found");
94+
callback("Service "+serviceUUID+" not found");
9595
}
96-
});
96+
});
9797
}
9898
}
9999

100+
Connection.prototype.getServices = function(callback) {
101+
var connection = this;
102+
103+
function handleService(allServices,index){
104+
matchedService = allServices[index]
105+
log(connection.name+": found service: " + matchedService.uuid, "getting Characteristic....");
106+
if (!connection.services[matchedService.uuid])
107+
connection.services[matchedService.uuid] = { service : matchedService };
108+
matchedService.discoverCharacteristics(null, function(error, characteristics) { // do search for all characteristics
109+
if (!error) {
110+
if (timeout) clearTimeout(timeout);
111+
if (characteristics != undefined && characteristics.length) {
112+
characteristics.forEach(function(matchedCharacteristic){
113+
connection.services[matchedService.uuid][matchedCharacteristic.uuid] = {
114+
characteristic : matchedCharacteristic,
115+
notifyCallback : undefined
116+
};
117+
log(connection.name+": found characteristic: " + matchedCharacteristic.uuid);
118+
});
119+
}
120+
if (index < allServices.length - 1) { // Last service in array?
121+
handleService(allServices,index + 1) // Handle next service
122+
} else {
123+
callback(null,connection.services) // Return connection's services
124+
}
125+
} else {
126+
callback("Failed to discover characteristics")
127+
}
128+
});
129+
}
130+
131+
// don't look in cache
132+
133+
log(connection.name+": Getting Services...");
134+
var timeout = setTimeout(function() {
135+
timeout = undefined;
136+
log(connection.name+": Timed out getting services.");
137+
callback("Timed out getting services.");
138+
}, 4000);
139+
140+
141+
this.device.discoverServices(null, function(error, services) { // do search for all services
142+
if(!error) {
143+
144+
if (services != undefined && services.length) {
145+
handleService(services,0)
146+
} else {
147+
callback(null,{})
148+
}
149+
} else {
150+
callback("Failed to discover services");
151+
}
152+
});
153+
}
100154

101-
Connection.prototype.close = function() {
155+
Connection.prototype.close = function() {
102156
if (this.device) {
103157
log(this.name+": Disconnecting.");
104-
try {
105-
this.device.disconnect();
158+
try {
159+
this.device.disconnect();
106160
log(this.name+": Disconnected");
107-
} catch (e) {
108-
log(this.name+": Disconnect error: "+e);
161+
} catch (e) {
162+
log(this.name+": Disconnect error: "+e);
109163
}
110164
this.device = undefined;
111165
}
@@ -124,7 +178,7 @@ Connection.prototype.setUsed = function() {
124178
// -----------------------------------------------------------------------------
125179
// -----------------------------------------------------------------------------
126180

127-
// Repeated write to characteristic
181+
// Repeated write to characteristic
128182
function writeToCharacteristic(characteristic, message, callback) { // added function to write longer strings
129183
if (message.length) {
130184
var data = message.slice(0, 20);
@@ -176,7 +230,7 @@ function serviceQueue() {
176230
if (connections.length < MAX_CONNECTIONS) {
177231
var job = queue.shift();
178232
discovery.stopScan();
179-
setTimeout(job, 1000);
233+
setTimeout(job, 1000);
180234
}
181235
}
182236

@@ -200,7 +254,7 @@ exports.write = function(device, service, characteristic, data, callback) {
200254
getConnectedDevice(device, function(err, connection) {
201255
if (err) return setNotBusy();
202256

203-
connection.getCharacteristic(util.uuid2noble(service),
257+
connection.getCharacteristic(util.uuid2noble(service),
204258
util.uuid2noble(characteristic),
205259
function(err,char) {
206260
if (err) return setNotBusy();;
@@ -222,7 +276,7 @@ exports.read = function(device, service, characteristic, callback) {
222276
isBusy = true;
223277
getConnectedDevice(device, function(err, connection) {
224278
if (err) return;
225-
connection.getCharacteristic(util.uuid2noble(service),
279+
connection.getCharacteristic(util.uuid2noble(service),
226280
util.uuid2noble(characteristic),
227281
function(err,char) {
228282
if (err) return setNotBusy();
@@ -235,6 +289,48 @@ exports.read = function(device, service, characteristic, callback) {
235289
});
236290
};
237291

292+
/* Read services from the given device */
293+
exports.readServices = function(device, callback) {
294+
if (isBusy) {
295+
queue.push(function() { exports.readServices(device,callback); });
296+
return;
297+
}
298+
isBusy = true;
299+
getConnectedDevice(device, function(err, connection) {
300+
if (err) return;
301+
connection.getServices(function(err,services) {
302+
if (err) return setNotBusy();
303+
/* Extract UUIDs from the connection's services object.
304+
Output array format:
305+
[
306+
{
307+
uuid:serviceUuid,
308+
characteristics: [
309+
{
310+
uuid:characteristicUuid
311+
}
312+
]
313+
}
314+
]
315+
*/
316+
var output = []
317+
for (service in services) {
318+
var item = {
319+
uuid:service,
320+
characteristics:[]
321+
}
322+
for (key in services[service]) {
323+
if(key !== 'service') item.characteristics.push({uuid:key})
324+
}
325+
output.push(item)
326+
}
327+
// Stringifies array before sending
328+
callback(JSON.stringify(output))
329+
setNotBusy();
330+
});
331+
});
332+
};
333+
238334
/* Start notifications on the given device */
239335
exports.notify = function(device, service, characteristic, callback) {
240336
if (isBusy) {
@@ -246,17 +342,17 @@ exports.notify = function(device, service, characteristic, callback) {
246342
if (err) return setNotBusy();
247343
var serviceUUID = util.uuid2noble(service);
248344
var characteristicUUID = util.uuid2noble(characteristic);
249-
connection.getCharacteristic(serviceUUID,characteristicUUID,
345+
connection.getCharacteristic(serviceUUID,characteristicUUID,
250346
function(err,char) {
251347
if (err) return setNotBusy();
252348
if (connection.services[serviceUUID][characteristicUUID].notifyCallback) {
253349
connection.services[serviceUUID][characteristicUUID].notifyCallback = callback;
254350
return setNotBusy(); // notifications were already set up
255-
}
351+
}
256352
char.on('data', function (data) {
257353
//log(connection.name+": notification on "+data.toString());
258354
//new Uint8Array(data).buffer
259-
if (connection.services[serviceUUID][characteristicUUID].notifyCallback)
355+
if (connection.services[serviceUUID][characteristicUUID].notifyCallback)
260356
connection.services[serviceUUID][characteristicUUID].notifyCallback(data.toString());
261357
});
262358
char.subscribe(function() {
@@ -276,7 +372,7 @@ exports.ping = function(device, callback) {
276372
}
277373
isBusy = true;
278374
getConnectedDevice(device, function(err, connection) {
279-
if (err) return setNotBusy();
375+
if (err) return setNotBusy();
280376
if (callback) callback(null);
281377
setNotBusy();
282378
});
@@ -296,5 +392,5 @@ setInterval(function() {
296392
connection.close();
297393
i--; // connection automatically removes itself from list
298394
}
299-
}
395+
}
300396
}, 1000);

‎lib/mqttclient.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,20 @@ client.on('message', function (topic, message) {
8484
if (path[1]=="read") {
8585
var id = config.deviceToAddr(path[2]);
8686
if (discovery.inRange[id]) {
87-
var device = discovery.inRange[id].peripheral;
88-
var service = attributes.lookup(path[3].toLowerCase());
89-
var charc = attributes.lookup(path[4].toLowerCase());
90-
connect.read(device, service, charc, function(data) {
91-
client.publish("/ble/data/"+path[2]+"/"+path[3]+"/"+path[4], data);
92-
});
87+
var device = discovery.inRange[id].peripheral;
88+
if(path.length === 5) {
89+
var service = attributes.lookup(path[3].toLowerCase());
90+
var charc = attributes.lookup(path[4].toLowerCase());
91+
connect.read(device, service, charc, function(data) {
92+
client.publish("/ble/data/"+path[2]+"/"+path[3]+"/"+path[4], data);
93+
});
94+
} else if(path.length === 3) {
95+
connect.readServices(device, function(data) {
96+
client.publish("/ble/data/"+path[2], data);
97+
});
98+
} else {
99+
log("Invalid number of topic levels");
100+
}
93101
} else {
94102
log("Read from "+id+" but not in range");
95103
}

0 commit comments

Comments
 (0)