diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..747725c --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2012 Cam Pedersen + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index f3d84d2..61cdd6e 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,22 @@ Pins can be sent as an integer or a string(`1`, `2`, `"3"`, `"A0"`) * `board.HIGH`(`255`) * integer/string from `0`-`255` for PWM pins + +## Development + +# Requires grunt for code linting & test running. + +```bash +npm install grunt -g +``` + +Run with: + +``` +grunt +``` + + # license (The MIT License) @@ -236,4 +252,3 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/examples/analogled.js b/examples/analogled.js index 66d891a..a4d5c3d 100644 --- a/examples/analogled.js +++ b/examples/analogled.js @@ -6,9 +6,9 @@ var board = new arduino.Board({ var aled = new arduino.Led({ board: board, - pin: 9 + pin: 'A0' }); -board.on('ready', function(){ +board.on('ready', function() { aled.fade(); }); diff --git a/examples/basic.js b/examples/basic.js index ca573c0..12676d2 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -1,9 +1,11 @@ var arduino = require('../'); -var board = new arduino.Board(); +var board = new arduino.Board({ + debug: true +}); -board.on('connected', function(){ +board.on('connected', function() { board.write('HELLO WORLD'); }); diff --git a/examples/button.js b/examples/button.js index 1ba9ad6..faf128f 100644 --- a/examples/button.js +++ b/examples/button.js @@ -7,10 +7,10 @@ var button = new arduino.Button({ pin: 2 }); -button.on('down', function(){ +button.on('down', function() { console.log('DOWN'); }); -button.on('up', function(){ +button.on('up', function() { console.log('UP'); }); diff --git a/examples/combination.js b/examples/combination.js index a5812e4..8ff85c6 100644 --- a/examples/combination.js +++ b/examples/combination.js @@ -12,10 +12,10 @@ var led = new arduino.Led({ pin: 13 }); -button.on('down', function(){ +button.on('down', function() { led.on(); }); -button.on('up', function(){ +button.on('up', function() { led.off(); }); diff --git a/examples/gps.js b/examples/gps.js new file mode 100644 index 0000000..717484f --- /dev/null +++ b/examples/gps.js @@ -0,0 +1,36 @@ +var arduino = require('../'), + board, gps; + +board = new arduino.Board({ + debug: true +}); + +gps = new arduino.GPS({ + board: board, + pin: 2 +}); + +// watchLocation +gps.on('locate', function( err, data ) { + + console.log( data ); + +/* + +Looks like: + +{ + time: '033355.000', + latitude: '4221.1902', + longitude: '07108.7709', + hemisphere: { latitude: 'N', longitude: 'W' }, + quality: '2', + satellites: 6, + hdop: 1.6, + altitude: '8.0M' +} + +*/ + + +}); diff --git a/examples/led.js b/examples/led.js index 1325284..4d2030d 100644 --- a/examples/led.js +++ b/examples/led.js @@ -6,9 +6,9 @@ var board = new arduino.Board({ var led = new arduino.Led({ board: board, - pin: "A0" + pin: 9 }); -board.on('ready', function(){ +board.on('ready', function() { led.blink(); }); diff --git a/examples/piezo.js b/examples/piezo.js index dd59278..c1ecd12 100644 --- a/examples/piezo.js +++ b/examples/piezo.js @@ -8,7 +8,7 @@ var piezo = new arduino.Piezo({ board: board }); -board.on('ready', function(){ +board.on('ready', function() { piezo.note('a', 1000); setTimeout(function(){ diff --git a/examples/servo.js b/examples/servo.js index eb732ab..0c2b44c 100644 --- a/examples/servo.js +++ b/examples/servo.js @@ -17,13 +17,13 @@ servo = new arduino.Servo({ }); // Once servo is attached: -// - "read" +// - 'read' // - log position -// - "aftersweep" +// - 'aftersweep' // - blink the led // - read the position // - detach the servo -// - "detached" +// - 'detached' // - log detach message // // - execute full sweep @@ -52,5 +52,3 @@ servo.on('attached', function(err) { // To test, use the following: // http://arduino.cc/en/uploads/Tutorial/sweep_BB.png // -// More information: -// http://www.ladyada.net/learn/sensors/pir.html diff --git a/grunt.js b/grunt.js new file mode 100644 index 0000000..79359ee --- /dev/null +++ b/grunt.js @@ -0,0 +1,38 @@ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + pkg: '', + // test: { + // files: ['test/**/*.js'] + // }, + lint: { + files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] + }, + watch: { + files: '', + tasks: 'default' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true + }, + globals: { + exports: true + } + } + }); + + // Default task. + grunt.registerTask('default', 'lint'); +}; diff --git a/index.js b/index.js index a30f8f9..54f2709 100644 --- a/index.js +++ b/index.js @@ -7,5 +7,6 @@ module.exports = { Servo: require('./lib/servo'), Sensor: require('./lib/sensor'), Ping: require('./lib/ping'), - PIR: require('./lib/pir') + PIR: require('./lib/pir'), + GPS: require('./lib/gps') }; diff --git a/lib/board.js b/lib/board.js index af30801..9f05d2d 100644 --- a/lib/board.js +++ b/lib/board.js @@ -9,19 +9,25 @@ var events = require('events'), * The main Arduino constructor * Connect to the serial port and bind */ -var Board = function (options) { +var Board = function(options) { this.log('info', 'initializing'); this.debug = options && options.debug || false; this.writeBuffer = []; var self = this; - this.detect(function (err, serial) { - if (err) throw err; + + this.detect(function(err, serial) { + if (err) { + throw err; + } self.serial = serial; self.emit('connected'); self.log('info', 'binding serial events'); - self.serial.on('data', function(data){ + self.serial.on('data', function(data) { + if ( !data.trim() ) { + return; + } self.log('receive', data.toString().red); self.emit('data', data); }); @@ -50,7 +56,7 @@ var Board = function (options) { self.emit('ready'); }, 500); }); -} +}; /* * EventEmitter, I choose you! @@ -62,13 +68,13 @@ util.inherits(Board, events.EventEmitter); * Loop through all USB devices and try to connect * This should really message the device and wait for a correct response */ -Board.prototype.detect = function (callback) { +Board.prototype.detect = function(callback) { this.log('info', 'attempting to find Arduino board'); var self = this; - child.exec('ls /dev | grep usb', function(err, stdout, stderr){ + child.exec('ls /dev | grep usb', function(err, stdout, stderr) { + err = err || null; var usb = stdout.slice(0, -1).split('\n'), found = false, - err = null, possible, temp; while ( usb.length ) { @@ -95,31 +101,31 @@ Board.prototype.detect = function (callback) { callback(err, found); }); -} +}; /* * The board will eat the first 4 bytes of the session * So we give it crap to eat */ -Board.prototype.sendClearingBytes = function () { +Board.prototype.sendClearingBytes = function() { this.serial.write('00000000'); -} +}; /* * Process the writeBuffer (messages attempted before serial was ready) */ -Board.prototype.processWriteBuffer = function () { +Board.prototype.processWriteBuffer = function() { this.log('info', 'processing buffered messages'); while (this.writeBuffer.length > 0) { this.log('info', 'writing buffered message'); this.write(this.writeBuffer.shift()); } -} +}; /* * Low-level serial write */ -Board.prototype.write = function (m) { +Board.prototype.write = function(m) { if (this.serial) { this.log('write', m); this.serial.write('!' + m + '.'); @@ -127,22 +133,22 @@ Board.prototype.write = function (m) { this.log('info', 'serial not ready, buffering message: ' + m.red); this.writeBuffer.push(m); } -} +}; /* * Add a 0 to the front of a single-digit pin number */ -Board.prototype.normalizePin = function (pin) { +Board.prototype.normalizePin = function(pin) { return this.lpad( 2, '0', pin ); -} +}; Board.prototype.normalizeVal = function(val) { return this.lpad( 3, '0', val ); -} +}; // Board.prototype.lpad = function(len, chr, str) { - return (Array(len + 1).join(chr || ' ') + str).substr(-len); + return ((new Array(len + 1)).join(chr || ' ') + str).substr(-len); }; /* @@ -156,64 +162,65 @@ Board.prototype.LOW = '000'; * val == out = 001 * val == in = 000 */ -Board.prototype.pinMode = function (pin, val) { +Board.prototype.pinMode = function(pin, val) { pin = this.normalizePin(pin); this.log('info', 'set pin ' + pin + ' mode to ' + val); val = ( - val == 'out' ? + val === 'out' ? this.normalizeVal(1) : this.normalizeVal(0) ); this.write('00' + pin + val); -} +}; /* * Tell the board to write to a digital pin */ -Board.prototype.digitalWrite = function (pin, val) { +Board.prototype.digitalWrite = function(pin, val) { pin = this.normalizePin(pin); val = this.normalizeVal(val); this.log('info', 'digitalWrite to pin ' + pin + ': ' + val.green); this.write('01' + pin + val); -} +}; /* * Tell the board to extract data from a pin */ -Board.prototype.digitalRead = function (pin) { +Board.prototype.digitalRead = function(pin) { pin = this.normalizePin(pin); this.log('info', 'digitalRead from pin ' + pin); this.write('02' + pin + this.normalizeVal(0)); -} +}; -Board.prototype.analogWrite = function (pin, val) { +Board.prototype.analogWrite = function(pin, val) { pin = this.normalizePin(pin); val = this.normalizeVal(val); this.log('info', 'analogWrite to pin ' + pin + ': ' + val.green); this.write('03' + pin + val); -} -Board.prototype.analogRead = function (pin) { +}; + +Board.prototype.analogRead = function(pin) { pin = this.normalizePin(pin); this.log('info', 'analogRead from pin ' + pin); this.write('04' + pin + this.normalizeVal(0)); -} +}; /* * Utility function to pause for a given time */ -Board.prototype.delay = function (ms) { +Board.prototype.delay = function(ms) { ms += +new Date(); while (+new Date() < ms) { } -} +}; /* * Logger utility function */ -Board.prototype.log = function (/*level, message*/) { +Board.prototype.log = function(/*level, message*/) { var args = [].slice.call(arguments); if (this.debug) { console.log(String(+new Date()).grey + ' duino '.blue + args.shift().magenta + ' ' + args.join(', ')); } -} +}; module.exports = Board; diff --git a/lib/button.js b/lib/button.js index 636a132..ed65272 100644 --- a/lib/button.js +++ b/lib/button.js @@ -6,35 +6,40 @@ var events = require('events'), * Process options * Tell the board to set it up */ -var Button = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to Button'); +var Button = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to Button'); + } this.board = options.board; - this.pin = options.pin || 13; - this.down = false; + this.pin = this.board.normalizePin(options.pin || 13); this.board.pinMode(this.pin, 'in'); - var self = this; - setInterval(function () { - self.board.digitalRead(self.pin); - }, 50); - this.board.on('data', function (m) { + + this.down = false; + + setInterval(function() { + this.board.digitalRead(this.pin); + }.bind(this), 50); + + this.board.on('data', function(m) { m = m.slice(0, -1).split('::'); var err = null; - if (m.length > 1 && m[0] == self.pin) { + if (m.length > 1 && m[0] === this.pin) { // 0 is up // 1 is down - if (m[1] == 0 && self.down) { - self.down = false; - self.emit('up', err); - } - if (m[1] == 1 && !self.down) { - self.down = true; - self.emit('down', err); + if (+m[1] === 0 && this.down) { + this.down = false; + this.emit('up', err); + } else { + if (+m[1] === 1 && !this.down) { + this.down = true; + this.emit('down', err); + } } } - }); -} + }.bind(this)); +}; /* * EventEmitter, I choose you! diff --git a/lib/gps.js b/lib/gps.js new file mode 100644 index 0000000..0098a44 --- /dev/null +++ b/lib/gps.js @@ -0,0 +1,105 @@ +var events = require('events'), + util = require('util'); + +/* + * Main GPS constructor + * Process options + * Tell the board to set it up + */ + +function ToDecimalDegrees() { +} + +var GPS = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to GPS'); + } + this.board = options.board; + this.pin = this.board.normalizePin(options.pin || 2); + + this.board.on('ready', function() { + this.board.log('info', 'initializing gps'); + this.init(); + }.bind(this)); + + this.board.on('data', function(message) { + var m = message.slice(0, -1), //m = message.slice(0, -1).split('::'), + err = null, + data = { + time: null, + latitude: null, + longitude: null, + hemisphere: { + latitude: null, + longitude: null + }, + quality: null, + satellites: null, + hdop: null, + altitude: null + }, + pin, fields; + + if (!m.length) { + return; + } + + // pin = m[0]; + + // Refer to + // http://aprs.gids.nl/nmea/#gga + // http://www.gpsinformation.org/dale/nmea.htm#position + + + if ( m.indexOf('$GPGGA') === 0 ) { + // console.log( 'valid' ); + // console.log( message ); + + fields = m.split(','); + + // TODO: make time less shitty. + data.time = fields[1]; + data.latitude = fields[2]; + data.longitude = fields[4]; + + data.hemisphere = { + latitude: fields[3], + longitude: fields[5] + }; + + data.quality = fields[6]; + data.satellites = +fields[7]; + data.hdop = +fields[8]; + data.altitude = fields[9] + fields[10]; + + // TODO: Do something usefull with err + //$GPGGA,024322.000,4221.1928,N,07108.7770,W,2,07,1.2,6.2,M,-33.7,M,1.8,0000*4B + this.emit('locate', err, data); + } + }.bind(this)); +}; + +util.inherits(GPS, events.EventEmitter); + +GPS.prototype.command = function() { + var msg = '96' + this.pin + ([].slice.call(arguments).join('')); + + // this.board.log( 'info', 'command', msg ); + this.board.write(msg); +}; + +GPS.prototype.init = function() { + this.command('01'); +}; + +GPS.prototype.setPosition = function() { + // Reserved 02 code + // this.command('02'); +}; + +GPS.prototype.getPosition = function() { + this.command('03'); +}; + + +module.exports = GPS; diff --git a/lib/led.js b/lib/led.js index 6017587..c061623 100644 --- a/lib/led.js +++ b/lib/led.js @@ -4,60 +4,71 @@ * Process options * Tell the board to set it up */ -var Led = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to LED'); +var Led = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to LED'); + } this.board = options.board; this.pin = options.pin || 13; - this.bright = 0; this.board.pinMode(this.pin, 'out'); + + this.bright = 0; this.direction = -1; -} +}; /* * Turn the LED on */ -Led.prototype.on = function () { +Led.prototype.on = function() { this.board.digitalWrite(this.pin, this.board.HIGH); this.bright = 255; -} +}; -/* +/* * Turn the LED off */ -Led.prototype.off = function () { +Led.prototype.off = function() { this.board.digitalWrite(this.pin, this.board.LOW); this.bright = 0; -} +}; Led.prototype.brightLevel = function(val) { this.board.analogWrite(this.pin, this.bright = val); -} +}; Led.prototype.fade = function(interval) { - to = (interval||5000)/(255*2); - var self = this; + var to = (interval||5000)/(255*2), + direction; + setInterval(function() { - if(!self.board.serial) return; //Interval too fast for debug messages on ^c - if(self.bright==0) direction = 1; - if(self.bright==255) direction = -1; - self.brightLevel(self.bright+direction); - },to); -} + if (!this.board.serial) { + //Interval too fast for debug messages on ^c + return; + } + if (+this.bright === 0) { + direction = 1; + } + if (+this.bright === 255) { + direction = -1; + } + this.brightLevel(this.bright + direction); + }.bind(this), to); +}; /* * Start a bariable blinking pattern */ -Led.prototype.blink = function (interval) { +Led.prototype.blink = function(interval) { interval = interval || 1000; - var self = this; + setInterval(function(){ - if (self.bright) { - self.off() + if (this.bright) { + this.off(); } else { - self.on(); + this.on(); } - }, interval); -} + }.bind(this), interval); +}; module.exports = Led; diff --git a/lib/piezo.js b/lib/piezo.js index a3a0dbb..cee881d 100644 --- a/lib/piezo.js +++ b/lib/piezo.js @@ -1,43 +1,65 @@ /* - * Main Pieze constructor + * Main Piezo constructor * Process options * Tell the board to set pin mode */ -var Piezo = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to Piezo'); +var Piezo = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to Piezo'); + } + this.board = options.board; - this.pin = options.pin || 13; - this.bright = false; + this.pin = this.board.normalizePin(options.pin || 11); + + // Set pinMode for OUTPUT this.board.pinMode(this.pin, 'out'); -} + + // Module specific instance properties + // Piezo buzzer brightness + this.bright = false; + + this.playing = false; + // Locked? This piezo's current lock state, + // this allows us to avoid potentially making way too + // may serial writes + this.isLocked = false; + // Stores data about this piezo's state + // used to determine lock state + this.state = { + tone: null, + note: null, + lapse: null + }; +}; /* * Send a square wave to the speaker for a given duration */ -Piezo.prototype.tone = function (tone, duration) { +Piezo.prototype.tone = function(tone, duration) { this.board.log('info', 'starting tone ' + tone.toString().green + ' for ' + duration.toString().green + ' milliseconds'); - var self = this, - tone = Math.floor(tone / 1000); // timeHigh is in microseconds and our delay() function is in milliseconds + // timeHigh is in microseconds and our delay() function is in milliseconds + var micro = Math.floor(tone / 1000), i = 0; setInterval(function(){ - if (self.bright) { - self.board.digitalWrite(self.pin, self.board.LOW); - self.bright = false; + if (this.bright) { + this.board.digitalWrite(this.pin, this.board.LOW); + this.bright = false; } else { - self.board.digitalWrite(self.pin, self.board.HIGH); - self.bright = true; + this.board.digitalWrite(this.pin, this.board.HIGH); + this.bright = true; } if (i++ >= duration) { - self.board.log('info', 'tone end'); clearInterval(this); + + this.board.log('info', 'tone end'); } - }, tone); + }.bind(this), micro); -} +}; /* * Play a tone for a given duration to create a note @@ -54,19 +76,17 @@ Piezo.prototype.tone = function (tone, duration) { * b 493hz 2028 1014 * c 523hz 1912 956 */ -Piezo.prototype.note = function (note, duration) { +Piezo.prototype.note = function(note, duration) { this.board.log('info', 'playing note ' + note.green + ' for ' + duration.toString().green + ' milliseconds'); - var notes = { + this.tone({ 'c': 1915, 'd': 1700, 'e': 1519, 'f': 1432, 'g': 1275, 'a': 1136, - 'b': 1014, - 'c': 956 - }; - this.tone(notes[note], duration); -} + 'b': 1014 + }[ note ], duration); +}; module.exports = Piezo; diff --git a/lib/ping.js b/lib/ping.js index 9eac71b..48eb21f 100644 --- a/lib/ping.js +++ b/lib/ping.js @@ -6,8 +6,10 @@ var events = require('events'), * Process options * Tell the board to set it up */ -var Ping = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to Ping'); +var Ping = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to Ping'); + } this.board = options.board; this.pin = this.board.normalizePin(options.pin || 9); @@ -21,11 +23,11 @@ var Ping = function (options) { }; // Loop and trigger fire-read sequences - setInterval(function () { + setInterval(function() { this.fire(); }.bind(this), 50); - this.board.on('data', function (message) { + this.board.on('data', function(message) { var m = message.slice(0, -1).split('::'), err = null, pin, type, data; @@ -39,6 +41,7 @@ var Ping = function (options) { data = m.length === 3 ? m[2] : null; if (pin === this.pin && types[type]) { + this.microseconds = +data; // See: http://arduino.cc/en/Tutorial/Ping // According to Parallax's datasheet for the PING))), there are // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per @@ -58,13 +61,13 @@ var Ping = function (options) { util.inherits(Ping, events.EventEmitter); -Ping.prototype.command = function () { +Ping.prototype.command = function() { var msg = '97' + this.pin + ([].slice.call(arguments).join('')); this.board.write(msg); }; -Ping.prototype.fire = function () { +Ping.prototype.fire = function() { this.command('01'); }; diff --git a/lib/pir.js b/lib/pir.js index cef77dc..21e7029 100644 --- a/lib/pir.js +++ b/lib/pir.js @@ -6,7 +6,7 @@ var events = require('events'), * Process options * Tell the board to set it up */ -var PIR = function (options) { +var PIR = function(options) { if (!options || !options.board) { throw new Error('Must supply required options to PIR'); } @@ -15,11 +15,11 @@ var PIR = function (options) { this.state = null; this.calibrated = false; - setInterval(function () { + setInterval(function() { this.board.digitalRead(this.pin); }.bind(this), 50); - this.board.on('data', function (message) { + this.board.on('data', function(message) { var m = message.slice(0, -1).split('::'), timestamp = new Date(), err = null, @@ -35,7 +35,7 @@ var PIR = function (options) { if (pin === this.pin) { // If this is not a calibration event - if (this.state != null && this.state != +data) { + if (this.state != null && this.state !== +data) { // Update current state of PIR instance this.state = +data; @@ -67,3 +67,7 @@ var PIR = function (options) { util.inherits(PIR, events.EventEmitter); module.exports = PIR; + + +// More information: +// http://www.ladyada.net/learn/sensors/pir.html diff --git a/lib/sensor.js b/lib/sensor.js index a1883fc..9066e55 100644 --- a/lib/sensor.js +++ b/lib/sensor.js @@ -6,20 +6,23 @@ var events = require('events'), * Process options * Tell the board to set it up */ -var Sensor = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to Sensor'); +var Sensor = function(options) { + if (!options || !options.board) { + throw new Error('Must supply required options to Sensor'); + } this.board = options.board; - this.pin = options.pin || 'A0'; + this.pin = this.board.normalizePin(options.pin || 'A0'); this.board.pinMode(this.pin, 'in'); // Poll for sensor readings - setInterval(function () { + setInterval(function() { + // TODO: make this dependant on the pin type this.board.analogRead(this.pin); }.bind(this), options.throttle || 50); // When data is received, parse inbound message // match pin to instance pin value - this.board.on('data', function (message) { + this.board.on('data', function(message) { var m = message.slice(0, -1).split('::'), err = null, pin, data; @@ -28,7 +31,7 @@ var Sensor = function (options) { return; } - pin = m[0] + pin = m[0]; data = m.length === 2 ? m[1] : null; if (pin === this.pin) { diff --git a/lib/servo.js b/lib/servo.js index 2a9b1d4..983151c 100644 --- a/lib/servo.js +++ b/lib/servo.js @@ -7,7 +7,9 @@ var events = require('events'), * Tell the board to set it up */ var Servo = function (options) { - if (!options || !options.board) throw new Error('Must supply required options to Servo'); + if (!options || !options.board) { + throw new Error('Must supply required options to Servo'); + } this.board = options.board; this.pin = this.board.normalizePin(options.pin || 9); @@ -32,7 +34,7 @@ var Servo = function (options) { return; } - pin = m[0] + pin = m[0]; type = m[1]; data = m.length === 3 ? m[2] : null; @@ -86,7 +88,7 @@ Servo.prototype.sweep = function (options) { // sweep settings lapse = options.lapse || 2000, to = options.to || 180, - from = options.from || 1; + from = options.from || 1, // sweep handlers doSweep = function doSweep(pos) { // this.board.log('info', 'current position: ', pos); @@ -103,7 +105,7 @@ Servo.prototype.sweep = function (options) { if (posint < to) { moveTo = to; - } else if (posint == to) { + } else if (posint === to) { moveTo = 90; } else { moveTo = from; @@ -113,30 +115,31 @@ Servo.prototype.sweep = function (options) { this.write(moveTo); moves++; - }; + }, + loop = function() { + // Read the current position, will trigger + // 'read' event with position data; + if (moves < 2) { + this.read(); - this.on('read', doSweep); + timeout.inner = setTimeout(loop.bind(this), lapse); + } else { + // this.board.log('info', 'info', 'clearing'); - // Initialize sweep; wait for for stack unwind. - timeout.outer = setTimeout(function loop() { - // Read the current position, will trigger - // 'read' event with position data; - if (moves < 2) { - this.read(); + clearTimeout(timeout.inner); + clearTimeout(timeout.outer); - timeout.inner = setTimeout(loop.bind(this), lapse); - } else { - // this.board.log('info', 'info', 'clearing'); + loop = null; - clearTimeout(timeout.inner); - clearTimeout(timeout.outer); + this.removeListener('read', doSweep); + this.emit.call(this, 'aftersweep'); + } + }.bind(this); - loop = null; + this.on('read', doSweep); - this.removeListener('read', doSweep); - this.emit.call(this, 'aftersweep'); - } - }.bind(this), 0); + // Initialize sweep; wait for for stack unwind. + timeout.outer = setTimeout(loop, 0); }; module.exports = Servo; diff --git a/package.json b/package.json index 0f606f2..16d6493 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,36 @@ { - "author": "Cam Pedersen (http://campedersen.com/)", "name": "duino", - "description": "Arduino framework for mad scientists", - "version": "0.0.5", + "description": "A framework for working with Arduinos in node.js", + "version": "0.1.0", + "homepage": "https://github.com/ecto/duino", + "author": { + "name": "Cam Pedersen", + "email": "diffference@gmail.com", + "url": "none" + }, "repository": { "type": "git", "url": "git://github.com/ecto/duino.git" }, - "main": "index.js", - "engines": { - "node": "*" + "bugs": { + "url": "https://github.com/ecto/duino/issues" }, - "dependencies": { - "serialport": "*", - "colors": "*" + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/ecto/duino/blob/master/LICENSE-MIT" + } + ], + "dependencies": {}, + "devDependencies": { + "grunt": "~0.3.0" }, - "devDependencies": {} -} + "keywords": [], + "engines": { + "node": ">= 0.6.13" + }, + "main": "index.js", + "scripts": { + "test": "grunt test" + } +} \ No newline at end of file diff --git a/src/du.ino b/src/du.ino index 2427435..bf46749 100644 --- a/src/du.ino +++ b/src/du.ino @@ -1,9 +1,12 @@ #include +#include bool debug = false; +bool gps = false; int index = 0; +int gindex = 0; char messageBuffer[12]; char cmd[3]; @@ -11,7 +14,12 @@ char pin[3]; char val[4]; char aux[4]; +char buffer[100]; +char response[100]; + Servo servo; +SoftwareSerial GPS = SoftwareSerial(2, 3); + void setup() { Serial.begin(115200); @@ -24,6 +32,28 @@ void loop() { else if (x == '.') process(); // end else messageBuffer[index++] = x; } + + // If gps boolean flag has been set to true + // via API call to gps.init(); + // + // TODO: Abstract the process of creating new + // SoftwareSerial objects to allow for + // arbitrary module connections + if (gps) { + while(GPS.available()) { + char g = GPS.read(); + // At the beginning of a GPS sentence + Serial.println(" "); + // Serial.println("ho"); + if (g == '$') { + gindex = 0; // start + // sprintf(response, "%s::%s", pin, buffer); + // Serial.println(response); + Serial.println(buffer); + } + buffer[gindex++] = g; + } + } } /* @@ -65,6 +95,7 @@ void process() { case 2: dr(pin,val); break; case 3: aw(pin,val); break; case 4: ar(pin,val); break; + case 96: handleGPS(pin,val,aux); break; case 97: handlePing(pin,val,aux); break; case 98: handleServo(pin,val,aux); break; case 99: toggleDebug(val); break; @@ -171,6 +202,25 @@ int getPin(char *pin) { //Converts to A0-A5, and returns -1 on error return ret; } +void handleGPS(char *pin, char *val, char *aux) { + if (debug) Serial.println("ss"); + int p = getPin(pin); + + if(p == -1) { if(debug) Serial.println("badpin"); return; } + Serial.println("Initializing GPS"); + + // 01(1) Connect + if (atoi(val) == 1) { + GPS.begin(4800); + + // Set the global gps flag to true + // this will tell the main event loop that + // this program expects gps readings + gps = true; + + Serial.println("GPS Connected"); + } +} /* * Handle Ping commands * fire, read diff --git a/test/board.js b/test/board.js new file mode 100644 index 0000000..01892db --- /dev/null +++ b/test/board.js @@ -0,0 +1,26 @@ +var arduino = require('../'), + board; + +board = new arduino.Board({ + debug: true +}); + +exports['board'] = { + 'connected & message': function(test) { + test.expect(3); + + board.on('connected', function() { + test.ok(true, 'connected'); + board.write('foo'); + }); + + board.on('ready', function() { + test.ok(true, 'ready'); + }); + + board.on('data', function(data) { + test.ok(true, 'message'); + test.done(); + }); + } +}; diff --git a/test/button.js b/test/button.js new file mode 100644 index 0000000..86f7200 --- /dev/null +++ b/test/button.js @@ -0,0 +1,30 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['button'] = function(test) { + test.expect(2); + + var board = new arduino.Board({ + debug: true + }); + + var button = new arduino.Button({ + board: board, + pin: 9 + }); + + button.on('down', function() { + test.ok(true, 'down'); + test.ok(button.down, 'state'); + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.on('ready', function() { + board.serial.emit('data', '09::01\r'); + }); +}; diff --git a/test/button_test.js b/test/button_test.js new file mode 100644 index 0000000..86f7200 --- /dev/null +++ b/test/button_test.js @@ -0,0 +1,30 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['button'] = function(test) { + test.expect(2); + + var board = new arduino.Board({ + debug: true + }); + + var button = new arduino.Button({ + board: board, + pin: 9 + }); + + button.on('down', function() { + test.ok(true, 'down'); + test.ok(button.down, 'state'); + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.on('ready', function() { + board.serial.emit('data', '09::01\r'); + }); +}; diff --git a/test/gps.js b/test/gps.js new file mode 100644 index 0000000..7fd82c0 --- /dev/null +++ b/test/gps.js @@ -0,0 +1,43 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['gps'] = function(test) { + test.expect(1); + + var board = new arduino.Board({ + debug: true + }); + + var gps = new arduino.GPS({ + board: board, + pin: 9 + }); + + var expecting = { + time: '024322.000', + latitude: '4221.1928', + longitude: '07108.7770', + hemisphere: { latitude: 'N', longitude: 'W' }, + quality: '2', + satellites: 7, + hdop: 1.2, + altitude: '6.2M' + }; + + gps.on('locate', function(err, data) { + test.deepEqual( data, expecting, "data object matches expected "); + + console.log( data, expecting ); + + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.on('ready', function() { + board.serial.emit('data', '$GPGGA,024322.000,4221.1928,N,07108.7770,W,2,07,1.2,6.2,M,-33.7,M,1.8,0000*4B\r'); + }); +}; diff --git a/test/led.js b/test/led.js new file mode 100644 index 0000000..d0dd431 --- /dev/null +++ b/test/led.js @@ -0,0 +1,31 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['led'] = function(test) { + test.expect(4); + + var board = new arduino.Board({ + debug: true + }); + + var led = new arduino.Led({ + board: board, + pin: 9 + }); + + var api = [ 'on', 'off', 'blink', 'fade' ]; + + board.on('ready', function() { + + api.forEach(function(method) { + test.ok(method in led, method); + }); + + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); +}; diff --git a/test/piezo.js b/test/piezo.js new file mode 100644 index 0000000..e072716 --- /dev/null +++ b/test/piezo.js @@ -0,0 +1,31 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['piezo'] = function(test) { + test.expect(2); + + var board = new arduino.Board({ + debug: true + }); + + var piezo = new arduino.Piezo({ + board: board, + pin: 9 + }); + + var api = [ 'tone', 'note' ]; + + board.on('ready', function() { + + api.forEach(function(method) { + test.ok(method in piezo, method); + }); + + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); +}; diff --git a/test/ping.js b/test/ping.js new file mode 100644 index 0000000..acae076 --- /dev/null +++ b/test/ping.js @@ -0,0 +1,33 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['ping'] = function(test) { + test.expect(3); + + var board = new arduino.Board({ + debug: true + }); + + var ping = new arduino.Ping({ + board: board, + pin: 9 + }); + + ping.on('read', function(err, data) { + + test.equal(this.microseconds, 300, "microseconds"); + test.equal(this.inches, 2.027027027027027, "inches"); + test.equal(this.centimeters, 5.172413793103448, "centimeters"); + + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.on('ready', function() { + board.serial.emit('data', '09::read::300\r'); + }); +}; diff --git a/test/pir.js b/test/pir.js new file mode 100644 index 0000000..869ba9b --- /dev/null +++ b/test/pir.js @@ -0,0 +1,38 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['pir'] = function(test) { + test.expect(3); + + var board = new arduino.Board({ + debug: true + }); + + var pir = new arduino.PIR({ + board: board, + pin: 9 + }); + + pir.on('calibrated', function(err, data) { + test.ok(true, 'calibrated'); + + pir.on('motionstart', function(err, data) { + test.ok(true, data); + + board.serial.emit('data', '09::00\r'); + }); + + pir.on('motionend', function(err, data) { + + test.ok(true, data); + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.serial.emit('data', '09::01\r'); + }); +}; diff --git a/test/sensor.js b/test/sensor.js new file mode 100644 index 0000000..aef4865 --- /dev/null +++ b/test/sensor.js @@ -0,0 +1,30 @@ +var arduino = require('../'); + + +// console.log( board ); +exports['sensor'] = function(test) { + test.expect(1); + + var board = new arduino.Board({ + debug: true + }); + + var sensor = new arduino.Sensor({ + board: board, + pin: 'A0' + }); + + sensor.on('read', function(err, data) { + + test.ok(!!data, "mock data"); + test.done(); + + process.nextTick(function() { + process.exit(); + }); + }); + + board.on('ready', function() { + board.serial.emit('data', 'A0::300\r'); + }); +};