├── .gitignore ├── .travis.yml ├── changelog.md ├── examples ├── accelerometer.js ├── accelerometerLog.js ├── all.js ├── debug.js ├── gpio.js ├── gyro.js ├── magnetometer.js ├── sensorFusion.js ├── server.js └── stepDetector.js ├── npm-debug.log ├── package.json ├── readme.md ├── src ├── data-source.js ├── device.js └── registers │ ├── accelerometer.js │ ├── ambient-light.js │ ├── barometer.js │ ├── config │ ├── ambient-light.js │ ├── barometer.js │ ├── gyro.js │ ├── led.js │ ├── neo-pixel.js │ ├── sensorFusion.js │ └── util.js │ ├── core.js │ ├── data-processing.js │ ├── debug.js │ ├── event.js │ ├── gpio.js │ ├── gyro.js │ ├── haptic.js │ ├── i2c.js │ ├── ibeacon.js │ ├── led.js │ ├── log.js │ ├── macro.js │ ├── magnetometer.js │ ├── neo-pixel.js │ ├── registers.js │ ├── sensorFusion.js │ ├── settings.js │ ├── switch.js │ ├── temperature.js │ └── timer.js └── tests ├── jasmine-runner.js └── spec ├── helpers └── device.js ├── registers ├── accelerometerSpec.js ├── logSpec.js ├── magnetometerSpec.js ├── sensorfusionSpec.js ├── settingsSpec.js └── switchSpec.js └── support └── jasmine.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .idea 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | 4 | node_js: 5 | - 0.10.44 6 | - 0.12 7 | - 4.0 8 | - 5.0 9 | 10 | before_script: 11 | - npm install -g mocha 12 | - npm test 13 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Version 3.7 2 | 3 | * Bug fixing : Gyro configuration [issue#38](https://github.com/brainexe/node-metawear/issues/38) 4 | * Magnetometer configuration 5 | * SensorFusion condiguration for the NDOF mode -------------------------------------------------------------------------------- /examples/accelerometer.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | var rate = parseFloat(process.argv[2]) || 50; 5 | var range = parseFloat(process.argv[3]) || 2; 6 | 7 | devices.discover(function(device) { 8 | console.log('discovered device ', device.address); 9 | 10 | device.on('disconnect', function() { 11 | console.log('we got disconnected! :( '); 12 | }); 13 | 14 | device.connectAndSetup(function(error) { 15 | console.log('were connected!'); 16 | console.log('Start accelerometer with ' + rate + 'hz ang +-' + range + 'g'); 17 | 18 | var accelerometer = new device.Accelerometer(device); 19 | var logger = new device.Log(device); 20 | 21 | accelerometer.setOutputDataRate(rate); 22 | accelerometer.setAxisSamplingRange(range); 23 | logger.startLogging(false); 24 | 25 | accelerometer.setConfig(); 26 | accelerometer.enableNotifications(); 27 | accelerometer.enableAxisSampling(); 28 | accelerometer.start(); 29 | 30 | accelerometer.onChange(function(data) { 31 | console.log("x:", data.x, "\t\ty:", data.y, "\t\tz:", data.z); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /examples/accelerometerLog.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | device.on('disconnect', function() { 7 | console.log('we got disconnected! :( '); 8 | }); 9 | 10 | device.connectAndSetup(function(error) { 11 | console.log('were connected!'); 12 | 13 | var accelerometer = new device.Accelerometer(device); 14 | var logger = new device.Log(device); 15 | 16 | //accelerometer.setOutputDataRate(rate); 17 | //accelerometer.setAxisSamplingRange(range); 18 | logger.removeAll(); 19 | logger.startLogging(false); 20 | 21 | accelerometer.setConfig(); 22 | //accelerometer.enableNotifications(); 23 | accelerometer.enableAxisSampling(); 24 | accelerometer.start(); 25 | 26 | 27 | setTimeout(function() { 28 | console.log('Stop'); 29 | accelerometer.stop(); 30 | accelerometer.disableAxisSampling(); 31 | logger.stopLogging(); 32 | logger.downloadLog(); 33 | 34 | }, 2000); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /examples/all.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | device.on('disconnect', function() { 7 | console.log('we got disconnected! :( '); 8 | }); 9 | 10 | device.connectAndSetup(function(error) { 11 | console.log('were connected!'); 12 | 13 | // load some registers 14 | var log = new device.Log(device); 15 | var gpio = new device.Gpio(device); 16 | var led = new device.Led(device); 17 | var settings = new device.Settings(device); 18 | var switchRegister = new device.Switch(device); 19 | var temperature = new device.Temperature(device, device.Temperature.NRF_DIE); // todo 20 | var dataProcessing = new device.DataProcessing(device); 21 | var ambientLight = new device.AmbiantLight(device); 22 | var haptic = new device.Haptic(device); 23 | var barometer = new device.Barometer(device); 24 | 25 | // read generic device information 26 | device.readFirmwareRevision(function(error, version){ 27 | console.log('Firmware: ', version); 28 | }); 29 | device.readHardwareRevision(function(error, revision){ 30 | console.log('Hardware revision: ', revision); 31 | }); 32 | device.readBatteryLevel(function(error, batteryLevel) { 33 | console.log("Battery:", batteryLevel); 34 | }); 35 | 36 | // blink LED 20 times in blue 37 | led.config 38 | .setColor(led.config.BLUE) 39 | .setRiseTime(1000) 40 | .setHighTime(500) 41 | .setFallTime(1000) 42 | .setPulseDuration(2500) 43 | .setRepeatCount(20) 44 | .setHighIntensity(16) 45 | .setLowIntensity(1); 46 | 47 | led.commitConfig(); 48 | led.play(true); 49 | 50 | // tbd 51 | gpio.startPinChangeDetection(0); 52 | gpio.startPinChangeDetection(1); 53 | gpio.startPinChangeDetection(2); 54 | 55 | log.startLogging(false); 56 | log.downloadLog(function(line) { 57 | console.log('Log: ', line); 58 | }); 59 | 60 | dataProcessing.enableNotification(); 61 | 62 | switchRegister.register(); 63 | switchRegister.onChange(function(status) { 64 | console.log("Switch status: ", status); 65 | }); 66 | 67 | temperature.startInterval(2500, function(temp) { 68 | console.log('Temperature: ', temp); 69 | }); 70 | 71 | settings.getDeviceName(function(deviceName) { 72 | console.log("Device name: " + deviceName); 73 | }); 74 | 75 | ambientLight.enable(function(light) { 76 | console.log("Light: " + light + ' lux'); 77 | }); 78 | 79 | barometer.config.standbyTime = barometer.config.STANDBY_TIME.TIME_2000; 80 | barometer.commitConfig(); 81 | 82 | barometer.enablePressure(function(pressure) { 83 | console.log("Pressure: " + pressure); 84 | }); 85 | 86 | setInterval(function() { 87 | //haptic.startMotor(5000, 100); 88 | haptic.startBuzzer(5000); 89 | }, 10000); 90 | 91 | //settings.setDeviceName('brainexe'); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /examples/debug.js: -------------------------------------------------------------------------------- 1 | var metawear = require('./../src/device'); 2 | 3 | metawear.discover(function(device) { 4 | 5 | device.on('disconnect', function() { 6 | console.log('we got disconnected! :( '); 7 | }); 8 | 9 | device.connectAndSetup(function(error) { 10 | console.log('Were connected!'); 11 | console.log('Reset in 5 seconds'); 12 | 13 | var debug = new device.Debug(device); 14 | 15 | setTimeout(function() { 16 | debug.reset(); 17 | console.log('Reset done !'); 18 | }, 5000); 19 | 20 | }); 21 | 22 | }); -------------------------------------------------------------------------------- /examples/gpio.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | 7 | device.on('disconnect', function() { 8 | console.log('we got disconnected! :( '); 9 | }); 10 | 11 | device.connectAndSetup(function(error) { 12 | console.log('were connected!'); 13 | 14 | var gpio = new device.Gpio(device); 15 | 16 | // todo 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/gyro.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | 7 | device.on('disconnect', function() { 8 | console.log('we got disconnected! :( '); 9 | }); 10 | 11 | device.connectAndSetup(function(error) { 12 | console.log('were connected!'); 13 | 14 | var gyro = new device.Gyro(device); 15 | 16 | gyro.config.setRate(1600); 17 | gyro.config.setRange(125); 18 | gyro.commitConfig(); 19 | 20 | gyro.enable(); 21 | gyro.onChange(function(x, y, z) { 22 | console.log("x:", x, "\t\ty:", y, "\t\tz:", z); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/magnetometer.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | 7 | device.on('disconnect', function() { 8 | console.log('we got disconnected! :( '); 9 | }); 10 | 11 | device.connectAndSetup(function(error) { 12 | console.log('were connected!'); 13 | 14 | 15 | var magnetometer = new device.Magnetometer(device); 16 | 17 | magnetometer.subscribe(); 18 | magnetometer.enableAxisSampling(); 19 | magnetometer.start(); 20 | 21 | magnetometer.onChange(function(data) { 22 | console.log("x:", data.x, "\t\ty:", data.y, "\t\tz:", data.z); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/sensorFusion.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | var devices = require('./../src/device'); 4 | 5 | devices.discover(function(device) { 6 | console.log('discovered device ', device.address); 7 | 8 | device.on('disconnect', function() { 9 | console.log('we got disconnected! :( '); 10 | }); 11 | 12 | device.connectAndSetup(function(error) { 13 | console.log('were connected!'); 14 | 15 | const QUATERION = 0x7; 16 | const DATA_QUATERION = 0x3; 17 | const MODE_NDOF = 0x1; 18 | 19 | var sensorFusion = new device.SensorFusion(device); 20 | 21 | sensorFusion.config.setMode(MODE_NDOF); 22 | sensorFusion.subscribe(QUATERION); 23 | sensorFusion.enableData(DATA_QUATERION); 24 | sensorFusion.start(); 25 | 26 | sensorFusion.onChange(function(data) { 27 | console.log("w", data.w, "\t\tx:", data.x, "\t\ty:", data.y, "\t\tz:", data.z); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This example scripts starts an simple HTTP server on port 8082. 3 | * Implemented routes: 4 | * GET http://localhost:8082/temperature/ 5 | * GET http://localhost:8082/pressure/ 6 | * GET http://localhost:8082/brightness/ 7 | */ 8 | 9 | var http = require('http'); 10 | var devices = require('./../src/device'); 11 | var port = 8082; 12 | 13 | var currentDevice = null; 14 | devices.discover(function(device) { 15 | console.log('discovered device', device.address); 16 | device.on('disconnect', function () { 17 | console.log('we got disconnected! :( '); 18 | }); 19 | 20 | device.connectAndSetup(function (error) { 21 | console.log('we are ready'); 22 | currentDevice = device; 23 | }); 24 | }); 25 | console.log('start server on http://localhost:' + port); 26 | 27 | http.createServer(function(request, response){ 28 | function writeResponse(code, body) { 29 | response.writeHeader(code, {"Content-Type": "text/plain"}); 30 | response.write(body); 31 | response.end(); 32 | } 33 | 34 | console.log('HTTP request - ' + request.url); 35 | 36 | if (!currentDevice) { 37 | writeResponse(503, "metawear not ready yet"); 38 | console.error('Device not ready..'); 39 | return; 40 | } 41 | 42 | switch (request.url) { 43 | case '/': 44 | case '/info/': 45 | writeResponse(200, 'OK'); 46 | break; 47 | case '/temperature/': 48 | var temperature = new currentDevice.Temperature( 49 | currentDevice, 50 | currentDevice.Temperature.ON_BOARD_THERMISTOR 51 | ); 52 | 53 | temperature.getValue(function(value) { 54 | writeResponse(200, "" + value); 55 | }); 56 | break; 57 | case '/pressure/': 58 | var barometer = new currentDevice.Barometer(currentDevice); 59 | 60 | barometer.enablePressure(function(value) { 61 | writeResponse(200, "" + value); 62 | barometer.disable(); 63 | }); 64 | break; 65 | case '/brightness/': 66 | var light = new currentDevice.AmbiantLight(currentDevice); 67 | 68 | light.enable(function(value) { 69 | writeResponse(200, "" + value); 70 | light.disable(); 71 | }); 72 | break; 73 | default: 74 | writeResponse(404, "Route not found"); 75 | } 76 | }).listen(port); 77 | -------------------------------------------------------------------------------- /examples/stepDetector.js: -------------------------------------------------------------------------------- 1 | 2 | var devices = require('./../src/device'); 3 | 4 | devices.discover(function(device) { 5 | console.log('discovered device ', device.address); 6 | 7 | device.on('disconnect', function() { 8 | console.log('we got disconnected! :( '); 9 | }); 10 | 11 | device.connectAndSetup(function() { 12 | console.log('were connected!'); 13 | 14 | var accelerometer = new device.Accelerometer(device); 15 | 16 | accelerometer.start(); 17 | 18 | accelerometer.enableStepDetector(function(){ 19 | process.stdout.write("."); 20 | }, 'SENSITIVE'); 21 | 22 | setInterval(function() { 23 | accelerometer.readStepCounter(function (count) { 24 | console.log("\n" + count) 25 | }); 26 | }, 5000); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'publish' ] 3 | 2 info using npm@2.15.1 4 | 3 info using node@v0.12.16 5 | 4 verbose publish [ '.' ] 6 | 5 silly cache add args [ '.', null ] 7 | 6 verbose cache add spec . 8 | 7 silly cache add parsed spec { raw: '.', 9 | 7 silly cache add scope: null, 10 | 7 silly cache add name: null, 11 | 7 silly cache add rawSpec: '.', 12 | 7 silly cache add spec: '/Users/alanhortz/Documents/Development/node-metawear', 13 | 7 silly cache add type: 'directory' } 14 | 8 verbose addLocalDirectory /Users/alanhortz/.npm/node-metawear/1.0.0/package.tgz not in flight; packing 15 | 9 verbose correctMkdir /Users/alanhortz/.npm correctMkdir not in flight; initializing 16 | 10 verbose tar pack [ '/Users/alanhortz/.npm/node-metawear/1.0.0/package.tgz', 17 | 10 verbose tar pack '/Users/alanhortz/Documents/Development/node-metawear' ] 18 | 11 verbose tarball /Users/alanhortz/.npm/node-metawear/1.0.0/package.tgz 19 | 12 verbose folder /Users/alanhortz/Documents/Development/node-metawear 20 | 13 info prepublish node-metawear@1.0.0 21 | 14 verbose addLocalTarball adding from inside cache /Users/alanhortz/.npm/node-metawear/1.0.0/package.tgz 22 | 15 verbose correctMkdir /Users/alanhortz/.npm correctMkdir not in flight; initializing 23 | 16 silly cache afterAdd node-metawear@1.0.0 24 | 17 verbose afterAdd /Users/alanhortz/.npm/node-metawear/1.0.0/package/package.json not in flight; writing 25 | 18 verbose correctMkdir /Users/alanhortz/.npm correctMkdir not in flight; initializing 26 | 19 verbose afterAdd /Users/alanhortz/.npm/node-metawear/1.0.0/package/package.json written 27 | 20 silly publish { name: 'node-metawear', 28 | 20 silly publish version: '1.0.0', 29 | 20 silly publish description: 'API for metawear', 30 | 20 silly publish main: 'src/device.js', 31 | 20 silly publish dependencies: 32 | 20 silly publish { debug: '~2.6.6', 33 | 20 silly publish eventemitter2: '~4.1.0', 34 | 20 silly publish hashmap: '~2.1.0', 35 | 20 silly publish 'noble-device': '1.4.1' }, 36 | 20 silly publish devDependencies: 37 | 20 silly publish { 'buffer-equal': '^1.0.0', 38 | 20 silly publish clone: '^2.1.0', 39 | 20 silly publish jasmine: '^2.4.1', 40 | 20 silly publish 'jasmine-debug': '^0.1.1', 41 | 20 silly publish 'jasmine-node': '^1.14.5', 42 | 20 silly publish 'jasmine-spec-reporter': '^4.1.0', 43 | 20 silly publish mocha: '^3.4.1' }, 44 | 20 silly publish keywords: 45 | 20 silly publish [ 'Metawear', 46 | 20 silly publish 'mbientlab', 47 | 20 silly publish 'Bluetooth', 48 | 20 silly publish 'BTLE', 49 | 20 silly publish 'Noble', 50 | 20 silly publish 'Bluetooth Low Energy' ], 51 | 20 silly publish bugs: { url: 'https://github.com/brainexe/node-metawear/issues' }, 52 | 20 silly publish repository: 53 | 20 silly publish { type: 'git', 54 | 20 silly publish url: 'git+ssh://git@github.com/brainexe/node-metawear.git' }, 55 | 20 silly publish scripts: { test: 'cd tests && node jasmine-runner.js' }, 56 | 20 silly publish author: { name: 'Matthias Dötsch', email: 'matze@mdoetsch.de' }, 57 | 20 silly publish contributors: [ { name: 'Alan Hortz', email: 'alan@handson.io' } ], 58 | 20 silly publish license: 'MIT', 59 | 20 silly publish readme: '[![Build Status](https://travis-ci.org/brainexe/node-metawear.svg?branch=master)](https://travis-ci.org/brainexe/node-metawear)\n[![Downloads](https://img.shields.io/npm/dt/node-metawear.svg)]()\n[![Dependencies](https://david-dm.org/brainexe/node-metawear.svg)]()\n\n# Introduction\nnodejs library for MetaWear platform from https://www.mbientlab.com/\n\n# Installation\nBuild package from repository:\n```\ngit clone git@github.com:brainexe/node-metawear.git\ncd node-metawear\nnpm install\n```\nAn alternative is to install the NPM module directly:\n```\nnpm install node-metawear\n```\n\nAdditionally you have to follow some steps, depending of your OS\n\n## Linux (Ubuntu / RaspberryPi)\nInstall required system packages\n```\nsudo apt-get install bluetooth bluez-utils libbluetooth-dev\n```\nThis command grants the node binary cap_net_raw privileges, so it can start/stop BLE advertising. Then you don\'t need the "sudo" prefix anymore:\n```\nsudo setcap cap_net_raw+eip $(eval readlink -f `which node`)\n```\n\n## ChromeOS\nSame as Linux, but the main difference here is that you’ll need to run Node through host-dbus, since it’s accessing the Bluetooth adapter attached through Chrome OS.\n```\nsudo setcap cap_net_raw+eip $(eval readlink -f `which node`)\nexport DEBUG="noble-device" \nhost-dbus node examples/all.js\n```\nthx to [Lance](http://www.polyglotprogramminginc.com/using-metawear-with-node-js/)\n\n## Mac OS\n```\nnpm install\nDEBUG="noble-device" node examples/all.js\n```\n\n## Run\nRun examples in debug output\n```\nDEBUG="noble-device" node examples/all.js\n```\n\n## Functions\nThe functionality is very limited at the moment:\n- Connection Parameters setup\n- Control LED\n- Start buzzer/motor\n- Read/Set Device name\n- Read switch status (+ pressed/release events)\n- Read out battery status\n- Accelerometer / Gyroscope\n- Step counter\n- Temperature sensor\n- Ambient light sensor\n- Barometer sensor\n- BMM150 Magnetometer\n- QUATERNION data from SensorFusion, ONLY FOR METAMOTION R Boards !!\n\n## Next release -> v1.1.0\n- SensorFusion - NDOF mode support for all output types\n- Acceleromter - TAP_CONFIG, ORIENT_CONFIG, FLAT_CONFIG, LOW_HIGH_G_CONFIG\n- Refactoring of the sensor configuration implementation\n\nNote : The versioning restarted at v1.0.0 in order to better handle the major and minor releases as from now.\n\n## Run unit tests\n```\nnpm install -g jasmine\nnpm test\n```\n', 60 | 20 silly publish readmeFilename: 'readme.md', 61 | 20 silly publish gitHead: '3a17c29860e1419b6260f7eea4808ed091b946c1', 62 | 20 silly publish homepage: 'https://github.com/brainexe/node-metawear#readme', 63 | 20 silly publish _id: 'node-metawear@1.0.0', 64 | 20 silly publish _shasum: '7c3cf027dbaa5e7a24af7feb794600e9fc861449', 65 | 20 silly publish _from: '.' } 66 | 21 verbose getPublishConfig undefined 67 | 22 silly mapToRegistry name node-metawear 68 | 23 silly mapToRegistry using default registry 69 | 24 silly mapToRegistry registry https://registry.npmjs.org/ 70 | 25 silly mapToRegistry data { raw: 'node-metawear', 71 | 25 silly mapToRegistry scope: null, 72 | 25 silly mapToRegistry name: 'node-metawear', 73 | 25 silly mapToRegistry rawSpec: '', 74 | 25 silly mapToRegistry spec: 'latest', 75 | 25 silly mapToRegistry type: 'tag' } 76 | 26 silly mapToRegistry uri https://registry.npmjs.org/node-metawear 77 | 27 verbose publish registryBase https://registry.npmjs.org/ 78 | 28 silly publish uploading /Users/alanhortz/.npm/node-metawear/1.0.0/package.tgz 79 | 29 verbose request uri https://registry.npmjs.org/node-metawear 80 | 30 verbose request sending authorization for write operation 81 | 31 info attempt registry request try #1 at 3:54:00 PM 82 | 32 verbose request using bearer token for auth 83 | 33 verbose request id df02100a83faf30f 84 | 34 http request PUT https://registry.npmjs.org/node-metawear 85 | 35 http 403 https://registry.npmjs.org/node-metawear 86 | 36 verbose headers { 'content-type': 'application/json', 87 | 36 verbose headers 'cache-control': 'max-age=300', 88 | 36 verbose headers 'content-length': '95', 89 | 36 verbose headers 'accept-ranges': 'bytes', 90 | 36 verbose headers date: 'Tue, 16 May 2017 13:54:02 GMT', 91 | 36 verbose headers via: '1.1 varnish', 92 | 36 verbose headers connection: 'keep-alive', 93 | 36 verbose headers 'x-served-by': 'cache-ams4436-AMS', 94 | 36 verbose headers 'x-cache': 'MISS', 95 | 36 verbose headers 'x-cache-hits': '0', 96 | 36 verbose headers 'x-timer': 'S1494942841.860281,VS0,VE1316', 97 | 36 verbose headers vary: 'Accept-Encoding' } 98 | 37 verbose request invalidating /Users/alanhortz/.npm/registry.npmjs.org/node-metawear on PUT 99 | 38 error publish Failed PUT 403 100 | 39 verbose stack Error: "You cannot publish over the previously published version 1.0.0." : node-metawear 101 | 39 verbose stack at makeError (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:264:12) 102 | 39 verbose stack at CachingRegistryClient. (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:252:14) 103 | 39 verbose stack at Request._callback (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:172:14) 104 | 39 verbose stack at Request.self.callback (/usr/local/lib/node_modules/npm/node_modules/request/request.js:199:22) 105 | 39 verbose stack at Request.emit (events.js:110:17) 106 | 39 verbose stack at Request. (/usr/local/lib/node_modules/npm/node_modules/request/request.js:1036:10) 107 | 39 verbose stack at Request.emit (events.js:129:20) 108 | 39 verbose stack at IncomingMessage. (/usr/local/lib/node_modules/npm/node_modules/request/request.js:963:12) 109 | 39 verbose stack at IncomingMessage.emit (events.js:129:20) 110 | 39 verbose stack at _stream_readable.js:908:16 111 | 40 verbose statusCode 403 112 | 41 verbose pkgid node-metawear 113 | 42 verbose cwd /Users/alanhortz/Documents/Development/node-metawear 114 | 43 error Darwin 16.5.0 115 | 44 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "publish" 116 | 45 error node v0.12.16 117 | 46 error npm v2.15.1 118 | 47 error code E403 119 | 48 error "You cannot publish over the previously published version 1.0.0." : node-metawear 120 | 49 error If you need help, you may report this error at: 121 | 49 error 122 | 50 verbose exit [ 1, true ] 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-metawear", 3 | "version": "1.0.1", 4 | "description": "API for metawear", 5 | "main": "src/device.js", 6 | "dependencies": { 7 | "debug": "~2.6.6", 8 | "eventemitter2": "~4.1.0", 9 | "hashmap": "~2.1.0", 10 | "noble-device": "1.4.1" 11 | }, 12 | "devDependencies": { 13 | "buffer-equal": "^1.0.0", 14 | "clone": "^2.1.0", 15 | "jasmine": "^2.4.1", 16 | "jasmine-debug": "^0.1.1", 17 | "jasmine-node": "^1.14.5", 18 | "jasmine-spec-reporter": "^4.1.0", 19 | "mocha": "^3.4.1" 20 | }, 21 | "keywords": [ 22 | "Metawear", 23 | "mbientlab", 24 | "Bluetooth", 25 | "BTLE", 26 | "Noble", 27 | "Bluetooth Low Energy" 28 | ], 29 | "bugs": { 30 | "url": "https://github.com/brainexe/node-metawear/issues" 31 | }, 32 | "repository": "git@github.com:brainexe/node-metawear.git", 33 | "scripts": { 34 | "test": "cd tests && node jasmine-runner.js" 35 | }, 36 | "author": "Matthias Dötsch ", 37 | "contributors": [ 38 | "Alan Hortz " 39 | ], 40 | "license": "MIT" 41 | } 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/brainexe/node-metawear.svg?branch=master)](https://travis-ci.org/brainexe/node-metawear) 2 | [![Downloads](https://img.shields.io/npm/dt/node-metawear.svg)]() 3 | [![Dependencies](https://david-dm.org/brainexe/node-metawear.svg)]() 4 | 5 | # Introduction 6 | nodejs library for MetaWear platform from https://www.mbientlab.com/ 7 | 8 | # Installation 9 | Build package from repository: 10 | ``` 11 | git clone git@github.com:brainexe/node-metawear.git 12 | cd node-metawear 13 | npm install 14 | ``` 15 | An alternative is to install the NPM module directly: 16 | ``` 17 | npm install node-metawear 18 | ``` 19 | 20 | Additionally you have to follow some steps, depending of your OS 21 | 22 | ## Linux (Ubuntu / RaspberryPi) 23 | Install required system packages 24 | ``` 25 | sudo apt-get install bluetooth bluez-utils libbluetooth-dev 26 | ``` 27 | This command grants the node binary cap_net_raw privileges, so it can start/stop BLE advertising. Then you don't need the "sudo" prefix anymore: 28 | ``` 29 | sudo setcap cap_net_raw+eip $(eval readlink -f `which node`) 30 | ``` 31 | 32 | ## ChromeOS 33 | Same as Linux, but the main difference here is that you’ll need to run Node through host-dbus, since it’s accessing the Bluetooth adapter attached through Chrome OS. 34 | ``` 35 | sudo setcap cap_net_raw+eip $(eval readlink -f `which node`) 36 | export DEBUG="noble-device" 37 | host-dbus node examples/all.js 38 | ``` 39 | thx to [Lance](http://www.polyglotprogramminginc.com/using-metawear-with-node-js/) 40 | 41 | ## Mac OS 42 | ``` 43 | npm install 44 | DEBUG="noble-device" node examples/all.js 45 | ``` 46 | 47 | ## Run 48 | Run examples in debug output 49 | ``` 50 | DEBUG="noble-device" node examples/all.js 51 | ``` 52 | 53 | ## Functions 54 | The functionality is very limited at the moment: 55 | - Connection Parameters setup 56 | - Control LED 57 | - Start buzzer/motor 58 | - Read/Set Device name 59 | - Read switch status (+ pressed/release events) 60 | - Read out battery status 61 | - Accelerometer / Gyroscope 62 | - Step counter 63 | - Temperature sensor 64 | - Ambient light sensor 65 | - Barometer sensor 66 | - BMM150 Magnetometer 67 | - QUATERNION data from SensorFusion, ONLY FOR METAMOTION R Boards !! 68 | 69 | ## Next release -> v1.1.0 70 | - SensorFusion - NDOF mode support for all output types 71 | - Acceleromter - TAP_CONFIG, ORIENT_CONFIG, FLAT_CONFIG, LOW_HIGH_G_CONFIG 72 | - Refactoring of the sensor configuration implementation 73 | 74 | Note : The versioning restarted at v1.0.0 in order to better handle the major and minor releases as from now. 75 | 76 | ## Run unit tests 77 | ``` 78 | npm install -g jasmine 79 | npm test 80 | ``` 81 | -------------------------------------------------------------------------------- /src/data-source.js: -------------------------------------------------------------------------------- 1 | 2 | var registers = require('./registers/registers'); 3 | 4 | const 5 | DISABLE = 0x0, 6 | ENABLE = 0x1; 7 | NO_DATA_ID = 0xff; 8 | 9 | var DataSourceId = { 10 | SWITCH : 0, 11 | ACCEL_MMA8452Q : 1, 12 | ACCEL_BMI160 : 2, 13 | BARO_BMP280_PRESSURE : 3, 14 | BARO_BMP280_ALTITUDE : 4, 15 | GYRO_BMI160 : 5, 16 | ALS_LTR329 : 6 17 | }; 18 | 19 | var DataType = { 20 | UINT : 0, 21 | INT : 1, 22 | BMP280_PRESSURE : 2, 23 | BMP280_ALTITUDE : 3, 24 | BMI160_THREE_AXIS : 4, 25 | MMA8452Q_THREE_AXIS : 5 26 | }; 27 | 28 | var ProcessorId = { 29 | ACCUMULATOR : 0x2, 30 | COMPARISON : 0x6, 31 | MATH : 0x9 32 | }; 33 | 34 | var DataSource = function(module_id, register_id, data_id, data_type_id, is_signed, length, offset) { 35 | this.module_id = module_id; 36 | this.register_id = register_id; 37 | this.data_id = data_id; 38 | this.data_type_id = data_type_id; 39 | this.is_signed = is_signed; 40 | this.length = length; 41 | this.offset = offset; 42 | }; 43 | 44 | var dataSources = [ 45 | new DataSource(registers.SWITCH, DataSourceId.SWITCH_DATA, NO_DATA_ID, DataType.UINT, 0, 1, 0), 46 | new DataSource(registers.ACC_MMA8452Q, DataSourceId.ACC_MMA8452Q_DATA, NO_DATA_ID, DataType.MMA8452Q_THREE_AXIS, 1, 6, 0), 47 | new DataSource(registers.ACC_BMI160, DataSourceId.ACC_BMI160_DATA, NO_DATA_ID, DataType.BMI160_THREE_AXIS, 1, 6, 0), 48 | new DataSource(registers.BAROMETER, DataSourceId.BARO_BMP280_PRESSURE_DATA, NO_DATA_ID, DataType.BMP280_PRESSURE, 0, 4, 0), 49 | new DataSource(registers.BAROMETER, DataSourceId.BARO_BMP280_ALTITUDE_DATA, NO_DATA_ID, DataType.BMP280_ALTITUDE, 1, 4, 0), 50 | new DataSource(registers.GYRO, DataSourceId.GYRO_BMI160_DATA, NO_DATA_ID, DataType.BMI160_THREE_AXIS, 1, 6, 0), 51 | new DataSource(registers.ALS_LTR329, DataSourceId.ALS_LTR329_DATA, NO_DATA_ID, DataType.UINT, 0, 4, 0) 52 | ]; 53 | -------------------------------------------------------------------------------- /src/device.js: -------------------------------------------------------------------------------- 1 | var 2 | NobleDevice = require('noble-device'), 3 | EventEmitter = require('eventemitter2').EventEmitter2, 4 | registers = require('./registers/registers'), 5 | debug = require('debug')('noble-device'), 6 | HashMap = require( 'hashmap' ); 7 | 8 | const BASE_URI = '326a#id#85cb9195d9dd464cfbbae75a', 9 | SERVICE_UUID = BASE_URI.replace('#id#', '9000'); 10 | COMMAND_UUID = BASE_URI.replace('#id#', '9001'); 11 | NOTIFY_UUID = BASE_URI.replace('#id#', '9006'); 12 | 13 | var Device = function(peripheral) { 14 | debug('start'); 15 | NobleDevice.call(this, peripheral); 16 | 17 | this.emitter = new EventEmitter({ 18 | wildcard: true, 19 | maxListeners: 30 20 | }); 21 | //this.emitter.setMaxListeners(11); 22 | this.logReferenceTicks = new HashMap(); 23 | }; 24 | 25 | Device.SCAN_UUIDS = [SERVICE_UUID]; 26 | 27 | NobleDevice.Util.inherits(Device, NobleDevice); 28 | NobleDevice.Util.mixin(Device, NobleDevice.BatteryService); 29 | NobleDevice.Util.mixin(Device, NobleDevice.DeviceInformationService); 30 | 31 | 32 | Device.prototype.connectAndSetup = function(callback) { 33 | var self = this; 34 | 35 | NobleDevice.prototype.connectAndSetup.call(self, function(){ 36 | 37 | self.notifyCharacteristic(SERVICE_UUID, NOTIFY_UUID, true, self._onRead.bind(self), function(err){ 38 | if (err) throw err; 39 | 40 | self.emit('ready',err); 41 | callback(err); 42 | }); 43 | }); 44 | }; 45 | 46 | Device.prototype._onRead = function(buffer) { 47 | var tmp = buffer.slice(0, 2); 48 | 49 | var module = tmp[0]; 50 | var action = tmp[1] & 0x1f; 51 | var data = buffer.slice(2); 52 | 53 | this.emitter.emit([module, action], data, module.toString(16), action.toString(16)); 54 | 55 | 56 | 57 | if(module == registers['LOGGING']) { 58 | var referenceTick = this.Log.getLoggingTick(data); 59 | if(referenceTick.restUid != -1) { 60 | this.logReferenceTicks.set(referenceTick.restUid, referenceTick); 61 | } 62 | } 63 | 64 | debug('', 65 | "received", 66 | registers.byId[module], 67 | action.toString(16), 68 | buffer 69 | ); 70 | }; 71 | 72 | Device.prototype.send = function(data, callback) { 73 | debug('', 'send', registers.byId[data[0]], data); 74 | 75 | this.writeDataCharacteristic(SERVICE_UUID, COMMAND_UUID, data, callback || function(error) { 76 | if (error) { 77 | console.log("error while sending data: ", error, data); 78 | } 79 | }); 80 | }; 81 | 82 | Device.prototype.sendRead = function(data, callback) { 83 | data[1] |= 0x80; // change register opcode 84 | debug('', "sendRead", registers.byId[data[0]], data); 85 | 86 | this.writeDataCharacteristic(SERVICE_UUID, COMMAND_UUID, data, callback || function(error) { 87 | if (error) { 88 | console.log("error while sending data: ", error, data); 89 | } 90 | }); 91 | }; 92 | 93 | Device.prototype.Accelerometer = require('./registers/accelerometer'); 94 | Device.prototype.AmbiantLight = require('./registers/ambient-light'); 95 | Device.prototype.Barometer = require('./registers/barometer'); 96 | Device.prototype.DataProcessing = require('./registers/data-processing'); 97 | Device.prototype.Debug = require('./registers/debug'); 98 | Device.prototype.Event = require('./registers/event'); 99 | Device.prototype.Gpio = require('./registers/gpio'); 100 | Device.prototype.Gyro = require('./registers/gyro'); 101 | Device.prototype.Haptic = require('./registers/haptic'); 102 | Device.prototype.I2C = require('./registers/i2c'); 103 | Device.prototype.IBeacon = require('./registers/ibeacon'); 104 | Device.prototype.Led = require('./registers/led'); 105 | Device.prototype.Log = require('./registers/log'); 106 | Device.prototype.Macro = require('./registers/macro'); 107 | Device.prototype.Magnetometer = require('./registers/magnetometer'); 108 | Device.prototype.NeoPixel = require('./registers/neo-pixel'); 109 | Device.prototype.Settings = require('./registers/settings'); 110 | Device.prototype.SensorFusion = require('./registers/sensorFusion'); 111 | Device.prototype.Switch = require('./registers/switch'); 112 | Device.prototype.Temperature = require('./registers/temperature'); 113 | Device.prototype.Timer = require('./registers/timer'); 114 | 115 | module.exports = Device; 116 | -------------------------------------------------------------------------------- /src/registers/accelerometer.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | var util = require('./config/util'); 4 | 5 | const MODULE_OPCODE = 0x03; 6 | 7 | const X_OFFSET = 0, 8 | Y_OFFSET = 2, 9 | Z_OFFSET = 4; 10 | 11 | const 12 | POWER_MODE = 0x1, 13 | DATA_INTERRUPT_ENABLE = 0x2, 14 | DATA_CONFIG = 0x3, 15 | DATA_INTERRUPT = 0x4, 16 | DATA_INTERRUPT_CONFIG = 0x5; 17 | LOW_HIGH_G_INTERRUPT_ENABLE = 0x6; 18 | LOW_HIGH_G_CONFIG = 0x7; 19 | LOW_HIGH_G_INTERRUPT = 0x8; 20 | MOTION_CONFIG = 0xa; 21 | MOTION_INTERRUPT = 0xb; 22 | TAP_INTERRUPT_ENABLE = 0xc; 23 | TAP_CONFIG = 0xd; 24 | ORIENT_INTERRUPT_ENABLE = 0xf; 25 | ORIENT_CONFIG = 0x10; 26 | ORIENT_INTERRUPT = 0x11; 27 | FLAT_INTERRUPT_ENABLE = 0x12; 28 | FLAT_CONFIG = 0x13; 29 | FLAT_INTERRUPT = 0x14; 30 | STEP_DETECTOR_INTERRUPT_ENABLE = 0x17; 31 | STEP_DETECTOR_CONFIG = 0x18; 32 | STEP_DETECTOR_INTERRUPT = 0x19; 33 | STEP_COUNTER_DATA = 0x1a; 34 | STEP_COUNTER_RESET = 0x1b; 35 | 36 | const BOARD_ORIENTATION = { 37 | FACE_UP_PORTRAIT_UPRIGHT: 0x0, 38 | FACE_UP_PORTRAIT_UPSIDE_DOWN: 0x1, 39 | FACE_UP_LANDSCAPE_LEFT: 0x2, 40 | FACE_UP_LANDSCAPE_RIGHT: 0x3, 41 | FACE_DOWN_PORTRAIT_UPRIGHT: 0x4, 42 | FACE_DOWN_PORTRAIT_UPSIDE_DOWN: 0x5, 43 | FACE_DOWN_LANDSCAPE_LEFT: 0x6, 44 | FACE_DOWN_LANDSCAPE_RIGHT: 0x7 45 | }; 46 | 47 | // Operating frequency of the accelerometer. Unit: "hz" 48 | const OUTPUT_DATA_RATE = { 49 | "0.78125": 0x1, 50 | "1.5625": 0x2, 51 | "3.125": 0x3, 52 | "6.25": 0x4, 53 | "12.5": 0x5, 54 | "25.0": 0x6, 55 | "50.0": 0x7, 56 | "100.0": 0x8, 57 | "200.0": 0xa, 58 | "400.0": 0xb, 59 | "800.0": 0xc, 60 | "1600.0": 0xd, 61 | "3200.0": 0xe 62 | }; 63 | 64 | // Supported g-ranges for the accelerometer. Unit "g" 65 | const ACC_RANGE = { 66 | //g bitmask, scale 67 | 2: [0x3, 16384], 68 | 4: [0x5, 8192], 69 | 8: [0x8, 4096], 70 | 16: [0xc, 2048] 71 | }; 72 | 73 | var Accelerometer = function(device) { 74 | this.device = device; 75 | this.setOutputDataRate(50); 76 | this.setAxisSamplingRange(2); 77 | }; 78 | 79 | Accelerometer.prototype.ACC_RANGE = ACC_RANGE; 80 | Accelerometer.prototype.OUTPUT_DATA_RATE = OUTPUT_DATA_RATE; 81 | 82 | Accelerometer.prototype.start = function() { 83 | 84 | 85 | // TODO : Move the respective configuration into a proper method 86 | 87 | // var buffer = new Buffer(4); 88 | // buffer[0] = MODULE_OPCODE; 89 | // buffer[1] = TAP_CONFIG; 90 | // buffer[2] = 0x04; // TODO configurable values 91 | // buffer[3] = 0x0a; 92 | // this.device.send(buffer); 93 | 94 | // buffer = new Buffer(6); 95 | // buffer[0] = MODULE_OPCODE; 96 | // buffer[1] = MOTION_CONFIG; 97 | // buffer[2] = 0x00; // TODO configurable values 98 | // buffer[3] = 0x14; 99 | // buffer[4] = 0x14; 100 | // buffer[5] = 0x14; 101 | // this.device.send(buffer); 102 | 103 | // buffer = new Buffer(4); 104 | // buffer[0] = MODULE_OPCODE; 105 | // buffer[1] = DATA_CONFIG; 106 | // buffer[2] = 0x20 | this.dataRate; 107 | // buffer[3] = this.accRange[0]; 108 | // this.device.send(buffer); 109 | 110 | // buffer = new Buffer(7); 111 | // buffer[0] = MODULE_OPCODE; 112 | // buffer[1] = LOW_HIGH_G_CONFIG; 113 | // buffer[2] = 0x07; // TODO configurable values 114 | // buffer[3] = 0x30; 115 | // buffer[4] = 0x81; 116 | // buffer[5] = 0x0b; 117 | // buffer[6] = 0xc0; 118 | // this.device.send(buffer); 119 | 120 | var buffer = new Buffer(3); 121 | buffer[0] = MODULE_OPCODE; 122 | buffer[1] = POWER_MODE; 123 | buffer[2] = 0x1; 124 | this.device.send(buffer); 125 | }; 126 | 127 | Accelerometer.prototype.enableStepDetector = function(callback, sensitivity) { 128 | var config = new Buffer(2); 129 | switch (sensitivity) { 130 | case 'ROBUST': 131 | config[0] = 0x1d; 132 | config[1] = 0x7; 133 | break; 134 | case 'SENSITIVE': 135 | config[0] = 0x2d; 136 | config[1] = 0x3; 137 | break; 138 | case 'NORMAL': 139 | default: 140 | config[0] = 0x15; 141 | config[1] = 0x3; 142 | break; 143 | } 144 | 145 | var buffer = new Buffer(4); 146 | buffer[0] = MODULE_OPCODE; 147 | buffer[1] = STEP_DETECTOR_CONFIG; 148 | buffer[2] = config[0]; 149 | buffer[3] = config[1] | 0x08; 150 | this.device.send(buffer); 151 | 152 | buffer = new Buffer(4); 153 | buffer[0] = MODULE_OPCODE; 154 | buffer[1] = STEP_DETECTOR_INTERRUPT_ENABLE; 155 | buffer[2] = 0x1; 156 | buffer[3] = 0x0; 157 | this.device.send(buffer); 158 | 159 | buffer = new Buffer(3); 160 | buffer[0] = MODULE_OPCODE; 161 | buffer[1] = STEP_DETECTOR_INTERRUPT; 162 | buffer[2] = 0x1; 163 | this.device.send(buffer); 164 | 165 | this.device.emitter.on([MODULE_OPCODE, STEP_DETECTOR_INTERRUPT], function() { 166 | callback(); 167 | }); 168 | }; 169 | 170 | Accelerometer.prototype.disableStepDetector = function() { 171 | var buffer = new Buffer(4); 172 | buffer[0] = MODULE_OPCODE; 173 | buffer[1] = STEP_DETECTOR_INTERRUPT_ENABLE; 174 | buffer[2] = 0x0; 175 | buffer[3] = 0x1; 176 | this.device.send(buffer); 177 | }; 178 | 179 | Accelerometer.prototype.resetStepCounter = function() { 180 | var buffer = new Buffer(2); 181 | buffer[0] = MODULE_OPCODE; 182 | buffer[1] = STEP_COUNTER_RESET; 183 | this.device.send(buffer); 184 | }; 185 | 186 | Accelerometer.prototype.readStepCounter = function(callback, silent) { 187 | var buffer = new Buffer(3); 188 | buffer[0] = MODULE_OPCODE; 189 | buffer[1] = STEP_COUNTER_DATA; 190 | buffer[2] = silent; 191 | this.device.sendRead(buffer); 192 | 193 | this.device.emitter.once([MODULE_OPCODE, STEP_COUNTER_DATA], function(buffer) { 194 | callback(buffer.readInt16LE(0)); 195 | }); 196 | }; 197 | 198 | Accelerometer.prototype.onChange = function(callback) { 199 | var scale = this.accRange[1]; 200 | 201 | this.device.emitter.on([MODULE_OPCODE, DATA_INTERRUPT], function(buffer) { 202 | var formatted = { 203 | x: buffer.readInt16LE(X_OFFSET) / scale, 204 | y: buffer.readInt16LE(Y_OFFSET) / scale, 205 | z: buffer.readInt16LE(Z_OFFSET) / scale 206 | }; 207 | callback(formatted); 208 | }); 209 | }; 210 | 211 | // TODO : Rename setConfig to writeConfig 212 | Accelerometer.prototype.setConfig = function() { 213 | var buffer = new Buffer(4); 214 | buffer[0] = MODULE_OPCODE; 215 | buffer[1] = DATA_CONFIG; 216 | buffer[2] = 0x20 | this.dataRate; 217 | buffer[3] = this.accRange[0]; 218 | this.device.send(buffer); 219 | }; 220 | 221 | /** 222 | * Set the sampling range. between 2g and 16g 223 | * @param {Number} rate 224 | */ 225 | Accelerometer.prototype.setAxisSamplingRange = function(rate) { 226 | this.accRange = util.findClosestValue(ACC_RANGE, rate); 227 | }; 228 | 229 | /** 230 | * Set output data rate in HZ 231 | * @param {Number} frequency 232 | */ 233 | Accelerometer.prototype.setOutputDataRate = function(frequency) { 234 | this.dataRate = util.findClosestValue(OUTPUT_DATA_RATE, frequency); 235 | }; 236 | 237 | Accelerometer.prototype.stop = function() { 238 | var buffer = new Buffer(3); 239 | buffer[0] = MODULE_OPCODE; 240 | buffer[1] = POWER_MODE; 241 | buffer[2] = 0x0; 242 | this.device.send(buffer); 243 | }; 244 | // TODO rename to unsubscribe to me more consistent 245 | Accelerometer.prototype.enableNotifications = function() { 246 | var buffer = new Buffer(3); 247 | buffer[0] = MODULE_OPCODE; 248 | buffer[1] = DATA_INTERRUPT; 249 | buffer[2] = 0x1; 250 | this.device.send(buffer); 251 | }; 252 | Accelerometer.prototype.unsubscribe = function() { 253 | var buffer = new Buffer(3); 254 | buffer[0] = MODULE_OPCODE; 255 | buffer[1] = DATA_INTERRUPT; 256 | buffer[2] = 0x0; 257 | this.device.send(buffer); 258 | }; 259 | 260 | Accelerometer.prototype.enableAxisSampling = function() { 261 | var buffer = new Buffer(4); 262 | buffer[0] = MODULE_OPCODE; 263 | buffer[1] = DATA_INTERRUPT_ENABLE; 264 | buffer[2] = 0x1; 265 | buffer[3] = 0x0; 266 | this.device.send(buffer); 267 | }; 268 | 269 | Accelerometer.prototype.disableAxisSampling = function() { 270 | var buffer = new Buffer(4); 271 | buffer[0] = MODULE_OPCODE; 272 | buffer[1] = DATA_INTERRUPT_ENABLE; 273 | buffer[2] = 0x0; 274 | buffer[3] = 0x1; 275 | this.device.send(buffer); 276 | }; 277 | 278 | module.exports = Accelerometer; 279 | -------------------------------------------------------------------------------- /src/registers/ambient-light.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x14; 3 | 4 | const 5 | ENABLE = 0x1, 6 | CONFIG = 0x2, 7 | OUTPUT = 0x3; 8 | 9 | var AmbientLight = function(device) { 10 | this.device = device; 11 | this.lastValue = null; 12 | }; 13 | 14 | AmbientLight.prototype.enable = function(callback) { 15 | var buffer = new Buffer(3); 16 | buffer[0] = MODULE_OPCODE; 17 | buffer[1] = OUTPUT; 18 | buffer[2] = 0x1; 19 | this.device.send(buffer); 20 | 21 | buffer = new Buffer(3); 22 | buffer[0] = MODULE_OPCODE; 23 | buffer[1] = ENABLE; 24 | buffer[2] = 0x1; 25 | this.device.send(buffer); 26 | 27 | this.device.emitter.on([MODULE_OPCODE, OUTPUT], function(buffer) { 28 | var lux = buffer.readUInt16LE(0) / 1000; 29 | 30 | if (lux != this.lastValue) { 31 | callback(lux); 32 | this.lastValue = lux; 33 | } 34 | }.bind(this)); 35 | }; 36 | 37 | AmbientLight.prototype.config = function(rate, time, gain) { 38 | //var ltr329Rate = 4; 39 | //var ltr329Time = 1; 40 | //var ltr329Gain = 1; 41 | 42 | var buffer = new Buffer(4); 43 | buffer[0] = MODULE_OPCODE; 44 | buffer[1] = CONFIG; 45 | buffer[2] = gain.mask << 2; 46 | buffer[3] = (time.mask << 3) | rate; 47 | this.device.send(buffer); 48 | }; 49 | 50 | AmbientLight.prototype.disable = function() { 51 | var buffer = new Buffer(3); 52 | buffer[0] = MODULE_OPCODE; 53 | buffer[1] = ENABLE; 54 | buffer[2] = 0x0; 55 | this.device.send(buffer); 56 | }; 57 | 58 | module.exports = AmbientLight; 59 | -------------------------------------------------------------------------------- /src/registers/barometer.js: -------------------------------------------------------------------------------- 1 | 2 | var Config = require('./config/barometer'); 3 | 4 | const MODULE_OPCODE = 0x12; 5 | 6 | const 7 | PRESSURE = 0x1, 8 | ALTITUDE = 0x2, 9 | CONFIG = 0x3, 10 | CYCLIC = 0x4; 11 | 12 | const SCALE = 25600; // TODO 256? 13 | 14 | var Barometer = function(device) { 15 | this.device = device; 16 | this.config = new Config(); 17 | this.lastValue = null; 18 | }; 19 | 20 | Barometer.prototype.enablePressure = function(callback) { 21 | var buffer = new Buffer(3); 22 | buffer[0] = MODULE_OPCODE; 23 | buffer[1] = PRESSURE; 24 | buffer[2] = 0x1; 25 | this.device.send(buffer); 26 | 27 | buffer = new Buffer(4); 28 | buffer[0] = MODULE_OPCODE; 29 | buffer[1] = CYCLIC; 30 | buffer[2] = 0x1; 31 | buffer[4] = 0x1; 32 | this.device.send(buffer); 33 | 34 | this.device.emitter.on([MODULE_OPCODE, PRESSURE], function(buffer) { 35 | var pressure = ~~(buffer.readInt32LE(0) / SCALE); 36 | if (pressure != this.lastValue) { 37 | this.lastValue = pressure; 38 | callback(pressure); 39 | } 40 | }.bind(this)); 41 | 42 | this.device.send(buffer); 43 | }; 44 | 45 | Barometer.prototype.commitConfig = function() { 46 | var buffer = new Buffer(4); 47 | buffer[0] = MODULE_OPCODE; 48 | buffer[1] = CONFIG; 49 | buffer[2] = this.config.oversamplingMode << 2; 50 | buffer[3] = (this.config.filterMode << 2) | (this.config.standbyTime << 5); 51 | this.device.send(buffer) 52 | }; 53 | 54 | Barometer.prototype.disable = function() { 55 | var buffer = new Buffer(4); 56 | buffer[0] = MODULE_OPCODE; 57 | buffer[1] = CYCLIC; 58 | buffer[2] = 0x0; 59 | buffer[3] = 0x0; 60 | this.device.send(buffer); 61 | }; 62 | 63 | module.exports = Barometer; 64 | -------------------------------------------------------------------------------- /src/registers/config/ambient-light.js: -------------------------------------------------------------------------------- 1 | 2 | function AmbientLightConfig() { 3 | this.GAIN = { 4 | GAIN_1X: 0, // range between [1, 64k] lux (default) 5 | GAIN_2X: 1, // range between [0.5, 32k] lux 6 | GAIN_4X: 2, // range between [0.25, 16k] lux 7 | GAIN_8X: 3, // range between [0.125, 8k] lux 8 | GAIN_48X: 6, // range between [0.02, 1.3k] lux 9 | GAIN_96X: 7 // range between [0.01, 600] lux 10 | }; 11 | 12 | this.INTEGRATION_TIME = { 13 | TIME_100MS : 0, 14 | TIME_50MS : 1, 15 | TIME_200MS : 2, 16 | TIME_400MS : 3, 17 | TIME_150MS : 4, 18 | TIME_250MS : 5, 19 | TIME_300MS : 6, 20 | TIME_350MS : 7 21 | }; 22 | 23 | this.MESSURE_TIME = { 24 | RATE_50MS : 0, 25 | RATE_100MS : 1, 26 | RATE_200MS : 2, 27 | RATE_500MS : 3, // Default setting 28 | RATE_1000MS : 4, 29 | RATE_2000MS : 5 30 | } 31 | } 32 | 33 | module.exports = AmbientLightConfig; 34 | -------------------------------------------------------------------------------- /src/registers/config/barometer.js: -------------------------------------------------------------------------------- 1 | 2 | function BarometerConfig() { 3 | this.SAMPLING_MODE = { 4 | SKIP: 0, 5 | ULTRA_LOW_POWER: 1, 6 | LOW_POWER: 2, 7 | STANDARD: 3, 8 | HIGH: 4, 9 | ULTRA_HIGH: 5 10 | }; 11 | 12 | this.FILTER_MODE = { 13 | OFF: 0, 14 | AVG_2: 1, 15 | AVG_4: 2, 16 | AVG_8: 3, 17 | AVG_16: 4 18 | }; 19 | 20 | this.STANDBY_TIME = { 21 | TIME_0_5: 0, 22 | TIME_62_5: 1, 23 | TIME_125: 2, 24 | TIME_250: 3, 25 | TIME_500: 4, 26 | TIME_1000: 5, 27 | TIME_2000: 6, 28 | TIME_4000: 7 29 | }; 30 | 31 | this.filterMode = this.FILTER_MODE.OFF; 32 | this.standbyTime = this.STANDBY_TIME.TIME_125; 33 | this.samplingMode = this.SAMPLING_MODE.STANDARD; 34 | } 35 | 36 | module.exports = BarometerConfig; 37 | -------------------------------------------------------------------------------- /src/registers/config/gyro.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('./util'); 3 | 4 | function GyroConfig () { 5 | this.RATE_MAP = { 6 | 25: 6, 7 | 50: 7, 8 | 100: 8, 9 | 200: 9, 10 | 400: 10, 11 | 800: 11, 12 | 1600: 12, 13 | 3200: 13 14 | }; 15 | 16 | this.RANGE_MAP = { 17 | 2000: [0, 16.4], // +/-2000 degrees per second 18 | 1000: [1, 32.8], 19 | 500: [2, 65.6], 20 | 250: [3, 131.2], 21 | 125: [4, 262.4] 22 | }; 23 | 24 | this.scale = 0.01; 25 | this.setRange(125); 26 | this.setRate(25); 27 | } 28 | 29 | GyroConfig.prototype.setRate = function(hz) { 30 | this.frequency = util.findClosestValue(this.RATE_MAP, hz); 31 | }; 32 | 33 | GyroConfig.prototype.setRange = function(range) { 34 | this.range = util.findClosestValue(this.RANGE_MAP, range)[0]; 35 | }; 36 | 37 | module.exports = GyroConfig; 38 | -------------------------------------------------------------------------------- /src/registers/config/led.js: -------------------------------------------------------------------------------- 1 | 2 | var LedConfig = function() { 3 | this.GREEN = 0x0; 4 | this.RED = 0x1; 5 | this.BLUE = 0x2; 6 | 7 | this.REPEAT_INDEFINITELY = 0xff; 8 | 9 | this.buffer = new Buffer(15); 10 | this.buffer.fill(0); 11 | 12 | this.buffer[1] = 0x2; 13 | }; 14 | 15 | LedConfig.prototype.setColor = function(color) { 16 | this.buffer[0] = color; 17 | 18 | return this; 19 | }; 20 | 21 | LedConfig.prototype.setHighIntensity = function(intensity) { 22 | this.buffer[2] = intensity; 23 | return this; 24 | }; 25 | 26 | LedConfig.prototype.setLowIntensity = function(intensity) { 27 | this.buffer[3] = intensity; 28 | return this; 29 | }; 30 | 31 | LedConfig.prototype.setRiseTime = function(time) { 32 | this.buffer[5] = (time >> 8) & 0xff; 33 | this.buffer[4] = time & 0xff; 34 | return this; 35 | }; 36 | 37 | LedConfig.prototype.setHighTime = function(time) { 38 | this.buffer[7] = (time >> 8) & 0xff; 39 | this.buffer[6] = time & 0xff; 40 | return this; 41 | }; 42 | 43 | LedConfig.prototype.setFallTime = function(time) { 44 | this.buffer[9] = (time >> 8) & 0xff; 45 | this.buffer[8] = time & 0xff; 46 | return this; 47 | }; 48 | 49 | LedConfig.prototype.setPulseDuration = function(duration) { 50 | this.buffer[11] = (duration >> 8) & 0xff; 51 | this.buffer[10] = duration & 0xff; 52 | return this; 53 | }; 54 | 55 | LedConfig.prototype.setRepeatCount = function(count) { 56 | this.buffer[14] = count; 57 | return this; 58 | }; 59 | 60 | LedConfig.prototype.getBuffer = function() { 61 | return this.buffer; 62 | }; 63 | 64 | LedConfig.prototype.patternBlink = function() { 65 | this.setHighIntensity(31); 66 | this.setLowIntensity(0); 67 | this.setRiseTime(0); 68 | this.setHighTime(50); 69 | this.setFallTime(0); 70 | this.setPulseDuration(500); 71 | }; 72 | 73 | LedConfig.prototype.patternPulse = function() { 74 | this.setHighIntensity(31); 75 | this.setLowIntensity(31); 76 | this.setRiseTime(725); 77 | this.setHighTime(500); 78 | this.setFallTime(725); 79 | this.setPulseDuration(2000); 80 | }; 81 | 82 | LedConfig.prototype.patternSolid = function() { 83 | this.setHighIntensity(31); 84 | this.setLowIntensity(31); 85 | this.setRiseTime(0); 86 | this.setHighTime(500); 87 | this.setFallTime(0); 88 | this.setPulseDuration(1000); 89 | }; 90 | 91 | module.exports = LedConfig; 92 | -------------------------------------------------------------------------------- /src/registers/config/neo-pixel.js: -------------------------------------------------------------------------------- 1 | 2 | function NeoPixelConfig() { 3 | this.ORDERING = { 4 | WS2811_RGB: 0, 5 | WS2811_RBG: 1, 6 | WS2811_GRB: 2, 7 | WS2811_GBR: 3 8 | }; 9 | 10 | this.DIRECTION = { 11 | ROT_DIR_TOWARDS: 0, 12 | ROT_DIR_AWAY: 1 13 | }; 14 | } 15 | 16 | module.exports = AccelerometerConfig; 17 | -------------------------------------------------------------------------------- /src/registers/config/sensorFusion.js: -------------------------------------------------------------------------------- 1 | var SensorFusionConfig = function() { 2 | this.mode = SensorFusionConfig.MODE.SLEEP; 3 | this.acc_range = SensorFusionConfig.ACC_RANGE.AR_16G; 4 | this.gyro_range = SensorFusionConfig.GYRO_RANGE.GR_2000DPS; 5 | }; 6 | 7 | const config_masks = [ 8 | [0x10, 0x11, 0x12, 0x13], 9 | [0x20, 0x21, 0x22, 0x23], 10 | [0x30, 0x31, 0x32, 0x33], 11 | [0x40, 0x41, 0x42, 0x43] 12 | ]; 13 | 14 | SensorFusionConfig.CALIBRATION_ACCURACY = { 15 | UNRELIABLE: 0x0, 16 | LOW: 0x1, 17 | MEDIUM: 0x2, 18 | HIGH: 0x3 19 | }; 20 | 21 | SensorFusionConfig.MODE = { 22 | SLEEP: 0x0, 23 | NDOF: 0x1, 24 | IMU_PLUS: 0x2, 25 | COMPASS: 0x3, 26 | M4G: 0x4 27 | }; 28 | 29 | SensorFusionConfig.ACC_RANGE = { 30 | AR_2G: 0x0, 31 | AR_4G: 0x1, 32 | AR_8G: 0x2, 33 | AR_16G: 0x3 34 | }; 35 | 36 | SensorFusionConfig.GYRO_RANGE = { 37 | GR_2000DPS: 0x0, 38 | GR_1000DPS: 0x1, 39 | GR_500DPS: 0x2, 40 | GR_250DPS: 0x3 41 | }; 42 | 43 | SensorFusionConfig.prototype.setMode = function(mode) { 44 | this.mode = mode; 45 | }; 46 | SensorFusionConfig.prototype.setAccRange = function(acc_range) { 47 | this.acc_range = acc_range; 48 | }; 49 | SensorFusionConfig.prototype.setGyroRange = function(gyro_range) { 50 | this.gyro_range = gyro_range; 51 | }; 52 | SensorFusionConfig.prototype.getConfigMask = function() { 53 | return config_masks[this.gyro_range][this.acc_range]; 54 | }; 55 | 56 | module.exports = SensorFusionConfig; -------------------------------------------------------------------------------- /src/registers/config/util.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports.findClosestValue = function(map, given) { 3 | var current; 4 | 5 | for (current in map) { 6 | if (given <= current) { 7 | break; 8 | } 9 | } 10 | 11 | // todo improve 12 | return map[current]; 13 | 14 | if (i == map.length - 1) { 15 | // last element 16 | return map[current]; 17 | } 18 | 19 | var leftDist = boundedGiven - map[i]; 20 | var rightDist = map[i + 1] - boundedGiven; 21 | if (leftDist < rightDist) { 22 | return i; 23 | } else { 24 | return i + 1; 25 | } 26 | 27 | return map[map.length - 1]; 28 | }; 29 | -------------------------------------------------------------------------------- /src/registers/core.js: -------------------------------------------------------------------------------- 1 | var Core = {}; 2 | 3 | Core.isClose = function(fst, snd) { 4 | return Math.abs(fst - snd) <= Math.max( 0.001 * Math.max(Math.abs(fst), Math.abs(snd)), 0.001 ); 5 | }; 6 | 7 | Core.CartesianFloat = function(x,y,z) { 8 | this.x = x; 9 | this.y = y; 10 | this.z = z; 11 | }; 12 | 13 | Core.CartesianFloat.prototype.isEqual = function(cartesianFloat) { 14 | return Core.isClose(this.x, cartesianFloat.x) && Core.isClose(this.y, cartesianFloat.y) && Core.isClose(this.z, cartesianFloat.z); 15 | }; 16 | 17 | Core.CorrectedCartesianFloat = function(x, y, z, accuracy) { 18 | var that = new Core.CartesianFloat(x, y, z); 19 | that.accuracy = accuracy; 20 | return that; 21 | }; 22 | 23 | Core.CorrectedCartesianFloat.prototype.isEqual = function(correctedCartesianFloat) { 24 | return this.isEqual(correctedCartesianFloat) && (this.accuracy === correctedCartesianFloat.accuracy); 25 | }; 26 | 27 | Core.Quaternion = function(w, x, y, z) { 28 | this.w = w; 29 | this.x = x; 30 | this.y = y; 31 | this.z = z; 32 | }; 33 | 34 | Core.Quaternion.prototype.isEqual = function(quaternion) { 35 | return Core.isClose(this.w, quaternion.w) && Core.isClose(this.x, quaternion.x) && Core.isClose(this.y, quaternion.y) && Core.isClose(this.z, quaternion.z); 36 | }; 37 | 38 | Core.EulerAngle = function(heading, pitch, roll, yaw) { 39 | this.heading = heading; 40 | this.pitch = pitch; 41 | this.roll = roll; 42 | this.yaw = yaw; 43 | }; 44 | 45 | Core.EulerAngle.prototype.isEqual = function(eulerAngle) { 46 | return Core.isClose(this.heading, eulerAngle.heading) && Core.isClose(this.pitch, eulerAngle.pitch) && Core.isClose(this.roll, eulerAngle.roll) && Core.isClose(this.yaw, eulerAngle.yaw); 47 | }; 48 | 49 | module.exports = Core; -------------------------------------------------------------------------------- /src/registers/data-processing.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x09; 3 | 4 | const 5 | ADD = 0x2, 6 | NOTIFY = 0x3, 7 | STATE = 0x4, 8 | PARAMETER = 0x5, 9 | REMOVE = 0x6, 10 | NOTIFY_ENABLE = 0x7, 11 | REMOVE_ALL = 0x8; 12 | 13 | var COMPARISON = { 14 | EQ : 0, 15 | NEQ : 1, 16 | LT : 2, 17 | LTE : 3, 18 | GT : 4, 19 | GTE : 5 20 | }; 21 | 22 | var ARITHMETIC = { 23 | ADD : 1, 24 | MULTIPLY : 2, 25 | DIVIDE : 3, 26 | MODULUS : 4, 27 | EXPONENT : 5, 28 | SQRT : 6, 29 | L_SHIFT : 7, 30 | R_SHIFT : 8, 31 | SUBTRACT : 9, 32 | ABS_VALUE : 10 33 | }; 34 | 35 | var DataProcessing = function(device) { 36 | this.device = device; 37 | 38 | this.device.emitter.on([MODULE_OPCODE, NOTIFY], function(buffer) { 39 | // todo 40 | }); 41 | }; 42 | 43 | DataProcessing.prototype.enableNotification = function() { 44 | var buffer = new Buffer(3); 45 | buffer[0] = MODULE_OPCODE; 46 | buffer[1] = NOTIFY; 47 | buffer[2] = 0x1; 48 | 49 | this.device.send(buffer); 50 | 51 | var headerId = 2; // TODO? 52 | 53 | buffer = new Buffer(4); 54 | buffer[0] = MODULE_OPCODE; 55 | buffer[1] = NOTIFY_ENABLE; 56 | buffer[2] = headerId; 57 | buffer[3] = 0x1; 58 | this.device.send(buffer); 59 | }; 60 | 61 | DataProcessing.prototype.add = function() { 62 | // TODO 63 | //byte[] nextCfg = newProcessor.getFilterConfig(); 64 | //byte[] parentCfg= parent.getTriggerConfig(); 65 | //byte[] addFilter = new byte[parentCfg.length + nextCfg.length]; 66 | //System.arraycopy(parentCfg, 0, addFilter, 0, parentCfg.length); 67 | //System.arraycopy(nextCfg, 0, addFilter, parentCfg.length, nextCfg.length); 68 | //writeRegister(DataProcessorRegister.ADD, addFilter); 69 | }; 70 | 71 | module.exports = DataProcessing; 72 | -------------------------------------------------------------------------------- /src/registers/debug.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0xfe; 3 | 4 | const 5 | RESET_DEVICE = 0x1, 6 | JUMP_TO_BOOTLOADER = 0x2, 7 | DELAYED_RESET = 0x5, 8 | GAP_DISCONNECT = 0x6; 9 | 10 | var Debug = function(device) { 11 | this.device = device; 12 | }; 13 | 14 | Debug.prototype.reset = function() { 15 | var buffer = new Buffer(2); 16 | buffer[0] = MODULE_OPCODE; 17 | buffer[1] = RESET_DEVICE; 18 | this.device.send(buffer); 19 | }; 20 | 21 | Debug.prototype.bootloader = function() { 22 | var buffer = new Buffer(2); 23 | buffer[0] = MODULE_OPCODE; 24 | buffer[1] = JUMP_TO_BOOTLOADER; 25 | this.device.send(buffer); 26 | }; 27 | 28 | Debug.prototype.delayedReset = function() { 29 | var buffer = new Buffer(2); 30 | buffer[0] = MODULE_OPCODE; 31 | buffer[1] = DELAYED_RESET; 32 | this.device.send(buffer); 33 | }; 34 | Debug.prototype.disconnect = function() { 35 | var buffer = new Buffer(2); 36 | buffer[0] = MODULE_OPCODE; 37 | buffer[1] = GAP_DISCONNECT; 38 | this.device.send(buffer); 39 | }; 40 | 41 | module.exports = Debug; 42 | -------------------------------------------------------------------------------- /src/registers/event.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x0a; 3 | 4 | const 5 | ENTRY = 0x2, 6 | CMD_PARAMETERS = 0x3, 7 | REMOVE = 0x4, 8 | REMOVE_ALL = 0x5; 9 | 10 | var Event = function(device) { 11 | this.device = device; 12 | }; 13 | 14 | module.exports = Event; 15 | -------------------------------------------------------------------------------- /src/registers/gpio.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x05; 3 | 4 | /** 5 | * Useful android references: 6 | * 7 | * GPIO interface: https://github.com/mbientlab/Metawear-AndroidAPI/blob/04a18a8b3eb79669a3b3f96951953ff1991c26d6/library/src/main/java/com/mbientlab/metawear/module/Gpio.java 8 | * GPIO implementation: https://github.com/mbientlab/Metawear-AndroidAPI/blob/04a18a8b3eb79669a3b3f96951953ff1991c26d6/library/src/main/java/com/mbientlab/metawear/impl/DefaultMetaWearBoard.java#L3986 9 | * Register definition: https://github.com/mbientlab/Metawear-AndroidAPI/blob/26e2eee76c0d15ee68af01b8c5c7ddab96a532c6/library/src/main/java/com/mbientlab/metawear/impl/characteristic/GpioRegister.java 10 | * 11 | * For reading data the file "switch.js" is an easy example...just use "this.device.emitter" 12 | */ 13 | 14 | const 15 | SET_DO = 0x1, 16 | CLEAR_DO = 0x2, 17 | PULL_UP_DI = 0x3, 18 | PULL_DOWN_DI = 0x4, 19 | NO_PULL_DI = 0x5, 20 | READ_AI_ABS_REF = 0x6, 21 | READ_AI_ADC = 0x7, 22 | READ_DI = 0x8, 23 | PIN_CHANGE = 0x9, 24 | PIN_CHANGE_NOTIFY = 0xa, // ResponseHeader header= new ResponseHeader(response[0], response[1], response[2]); 25 | PIN_CHANGE_NOTIFY_ENABLE = 0xb; 26 | 27 | var Gpio = function(device) { 28 | this.device = device; 29 | 30 | this.ANALOG_READ_MODE = { 31 | ABS_REFERENCE : 0, 32 | ADC : 1 33 | }; 34 | 35 | this.PULL_MODE = { 36 | PULL_UP : 0, 37 | PULL_DOWN : 1, 38 | NO_PULL : 2 39 | }; 40 | this.PIN_CHANGE_TYPE = { 41 | RISING : 2, 42 | FALLING : 2, 43 | ANY : 3 44 | }; 45 | }; 46 | 47 | /** 48 | * @param {number} pin 49 | * @param {number} readMode 50 | * @param {function} callback 51 | * @param {boolean} silent 52 | */ 53 | Gpio.prototype.readAnalogIn = function(pin, readMode, callback, silent) { 54 | var method; 55 | if (readMode == this.ANALOG_READ_MODE.ADC) { 56 | method = READ_AI_ABS_REF; 57 | } else { 58 | method = READ_AI_ADC; 59 | } 60 | 61 | var buffer = new Buffer(4); 62 | buffer[0] = MODULE_OPCODE; 63 | buffer[1] = method; 64 | buffer[2] = silent ? 0 : 1; 65 | buffer[3] = pin; 66 | 67 | this.device.emitter.once([MODULE_OPCODE, method], function(buffer) { 68 | // todo https://github.com/mbientlab/Metawear-AndroidAPI/blob/04a18a8b3eb79669a3b3f96951953ff1991c26d6/library/src/main/java/com/mbientlab/metawear/impl/DefaultMetaWearBoard.java#L3060 69 | callback(buffer); 70 | }); 71 | 72 | this.device.sendRead(buffer); 73 | }; 74 | 75 | /** 76 | * @param {number} pin 77 | * @param {number} pullMode 78 | */ 79 | Gpio.prototype.setPinPullMode = function(pin, pullMode) { 80 | var method; 81 | switch (pullMode) { 82 | case this.PULL_MODE.PULL_UP: 83 | method = PULL_UP_DI; 84 | break; 85 | case this.PULL_MODE.PULL_DOWN: 86 | method = PULL_DOWN_DI; 87 | break; 88 | case this.PULL_MODE.NO_PULL: 89 | method = NO_PULL_DI; 90 | break; 91 | } 92 | 93 | var buffer = new Buffer(3); 94 | buffer[0] = MODULE_OPCODE; 95 | buffer[1] = method; 96 | buffer[2] = pin; 97 | 98 | this.device.send(buffer); 99 | }; 100 | 101 | /** 102 | * @param {number} pin 103 | * @param {function} callback 104 | * @param {boolean} silent 105 | */ 106 | Gpio.prototype.readDigitalIn = function(pin, callback, silent) { 107 | var buffer = new Buffer(4); 108 | buffer[0] = MODULE_OPCODE; 109 | buffer[1] = READ_DI; 110 | buffer[2] = silent ? 0 : 1; 111 | buffer[3] = pin; 112 | 113 | this.device.sendRead(buffer); 114 | 115 | this.device.emitter.once([MODULE_OPCODE, READ_DI], function(buffer) { 116 | // TODO 117 | callback(buffer); 118 | }); 119 | }; 120 | 121 | /** 122 | * @param {number} pin 123 | */ 124 | Gpio.prototype.setDigitalOut = function(pin) { 125 | var buffer = new Buffer(3); 126 | buffer[0] = MODULE_OPCODE; 127 | buffer[1] = SET_DO; 128 | buffer[2] = pin; 129 | 130 | this.device.send(buffer); 131 | }; 132 | 133 | /** 134 | * @param {number} pin 135 | */ 136 | Gpio.prototype.clearDigitalOut = function(pin) { 137 | var buffer = new Buffer(3); 138 | buffer[0] = MODULE_OPCODE; 139 | buffer[1] = CLEAR_DO; 140 | buffer[2] = pin; 141 | 142 | this.device.send(buffer); 143 | }; 144 | 145 | /** 146 | * @param {number} pin 147 | * @param {number} pinChangeType 148 | */ 149 | Gpio.prototype.setPinChangeType = function(pin, pinChangeType) { 150 | var buffer = new Buffer(3); 151 | buffer[0] = MODULE_OPCODE; 152 | buffer[1] = PIN_CHANGE; 153 | buffer[2] = pin; 154 | buffer[3] = pinChangeType; 155 | 156 | this.device.send(buffer); 157 | }; 158 | 159 | /** 160 | * @param {number} pin 161 | */ 162 | Gpio.prototype.startPinChangeDetection = function(pin) { 163 | var buffer = new Buffer(3); 164 | buffer[0] = MODULE_OPCODE; 165 | buffer[1] = PIN_CHANGE_NOTIFY_ENABLE; 166 | buffer[2] = pin; 167 | buffer[3] = 1; 168 | 169 | this.device.send(buffer); 170 | }; 171 | 172 | /** 173 | * @param {number} pin 174 | */ 175 | Gpio.prototype.stopPinChangeDetection = function(pin) { 176 | var buffer = new Buffer(3); 177 | buffer[0] = MODULE_OPCODE; 178 | buffer[1] = PIN_CHANGE_NOTIFY_ENABLE; 179 | buffer[2] = pin; 180 | buffer[3] = 0; 181 | 182 | this.device.send(buffer); 183 | }; 184 | 185 | /** 186 | * @param {number} pin 187 | */ 188 | Gpio.prototype.enableNotifications = function(pin, callback) { 189 | var buffer = new Buffer(3); 190 | buffer[0] = MODULE_OPCODE; 191 | buffer[1] = PIN_CHANGE_NOTIFY; 192 | buffer[2] = pin; 193 | buffer[3] = 1; 194 | 195 | this.device.send(buffer); 196 | 197 | this.device.emitter.on([MODULE_OPCODE, PIN_CHANGE_NOTIFY], function(buffer) { 198 | // TODO 199 | callback(buffer); 200 | }); 201 | }; 202 | 203 | /** 204 | * @param {number} pin 205 | */ 206 | Gpio.prototype.unsubscribe = function(pin) { 207 | var buffer = new Buffer(3); 208 | buffer[0] = MODULE_OPCODE; 209 | buffer[1] = PIN_CHANGE_NOTIFY; 210 | buffer[2] = pin; 211 | buffer[3] = 0; 212 | 213 | this.device.send(buffer); 214 | }; 215 | 216 | module.exports = Gpio; 217 | -------------------------------------------------------------------------------- /src/registers/gyro.js: -------------------------------------------------------------------------------- 1 | 2 | var Config = require('./config/gyro'); 3 | 4 | const MODULE_OPCODE = 0x13; 5 | 6 | const 7 | POWER_MODE = 0x1, 8 | DATA_INTERRUPT_ENABLE = 0x2, 9 | CONFIG = 0x3, 10 | DATA = 0x5; 11 | 12 | var Gyro = function(device) { 13 | this.device = device; 14 | this.config = new Config(); 15 | }; 16 | 17 | Gyro.prototype.enableAxisSampling = function() { 18 | var buffer = new Buffer(4); 19 | buffer[0] = MODULE_OPCODE; 20 | buffer[1] = DATA_INTERRUPT_ENABLE; 21 | buffer[2] = 0x1; 22 | buffer[3] = 0x0; 23 | this.device.send(buffer); 24 | }; 25 | 26 | Gyro.prototype.disableAxisSampling = function() { 27 | var buffer = new Buffer(4); 28 | buffer[0] = MODULE_OPCODE; 29 | buffer[1] = DATA_INTERRUPT_ENABLE; 30 | buffer[2] = 0x0; 31 | buffer[3] = 0x1; 32 | this.device.send(buffer); 33 | }; 34 | 35 | Gyro.prototype.start = function() { 36 | var buffer = new Buffer(3); 37 | buffer[0] = MODULE_OPCODE; 38 | buffer[1] = POWER_MODE; 39 | buffer[2] = 0x1; 40 | this.device.send(buffer); 41 | }; 42 | 43 | Gyro.prototype.stop = function() { 44 | var buffer = new Buffer(3); 45 | buffer[0] = MODULE_OPCODE; 46 | buffer[1] = POWER_MODE; 47 | buffer[2] = 0x0; 48 | this.device.send(buffer); 49 | }; 50 | 51 | 52 | Gyro.prototype.enable = function() { 53 | var buffer = new Buffer(4); 54 | buffer[0] = MODULE_OPCODE; 55 | buffer[1] = DATA_INTERRUPT_ENABLE; 56 | buffer[2] = 0x1; 57 | buffer[3] = 0x0; 58 | this.device.send(buffer); 59 | 60 | this.commitConfig(); 61 | 62 | buffer = new Buffer(3); 63 | buffer[0] = MODULE_OPCODE; 64 | buffer[1] = POWER_MODE; 65 | buffer[2] = 0x1; 66 | this.device.send(buffer); 67 | 68 | buffer = new Buffer(3); 69 | buffer[0] = MODULE_OPCODE; 70 | buffer[1] = DATA; 71 | buffer[2] = 0x1; 72 | this.device.send(buffer); 73 | }; 74 | 75 | Gyro.prototype.disable = function() { 76 | var buffer = new Buffer(3); 77 | buffer[0] = MODULE_OPCODE; 78 | buffer[1] = POWER_MODE; 79 | buffer[2] = 0x0; 80 | this.device.send(buffer); 81 | 82 | buffer = new Buffer(4); 83 | buffer[0] = MODULE_OPCODE; 84 | buffer[1] = DATA_INTERRUPT_ENABLE; 85 | buffer[2] = 0x0; 86 | buffer[3] = 0x1; 87 | this.device.send(buffer); 88 | 89 | buffer = new Buffer(3); 90 | buffer[0] = MODULE_OPCODE; 91 | buffer[1] = DATA; 92 | buffer[2] = 0x1; 93 | this.device.send(buffer); 94 | }; 95 | 96 | Gyro.prototype.onChange = function(callback) { 97 | var x, y, z; 98 | 99 | this.device.emitter.on([MODULE_OPCODE, DATA], function(buffer) { 100 | x = ~~buffer.readInt16LE(0) * this.config.scale; 101 | y = ~~buffer.readInt16LE(2) * this.config.scale; 102 | z = ~~buffer.readInt16LE(4) * this.config.scale; 103 | 104 | callback(x, y, z); 105 | }.bind(this)); 106 | }; 107 | 108 | Gyro.prototype.commitConfig = function() { 109 | var buffer = new Buffer(4); 110 | buffer[0] = MODULE_OPCODE; 111 | buffer[1] = CONFIG; 112 | buffer[2] = 0x20 | this.config.frequency; 113 | buffer[3] = this.config.range; 114 | this.device.send(buffer); 115 | }; 116 | 117 | module.exports = Gyro; 118 | -------------------------------------------------------------------------------- /src/registers/haptic.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x08; 3 | 4 | const PULSE = 0x1; 5 | 6 | var Haptic = function(device) { 7 | this.device = device; 8 | }; 9 | 10 | Haptic.prototype.startBuzzer = function(duration) { 11 | var buffer = new Buffer(6); 12 | buffer[0] = MODULE_OPCODE; 13 | buffer[1] = PULSE; 14 | buffer[2] = 127; // duty cycle 15 | buffer.writeInt16LE(duration, 3); 16 | buffer[5] = 0x1; 17 | 18 | this.device.send(buffer); 19 | }; 20 | 21 | Haptic.prototype.startMotor = function(duration, strength) { 22 | strength = strength || 100; 23 | 24 | var converted = (duration / 100) * 248; 25 | 26 | var buffer = new Buffer(6); 27 | buffer[0] = MODULE_OPCODE; 28 | buffer[1] = PULSE; 29 | buffer[2] = converted & 0xff; // duty cycle 30 | buffer.writeInt16LE(strength, 3); 31 | buffer[5] = 0x0; 32 | 33 | this.device.send(buffer); 34 | }; 35 | 36 | 37 | module.exports = Haptic; 38 | -------------------------------------------------------------------------------- /src/registers/i2c.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x0d; 3 | 4 | const READ_WRITE = 0x1; 5 | 6 | const READ_DATA_TIMEOUT= 5000; 7 | 8 | var I2C = function(device) { 9 | this.device = device; 10 | }; 11 | 12 | I2C.prototype.onData = function(callback) { 13 | this.device.emitter.on([MODULE_OPCODE, READ_WRITE], function(buffer) { 14 | // todo 15 | console.log(buffer); 16 | }); 17 | }; 18 | 19 | I2C.prototype.writeData = function(deviceAddr, registerAddr, data) { 20 | var buffer = new Buffer(6); 21 | buffer[0] = MODULE_OPCODE; 22 | buffer[1] = READ_WRITE; 23 | buffer[2] = deviceAddr; 24 | buffer[3] = registerAddr; 25 | buffer[4] = 0xff; 26 | buffer[5] = data.length; 27 | 28 | this.device.send(Buffer.concat([buffer, data])); 29 | }; 30 | 31 | I2C.prototype.readData = function(deviceAddr, registerAddr, numBytes, id) { 32 | var buffer = new Buffer(); 33 | buffer[0] = MODULE_OPCODE; 34 | buffer[1] = READ_WRITE; 35 | buffer[2] = deviceAddr; 36 | buffer[3] = registerAddr; 37 | buffer[4] = id || 0xff; 38 | buffer[5] = numBytes; 39 | 40 | this.device.sendRead(buffer); 41 | }; 42 | 43 | module.exports = I2C; 44 | -------------------------------------------------------------------------------- /src/registers/ibeacon.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x07; 3 | 4 | const 5 | ENABLE = 0x1, 6 | UUID = 0x2, 7 | MAJOR = 0x3, 8 | MINOR = 0x4, 9 | RX = 0x5, 10 | TX = 0x6, 11 | PERIOD = 0x7; 12 | 13 | var IBeacon = function(device) { 14 | this.device = device; 15 | }; 16 | 17 | IBeacon.prototype.enable = function() { 18 | var buffer = new Buffer(3); 19 | buffer[0] = MODULE_OPCODE; 20 | buffer[1] = ENABLE; 21 | buffer[2] = 0x1; 22 | 23 | this.device.send(buffer); 24 | }; 25 | 26 | IBeacon.prototype.disable = function() { 27 | var buffer = new Buffer(3); 28 | buffer[0] = MODULE_OPCODE; 29 | buffer[1] = ENABLE; 30 | buffer[2] = 0x0; 31 | 32 | this.device.send(buffer); 33 | }; 34 | 35 | // todo implement methods: 36 | IBeacon.prototype.setMinor = function(major) { 37 | }; 38 | IBeacon.prototype.setMajor = function(major) { 39 | }; 40 | IBeacon.prototype.setUUID = function(uuid) { 41 | }; 42 | IBeacon.prototype.setPeriod = function() { 43 | }; 44 | IBeacon.prototype.setTxPower = function() { 45 | }; 46 | IBeacon.prototype.setRxPower = function() { 47 | }; 48 | 49 | module.exports = IBeacon; 50 | -------------------------------------------------------------------------------- /src/registers/led.js: -------------------------------------------------------------------------------- 1 | 2 | var Config = require('./config/led'); 3 | 4 | const MODULE_OPCODE = 0x02; 5 | 6 | const 7 | PLAY = 0x1, 8 | STOP = 0x2, 9 | MODE = 0x3; 10 | 11 | var Led = function(device) { 12 | this.config = new Config(); 13 | this.device = device; 14 | }; 15 | 16 | /** 17 | * @param {Boolean} autoPlay 18 | */ 19 | Led.prototype.play = function(autoPlay) { 20 | var buffer = new Buffer(3); 21 | buffer[0] = MODULE_OPCODE; 22 | buffer[1] = PLAY; 23 | buffer[2] = autoPlay ? 2 : 1; 24 | 25 | this.device.send(buffer); 26 | }; 27 | 28 | Led.prototype.pause = function() { 29 | var buffer = new Buffer(3); 30 | buffer[0] = MODULE_OPCODE; 31 | buffer[1] = PLAY; 32 | buffer[2] = 0x0; 33 | 34 | this.device.send(buffer); 35 | }; 36 | 37 | Led.prototype.stop = function(clearConfig) { 38 | var buffer = new Buffer(3); 39 | buffer[0] = MODULE_OPCODE; 40 | buffer[1] = STOP; 41 | buffer[2] = clearConfig; 42 | 43 | this.device.send(buffer); 44 | }; 45 | 46 | Led.prototype.commitConfig = function() { 47 | var buffer = new Buffer(2); 48 | buffer[0] = MODULE_OPCODE; 49 | buffer[1] = MODE; 50 | 51 | this.device.send(Buffer.concat([buffer, this.config.getBuffer()])); 52 | }; 53 | 54 | module.exports = Led; 55 | -------------------------------------------------------------------------------- /src/registers/log.js: -------------------------------------------------------------------------------- 1 | 2 | var debug = require('debug')('log'); 3 | 4 | const MODULE_OPCODE = 0x0b; 5 | 6 | const 7 | ENABLE = 0x1, 8 | TRIGGER = 0x2, 9 | REMOVE = 0x3, 10 | TIME = 0x4, 11 | LENGTH = 0x5, 12 | READOUT = 0x6, 13 | READOUT_NOTIFY = 0x7, 14 | READOUT_PROGRESS = 0x8, 15 | REMOVE_ENTRIES = 0x9, 16 | REMOVE_ALL = 0xa, 17 | CIRCULAR_BUFFER = 0xb, 18 | READOUT_PAGE_COMPLETED = 0xd, 19 | READOUT_PAGE_CONFIRM = 0xe; 20 | 21 | var Log = function(device) { 22 | this.device = device; 23 | 24 | }; 25 | 26 | 27 | /* 28 | 29 | private final Map lastTimestamp= new HashMap<>(); 30 | 31 | */ 32 | 33 | 34 | 35 | 36 | 37 | 38 | /** 39 | * @param {Boolean} overwrite 40 | */ 41 | Log.prototype.startLogging = function(overwrite) { 42 | var buffer = new Buffer(3); 43 | buffer[0] = MODULE_OPCODE; 44 | buffer[1] = CIRCULAR_BUFFER; 45 | buffer[2] = overwrite ? 1 : 0; 46 | this.device.send(buffer); 47 | 48 | buffer = new Buffer(3); 49 | buffer[0] = MODULE_OPCODE; 50 | buffer[1] = ENABLE; 51 | buffer[2] = 0x1; 52 | this.device.send(buffer); 53 | }; 54 | 55 | Log.prototype.stopLogging = function() { 56 | buffer = new Buffer(3); 57 | buffer[0] = MODULE_OPCODE; 58 | buffer[1] = ENABLE; 59 | buffer[2] = 0x0; 60 | 61 | this.device.send(buffer); 62 | }; 63 | 64 | Log.prototype.downloadLog = function(callback) { 65 | 66 | buffer = new Buffer(3); 67 | buffer[0] = MODULE_OPCODE; 68 | buffer[1] = READOUT_PAGE_COMPLETED; 69 | buffer[2] = 0x1; 70 | this.device.send(buffer); 71 | 72 | var buffer = new Buffer(3); 73 | buffer[0] = MODULE_OPCODE; 74 | buffer[1] = READOUT_NOTIFY; 75 | buffer[2] = 0x1; 76 | this.device.send(buffer); 77 | 78 | buffer = new Buffer(3); 79 | buffer[0] = MODULE_OPCODE; 80 | buffer[1] = READOUT_PROGRESS; 81 | buffer[2] = 0x1; 82 | this.device.send(buffer); 83 | 84 | buffer = new Buffer(2); 85 | buffer[0] = MODULE_OPCODE; 86 | buffer[1] = TIME; 87 | this.device.sendRead(buffer); 88 | 89 | // TODO 90 | buffer = new Buffer(2); 91 | buffer[0] = MODULE_OPCODE; 92 | buffer[1] = LENGTH; //read command 93 | this.device.sendRead(buffer); 94 | 95 | var self = this; 96 | 97 | this.device.emitter.on([MODULE_OPCODE, TIME], function(buffer) { 98 | 99 | 100 | 101 | self.latestTick = Log.getLoggingTick(buffer); 102 | 103 | //callback(buffer.readInt8(0)); 104 | 105 | /* 106 | 107 | final long tick= ByteBuffer.wrap(response, 2, 4).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xffffffffL; 108 | byte resetUid= (response.length > 6) ? response[6] : -1; 109 | 110 | latestTick= new ReferenceTick(resetUid, tick, Calendar.getInstance()); 111 | if (resetUid != -1) { 112 | logReferenceTicks.put(latestTick.resetUid(), latestTick); 113 | } 114 | 115 | */ 116 | }); 117 | 118 | 119 | this.device.emitter.on([MODULE_OPCODE, LENGTH], function(lengthBuffer) { 120 | 121 | //console.log(lengthBuffer); 122 | 123 | var logEntries = (lengthBuffer.length > 2) ? lengthBuffer.readInt32LE(0) : lengthBuffer.readInt16LE(0); 124 | 125 | if (!logEntries) { 126 | return; // no logs 127 | } 128 | var progress = 0.05; 129 | var entriesNotify = logEntries * progress; 130 | 131 | //console.log(length + ' logs to download'); 132 | 133 | buffer = new Buffer(10); 134 | buffer[0] = MODULE_OPCODE; 135 | buffer[1] = READOUT; 136 | buffer[2] = lengthBuffer[0]; 137 | buffer[3] = lengthBuffer[1]; 138 | 139 | // Dirty hack, the data buffer should be replicated at the offset #2 140 | 141 | buffer[4] = 0; 142 | buffer[5] = 0; 143 | 144 | 145 | buffer.writeInt32LE(entriesNotify & 0xff,6); 146 | 147 | /* 148 | buffer[4] = entriesNotify & 0xff; 149 | buffer[5] = (entriesNotify >> 8) & 0xff; 150 | */ 151 | self.device.send(buffer); 152 | }); 153 | 154 | this.device.emitter.on([MODULE_OPCODE, READOUT_NOTIFY], function(buffer) { 155 | 156 | var logId = buffer[0] & 0x1f; 157 | var resetUid = (buffer[0] & 0xe0) >> 5; 158 | 159 | var formatted = { 160 | x: buffer.readInt16LE(7) / 16384, 161 | y: buffer.readInt16LE(9) / 16384, 162 | z: buffer.readInt16LE(12) / 16384 163 | }; 164 | 165 | //store the logId into logEntries 166 | //console.log('logId :' + logId + ' - resetUid : ' + resetUid + ' | accel: ' + formatted.x + ' ' + formatted.y + ' ' + formatted.z ); 167 | 168 | if(callback) { 169 | callback(formatted); 170 | } 171 | 172 | 173 | }); 174 | 175 | this.device.emitter.on([MODULE_OPCODE, READOUT_PROGRESS], function(buffer) { 176 | /* 177 | ByteBuffer buffer= ByteBuffer.wrap(response, 2, response.length - 2).order(ByteOrder.LITTLE_ENDIAN); 178 | final long nEntriesLeft= (response.length > 4) ? buffer.getInt() & 0xffffffffL : buffer.getShort() & 0xffff; 179 | 180 | if (nEntriesLeft == 0) { 181 | lastTimestamp.clear(); 182 | } 183 | */ 184 | }); 185 | 186 | this.device.emitter.on([MODULE_OPCODE, READOUT_PAGE_COMPLETED], function(buffer) { 187 | buffer = new Buffer(2); 188 | buffer[0] = MODULE_OPCODE; 189 | buffer[1] = READOUT_PAGE_CONFIRM; 190 | 191 | self.device.send(buffer); 192 | }); 193 | 194 | 195 | }; 196 | 197 | Log.prototype.onLogData = function(callback) { 198 | this.device.emitter.on([MODULE_OPCODE, READOUT_NOTIFY], function(buffer) { 199 | var logId = buffer[0] & 0x1f; 200 | var resetUid = (buffer[0] & 0xe0) >> 5; 201 | 202 | 203 | if(buffer.length == 18) { 204 | var formatted = { 205 | x: buffer.readInt16LE(7) / 16384, 206 | y: buffer.readInt16LE(9) / 16384, 207 | z: buffer.readInt16LE(12) / 16384 208 | }; 209 | callback(formatted); 210 | } 211 | 212 | //store the logId into logEntries 213 | 214 | /* 215 | Process buffer 2 -> 11 216 | 217 | If buffer.length == 20 218 | Process buffer 11 -> 20 219 | 220 | */ 221 | }); 222 | }; 223 | 224 | Log.prototype.removeAll = function() { 225 | var buffer = new Buffer(4); 226 | buffer[0] = MODULE_OPCODE; 227 | buffer[1] = REMOVE_ENTRIES; 228 | buffer[2] = 0xff; 229 | buffer[3] = 0xff; 230 | this.device.send(buffer); 231 | }; 232 | 233 | Log.getLoggingTick = function(response) { 234 | if(!response || response.length <= 2) { 235 | return undefined; 236 | } 237 | 238 | var tick = response.readInt32LE(2,4); 239 | var resetUid = (response.length > 6) ? response[6] : -1; 240 | 241 | return { 242 | 'resetUid': resetUid, 243 | 'tick': tick, 244 | 'creationDate' : new Date() 245 | }; 246 | }; 247 | 248 | module.exports = Log; 249 | -------------------------------------------------------------------------------- /src/registers/macro.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x0f; 3 | 4 | const 5 | ENABLE = 0x1, 6 | BEGIN = 0x2, 7 | ADD_COMMAND = 0x3, 8 | END = 0x4, 9 | EXECUTE = 0x5, 10 | NOTIFY_ENABLE = 0x6, 11 | NOTIFY = 0x7, 12 | ERASE_ALL = 0x8, 13 | ADD_PARTIAL = 0x9; 14 | 15 | var Macro = function(device) { 16 | this.device = device; 17 | this.macroIds = []; 18 | 19 | var self = this; 20 | this.device.emitter.on([MODULE_OPCODE, BEGIN], function(buffer) { 21 | self.macroIds.push(buffer[2]); 22 | }); 23 | }; 24 | 25 | Macro.prototype.execute = function(macroId) { 26 | var buffer = new Buffer(3); 27 | buffer[0] = MODULE_OPCODE; 28 | buffer[1] = NOTIFY; 29 | buffer[2] = 0x1; 30 | this.device.send(buffer); 31 | 32 | buffer = new Buffer(4); 33 | buffer[0] = MODULE_OPCODE; 34 | buffer[1] = NOTIFY_ENABLE; 35 | buffer[2] = macroId; 36 | buffer[3] = 0x1; 37 | this.device.send(buffer); 38 | 39 | buffer = new Buffer(3); 40 | buffer[0] = MODULE_OPCODE; 41 | buffer[1] = EXECUTE; 42 | buffer[2] = macroId; 43 | this.device.send(buffer); 44 | }; 45 | 46 | Macro.prototype.eraseMacros = function() { 47 | var buffer = new Buffer(2); 48 | buffer[0] = MODULE_OPCODE; 49 | buffer[1] = ERASE_ALL; 50 | this.device.send(buffer); 51 | }; 52 | 53 | module.exports = Macro; 54 | -------------------------------------------------------------------------------- /src/registers/magnetometer.js: -------------------------------------------------------------------------------- 1 | /*jshint esversion: 6 */ 2 | 3 | const MODULE_OPCODE = 0x15; 4 | 5 | const POWER_MODE = 0x1, 6 | DATA_INTERRUPT_ENABLE = 0x2, 7 | DATA_RATE = 0x3, 8 | DATA_REPETITIONS = 0x4, 9 | MAG_DATA = 0x5, 10 | THRESHOLD_INTERRUPT_ENABLE = 0x6, 11 | THRESHOLD_CONFIG = 0x7, 12 | THRESHOLD_INTERRUPT = 0x8, 13 | PACKED_MAG_DATA = 0x9; 14 | 15 | const ODR_10_HZ = 0x0, 16 | ODR_2_HZ = 0x1, 17 | ODR_6_HZ = 0x2, 18 | ODR_8_HZ = 0x3, 19 | ODR_15_HZ = 0x4, 20 | ODR_20_HZ = 0x5, 21 | ODR_25_HZ = 0x6, 22 | ODR_30_HZ = 0x7; 23 | 24 | const PRESET_LOW_POWER = 0x0, 25 | PRESET_REGULAR = 0x1, 26 | PRESET_ENHANCED_REGULAR = 0x2, 27 | PRESET_HIGH_ACCURACY = 0x3; 28 | 29 | const X_OFFSET = 0, 30 | Y_OFFSET = 2, 31 | Z_OFFSET = 4; 32 | 33 | 34 | var Magnetometer = function (device) { 35 | this.device = device; 36 | }; 37 | 38 | Magnetometer.prototype.enableAxisSampling = function() { 39 | var buffer = new Buffer(4); 40 | buffer[0] = MODULE_OPCODE; 41 | buffer[1] = DATA_INTERRUPT_ENABLE; 42 | buffer[2] = 0x1; 43 | buffer[3] = 0x0; 44 | this.device.send(buffer); 45 | }; 46 | 47 | Magnetometer.prototype.disableAxisSampling = function() { 48 | var buffer = new Buffer(4); 49 | buffer[0] = MODULE_OPCODE; 50 | buffer[1] = DATA_INTERRUPT_ENABLE; 51 | buffer[2] = 0x0; 52 | buffer[3] = 0x1; 53 | this.device.send(buffer); 54 | }; 55 | 56 | Magnetometer.prototype.start = function() { 57 | var buffer = new Buffer(3); 58 | buffer[0] = MODULE_OPCODE; 59 | buffer[1] = POWER_MODE; 60 | buffer[2] = 0x1; 61 | this.device.send(buffer); 62 | }; 63 | 64 | Magnetometer.prototype.stop = function() { 65 | var buffer = new Buffer(3); 66 | buffer[0] = MODULE_OPCODE; 67 | buffer[1] = POWER_MODE; 68 | buffer[2] = 0x0; 69 | this.device.send(buffer); 70 | }; 71 | 72 | Magnetometer.prototype.subscribe = function() { 73 | var buffer = new Buffer(3); 74 | buffer[0] = MODULE_OPCODE; 75 | buffer[1] = MAG_DATA; 76 | buffer[2] = 0x1; 77 | this.device.send(buffer); 78 | }; 79 | 80 | Magnetometer.prototype.unsubscribe = function() { 81 | var buffer = new Buffer(3); 82 | buffer[0] = MODULE_OPCODE; 83 | buffer[1] = MAG_DATA; 84 | buffer[2] = 0x0; 85 | this.device.send(buffer); 86 | }; 87 | 88 | Magnetometer.prototype.onChange = function(callback) { 89 | 90 | this.device.emitter.on([MODULE_OPCODE, MAG_DATA], function(buffer) { 91 | 92 | var formatted = { 93 | x: buffer.readInt16LE(X_OFFSET) / 16, 94 | y: buffer.readInt16LE(Y_OFFSET) / 16, 95 | z: buffer.readInt16LE(Z_OFFSET) / 16 96 | }; 97 | 98 | callback(formatted); 99 | 100 | }); 101 | }; 102 | 103 | Magnetometer.prototype.writeConfig = function(xy_reps, z_reps, dataRate) { 104 | var buffer = new Buffer(4); 105 | buffer[0] = MODULE_OPCODE; 106 | buffer[1] = DATA_REPETITIONS; 107 | buffer[2] = (xy_reps - 1) / 2; 108 | buffer[3] = (z_reps - 1); 109 | this.device.send(buffer); 110 | 111 | buffer = new Buffer(3); 112 | buffer[0] = MODULE_OPCODE; 113 | buffer[1] = DATA_RATE; 114 | buffer[2] = dataRate; 115 | this.device.send(buffer); 116 | 117 | }; 118 | 119 | Magnetometer.prototype.setPreset = function(preset) { 120 | switch(preset) { 121 | case PRESET_LOW_POWER: 122 | this.writeConfig(3,3,ODR_10_HZ); 123 | break; 124 | case PRESET_REGULAR: 125 | this.writeConfig(9,15,ODR_10_HZ); 126 | break; 127 | case PRESET_ENHANCED_REGULAR: 128 | this.writeConfig(15,27,ODR_10_HZ); 129 | break; 130 | case PRESET_HIGH_ACCURACY: 131 | this.writeConfig(47,83,ODR_20_HZ); 132 | break; 133 | } 134 | }; 135 | 136 | Magnetometer.PRESET_LOW_POWER = PRESET_LOW_POWER; 137 | Magnetometer.PRESET_REGULAR = PRESET_REGULAR; 138 | Magnetometer.PRESET_ENHANCED_REGULAR = PRESET_ENHANCED_REGULAR; 139 | Magnetometer.PRESET_HIGH_ACCURACY = PRESET_HIGH_ACCURACY; 140 | 141 | 142 | module.exports = Magnetometer; 143 | 144 | -------------------------------------------------------------------------------- /src/registers/neo-pixel.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x06; 3 | 4 | const 5 | INITIALIZE = 0x1, 6 | HOLD = 0x2, 7 | CLEAR = 0x3, 8 | PIXEL = 0x4, 9 | ROTATE = 0x5, 10 | DEINITIALIZE = 0x6; 11 | 12 | var NeoPixel = function(device) { 13 | this.device = device; 14 | }; 15 | 16 | module.exports = NeoPixel; 17 | -------------------------------------------------------------------------------- /src/registers/registers.js: -------------------------------------------------------------------------------- 1 | 2 | // just a list of all available register (InfoRegister.java) 3 | 4 | var registers = module.exports.byId = { 5 | 0x01: 'MECHANICAL_SWITCH', 6 | 0x02: 'LED', 7 | 0x03: 'ACCELEROMETER', 8 | 0x04: 'TEMPERATURE', 9 | 0x05: 'GPIO', 10 | 0x06: 'NEO_PIXEL', 11 | 0x07: 'IBEACON', 12 | 0x08: 'HAPTIC', 13 | 0x09: 'DATA_PROCESSOR', 14 | 0x0a: 'EVENT', 15 | 0x0b: 'LOGGING', 16 | 0x0c: 'TIMER', 17 | 0x0d: 'I2C', 18 | 0x0f: 'MACRO', 19 | 0x10: 'GSR', 20 | 0x11: 'SETTINGS', 21 | 0x12: 'BAROMETER', 22 | 0x13: 'GYRO', 23 | 0x14: 'AMBIENT_LIGHT', 24 | 0x15: 'MAGNETOMETER', 25 | 0x19: 'SENSORFUSION', 26 | 0xfe: 'DEBUG' 27 | }; 28 | 29 | module.exports.byName = {}; 30 | 31 | for (var id in registers.byId) { 32 | var name = registers.byId[id]; 33 | 34 | module.exports.byName[name] = id; 35 | module.exports[name] = id; 36 | } 37 | -------------------------------------------------------------------------------- /src/registers/sensorFusion.js: -------------------------------------------------------------------------------- 1 | /*jshint esversion: 6*/ 2 | 3 | const Config = require('./config/sensorFusion'); 4 | const Core = require('./core'); 5 | const Accelerometer = require('./accelerometer'); 6 | const Gyro = require('./gyro'); 7 | const Magnetometer = require('./magnetometer'); 8 | 9 | const MODULE_OPCODE = 0x19; 10 | 11 | const W_OFFSET = 0, 12 | X_OFFSET = 4, 13 | Y_OFFSET = 8, 14 | Z_OFFSET = 12; 15 | 16 | const ENABLE= 0x1, 17 | MODE= 0x2, 18 | OUTPUT_ENABLE= 0x3, 19 | CORRECTED_ACC= 0x4, 20 | CORRECTED_GYRO= 0x5, 21 | CORRECTED_MAG= 0x6, 22 | QUATERNION= 0x7, 23 | EULER_ANGLES= 0x8, 24 | GRAVITY_VECTOR= 0x9, 25 | LINEAR_ACC= 0xa; 26 | 27 | /* Data sources */ 28 | 29 | const DATA_CORRECTED_ACC = 0, 30 | DATA_CORRECTED_GYRO = 1, 31 | DATA_CORRECTED_MAG = 2, 32 | DATA_QUATERION = 3, 33 | DATA_EULER_ANGLE = 4, 34 | DATA_GRAVITY_VECTOR = 5, 35 | DATA_LINEAR_ACC = 6; 36 | 37 | var SensorFusion = function(device) { 38 | this.device = device; 39 | this.config = new Config(); 40 | this.dataSourceMask = 0x0; 41 | this.accelerometer = new Accelerometer(device); 42 | this.gyro = new Gyro(device); 43 | this.magnetometer = new Magnetometer(device); 44 | }; 45 | 46 | SensorFusion.prototype.enableData = function(data_source) { 47 | this.dataSourceMask = 0x0; 48 | this.dataSourceMask |= (0x1 << data_source); 49 | }; 50 | 51 | SensorFusion.prototype.clearEnabledMask = function() { 52 | this.dataSourceMask = 0x0; 53 | }; 54 | 55 | SensorFusion.prototype.writeConfig = function() { 56 | var buffer = new Buffer(4); 57 | buffer[0] = MODULE_OPCODE; 58 | buffer[1] = MODE; 59 | buffer[2] = this.config.mode; 60 | buffer[3] = this.config.getConfigMask(); 61 | this.device.send(buffer); 62 | 63 | this.accelerometer.setAxisSamplingRange(parseInt(Object.keys(this.accelerometer.ACC_RANGE)[this.config.acc_range],10)); 64 | 65 | switch(this.config.mode) { 66 | case Config.MODE.NDOF: 67 | case Config.MODE.IMU_PLUS: 68 | this.accelerometer.setOutputDataRate(100); 69 | break; 70 | } 71 | this.accelerometer.setConfig(); // TODO refactor the name to be consistent 72 | 73 | //dirty hack !!! 74 | switch(this.config.mode) { 75 | case Config.MODE.NDOF: 76 | case Config.MODE.IMU_PLUS: 77 | this.gyro.config.setRate(100); 78 | break; 79 | } 80 | this.gyro.config.range = this.config.gyro_range; 81 | this.gyro.commitConfig(); // TODO refactor the name to be consistent 82 | 83 | //dirty hack !! use constants instead !! 84 | this.magnetometer.writeConfig(9,15,0x6); 85 | 86 | 87 | }; 88 | 89 | SensorFusion.prototype.subscribe = function(output_type) { 90 | var buffer = new Buffer(3); 91 | buffer[0] = MODULE_OPCODE; 92 | buffer[1] = output_type; 93 | buffer[2] = 0x1; 94 | this.device.send(buffer); 95 | }; 96 | 97 | SensorFusion.prototype.start = function() { 98 | 99 | switch(this.config.mode) { 100 | case Config.MODE.NDOF: 101 | this.accelerometer.enableAxisSampling(); 102 | this.gyro.enableAxisSampling(); //TODO refactor the method name to be consistent 103 | this.magnetometer.enableAxisSampling(); 104 | this.accelerometer.start(); 105 | this.gyro.start(); 106 | this.magnetometer.start(); 107 | break; 108 | } 109 | var buffer = new Buffer(4); 110 | buffer[0] = MODULE_OPCODE; 111 | buffer[1] = OUTPUT_ENABLE; 112 | buffer[2] = this.dataSourceMask; 113 | buffer[3] = 0x0; 114 | this.device.send(buffer); 115 | 116 | buffer = new Buffer(3); 117 | buffer[0] = MODULE_OPCODE; 118 | buffer[1] = ENABLE; 119 | buffer[2] = 0x1; 120 | 121 | this.device.send(buffer); 122 | }; 123 | 124 | SensorFusion.prototype.stop = function() { 125 | var buffer = new Buffer(3); 126 | buffer[0] = MODULE_OPCODE; 127 | buffer[1] = ENABLE; 128 | buffer[2] = 0x0; 129 | 130 | buffer = new Buffer(4); 131 | buffer[0] = MODULE_OPCODE; 132 | buffer[1] = OUTPUT_ENABLE; 133 | buffer[2] = 0x0; 134 | buffer[3] = 0x7f; 135 | this.device.send(buffer); 136 | 137 | switch(this.config.mode) { 138 | case Config.MODE.NDOF: 139 | this.accelerometer.stop(); 140 | this.gyro.stop(); 141 | this.magnetometer.stop(); 142 | this.accelerometer.disableAxisSampling(); 143 | this.gyro.disableAxisSampling(); //TODO refactor the method name to be consistent 144 | this.magnetometer.disableAxisSampling(); 145 | break; 146 | } 147 | 148 | }; 149 | 150 | SensorFusion.prototype.unsubscribe = function(output_type) { 151 | var buffer = new Buffer(3); 152 | buffer[0] = MODULE_OPCODE; 153 | buffer[1] = output_type; 154 | buffer[2] = 0x0; 155 | this.device.send(buffer); 156 | }; 157 | 158 | 159 | SensorFusion.prototype.onChange = function(callback) { 160 | this.device.emitter.on([MODULE_OPCODE, QUATERNION], function(buffer) { 161 | var quaternion = new Core.Quaternion( 162 | Math.round(buffer.readFloatLE(W_OFFSET) * 1000) / 1000, 163 | Math.round(buffer.readFloatLE(X_OFFSET) * 1000) / 1000, 164 | Math.round(buffer.readFloatLE(Y_OFFSET) * 1000) / 1000, 165 | Math.round(buffer.readFloatLE(Z_OFFSET) * 1000) / 1000); 166 | callback(quaternion); 167 | }); 168 | }; 169 | 170 | module.exports = SensorFusion; -------------------------------------------------------------------------------- /src/registers/settings.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x11; 3 | 4 | const 5 | DEVICE_NAME = 0x1, 6 | ADVERTISING_INTERVAL = 0x2, 7 | TX_POWER = 0x3, 8 | DELETE_BOND = 0x4, 9 | START_ADVERTISEMENT = 0x5, 10 | INIT_BOND = 0x6, 11 | SCAN_RESPONSE = 0x7, 12 | PARTIAL_SCAN_RESPONSE = 0x8, 13 | CONNECTION_PARAMS = 0x9; 14 | 15 | const 16 | CONN_INTERVAL_STEP= 1.25, 17 | TIMEOUT_STEP= 10; 18 | 19 | var Settings = function(device) { 20 | this.device = device; 21 | }; 22 | 23 | /** 24 | * @param callback 25 | */ 26 | Settings.prototype.getDeviceName = function(callback) { 27 | var buffer = new Buffer(2); 28 | buffer[0] = MODULE_OPCODE; 29 | buffer[1] = DEVICE_NAME; 30 | 31 | this.device.emitter.once([MODULE_OPCODE, DEVICE_NAME], function(buffer) { 32 | callback(buffer.toString()); 33 | }); 34 | 35 | this.device.sendRead(buffer); 36 | }; 37 | 38 | /** 39 | * @param {String} name 40 | */ 41 | Settings.prototype.setDeviceName = function(name) { 42 | var buffer = new Buffer(2); 43 | buffer[0] = MODULE_OPCODE; 44 | buffer[1] = DEVICE_NAME; 45 | 46 | this.device.send(Buffer.concat([buffer, new Buffer(name)])); 47 | }; 48 | /** 49 | * Sets connection parameters 50 | * @param min_conn_interval Connection interval lower bound, min 7.5ms 51 | * @param max_conn_interval Connection interval upper bound, max 4000ms 52 | * @param latency Number of connection intervals to skip, betwen [0, 1000] 53 | * @param timeout Max time between data exchanges until the connection is considered to be lost, between [10, 32000]ms 54 | */ 55 | Settings.prototype.setConnectionParameters = function(min_conn_interval, max_conn_interval, latency, timeout) { 56 | var buffer = new Buffer(10); 57 | buffer[0] = MODULE_OPCODE; 58 | buffer[1] = CONNECTION_PARAMS; 59 | buffer.writeUInt16LE((min_conn_interval/CONN_INTERVAL_STEP), 2); 60 | buffer.writeUInt16LE((max_conn_interval/CONN_INTERVAL_STEP), 4); 61 | buffer.writeUInt16LE(latency,6); 62 | buffer.writeUInt16LE(Math.round((timeout/TIMEOUT_STEP)), 8); 63 | this.device.send(buffer); 64 | }; 65 | 66 | module.exports = Settings; 67 | -------------------------------------------------------------------------------- /src/registers/switch.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x01; 3 | 4 | const STATE = 0x1; 5 | 6 | var Switch = function(device) { 7 | this.device = device; 8 | }; 9 | 10 | Switch.prototype.register = function() { 11 | var buffer = new Buffer(3); 12 | buffer[0] = MODULE_OPCODE; 13 | buffer[1] = STATE; 14 | buffer[2] = 0x1; 15 | 16 | this.device.send(buffer); 17 | }; 18 | 19 | Switch.prototype.onChange = function(callback) { 20 | this.device.emitter.on([MODULE_OPCODE, STATE], function(buffer) { 21 | callback(buffer.readInt8(0)); 22 | }); 23 | }; 24 | 25 | module.exports = Switch; 26 | -------------------------------------------------------------------------------- /src/registers/temperature.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x04; 3 | 4 | const 5 | VALUE = 0x1, 6 | MODE = 0x2, 7 | THERMISTOR = 0x5; 8 | 9 | const SCALE = 8; 10 | 11 | var Temperature = function(device, channel) { 12 | this.channel = channel; 13 | this.lastValue = null; 14 | 15 | this.NRF_DIE = 0; 16 | this.ON_BOARD_THERMISTOR = 1; 17 | this.EXT_THERMISTOR = 2; // todo in case of non-pro edition: EXT_THERMISTOR = 1 18 | this.BMP_280 = 3; 19 | 20 | this.device = device; 21 | }; 22 | 23 | /** 24 | * @param {Function} callback 25 | */ 26 | Temperature.prototype.getValue = function(callback) { 27 | this.device.emitter.once([MODULE_OPCODE, VALUE], function(buffer) { 28 | var temp = buffer.readInt16LE(1) / SCALE; 29 | callback(temp); 30 | }); 31 | 32 | var buffer = new Buffer(3); 33 | buffer[0] = MODULE_OPCODE; 34 | buffer[1] = VALUE; 35 | buffer[2] = this.channel; 36 | this.device.sendRead(buffer); 37 | }; 38 | 39 | Temperature.prototype.startInterval = function(interval, callback, allEvents) { 40 | var self = this; 41 | 42 | this.device.emitter.on([MODULE_OPCODE, VALUE], function(buffer) { 43 | var temp = buffer.readInt16LE(1) / SCALE; 44 | if (temp != self.lastValue || allEvents) { 45 | self.lastValue = temp; 46 | callback(temp); 47 | } 48 | }); 49 | 50 | function sendRequest() { 51 | var buffer = new Buffer(3); 52 | buffer[0] = MODULE_OPCODE; 53 | buffer[1] = VALUE; 54 | buffer[2] = self.channel; 55 | self.device.sendRead(buffer); 56 | } 57 | 58 | sendRequest(); 59 | 60 | return setInterval(sendRequest, interval); 61 | }; 62 | 63 | Temperature.prototype.enableThermistorMode = function(analogReadPin, pulldownPin) { 64 | var buffer = new Buffer(5); 65 | buffer[0] = MODULE_OPCODE; 66 | buffer[1] = THERMISTOR; 67 | buffer[2] = 0x1; 68 | buffer[3] = analogReadPin; 69 | buffer[4] = pulldownPin; 70 | 71 | this.device.sendRead(buffer); 72 | }; 73 | 74 | Temperature.prototype.disableThermistorMode = function() { 75 | var buffer = new Buffer(5); 76 | buffer[0] = MODULE_OPCODE; 77 | buffer[1] = THERMISTOR; 78 | buffer[2] = 0x0; 79 | buffer[3] = 0x0; 80 | buffer[4] = 0x0; 81 | 82 | this.device.sendRead(buffer); 83 | }; 84 | 85 | module.exports = Temperature; 86 | -------------------------------------------------------------------------------- /src/registers/timer.js: -------------------------------------------------------------------------------- 1 | 2 | const MODULE_OPCODE = 0x0c; 3 | 4 | const 5 | ENABLE = 0x1, 6 | TIMER_ENTRY = 0x2, 7 | START = 0x3, 8 | STOP = 0x4, 9 | REMOVE = 0x5, 10 | NOTIFY = 0x6, 11 | NOTIFY_ENABLE = 0x7; 12 | 13 | var Timer = function(device) { 14 | this.device = device; 15 | this.id = 0; 16 | }; 17 | 18 | Timer.prototype.start = function() { 19 | var id = ++this.id; 20 | 21 | var buffer = new Buffer(3); 22 | buffer[0] = MODULE_OPCODE; 23 | buffer[1] = START; 24 | buffer[2] = id; 25 | this.device.send(buffer); 26 | }; 27 | 28 | Timer.prototype.start = function(id) { 29 | var buffer = new Buffer(3); 30 | buffer[0] = MODULE_OPCODE; 31 | buffer[1] = START; 32 | buffer[2] = id; 33 | this.device.send(buffer); 34 | }; 35 | 36 | Timer.prototype.remove = function(id) { 37 | var buffer = new Buffer(3); 38 | buffer[0] = MODULE_OPCODE; 39 | buffer[1] = REMOVE; 40 | buffer[2] = id; 41 | this.device.send(buffer); 42 | }; 43 | 44 | module.exports = Timer; 45 | -------------------------------------------------------------------------------- /tests/jasmine-runner.js: -------------------------------------------------------------------------------- 1 | var Jasmine = require('jasmine'); 2 | var SpecReporter = require('jasmine-spec-reporter').SpecReporter; 3 | var noop = function() {}; 4 | 5 | var jrunner = new Jasmine(); 6 | //jrunner.configureDefaultReporter({print: noop}); // remove default reporter logs 7 | jasmine.getEnv().addReporter(new SpecReporter()); // add jasmine-spec-reporter 8 | jrunner.loadConfigFile(); // load jasmine.json configuration 9 | jrunner.execute(); 10 | -------------------------------------------------------------------------------- /tests/spec/helpers/device.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | 3 | var Device = function() { 4 | this.buffers = []; 5 | this.emitter = new events.EventEmitter(); 6 | }; 7 | 8 | Device.prototype.send = function(buffer) { 9 | this.buffers.push(buffer); 10 | }; 11 | 12 | Device.prototype.sendRead = function(buffer) { 13 | buffer[1] |= 0x80; 14 | this.buffers.push(buffer); 15 | }; 16 | 17 | Device.prototype.reset = function() { 18 | this.buffers = []; 19 | }; 20 | 21 | module.exports = Device; 22 | -------------------------------------------------------------------------------- /tests/spec/registers/accelerometerSpec.js: -------------------------------------------------------------------------------- 1 | var Accelerometer = require('../../../src/registers/accelerometer'), 2 | Device = require('../helpers/device'), 3 | bufferEqual = require('buffer-equal'); 4 | 5 | describe("Accelerometer", function() { 6 | var device = new Device(), 7 | accelerometer = new Accelerometer(device); 8 | 9 | it("should have 50hz as default value for output data rate", function() { 10 | expect(accelerometer.dataRate).toEqual(0x7); 11 | }); 12 | 13 | it("should have +-2g as default axis sampling range", function() { 14 | expect(accelerometer.accRange).toEqual([0x3, 16384]); 15 | }); 16 | 17 | describe("setConfig()", function() { 18 | 19 | beforeEach(function() { 20 | spyOn(device, 'send').and.callThrough(); 21 | jasmine.addCustomEqualityTester(bufferEqual); 22 | }); 23 | 24 | it("should send the default configured output data rate and axis sampling range to the MetaWear device", function() { 25 | accelerometer.setConfig(); 26 | expect(device.send).toHaveBeenCalled(); 27 | expect(device.buffers.pop()).toEqual(new Buffer([0x3,0x3,0x27,0x3])); 28 | }); 29 | }); 30 | 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /tests/spec/registers/logSpec.js: -------------------------------------------------------------------------------- 1 | var Log = require('../../../src/registers/log'), 2 | Device = require('../helpers/device'), 3 | bufferEqual = require('buffer-equal'); 4 | 5 | describe("Log", function() { 6 | var device = new Device(), 7 | log = new Log(device), 8 | MODULE = 0xb, // LOG 9 | LENGTH = 0x5; // LENGTH 10 | TIME = 0x4; 11 | READOUT_NOTIFY = 0x7; //READOUT_NOTIFY 12 | READOUT_PAGE_COMPLETED = 0xd; 13 | READOUT_PAGE_CONFIRM = 0xe; 14 | 15 | 16 | describe('getLoggingTick', function() { 17 | it('should return the logging tick from a log module notification', function() { 18 | var response = new Buffer([0xb, 0x84, 0x78, 0xe5, 0xc9, 0x5e, 0x4]); 19 | 20 | var referenceTick = Log.getLoggingTick(response); 21 | 22 | 23 | expect(referenceTick).toBeDefined(); 24 | expect(referenceTick.resetUid).toEqual(0x4); 25 | expect(referenceTick.tick).toEqual(1590289784); 26 | 27 | }); 28 | 29 | it('should prevent any error to occurs during the parsing of the tick', function() { 30 | var response = new Buffer([0xb, 0x84]); 31 | 32 | var referenceTick = Log.getLoggingTick(response); 33 | 34 | expect(referenceTick).toBeUndefined(); 35 | 36 | }) 37 | }); 38 | 39 | describe('downloadLog()', function() { 40 | beforeAll(function() { 41 | spyOn(device, 'send').and.callThrough(); 42 | spyOn(device, 'sendRead').and.callThrough(); 43 | jasmine.addCustomEqualityTester(bufferEqual); 44 | }); 45 | 46 | beforeEach(function() { 47 | device.reset(); 48 | device.send.calls.reset(); 49 | device.sendRead.calls.reset(); 50 | }); 51 | 52 | it('should trigger the subscribtion to READOUT_PAGE_COMPLETED, READOUT_NOTIFY, READOUT_PROGRESS', function() { 53 | log.downloadLog(); 54 | expect(device.send).toHaveBeenCalledTimes(3); 55 | expect(device.buffers[0]).toEqual(new Buffer([0xb,0xd,0x1])); 56 | expect(device.buffers[1]).toEqual(new Buffer([0xb,0x7,0x1])); 57 | expect(device.buffers[2]).toEqual(new Buffer([0xb,0x8,0x1])); 58 | }); 59 | 60 | it('should trigger the reading of the latest tick in order to have an initial reference for the log', function() { 61 | log.downloadLog(); 62 | expect(device.sendRead).toHaveBeenCalled(); 63 | expect(device.buffers[3]).toEqual(new Buffer([0xb,0x84])); 64 | }); 65 | 66 | 67 | it('should trigger the reading the length of the log', function() { 68 | log.downloadLog(); 69 | expect(device.sendRead).toHaveBeenCalled(); 70 | expect(device.buffers[4]).toEqual(new Buffer([0xb,0x85])); 71 | }); 72 | 73 | it('should not trigger the log READOUT if no entries', function() { 74 | 75 | var data = new Buffer([0x0,0x0,0x0,0x0]); 76 | log.downloadLog(); 77 | 78 | expect(device.send.calls.any()).toBe(true); 79 | 80 | device.send.calls.reset(); 81 | device.emitter.emit([MODULE, LENGTH], data, MODULE.toString(16), LENGTH.toString(16)); 82 | 83 | expect(device.send.calls.any()).toBe(false); 84 | 85 | }); 86 | 87 | it('should trigger the log READOUT specifying the correct number of entries to be notified for 0.05 notification progress', function() { 88 | var data = new Buffer([0x68,0x8,0x0,0x0]); 89 | log.downloadLog(); 90 | 91 | expect(device.send.calls.any()).toBe(true); 92 | 93 | device.send.calls.reset(); 94 | 95 | device.emitter.emit([MODULE, LENGTH], data, MODULE.toString(16), LENGTH.toString(16)); 96 | expect(device.send).toHaveBeenCalled(); 97 | expect(device.buffers.pop()).toEqual(new Buffer([0xb,0x6,0x68,0x8,0x0,0x0,0x6b,0x0,0x0,0x0])); 98 | }); 99 | 100 | describe('latestTick', function() { 101 | it('should be stored when the response of the first TIME request is received', function() { 102 | var data = new Buffer([0xb, 0x84, 0x78, 0xe5, 0xc9, 0x5e, 0x4]); 103 | 104 | log.downloadLog(); 105 | device.emitter.emit([MODULE, TIME], data, MODULE.toString(16), TIME.toString(16)); 106 | 107 | expect(log.latestTick.tick).toBeDefined(); 108 | expect(log.latestTick.tick).toEqual(1590289784); 109 | 110 | 111 | }); 112 | 113 | it('should handle value coded on less than 4 bytes', function() { 114 | var data = new Buffer([0xb, 0x84, 0x78, 0xe5, 0xc9, 0x5e, 0x4]); 115 | }); 116 | }); 117 | 118 | describe('log data processing', function() { 119 | it('should extract the accelerometer measures from a 20 bytes data frame', function() { 120 | 121 | var data = new Buffer([0x0b,0x7,0x40,0x3c,0xf9,0x91,0x18,0x8a,0xfc,0x87,0x4,0x41,0x3c,0xf9,0x91,0x18,0x84,0x41,0x0,0x0]); 122 | 123 | var accelerometerData_1 = {x: -0.0540771484375, y: 0.07073974609375, z: -0.105712890625}; 124 | //var accelerometerData_2 = {x: 0, y: 0, z: 0}; 125 | 126 | var foo = { 127 | callback: function(accelerometer_data) { 128 | return accelerometer_data; 129 | } 130 | }; 131 | 132 | spyOn(foo,'callback').and.callThrough(); 133 | 134 | log.downloadLog(foo.callback); 135 | //log.downloadLog(); 136 | log.onLogData(foo.callback); 137 | 138 | device.emitter.emit([MODULE, READOUT_NOTIFY], data, MODULE.toString(16), READOUT_NOTIFY.toString(32)); 139 | 140 | //console.log(foo.callback.calls.argsFor(0)[0]); 141 | expect(foo.callback.calls.argsFor(0)[0].x).toEqual(accelerometerData_1.x); 142 | expect(foo.callback.calls.argsFor(0)[0].y).toEqual(accelerometerData_1.y); 143 | expect(foo.callback.calls.argsFor(0)[0].z).toEqual(accelerometerData_1.z); 144 | 145 | }); 146 | 147 | describe('A completed log page notification', function() { 148 | it('should trigger the confirmation of its processing', function() { 149 | var data = new Buffer([0x0b,0xd]); 150 | 151 | log.downloadLog(); 152 | 153 | device.send.calls.reset(); 154 | 155 | device.emitter.emit([MODULE, READOUT_PAGE_COMPLETED], data, MODULE.toString(16), READOUT_PAGE_COMPLETED.toString(32)); 156 | expect(device.send).toHaveBeenCalled(); 157 | //console.log(device.buffers); 158 | expect(device.buffers.pop()).toEqual(new Buffer([0xb,READOUT_PAGE_CONFIRM])); 159 | }); 160 | }); 161 | 162 | }); 163 | 164 | 165 | }); 166 | 167 | }); 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /tests/spec/registers/magnetometerSpec.js: -------------------------------------------------------------------------------- 1 | // Magnetometer Specifications 2 | var Magnetometer = require('../../../src/registers/magnetometer'), 3 | Device = require('../helpers/device'), 4 | bufferEqual = require('buffer-equal'); 5 | 6 | var MODULE_OPCODE = 0x15; 7 | 8 | var MAG_DATA = 0x5; 9 | 10 | var PRESET_LOW_POWER = 0x0, 11 | PRESET_REGULAR = 0x1, 12 | PRESET_ENHANCED_REGULAR = 0x2, 13 | PRESET_HIGH_ACCURACY = 0x3; 14 | 15 | var ODR_10_HZ = 0x0, 16 | ODR_2_HZ = 0x1, 17 | ODR_6_HZ = 0x2, 18 | ODR_8_HZ = 0x3, 19 | ODR_15_HZ = 0x4, 20 | ODR_20_HZ = 0x5, 21 | ODR_25_HZ = 0x6, 22 | ODR_30_HZ = 0x7; 23 | 24 | var BFIELD_X_AXIS_INDEX = 0x1, 25 | BFIELD_Y_AXIS_INDEX = 0x2, 26 | BFIELD_Z_AXIS_INDEX = 0x3; 27 | 28 | const presets = [ 29 | { 30 | 'expected': [new Buffer([0x15, 0x04, 0x01, 0x02]), new Buffer([0x15, 0x03, 0x0])] , 31 | 'preset': Magnetometer.PRESET_LOW_POWER, 32 | 'preset_name': 'low power' 33 | }, 34 | { 35 | 'expected': [new Buffer([0x15, 0x04, 0x04, 0x0e]), new Buffer([0x15, 0x03, 0x00])], 36 | 'preset': Magnetometer.PRESET_REGULAR, 37 | 'preset_name': 'regular' 38 | }, 39 | { 40 | 'expected': [new Buffer([0x15, 0x04, 0x07, 0x1a]), new Buffer([0x15, 0x03, 0x00])], 41 | 'preset': Magnetometer.PRESET_ENHANCED_REGULAR, 42 | 'preset_name': 'enhanced regular' 43 | }, 44 | { 45 | 'expected': [new Buffer([0x15, 0x04, 0x17, 0x52]), new Buffer([0x15, 0x03, 0x05])], 46 | 'preset': Magnetometer.PRESET_HIGH_ACCURACY, 47 | 'preset_name': 'high accuracy' 48 | } 49 | ]; 50 | 51 | describe('Magnetometer', function() { 52 | var device = new Device(), 53 | magnetometer = new Magnetometer(device); 54 | 55 | beforeAll(function() { 56 | spyOn(device, 'send').and.callThrough(); 57 | spyOn(device, 'sendRead').and.callThrough(); 58 | jasmine.addCustomEqualityTester(bufferEqual); 59 | }); 60 | 61 | beforeEach(function() { 62 | device.reset(); 63 | device.send.calls.reset(); 64 | device.sendRead.calls.reset(); 65 | }); 66 | 67 | describe('enableAxisSampling()', function() { 68 | it('should enable sampling on the magnetometer', function() { 69 | magnetometer.enableAxisSampling(); 70 | expect(device.send).toHaveBeenCalled(); 71 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x2,0x1,0x0])); 72 | }); 73 | }); 74 | 75 | describe('disableAxisSampling()', function() { 76 | it('should disable sampling on the magnetometer', function() { 77 | magnetometer.disableAxisSampling(); 78 | expect(device.send).toHaveBeenCalled(); 79 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x2,0x0,0x1])); 80 | }); 81 | }); 82 | 83 | describe('start()', function() { 84 | it('should power on the magnetometer', function() { 85 | magnetometer.start(); 86 | expect(device.send).toHaveBeenCalled(); 87 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x1,0x1])); 88 | }); 89 | }); 90 | 91 | describe('stop()', function() { 92 | it('should power off the magnetometer', function() { 93 | magnetometer.stop(); 94 | expect(device.send).toHaveBeenCalled(); 95 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x1,0x0])); 96 | }); 97 | }); 98 | 99 | describe('subscribe()', function() { 100 | it('should subscribe to the magnetometer notification', function() { 101 | magnetometer.subscribe(); 102 | expect(device.send).toHaveBeenCalled(); 103 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x5,0x1])); 104 | }); 105 | }); 106 | 107 | describe('unsubscribe()', function() { 108 | it('should unsubscribe to the magnetometer notification', function() { 109 | magnetometer.unsubscribe(); 110 | expect(device.send).toHaveBeenCalled(); 111 | expect(device.buffers.pop()).toEqual(new Buffer([0x15,0x5,0x0])); 112 | }); 113 | }); 114 | 115 | describe('onChange', function() { 116 | it('should properly extract magnetometer data coded on 8 bytes and execute the callback', function() { 117 | var data = new Buffer([0x4e,0xf0,0x53,0x0a,0x75,0x04]); 118 | var expectedData = {x: -251.1250, y: 165.1875, z: 71.3125}; 119 | var foo = { 120 | callback: function(magnetometer_data) { 121 | return magnetometer_data; 122 | } 123 | }; 124 | spyOn(foo,'callback').and.callThrough(); 125 | magnetometer.onChange(foo.callback); 126 | device.emitter.emit([MODULE_OPCODE, MAG_DATA], data, MODULE_OPCODE.toString(16), MAG_DATA.toString(32)); 127 | 128 | expect(foo.callback.calls.argsFor(0)[0].x).toEqual(expectedData.x); 129 | expect(foo.callback.calls.argsFor(0)[0].y).toEqual(expectedData.y); 130 | expect(foo.callback.calls.argsFor(0)[0].z).toEqual(expectedData.z); 131 | }); 132 | }); 133 | 134 | describe('setPreset', function() { 135 | it('should send the right preset recommended by Bosh for the BMM150 magnetometer to the device', function() { 136 | for(var i = 0; i< presets.length; i++) { 137 | magnetometer.setPreset(presets[i].preset); 138 | expect(device.send).toHaveBeenCalled(); 139 | expect(device.buffers[0]).toEqual(presets[i].expected[0]); 140 | expect(device.buffers[1]).toEqual(presets[i].expected[1]); 141 | device.reset(); 142 | } 143 | }); 144 | }); 145 | 146 | 147 | }); 148 | -------------------------------------------------------------------------------- /tests/spec/registers/sensorfusionSpec.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | /* 4 | This specification has been build based on the python test 5 | suite located at https://github.com/mbientlab/Metawear-CppAPI/blob/master/test/test_sensor_fusion.py 6 | */ 7 | 8 | var SensorFusion = require('../../../src/registers/sensorFusion'), 9 | SensorFusionConfig = require('../../../src/registers/config/sensorFusion'), 10 | Device = require('../helpers/device'), 11 | Core = require('../../../src/registers/core'), 12 | bufferEqual = require('buffer-equal'), 13 | clone = require('clone'); 14 | 15 | 16 | const MODULE_OPCODE = 0x19; 17 | 18 | const MODE_SLEEP = 0x0, 19 | MODE_NDOF = 0x1, 20 | MODE_IMU_PLUS = 0x2, 21 | MODE_COMPASS = 0x3, 22 | MODE_M4G = 0x4; 23 | 24 | const ACC_RANGE_2G = 0x0, 25 | ACC_RANGE_4G = 0x1, 26 | ACC_RANGE_8G = 0x2, 27 | ACC_RANGE_16G = 0x3; 28 | 29 | const GYRO_RANGE_2000DPS = 0x0, 30 | GYRO_RANGE_1000DPS = 0x1, 31 | GYRO_RANGE_500DPS = 0x2, 32 | GYRO_RANGE_250DPS = 0x3; 33 | 34 | const ENABLE= 0x1, 35 | MODE= 0x2, 36 | OUTPUT_ENABLE= 0x3, 37 | CORRECTED_ACC= 0x4, 38 | CORRECTED_GYRO= 0x5, 39 | CORRECTED_MAG= 0x6, 40 | QUATERNION= 0x7, 41 | EULER_ANGLES= 0x8, 42 | GRAVITY_VECTOR= 0x9, 43 | LINEAR_ACC= 0xa; 44 | 45 | /* Data sources */ 46 | 47 | const DATA_CORRECTED_ACC = 0, 48 | DATA_CORRECTED_GYRO = 1, 49 | DATA_CORRECTED_MAG = 2, 50 | DATA_QUATERION = 3, 51 | DATA_EULER_ANGLE = 4, 52 | DATA_GRAVITY_VECTOR = 5, 53 | DATA_LINEAR_ACC = 6; 54 | 55 | const acc_ranges = [ 56 | ACC_RANGE_2G, 57 | ACC_RANGE_4G, 58 | ACC_RANGE_8G, 59 | ACC_RANGE_16G 60 | ]; 61 | 62 | const rot_ranges = [ 63 | GYRO_RANGE_2000DPS, 64 | GYRO_RANGE_1000DPS, 65 | GYRO_RANGE_500DPS, 66 | GYRO_RANGE_250DPS 67 | ]; 68 | 69 | const config_masks = [ 70 | [0x10, 0x11, 0x12, 0x13], 71 | [0x20, 0x21, 0x22, 0x23], 72 | [0x30, 0x31, 0x32, 0x33], 73 | [0x40, 0x41, 0x42, 0x43] 74 | ]; 75 | 76 | const bmi160_acc_range_bitmask = [ 0x3, 0x5, 0x8, 0xc ]; 77 | const bmi160_rot_range_bitmask = [ 0x0, 0x1, 0x2, 0x3, 0x4 ]; 78 | 79 | const tests_output = [ 80 | { 81 | 'expected': new Core.Quaternion(0.940, -0.050, -0.154, -0.301), 82 | 'response': new Buffer([0x1b,0x9b,0x70,0x3f,0x8c,0x5e,0x4d,0xbd,0x07,0x7f,0x1d,0xbe,0x78,0x02,0x9a,0xbe]), 83 | 'data': QUATERNION 84 | } 85 | ]; 86 | 87 | const data_sources = [ 88 | DATA_CORRECTED_ACC, 89 | DATA_CORRECTED_GYRO, 90 | DATA_CORRECTED_MAG, 91 | DATA_QUATERION, 92 | DATA_EULER_ANGLE, 93 | DATA_GRAVITY_VECTOR, 94 | DATA_LINEAR_ACC 95 | ]; 96 | 97 | /* The 'enable' property indexes the 'expected_start's item to mask in order to indicates the fusion's data_source */ 98 | 99 | const test_bases = [ 100 | { 101 | 'enable': 6, 102 | 'mode': MODE_NDOF, 103 | 'name': 'ndof', 104 | 'expected_start': [ 105 | new Buffer([0x03, 0x02, 0x01, 0x00]), 106 | new Buffer([0x13, 0x02, 0x01, 0x00]), 107 | new Buffer([0x15, 0x02, 0x01, 0x00]), 108 | new Buffer([0x03, 0x01, 0x01]), 109 | new Buffer([0x13, 0x01, 0x01]), 110 | new Buffer([0x15, 0x01, 0x01]), 111 | new Buffer([0x19, 0x03, 0x00, 0x00]), 112 | new Buffer([0x19, 0x01, 0x01]) 113 | ], 114 | 'expected_stop': [ 115 | new Buffer([0x19, 0x01, 0x00]), 116 | new Buffer([0x19, 0x03, 0x00, 0x7f]), 117 | new Buffer([0x03, 0x01, 0x00]), 118 | new Buffer([0x13, 0x01, 0x00]), 119 | new Buffer([0x15, 0x01, 0x00]), 120 | new Buffer([0x03, 0x02, 0x00, 0x01]), 121 | new Buffer([0x13, 0x02, 0x00, 0x01]), 122 | new Buffer([0x15, 0x02, 0x00, 0x01]) 123 | ] 124 | } 125 | ]; 126 | 127 | describe('SensorFusion - Metawear Motion R Board', function() { 128 | var device = new Device(), 129 | sensorFusion; 130 | 131 | beforeAll(function() { 132 | spyOn(device, 'send').and.callThrough(); 133 | jasmine.addCustomEqualityTester(bufferEqual); 134 | }); 135 | 136 | beforeEach(function() { 137 | device.reset(); 138 | device.send.calls.reset(); 139 | sensorFusion = new SensorFusion(device); 140 | }); 141 | 142 | describe('Configuration', function() { 143 | 144 | var queue_tests = []; 145 | var configureAlgorithm; 146 | 147 | beforeAll(function() { 148 | for(var i = 0; i < acc_ranges.length; i++) { 149 | for(var j = 0; j< rot_ranges.length; j++) { 150 | queue_tests.push({ 151 | 'acc_range': acc_ranges[i], 152 | 'gyro_range': rot_ranges[j] 153 | }); 154 | } 155 | } 156 | configureAlgorithm = function(mode, acc_range, gyro_range) { 157 | sensorFusion.config.setMode(mode); 158 | sensorFusion.config.setAccRange(acc_range); 159 | sensorFusion.config.setGyroRange(gyro_range); 160 | sensorFusion.writeConfig(); 161 | }; 162 | }); 163 | 164 | it('should be in sleep mode by default', function() { 165 | expect(sensorFusion.config.mode).toEqual(MODE_SLEEP); 166 | }); 167 | 168 | it('should be configured with a 16G accelerometer range by default', function() { 169 | expect(sensorFusion.config.acc_range).toEqual(ACC_RANGE_16G); 170 | }); 171 | 172 | it('should be configured with a 2000 DPS (degrees per second °/s) gyroscope range by default', function() { 173 | expect(sensorFusion.config.gyro_range).toEqual(GYRO_RANGE_2000DPS); 174 | }); 175 | 176 | describe('writeConfig() for a configured NDOF mode', function() { 177 | 178 | it('should properly send the NDOF mode, accelerometer and gyroscope range to the device', function() { 179 | for (var i = 0; i < queue_tests.length; i++) { 180 | configureAlgorithm(MODE_NDOF, queue_tests[i].acc_range, queue_tests[i].gyro_range); 181 | expect(device.send).toHaveBeenCalled(); 182 | expect(device.buffers[0]).toEqual(new Buffer([0x19, 0x02, MODE_NDOF, config_masks[queue_tests[i].gyro_range][queue_tests[i].acc_range]])); 183 | expect(device.buffers[1]).toEqual(new Buffer([0x03, 0x03, 0x28, bmi160_acc_range_bitmask[queue_tests[i].acc_range]])); 184 | expect(device.buffers[2]).toEqual(new Buffer([0x13, 0x03, 0x28, bmi160_rot_range_bitmask[queue_tests[i].gyro_range]])); 185 | expect(device.buffers[3]).toEqual(new Buffer([0x15, 0x04, 0x04, 0x0e])); 186 | expect(device.buffers[4]).toEqual(new Buffer([0x15, 0x03, 0x6])); 187 | device.reset(); 188 | } 189 | }); 190 | 191 | }); 192 | describe('setMode(mode)', function() { 193 | it('should properly set the mode', function() { 194 | sensorFusion.config.setMode(SensorFusionConfig.MODE.M4G); 195 | expect(sensorFusion.config.mode).toEqual(MODE_M4G); 196 | }); 197 | }); 198 | describe('setAccRange(acc_range)', function() { 199 | it('should properly set the accelerometer range', function() { 200 | sensorFusion.config.setAccRange(SensorFusionConfig.ACC_RANGE.AR_2G); 201 | expect(sensorFusion.config.acc_range).toEqual(ACC_RANGE_2G); 202 | }); 203 | }); 204 | describe('setGyroRange(gyro_range)', function() { 205 | it('should properly set the gyroscope range', function() { 206 | sensorFusion.config.setGyroRange(SensorFusionConfig.GYRO_RANGE.GR_250DPS); 207 | expect(sensorFusion.config.gyro_range).toEqual(GYRO_RANGE_250DPS); 208 | }); 209 | }); 210 | }); 211 | 212 | describe('onChange listener', function() { 213 | 214 | var foo = {}; 215 | 216 | beforeAll(function() { 217 | foo.callback = function(data) { return data;}; 218 | spyOn(foo,'callback').and.callThrough(); 219 | sensorFusion.onChange(foo.callback); 220 | }); 221 | 222 | //TODO : Implement every data type extraction 223 | xit('should properly extract any data type and execute the registered callback', function() {}); 224 | 225 | it('should properly extract QUATERNION data type and execute the registered callback', function() { 226 | for(var i = 0; i < tests_output.length; i++) { 227 | device.emitter.emit([MODULE_OPCODE, tests_output[i].data], tests_output[i].response, MODULE_OPCODE.toString(16), tests_output[i].data.toString(32)); 228 | expect(foo.callback.calls.argsFor(0)[0].isEqual(tests_output[i].expected)).toBe(true); 229 | foo.callback.calls.reset(); 230 | } 231 | }); 232 | }); 233 | 234 | describe('NDOF mode', function() { 235 | var tests = []; 236 | 237 | beforeAll(function() { 238 | for(var i=0; i