├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── lib ├── cc2540.js ├── cc2650.js ├── common.js └── sensortag.js ├── package.json ├── test-cc2650-io.js ├── test-continous-discover-connect.js ├── test-discover.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/ 16 | 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 1.3.0 2 | 3 | * Add support to CC2650 battery service ([@micahnyc](https://github.com/micahnyc)) 4 | 5 | # Version 1.2.3 6 | 7 | * CC2650 now also treats advertised service UUID ```aa80``` as device ([@julianoAffonso](https://github.com/julianoAffonso)) 8 | * use noble-device ^1.4.1 9 | 10 | # Version 1.2.2 11 | 12 | * revert "only set specific MPU9250 sensor bits with enabling/disabling" 13 | 14 | # Version 1.2.1 15 | 16 | * only set specific MPU9250 sensor bits with enabling/disabling 17 | * use 8G accelerometer mode and conversion formula (#62) 18 | * update MPU9250 formulas to match TI Android source code 19 | 20 | # Version 1.2.0 21 | 22 | * new CC2650 IO API's: ``readIoData``, ``writeIoData``, ``readIoConfig``, and ``writeIoConfig`` 23 | * add ``reedRelay`` arg to ``simpleKeyChange`` event for CC2650 24 | 25 | # Version 1.1.1 26 | 27 | * add missing ```discoverById``` API 28 | 29 | ## Version 1.1 30 | 31 | * use noble-device ^1.1.0 32 | * use id instead of uuid 33 | * new ```discoverByAddress``` and ```discoverById``` API's - ```discoverByUuid``` is deprecated now 34 | 35 | ## Version 1.0.2 36 | 37 | * Correct code for reading barometric pressure with firmware 0.89 or older ([@bChiquet](https://github.com/bChiquet)) 38 | 39 | ## Version 1.0.1 40 | 41 | * barometric sensor reading and luxometer conversion formula corrections ([@martin-doyle](https://github.com/martin-doyle)) 42 | 43 | ## Version 1.0 44 | 45 | * use noble-device instead of noble directly 46 | * add error parameters to callbacks 47 | * support for SensorTag 2.0 / CC2650 48 | * remove ```enableGyroscopeAxis``` API 49 | * new ```discoverAll```, ```stopDiscoverAll```, and ```discoverByUuid``` API's 50 | 51 | ## Older 52 | 53 | * Changes not recorded 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sandeep Mistry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-sensortag 2 | 3 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sandeepmistry/node-sensortag?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | 6 | Node.js lib for the [TI SensorTag](http://www.ti.com/tool/cc2541dk-sensor) and [TI CC2650 SensorTag](http://www.ti.com/tool/cc2650stk) 7 | 8 | ## Prerequisites 9 | 10 | * [node-gyp install guide](https://github.com/nodejs/node-gyp#installation) 11 | * [noble prerequisites](https://github.com/sandeepmistry/noble#prerequisites) 12 | 13 | **NOTE**: Certain API's may not be funcational if your SensorTag is running an older firmware version. You can use TI's iOS or Android apps to update the firmare. 14 | 15 | ## Install 16 | 17 | ```sh 18 | npm install sensortag 19 | ``` 20 | 21 | ## Examples 22 | 23 | See [test.js](test.js) or [sensorTag folder in Tom Igoe's BluetoothLE-Examples repo ](https://github.com/tigoe/BluetoothLE-Examples/tree/master/sensorTag) 24 | 25 | ## Usage 26 | 27 | ```javascript 28 | var SensorTag = require('sensortag'); 29 | ``` 30 | 31 | ### Discover 32 | 33 | #### One 34 | 35 | ```javascript 36 | SensorTag.discover(callback(sensorTag)); 37 | ``` 38 | 39 | #### All 40 | 41 | ```javascript 42 | function onDiscover(sensorTag) { 43 | // ... 44 | } 45 | 46 | SensorTag.discoverAll(onDiscover); 47 | 48 | SensorTag.stopDiscoverAll(onDiscover); 49 | ``` 50 | 51 | #### By id 52 | 53 | ```javascript 54 | SensorTag.discoverById(id, callback(sensorTag)); 55 | ``` 56 | 57 | #### By address 58 | 59 | ```javascript 60 | SensorTag.discoverByAddress(address, callback(sensorTag)); 61 | ``` 62 | 63 | #### Properties: 64 | 65 | ```javascript 66 | sensorTag = { 67 | id: "", 68 | type: "cc2540" | "cc2650" 69 | } 70 | ``` 71 | 72 | ### Connect and Set Up 73 | 74 | ```javascript 75 | sensorTag.connectAndSetUp(callback(error)); 76 | ``` 77 | 78 | ### Disconnect 79 | 80 | ```javascript 81 | sensorTag.disconnect(callback); 82 | ``` 83 | 84 | ### Disconnect event 85 | 86 | Add listener for when SensorTag is disconnected: 87 | 88 | ```javascript 89 | sensorTag.once('disconnect', callback); 90 | ``` 91 | 92 | ### Device Info 93 | 94 | ```javascript 95 | sensorTag.readDeviceName(callback(error, deviceName)); 96 | 97 | sensorTag.readSystemId(callback(error, systemId)); 98 | 99 | sensorTag.readSerialNumber(callback(error, serialNumber)); 100 | 101 | sensorTag.readFirmwareRevision(callback(error, firmwareRevision)); 102 | 103 | sensorTag.readHardwareRevision(callback(error, hardwareRevision)); 104 | 105 | sensorTag.readSoftwareRevision(callback(error, softwareRevision)); 106 | 107 | sensorTag.readManufacturerName(callback(error, manufacturerName)); 108 | ``` 109 | 110 | ### IR Temperature Sensor 111 | 112 | #### Enable/disable 113 | 114 | ```javascript 115 | sensorTag.enableIrTemperature(callback(error)); 116 | 117 | sensorTag.disableIrTemperature(callback(error)); 118 | 119 | sensorTag.setIrTemperaturePeriod(period, callback(error)); // period min 300ms, default period is 1000 ms 120 | ``` 121 | 122 | #### Read 123 | 124 | ```javascript 125 | sensorTag.readIrTemperature(callback(error, objectTemperature, ambientTemperature)); 126 | ``` 127 | 128 | #### Notify/Unnotify 129 | 130 | ```javascript 131 | sensorTag.notifyIrTemperature(callback(error)); 132 | 133 | sensorTag.unnotifyIrTemperature(callback(error)); 134 | 135 | sensorTag.on('irTemperatureChange', callback(objectTemperature, ambientTemperature)); 136 | ``` 137 | 138 | ### Accelerometer 139 | 140 | #### Enable/disable/configure 141 | 142 | ```javascript 143 | sensorTag.enableAccelerometer(callback(error)); 144 | 145 | sensorTag.disableAccelerometer(callback(error)); 146 | 147 | // CC2540: period 1 - 2550 ms, default period is 2000 ms 148 | // CC2650: period 100 - 2550 ms, default period is 1000 ms 149 | sensorTag.setAccelerometerPeriod(period, callback(error)); 150 | ``` 151 | 152 | #### Read 153 | 154 | ```javascript 155 | sensorTag.readAccelerometer(callback(error, x, y, z)); 156 | ``` 157 | 158 | #### Notify/Unnotify 159 | 160 | ```javascript 161 | sensorTag.notifyAccelerometer(callback(error)); 162 | 163 | sensorTag.unnotifyAccelerometer(callback(error)); 164 | 165 | sensorTag.on('accelerometerChange', callback(x, y, z)); 166 | ``` 167 | 168 | ### Humidity Sensor 169 | 170 | #### Enable/disable 171 | 172 | ```javascript 173 | sensorTag.enableHumidity(callback(error)); 174 | 175 | sensorTag.disableHumidity(callback(error)); 176 | 177 | sensorTag.setHumidityPeriod(period, callback(error)); 178 | ``` 179 | 180 | #### Read 181 | 182 | ```javascript 183 | sensorTag.readHumidity(callback(error, temperature, humidity)); 184 | ``` 185 | 186 | #### Notify/Unnotify 187 | 188 | ```javascript 189 | sensorTag.notifyHumidity(callback(error)); 190 | 191 | sensorTag.unnotifyHumidity(callback(error)); 192 | 193 | sensorTag.on('humidityChange', callback(temperature, humidity)); 194 | ``` 195 | 196 | ### Magnetometer 197 | 198 | #### Enable/disable 199 | 200 | ```javascript 201 | sensorTag.enableMagnetometer(callback(error)); 202 | 203 | sensorTag.disableMagnetometer(callback(error)); 204 | 205 | // CC2540: period 1 - 2550 ms, default period is 2000 ms 206 | // CC2650: period 100 - 2550 ms, default period is 1000 ms 207 | sensorTag.setMagnetometerPeriod(period, callback(error)); 208 | ``` 209 | 210 | #### Read 211 | 212 | ```javascript 213 | sensorTag.readMagnetometer(callback(error, x, y, z)); 214 | ``` 215 | 216 | #### Notify/Unnotify 217 | 218 | ```javascript 219 | sensorTag.notifyMagnetometer(callback(error)); 220 | 221 | sensorTag.unnotifyMagnetometer(callback(error)); 222 | 223 | sensorTag.on('magnetometerChange', callback(x, y, z)); 224 | ``` 225 | 226 | ### Barometric Pressure Sensor 227 | 228 | #### Enable/disable 229 | 230 | ```javascript 231 | sensorTag.enableBarometricPressure(callback(error)); 232 | 233 | sensorTag.disableBarometricPressure(callback(error)); 234 | 235 | sensorTag.setBarometricPressurePeriod(period, callback(error)); // period 100 - 2550 ms 236 | ``` 237 | 238 | #### Read 239 | 240 | ```javascript 241 | sensorTag.readBarometricPressure(callback(error, pressure)); 242 | ``` 243 | 244 | #### Notify/Unnotify 245 | 246 | ```javascript 247 | sensorTag.notifyBarometricPressure(callback(error)); 248 | 249 | sensorTag.unnotifyBarometricPressure(callback(error)); 250 | 251 | sensorTag.on('barometricPressureChange', callback(pressure)); 252 | ``` 253 | 254 | ### Gyroscope 255 | 256 | #### Enable/disable/configure 257 | 258 | ```javascript 259 | sensorTag.enableGyroscope(callback(error)); 260 | 261 | sensorTag.disableGyroscope(callback(error)); 262 | 263 | // period 100 - 2550 ms, default period is 1000 ms 264 | sensorTag.setGyroscopePeriod(period, callback(error)); 265 | ``` 266 | 267 | #### Read 268 | 269 | ```javascript 270 | sensorTag.readGyroscope(callback(error, x, y, z)); 271 | ``` 272 | 273 | #### Notify/Unnotify 274 | 275 | ```javascript 276 | sensorTag.notifyGyroscope(callback(error)); 277 | 278 | sensorTag.unnotifyGyroscope(callback(error)); 279 | 280 | sensorTag.on('gyroscopeChange', callback(x, y, z)); 281 | ``` 282 | 283 | ### IO (CC2650 only) 284 | 285 | #### Data read/write 286 | 287 | ```javascript 288 | sensorTag.readIoData(callback(error, value)); 289 | sensorTag.writeIoData(value, callback(error)); 290 | ``` 291 | 292 | #### Config read/write 293 | 294 | ```javascript 295 | sensorTag.readIoConfig(callback(error, value)); 296 | sensorTag.writeIoConfig(value, callback(error)); 297 | ``` 298 | 299 | ### Luxometer (CC2650 only) 300 | 301 | #### Enable/disable/configure 302 | 303 | ```javascript 304 | sensorTag.enableLuxometer(callback(error)); 305 | 306 | sensorTag.disableLuxometer(callback(error)); 307 | 308 | sensorTag.setLuxometerPeriod(period, callback(error)); 309 | ``` 310 | 311 | #### Read 312 | 313 | ```javascript 314 | sensorTag.readLuxometer(callback(error, lux)); 315 | ``` 316 | 317 | #### Notify/Unnotify 318 | 319 | ```javascript 320 | sensorTag.notifyLuxometer(callback(error)); 321 | 322 | sensorTag.unnotifyLuxometer(callback(error)); 323 | 324 | sensorTag.on('luxometerChange', callback(lux)); 325 | ``` 326 | 327 | ### Battery Level (CC2650 only) 328 | 329 | #### Read 330 | 331 | ```javascript 332 | sensorTag.readBatteryLevel(callback(error, batteryLevel)); 333 | ``` 334 | 335 | ### Simple Key 336 | 337 | #### Notify/Unnotify 338 | 339 | ```javascript 340 | sensorTag.notifySimpleKey(callback(error)); 341 | 342 | sensorTag.unnotifySimpleKey(callback(error)); 343 | ``` 344 | 345 | CC2540: 346 | 347 | ```javascript 348 | sensorTag.on('simpleKeyChange', callback(left, right)); 349 | ``` 350 | 351 | CC2650: 352 | 353 | ```javascript 354 | sensorTag.on('simpleKeyChange', callback(left, right, reedRelay)); 355 | ``` 356 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/sensortag'); 2 | -------------------------------------------------------------------------------- /lib/cc2540.js: -------------------------------------------------------------------------------- 1 | var NobleDevice = require('noble-device'); 2 | 3 | var Common = require('./common'); 4 | 5 | var ACCELEROMETER_UUID = 'f000aa1004514000b000000000000000'; 6 | var MAGNETOMETER_UUID = 'f000aa3004514000b000000000000000'; 7 | var GYROSCOPE_UUID = 'f000aa5004514000b000000000000000'; 8 | var BAROMETRIC_PRESSURE_UUID = 'f000aa4004514000b000000000000000'; 9 | var TEST_UUID = 'f000aa6004514000b000000000000000'; 10 | var OAD_UUID = 'f000ffc004514000b000000000000000'; 11 | 12 | var ACCELEROMETER_CONFIG_UUID = 'f000aa1204514000b000000000000000'; 13 | var ACCELEROMETER_DATA_UUID = 'f000aa1104514000b000000000000000'; 14 | var ACCELEROMETER_PERIOD_UUID = 'f000aa1304514000b000000000000000'; 15 | 16 | var MAGNETOMETER_CONFIG_UUID = 'f000aa3204514000b000000000000000'; 17 | var MAGNETOMETER_DATA_UUID = 'f000aa3104514000b000000000000000'; 18 | var MAGNETOMETER_PERIOD_UUID = 'f000aa3304514000b000000000000000'; 19 | 20 | var BAROMETRIC_PRESSURE_CONFIG_UUID = 'f000aa4204514000b000000000000000'; 21 | var BAROMETRIC_PRESSURE_CALIBRATION_UUID = 'f000aa4304514000b000000000000000'; 22 | 23 | var GYROSCOPE_CONFIG_UUID = 'f000aa5204514000b000000000000000'; 24 | var GYROSCOPE_DATA_UUID = 'f000aa5104514000b000000000000000'; 25 | var GYROSCOPE_PERIOD_UUID = 'f000aa5304514000b000000000000000'; 26 | 27 | var TEST_DATA_UUID = 'f000aa6104514000b000000000000000'; 28 | var TEST_CONFIGURATION_UUID = 'f000aa6204514000b000000000000000'; 29 | 30 | var CC2540SensorTag = function(peripheral) { 31 | NobleDevice.call(this, peripheral); 32 | Common.call(this); 33 | 34 | this.type = 'cc2540'; 35 | 36 | this.onAccelerometerChangeBinded = this.onAccelerometerChange.bind(this); 37 | this.onMagnetometerChangeBinded = this.onMagnetometerChange.bind(this); 38 | this.onGyroscopeChangeBinded = this.onGyroscopeChange.bind(this); 39 | }; 40 | 41 | CC2540SensorTag.is = function(peripheral) { 42 | var localName = peripheral.advertisement.localName; 43 | 44 | return (localName === 'SensorTag') || 45 | (localName === 'TI BLE Sensor Tag'); 46 | }; 47 | 48 | NobleDevice.Util.inherits(CC2540SensorTag, NobleDevice); 49 | NobleDevice.Util.mixin(CC2540SensorTag, NobleDevice.DeviceInformationService); 50 | NobleDevice.Util.mixin(CC2540SensorTag, Common); 51 | 52 | CC2540SensorTag.prototype.convertIrTemperatureData = function(data, callback) { 53 | // For computation refer : http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#IR_Temperature_Sensor 54 | 55 | var ambientTemperature = data.readInt16LE(2) / 128.0; 56 | 57 | var Vobj2 = data.readInt16LE(0) * 0.00000015625; 58 | var Tdie2 = ambientTemperature + 273.15; 59 | var S0 = 5.593 * Math.pow(10, -14); 60 | var a1 = 1.75 * Math.pow(10 , -3); 61 | var a2 = -1.678 * Math.pow(10, -5); 62 | var b0 = -2.94 * Math.pow(10, -5); 63 | var b1 = -5.7 * Math.pow(10, -7); 64 | var b2 = 4.63 * Math.pow(10, -9); 65 | var c2 = 13.4; 66 | var Tref = 298.15; 67 | var S = S0 * (1 + a1 * (Tdie2 - Tref) + a2 * Math.pow((Tdie2 - Tref), 2)); 68 | var Vos = b0 + b1 * (Tdie2 - Tref) + b2 * Math.pow((Tdie2 - Tref), 2); 69 | var fObj = (Vobj2 - Vos) + c2 * Math.pow((Vobj2 - Vos), 2); 70 | var objectTemperature = Math.pow(Math.pow(Tdie2, 4) + (fObj/S), 0.25); 71 | objectTemperature = (objectTemperature - 273.15); 72 | 73 | callback(objectTemperature, ambientTemperature); 74 | }; 75 | 76 | CC2540SensorTag.prototype.convertHumidityData = function(data, callback) { 77 | var temperature = -46.85 + 175.72 / 65536.0 * data.readUInt16LE(0); 78 | var humidity = -6.0 + 125.0 / 65536.0 * (data.readUInt16LE(2) & ~0x0003); 79 | 80 | callback(temperature, humidity); 81 | }; 82 | 83 | CC2540SensorTag.prototype.enableBarometricPressure = function(callback) { 84 | this.writeUInt8Characteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, 0x02, function(error) { 85 | if (error) { 86 | return callback(error); 87 | } 88 | 89 | this.readDataCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CALIBRATION_UUID, function(error, data) { 90 | if (error) { 91 | return callback(error); 92 | } 93 | 94 | this._barometricPressureCalibrationData = data; 95 | 96 | this.enableConfigCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, callback); 97 | }.bind(this)); 98 | }.bind(this)); 99 | }; 100 | 101 | CC2540SensorTag.prototype.convertBarometricPressureData = function(data, callback) { 102 | 103 | // For computation refer : http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Barometric_Pressure_Sensor_2 104 | var temp; // Temperature raw value from sensor 105 | var pressure; // Pressure raw value from sensor 106 | var S; // Interim value in calculation 107 | var O; // Interim value in calculation 108 | var p_a; // Pressure actual value in unit Pascal. 109 | var Pa; // Computed value of the function 110 | 111 | var c0 = this._barometricPressureCalibrationData.readUInt16LE(0); 112 | var c1 = this._barometricPressureCalibrationData.readUInt16LE(2); 113 | var c2 = this._barometricPressureCalibrationData.readUInt16LE(4); 114 | var c3 = this._barometricPressureCalibrationData.readUInt16LE(6); 115 | 116 | var c4 = this._barometricPressureCalibrationData.readInt16LE(8); 117 | var c5 = this._barometricPressureCalibrationData.readInt16LE(10); 118 | var c6 = this._barometricPressureCalibrationData.readInt16LE(12); 119 | var c7 = this._barometricPressureCalibrationData.readInt16LE(14); 120 | 121 | temp = data.readInt16LE(0); 122 | pressure = data.readUInt16LE(2); 123 | 124 | S = c2 + ((c3 * temp)/ 131072.0) + ((c4 * (temp * temp)) / 17179869184.0); 125 | O = (c5 * 16384.0) + (((c6 * temp) / 8)) + ((c7 * (temp * temp)) / 524288.0); 126 | Pa = (((S * pressure) + O) / 16384.0); 127 | 128 | Pa /= 100.0; 129 | 130 | callback(Pa); 131 | }; 132 | 133 | CC2540SensorTag.prototype.enableAccelerometer = function(callback) { 134 | this.enableConfigCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_CONFIG_UUID, callback); 135 | }; 136 | 137 | CC2540SensorTag.prototype.disableAccelerometer = function(callback) { 138 | this.disableConfigCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_CONFIG_UUID, callback); 139 | }; 140 | 141 | CC2540SensorTag.prototype.readAccelerometer = function(callback) { 142 | this.readDataCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_DATA_UUID, function(error, data) { 143 | if (error) { 144 | return callback(error); 145 | } 146 | 147 | this.convertAccelerometerData(data, function(x, y, z) { 148 | callback(null, x, y, z); 149 | }.bind(this)); 150 | }.bind(this)); 151 | }; 152 | 153 | CC2540SensorTag.prototype.onAccelerometerChange = function(data) { 154 | this.convertAccelerometerData(data, function(x, y, z) { 155 | this.emit('accelerometerChange', x, y, z); 156 | }.bind(this)); 157 | }; 158 | 159 | CC2540SensorTag.prototype.convertAccelerometerData = function(data, callback) { 160 | var x = data.readInt8(0) / 16.0; 161 | var y = data.readInt8(1) / 16.0; 162 | var z = data.readInt8(2) / 16.0; 163 | 164 | callback(x, y, z); 165 | }; 166 | 167 | CC2540SensorTag.prototype.notifyAccelerometer = function(callback) { 168 | this.notifyCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_DATA_UUID, true, this.onAccelerometerChangeBinded, callback); 169 | }; 170 | 171 | CC2540SensorTag.prototype.unnotifyAccelerometer = function(callback) { 172 | this.notifyCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_DATA_UUID, false, this.onAccelerometerChangeBinded, callback); 173 | }; 174 | 175 | CC2540SensorTag.prototype.setAccelerometerPeriod = function(period, callback) { 176 | this.writePeriodCharacteristic(ACCELEROMETER_UUID, ACCELEROMETER_PERIOD_UUID, period, callback); 177 | }; 178 | 179 | CC2540SensorTag.prototype.enableMagnetometer = function(callback) { 180 | this.enableConfigCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_CONFIG_UUID, callback); 181 | }; 182 | 183 | CC2540SensorTag.prototype.disableMagnetometer = function(callback) { 184 | this.disableConfigCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_CONFIG_UUID, callback); 185 | }; 186 | 187 | CC2540SensorTag.prototype.readMagnetometer = function(callback) { 188 | this.readDataCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_DATA_UUID, function(error, data) { 189 | if (error) { 190 | return callback(error); 191 | } 192 | 193 | this.convertMagnetometerData(data, function(x, y, z) { 194 | callback(null, x, y, z); 195 | }.bind(this)); 196 | }.bind(this)); 197 | }; 198 | 199 | CC2540SensorTag.prototype.onMagnetometerChange = function(data) { 200 | this.convertMagnetometerData(data, function(x, y, z) { 201 | this.emit('magnetometerChange', x, y, z); 202 | }.bind(this)); 203 | }; 204 | 205 | CC2540SensorTag.prototype.convertMagnetometerData = function(data, callback) { 206 | var x = data.readInt16LE(0) * 2000.0 / 65536.0; 207 | var y = data.readInt16LE(2) * 2000.0 / 65536.0; 208 | var z = data.readInt16LE(4) * 2000.0 / 65536.0; 209 | 210 | callback(x, y, z); 211 | }; 212 | 213 | CC2540SensorTag.prototype.notifyMagnetometer = function(callback) { 214 | this.notifyCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_DATA_UUID, true, this.onMagnetometerChangeBinded, callback); 215 | }; 216 | 217 | CC2540SensorTag.prototype.unnotifyMagnetometer = function(callback) { 218 | this.notifyCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_DATA_UUID, false, this.onMagnetometerChangeBinded, callback); 219 | }; 220 | 221 | CC2540SensorTag.prototype.setMagnetometerPeriod = function(period, callback) { 222 | this.writePeriodCharacteristic(MAGNETOMETER_UUID, MAGNETOMETER_PERIOD_UUID, period, callback); 223 | }; 224 | 225 | CC2540SensorTag.prototype.setGyroscopePeriod = function(period, callback) { 226 | this.writePeriodCharacteristic(GYROSCOPE_UUID, GYROSCOPE_PERIOD_UUID, period, callback); 227 | }; 228 | 229 | CC2540SensorTag.prototype.enableGyroscope = function(callback) { 230 | this.writeUInt8Characteristic(GYROSCOPE_UUID, GYROSCOPE_CONFIG_UUID, 0x07, callback); 231 | }; 232 | 233 | CC2540SensorTag.prototype.disableGyroscope = function(callback) { 234 | this.disableConfigCharacteristic(GYROSCOPE_UUID, GYROSCOPE_CONFIG_UUID, callback); 235 | }; 236 | 237 | CC2540SensorTag.prototype.readGyroscope = function(callback) { 238 | this.readDataCharacteristic(GYROSCOPE_UUID, GYROSCOPE_DATA_UUID, function(error, data) { 239 | if (error) { 240 | return callback(error); 241 | } 242 | 243 | this.convertGyroscopeData(data, function(x, y, z) { 244 | callback(null, x, y, z); 245 | }.bind(this)); 246 | }.bind(this)); 247 | }; 248 | 249 | CC2540SensorTag.prototype.onGyroscopeChange = function(data) { 250 | this.convertGyroscopeData(data, function(x, y, z) { 251 | this.emit('gyroscopeChange', x, y, z); 252 | }.bind(this)); 253 | }; 254 | 255 | CC2540SensorTag.prototype.convertGyroscopeData = function(data, callback) { 256 | var x = data.readInt16LE(0) * (500.0 / 65536.0) * -1; 257 | var y = data.readInt16LE(2) * (500.0 / 65536.0); 258 | var z = data.readInt16LE(4) * (500.0 / 65536.0); 259 | 260 | callback(x, y, z); 261 | }; 262 | 263 | CC2540SensorTag.prototype.notifyGyroscope = function(callback) { 264 | this.notifyCharacteristic(GYROSCOPE_UUID, GYROSCOPE_DATA_UUID, true, this.onGyroscopeChangeBinded, callback); 265 | }; 266 | 267 | CC2540SensorTag.prototype.unnotifyGyroscope = function(callback) { 268 | this.notifyCharacteristic(GYROSCOPE_UUID, GYROSCOPE_DATA_UUID, false, this.onGyroscopeChangeBinded, callback); 269 | }; 270 | 271 | CC2540SensorTag.prototype.readTestData = function(callback) { 272 | this.readUInt16LECharacteristic(TEST_UUID, TEST_DATA_UUID, callback); 273 | }; 274 | 275 | CC2540SensorTag.prototype.readTestConfiguration = function(callback) { 276 | this.readUInt8Characteristic(TEST_UUID, TEST_CONFIGURATION_UUID, callback); 277 | }; 278 | 279 | module.exports = CC2540SensorTag; 280 | -------------------------------------------------------------------------------- /lib/cc2650.js: -------------------------------------------------------------------------------- 1 | // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide 2 | 3 | var NobleDevice = require('noble-device'); 4 | 5 | var Common = require('./common'); 6 | 7 | var MPU9250_UUID = 'f000aa8004514000b000000000000000'; 8 | var BAROMETRIC_PRESSURE_UUID = 'f000aa4004514000b000000000000000'; 9 | var IO_UUID = 'f000aa6404514000b000000000000000'; 10 | var LUXOMETER_UUID = 'f000aa7004514000b000000000000000'; 11 | 12 | var BAROMETRIC_PRESSURE_CONFIG_UUID = 'f000aa4204514000b000000000000000'; 13 | 14 | var MPU9250_CONFIG_UUID = 'f000aa8204514000b000000000000000'; 15 | var MPU9250_DATA_UUID = 'f000aa8104514000b000000000000000'; 16 | var MPU9250_PERIOD_UUID = 'f000aa8304514000b000000000000000'; 17 | 18 | var MPU9250_GYROSCOPE_MASK = 0x0007; 19 | var MPU9250_ACCELEROMETER_MASK = 0x0238; 20 | var MPU9250_MAGNETOMETER_MASK = 0x0040; 21 | 22 | var IO_DATA_UUID = 'f000aa6504514000b000000000000000'; 23 | var IO_CONFIG_UUID = 'f000aa6604514000b000000000000000'; 24 | 25 | var LUXOMETER_CONFIG_UUID = 'f000aa7204514000b000000000000000'; 26 | var LUXOMETER_DATA_UUID = 'f000aa7104514000b000000000000000'; 27 | var LUXOMETER_PERIOD_UUID = 'f000aa7304514000b000000000000000'; 28 | 29 | var CC2650SensorTag = function(peripheral) { 30 | NobleDevice.call(this, peripheral); 31 | Common.call(this); 32 | 33 | this.type = 'cc2650'; 34 | this.mpu9250mask = 0; 35 | this.mpu9250notifyCount = 0; 36 | 37 | this.onMPU9250ChangeBinded = this.onMPU9250Change.bind(this); 38 | this.onLuxometerChangeBinded = this.onLuxometerChange.bind(this); 39 | }; 40 | 41 | CC2650SensorTag.is = function(peripheral) { 42 | var localName = peripheral.advertisement.localName; 43 | var serviceUuids = peripheral.advertisement.serviceUuids; 44 | 45 | return (localName === 'CC2650 SensorTag') || (localName === 'SensorTag 2.0') || (serviceUuids.indexOf('aa80') > -1 ); 46 | }; 47 | 48 | NobleDevice.Util.inherits(CC2650SensorTag, NobleDevice); 49 | NobleDevice.Util.mixin(CC2650SensorTag, NobleDevice.DeviceInformationService); 50 | NobleDevice.Util.mixin(CC2650SensorTag, Common); 51 | NobleDevice.Util.mixin(CC2650SensorTag, NobleDevice.BatteryService); 52 | 53 | 54 | CC2650SensorTag.prototype.convertIrTemperatureData = function(data, callback) { 55 | var ambientTemperature = data.readInt16LE(2) / 128.0; 56 | var objectTemperature = data.readInt16LE(0) / 128.0; 57 | 58 | callback(objectTemperature, ambientTemperature); 59 | }; 60 | 61 | CC2650SensorTag.prototype.convertHumidityData = function(data, callback) { 62 | var temperature = -40 + ((165 * data.readUInt16LE(0)) / 65536.0); 63 | var humidity = data.readUInt16LE(2) * 100 / 65536.0; 64 | 65 | callback(temperature, humidity); 66 | }; 67 | 68 | CC2650SensorTag.prototype.enableBarometricPressure = function(callback) { 69 | this.enableConfigCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, callback); 70 | }; 71 | 72 | CC2650SensorTag.prototype.convertBarometricPressureData = function(data, callback) { 73 | // data is returned as 74 | // Firmare 0.89 16 bit single precision float 75 | // Firmare 1.01 24 bit single precision float 76 | 77 | var flTempBMP; 78 | var flPressure; 79 | 80 | if (data.length > 4) { 81 | // Firmware 1.01 82 | 83 | flTempBMP = (data.readUInt32LE(0) & 0x00ffffff)/ 100.0; 84 | flPressure = ((data.readUInt32LE(2) >> 8) & 0x00ffffff) / 100.0; 85 | } else { 86 | // Firmware 0.89 87 | 88 | var tempBMP = data.readUInt16LE(0); 89 | var tempExponent = (tempBMP & 0xF000) >> 12; 90 | var tempMantissa = (tempBMP & 0x0FFF); 91 | flTempBMP = tempMantissa * Math.pow(2, tempExponent) / 100.0; 92 | 93 | var tempPressure = data.readUInt16LE(2); 94 | var pressureExponent = (tempPressure & 0xF000) >> 12; 95 | var pressureMantissa = (tempPressure & 0x0FFF); 96 | flPressure = pressureMantissa * Math.pow(2, pressureExponent) / 100.0; 97 | } 98 | 99 | callback(flPressure); 100 | }; 101 | 102 | CC2650SensorTag.prototype.setMPU9250Period = function(period, callback) { 103 | this.writePeriodCharacteristic(MPU9250_UUID, MPU9250_PERIOD_UUID, period, callback); 104 | }; 105 | 106 | CC2650SensorTag.prototype.enableMPU9250 = function(mask, callback) { 107 | this.mpu9250mask |= mask; 108 | 109 | // for now, always write 0x007f, magnetometer does not seem to notify is specific mask is used 110 | this.writeUInt16LECharacteristic(MPU9250_UUID, MPU9250_CONFIG_UUID, 0x007f, callback); 111 | }; 112 | 113 | CC2650SensorTag.prototype.disableMPU9250 = function(mask, callback) { 114 | this.mpu9250mask &= ~mask; 115 | 116 | if (this.mpu9250mask === 0) { 117 | this.writeUInt16LECharacteristic(MPU9250_UUID, MPU9250_CONFIG_UUID, 0x0000, callback); 118 | } else if (typeof(callback) === 'function') { 119 | callback(); 120 | } 121 | }; 122 | 123 | CC2650SensorTag.prototype.notifyMPU9250 = function(callback) { 124 | this.mpu9250notifyCount++; 125 | 126 | if (this.mpu9250notifyCount === 1) { 127 | this.notifyCharacteristic(MPU9250_UUID, MPU9250_DATA_UUID, true, this.onMPU9250ChangeBinded, callback); 128 | } else if (typeof(callback) === 'function') { 129 | callback(); 130 | } 131 | }; 132 | 133 | CC2650SensorTag.prototype.unnotifyMPU9250 = function(callback) { 134 | this.mpu9250notifyCount--; 135 | 136 | if (this.mpu9250notifyCount === 0) { 137 | this.notifyCharacteristic(MPU9250_UUID, MPU9250_DATA_UUID, false, this.onMPU9250ChangeBinded, callback); 138 | } else if (typeof(callback) === 'function') { 139 | callback(); 140 | } 141 | }; 142 | 143 | CC2650SensorTag.prototype.enableAccelerometer = function(callback) { 144 | this.enableMPU9250(MPU9250_ACCELEROMETER_MASK, callback); 145 | }; 146 | 147 | CC2650SensorTag.prototype.disableAccelerometer = function(callback) { 148 | this.disableMPU9250(MPU9250_ACCELEROMETER_MASK, callback); 149 | }; 150 | 151 | CC2650SensorTag.prototype.readAccelerometer = function(callback) { 152 | this.readDataCharacteristic(MPU9250_UUID, MPU9250_DATA_UUID, function(error, data) { 153 | if (error) { 154 | return callback(error); 155 | } 156 | 157 | this.convertMPU9250Data(data, function(x, y, z) { 158 | callback(null, x, y, z); 159 | }.bind(this)); 160 | }.bind(this)); 161 | }; 162 | 163 | CC2650SensorTag.prototype.onMPU9250Change = function(data) { 164 | this.convertMPU9250Data(data, function(x, y, z, xG, yG, zG, xM, yM, zM) { 165 | if (this.mpu9250mask & MPU9250_ACCELEROMETER_MASK) { 166 | this.emit('accelerometerChange', x, y, z); 167 | } 168 | 169 | if (this.mpu9250mask & MPU9250_GYROSCOPE_MASK) { 170 | this.emit('gyroscopeChange', xG, yG, zG); 171 | } 172 | 173 | if (this.mpu9250mask & MPU9250_MAGNETOMETER_MASK) { 174 | this.emit('magnetometerChange', xM, yM, zM); 175 | } 176 | }.bind(this)); 177 | }; 178 | 179 | CC2650SensorTag.prototype.convertMPU9250Data = function(data, callback) { 180 | // 250 deg/s range 181 | var xG = data.readInt16LE(0) / 128.0; 182 | var yG = data.readInt16LE(2) / 128.0; 183 | var zG = data.readInt16LE(4) / 128.0; 184 | 185 | // we specify 8G range in setup 186 | var x = data.readInt16LE(6) / 4096.0; 187 | var y = data.readInt16LE(8) / 4096.0; 188 | var z = data.readInt16LE(10) / 4096.0; 189 | 190 | // magnetometer (page 50 of http://www.invensense.com/mems/gyro/documents/RM-MPU-9250A-00.pdf) 191 | var xM = data.readInt16LE(12) * 4912.0 / 32768.0; 192 | var yM = data.readInt16LE(14) * 4912.0 / 32768.0; 193 | var zM = data.readInt16LE(16) * 4912.0 / 32768.0; 194 | 195 | callback(x, y, z, xG, yG, zG, xM, yM, zM); 196 | }; 197 | 198 | CC2650SensorTag.prototype.notifyAccelerometer = function(callback) { 199 | this.notifyMPU9250(callback); 200 | }; 201 | 202 | CC2650SensorTag.prototype.unnotifyAccelerometer = function(callback) { 203 | this.unnotifyMPU9250(callback); 204 | }; 205 | 206 | CC2650SensorTag.prototype.setAccelerometerPeriod = function(period, callback) { 207 | this.setMPU9250Period(period, callback); 208 | }; 209 | 210 | CC2650SensorTag.prototype.enableMagnetometer = function(callback) { 211 | this.enableMPU9250(MPU9250_MAGNETOMETER_MASK, callback); 212 | }; 213 | 214 | CC2650SensorTag.prototype.disableMagnetometer = function(callback) { 215 | this.disableMPU9250(MPU9250_MAGNETOMETER_MASK, callback); 216 | }; 217 | 218 | CC2650SensorTag.prototype.readMagnetometer = function(callback) { 219 | this.readDataCharacteristic(MPU9250_UUID, MPU9250_DATA_UUID, function(error, data) { 220 | if (error) { 221 | return callback(error); 222 | } 223 | 224 | this.convertMPU9250Data(data, function(x, y, z, xG, yG, zG, xM, yM, zM) { 225 | callback(null, xM, yM, zM); 226 | }.bind(this)); 227 | }.bind(this)); 228 | }; 229 | 230 | CC2650SensorTag.prototype.notifyMagnetometer = function(callback) { 231 | this.notifyMPU9250(callback); 232 | }; 233 | 234 | CC2650SensorTag.prototype.unnotifyMagnetometer = function(callback) { 235 | this.unnotifyMPU9250(callback); 236 | }; 237 | 238 | CC2650SensorTag.prototype.setMagnetometerPeriod = function(period, callback) { 239 | this.setMPU9250Period(period, callback); 240 | }; 241 | 242 | CC2650SensorTag.prototype.setGyroscopePeriod = function(period, callback) { 243 | this.setMPU9250Period(period, callback); 244 | }; 245 | 246 | CC2650SensorTag.prototype.enableGyroscope = function(callback) { 247 | this.enableMPU9250(MPU9250_GYROSCOPE_MASK, callback); 248 | }; 249 | 250 | CC2650SensorTag.prototype.disableGyroscope = function(callback) { 251 | this.disableMPU9250(MPU9250_GYROSCOPE_MASK, callback); 252 | }; 253 | 254 | CC2650SensorTag.prototype.readGyroscope = function(callback) { 255 | this.readDataCharacteristic(MPU9250_UUID, MPU9250_DATA_UUID, function(error, data) { 256 | if (error) { 257 | return callback(error); 258 | } 259 | 260 | this.convertMPU9250Data(data, function(x, y, z, xG, yG, zG) { 261 | callback(null, xG, yG, zG); 262 | }.bind(this)); 263 | }.bind(this)); 264 | }; 265 | 266 | CC2650SensorTag.prototype.notifyGyroscope = function(callback) { 267 | this.notifyMPU9250(callback); 268 | }; 269 | 270 | CC2650SensorTag.prototype.unnotifyGyroscope = function(callback) { 271 | this.unnotifyMPU9250(callback); 272 | }; 273 | 274 | CC2650SensorTag.prototype.readIoData = function(callback) { 275 | this.readUInt8Characteristic(IO_UUID, IO_DATA_UUID, callback); 276 | }; 277 | 278 | CC2650SensorTag.prototype.writeIoData = function(value, callback) { 279 | this.writeUInt8Characteristic(IO_UUID, IO_DATA_UUID, value, callback); 280 | }; 281 | 282 | CC2650SensorTag.prototype.readIoConfig = function(callback) { 283 | this.readUInt8Characteristic(IO_UUID, IO_CONFIG_UUID, callback); 284 | }; 285 | 286 | CC2650SensorTag.prototype.writeIoConfig = function(value, callback) { 287 | this.writeUInt8Characteristic(IO_UUID, IO_CONFIG_UUID, value, callback); 288 | }; 289 | 290 | CC2650SensorTag.prototype.enableLuxometer = function(callback) { 291 | this.enableConfigCharacteristic(LUXOMETER_UUID, LUXOMETER_CONFIG_UUID, callback); 292 | }; 293 | 294 | CC2650SensorTag.prototype.disableLuxometer = function(callback) { 295 | this.disableConfigCharacteristic(LUXOMETER_UUID, LUXOMETER_CONFIG_UUID, callback); 296 | }; 297 | 298 | CC2650SensorTag.prototype.readLuxometer = function(callback) { 299 | this.readDataCharacteristic(LUXOMETER_UUID, LUXOMETER_DATA_UUID, function(error, data) { 300 | if (error) { 301 | return callback(error); 302 | } 303 | 304 | this.convertLuxometerData(data, function(lux) { 305 | callback(null, lux); 306 | }.bind(this)); 307 | }.bind(this)); 308 | }; 309 | 310 | CC2650SensorTag.prototype.onLuxometerChange = function(data) { 311 | this.convertLuxometerData(data, function(lux) { 312 | this.emit('luxometerChange', lux); 313 | }.bind(this)); 314 | }; 315 | 316 | CC2650SensorTag.prototype.convertLuxometerData = function(data, callback) { 317 | var rawLux = data.readUInt16LE(0); 318 | 319 | var exponent = (rawLux & 0xF000) >> 12; 320 | var mantissa = (rawLux & 0x0FFF); 321 | 322 | var flLux = mantissa * Math.pow(2, exponent) / 100.0; 323 | 324 | callback(flLux); 325 | }; 326 | 327 | CC2650SensorTag.prototype.notifyLuxometer = function(callback) { 328 | this.notifyCharacteristic(LUXOMETER_UUID, LUXOMETER_DATA_UUID, true, this.onLuxometerChangeBinded, callback); 329 | }; 330 | 331 | CC2650SensorTag.prototype.unnotifyLuxometer = function(callback) { 332 | this.notifyCharacteristic(LUXOMETER_UUID, LUXOMETER_DATA_UUID, false, this.onLuxometerChangeBinded, callback); 333 | }; 334 | 335 | CC2650SensorTag.prototype.setLuxometerPeriod = function(period, callback) { 336 | this.writePeriodCharacteristic(LUXOMETER_UUID, LUXOMETER_PERIOD_UUID, period, callback); 337 | }; 338 | 339 | CC2650SensorTag.prototype.convertSimpleKeyData = function(data, callback) { 340 | var b = data.readUInt8(0); 341 | 342 | var left = (b & 0x2) ? true : false; 343 | var right = (b & 0x1) ? true : false; 344 | var reedRelay = (b & 0x4) ? true : false; 345 | 346 | callback(left, right, reedRelay); 347 | }; 348 | 349 | module.exports = CC2650SensorTag; 350 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | // http://processors.wiki.ti.com/index.php/SensorTag_User_Guide 2 | 3 | var IR_TEMPERATURE_UUID = 'f000aa0004514000b000000000000000'; 4 | var HUMIDITY_UUID = 'f000aa2004514000b000000000000000'; 5 | var BAROMETRIC_PRESSURE_UUID = 'f000aa4004514000b000000000000000'; 6 | var SIMPLE_KEY_UUID = 'ffe0'; 7 | 8 | var IR_TEMPERATURE_CONFIG_UUID = 'f000aa0204514000b000000000000000'; 9 | var IR_TEMPERATURE_DATA_UUID = 'f000aa0104514000b000000000000000'; 10 | var IR_TEMPERATURE_PERIOD_UUID = 'f000aa0304514000b000000000000000'; 11 | 12 | var HUMIDITY_CONFIG_UUID = 'f000aa2204514000b000000000000000'; 13 | var HUMIDITY_DATA_UUID = 'f000aa2104514000b000000000000000'; 14 | var HUMIDITY_PERIOD_UUID = 'f000aa2304514000b000000000000000'; 15 | 16 | var BAROMETRIC_PRESSURE_CONFIG_UUID = 'f000aa4204514000b000000000000000'; 17 | var BAROMETRIC_PRESSURE_DATA_UUID = 'f000aa4104514000b000000000000000'; 18 | var BAROMETRIC_PRESSURE_PERIOD_UUID = 'f000aa4404514000b000000000000000'; 19 | 20 | var SIMPLE_KEY_DATA_UUID = 'ffe1'; 21 | 22 | function SensorTagCommon() { 23 | this.onIrTemperatureChangeBinded = this.onIrTemperatureChange.bind(this); 24 | this.onHumidityChangeBinded = this.onHumidityChange.bind(this); 25 | this.onBarometricPressureChangeBinded = this.onBarometricPressureChange.bind(this); 26 | this.onSimpleKeyChangeBinded = this.onSimpleKeyChange.bind(this); 27 | } 28 | 29 | SensorTagCommon.prototype.toString = function() { 30 | return JSON.stringify({ 31 | id: this.id, 32 | type: this.type 33 | }); 34 | }; 35 | 36 | SensorTagCommon.prototype.writePeriodCharacteristic = function(serviceUuid, characteristicUuid, period, callback) { 37 | period /= 10; // input is scaled by units of 10ms 38 | 39 | if (period < 1) { 40 | period = 1; 41 | } else if (period > 255) { 42 | period = 255; 43 | } 44 | 45 | this.writeUInt8Characteristic(serviceUuid, characteristicUuid, period, callback); 46 | }; 47 | 48 | SensorTagCommon.prototype.enableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) { 49 | this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x01, callback); 50 | }; 51 | 52 | SensorTagCommon.prototype.disableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) { 53 | this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x00, callback); 54 | }; 55 | 56 | SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) { 57 | this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback); 58 | }; 59 | 60 | SensorTagCommon.prototype.enableIrTemperature = function(callback) { 61 | this.enableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback); 62 | }; 63 | 64 | SensorTagCommon.prototype.disableIrTemperature = function(callback) { 65 | this.disableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback); 66 | }; 67 | 68 | SensorTagCommon.prototype.readIrTemperature = function(callback) { 69 | this.readDataCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, function(error, data) { 70 | if (error) { 71 | return callback(error); 72 | } 73 | 74 | this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) { 75 | callback(null, objectTemperature, ambientTemperature); 76 | }.bind(this)); 77 | }.bind(this)); 78 | }; 79 | 80 | SensorTagCommon.prototype.onIrTemperatureChange = function(data) { 81 | this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) { 82 | this.emit('irTemperatureChange', objectTemperature, ambientTemperature); 83 | }.bind(this)); 84 | }; 85 | 86 | SensorTagCommon.prototype.notifyIrTemperature = function(callback) { 87 | this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, true, this.onIrTemperatureChangeBinded, callback); 88 | }; 89 | 90 | SensorTagCommon.prototype.unnotifyIrTemperature = function(callback) { 91 | this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, false, this.onIrTemperatureChangeBinded, callback); 92 | }; 93 | 94 | SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) { 95 | this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback); 96 | }; 97 | 98 | SensorTagCommon.prototype.setHumidityPeriod = function(period, callback) { 99 | this.writePeriodCharacteristic(HUMIDITY_UUID, HUMIDITY_PERIOD_UUID, period, callback); 100 | }; 101 | 102 | SensorTagCommon.prototype.enableHumidity = function(callback) { 103 | this.enableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback); 104 | }; 105 | 106 | SensorTagCommon.prototype.disableHumidity = function(callback) { 107 | this.disableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback); 108 | }; 109 | 110 | SensorTagCommon.prototype.readHumidity = function(callback) { 111 | this.readDataCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, function(error, data) { 112 | if (error) { 113 | return callback(error); 114 | } 115 | 116 | this.convertHumidityData(data, function(temperature, humidity) { 117 | callback(null, temperature, humidity); 118 | }); 119 | }.bind(this)); 120 | }; 121 | 122 | SensorTagCommon.prototype.onHumidityChange = function(data) { 123 | this.convertHumidityData(data, function(temperature, humidity) { 124 | this.emit('humidityChange', temperature, humidity); 125 | }.bind(this)); 126 | }; 127 | 128 | SensorTagCommon.prototype.notifyHumidity = function(callback) { 129 | this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, true, this.onHumidityChangeBinded, callback); 130 | }; 131 | 132 | SensorTagCommon.prototype.unnotifyHumidity = function(callback) { 133 | this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, false, this.onHumidityChangeBinded, callback); 134 | }; 135 | 136 | SensorTagCommon.prototype.setBarometricPressurePeriod = function(period, callback) { 137 | this.writePeriodCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_PERIOD_UUID, period, callback); 138 | }; 139 | 140 | SensorTagCommon.prototype.disableBarometricPressure = function(callback) { 141 | this.disableConfigCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, callback); 142 | }; 143 | 144 | SensorTagCommon.prototype.readBarometricPressure = function(callback) { 145 | this.readDataCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, function(error, data) { 146 | if (error) { 147 | return callback(error); 148 | } 149 | 150 | this.convertBarometricPressureData(data, function(pressure) { 151 | callback(null, pressure); 152 | }.bind(this)); 153 | }.bind(this)); 154 | }; 155 | 156 | SensorTagCommon.prototype.onBarometricPressureChange = function(data) { 157 | this.convertBarometricPressureData(data, function(pressure) { 158 | this.emit('barometricPressureChange', pressure); 159 | }.bind(this)); 160 | }; 161 | 162 | SensorTagCommon.prototype.notifyBarometricPressure = function(callback) { 163 | this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, true, this.onBarometricPressureChangeBinded, callback); 164 | }; 165 | 166 | SensorTagCommon.prototype.unnotifyBarometricPressure = function(callback) { 167 | this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, false, this.onBarometricPressureChangeBinded, callback); 168 | }; 169 | 170 | SensorTagCommon.prototype.onSimpleKeyChange = function(data) { 171 | this.convertSimpleKeyData(data, function(/*left, right, ...*/) { 172 | var emitArguments = Array.prototype.slice.call(arguments); 173 | emitArguments.unshift('simpleKeyChange'); 174 | 175 | this.emit.apply(this, emitArguments); 176 | }.bind(this)); 177 | }; 178 | 179 | SensorTagCommon.prototype.convertSimpleKeyData = function(data, callback) { 180 | var b = data.readUInt8(0); 181 | 182 | var left = (b & 0x2) ? true : false; 183 | var right = (b & 0x1) ? true : false; 184 | 185 | callback(left, right); 186 | }; 187 | 188 | SensorTagCommon.prototype.notifySimpleKey = function(callback) { 189 | this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, true, this.onSimpleKeyChangeBinded, callback); 190 | }; 191 | 192 | SensorTagCommon.prototype.unnotifySimpleKey = function(callback) { 193 | this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, false, this.onSimpleKeyChangeBinded, callback); 194 | }; 195 | 196 | module.exports = SensorTagCommon; 197 | -------------------------------------------------------------------------------- /lib/sensortag.js: -------------------------------------------------------------------------------- 1 | var CC2540SensorTag = require('./cc2540'); 2 | var CC2650SensorTag = require('./cc2650'); 3 | 4 | var SensorTag = function() { 5 | }; 6 | 7 | SensorTag.discoverAll = function(onDiscover) { 8 | CC2540SensorTag.discoverAll(onDiscover); 9 | CC2650SensorTag.discoverAll(onDiscover); 10 | }; 11 | 12 | SensorTag.stopDiscoverAll = function(onDiscover) { 13 | CC2540SensorTag.stopDiscoverAll(onDiscover); 14 | CC2650SensorTag.stopDiscoverAll(onDiscover); 15 | }; 16 | 17 | SensorTag.discover = function(callback) { 18 | var onDiscover = function(sensorTag) { 19 | SensorTag.stopDiscoverAll(onDiscover); 20 | 21 | callback(sensorTag); 22 | }; 23 | 24 | SensorTag.discoverAll(onDiscover); 25 | }; 26 | 27 | SensorTag.discoverByAddress = function(address, callback) { 28 | address = address.toLowerCase(); 29 | 30 | var onDiscoverByAddress = function(sensorTag) { 31 | if (sensorTag._peripheral.address === address) { 32 | SensorTag.stopDiscoverAll(onDiscoverByAddress); 33 | 34 | callback(sensorTag); 35 | } 36 | }; 37 | 38 | SensorTag.discoverAll(onDiscoverByAddress); 39 | }; 40 | 41 | SensorTag.discoverById = function(id, callback) { 42 | var onDiscoverById = function(sensorTag) { 43 | if (sensorTag.id === id) { 44 | SensorTag.stopDiscoverAll(onDiscoverById); 45 | 46 | callback(sensorTag); 47 | } 48 | }; 49 | 50 | SensorTag.discoverAll(onDiscoverById); 51 | }; 52 | 53 | // deprecated 54 | SensorTag.discoverByUuid = function(uuid, callback) { 55 | var onDiscoverByUuid = function(sensorTag) { 56 | if (sensorTag.uuid === uuid) { 57 | SensorTag.stopDiscoverAll(onDiscoverByUuid); 58 | 59 | callback(sensorTag); 60 | } 61 | }; 62 | 63 | SensorTag.discoverAll(onDiscoverByUuid); 64 | }; 65 | 66 | SensorTag.CC2540 = CC2540SensorTag; 67 | SensorTag.CC2650 = CC2650SensorTag; 68 | 69 | module.exports = SensorTag; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sensortag", 3 | "version": "1.3.0", 4 | "description": "Node.js lib for the TI SensorTag", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jshint lib/*.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/sandeepmistry/node-sensortag" 12 | }, 13 | "keywords": [ 14 | "TI", 15 | "SensorTag" 16 | ], 17 | "author": "Sandeep Mistry", 18 | "license": "MIT", 19 | "readmeFilename": "README.md", 20 | "dependencies": { 21 | "noble-device": "^1.4.1" 22 | }, 23 | "devDependencies": { 24 | "async": "~0.2.7", 25 | "jshint": "~1.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-cc2650-io.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | var async = require('async'); 4 | 5 | var CC2650SensorTag = require('./index').CC2650; 6 | 7 | var USE_READ = true; 8 | 9 | CC2650SensorTag.discover(function(sensorTag) { 10 | console.log('discovered: ' + sensorTag); 11 | 12 | sensorTag.on('disconnect', function() { 13 | console.log('disconnected!'); 14 | process.exit(0); 15 | }); 16 | 17 | async.series([ 18 | function(callback) { 19 | console.log('connectAndSetUp'); 20 | sensorTag.connectAndSetUp(callback); 21 | }, 22 | // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide#IO_Service 23 | function(callback) { 24 | console.log('IO off'); 25 | sensorTag.writeIoData(0, callback); 26 | }, 27 | function(callback) { 28 | console.log('writeIoConfig'); 29 | sensorTag.writeIoConfig(1, callback); 30 | }, 31 | function(callback) { 32 | console.log('LED red'); 33 | sensorTag.writeIoData(1, function() { 34 | setTimeout(callback, 1000); 35 | }); 36 | }, 37 | function(callback) { 38 | console.log('LED green'); 39 | sensorTag.writeIoData(2, function() { 40 | setTimeout(callback, 1000); 41 | }); 42 | }, 43 | function(callback) { 44 | console.log('buzzer'); 45 | sensorTag.writeIoData(4, function() { 46 | setTimeout(callback, 1000); 47 | }); 48 | }, 49 | function(callback) { 50 | console.log('IO off'); 51 | sensorTag.writeIoData(0, callback); 52 | }, 53 | function(callback) { 54 | console.log('disconnect'); 55 | sensorTag.disconnect(callback); 56 | } 57 | ] 58 | ); 59 | }); 60 | -------------------------------------------------------------------------------- /test-continous-discover-connect.js: -------------------------------------------------------------------------------- 1 | var SensorTag = require('./index'); 2 | 3 | function sensorTagDisovered(sensorTag) { 4 | console.log('discovered: ' + sensorTag); 5 | 6 | sensorTag.once('disconnect', function() { 7 | console.log('disconnected'); 8 | }); 9 | 10 | sensorTag.connectAndSetUp(function(err) { 11 | // restart discovery 12 | SensorTag.discover(sensorTagDisovered); 13 | 14 | if (err) { 15 | console.log('error occurred on connect or set up!'); 16 | return; 17 | } 18 | 19 | console.log('connected'); 20 | 21 | // do some stuff with the sensorTag ... 22 | }); 23 | } 24 | 25 | // start discovery of a SensorTag 26 | SensorTag.discover(sensorTagDisovered); 27 | -------------------------------------------------------------------------------- /test-discover.js: -------------------------------------------------------------------------------- 1 | var SensorTag = require('./index'); 2 | 3 | function onDiscover(sensorTag) { 4 | console.log('discovered: ' + sensorTag.id + ', type = ' + sensorTag.type); 5 | } 6 | 7 | // SensorTag.discoverAll(onDiscover); 8 | 9 | // SensorTag.discoverById('3d7bd14925ad4f69b73a98cbc998c4db', onDiscover); 10 | 11 | // SensorTag.discoverByAddress('90:59:af:0a:ab:34', onDiscover); 12 | 13 | SensorTag.discover(onDiscover); 14 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | var async = require('async'); 4 | 5 | var SensorTag = require('./index'); 6 | 7 | var USE_READ = true; 8 | 9 | SensorTag.discover(function(sensorTag) { 10 | console.log('discovered: ' + sensorTag); 11 | 12 | sensorTag.on('disconnect', function() { 13 | console.log('disconnected!'); 14 | process.exit(0); 15 | }); 16 | 17 | async.series([ 18 | function(callback) { 19 | console.log('connectAndSetUp'); 20 | sensorTag.connectAndSetUp(callback); 21 | }, 22 | function(callback) { 23 | console.log('readDeviceName'); 24 | sensorTag.readDeviceName(function(error, deviceName) { 25 | console.log('\tdevice name = ' + deviceName); 26 | callback(); 27 | }); 28 | }, 29 | function(callback) { 30 | console.log('readSystemId'); 31 | sensorTag.readSystemId(function(error, systemId) { 32 | console.log('\tsystem id = ' + systemId); 33 | callback(); 34 | }); 35 | }, 36 | function(callback) { 37 | console.log('readSerialNumber'); 38 | sensorTag.readSerialNumber(function(error, serialNumber) { 39 | console.log('\tserial number = ' + serialNumber); 40 | callback(); 41 | }); 42 | }, 43 | function(callback) { 44 | console.log('readFirmwareRevision'); 45 | sensorTag.readFirmwareRevision(function(error, firmwareRevision) { 46 | console.log('\tfirmware revision = ' + firmwareRevision); 47 | callback(); 48 | }); 49 | }, 50 | function(callback) { 51 | console.log('readHardwareRevision'); 52 | sensorTag.readHardwareRevision(function(error, hardwareRevision) { 53 | console.log('\thardware revision = ' + hardwareRevision); 54 | callback(); 55 | }); 56 | }, 57 | function(callback) { 58 | console.log('readSoftwareRevision'); 59 | sensorTag.readHardwareRevision(function(error, softwareRevision) { 60 | console.log('\tsoftware revision = ' + softwareRevision); 61 | callback(); 62 | }); 63 | }, 64 | function(callback) { 65 | console.log('readManufacturerName'); 66 | sensorTag.readManufacturerName(function(error, manufacturerName) { 67 | console.log('\tmanufacturer name = ' + manufacturerName); 68 | callback(); 69 | }); 70 | }, 71 | function(callback) { 72 | console.log('enableIrTemperature'); 73 | sensorTag.enableIrTemperature(callback); 74 | }, 75 | function(callback) { 76 | setTimeout(callback, 2000); 77 | }, 78 | function(callback) { 79 | if (USE_READ) { 80 | console.log('readIrTemperature'); 81 | sensorTag.readIrTemperature(function(error, objectTemperature, ambientTemperature) { 82 | console.log('\tobject temperature = %d °C', objectTemperature.toFixed(1)); 83 | console.log('\tambient temperature = %d °C', ambientTemperature.toFixed(1)); 84 | 85 | callback(); 86 | }); 87 | } else { 88 | sensorTag.on('irTemperatureChange', function(objectTemperature, ambientTemperature) { 89 | console.log('\tobject temperature = %d °C', objectTemperature.toFixed(1)); 90 | console.log('\tambient temperature = %d °C', ambientTemperature.toFixed(1)) 91 | }); 92 | 93 | console.log('setIrTemperaturePeriod'); 94 | sensorTag.setIrTemperaturePeriod(500, function(error) { 95 | console.log('notifyIrTemperature'); 96 | sensorTag.notifyIrTemperature(function(error) { 97 | setTimeout(function() { 98 | console.log('unnotifyIrTemperature'); 99 | sensorTag.unnotifyIrTemperature(callback); 100 | }, 5000); 101 | }); 102 | }); 103 | } 104 | }, 105 | function(callback) { 106 | console.log('disableIrTemperature'); 107 | sensorTag.disableIrTemperature(callback); 108 | }, 109 | function(callback) { 110 | console.log('enableAccelerometer'); 111 | sensorTag.enableAccelerometer(callback); 112 | }, 113 | function(callback) { 114 | setTimeout(callback, 2000); 115 | }, 116 | function(callback) { 117 | if (USE_READ) { 118 | console.log('readAccelerometer'); 119 | sensorTag.readAccelerometer(function(error, x, y, z) { 120 | console.log('\tx = %d G', x.toFixed(1)); 121 | console.log('\ty = %d G', y.toFixed(1)); 122 | console.log('\tz = %d G', z.toFixed(1)); 123 | 124 | callback(); 125 | }); 126 | } else { 127 | sensorTag.on('accelerometerChange', function(x, y, z) { 128 | console.log('\tx = %d G', x.toFixed(1)); 129 | console.log('\ty = %d G', y.toFixed(1)); 130 | console.log('\tz = %d G', z.toFixed(1)); 131 | }); 132 | 133 | console.log('setAccelerometerPeriod'); 134 | sensorTag.setAccelerometerPeriod(500, function(error) { 135 | console.log('notifyAccelerometer'); 136 | sensorTag.notifyAccelerometer(function(error) { 137 | setTimeout(function() { 138 | console.log('unnotifyAccelerometer'); 139 | sensorTag.unnotifyAccelerometer(callback); 140 | }, 5000); 141 | }); 142 | }); 143 | } 144 | }, 145 | function(callback) { 146 | console.log('disableAccelerometer'); 147 | sensorTag.disableAccelerometer(callback); 148 | }, 149 | function(callback) { 150 | console.log('enableHumidity'); 151 | sensorTag.enableHumidity(callback); 152 | }, 153 | function(callback) { 154 | setTimeout(callback, 2000); 155 | }, 156 | function(callback) { 157 | if (USE_READ) { 158 | console.log('readHumidity'); 159 | sensorTag.readHumidity(function(error, temperature, humidity) { 160 | console.log('\ttemperature = %d °C', temperature.toFixed(1)); 161 | console.log('\thumidity = %d %', humidity.toFixed(1)); 162 | 163 | callback(); 164 | }); 165 | } else { 166 | sensorTag.on('humidityChange', function(temperature, humidity) { 167 | console.log('\ttemperature = %d °C', temperature.toFixed(1)); 168 | console.log('\thumidity = %d %', humidity.toFixed(1)); 169 | }); 170 | 171 | console.log('setHumidityPeriod'); 172 | sensorTag.setHumidityPeriod(500, function(error) { 173 | console.log('notifyHumidity'); 174 | sensorTag.notifyHumidity(function(error) { 175 | setTimeout(function() { 176 | console.log('unnotifyHumidity'); 177 | sensorTag.unnotifyHumidity(callback); 178 | }, 5000); 179 | }); 180 | }); 181 | } 182 | }, 183 | function(callback) { 184 | console.log('disableHumidity'); 185 | sensorTag.disableHumidity(callback); 186 | }, 187 | function(callback) { 188 | console.log('enableMagnetometer'); 189 | sensorTag.enableMagnetometer(callback); 190 | }, 191 | function(callback) { 192 | setTimeout(callback, 2000); 193 | }, 194 | function(callback) { 195 | if (USE_READ) { 196 | console.log('readMagnetometer'); 197 | sensorTag.readMagnetometer(function(error, x, y, z) { 198 | console.log('\tx = %d μT', x.toFixed(1)); 199 | console.log('\ty = %d μT', y.toFixed(1)); 200 | console.log('\tz = %d μT', z.toFixed(1)); 201 | 202 | callback(); 203 | }); 204 | } else { 205 | sensorTag.on('magnetometerChange', function(x, y, z) { 206 | console.log('\tx = %d μT', x.toFixed(1)); 207 | console.log('\ty = %d μT', y.toFixed(1)); 208 | console.log('\tz = %d μT', z.toFixed(1)); 209 | }); 210 | 211 | console.log('setMagnetometerPeriod'); 212 | sensorTag.setMagnetometerPeriod(500, function(error) { 213 | console.log('notifyMagnetometer'); 214 | sensorTag.notifyMagnetometer(function(error) { 215 | setTimeout(function() { 216 | console.log('unnotifyMagnetometer'); 217 | sensorTag.unnotifyMagnetometer(callback); 218 | }, 5000); 219 | }); 220 | }); 221 | } 222 | }, 223 | function(callback) { 224 | console.log('disableMagnetometer'); 225 | sensorTag.disableMagnetometer(callback); 226 | }, 227 | function(callback) { 228 | console.log('enableBarometricPressure'); 229 | sensorTag.enableBarometricPressure(callback); 230 | }, 231 | function(callback) { 232 | setTimeout(callback, 2000); 233 | }, 234 | function(callback) { 235 | if (USE_READ) { 236 | console.log('readBarometricPressure'); 237 | sensorTag.readBarometricPressure(function(error, pressure) { 238 | console.log('\tpressure = %d mBar', pressure.toFixed(1)); 239 | 240 | callback(); 241 | }); 242 | } else { 243 | sensorTag.on('barometricPressureChange', function(pressure) { 244 | console.log('\tpressure = %d mBar', pressure.toFixed(1)); 245 | }); 246 | 247 | console.log('setBarometricPressurePeriod'); 248 | sensorTag.setBarometricPressurePeriod(500, function(error) { 249 | console.log('notifyBarometricPressure'); 250 | sensorTag.notifyBarometricPressure(function(error) { 251 | setTimeout(function() { 252 | console.log('unnotifyBarometricPressure'); 253 | sensorTag.unnotifyBarometricPressure(callback); 254 | }, 5000); 255 | }); 256 | }); 257 | } 258 | }, 259 | function(callback) { 260 | console.log('disableBarometricPressure'); 261 | sensorTag.disableBarometricPressure(callback); 262 | }, 263 | function(callback) { 264 | console.log('enableGyroscope'); 265 | sensorTag.enableGyroscope(callback); 266 | }, 267 | function(callback) { 268 | setTimeout(callback, 2000); 269 | }, 270 | function(callback) { 271 | if (USE_READ) { 272 | console.log('readGyroscope'); 273 | sensorTag.readGyroscope(function(error, x, y, z) { 274 | console.log('\tx = %d °/s', x.toFixed(1)); 275 | console.log('\ty = %d °/s', y.toFixed(1)); 276 | console.log('\tz = %d °/s', z.toFixed(1)); 277 | 278 | callback(); 279 | }); 280 | } else { 281 | sensorTag.on('gyroscopeChange', function(x, y, z) { 282 | console.log('\tx = %d °/s', x.toFixed(1)); 283 | console.log('\ty = %d °/s', y.toFixed(1)); 284 | console.log('\tz = %d °/s', z.toFixed(1)); 285 | }); 286 | 287 | console.log('setGyroscopePeriod'); 288 | sensorTag.setGyroscopePeriod(500, function(error) { 289 | console.log('notifyGyroscope'); 290 | sensorTag.notifyGyroscope(function(error) { 291 | setTimeout(function() { 292 | console.log('unnotifyGyroscope'); 293 | sensorTag.unnotifyGyroscope(callback); 294 | }, 5000); 295 | }); 296 | }); 297 | } 298 | }, 299 | function(callback) { 300 | console.log('disableGyroscope'); 301 | sensorTag.disableGyroscope(callback); 302 | }, 303 | function(callback) { 304 | if (sensorTag.type === 'cc2540') { 305 | async.series([ 306 | function(callback) { 307 | console.log('readTestData'); 308 | sensorTag.readTestData(function(error, data) { 309 | console.log('\tdata = ' + data); 310 | 311 | callback(); 312 | }); 313 | }, 314 | function(callback) { 315 | console.log('readTestConfiguration'); 316 | sensorTag.readTestConfiguration(function(error, configuration) { 317 | console.log('\tconfiguration = ' + configuration); 318 | 319 | callback(); 320 | }); 321 | }, 322 | function() { 323 | callback(); 324 | } 325 | ]); 326 | } else if (sensorTag.type === 'cc2650') { 327 | async.series([ 328 | function(callback) { 329 | console.log('readIoData'); 330 | sensorTag.readIoData(function(error, value) { 331 | console.log('\tdata = ' + value); 332 | 333 | console.log('writeIoData'); 334 | sensorTag.writeIoData(value, callback); 335 | }); 336 | }, 337 | function(callback) { 338 | console.log('readIoConfig'); 339 | sensorTag.readIoConfig(function(error, value) { 340 | console.log('\tconfig = ' + value); 341 | 342 | console.log('writeIoConfig'); 343 | sensorTag.writeIoConfig(value, callback); 344 | }); 345 | }, 346 | function(callback) { 347 | console.log('enableLuxometer'); 348 | sensorTag.enableLuxometer(callback); 349 | }, 350 | function(callback) { 351 | setTimeout(callback, 2000); 352 | }, 353 | function(callback) { 354 | if (USE_READ) { 355 | console.log('readLuxometer'); 356 | sensorTag.readLuxometer(function(error, lux) { 357 | console.log('\tlux = %d', lux.toFixed(1)); 358 | 359 | callback(); 360 | }); 361 | } else { 362 | sensorTag.on('luxometerChange', function(lux) { 363 | console.log('\tlux = %d', lux.toFixed(1)); 364 | }); 365 | 366 | console.log('setLuxometer'); 367 | sensorTag.setLuxometerPeriod(500, function(error) { 368 | console.log('notifyLuxometer'); 369 | sensorTag.notifyLuxometer(function(error) { 370 | setTimeout(function() { 371 | console.log('unnotifyLuxometer'); 372 | sensorTag.unnotifyLuxometer(callback); 373 | }, 5000); 374 | }); 375 | }); 376 | } 377 | }, 378 | function(callback) { 379 | console.log('disableLuxometer'); 380 | sensorTag.disableLuxometer(callback); 381 | }, 382 | function() { 383 | callback(); 384 | } 385 | ]); 386 | } else { 387 | callback(); 388 | } 389 | }, 390 | function(callback) { 391 | console.log('readSimpleRead - waiting for button press ...'); 392 | sensorTag.on('simpleKeyChange', function(left, right, reedRelay) { 393 | console.log('left: ' + left); 394 | console.log('right: ' + right); 395 | if (sensorTag.type === 'cc2650') { 396 | console.log('reed relay: ' + reedRelay); 397 | } 398 | 399 | if (left || right) { 400 | sensorTag.notifySimpleKey(callback); 401 | } 402 | }); 403 | 404 | sensorTag.notifySimpleKey(); 405 | }, 406 | function(callback) { 407 | console.log('disconnect'); 408 | sensorTag.disconnect(callback); 409 | } 410 | ] 411 | ); 412 | }); 413 | --------------------------------------------------------------------------------