diff --git a/README.md b/README.md index d8969f9..c5da837 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,14 @@ ![](http://i.imgur.com/ef7aNZi.png) ## Introduction -This library makes it easy to control your UDOO Neo from node. +**TL;DR** +With this library, you can do ``require("udooneo").GPIO(37).out().val(1);`` -``var udooneo = require("./udooneo")`` and you have access to the GPIOs and motion sensors. +``var neo = require("./udooneo")`` and you have access to all your UDOO Neo GPIOs and motion sensors from Node.js. -You can get started by running the test example: ``sudo node examples/test`` + +You can get started by plugging a LED in the GPIO port 37 and running the test example: ``sudo node examples/test``. +The LED will blink every second. ## GPIOs @@ -15,82 +18,70 @@ You can get started by running the test example: ``sudo node examples/test`` You can either get access to the GPIO from its Extended Pinout number (the white number on pink background in the illustration above) or from its real, internal number. # Most common case - var gpio = new udooneo.GPIO().fromPin(pinNum); + var gpio = new neo.GPIO(pinNum); # For geeks only - var gpio = new udooneo.GPIO(gpioNum); + var gpio = new neo.GPIO().fromGpio(gpioNum); ### Read or change direction -Then you have to set the direction, either ``udooneo.DIRECTION.INPUT`` or ``udooneo.DIRECTION.OUTPUT``. Like most methods, ``setDirection(direction, callback)`` is asynchronous and takes a lambda function as a second parameter. - - gpio.setDirection(udooneo.DIRECTION.OUTPUT, function() { - console.log("The direction is set!"); - }); +To set the direction, you can use the shorthand methods ``gpio.in()`` and ``gpio.out()``. + +You can also get the current direction with ``getDirection()`` + + switch (gpio.getDirection()) { + case neo.DIRECTION.INPUT: + console.log("Direction = input!"); + break; + case neo.DIRECTION.OUTPUT: + console.log("Direction = output!"); + break; + } -You can also get the current direction with ``getDirection(callback)`` - - gpio.getDirection(function(direction) { - switch (direction) { - case udooneo.DIRECTION.INPUT: - console.log("Direction = input!"); - break; - case udooneo.DIRECTION.OUTPUT: - console.log("Direction = output!"); - break; - } - }); ### Read or change value -You can now either use ``gpio.getValue(callback)`` or ``gpio.setValue(value, callback)`` - -The ``callback`` function will be executed when the operation is done. In the case of ``getValue``, the first parameter of the callback function is fed the value of the GPIO. - - gpio.getValue(function(gpioValue) { - switch (gpioValue) { - case udooneo.VALUE.HIGH: - console.log("Value = High!"); - break; - case udooneo.VALUE.LOW: - console.log("Value = Low!"); - break; - default: - console.log("Value = " + gpioValue + "!"); - break; - } - }); +To read a value, use ``gpio.val()``. To set the value, use ``gpio.val(value)``. + + # Read value + switch (gpio.val()) { + case neo.VALUE.HIGH: + console.log("Value = High!"); + break; + case neo.VALUE.LOW: + console.log("Value = Low!"); + break; + default: + console.log("Value = " + gpio.val() + "!"); + break; + } - gpio.setValue(udooneo.VALUE.HIGH, function() { - console.log("The value is now high!"); - }); + # Change value + gpio.val(neo.VALUE.HIGH); ### Watch value changes -Another nice method to know and use is ``gpio.watchValue(callback)``. It works like an event listener, ``callback`` will be executed everytime the value of the GPIO changes, in real time. +You can make your script react to value changes by using ``gpio.watch(callback)``. It works like an event listener, ``callback`` will be executed everytime the value of the GPIO changes, in real time. - gpio.watchValue(function() { - gpio.getValue(function(gpioValue) { - console.log("The value has changed! The new value is " + gpioValue); - }); + gpio.watch(function() { + console.log("The value has changed! The new value is " + gpio.val()); }); ###Realease the GPIO access when you're done -Though the access to the GPIO is abstracted by the library, it has to be undone manually by calling ``gpio.unexport(callback)`` when you're done with the GPIO. I don't know the implications of failing to do this, so I wouldn't risk it. Consider it a good practice. The callback parameter/function is not mandatory. +Though the access to the GPIO is abstracted by the library, it has to be undone manually by calling ``gpio.unexport()`` when you're done with the GPIO. I don't know the implications of failing to do this, so I wouldn't risk it. Consider it a good practice. The callback parameter/function is not mandatory. ## Motion sensors -The 3 motion sensors (depending on your Neo version) are accessible via ``udooneo.Accelerometer``, ``udooneo.Magnetometer`` and ``udooneo.Gyroscope``. +The 3 motion sensors (depending on your Neo version) are accessible via ``neo.sensors`` (``.Accelerometer``, ``.Magnetometer`` and ``.Gyroscope``). To make it easier, you can access them via ``var sensors = require("udooneo").sensors``. ### Enable or disable -This couldn't be more straightforward, just ``.enable(callback)`` or ``.disable(calback)`` the sensor. As usual, these methods are asynchronous and take an optional callback function as a parameter. +This couldn't be more straightforward, just ``.enable()`` or ``.disable()`` the sensor. - udooneo.Accelerometer.enable(); - udooneo.Magnetometer.enable(); - udooneo.Gyroscope.enable(function() { - console.log("The Gyroscope is enabled!"); - udooneo.Gyroscope.disable(function() { - console.log("The Gyroscope is disabled!"); - }); - }); + sensors.Accelerometer.enable(); + sensors.Magnetometer.enable(); + sensors.Gyroscope.enable(); + console.log("All the sensors are enabled"); + + sensors.Gyroscope.disable(); + console.log("Gyroscope is disabled"); ![](https://38.media.tumblr.com/tumblr_lkx3onuocM1qzvwy3.gif) @@ -98,13 +89,11 @@ This couldn't be more straightforward, just ``.enable(callback)`` or ``.disable( ### Get data -To get the current data for a motion sensor, use its ``.getData(callback)`` method. +To get the current data for a motion sensor, use its ``.data()`` method. - udooneo.Magnetometer.getData(function(data) { - console.log("Magnometer data: " + data); - }); + console.log("Magnometer data: " + sensors.Magnetometer.data(); -Due to the way the value is returned, there's no ``watchData(callback)`` method, you have to use a ``setInterval`` or ``setTimeout`` to know if the value of a motion sensor has changed (hint: it's changing constantly). +Due to the way the value is returned, there's no ``.watch(callback)`` method, you have to use a ``setInterval`` or ``setTimeout`` to know if the value of a motion sensor has changed (hint: it's changing constantly). The data is returned in the form of a string, someting like: ``data = "1.0,1.0,1.0"``. I decided to return this bluntly as a string and not to convert it to a special kind of object for the moment for performance purposes. It's up to you. diff --git a/examples/motion.js b/examples/motion.js index b891bff..286e6fe 100644 --- a/examples/motion.js +++ b/examples/motion.js @@ -1,43 +1,29 @@ -var udooneo = require("../udooneo"); +var sensors = require("../udooneo").sensors; -udooneo.Accelerometer.enable(); -udooneo.Magnetometer.enable(); -udooneo.Gyroscope.enable(); +var acc = sensors.Accelerometer.enable(); +var mag = sensors.Magnetometer.enable(); +var gyr = sensors.Gyroscope.enable(); -var values = { - Accelerometer: "", - Magnetometer: "", - Gyroscope: "" -}; +var int = setInterval(function () { + // Write values + process.stdout.write('\033c'); + console.log([ + + "Accelerometer: " + acc.data(), + "Magnetometer: " + mag.data(), + "Gyroscope: " + gyr.data() + + ].join("\r\n")); + +}, 100); + +// Disable to preserve battery setTimeout(function () { + clearInterval(int); + + acc.disable(); + mag.disable(); + gyr.disable(); - var int = setInterval(function () { - - udooneo.Accelerometer.getData(function (data) { - values.Accelerometer = data; - }); - udooneo.Magnetometer.getData(function (data) { - values.Magnetometer = data; - }); - udooneo.Gyroscope.getData(function (data) { - values.Gyroscope = data; - }); - - process.stdout.write('\033c'); - console.log([ - "Accelerometer: " + values.Accelerometer, - "Magnetometer: " + values.Magnetometer, - "Gyroscope: " + values.Gyroscope, - ].join("\r\n")); - - }, 100); - - setTimeout(function () { - clearInterval(int); - udooneo.Accelerometer.disable(); - udooneo.Magnetometer.disable(); - udooneo.Gyroscope.disable(); - }, 10000); - -}, 500); \ No newline at end of file +}, 10000); \ No newline at end of file diff --git a/examples/test.js b/examples/test.js index e18dbcb..1f41f4f 100644 --- a/examples/test.js +++ b/examples/test.js @@ -1,22 +1,8 @@ -var udooneo = require("../udooneo"); +var neo = require("../udooneo"); -udooneo.gpioNumbers.forEach(function (gpioNum) { - var gpio = new udooneo.GPIO(gpioNum); - gpio.getValue(function (value) { - console.log("GPIO " + gpioNum + " current value: " + value); - }); - gpio.watchValue(function () { - gpio.getValue(function (value) { - console.log("GPIO " + gpioNum + " change - new value: " + value); - }); - }); -}); +var gpio = new neo.GPIO(37).out(); var x = 0; -var targetGpio = new udooneo.GPIO().fromPin(34); -targetGpio.setDirection(udooneo.DIRECTION.OUTPUT, function () { - var int = setInterval(function () { - if (x > 4) clearInterval(int); - targetGpio.setValue(x++ % 2 ? udooneo.VALUE.LOW : udooneo.VALUE.HIGH); - }, 2000); -}); \ No newline at end of file +setInterval(function () { + gpio.val(x++ % 2 ? neo.VALUE.LOW : neo.VALUE.HIGH); +}, 1000); \ No newline at end of file diff --git a/examples/unexport-all.js b/examples/unexport-all.js index 39ad720..4d16b68 100644 --- a/examples/unexport-all.js +++ b/examples/unexport-all.js @@ -1,8 +1,6 @@ -var udooneo = require("../udooneo"); +var neo = require("../udooneo"); -udooneo.gpioNumbers.forEach(function (gpioNum) { - var gpio = new udooneo.GPIO(gpioNum); - gpio.unexport(function () { - console.log("GPIO " + gpioNum + " unexported."); - }); +neo.gpios.each(function (gpio) { + gpio.unexport(); + console.log(gpio.num() + " unexported."); }); \ No newline at end of file diff --git a/udooneo.js b/udooneo.js index 45a9c23..9ee0864 100644 --- a/udooneo.js +++ b/udooneo.js @@ -9,9 +9,9 @@ var FILE_PATHS = { EXPORT_FILE: "export", UNEXPORT_FILE: "unexport" }; -var GPIOReference = { +var Ref = { - gpioNumbers: [ + gpios: [ 106, 107, 180, 181, 172, 173, 182, 124, 25, 22, 14, 15, 16, 17, 18, 19, 20, 21, 203, 202, 177, 176, 175, 174, @@ -29,18 +29,16 @@ var GPIOReference = { fromPin: function (pinNumber) { if (16 > pinNumber || pinNumber > 47) throw new Error("Invalid pin number."); - return GPIOReference.gpioNumbers[pinNumber - 16]; + return Ref.gpios[pinNumber - 16]; }, isValid: function (gpioNumber) { - return GPIOReference.gpioNumbers.indexOf(gpioNumber) > -1; + return Ref.gpios.indexOf(gpioNumber) > -1; } }; function GPIO(num) { - if (num) { - this.fromGpio(num); - } + if (num) return this.fromPin(num); } GPIO.prototype = { @@ -52,93 +50,89 @@ GPIO.prototype = { }, fromPin: function (num) { - this.currentGpioNumber = GPIOReference.fromPin(num); + this.currentGpioNumber = Ref.fromPin(num); return this; }, fromGpio: function (gpioNumber) { - if (!GPIOReference.isValid(gpioNumber)) throw new Error("GPIO number (" + gpioNumber + ") is out of range."); + if (!Ref.isValid(gpioNumber)) throw new Error("GPIO number (" + gpioNumber + ") is out of range."); this.currentGpioNumber = gpioNumber; return this; }, - export: function (callback) { - var currentNum = this.currentGPIO(); - var exportFilePath = FILE_PATHS.GPIO_ROOT + path.sep + FILE_PATHS.EXPORT_FILE; - File.exists(FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum, - function success () { - // already exported - if (callback) callback(); - }, - function failure () { - File.write(currentNum.toString(), exportFilePath, function () { - if (callback) callback(); - }); - } - ); + _paths: function () { + var gpioPath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + this.currentGPIO(); + return { + value: gpioPath + path.sep + "value", + direction: gpioPath + path.sep + "direction" + } }, - unexport: function (callback) { - var currentNum = this.currentGPIO(); - var exportFilePath = FILE_PATHS.GPIO_ROOT + path.sep + FILE_PATHS.UNEXPORT_FILE; - File.exists(FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum, - function success () { - File.write(currentNum.toString(), exportFilePath, function () { - if (callback) callback(); - }); - }, - function failure () { - // already unexported - if (callback) callback(); - } - ); + + _export: function (yes) { + var currentNum = this.currentGPIO().toString(); + var rootPath = FILE_PATHS.GPIO_ROOT + path.sep; + + var gpioFileExists = File.exists(rootPath + "gpio" + currentNum); + + if (yes && gpioFileExists) return; // Already exported + if (!yes && !gpioFileExists) return; // Already unexported + + File.write(currentNum, rootPath + (yes ? FILE_PATHS.EXPORT_FILE : FILE_PATHS.UNEXPORT_FILE)); + + }, + export: function () { + this._export(true); + }, + unexport: function () { + this._export(false); }, - setDirection: function (direction, callback) { - if ([GPIOReference.DIRECTION.INPUT, GPIOReference.DIRECTION.OUTPUT].indexOf(direction) < 0) throw new Error("Invalid direction."); - var currentGPIO = this.currentGPIO(); - this.export(function () { - var directionPath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentGPIO + path.sep + "direction"; - File.write(direction, directionPath, function () { - if (callback) callback(); - }); - }); + setDirection: function (direction) { + if ([Ref.DIRECTION.INPUT, Ref.DIRECTION.OUTPUT].indexOf(direction) < 0) throw new Error("Invalid direction."); + this.export(); + File.write(direction, this._paths().direction); + return this; }, - getDirection: function (callback) { - var currentNum = this.currentGPIO(); - this.export(function () { - var directionPath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum + path.sep + "direction"; - File.read(directionPath, function (data) { - callback(data); - }); - }); + setValue: function (value) { + this.export(); + File.write(value.toString(), this._paths().value); + return this; }, - setValue: function (value, callback) { - if ([GPIOReference.VALUE.HIGH, GPIOReference.VALUE.LOW].indexOf(value) < 0) throw new Error("Invalid value."); - var currentNum = this.currentGPIO(); - this.export(function () { - var valuePath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum + path.sep + "value"; - File.write(value.toString(), valuePath, function () { - if (callback) callback(); - }); - }); + getDirection: function () { + this.export(); + return File.read(this._paths().direction); }, - getValue: function (callback) { - var currentNum = this.currentGPIO(); - var valuePath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum + path.sep + "value"; - this.export(function () { - File.read(valuePath, function (data) { - callback(data); - }); - }); + getValue: function () { + this.export(); + return File.read(this._paths().value); }, watchValue: function (callback) { - var currentNum = this.currentGPIO(); - var valuePath = FILE_PATHS.GPIO_ROOT + path.sep + "gpio" + currentNum + path.sep + "value"; - this.export(function () { - File.watch(valuePath, function () { - callback(); - }); + this.export(); + File.watch(this._paths().value, function () { + callback(); }); + }, + + // Shorthands + in: function () { + return this.setDirection(Ref.DIRECTION.INPUT); + }, + out: function () { + return this.setDirection(Ref.DIRECTION.OUTPUT); + }, + dir: function (d) { + if (!d) return this.getDirection(); + this.setDirection(d); + }, + val: function (v) { + if (!v) return this.getValue(); + this.setValue(v); + }, + num: function () { + return this.currentGPIO(); + }, + watch: function (cb) { + this.watchValue(cb); } }; @@ -146,47 +140,53 @@ function MotionSensor(path) { this.path = path; } MotionSensor.prototype = { - enable: function (callback) { - var filePath = this.path + path.sep + "enable"; - var value = 1; - File.write(value.toString(), filePath, function () { - if (callback) callback(); - }); + _enable: function (y) { + File.write((y ? 1 : 0).toString(), + this.path + path.sep + (y ? "enable" : "disable") + ); }, - disable: function (callback) { - var filePath = this.path + path.sep + "disable"; - var value = 0; - File.write(value.toString(), filePath, function () { - if (callback) callback(); - }); + enable: function () { + this._enable(true); + return this; }, - getData: function (callback) { - var filePath = this.path + path.sep + "data"; - File.read(filePath, function (data) { - callback(data); - }); + disable: function () { + this._enable(false); + return this; + }, + getData: function () { + return File.read(this.path + path.sep + "data"); + }, + + // Shorthands + on: function () { + return this.enable(); + }, + off: function () { + return this.disable(); + }, + data: function () { + return this.getData(); } }; var File = { - exists: function (path, success, failure) { - fs.access(path, function (err) { - if (!err) success(); else failure(); - }); + exists: function (path) { + try { + fs.accessSync(path); + return true; + } catch (e) { + return false; + } }, - read: function (filePath, callback) { - fs.readFile(filePath, "utf-8", function (err, data) { - if (err) throw err; - callback(data); - }); + read: function (filePath) { + return fs.readFileSync(filePath, "utf-8"); }, watch: function (filePath, callback) { fs.watch(filePath, function (event) { if ("change" === event) callback(); }); }, - write: function (data, filePath, callback) { - // console.log("trying to write " + data + " in " + filePath); + write: function (data, filePath) { try { var fd = fs.openSync(filePath, "w", function (err) { if (err) { @@ -194,22 +194,31 @@ var File = { throw err; } }); - fs.writeSync(fd, data, "utf-8"); + return fs.writeSync(fd, data, "utf-8"); } catch (err) { if ("EBUSY" === err.errno) { - this.write(data, filePath, callback); + return this.write(data, filePath); } } - callback(); } }; module.exports = { - gpioNumbers: GPIOReference.gpioNumbers, - DIRECTION: GPIOReference.DIRECTION, - VALUE: GPIOReference.VALUE, GPIO: GPIO, - Accelerometer: new MotionSensor(FILE_PATHS.ACCELEROMETER_ROOT), - Magnetometer: new MotionSensor(FILE_PATHS.MAGNETOMETER_ROOT), - Gyroscope: new MotionSensor(FILE_PATHS.GYROSCOPE_ROOT) + + DIRECTION: Ref.DIRECTION, + VALUE: Ref.VALUE, + + gpioNumbers: Ref.gpios, + gpios: { + each: function (callback) { + var num = Ref.gpios.length; + while (num--) callback(new GPIO().fromGpio(Ref.gpios[num])); + } + }, + sensors: { + Accelerometer: new MotionSensor(FILE_PATHS.ACCELEROMETER_ROOT), + Magnetometer: new MotionSensor(FILE_PATHS.MAGNETOMETER_ROOT), + Gyroscope: new MotionSensor(FILE_PATHS.GYROSCOPE_ROOT) + } }; \ No newline at end of file