UNPKG

@abandonware/noble

Version:

A Node.js BLE (Bluetooth Low Energy) central library.

199 lines (164 loc) 6.2 kB
/* eslint-disable handle-callback-err */ /** discover a device (here, the first one where the name was resolved), * for the first device discover all services and characteristics, * store the collected GATT information into a meta-data object and write to disk. * Finds a temperature characteristic and registers for data. * Prints timing information from discovered to connected to reading states. */ const noble = require('../index'); const fs = require('fs'); // the sensor value to scan for, number of bits and factor for displaying it const CHANNEL = process.env.CHANNEL ? process.env.CHANNEL : 'Temperature'; const BITS = process.env.BITS ? 1 * process.env.BITS : 16; const FACTOR = process.env.FACTOR ? 1.0 * process.env.FACTOR : 0.1; const EXT = '.dump'; noble.on('stateChange', function (state) { if (state === 'poweredOn') { noble.startScanning([], false); } else { noble.stopScanning(); } }); let tDisco = 0; // time when device was discovered let tConn = 0; // time when connection to device was established let tRead = 0; // time when reading data starts. // collect device meta-data into this object: const meta = { services: [], // stores an array of GATT service data objects characteristics: {} // a map with key service-UUID, stores the array of characteristics }; noble.on('discover', function (peripheral) { console.log( `peripheral discovered (${peripheral.id} with address <${peripheral.address}, ${peripheral.addressType}>, connectable ${peripheral.connectable}, RSSI ${peripheral.rssi}:` ); console.log('\thello my local name is:'); console.log(`\t\t${peripheral.advertisement.localName}`); console.log(); // connect to the first device with a valid name if (peripheral.advertisement.localName) { console.log( `Connecting to ${peripheral.address} ${peripheral.advertisement.localName}` ); tDisco = Date.now(); connectToDevice(peripheral); } }); const connectToDevice = function (peripheral) { // BLE cannot scan and connect in parallel, so we stop scanning here: noble.stopScanning(); peripheral.connect((error) => { // noble.startScanning([], true) if (error) { console.log(`Connect error: ${error}`); noble.startScanning([], true); return; } tConn = Date.now(); console.log('Connected!'); findServices(noble, peripheral); }); }; let servicesToRead = 0; const findServices = function (noble, peripheral) { meta.uuid = peripheral.uuid; meta.address = peripheral.address; meta.name = peripheral.advertisement.localName; // not needed but nice to have meta.characteristics = {}; // callback triggers with GATT-relevant data peripheral.on('servicesDiscovered', (peripheral, services) => { console.log(`servicesDiscovered: Found ${services.length} services! `); meta.services = services; for (const i in services) { const service = services[i]; console.log(`\tservice ${i} : ${JSON.stringify(service)}`); // meta.services[ service.uuid ] = service } }); peripheral.discoverServices([], (error, services) => { if (error) { console.error(error); return; } let sensorCharacteristic; servicesToRead = services.length; // we found the list of services, now trigger characteristics lookup for each of them: for (let i = 0; i < services.length; i++) { const service = services[i]; service.on('characteristicsDiscovered', (characteristics) => { // store the list of characteristics per service meta.characteristics[service.uuid] = characteristics; console.log(`SRV\t${service.uuid} characteristic GATT data: `); for (let i = 0; i < characteristics.length; i++) { console.log( `\t${service.uuid} chara.\t ${i} ${JSON.stringify( characteristics[i] )}` ); } }); service.discoverCharacteristics([], function (error, characteristics) { if (error) { console.error(error); return; } console.log(`SRV\t${service.uuid} characteristic decoded data: `); for (let j = 0; j < characteristics.length; j++) { const ch = characteristics[j]; console.log(`\t${service.uuid} chara.\t ${j} ${ch}`); if (ch.name === CHANNEL) { console.log(`found ${CHANNEL} characteristic!`); sensorCharacteristic = ch; } } servicesToRead--; if (!servicesToRead) { console.log('----------------- FINISHED'); console.log(JSON.stringify(meta, null, 4)); // write to file fs.writeFile( meta.uuid + EXT, JSON.stringify(meta, null, 2), function (err) { if (err) { return console.log(err); } console.log('The data was saved to ', meta.uuid + EXT); } ); if (sensorCharacteristic) { console.log('Listening for temperature data...'); tRead = Date.now(); sensorCharacteristic.on('data', (data) => { if (BITS === 16) { console.log(` new ${CHANNEL} ${data.readUInt16LE() * FACTOR}`); } else if (BITS === 32) { console.log(` new ${CHANNEL} ${data.readUInt32LE() * FACTOR}`); } else { console.log(` Cannot cope with BITS value ${BITS}`); } }); sensorCharacteristic.read(); } console.log( `Timespan from discovery to connected: ${tConn - tDisco} ms` ); console.log( `Timespan from connected to reading : ${tRead - tConn} ms` ); } }); } }); }; process.on('SIGINT', function () { console.log('Caught interrupt signal'); noble.stopScanning(() => process.exit()); }); process.on('SIGQUIT', function () { console.log('Caught interrupt signal'); noble.stopScanning(() => process.exit()); }); process.on('SIGTERM', function () { console.log('Caught interrupt signal'); noble.stopScanning(() => process.exit()); });