From a6e5b7184aea2345d619ed361ae4b2510ebffc56 Mon Sep 17 00:00:00 2001 From: Daruvala Date: Sat, 18 Aug 2012 11:12:49 -0700 Subject: [PATCH 01/15] Added a disconnect method so the user doesn't need to mess directly with the serial port during program exit. --- lib/sphero.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/sphero.js b/lib/sphero.js index 67c51a8..343762d 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -290,6 +290,10 @@ Sphero.prototype.getAutoReconnect = function(cb) { return this; }; +Sphero.prototype.disconect = function() { + this.port.close(); +}; + // Bunch of missing features go here /** From 02b4da19e64f5e3b0ec519bea836d79934bfe421 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sat, 18 Aug 2012 15:07:11 -0700 Subject: [PATCH 02/15] Added some Collision Detection. --- lib/sphero.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/sphero.js b/lib/sphero.js index 343762d..a839c9a 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -121,7 +121,10 @@ var Sphero = function(path) { if (self.calls[packet[RESPONSE_SEQ]].success) { self.calls[packet[RESPONSE_SEQ]].success(packet); } - } else { + } else if (packet[RESPONSE_MRSP] == 0x07) { + // Collision Detection Async Response + self.onHit(packet); + } else { // Error :( if (self.calls[packet[RESPONSE_SEQ]].error) { self.calls[packet[RESPONSE_SEQ]].error(packet); @@ -401,4 +404,17 @@ Sphero.prototype.stop = function(cb) { return this; }; +Sphero.prototype.setCollisionDetection = function (data, _onHit) { + this.onHit = _onHit || function() {return;}; + data = data || [0x01, 120, 120, 120, 120, 120]; + var options = { + device: this.devices.sphero, + command: 0x12, + success: function() { console.log("Collision Detection Activated"); }, + error: function(err) { console.log("Collision Detection Activation Error", err); }, + data: data + }; + this.send(options); + return this; +} exports.Sphero = Sphero; From c5908794bad7c28c0adbab3c6530fe3b087b81e5 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sat, 18 Aug 2012 15:25:08 -0700 Subject: [PATCH 03/15] Spelling error fix --- lib/sphero.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sphero.js b/lib/sphero.js index a839c9a..aaf06e4 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -293,7 +293,7 @@ Sphero.prototype.getAutoReconnect = function(cb) { return this; }; -Sphero.prototype.disconect = function() { +Sphero.prototype.disconnect = function() { this.port.close(); }; From 86c06580150fb33a0435e0abf4c64d2927d2de45 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sat, 18 Aug 2012 17:16:01 -0700 Subject: [PATCH 04/15] Giving the users more control over setCollision Detection --- lib/sphero.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index aaf06e4..5ec6b3b 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -404,17 +404,17 @@ Sphero.prototype.stop = function(cb) { return this; }; -Sphero.prototype.setCollisionDetection = function (data, _onHit) { - this.onHit = _onHit || function() {return;}; - data = data || [0x01, 120, 120, 120, 120, 120]; - var options = { - device: this.devices.sphero, - command: 0x12, - success: function() { console.log("Collision Detection Activated"); }, - error: function(err) { console.log("Collision Detection Activation Error", err); }, - data: data - }; +Sphero.prototype.setCollisionDetection = function (options, onHit) { + options.device = this.devices.sphero; + options.command = 0x12; + options.success = options.success || function() { return; }, + options.error = options.error || function() { return; }, + options.data = options.data || [0x01, 120, 120, 120, 120, 120]; + + this.onHit = onHit || function() { return; }; + this.send(options); return this; -} +}; + exports.Sphero = Sphero; From 6d94a8ded31e995023ff86aba9c27a71ebe9032b Mon Sep 17 00:00:00 2001 From: robbles Date: Sat, 18 Aug 2012 22:18:31 -0700 Subject: [PATCH 05/15] fix sequence number bug in sphero.js --- lib/sphero.js | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index 67c51a8..76c267f 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -115,20 +115,24 @@ var Sphero = function(path) { var packet = self.buffer.slice(0, end); self.buffer = self.buffer.slice(end); - // Do the callback - if (packet[RESPONSE_MRSP] == RSP_CODE_OK) { - // Success! - if (self.calls[packet[RESPONSE_SEQ]].success) { - self.calls[packet[RESPONSE_SEQ]].success(packet); + if(typeof self.calls[packet[RESPONSE_SEQ]] !== 'undefined') { + // Do the callback + if (packet[RESPONSE_MRSP] == RSP_CODE_OK) { + // Success! + if (self.calls[packet[RESPONSE_SEQ]].success) { + self.calls[packet[RESPONSE_SEQ]].success(packet); + } + } else { + // Error :( + if (self.calls[packet[RESPONSE_SEQ]].error) { + self.calls[packet[RESPONSE_SEQ]].error(packet); + } } + // Remove the saved options, no longer needed + delete self.calls[packet[RESPONSE_SEQ]]; } else { - // Error :( - if (self.calls[packet[RESPONSE_SEQ]].error) { - self.calls[packet[RESPONSE_SEQ]].error(packet); - } + console.warn('Call does not exist: ' + packet[RESPONSE_SEQ]); } - // Remove the saved options, no longer needed - delete self.calls[packet[RESPONSE_SEQ]]; // If the remaining buffer is long enough to be a result process it if (self.buffer.length >= 6) { @@ -136,7 +140,7 @@ var Sphero = function(path) { } } } - } + }; this.port.on("data", processData); }; @@ -182,7 +186,8 @@ Sphero.prototype.send = function(options) { this.calls[this.seq] = options; this.port.write(packet); - this.seq++; + + this.seq = (this.seq + 1) % 256; }; // Returns new Buffer of result data (if any) out of a response packet buffer @@ -397,4 +402,16 @@ Sphero.prototype.stop = function(cb) { return this; }; +Sphero.prototype.disconnect = function() { + try { + // Try to make sure Sphero stops moving before disconnecting + this.stop(); + } catch(err) { + + } + // Close the serial port + this.port.close(); + return this; +}; + exports.Sphero = Sphero; From 637e70ca1ca49084b13e2c495e9852abcebae06d Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sat, 18 Aug 2012 23:00:33 -0700 Subject: [PATCH 06/15] Updated the Collision Detection to be more intuitve. Also readded the delete that should have been there --- lib/sphero.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index c376a72..3e97597 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -128,15 +128,28 @@ var Sphero = function(path) { self.calls[packet[RESPONSE_SEQ]].error(packet); } } - } else if (packet[RESPONSE_MRSP] == 0x07) { + } else if (self.onHit && packet[RESPONSE_MRSP] == 0x07) { // Collision Detection Async Response - self.onHit(packet); + packet = packet.slice(5, packet.length - 1); + var object = { + x: values[0]*256 + values[1], + y: values[2]*256 + values[3], + z: values[4]*256 + values[5], + axis: ((values[6]-1) && 'Y') || 'X', + xMagnitude: values[7]*256 + values[8], + yMagnitude: values[9]*256 + values[10], + speed: values[11], + timeStamp: values[12]*256*256*256 + values[13]*256*256 + values[14]*256 + values[15] + }; + self.onHit(object) } else { // Error :( if (self.calls[packet[RESPONSE_SEQ]].error) { self.calls[packet[RESPONSE_SEQ]].error(packet); } } + // Remove the saved options, no longer needed + delete self.calls[packet[RESPONSE_SEQ]]; // If the remaining buffer is long enough to be a result process it if (self.buffer.length >= 6) { From e407c65d6c4ab4e9541efce7fba59a3793027cf5 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sun, 19 Aug 2012 11:12:28 -0700 Subject: [PATCH 07/15] Added methods to change color --- lib/sphero.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index 3e97597..c0cb333 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -429,9 +429,31 @@ Sphero.prototype.setCollisionDetection = function (options, onHit) { options.success = options.success || function() { return; }, options.error = options.error || function() { return; }, options.data = options.data || [0x01, 120, 120, 120, 120, 120]; - this.onHit = onHit || function() { return; }; - + this.send(options); + return this; +}; + +Sphero.prototype.setColor = function(red, green, blue, cb) { + var options = { + device: this.devices.sphero, + command: 0x20, + success: cb, + data: [red, green, blue] + }; + this.send(options); + return this; +}; + +Sphero.prototype.setBlackLED = function(val, cb) { + var options = { + device: this.devices.sphero, + command: 0x21, + data: [255], + success: function(packet) { + cb(packet); + } + }; this.send(options); return this; }; From 1812b16ad1d32e991911b5dc791b834cb2fb1a1c Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Sun, 19 Aug 2012 13:48:03 -0700 Subject: [PATCH 08/15] Correctly changed incorrect variable name 'values' -> 'packet'. --- lib/sphero.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index c0cb333..032f1bb 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -132,14 +132,14 @@ var Sphero = function(path) { // Collision Detection Async Response packet = packet.slice(5, packet.length - 1); var object = { - x: values[0]*256 + values[1], - y: values[2]*256 + values[3], - z: values[4]*256 + values[5], - axis: ((values[6]-1) && 'Y') || 'X', - xMagnitude: values[7]*256 + values[8], - yMagnitude: values[9]*256 + values[10], - speed: values[11], - timeStamp: values[12]*256*256*256 + values[13]*256*256 + values[14]*256 + values[15] + x: packet[0]*256 + packet[1], + y: packet[2]*256 + packet[3], + z: packet[4]*256 + packet[5], + axis: ((packet[6]-1) && 'Y') || 'X', + xMagnitude: packet[7]*256 + packet[8], + yMagnitude: packet[9]*256 + packet[10], + speed: packet[11], + timeStamp: packet[12]*256*256*256 + packet[13]*256*256 + packet[14]*256 + packet[15] }; self.onHit(object) } else { From 402f0a3a16a3ad1f36a3444943b3f163883c4ff1 Mon Sep 17 00:00:00 2001 From: robbles Date: Sun, 19 Aug 2012 15:28:12 -0700 Subject: [PATCH 09/15] fix to message parsing --- lib/sphero.js | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index 032f1bb..976ca61 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -122,32 +122,32 @@ var Sphero = function(path) { if (self.calls[packet[RESPONSE_SEQ]].success) { self.calls[packet[RESPONSE_SEQ]].success(packet); } + } + else if (self.onHit && packet[RESPONSE_MRSP] == 0x07) { + // Collision Detection Async Response + packet = packet.slice(5, packet.length - 1); + var object = { + x: packet[0]*256 + packet[1], + y: packet[2]*256 + packet[3], + z: packet[4]*256 + packet[5], + axis: ((packet[6]-1) && 'Y') || 'X', + xMagnitude: packet[7]*256 + packet[8], + yMagnitude: packet[9]*256 + packet[10], + speed: packet[11], + timeStamp: packet[12]*256*256*256 + packet[13]*256*256 + packet[14]*256 + packet[15] + }; + + self.onHit(object); } else { + console.log('Error packet: '); + console.log(packet); // Error :( if (self.calls[packet[RESPONSE_SEQ]].error) { self.calls[packet[RESPONSE_SEQ]].error(packet); } } - } else if (self.onHit && packet[RESPONSE_MRSP] == 0x07) { - // Collision Detection Async Response - packet = packet.slice(5, packet.length - 1); - var object = { - x: packet[0]*256 + packet[1], - y: packet[2]*256 + packet[3], - z: packet[4]*256 + packet[5], - axis: ((packet[6]-1) && 'Y') || 'X', - xMagnitude: packet[7]*256 + packet[8], - yMagnitude: packet[9]*256 + packet[10], - speed: packet[11], - timeStamp: packet[12]*256*256*256 + packet[13]*256*256 + packet[14]*256 + packet[15] - }; - self.onHit(object) - } else { - // Error :( - if (self.calls[packet[RESPONSE_SEQ]].error) { - self.calls[packet[RESPONSE_SEQ]].error(packet); - } } + // Remove the saved options, no longer needed delete self.calls[packet[RESPONSE_SEQ]]; @@ -180,6 +180,9 @@ Sphero.prototype.send = function(options) { } // Determine length of data. Length is always data length + 1 (for checksum) + if(typeof options.data === 'number') { + options.data = [options.data]; + } var dataLength = options.data.length > 254 ? 0xFF : options.data.length + 0x01; // Construct the packet (minus checksum) @@ -339,16 +342,16 @@ Sphero.prototype.setHeading = function(heading, cb) { return this; }; -Sphero.prototype.setStabilization = function(enable, cb) { +Sphero.prototype.setStabilization = function(data, cb) { var options = { device: this.devices.sphero, command: 0x02, - data: enable ? 0x01 : 0x00 + data: data }; if (cb) { options.success = function() { cb(); - } + }; } this.send(options); return this; @@ -449,7 +452,7 @@ Sphero.prototype.setBlackLED = function(val, cb) { var options = { device: this.devices.sphero, command: 0x21, - data: [255], + data: [val], success: function(packet) { cb(packet); } From f87da5958e5b13ba11f5f978e4c00b01b376b8d8 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Tue, 21 Aug 2012 22:38:13 -0700 Subject: [PATCH 10/15] Moved core functions to a new file --- lib/core.js | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/sphero.js | 97 ++------------------------------------------------- 2 files changed, 97 insertions(+), 95 deletions(-) create mode 100644 lib/core.js diff --git a/lib/core.js b/lib/core.js new file mode 100644 index 0000000..b982906 --- /dev/null +++ b/lib/core.js @@ -0,0 +1,95 @@ +// NodeJS Sphero SDK + +export core = function ( Sphero ) { + + Sphero.prototype.ping = function(cb) { + var options = { + device: this.devices.core, + command: 0x01 + }; + if (cb) { + options.success = function() { + cb(); + } + } + this.send(options); + return this; + }; + + Sphero.prototype.getVersioning = function(cb) { + var options = { + device: this.devices.core, + command: 0x02 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + } + } + this.send(options); + return this; + }; + + // setDeviceName here + + Sphero.prototype.getBluetoothInfo = function(cb) { + var options = { + device: this.devices.core, + command: 0x11 + }; + if (cb) { + var self = this; + options.success = function(packet) { + var data = self.parseData(packet); + var name = data.slice(0, 15).toString(); + var id = data.slice(16).toString(); + cb(name, id); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.setAutoReconnect = function(enable, time, cb) { + // Allow user to pass callback without time for disabling + if (typeof time === "function") { + cb = time; + time = 0; + } + if (enable && typeof time !== "number") { + throw new Error("Reconnect time is required"); + } + var options = { + device: this.devices.core, + command: 0x12, + // Default time to 30 to be safe + data: new Buffer([enable ? 0x01 : 0x00, time]) + }; + if (cb) { + var self = this; + options.success = function() { + cb(); + } + } + this.send(options); + return this; + }; + + Sphero.prototype.getAutoReconnect = function(cb) { + var options = { + device: this.devices.core, + command: 0x13 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; + + return Sphero; +}; diff --git a/lib/sphero.js b/lib/sphero.js index 976ca61..2d9f309 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -222,105 +222,10 @@ Sphero.prototype.parseData = function(packet) { return result; }; -/** - * These are the helper functions that make sending raw calls easy. - */ - -Sphero.prototype.ping = function(cb) { - var options = { - device: this.devices.core, - command: 0x01 - }; - if (cb) { - options.success = function() { - cb(); - } - } - this.send(options); - return this; -}; - -Sphero.prototype.getVersioning = function(cb) { - var options = { - device: this.devices.core, - command: 0x02 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - } - } - this.send(options); - return this; -}; - -// setDeviceName here - -Sphero.prototype.getBluetoothInfo = function(cb) { - var options = { - device: this.devices.core, - command: 0x11 - }; - if (cb) { - var self = this; - options.success = function(packet) { - var data = self.parseData(packet); - var name = data.slice(0, 15).toString(); - var id = data.slice(16).toString(); - cb(name, id); - }; - } - this.send(options); - return this; -}; - -Sphero.prototype.setAutoReconnect = function(enable, time, cb) { - // Allow user to pass callback without time for disabling - if (typeof time === "function") { - cb = time; - time = 0; - } - if (enable && typeof time !== "number") { - throw new Error("Reconnect time is required"); - } - var options = { - device: this.devices.core, - command: 0x12, - // Default time to 30 to be safe - data: new Buffer([enable ? 0x01 : 0x00, time]) - }; - if (cb) { - var self = this; - options.success = function() { - cb(); - } - } - this.send(options); - return this; -}; - -Sphero.prototype.getAutoReconnect = function(cb) { - var options = { - device: this.devices.core, - command: 0x13 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - }; - } - this.send(options); - return this; -}; - Sphero.prototype.disconnect = function() { this.port.close(); }; -// Bunch of missing features go here - /** * Sphero commands */ @@ -461,4 +366,6 @@ Sphero.prototype.setBlackLED = function(val, cb) { return this; }; +Sphero = require('./core').core( Sphero ); + exports.Sphero = Sphero; From 4971fecc5ca060db86f34f3a1bcdd4fc9804e2f2 Mon Sep 17 00:00:00 2001 From: Yazad Daruvala Date: Tue, 21 Aug 2012 23:57:47 -0700 Subject: [PATCH 11/15] Updated core to 1.31 --- lib/core.js | 177 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 38 deletions(-) diff --git a/lib/core.js b/lib/core.js index b982906..0488fee 100644 --- a/lib/core.js +++ b/lib/core.js @@ -4,13 +4,13 @@ export core = function ( Sphero ) { Sphero.prototype.ping = function(cb) { var options = { - device: this.devices.core, - command: 0x01 + device: this.devices.core, + command: 0x01 }; if (cb) { - options.success = function() { - cb(); - } + options.success = function() { + cb(); + } } this.send(options); return this; @@ -18,34 +18,51 @@ export core = function ( Sphero ) { Sphero.prototype.getVersioning = function(cb) { var options = { - device: this.devices.core, - command: 0x02 + device: this.devices.core, + command: 0x02 }; if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - } + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + } } this.send(options); return this; }; - // setDeviceName here - - Sphero.prototype.getBluetoothInfo = function(cb) { + Sphero.prototype.setDeviceName = function(name, cb) { + if (typeof(name) === "string") { + name = name.split(""); + } var options = { - device: this.devices.core, - command: 0x11 + device: this.devices.core, + command: 0x10, + data: name }; if (cb) { - var self = this; - options.success = function(packet) { - var data = self.parseData(packet); - var name = data.slice(0, 15).toString(); - var id = data.slice(16).toString(); - cb(name, id); + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + } + } + this.send(options); + return this; + }; + + Sphero.prototype.getBluetoothInfo = function(cb) { + var options = { + device: this.devices.core, + command: 0x11 }; + if (cb) { + var self = this; + options.success = function(packet) { + var data = self.parseData(packet); + var name = data.slice(0, 15).toString(); + var id = data.slice(16).toString(); + cb(name, id); + }; } this.send(options); return this; @@ -54,23 +71,23 @@ export core = function ( Sphero ) { Sphero.prototype.setAutoReconnect = function(enable, time, cb) { // Allow user to pass callback without time for disabling if (typeof time === "function") { - cb = time; - time = 0; + cb = time; + time = 0; } if (enable && typeof time !== "number") { - throw new Error("Reconnect time is required"); + throw new Error("Reconnect time is required"); } var options = { - device: this.devices.core, - command: 0x12, - // Default time to 30 to be safe - data: new Buffer([enable ? 0x01 : 0x00, time]) + device: this.devices.core, + command: 0x12, + // Default time to 30 to be safe + data: new Buffer([enable ? 0x01 : 0x00, time]) }; if (cb) { - var self = this; - options.success = function() { - cb(); - } + var self = this; + options.success = function() { + cb(); + } } this.send(options); return this; @@ -78,18 +95,102 @@ export core = function ( Sphero ) { Sphero.prototype.getAutoReconnect = function(cb) { var options = { - device: this.devices.core, - command: 0x13 + device: this.devices.core, + command: 0x13 }; if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.getPowerState = function(cb) { + var options = { + device: this.device.core, + command: 0x20 }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + } } this.send(options); return this; }; + Sphero.prototype.setPowerNotification = function(flag, cb) { + var options = { + device: this.device.core, + command: 0x21, + success: cb, + data: [flag] + }; + this.send(options); + return this; + }; + + Sphero.prototype.sleep = function(time, macro, orbBasic, cb) { + var options = { + device: this.device.core, + command: 0x22, + success: cb, + data: [time/256, time%256, macro, orbBasic] + }; + this.send(options); + return this; + }; + + Sphero.prototype.getVoltageTripPoints = function(cb) { + var options = { + device: this.device.core, + command: 0x23 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(packet[5]*256 + packet[6], packet[7]*256 + packet[8]); + } + } + this.send(options); + return this; + }; + + Sphero.prototype.setVoltageTripPoints = function(vLow, vCrit, cb) { + var options = { + device: this.device.core, + command: 0x24, + success: cb, + data: [vLow/256, vLow%256, vCrit/256, vCrit%256] + }; + this.send(options); + return this; + }; + + Sphero.prototype.setInactivityTimeout = function(time, cb) { + var options = { + device: this.device.core, + command: 0x25, + success: cb, + data: [time/256, time%256] + }; + this.send(options); + return this; + }; + + Sphero.prototype.jumpToBootloader = function(cb) { + var options = { + device: this.device.core, + command: 0x30, + success: cb + }; + this.send(options); + return this; + }; + return Sphero; }; From 1d28afd5f6252054013793b5db6c7acd4841b985 Mon Sep 17 00:00:00 2001 From: robbles Date: Sat, 25 Aug 2012 19:59:35 -0700 Subject: [PATCH 12/15] minor cleanup after testing --- lib/core.js | 370 +++++++++++++++++++++++++------------------------- lib/sphero.js | 98 ++++++------- 2 files changed, 235 insertions(+), 233 deletions(-) diff --git a/lib/core.js b/lib/core.js index 0488fee..086a270 100644 --- a/lib/core.js +++ b/lib/core.js @@ -1,196 +1,196 @@ // NodeJS Sphero SDK -export core = function ( Sphero ) { +module.exports = function (Sphero) { - Sphero.prototype.ping = function(cb) { - var options = { - device: this.devices.core, - command: 0x01 - }; - if (cb) { - options.success = function() { - cb(); - } - } - this.send(options); - return this; - }; - - Sphero.prototype.getVersioning = function(cb) { - var options = { - device: this.devices.core, - command: 0x02 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - } - } - this.send(options); - return this; - }; - - Sphero.prototype.setDeviceName = function(name, cb) { - if (typeof(name) === "string") { - name = name.split(""); - } - var options = { - device: this.devices.core, - command: 0x10, - data: name - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - } - } - this.send(options); - return this; - }; + Sphero.prototype.ping = function(cb) { + var options = { + device: this.devices.core, + command: 0x01 + }; + if (cb) { + options.success = function() { + cb(); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.getVersioning = function(cb) { + var options = { + device: this.devices.core, + command: 0x02 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.setDeviceName = function(name, cb) { + if (typeof(name) === "string") { + name = name.split(""); + } + var options = { + device: this.devices.core, + command: 0x10, + data: name + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; - Sphero.prototype.getBluetoothInfo = function(cb) { - var options = { - device: this.devices.core, - command: 0x11 - }; - if (cb) { - var self = this; - options.success = function(packet) { - var data = self.parseData(packet); - var name = data.slice(0, 15).toString(); - var id = data.slice(16).toString(); - cb(name, id); - }; - } - this.send(options); - return this; - }; - - Sphero.prototype.setAutoReconnect = function(enable, time, cb) { - // Allow user to pass callback without time for disabling - if (typeof time === "function") { - cb = time; - time = 0; - } - if (enable && typeof time !== "number") { - throw new Error("Reconnect time is required"); - } - var options = { - device: this.devices.core, - command: 0x12, - // Default time to 30 to be safe - data: new Buffer([enable ? 0x01 : 0x00, time]) - }; - if (cb) { - var self = this; - options.success = function() { - cb(); - } - } - this.send(options); - return this; - }; - - Sphero.prototype.getAutoReconnect = function(cb) { - var options = { - device: this.devices.core, - command: 0x13 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - }; - } - this.send(options); - return this; - }; + Sphero.prototype.getBluetoothInfo = function(cb) { + var options = { + device: this.devices.core, + command: 0x11 + }; + if (cb) { + var self = this; + options.success = function(packet) { + var data = self.parseData(packet); + var name = data.slice(0, 15).toString(); + var id = data.slice(16).toString(); + cb(name, id); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.setAutoReconnect = function(enable, time, cb) { + // Allow user to pass callback without time for disabling + if (typeof time === "function") { + cb = time; + time = 0; + } + if (enable && typeof time !== "number") { + throw new Error("Reconnect time is required"); + } + var options = { + device: this.devices.core, + command: 0x12, + // Default time to 30 to be safe + data: new Buffer([enable ? 0x01 : 0x00, time]) + }; + if (cb) { + var self = this; + options.success = function() { + cb(); + }; + } + this.send(options); + return this; + }; + + Sphero.prototype.getAutoReconnect = function(cb) { + var options = { + device: this.devices.core, + command: 0x13 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; - Sphero.prototype.getPowerState = function(cb) { - var options = { - device: this.device.core, - command: 0x20 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(self.parseData(packet)); - } - } - this.send(options); - return this; - }; + Sphero.prototype.getPowerState = function(cb) { + var options = { + device: this.device.core, + command: 0x20 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(self.parseData(packet)); + }; + } + this.send(options); + return this; + }; - Sphero.prototype.setPowerNotification = function(flag, cb) { - var options = { - device: this.device.core, - command: 0x21, - success: cb, - data: [flag] - }; - this.send(options); - return this; - }; - - Sphero.prototype.sleep = function(time, macro, orbBasic, cb) { - var options = { - device: this.device.core, - command: 0x22, - success: cb, - data: [time/256, time%256, macro, orbBasic] - }; - this.send(options); - return this; - }; - - Sphero.prototype.getVoltageTripPoints = function(cb) { - var options = { - device: this.device.core, - command: 0x23 - }; - if (cb) { - var self = this; - options.success = function(packet) { - cb(packet[5]*256 + packet[6], packet[7]*256 + packet[8]); - } - } - this.send(options); - return this; - }; + Sphero.prototype.setPowerNotification = function(flag, cb) { + var options = { + device: this.device.core, + command: 0x21, + success: cb, + data: [flag] + }; + this.send(options); + return this; + }; + + Sphero.prototype.sleep = function(time, macro, orbBasic, cb) { + var options = { + device: this.device.core, + command: 0x22, + success: cb, + data: [time/256, time%256, macro, orbBasic] + }; + this.send(options); + return this; + }; + + Sphero.prototype.getVoltageTripPoints = function(cb) { + var options = { + device: this.device.core, + command: 0x23 + }; + if (cb) { + var self = this; + options.success = function(packet) { + cb(packet[5]*256 + packet[6], packet[7]*256 + packet[8]); + }; + } + this.send(options); + return this; + }; - Sphero.prototype.setVoltageTripPoints = function(vLow, vCrit, cb) { - var options = { - device: this.device.core, - command: 0x24, - success: cb, - data: [vLow/256, vLow%256, vCrit/256, vCrit%256] - }; - this.send(options); - return this; - }; + Sphero.prototype.setVoltageTripPoints = function(vLow, vCrit, cb) { + var options = { + device: this.device.core, + command: 0x24, + success: cb, + data: [vLow/256, vLow%256, vCrit/256, vCrit%256] + }; + this.send(options); + return this; + }; - Sphero.prototype.setInactivityTimeout = function(time, cb) { - var options = { - device: this.device.core, - command: 0x25, - success: cb, - data: [time/256, time%256] - }; - this.send(options); - return this; - }; + Sphero.prototype.setInactivityTimeout = function(time, cb) { + var options = { + device: this.device.core, + command: 0x25, + success: cb, + data: [time/256, time%256] + }; + this.send(options); + return this; + }; - Sphero.prototype.jumpToBootloader = function(cb) { - var options = { - device: this.device.core, - command: 0x30, - success: cb - }; - this.send(options); - return this; - }; + Sphero.prototype.jumpToBootloader = function(cb) { + var options = { + device: this.device.core, + command: 0x30, + success: cb + }; + this.send(options); + return this; + }; - return Sphero; + return Sphero; }; diff --git a/lib/sphero.js b/lib/sphero.js index 2d9f309..c38ac68 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -5,49 +5,49 @@ var SerialPort = require("serialport").SerialPort; // Constant values // Buffer positions for packet fields -var COMMAND_SOP1 = 0 - , COMMAND_SOP2 = 1 - , COMMAND_DID = 2 - , COMMAND_CID = 3 - , COMMAND_SEQ = 4 - , COMMAND_DLEN = 5 - // First data position - , COMMAND_DATA = 6 - // CHK will be variable position if data is set - , COMMAND_CHK = 6 - - // Response packet fields - , RESPONSE_SOP1 = 0 - , RESPONSE_SOP2 = 1 - , RESPONSE_MRSP = 2 - , RESPONSE_SEQ = 3 - , RESPONSE_DLEN = 4 - // First data position - , RESPONSE_DATA = 5 - // CHK will be variable position if data is set - , RESPONSE_CHK = 5 - - // Async - , RESPONSE_ID = 2 - , RESPONSE_DLEN_MSB = 3 - , RESPONSE_DLEN_LSB = 4 - - // Message response codes - , RSP_CODE_OK = 0x00 - , RSP_CODE_EGEN = 0x01 - , RSP_CODE_ECHKSUM = 0x02 - , RSP_CODE_EFRAG = 0x03 - , RSP_CODE_EBAD_CMD = 0x04 - , RSP_CODE_EUNSUPP = 0x05 - , RSP_CODE_EBAD_MSG = 0x06 - , RSP_CODE_EPARAM = 0x07 - , RSP_CODE_EEXEC = 0x08 - , RSP_CODE_EBAD_DID = 0x09 - , RSP_CODE_POWER_NOGOOD = 0x31 - , RSP_CODE_PAGE_ILLEGAL = 0x32 - , RSP_CODE_FLASH_FAIL = 0x33 - , RSP_CODE_MA_CORRUPT = 0x34 - , RSP_CODE_MSG_TIMEOUT = 0x35; +var COMMAND_SOP1 = 0, + COMMAND_SOP2 = 1, + COMMAND_DID = 2, + COMMAND_CID = 3, + COMMAND_SEQ = 4, + COMMAND_DLEN = 5, + // First data position + COMMAND_DATA = 6, + // CHK will be variable position if data is set + COMMAND_CHK = 6, + + // Response packet fields + RESPONSE_SOP1 = 0, + RESPONSE_SOP2 = 1, + RESPONSE_MRSP = 2, + RESPONSE_SEQ = 3, + RESPONSE_DLEN = 4, + // First data position + RESPONSE_DATA = 5, + // CHK will be variable position if data is set + RESPONSE_CHK = 5, + + // Async + RESPONSE_ID = 2, + RESPONSE_DLEN_MSB = 3, + RESPONSE_DLEN_LSB = 4, + + // Message response codes + RSP_CODE_OK = 0x00, + RSP_CODE_EGEN = 0x01, + RSP_CODE_ECHKSUM = 0x02, + RSP_CODE_EFRAG = 0x03, + RSP_CODE_EBAD_CMD = 0x04, + RSP_CODE_EUNSUPP = 0x05, + RSP_CODE_EBAD_MSG = 0x06, + RSP_CODE_EPARAM = 0x07, + RSP_CODE_EEXEC = 0x08, + RSP_CODE_EBAD_DID = 0x09, + RSP_CODE_POWER_NOGOOD = 0x31, + RSP_CODE_PAGE_ILLEGAL = 0x32, + RSP_CODE_FLASH_FAIL = 0x33, + RSP_CODE_MA_CORRUPT = 0x34, + RSP_CODE_MSG_TIMEOUT = 0x35; var Sphero = function(path) { var self = this; @@ -241,7 +241,7 @@ Sphero.prototype.setHeading = function(heading, cb) { if (cb) { options.success = function() { cb(); - } + }; } this.send(options); return this; @@ -271,7 +271,7 @@ Sphero.prototype.setRotationRate = function(rate, cb) { if (cb) { options.success = function() { cb(); - } + }; } this.send(options); return this; @@ -310,7 +310,7 @@ Sphero.prototype.roll = function(speed, heading, timeout, cb) { } else if (cb) { options.success = function(packet) { cb(packet); - } + }; } this.send(options); return this; @@ -325,7 +325,7 @@ Sphero.prototype.stop = function(cb) { if (cb) { options.success = function() { cb(); - } + }; } this.send(options); return this; @@ -366,6 +366,8 @@ Sphero.prototype.setBlackLED = function(val, cb) { return this; }; -Sphero = require('./core').core( Sphero ); +require('./core')(Sphero); + +console.log(Sphero.prototype); exports.Sphero = Sphero; From 75d6caaeb219bda42b02ca0b9251292d970f69cd Mon Sep 17 00:00:00 2001 From: robbles Date: Sat, 25 Aug 2012 21:38:29 -0700 Subject: [PATCH 13/15] Add glob library to find default Sphero port --- lib/sphero.js | 19 +++++++++++++------ package.json | 3 ++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/sphero.js b/lib/sphero.js index c38ac68..2e9b62e 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -1,6 +1,7 @@ // NodeJS Sphero SDK -var SerialPort = require("serialport").SerialPort; +var SerialPort = require("serialport").SerialPort, + glob = require('glob'); // Constant values @@ -47,13 +48,21 @@ var COMMAND_SOP1 = 0, RSP_CODE_PAGE_ILLEGAL = 0x32, RSP_CODE_FLASH_FAIL = 0x33, RSP_CODE_MA_CORRUPT = 0x34, - RSP_CODE_MSG_TIMEOUT = 0x35; + RSP_CODE_MSG_TIMEOUT = 0x35, + + // Glob expression for Sphero serial ports + DEFAULT_SPHERO_PORT = '/dev/tty.Sphero*'; var Sphero = function(path) { var self = this; - // Path to port Sphero is on - path = path || "/dev/tty.Sphero"; + // Path to port Sphero is on - use glob to find default port + if(typeof path === 'undefined') { + path = glob.sync(DEFAULT_SPHERO_PORT)[0]; + if(typeof path === 'undefined') { + throw new Error('No Sphero found matching ' + DEFAULT_SPHERO_PORT); + } + } // Command sequence number this.seq = 0x00; @@ -368,6 +377,4 @@ Sphero.prototype.setBlackLED = function(val, cb) { require('./core')(Sphero); -console.log(Sphero.prototype); - exports.Sphero = Sphero; diff --git a/package.json b/package.json index e9a3588..a7662e5 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "node": "~0.6.2" }, "dependencies": { - "serialport": "0.6.3" + "serialport": "0.6.3", + "glob": "3.1.x" }, "devDependencies": {} } From 8cecb10bdc2b8756902cdd03d9a613828c3d7091 Mon Sep 17 00:00:00 2001 From: robbles Date: Sat, 25 Aug 2012 22:54:51 -0700 Subject: [PATCH 14/15] new tools used in hackathon added to examples folder --- examples/connect_spheros.js | 115 ++++++++++++++++++++++++++++++++++++ examples/orient_spheros.js | 71 ++++++++++++++++++++++ examples/setup_sphero.js | 36 +++++++++++ 3 files changed, 222 insertions(+) create mode 100755 examples/connect_spheros.js create mode 100755 examples/orient_spheros.js create mode 100755 examples/setup_sphero.js diff --git a/examples/connect_spheros.js b/examples/connect_spheros.js new file mode 100755 index 0000000..a9f3789 --- /dev/null +++ b/examples/connect_spheros.js @@ -0,0 +1,115 @@ +#!/usr/bin/env node +/** + * Creates virtual serial ports in /tmp for each Sphero port and holds the + * bluetooth ones open. This is helpful for avoiding connection errors and + * interference from iOS devices when connecting multiple Spheros. + * + * Requires the 'socat' command-line tool to create the virtual serial ports. + */ +var spawn = require('child_process').spawn; +var extname = require('path').extname; + +// where to store the virtual ports while running +var virtual_port_dir = '/tmp/'; + +// socat displays this when successfully connected +var success_str = 'starting data transfer loop'; + +// socat binary +//var socat = './testsocat.sh'; +var socat = 'socat'; + +// terminal colors +red   = '\033[31m'; +blue  = '\033[34m'; +green  = '\033[32m'; +reset = '\033[0m'; + +var paths = process.argv.slice(2); +var numPaths = paths.length; +if(paths.length === 0) { + console.log('usage connectSpheros.js [sphero serial port paths]'); + process.exit(1); +} +console.log('Connecting ' + numPaths + ' paired Spheros'); + +var connectPort = function(index, path, callback) { + var self; + var virtual_port = virtual_port_dir + extname(path).slice(1); + console.log('Starting connection to ' + path + ' at ' + virtual_port); + + // Run socat to connect virtual serial port to real one + child = spawn(socat, [ + '-d','-d','-d', // Extra debugging + 'pty,link=' + virtual_port + ',raw,echo=0', // Create virtual serial port + 'file:' + path // Connect to Sphero serial port + ]); + + child.on('exit', function() { + if(index in activeStreams) { + // We lost an active connection + console.log(red + 'Connection lost for ' + path + reset); + } else { + console.log('Failed to connect to ' + path); + } + + delete activeStreams[index]; + + // Restart self + children[self.index] = connectPort(index, path, callback); + }); + + var handleOutput = function(buffer) { + var data = buffer.toString(); + //console.log(index + ' stdout: ' + data); + + if(data.search('starting data transfer loop') !== -1) { + self.ready = true; + + console.log(self.path, ': stream is now active at ' + self.port); + callback(); + + activeStreams[index] = true; + if(Object.keys(activeStreams).length === numPaths) { + console.log('\n' + green + 'READY TO GO!!!' + reset + '\n'); + } + } + }; + child.stdout.on('data', handleOutput); + child.stderr.on('data', handleOutput); + + self = { + index: index, + path: path, + port: virtual_port, + child: child, + ready: false + }; + return self; +}; + +var children = Object.create(null); +var activeStreams = Object.create(null); + +// connect one at a time, waiting for callback +var i = 0; +var connectNext = function() { + if(!paths.length) { return; } + + i = i + 1; + var path = paths.shift(); + + children[i] = connectPort(i, path, connectNext); +}; +connectNext(); + +process.on('SIGINT', function() { + process.exit(); +}); +process.on('exit', function() { + for(var i=0; i '); +var stdin = process.openStdin(); +stdin.once('data', function() { + spheros.forEach(function(sphero) { + reset(sphero); + }); +}); + diff --git a/examples/setup_sphero.js b/examples/setup_sphero.js new file mode 100755 index 0000000..a95a0e8 --- /dev/null +++ b/examples/setup_sphero.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +var Sphero = require("../lib/sphero.js").Sphero; + +var run = function(sphero) { + + sphero.getBluetoothInfo(function(name, id) { + console.log("Device name is " + name + ' / ' + id); + }) + .setAutoReconnect(false, 5, function() { + console.log("Turned off auto reconnect"); + }) + .send({ + device: sphero.devices.core, + command: 0x25, + data: new Buffer([0xFF, 0xFF]), + success: function(packet) { + console.log("Set timeout to 65535 seconds"); + sphero.disconnect(); + process.exit(0); + } + }); +}; + + +var sphero; +if(process.argv.length === 2) { + sphero = new Sphero(); +} +else if(process.argv.length === 3) { + sphero = new Sphero(process.argv[2]); +} else { + console.log('usage: setup_sphero.js [path to sphero port]'); + process.exit(1); +} + +run(sphero); From cd6332f5cd3cf4f3d6090960ee91e772fda662a3 Mon Sep 17 00:00:00 2001 From: robbles Date: Sat, 25 Aug 2012 22:58:03 -0700 Subject: [PATCH 15/15] setBlackLED -> setBackLED --- examples/orient_spheros.js | 2 +- lib/sphero.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/orient_spheros.js b/examples/orient_spheros.js index 4e88e06..1b57e94 100755 --- a/examples/orient_spheros.js +++ b/examples/orient_spheros.js @@ -25,7 +25,7 @@ var orient = function(path) { console.log(deviceName + ': stabilization turned off'); }) // Turn on back led - .setBlackLED(255, function() { + .setBackLED(255, function() { console.log(deviceName + ': back LED on'); }) .setColor(0, 255, 0, function() { diff --git a/lib/sphero.js b/lib/sphero.js index 2e9b62e..999a1e4 100644 --- a/lib/sphero.js +++ b/lib/sphero.js @@ -286,8 +286,6 @@ Sphero.prototype.setRotationRate = function(rate, cb) { return this; }; -// More missing functions here - Sphero.prototype.roll = function(speed, heading, timeout, cb) { var self = this; @@ -362,7 +360,7 @@ Sphero.prototype.setColor = function(red, green, blue, cb) { return this; }; -Sphero.prototype.setBlackLED = function(val, cb) { +Sphero.prototype.setBackLED = function(val, cb) { var options = { device: this.devices.sphero, command: 0x21,