From 81da3b6ff556c928d015f971ddcd638031d66728 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:23:48 +0100 Subject: [PATCH 001/140] Updated package.json & README --- README.markdown | 17 ++++------------- package.json | 14 +++++++------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/README.markdown b/README.markdown index a5da08b..e3b42d9 100644 --- a/README.markdown +++ b/README.markdown @@ -1,16 +1,6 @@ -This module lets you bridge the real world to Node.js. Connect to sensors, robots, turn things on and off, take remote measurements. In fact if you find a creative use for this stuff, let me know! I'd be proud to hear of it being taken advantage of. - -(made up Javascript code to get your imagination going) - - frontdoor.on("open", function() { - if (alarm.state == "on") { - alarm.sound(); - hounds.release(); - } else { - lights.switchOn(); - voice.speak("Welcome home"); - } - }); +A more high level fork of Richard Morrison's node-xbee. + +Code Example Follows Background ========== @@ -90,4 +80,5 @@ See __example.js__ for a full working example (you'll need to use your own xbee Licence ------- +This work is based on the works of Richard Morrison Creative Commons License
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com. diff --git a/package.json b/package.json index 5467759..d36c48a 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ -{ "name" : "xbee", - "version" : "0.0.4", - "description" : "Node talks to xbee radios through serialport", - "author": "Richard Morrison ", - "main": "./xbee", +{ "name" : "xbee-svd", + "version" : "0.0.1", + "description" : "A more high level fork of Richard Morrison's node-xbee", + "author": "Jan Kolkmeier ", + "main": "xbee.js", "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], - "homepage": "https://github.com/mozz100/node-xbee", + "homepage": "https://github.com/jouz/node-xbee-svd", "repository": { "type" : "git", - "url" : "git://github.com/mozz100/node-xbee.git" + "url" : "git://github.com/jouz/node-xbee-svd.git" } } From a882d9cdd0b734b838c762c1ee72c1505b39e1c1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:24:04 +0100 Subject: [PATCH 002/140] Renamed sys to util --- xbee.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbee.js b/xbee.js index 8908e60..899e776 100644 --- a/xbee.js +++ b/xbee.js @@ -1,5 +1,5 @@ var Buffer = require('buffer').Buffer; -var sys = require('sys'); +var sys = require('util'); function decimalToHex(d, padding) { var hex = Number(d).toString(16); From a60e0922dae3a151f99257c6daecd423e75cde95 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:50:32 +0100 Subject: [PATCH 003/140] make packetToJson always return an object --- xbee.js | 111 +++++++++++++++++++++++++------------------------------- 1 file changed, 49 insertions(+), 62 deletions(-) diff --git a/xbee.js b/xbee.js index 899e776..92d4610 100644 --- a/xbee.js +++ b/xbee.js @@ -312,109 +312,96 @@ function packetToJS(packet) { // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) // So, the first byte in the packet is the frame type identifier. + + var json = { + type: undefined, + bytes: packet + } + if (packet[0]== exports.FT_AT_RESPONSE) { - return { - type: 'AT Response', - frameId: packet[1], - command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII - commandStatus: (packet[4] == 0) ? 'OK' : packet[4], - commandData: packet.slice(4), - bytes: packet - } + json.type: 'AT Response'; + json.frameId: packet[1], + json.command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII + json.commandStatus: (packet[4] == 0) ? 'OK' : packet[4], + json.commandData: packet.slice(4), } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - return { - type: 'Remote AT Response', - frameId: packet[1], - remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, - remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, - command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), - commandStatus: (packet[14] == 0) ? 'OK' : packet[14], - commandData: packet.slice(15), - bytes: packet - } + json.type: 'Remote AT Response', + json.frameId: packet[1], + json.remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, + json.remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, + json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), + json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], + json.commandData: packet.slice(15), } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { - p = { - type: 'RF Data', - remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, - receiveOptions: packet[11], - raw_data: packet.slice(12), - data: "", - bytes: packet + json.type: 'RF Data', + json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.receiveOptions: packet[11], + json.raw_data: packet.slice(12), + json.data: "", + for(i in json.raw_data) { + json.data += String.fromCharCode(json.raw_data[i]); } - // build ascii from raw_data - for(i in p.raw_data) { - p.data += String.fromCharCode(p.raw_data[i]); - } - return p } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - s = { - type: 'Data Sample', - remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, - receiveOptions: packet[11], - numSamples: packet[12], // apparently always set to 1 - digitalChannelMask: packet.slice(13,15), - analogChannelMask: packet[15], - bytes: packet - } + json.type: 'Data Sample', + json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.receiveOptions: packet[11], + json.numSamples: packet[12], // apparently always set to 1 + json.digitalChannelMask: packet.slice(13,15), + json.analogChannelMask: packet[15], // Bit more work to do on an I/O data sample. // First check s.digitalChannelMask - are there any digital samples? - if (s.digitalChannelMask[0] + s.digitalChannelMask[1] > 0) { + if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // digital channel mask indicates that digital samples are present, so they // are in the bytes 16 and 17. - s.digitalSamples = packet.slice(16,18); + json.digitalSamples = packet.slice(16,18); // Now check whether any analog samples are present - if (s.analogChannelMask > 0) { - s.analogSamples = packet.slice(18); + if (json.analogChannelMask > 0) { + json.analogSamples = packet.slice(18); } } else { // no digital samples. There might still be analog samples... - if (s.analogChannelMask > 0) { - s.analogSamples = packet.slice(16); + if (json.analogChannelMask > 0) { + json.analogSamples = packet.slice(16); } } // translate digital samples into JS for easier handling - s['samples'] = {} - - if (s.digitalChannelMask[0] + s.digitalChannelMask[1] > 0) { // if digital samples present, + json.samples = {} + if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // if digital samples present, // run through the first bitmask for digital pins, i.e. digiPinsByte1 for (x in digiPinsByte1) { // On first iteration, for example, x = 'D10', digiPinsByte1[x] = 4. // OK. So, is there a sample for this pin? Check the digital channel mask. - if (s.digitalChannelMask[0] & digiPinsByte1[x]) { + if (json.digitalChannelMask[0] & digiPinsByte1[x]) { // There is a sample for this pin. So, AND the sample byte and the bitmask, // and turn the result into a boolean. // On the first iteration, for example, this sets s['D10'] = 1 // if the bitwise AND of the first byte of the digital sample with 4 is > 0 - s['samples'][x] = ((s.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; + json.samples[x] = ((json.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; } } // do the same thing for the second load of digital inputs for (x in digiPinsByte2) { - if (s.digitalChannelMask[1] & digiPinsByte2[x]) { - s['samples'][x] = ((s.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; + if (json.digitalChannelMask[1] & digiPinsByte2[x]) { + json.samples[x] = ((json.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; } } } // Also translate analog samples into JS // The analog channel mask indicates which pins are enabled as analog channels. - if (s.analogChannelMask > 0) { + if (json.analogChannelMask > 0) { var sampleIndex = 0; for (x in analogPins) { // on first iteration, for example, x = 'A0', analogPins[x] = 1 - if (s.analogChannelMask & analogPins[x]) { - s['samples'][x] = 256*s.analogSamples[sampleIndex*2]+s.analogSamples[1+sampleIndex*2]; + if (json.analogChannelMask & analogPins[x]) { + json.samples[x] = 256*json.analogSamples[sampleIndex*2]+json.analogSamples[1+sampleIndex*2]; sampleIndex += 1; } } } - return s; - } else { - // The first byte of the packet indicates it's an as-yet unknown frame type. - // In this case, just return the bytes. - return packet; } + return packet; } From 755440d42c95c75ce24ca10311f16b8fafaa7a71 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:23:52 +0100 Subject: [PATCH 004/140] export conversion functions --- xbee-svd.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 xbee-svd.js diff --git a/xbee-svd.js b/xbee-svd.js new file mode 100644 index 0000000..242ab5b --- /dev/null +++ b/xbee-svd.js @@ -0,0 +1,49 @@ +var serialport = require("serialport"); +exports.XBee = require("./xbee"); + +// execute an AT command on the local xbee module +exports.AT = function(cmd, val, cb) { // e.g. 'ID' or '%V' + var atc = new xbee.ATCommand(); + atc.setCommand(cmd); + atc.commandParameter = val; + b = atc.getBytes(); + serial_xbee.write(b); + cb_stack +}; + +// execute an AT command on a remote xbee module +exports.RemoteAt = function(cmd, val, remote64, remote16) { + var atc = new xbee.RemoteATCommand(); + atc.setCommand(cmd); + atc.commandParameter = val; + atc.destination64 = remote64; + atc.destination16 = remote16; + b = atc.getBytes(); + serial_xbee.write(b); +} + +exports.open = function(port, cb) { + var xbee = {}; + xbee.serial = new serialport.SerialPort("/dev/ttyUSB0", { + parser: exports.XBee.packetParser() + }); + + xbee.serial.on("data", function(data) { + console.log('XB> ', data.type); + }); + + exports.AT('ID', false, function(res) { + xbee.id = res.commandData; + cb(xbee); + }); +} + +/* +// simple example: ATID on local xbee module +AT('ID'); +// simple example: query ATD0 on remote xbee module. +var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module +var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] + +RemoteAT('D0', null, remote64, remote16); +*/ From bd090a60ab7059da7b8616304a19399cd59f66cc Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:26:20 +0100 Subject: [PATCH 005/140] Revert "export conversion functions" This reverts commit 3b0897f1beaa418b8e81a7c74a4dc5827a248439. --- xbee-svd.js | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 xbee-svd.js diff --git a/xbee-svd.js b/xbee-svd.js deleted file mode 100644 index 242ab5b..0000000 --- a/xbee-svd.js +++ /dev/null @@ -1,49 +0,0 @@ -var serialport = require("serialport"); -exports.XBee = require("./xbee"); - -// execute an AT command on the local xbee module -exports.AT = function(cmd, val, cb) { // e.g. 'ID' or '%V' - var atc = new xbee.ATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - b = atc.getBytes(); - serial_xbee.write(b); - cb_stack -}; - -// execute an AT command on a remote xbee module -exports.RemoteAt = function(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); -} - -exports.open = function(port, cb) { - var xbee = {}; - xbee.serial = new serialport.SerialPort("/dev/ttyUSB0", { - parser: exports.XBee.packetParser() - }); - - xbee.serial.on("data", function(data) { - console.log('XB> ', data.type); - }); - - exports.AT('ID', false, function(res) { - xbee.id = res.commandData; - cb(xbee); - }); -} - -/* -// simple example: ATID on local xbee module -AT('ID'); -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - -RemoteAT('D0', null, remote64, remote16); -*/ From 13300b59fdf435689aa4bcde9ac3850545b1cdf1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:27:04 +0100 Subject: [PATCH 006/140] export conversion functions --- xbee.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/xbee.js b/xbee.js index 92d4610..5573888 100644 --- a/xbee.js +++ b/xbee.js @@ -1,7 +1,7 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); -function decimalToHex(d, padding) { +exports.decimalToHex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -12,10 +12,10 @@ function decimalToHex(d, padding) { return hex; } -function byteArrayToHexString(a) { +exports.byteArrayToHexString = function(a) { var s = ''; for(var i = 0; i < a.length; i++) { - s += decimalToHex(a[i]); + s += exports.decimalToHex(a[i]); } return s; } @@ -327,15 +327,15 @@ function packetToJS(packet) { } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { json.type: 'Remote AT Response', json.frameId: packet[1], - json.remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, - json.remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, + json.remote64: {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}, + json.remote16: {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}, json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], json.commandData: packet.slice(15), } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { json.type: 'RF Data', - json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, json.receiveOptions: packet[11], json.raw_data: packet.slice(12), json.data: "", @@ -344,8 +344,8 @@ function packetToJS(packet) { } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { json.type: 'Data Sample', - json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, json.receiveOptions: packet[11], json.numSamples: packet[12], // apparently always set to 1 json.digitalChannelMask: packet.slice(13,15), From a21ebcfa4af1e2aab4cd643f9f8b3e6643f0d1d1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:57:48 +0100 Subject: [PATCH 007/140] added gitignore to ignore node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules From 0fd3147fe61028959be5e09d91b9473decf43442 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:20:38 +0100 Subject: [PATCH 008/140] repaired some sloppieness --- xbee.js | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/xbee.js b/xbee.js index 5573888..7e961ae 100644 --- a/xbee.js +++ b/xbee.js @@ -315,41 +315,42 @@ function packetToJS(packet) { var json = { type: undefined, + ft: packet[0], bytes: packet } if (packet[0]== exports.FT_AT_RESPONSE) { - json.type: 'AT Response'; - json.frameId: packet[1], - json.command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII - json.commandStatus: (packet[4] == 0) ? 'OK' : packet[4], - json.commandData: packet.slice(4), + json.type = 'AT Response'; + json.frameId = packet[1]; + json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); // translate bytes back to ASCII + json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; + json.commandData = packet.slice(4); } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.type: 'Remote AT Response', - json.frameId: packet[1], - json.remote64: {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}, - json.remote16: {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}, - json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), - json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], - json.commandData: packet.slice(15), - } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.type: 'RF Data', - json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, - json.receiveOptions: packet[11], - json.raw_data: packet.slice(12), - json.data: "", + json.type = 'Remote AT Response'; + json.frameId = packet[1]; + json.remote64 = {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}; + json.remote16 = {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}; + json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); + json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; + json.commandData = packet.slice(15); + } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { + json.type = 'RF Data'; + json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.receiveOptions = packet[11]; + json.raw_data = packet.slice(12); + json.data = ""; for(i in json.raw_data) { - json.data += String.fromCharCode(json.raw_data[i]); + json.data += String.fromCharCode(json.raw_data[i]) } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.type: 'Data Sample', - json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, - json.receiveOptions: packet[11], - json.numSamples: packet[12], // apparently always set to 1 - json.digitalChannelMask: packet.slice(13,15), - json.analogChannelMask: packet[15], + json.type = 'Data Sample'; + json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.receiveOptions = packet[11]; + json.numSamples = packet[12]; // apparently always set to 1 + json.digitalChannelMask = packet.slice(13,15); + json.analogChannelMask = packet[15]; // Bit more work to do on an I/O data sample. // First check s.digitalChannelMask - are there any digital samples? if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { From 7b1edce888129bf8c6d60bb2c176d380e19e476b Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:21:05 +0100 Subject: [PATCH 009/140] added serialport as dependency --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d36c48a..f07b385 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,11 @@ "main": "xbee.js", "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], "homepage": "https://github.com/jouz/node-xbee-svd", + "dependencies": { + "serialport" : "0.7.x" + } "repository": { "type" : "git", "url" : "git://github.com/jouz/node-xbee-svd.git" } - } From 446a469a28fef73d8887f52815849b68cd620e4f Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:21:47 +0100 Subject: [PATCH 010/140] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3c3629e..1bd7226 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +*.swp From d63549439f09c8064f98f57d20fce44ecaad4be4 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Mon, 19 Mar 2012 21:27:41 +0100 Subject: [PATCH 011/140] plenty of changes --- xbee.js => xbee-serial.js | 129 +++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 28 deletions(-) rename xbee.js => xbee-serial.js (78%) diff --git a/xbee.js b/xbee-serial.js similarity index 78% rename from xbee.js rename to xbee-serial.js index 7e961ae..9cd70d5 100644 --- a/xbee.js +++ b/xbee-serial.js @@ -1,7 +1,7 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); -exports.decimalToHex = function(d, padding) { +exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -12,14 +12,22 @@ exports.decimalToHex = function(d, padding) { return hex; } -exports.byteArrayToHexString = function(a) { +exports.bArr2HexStr = function(a) { var s = ''; - for(var i = 0; i < a.length; i++) { - s += exports.decimalToHex(a[i]); + for(i in a) { + s += exports.dec2Hex(a[i]); } return s; } +exports.bArr2Str = function(a) { + var s = ''; + for(i in a) { + s += String.fromCharCode(a[i]); + } + return s; +} + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands @@ -32,9 +40,37 @@ function incrementFrameId() { return frameId; } -// Define some useful XBee constants exports.START_BYTE = 0x7e; // start of every XBee packet +exports.frameTypes = { + 0x92 : { + name: "DATA_SAMPLE_RX", + }, + 0x08 : { + name: "AT_COMMAND", + }, + 0x88 : { + name: "AT_RESPONSE", + }, + 0x17 : { + name: "REMOTE_AT_COMMAND", + }, + 0x97 : { + name: "REMOTE_AT_RESPONSE", + }, + 0x10 : { + name: "TRANSMIT_RF_DATA", + }, + 0x8b : { + name: "TRANSMIT_ACKNOWLEDGED", + }, + 0x90 : { + name: "RECEIVE_RF_DATA", + }, + 0x95 : { + name: "NODE_IDENTIFICATION", + }, +} // Frame Types exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) @@ -45,12 +81,15 @@ exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received +exports.FT_NODE_IDENTIFICATION = 0x95; + // Bitmasks for I/O pins var digiPinsByte1 = { D10: 4, D11: 8, D12: 16 }; + var digiPinsByte2 = { D0: 1, D1: 2, @@ -210,6 +249,7 @@ var TransmitRFData = function() { this.broadcastRadius = 0x00; // use maximum hops value by default this.options = 0x00; // see digi docs for more info } + sys.inherits(TransmitRFData, Packet); TransmitRFData.prototype.getPayload = function() { @@ -239,6 +279,7 @@ TransmitRFData.prototype.getPayload = function() { if (this.RFData) { for(var j=0; j 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // translate the packet into a JS object before emitting it - emitter.emit("data", packetToJS(packet)); + var json = packetToJS(packet); + emitter.emit(json.ft, json); } // there will still be a checksum byte. Currently this is ignored @@ -312,41 +354,69 @@ function packetToJS(packet) { // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) // So, the first byte in the packet is the frame type identifier. - var json = { - type: undefined, - ft: packet[0], - bytes: packet - } + ft: exports.frameTypes.hasOwnProperty(packet[0]) ? exports.frameTypes[packet[0]].name : packet[0] + }; - if (packet[0]== exports.FT_AT_RESPONSE) { - json.type = 'AT Response'; + if (packet[0] == exports.FT_NODE_IDENTIFICATION) { + json.sender64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.sender16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; + json.recieveOptions = packet[11]; + json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; + json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; + json.nodeIdentifier = ""; + var ni_length = 0; + while (packet[22+ni_length] != 0x00) { + json.nodeIdentifier += String.fromCharCode(packet[22+ni_length]); + ni_length += 1; + } + var offset = 22+ni_length+1; + json.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; + json.deviceType = packet[offset+2]; + json.sourceEvent = packet[offset+3]; + // skip digi application profile & manufacturer id + json.payload = packet.splice(offset); + } else if (packet[0] == exports.FT_AT_RESPONSE) { json.frameId = packet[1]; - json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); // translate bytes back to ASCII + json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; - json.commandData = packet.slice(4); + if (json.command == 'ND') { + json.node = {}; + json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; + json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; + json.node.nodeIdentifier = ""; + var ni_length = 0; + while (packet[15+ni_length] != 0x00) { + json.node.nodeIdentifier += String.fromCharCode(packet[15+ni_length]); + ni_length += 1; + } + var offset = 15+ni_length+1; + json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; + json.node.deviceType = packet[offset+2]; + json.node.sourceEvent = packet[offset+3]; + // skip status, digi application profile & manufacturer id + } else { + json.commandData = packet.slice(5); + } } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.type = 'Remote AT Response'; json.frameId = packet[1]; - json.remote64 = {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}; - json.remote16 = {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}; + json.remote64 = {dec: packet.slice(2,10), hex: exports.bArr2HexStr(packet.slice(2,10))}; + json.remote16 = {dec: packet.slice(10,12), hex: exports.bArr2HexStr(packet.slice(10,12))}; json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; json.commandData = packet.slice(15); } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.type = 'RF Data'; - json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; json.receiveOptions = packet[11]; - json.raw_data = packet.slice(12); json.data = ""; - for(i in json.raw_data) { - json.data += String.fromCharCode(json.raw_data[i]) + var raw_data = packet.slice(12); + for(i in raw_data) { + json.data += String.fromCharCode(raw_data[i]) } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.type = 'Data Sample'; - json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; json.receiveOptions = packet[11]; json.numSamples = packet[12]; // apparently always set to 1 json.digitalChannelMask = packet.slice(13,15); @@ -403,6 +473,9 @@ function packetToJS(packet) { } } } + } else { + json.payload = packet.slice(1); } - return packet; + + return json; } From a644e5aadee0c7defc8a92a405e09fa536503205 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Mon, 19 Mar 2012 21:28:55 +0100 Subject: [PATCH 012/140] added test and xbee class --- example.js | 45 ----------------- test.js | 6 +++ xbee.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 45 deletions(-) delete mode 100644 example.js create mode 100644 test.js create mode 100644 xbee.js diff --git a/example.js b/example.js deleted file mode 100644 index dcda477..0000000 --- a/example.js +++ /dev/null @@ -1,45 +0,0 @@ -var rsp = require("serialport"); -var xbee = require("xbee"); -var SerialPort = rsp.SerialPort; // localize object constructor - -// connect to xbee module on /dev/ttyUSB0 using serialport. -// Pass xbee.packetParser as the parser - that's it -var serial_xbee = new SerialPort("/dev/ttyUSB0", { - parser: xbee.packetParser() -}); - -// listen for incoming xbee data -serial_xbee.on("data", function(data) { - console.log('xbee data received:', data.type); -}); - -// execute an AT command on the local xbee module -function AT(cmd, val) { // e.g. 'ID' or '%V' - var atc = new xbee.ATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); -}; - -// simple example: ATID on local xbee module -AT('ID'); - -// execute an AT command on a remote xbee module -function RemoteAT(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); -} - -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - -RemoteAT('D0', null, remote64, remote16); diff --git a/test.js b/test.js new file mode 100644 index 0000000..ea9d41e --- /dev/null +++ b/test.js @@ -0,0 +1,6 @@ +var xbeesvd = require('./xbee-svd.js'); +var xbee; + +xbeesvd.open('/dev/ttyO1', function(xb) { + xbee = xb; +}); diff --git a/xbee.js b/xbee.js new file mode 100644 index 0000000..848d0f1 --- /dev/null +++ b/xbee.js @@ -0,0 +1,143 @@ +var util = require('util'); +var async = require('async'); +var serialport = require("serialport"); +exports.XBee = require("./xbee"); + +exports.open = function(port, cb) { + var xbee = {}; + xbee._cb = undefined; + + // execute an AT command on the local xbee module + xbee.AT = function(cmd, val, cb) { + var atc = new exports.XBee.ATCommand(); + atc.setCommand(cmd); + if (typeof val === 'function') cb = val; + else if (val) atc.commandParameter = val; + b = atc.getBytes(); + if (xbee._cb == undefined) { + xbee.serial.write(b); + if (typeof cb === 'function') xbee._cb = cb; + } else { + if (cb) cb(false); + console.log("ERROR: XBee occupied"); + } + }; + + xbee.send = function(payload, remote64, remote16) { + var trf = new exports.XBee.TransmitRFData(); + trf.destination64 = remote64; + trf.destination16 = remote16; + trf.RFData = payload; + b = trf.getBytes(); + if (true || xbee._cb == undefined) { + xbee.serial.write(b); + } else { + console.log("ERROR: XBee occupied"); + } + } + + xbee.RemoteAT = function(cmd, remote64, remote16, val, cb) { + var atc = new exports.XBee.RemoteATCommand(); + atc.setCommand(cmd); + if (typeof val === 'function') cb = val; + else if (val) atc.commandParameter = val; + atc.destination64 = remote64; + atc.destination16 = remote16; + b = atc.getBytes(); + if (xbee._cb == undefined) { + xbee.serial.write(b); + if (typeof cb === 'function') xbee._cb = cb; + } else { + if (cb) cb(false); + console.log("ERROR: XBee occupied"); + } + } + + xbee.serial = new serialport.SerialPort(port, { + parser: exports.XBee.packetParser() + }); + + var at_cb = function(data) { + //console.log(util.inspect(data)); + if (data.command == "ND") { + console.log("ATND RES: "+util.inspect(data.node)); + } else if (xbee._cb != undefined) { + var cb = xbee._cb; + xbee._cb = undefined; + cb(data); + } else { + console.log("UNHANDLED "+util.inspect(data)); + } + } + xbee.serial.on("REMOTE_AT_RESPONSE", at_cb); + xbee.serial.on("AT_RESPONSE", at_cb); + xbee.serial.on("RECEIVE_RF_DATA", function(data) { + console.log("> "+data.data); + }); + + xbee.serial.on("NODE_DENTIFICATION", function(node) { + console.log(node.nodeIdentifier+" is online"); + xbee.send("Hey! Nice you're here!\n", node.remote64.dec, node.remote16.dec); + /* + if (node.nodeIdentifier === "NODE2") { + var spam = function() { + xbee.RemoteAT('ID', node.remote64.dec, node.remote16.dec, function(data) { + console.log("REMOTE_AT_RESPONSE: "+data.commandStatus); + }); + } + } else { + var spam = function() { + } + } + */ + + }); + + var QF = function(command, f) { + f = typeof f !== 'undefined' ? f : function(a){return a}; + return function(cb) { + xbee.AT(command, function(data) { + cb(!data.commandStatus, f(data.commandData)); + }); + } + } + + var config = { + panid: QF('ID', exports.XBee.bArr2HexStr), + identifier: QF('NI', exports.XBee.bArr2Str), + sourceLow: QF('SL', exports.XBee.bArr2HexStr), + sourceHigh: QF('SH', exports.XBee.bArr2HexStr) + }; + + async.series(config, function(err, results) { + console.log(results); + xbee.config = results; + xbee.AT('ND', function(data) { + console.log(util.inspect(data)); + }); + //var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x33,0x02,0x87]; + //var remote16 = [0x0e,0xdf]; + //var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + //var remote16 = [0xff,0xfe]; + //xbee.RemoteAT('FR', remote64, remote16, function(data) { + //xbee.send("Hello World\n", remote64, remote16); + //console.log(util.inspect(data)); + //}); + //}); + cb(xbee); + }); + +} + +/* +// execute an AT command on a remote xbee module + +/* +// simple example: ATID on local xbee module +AT('ID'); +// simple example: query ATD0 on remote xbee module. +var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module +var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] + +RemoteAT('D0', null, remote64, remote16); +*/ From 03fbd064569a6c3ee259a9a76b8f65299979c0e2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 08:39:51 +0100 Subject: [PATCH 013/140] wrote new example --- example.js | 16 ++++++++++++++++ test.js | 6 ------ xbee-serial.js => xbee-api.js | 0 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 example.js delete mode 100644 test.js rename xbee-serial.js => xbee-api.js (100%) diff --git a/example.js b/example.js new file mode 100644 index 0000000..841ea4f --- /dev/null +++ b/example.js @@ -0,0 +1,16 @@ +var util = require('util'); +var XBee = require('./xbee').XBee; +var xbee = new XBee('/dev/ttyO1'); + +xbee.on("configured", function(config) { + console.log("XBee Config: %s", util.inspect(config)); +}); + +xbee.on("node", function(node) { + console.log("Node %s connected", node.id); + + node.on("data", function(data) { + console.log("%s: %s", node.id, util.inspect(data)); + }); + +}); diff --git a/test.js b/test.js deleted file mode 100644 index ea9d41e..0000000 --- a/test.js +++ /dev/null @@ -1,6 +0,0 @@ -var xbeesvd = require('./xbee-svd.js'); -var xbee; - -xbeesvd.open('/dev/ttyO1', function(xb) { - xbee = xb; -}); diff --git a/xbee-serial.js b/xbee-api.js similarity index 100% rename from xbee-serial.js rename to xbee-api.js From 8e131a6727a37161951dd4a80bfd41f21aec86e8 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 08:40:23 +0100 Subject: [PATCH 014/140] rewrote abstraction --- xbee-api.js | 12 ++- xbee.js | 245 +++++++++++++++++++++++++++++----------------------- 2 files changed, 145 insertions(+), 112 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index 9cd70d5..a21ced0 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,6 +1,8 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); +// TODO: CHAIN PARSING + exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -338,6 +340,7 @@ exports.packetParser = function () { if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // translate the packet into a JS object before emitting it var json = packetToJS(packet); + console.log("F: "+json.ft); emitter.emit(json.ft, json); } @@ -364,10 +367,10 @@ function packetToJS(packet) { json.recieveOptions = packet[11]; json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; - json.nodeIdentifier = ""; + json.id = ""; var ni_length = 0; while (packet[22+ni_length] != 0x00) { - json.nodeIdentifier += String.fromCharCode(packet[22+ni_length]); + json.id += String.fromCharCode(packet[22+ni_length]); ni_length += 1; } var offset = 22+ni_length+1; @@ -384,16 +387,17 @@ function packetToJS(packet) { json.node = {}; json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; - json.node.nodeIdentifier = ""; + json.node.id = ""; var ni_length = 0; while (packet[15+ni_length] != 0x00) { - json.node.nodeIdentifier += String.fromCharCode(packet[15+ni_length]); + json.node.id += String.fromCharCode(packet[15+ni_length]); ni_length += 1; } var offset = 15+ni_length+1; json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; json.node.deviceType = packet[offset+2]; json.node.sourceEvent = packet[offset+3]; + json.status = packet[offset+4]; // skip status, digi application profile & manufacturer id } else { json.commandData = packet.slice(5); diff --git a/xbee.js b/xbee.js index 848d0f1..ab8c1e7 100644 --- a/xbee.js +++ b/xbee.js @@ -1,143 +1,172 @@ var util = require('util'); -var async = require('async'); +var EventEmitter = require('events').EventEmitter; +var api = require("./xbee-api"); var serialport = require("serialport"); -exports.XBee = require("./xbee"); - -exports.open = function(port, cb) { - var xbee = {}; - xbee._cb = undefined; - - // execute an AT command on the local xbee module - xbee.AT = function(cmd, val, cb) { - var atc = new exports.XBee.ATCommand(); - atc.setCommand(cmd); - if (typeof val === 'function') cb = val; - else if (val) atc.commandParameter = val; - b = atc.getBytes(); - if (xbee._cb == undefined) { - xbee.serial.write(b); - if (typeof cb === 'function') xbee._cb = cb; - } else { - if (cb) cb(false); - console.log("ERROR: XBee occupied"); - } - }; - - xbee.send = function(payload, remote64, remote16) { - var trf = new exports.XBee.TransmitRFData(); - trf.destination64 = remote64; - trf.destination16 = remote16; - trf.RFData = payload; - b = trf.getBytes(); - if (true || xbee._cb == undefined) { - xbee.serial.write(b); +var async = require('async'); + +function XBee(port) { + EventEmitter.call(this); + + this.nodes = {}; + + this.serial = new serialport.SerialPort(port, { + parser: api.packetParser() + }); + + var self = this; + + this._onNodeDiscovery = function(node) { + // RemoveAllListeners??ß + if (self.nodes[node.remote64.hex]) { + self.nodes[node.remote64.hex].removeAllListeners(); } else { - console.log("ERROR: XBee occupied"); + self.nodes[node.remote64.hex] = new Node(self, node); } + this.emit("node", self.nodes[node.remote64.hex]); } - - xbee.RemoteAT = function(cmd, remote64, remote16, val, cb) { - var atc = new exports.XBee.RemoteATCommand(); - atc.setCommand(cmd); - if (typeof val === 'function') cb = val; - else if (val) atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - if (xbee._cb == undefined) { - xbee.serial.write(b); - if (typeof cb === 'function') xbee._cb = cb; + + // On AT Response + this._onRemoteATResponse = function(res) { + // On Node Discovery Packet, emit new Node + if (self.nodes[res.remote64.dec]) { + self.nodes[res.remote64.dec]._onRemoteATResponse(res); } else { - if (cb) cb(false); - console.log("ERROR: XBee occupied"); + console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); } } - xbee.serial = new serialport.SerialPort(port, { - parser: exports.XBee.packetParser() - }); + this._onATResponse_FilterND = function(res) { + if (res.command == "ND") self._onNodeDiscovery(res.node); + } - var at_cb = function(data) { - //console.log(util.inspect(data)); - if (data.command == "ND") { - console.log("ATND RES: "+util.inspect(data.node)); - } else if (xbee._cb != undefined) { - var cb = xbee._cb; - xbee._cb = undefined; - cb(data); + this._onMessage = function(data) { + if (self.nodes[data.remote64.hex]) { + self.nodes[data.remote64.hex]._onData(data); } else { - console.log("UNHANDLED "+util.inspect(data)); + console.log("ERROR: Data from unknown node!"); } } - xbee.serial.on("REMOTE_AT_RESPONSE", at_cb); - xbee.serial.on("AT_RESPONSE", at_cb); - xbee.serial.on("RECEIVE_RF_DATA", function(data) { - console.log("> "+data.data); - }); - xbee.serial.on("NODE_DENTIFICATION", function(node) { - console.log(node.nodeIdentifier+" is online"); - xbee.send("Hey! Nice you're here!\n", node.remote64.dec, node.remote16.dec); - /* - if (node.nodeIdentifier === "NODE2") { - var spam = function() { - xbee.RemoteAT('ID', node.remote64.dec, node.remote16.dec, function(data) { - console.log("REMOTE_AT_RESPONSE: "+data.commandStatus); - }); - } - } else { - var spam = function() { - } - } - */ - - }); + this.serial.on("REMOTE_AT_RESPONSE", this._onRemoteATResponse); + this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); + this.serial.on("RECEIVE_RF_DATA", this._onMessage); + + this.configure(); +} + +util.inherits(XBee, EventEmitter); +XBee.prototype.configure = function() { + var self = this; var QF = function(command, f) { f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { - xbee.AT(command, function(data) { + self._ATCB(command, function(data) { cb(!data.commandStatus, f(data.commandData)); }); } } var config = { - panid: QF('ID', exports.XBee.bArr2HexStr), - identifier: QF('NI', exports.XBee.bArr2Str), - sourceLow: QF('SL', exports.XBee.bArr2HexStr), - sourceHigh: QF('SH', exports.XBee.bArr2HexStr) + panid: QF('ID', api.bArr2HexStr), + id: QF('NI', api.bArr2Str), + sourceLow: QF('SL', api.bArr2HexStr), + sourceHigh: QF('SH', api.bArr2HexStr), + nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) }; async.series(config, function(err, results) { console.log(results); - xbee.config = results; - xbee.AT('ND', function(data) { - console.log(util.inspect(data)); + self.config = results; + self.emit("configured", self.config); + self.discover(function() { + console.log("Discovery Over"); }); - //var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x33,0x02,0x87]; - //var remote16 = [0x0e,0xdf]; - //var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - //var remote16 = [0xff,0xfe]; - //xbee.RemoteAT('FR', remote64, remote16, function(data) { - //xbee.send("Hello World\n", remote64, remote16); - //console.log(util.inspect(data)); - //}); - //}); - cb(xbee); }); +} +XBee.prototype.discover = function(cb) { + this._AT('ND'); + this.serial.on("AT_RESPONSE", this._onATResponse_FilterND); + setTimeout(function() { + cb(); + }, this.config.nodeDiscoveryTime); } -/* -// execute an AT command on a remote xbee module +XBee.prototype.broadcast = function(data) { + var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + var remote16 = [0xff,0xfe]; + this._send(data, remote64, remote16); +} + +XBee._send = function(data, remote64, remote16) { + var frame = new api.TransmitRFData(); + if (typeof remote64.dec === 'undefined') { + frame.destination64 = remote64; + frame.destination16 = remote16; + } else { + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + } + frame.RFData = data; + this.serial.write(frame.getBytes()); +} + +XBee.prototype._ATCB = function(cmd, val, cb) { + if (typeof val === 'function') { + cb = val; + val = undefined; + } + this._AT(cmd, val); + this.serial.on("AT_RESPONSE", function(res) { + if (res.command == cmd) { + cb(res); + } + }); +} -/* -// simple example: ATID on local xbee module -AT('ID'); -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] +XBee.prototype._AT = function(cmd, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + this.serial.write(frame.getBytes()); +} + +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + if (typeof remote64.dec === 'undefined') { + frame.destination64 = remote64; + frame.destination16 = remote16; + } else { + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + } + this.serial.write(frame.getBytes()); +} + +exports.XBee = XBee; + +function Node(xbee, params) { + EventEmitter.call(this); + this.xbee = xbee; + this.id = params.id; + this.remote16 = params.remote16; + this.remote64 = params.remote64; +} + + +Node.prototype.send = function(data) { + this.xbee._send(data, this.remote64, this.remote16); +} + +Node.prototype._AT = function(cmd, val) { + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); +} + +Node.prototype._onATResponse = function(res) { + console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); +} -RemoteAT('D0', null, remote64, remote16); -*/ +util.inherits(Node, EventEmitter); From 3f917be593a040bc2ae2f84f50e59c71e35a62b7 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 17 Mar 2012 15:15:09 +0000 Subject: [PATCH 015/140] added dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f07b385..22dff87 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { "serialport" : "0.7.x" + "async" : "0.1.x" } "repository": { "type" : "git", From b8fdf572f5ec652617796fb155988e5092833d2a Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:23:48 +0100 Subject: [PATCH 016/140] Updated package.json & README --- README.markdown | 17 ++++------------- package.json | 14 +++++++------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/README.markdown b/README.markdown index a5da08b..e3b42d9 100644 --- a/README.markdown +++ b/README.markdown @@ -1,16 +1,6 @@ -This module lets you bridge the real world to Node.js. Connect to sensors, robots, turn things on and off, take remote measurements. In fact if you find a creative use for this stuff, let me know! I'd be proud to hear of it being taken advantage of. - -(made up Javascript code to get your imagination going) - - frontdoor.on("open", function() { - if (alarm.state == "on") { - alarm.sound(); - hounds.release(); - } else { - lights.switchOn(); - voice.speak("Welcome home"); - } - }); +A more high level fork of Richard Morrison's node-xbee. + +Code Example Follows Background ========== @@ -90,4 +80,5 @@ See __example.js__ for a full working example (you'll need to use your own xbee Licence ------- +This work is based on the works of Richard Morrison Creative Commons License
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com. diff --git a/package.json b/package.json index 5467759..d36c48a 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ -{ "name" : "xbee", - "version" : "0.0.4", - "description" : "Node talks to xbee radios through serialport", - "author": "Richard Morrison ", - "main": "./xbee", +{ "name" : "xbee-svd", + "version" : "0.0.1", + "description" : "A more high level fork of Richard Morrison's node-xbee", + "author": "Jan Kolkmeier ", + "main": "xbee.js", "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], - "homepage": "https://github.com/mozz100/node-xbee", + "homepage": "https://github.com/jouz/node-xbee-svd", "repository": { "type" : "git", - "url" : "git://github.com/mozz100/node-xbee.git" + "url" : "git://github.com/jouz/node-xbee-svd.git" } } From 5bc18ebe1887923c3f591f66ad63e5d2dd64c243 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:24:04 +0100 Subject: [PATCH 017/140] Renamed sys to util --- xbee.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbee.js b/xbee.js index 8908e60..899e776 100644 --- a/xbee.js +++ b/xbee.js @@ -1,5 +1,5 @@ var Buffer = require('buffer').Buffer; -var sys = require('sys'); +var sys = require('util'); function decimalToHex(d, padding) { var hex = Number(d).toString(16); From b16b3efd2617419783845e1d1bb80f08930b6586 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 13:50:32 +0100 Subject: [PATCH 018/140] make packetToJson always return an object --- xbee.js | 111 +++++++++++++++++++++++++------------------------------- 1 file changed, 49 insertions(+), 62 deletions(-) diff --git a/xbee.js b/xbee.js index 899e776..92d4610 100644 --- a/xbee.js +++ b/xbee.js @@ -312,109 +312,96 @@ function packetToJS(packet) { // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) // So, the first byte in the packet is the frame type identifier. + + var json = { + type: undefined, + bytes: packet + } + if (packet[0]== exports.FT_AT_RESPONSE) { - return { - type: 'AT Response', - frameId: packet[1], - command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII - commandStatus: (packet[4] == 0) ? 'OK' : packet[4], - commandData: packet.slice(4), - bytes: packet - } + json.type: 'AT Response'; + json.frameId: packet[1], + json.command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII + json.commandStatus: (packet[4] == 0) ? 'OK' : packet[4], + json.commandData: packet.slice(4), } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - return { - type: 'Remote AT Response', - frameId: packet[1], - remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, - remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, - command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), - commandStatus: (packet[14] == 0) ? 'OK' : packet[14], - commandData: packet.slice(15), - bytes: packet - } + json.type: 'Remote AT Response', + json.frameId: packet[1], + json.remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, + json.remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, + json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), + json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], + json.commandData: packet.slice(15), } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { - p = { - type: 'RF Data', - remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, - receiveOptions: packet[11], - raw_data: packet.slice(12), - data: "", - bytes: packet + json.type: 'RF Data', + json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.receiveOptions: packet[11], + json.raw_data: packet.slice(12), + json.data: "", + for(i in json.raw_data) { + json.data += String.fromCharCode(json.raw_data[i]); } - // build ascii from raw_data - for(i in p.raw_data) { - p.data += String.fromCharCode(p.raw_data[i]); - } - return p } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - s = { - type: 'Data Sample', - remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, - receiveOptions: packet[11], - numSamples: packet[12], // apparently always set to 1 - digitalChannelMask: packet.slice(13,15), - analogChannelMask: packet[15], - bytes: packet - } + json.type: 'Data Sample', + json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.receiveOptions: packet[11], + json.numSamples: packet[12], // apparently always set to 1 + json.digitalChannelMask: packet.slice(13,15), + json.analogChannelMask: packet[15], // Bit more work to do on an I/O data sample. // First check s.digitalChannelMask - are there any digital samples? - if (s.digitalChannelMask[0] + s.digitalChannelMask[1] > 0) { + if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // digital channel mask indicates that digital samples are present, so they // are in the bytes 16 and 17. - s.digitalSamples = packet.slice(16,18); + json.digitalSamples = packet.slice(16,18); // Now check whether any analog samples are present - if (s.analogChannelMask > 0) { - s.analogSamples = packet.slice(18); + if (json.analogChannelMask > 0) { + json.analogSamples = packet.slice(18); } } else { // no digital samples. There might still be analog samples... - if (s.analogChannelMask > 0) { - s.analogSamples = packet.slice(16); + if (json.analogChannelMask > 0) { + json.analogSamples = packet.slice(16); } } // translate digital samples into JS for easier handling - s['samples'] = {} - - if (s.digitalChannelMask[0] + s.digitalChannelMask[1] > 0) { // if digital samples present, + json.samples = {} + if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // if digital samples present, // run through the first bitmask for digital pins, i.e. digiPinsByte1 for (x in digiPinsByte1) { // On first iteration, for example, x = 'D10', digiPinsByte1[x] = 4. // OK. So, is there a sample for this pin? Check the digital channel mask. - if (s.digitalChannelMask[0] & digiPinsByte1[x]) { + if (json.digitalChannelMask[0] & digiPinsByte1[x]) { // There is a sample for this pin. So, AND the sample byte and the bitmask, // and turn the result into a boolean. // On the first iteration, for example, this sets s['D10'] = 1 // if the bitwise AND of the first byte of the digital sample with 4 is > 0 - s['samples'][x] = ((s.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; + json.samples[x] = ((json.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; } } // do the same thing for the second load of digital inputs for (x in digiPinsByte2) { - if (s.digitalChannelMask[1] & digiPinsByte2[x]) { - s['samples'][x] = ((s.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; + if (json.digitalChannelMask[1] & digiPinsByte2[x]) { + json.samples[x] = ((json.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; } } } // Also translate analog samples into JS // The analog channel mask indicates which pins are enabled as analog channels. - if (s.analogChannelMask > 0) { + if (json.analogChannelMask > 0) { var sampleIndex = 0; for (x in analogPins) { // on first iteration, for example, x = 'A0', analogPins[x] = 1 - if (s.analogChannelMask & analogPins[x]) { - s['samples'][x] = 256*s.analogSamples[sampleIndex*2]+s.analogSamples[1+sampleIndex*2]; + if (json.analogChannelMask & analogPins[x]) { + json.samples[x] = 256*json.analogSamples[sampleIndex*2]+json.analogSamples[1+sampleIndex*2]; sampleIndex += 1; } } } - return s; - } else { - // The first byte of the packet indicates it's an as-yet unknown frame type. - // In this case, just return the bytes. - return packet; } + return packet; } From 3b0897f1beaa418b8e81a7c74a4dc5827a248439 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:23:52 +0100 Subject: [PATCH 019/140] export conversion functions --- xbee-svd.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 xbee-svd.js diff --git a/xbee-svd.js b/xbee-svd.js new file mode 100644 index 0000000..242ab5b --- /dev/null +++ b/xbee-svd.js @@ -0,0 +1,49 @@ +var serialport = require("serialport"); +exports.XBee = require("./xbee"); + +// execute an AT command on the local xbee module +exports.AT = function(cmd, val, cb) { // e.g. 'ID' or '%V' + var atc = new xbee.ATCommand(); + atc.setCommand(cmd); + atc.commandParameter = val; + b = atc.getBytes(); + serial_xbee.write(b); + cb_stack +}; + +// execute an AT command on a remote xbee module +exports.RemoteAt = function(cmd, val, remote64, remote16) { + var atc = new xbee.RemoteATCommand(); + atc.setCommand(cmd); + atc.commandParameter = val; + atc.destination64 = remote64; + atc.destination16 = remote16; + b = atc.getBytes(); + serial_xbee.write(b); +} + +exports.open = function(port, cb) { + var xbee = {}; + xbee.serial = new serialport.SerialPort("/dev/ttyUSB0", { + parser: exports.XBee.packetParser() + }); + + xbee.serial.on("data", function(data) { + console.log('XB> ', data.type); + }); + + exports.AT('ID', false, function(res) { + xbee.id = res.commandData; + cb(xbee); + }); +} + +/* +// simple example: ATID on local xbee module +AT('ID'); +// simple example: query ATD0 on remote xbee module. +var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module +var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] + +RemoteAT('D0', null, remote64, remote16); +*/ From 10b70892d534991ad364728f1aa31ecbe094ce24 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:26:20 +0100 Subject: [PATCH 020/140] Revert "export conversion functions" This reverts commit 3b0897f1beaa418b8e81a7c74a4dc5827a248439. --- xbee-svd.js | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 xbee-svd.js diff --git a/xbee-svd.js b/xbee-svd.js deleted file mode 100644 index 242ab5b..0000000 --- a/xbee-svd.js +++ /dev/null @@ -1,49 +0,0 @@ -var serialport = require("serialport"); -exports.XBee = require("./xbee"); - -// execute an AT command on the local xbee module -exports.AT = function(cmd, val, cb) { // e.g. 'ID' or '%V' - var atc = new xbee.ATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - b = atc.getBytes(); - serial_xbee.write(b); - cb_stack -}; - -// execute an AT command on a remote xbee module -exports.RemoteAt = function(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); -} - -exports.open = function(port, cb) { - var xbee = {}; - xbee.serial = new serialport.SerialPort("/dev/ttyUSB0", { - parser: exports.XBee.packetParser() - }); - - xbee.serial.on("data", function(data) { - console.log('XB> ', data.type); - }); - - exports.AT('ID', false, function(res) { - xbee.id = res.commandData; - cb(xbee); - }); -} - -/* -// simple example: ATID on local xbee module -AT('ID'); -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - -RemoteAT('D0', null, remote64, remote16); -*/ From 406ce9d4fb3edd6c14f106d2ea735e41d5947fbe Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:27:04 +0100 Subject: [PATCH 021/140] export conversion functions --- xbee.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/xbee.js b/xbee.js index 92d4610..5573888 100644 --- a/xbee.js +++ b/xbee.js @@ -1,7 +1,7 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); -function decimalToHex(d, padding) { +exports.decimalToHex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -12,10 +12,10 @@ function decimalToHex(d, padding) { return hex; } -function byteArrayToHexString(a) { +exports.byteArrayToHexString = function(a) { var s = ''; for(var i = 0; i < a.length; i++) { - s += decimalToHex(a[i]); + s += exports.decimalToHex(a[i]); } return s; } @@ -327,15 +327,15 @@ function packetToJS(packet) { } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { json.type: 'Remote AT Response', json.frameId: packet[1], - json.remote64: {dec: packet.slice(2,10), hex: byteArrayToHexString(packet.slice(2,10))}, - json.remote16: {dec: packet.slice(10,12), hex: byteArrayToHexString(packet.slice(10,12))}, + json.remote64: {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}, + json.remote16: {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}, json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], json.commandData: packet.slice(15), } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { json.type: 'RF Data', - json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, json.receiveOptions: packet[11], json.raw_data: packet.slice(12), json.data: "", @@ -344,8 +344,8 @@ function packetToJS(packet) { } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { json.type: 'Data Sample', - json.remote64: {dec: packet.slice(1,9), hex: byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: byteArrayToHexString(packet.slice(9,11))}, + json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, + json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, json.receiveOptions: packet[11], json.numSamples: packet[12], // apparently always set to 1 json.digitalChannelMask: packet.slice(13,15), From 20ceeeb5a52dd51ad63144e51e4778f60cb28215 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 14:57:48 +0100 Subject: [PATCH 022/140] added gitignore to ignore node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules From 65b78c82a2629d78b0b24cb786ac0c21bd3e9371 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:20:38 +0100 Subject: [PATCH 023/140] repaired some sloppieness --- xbee.js | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/xbee.js b/xbee.js index 5573888..7e961ae 100644 --- a/xbee.js +++ b/xbee.js @@ -315,41 +315,42 @@ function packetToJS(packet) { var json = { type: undefined, + ft: packet[0], bytes: packet } if (packet[0]== exports.FT_AT_RESPONSE) { - json.type: 'AT Response'; - json.frameId: packet[1], - json.command: String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]), // translate bytes back to ASCII - json.commandStatus: (packet[4] == 0) ? 'OK' : packet[4], - json.commandData: packet.slice(4), + json.type = 'AT Response'; + json.frameId = packet[1]; + json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); // translate bytes back to ASCII + json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; + json.commandData = packet.slice(4); } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.type: 'Remote AT Response', - json.frameId: packet[1], - json.remote64: {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}, - json.remote16: {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}, - json.command: String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]), - json.commandStatus: (packet[14] == 0) ? 'OK' : packet[14], - json.commandData: packet.slice(15), - } else if(packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.type: 'RF Data', - json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, - json.receiveOptions: packet[11], - json.raw_data: packet.slice(12), - json.data: "", + json.type = 'Remote AT Response'; + json.frameId = packet[1]; + json.remote64 = {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}; + json.remote16 = {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}; + json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); + json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; + json.commandData = packet.slice(15); + } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { + json.type = 'RF Data'; + json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.receiveOptions = packet[11]; + json.raw_data = packet.slice(12); + json.data = ""; for(i in json.raw_data) { - json.data += String.fromCharCode(json.raw_data[i]); + json.data += String.fromCharCode(json.raw_data[i]) } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.type: 'Data Sample', - json.remote64: {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}, - json.remote16: {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}, - json.receiveOptions: packet[11], - json.numSamples: packet[12], // apparently always set to 1 - json.digitalChannelMask: packet.slice(13,15), - json.analogChannelMask: packet[15], + json.type = 'Data Sample'; + json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.receiveOptions = packet[11]; + json.numSamples = packet[12]; // apparently always set to 1 + json.digitalChannelMask = packet.slice(13,15); + json.analogChannelMask = packet[15]; // Bit more work to do on an I/O data sample. // First check s.digitalChannelMask - are there any digital samples? if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { From 94c2e85c9df53bf45bb51534e0b937ac4e19ad05 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:21:05 +0100 Subject: [PATCH 024/140] added serialport as dependency --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d36c48a..f07b385 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,11 @@ "main": "xbee.js", "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], "homepage": "https://github.com/jouz/node-xbee-svd", + "dependencies": { + "serialport" : "0.7.x" + } "repository": { "type" : "git", "url" : "git://github.com/jouz/node-xbee-svd.git" } - } From e034fcdec20e32573e7cace14ac66656b5383ff0 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 18 Mar 2012 15:21:47 +0100 Subject: [PATCH 025/140] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3c3629e..1bd7226 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +*.swp From 2586fb955fa73f6f102767f371760ac1706e4c37 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Mon, 19 Mar 2012 21:27:41 +0100 Subject: [PATCH 026/140] plenty of changes --- xbee.js => xbee-serial.js | 129 +++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 28 deletions(-) rename xbee.js => xbee-serial.js (78%) diff --git a/xbee.js b/xbee-serial.js similarity index 78% rename from xbee.js rename to xbee-serial.js index 7e961ae..9cd70d5 100644 --- a/xbee.js +++ b/xbee-serial.js @@ -1,7 +1,7 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); -exports.decimalToHex = function(d, padding) { +exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -12,14 +12,22 @@ exports.decimalToHex = function(d, padding) { return hex; } -exports.byteArrayToHexString = function(a) { +exports.bArr2HexStr = function(a) { var s = ''; - for(var i = 0; i < a.length; i++) { - s += exports.decimalToHex(a[i]); + for(i in a) { + s += exports.dec2Hex(a[i]); } return s; } +exports.bArr2Str = function(a) { + var s = ''; + for(i in a) { + s += String.fromCharCode(a[i]); + } + return s; +} + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands @@ -32,9 +40,37 @@ function incrementFrameId() { return frameId; } -// Define some useful XBee constants exports.START_BYTE = 0x7e; // start of every XBee packet +exports.frameTypes = { + 0x92 : { + name: "DATA_SAMPLE_RX", + }, + 0x08 : { + name: "AT_COMMAND", + }, + 0x88 : { + name: "AT_RESPONSE", + }, + 0x17 : { + name: "REMOTE_AT_COMMAND", + }, + 0x97 : { + name: "REMOTE_AT_RESPONSE", + }, + 0x10 : { + name: "TRANSMIT_RF_DATA", + }, + 0x8b : { + name: "TRANSMIT_ACKNOWLEDGED", + }, + 0x90 : { + name: "RECEIVE_RF_DATA", + }, + 0x95 : { + name: "NODE_IDENTIFICATION", + }, +} // Frame Types exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) @@ -45,12 +81,15 @@ exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received +exports.FT_NODE_IDENTIFICATION = 0x95; + // Bitmasks for I/O pins var digiPinsByte1 = { D10: 4, D11: 8, D12: 16 }; + var digiPinsByte2 = { D0: 1, D1: 2, @@ -210,6 +249,7 @@ var TransmitRFData = function() { this.broadcastRadius = 0x00; // use maximum hops value by default this.options = 0x00; // see digi docs for more info } + sys.inherits(TransmitRFData, Packet); TransmitRFData.prototype.getPayload = function() { @@ -239,6 +279,7 @@ TransmitRFData.prototype.getPayload = function() { if (this.RFData) { for(var j=0; j 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // translate the packet into a JS object before emitting it - emitter.emit("data", packetToJS(packet)); + var json = packetToJS(packet); + emitter.emit(json.ft, json); } // there will still be a checksum byte. Currently this is ignored @@ -312,41 +354,69 @@ function packetToJS(packet) { // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) // So, the first byte in the packet is the frame type identifier. - var json = { - type: undefined, - ft: packet[0], - bytes: packet - } + ft: exports.frameTypes.hasOwnProperty(packet[0]) ? exports.frameTypes[packet[0]].name : packet[0] + }; - if (packet[0]== exports.FT_AT_RESPONSE) { - json.type = 'AT Response'; + if (packet[0] == exports.FT_NODE_IDENTIFICATION) { + json.sender64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.sender16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; + json.recieveOptions = packet[11]; + json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; + json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; + json.nodeIdentifier = ""; + var ni_length = 0; + while (packet[22+ni_length] != 0x00) { + json.nodeIdentifier += String.fromCharCode(packet[22+ni_length]); + ni_length += 1; + } + var offset = 22+ni_length+1; + json.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; + json.deviceType = packet[offset+2]; + json.sourceEvent = packet[offset+3]; + // skip digi application profile & manufacturer id + json.payload = packet.splice(offset); + } else if (packet[0] == exports.FT_AT_RESPONSE) { json.frameId = packet[1]; - json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); // translate bytes back to ASCII + json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; - json.commandData = packet.slice(4); + if (json.command == 'ND') { + json.node = {}; + json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; + json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; + json.node.nodeIdentifier = ""; + var ni_length = 0; + while (packet[15+ni_length] != 0x00) { + json.node.nodeIdentifier += String.fromCharCode(packet[15+ni_length]); + ni_length += 1; + } + var offset = 15+ni_length+1; + json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; + json.node.deviceType = packet[offset+2]; + json.node.sourceEvent = packet[offset+3]; + // skip status, digi application profile & manufacturer id + } else { + json.commandData = packet.slice(5); + } } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.type = 'Remote AT Response'; json.frameId = packet[1]; - json.remote64 = {dec: packet.slice(2,10), hex: exports.byteArrayToHexString(packet.slice(2,10))}; - json.remote16 = {dec: packet.slice(10,12), hex: exports.byteArrayToHexString(packet.slice(10,12))}; + json.remote64 = {dec: packet.slice(2,10), hex: exports.bArr2HexStr(packet.slice(2,10))}; + json.remote16 = {dec: packet.slice(10,12), hex: exports.bArr2HexStr(packet.slice(10,12))}; json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; json.commandData = packet.slice(15); } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.type = 'RF Data'; - json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; json.receiveOptions = packet[11]; - json.raw_data = packet.slice(12); json.data = ""; - for(i in json.raw_data) { - json.data += String.fromCharCode(json.raw_data[i]) + var raw_data = packet.slice(12); + for(i in raw_data) { + json.data += String.fromCharCode(raw_data[i]) } } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.type = 'Data Sample'; - json.remote64 = {dec: packet.slice(1,9), hex: exports.byteArrayToHexString(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.byteArrayToHexString(packet.slice(9,11))}; + json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; + json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; json.receiveOptions = packet[11]; json.numSamples = packet[12]; // apparently always set to 1 json.digitalChannelMask = packet.slice(13,15); @@ -403,6 +473,9 @@ function packetToJS(packet) { } } } + } else { + json.payload = packet.slice(1); } - return packet; + + return json; } From 9687607444462148ed4fcb044b60d26640008f70 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Mon, 19 Mar 2012 21:28:55 +0100 Subject: [PATCH 027/140] added test and xbee class --- example.js | 45 ----------------- test.js | 6 +++ xbee.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 45 deletions(-) delete mode 100644 example.js create mode 100644 test.js create mode 100644 xbee.js diff --git a/example.js b/example.js deleted file mode 100644 index dcda477..0000000 --- a/example.js +++ /dev/null @@ -1,45 +0,0 @@ -var rsp = require("serialport"); -var xbee = require("xbee"); -var SerialPort = rsp.SerialPort; // localize object constructor - -// connect to xbee module on /dev/ttyUSB0 using serialport. -// Pass xbee.packetParser as the parser - that's it -var serial_xbee = new SerialPort("/dev/ttyUSB0", { - parser: xbee.packetParser() -}); - -// listen for incoming xbee data -serial_xbee.on("data", function(data) { - console.log('xbee data received:', data.type); -}); - -// execute an AT command on the local xbee module -function AT(cmd, val) { // e.g. 'ID' or '%V' - var atc = new xbee.ATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); -}; - -// simple example: ATID on local xbee module -AT('ID'); - -// execute an AT command on a remote xbee module -function RemoteAT(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); -} - -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - -RemoteAT('D0', null, remote64, remote16); diff --git a/test.js b/test.js new file mode 100644 index 0000000..ea9d41e --- /dev/null +++ b/test.js @@ -0,0 +1,6 @@ +var xbeesvd = require('./xbee-svd.js'); +var xbee; + +xbeesvd.open('/dev/ttyO1', function(xb) { + xbee = xb; +}); diff --git a/xbee.js b/xbee.js new file mode 100644 index 0000000..848d0f1 --- /dev/null +++ b/xbee.js @@ -0,0 +1,143 @@ +var util = require('util'); +var async = require('async'); +var serialport = require("serialport"); +exports.XBee = require("./xbee"); + +exports.open = function(port, cb) { + var xbee = {}; + xbee._cb = undefined; + + // execute an AT command on the local xbee module + xbee.AT = function(cmd, val, cb) { + var atc = new exports.XBee.ATCommand(); + atc.setCommand(cmd); + if (typeof val === 'function') cb = val; + else if (val) atc.commandParameter = val; + b = atc.getBytes(); + if (xbee._cb == undefined) { + xbee.serial.write(b); + if (typeof cb === 'function') xbee._cb = cb; + } else { + if (cb) cb(false); + console.log("ERROR: XBee occupied"); + } + }; + + xbee.send = function(payload, remote64, remote16) { + var trf = new exports.XBee.TransmitRFData(); + trf.destination64 = remote64; + trf.destination16 = remote16; + trf.RFData = payload; + b = trf.getBytes(); + if (true || xbee._cb == undefined) { + xbee.serial.write(b); + } else { + console.log("ERROR: XBee occupied"); + } + } + + xbee.RemoteAT = function(cmd, remote64, remote16, val, cb) { + var atc = new exports.XBee.RemoteATCommand(); + atc.setCommand(cmd); + if (typeof val === 'function') cb = val; + else if (val) atc.commandParameter = val; + atc.destination64 = remote64; + atc.destination16 = remote16; + b = atc.getBytes(); + if (xbee._cb == undefined) { + xbee.serial.write(b); + if (typeof cb === 'function') xbee._cb = cb; + } else { + if (cb) cb(false); + console.log("ERROR: XBee occupied"); + } + } + + xbee.serial = new serialport.SerialPort(port, { + parser: exports.XBee.packetParser() + }); + + var at_cb = function(data) { + //console.log(util.inspect(data)); + if (data.command == "ND") { + console.log("ATND RES: "+util.inspect(data.node)); + } else if (xbee._cb != undefined) { + var cb = xbee._cb; + xbee._cb = undefined; + cb(data); + } else { + console.log("UNHANDLED "+util.inspect(data)); + } + } + xbee.serial.on("REMOTE_AT_RESPONSE", at_cb); + xbee.serial.on("AT_RESPONSE", at_cb); + xbee.serial.on("RECEIVE_RF_DATA", function(data) { + console.log("> "+data.data); + }); + + xbee.serial.on("NODE_DENTIFICATION", function(node) { + console.log(node.nodeIdentifier+" is online"); + xbee.send("Hey! Nice you're here!\n", node.remote64.dec, node.remote16.dec); + /* + if (node.nodeIdentifier === "NODE2") { + var spam = function() { + xbee.RemoteAT('ID', node.remote64.dec, node.remote16.dec, function(data) { + console.log("REMOTE_AT_RESPONSE: "+data.commandStatus); + }); + } + } else { + var spam = function() { + } + } + */ + + }); + + var QF = function(command, f) { + f = typeof f !== 'undefined' ? f : function(a){return a}; + return function(cb) { + xbee.AT(command, function(data) { + cb(!data.commandStatus, f(data.commandData)); + }); + } + } + + var config = { + panid: QF('ID', exports.XBee.bArr2HexStr), + identifier: QF('NI', exports.XBee.bArr2Str), + sourceLow: QF('SL', exports.XBee.bArr2HexStr), + sourceHigh: QF('SH', exports.XBee.bArr2HexStr) + }; + + async.series(config, function(err, results) { + console.log(results); + xbee.config = results; + xbee.AT('ND', function(data) { + console.log(util.inspect(data)); + }); + //var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x33,0x02,0x87]; + //var remote16 = [0x0e,0xdf]; + //var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + //var remote16 = [0xff,0xfe]; + //xbee.RemoteAT('FR', remote64, remote16, function(data) { + //xbee.send("Hello World\n", remote64, remote16); + //console.log(util.inspect(data)); + //}); + //}); + cb(xbee); + }); + +} + +/* +// execute an AT command on a remote xbee module + +/* +// simple example: ATID on local xbee module +AT('ID'); +// simple example: query ATD0 on remote xbee module. +var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module +var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] + +RemoteAT('D0', null, remote64, remote16); +*/ From 58717478bb22a0ae26b9c5c9b643f2e14cf70f22 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 08:39:51 +0100 Subject: [PATCH 028/140] wrote new example --- example.js | 16 ++++++++++++++++ test.js | 6 ------ xbee-serial.js => xbee-api.js | 0 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 example.js delete mode 100644 test.js rename xbee-serial.js => xbee-api.js (100%) diff --git a/example.js b/example.js new file mode 100644 index 0000000..841ea4f --- /dev/null +++ b/example.js @@ -0,0 +1,16 @@ +var util = require('util'); +var XBee = require('./xbee').XBee; +var xbee = new XBee('/dev/ttyO1'); + +xbee.on("configured", function(config) { + console.log("XBee Config: %s", util.inspect(config)); +}); + +xbee.on("node", function(node) { + console.log("Node %s connected", node.id); + + node.on("data", function(data) { + console.log("%s: %s", node.id, util.inspect(data)); + }); + +}); diff --git a/test.js b/test.js deleted file mode 100644 index ea9d41e..0000000 --- a/test.js +++ /dev/null @@ -1,6 +0,0 @@ -var xbeesvd = require('./xbee-svd.js'); -var xbee; - -xbeesvd.open('/dev/ttyO1', function(xb) { - xbee = xb; -}); diff --git a/xbee-serial.js b/xbee-api.js similarity index 100% rename from xbee-serial.js rename to xbee-api.js From b8e36e7b4d45a3f5db5d02821d7ba269d6708989 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 08:40:23 +0100 Subject: [PATCH 029/140] rewrote abstraction --- xbee-api.js | 12 ++- xbee.js | 245 +++++++++++++++++++++++++++++----------------------- 2 files changed, 145 insertions(+), 112 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index 9cd70d5..a21ced0 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,6 +1,8 @@ var Buffer = require('buffer').Buffer; var sys = require('util'); +// TODO: CHAIN PARSING + exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -338,6 +340,7 @@ exports.packetParser = function () { if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // translate the packet into a JS object before emitting it var json = packetToJS(packet); + console.log("F: "+json.ft); emitter.emit(json.ft, json); } @@ -364,10 +367,10 @@ function packetToJS(packet) { json.recieveOptions = packet[11]; json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; - json.nodeIdentifier = ""; + json.id = ""; var ni_length = 0; while (packet[22+ni_length] != 0x00) { - json.nodeIdentifier += String.fromCharCode(packet[22+ni_length]); + json.id += String.fromCharCode(packet[22+ni_length]); ni_length += 1; } var offset = 22+ni_length+1; @@ -384,16 +387,17 @@ function packetToJS(packet) { json.node = {}; json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; - json.node.nodeIdentifier = ""; + json.node.id = ""; var ni_length = 0; while (packet[15+ni_length] != 0x00) { - json.node.nodeIdentifier += String.fromCharCode(packet[15+ni_length]); + json.node.id += String.fromCharCode(packet[15+ni_length]); ni_length += 1; } var offset = 15+ni_length+1; json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; json.node.deviceType = packet[offset+2]; json.node.sourceEvent = packet[offset+3]; + json.status = packet[offset+4]; // skip status, digi application profile & manufacturer id } else { json.commandData = packet.slice(5); diff --git a/xbee.js b/xbee.js index 848d0f1..ab8c1e7 100644 --- a/xbee.js +++ b/xbee.js @@ -1,143 +1,172 @@ var util = require('util'); -var async = require('async'); +var EventEmitter = require('events').EventEmitter; +var api = require("./xbee-api"); var serialport = require("serialport"); -exports.XBee = require("./xbee"); - -exports.open = function(port, cb) { - var xbee = {}; - xbee._cb = undefined; - - // execute an AT command on the local xbee module - xbee.AT = function(cmd, val, cb) { - var atc = new exports.XBee.ATCommand(); - atc.setCommand(cmd); - if (typeof val === 'function') cb = val; - else if (val) atc.commandParameter = val; - b = atc.getBytes(); - if (xbee._cb == undefined) { - xbee.serial.write(b); - if (typeof cb === 'function') xbee._cb = cb; - } else { - if (cb) cb(false); - console.log("ERROR: XBee occupied"); - } - }; - - xbee.send = function(payload, remote64, remote16) { - var trf = new exports.XBee.TransmitRFData(); - trf.destination64 = remote64; - trf.destination16 = remote16; - trf.RFData = payload; - b = trf.getBytes(); - if (true || xbee._cb == undefined) { - xbee.serial.write(b); +var async = require('async'); + +function XBee(port) { + EventEmitter.call(this); + + this.nodes = {}; + + this.serial = new serialport.SerialPort(port, { + parser: api.packetParser() + }); + + var self = this; + + this._onNodeDiscovery = function(node) { + // RemoveAllListeners??ß + if (self.nodes[node.remote64.hex]) { + self.nodes[node.remote64.hex].removeAllListeners(); } else { - console.log("ERROR: XBee occupied"); + self.nodes[node.remote64.hex] = new Node(self, node); } + this.emit("node", self.nodes[node.remote64.hex]); } - - xbee.RemoteAT = function(cmd, remote64, remote16, val, cb) { - var atc = new exports.XBee.RemoteATCommand(); - atc.setCommand(cmd); - if (typeof val === 'function') cb = val; - else if (val) atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - if (xbee._cb == undefined) { - xbee.serial.write(b); - if (typeof cb === 'function') xbee._cb = cb; + + // On AT Response + this._onRemoteATResponse = function(res) { + // On Node Discovery Packet, emit new Node + if (self.nodes[res.remote64.dec]) { + self.nodes[res.remote64.dec]._onRemoteATResponse(res); } else { - if (cb) cb(false); - console.log("ERROR: XBee occupied"); + console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); } } - xbee.serial = new serialport.SerialPort(port, { - parser: exports.XBee.packetParser() - }); + this._onATResponse_FilterND = function(res) { + if (res.command == "ND") self._onNodeDiscovery(res.node); + } - var at_cb = function(data) { - //console.log(util.inspect(data)); - if (data.command == "ND") { - console.log("ATND RES: "+util.inspect(data.node)); - } else if (xbee._cb != undefined) { - var cb = xbee._cb; - xbee._cb = undefined; - cb(data); + this._onMessage = function(data) { + if (self.nodes[data.remote64.hex]) { + self.nodes[data.remote64.hex]._onData(data); } else { - console.log("UNHANDLED "+util.inspect(data)); + console.log("ERROR: Data from unknown node!"); } } - xbee.serial.on("REMOTE_AT_RESPONSE", at_cb); - xbee.serial.on("AT_RESPONSE", at_cb); - xbee.serial.on("RECEIVE_RF_DATA", function(data) { - console.log("> "+data.data); - }); - xbee.serial.on("NODE_DENTIFICATION", function(node) { - console.log(node.nodeIdentifier+" is online"); - xbee.send("Hey! Nice you're here!\n", node.remote64.dec, node.remote16.dec); - /* - if (node.nodeIdentifier === "NODE2") { - var spam = function() { - xbee.RemoteAT('ID', node.remote64.dec, node.remote16.dec, function(data) { - console.log("REMOTE_AT_RESPONSE: "+data.commandStatus); - }); - } - } else { - var spam = function() { - } - } - */ - - }); + this.serial.on("REMOTE_AT_RESPONSE", this._onRemoteATResponse); + this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); + this.serial.on("RECEIVE_RF_DATA", this._onMessage); + + this.configure(); +} + +util.inherits(XBee, EventEmitter); +XBee.prototype.configure = function() { + var self = this; var QF = function(command, f) { f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { - xbee.AT(command, function(data) { + self._ATCB(command, function(data) { cb(!data.commandStatus, f(data.commandData)); }); } } var config = { - panid: QF('ID', exports.XBee.bArr2HexStr), - identifier: QF('NI', exports.XBee.bArr2Str), - sourceLow: QF('SL', exports.XBee.bArr2HexStr), - sourceHigh: QF('SH', exports.XBee.bArr2HexStr) + panid: QF('ID', api.bArr2HexStr), + id: QF('NI', api.bArr2Str), + sourceLow: QF('SL', api.bArr2HexStr), + sourceHigh: QF('SH', api.bArr2HexStr), + nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) }; async.series(config, function(err, results) { console.log(results); - xbee.config = results; - xbee.AT('ND', function(data) { - console.log(util.inspect(data)); + self.config = results; + self.emit("configured", self.config); + self.discover(function() { + console.log("Discovery Over"); }); - //var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x33,0x02,0x87]; - //var remote16 = [0x0e,0xdf]; - //var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - //var remote16 = [0xff,0xfe]; - //xbee.RemoteAT('FR', remote64, remote16, function(data) { - //xbee.send("Hello World\n", remote64, remote16); - //console.log(util.inspect(data)); - //}); - //}); - cb(xbee); }); +} +XBee.prototype.discover = function(cb) { + this._AT('ND'); + this.serial.on("AT_RESPONSE", this._onATResponse_FilterND); + setTimeout(function() { + cb(); + }, this.config.nodeDiscoveryTime); } -/* -// execute an AT command on a remote xbee module +XBee.prototype.broadcast = function(data) { + var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + var remote16 = [0xff,0xfe]; + this._send(data, remote64, remote16); +} + +XBee._send = function(data, remote64, remote16) { + var frame = new api.TransmitRFData(); + if (typeof remote64.dec === 'undefined') { + frame.destination64 = remote64; + frame.destination16 = remote16; + } else { + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + } + frame.RFData = data; + this.serial.write(frame.getBytes()); +} + +XBee.prototype._ATCB = function(cmd, val, cb) { + if (typeof val === 'function') { + cb = val; + val = undefined; + } + this._AT(cmd, val); + this.serial.on("AT_RESPONSE", function(res) { + if (res.command == cmd) { + cb(res); + } + }); +} -/* -// simple example: ATID on local xbee module -AT('ID'); -// simple example: query ATD0 on remote xbee module. -var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module -var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] +XBee.prototype._AT = function(cmd, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + this.serial.write(frame.getBytes()); +} + +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + if (typeof remote64.dec === 'undefined') { + frame.destination64 = remote64; + frame.destination16 = remote16; + } else { + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + } + this.serial.write(frame.getBytes()); +} + +exports.XBee = XBee; + +function Node(xbee, params) { + EventEmitter.call(this); + this.xbee = xbee; + this.id = params.id; + this.remote16 = params.remote16; + this.remote64 = params.remote64; +} + + +Node.prototype.send = function(data) { + this.xbee._send(data, this.remote64, this.remote16); +} + +Node.prototype._AT = function(cmd, val) { + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); +} + +Node.prototype._onATResponse = function(res) { + console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); +} -RemoteAT('D0', null, remote64, remote16); -*/ +util.inherits(Node, EventEmitter); From b461c512aa431c93fa809a82822f6b8d581d8ebd Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 17 Mar 2012 15:15:09 +0000 Subject: [PATCH 030/140] added dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f07b385..22dff87 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { "serialport" : "0.7.x" + "async" : "0.1.x" } "repository": { "type" : "git", From f92bad178f93533db02c6b38821afe5009c8521c Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 17 Mar 2012 15:15:39 +0000 Subject: [PATCH 031/140] removed debug line --- xbee.js | 1 - 1 file changed, 1 deletion(-) diff --git a/xbee.js b/xbee.js index ab8c1e7..abf95ed 100644 --- a/xbee.js +++ b/xbee.js @@ -76,7 +76,6 @@ XBee.prototype.configure = function() { }; async.series(config, function(err, results) { - console.log(results); self.config = results; self.emit("configured", self.config); self.discover(function() { From 06a6e1bc013b7a506bc617891a6ec9d90c5117bd Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 17 Mar 2012 15:15:39 +0000 Subject: [PATCH 032/140] removed debug line --- xbee.js | 1 - 1 file changed, 1 deletion(-) diff --git a/xbee.js b/xbee.js index ab8c1e7..abf95ed 100644 --- a/xbee.js +++ b/xbee.js @@ -76,7 +76,6 @@ XBee.prototype.configure = function() { }; async.series(config, function(err, results) { - console.log(results); self.config = results; self.emit("configured", self.config); self.discover(function() { From 3769cd698641dc5dbf8d10e5167b3b4f23195435 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 16:22:35 +0100 Subject: [PATCH 033/140] clean(er) rewrite --- xbee-api.js | 365 +++++++++++++++++++++++----------------------------- xbee.js | 31 ++--- 2 files changed, 177 insertions(+), 219 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index a21ced0..c173d84 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,7 +1,5 @@ var Buffer = require('buffer').Buffer; -var sys = require('util'); - -// TODO: CHAIN PARSING +var util = require('util'); exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); @@ -44,36 +42,6 @@ function incrementFrameId() { exports.START_BYTE = 0x7e; // start of every XBee packet -exports.frameTypes = { - 0x92 : { - name: "DATA_SAMPLE_RX", - }, - 0x08 : { - name: "AT_COMMAND", - }, - 0x88 : { - name: "AT_RESPONSE", - }, - 0x17 : { - name: "REMOTE_AT_COMMAND", - }, - 0x97 : { - name: "REMOTE_AT_RESPONSE", - }, - 0x10 : { - name: "TRANSMIT_RF_DATA", - }, - 0x8b : { - name: "TRANSMIT_ACKNOWLEDGED", - }, - 0x90 : { - name: "RECEIVE_RF_DATA", - }, - 0x95 : { - name: "NODE_IDENTIFICATION", - }, -} -// Frame Types exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) exports.FT_AT_RESPONSE = 0x88; // AT response (local) @@ -82,7 +50,6 @@ exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received - exports.FT_NODE_IDENTIFICATION = 0x95; // Bitmasks for I/O pins @@ -102,6 +69,7 @@ var digiPinsByte2 = { D6: 64, D7: 128 }; + var analogPins = { A0: 1, A1: 2, @@ -161,15 +129,11 @@ Packet.prototype.getPayload = function() { exports.Packet = Packet; -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~ OUTGOING XBEE PACKETS ~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // ATCommand is for setting/reading AT registers on the local XBee node. var ATCommand = function() { this.frameId = incrementFrameId(); }; -sys.inherits(ATCommand, Packet); +util.inherits(ATCommand, Packet); ATCommand.prototype.setCommand = function(strCmd) { // take the ascii command and save it internally as byte values command0 and command1 @@ -206,7 +170,7 @@ var RemoteATCommand = function() { this.frameId = incrementFrameId(); this.remoteCommandOptions = 0x02; // set default command options on creation }; -sys.inherits(RemoteATCommand, ATCommand); +util.inherits(RemoteATCommand, ATCommand); RemoteATCommand.prototype.getPayload = function() { // Returns a JS array of byte values @@ -252,7 +216,7 @@ var TransmitRFData = function() { this.options = 0x00; // see digi docs for more info } -sys.inherits(TransmitRFData, Packet); +util.inherits(TransmitRFData, Packet); TransmitRFData.prototype.getPayload = function() { // Returns a JS array of byte values @@ -290,196 +254,189 @@ TransmitRFData.prototype.getPayload = function() { exports.TransmitRFData = TransmitRFData; - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~ INCOMING XBEE PACKETS ~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -exports.packetParser = function () { - // A function which can be used with the 'serialport' npm package - // and a XBee radio in API mode. - // It builds a JS array of integers as data is received, and when the - // array represents a complete XBee packet, it emits it as a 'data' event, - // passing a JS object (as translated by packetToJS) instead of a load of numbers. - - // incoming data buffer saved in closure as a JS array of integers called 'packet' - var packet = []; +// Builds Packets out of data received from Serial Port. +exports.packetBuilder = function () { + var packet = []; // incoming data buffer saved in closure as a JS array of integers called 'packet' var packpos = 999; // this variable is used to remember at which position we are up to within the overall packet - var packlen = 0; // used to remember the length of the current packet. XBee API packets have two length bytes immediately after the start byte + var packlen = 0; // used to remember the length of the current packet. return function (emitter, buffer) { - // Collecting data. 'buffer' needs to be run through - it contains bytes received from the serial port - // which may or may not represent an entire XBee packet. - + // Collecting data. for(var i=0; i < buffer.length; i++) { - b = buffer[i]; // store the working byte + b = buffer[i]; // store the working byte packpos += 1; + // Detected start of packet. if (b == exports.START_BYTE) { - // Detected start of packet. - // exports.START_BYTE = 126, the start of a zigbee packet i.e. 0x7e packpos = 0; - packlen = 0; // length of packet is unknown, as yet. - packet = []; // store the bytes as they come in. Don't keep start byte or length bytes - } - if (packpos == 1) { - // most significant bit of the length - packlen += b<<8; - } - if (packpos == 2) { - // least significant bit of the length - packlen += b; + packlen = 0; + packet = []; } - // for all other bytes, collect them onto the end of our growing 'packet' array + if (packpos == 1) packlen += b << 8; // most significant bit of the length + if (packpos == 2) packlen += b; // least significant bit of the length + if ((packlen > 0) && (packpos > 2) && (packet.length < packlen)) { packet.push(b); } - // emit the packet when it's fully built. packlen + 3 = position of final byte + // Packet is complete. Parse & Emit if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // translate the packet into a JS object before emitting it - var json = packetToJS(packet); - console.log("F: "+json.ft); - emitter.emit(json.ft, json); - } - - // there will still be a checksum byte. Currently this is ignored - if ((packlen > 0) && (packet.length == packlen) && (packpos > packlen + 3)) { - // ignore checksum for now + // There will still be a checksum byte. Currently this is ignored + var parser = new PacketParser(packet) + var json = parser.parse(); + //console.log("P: "+util.inspect(json)); + var event = json.type; + if (json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + event += "_"+json.frameId; + } + console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); + emitter.emit(event, json); } } }; } -function packetToJS(packet) { - // given an array of byte values, return a JS object representing the packet - // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) +// Packet Parser Class. Used to parse packets if they are known +var PacketParser = function(p) { + this.json = { + ft: p.splice(0,1)[0], + } - // So, the first byte in the packet is the frame type identifier. - var json = { - ft: exports.frameTypes.hasOwnProperty(packet[0]) ? exports.frameTypes[packet[0]].name : packet[0] - }; + // Used as pointer to the object data is parsed into + this.write = this.json; + this.payload = p; +} - if (packet[0] == exports.FT_NODE_IDENTIFICATION) { - json.sender64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.sender16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.recieveOptions = packet[11]; - json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; - json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; - json.id = ""; - var ni_length = 0; - while (packet[22+ni_length] != 0x00) { - json.id += String.fromCharCode(packet[22+ni_length]); - ni_length += 1; - } - var offset = 22+ni_length+1; - json.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; - json.deviceType = packet[offset+2]; - json.sourceEvent = packet[offset+3]; - // skip digi application profile & manufacturer id - json.payload = packet.splice(offset); - } else if (packet[0] == exports.FT_AT_RESPONSE) { - json.frameId = packet[1]; - json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); - json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; - if (json.command == 'ND') { - json.node = {}; - json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; - json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; - json.node.id = ""; - var ni_length = 0; - while (packet[15+ni_length] != 0x00) { - json.node.id += String.fromCharCode(packet[15+ni_length]); - ni_length += 1; - } - var offset = 15+ni_length+1; - json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; - json.node.deviceType = packet[offset+2]; - json.node.sourceEvent = packet[offset+3]; - json.status = packet[offset+4]; - // skip status, digi application profile & manufacturer id - } else { - json.commandData = packet.slice(5); +PacketParser.prototype.parse = function() { + if (this.knownFrames[this.json.ft]) { + this.json.type = this.knownFrames[this.json.ft].type; + this.knownFrames[this.json.ft].parse(this); + } else { + this.json.type = "UNKNOWN"; + } + return this.json; +} + +PacketParser.prototype.readAddr = function(name, length) { + var dec = this.payload.splice(0, length); + this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } + return this; +} + +PacketParser.prototype.readByte = function(name, length) { + if (typeof length === 'number') + this.write[name] = this.payload.splice(0,length); + else this.write[name] = this.payload.splice(0,1)[0]; + return this; +} + +PacketParser.prototype.readAddr64 = function(name) { + return this.readAddr(name, 8); +} + +PacketParser.prototype.readAddr16 = function(name) { + return this.readAddr(name, 2); +} + +PacketParser.prototype.readString = function(name, length) { + this.write[name] = ""; + if (typeof length === 'number') { + for (var i = 0; i < length; i++) + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } else { + while(this.payload[0] != 0x00) { + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); } - } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.frameId = packet[1]; - json.remote64 = {dec: packet.slice(2,10), hex: exports.bArr2HexStr(packet.slice(2,10))}; - json.remote16 = {dec: packet.slice(10,12), hex: exports.bArr2HexStr(packet.slice(10,12))}; - json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); - json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; - json.commandData = packet.slice(15); - } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.receiveOptions = packet[11]; - json.data = ""; - var raw_data = packet.slice(12); - for(i in raw_data) { - json.data += String.fromCharCode(raw_data[i]) + this.payload.splice(0,1); // Read 0x00 away + } + return this; +} + +PacketParser.prototype.collectPayload = function(name) { + this.write[name] = this.payload.splice(0); + return this; +} + +PacketParser.prototype.knownFrames = { + 0x95: { + type: "NODE_IDENTIFICATION", + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); } - } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.receiveOptions = packet[11]; - json.numSamples = packet[12]; // apparently always set to 1 - json.digitalChannelMask = packet.slice(13,15); - json.analogChannelMask = packet[15]; - // Bit more work to do on an I/O data sample. - // First check s.digitalChannelMask - are there any digital samples? - if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { - // digital channel mask indicates that digital samples are present, so they - // are in the bytes 16 and 17. - json.digitalSamples = packet.slice(16,18); - // Now check whether any analog samples are present - if (json.analogChannelMask > 0) { - json.analogSamples = packet.slice(18); - } - } else { - // no digital samples. There might still be analog samples... - if (json.analogChannelMask > 0) { - json.analogSamples = packet.slice(16); + }, + 0x88: { + type: "AT_RESPONSE", + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + if (parser.json.command == 'ND') { + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') } } - - // translate digital samples into JS for easier handling - json.samples = {} - if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // if digital samples present, - // run through the first bitmask for digital pins, i.e. digiPinsByte1 - for (x in digiPinsByte1) { - // On first iteration, for example, x = 'D10', digiPinsByte1[x] = 4. - // OK. So, is there a sample for this pin? Check the digital channel mask. - if (json.digitalChannelMask[0] & digiPinsByte1[x]) { - // There is a sample for this pin. So, AND the sample byte and the bitmask, - // and turn the result into a boolean. - // On the first iteration, for example, this sets s['D10'] = 1 - // if the bitwise AND of the first byte of the digital sample with 4 is > 0 - json.samples[x] = ((json.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; - } - } - // do the same thing for the second load of digital inputs - for (x in digiPinsByte2) { - if (json.digitalChannelMask[1] & digiPinsByte2[x]) { - json.samples[x] = ((json.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; - } - } + }, + 0x97: { + type: "REMOTE_AT_RESPONSE", + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); } - - // Also translate analog samples into JS - // The analog channel mask indicates which pins are enabled as analog channels. - if (json.analogChannelMask > 0) { - var sampleIndex = 0; - for (x in analogPins) { - // on first iteration, for example, x = 'A0', analogPins[x] = 1 - if (json.analogChannelMask & analogPins[x]) { - json.samples[x] = 256*json.analogSamples[sampleIndex*2]+json.analogSamples[1+sampleIndex*2]; - sampleIndex += 1; - } - } + }, + 0x90: { + type: "RECEIVE_RF_DATA", + parse: function(parser) { + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readByte('receiveOptions') + .collectPayload('rawData'); + } + }, + 0x92: { + type: "DATA_SAMPLE_RX", + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .readByte('numSamples') + .readByte('digitalChanelMask', 2) + .readByte('analogChannelMask') + if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) + parser.readByte('digitalSamples',2); + if (parser.json.analogChannelMask > 0) + parser.collectPayload('analogSamples'); + // skip formatting data for now } - } else { - json.payload = packet.slice(1); } - - return json; } diff --git a/xbee.js b/xbee.js index abf95ed..7710811 100644 --- a/xbee.js +++ b/xbee.js @@ -10,19 +10,20 @@ function XBee(port) { this.nodes = {}; this.serial = new serialport.SerialPort(port, { - parser: api.packetParser() + parser: api.packetBuilder() }); var self = this; - this._onNodeDiscovery = function(node) { - // RemoveAllListeners??ß + this._onNodeDiscovery = function(data) { + var node = data.node; if (self.nodes[node.remote64.hex]) { + // RemoveAllListeners??ß self.nodes[node.remote64.hex].removeAllListeners(); } else { self.nodes[node.remote64.hex] = new Node(self, node); } - this.emit("node", self.nodes[node.remote64.hex]); + self.emit("node", self.nodes[node.remote64.hex]); } // On AT Response @@ -35,8 +36,8 @@ function XBee(port) { } } + // Remove this bullcrap, filter by frameid instead! this._onATResponse_FilterND = function(res) { - if (res.command == "ND") self._onNodeDiscovery(res.node); } this._onMessage = function(data) { @@ -62,7 +63,7 @@ XBee.prototype.configure = function() { f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { self._ATCB(command, function(data) { - cb(!data.commandStatus, f(data.commandData)); + cb(!(data.commandStatus==0x00), f(data.commandData)); }); } } @@ -79,16 +80,18 @@ XBee.prototype.configure = function() { self.config = results; self.emit("configured", self.config); self.discover(function() { - console.log("Discovery Over"); + // Discovery Over }); }); } XBee.prototype.discover = function(cb) { - this._AT('ND'); - this.serial.on("AT_RESPONSE", this._onATResponse_FilterND); + var frameId = this._AT('ND'); + var self = this; + self.serial.on("AT_RESPONSE_"+frameId, self._onNodeDiscovery); setTimeout(function() { cb(); + self.serial.removeAllListeners("AT_RESPONSE_"+frameId); }, this.config.nodeDiscoveryTime); } @@ -116,12 +119,8 @@ XBee.prototype._ATCB = function(cmd, val, cb) { cb = val; val = undefined; } - this._AT(cmd, val); - this.serial.on("AT_RESPONSE", function(res) { - if (res.command == cmd) { - cb(res); - } - }); + var frameId = this._AT(cmd, val); + this.serial.once("AT_RESPONSE_"+frameId, cb); } XBee.prototype._AT = function(cmd, val) { @@ -129,6 +128,7 @@ XBee.prototype._AT = function(cmd, val) { frame.setCommand(cmd); frame.commandParameter = val; this.serial.write(frame.getBytes()); + return frame.frameId; } XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { @@ -143,6 +143,7 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { frame.destination16 = remote16.dec; } this.serial.write(frame.getBytes()); + return frame.frameId; } exports.XBee = XBee; From c2bb60c34d1af2fbd4d1f8f01aea6da415776890 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 20 Mar 2012 16:22:35 +0100 Subject: [PATCH 034/140] clean(er) rewrite --- xbee-api.js | 365 +++++++++++++++++++++++----------------------------- xbee.js | 31 ++--- 2 files changed, 177 insertions(+), 219 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index a21ced0..c173d84 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,7 +1,5 @@ var Buffer = require('buffer').Buffer; -var sys = require('util'); - -// TODO: CHAIN PARSING +var util = require('util'); exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); @@ -44,36 +42,6 @@ function incrementFrameId() { exports.START_BYTE = 0x7e; // start of every XBee packet -exports.frameTypes = { - 0x92 : { - name: "DATA_SAMPLE_RX", - }, - 0x08 : { - name: "AT_COMMAND", - }, - 0x88 : { - name: "AT_RESPONSE", - }, - 0x17 : { - name: "REMOTE_AT_COMMAND", - }, - 0x97 : { - name: "REMOTE_AT_RESPONSE", - }, - 0x10 : { - name: "TRANSMIT_RF_DATA", - }, - 0x8b : { - name: "TRANSMIT_ACKNOWLEDGED", - }, - 0x90 : { - name: "RECEIVE_RF_DATA", - }, - 0x95 : { - name: "NODE_IDENTIFICATION", - }, -} -// Frame Types exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) exports.FT_AT_RESPONSE = 0x88; // AT response (local) @@ -82,7 +50,6 @@ exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received - exports.FT_NODE_IDENTIFICATION = 0x95; // Bitmasks for I/O pins @@ -102,6 +69,7 @@ var digiPinsByte2 = { D6: 64, D7: 128 }; + var analogPins = { A0: 1, A1: 2, @@ -161,15 +129,11 @@ Packet.prototype.getPayload = function() { exports.Packet = Packet; -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~ OUTGOING XBEE PACKETS ~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // ATCommand is for setting/reading AT registers on the local XBee node. var ATCommand = function() { this.frameId = incrementFrameId(); }; -sys.inherits(ATCommand, Packet); +util.inherits(ATCommand, Packet); ATCommand.prototype.setCommand = function(strCmd) { // take the ascii command and save it internally as byte values command0 and command1 @@ -206,7 +170,7 @@ var RemoteATCommand = function() { this.frameId = incrementFrameId(); this.remoteCommandOptions = 0x02; // set default command options on creation }; -sys.inherits(RemoteATCommand, ATCommand); +util.inherits(RemoteATCommand, ATCommand); RemoteATCommand.prototype.getPayload = function() { // Returns a JS array of byte values @@ -252,7 +216,7 @@ var TransmitRFData = function() { this.options = 0x00; // see digi docs for more info } -sys.inherits(TransmitRFData, Packet); +util.inherits(TransmitRFData, Packet); TransmitRFData.prototype.getPayload = function() { // Returns a JS array of byte values @@ -290,196 +254,189 @@ TransmitRFData.prototype.getPayload = function() { exports.TransmitRFData = TransmitRFData; - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~ INCOMING XBEE PACKETS ~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -exports.packetParser = function () { - // A function which can be used with the 'serialport' npm package - // and a XBee radio in API mode. - // It builds a JS array of integers as data is received, and when the - // array represents a complete XBee packet, it emits it as a 'data' event, - // passing a JS object (as translated by packetToJS) instead of a load of numbers. - - // incoming data buffer saved in closure as a JS array of integers called 'packet' - var packet = []; +// Builds Packets out of data received from Serial Port. +exports.packetBuilder = function () { + var packet = []; // incoming data buffer saved in closure as a JS array of integers called 'packet' var packpos = 999; // this variable is used to remember at which position we are up to within the overall packet - var packlen = 0; // used to remember the length of the current packet. XBee API packets have two length bytes immediately after the start byte + var packlen = 0; // used to remember the length of the current packet. return function (emitter, buffer) { - // Collecting data. 'buffer' needs to be run through - it contains bytes received from the serial port - // which may or may not represent an entire XBee packet. - + // Collecting data. for(var i=0; i < buffer.length; i++) { - b = buffer[i]; // store the working byte + b = buffer[i]; // store the working byte packpos += 1; + // Detected start of packet. if (b == exports.START_BYTE) { - // Detected start of packet. - // exports.START_BYTE = 126, the start of a zigbee packet i.e. 0x7e packpos = 0; - packlen = 0; // length of packet is unknown, as yet. - packet = []; // store the bytes as they come in. Don't keep start byte or length bytes - } - if (packpos == 1) { - // most significant bit of the length - packlen += b<<8; - } - if (packpos == 2) { - // least significant bit of the length - packlen += b; + packlen = 0; + packet = []; } - // for all other bytes, collect them onto the end of our growing 'packet' array + if (packpos == 1) packlen += b << 8; // most significant bit of the length + if (packpos == 2) packlen += b; // least significant bit of the length + if ((packlen > 0) && (packpos > 2) && (packet.length < packlen)) { packet.push(b); } - // emit the packet when it's fully built. packlen + 3 = position of final byte + // Packet is complete. Parse & Emit if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // translate the packet into a JS object before emitting it - var json = packetToJS(packet); - console.log("F: "+json.ft); - emitter.emit(json.ft, json); - } - - // there will still be a checksum byte. Currently this is ignored - if ((packlen > 0) && (packet.length == packlen) && (packpos > packlen + 3)) { - // ignore checksum for now + // There will still be a checksum byte. Currently this is ignored + var parser = new PacketParser(packet) + var json = parser.parse(); + //console.log("P: "+util.inspect(json)); + var event = json.type; + if (json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + event += "_"+json.frameId; + } + console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); + emitter.emit(event, json); } } }; } -function packetToJS(packet) { - // given an array of byte values, return a JS object representing the packet - // the array of bytes excludes the start bit and the length bits (these are not collected by the serial parser funciton) +// Packet Parser Class. Used to parse packets if they are known +var PacketParser = function(p) { + this.json = { + ft: p.splice(0,1)[0], + } - // So, the first byte in the packet is the frame type identifier. - var json = { - ft: exports.frameTypes.hasOwnProperty(packet[0]) ? exports.frameTypes[packet[0]].name : packet[0] - }; + // Used as pointer to the object data is parsed into + this.write = this.json; + this.payload = p; +} - if (packet[0] == exports.FT_NODE_IDENTIFICATION) { - json.sender64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.sender16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.recieveOptions = packet[11]; - json.remote16 = {dec: packet.slice(12,14), hex: exports.bArr2HexStr(packet.slice(12,14))}; - json.remote64 = {dec: packet.slice(14,22), hex: exports.bArr2HexStr(packet.slice(14,22))}; - json.id = ""; - var ni_length = 0; - while (packet[22+ni_length] != 0x00) { - json.id += String.fromCharCode(packet[22+ni_length]); - ni_length += 1; - } - var offset = 22+ni_length+1; - json.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; - json.deviceType = packet[offset+2]; - json.sourceEvent = packet[offset+3]; - // skip digi application profile & manufacturer id - json.payload = packet.splice(offset); - } else if (packet[0] == exports.FT_AT_RESPONSE) { - json.frameId = packet[1]; - json.command = String.fromCharCode(packet[2]) + String.fromCharCode(packet[3]); - json.commandStatus = (packet[4] == 0) ? 'OK' : packet[4]; - if (json.command == 'ND') { - json.node = {}; - json.node.remote16 = {dec: packet.slice(5,7), hex: exports.bArr2HexStr(packet.slice(5,7))}; - json.node.remote64 = {dec: packet.slice(7,15), hex: exports.bArr2HexStr(packet.slice(7,15))}; - json.node.id = ""; - var ni_length = 0; - while (packet[15+ni_length] != 0x00) { - json.node.id += String.fromCharCode(packet[15+ni_length]); - ni_length += 1; - } - var offset = 15+ni_length+1; - json.node.remoteParent16 = {dec: packet.slice(offset,offset+2), hex: exports.bArr2HexStr(packet.slice(offset,offset+2))}; - json.node.deviceType = packet[offset+2]; - json.node.sourceEvent = packet[offset+3]; - json.status = packet[offset+4]; - // skip status, digi application profile & manufacturer id - } else { - json.commandData = packet.slice(5); +PacketParser.prototype.parse = function() { + if (this.knownFrames[this.json.ft]) { + this.json.type = this.knownFrames[this.json.ft].type; + this.knownFrames[this.json.ft].parse(this); + } else { + this.json.type = "UNKNOWN"; + } + return this.json; +} + +PacketParser.prototype.readAddr = function(name, length) { + var dec = this.payload.splice(0, length); + this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } + return this; +} + +PacketParser.prototype.readByte = function(name, length) { + if (typeof length === 'number') + this.write[name] = this.payload.splice(0,length); + else this.write[name] = this.payload.splice(0,1)[0]; + return this; +} + +PacketParser.prototype.readAddr64 = function(name) { + return this.readAddr(name, 8); +} + +PacketParser.prototype.readAddr16 = function(name) { + return this.readAddr(name, 2); +} + +PacketParser.prototype.readString = function(name, length) { + this.write[name] = ""; + if (typeof length === 'number') { + for (var i = 0; i < length; i++) + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } else { + while(this.payload[0] != 0x00) { + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); } - } else if (packet[0] == exports.FT_REMOTE_AT_RESPONSE) { - json.frameId = packet[1]; - json.remote64 = {dec: packet.slice(2,10), hex: exports.bArr2HexStr(packet.slice(2,10))}; - json.remote16 = {dec: packet.slice(10,12), hex: exports.bArr2HexStr(packet.slice(10,12))}; - json.command = String.fromCharCode(packet[12]) + String.fromCharCode(packet[13]); - json.commandStatus = (packet[14] == 0) ? 'OK' : packet[14]; - json.commandData = packet.slice(15); - } else if (packet[0] == exports.FT_RECEIVE_RF_DATA) { - json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.receiveOptions = packet[11]; - json.data = ""; - var raw_data = packet.slice(12); - for(i in raw_data) { - json.data += String.fromCharCode(raw_data[i]) + this.payload.splice(0,1); // Read 0x00 away + } + return this; +} + +PacketParser.prototype.collectPayload = function(name) { + this.write[name] = this.payload.splice(0); + return this; +} + +PacketParser.prototype.knownFrames = { + 0x95: { + type: "NODE_IDENTIFICATION", + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); } - } else if (packet[0] == exports.FT_DATA_SAMPLE_RX) { - json.remote64 = {dec: packet.slice(1,9), hex: exports.bArr2HexStr(packet.slice(1,9))}; - json.remote16 = {dec: packet.slice(9,11), hex: exports.bArr2HexStr(packet.slice(9,11))}; - json.receiveOptions = packet[11]; - json.numSamples = packet[12]; // apparently always set to 1 - json.digitalChannelMask = packet.slice(13,15); - json.analogChannelMask = packet[15]; - // Bit more work to do on an I/O data sample. - // First check s.digitalChannelMask - are there any digital samples? - if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { - // digital channel mask indicates that digital samples are present, so they - // are in the bytes 16 and 17. - json.digitalSamples = packet.slice(16,18); - // Now check whether any analog samples are present - if (json.analogChannelMask > 0) { - json.analogSamples = packet.slice(18); - } - } else { - // no digital samples. There might still be analog samples... - if (json.analogChannelMask > 0) { - json.analogSamples = packet.slice(16); + }, + 0x88: { + type: "AT_RESPONSE", + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + if (parser.json.command == 'ND') { + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') } } - - // translate digital samples into JS for easier handling - json.samples = {} - if (json.digitalChannelMask[0] + json.digitalChannelMask[1] > 0) { // if digital samples present, - // run through the first bitmask for digital pins, i.e. digiPinsByte1 - for (x in digiPinsByte1) { - // On first iteration, for example, x = 'D10', digiPinsByte1[x] = 4. - // OK. So, is there a sample for this pin? Check the digital channel mask. - if (json.digitalChannelMask[0] & digiPinsByte1[x]) { - // There is a sample for this pin. So, AND the sample byte and the bitmask, - // and turn the result into a boolean. - // On the first iteration, for example, this sets s['D10'] = 1 - // if the bitwise AND of the first byte of the digital sample with 4 is > 0 - json.samples[x] = ((json.digitalSamples[0] & digiPinsByte1[x]) > 0) ? 1 : 0; - } - } - // do the same thing for the second load of digital inputs - for (x in digiPinsByte2) { - if (json.digitalChannelMask[1] & digiPinsByte2[x]) { - json.samples[x] = ((json.digitalSamples[1] & digiPinsByte2[x]) > 0) ? 1 : 0; - } - } + }, + 0x97: { + type: "REMOTE_AT_RESPONSE", + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); } - - // Also translate analog samples into JS - // The analog channel mask indicates which pins are enabled as analog channels. - if (json.analogChannelMask > 0) { - var sampleIndex = 0; - for (x in analogPins) { - // on first iteration, for example, x = 'A0', analogPins[x] = 1 - if (json.analogChannelMask & analogPins[x]) { - json.samples[x] = 256*json.analogSamples[sampleIndex*2]+json.analogSamples[1+sampleIndex*2]; - sampleIndex += 1; - } - } + }, + 0x90: { + type: "RECEIVE_RF_DATA", + parse: function(parser) { + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readByte('receiveOptions') + .collectPayload('rawData'); + } + }, + 0x92: { + type: "DATA_SAMPLE_RX", + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .readByte('numSamples') + .readByte('digitalChanelMask', 2) + .readByte('analogChannelMask') + if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) + parser.readByte('digitalSamples',2); + if (parser.json.analogChannelMask > 0) + parser.collectPayload('analogSamples'); + // skip formatting data for now } - } else { - json.payload = packet.slice(1); } - - return json; } diff --git a/xbee.js b/xbee.js index abf95ed..7710811 100644 --- a/xbee.js +++ b/xbee.js @@ -10,19 +10,20 @@ function XBee(port) { this.nodes = {}; this.serial = new serialport.SerialPort(port, { - parser: api.packetParser() + parser: api.packetBuilder() }); var self = this; - this._onNodeDiscovery = function(node) { - // RemoveAllListeners??ß + this._onNodeDiscovery = function(data) { + var node = data.node; if (self.nodes[node.remote64.hex]) { + // RemoveAllListeners??ß self.nodes[node.remote64.hex].removeAllListeners(); } else { self.nodes[node.remote64.hex] = new Node(self, node); } - this.emit("node", self.nodes[node.remote64.hex]); + self.emit("node", self.nodes[node.remote64.hex]); } // On AT Response @@ -35,8 +36,8 @@ function XBee(port) { } } + // Remove this bullcrap, filter by frameid instead! this._onATResponse_FilterND = function(res) { - if (res.command == "ND") self._onNodeDiscovery(res.node); } this._onMessage = function(data) { @@ -62,7 +63,7 @@ XBee.prototype.configure = function() { f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { self._ATCB(command, function(data) { - cb(!data.commandStatus, f(data.commandData)); + cb(!(data.commandStatus==0x00), f(data.commandData)); }); } } @@ -79,16 +80,18 @@ XBee.prototype.configure = function() { self.config = results; self.emit("configured", self.config); self.discover(function() { - console.log("Discovery Over"); + // Discovery Over }); }); } XBee.prototype.discover = function(cb) { - this._AT('ND'); - this.serial.on("AT_RESPONSE", this._onATResponse_FilterND); + var frameId = this._AT('ND'); + var self = this; + self.serial.on("AT_RESPONSE_"+frameId, self._onNodeDiscovery); setTimeout(function() { cb(); + self.serial.removeAllListeners("AT_RESPONSE_"+frameId); }, this.config.nodeDiscoveryTime); } @@ -116,12 +119,8 @@ XBee.prototype._ATCB = function(cmd, val, cb) { cb = val; val = undefined; } - this._AT(cmd, val); - this.serial.on("AT_RESPONSE", function(res) { - if (res.command == cmd) { - cb(res); - } - }); + var frameId = this._AT(cmd, val); + this.serial.once("AT_RESPONSE_"+frameId, cb); } XBee.prototype._AT = function(cmd, val) { @@ -129,6 +128,7 @@ XBee.prototype._AT = function(cmd, val) { frame.setCommand(cmd); frame.commandParameter = val; this.serial.write(frame.getBytes()); + return frame.frameId; } XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { @@ -143,6 +143,7 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { frame.destination16 = remote16.dec; } this.serial.write(frame.getBytes()); + return frame.frameId; } exports.XBee = XBee; From 41ff191a8216f198c59185a9c3e530dd6c42a644 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:30:54 +0200 Subject: [PATCH 035/140] Preparing first npm release --- example.js => examples/simple.js | 4 +++- package.json | 6 +++--- xbee.js | 19 +++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) rename example.js => examples/simple.js (81%) diff --git a/example.js b/examples/simple.js similarity index 81% rename from example.js rename to examples/simple.js index 841ea4f..a8f01c2 100644 --- a/example.js +++ b/examples/simple.js @@ -1,5 +1,7 @@ var util = require('util'); -var XBee = require('./xbee').XBee; +var XBee = require('xbee-svd').XBee; + +// Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); xbee.on("configured", function(config) { diff --git a/package.json b/package.json index 22dff87..0f27871 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", - "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], + "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport" : "0.7.x" + "serialport" : "0.7.x", "async" : "0.1.x" - } + }, "repository": { "type" : "git", "url" : "git://github.com/jouz/node-xbee-svd.git" diff --git a/xbee.js b/xbee.js index 7710811..3c42825 100644 --- a/xbee.js +++ b/xbee.js @@ -6,9 +6,11 @@ var async = require('async'); function XBee(port) { EventEmitter.call(this); - + + // Current nodes this.nodes = {}; + // Serial connection to the XBee this.serial = new serialport.SerialPort(port, { parser: api.packetBuilder() }); @@ -59,7 +61,11 @@ util.inherits(XBee, EventEmitter); XBee.prototype.configure = function() { var self = this; - var QF = function(command, f) { + + // Returns a function that initiates an AT command to + // query a configuration parameter's value. + // To be passed to an async.parallel. + var QF = function(command, f) { // Format the result using f f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { self._ATCB(command, function(data) { @@ -76,7 +82,8 @@ XBee.prototype.configure = function() { nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) }; - async.series(config, function(err, results) { + // Using async to start discovery only when all parameters have been read. + async.parallel(config, function(err, results) { self.config = results; self.emit("configured", self.config); self.discover(function() { @@ -85,10 +92,14 @@ XBee.prototype.configure = function() { }); } +// Run network discovery. Associated nodes can report in +// for config.nodeDiscoveryTime ms. XBee.prototype.discover = function(cb) { var frameId = this._AT('ND'); var self = this; + // Whenever a node reports in, treat him as rejoined. self.serial.on("AT_RESPONSE_"+frameId, self._onNodeDiscovery); + // Wait for nodeDiscoveryTime ms before calling back setTimeout(function() { cb(); self.serial.removeAllListeners("AT_RESPONSE_"+frameId); @@ -156,6 +167,7 @@ function Node(xbee, params) { this.remote64 = params.remote64; } +util.inherits(Node, EventEmitter); Node.prototype.send = function(data) { this.xbee._send(data, this.remote64, this.remote16); @@ -169,4 +181,3 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } -util.inherits(Node, EventEmitter); From d9f98d2a69c43e3d76ea6e80b1ded66ea51c29e0 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:30:54 +0200 Subject: [PATCH 036/140] Preparing first npm release --- example.js => examples/simple.js | 4 +++- package.json | 6 +++--- xbee.js | 19 +++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) rename example.js => examples/simple.js (81%) diff --git a/example.js b/examples/simple.js similarity index 81% rename from example.js rename to examples/simple.js index 841ea4f..a8f01c2 100644 --- a/example.js +++ b/examples/simple.js @@ -1,5 +1,7 @@ var util = require('util'); -var XBee = require('./xbee').XBee; +var XBee = require('xbee-svd').XBee; + +// Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); xbee.on("configured", function(config) { diff --git a/package.json b/package.json index 22dff87..0f27871 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", - "keywords": ["xbee", "serialport", "robots", "sensors", "automation", "control"], + "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport" : "0.7.x" + "serialport" : "0.7.x", "async" : "0.1.x" - } + }, "repository": { "type" : "git", "url" : "git://github.com/jouz/node-xbee-svd.git" diff --git a/xbee.js b/xbee.js index 7710811..3c42825 100644 --- a/xbee.js +++ b/xbee.js @@ -6,9 +6,11 @@ var async = require('async'); function XBee(port) { EventEmitter.call(this); - + + // Current nodes this.nodes = {}; + // Serial connection to the XBee this.serial = new serialport.SerialPort(port, { parser: api.packetBuilder() }); @@ -59,7 +61,11 @@ util.inherits(XBee, EventEmitter); XBee.prototype.configure = function() { var self = this; - var QF = function(command, f) { + + // Returns a function that initiates an AT command to + // query a configuration parameter's value. + // To be passed to an async.parallel. + var QF = function(command, f) { // Format the result using f f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { self._ATCB(command, function(data) { @@ -76,7 +82,8 @@ XBee.prototype.configure = function() { nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) }; - async.series(config, function(err, results) { + // Using async to start discovery only when all parameters have been read. + async.parallel(config, function(err, results) { self.config = results; self.emit("configured", self.config); self.discover(function() { @@ -85,10 +92,14 @@ XBee.prototype.configure = function() { }); } +// Run network discovery. Associated nodes can report in +// for config.nodeDiscoveryTime ms. XBee.prototype.discover = function(cb) { var frameId = this._AT('ND'); var self = this; + // Whenever a node reports in, treat him as rejoined. self.serial.on("AT_RESPONSE_"+frameId, self._onNodeDiscovery); + // Wait for nodeDiscoveryTime ms before calling back setTimeout(function() { cb(); self.serial.removeAllListeners("AT_RESPONSE_"+frameId); @@ -156,6 +167,7 @@ function Node(xbee, params) { this.remote64 = params.remote64; } +util.inherits(Node, EventEmitter); Node.prototype.send = function(data) { this.xbee._send(data, this.remote64, this.remote16); @@ -169,4 +181,3 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } -util.inherits(Node, EventEmitter); From bddf072289c90ee499457c4a2c965c1e626aaad4 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:39:31 +0200 Subject: [PATCH 037/140] updated readme --- README.markdown | 81 ++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/README.markdown b/README.markdown index e3b42d9..01761a0 100644 --- a/README.markdown +++ b/README.markdown @@ -1,10 +1,34 @@ A more high level fork of Richard Morrison's node-xbee. -Code Example Follows +Example +======= + +```javascript +var util = require('util'); +var XBee = require('xbee-svd').XBee; + +// Replace with your xbee's UART location +var xbee = new XBee('/dev/ttyO1'); + +xbee.on("configured", function(config) { + console.log("XBee Config: %s", util.inspect(config)); +}); + +xbee.on("node", function(node) { + console.log("Node %s connected", node.id); + + node.on("data", function(data) { + console.log("%s: %s", node.id, util.inspect(data)); + }); + +}); +``` Background ========== +Note that this readme is still mostly copied from the original module! + [Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks. They can be connected to a computer over RS232 and communicated on using a standard serial port. @@ -25,60 +49,13 @@ My remote xbee network modules send periodic measurements and I can push them to I can also use this library to send remote commands and query remote xbee modules. For instance, setting a digital output on a remote module could turn a light on, or a motor, or a laser beam - up to you! -How To Use -========== - -Like node-serialport, using this is "pretty easy because it is pretty basic. It provides you with the building block to make great things, it is not a complete solution - just a cog in the (world domination) machine." - -To Install ----------- - -You'll need serialport as well (this module doesn't depend on it, but it provides a parser so this is the intended use pattern) - - npm install serialport - npm install xbee - -To Use ------- - -Open a serial port and give the xbee parser as an option: - - var serial_xbee = new SerialPort("/dev/ttyUSB0", { - parser: xbee.packetParser() - }); - -Then listen for incoming xbee packets like this: - - serial_xbee.on("data", function(data) { - console.log('xbee data received:', data.type); - }); - -(the __data__ object passed has lot more packet-type-dependent properties) - -Send remote AT commands (e.g. query remote module, or "release the hounds"): - - // execute an AT command on a remote xbee module - function RemoteAT(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); - } - - // simple example: query ATD0 on remote xbee module. - var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module - var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - - RemoteAT('D0', null, remote64, remote16); +Installation +============ -See __example.js__ for a full working example (you'll need to use your own xbee IDs, though). + npm install xbee-svd Licence -------- +======= This work is based on the works of Richard Morrison Creative Commons License
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com. From 4ce69bf3eefc9386c58892c8d471363d13527edb Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:39:31 +0200 Subject: [PATCH 038/140] updated readme --- README.markdown | 81 ++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/README.markdown b/README.markdown index e3b42d9..01761a0 100644 --- a/README.markdown +++ b/README.markdown @@ -1,10 +1,34 @@ A more high level fork of Richard Morrison's node-xbee. -Code Example Follows +Example +======= + +```javascript +var util = require('util'); +var XBee = require('xbee-svd').XBee; + +// Replace with your xbee's UART location +var xbee = new XBee('/dev/ttyO1'); + +xbee.on("configured", function(config) { + console.log("XBee Config: %s", util.inspect(config)); +}); + +xbee.on("node", function(node) { + console.log("Node %s connected", node.id); + + node.on("data", function(data) { + console.log("%s: %s", node.id, util.inspect(data)); + }); + +}); +``` Background ========== +Note that this readme is still mostly copied from the original module! + [Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks. They can be connected to a computer over RS232 and communicated on using a standard serial port. @@ -25,60 +49,13 @@ My remote xbee network modules send periodic measurements and I can push them to I can also use this library to send remote commands and query remote xbee modules. For instance, setting a digital output on a remote module could turn a light on, or a motor, or a laser beam - up to you! -How To Use -========== - -Like node-serialport, using this is "pretty easy because it is pretty basic. It provides you with the building block to make great things, it is not a complete solution - just a cog in the (world domination) machine." - -To Install ----------- - -You'll need serialport as well (this module doesn't depend on it, but it provides a parser so this is the intended use pattern) - - npm install serialport - npm install xbee - -To Use ------- - -Open a serial port and give the xbee parser as an option: - - var serial_xbee = new SerialPort("/dev/ttyUSB0", { - parser: xbee.packetParser() - }); - -Then listen for incoming xbee packets like this: - - serial_xbee.on("data", function(data) { - console.log('xbee data received:', data.type); - }); - -(the __data__ object passed has lot more packet-type-dependent properties) - -Send remote AT commands (e.g. query remote module, or "release the hounds"): - - // execute an AT command on a remote xbee module - function RemoteAT(cmd, val, remote64, remote16) { - var atc = new xbee.RemoteATCommand(); - atc.setCommand(cmd); - atc.commandParameter = val; - atc.destination64 = remote64; - atc.destination16 = remote16; - b = atc.getBytes(); - serial_xbee.write(b); - //console.log('Wrote bytes to serial port', b); - } - - // simple example: query ATD0 on remote xbee module. - var remote64 = [0x00,0x13,0xa2,0x00,0x40,0x7a,0x1f,0x95]; // <-- you'll need to replace this with the 64-bit hex address of your module - var remote16 = [0xff,0xfe]; // <-- put the 16 bit address of remote module here, if known. Otherwise use [0xff, 0xfe] - - RemoteAT('D0', null, remote64, remote16); +Installation +============ -See __example.js__ for a full working example (you'll need to use your own xbee IDs, though). + npm install xbee-svd Licence -------- +======= This work is based on the works of Richard Morrison Creative Commons License
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com. From 50a70674121aa86a86f55454df45e8661d57e25b Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:41:45 +0200 Subject: [PATCH 039/140] package updates --- .gitignore | 1 + .npmignore | 1 - package.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1bd7226..140eb6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules *.swp +.npmignore diff --git a/.npmignore b/.npmignore index a6cbb3c..e69de29 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +0,0 @@ -example.js diff --git a/package.json b/package.json index 0f27871..4fee4cb 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.1", + "version" : "0.0.2", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From c33b4781195d66f0a3c4bfee1b0ad157e0ddadde Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:41:45 +0200 Subject: [PATCH 040/140] package updates --- .gitignore | 1 + .npmignore | 1 - package.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1bd7226..140eb6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules *.swp +.npmignore diff --git a/.npmignore b/.npmignore index a6cbb3c..e69de29 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +0,0 @@ -example.js diff --git a/package.json b/package.json index 0f27871..4fee4cb 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.1", + "version" : "0.0.2", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From 1a0d9e63cfadb99b13f95dd4b9901af8faab00b5 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:42:23 +0200 Subject: [PATCH 041/140] package updates --- .npmignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .npmignore diff --git a/.npmignore b/.npmignore deleted file mode 100644 index e69de29..0000000 From 6fac9c47488d01c5b2dd2d794a1c1549e2132ea6 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 13:42:23 +0200 Subject: [PATCH 042/140] package updates --- .npmignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .npmignore diff --git a/.npmignore b/.npmignore deleted file mode 100644 index e69de29..0000000 From 449f0af8aa2fbbe28dc2de63b003a587de7266fd Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 15:07:02 +0200 Subject: [PATCH 043/140] communication with nodes now works properly --- package.json | 2 +- xbee-api.js | 35 +++++++++++++++++++++++++++++++++-- xbee.js | 26 +++++++++++++++++++++----- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4fee4cb..51018e8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.2", + "version" : "0.0.3", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", diff --git a/xbee-api.js b/xbee-api.js index c173d84..0533541 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -45,6 +45,7 @@ exports.START_BYTE = 0x7e; // start of every XBee packet exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) exports.FT_AT_RESPONSE = 0x88; // AT response (local) +exports.FT_TX_TRANSMIT_STATUS = 0x8b; // Status response of transmission exports.FT_REMOTE_AT_COMMAND = 0x17; // AT command (to remote radio) exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data @@ -52,6 +53,25 @@ exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received exports.FT_NODE_IDENTIFICATION = 0x95; +exports.DELIVERY_STATES = { + 0x00: "Success", + 0x02: "CCA Failure", + 0x15: "Invalid destination endpoint", + 0x21: "Network ACK Failure", + 0x22: "Not Joined to Network", + 0x23: "Self-addressed", + 0x24: "Address Not Found", + 0x25: "Route Not Found", + 0x74: "Data payload too large" +}; + +exports.DISCOVERY_STATES = { + 0x00: "No Discovery Overhead", + 0x01: "Address Discovery", + 0x02: "Route Discovery", + 0x03: "Address and Route Discovery" +}; + // Bitmasks for I/O pins var digiPinsByte1 = { D10: 4, @@ -287,7 +307,7 @@ exports.packetBuilder = function () { var json = parser.parse(); //console.log("P: "+util.inspect(json)); var event = json.type; - if (json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { event += "_"+json.frameId; } console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); @@ -377,6 +397,17 @@ PacketParser.prototype.knownFrames = { .readByte('sourceEvent'); } }, + 0x8b: { + type: "TX_TRANSMIT_STATUS", + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } + }, 0x88: { type: "AT_RESPONSE", parse: function(parser) { @@ -416,8 +447,8 @@ PacketParser.prototype.knownFrames = { type: "RECEIVE_RF_DATA", parse: function(parser) { parser - .readAddr16('remote16') .readAddr64('remote64') + .readAddr16('remote16') .readByte('receiveOptions') .collectPayload('rawData'); } diff --git a/xbee.js b/xbee.js index 3c42825..394bf0b 100644 --- a/xbee.js +++ b/xbee.js @@ -31,8 +31,8 @@ function XBee(port) { // On AT Response this._onRemoteATResponse = function(res) { // On Node Discovery Packet, emit new Node - if (self.nodes[res.remote64.dec]) { - self.nodes[res.remote64.dec]._onRemoteATResponse(res); + if (self.nodes[res.remote64.hex]) { + self.nodes[res.remote64.hex]._onRemoteATResponse(res); } else { console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); } @@ -112,7 +112,7 @@ XBee.prototype.broadcast = function(data) { this._send(data, remote64, remote16); } -XBee._send = function(data, remote64, remote16) { +XBee.prototype._send = function(data, remote64, remote16) { var frame = new api.TransmitRFData(); if (typeof remote64.dec === 'undefined') { frame.destination64 = remote64; @@ -123,6 +123,7 @@ XBee._send = function(data, remote64, remote16) { } frame.RFData = data; this.serial.write(frame.getBytes()); + return frame.frameId; } XBee.prototype._ATCB = function(cmd, val, cb) { @@ -169,8 +170,23 @@ function Node(xbee, params) { util.inherits(Node, EventEmitter); -Node.prototype.send = function(data) { - this.xbee._send(data, this.remote64, this.remote16); +Node.prototype.send = function(data, cb) { + var frameId = this.xbee._send(data, this.remote64, this.remote16); + if (typeof cb === 'function') { + this.xbee.serial.once("TX_TRANSMIT_STATUS_"+frameId, function(data) { + var error = false; + if (data.deliveryStatus != 0x00) { + error = data; + error.msg = api.DELIVERY_STATES[data.deliveryStatus]; + } + cb(error); + }); + } +} + +Node.prototype._onData = function(data) { + // Send the whole data object, or just the parsed msg? + this.emit('data', api.bArr2Str(data.rawData)); } Node.prototype._AT = function(cmd, val) { From 2972984619cbe00f0b5d45ca26b5bcf134584144 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 15:07:02 +0200 Subject: [PATCH 044/140] communication with nodes now works properly --- package.json | 2 +- xbee-api.js | 35 +++++++++++++++++++++++++++++++++-- xbee.js | 26 +++++++++++++++++++++----- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4fee4cb..51018e8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.2", + "version" : "0.0.3", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", diff --git a/xbee-api.js b/xbee-api.js index c173d84..0533541 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -45,6 +45,7 @@ exports.START_BYTE = 0x7e; // start of every XBee packet exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received exports.FT_AT_COMMAND = 0x08; // AT command (local) exports.FT_AT_RESPONSE = 0x88; // AT response (local) +exports.FT_TX_TRANSMIT_STATUS = 0x8b; // Status response of transmission exports.FT_REMOTE_AT_COMMAND = 0x17; // AT command (to remote radio) exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data @@ -52,6 +53,25 @@ exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response exports.FT_RECEIVE_RF_DATA = 0x90; // RX received exports.FT_NODE_IDENTIFICATION = 0x95; +exports.DELIVERY_STATES = { + 0x00: "Success", + 0x02: "CCA Failure", + 0x15: "Invalid destination endpoint", + 0x21: "Network ACK Failure", + 0x22: "Not Joined to Network", + 0x23: "Self-addressed", + 0x24: "Address Not Found", + 0x25: "Route Not Found", + 0x74: "Data payload too large" +}; + +exports.DISCOVERY_STATES = { + 0x00: "No Discovery Overhead", + 0x01: "Address Discovery", + 0x02: "Route Discovery", + 0x03: "Address and Route Discovery" +}; + // Bitmasks for I/O pins var digiPinsByte1 = { D10: 4, @@ -287,7 +307,7 @@ exports.packetBuilder = function () { var json = parser.parse(); //console.log("P: "+util.inspect(json)); var event = json.type; - if (json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { event += "_"+json.frameId; } console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); @@ -377,6 +397,17 @@ PacketParser.prototype.knownFrames = { .readByte('sourceEvent'); } }, + 0x8b: { + type: "TX_TRANSMIT_STATUS", + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } + }, 0x88: { type: "AT_RESPONSE", parse: function(parser) { @@ -416,8 +447,8 @@ PacketParser.prototype.knownFrames = { type: "RECEIVE_RF_DATA", parse: function(parser) { parser - .readAddr16('remote16') .readAddr64('remote64') + .readAddr16('remote16') .readByte('receiveOptions') .collectPayload('rawData'); } diff --git a/xbee.js b/xbee.js index 3c42825..394bf0b 100644 --- a/xbee.js +++ b/xbee.js @@ -31,8 +31,8 @@ function XBee(port) { // On AT Response this._onRemoteATResponse = function(res) { // On Node Discovery Packet, emit new Node - if (self.nodes[res.remote64.dec]) { - self.nodes[res.remote64.dec]._onRemoteATResponse(res); + if (self.nodes[res.remote64.hex]) { + self.nodes[res.remote64.hex]._onRemoteATResponse(res); } else { console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); } @@ -112,7 +112,7 @@ XBee.prototype.broadcast = function(data) { this._send(data, remote64, remote16); } -XBee._send = function(data, remote64, remote16) { +XBee.prototype._send = function(data, remote64, remote16) { var frame = new api.TransmitRFData(); if (typeof remote64.dec === 'undefined') { frame.destination64 = remote64; @@ -123,6 +123,7 @@ XBee._send = function(data, remote64, remote16) { } frame.RFData = data; this.serial.write(frame.getBytes()); + return frame.frameId; } XBee.prototype._ATCB = function(cmd, val, cb) { @@ -169,8 +170,23 @@ function Node(xbee, params) { util.inherits(Node, EventEmitter); -Node.prototype.send = function(data) { - this.xbee._send(data, this.remote64, this.remote16); +Node.prototype.send = function(data, cb) { + var frameId = this.xbee._send(data, this.remote64, this.remote16); + if (typeof cb === 'function') { + this.xbee.serial.once("TX_TRANSMIT_STATUS_"+frameId, function(data) { + var error = false; + if (data.deliveryStatus != 0x00) { + error = data; + error.msg = api.DELIVERY_STATES[data.deliveryStatus]; + } + cb(error); + }); + } +} + +Node.prototype._onData = function(data) { + // Send the whole data object, or just the parsed msg? + this.emit('data', api.bArr2Str(data.rawData)); } Node.prototype._AT = function(cmd, val) { From a1533b3193d0051de9516e85e6cb7380f083afa2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 15:09:28 +0200 Subject: [PATCH 045/140] mod. gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 140eb6c..fc14622 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules *.swp .npmignore +npm-debug.log From e301e5288e0c3173288a584bde12deca79f329da Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 5 Apr 2012 15:09:28 +0200 Subject: [PATCH 046/140] mod. gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 140eb6c..fc14622 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules *.swp .npmignore +npm-debug.log From be91776d92115edf0f9991026f28b9b1b6896cbe Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:02:08 +0200 Subject: [PATCH 047/140] massive changes --- xbee-api.js | 36 +++++++++++++++++++++++++----------- xbee.js | 26 +++++++++++++++----------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index 0533541..9572721 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -261,7 +261,6 @@ TransmitRFData.prototype.getPayload = function() { payload.push(this.broadcastRadius); payload.push(this.options); - // this.commandParameter can either be undefined (to query a register), or an array (to set an AT register) if (this.RFData) { for(var j=0; j 0) && (packpos > 2) && (packet.length < packlen)) { - packet.push(b); + if ((packlen > 0) && (packpos > 2)) { + if (packet.length < packlen) { + packet.push(b); + running_total += b; + } else { + checksum = b; + } } + // Packet is complete. Parse & Emit if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // There will still be a checksum byte. Currently this is ignored - var parser = new PacketParser(packet) - var json = parser.parse(); - //console.log("P: "+util.inspect(json)); - var event = json.type; - if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { - event += "_"+json.frameId; + if (!checksum === 255 - (running_total % 256)) { + console.log("CHECKSUM_MISMATCH"); + } else { + var parser = new PacketParser(packet) + var json = parser.parse(); + //console.log("P: "+util.inspect(json)); + var event = json.type; + if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + event += "_"+json.frameId; + } + if (json.type === "UNKNOWN") + console.log("FRAME: %s (%s). EVT: %s RAW:[%s]", json.ft, json.type, event, exports.bArr2Str(json.rawData)); + emitter.emit(event, json); } - console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); - emitter.emit(event, json); } } }; diff --git a/xbee.js b/xbee.js index 394bf0b..d10b27a 100644 --- a/xbee.js +++ b/xbee.js @@ -4,14 +4,19 @@ var api = require("./xbee-api"); var serialport = require("serialport"); var async = require('async'); -function XBee(port) { +function XBee(port, data_parser) { EventEmitter.call(this); - + + if (typeof data_parser !== 'function') { + console.log("loading simple parser"); + data_parser = require("./simple-parser"); + } // Current nodes this.nodes = {}; // Serial connection to the XBee this.serial = new serialport.SerialPort(port, { + baudrate: 57600, parser: api.packetBuilder() }); @@ -23,7 +28,7 @@ function XBee(port) { // RemoveAllListeners??ß self.nodes[node.remote64.hex].removeAllListeners(); } else { - self.nodes[node.remote64.hex] = new Node(self, node); + self.nodes[node.remote64.hex] = new Node(self, node, data_parser); } self.emit("node", self.nodes[node.remote64.hex]); } @@ -38,12 +43,9 @@ function XBee(port) { } } - // Remove this bullcrap, filter by frameid instead! - this._onATResponse_FilterND = function(res) { - } - this._onMessage = function(data) { if (self.nodes[data.remote64.hex]) { + //console.log("Data for %s", data.remote64.hex); self.nodes[data.remote64.hex]._onData(data); } else { console.log("ERROR: Data from unknown node!"); @@ -87,7 +89,7 @@ XBee.prototype.configure = function() { self.config = results; self.emit("configured", self.config); self.discover(function() { - // Discovery Over + console.log("======================="); }); }); } @@ -122,6 +124,7 @@ XBee.prototype._send = function(data, remote64, remote16) { frame.destination16 = remote16.dec; } frame.RFData = data; + console.log(">>OUT: RAW:[%s]", data); this.serial.write(frame.getBytes()); return frame.frameId; } @@ -160,14 +163,15 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { exports.XBee = XBee; -function Node(xbee, params) { +function Node(xbee, params, data_parser) { EventEmitter.call(this); this.xbee = xbee; this.id = params.id; this.remote16 = params.remote16; this.remote64 = params.remote64; + this.buffer = ""; + this.parser = data_parser(this); } - util.inherits(Node, EventEmitter); Node.prototype.send = function(data, cb) { @@ -186,7 +190,7 @@ Node.prototype.send = function(data, cb) { Node.prototype._onData = function(data) { // Send the whole data object, or just the parsed msg? - this.emit('data', api.bArr2Str(data.rawData)); + this.parser.parse(api.bArr2Str(data.rawData)); } Node.prototype._AT = function(cmd, val) { From 27cb3d97a4830bee55e03444c8f5dd4ac79778a6 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:02:08 +0200 Subject: [PATCH 048/140] massive changes --- xbee-api.js | 36 +++++++++++++++++++++++++----------- xbee.js | 26 +++++++++++++++----------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index 0533541..9572721 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -261,7 +261,6 @@ TransmitRFData.prototype.getPayload = function() { payload.push(this.broadcastRadius); payload.push(this.options); - // this.commandParameter can either be undefined (to query a register), or an array (to set an AT register) if (this.RFData) { for(var j=0; j 0) && (packpos > 2) && (packet.length < packlen)) { - packet.push(b); + if ((packlen > 0) && (packpos > 2)) { + if (packet.length < packlen) { + packet.push(b); + running_total += b; + } else { + checksum = b; + } } + // Packet is complete. Parse & Emit if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { // There will still be a checksum byte. Currently this is ignored - var parser = new PacketParser(packet) - var json = parser.parse(); - //console.log("P: "+util.inspect(json)); - var event = json.type; - if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { - event += "_"+json.frameId; + if (!checksum === 255 - (running_total % 256)) { + console.log("CHECKSUM_MISMATCH"); + } else { + var parser = new PacketParser(packet) + var json = parser.parse(); + //console.log("P: "+util.inspect(json)); + var event = json.type; + if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { + event += "_"+json.frameId; + } + if (json.type === "UNKNOWN") + console.log("FRAME: %s (%s). EVT: %s RAW:[%s]", json.ft, json.type, event, exports.bArr2Str(json.rawData)); + emitter.emit(event, json); } - console.log("FRAME: %s (%s). EVT: %s", json.ft, json.type, event); - emitter.emit(event, json); } } }; diff --git a/xbee.js b/xbee.js index 394bf0b..d10b27a 100644 --- a/xbee.js +++ b/xbee.js @@ -4,14 +4,19 @@ var api = require("./xbee-api"); var serialport = require("serialport"); var async = require('async'); -function XBee(port) { +function XBee(port, data_parser) { EventEmitter.call(this); - + + if (typeof data_parser !== 'function') { + console.log("loading simple parser"); + data_parser = require("./simple-parser"); + } // Current nodes this.nodes = {}; // Serial connection to the XBee this.serial = new serialport.SerialPort(port, { + baudrate: 57600, parser: api.packetBuilder() }); @@ -23,7 +28,7 @@ function XBee(port) { // RemoveAllListeners??ß self.nodes[node.remote64.hex].removeAllListeners(); } else { - self.nodes[node.remote64.hex] = new Node(self, node); + self.nodes[node.remote64.hex] = new Node(self, node, data_parser); } self.emit("node", self.nodes[node.remote64.hex]); } @@ -38,12 +43,9 @@ function XBee(port) { } } - // Remove this bullcrap, filter by frameid instead! - this._onATResponse_FilterND = function(res) { - } - this._onMessage = function(data) { if (self.nodes[data.remote64.hex]) { + //console.log("Data for %s", data.remote64.hex); self.nodes[data.remote64.hex]._onData(data); } else { console.log("ERROR: Data from unknown node!"); @@ -87,7 +89,7 @@ XBee.prototype.configure = function() { self.config = results; self.emit("configured", self.config); self.discover(function() { - // Discovery Over + console.log("======================="); }); }); } @@ -122,6 +124,7 @@ XBee.prototype._send = function(data, remote64, remote16) { frame.destination16 = remote16.dec; } frame.RFData = data; + console.log(">>OUT: RAW:[%s]", data); this.serial.write(frame.getBytes()); return frame.frameId; } @@ -160,14 +163,15 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { exports.XBee = XBee; -function Node(xbee, params) { +function Node(xbee, params, data_parser) { EventEmitter.call(this); this.xbee = xbee; this.id = params.id; this.remote16 = params.remote16; this.remote64 = params.remote64; + this.buffer = ""; + this.parser = data_parser(this); } - util.inherits(Node, EventEmitter); Node.prototype.send = function(data, cb) { @@ -186,7 +190,7 @@ Node.prototype.send = function(data, cb) { Node.prototype._onData = function(data) { // Send the whole data object, or just the parsed msg? - this.emit('data', api.bArr2Str(data.rawData)); + this.parser.parse(api.bArr2Str(data.rawData)); } Node.prototype._AT = function(cmd, val) { From 3089b5f4873718483f18924624bbf43fea490036 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:02:38 +0200 Subject: [PATCH 049/140] added a simple parser --- simple-parser.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 simple-parser.js diff --git a/simple-parser.js b/simple-parser.js new file mode 100644 index 0000000..5517592 --- /dev/null +++ b/simple-parser.js @@ -0,0 +1,18 @@ +module.exports = function (device) { + function DataParser(device) { + this.device = device; + this.buffer = ""; + } + + DataParser.prototype.parse = function(data) { + this.buffer += data; + var split = this.buffer.indexOf('\r\n'); + while (split > -1) { + this.device.emit('data', this.buffer.slice(0,split)); + this.buffer = this.buffer.slice(split+2); + split = this.buffer.indexOf('\r\n'); + } + } + + return new DataParser(device); +} From d2ea0a70dc68b4f42188696a41bde9a8c18e1fd5 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:02:38 +0200 Subject: [PATCH 050/140] added a simple parser --- simple-parser.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 simple-parser.js diff --git a/simple-parser.js b/simple-parser.js new file mode 100644 index 0000000..5517592 --- /dev/null +++ b/simple-parser.js @@ -0,0 +1,18 @@ +module.exports = function (device) { + function DataParser(device) { + this.device = device; + this.buffer = ""; + } + + DataParser.prototype.parse = function(data) { + this.buffer += data; + var split = this.buffer.indexOf('\r\n'); + while (split > -1) { + this.device.emit('data', this.buffer.slice(0,split)); + this.buffer = this.buffer.slice(split+2); + split = this.buffer.indexOf('\r\n'); + } + } + + return new DataParser(device); +} From ec14d870a687e447235341a2f1e66588360989f1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:03:19 +0200 Subject: [PATCH 051/140] pushed to version 0.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51018e8..aff2c68 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.3", + "version" : "0.0.4", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From e66f464efa72a432ef5cfb77bdeb1ab82af7b130 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 13 Apr 2012 00:03:19 +0200 Subject: [PATCH 052/140] pushed to version 0.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51018e8..aff2c68 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "xbee-svd", - "version" : "0.0.3", + "version" : "0.0.4", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From dab794a01ec35a4fe47a4c343e2054e5fd34fe66 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 4 May 2012 11:58:47 +0200 Subject: [PATCH 053/140] renamed to follow my naming conventions --- package.json | 4 ++-- xbee.js | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index aff2c68..636c092 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ -{ "name" : "xbee-svd", - "version" : "0.0.4", +{ "name" : "svd-xbee", + "version" : "0.0.5", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", diff --git a/xbee.js b/xbee.js index d10b27a..3a2ab70 100644 --- a/xbee.js +++ b/xbee.js @@ -24,13 +24,15 @@ function XBee(port, data_parser) { this._onNodeDiscovery = function(data) { var node = data.node; - if (self.nodes[node.remote64.hex]) { - // RemoveAllListeners??ß - self.nodes[node.remote64.hex].removeAllListeners(); - } else { + if (!self.nodes[node.remote64.hex]) { self.nodes[node.remote64.hex] = new Node(self, node, data_parser); + self.emit("node", self.nodes[node.remote64.hex]); + } else { + self.nodes[node.remote64.hex].emit("reconnect"); + // RemoveAllListeners??ß + // self.nodes[node.remote64.hex].removeAllListeners(); + // } - self.emit("node", self.nodes[node.remote64.hex]); } // On AT Response @@ -124,7 +126,6 @@ XBee.prototype._send = function(data, remote64, remote16) { frame.destination16 = remote16.dec; } frame.RFData = data; - console.log(">>OUT: RAW:[%s]", data); this.serial.write(frame.getBytes()); return frame.frameId; } @@ -201,3 +202,4 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } +exports.Node = Node; From 7b3da7155ad455f7fdc293d129b9d9297e673d2a Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 4 May 2012 11:58:47 +0200 Subject: [PATCH 054/140] renamed to follow my naming conventions --- package.json | 4 ++-- xbee.js | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index aff2c68..636c092 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ -{ "name" : "xbee-svd", - "version" : "0.0.4", +{ "name" : "svd-xbee", + "version" : "0.0.5", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", diff --git a/xbee.js b/xbee.js index d10b27a..3a2ab70 100644 --- a/xbee.js +++ b/xbee.js @@ -24,13 +24,15 @@ function XBee(port, data_parser) { this._onNodeDiscovery = function(data) { var node = data.node; - if (self.nodes[node.remote64.hex]) { - // RemoveAllListeners??ß - self.nodes[node.remote64.hex].removeAllListeners(); - } else { + if (!self.nodes[node.remote64.hex]) { self.nodes[node.remote64.hex] = new Node(self, node, data_parser); + self.emit("node", self.nodes[node.remote64.hex]); + } else { + self.nodes[node.remote64.hex].emit("reconnect"); + // RemoveAllListeners??ß + // self.nodes[node.remote64.hex].removeAllListeners(); + // } - self.emit("node", self.nodes[node.remote64.hex]); } // On AT Response @@ -124,7 +126,6 @@ XBee.prototype._send = function(data, remote64, remote16) { frame.destination16 = remote16.dec; } frame.RFData = data; - console.log(">>OUT: RAW:[%s]", data); this.serial.write(frame.getBytes()); return frame.frameId; } @@ -201,3 +202,4 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } +exports.Node = Node; From 03d28807ed32b986a0e15c32ceb3bc6c76b58d2c Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 19 May 2012 20:52:06 +0200 Subject: [PATCH 055/140] fixed renaming issues in readme and example --- examples/simple.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/simple.js b/examples/simple.js index a8f01c2..cbee222 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,5 +1,5 @@ var util = require('util'); -var XBee = require('xbee-svd').XBee; +var XBee = require('svd-xbee').XBee; // Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); @@ -12,6 +12,7 @@ xbee.on("node", function(node) { console.log("Node %s connected", node.id); node.on("data", function(data) { + node.send("pong"); console.log("%s: %s", node.id, util.inspect(data)); }); From 1e904ef76b14d4b9c62c8c4ef88d3d4a3287afd2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 19 May 2012 20:52:06 +0200 Subject: [PATCH 056/140] fixed renaming issues in readme and example --- examples/simple.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/simple.js b/examples/simple.js index a8f01c2..cbee222 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,5 +1,5 @@ var util = require('util'); -var XBee = require('xbee-svd').XBee; +var XBee = require('svd-xbee').XBee; // Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); @@ -12,6 +12,7 @@ xbee.on("node", function(node) { console.log("Node %s connected", node.id); node.on("data", function(data) { + node.send("pong"); console.log("%s: %s", node.id, util.inspect(data)); }); From 1ff08b76f36ce1d2c5808fe5984d762e798f145f Mon Sep 17 00:00:00 2001 From: Michael Sealand Date: Tue, 22 May 2012 21:08:24 -0700 Subject: [PATCH 057/140] Allow for setting baud rate Change the port parameter in the XBee constructor to an options object which contains the port and optionally a baud rate. If a string is passed in for the options parameter, assume it is the port for backwards compatibility. If no baud rate is passed in, default to 57600 for backwards compatibility. --- xbee.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xbee.js b/xbee.js index 3a2ab70..14df4ab 100644 --- a/xbee.js +++ b/xbee.js @@ -4,9 +4,13 @@ var api = require("./xbee-api"); var serialport = require("serialport"); var async = require('async'); -function XBee(port, data_parser) { +function XBee(options, data_parser) { EventEmitter.call(this); + if (typeof options === 'string') { + options = {port: options}; + } + if (typeof data_parser !== 'function') { console.log("loading simple parser"); data_parser = require("./simple-parser"); @@ -15,8 +19,8 @@ function XBee(port, data_parser) { this.nodes = {}; // Serial connection to the XBee - this.serial = new serialport.SerialPort(port, { - baudrate: 57600, + this.serial = new serialport.SerialPort(options.port, { + baudrate: options.baudrate || 57600, parser: api.packetBuilder() }); From a6e46d3e5e93329f2b7fde7e8f52535ea6f97eff Mon Sep 17 00:00:00 2001 From: Michael Sealand Date: Tue, 22 May 2012 21:08:24 -0700 Subject: [PATCH 058/140] Allow for setting baud rate Change the port parameter in the XBee constructor to an options object which contains the port and optionally a baud rate. If a string is passed in for the options parameter, assume it is the port for backwards compatibility. If no baud rate is passed in, default to 57600 for backwards compatibility. --- xbee.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xbee.js b/xbee.js index 3a2ab70..14df4ab 100644 --- a/xbee.js +++ b/xbee.js @@ -4,9 +4,13 @@ var api = require("./xbee-api"); var serialport = require("serialport"); var async = require('async'); -function XBee(port, data_parser) { +function XBee(options, data_parser) { EventEmitter.call(this); + if (typeof options === 'string') { + options = {port: options}; + } + if (typeof data_parser !== 'function') { console.log("loading simple parser"); data_parser = require("./simple-parser"); @@ -15,8 +19,8 @@ function XBee(port, data_parser) { this.nodes = {}; // Serial connection to the XBee - this.serial = new serialport.SerialPort(port, { - baudrate: 57600, + this.serial = new serialport.SerialPort(options.port, { + baudrate: options.baudrate || 57600, parser: api.packetBuilder() }); From 51230c3c9a7410826206d8b25c96e95866cdc020 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 23 May 2012 06:46:04 +0200 Subject: [PATCH 059/140] new npm version --- README.markdown | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 01761a0..420067b 100644 --- a/README.markdown +++ b/README.markdown @@ -52,7 +52,7 @@ I can also use this library to send remote commands and query remote xbee module Installation ============ - npm install xbee-svd + npm install svd-xbee Licence ======= diff --git a/package.json b/package.json index 636c092..5b871c9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.0.5", + "version" : "0.0.7", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From 3b9d29ed737068527bb757d03835fe0a210cadf1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 23 May 2012 06:46:04 +0200 Subject: [PATCH 060/140] new npm version --- README.markdown | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 01761a0..420067b 100644 --- a/README.markdown +++ b/README.markdown @@ -52,7 +52,7 @@ I can also use this library to send remote commands and query remote xbee module Installation ============ - npm install xbee-svd + npm install svd-xbee Licence ======= diff --git a/package.json b/package.json index 636c092..5b871c9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.0.5", + "version" : "0.0.7", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From 28005aa0be904070abfa4b713de3d7c054681a37 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 23 May 2012 07:28:52 +0200 Subject: [PATCH 061/140] fixed readme --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 420067b..b480a66 100644 --- a/README.markdown +++ b/README.markdown @@ -2,10 +2,11 @@ A more high level fork of Richard Morrison's node-xbee. Example ======= +As no parser function is passed to the XBee() constructor, the default parser will be used (see simple-parser.js). The default parser will merge frames and emit them split by \r\n (so if you wonder why no data is emitted, make sure you use the right delimiter!). ```javascript var util = require('util'); -var XBee = require('xbee-svd').XBee; +var XBee = require('svd-xbee').XBee; // Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); From 90e11db012ad91fc6f679eec1aa9a93bbabf692d Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 23 May 2012 07:28:52 +0200 Subject: [PATCH 062/140] fixed readme --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 420067b..b480a66 100644 --- a/README.markdown +++ b/README.markdown @@ -2,10 +2,11 @@ A more high level fork of Richard Morrison's node-xbee. Example ======= +As no parser function is passed to the XBee() constructor, the default parser will be used (see simple-parser.js). The default parser will merge frames and emit them split by \r\n (so if you wonder why no data is emitted, make sure you use the right delimiter!). ```javascript var util = require('util'); -var XBee = require('xbee-svd').XBee; +var XBee = require('svd-xbee').XBee; // Replace with your xbee's UART location var xbee = new XBee('/dev/ttyO1'); From 8d5a8ff8872a811215b98611950ae82166193ae2 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Fri, 1 Jun 2012 00:08:44 +0100 Subject: [PATCH 063/140] Handle responses from 'NT' that have more than one byte. (see http://forums.digi.com/support/forum/viewthread_thread,10313) If the XBee module responds to the 'NT' (network discovery timeout) command with one byte, then it's fine to simply multiply by 100. However, some XBee modules reply with a 16bit value (mine do!), so this change handles responses to the NT command having arbitrary lengths. --- xbee-api.js | 11 +++++++++++ xbee.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/xbee-api.js b/xbee-api.js index 9572721..6f1d307 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -28,6 +28,17 @@ exports.bArr2Str = function(a) { return s; } +exports.bArr2Dec = function(a) { + // given a byte array like [3,21], convert to a decimal value. + // e.g. [3,21] --> 3 * 256 + 21 = 789 + var r = 0; + for(var i = 0; i < a.length; i++) { + var power = a.length - i - 1; + r += a[i] * Math.pow(256,power); + } + return r +} + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands diff --git a/xbee.js b/xbee.js index 14df4ab..5b4cbff 100644 --- a/xbee.js +++ b/xbee.js @@ -87,7 +87,7 @@ XBee.prototype.configure = function() { id: QF('NI', api.bArr2Str), sourceLow: QF('SL', api.bArr2HexStr), sourceHigh: QF('SH', api.bArr2HexStr), - nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) + nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) }; // Using async to start discovery only when all parameters have been read. From 0fcd29a494add0fa21030a2b0a8231b2bba6d25e Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Fri, 1 Jun 2012 00:08:44 +0100 Subject: [PATCH 064/140] Handle responses from 'NT' that have more than one byte. (see http://forums.digi.com/support/forum/viewthread_thread,10313) If the XBee module responds to the 'NT' (network discovery timeout) command with one byte, then it's fine to simply multiply by 100. However, some XBee modules reply with a 16bit value (mine do!), so this change handles responses to the NT command having arbitrary lengths. --- xbee-api.js | 11 +++++++++++ xbee.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/xbee-api.js b/xbee-api.js index 9572721..6f1d307 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -28,6 +28,17 @@ exports.bArr2Str = function(a) { return s; } +exports.bArr2Dec = function(a) { + // given a byte array like [3,21], convert to a decimal value. + // e.g. [3,21] --> 3 * 256 + 21 = 789 + var r = 0; + for(var i = 0; i < a.length; i++) { + var power = a.length - i - 1; + r += a[i] * Math.pow(256,power); + } + return r +} + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands diff --git a/xbee.js b/xbee.js index 14df4ab..5b4cbff 100644 --- a/xbee.js +++ b/xbee.js @@ -87,7 +87,7 @@ XBee.prototype.configure = function() { id: QF('NI', api.bArr2Str), sourceLow: QF('SL', api.bArr2HexStr), sourceHigh: QF('SH', api.bArr2HexStr), - nodeDiscoveryTime: QF('NT', function(a) { return parseInt(a[0])*100; }) + nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) }; // Using async to start discovery only when all parameters have been read. From ceb0d30184041630ac3a8758364e0b9fb640ca15 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Tue, 5 Jun 2012 14:55:43 +0100 Subject: [PATCH 065/140] Correct typo in data sample packet parsing --- xbee-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbee-api.js b/xbee-api.js index 6f1d307..f76a534 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -486,7 +486,7 @@ PacketParser.prototype.knownFrames = { .readAddr16('remote16') .readByte('receiveOptions') .readByte('numSamples') - .readByte('digitalChanelMask', 2) + .readByte('digitalChannelMask', 2) .readByte('analogChannelMask') if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) parser.readByte('digitalSamples',2); From 5cff3bacb88e88848f90656e714e761ae1864c00 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Tue, 5 Jun 2012 14:55:43 +0100 Subject: [PATCH 066/140] Correct typo in data sample packet parsing --- xbee-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbee-api.js b/xbee-api.js index 6f1d307..f76a534 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -486,7 +486,7 @@ PacketParser.prototype.knownFrames = { .readAddr16('remote16') .readByte('receiveOptions') .readByte('numSamples') - .readByte('digitalChanelMask', 2) + .readByte('digitalChannelMask', 2) .readByte('analogChannelMask') if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) parser.readByte('digitalSamples',2); From 0ff278a774cdafd39802a85a5af87c38669d691a Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Tue, 5 Jun 2012 14:56:45 +0100 Subject: [PATCH 067/140] Add listeners for Xbee data samples --- xbee.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xbee.js b/xbee.js index 5b4cbff..017122f 100644 --- a/xbee.js +++ b/xbee.js @@ -58,9 +58,18 @@ function XBee(options, data_parser) { } } + this._onDataSampleRx = function(data) { + if (self.nodes[data.remote64.hex]) { + self.nodes[data.remote64.hex]._onDataSampleRx(data); + } else { + console.log("ERROR: Data sample from unknown node!"); + } + } + this.serial.on("REMOTE_AT_RESPONSE", this._onRemoteATResponse); this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); this.serial.on("RECEIVE_RF_DATA", this._onMessage); + this.serial.on("DATA_SAMPLE_RX", this._onDataSampleRx); this.configure(); } @@ -206,4 +215,8 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } +Node.prototype._onDataSampleRx = function(res) { + this.emit('data_sample', res); +} + exports.Node = Node; From 34c1923d8949a9ca87f48d5ffb6040f4a3a9c768 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Tue, 5 Jun 2012 14:56:45 +0100 Subject: [PATCH 068/140] Add listeners for Xbee data samples --- xbee.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xbee.js b/xbee.js index 5b4cbff..017122f 100644 --- a/xbee.js +++ b/xbee.js @@ -58,9 +58,18 @@ function XBee(options, data_parser) { } } + this._onDataSampleRx = function(data) { + if (self.nodes[data.remote64.hex]) { + self.nodes[data.remote64.hex]._onDataSampleRx(data); + } else { + console.log("ERROR: Data sample from unknown node!"); + } + } + this.serial.on("REMOTE_AT_RESPONSE", this._onRemoteATResponse); this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); this.serial.on("RECEIVE_RF_DATA", this._onMessage); + this.serial.on("DATA_SAMPLE_RX", this._onDataSampleRx); this.configure(); } @@ -206,4 +215,8 @@ Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } +Node.prototype._onDataSampleRx = function(res) { + this.emit('data_sample', res); +} + exports.Node = Node; From 167021fe02cab33cb8f9f4ede55e81db1823e63d Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Tue, 5 Jun 2012 19:15:05 +0200 Subject: [PATCH 069/140] v0.0.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b871c9..4e15612 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.0.7", + "version" : "0.0.8", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", From dc8416d7b6f3b89fe20e61c843678a903ee98870 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 8 Jun 2012 12:42:03 +0200 Subject: [PATCH 070/140] ... --- package.json | 2 +- xbee.js | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4e15612..d7bb2a7 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport" : "0.7.x", + "serialport2" : "0.0.x", "async" : "0.1.x" }, "repository": { diff --git a/xbee.js b/xbee.js index 017122f..46741b9 100644 --- a/xbee.js +++ b/xbee.js @@ -1,7 +1,7 @@ var util = require('util'); var EventEmitter = require('events').EventEmitter; var api = require("./xbee-api"); -var serialport = require("serialport"); +var serialport = require("serialport2"); var async = require('async'); function XBee(options, data_parser) { @@ -20,10 +20,13 @@ function XBee(options, data_parser) { // Serial connection to the XBee this.serial = new serialport.SerialPort(options.port, { - baudrate: options.baudrate || 57600, + baudRate: options.baudrate || 57600, parser: api.packetBuilder() }); + serial.on("data", function(data) { + }); + var self = this; this._onNodeDiscovery = function(data) { From 4b8def053b3a320066862595cb788a176d13b7a2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 8 Jun 2012 15:33:25 +0200 Subject: [PATCH 071/140] serialport2 --- xbee-api.js | 15 +++++++++++++-- xbee.js | 24 +++++++++++++++++------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index f76a534..d3d2c75 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -149,8 +149,19 @@ Packet.prototype.getBytes = function() { // finally append the checksum byte and return the packet as a JS array packetdata.push(checksum); - - return packetdata; + + + var string = ""; + for (i in packetdata) { + string += String.fromCharCode(packetdata[i]); + } + + var res = new Buffer(packetdata); + + //console.log(packetdata); + //console.log(util.inspect(res)); + + return res; } Packet.prototype.getPayload = function() { diff --git a/xbee.js b/xbee.js index 46741b9..f8e4c4d 100644 --- a/xbee.js +++ b/xbee.js @@ -2,10 +2,12 @@ var util = require('util'); var EventEmitter = require('events').EventEmitter; var api = require("./xbee-api"); var serialport = require("serialport2"); +//var serialport = require("serialport"); var async = require('async'); function XBee(options, data_parser) { EventEmitter.call(this); + var self = this; if (typeof options === 'string') { options = {port: options}; @@ -18,17 +20,26 @@ function XBee(options, data_parser) { // Current nodes this.nodes = {}; + var packetBuilder = api.packetBuilder(); + // Serial connection to the XBee - this.serial = new serialport.SerialPort(options.port, { + this.serial = new serialport.SerialPort(); + + this.serial.open(options.port, { baudRate: options.baudrate || 57600, - parser: api.packetBuilder() + dataBits: 8, + parity: 'none', + stopBits: 1 + }, function(err) { + if (err) console.log("ERROR: "+err); + else self.configure(); }); - serial.on("data", function(data) { + this.serial.on("data", function(buffer) { + //console.log(">"+buffer+"<"); + packetBuilder(this, buffer); }); - var self = this; - this._onNodeDiscovery = function(data) { var node = data.node; if (!self.nodes[node.remote64.hex]) { @@ -73,8 +84,6 @@ function XBee(options, data_parser) { this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); this.serial.on("RECEIVE_RF_DATA", this._onMessage); this.serial.on("DATA_SAMPLE_RX", this._onDataSampleRx); - - this.configure(); } util.inherits(XBee, EventEmitter); @@ -159,6 +168,7 @@ XBee.prototype._AT = function(cmd, val) { var frame = new api.ATCommand(); frame.setCommand(cmd); frame.commandParameter = val; + console.log(cmd+":"); this.serial.write(frame.getBytes()); return frame.frameId; } From 41e852992e7351ffff94d81ff272e8a7fe8cf3b8 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 8 Jun 2012 15:34:23 +0200 Subject: [PATCH 072/140] publishing to npm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7bb2a7..176efd0 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,4 @@ -{ "name" : "svd-xbee", +{ "name" : "svd-xbee-sp2", "version" : "0.0.8", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", From 9be434a416d9969695799a97122f8a345e2371bc Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 9 Jun 2012 13:42:22 +0200 Subject: [PATCH 073/140] Outsourced and rewrote constants --- constants.js | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 constants.js diff --git a/constants.js b/constants.js new file mode 100644 index 0000000..d5eac87 --- /dev/null +++ b/constants.js @@ -0,0 +1,135 @@ +exports = module.exports; +exports.START_BYTE = 0x7E; +exports.EVT_SEP = "_"; +exports.MAX_PAYLOAD_SIZE = 74; + +var ft = exports.FRAME_TYPE = {}; +var diss = exports.DISCOVERY_STATUS = {}; +var dels = exports.DELIVERY_STATUS = {}; +var ro = exports.RECEIVE_OPTIONS = {}; + + +// Frame Type +ft.AT_COMMAND = 0x08; +ft[0x08] = "AT Command (0x08)"; +ft.AT_COMMAND_QUEUE_PARAMETER_VALUE = 0x09; +ft[0x09] = "AT Command - Queue Parameter Value (0x09)"; +ft.ZIGBEE_TRANSMIT_REQUEST = 0x10; +ft[0x10] = "ZigBee Transmit Request (0x10)"; +ft.EXPLICIT_ADDRESSING_ZIGBEE_COMMAND_FRAME = 0x11; +ft[0x11] = "Explicit Addressing ZigBee Command Frame (0x11)"; +ft.REMOTE_COMMAND_REQUEST = 0x17; +ft[0x17] = "Remote Command Request (0x17)"; +ft.CREATE_SOURCE_ROUTE = 0x21; +ft[0x21] = "Create Source Route (0x21)"; +ft.AT_COMMAND_RESPONSE = 0x88; +ft[0x88] = "AT Command Response (0x88)"; +ft.MODEM_STATUS = 0x8A; +ft[0x8A] = "Modem Status (0x8A)"; +ft.ZIGBEE_TRANSMIT_STATUS = 0x8B; +ft[0x8B] = "ZigBee Transmit Status (0x8B)"; +ft.ZIGBEE_RECEIVE_PACKET = 0x90; +ft[0x90] = "ZigBee Receive Packet (AO=0) (0x90)"; +ft.ZIGBEE_EXPLICIT_RX = 0x91; +ft[0x91] = "ZigBee Explicit Rx Indicator (AO=1) (0x91)"; +ft.ZIGBEE_IO_DATA_SAMPLE_RX = 0x92; +ft[0x92] = "ZigBee IO Data Sample Rx Indicator (0x92)"; +ft.XBEE_SENSOR_READ = 0x94; +ft[0x94] = "XBee Sensor Read Indicator (AO=0) (0x94)"; +ft.NODE_IDENTIFICATION = 0x95; +ft[0x95] = "Node Identification Indicator (AO=0) (0x95)"; +ft.REMOTE_COMMAND_RESPONSE = 0x97; +ft[0x97] = "Remote Command Response (0x97)"; +ft.OTA_FIRMWARE_UPDATE_STATUS = 0xA0; +ft[0xA0] = "Over-the-Air Firmware Update Status (0xA0)"; +ft.ROUTE_RECORD = 0xA1; +ft[0xA1] = "Route Record Indicator (0xA1)"; +ft.MTO_ROUTE_REQUEST = 0xA3; +ft[0xA3] = "Many-to-One Route Request Indicator (0xA3)"; + +// Delivery Status +dels.SUCCESS = 0x00; +dels[0x00] = "Success (0x00)"; +dels.MAC_ACK_FALIURE = 0x01; +dels[0x01] = "MAC ACK Failure (0x01)"; +dels.CA_FAILURE = 0x02; +dels[0x02] = "CA Failure (0x02)"; +dels.INVALID_DESTINATION_ENDPOINT = 0x15; +dels[0x15] = "Invalid destination endpoint (0x15)"; +dels.NETWORK_ACK_FAILURE = 0x21; +dels[0x21] = "Network ACK Failure (0x21)"; +dels.NOT_JOINED_TO_NETWORK = 0x22; +dels[0x22] = "Not Joined to Network (0x22)"; +dels.SELF_ADDRESSED = 0x23; +dels[0x23] = "Self-addressed (0x23)"; +dels.ADDRESS_NOT_FOUND = 0x24; +dels[0x24] = "Address Not Found (0x24)"; +dels.ROUTE_NOT_FOUND = 0x25; +dels[0x25] = "Route Not Found (0x25)"; +dels.BROADCAST_SOURCE_FAILED = 0x26; +dels[0x26] = "Broadcast source failed to hear a neighbor relay the message (0x26)"; +dels.INVALID_BINDING_TABLE_INDEX = 0x2B; +dels[0x2B] = "Invalid binding table index (0x2B)"; +dels.RESOURCE_ERROR = 0x2C; +dels[0x2C] = "Resource error lack of free buffers, timers, etc. (0x2C)"; +dels.ATTEMPTED_BROADCAST_WITH_APS_TRANS = 0x2D; +dels[0x2D] = "Attempted broadcast with APS transmission (0x2D)"; +dels.ATTEMPTED_BROADCAST_WITH_APS_TRANS_EE0 = 0x2D; +dels[0x2E] = "Attempted unicast with APS transmission, but EE=0 (0x2E)"; +dels.RESOURCE_ERROR_B = 0x32; +dels[0x32] = "Resource error lack of free buffers, timers, etc. (0x32)"; +dels.DATA_PAYLOAD_TOO_LARGE = 0x74; +dels[0x74] = "Data payload too large (0x74)"; +dels.INDIRECT_MESSAGE_UNREQUESTED = 0x75; +dels[0x75] = "Indirect message unrequested (0x75)"; + +// Discovery Status +diss.NO_DISCOVERY_OVERHEAD = 0x00; +diss[0x00] = "No Discovery Overhead (0x00)"; +diss.ADDRESS_DISCOVERY = 0x01; +diss[0x01] = "Address Discovery (0x01)"; +diss.ROUTE_DISCOVERY = 0x02; +diss[0x02] = "Route Discovery (0x02)"; +diss.ADDRESS_AND_ROUTE_DISCOVERY = 0x03; +diss[0x03] = "Address and Route (0x03)"; +diss.EXTENDED_TIMEOUT_DISCOVERY = 0x40; +diss[0x40] = "Extended Timeout Discovery (0x40)"; + +// Receive Options +ro.PACKET_ACKNOWLEDGED = 0x01; +ro[0x01] = "Packet Acknowledged (0x01)"; +ro.PACKET_WAS_BROADCAST = 0x02; +ro[0x02] = "Packet was a broadcast packet (0x02)"; +ro.PACKET_ENCRYPTED = 0x20; +ro[0x20] = "Packet encrypted with APS encryption (0x20)"; +ro.PACKET_SENT_FROM_END_DEVICE = 0x40; +ro[0x40] = "Packet was sent from an end device (if known) (0x40)"; + +/* Bitmasks for I/O pins + * (what was this used for?) +var digiPinsByte1 = { + D10: 4, + D11: 8, + D12: 16 +}; + +var digiPinsByte2 = { + D0: 1, + D1: 2, + D2: 4, + D3: 8, + D4: 16, + D5: 32, + D6: 64, + D7: 128 +}; + +var analogPins = { + A0: 1, + A1: 2, + A2: 4, + A3: 8, + supply: 128 +}; + +*/ From d5f0b701fe813f5e0071aa8b3d27c73adb66e8c9 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 9 Jun 2012 13:43:01 +0200 Subject: [PATCH 074/140] Fixed references to constants --- xbee-api.js | 276 ++++++++++++++++++++++------------------------------ 1 file changed, 118 insertions(+), 158 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index d3d2c75..5963b79 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,6 +1,9 @@ var Buffer = require('buffer').Buffer; var util = require('util'); +var C = exports.Constants = require('./constants.js'); +exports = module.exports; + exports.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -51,64 +54,6 @@ function incrementFrameId() { return frameId; } -exports.START_BYTE = 0x7e; // start of every XBee packet - -exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received -exports.FT_AT_COMMAND = 0x08; // AT command (local) -exports.FT_AT_RESPONSE = 0x88; // AT response (local) -exports.FT_TX_TRANSMIT_STATUS = 0x8b; // Status response of transmission -exports.FT_REMOTE_AT_COMMAND = 0x17; // AT command (to remote radio) -exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) -exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data -exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response -exports.FT_RECEIVE_RF_DATA = 0x90; // RX received -exports.FT_NODE_IDENTIFICATION = 0x95; - -exports.DELIVERY_STATES = { - 0x00: "Success", - 0x02: "CCA Failure", - 0x15: "Invalid destination endpoint", - 0x21: "Network ACK Failure", - 0x22: "Not Joined to Network", - 0x23: "Self-addressed", - 0x24: "Address Not Found", - 0x25: "Route Not Found", - 0x74: "Data payload too large" -}; - -exports.DISCOVERY_STATES = { - 0x00: "No Discovery Overhead", - 0x01: "Address Discovery", - 0x02: "Route Discovery", - 0x03: "Address and Route Discovery" -}; - -// Bitmasks for I/O pins -var digiPinsByte1 = { - D10: 4, - D11: 8, - D12: 16 -}; - -var digiPinsByte2 = { - D0: 1, - D1: 2, - D2: 4, - D3: 8, - D4: 16, - D5: 32, - D6: 64, - D7: 128 -}; - -var analogPins = { - A0: 1, - A1: 2, - A2: 4, - A3: 8, - supply: 128 -}; - // constructor for an outgoing Packet. var Packet = function() { this.frameId = incrementFrameId(); @@ -117,7 +62,7 @@ var Packet = function() { // call getBytes to get a JS array of byte values, ready to send down the serial port Packet.prototype.getBytes = function() { // build a JS array to hold the bytes - var packetdata = [exports.START_BYTE]; + var packetdata = [C.START_BYTE]; // calculate the length bytes. First, get the entire payload by calling the internal function var payload = this.getPayload(); @@ -189,7 +134,7 @@ ATCommand.prototype.getPayload = function() { // Uses command0, command1 and commandParameter to build the payload. // begin with the frame type and frame ID - var payload = [exports.FT_AT_COMMAND, this.frameId]; + var payload = [C.FRAME_TYPE.AT_COMMAND, this.frameId]; // add two bytes to identify which AT command is being used payload.push(this.command0); @@ -221,7 +166,7 @@ RemoteATCommand.prototype.getPayload = function() { // remoteCommandOptions, destination64 and destination16 are also used. // begin with the frame type and frame ID - var payload = [exports.FT_REMOTE_AT_COMMAND, this.frameId]; + var payload = [C.FRAME_TYPE.REMOTE_COMMAND_REQUEST, this.frameId]; // this.destination64 should be an array of 8 integers. Append it to the payload now. for(var i=0; i<8; i++) { @@ -267,7 +212,7 @@ TransmitRFData.prototype.getPayload = function() { // .destination64 and .destination16 are also required. // begin with the frame type and frame ID - var payload = [exports.FT_TRANSMIT_RF_DATA, this.frameId]; + var payload = [C.FRAME_TYPE.ZIGBEE_TRANSMIT_REQUEST, this.frameId]; // this.destination64 should be an array of 8 integers. Append it to the payload now. for(var i=0; i<8; i++) { @@ -286,7 +231,6 @@ TransmitRFData.prototype.getPayload = function() { if (this.RFData) { for(var j=0; j= 0) evt += C.EVT_SEP+json.frameId; + emitter.emit(evt, json); } - if (json.type === "UNKNOWN") - console.log("FRAME: %s (%s). EVT: %s RAW:[%s]", json.ft, json.type, event, exports.bArr2Str(json.rawData)); - emitter.emit(event, json); } } } @@ -365,12 +310,12 @@ var PacketParser = function(p) { } PacketParser.prototype.parse = function() { - if (this.knownFrames[this.json.ft]) { - this.json.type = this.knownFrames[this.json.ft].type; - this.knownFrames[this.json.ft].parse(this); - } else { - this.json.type = "UNKNOWN"; + if (false) { // TODO: Debug option + this.json.desc = C.FRAME_TYPES[this.json.ft]; } + + this.frames[this.json.ft].parse(this); + return this.json; } @@ -414,14 +359,44 @@ PacketParser.prototype.collectPayload = function(name) { return this; } -PacketParser.prototype.knownFrames = { - 0x95: { - type: "NODE_IDENTIFICATION", - parse: function(parser) { - parser - .readAddr64('sender64') - .readAddr16('sender16') - .readByte('recieveOptions'); +var frames = PacketParser.prototype.frames = {}; + +frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } +}; + +frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + if (parser.json.command == 'ND') { parser.json.node = {}; parser.write = parser.json.node; parser @@ -430,80 +405,65 @@ PacketParser.prototype.knownFrames = { .readString('id') .readAddr16('remoteParent16') .readByte('deviceType') - .readByte('sourceEvent'); - } - }, - 0x8b: { - type: "TX_TRANSMIT_STATUS", - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readByte('transmitRetryCount') - .readByte('deliveryStatus') - .readByte('discoveryStatus') - } - }, - 0x88: { - type: "AT_RESPONSE", - parse: function(parser) { - parser - .readByte('frameId') - .readString('command', 2) - .readByte('commandStatus') - if (parser.json.command == 'ND') { - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } - } - }, - 0x97: { - type: "REMOTE_AT_RESPONSE", - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readAddr64('remote64') - .readString('command', 2) - .readByte('commandStatus') - .collectPayload('commandData'); - } - }, - 0x90: { - type: "RECEIVE_RF_DATA", - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawData'); + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') } - }, - 0x92: { - type: "DATA_SAMPLE_RX", - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .readByte('numSamples') - .readByte('digitalChannelMask', 2) - .readByte('analogChannelMask') - if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) - parser.readByte('digitalSamples',2); - if (parser.json.analogChannelMask > 0) - parser.collectPayload('analogSamples'); + } +}; + +frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('rawData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .readByte('numSamples') + .readByte('digitalChannelMask', 2) + .readByte('analogChannelMask') + if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) + parser.readByte('digitalSamples',2); + if (parser.json.analogChannelMask > 0) + parser.collectPayload('analogSamples'); // skip formatting data for now - } } -} +}; + +// Unsupportet Frame Types +for (key in C.FRAME_TYPES) { + var val = C.FRAME_TYPES[key]; + if (typeof val === 'number') { + if (!frames.hasOwnProperty[val]) { + frames[val] = { + parse: function(parser) { + parser.json.not_implemented = true; + } + } + } else { + // + } + } +} From 7cf54e6c547f75b74e7d336bba9b520839ce2e1c Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 9 Jun 2012 13:44:17 +0200 Subject: [PATCH 075/140] Fixed references to constants. Also XBee object is now main event emitter, not serial. Also, switched to serialport2 for better windows support --- xbee.js | 113 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/xbee.js b/xbee.js index f8e4c4d..590d507 100644 --- a/xbee.js +++ b/xbee.js @@ -3,29 +3,32 @@ var EventEmitter = require('events').EventEmitter; var api = require("./xbee-api"); var serialport = require("serialport2"); //var serialport = require("serialport"); -var async = require('async'); + +var C = api.Constants; function XBee(options, data_parser) { EventEmitter.call(this); var self = this; + // Option Parsing if (typeof options === 'string') { options = {port: options}; } if (typeof data_parser !== 'function') { - console.log("loading simple parser"); + console.log("Loading simple parser, data will emitted on \\r\\n delimiter."); data_parser = require("./simple-parser"); } + // Current nodes - this.nodes = {}; + self.nodes = {}; - var packetBuilder = api.packetBuilder(); + // Assembles frames from serial port + self.packetBuilder = api.packetBuilder(); // Serial connection to the XBee - this.serial = new serialport.SerialPort(); - - this.serial.open(options.port, { + self.serial = new serialport.SerialPort(); + self.serial.open(options.port, { baudRate: options.baudrate || 57600, dataBits: 8, parity: 'none', @@ -35,44 +38,51 @@ function XBee(options, data_parser) { else self.configure(); }); - this.serial.on("data", function(buffer) { - //console.log(">"+buffer+"<"); - packetBuilder(this, buffer); + // Forward data to PacketBuilder + self.serial.on("data", function(buffer) { + self.packetBuilder(self, buffer); }); - this._onNodeDiscovery = function(data) { + + + /* Frame-specific Handlers */ + + // Whenever a node is identified (on ATND command). + self._onNodeIdentification = function(data) { var node = data.node; if (!self.nodes[node.remote64.hex]) { self.nodes[node.remote64.hex] = new Node(self, node, data_parser); self.emit("node", self.nodes[node.remote64.hex]); } else { self.nodes[node.remote64.hex].emit("reconnect"); - // RemoveAllListeners??ß + // RemoveAllListeners? // self.nodes[node.remote64.hex].removeAllListeners(); // } } - // On AT Response - this._onRemoteATResponse = function(res) { + // AT Command Responses from remote AT Commands + self._onRemoteCommandResponse = function(res) { // On Node Discovery Packet, emit new Node if (self.nodes[res.remote64.hex]) { - self.nodes[res.remote64.hex]._onRemoteATResponse(res); + self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); } else { console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); } } - this._onMessage = function(data) { + // Messages + self._onReceivePacket = function(data) { if (self.nodes[data.remote64.hex]) { //console.log("Data for %s", data.remote64.hex); - self.nodes[data.remote64.hex]._onData(data); + self.nodes[data.remote64.hex]._onReceivePacket(data); } else { console.log("ERROR: Data from unknown node!"); } } - this._onDataSampleRx = function(data) { + // Data samples (from XBee's I/O) + self._onDataSampleRx = function(data) { if (self.nodes[data.remote64.hex]) { self.nodes[data.remote64.hex]._onDataSampleRx(data); } else { @@ -80,10 +90,10 @@ function XBee(options, data_parser) { } } - this.serial.on("REMOTE_AT_RESPONSE", this._onRemoteATResponse); - this.serial.on("NODE_IDENTIFICATION", this._onNodeDiscovery); - this.serial.on("RECEIVE_RF_DATA", this._onMessage); - this.serial.on("DATA_SAMPLE_RX", this._onDataSampleRx); + self.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); + self.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); + self.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); + self.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); } util.inherits(XBee, EventEmitter); @@ -104,21 +114,38 @@ XBee.prototype.configure = function() { } var config = { - panid: QF('ID', api.bArr2HexStr), - id: QF('NI', api.bArr2Str), - sourceLow: QF('SL', api.bArr2HexStr), - sourceHigh: QF('SH', api.bArr2HexStr), + panid: QF('ID', api.bArr2HexStr), + id: QF('NI', api.bArr2Str), + sourceLow: QF('SL', api.bArr2HexStr), + sourceHigh: QF('SH', api.bArr2HexStr), + //maxPayloadSize: QF('NP', api.bArr2HexStr), // Returns ERROR :/ nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) }; - // Using async to start discovery only when all parameters have been read. - async.parallel(config, function(err, results) { + var done = function(err, results) { + if (err) return console.log("Failure to configure XBee module: %s", err); self.config = results; self.emit("configured", self.config); self.discover(function() { console.log("======================="); }); - }); + } + + // Using async to start discovery only when all parameters have been read. + var res_stop = Object.keys(config).length; + var results = {}; + for (k in config) { + config[k]((function(key) { + return function(err, data) { + if (err) return done(err, null); + results[key] = data; + // TODO: Timeout? + if (--res_stop === 0) { + done(null, results); + } + } + })(k)); + } } // Run network discovery. Associated nodes can report in @@ -126,12 +153,13 @@ XBee.prototype.configure = function() { XBee.prototype.discover = function(cb) { var frameId = this._AT('ND'); var self = this; + var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; // Whenever a node reports in, treat him as rejoined. - self.serial.on("AT_RESPONSE_"+frameId, self._onNodeDiscovery); + self.on(ATCBEvt, self._onNodeIdentification); // Wait for nodeDiscoveryTime ms before calling back setTimeout(function() { cb(); - self.serial.removeAllListeners("AT_RESPONSE_"+frameId); + self.removeAllListeners(ATCBEvt); }, this.config.nodeDiscoveryTime); } @@ -150,8 +178,18 @@ XBee.prototype._send = function(data, remote64, remote16) { frame.destination64 = remote64.dec; frame.destination16 = remote16.dec; } + + while (data.length > C.MAX_PAYLOAD_SIZE) { + frame.RFData = data.slice(0,C.MAX_PAYLOAD_SIZE); + data = data.slice(C.MAX_PAYLOAD_SIZE); + this.serial.write(frame.getBytes()); + } + frame.RFData = data; this.serial.write(frame.getBytes()); + + // TODO: If split, status callbacks should wait or all + // transmit-stauses, not just the last. return frame.frameId; } @@ -161,14 +199,14 @@ XBee.prototype._ATCB = function(cmd, val, cb) { val = undefined; } var frameId = this._AT(cmd, val); - this.serial.once("AT_RESPONSE_"+frameId, cb); + var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; + this.once(ATCBEvt, cb); } XBee.prototype._AT = function(cmd, val) { var frame = new api.ATCommand(); frame.setCommand(cmd); frame.commandParameter = val; - console.log(cmd+":"); this.serial.write(frame.getBytes()); return frame.frameId; } @@ -204,18 +242,19 @@ util.inherits(Node, EventEmitter); Node.prototype.send = function(data, cb) { var frameId = this.xbee._send(data, this.remote64, this.remote16); if (typeof cb === 'function') { - this.xbee.serial.once("TX_TRANSMIT_STATUS_"+frameId, function(data) { + var TXStatusCBEvt = C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frameId; + this.xbee.once(TXStatusCBEvt, function(data) { var error = false; - if (data.deliveryStatus != 0x00) { + if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { error = data; - error.msg = api.DELIVERY_STATES[data.deliveryStatus]; + error.msg = C.DELIVERY_STATUS[data.deliveryStatus]; } cb(error); }); } } -Node.prototype._onData = function(data) { +Node.prototype._onReceivePacket = function(data) { // Send the whole data object, or just the parsed msg? this.parser.parse(api.bArr2Str(data.rawData)); } From fea78f4873bfd7eb6547bf23bfcc43d0b1baf061 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 9 Jun 2012 13:45:36 +0200 Subject: [PATCH 076/140] 0.0.9, ditched async, moved to serialport2 --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 176efd0..5aaac64 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,12 @@ { "name" : "svd-xbee-sp2", - "version" : "0.0.8", + "version" : "0.0.9", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "main": "xbee.js", "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport2" : "0.0.x", - "async" : "0.1.x" + "serialport2" : "0.0.x" }, "repository": { "type" : "git", From 85b543ca6d040f183ea86191c8a65c59852dc149 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 9 Jun 2012 13:49:53 +0200 Subject: [PATCH 077/140] svd-xbee-sp2 -> svd-xbee --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5aaac64..c2d877d 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,4 @@ -{ "name" : "svd-xbee-sp2", +{ "name" : "svd-xbee", "version" : "0.0.9", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", From 9c2a68d759d2146b38b265affabe4e9e3c5cdc66 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Jun 2012 11:21:33 +0200 Subject: [PATCH 078/140] added contributors --- package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.json b/package.json index c2d877d..25be77b 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,12 @@ "version" : "0.0.9", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", + "maintainers": "Jan Kolkmeier ", + "contributors": [ + "Jan Kolkmeier ", + "Richard Morrison ", + "msealand" + ], "main": "xbee.js", "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", From 6ff81569526bb6b6a90ae46c6cd6d57c84a3eaff Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Jun 2012 11:36:15 +0200 Subject: [PATCH 079/140] Fixed bug with incrementFrameId() --- xbee-api.js | 940 ++++++++++++++++++++++++++-------------------------- 1 file changed, 471 insertions(+), 469 deletions(-) diff --git a/xbee-api.js b/xbee-api.js index 5963b79..2681610 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -1,469 +1,471 @@ -var Buffer = require('buffer').Buffer; -var util = require('util'); - -var C = exports.Constants = require('./constants.js'); -exports = module.exports; - -exports.dec2Hex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - - while (hex.length < padding) { - hex = "0" + hex; - } - - return hex; -} - -exports.bArr2HexStr = function(a) { - var s = ''; - for(i in a) { - s += exports.dec2Hex(a[i]); - } - return s; -} - -exports.bArr2Str = function(a) { - var s = ''; - for(i in a) { - s += String.fromCharCode(a[i]); - } - return s; -} - -exports.bArr2Dec = function(a) { - // given a byte array like [3,21], convert to a decimal value. - // e.g. [3,21] --> 3 * 256 + 21 = 789 - var r = 0; - for(var i = 0; i < a.length; i++) { - var power = a.length - i - 1; - r += a[i] * Math.pow(256,power); - } - return r -} - -// module-level variable for storing a frameId. -// Gets incremented by 1 each time it's used, so that you can -// tell which responses relate to which XBee commands -var frameId = 0x00; - -function incrementFrameId() { - // increment frameId and make sure it's <=255 - frameId += 1; - frameId %= 256; - return frameId; -} - -// constructor for an outgoing Packet. -var Packet = function() { - this.frameId = incrementFrameId(); -}; - -// call getBytes to get a JS array of byte values, ready to send down the serial port -Packet.prototype.getBytes = function() { - // build a JS array to hold the bytes - var packetdata = [C.START_BYTE]; - - // calculate the length bytes. First, get the entire payload by calling the internal function - var payload = this.getPayload(); - - // least significant length byte is easy - var len_lsb = payload.length % 256; - - // if payload length is greater than 255, have to calculate the more significant byte... - if (payload.length > 255) { - var len_msb = payload.length >>> 8; - } else { - //...otherwise the MSB is zero - var len_msb = 0; - } - - // add the length bytes to our growing packet array - packetdata.push(len_msb); - packetdata.push(len_lsb); - - // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array - var running_total = 0; - - for(var j = 0; j < payload.length; j++) { - packetdata.push(payload[j]); - running_total += payload[j]; - } - - checksum = 255 - (running_total % 256); - - // finally append the checksum byte and return the packet as a JS array - packetdata.push(checksum); - - - var string = ""; - for (i in packetdata) { - string += String.fromCharCode(packetdata[i]); - } - - var res = new Buffer(packetdata); - - //console.log(packetdata); - //console.log(util.inspect(res)); - - return res; -} - -Packet.prototype.getPayload = function() { - // this function is overridden by subclasses - return this.payload; -} - -exports.Packet = Packet; - -// ATCommand is for setting/reading AT registers on the local XBee node. -var ATCommand = function() { - this.frameId = incrementFrameId(); -}; -util.inherits(ATCommand, Packet); - -ATCommand.prototype.setCommand = function(strCmd) { - // take the ascii command and save it internally as byte values command0 and command1 - this.command0 = strCmd.charCodeAt(0); - this.command1 = strCmd.charCodeAt(1); -} - -ATCommand.prototype.getPayload = function() { - // Returns a JS array of byte values - // which form the payload of an AT command packet. - // Uses command0, command1 and commandParameter to build the payload. - - // begin with the frame type and frame ID - var payload = [C.FRAME_TYPE.AT_COMMAND, this.frameId]; - - // add two bytes to identify which AT command is being used - payload.push(this.command0); - payload.push(this.command1); - - // this.commandParameter can either be undefined (to query an AT register), or an array (to set an AT register) - if (this.commandParameter) { - for(var j=0; j 0) && (packpos > 2)) { - if (packet.length < packlen) { - packet.push(b); - running_total += b; - } else { - checksum = b; - } - } - - - // Packet is complete. Parse & Emit - if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // There will still be a checksum byte. Currently this is ignored - if (!checksum === 255 - (running_total % 256)) { - console.log("CHECKSUM_MISMATCH"); - } else { - var parser = new PacketParser(packet) - var json = parser.parse(); - if (json.not_implemented) { - console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); - } else { - var evt = json.ft; - if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, - C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, - C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) evt += C.EVT_SEP+json.frameId; - emitter.emit(evt, json); - } - } - } - } - }; -} - -// Packet Parser Class. Used to parse packets if they are known -var PacketParser = function(p) { - this.json = { - ft: p.splice(0,1)[0], - } - - // Used as pointer to the object data is parsed into - this.write = this.json; - this.payload = p; -} - -PacketParser.prototype.parse = function() { - if (false) { // TODO: Debug option - this.json.desc = C.FRAME_TYPES[this.json.ft]; - } - - this.frames[this.json.ft].parse(this); - - return this.json; -} - -PacketParser.prototype.readAddr = function(name, length) { - var dec = this.payload.splice(0, length); - this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } - return this; -} - -PacketParser.prototype.readByte = function(name, length) { - if (typeof length === 'number') - this.write[name] = this.payload.splice(0,length); - else this.write[name] = this.payload.splice(0,1)[0]; - return this; -} - -PacketParser.prototype.readAddr64 = function(name) { - return this.readAddr(name, 8); -} - -PacketParser.prototype.readAddr16 = function(name) { - return this.readAddr(name, 2); -} - -PacketParser.prototype.readString = function(name, length) { - this.write[name] = ""; - if (typeof length === 'number') { - for (var i = 0; i < length; i++) - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } else { - while(this.payload[0] != 0x00) { - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } - this.payload.splice(0,1); // Read 0x00 away - } - return this; -} - -PacketParser.prototype.collectPayload = function(name) { - this.write[name] = this.payload.splice(0); - return this; -} - -var frames = PacketParser.prototype.frames = {}; - -frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { - parse: function(parser) { - parser - .readAddr64('sender64') - .readAddr16('sender16') - .readByte('recieveOptions'); - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readByte('transmitRetryCount') - .readByte('deliveryStatus') - .readByte('discoveryStatus') - } -}; - -frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readString('command', 2) - .readByte('commandStatus') - if (parser.json.command == 'ND') { - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } - } -}; - -frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readAddr64('remote64') - .readString('command', 2) - .readByte('commandStatus') - .collectPayload('commandData'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawData'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .readByte('numSamples') - .readByte('digitalChannelMask', 2) - .readByte('analogChannelMask') - if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) - parser.readByte('digitalSamples',2); - if (parser.json.analogChannelMask > 0) - parser.collectPayload('analogSamples'); - // skip formatting data for now - } -}; - -// Unsupportet Frame Types -for (key in C.FRAME_TYPES) { - var val = C.FRAME_TYPES[key]; - if (typeof val === 'number') { - if (!frames.hasOwnProperty[val]) { - frames[val] = { - parse: function(parser) { - parser.json.not_implemented = true; - } - } - } else { - // - } - } -} +var Buffer = require('buffer').Buffer; +var util = require('util'); + +var C = exports.Constants = require('./constants.js'); +exports = module.exports; + +exports.dec2Hex = function(d, padding) { + var hex = Number(d).toString(16); + padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; + + while (hex.length < padding) { + hex = "0" + hex; + } + + return hex; +} + +exports.bArr2HexStr = function(a) { + var s = ''; + for(i in a) { + s += exports.dec2Hex(a[i]); + } + return s; +} + +exports.bArr2Str = function(a) { + var s = ''; + for(i in a) { + s += String.fromCharCode(a[i]); + } + return s; +} + +exports.bArr2Dec = function(a) { + // given a byte array like [3,21], convert to a decimal value. + // e.g. [3,21] --> 3 * 256 + 21 = 789 + var r = 0; + for(var i = 0; i < a.length; i++) { + var power = a.length - i - 1; + r += a[i] * Math.pow(256,power); + } + return r +} + +// module-level variable for storing a frameId. +// Gets incremented by 1 each time it's used, so that you can +// tell which responses relate to which XBee commands +var frameId = 0x00; + +function incrementFrameId() { + frameId += 1; + frameId %= 126; // fails if it exactly 126 (0x7e is start byte) + if (frameId == 0) frameId = 1; // 0x00 means: no response expected + return frameId; +} + +// constructor for an outgoing Packet. +var Packet = function() { + this.frameId = incrementFrameId(); +}; + +// call getBytes to get a JS array of byte values, ready to send down the serial port +Packet.prototype.getBytes = function() { + // build a JS array to hold the bytes + var packetdata = [C.START_BYTE]; + + // calculate the length bytes. First, get the entire payload by calling the internal function + var payload = this.getPayload(); + + // least significant length byte is easy + var len_lsb = payload.length % 256; + + // if payload length is greater than 255, have to calculate the more significant byte... + if (payload.length > 255) { + var len_msb = payload.length >>> 8; + } else { + //...otherwise the MSB is zero + var len_msb = 0; + } + + // add the length bytes to our growing packet array + packetdata.push(len_msb); + packetdata.push(len_lsb); + + // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array + var running_total = 0; + + for(var j = 0; j < payload.length; j++) { + packetdata.push(payload[j]); + running_total += payload[j]; + } + + checksum = 255 - (running_total % 256); + + // finally append the checksum byte and return the packet as a JS array + packetdata.push(checksum); + + + var string = ""; + for (i in packetdata) { + string += String.fromCharCode(packetdata[i]); + } + + var res = new Buffer(packetdata); + + //console.log(packetdata); + //console.log(util.inspect(res)); + + return res; +} + +Packet.prototype.getPayload = function() { + // this function is overridden by subclasses + return this.payload; +} + +exports.Packet = Packet; + +// ATCommand is for setting/reading AT registers on the local XBee node. +var ATCommand = function() { + this.frameId = incrementFrameId(); +}; +util.inherits(ATCommand, Packet); + +ATCommand.prototype.setCommand = function(strCmd) { + // take the ascii command and save it internally as byte values command0 and command1 + this.command0 = strCmd.charCodeAt(0); + this.command1 = strCmd.charCodeAt(1); +} + +ATCommand.prototype.getPayload = function() { + // Returns a JS array of byte values + // which form the payload of an AT command packet. + // Uses command0, command1 and commandParameter to build the payload. + + // begin with the frame type and frame ID + var payload = [C.FRAME_TYPE.AT_COMMAND, this.frameId]; + + // add two bytes to identify which AT command is being used + payload.push(this.command0); + payload.push(this.command1); + + // this.commandParameter can either be undefined (to query an AT register), or an array (to set an AT register) + if (this.commandParameter) { + for(var j=0; j 0) && (packpos > 2)) { + if (packet.length < packlen) { + packet.push(b); + running_total += b; + } else { + checksum = b; + } + } + + + // Packet is complete. Parse & Emit + if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { + // There will still be a checksum byte. Currently this is ignored + if (!checksum === 255 - (running_total % 256)) { + console.log("CHECKSUM_MISMATCH"); + } else { + var parser = new PacketParser(packet) + var json = parser.parse(); + if (json.not_implemented) { + console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); + } else { + var evt = json.ft; + if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, + C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, + C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { + evt += C.EVT_SEP+json.frameId; + } + emitter.emit(evt, json); + } + } + } + } + }; +} + +// Packet Parser Class. Used to parse packets if they are known +var PacketParser = function(p) { + this.json = { + ft: p.splice(0,1)[0], + } + + // Used as pointer to the object data is parsed into + this.write = this.json; + this.payload = p; +} + +PacketParser.prototype.parse = function() { + if (false) { // TODO: Debug option + this.json.desc = C.FRAME_TYPES[this.json.ft]; + } + + this.frames[this.json.ft].parse(this); + + return this.json; +} + +PacketParser.prototype.readAddr = function(name, length) { + var dec = this.payload.splice(0, length); + this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } + return this; +} + +PacketParser.prototype.readByte = function(name, length) { + if (typeof length === 'number') + this.write[name] = this.payload.splice(0,length); + else this.write[name] = this.payload.splice(0,1)[0]; + return this; +} + +PacketParser.prototype.readAddr64 = function(name) { + return this.readAddr(name, 8); +} + +PacketParser.prototype.readAddr16 = function(name) { + return this.readAddr(name, 2); +} + +PacketParser.prototype.readString = function(name, length) { + this.write[name] = ""; + if (typeof length === 'number') { + for (var i = 0; i < length; i++) + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } else { + while(this.payload[0] != 0x00) { + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } + this.payload.splice(0,1); // Read 0x00 away + } + return this; +} + +PacketParser.prototype.collectPayload = function(name) { + this.write[name] = this.payload.splice(0); + return this; +} + +var frames = PacketParser.prototype.frames = {}; + +frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } +}; + +frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + if (parser.json.command == 'ND') { + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') + } + } +}; + +frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('rawData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .readByte('numSamples') + .readByte('digitalChannelMask', 2) + .readByte('analogChannelMask') + if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) + parser.readByte('digitalSamples',2); + if (parser.json.analogChannelMask > 0) + parser.collectPayload('analogSamples'); + // skip formatting data for now + } +}; + +// Unsupportet Frame Types +for (key in C.FRAME_TYPES) { + var val = C.FRAME_TYPES[key]; + if (typeof val === 'number') { + if (!frames.hasOwnProperty[val]) { + frames[val] = { + parse: function(parser) { + parser.json.not_implemented = true; + } + } + } else { + // + } + } +} From 789d5282177ead71527686659f432806b0c0971f Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Jun 2012 11:40:10 +0200 Subject: [PATCH 080/140] Frames are now being queued before sent to serial. This didn't seem an issue before, but at least on windows this regularly failed. Now every frame is being properly split and queued. Where the queue advances after receiving status response from xbee about previous frame. We probably could work more parallel by having seperate queues per node. --- xbee.js | 565 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 291 insertions(+), 274 deletions(-) diff --git a/xbee.js b/xbee.js index 590d507..8a38585 100644 --- a/xbee.js +++ b/xbee.js @@ -1,274 +1,291 @@ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var api = require("./xbee-api"); -var serialport = require("serialport2"); -//var serialport = require("serialport"); - -var C = api.Constants; - -function XBee(options, data_parser) { - EventEmitter.call(this); - var self = this; - - // Option Parsing - if (typeof options === 'string') { - options = {port: options}; - } - - if (typeof data_parser !== 'function') { - console.log("Loading simple parser, data will emitted on \\r\\n delimiter."); - data_parser = require("./simple-parser"); - } - - // Current nodes - self.nodes = {}; - - // Assembles frames from serial port - self.packetBuilder = api.packetBuilder(); - - // Serial connection to the XBee - self.serial = new serialport.SerialPort(); - self.serial.open(options.port, { - baudRate: options.baudrate || 57600, - dataBits: 8, - parity: 'none', - stopBits: 1 - }, function(err) { - if (err) console.log("ERROR: "+err); - else self.configure(); - }); - - // Forward data to PacketBuilder - self.serial.on("data", function(buffer) { - self.packetBuilder(self, buffer); - }); - - - - /* Frame-specific Handlers */ - - // Whenever a node is identified (on ATND command). - self._onNodeIdentification = function(data) { - var node = data.node; - if (!self.nodes[node.remote64.hex]) { - self.nodes[node.remote64.hex] = new Node(self, node, data_parser); - self.emit("node", self.nodes[node.remote64.hex]); - } else { - self.nodes[node.remote64.hex].emit("reconnect"); - // RemoveAllListeners? - // self.nodes[node.remote64.hex].removeAllListeners(); - // - } - } - - // AT Command Responses from remote AT Commands - self._onRemoteCommandResponse = function(res) { - // On Node Discovery Packet, emit new Node - if (self.nodes[res.remote64.hex]) { - self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); - } else { - console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); - } - } - - // Messages - self._onReceivePacket = function(data) { - if (self.nodes[data.remote64.hex]) { - //console.log("Data for %s", data.remote64.hex); - self.nodes[data.remote64.hex]._onReceivePacket(data); - } else { - console.log("ERROR: Data from unknown node!"); - } - } - - // Data samples (from XBee's I/O) - self._onDataSampleRx = function(data) { - if (self.nodes[data.remote64.hex]) { - self.nodes[data.remote64.hex]._onDataSampleRx(data); - } else { - console.log("ERROR: Data sample from unknown node!"); - } - } - - self.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); - self.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); - self.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); - self.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); -} - -util.inherits(XBee, EventEmitter); - -XBee.prototype.configure = function() { - var self = this; - - // Returns a function that initiates an AT command to - // query a configuration parameter's value. - // To be passed to an async.parallel. - var QF = function(command, f) { // Format the result using f - f = typeof f !== 'undefined' ? f : function(a){return a}; - return function(cb) { - self._ATCB(command, function(data) { - cb(!(data.commandStatus==0x00), f(data.commandData)); - }); - } - } - - var config = { - panid: QF('ID', api.bArr2HexStr), - id: QF('NI', api.bArr2Str), - sourceLow: QF('SL', api.bArr2HexStr), - sourceHigh: QF('SH', api.bArr2HexStr), - //maxPayloadSize: QF('NP', api.bArr2HexStr), // Returns ERROR :/ - nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) - }; - - var done = function(err, results) { - if (err) return console.log("Failure to configure XBee module: %s", err); - self.config = results; - self.emit("configured", self.config); - self.discover(function() { - console.log("======================="); - }); - } - - // Using async to start discovery only when all parameters have been read. - var res_stop = Object.keys(config).length; - var results = {}; - for (k in config) { - config[k]((function(key) { - return function(err, data) { - if (err) return done(err, null); - results[key] = data; - // TODO: Timeout? - if (--res_stop === 0) { - done(null, results); - } - } - })(k)); - } -} - -// Run network discovery. Associated nodes can report in -// for config.nodeDiscoveryTime ms. -XBee.prototype.discover = function(cb) { - var frameId = this._AT('ND'); - var self = this; - var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; - // Whenever a node reports in, treat him as rejoined. - self.on(ATCBEvt, self._onNodeIdentification); - // Wait for nodeDiscoveryTime ms before calling back - setTimeout(function() { - cb(); - self.removeAllListeners(ATCBEvt); - }, this.config.nodeDiscoveryTime); -} - -XBee.prototype.broadcast = function(data) { - var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - var remote16 = [0xff,0xfe]; - this._send(data, remote64, remote16); -} - -XBee.prototype._send = function(data, remote64, remote16) { - var frame = new api.TransmitRFData(); - if (typeof remote64.dec === 'undefined') { - frame.destination64 = remote64; - frame.destination16 = remote16; - } else { - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - } - - while (data.length > C.MAX_PAYLOAD_SIZE) { - frame.RFData = data.slice(0,C.MAX_PAYLOAD_SIZE); - data = data.slice(C.MAX_PAYLOAD_SIZE); - this.serial.write(frame.getBytes()); - } - - frame.RFData = data; - this.serial.write(frame.getBytes()); - - // TODO: If split, status callbacks should wait or all - // transmit-stauses, not just the last. - return frame.frameId; -} - -XBee.prototype._ATCB = function(cmd, val, cb) { - if (typeof val === 'function') { - cb = val; - val = undefined; - } - var frameId = this._AT(cmd, val); - var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; - this.once(ATCBEvt, cb); -} - -XBee.prototype._AT = function(cmd, val) { - var frame = new api.ATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - this.serial.write(frame.getBytes()); - return frame.frameId; -} - -XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { - var frame = new api.ATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - if (typeof remote64.dec === 'undefined') { - frame.destination64 = remote64; - frame.destination16 = remote16; - } else { - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - } - this.serial.write(frame.getBytes()); - return frame.frameId; -} - -exports.XBee = XBee; - -function Node(xbee, params, data_parser) { - EventEmitter.call(this); - this.xbee = xbee; - this.id = params.id; - this.remote16 = params.remote16; - this.remote64 = params.remote64; - this.buffer = ""; - this.parser = data_parser(this); -} -util.inherits(Node, EventEmitter); - -Node.prototype.send = function(data, cb) { - var frameId = this.xbee._send(data, this.remote64, this.remote16); - if (typeof cb === 'function') { - var TXStatusCBEvt = C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frameId; - this.xbee.once(TXStatusCBEvt, function(data) { - var error = false; - if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { - error = data; - error.msg = C.DELIVERY_STATUS[data.deliveryStatus]; - } - cb(error); - }); - } -} - -Node.prototype._onReceivePacket = function(data) { - // Send the whole data object, or just the parsed msg? - this.parser.parse(api.bArr2Str(data.rawData)); -} - -Node.prototype._AT = function(cmd, val) { - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); -} - -Node.prototype._onATResponse = function(res) { - console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); -} - -Node.prototype._onDataSampleRx = function(res) { - this.emit('data_sample', res); -} - -exports.Node = Node; +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var api = require("./xbee-api"); +var serialport = require("serialport2"); +var async = require('async'); +//var serialport = require("serialport"); + +var C = api.Constants; + +function XBee(options, data_parser) { + EventEmitter.call(this); + var self = this; + + // Option Parsing + if (typeof options === 'string') { + options = {port: options}; + } + + if (typeof data_parser !== 'function') { + console.log("Loading simple parser, data will emitted on \\r\\n delimiter."); + data_parser = require("./simple-parser"); + } + + // Current nodes + self.nodes = {}; + + // Assembles frames from serial port + self.packetBuilder = api.packetBuilder(); + + // Serial connection to the XBee + self.serial = new serialport.SerialPort(); + self.serial.open(options.port, { + baudRate: options.baudrate || 57600, + dataBits: 8, + parity: 'none', + stopBits: 1 + }, function(err) { + if (err) console.log("ERROR: "+err); + else self.configure(); + }); + + // Forward data to PacketBuilder + self.serial.on("data", function(buffer) { + self.packetBuilder(self, buffer); + }); + + + + /* Frame-specific Handlers */ + + // Whenever a node is identified (on ATND command). + self._onNodeIdentification = function(data) { + var node = data.node; + if (!self.nodes[node.remote64.hex]) { + self.nodes[node.remote64.hex] = new Node(self, node, data_parser); + self.emit("node", self.nodes[node.remote64.hex]); + } else { + self.nodes[node.remote64.hex].emit("reconnect"); + // RemoveAllListeners? + // self.nodes[node.remote64.hex].removeAllListeners(); + // + } + } + + // AT Command Responses from remote AT Commands + self._onRemoteCommandResponse = function(res) { + // On Node Discovery Packet, emit new Node + if (self.nodes[res.remote64.hex]) { + self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); + } else { + console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); + } + } + + // Messages + self._onReceivePacket = function(data) { + if (self.nodes[data.remote64.hex]) { + //console.log("Data for %s", data.remote64.hex); + self.nodes[data.remote64.hex]._onReceivePacket(data); + } else { + console.log("ERROR: Data from unknown node!"); + } + } + + // Data samples (from XBee's I/O) + self._onDataSampleRx = function(data) { + if (self.nodes[data.remote64.hex]) { + self.nodes[data.remote64.hex]._onDataSampleRx(data); + } else { + console.log("ERROR: Data sample from unknown node!"); + } + } + + self.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); + self.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); + self.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); + self.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + + self._queue = async.queue(function(task, callback) { + async.series(task.tasks, function(err) { + if (err) { + console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + console.log("Task series stopped with error: "+err.msg); + } + callback(err); + if (typeof task._cb === 'function') task._cb(err); + }); + }, 1); +} + +util.inherits(XBee, EventEmitter); + +XBee.prototype.configure = function() { + var self = this; + + // Returns a function that initiates an AT command to + // query a configuration parameter's value. + // To be passed to an async.parallel. + var QF = function(command, f) { // Format the result using f + f = typeof f !== 'undefined' ? f : function(a){return a}; + return function(cb) { + self._ATCB(command, function(data) { + cb(!(data.commandStatus==0x00), f(data.commandData)); + }); + } + } + + var config = { + panid: QF('ID', api.bArr2HexStr), + id: QF('NI', api.bArr2Str), + sourceLow: QF('SL', api.bArr2HexStr), + sourceHigh: QF('SH', api.bArr2HexStr), + //maxPayloadSize: QF('NP', api.bArr2HexStr), // Returns ERROR :/ + nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) + }; + + var done = function(err, results) { + if (err) return console.log("Failure to configure XBee module: %s", err); + self.config = results; + self.emit("configured", self.config); + self.discover(function() { + console.log("======================="); + }); + } + + // Using async to start discovery only when all parameters have been read. + var res_stop = Object.keys(config).length; + var results = {}; + for (k in config) { + config[k]((function(key) { + return function(err, data) { + if (err) return done(err, null); + results[key] = data; + // TODO: Timeout? + if (--res_stop === 0) { + done(null, results); + } + } + })(k)); + } +} + +// Run network discovery. Associated nodes can report in +// for config.nodeDiscoveryTime ms. +XBee.prototype.discover = function(cb) { + var frameId = this._AT('ND'); + var self = this; + var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; + // Whenever a node reports in, treat him as rejoined. + self.on(ATCBEvt, self._onNodeIdentification); + // Wait for nodeDiscoveryTime ms before calling back + setTimeout(function() { + cb(); + self.removeAllListeners(ATCBEvt); + }, this.config.nodeDiscoveryTime); +} + +XBee.prototype.broadcast = function(data, cb) { + var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + var remote16 = [0xff,0xfe]; + this._send(data, remote64, remote16, cb); +} + +XBee.prototype._makeTask = function(bytes, frameId) { + var self = this; + var TXStatusCBEvt = C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frameId; + return function(cb) { + //console.log("EVT: "+TXStatusCBEvt); + //console.log("~~["+bytes.toString("ascii")+"]~~"); + self.serial.write(bytes); + // TODO TIMEOUT!! + self.once(TXStatusCBEvt, function(data) { + //console.log("CB: "+TXStatusCBEvt); + var error = null; + if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { + error = data; + error.msg = C.DELIVERY_STATUS[data.deliveryStatus]; + } + cb(error); + }); + } +} + + + +XBee.prototype._send = function(data, remote64, remote16, _cb) { + var self = this; + var tasks = []; + + while (data.length > 0) { + var frame = new api.TransmitRFData(); + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + frame.RFData = data.slice(0, C.MAX_PAYLOAD_SIZE); + data = data.slice(C.MAX_PAYLOAD_SIZE); + tasks.push(self._makeTask(frame.getBytes(), frame.frameId)); + } + if (self._queue.length()>0) console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + self._queue.push({ tasks: tasks, _cb: _cb }); +} + +XBee.prototype._ATCB = function(cmd, val, cb) { + if (typeof val === 'function') { + cb = val; + val = undefined; + } + var frameId = this._AT(cmd, val); + var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; + this.once(ATCBEvt, cb); +} + +XBee.prototype._AT = function(cmd, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + //console.log("USE QUEUE!"); + this.serial.write(frame.getBytes()); + return frame.frameId; +} + +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + if (typeof remote64.dec === 'undefined') { + frame.destination64 = remote64; + frame.destination16 = remote16; + } else { + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + } + //console.log("USE QUEUE!"); + this.serial.write(frame.getBytes()); + return frame.frameId; +} + +exports.XBee = XBee; + +function Node(xbee, params, data_parser) { + EventEmitter.call(this); + this.xbee = xbee; + this.id = params.id; + this.remote16 = params.remote16; + this.remote64 = params.remote64; + this.buffer = ""; + this.parser = data_parser(this); +} +util.inherits(Node, EventEmitter); + +Node.prototype.send = function(data, cb) { + this.xbee._send(data, this.remote64, this.remote16, cb); +} + +Node.prototype._onReceivePacket = function(data) { + // Send the whole data object, or just the parsed msg? + this.parser.parse(api.bArr2Str(data.rawData)); +} + +Node.prototype._AT = function(cmd, val) { + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); +} + +Node.prototype._onATResponse = function(res) { + console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); +} + +Node.prototype._onDataSampleRx = function(res) { + this.emit('data_sample', res); +} + +exports.Node = Node; From ccbaff184d7feec1654970826b2084cf8f97516f Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Jun 2012 14:58:14 +0200 Subject: [PATCH 081/140] 0.1.0 --- package.json | 5 +++-- xbee.js | 17 ++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 25be77b..136fcfc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.0.9", + "version" : "0.1.0", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "maintainers": "Jan Kolkmeier ", @@ -12,7 +12,8 @@ "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport2" : "0.0.x" + "serialport2" : "0.0.x", + "async" : "0.1.x" }, "repository": { "type" : "git", diff --git a/xbee.js b/xbee.js index 8a38585..671d372 100644 --- a/xbee.js +++ b/xbee.js @@ -55,6 +55,8 @@ function XBee(options, data_parser) { self.nodes[node.remote64.hex] = new Node(self, node, data_parser); self.emit("node", self.nodes[node.remote64.hex]); } else { + // update 16-bit address, as it may change during reconnects. + self.nodes[node.remote64.hex].remote16 = node.remote16; self.nodes[node.remote64.hex].emit("reconnect"); // RemoveAllListeners? // self.nodes[node.remote64.hex].removeAllListeners(); @@ -99,10 +101,9 @@ function XBee(options, data_parser) { self._queue = async.queue(function(task, callback) { async.series(task.tasks, function(err) { if (err) { - console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - console.log("Task series stopped with error: "+err.msg); + console.log("Error writing data to XBee: "+err.msg); } - callback(err); + callback(); // maybe pass the error here, too? Would clear whole queue if (typeof task._cb === 'function') task._cb(err); }); }, 1); @@ -189,8 +190,13 @@ XBee.prototype._makeTask = function(bytes, frameId) { //console.log("~~["+bytes.toString("ascii")+"]~~"); self.serial.write(bytes); // TODO TIMEOUT!! + var timeout = setTimeout(function() { + cb({ msg: "Never got Transmit status from XBee" }); + }, 100); + self.once(TXStatusCBEvt, function(data) { - //console.log("CB: "+TXStatusCBEvt); + clearTimeout(timeout); + console.log("CB: "+TXStatusCBEvt); var error = null; if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { error = data; @@ -201,8 +207,6 @@ XBee.prototype._makeTask = function(bytes, frameId) { } } - - XBee.prototype._send = function(data, remote64, remote16, _cb) { var self = this; var tasks = []; @@ -215,7 +219,6 @@ XBee.prototype._send = function(data, remote64, remote16, _cb) { data = data.slice(C.MAX_PAYLOAD_SIZE); tasks.push(self._makeTask(frame.getBytes(), frame.frameId)); } - if (self._queue.length()>0) console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); self._queue.push({ tasks: tasks, _cb: _cb }); } From 786e67d72d2a3321fc9e14ef59f799640a90ced7 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Jun 2012 15:29:52 +0200 Subject: [PATCH 082/140] ...I need to use debug flags... --- package.json | 2 +- xbee.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 136fcfc..7574a3b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.1.0", + "version" : "0.1.1", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "maintainers": "Jan Kolkmeier ", diff --git a/xbee.js b/xbee.js index 671d372..3e192ca 100644 --- a/xbee.js +++ b/xbee.js @@ -196,7 +196,7 @@ XBee.prototype._makeTask = function(bytes, frameId) { self.once(TXStatusCBEvt, function(data) { clearTimeout(timeout); - console.log("CB: "+TXStatusCBEvt); + //console.log("CB: "+TXStatusCBEvt); var error = null; if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { error = data; From bad5922b6d3b3add45c3d0cc116e54c9a31f8650 Mon Sep 17 00:00:00 2001 From: Warren Bloomer Date: Sun, 15 Jul 2012 12:11:54 +1000 Subject: [PATCH 083/140] Changed ATCommand to RemoteATCommand --- xbee.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbee.js b/xbee.js index 3e192ca..5ed8539 100644 --- a/xbee.js +++ b/xbee.js @@ -242,7 +242,7 @@ XBee.prototype._AT = function(cmd, val) { } XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { - var frame = new api.ATCommand(); + var frame = new api.RemoteATCommand(); frame.setCommand(cmd); frame.commandParameter = val; if (typeof remote64.dec === 'undefined') { From 786a02a3a4cbaa718966f2bff8ce439f8cdb7b1a Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 1 Aug 2012 21:39:38 +0200 Subject: [PATCH 084/140] long due commit --- constants.js | 16 ++++ examples/simple.js | 12 +-- package.json | 2 +- simple-parser.js | 6 +- xbee-api.js | 48 ++++++++--- xbee.js | 209 ++++++++++++++++++++++++++------------------- 6 files changed, 183 insertions(+), 110 deletions(-) diff --git a/constants.js b/constants.js index d5eac87..eae7e42 100644 --- a/constants.js +++ b/constants.js @@ -1,11 +1,15 @@ exports = module.exports; exports.START_BYTE = 0x7E; +exports.ESCAPE = 0x7D; +exports.XOFF = 0x13; +exports.XON = 0x11; exports.EVT_SEP = "_"; exports.MAX_PAYLOAD_SIZE = 74; var ft = exports.FRAME_TYPE = {}; var diss = exports.DISCOVERY_STATUS = {}; var dels = exports.DELIVERY_STATUS = {}; +var coms = exports.COMMAND_STATUS = {}; var ro = exports.RECEIVE_OPTIONS = {}; @@ -47,6 +51,18 @@ ft[0xA1] = "Route Record Indicator (0xA1)"; ft.MTO_ROUTE_REQUEST = 0xA3; ft[0xA3] = "Many-to-One Route Request Indicator (0xA3)"; +// Command Status +coms.OK = 0x00; +coms[0x00] = "OK (0x00)"; +coms.ERROR = 0x01; +coms[0x01] = "ERROR (0x01)"; +coms.INVALID_COMMAND = 0x02; +coms[0x02] = "Invalid Command (0x02)"; +coms.INVALID_PARAMETER = 0x03; +coms[0x03] = "Invalid Command (0x03)"; +coms.REMOTE_CMD_TRANS_FAILURE = 0x04; +coms[0x04] = "Remote Command Transmission Failed (0x04)"; + // Delivery Status dels.SUCCESS = 0x00; dels[0x00] = "Success (0x00)"; diff --git a/examples/simple.js b/examples/simple.js index cbee222..539f2b8 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,8 +1,10 @@ var util = require('util'); -var XBee = require('svd-xbee').XBee; +var XBee = require('../xbee.js').XBee; +var Parser = require('svd-http-parser'); // Replace with your xbee's UART location -var xbee = new XBee('/dev/ttyO1'); +//var xbee = new XBee('/dev/ttyO1'); +var xbee = new XBee('/dev/ttyO1', Parser); xbee.on("configured", function(config) { console.log("XBee Config: %s", util.inspect(config)); @@ -11,9 +13,9 @@ xbee.on("configured", function(config) { xbee.on("node", function(node) { console.log("Node %s connected", node.id); - node.on("data", function(data) { - node.send("pong"); - console.log("%s: %s", node.id, util.inspect(data)); + node.on("/register", function(data) { + //node.send("pong"); + console.log("%s: %s", node.id, util.inspect(data.payload)); }); }); diff --git a/package.json b/package.json index 7574a3b..ffd6182 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { - "serialport2" : "0.0.x", + "serialport" : "1.0.x", "async" : "0.1.x" }, "repository": { diff --git a/simple-parser.js b/simple-parser.js index 5517592..046c046 100644 --- a/simple-parser.js +++ b/simple-parser.js @@ -1,16 +1,16 @@ module.exports = function (device) { + var delimiter = "\r"; function DataParser(device) { this.device = device; this.buffer = ""; } - DataParser.prototype.parse = function(data) { this.buffer += data; - var split = this.buffer.indexOf('\r\n'); + var split = this.buffer.indexOf(delimiter); while (split > -1) { this.device.emit('data', this.buffer.slice(0,split)); this.buffer = this.buffer.slice(split+2); - split = this.buffer.indexOf('\r\n'); + split = this.buffer.indexOf(delimiter); } } diff --git a/xbee-api.js b/xbee-api.js index 2681610..13dc85d 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -45,11 +45,11 @@ exports.bArr2Dec = function(a) { // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands -var frameId = 0x00; +var frameId = 0x30; function incrementFrameId() { - frameId += 1; - frameId %= 126; // fails if it exactly 126 (0x7e is start byte) + frameId++; + frameId %= 255; // fails if it exactly 126 (0x7e is start byte) if (frameId == 0) frameId = 1; // 0x00 means: no response expected return frameId; } @@ -95,18 +95,26 @@ Packet.prototype.getBytes = function() { // finally append the checksum byte and return the packet as a JS array packetdata.push(checksum); - - var string = ""; - for (i in packetdata) { - string += String.fromCharCode(packetdata[i]); + // could be shorter: + var res = [packetdata[0]]; + for (var p = 1; p>> "+util.inspect(out)); - return res; + return out; } Packet.prototype.getPayload = function() { @@ -246,12 +254,24 @@ exports.packetBuilder = function () { var packlen = 0; // used to remember the length of the current packet. var running_total = 0; var checksum = -1; + var escape_next = false; - return function (emitter, buffer) { + return function(emitter, buffer) { // Collecting data. for(var i=0; i < buffer.length; i++) { b = buffer[i]; // store the working byte - packpos += 1; + + if (packpos > 0 && b == C.ESCAPE) { + escape_next = true; + continue; + } + + if (escape_next) { + b = 0x20 ^ b; + escape_next = false; + } + + packpos += 1; // Detected start of packet. if (b == C.START_BYTE) { @@ -260,11 +280,11 @@ exports.packetBuilder = function () { running_total = 0; checksum = -1; packet = []; + escape_next = false; } if (packpos == 1) packlen += b << 8; // most significant bit of the length if (packpos == 2) packlen += b; // least significant bit of the length - if ((packlen > 0) && (packpos > 2)) { if (packet.length < packlen) { packet.push(b); @@ -281,6 +301,7 @@ exports.packetBuilder = function () { if (!checksum === 255 - (running_total % 256)) { console.log("CHECKSUM_MISMATCH"); } else { + //console.log(">>> "+util.inspect(packet)); var parser = new PacketParser(packet) var json = parser.parse(); if (json.not_implemented) { @@ -292,6 +313,7 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } + //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt); emitter.emit(evt, json); } } diff --git a/xbee.js b/xbee.js index 3e192ca..ad29a2f 100644 --- a/xbee.js +++ b/xbee.js @@ -1,9 +1,8 @@ var util = require('util'); var EventEmitter = require('events').EventEmitter; var api = require("./xbee-api"); -var serialport = require("serialport2"); +var serialport = require("serialport"); var async = require('async'); -//var serialport = require("serialport"); var C = api.Constants; @@ -24,27 +23,27 @@ function XBee(options, data_parser) { // Current nodes self.nodes = {}; - // Assembles frames from serial port - self.packetBuilder = api.packetBuilder(); - // Serial connection to the XBee - self.serial = new serialport.SerialPort(); - self.serial.open(options.port, { - baudRate: options.baudrate || 57600, - dataBits: 8, + self.serial = new serialport.SerialPort(options.port, { + baudrate: options.baudrate || 57600, + databits: 8, + stopbits: 1, parity: 'none', - stopBits: 1 - }, function(err) { - if (err) console.log("ERROR: "+err); - else self.configure(); + parser: api.packetBuilder() }); - // Forward data to PacketBuilder - self.serial.on("data", function(buffer) { - self.packetBuilder(self, buffer); - }); + self.serial.on("open", self.configure.bind(self)); + + var exit = function() { + console.log("Closing Serial"); + self.serial.close(function(err) { + if (err) console.log(err); + else console.log("Done"); + process.exit(); + }); + } + process.on('SIGINT', exit); - /* Frame-specific Handlers */ @@ -57,10 +56,8 @@ function XBee(options, data_parser) { } else { // update 16-bit address, as it may change during reconnects. self.nodes[node.remote64.hex].remote16 = node.remote16; + self.nodes[node.remote64.hex].id = node.id; self.nodes[node.remote64.hex].emit("reconnect"); - // RemoveAllListeners? - // self.nodes[node.remote64.hex].removeAllListeners(); - // } } @@ -76,12 +73,13 @@ function XBee(options, data_parser) { // Messages self._onReceivePacket = function(data) { - if (self.nodes[data.remote64.hex]) { - //console.log("Data for %s", data.remote64.hex); - self.nodes[data.remote64.hex]._onReceivePacket(data); - } else { + if (!self.nodes[data.remote64.hex]) { + var _data = { node:data }; + _data.node.id = "UNKNOWN"; + self._onNodeIdentification(_data); console.log("ERROR: Data from unknown node!"); } + self.nodes[data.remote64.hex]._onReceivePacket(data); } // Data samples (from XBee's I/O) @@ -93,18 +91,16 @@ function XBee(options, data_parser) { } } - self.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); - self.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); - self.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); - self.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + self.serial.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); + self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); + self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); self._queue = async.queue(function(task, callback) { - async.series(task.tasks, function(err) { - if (err) { - console.log("Error writing data to XBee: "+err.msg); - } - callback(); // maybe pass the error here, too? Would clear whole queue - if (typeof task._cb === 'function') task._cb(err); + async.series(task.packets, function(err, data) { + if (err) console.log("Error writing data to XBee: "+err); + if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); + callback(); }); }, 1); } @@ -113,34 +109,40 @@ util.inherits(XBee, EventEmitter); XBee.prototype.configure = function() { var self = this; - + /* + self._ATCB('ID', undefined, function(data) { + console.log("ID: "+util.inspect(data)); + }); + */ // Returns a function that initiates an AT command to // query a configuration parameter's value. // To be passed to an async.parallel. - var QF = function(command, f) { // Format the result using f + var QF = function(command, val, f) { // Format the result using f f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { - self._ATCB(command, function(data) { - cb(!(data.commandStatus==0x00), f(data.commandData)); + self._AT(command, val, function(err, data) { + cb(err, f(data.commandData)); }); } } var config = { - panid: QF('ID', api.bArr2HexStr), - id: QF('NI', api.bArr2Str), - sourceLow: QF('SL', api.bArr2HexStr), - sourceHigh: QF('SH', api.bArr2HexStr), + panid: QF('ID', undefined, api.bArr2HexStr), + id: QF('NI', undefined, api.bArr2Str), + sourceLow: QF('SL', undefined, api.bArr2HexStr), + sourceHigh: QF('SH', undefined, api.bArr2HexStr), //maxPayloadSize: QF('NP', api.bArr2HexStr), // Returns ERROR :/ - nodeDiscoveryTime: QF('NT', function(a) { return 100 * api.bArr2Dec(a); }) + setNodeDiscoveryTime: QF('NT', [0x96], function(a) { return "NT SET: "+api.bArr2Dec(a); }), + nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.bArr2Dec(a); }) }; var done = function(err, results) { if (err) return console.log("Failure to configure XBee module: %s", err); self.config = results; self.emit("configured", self.config); + console.log("[Start Discovery]"); self.discover(function() { - console.log("======================="); + console.log("[End Discovery]"); }); } @@ -164,16 +166,14 @@ XBee.prototype.configure = function() { // Run network discovery. Associated nodes can report in // for config.nodeDiscoveryTime ms. XBee.prototype.discover = function(cb) { - var frameId = this._AT('ND'); var self = this; - var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; - // Whenever a node reports in, treat him as rejoined. - self.on(ATCBEvt, self._onNodeIdentification); - // Wait for nodeDiscoveryTime ms before calling back + // todo: could be nicer, pass _onNodeIdentification to _AT - but it's "once"... + var cbid = self._AT('ND'); + self.serial.on(cbid, self._onNodeIdentification); setTimeout(function() { cb(); - self.removeAllListeners(ATCBEvt); - }, this.config.nodeDiscoveryTime); + self.removeAllListeners(cbid); + }, self.config.nodeDiscoveryTime || 6000); } XBee.prototype.broadcast = function(data, cb) { @@ -182,65 +182,75 @@ XBee.prototype.broadcast = function(data, cb) { this._send(data, remote64, remote16, cb); } -XBee.prototype._makeTask = function(bytes, frameId) { +XBee.prototype._makeTask = function(packet) { var self = this; - var TXStatusCBEvt = C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frameId; - return function(cb) { - //console.log("EVT: "+TXStatusCBEvt); - //console.log("~~["+bytes.toString("ascii")+"]~~"); - self.serial.write(bytes); - // TODO TIMEOUT!! - var timeout = setTimeout(function() { - cb({ msg: "Never got Transmit status from XBee" }); - }, 100); - - self.once(TXStatusCBEvt, function(data) { - clearTimeout(timeout); - //console.log("CB: "+TXStatusCBEvt); - var error = null; - if (data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { - error = data; - error.msg = C.DELIVERY_STATUS[data.deliveryStatus]; + //console.log("Making task: "+packet.cbid); + return function Writer(cb) { + //console.log("<<< "+util.inspect(packet.data)); + self.serial.write(packet.data, function(err, results) { + if (err) { + cb(err); + } else { + if (results != packet.data.length) return cb(new Error("Not all bytes written")); + self.serial.once(packet.cbid, function(data) { + //console.log("Got Respones: "+packet.cbid); + //clearTimeout(timeout); + var error = null; + if (data.commandStatus && data.commandStatus != C.COMMAND_STATUS.OK) { + error = C.COMMAND_STATUS[data.commandStatus]; + } else if (data.deliveryStatus && data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { + error = C.DELIVERY_STATUS[data.deliveryStatus]; + } + cb(error, data); + }); } - cb(error); }); - } + /* + var timeout = setTimeout(function() { + cb({ msg: "Never got Transmit status from XBee" }); + }, 1000); + */ + + }; } XBee.prototype._send = function(data, remote64, remote16, _cb) { - var self = this; - var tasks = []; - + var packets = []; while (data.length > 0) { var frame = new api.TransmitRFData(); frame.destination64 = remote64.dec; frame.destination16 = remote16.dec; frame.RFData = data.slice(0, C.MAX_PAYLOAD_SIZE); data = data.slice(C.MAX_PAYLOAD_SIZE); - tasks.push(self._makeTask(frame.getBytes(), frame.frameId)); + packets.push(this._makeTask({ + data: frame.getBytes(), + cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId + })); } - self._queue.push({ tasks: tasks, _cb: _cb }); + //console.log("Sending %s packets", packets.length); + + this._queue.push({ packets:packets, cb:_cb }); } -XBee.prototype._ATCB = function(cmd, val, cb) { +XBee.prototype._AT = function(cmd, val, _cb) { if (typeof val === 'function') { - cb = val; + _cb = val; val = undefined; } - var frameId = this._AT(cmd, val); - var ATCBEvt = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frameId; - this.once(ATCBEvt, cb); -} -XBee.prototype._AT = function(cmd, val) { var frame = new api.ATCommand(); frame.setCommand(cmd); frame.commandParameter = val; - //console.log("USE QUEUE!"); - this.serial.write(frame.getBytes()); - return frame.frameId; + var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; } +/* XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { var frame = new api.ATCommand(); frame.setCommand(cmd); @@ -256,6 +266,7 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { this.serial.write(frame.getBytes()); return frame.frameId; } +*/ exports.XBee = XBee; @@ -267,18 +278,39 @@ function Node(xbee, params, data_parser) { this.remote64 = params.remote64; this.buffer = ""; this.parser = data_parser(this); + this.timeout = {}; + this.connected = true; + this.refreshTimeout(); } + util.inherits(Node, EventEmitter); +Node.prototype.timeoutOccured = function() { + this.connected = false; + this.emit('disconnect'); +} + +Node.prototype.refreshTimeout = function() { + clearTimeout(this.timeout); + this.timeout = setTimeout(this.timeoutOccured.bind(this), 8000); + if (!this.connected) { + this.connected = true; + // todo other stuff + } +} + Node.prototype.send = function(data, cb) { this.xbee._send(data, this.remote64, this.remote16, cb); } Node.prototype._onReceivePacket = function(data) { // Send the whole data object, or just the parsed msg? - this.parser.parse(api.bArr2Str(data.rawData)); + var packet = api.bArr2Str(data.rawData); + if (packet === '```') this.refreshTimeout(); + else this.parser.parse(packet); } +/* Node.prototype._AT = function(cmd, val) { this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); } @@ -290,5 +322,6 @@ Node.prototype._onATResponse = function(res) { Node.prototype._onDataSampleRx = function(res) { this.emit('data_sample', res); } +*/ exports.Node = Node; From 13c2e3016edd256af932c1e815a50dcceafcabd5 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 8 Aug 2012 20:50:18 +0200 Subject: [PATCH 085/140] refactoring --- examples/simple.js | 7 ++-- xbee.js => index.js | 92 +++++++++++++++++++++++++-------------------- xbee-api.js | 12 +----- 3 files changed, 57 insertions(+), 54 deletions(-) rename xbee.js => index.js (79%) diff --git a/examples/simple.js b/examples/simple.js index 539f2b8..22f9435 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,10 +1,9 @@ var util = require('util'); -var XBee = require('../xbee.js').XBee; -var Parser = require('svd-http-parser'); +var XBee = require('../index.js').XBee; // Replace with your xbee's UART location //var xbee = new XBee('/dev/ttyO1'); -var xbee = new XBee('/dev/ttyO1', Parser); +var xbee = new XBee('/dev/ttyO1'); xbee.on("configured", function(config) { console.log("XBee Config: %s", util.inspect(config)); @@ -13,7 +12,7 @@ xbee.on("configured", function(config) { xbee.on("node", function(node) { console.log("Node %s connected", node.id); - node.on("/register", function(data) { + node.on("data", function(data) { //node.send("pong"); console.log("%s: %s", node.id, util.inspect(data.payload)); }); diff --git a/xbee.js b/index.js similarity index 79% rename from xbee.js rename to index.js index ad29a2f..563188f 100644 --- a/xbee.js +++ b/index.js @@ -3,46 +3,57 @@ var EventEmitter = require('events').EventEmitter; var api = require("./xbee-api"); var serialport = require("serialport"); var async = require('async'); +var os = require('os'); var C = api.Constants; function XBee(options, data_parser) { EventEmitter.call(this); - var self = this; // Option Parsing if (typeof options === 'string') { - options = {port: options}; + this.options = { port: options }; + } else { + this.options = options; } - if (typeof data_parser !== 'function') { - console.log("Loading simple parser, data will emitted on \\r\\n delimiter."); - data_parser = require("./simple-parser"); - } + this.data_parser = data_parser || options.data_parser || undefined; + + this.use_heartbeat = options.use_heartbeat || false; + this.heartbeat_packet = options.heartbeat_packet || '```'; + this.heartbeat_timeout = options.heartbeat_timeout || 8000; // Current nodes - self.nodes = {}; + this.nodes = {}; +} +util.inherits(XBee, EventEmitter); + +XBee.prototype.init = function(cb) { + var self = this; // Serial connection to the XBee - self.serial = new serialport.SerialPort(options.port, { - baudrate: options.baudrate || 57600, + self.serial = new serialport.SerialPort(self.options.port, { + baudrate: self.options.baudrate || 57600, databits: 8, stopbits: 1, parity: 'none', parser: api.packetBuilder() }); - self.serial.on("open", self.configure.bind(self)); + self.serial.on("open", function() { + self.configure.bind(self)(cb); + }); var exit = function() { - console.log("Closing Serial"); self.serial.close(function(err) { - if (err) console.log(err); - else console.log("Done"); + if (err) console.log("Error closing port: "+util.inspect(err)); process.exit(); }); } - process.on('SIGINT', exit); + + if (os.platform() !== 'win32') { + process.on('SIGINT', exit); + } /* Frame-specific Handlers */ @@ -51,7 +62,7 @@ function XBee(options, data_parser) { self._onNodeIdentification = function(data) { var node = data.node; if (!self.nodes[node.remote64.hex]) { - self.nodes[node.remote64.hex] = new Node(self, node, data_parser); + self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); self.emit("node", self.nodes[node.remote64.hex]); } else { // update 16-bit address, as it may change during reconnects. @@ -98,16 +109,14 @@ function XBee(options, data_parser) { self._queue = async.queue(function(task, callback) { async.series(task.packets, function(err, data) { - if (err) console.log("Error writing data to XBee: "+err); + if (err) console.log("Error writing data to XBee "+util.inspect(err)); if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); callback(); }); }, 1); } -util.inherits(XBee, EventEmitter); - -XBee.prototype.configure = function() { +XBee.prototype.configure = function(_done_cb) { var self = this; /* self._ATCB('ID', undefined, function(data) { @@ -137,13 +146,14 @@ XBee.prototype.configure = function() { }; var done = function(err, results) { - if (err) return console.log("Failure to configure XBee module: %s", err); + if (err) { + self.emit("error", new Error("Failure to configure XBee module: "+util.inspect(err))); + if (typeof _done_cb === 'function') _done_cb(err); + } self.config = results; self.emit("configured", self.config); - console.log("[Start Discovery]"); - self.discover(function() { - console.log("[End Discovery]"); - }); + if (typeof _done_cb === 'function') _done_cb(null, self.config); + self.discover(); } // Using async to start discovery only when all parameters have been read. @@ -171,7 +181,7 @@ XBee.prototype.discover = function(cb) { var cbid = self._AT('ND'); self.serial.on(cbid, self._onNodeIdentification); setTimeout(function() { - cb(); + if (typeof cb === 'function') cb(); self.removeAllListeners(cbid); }, self.config.nodeDiscoveryTime || 6000); } @@ -184,17 +194,21 @@ XBee.prototype.broadcast = function(data, cb) { XBee.prototype._makeTask = function(packet) { var self = this; - //console.log("Making task: "+packet.cbid); return function Writer(cb) { //console.log("<<< "+util.inspect(packet.data)); + //console.log("<<< "+packet.data); + var timeout = setTimeout(function() { + cb({ msg: "Never got Transmit status from XBee" }); + }, 1000); self.serial.write(packet.data, function(err, results) { if (err) { cb(err); } else { + //console.log(util.inspect(packet.data)); if (results != packet.data.length) return cb(new Error("Not all bytes written")); self.serial.once(packet.cbid, function(data) { //console.log("Got Respones: "+packet.cbid); - //clearTimeout(timeout); + clearTimeout(timeout); var error = null; if (data.commandStatus && data.commandStatus != C.COMMAND_STATUS.OK) { error = C.COMMAND_STATUS[data.commandStatus]; @@ -205,12 +219,6 @@ XBee.prototype._makeTask = function(packet) { }); } }); - /* - var timeout = setTimeout(function() { - cb({ msg: "Never got Transmit status from XBee" }); - }, 1000); - */ - }; } @@ -227,7 +235,6 @@ XBee.prototype._send = function(data, remote64, remote16, _cb) { cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId })); } - //console.log("Sending %s packets", packets.length); this._queue.push({ packets:packets, cb:_cb }); } @@ -277,7 +284,8 @@ function Node(xbee, params, data_parser) { this.remote16 = params.remote16; this.remote64 = params.remote64; this.buffer = ""; - this.parser = data_parser(this); + if (typeof data_parser === 'function') + this.parser = data_parser(this); this.timeout = {}; this.connected = true; this.refreshTimeout(); @@ -292,7 +300,7 @@ Node.prototype.timeoutOccured = function() { Node.prototype.refreshTimeout = function() { clearTimeout(this.timeout); - this.timeout = setTimeout(this.timeoutOccured.bind(this), 8000); + this.timeout = setTimeout(this.timeoutOccured.bind(this), this.xbee.heartbeat_timeout); if (!this.connected) { this.connected = true; // todo other stuff @@ -304,10 +312,14 @@ Node.prototype.send = function(data, cb) { } Node.prototype._onReceivePacket = function(data) { - // Send the whole data object, or just the parsed msg? - var packet = api.bArr2Str(data.rawData); - if (packet === '```') this.refreshTimeout(); - else this.parser.parse(packet); + // TODO: should be buffer all along! + var packet = new Buffer(data.rawData).toString('ascii'); + if (this.xbee.use_heartbeat && packet === this.xbee.heartbeat_packet) + this.refreshTimeout(); + else if (this.parser !== undefined) + this.parser.parse(packet); + else + this.emit('data', packet); } /* diff --git a/xbee-api.js b/xbee-api.js index 13dc85d..405e7d4 100644 --- a/xbee-api.js +++ b/xbee-api.js @@ -106,15 +106,7 @@ Packet.prototype.getBytes = function() { res.push(packetdata[p] ^ 0x20); } else res.push(packetdata[p]); } - - //console.log(res+" "+res.toString('hex')); - //console.log(packetdata); - //console.log(util.inspect(res)); - // - var out = new Buffer(res); - //console.log(">>> "+util.inspect(out)); - - return out; + return new Buffer(res, 'ascii'); } Packet.prototype.getPayload = function() { @@ -313,7 +305,7 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } - //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt); + //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); emitter.emit(evt, json); } } From 5c40fa1c799e5b00843ac871199b467de1b1a5b0 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 8 Aug 2012 20:52:44 +0200 Subject: [PATCH 086/140] refactoring 2 --- examples/simple-parser.js | 18 ++ index.js | 2 +- lib/xbee-api.js | 485 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+), 1 deletion(-) create mode 100644 examples/simple-parser.js create mode 100644 lib/xbee-api.js diff --git a/examples/simple-parser.js b/examples/simple-parser.js new file mode 100644 index 0000000..046c046 --- /dev/null +++ b/examples/simple-parser.js @@ -0,0 +1,18 @@ +module.exports = function (device) { + var delimiter = "\r"; + function DataParser(device) { + this.device = device; + this.buffer = ""; + } + DataParser.prototype.parse = function(data) { + this.buffer += data; + var split = this.buffer.indexOf(delimiter); + while (split > -1) { + this.device.emit('data', this.buffer.slice(0,split)); + this.buffer = this.buffer.slice(split+2); + split = this.buffer.indexOf(delimiter); + } + } + + return new DataParser(device); +} diff --git a/index.js b/index.js index 563188f..461bee8 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ var util = require('util'); var EventEmitter = require('events').EventEmitter; -var api = require("./xbee-api"); +var api = require("./lib/xbee-api.js"); var serialport = require("serialport"); var async = require('async'); var os = require('os'); diff --git a/lib/xbee-api.js b/lib/xbee-api.js new file mode 100644 index 0000000..405e7d4 --- /dev/null +++ b/lib/xbee-api.js @@ -0,0 +1,485 @@ +var Buffer = require('buffer').Buffer; +var util = require('util'); + +var C = exports.Constants = require('./constants.js'); +exports = module.exports; + +exports.dec2Hex = function(d, padding) { + var hex = Number(d).toString(16); + padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; + + while (hex.length < padding) { + hex = "0" + hex; + } + + return hex; +} + +exports.bArr2HexStr = function(a) { + var s = ''; + for(i in a) { + s += exports.dec2Hex(a[i]); + } + return s; +} + +exports.bArr2Str = function(a) { + var s = ''; + for(i in a) { + s += String.fromCharCode(a[i]); + } + return s; +} + +exports.bArr2Dec = function(a) { + // given a byte array like [3,21], convert to a decimal value. + // e.g. [3,21] --> 3 * 256 + 21 = 789 + var r = 0; + for(var i = 0; i < a.length; i++) { + var power = a.length - i - 1; + r += a[i] * Math.pow(256,power); + } + return r +} + +// module-level variable for storing a frameId. +// Gets incremented by 1 each time it's used, so that you can +// tell which responses relate to which XBee commands +var frameId = 0x30; + +function incrementFrameId() { + frameId++; + frameId %= 255; // fails if it exactly 126 (0x7e is start byte) + if (frameId == 0) frameId = 1; // 0x00 means: no response expected + return frameId; +} + +// constructor for an outgoing Packet. +var Packet = function() { + this.frameId = incrementFrameId(); +}; + +// call getBytes to get a JS array of byte values, ready to send down the serial port +Packet.prototype.getBytes = function() { + // build a JS array to hold the bytes + var packetdata = [C.START_BYTE]; + + // calculate the length bytes. First, get the entire payload by calling the internal function + var payload = this.getPayload(); + + // least significant length byte is easy + var len_lsb = payload.length % 256; + + // if payload length is greater than 255, have to calculate the more significant byte... + if (payload.length > 255) { + var len_msb = payload.length >>> 8; + } else { + //...otherwise the MSB is zero + var len_msb = 0; + } + + // add the length bytes to our growing packet array + packetdata.push(len_msb); + packetdata.push(len_lsb); + + // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array + var running_total = 0; + + for(var j = 0; j < payload.length; j++) { + packetdata.push(payload[j]); + running_total += payload[j]; + } + + checksum = 255 - (running_total % 256); + + // finally append the checksum byte and return the packet as a JS array + packetdata.push(checksum); + + // could be shorter: + var res = [packetdata[0]]; + for (var p = 1; p 0 && b == C.ESCAPE) { + escape_next = true; + continue; + } + + if (escape_next) { + b = 0x20 ^ b; + escape_next = false; + } + + packpos += 1; + + // Detected start of packet. + if (b == C.START_BYTE) { + packpos = 0; + packlen = 0; + running_total = 0; + checksum = -1; + packet = []; + escape_next = false; + } + + if (packpos == 1) packlen += b << 8; // most significant bit of the length + if (packpos == 2) packlen += b; // least significant bit of the length + if ((packlen > 0) && (packpos > 2)) { + if (packet.length < packlen) { + packet.push(b); + running_total += b; + } else { + checksum = b; + } + } + + + // Packet is complete. Parse & Emit + if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { + // There will still be a checksum byte. Currently this is ignored + if (!checksum === 255 - (running_total % 256)) { + console.log("CHECKSUM_MISMATCH"); + } else { + //console.log(">>> "+util.inspect(packet)); + var parser = new PacketParser(packet) + var json = parser.parse(); + if (json.not_implemented) { + console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); + } else { + var evt = json.ft; + if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, + C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, + C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { + evt += C.EVT_SEP+json.frameId; + } + //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); + emitter.emit(evt, json); + } + } + } + } + }; +} + +// Packet Parser Class. Used to parse packets if they are known +var PacketParser = function(p) { + this.json = { + ft: p.splice(0,1)[0], + } + + // Used as pointer to the object data is parsed into + this.write = this.json; + this.payload = p; +} + +PacketParser.prototype.parse = function() { + if (false) { // TODO: Debug option + this.json.desc = C.FRAME_TYPES[this.json.ft]; + } + + this.frames[this.json.ft].parse(this); + + return this.json; +} + +PacketParser.prototype.readAddr = function(name, length) { + var dec = this.payload.splice(0, length); + this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } + return this; +} + +PacketParser.prototype.readByte = function(name, length) { + if (typeof length === 'number') + this.write[name] = this.payload.splice(0,length); + else this.write[name] = this.payload.splice(0,1)[0]; + return this; +} + +PacketParser.prototype.readAddr64 = function(name) { + return this.readAddr(name, 8); +} + +PacketParser.prototype.readAddr16 = function(name) { + return this.readAddr(name, 2); +} + +PacketParser.prototype.readString = function(name, length) { + this.write[name] = ""; + if (typeof length === 'number') { + for (var i = 0; i < length; i++) + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } else { + while(this.payload[0] != 0x00) { + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } + this.payload.splice(0,1); // Read 0x00 away + } + return this; +} + +PacketParser.prototype.collectPayload = function(name) { + this.write[name] = this.payload.splice(0); + return this; +} + +var frames = PacketParser.prototype.frames = {}; + +frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } +}; + +frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + if (parser.json.command == 'ND') { + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') + } + } +}; + +frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('rawData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .readByte('numSamples') + .readByte('digitalChannelMask', 2) + .readByte('analogChannelMask') + if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) + parser.readByte('digitalSamples',2); + if (parser.json.analogChannelMask > 0) + parser.collectPayload('analogSamples'); + // skip formatting data for now + } +}; + +// Unsupportet Frame Types +for (key in C.FRAME_TYPES) { + var val = C.FRAME_TYPES[key]; + if (typeof val === 'number') { + if (!frames.hasOwnProperty[val]) { + frames[val] = { + parse: function(parser) { + parser.json.not_implemented = true; + } + } + } else { + // + } + } +} From 3691e997de0582edae3d2e692ba4d5688d707d6f Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 8 Aug 2012 20:56:20 +0200 Subject: [PATCH 087/140] refactoring 3 --- examples/simple.js | 8 +++++--- constants.js => lib/constants.js | 0 package.json | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) rename constants.js => lib/constants.js (100%) diff --git a/examples/simple.js b/examples/simple.js index 22f9435..db59995 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -4,17 +4,19 @@ var XBee = require('../index.js').XBee; // Replace with your xbee's UART location //var xbee = new XBee('/dev/ttyO1'); var xbee = new XBee('/dev/ttyO1'); +xbee.init(); xbee.on("configured", function(config) { console.log("XBee Config: %s", util.inspect(config)); }); + xbee.on("node", function(node) { - console.log("Node %s connected", node.id); + console.log("Node %s connected", node.remote64.hex); node.on("data", function(data) { - //node.send("pong"); - console.log("%s: %s", node.id, util.inspect(data.payload)); + console.log("%s: %s", node.remote64.hex, util.inspect(data)); + //node.send("pong", function(err, status) {}); }); }); diff --git a/constants.js b/lib/constants.js similarity index 100% rename from constants.js rename to lib/constants.js diff --git a/package.json b/package.json index ffd6182..01b7efa 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.1.1", + "version" : "0.2.0", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "maintainers": "Jan Kolkmeier ", @@ -8,7 +8,7 @@ "Richard Morrison ", "msealand" ], - "main": "xbee.js", + "main": "./index.js", "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], "homepage": "https://github.com/jouz/node-xbee-svd", "dependencies": { From 537de0cff67781adf6f27e9ba2f19bbe4adf7681 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 8 Aug 2012 20:57:03 +0200 Subject: [PATCH 088/140] delete old files --- simple-parser.js | 18 -- xbee-api.js | 485 ----------------------------------------------- 2 files changed, 503 deletions(-) delete mode 100644 simple-parser.js delete mode 100644 xbee-api.js diff --git a/simple-parser.js b/simple-parser.js deleted file mode 100644 index 046c046..0000000 --- a/simple-parser.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = function (device) { - var delimiter = "\r"; - function DataParser(device) { - this.device = device; - this.buffer = ""; - } - DataParser.prototype.parse = function(data) { - this.buffer += data; - var split = this.buffer.indexOf(delimiter); - while (split > -1) { - this.device.emit('data', this.buffer.slice(0,split)); - this.buffer = this.buffer.slice(split+2); - split = this.buffer.indexOf(delimiter); - } - } - - return new DataParser(device); -} diff --git a/xbee-api.js b/xbee-api.js deleted file mode 100644 index 405e7d4..0000000 --- a/xbee-api.js +++ /dev/null @@ -1,485 +0,0 @@ -var Buffer = require('buffer').Buffer; -var util = require('util'); - -var C = exports.Constants = require('./constants.js'); -exports = module.exports; - -exports.dec2Hex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - - while (hex.length < padding) { - hex = "0" + hex; - } - - return hex; -} - -exports.bArr2HexStr = function(a) { - var s = ''; - for(i in a) { - s += exports.dec2Hex(a[i]); - } - return s; -} - -exports.bArr2Str = function(a) { - var s = ''; - for(i in a) { - s += String.fromCharCode(a[i]); - } - return s; -} - -exports.bArr2Dec = function(a) { - // given a byte array like [3,21], convert to a decimal value. - // e.g. [3,21] --> 3 * 256 + 21 = 789 - var r = 0; - for(var i = 0; i < a.length; i++) { - var power = a.length - i - 1; - r += a[i] * Math.pow(256,power); - } - return r -} - -// module-level variable for storing a frameId. -// Gets incremented by 1 each time it's used, so that you can -// tell which responses relate to which XBee commands -var frameId = 0x30; - -function incrementFrameId() { - frameId++; - frameId %= 255; // fails if it exactly 126 (0x7e is start byte) - if (frameId == 0) frameId = 1; // 0x00 means: no response expected - return frameId; -} - -// constructor for an outgoing Packet. -var Packet = function() { - this.frameId = incrementFrameId(); -}; - -// call getBytes to get a JS array of byte values, ready to send down the serial port -Packet.prototype.getBytes = function() { - // build a JS array to hold the bytes - var packetdata = [C.START_BYTE]; - - // calculate the length bytes. First, get the entire payload by calling the internal function - var payload = this.getPayload(); - - // least significant length byte is easy - var len_lsb = payload.length % 256; - - // if payload length is greater than 255, have to calculate the more significant byte... - if (payload.length > 255) { - var len_msb = payload.length >>> 8; - } else { - //...otherwise the MSB is zero - var len_msb = 0; - } - - // add the length bytes to our growing packet array - packetdata.push(len_msb); - packetdata.push(len_lsb); - - // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array - var running_total = 0; - - for(var j = 0; j < payload.length; j++) { - packetdata.push(payload[j]); - running_total += payload[j]; - } - - checksum = 255 - (running_total % 256); - - // finally append the checksum byte and return the packet as a JS array - packetdata.push(checksum); - - // could be shorter: - var res = [packetdata[0]]; - for (var p = 1; p 0 && b == C.ESCAPE) { - escape_next = true; - continue; - } - - if (escape_next) { - b = 0x20 ^ b; - escape_next = false; - } - - packpos += 1; - - // Detected start of packet. - if (b == C.START_BYTE) { - packpos = 0; - packlen = 0; - running_total = 0; - checksum = -1; - packet = []; - escape_next = false; - } - - if (packpos == 1) packlen += b << 8; // most significant bit of the length - if (packpos == 2) packlen += b; // least significant bit of the length - if ((packlen > 0) && (packpos > 2)) { - if (packet.length < packlen) { - packet.push(b); - running_total += b; - } else { - checksum = b; - } - } - - - // Packet is complete. Parse & Emit - if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // There will still be a checksum byte. Currently this is ignored - if (!checksum === 255 - (running_total % 256)) { - console.log("CHECKSUM_MISMATCH"); - } else { - //console.log(">>> "+util.inspect(packet)); - var parser = new PacketParser(packet) - var json = parser.parse(); - if (json.not_implemented) { - console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); - } else { - var evt = json.ft; - if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, - C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, - C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { - evt += C.EVT_SEP+json.frameId; - } - //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); - emitter.emit(evt, json); - } - } - } - } - }; -} - -// Packet Parser Class. Used to parse packets if they are known -var PacketParser = function(p) { - this.json = { - ft: p.splice(0,1)[0], - } - - // Used as pointer to the object data is parsed into - this.write = this.json; - this.payload = p; -} - -PacketParser.prototype.parse = function() { - if (false) { // TODO: Debug option - this.json.desc = C.FRAME_TYPES[this.json.ft]; - } - - this.frames[this.json.ft].parse(this); - - return this.json; -} - -PacketParser.prototype.readAddr = function(name, length) { - var dec = this.payload.splice(0, length); - this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } - return this; -} - -PacketParser.prototype.readByte = function(name, length) { - if (typeof length === 'number') - this.write[name] = this.payload.splice(0,length); - else this.write[name] = this.payload.splice(0,1)[0]; - return this; -} - -PacketParser.prototype.readAddr64 = function(name) { - return this.readAddr(name, 8); -} - -PacketParser.prototype.readAddr16 = function(name) { - return this.readAddr(name, 2); -} - -PacketParser.prototype.readString = function(name, length) { - this.write[name] = ""; - if (typeof length === 'number') { - for (var i = 0; i < length; i++) - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } else { - while(this.payload[0] != 0x00) { - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } - this.payload.splice(0,1); // Read 0x00 away - } - return this; -} - -PacketParser.prototype.collectPayload = function(name) { - this.write[name] = this.payload.splice(0); - return this; -} - -var frames = PacketParser.prototype.frames = {}; - -frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { - parse: function(parser) { - parser - .readAddr64('sender64') - .readAddr16('sender16') - .readByte('recieveOptions'); - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readByte('transmitRetryCount') - .readByte('deliveryStatus') - .readByte('discoveryStatus') - } -}; - -frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readString('command', 2) - .readByte('commandStatus') - if (parser.json.command == 'ND') { - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } - } -}; - -frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readAddr64('remote64') - .readString('command', 2) - .readByte('commandStatus') - .collectPayload('commandData'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawData'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .readByte('numSamples') - .readByte('digitalChannelMask', 2) - .readByte('analogChannelMask') - if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) - parser.readByte('digitalSamples',2); - if (parser.json.analogChannelMask > 0) - parser.collectPayload('analogSamples'); - // skip formatting data for now - } -}; - -// Unsupportet Frame Types -for (key in C.FRAME_TYPES) { - var val = C.FRAME_TYPES[key]; - if (typeof val === 'number') { - if (!frames.hasOwnProperty[val]) { - frames[val] = { - parse: function(parser) { - parser.json.not_implemented = true; - } - } - } else { - // - } - } -} From d979589a779025ecd8c47a65ab85b75cc3b25199 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 9 Aug 2012 14:21:05 +0200 Subject: [PATCH 089/140] remove log --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 461bee8..6bc7cce 100644 --- a/index.js +++ b/index.js @@ -109,7 +109,6 @@ XBee.prototype.init = function(cb) { self._queue = async.queue(function(task, callback) { async.series(task.packets, function(err, data) { - if (err) console.log("Error writing data to XBee "+util.inspect(err)); if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); callback(); }); From c89afa22c1106ee47730db65228a6a8c58ea3192 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Thu, 9 Aug 2012 19:09:41 +0200 Subject: [PATCH 090/140] 0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01b7efa..104b82f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "svd-xbee", - "version" : "0.2.0", + "version" : "0.2.1", "description" : "A more high level fork of Richard Morrison's node-xbee", "author": "Jan Kolkmeier ", "maintainers": "Jan Kolkmeier ", From dfa7875d96857900525e0f55568ccb0b5663b3af Mon Sep 17 00:00:00 2001 From: Georges-Etienne Legendre Date: Mon, 17 Sep 2012 17:22:24 -0400 Subject: [PATCH 091/140] Unknown frame types throws an exception: TypeError: Cannot call method 'parse' of undefined --- lib/xbee-api.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 405e7d4..b78d2c0 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -469,10 +469,10 @@ frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { }; // Unsupportet Frame Types -for (key in C.FRAME_TYPES) { - var val = C.FRAME_TYPES[key]; +for (key in C.FRAME_TYPE) { + var val = C.FRAME_TYPE[key]; if (typeof val === 'number') { - if (!frames.hasOwnProperty[val]) { + if (!frames[val]) { frames[val] = { parse: function(parser) { parser.json.not_implemented = true; From 9c5d8a5fdca41e6a281cc615d713e2bdba754de4 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 09:21:08 +0100 Subject: [PATCH 092/140] Remove old files --- simple-parser.js | 18 -- xbee-api.js | 498 ----------------------------------------------- 2 files changed, 516 deletions(-) delete mode 100644 simple-parser.js delete mode 100644 xbee-api.js diff --git a/simple-parser.js b/simple-parser.js deleted file mode 100644 index 5517592..0000000 --- a/simple-parser.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = function (device) { - function DataParser(device) { - this.device = device; - this.buffer = ""; - } - - DataParser.prototype.parse = function(data) { - this.buffer += data; - var split = this.buffer.indexOf('\r\n'); - while (split > -1) { - this.device.emit('data', this.buffer.slice(0,split)); - this.buffer = this.buffer.slice(split+2); - split = this.buffer.indexOf('\r\n'); - } - } - - return new DataParser(device); -} diff --git a/xbee-api.js b/xbee-api.js deleted file mode 100644 index f76a534..0000000 --- a/xbee-api.js +++ /dev/null @@ -1,498 +0,0 @@ -var Buffer = require('buffer').Buffer; -var util = require('util'); - -exports.dec2Hex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - - while (hex.length < padding) { - hex = "0" + hex; - } - - return hex; -} - -exports.bArr2HexStr = function(a) { - var s = ''; - for(i in a) { - s += exports.dec2Hex(a[i]); - } - return s; -} - -exports.bArr2Str = function(a) { - var s = ''; - for(i in a) { - s += String.fromCharCode(a[i]); - } - return s; -} - -exports.bArr2Dec = function(a) { - // given a byte array like [3,21], convert to a decimal value. - // e.g. [3,21] --> 3 * 256 + 21 = 789 - var r = 0; - for(var i = 0; i < a.length; i++) { - var power = a.length - i - 1; - r += a[i] * Math.pow(256,power); - } - return r -} - -// module-level variable for storing a frameId. -// Gets incremented by 1 each time it's used, so that you can -// tell which responses relate to which XBee commands -var frameId = 0x00; - -function incrementFrameId() { - // increment frameId and make sure it's <=255 - frameId += 1; - frameId %= 256; - return frameId; -} - -exports.START_BYTE = 0x7e; // start of every XBee packet - -exports.FT_DATA_SAMPLE_RX = 0x92; // I/O data sample packet received -exports.FT_AT_COMMAND = 0x08; // AT command (local) -exports.FT_AT_RESPONSE = 0x88; // AT response (local) -exports.FT_TX_TRANSMIT_STATUS = 0x8b; // Status response of transmission -exports.FT_REMOTE_AT_COMMAND = 0x17; // AT command (to remote radio) -exports.FT_REMOTE_AT_RESPONSE = 0x97; // AT response (from remote radio) -exports.FT_TRANSMIT_RF_DATA = 0x10; // Transmit RF data -exports.FT_TRANSMIT_ACKNOWLEDGED = 0x8b; // TX response -exports.FT_RECEIVE_RF_DATA = 0x90; // RX received -exports.FT_NODE_IDENTIFICATION = 0x95; - -exports.DELIVERY_STATES = { - 0x00: "Success", - 0x02: "CCA Failure", - 0x15: "Invalid destination endpoint", - 0x21: "Network ACK Failure", - 0x22: "Not Joined to Network", - 0x23: "Self-addressed", - 0x24: "Address Not Found", - 0x25: "Route Not Found", - 0x74: "Data payload too large" -}; - -exports.DISCOVERY_STATES = { - 0x00: "No Discovery Overhead", - 0x01: "Address Discovery", - 0x02: "Route Discovery", - 0x03: "Address and Route Discovery" -}; - -// Bitmasks for I/O pins -var digiPinsByte1 = { - D10: 4, - D11: 8, - D12: 16 -}; - -var digiPinsByte2 = { - D0: 1, - D1: 2, - D2: 4, - D3: 8, - D4: 16, - D5: 32, - D6: 64, - D7: 128 -}; - -var analogPins = { - A0: 1, - A1: 2, - A2: 4, - A3: 8, - supply: 128 -}; - -// constructor for an outgoing Packet. -var Packet = function() { - this.frameId = incrementFrameId(); -}; - -// call getBytes to get a JS array of byte values, ready to send down the serial port -Packet.prototype.getBytes = function() { - // build a JS array to hold the bytes - var packetdata = [exports.START_BYTE]; - - // calculate the length bytes. First, get the entire payload by calling the internal function - var payload = this.getPayload(); - - // least significant length byte is easy - var len_lsb = payload.length % 256; - - // if payload length is greater than 255, have to calculate the more significant byte... - if (payload.length > 255) { - var len_msb = payload.length >>> 8; - } else { - //...otherwise the MSB is zero - var len_msb = 0; - } - - // add the length bytes to our growing packet array - packetdata.push(len_msb); - packetdata.push(len_lsb); - - // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array - var running_total = 0; - - for(var j = 0; j < payload.length; j++) { - packetdata.push(payload[j]); - running_total += payload[j]; - } - - checksum = 255 - (running_total % 256); - - // finally append the checksum byte and return the packet as a JS array - packetdata.push(checksum); - - return packetdata; -} - -Packet.prototype.getPayload = function() { - // this function is overridden by subclasses - return this.payload; -} - -exports.Packet = Packet; - -// ATCommand is for setting/reading AT registers on the local XBee node. -var ATCommand = function() { - this.frameId = incrementFrameId(); -}; -util.inherits(ATCommand, Packet); - -ATCommand.prototype.setCommand = function(strCmd) { - // take the ascii command and save it internally as byte values command0 and command1 - this.command0 = strCmd.charCodeAt(0); - this.command1 = strCmd.charCodeAt(1); -} - -ATCommand.prototype.getPayload = function() { - // Returns a JS array of byte values - // which form the payload of an AT command packet. - // Uses command0, command1 and commandParameter to build the payload. - - // begin with the frame type and frame ID - var payload = [exports.FT_AT_COMMAND, this.frameId]; - - // add two bytes to identify which AT command is being used - payload.push(this.command0); - payload.push(this.command1); - - // this.commandParameter can either be undefined (to query an AT register), or an array (to set an AT register) - if (this.commandParameter) { - for(var j=0; j 0) && (packpos > 2)) { - if (packet.length < packlen) { - packet.push(b); - running_total += b; - } else { - checksum = b; - } - } - - - // Packet is complete. Parse & Emit - if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // There will still be a checksum byte. Currently this is ignored - if (!checksum === 255 - (running_total % 256)) { - console.log("CHECKSUM_MISMATCH"); - } else { - var parser = new PacketParser(packet) - var json = parser.parse(); - //console.log("P: "+util.inspect(json)); - var event = json.type; - if (json.ft === exports.FT_TX_TRANSMIT_STATUS || json.ft === exports.FT_AT_RESPONSE || json.ft === exports.FT_AT_REMOTE_RESPONSE) { - event += "_"+json.frameId; - } - if (json.type === "UNKNOWN") - console.log("FRAME: %s (%s). EVT: %s RAW:[%s]", json.ft, json.type, event, exports.bArr2Str(json.rawData)); - emitter.emit(event, json); - } - } - } - }; -} - -// Packet Parser Class. Used to parse packets if they are known -var PacketParser = function(p) { - this.json = { - ft: p.splice(0,1)[0], - } - - // Used as pointer to the object data is parsed into - this.write = this.json; - this.payload = p; -} - -PacketParser.prototype.parse = function() { - if (this.knownFrames[this.json.ft]) { - this.json.type = this.knownFrames[this.json.ft].type; - this.knownFrames[this.json.ft].parse(this); - } else { - this.json.type = "UNKNOWN"; - } - return this.json; -} - -PacketParser.prototype.readAddr = function(name, length) { - var dec = this.payload.splice(0, length); - this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } - return this; -} - -PacketParser.prototype.readByte = function(name, length) { - if (typeof length === 'number') - this.write[name] = this.payload.splice(0,length); - else this.write[name] = this.payload.splice(0,1)[0]; - return this; -} - -PacketParser.prototype.readAddr64 = function(name) { - return this.readAddr(name, 8); -} - -PacketParser.prototype.readAddr16 = function(name) { - return this.readAddr(name, 2); -} - -PacketParser.prototype.readString = function(name, length) { - this.write[name] = ""; - if (typeof length === 'number') { - for (var i = 0; i < length; i++) - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } else { - while(this.payload[0] != 0x00) { - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } - this.payload.splice(0,1); // Read 0x00 away - } - return this; -} - -PacketParser.prototype.collectPayload = function(name) { - this.write[name] = this.payload.splice(0); - return this; -} - -PacketParser.prototype.knownFrames = { - 0x95: { - type: "NODE_IDENTIFICATION", - parse: function(parser) { - parser - .readAddr64('sender64') - .readAddr16('sender16') - .readByte('recieveOptions'); - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent'); - } - }, - 0x8b: { - type: "TX_TRANSMIT_STATUS", - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readByte('transmitRetryCount') - .readByte('deliveryStatus') - .readByte('discoveryStatus') - } - }, - 0x88: { - type: "AT_RESPONSE", - parse: function(parser) { - parser - .readByte('frameId') - .readString('command', 2) - .readByte('commandStatus') - if (parser.json.command == 'ND') { - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } - } - }, - 0x97: { - type: "REMOTE_AT_RESPONSE", - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readAddr64('remote64') - .readString('command', 2) - .readByte('commandStatus') - .collectPayload('commandData'); - } - }, - 0x90: { - type: "RECEIVE_RF_DATA", - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawData'); - } - }, - 0x92: { - type: "DATA_SAMPLE_RX", - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .readByte('numSamples') - .readByte('digitalChannelMask', 2) - .readByte('analogChannelMask') - if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) - parser.readByte('digitalSamples',2); - if (parser.json.analogChannelMask > 0) - parser.collectPayload('analogSamples'); - // skip formatting data for now - } - } -} From 45da34b96f9e71f4acb78a8da66d93a050802ab8 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 09:50:55 +0100 Subject: [PATCH 093/140] Mention API mode 2 in the readme --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b480a66..522dc14 100644 --- a/README.markdown +++ b/README.markdown @@ -44,7 +44,7 @@ This work is inspired by: Setup ===== -I have my xbee coordinator radio connected to the computer running Node. Crucially, the coordinator is in xbee's API mode - this is required to allow you to send remote instructions, and so on. +I have my xbee coordinator radio connected to the computer running Node. Crucially, the coordinator is in xbee's API mode 2 - this is required to allow you to send remote instructions, and so on, and uses escaping to improve reliability. My remote xbee network modules send periodic measurements and I can push them to web browsers, save them in a database, etc. From f8ce33e81e642a7831039ae5cd8036ca1d00387c Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 09:54:04 +0100 Subject: [PATCH 094/140] Update readme to try and help people avoid a couple of gotchas --- README.markdown | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 522dc14..262eb51 100644 --- a/README.markdown +++ b/README.markdown @@ -4,12 +4,14 @@ Example ======= As no parser function is passed to the XBee() constructor, the default parser will be used (see simple-parser.js). The default parser will merge frames and emit them split by \r\n (so if you wonder why no data is emitted, make sure you use the right delimiter!). +Set things up like this, substituting in paramaters that match your Xbee network: + ```javascript var util = require('util'); var XBee = require('svd-xbee').XBee; -// Replace with your xbee's UART location -var xbee = new XBee('/dev/ttyO1'); +// Replace with your xbee's UART location and correct baud rate (if you omit baudrate, the code assumes your xbee talks at 57600). +var xbee = new XBee({port: '/dev/tty01', baudrate:9600}); xbee.on("configured", function(config) { console.log("XBee Config: %s", util.inspect(config)); @@ -24,6 +26,13 @@ xbee.on("node", function(node) { }); ``` +Then, you can run: + +```javascript +xbee.init(); +``` + +and you should start to see things logged back to your console. Background ========== From 71a2f3fcc634a90910ff669e90eb30ff9c9a8699 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 10:16:58 +0100 Subject: [PATCH 095/140] Fix a mistake (I think) in simple-parser.js The code as it stands assumes a delimiter length of 2, even though the supplied delimiter has a length of 1. I found characters being dropped from the start of my emitted data, as you'd expect. Corrected version of code should handle delimiters of any length. --- examples/simple-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple-parser.js b/examples/simple-parser.js index 046c046..ce397d2 100644 --- a/examples/simple-parser.js +++ b/examples/simple-parser.js @@ -9,7 +9,7 @@ module.exports = function (device) { var split = this.buffer.indexOf(delimiter); while (split > -1) { this.device.emit('data', this.buffer.slice(0,split)); - this.buffer = this.buffer.slice(split+2); + this.buffer = this.buffer.slice(split+delimiter.length); split = this.buffer.indexOf(delimiter); } } From 50518dececc7018184e53808ebb3ab3eeed35800 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 10:19:20 +0100 Subject: [PATCH 096/140] Update README to match reality --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 262eb51..3575a75 100644 --- a/README.markdown +++ b/README.markdown @@ -2,7 +2,7 @@ A more high level fork of Richard Morrison's node-xbee. Example ======= -As no parser function is passed to the XBee() constructor, the default parser will be used (see simple-parser.js). The default parser will merge frames and emit them split by \r\n (so if you wonder why no data is emitted, make sure you use the right delimiter!). +If you pass no parser function to the XBee() constructor, nodes will emit each data packet that they receive as a 'data' event. See simple-parser.js for a basic parser that splits on \r, and is easily adapted if you wish to use \n or some other delimiter. The simple parser will merge frames and emit them split by your delimiter (so if you wonder why no data is emitted, make sure you use the right delimiter!). Set things up like this, substituting in paramaters that match your Xbee network: From f2e1d9f98ab4554ae6b0922e1a12c3d4283797f9 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 11:16:26 +0100 Subject: [PATCH 097/140] Allow setting API mode to values other than 2 --- index.js | 2 ++ lib/xbee-api.js | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 6bc7cce..8ae0612 100644 --- a/index.js +++ b/index.js @@ -23,6 +23,8 @@ function XBee(options, data_parser) { this.heartbeat_packet = options.heartbeat_packet || '```'; this.heartbeat_timeout = options.heartbeat_timeout || 8000; + if (options.api_mode) api.api_mode = options.api_mode; + // Current nodes this.nodes = {}; } diff --git a/lib/xbee-api.js b/lib/xbee-api.js index b78d2c0..a57d28c 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -47,6 +47,9 @@ exports.bArr2Dec = function(a) { // tell which responses relate to which XBee commands var frameId = 0x30; +// use API mode 2 (escape special chars) by default +exports.api_mode = 2; + function incrementFrameId() { frameId++; frameId %= 255; // fails if it exactly 126 (0x7e is start byte) @@ -95,13 +98,14 @@ Packet.prototype.getBytes = function() { // finally append the checksum byte and return the packet as a JS array packetdata.push(checksum); + // Escape characters that need to be escaped (if using API mode 2 (the default)). // could be shorter: var res = [packetdata[0]]; for (var p = 1; p Date: Sun, 21 Oct 2012 12:46:38 +0100 Subject: [PATCH 098/140] Implement remote AT commands following jouz's method used for AT commands --- index.js | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 8ae0612..c8aaf35 100644 --- a/index.js +++ b/index.js @@ -23,6 +23,9 @@ function XBee(options, data_parser) { this.heartbeat_packet = options.heartbeat_packet || '```'; this.heartbeat_timeout = options.heartbeat_timeout || 8000; + // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? + this.transmit_status_timeout = options.transmit_status_timeout || 1000; + if (options.api_mode) api.api_mode = options.api_mode; // Current nodes @@ -76,7 +79,6 @@ XBee.prototype.init = function(cb) { // AT Command Responses from remote AT Commands self._onRemoteCommandResponse = function(res) { - // On Node Discovery Packet, emit new Node if (self.nodes[res.remote64.hex]) { self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); } else { @@ -198,9 +200,10 @@ XBee.prototype._makeTask = function(packet) { return function Writer(cb) { //console.log("<<< "+util.inspect(packet.data)); //console.log("<<< "+packet.data); + var timeout = setTimeout(function() { cb({ msg: "Never got Transmit status from XBee" }); - }, 1000); + }, self.transmit_status_timeout ); self.serial.write(packet.data, function(err, results) { if (err) { cb(err); @@ -241,6 +244,7 @@ XBee.prototype._send = function(data, remote64, remote16, _cb) { } XBee.prototype._AT = function(cmd, val, _cb) { + // val parameter is optional if (typeof val === 'function') { _cb = val; val = undefined; @@ -258,23 +262,27 @@ XBee.prototype._AT = function(cmd, val, _cb) { return cbid; } -/* -XBee.prototype._remoteAT = function(cmd, remote64, remote16, val) { - var frame = new api.ATCommand(); + +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.RemoteATCommand(); frame.setCommand(cmd); frame.commandParameter = val; - if (typeof remote64.dec === 'undefined') { - frame.destination64 = remote64; - frame.destination16 = remote16; - } else { - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - } - //console.log("USE QUEUE!"); - this.serial.write(frame.getBytes()); - return frame.frameId; + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; } -*/ exports.XBee = XBee; From f039b19074af618b58b765fc58057dec92e73df8 Mon Sep 17 00:00:00 2001 From: Richard Morrison Date: Sun, 21 Oct 2012 13:46:58 +0100 Subject: [PATCH 099/140] Enable node.ATCommand(cmd, str, callback) --- index.js | 13 ++++++++++--- lib/xbee-api.js | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index c8aaf35..422f345 100644 --- a/index.js +++ b/index.js @@ -331,11 +331,18 @@ Node.prototype._onReceivePacket = function(data) { this.emit('data', packet); } -/* -Node.prototype._AT = function(cmd, val) { - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); +Node.prototype.ATCommand = function(cmd, val, cb) { + // val parameter is optional + if (typeof val === "function") { + // use val as the callback in this case + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); + } else { + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); + } + } +/* Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } diff --git a/lib/xbee-api.js b/lib/xbee-api.js index a57d28c..934cdad 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -144,10 +144,14 @@ ATCommand.prototype.getPayload = function() { payload.push(this.command0); payload.push(this.command1); - // this.commandParameter can either be undefined (to query an AT register), or an array (to set an AT register) + // this.commandParameter can either be undefined (to query an AT register), or an array (to set an AT register), or a string (also sets an AT register) if (this.commandParameter) { for(var j=0; j Date: Wed, 27 Feb 2013 20:51:32 -0500 Subject: [PATCH 100/140] Check for errors in Xbee.config --- index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 422f345..9c21a14 100644 --- a/index.js +++ b/index.js @@ -133,7 +133,11 @@ XBee.prototype.configure = function(_done_cb) { f = typeof f !== 'undefined' ? f : function(a){return a}; return function(cb) { self._AT(command, val, function(err, data) { - cb(err, f(data.commandData)); + if (!err) { + cb(err, f(data.commandData)); + } else { + console.log('XBee.configure.QF - ', err.msg); + } }); } } From a75f9f9e4c7fbbab56ae90e1a0d4fbd291e2cb05 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Mar 2013 09:56:38 -0500 Subject: [PATCH 101/140] updated npm package --- package.json | 66 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 104b82f..e27c9a2 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,48 @@ -{ "name" : "svd-xbee", - "version" : "0.2.1", - "description" : "A more high level fork of Richard Morrison's node-xbee", - "author": "Jan Kolkmeier ", - "maintainers": "Jan Kolkmeier ", - "contributors": [ - "Jan Kolkmeier ", - "Richard Morrison ", - "msealand" - ], - "main": "./index.js", - "keywords": [ "xbee", "serialport", "robots", "sensors", "automation", "control" ], - "homepage": "https://github.com/jouz/node-xbee-svd", - "dependencies": { - "serialport" : "1.0.x", - "async" : "0.1.x" +{ + "name": "svd-xbee", + "version": "0.2.1", + "description": "A more high level fork of Richard Morrison's node-xbee", + "author": { + "name": "Jan Kolkmeier", + "email": "jankolkmeier@gmail.com" + }, + "maintainers": "Jan Kolkmeier ", + "contributors": [ + { + "name": "Jan Kolkmeier", + "email": "jankolkmeier@gmail.com" }, - "repository": { - "type" : "git", - "url" : "git://github.com/jouz/node-xbee-svd.git" - } + { + "name": "Richard Morrison", + "email": "richard@rmorrison.net" + }, + { + "name": "msealand" + } + ], + "main": "./index.js", + "keywords": [ + "xbee", + "serialport", + "robots", + "sensors", + "automation", + "control" + ], + "homepage": "https://github.com/jouz/node-xbee-svd", + "dependencies": { + "serialport": "1.0.x", + "async": "0.1.x" + }, + "repository": { + "type": "git", + "url": "git://github.com/jouz/node-xbee-svd.git" + }, + "readme": "A more high level fork of Richard Morrison's node-xbee.\n\nExample\n=======\nIf you pass no parser function to the XBee() constructor, nodes will emit each data packet that they receive as a 'data' event. See simple-parser.js for a basic parser that splits on \\r, and is easily adapted if you wish to use \\n or some other delimiter. The simple parser will merge frames and emit them split by your delimiter (so if you wonder why no data is emitted, make sure you use the right delimiter!).\n\nSet things up like this, substituting in paramaters that match your Xbee network:\n\n```javascript\nvar util = require('util');\nvar XBee = require('svd-xbee').XBee;\n\n// Replace with your xbee's UART location and correct baud rate (if you omit baudrate, the code assumes your xbee talks at 57600).\nvar xbee = new XBee({port: '/dev/tty01', baudrate:9600});\n\nxbee.on(\"configured\", function(config) {\n console.log(\"XBee Config: %s\", util.inspect(config));\n});\n\nxbee.on(\"node\", function(node) {\n console.log(\"Node %s connected\", node.id);\n\n node.on(\"data\", function(data) {\n console.log(\"%s: %s\", node.id, util.inspect(data));\n });\n\n});\n```\nThen, you can run:\n\n```javascript\nxbee.init();\n```\n\nand you should start to see things logged back to your console.\n\nBackground\n==========\n\nNote that this readme is still mostly copied from the original module!\n\n[Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks.\n\nThey can be connected to a computer over RS232 and communicated on using a standard serial port.\n\nEven easier, with something like the [XBee USB Explorer](http://www.sparkfun.com/products/8687) by SparkFun, you can connect to them easily over USB.\n\nThis work is inspired by:\n\n* voodootikigod's [serialport module](https://github.com/voodootikigod/node-serialport) (in fact you're going to need this to use this package)\n* \"[Building Wireless Sensor Networks](http://shop.oreilly.com/product/9780596807740.do)\" by Rob Faludi\n\nSetup\n=====\n\nI have my xbee coordinator radio connected to the computer running Node. Crucially, the coordinator is in xbee's API mode 2 - this is required to allow you to send remote instructions, and so on, and uses escaping to improve reliability.\n\nMy remote xbee network modules send periodic measurements and I can push them to web browsers, save them in a database, etc.\n\nI can also use this library to send remote commands and query remote xbee modules. For instance, setting a digital output on a remote module could turn a light on, or a motor, or a laser beam - up to you!\n\nInstallation\n============\n\n npm install svd-xbee\n\nLicence\n=======\n\nThis work is based on the works of Richard Morrison\n\"Creative
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com.\n", + "readmeFilename": "README.markdown", + "_id": "svd-xbee@0.2.1", + "dist": { + "shasum": "0f4c78bbf31109a0c04f874badcfc0c51a0d7361" + }, + "_from": "svd-xbee" } From 9b74113c1889c76cdc11089b40e1d7715bb3e5f9 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Mar 2013 09:58:56 -0500 Subject: [PATCH 102/140] updated examples --- examples/simple-parser.js | 2 +- examples/simple.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/simple-parser.js b/examples/simple-parser.js index ce397d2..8a3f001 100644 --- a/examples/simple-parser.js +++ b/examples/simple-parser.js @@ -1,5 +1,5 @@ module.exports = function (device) { - var delimiter = "\r"; + var delimiter = " "; function DataParser(device) { this.device = device; this.buffer = ""; diff --git a/examples/simple.js b/examples/simple.js index db59995..1ca2c85 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,9 +1,10 @@ var util = require('util'); +var parser = require('./parser'); var XBee = require('../index.js').XBee; // Replace with your xbee's UART location -//var xbee = new XBee('/dev/ttyO1'); var xbee = new XBee('/dev/ttyO1'); +//var xbee = new XBee('/dev/ttyO1', parser); xbee.init(); xbee.on("configured", function(config) { From 71ecbbd713a898eb0adff2b9a243f055751dd797 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 10 Mar 2013 10:00:24 -0500 Subject: [PATCH 103/140] fix typo in example --- examples/simple.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple.js b/examples/simple.js index 1ca2c85..17079a9 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,5 +1,5 @@ var util = require('util'); -var parser = require('./parser'); +var parser = require('./simple-parser.js'); var XBee = require('../index.js').XBee; // Replace with your xbee's UART location From 8a4782a158eff90833659f142890a45455738b40 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sat, 16 Mar 2013 22:05:49 +0100 Subject: [PATCH 104/140] Massive API changes --- README.markdown | 71 ----------------- README.md | 69 ++++++++++++++++ examples/simple-parser.js | 18 ----- examples/simple.js | 23 ------ index.js | 161 ++++++++++++++++++++++++++------------ lib/constants.js | 75 +++++++++++------- lib/xbee-api.js | 85 ++++++++++++++++---- 7 files changed, 294 insertions(+), 208 deletions(-) delete mode 100644 README.markdown create mode 100644 README.md delete mode 100644 examples/simple-parser.js delete mode 100644 examples/simple.js diff --git a/README.markdown b/README.markdown deleted file mode 100644 index 3575a75..0000000 --- a/README.markdown +++ /dev/null @@ -1,71 +0,0 @@ -A more high level fork of Richard Morrison's node-xbee. - -Example -======= -If you pass no parser function to the XBee() constructor, nodes will emit each data packet that they receive as a 'data' event. See simple-parser.js for a basic parser that splits on \r, and is easily adapted if you wish to use \n or some other delimiter. The simple parser will merge frames and emit them split by your delimiter (so if you wonder why no data is emitted, make sure you use the right delimiter!). - -Set things up like this, substituting in paramaters that match your Xbee network: - -```javascript -var util = require('util'); -var XBee = require('svd-xbee').XBee; - -// Replace with your xbee's UART location and correct baud rate (if you omit baudrate, the code assumes your xbee talks at 57600). -var xbee = new XBee({port: '/dev/tty01', baudrate:9600}); - -xbee.on("configured", function(config) { - console.log("XBee Config: %s", util.inspect(config)); -}); - -xbee.on("node", function(node) { - console.log("Node %s connected", node.id); - - node.on("data", function(data) { - console.log("%s: %s", node.id, util.inspect(data)); - }); - -}); -``` -Then, you can run: - -```javascript -xbee.init(); -``` - -and you should start to see things logged back to your console. - -Background -========== - -Note that this readme is still mostly copied from the original module! - -[Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks. - -They can be connected to a computer over RS232 and communicated on using a standard serial port. - -Even easier, with something like the [XBee USB Explorer](http://www.sparkfun.com/products/8687) by SparkFun, you can connect to them easily over USB. - -This work is inspired by: - -* voodootikigod's [serialport module](https://github.com/voodootikigod/node-serialport) (in fact you're going to need this to use this package) -* "[Building Wireless Sensor Networks](http://shop.oreilly.com/product/9780596807740.do)" by Rob Faludi - -Setup -===== - -I have my xbee coordinator radio connected to the computer running Node. Crucially, the coordinator is in xbee's API mode 2 - this is required to allow you to send remote instructions, and so on, and uses escaping to improve reliability. - -My remote xbee network modules send periodic measurements and I can push them to web browsers, save them in a database, etc. - -I can also use this library to send remote commands and query remote xbee modules. For instance, setting a digital output on a remote module could turn a light on, or a motor, or a laser beam - up to you! - -Installation -============ - - npm install svd-xbee - -Licence -======= - -This work is based on the works of Richard Morrison -Creative Commons License
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd863c8 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# SVD-XBEE + +[Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks. They can be used to send/receive text data from serial ports of different devices. XBees can also be used alone for their on board digital and analog I/O capabilities. + +**svd-xbee** is a high level, actively maintained fork of Richard Morrison's [node-xbee](http://github.com/mozz100/node-xbee). It talks the ZigBee API with XBee radio modules over serial connections and provides high level abstractions of the XBee's functionality. + +### Nutshell +```javascript +var xbee = new require('svd-xbee').XBee({ + port: 'COM3', // replace with yours + baudrate: 9600 // 9600 is default +}) + +var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0xaa,0xe2]); + +var robot.on("data", function(data) { + if (data === "I LOVE HUMANS") robot.send("I LOVE ROBOTS"); +}); +``` +### Features + +- Asynchronous architecture +- Event-based Node Discovery +- Local and remote AT commands +- High-level abstraction of common tasks +- Parse API frames into meaningful objects + - Regular text data + - Analog & Digital I/O Samples + - Modem Status + - Transmission Reports + +### Installation + + npm install svd-xbee + + +SUPPORTED XBEE MODELS +===================== + +Both ZNet 2.5 and ZIGBEE modules should be supported. Since ZIGBEE offers more features and is more robust, you might be interested in upgrading your modules from ZNet 2.5 to ZIGBEE: [upgradingfromznettozb.pdf](ftp://ftp1.digi.com/support/documentation/upgradingfromznettozb.pdf). +Development is done using Series 2 XBee modules with XB24-ZB (ZIGBEE) firmware. In specific, this document is used as reference: [90000976_M.pdf](http://ftp1.digi.com/support/documentation/90000976_M.pdf "http://ftp1.digi.com/support/documentation/90000976_M.pdf"). See the [wiki](https://github.com/jouz/svd-xbee/wiki) for more details. + +Note that this module is not automatically tested right now - this is high on the TODO list. + + +PREPARATION +=========== + +The module communicating with svd-xbee must be set to use an API function set with escape characters enabled (ATAP = 2). Other nodes in the network can be configured however you find it convenient. See the [wiki](https://github.com/jouz/svd-xbee/wiki) for more details. + + +EXAMPLES +======== + +See the [Example Wiki](https://github.com/jouz/svd-xbee/wiki) for examples. + + +ACKNOWLEDGMENTS +=============== + +* voodootikigod's [serialport module](https://github.com/voodootikigod/node-serialport) (in fact you're going to need this to use this package) +* "[Building Wireless Sensor Networks](http://shop.oreilly.com/product/9780596807740.do)" by Rob Faludi + + +LICENSE +======= + + +> This work by Jan Kolkmeier is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Creative Commons License
Based on a work at github.com. diff --git a/examples/simple-parser.js b/examples/simple-parser.js deleted file mode 100644 index 8a3f001..0000000 --- a/examples/simple-parser.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = function (device) { - var delimiter = " "; - function DataParser(device) { - this.device = device; - this.buffer = ""; - } - DataParser.prototype.parse = function(data) { - this.buffer += data; - var split = this.buffer.indexOf(delimiter); - while (split > -1) { - this.device.emit('data', this.buffer.slice(0,split)); - this.buffer = this.buffer.slice(split+delimiter.length); - split = this.buffer.indexOf(delimiter); - } - } - - return new DataParser(device); -} diff --git a/examples/simple.js b/examples/simple.js deleted file mode 100644 index 17079a9..0000000 --- a/examples/simple.js +++ /dev/null @@ -1,23 +0,0 @@ -var util = require('util'); -var parser = require('./simple-parser.js'); -var XBee = require('../index.js').XBee; - -// Replace with your xbee's UART location -var xbee = new XBee('/dev/ttyO1'); -//var xbee = new XBee('/dev/ttyO1', parser); -xbee.init(); - -xbee.on("configured", function(config) { - console.log("XBee Config: %s", util.inspect(config)); -}); - - -xbee.on("node", function(node) { - console.log("Node %s connected", node.remote64.hex); - - node.on("data", function(data) { - console.log("%s: %s", node.remote64.hex, util.inspect(data)); - //node.send("pong", function(err, status) {}); - }); - -}); diff --git a/index.js b/index.js index 9c21a14..f1517d2 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ var os = require('os'); var C = api.Constants; -function XBee(options, data_parser) { +function XBee(options) { EventEmitter.call(this); // Option Parsing @@ -17,7 +17,9 @@ function XBee(options, data_parser) { this.options = options; } - this.data_parser = data_parser || options.data_parser || undefined; + this.tools = api.tools; + + this.data_parser = options.data_parser || undefined; this.use_heartbeat = options.use_heartbeat || false; this.heartbeat_packet = options.heartbeat_packet || '```'; @@ -38,7 +40,7 @@ XBee.prototype.init = function(cb) { var self = this; // Serial connection to the XBee self.serial = new serialport.SerialPort(self.options.port, { - baudrate: self.options.baudrate || 57600, + baudrate: self.options.baudrate || 9600, databits: 8, stopbits: 1, parity: 'none', @@ -46,7 +48,7 @@ XBee.prototype.init = function(cb) { }); self.serial.on("open", function() { - self.configure.bind(self)(cb); + self.readParameters.bind(self)(cb); }); var exit = function() { @@ -64,50 +66,66 @@ XBee.prototype.init = function(cb) { /* Frame-specific Handlers */ // Whenever a node is identified (on ATND command). - self._onNodeIdentification = function(data) { + self._onNodeDiscovery = function(data) { var node = data.node; if (!self.nodes[node.remote64.hex]) { self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); - self.emit("node", self.nodes[node.remote64.hex]); + self.emit("newNodeDiscovered", self.nodes[node.remote64.hex]); } else { // update 16-bit address, as it may change during reconnects. self.nodes[node.remote64.hex].remote16 = node.remote16; self.nodes[node.remote64.hex].id = node.id; - self.nodes[node.remote64.hex].emit("reconnect"); + self.nodes[node.remote64.hex].emit("discovered", self.nodes[node.remote64.hex]); + } + self.nodes[node.remote64.hex].connected = true; + } + + // Modem Status + self._onModemStatus = function(res) { + if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { + self.emit("joinedNetwork"); + } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { + self.emit("hardwareReset"); + } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { + self.emit("watchdogReset"); + } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { + self.emit("disassociated"); + } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { + self.emit("coordinatorStarted"); + } else { + console.warn("Modem status: ", C.MODEM_STATUS[res.status]); } } - // AT Command Responses from remote AT Commands + // Loose AT Command Responses from remote AT Commands. + // Should not arrive here if callback was passed to remote AT cmd. self._onRemoteCommandResponse = function(res) { if (self.nodes[res.remote64.hex]) { self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); - } else { - console.log("Unhandled REMOTE_AT_RESPONSE: %s", util.inspect(res)); - } + } } // Messages self._onReceivePacket = function(data) { if (!self.nodes[data.remote64.hex]) { var _data = { node:data }; - _data.node.id = "UNKNOWN"; - self._onNodeIdentification(_data); - console.log("ERROR: Data from unknown node!"); + self._onNodeDiscovery(_data); } self.nodes[data.remote64.hex]._onReceivePacket(data); } // Data samples (from XBee's I/O) self._onDataSampleRx = function(data) { - if (self.nodes[data.remote64.hex]) { - self.nodes[data.remote64.hex]._onDataSampleRx(data); - } else { - console.log("ERROR: Data sample from unknown node!"); + if (!self.nodes[data.remote64.hex]) { + var _data = { node:data }; + self._onNodeDiscovery(_data); } + self.nodes[data.remote64.hex]._onDataSampleRx(data); } + self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); self.serial.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); - self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeDiscovery); self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); @@ -119,13 +137,9 @@ XBee.prototype.init = function(cb) { }, 1); } -XBee.prototype.configure = function(_done_cb) { +XBee.prototype.readParameters = function(_done_cb) { var self = this; - /* - self._ATCB('ID', undefined, function(data) { - console.log("ID: "+util.inspect(data)); - }); - */ + // Returns a function that initiates an AT command to // query a configuration parameter's value. // To be passed to an async.parallel. @@ -136,38 +150,35 @@ XBee.prototype.configure = function(_done_cb) { if (!err) { cb(err, f(data.commandData)); } else { - console.log('XBee.configure.QF - ', err.msg); + console.error('Error: XBee.readParameters.QF; ', err.msg); } }); } } - var config = { - panid: QF('ID', undefined, api.bArr2HexStr), - id: QF('NI', undefined, api.bArr2Str), - sourceLow: QF('SL', undefined, api.bArr2HexStr), - sourceHigh: QF('SH', undefined, api.bArr2HexStr), - //maxPayloadSize: QF('NP', api.bArr2HexStr), // Returns ERROR :/ - setNodeDiscoveryTime: QF('NT', [0x96], function(a) { return "NT SET: "+api.bArr2Dec(a); }), - nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.bArr2Dec(a); }) + var parameters = { + panid: QF('ID', undefined, api.tools.bArr2HexStr), + id: QF('NI', undefined, api.tools.bArr2Str), + sourceHigh: QF('SH', undefined, api.tools.bArr2HexStr), + sourceLow: QF('SL', undefined, api.tools.bArr2HexStr), + nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.tools.bArr2Dec(a); }) }; var done = function(err, results) { if (err) { - self.emit("error", new Error("Failure to configure XBee module: "+util.inspect(err))); + self.emit("error", new Error("Failure to read XBee params: "+util.inspect(err))); if (typeof _done_cb === 'function') _done_cb(err); } - self.config = results; - self.emit("configured", self.config); - if (typeof _done_cb === 'function') _done_cb(null, self.config); - self.discover(); + self.parameters = results; + self.emit("initialized", self.parameters); + if (typeof _done_cb === 'function') _done_cb(null, self.parameters); } - // Using async to start discovery only when all parameters have been read. - var res_stop = Object.keys(config).length; + // Using async to read parameters + var res_stop = Object.keys(parameters).length; var results = {}; - for (k in config) { - config[k]((function(key) { + for (k in parameters) { + parameters[k]((function(key) { return function(err, data) { if (err) return done(err, null); results[key] = data; @@ -180,17 +191,34 @@ XBee.prototype.configure = function(_done_cb) { } } +// Add a node by hand. +XBee.prototype.addNode = function(remote64) { + var self = this; + var remote16 = [0xff,0xfe]; // Unknown + var node = { + remote16: { dec: remote16, hex: api.tools.bArr2HexStr(remote16) }, + remote64: { dec: remote64, hex: api.tools.bArr2HexStr(remote64) } + }; + + if (!self.nodes[node.remote64.hex]) { + self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); + self.nodes[node.remote64.hex].connected = false; + } + + return self.nodes[node.remote64.hex]; +} + // Run network discovery. Associated nodes can report in // for config.nodeDiscoveryTime ms. XBee.prototype.discover = function(cb) { var self = this; - // todo: could be nicer, pass _onNodeIdentification to _AT - but it's "once"... var cbid = self._AT('ND'); - self.serial.on(cbid, self._onNodeIdentification); + self.serial.on(cbid, self._onNodeDiscovery); setTimeout(function() { if (typeof cb === 'function') cb(); self.removeAllListeners(cbid); - }, self.config.nodeDiscoveryTime || 6000); + self.emit("discoveryEnd"); + }, self.parameters.nodeDiscoveryTime || 6000); } XBee.prototype.broadcast = function(data, cb) { @@ -247,6 +275,10 @@ XBee.prototype._send = function(data, remote64, remote16, _cb) { this._queue.push({ packets:packets, cb:_cb }); } +XBee.prototype.AT = function(cmd, val, _cb) { + this._AT(cmd, val, _cb); +} + XBee.prototype._AT = function(cmd, val, _cb) { // val parameter is optional if (typeof val === 'function') { @@ -293,9 +325,10 @@ exports.XBee = XBee; function Node(xbee, params, data_parser) { EventEmitter.call(this); this.xbee = xbee; - this.id = params.id; this.remote16 = params.remote16; this.remote64 = params.remote64; + this.id = params.id || ""; + this.deviceType = params.deviceType || -1; this.buffer = ""; if (typeof data_parser === 'function') this.parser = data_parser(this); @@ -335,25 +368,49 @@ Node.prototype._onReceivePacket = function(data) { this.emit('data', packet); } -Node.prototype.ATCommand = function(cmd, val, cb) { +Node.prototype.AT = function(cmd, val, cb) { + var cbid; // val parameter is optional if (typeof val === "function") { // use val as the callback in this case - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); + cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); } else { - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); + cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); } + /* + this.xbee.serial.on(cbid, function(res) { + console.log("Remote AT Response (2): ", util.inspect(err), util.inspect(res)); + }); + */ +} + +Node.prototype._onRemoteCommandResponse = function(res) { + console.warn("Remote Command Response not Implemented.", util.inspect(res)); } + +Node.prototype.isCoordinator = function() { + return this.deviceType === C.DEVICE_TYPES.COORDINATOR; +} + +Node.prototype.isRouter = function() { + return this.deviceType === C.DEVICE_TYPES.ROUTER; +} + +Node.prototype.isEndDevice = function() { + return this.deviceType === C.DEVICE_TYPES.END_DEVICE +} + + /* Node.prototype._onATResponse = function(res) { console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); } +*/ Node.prototype._onDataSampleRx = function(res) { - this.emit('data_sample', res); + this.emit('io', res.ioSample); } -*/ exports.Node = Node; diff --git a/lib/constants.js b/lib/constants.js index eae7e42..5d14e12 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -10,8 +10,20 @@ var ft = exports.FRAME_TYPE = {}; var diss = exports.DISCOVERY_STATUS = {}; var dels = exports.DELIVERY_STATUS = {}; var coms = exports.COMMAND_STATUS = {}; +var ms = exports.MODEM_STATUS = {}; var ro = exports.RECEIVE_OPTIONS = {}; +var dt = exports.DEVICE_TYPES = {}; +var dc = exports.DIGITAL_CHANNELS = {}; +var ac = exports.ANALOG_CHANNELS = {}; + +// Device Type +dt.COORDINATOR = 0x00; +dt[0x00] = "COORDINATOR"; +dt.ROUTER = 0x01; +dt[0x01] = "ROUTER"; +dt.END_DEVICE = 0x02; +dt[0x02] = "END_DEVICE"; // Frame Type ft.AT_COMMAND = 0x08; @@ -51,6 +63,26 @@ ft[0xA1] = "Route Record Indicator (0xA1)"; ft.MTO_ROUTE_REQUEST = 0xA3; ft[0xA3] = "Many-to-One Route Request Indicator (0xA3)"; +// Modem Status +ms.HARDWARE_RESET = 0x00; +ms[0x00] = "Hardware Reset"; +ms.WATCHDOG_RESET = 0x01; +ms[0x01] = "Watchdog timer reset"; +ms.JOINED_NETWORK = 0x02; +ms[0x02] = "Joined network"; +ms.DISASSOCIATED = 0x03; +ms[0x03] = "Disassociated"; +ms.COORDINATOR_STARTED = 0x06; +ms[0x06] = "Coordinator started"; +ms.SECURITY_KEY_UPDATED = 0x07; +ms[0x07] = "Network security key was updated"; +ms.VOLTAGE_SUPPLY_LIMIT_EXCEEDED = 0x0D; +ms[0x0D] = "Voltage supply limit exceeded"; +ms.CONFIGURATION_CHANGED_DURING_JOIN = 0x11; +ms[0x11] = "Modem Configuration changed while join in progress"; +ms.STACK_ERROR = 0x80; +ms[0x80] = "Stack Error"; + // Command Status coms.OK = 0x00; coms[0x00] = "OK (0x00)"; @@ -121,31 +153,20 @@ ro[0x20] = "Packet encrypted with APS encryption (0x20)"; ro.PACKET_SENT_FROM_END_DEVICE = 0x40; ro[0x40] = "Packet was sent from an end device (if known) (0x40)"; -/* Bitmasks for I/O pins - * (what was this used for?) -var digiPinsByte1 = { - D10: 4, - D11: 8, - D12: 16 -}; - -var digiPinsByte2 = { - D0: 1, - D1: 2, - D2: 4, - D3: 8, - D4: 16, - D5: 32, - D6: 64, - D7: 128 -}; - -var analogPins = { - A0: 1, - A1: 2, - A2: 4, - A3: 8, - supply: 128 -}; +dc[0] = "DIO0"; // AD0 +dc[1] = "DIO1"; // AD1 +dc[2] = "DIO2"; // AD2 +dc[3] = "DIO3"; // AD3 +dc[4] = "DIO4"; // - +dc[5] = "DIO5"; // ASSOC +dc[6] = "DIO6"; // RTS +dc[7] = "GPIO7"; // CTS +dc[10] = "DIO10"; // RSSI +dc[11] = "DIO11"; // PWM +dc[12] = "DIO12"; // CD -*/ +ac[0] = "AD0"; // DIO0 +ac[1] = "AD1"; // DIO1 +ac[2] = "AD2"; // DIO2 +ac[3] = "AD3"; // DIO3 +ac[7] = "SUPPLY"; diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 934cdad..9555d11 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -4,7 +4,10 @@ var util = require('util'); var C = exports.Constants = require('./constants.js'); exports = module.exports; -exports.dec2Hex = function(d, padding) { +var tools = exports.tools = {}; + + +tools.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -15,15 +18,17 @@ exports.dec2Hex = function(d, padding) { return hex; } -exports.bArr2HexStr = function(a) { +tools.bArr2HexStr = function(a) { var s = ''; + var leadingZero = true; for(i in a) { - s += exports.dec2Hex(a[i]); + if (a[i] != 0) leadingZero = false; + if (!leadingZero) s += tools.dec2Hex(a[i]); } return s; } -exports.bArr2Str = function(a) { +tools.bArr2Str = function(a) { var s = ''; for(i in a) { s += String.fromCharCode(a[i]); @@ -31,7 +36,7 @@ exports.bArr2Str = function(a) { return s; } -exports.bArr2Dec = function(a) { +tools.bArr2Dec = function(a) { // given a byte array like [3,21], convert to a decimal value. // e.g. [3,21] --> 3 * 256 + 21 = 789 var r = 0; @@ -42,6 +47,15 @@ exports.bArr2Dec = function(a) { return r } +tools.dec2bArr = function(a, m) { + var r = []; + while (a > 0 || r.length < m) { + r.unshift(a & 0xff); + a = a >> 8; + } + return r; +} + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands @@ -52,7 +66,7 @@ exports.api_mode = 2; function incrementFrameId() { frameId++; - frameId %= 255; // fails if it exactly 126 (0x7e is start byte) + frameId %= 255; if (frameId == 0) frameId = 1; // 0x00 means: no response expected return frameId; } @@ -305,9 +319,9 @@ exports.packetBuilder = function () { if (!checksum === 255 - (running_total % 256)) { console.log("CHECKSUM_MISMATCH"); } else { - //console.log(">>> "+util.inspect(packet)); var parser = new PacketParser(packet) var json = parser.parse(); + //console.log("FRAME: %s", C.FRAME_TYPE[json.ft]); if (json.not_implemented) { console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); } else { @@ -349,7 +363,7 @@ PacketParser.prototype.parse = function() { PacketParser.prototype.readAddr = function(name, length) { var dec = this.payload.splice(0, length); - this.write[name] = { dec: dec, hex: exports.bArr2HexStr(dec) } + this.write[name] = { dec: dec, hex: tools.bArr2HexStr(dec) } return this; } @@ -463,24 +477,61 @@ frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { } }; +frames[C.FRAME_TYPE.MODEM_STATUS] = { + parse: function(parser) { + parser + .readByte('status'); + } +} + frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { parse: function(parser) { parser .readAddr64('remote64') .readAddr16('remote16') .readByte('receiveOptions') - .readByte('numSamples') - .readByte('digitalChannelMask', 2) - .readByte('analogChannelMask') - if (parser.json.digitalChannelMask[0] + parser.json.digitalChannelMask[1] > 0) - parser.readByte('digitalSamples',2); - if (parser.json.analogChannelMask > 0) - parser.collectPayload('analogSamples'); - // skip formatting data for now + .collectPayload('rawIOSample'); + parser.json.ioSample = tools.parseIOSample(parser.json.rawIOSample); } }; -// Unsupportet Frame Types +tools.parseIOSample = function(sample) { + var res = { + digitalSamples: {}, + analogSamples: {} + } + var numSamples = sample.splice(0,1)[0]; + var digitalChannelMask = sample.splice(0,2); + var analogChannelMask = sample.splice(0,1)[0]; + var mskD = (digitalChannelMask[0] << 8) | digitalChannelMask[1]; + var mskA = analogChannelMask; + + if (mskD > 0) { + var digitalSamples = sample.splice(0,2); + var valD = (digitalSamples[0] << 8) | digitalSamples[1]; + for (var i in C.DIGITAL_CHANNELS) { + if ((mskD & (1 << i)) >> i) { + res.digitalSamples[C.DIGITAL_CHANNELS[i]] = (valD & (1 << i)) >> i; + } + } + } + + if (mskA > 0) { + var analogSamples = sample.splice(0); + var sampleNr = 0; + for (var i in C.ANALOG_CHANNELS) { + if ((mskA & (1 << i)) >> i) { + var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; + // Convert to mV: + res.analogSamples[C.ANALOG_CHANNELS[i]] = (valA * 1200) / 1023; + sampleNr++; + } + } + } + return res; +} + +// Unsupported Frame Types for (key in C.FRAME_TYPE) { var val = C.FRAME_TYPE[key]; if (typeof val === 'number') { From 7ead1f6a6bfcbbe0fdb820e50d568882ad37396e Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 00:37:08 +0100 Subject: [PATCH 105/140] Updated Readme --- README.md | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index fd863c8..0be79ba 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ ### Nutshell ```javascript -var xbee = new require('svd-xbee').XBee({ +var XBee = require('svd-xbee'); + +var xbee = new XBee({ port: 'COM3', // replace with yours baudrate: 9600 // 9600 is default }) @@ -14,7 +16,8 @@ var xbee = new require('svd-xbee').XBee({ var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0xaa,0xe2]); var robot.on("data", function(data) { - if (data === "I LOVE HUMANS") robot.send("I LOVE ROBOTS"); + console.log("robot>", data); + if (data == "ping") robot.send("pong"); }); ``` ### Features @@ -33,37 +36,32 @@ var robot.on("data", function(data) { npm install svd-xbee +### Documentation -SUPPORTED XBEE MODELS -===================== - -Both ZNet 2.5 and ZIGBEE modules should be supported. Since ZIGBEE offers more features and is more robust, you might be interested in upgrading your modules from ZNet 2.5 to ZIGBEE: [upgradingfromznettozb.pdf](ftp://ftp1.digi.com/support/documentation/upgradingfromznettozb.pdf). -Development is done using Series 2 XBee modules with XB24-ZB (ZIGBEE) firmware. In specific, this document is used as reference: [90000976_M.pdf](http://ftp1.digi.com/support/documentation/90000976_M.pdf "http://ftp1.digi.com/support/documentation/90000976_M.pdf"). See the [wiki](https://github.com/jouz/svd-xbee/wiki) for more details. +For documentation, see the [Documentation](https://github.com/jouz/svd-xbee/wiki/Documentation). -Note that this module is not automatically tested right now - this is high on the TODO list. +### EXAMPLES +See the [examples folder](https://github.com/jouz/svd-xbee/tree/master/examples) in the repository for more examples. -PREPARATION -=========== +## SUPPORTED XBEE MODELS -The module communicating with svd-xbee must be set to use an API function set with escape characters enabled (ATAP = 2). Other nodes in the network can be configured however you find it convenient. See the [wiki](https://github.com/jouz/svd-xbee/wiki) for more details. +Both ZNet 2.5 and ZIGBEE modules should be supported. Since ZIGBEE offers more features and is more robust, you might be interested in upgrading your modules from ZNet 2.5 to ZIGBEE: [upgradingfromznettozb.pdf](ftp://ftp1.digi.com/support/documentation/upgradingfromznettozb.pdf). +Development is done using Series 2 XBee modules with XB24-ZB (ZIGBEE) firmware. In specific, this document is used as reference: [90000976_M.pdf](http://ftp1.digi.com/support/documentation/90000976_M.pdf "http://ftp1.digi.com/support/documentation/90000976_M.pdf"). -EXAMPLES -======== +## MODULE CONFIGURATION -See the [Example Wiki](https://github.com/jouz/svd-xbee/wiki) for examples. +The module communicating with svd-xbee must be set to use an API function set with escape characters enabled (ATAP = 2). Other nodes in the network can be configured however you find it convenient. See [Module Configuration](https://github.com/jouz/svd-xbee/wiki/Module-Configurationi) for more details. -ACKNOWLEDGMENTS -=============== +## ACKNOWLEDGMENTS * voodootikigod's [serialport module](https://github.com/voodootikigod/node-serialport) (in fact you're going to need this to use this package) * "[Building Wireless Sensor Networks](http://shop.oreilly.com/product/9780596807740.do)" by Rob Faludi -LICENSE -======= +## LICENSE > This work by Jan Kolkmeier is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Creative Commons License
Based on a work at github.com. From b223336351d7fb50e26bdccbc5d894282c952be7 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 00:37:25 +0100 Subject: [PATCH 106/140] Added new examples --- examples/full/full.js | 107 ++++++++++++++++++++++++++++++++++ examples/full/parser.js | 18 ++++++ examples/nutshell/nutshell.js | 14 +++++ 3 files changed, 139 insertions(+) create mode 100644 examples/full/full.js create mode 100644 examples/full/parser.js create mode 100644 examples/nutshell/nutshell.js diff --git a/examples/full/full.js b/examples/full/full.js new file mode 100644 index 0000000..8414d5f --- /dev/null +++ b/examples/full/full.js @@ -0,0 +1,107 @@ +var util = require('util'); +var XBee = require('../../index.js').XBee; + +// This parser buffers data, emits chucks +// seperated by space chars (" ") +var Parser = require('./parser.js'); + +var xbee = new XBee({ + port: 'COM3', // replace with yours + baudrate: 9600 // 9600 is default +}) + +// Open COM port, read some parameters from the XBee at once. +xbee.init(); + +// Add Node by hand... +var myNode = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0x2f,0xe4], Parser); + +// Triggered whenever this node +// - responds to discovery request +// - sends identification frame on network association +// (not necessarily on power up, as node might +// already be associated) +myNode.on("discovered", function(node) { + console.log("Discovered myNode"); + + // We configure pin D4 to sample digital data. + myNode.AT("D4", [ 0x03 ], function(err, res, x) { + if (err) return console.log("AT Error D4:", err); + + // This configures pin 2 to sample analog data. + myNode.AT("D2", [ 0x02 ], function(err, res, x) { + if (err) return console.log("AT Error D2:", err); + + // We can put requests in the callback, but generally + // but generally parallel requests are fine! + myNode.AT("IS", function(err, res) { // Manually query IO Sample + if (err) return console.log("AT Error IS:", err); + console.log("Queried IO Sample: ", util.inspect(xbee.tools.parseIOSample(res))); + }); + }); + + }); +}); + +// Whenever myNode sends us an IO data sample... +myNode.on("io", function(io) { + console.log("I/O: ", util.inspect(io)); +}); + +// Whenever myNode sends us text data - since we are using +// a parser, data will first be processed there before landing +// here: +myNode.on("data", function(data) { + console.log("Data: ", util.inspect(data)); + if (data == "ping") myNode.send("pong"); +}); + + +// Emitted when .init() is done (COM port open, parameters read) +xbee.on("initialized", function(params) { + console.log("XBee Parameters: %s", util.inspect(params)); + // Start Node discovery to find currently connected nodes. + xbee.discover(); + console.log("Node discovery starded..."); + + // Local Request: + xbee.AT("VR", function(err, res) { + console.log("Firmware Version:", xbee.tools.bArr2HexStr(res)); + }); + + + // Remote Requests ... + // Enable auto reporting of IO Samples with a 2000ms interval set + // Note that this request will most typically fail. + // Try uncommenting the line "xbee.discover()", and this should + // work fine (if your network is configured properly). + // The reason is that remote nodes seem to become unresponsive for + // a little while during node discovery. + myNode.AT("IR", xbee.tools.dec2bArr(2000), function(err, res) { + if (err) return console.log("AT Error:", util.inspect(err)); + console.log("Auto Reporting Enabled!"); + }); +}); + +xbee.on("discoveryEnd", function() { + // Discovery is over. If the XBee is an End Device/Router, + // you may want to re-issue xbee.discover() later. + // For Coordinators this should not be necessary, as + // nodes will notify coordinators once they join the PAN, + // triggering the "node" event. + console.log("...node discovery over"); +}); + +// Triggered whenever a node is discovered that is not already +// added. / myNode will not show up here! +xbee.on("newNodeDiscovered", function(node) { + console.log("Node %s discovered", node.remote64.hex); + console.log(util.inspect(node)); + + node.on("data", function(data) { + console.log("%s> %s", node.remote64.hex, util.inspect(data)); + node.send("pong", function(err, status) { + // Transmission successful if err is null + }); + }); +}); diff --git a/examples/full/parser.js b/examples/full/parser.js new file mode 100644 index 0000000..c9af6f3 --- /dev/null +++ b/examples/full/parser.js @@ -0,0 +1,18 @@ +exports = module.exports = function (device) { + var delimiter = " "; + function DataParser(device) { + this.device = device; + this.buffer = ""; + } + DataParser.prototype.parse = function(data) { + this.buffer += data; + var split = this.buffer.indexOf(delimiter); + while (split > -1) { + this.device.emit('data', this.buffer.slice(0,split)); + this.buffer = this.buffer.slice(split+delimiter.length); + split = this.buffer.indexOf(delimiter); + } + } + + return new DataParser(device); +} diff --git a/examples/nutshell/nutshell.js b/examples/nutshell/nutshell.js new file mode 100644 index 0000000..62c85e5 --- /dev/null +++ b/examples/nutshell/nutshell.js @@ -0,0 +1,14 @@ +var XBee = require('../../index.js').XBee; + +var xbee = new XBee({ + port: 'COM3', // replace with yours + baudrate: 9600 // 9600 is default +}) + +// Add Set to your remote node +var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0x2f,0xe4]); + +var robot.on("data", function(data) { + console.log("robot>", data); + if (data == "ping") robot.send("pong"); +}); From 2ad8e3570c751a131c22689e088797664913e24b Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 00:37:58 +0100 Subject: [PATCH 107/140] change event emitter params --- index.js | 844 ++++++++++++++++++------------------ lib/xbee-api.js | 1097 ++++++++++++++++++++++++----------------------- 2 files changed, 977 insertions(+), 964 deletions(-) diff --git a/index.js b/index.js index f1517d2..aea2fd6 100644 --- a/index.js +++ b/index.js @@ -1,416 +1,428 @@ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var api = require("./lib/xbee-api.js"); -var serialport = require("serialport"); -var async = require('async'); -var os = require('os'); - -var C = api.Constants; - -function XBee(options) { - EventEmitter.call(this); - - // Option Parsing - if (typeof options === 'string') { - this.options = { port: options }; - } else { - this.options = options; - } - - this.tools = api.tools; - - this.data_parser = options.data_parser || undefined; - - this.use_heartbeat = options.use_heartbeat || false; - this.heartbeat_packet = options.heartbeat_packet || '```'; - this.heartbeat_timeout = options.heartbeat_timeout || 8000; - - // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? - this.transmit_status_timeout = options.transmit_status_timeout || 1000; - - if (options.api_mode) api.api_mode = options.api_mode; - - // Current nodes - this.nodes = {}; -} - -util.inherits(XBee, EventEmitter); - -XBee.prototype.init = function(cb) { - var self = this; - // Serial connection to the XBee - self.serial = new serialport.SerialPort(self.options.port, { - baudrate: self.options.baudrate || 9600, - databits: 8, - stopbits: 1, - parity: 'none', - parser: api.packetBuilder() - }); - - self.serial.on("open", function() { - self.readParameters.bind(self)(cb); - }); - - var exit = function() { - self.serial.close(function(err) { - if (err) console.log("Error closing port: "+util.inspect(err)); - process.exit(); - }); - } - - if (os.platform() !== 'win32') { - process.on('SIGINT', exit); - } - - - /* Frame-specific Handlers */ - - // Whenever a node is identified (on ATND command). - self._onNodeDiscovery = function(data) { - var node = data.node; - if (!self.nodes[node.remote64.hex]) { - self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); - self.emit("newNodeDiscovered", self.nodes[node.remote64.hex]); - } else { - // update 16-bit address, as it may change during reconnects. - self.nodes[node.remote64.hex].remote16 = node.remote16; - self.nodes[node.remote64.hex].id = node.id; - self.nodes[node.remote64.hex].emit("discovered", self.nodes[node.remote64.hex]); - } - self.nodes[node.remote64.hex].connected = true; - } - - // Modem Status - self._onModemStatus = function(res) { - if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { - self.emit("joinedNetwork"); - } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { - self.emit("hardwareReset"); - } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { - self.emit("watchdogReset"); - } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { - self.emit("disassociated"); - } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { - self.emit("coordinatorStarted"); - } else { - console.warn("Modem status: ", C.MODEM_STATUS[res.status]); - } - } - - // Loose AT Command Responses from remote AT Commands. - // Should not arrive here if callback was passed to remote AT cmd. - self._onRemoteCommandResponse = function(res) { - if (self.nodes[res.remote64.hex]) { - self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); - } - } - - // Messages - self._onReceivePacket = function(data) { - if (!self.nodes[data.remote64.hex]) { - var _data = { node:data }; - self._onNodeDiscovery(_data); - } - self.nodes[data.remote64.hex]._onReceivePacket(data); - } - - // Data samples (from XBee's I/O) - self._onDataSampleRx = function(data) { - if (!self.nodes[data.remote64.hex]) { - var _data = { node:data }; - self._onNodeDiscovery(_data); - } - self.nodes[data.remote64.hex]._onDataSampleRx(data); - } - - self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); - self.serial.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); - self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeDiscovery); - self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); - self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); - - self._queue = async.queue(function(task, callback) { - async.series(task.packets, function(err, data) { - if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); - callback(); - }); - }, 1); -} - -XBee.prototype.readParameters = function(_done_cb) { - var self = this; - - // Returns a function that initiates an AT command to - // query a configuration parameter's value. - // To be passed to an async.parallel. - var QF = function(command, val, f) { // Format the result using f - f = typeof f !== 'undefined' ? f : function(a){return a}; - return function(cb) { - self._AT(command, val, function(err, data) { - if (!err) { - cb(err, f(data.commandData)); - } else { - console.error('Error: XBee.readParameters.QF; ', err.msg); - } - }); - } - } - - var parameters = { - panid: QF('ID', undefined, api.tools.bArr2HexStr), - id: QF('NI', undefined, api.tools.bArr2Str), - sourceHigh: QF('SH', undefined, api.tools.bArr2HexStr), - sourceLow: QF('SL', undefined, api.tools.bArr2HexStr), - nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.tools.bArr2Dec(a); }) - }; - - var done = function(err, results) { - if (err) { - self.emit("error", new Error("Failure to read XBee params: "+util.inspect(err))); - if (typeof _done_cb === 'function') _done_cb(err); - } - self.parameters = results; - self.emit("initialized", self.parameters); - if (typeof _done_cb === 'function') _done_cb(null, self.parameters); - } - - // Using async to read parameters - var res_stop = Object.keys(parameters).length; - var results = {}; - for (k in parameters) { - parameters[k]((function(key) { - return function(err, data) { - if (err) return done(err, null); - results[key] = data; - // TODO: Timeout? - if (--res_stop === 0) { - done(null, results); - } - } - })(k)); - } -} - -// Add a node by hand. -XBee.prototype.addNode = function(remote64) { - var self = this; - var remote16 = [0xff,0xfe]; // Unknown - var node = { - remote16: { dec: remote16, hex: api.tools.bArr2HexStr(remote16) }, - remote64: { dec: remote64, hex: api.tools.bArr2HexStr(remote64) } - }; - - if (!self.nodes[node.remote64.hex]) { - self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); - self.nodes[node.remote64.hex].connected = false; - } - - return self.nodes[node.remote64.hex]; -} - -// Run network discovery. Associated nodes can report in -// for config.nodeDiscoveryTime ms. -XBee.prototype.discover = function(cb) { - var self = this; - var cbid = self._AT('ND'); - self.serial.on(cbid, self._onNodeDiscovery); - setTimeout(function() { - if (typeof cb === 'function') cb(); - self.removeAllListeners(cbid); - self.emit("discoveryEnd"); - }, self.parameters.nodeDiscoveryTime || 6000); -} - -XBee.prototype.broadcast = function(data, cb) { - var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - var remote16 = [0xff,0xfe]; - this._send(data, remote64, remote16, cb); -} - -XBee.prototype._makeTask = function(packet) { - var self = this; - return function Writer(cb) { - //console.log("<<< "+util.inspect(packet.data)); - //console.log("<<< "+packet.data); - - var timeout = setTimeout(function() { - cb({ msg: "Never got Transmit status from XBee" }); - }, self.transmit_status_timeout ); - self.serial.write(packet.data, function(err, results) { - if (err) { - cb(err); - } else { - //console.log(util.inspect(packet.data)); - if (results != packet.data.length) return cb(new Error("Not all bytes written")); - self.serial.once(packet.cbid, function(data) { - //console.log("Got Respones: "+packet.cbid); - clearTimeout(timeout); - var error = null; - if (data.commandStatus && data.commandStatus != C.COMMAND_STATUS.OK) { - error = C.COMMAND_STATUS[data.commandStatus]; - } else if (data.deliveryStatus && data.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { - error = C.DELIVERY_STATUS[data.deliveryStatus]; - } - cb(error, data); - }); - } - }); - }; -} - -XBee.prototype._send = function(data, remote64, remote16, _cb) { - var packets = []; - while (data.length > 0) { - var frame = new api.TransmitRFData(); - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - frame.RFData = data.slice(0, C.MAX_PAYLOAD_SIZE); - data = data.slice(C.MAX_PAYLOAD_SIZE); - packets.push(this._makeTask({ - data: frame.getBytes(), - cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId - })); - } - - this._queue.push({ packets:packets, cb:_cb }); -} - -XBee.prototype.AT = function(cmd, val, _cb) { - this._AT(cmd, val, _cb); -} - -XBee.prototype._AT = function(cmd, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; - } - - var frame = new api.ATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; -} - - -XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; - } - - var frame = new api.RemoteATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; -} - -exports.XBee = XBee; - -function Node(xbee, params, data_parser) { - EventEmitter.call(this); - this.xbee = xbee; - this.remote16 = params.remote16; - this.remote64 = params.remote64; - this.id = params.id || ""; - this.deviceType = params.deviceType || -1; - this.buffer = ""; - if (typeof data_parser === 'function') - this.parser = data_parser(this); - this.timeout = {}; - this.connected = true; - this.refreshTimeout(); -} - -util.inherits(Node, EventEmitter); - -Node.prototype.timeoutOccured = function() { - this.connected = false; - this.emit('disconnect'); -} - -Node.prototype.refreshTimeout = function() { - clearTimeout(this.timeout); - this.timeout = setTimeout(this.timeoutOccured.bind(this), this.xbee.heartbeat_timeout); - if (!this.connected) { - this.connected = true; - // todo other stuff - } -} - -Node.prototype.send = function(data, cb) { - this.xbee._send(data, this.remote64, this.remote16, cb); -} - -Node.prototype._onReceivePacket = function(data) { - // TODO: should be buffer all along! - var packet = new Buffer(data.rawData).toString('ascii'); - if (this.xbee.use_heartbeat && packet === this.xbee.heartbeat_packet) - this.refreshTimeout(); - else if (this.parser !== undefined) - this.parser.parse(packet); - else - this.emit('data', packet); -} - -Node.prototype.AT = function(cmd, val, cb) { - var cbid; - // val parameter is optional - if (typeof val === "function") { - // use val as the callback in this case - cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); - } else { - cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); - } - - /* - this.xbee.serial.on(cbid, function(res) { - console.log("Remote AT Response (2): ", util.inspect(err), util.inspect(res)); - }); - */ -} - -Node.prototype._onRemoteCommandResponse = function(res) { - console.warn("Remote Command Response not Implemented.", util.inspect(res)); -} - - -Node.prototype.isCoordinator = function() { - return this.deviceType === C.DEVICE_TYPES.COORDINATOR; -} - -Node.prototype.isRouter = function() { - return this.deviceType === C.DEVICE_TYPES.ROUTER; -} - -Node.prototype.isEndDevice = function() { - return this.deviceType === C.DEVICE_TYPES.END_DEVICE -} - - -/* -Node.prototype._onATResponse = function(res) { - console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); -} -*/ - -Node.prototype._onDataSampleRx = function(res) { - this.emit('io', res.ioSample); -} - -exports.Node = Node; +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var api = require("./lib/xbee-api.js"); +var serialport = require("serialport"); +var async = require('async'); +var os = require('os'); + +var C = api.Constants; + +function XBee(options) { + EventEmitter.call(this); + + // Option Parsing + if (typeof options === 'string') { + this.options = { port: options }; + } else { + this.options = options; + } + + this.tools = api.tools; + + this.data_parser = options.data_parser || undefined; + + this.use_heartbeat = options.use_heartbeat || false; + this.heartbeat_packet = options.heartbeat_packet || '```'; + this.heartbeat_timeout = options.heartbeat_timeout || 8000; + + // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? + this.transmit_status_timeout = options.transmit_status_timeout || 1000; + + if (options.api_mode) api.api_mode = options.api_mode; + + // Current nodes + this.nodes = {}; +} + +util.inherits(XBee, EventEmitter); + +XBee.prototype.init = function(cb) { + var self = this; + // Serial connection to the XBee + self.serial = new serialport.SerialPort(self.options.port, { + baudrate: self.options.baudrate || 9600, + databits: 8, + stopbits: 1, + parity: 'none', + parser: api.packetBuilder() + }); + + self.serial.on("open", function() { + self.readParameters.bind(self)(cb); + }); + + var exit = function() { + self.serial.close(function(err) { + if (err) console.log("Error closing port: "+util.inspect(err)); + process.exit(); + }); + } + + if (os.platform() !== 'win32') { + process.on('SIGINT', exit); + } + + + /* Frame-specific Handlers */ + + // Whenever a node is identified (on ATND command). + self._onNodeDiscovery = function(node) { + if (node.node) node = node.node; + if (!self.nodes[node.remote64.hex]) { + self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); + self.emit("newNodeDiscovered", self.nodes[node.remote64.hex]); + } else { + // update 16-bit address, as it may change during reconnects. + self.nodes[node.remote64.hex].remote16 = node.remote16; + self.nodes[node.remote64.hex].id = node.id; + self.nodes[node.remote64.hex].emit("discovered", self.nodes[node.remote64.hex]); + } + self.nodes[node.remote64.hex].connected = true; + } + + // Modem Status + self._onModemStatus = function(packet) { + if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { + self.emit("joinedNetwork", packet); + } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { + self.emit("hardwareReset", packet); + } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { + self.emit("watchdogReset", packet); + } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { + self.emit("disassociated", packet); + } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { + self.emit("coordinatorStarted", packet); + } else { + console.warn("Modem status: ", C.MODEM_STATUS[res.status]); + } + } + + // Loose AT Command Responses from remote AT Commands. + // Should not arrive here if callback was passed to remote AT cmd. + self._onRemoteCommandResponse = function(res) { + if (self.nodes[res.remote64.hex]) { + self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); + } + } + + // Messages + self._onReceivePacket = function(data) { + if (!self.nodes[data.remote64.hex]) { + var _data = { node:data }; + self._onNodeDiscovery(_data); + } + self.nodes[data.remote64.hex]._onReceivePacket(data); + } + + // Data samples (from XBee's I/O) + self._onDataSampleRx = function(data) { + if (!self.nodes[data.remote64.hex]) { + var _data = { node:data }; + self._onNodeDiscovery(_data); + } + self.nodes[data.remote64.hex]._onDataSampleRx(data); + } + + self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); + self.serial.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeDiscovery); + self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); + self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + + self._queue = async.queue(function(task, callback) { + async.series(task.packets, function(err, data) { + if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); + callback(); + }); + }, 1); +} + +XBee.prototype.readParameters = function(_done_cb) { + var self = this; + + // Returns a function that initiates an AT command to + // query a configuration parameter's value. + // To be passed to an async.parallel. + var QF = function(command, val, f) { // Format the result using f + f = typeof f !== 'undefined' ? f : function(a){return a}; + return function(cb) { + self._AT(command, val, function(err, res) { + if (!err) { + cb(err, f(res)); + } else { + console.error('Error: XBee.readParameters.QF; ', err.msg); + } + }); + } + } + + var parameters = { + panid: QF('ID', undefined, api.tools.bArr2HexStr), + id: QF('NI', undefined, api.tools.bArr2Str), + sourceHigh: QF('SH', undefined, api.tools.bArr2HexStr), + sourceLow: QF('SL', undefined, api.tools.bArr2HexStr), + nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.tools.bArr2Dec(a); }) + }; + + var done = function(err, results) { + if (err) { + self.emit("error", new Error("Failure to read XBee params: "+util.inspect(err))); + if (typeof _done_cb === 'function') _done_cb(err); + } + self.parameters = results; + self.emit("initialized", self.parameters); + if (typeof _done_cb === 'function') _done_cb(null, self.parameters); + } + + // Using async to read parameters + var res_stop = Object.keys(parameters).length; + var results = {}; + for (k in parameters) { + parameters[k]((function(key) { + return function(err, data) { + if (err) return done(err, null); + results[key] = data; + // TODO: Timeout? + if (--res_stop === 0) { + done(null, results); + } + } + })(k)); + } +} + +// Add a node by hand. +XBee.prototype.addNode = function(remote64, parser) { + var self = this; + var remote16 = [0xff,0xfe]; // Unknown + var node_data = { + remote16: { dec: remote16, hex: api.tools.bArr2HexStr(remote16) }, + remote64: { dec: remote64, hex: api.tools.bArr2HexStr(remote64) } + }; + + var node = self.nodes[node_data.remote64.hex]; + + if (!node) { + node = self.nodes[node_data.remote64.hex] = new Node(self, node_data, self.data_parser); + node.connected = false; + } + + if (typeof parser === "function") + node.parser = parser(node); + + return node; +} + +// Run network discovery. Associated nodes can report in +// for config.nodeDiscoveryTime ms. +XBee.prototype.discover = function(cb) { + var self = this; + var cbid = self._AT('ND'); + self.serial.on(cbid, function(node) { + self._onNodeDiscovery(node.commandData); + }) + setTimeout(function() { + if (typeof cb === 'function') cb(); + self.removeAllListeners(cbid); + self.emit("discoveryEnd"); + }, self.parameters.nodeDiscoveryTime || 6000); +} + +XBee.prototype.broadcast = function(data, cb) { + var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + var remote16 = [0xff,0xfe]; + this._send(data, remote64, remote16, cb); +} + +XBee.prototype._makeTask = function(packet) { + var self = this; + return function Writer(cb) { + //console.log("<<< "+util.inspect(packet.data)); + //console.log("<<< "+packet.data); + + var timeout = setTimeout(function() { + cb({ msg: "Never got Transmit status from XBee" }); + }, self.transmit_status_timeout ); + self.serial.write(packet.data, function(err, results) { + if (err) { + cb(err); + } else { + //console.log(util.inspect(packet.data)); + if (results != packet.data.length) return cb(new Error("Not all bytes written")); + self.serial.once(packet.cbid, function(packet) { + //console.log("Got Respones: "+packet.cbid); + clearTimeout(timeout); + var error = null; + if (packet.commandStatus !== undefined) { + if (packet.commandStatus != C.COMMAND_STATUS.OK) { + error = C.COMMAND_STATUS[packet.commandStatus]; + } + packet = packet.commandData; + } else if (packet.deliveryStatus !== undefined) { + if (packet.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { + error = C.DELIVERY_STATUS[packet.deliveryStatus]; + } + } + cb(error, packet); + }); + } + }); + }; +} + +XBee.prototype._send = function(data, remote64, remote16, _cb) { + var packets = []; + while (data.length > 0) { + var frame = new api.TransmitRFData(); + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + frame.RFData = data.slice(0, C.MAX_PAYLOAD_SIZE); + data = data.slice(C.MAX_PAYLOAD_SIZE); + packets.push(this._makeTask({ + data: frame.getBytes(), + cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId + })); + } + + this._queue.push({ packets:packets, cb:_cb }); +} + +XBee.prototype.AT = function(cmd, val, _cb) { + this._AT(cmd, val, _cb); +} + +XBee.prototype._AT = function(cmd, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; +} + + +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.RemoteATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; +} + +exports.XBee = XBee; + +function Node(xbee, params, data_parser) { + EventEmitter.call(this); + this.xbee = xbee; + this.remote16 = params.remote16; + this.remote64 = params.remote64; + this.id = params.id || ""; + this.deviceType = params.deviceType || -1; + this.buffer = ""; + if (typeof data_parser === 'function') + this.parser = data_parser(this); + this.timeout = {}; + this.connected = true; + this.refreshTimeout(); +} + +util.inherits(Node, EventEmitter); + +Node.prototype.timeoutOccured = function() { + this.connected = false; + this.emit('disconnect'); +} + +Node.prototype.refreshTimeout = function() { + clearTimeout(this.timeout); + this.timeout = setTimeout(this.timeoutOccured.bind(this), this.xbee.heartbeat_timeout); + if (!this.connected) { + this.connected = true; + // todo other stuff + } +} + +Node.prototype.send = function(data, cb) { + this.xbee._send(data, this.remote64, this.remote16, cb); +} + +Node.prototype._onReceivePacket = function(packet) { + // TODO: should be buffer all along! + var data = new Buffer(packet.rawData).toString('ascii'); + if (this.xbee.use_heartbeat && data === this.xbee.heartbeat_packet) + this.refreshTimeout(); + else if (this.parser !== undefined) + this.parser.parse(data); + else + this.emit('data', data, packet); +} + +Node.prototype.AT = function(cmd, val, cb) { + var cbid; + // val parameter is optional + if (typeof val === "function") { + // use val as the callback in this case + cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); + } else { + cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); + } + + /* + this.xbee.serial.on(cbid, function(res) { + console.log("Remote AT Response (2): ", util.inspect(err), util.inspect(res)); + }); + */ +} + +Node.prototype._onRemoteCommandResponse = function(res) { + console.warn("Remote Command Response not Implemented.", util.inspect(res)); +} + + +Node.prototype.isCoordinator = function() { + return this.deviceType === C.DEVICE_TYPES.COORDINATOR; +} + +Node.prototype.isRouter = function() { + return this.deviceType === C.DEVICE_TYPES.ROUTER; +} + +Node.prototype.isEndDevice = function() { + return this.deviceType === C.DEVICE_TYPES.END_DEVICE +} + + +/* +Node.prototype._onATResponse = function(res) { + console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); +} +*/ + +Node.prototype._onDataSampleRx = function(packet) { + this.emit('io', packet.ioSample, packet); +} + +exports.Node = Node; diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 9555d11..b82024a 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -1,548 +1,549 @@ -var Buffer = require('buffer').Buffer; -var util = require('util'); - -var C = exports.Constants = require('./constants.js'); -exports = module.exports; - -var tools = exports.tools = {}; - - -tools.dec2Hex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - - while (hex.length < padding) { - hex = "0" + hex; - } - - return hex; -} - -tools.bArr2HexStr = function(a) { - var s = ''; - var leadingZero = true; - for(i in a) { - if (a[i] != 0) leadingZero = false; - if (!leadingZero) s += tools.dec2Hex(a[i]); - } - return s; -} - -tools.bArr2Str = function(a) { - var s = ''; - for(i in a) { - s += String.fromCharCode(a[i]); - } - return s; -} - -tools.bArr2Dec = function(a) { - // given a byte array like [3,21], convert to a decimal value. - // e.g. [3,21] --> 3 * 256 + 21 = 789 - var r = 0; - for(var i = 0; i < a.length; i++) { - var power = a.length - i - 1; - r += a[i] * Math.pow(256,power); - } - return r -} - -tools.dec2bArr = function(a, m) { - var r = []; - while (a > 0 || r.length < m) { - r.unshift(a & 0xff); - a = a >> 8; - } - return r; -} - -// module-level variable for storing a frameId. -// Gets incremented by 1 each time it's used, so that you can -// tell which responses relate to which XBee commands -var frameId = 0x30; - -// use API mode 2 (escape special chars) by default -exports.api_mode = 2; - -function incrementFrameId() { - frameId++; - frameId %= 255; - if (frameId == 0) frameId = 1; // 0x00 means: no response expected - return frameId; -} - -// constructor for an outgoing Packet. -var Packet = function() { - this.frameId = incrementFrameId(); -}; - -// call getBytes to get a JS array of byte values, ready to send down the serial port -Packet.prototype.getBytes = function() { - // build a JS array to hold the bytes - var packetdata = [C.START_BYTE]; - - // calculate the length bytes. First, get the entire payload by calling the internal function - var payload = this.getPayload(); - - // least significant length byte is easy - var len_lsb = payload.length % 256; - - // if payload length is greater than 255, have to calculate the more significant byte... - if (payload.length > 255) { - var len_msb = payload.length >>> 8; - } else { - //...otherwise the MSB is zero - var len_msb = 0; - } - - // add the length bytes to our growing packet array - packetdata.push(len_msb); - packetdata.push(len_lsb); - - // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array - var running_total = 0; - - for(var j = 0; j < payload.length; j++) { - packetdata.push(payload[j]); - running_total += payload[j]; - } - - checksum = 255 - (running_total % 256); - - // finally append the checksum byte and return the packet as a JS array - packetdata.push(checksum); - - // Escape characters that need to be escaped (if using API mode 2 (the default)). - // could be shorter: - var res = [packetdata[0]]; - for (var p = 1; p 0 && b == C.ESCAPE) { - escape_next = true; - continue; - } - - if (escape_next) { - b = 0x20 ^ b; - escape_next = false; - } - - packpos += 1; - - // Detected start of packet. - if (b == C.START_BYTE) { - packpos = 0; - packlen = 0; - running_total = 0; - checksum = -1; - packet = []; - escape_next = false; - } - - if (packpos == 1) packlen += b << 8; // most significant bit of the length - if (packpos == 2) packlen += b; // least significant bit of the length - if ((packlen > 0) && (packpos > 2)) { - if (packet.length < packlen) { - packet.push(b); - running_total += b; - } else { - checksum = b; - } - } - - - // Packet is complete. Parse & Emit - if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { - // There will still be a checksum byte. Currently this is ignored - if (!checksum === 255 - (running_total % 256)) { - console.log("CHECKSUM_MISMATCH"); - } else { - var parser = new PacketParser(packet) - var json = parser.parse(); - //console.log("FRAME: %s", C.FRAME_TYPE[json.ft]); - if (json.not_implemented) { - console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); - } else { - var evt = json.ft; - if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, - C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, - C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { - evt += C.EVT_SEP+json.frameId; - } - //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); - emitter.emit(evt, json); - } - } - } - } - }; -} - -// Packet Parser Class. Used to parse packets if they are known -var PacketParser = function(p) { - this.json = { - ft: p.splice(0,1)[0], - } - - // Used as pointer to the object data is parsed into - this.write = this.json; - this.payload = p; -} - -PacketParser.prototype.parse = function() { - if (false) { // TODO: Debug option - this.json.desc = C.FRAME_TYPES[this.json.ft]; - } - - this.frames[this.json.ft].parse(this); - - return this.json; -} - -PacketParser.prototype.readAddr = function(name, length) { - var dec = this.payload.splice(0, length); - this.write[name] = { dec: dec, hex: tools.bArr2HexStr(dec) } - return this; -} - -PacketParser.prototype.readByte = function(name, length) { - if (typeof length === 'number') - this.write[name] = this.payload.splice(0,length); - else this.write[name] = this.payload.splice(0,1)[0]; - return this; -} - -PacketParser.prototype.readAddr64 = function(name) { - return this.readAddr(name, 8); -} - -PacketParser.prototype.readAddr16 = function(name) { - return this.readAddr(name, 2); -} - -PacketParser.prototype.readString = function(name, length) { - this.write[name] = ""; - if (typeof length === 'number') { - for (var i = 0; i < length; i++) - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } else { - while(this.payload[0] != 0x00) { - this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); - } - this.payload.splice(0,1); // Read 0x00 away - } - return this; -} - -PacketParser.prototype.collectPayload = function(name) { - this.write[name] = this.payload.splice(0); - return this; -} - -var frames = PacketParser.prototype.frames = {}; - -frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { - parse: function(parser) { - parser - .readAddr64('sender64') - .readAddr16('sender16') - .readByte('recieveOptions'); - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readByte('transmitRetryCount') - .readByte('deliveryStatus') - .readByte('discoveryStatus') - } -}; - -frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readString('command', 2) - .readByte('commandStatus') - if (parser.json.command == 'ND') { - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } - } -}; - -frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { - parse: function(parser) { - parser - .readByte('frameId') - .readAddr16('remote16') - .readAddr64('remote64') - .readString('command', 2) - .readByte('commandStatus') - .collectPayload('commandData'); - } -}; - -frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawData'); - } -}; - -frames[C.FRAME_TYPE.MODEM_STATUS] = { - parse: function(parser) { - parser - .readByte('status'); - } -} - -frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { - parse: function(parser) { - parser - .readAddr64('remote64') - .readAddr16('remote16') - .readByte('receiveOptions') - .collectPayload('rawIOSample'); - parser.json.ioSample = tools.parseIOSample(parser.json.rawIOSample); - } -}; - -tools.parseIOSample = function(sample) { - var res = { - digitalSamples: {}, - analogSamples: {} - } - var numSamples = sample.splice(0,1)[0]; - var digitalChannelMask = sample.splice(0,2); - var analogChannelMask = sample.splice(0,1)[0]; - var mskD = (digitalChannelMask[0] << 8) | digitalChannelMask[1]; - var mskA = analogChannelMask; - - if (mskD > 0) { - var digitalSamples = sample.splice(0,2); - var valD = (digitalSamples[0] << 8) | digitalSamples[1]; - for (var i in C.DIGITAL_CHANNELS) { - if ((mskD & (1 << i)) >> i) { - res.digitalSamples[C.DIGITAL_CHANNELS[i]] = (valD & (1 << i)) >> i; - } - } - } - - if (mskA > 0) { - var analogSamples = sample.splice(0); - var sampleNr = 0; - for (var i in C.ANALOG_CHANNELS) { - if ((mskA & (1 << i)) >> i) { - var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; - // Convert to mV: - res.analogSamples[C.ANALOG_CHANNELS[i]] = (valA * 1200) / 1023; - sampleNr++; - } - } - } - return res; -} - -// Unsupported Frame Types -for (key in C.FRAME_TYPE) { - var val = C.FRAME_TYPE[key]; - if (typeof val === 'number') { - if (!frames[val]) { - frames[val] = { - parse: function(parser) { - parser.json.not_implemented = true; - } - } - } else { - // - } - } -} +var Buffer = require('buffer').Buffer; +var util = require('util'); + +var C = exports.Constants = require('./constants.js'); +exports = module.exports; + +var tools = exports.tools = {}; + + +tools.dec2Hex = function(d, padding) { + var hex = Number(d).toString(16); + padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; + + while (hex.length < padding) { + hex = "0" + hex; + } + + return hex; +} + +tools.bArr2HexStr = function(a) { + var s = ''; + var leadingZero = true; + for(i in a) { + if (a[i] != 0) leadingZero = false; + if (!leadingZero) s += tools.dec2Hex(a[i]); + } + return s; +} + +tools.bArr2Str = function(a) { + var s = ''; + for(i in a) { + s += String.fromCharCode(a[i]); + } + return s; +} + +tools.bArr2Dec = function(a) { + // given a byte array like [3,21], convert to a decimal value. + // e.g. [3,21] --> 3 * 256 + 21 = 789 + var r = 0; + for (var i = 0; i < a.length; i++) { + var power = a.length - i - 1; + r += a[i] * Math.pow(256,power); + } + return r +} + +tools.dec2bArr = function(a, m) { + var r = []; + while (a > 0 || r.length < m) { + r.unshift(a & 0xff); + a = a >> 8; + } + return r; +} + +// module-level variable for storing a frameId. +// Gets incremented by 1 each time it's used, so that you can +// tell which responses relate to which XBee commands +var frameId = 0x30; + +// use API mode 2 (escape special chars) by default +exports.api_mode = 2; + +function incrementFrameId() { + frameId++; + frameId %= 255; + if (frameId == 0) frameId = 1; // 0x00 means: no response expected + return frameId; +} + +// constructor for an outgoing Packet. +var Packet = function() { + this.frameId = incrementFrameId(); +}; + +// call getBytes to get a JS array of byte values, ready to send down the serial port +Packet.prototype.getBytes = function() { + // build a JS array to hold the bytes + var packetdata = [C.START_BYTE]; + + // calculate the length bytes. First, get the entire payload by calling the internal function + var payload = this.getPayload(); + + // least significant length byte is easy + var len_lsb = payload.length % 256; + + // if payload length is greater than 255, have to calculate the more significant byte... + if (payload.length > 255) { + var len_msb = payload.length >>> 8; + } else { + //...otherwise the MSB is zero + var len_msb = 0; + } + + // add the length bytes to our growing packet array + packetdata.push(len_msb); + packetdata.push(len_lsb); + + // now calculate checksum, meanwhile pushing each byte from the payload onto the packet array + var running_total = 0; + + for(var j = 0; j < payload.length; j++) { + packetdata.push(payload[j]); + running_total += payload[j]; + } + + checksum = 255 - (running_total % 256); + + // finally append the checksum byte and return the packet as a JS array + packetdata.push(checksum); + + // Escape characters that need to be escaped (if using API mode 2 (the default)). + // could be shorter: + var res = [packetdata[0]]; + for (var p = 1; p 0 && b == C.ESCAPE) { + escape_next = true; + continue; + } + + if (escape_next) { + b = 0x20 ^ b; + escape_next = false; + } + + packpos += 1; + + // Detected start of packet. + if (b == C.START_BYTE) { + packpos = 0; + packlen = 0; + running_total = 0; + checksum = -1; + packet = []; + escape_next = false; + } + + if (packpos == 1) packlen += b << 8; // most significant bit of the length + if (packpos == 2) packlen += b; // least significant bit of the length + if ((packlen > 0) && (packpos > 2)) { + if (packet.length < packlen) { + packet.push(b); + running_total += b; + } else { + checksum = b; + } + } + + + // Packet is complete. Parse & Emit + if ((packlen > 0) && (packet.length == packlen) && (packpos == packlen + 3)) { + // There will still be a checksum byte. Currently this is ignored + if (!checksum === 255 - (running_total % 256)) { + console.log("CHECKSUM_MISMATCH"); + } else { + var parser = new PacketParser(packet) + var json = parser.parse(); + //console.log("FRAME: %s", C.FRAME_TYPE[json.ft]); + if (json.not_implemented) { + console.log("FRAME TYPE NOT IMPLEMENTED: %s", C.FRAME_TYPE[json.ft]); + } else { + var evt = json.ft; + if ([C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS, + C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, + C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { + evt += C.EVT_SEP+json.frameId; + } + //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); + emitter.emit(evt, json); + } + } + } + } + }; +} + +// Packet Parser Class. Used to parse packets if they are known +var PacketParser = function(p) { + this.json = { + ft: p.splice(0,1)[0], + } + + // Used as pointer to the object data is parsed into + this.write = this.json; + this.payload = p; +} + +PacketParser.prototype.parse = function() { + if (false) { // TODO: Debug option + this.json.desc = C.FRAME_TYPES[this.json.ft]; + } + + this.frames[this.json.ft].parse(this); + + return this.json; +} + +PacketParser.prototype.readAddr = function(name, length) { + var dec = this.payload.splice(0, length); + this.write[name] = { dec: dec, hex: tools.bArr2HexStr(dec) } + return this; +} + +PacketParser.prototype.readByte = function(name, length) { + if (typeof length === 'number') + this.write[name] = this.payload.splice(0,length); + else this.write[name] = this.payload.splice(0,1)[0]; + return this; +} + +PacketParser.prototype.readAddr64 = function(name) { + return this.readAddr(name, 8); +} + +PacketParser.prototype.readAddr16 = function(name) { + return this.readAddr(name, 2); +} + +PacketParser.prototype.readString = function(name, length) { + this.write[name] = ""; + if (typeof length === 'number') { + for (var i = 0; i < length; i++) + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } else { + while(this.payload[0] != 0x00) { + this.write[name] += String.fromCharCode(this.payload.splice(0,1)[0]); + } + this.payload.splice(0,1); // Read 0x00 away + } + return this; +} + +PacketParser.prototype.collectPayload = function(name) { + this.write[name] = this.payload.splice(0); + return this; +} + +var frames = PacketParser.prototype.frames = {}; + +frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { + parse: function(parser) { + parser + .readAddr64('sender64') + .readAddr16('sender16') + .readByte('recieveOptions'); + parser.json.node = {}; + parser.write = parser.json.node; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readByte('transmitRetryCount') + .readByte('deliveryStatus') + .readByte('discoveryStatus') + } +}; + +frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readString('command', 2) + .readByte('commandStatus') + // TODO: Should we parse here, or seperately? + if (parser.json.command == 'ND') { + parser.json.commandData = {}; + parser.write = parser.json.commandData; + parser + .readAddr16('remote16') + .readAddr64('remote64') + .readString('id') + .readAddr16('remoteParent16') + .readByte('deviceType') + .readByte('sourceEvent') + .readByte('status'); + } else { + parser.collectPayload('commandData') + } + } +}; + +frames[C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE] = { + parse: function(parser) { + parser + .readByte('frameId') + .readAddr16('remote16') + .readAddr64('remote64') + .readString('command', 2) + .readByte('commandStatus') + .collectPayload('commandData'); + } +}; + +frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('rawData'); + } +}; + +frames[C.FRAME_TYPE.MODEM_STATUS] = { + parse: function(parser) { + parser + .readByte('status'); + } +} + +frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('rawIOSample'); + parser.json.ioSample = tools.parseIOSample(parser.json.rawIOSample); + } +}; + +tools.parseIOSample = function(sample) { + var res = { + digitalSamples: {}, + analogSamples: {} + } + var numSamples = sample.splice(0,1)[0]; + var digitalChannelMask = sample.splice(0,2); + var analogChannelMask = sample.splice(0,1)[0]; + var mskD = (digitalChannelMask[0] << 8) | digitalChannelMask[1]; + var mskA = analogChannelMask; + + if (mskD > 0) { + var digitalSamples = sample.splice(0,2); + var valD = (digitalSamples[0] << 8) | digitalSamples[1]; + for (var i in C.DIGITAL_CHANNELS) { + if ((mskD & (1 << i)) >> i) { + res.digitalSamples[C.DIGITAL_CHANNELS[i]] = (valD & (1 << i)) >> i; + } + } + } + + if (mskA > 0) { + var analogSamples = sample.splice(0); + var sampleNr = 0; + for (var i in C.ANALOG_CHANNELS) { + if ((mskA & (1 << i)) >> i) { + var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; + // Convert to mV: + res.analogSamples[C.ANALOG_CHANNELS[i]] = (valA * 1200) / 1023; + sampleNr++; + } + } + } + return res; +} + +// Unsupported Frame Types +for (key in C.FRAME_TYPE) { + var val = C.FRAME_TYPE[key]; + if (typeof val === 'number') { + if (!frames[val]) { + frames[val] = { + parse: function(parser) { + parser.json.not_implemented = true; + } + } + } else { + // + } + } +} From ac91f68a94ab000510b4a9dd9cd0b3f9d39d5c82 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 00:38:13 +0100 Subject: [PATCH 108/140] new npm version --- package.json | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index e27c9a2..60f45f7 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ -{ - "name": "svd-xbee", - "version": "0.2.1", - "description": "A more high level fork of Richard Morrison's node-xbee", - "author": { - "name": "Jan Kolkmeier", - "email": "jankolkmeier@gmail.com" - }, + { + "name": "svd-xbee", + "version": "0.3.0", + "description": "A more high level fork of Richard Morrison's node-xbee", + "author": { + "name": "Jan Kolkmeier", + "email": "jankolkmeier@gmail.com" + }, "maintainers": "Jan Kolkmeier ", "contributors": [ { @@ -29,20 +29,14 @@ "automation", "control" ], - "homepage": "https://github.com/jouz/node-xbee-svd", + "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.0.x", + "serialport": "1.0.5", "async": "0.1.x" }, "repository": { "type": "git", - "url": "git://github.com/jouz/node-xbee-svd.git" - }, - "readme": "A more high level fork of Richard Morrison's node-xbee.\n\nExample\n=======\nIf you pass no parser function to the XBee() constructor, nodes will emit each data packet that they receive as a 'data' event. See simple-parser.js for a basic parser that splits on \\r, and is easily adapted if you wish to use \\n or some other delimiter. The simple parser will merge frames and emit them split by your delimiter (so if you wonder why no data is emitted, make sure you use the right delimiter!).\n\nSet things up like this, substituting in paramaters that match your Xbee network:\n\n```javascript\nvar util = require('util');\nvar XBee = require('svd-xbee').XBee;\n\n// Replace with your xbee's UART location and correct baud rate (if you omit baudrate, the code assumes your xbee talks at 57600).\nvar xbee = new XBee({port: '/dev/tty01', baudrate:9600});\n\nxbee.on(\"configured\", function(config) {\n console.log(\"XBee Config: %s\", util.inspect(config));\n});\n\nxbee.on(\"node\", function(node) {\n console.log(\"Node %s connected\", node.id);\n\n node.on(\"data\", function(data) {\n console.log(\"%s: %s\", node.id, util.inspect(data));\n });\n\n});\n```\nThen, you can run:\n\n```javascript\nxbee.init();\n```\n\nand you should start to see things logged back to your console.\n\nBackground\n==========\n\nNote that this readme is still mostly copied from the original module!\n\n[Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks.\n\nThey can be connected to a computer over RS232 and communicated on using a standard serial port.\n\nEven easier, with something like the [XBee USB Explorer](http://www.sparkfun.com/products/8687) by SparkFun, you can connect to them easily over USB.\n\nThis work is inspired by:\n\n* voodootikigod's [serialport module](https://github.com/voodootikigod/node-serialport) (in fact you're going to need this to use this package)\n* \"[Building Wireless Sensor Networks](http://shop.oreilly.com/product/9780596807740.do)\" by Rob Faludi\n\nSetup\n=====\n\nI have my xbee coordinator radio connected to the computer running Node. Crucially, the coordinator is in xbee's API mode 2 - this is required to allow you to send remote instructions, and so on, and uses escaping to improve reliability.\n\nMy remote xbee network modules send periodic measurements and I can push them to web browsers, save them in a database, etc.\n\nI can also use this library to send remote commands and query remote xbee modules. For instance, setting a digital output on a remote module could turn a light on, or a motor, or a laser beam - up to you!\n\nInstallation\n============\n\n npm install svd-xbee\n\nLicence\n=======\n\nThis work is based on the works of Richard Morrison\n\"Creative
This work by Richard Morrison is licensed under a Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales License.
Based on a work at github.com.\n", - "readmeFilename": "README.markdown", - "_id": "svd-xbee@0.2.1", - "dist": { - "shasum": "0f4c78bbf31109a0c04f874badcfc0c51a0d7361" + "url": "git://github.com/jouz/svd-xbee.git" }, - "_from": "svd-xbee" + "readmeFilename": "README.md" } From f3c151a499d9dd7745b69fc597a2ede9bfb19ab1 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 01:49:49 +0100 Subject: [PATCH 109/140] Added Web Example --- examples/web/index.html | 41 +++++++++++++++++++ examples/web/web.js | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/web/index.html create mode 100644 examples/web/web.js diff --git a/examples/web/index.html b/examples/web/index.html new file mode 100644 index 0000000..536e333 --- /dev/null +++ b/examples/web/index.html @@ -0,0 +1,41 @@ + + + + + SVD-XBEE WEB TEST + + + + + + +
+
+
+
+ + diff --git a/examples/web/web.js b/examples/web/web.js new file mode 100644 index 0000000..cff0ab4 --- /dev/null +++ b/examples/web/web.js @@ -0,0 +1,87 @@ +var util = require('util'); +var XBee = require('../../index.js').XBee; +var app = require('http').createServer(handler) + , io = require('socket.io').listen(app) + , fs = require('fs') + +app.listen(80); + +function handler(req, res) { + fs.readFile(__dirname + '/index.html', + function (err, data) { + if (err) { + res.writeHead(500); + return res.end('Error loading index.html'); + } + + res.writeHead(200); + res.end(data); + }); +} + +io.sockets.on('connection', function (socket) { +}); + +var xbee = new XBee({ + port: 'COM3', // replace with yours + baudrate: 9600 // 9600 is default +}); + +// Add Node by hand... +var myNode = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0x2f,0xe4]); + +xbee.on("initialized", function(params) { + console.log("XBee Parameters: %s", util.inspect(params)); + xbee.discover(); + console.log("Node discovery starded..."); +}); + +xbee.on("discoveryEnd", function() { + console.log("...node discovery over"); +}); + +xbee.init(); + +myNode.on("io", function(sample) { + console.log("I/O:", io); + io.sockets.emit("io", sample); +}); + +myNode.on("data", function(data) { + console.log("Data:", data); + io.sockets.emit("data", data); +}); + +myNode.on("discovered", function(node) { + console.log("myNode Discovered."); + + + // Enable Auto Reporting every 2 seconds + myNode.AT("IR", xbee.tools.dec2bArr(2000), function(err, res) { + if (err) return console.log("AT Error:", util.inspect(err)); + console.log("Auto Reporting Enabled."); + }); + + // Configure pin D4 to sample digital data. + myNode.AT("D4", [ 0x03 ], function(err, res) { + if (err) return console.log("AT Error:", err); + console.log("D4 configured."); + }); + + // Configures pin D2 to sample analog data. + myNode.AT("D2", [ 0x02 ], function(err, res) { + if (err) return console.log("AT Error:", err); + console.log("D2 configured."); + }); +}); + +io.sockets.on('connection', function(socket) { + socket.on("msg", function(data) { + myNode.send(data); + }); +}); + +// Triggered whenever a node is discovered that is not already +xbee.on("newNodeDiscovered", function(node) { + console.log("Node %s discovered.", node.remote64.hex); +}); From b1ee7ed6cfe00eafcd473be415426a30d8965de2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 02:28:46 +0100 Subject: [PATCH 110/140] fixed dependencies --- package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 60f45f7..ce183cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.0", + "version": "0.3.1", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", @@ -31,9 +31,12 @@ ], "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.0.5", + "serialport": "1.0.x", "async": "0.1.x" }, + "engines": { + "node": "0.6.x | 0.8.x" + }, "repository": { "type": "git", "url": "git://github.com/jouz/svd-xbee.git" From 187cdfe45aa5a2eac7d21312052ef44446c71090 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 21:53:28 +0100 Subject: [PATCH 111/140] Clean up source, add some more functions --- index.js | 500 +++++++++++++++++++++++++++++++++-------------- lib/constants.js | 215 ++++++++++++++++++-- lib/xbee-api.js | 70 +------ 3 files changed, 558 insertions(+), 227 deletions(-) diff --git a/index.js b/index.js index aea2fd6..75854e1 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,8 @@ var serialport = require("serialport"); var async = require('async'); var os = require('os'); -var C = api.Constants; +var C = exports.C = api.Constants; +var Tools = exports.Tools = api.tools; function XBee(options) { EventEmitter.call(this); @@ -17,7 +18,7 @@ function XBee(options) { this.options = options; } - this.tools = api.tools; + this.tools = Tools; // Remove this, use XBee.Tools instead this.data_parser = options.data_parser || undefined; @@ -36,6 +37,96 @@ function XBee(options) { util.inherits(XBee, EventEmitter); +XBee.prototype._makeTask = function(packet) { + var self = this; + return function Writer(cb) { + //console.log("<<< "+util.inspect(packet.data)); + //console.log("<<< "+packet.data); + + var timeout = setTimeout(function() { + cb({ msg: "Never got Transmit status from XBee" }); + }, self.transmit_status_timeout ); + self.serial.write(packet.data, function(err, results) { + if (err) { + cb(err); + } else { + //console.log(util.inspect(packet.data)); + if (results != packet.data.length) return cb(new Error("Not all bytes written")); + self.serial.once(packet.cbid, function(packet) { + //console.log("Got Respones: "+packet.cbid); + clearTimeout(timeout); + var error = null; + if (packet.commandStatus !== undefined) { + if (packet.commandStatus != C.COMMAND_STATUS.OK) { + error = C.COMMAND_STATUS[packet.commandStatus]; + } + packet = packet.commandData; + } else if (packet.deliveryStatus !== undefined) { + if (packet.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { + error = C.DELIVERY_STATUS[packet.deliveryStatus]; + } + } + cb(error, packet); + }); + } + }); + }; +} + +XBee.prototype._AT = function(cmd, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; +} + +// TODO: Merge this up with _AT to save some space +XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.RemoteATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets:packet, cb:_cb }); + return cbid; +} + +XBee.prototype._handleNodeIdentification = function(node) { + if (!this.nodes[node.remote64.hex]) { + this.nodes[node.remote64.hex] = new Node(this, node, this.data_parser); + this.emit("newNodeDiscovered", this.nodes[node.remote64.hex]); + } else { + // update 16-bit address, as it may change during reconnects. + this.nodes[node.remote64.hex].remote16 = node.remote16; + this.nodes[node.remote64.hex].id = node.id; + this.nodes[node.remote64.hex].emit("discovered", this.nodes[node.remote64.hex]); + } + this.nodes[node.remote64.hex].connected = true; +} + XBee.prototype.init = function(cb) { var self = this; // Serial connection to the XBee @@ -65,21 +156,12 @@ XBee.prototype.init = function(cb) { /* Frame-specific Handlers */ - // Whenever a node is identified (on ATND command). - self._onNodeDiscovery = function(node) { - if (node.node) node = node.node; - if (!self.nodes[node.remote64.hex]) { - self.nodes[node.remote64.hex] = new Node(self, node, self.data_parser); - self.emit("newNodeDiscovered", self.nodes[node.remote64.hex]); - } else { - // update 16-bit address, as it may change during reconnects. - self.nodes[node.remote64.hex].remote16 = node.remote16; - self.nodes[node.remote64.hex].id = node.id; - self.nodes[node.remote64.hex].emit("discovered", self.nodes[node.remote64.hex]); - } - self.nodes[node.remote64.hex].connected = true; + // Whenever a node reports with an identification frame. + self._onNodeIdentification = function(packet) { + var node = parseNodeIdentificationPayload(packet.nodeIdentificationPayload); + self._handleNodeIdentification(node); } - + // Modem Status self._onModemStatus = function(packet) { if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { @@ -97,35 +179,27 @@ XBee.prototype.init = function(cb) { } } - // Loose AT Command Responses from remote AT Commands. - // Should not arrive here if callback was passed to remote AT cmd. - self._onRemoteCommandResponse = function(res) { - if (self.nodes[res.remote64.hex]) { - self.nodes[res.remote64.hex]._onRemoteCommandResponse(res); - } - } - // Messages self._onReceivePacket = function(data) { if (!self.nodes[data.remote64.hex]) { - var _data = { node:data }; - self._onNodeDiscovery(_data); + var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); + self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? } self.nodes[data.remote64.hex]._onReceivePacket(data); } // Data samples (from XBee's I/O) self._onDataSampleRx = function(data) { + // Todo: data from local xbee? if (!self.nodes[data.remote64.hex]) { - var _data = { node:data }; - self._onNodeDiscovery(_data); + var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); + self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? } self.nodes[data.remote64.hex]._onDataSampleRx(data); } self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); - self.serial.on(C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE, self._onRemoteCommandResponse); - self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeDiscovery); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); @@ -135,6 +209,8 @@ XBee.prototype.init = function(cb) { callback(); }); }, 1); + + return self; } XBee.prototype.readParameters = function(_done_cb) { @@ -157,11 +233,11 @@ XBee.prototype.readParameters = function(_done_cb) { } var parameters = { - panid: QF('ID', undefined, api.tools.bArr2HexStr), - id: QF('NI', undefined, api.tools.bArr2Str), - sourceHigh: QF('SH', undefined, api.tools.bArr2HexStr), - sourceLow: QF('SL', undefined, api.tools.bArr2HexStr), - nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * api.tools.bArr2Dec(a); }) + panid: QF('ID', undefined, Tools.bArr2HexStr), + id: QF('NI', undefined, Tools.bArr2Str), + sourceHigh: QF('SH', undefined, Tools.bArr2HexStr), + sourceLow: QF('SL', undefined, Tools.bArr2HexStr), + nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * Tools.bArr2Dec(a); }) }; var done = function(err, results) { @@ -192,12 +268,15 @@ XBee.prototype.readParameters = function(_done_cb) { } // Add a node by hand. -XBee.prototype.addNode = function(remote64, parser) { +XBee.prototype.addNode = function(remote64, remote16, parser) { var self = this; - var remote16 = [0xff,0xfe]; // Unknown + //var remote16 = [0xff,0xfe]; // Unknown + if (!remote16 instanceof Array) { + parser = remote16; + } var node_data = { - remote16: { dec: remote16, hex: api.tools.bArr2HexStr(remote16) }, - remote64: { dec: remote64, hex: api.tools.bArr2HexStr(remote64) } + remote16: { dec: remote16, hex: Tools.bArr2HexStr(remote16) }, + remote64: { dec: remote64, hex: Tools.bArr2HexStr(remote64) } }; var node = self.nodes[node_data.remote64.hex]; @@ -218,8 +297,9 @@ XBee.prototype.addNode = function(remote64, parser) { XBee.prototype.discover = function(cb) { var self = this; var cbid = self._AT('ND'); - self.serial.on(cbid, function(node) { - self._onNodeDiscovery(node.commandData); + self.serial.on(cbid, function(packet) { + var node = parseNodeIdentificationPayload(packet.commandData); + self._handleNodeIdentification(node); }) setTimeout(function() { if (typeof cb === 'function') cb(); @@ -231,46 +311,10 @@ XBee.prototype.discover = function(cb) { XBee.prototype.broadcast = function(data, cb) { var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; var remote16 = [0xff,0xfe]; - this._send(data, remote64, remote16, cb); -} - -XBee.prototype._makeTask = function(packet) { - var self = this; - return function Writer(cb) { - //console.log("<<< "+util.inspect(packet.data)); - //console.log("<<< "+packet.data); - - var timeout = setTimeout(function() { - cb({ msg: "Never got Transmit status from XBee" }); - }, self.transmit_status_timeout ); - self.serial.write(packet.data, function(err, results) { - if (err) { - cb(err); - } else { - //console.log(util.inspect(packet.data)); - if (results != packet.data.length) return cb(new Error("Not all bytes written")); - self.serial.once(packet.cbid, function(packet) { - //console.log("Got Respones: "+packet.cbid); - clearTimeout(timeout); - var error = null; - if (packet.commandStatus !== undefined) { - if (packet.commandStatus != C.COMMAND_STATUS.OK) { - error = C.COMMAND_STATUS[packet.commandStatus]; - } - packet = packet.commandData; - } else if (packet.deliveryStatus !== undefined) { - if (packet.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { - error = C.DELIVERY_STATUS[packet.deliveryStatus]; - } - } - cb(error, packet); - }); - } - }); - }; + this.send(data, remote64, remote16, cb); } -XBee.prototype._send = function(data, remote64, remote16, _cb) { +XBee.prototype.send = function(data, remote64, remote16, _cb) { var packets = []; while (data.length > 0) { var frame = new api.TransmitRFData(); @@ -291,49 +335,144 @@ XBee.prototype.AT = function(cmd, val, _cb) { this._AT(cmd, val, _cb); } -XBee.prototype._AT = function(cmd, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; +XBee.prototype.setChangeDetection = function(pins, cb, remote) { + // TODO: this is lazy... + var mask = "000000000000".split(''); + for (pin in pins) { + var _pin = pins[pin]; + if (typeof _pin == "number") { + mask[C.CHANGE_DETECTION.PIN[_pin]] = '1'; + } else { + mask[C.CHANGE_DETECTION[_pin]] = '1'; + } } - var frame = new api.ATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; + var val = parseInt(mask.reverse().join(''), 2); + val = Tools.dec2bArr(val, 2); + + if (remote) + this._remoteAT("IC", remote.remote64, remote.remote16, val, cb); + else + this._AT("IC", val, cb); } +XBee.prototype.setSampleRate = function(interval, cb, remote) { + var _interval = Tools.dec2bArr(interval, 2); + if (remote) + this._remoteAT("IR", remote.remote64, remote.remote16, _interval, cb); + else + this._AT("IR", _interval, cb); +} -XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; +XBee.prototype.getSample = function(cb, remote) { + var _cb = function(err, res) { + if (err) cb(err); + else cb(err, parseIOSample(res)); } - var frame = new api.RemoteATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; + if (remote) + this._remoteAT("IS", remote.remote64, remote.remote16, _cb); + else + this._AT("IS", _cb); +} + + +// Change the mode of a pin. +// If "pin" is a string, +// we assume the descriptive name (AO3, etc.) +// If "pin" is a number +// we assume the phsical pin is meant (~1-20) +XBee.prototype.getAnalogPin = function(pin, cb, remote) { + var _pin; + if (typeof pin == "number") + _pin = C.ANALOG_CHANNEL.MASK[C.ANALOG_CHANNEL.PIN[pin]][0] + else _pin = pin; + + this.getSample(function(err, res) { + if (err) cb(err); + else if (res.analogSamples[_pin] == undefined) + cb(new Error("XBee not configured to take analog samples on pin "+_pin)); + else + cb(err, res.analogSamples[_pin]); + }, remote); +} + +// Change the mode of a pin. +// If "pin" is a string, +// we assume the descriptive name (DIO3, etc.) +// If "pin" is a number +// we assume the phsical pin is meant (~1-20) +XBee.prototype.getDigitalPin = function(pin, cb, remote) { + var _pin; + if (typeof pin == "number") + _pin = C.DIGITAL_CHANNEL.MASK[C.DIGITAL_CHANNEL.PIN[pin]][0] + else _pin = pin; + + this.getSample(function(err, res) { + if (err) cb(err); + else if (res.digitalSamples[_pin] == undefined) + cb(new Error("XBee not configured to take digital samples on pin "+_pin)); + else + cb(err, res.digitalSamples[_pin]); + }, remote); +} + +XBee.prototype.getPinMode = function(pin, mode, cb, remote) { + var _pin; + if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; + else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; + else _pin = pin; + if (_pin == undefined) + return cb(new Error("Unknown pin: "+pin)); + + var _cb = function(err, res) { + if (err) cb(err); + else cb(err, res[0]); + }; + + if (remote) + this._remoteAT(_pin, remote.remote64, remote.remote16, _cb); + else + this._AT(_pin, _cb); +} + +// Change the mode of a pin. +// If "pin" is not the two-letter AT command associated with the pin, +// we assume the descriptive name (DIO3, etc.) +// If "pin" is a number +// we assume the phsical pin is meant (~1-20) +// If "mode" is a number +// we assume it is the byte parameter +// If "mode" is a string +// we assume descriptive mode (DISABLED, DIGITAL_INPUT, ...) see constants.js +XBee.prototype.setPinMode = function(pin, mode, cb, remote) { + var _pin, _mode; + if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; + else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; + else _pin = pin; + if (_pin == undefined || C.PIN_MODE[_pin] == undefined) + return cb(new Error("Unknown pin: "+pin)); + if (typeof mode == "string") { + _mode = C.PIN_MODE[_pin][mode]; + } else _mode = mode; + if (_mode == undefined || C.PIN_MODE[_pin][mode] == undefined) + return cb(new Error("Unknown mode "+mode+" for pin "+pin)); + _mode = parseInt(_mode); // Make sure mode is dec + + if (remote) + this._remoteAT(_pin, remote.remote64, remote.remote16, [_mode], cb); + else + this._AT(_pin, [_mode], cb); +} + +XBee.prototype.setDestinationNode = function(dest, cb, remote) { + cb("Not Implemented Yet"); } exports.XBee = XBee; + + function Node(xbee, params, data_parser) { EventEmitter.call(this); this.xbee = xbee; @@ -351,6 +490,26 @@ function Node(xbee, params, data_parser) { util.inherits(Node, EventEmitter); +Node.prototype._onReceivePacket = function(packet) { + // TODO: should be buffer all along! + var data = new Buffer(packet.rawData).toString('ascii'); + if (this.xbee.use_heartbeat) { + this.refreshTimeout(); + if (data === this.xbee.heartbeat_packet) return; + } + + if (this.parser !== undefined) this.parser.parse(data); + else this.emit('data', data, packet); +} + +Node.prototype._onDataSampleRx = function(packet) { + var sample = parseIOSample(packet.ioSample); + this.emit('io', sample); + if (this.xbee.use_heartbeat) { + this.refreshTimeout(); + } +} + Node.prototype.timeoutOccured = function() { this.connected = false; this.emit('disconnect'); @@ -366,42 +525,19 @@ Node.prototype.refreshTimeout = function() { } Node.prototype.send = function(data, cb) { - this.xbee._send(data, this.remote64, this.remote16, cb); -} - -Node.prototype._onReceivePacket = function(packet) { - // TODO: should be buffer all along! - var data = new Buffer(packet.rawData).toString('ascii'); - if (this.xbee.use_heartbeat && data === this.xbee.heartbeat_packet) - this.refreshTimeout(); - else if (this.parser !== undefined) - this.parser.parse(data); - else - this.emit('data', data, packet); + this.xbee.send(data, this.remote64, this.remote16, cb); } Node.prototype.AT = function(cmd, val, cb) { - var cbid; // val parameter is optional if (typeof val === "function") { // use val as the callback in this case - cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); } else { - cbid = this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); } - - /* - this.xbee.serial.on(cbid, function(res) { - console.log("Remote AT Response (2): ", util.inspect(err), util.inspect(res)); - }); - */ } -Node.prototype._onRemoteCommandResponse = function(res) { - console.warn("Remote Command Response not Implemented.", util.inspect(res)); -} - - Node.prototype.isCoordinator = function() { return this.deviceType === C.DEVICE_TYPES.COORDINATOR; } @@ -414,15 +550,93 @@ Node.prototype.isEndDevice = function() { return this.deviceType === C.DEVICE_TYPES.END_DEVICE } +Node.prototype.setChangeDetection = function(pins, cb) { + this.xbee.setChangeDetection(pins, cb, this); +} -/* -Node.prototype._onATResponse = function(res) { - console.log("Node %s got AT_RESPONSE: %s", util.inspect(res)); +Node.prototype.setSampleRate = function(interval, cb) { + this.xbee.setSampleRate(interval, cb, this); } -*/ -Node.prototype._onDataSampleRx = function(packet) { - this.emit('io', packet.ioSample, packet); +Node.prototype.getSample = function(cb) { + this.xbee.getSample(cb, this); +}; + +Node.prototype.setPinMode = function(pin, mode, cb) { + this.xbee.setPinMode(pin, mode, cb, this); +} +Node.prototype.getAnalogPin = function(pin, cb) { + this.xbee.getAnalogPin(pin, cb, this); +} + +Node.prototype.getDigitalPin = function(pin, cb) { + this.xbee.getDigitalPin(pin, cb, this); +} + +Node.prototype.setDestinationNode = function(dest, cb) { + this.xbee.setDestinationNode(dest, cb); } exports.Node = Node; + +var parseIOSample = exports.parseIOSample = function(sample) { + var res = { + digitalSamples: {}, + analogSamples: {} + } + + var numSamples = sample.splice(0,1)[0]; + var mskD = sample.splice(0,2); + mskD = (mskD[0] << 8) | mskD[1]; + var mskA = sample.splice(0,1)[0]; + + if (mskD > 0) { + var digitalSamples = sample.splice(0,2); + var valD = (digitalSamples[0] << 8) | digitalSamples[1]; + for (var bit in C.DIGITAL_CHANNELS.MASK) { + if ((mskD & (1 << bit)) >> bit) { + res.digitalSamples[C.DIGITAL_CHANNELS.MASK[bit][0]] = (valD & (1 << bit)) >> bit; + } + } + } + + if (mskA > 0) { + var analogSamples = sample.splice(0); + var sampleNr = 0; + for (var bit in C.ANALOG_CHANNELS.MASK) { + if ((mskA & (1 << bit)) >> bit) { + var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; + // Convert to mV + res.analogSamples[C.ANALOG_CHANNELS.MASK[bit][0]] = (valA * 1200) / 1023; + sampleNr++; + } + } + } + + return res; +} + +var parseAddress = exports.parseAddress = function(dec) { + return { + dec: dec, + hex: Tools.bArr2HexStr(dec) + } +} + +var parseNodeIdentificationPayload = exports.parseNodeIdentificationPayload = function(payload) { + var res = {} + res.id = ""; + + res.remote16 = parseAddress(payload.splice(0, 2)); + res.remote64 = parseAddress(payload.splice(0, 8)); + while(payload[0] != 0x00) res.id += String.fromCharCode(payload.splice(0,1)[0]); + payload.splice(0,1); // Read 0x00 away + res.remoteParent16 = parseAddress(payload.splice(0, 2)); + res.deviceType = payload.splice(0, 1)[0]; + res.sourceEvent = payload.splice(0, 1)[0]; + res.nodeIdentificationPayload = payload.splice(0); + // res.status = payload.splice(0, 1)[0]; + // ... + + return res; +} diff --git a/lib/constants.js b/lib/constants.js index 5d14e12..5fe45d8 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -14,8 +14,12 @@ var ms = exports.MODEM_STATUS = {}; var ro = exports.RECEIVE_OPTIONS = {}; var dt = exports.DEVICE_TYPES = {}; -var dc = exports.DIGITAL_CHANNELS = {}; -var ac = exports.ANALOG_CHANNELS = {}; +var dc = exports.DIGITAL_CHANNELS = { MASK: {}, PIN:{} }; +var ac = exports.ANALOG_CHANNELS = { MASK: {}, PIN:{} }; +var pr = exports.PULLUP_RESISTOR = { MASK: {}, PIN:{} }; +var ic = exports.CHANGE_DETECTION = { MASK: {}, PIN:{} }; +var pm = exports.PIN_MODE = {}; +var pc = exports.PIN_COMMAND = { PIN:{} }; // Device Type dt.COORDINATOR = 0x00; @@ -153,20 +157,193 @@ ro[0x20] = "Packet encrypted with APS encryption (0x20)"; ro.PACKET_SENT_FROM_END_DEVICE = 0x40; ro[0x40] = "Packet was sent from an end device (if known) (0x40)"; -dc[0] = "DIO0"; // AD0 -dc[1] = "DIO1"; // AD1 -dc[2] = "DIO2"; // AD2 -dc[3] = "DIO3"; // AD3 -dc[4] = "DIO4"; // - -dc[5] = "DIO5"; // ASSOC -dc[6] = "DIO6"; // RTS -dc[7] = "GPIO7"; // CTS -dc[10] = "DIO10"; // RSSI -dc[11] = "DIO11"; // PWM -dc[12] = "DIO12"; // CD - -ac[0] = "AD0"; // DIO0 -ac[1] = "AD1"; // DIO1 -ac[2] = "AD2"; // DIO2 -ac[3] = "AD3"; // DIO3 -ac[7] = "SUPPLY"; + + +// +// Digital Channel Mask/Pins +// +// Map mask to name +dc.MASK[0] = ["DIO0", "AD0"]; +dc.MASK[1] = ["DIO1", "AD1"]; +dc.MASK[2] = ["DIO2", "AD2"]; +dc.MASK[3] = ["DIO3", "AD3"]; +dc.MASK[4] = ["DIO4"]; +dc.MASK[5] = ["DIO5", "ASSOCIATE"]; +dc.MASK[6] = ["DIO6", "RTS"]; +dc.MASK[7] = ["DIO7", "CTS"]; +dc.MASK[10] = ["DIO10", "RSSI"]; +dc.MASK[11] = ["DIO11", "PWM"]; +dc.MASK[12] = ["DIO12", "CD"]; +// Map pin/name to mask +ac.PIN[20] = dc.DIO0 = dc.AD0 = 0; +ac.PIN[19] = dc.DIO1 = dc.AD1 = 1; +ac.PIN[18] = dc.DIO2 = dc.AD2 = 2; +ac.PIN[17] = dc.DIO3 = dc.AD3 = 3; +ac.PIN[11] = dc.DIO4 = 4; +ac.PIN[15] = dc.DIO5 = dc.ASSOCIATE = 5; +ac.PIN[16] = dc.DIO6 = dc.RTS = 6; +ac.PIN[12] = dc.DIO7 = dc.CTS = 7; +ac.PIN[6] = dc.DIO10 = dc.RSSI = 10; +ac.PIN[7] = dc.DIO11 = dc.PWM = 11; +ac.PIN[4] = dc.DIO12 = dc.CD = 12; + +// +// Analog Channel Mask/Pins +// +// Map mask to name +ac.MASK[0] = ["AD0", "DIO0" ]; +ac.MASK[1] = ["AD1", "DIO1" ]; +ac.MASK[2] = ["AD2", "DIO2" ]; +ac.MASK[3] = ["AD3", "DIO3" ]; +ac.MASK[7] = ["SUPPLY"]; +// map pin/name to mask +ac.PIN[20] = ac.AD0 = ac.DIO0 = 0; +ac.PIN[19] = ac.AD1 = ac.DIO1 = 1; +ac.PIN[18] = ac.AD2 = ac.AD3 = 3; +ac.PIN[17] = ac.SUPPLY = 7; // 17 True? + + +// +// Pullup-enable Mask/Pins +// +// Map mask to name +pr.MASK[0] = ["DIO4"]; +pr.MASK[1] = ["DIO3", "AD3"]; +pr.MASK[2] = ["DIO2", "AD2"]; +pr.MASK[3] = ["DIO1", "AD1"]; +pr.MASK[4] = ["DIO0", "AD0"]; +pr.MASK[5] = ["DIO6", "RTS"]; +pr.MASK[6] = ["DIO8", "DTR", "SLEEP_REQUEST"]; +pr.MASK[7] = ["DIN", "CONFIG"]; +pr.MASK[8] = ["DIO5", "ASSOCIATE"]; +pr.MASK[9] = ["DIO9", "ON"]; +pr.MASK[10] = ["DIO12"]; +pr.MASK[11] = ["DIO10", "RSSI", "PWM0"]; +pr.MASK[12] = ["DIO11", "PWM1"]; +pr.MASK[13] = ["DIO7", "CTS"]; +// Map pin/name to maks +pr.PIN[11] = pr.DIO4 = 0; +pr.PIN[17] = pr.AD3 = pr.DIO3 = 1; +pr.PIN[18] = pr.AD2 = pr.DIO2 = 2; +pr.PIN[19] = pr.AD1 = pr.DIO1 = 3; +pr.PIN[20] = pr.AD0 = pr.DIO0 = 4; +pr.PIN[16] = pr.RTS = pr.DIO6 = 5; +pr.PIN[9] = pr.DIO8 = pr.DTR = pr.SLEEP_REQUEST = 6; +pr.PIN[3] = pr.DIN = pr.CONFIG = 7; +pr.PIN[15] = pr.ASSOCIATE = pr.DIO5 = 8; +pr.PIN[13] = pr.ON = pr.SLEEP = pr.DIO9 = 9; +pr.PIN[4] = pr.DIO12 = 10; +pr.PIN[6] = pr.PWM0 = pr.RSSI = pr.DIO10 = 11; +pr.PIN[7] = pr.PWM1 = pr.DIO11 = 12; +pr.PIN[12] = pr.CTS = pr.DIO7 = 13; + + +// +// Change Reporting Mask/Pins +// +// Map mask to name +ic.MASK[0] = ["DIO0"]; +ic.MASK[1] = ["DIO1"]; +ic.MASK[2] = ["DIO2"]; +ic.MASK[3] = ["DIO3"]; +ic.MASK[4] = ["DIO4"]; +ic.MASK[5] = ["DIO5"]; +ic.MASK[6] = ["DIO6"]; +ic.MASK[7] = ["DIO7"]; +ic.MASK[8] = ["DIO8"]; +ic.MASK[9] = ["DIO9"]; +ic.MASK[10] = ["DIO10"]; +ic.MASK[11] = ["DIO11"]; +// Map pin/name to mask +ic.PIN[20] = ic.DIO0 = 0; +ic.PIN[19] = ic.DIO1 = 1; +ic.PIN[18] = ic.DIO2 = 2; +ic.PIN[17] = ic.DIO3 = 3; +ic.PIN[11] = ic.DIO4 = 4; +ic.PIN[15] = ic.DIO5 = 5; +ic.PIN[16] = ic.DIO6 = 6; +ic.PIN[12] = ic.DIO7 = 7; +ic.PIN[9] = ic.DIO8 = 8; +ic.PIN[13] = ic.DIO9 = 9; +ic.PIN[6] = ic.DIO10 = 10; +ic.PIN[7] = ic.DIO11 = 11; + + +// +// Pin Modes +// +pm.P2 = pm.P1 = { + UNMONITORED_INPUT: 0x00, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +} + +pm.P0 = { + DISABLED: 0x00, + RSSI_PWM: 0x01, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +}; + +pm.D4 = { + DISABLED: 0x00, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +}; + +pm.D7 = { + DISABLED: 0x00, + CTS_FLOW_CTRL: 0x01, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05, + RS485_TX_LOW: 0x06, + RS485_TX_HIGH: 0x07 +}; + +pm.D5 = { + DISABLED: 0x00, + ASSOC_LED: 0x01, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +}; + +pm.D6 = { + DISABLED: 0x00, + RTS_FLOW_CTRL: 0x01, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +}; + +pm.D0 = pm.D1 = pm.D2 = pm.D3 = { + DISABLED: 0x00, + NODE_ID_ENABLED: 0x01, // Only valid for D0! + ANALOG_INPUT: 0x02, + DIGITAL_INPUT: 0x03, + DIGITAL_OUTPUT_LOW: 0x04, + DIGITAL_OUTPUT_HIGH: 0x05 +}; + +for (var pin in pm) { + for (var key in pm[pin]) { + pm[pin][pm[pin][key]] = key; + } +} + +pc.PIN[6] = pc.PWM0 = pc.DIO10 = pc.RSSIM = "P0"; +pc.PIN[7] = pc.DIO11 = pc.PWM1 = "P1"; +pc.PIN[4] = pc.DIO12 = "P2"; +pc.PIN[12] = pc.DIO7 = pc.CTS = "D7"; +pc.PIN[16] = pc.DIO6 = "D6"; +pc.PIN[20] = pc.AD0 = pc.DIO0 = "D0"; +pc.PIN[19] = pc.AD1 = pc.DIO1 = "D1"; +pc.PIN[18] = pc.AD2 = pc.DIO2 = "D2"; +pc.PIN[17] = pc.AD3 = pc.DIO3 = "D3"; +pc.PIN[11] = pc.DIO4 = "D4"; +pc.PIN[15] = pc.DIO5 = pc.ASSOC = "D5"; + diff --git a/lib/xbee-api.js b/lib/xbee-api.js index b82024a..6495413 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -20,10 +20,8 @@ tools.dec2Hex = function(d, padding) { tools.bArr2HexStr = function(a) { var s = ''; - var leadingZero = true; for(i in a) { - if (a[i] != 0) leadingZero = false; - if (!leadingZero) s += tools.dec2Hex(a[i]); + s += tools.dec2Hex(a[i]); } return s; } @@ -408,16 +406,8 @@ frames[C.FRAME_TYPE.NODE_IDENTIFICATION] = { parser .readAddr64('sender64') .readAddr16('sender16') - .readByte('recieveOptions'); - parser.json.node = {}; - parser.write = parser.json.node; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent'); + .readByte('recieveOptions') + .collectPayload('nodeIdentificationPayload'); } }; @@ -438,21 +428,7 @@ frames[C.FRAME_TYPE.AT_COMMAND_RESPONSE] = { .readByte('frameId') .readString('command', 2) .readByte('commandStatus') - // TODO: Should we parse here, or seperately? - if (parser.json.command == 'ND') { - parser.json.commandData = {}; - parser.write = parser.json.commandData; - parser - .readAddr16('remote16') - .readAddr64('remote64') - .readString('id') - .readAddr16('remoteParent16') - .readByte('deviceType') - .readByte('sourceEvent') - .readByte('status'); - } else { - parser.collectPayload('commandData') - } + .collectPayload('commandData'); } }; @@ -491,46 +467,10 @@ frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { .readAddr64('remote64') .readAddr16('remote16') .readByte('receiveOptions') - .collectPayload('rawIOSample'); - parser.json.ioSample = tools.parseIOSample(parser.json.rawIOSample); + .collectPayload('ioSample'); } }; -tools.parseIOSample = function(sample) { - var res = { - digitalSamples: {}, - analogSamples: {} - } - var numSamples = sample.splice(0,1)[0]; - var digitalChannelMask = sample.splice(0,2); - var analogChannelMask = sample.splice(0,1)[0]; - var mskD = (digitalChannelMask[0] << 8) | digitalChannelMask[1]; - var mskA = analogChannelMask; - - if (mskD > 0) { - var digitalSamples = sample.splice(0,2); - var valD = (digitalSamples[0] << 8) | digitalSamples[1]; - for (var i in C.DIGITAL_CHANNELS) { - if ((mskD & (1 << i)) >> i) { - res.digitalSamples[C.DIGITAL_CHANNELS[i]] = (valD & (1 << i)) >> i; - } - } - } - - if (mskA > 0) { - var analogSamples = sample.splice(0); - var sampleNr = 0; - for (var i in C.ANALOG_CHANNELS) { - if ((mskA & (1 << i)) >> i) { - var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; - // Convert to mV: - res.analogSamples[C.ANALOG_CHANNELS[i]] = (valA * 1200) / 1023; - sampleNr++; - } - } - } - return res; -} // Unsupported Frame Types for (key in C.FRAME_TYPE) { From 0a41e188e19cd072dc618624d6330798a8e27c15 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 22:08:56 +0100 Subject: [PATCH 112/140] Extended examples --- examples/full/full.js | 28 ++++++++++++++++++++++++++-- examples/nutshell/nutshell.js | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/examples/full/full.js b/examples/full/full.js index 8414d5f..0d37b76 100644 --- a/examples/full/full.js +++ b/examples/full/full.js @@ -1,11 +1,11 @@ var util = require('util'); -var XBee = require('../../index.js').XBee; +var XBee = require('../../index.js'); // This parser buffers data, emits chucks // seperated by space chars (" ") var Parser = require('./parser.js'); -var xbee = new XBee({ +var xbee = new XBee.XBee({ port: 'COM3', // replace with yours baudrate: 9600 // 9600 is default }) @@ -104,4 +104,28 @@ xbee.on("newNodeDiscovered", function(node) { // Transmission successful if err is null }); }); + + node.on("io", function(sample) { + console.log("%s> %s", node.remote64.hex, util.inspect(data)); + }); + + // Here some functions you might find helpful: + node.setPinMode("DIO2", "DIGITAL_INPUT"); + node.setPinMode("DIO3", "DIGITAL_INPUT"); + node.setPinMode("DIO0", "DIGITAL_OUTPUT_HIGH"); + + node.getPinMode("DIO0", function(err, res) { + console.log("Pin DIO0 has mode: ", res); + }); + + // XBee will send a sample whenever one of these pins measure a change + node.setChangeDetection([ "DIO2", "DIO3" ]); + + // Manually retrieve a sample + node.getSample(function(err, res) { + console.log("Res2:", util.inspect(res)); + }); + + // ...or instruct xbee to sample data every 5 seconds: + node.setSampleInterval(2000); }); diff --git a/examples/nutshell/nutshell.js b/examples/nutshell/nutshell.js index 62c85e5..5e1eb8a 100644 --- a/examples/nutshell/nutshell.js +++ b/examples/nutshell/nutshell.js @@ -8,7 +8,7 @@ var xbee = new XBee({ // Add Set to your remote node var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0x2f,0xe4]); -var robot.on("data", function(data) { +robot.on("data", function(data) { console.log("robot>", data); if (data == "ping") robot.send("pong"); }); From b3e1e1f6096ec56415a9db606ddd1769290dfb2c Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 22:09:16 +0100 Subject: [PATCH 113/140] formatting --- index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 75854e1..1eb8c30 100644 --- a/index.js +++ b/index.js @@ -356,7 +356,7 @@ XBee.prototype.setChangeDetection = function(pins, cb, remote) { this._AT("IC", val, cb); } -XBee.prototype.setSampleRate = function(interval, cb, remote) { +XBee.prototype.setSampleInterval = function(interval, cb, remote) { var _interval = Tools.dec2bArr(interval, 2); if (remote) this._remoteAT("IR", remote.remote64, remote.remote16, _interval, cb); @@ -427,7 +427,7 @@ XBee.prototype.getPinMode = function(pin, mode, cb, remote) { var _cb = function(err, res) { if (err) cb(err); - else cb(err, res[0]); + else cb(err, res[0]); // Or should we return the name of the mode? }; if (remote) @@ -554,8 +554,8 @@ Node.prototype.setChangeDetection = function(pins, cb) { this.xbee.setChangeDetection(pins, cb, this); } -Node.prototype.setSampleRate = function(interval, cb) { - this.xbee.setSampleRate(interval, cb, this); +Node.prototype.setSampleInterval = function(interval, cb) { + this.xbee.setSampleInterval(interval, cb, this); } Node.prototype.getSample = function(cb) { @@ -565,6 +565,7 @@ Node.prototype.getSample = function(cb) { Node.prototype.setPinMode = function(pin, mode, cb) { this.xbee.setPinMode(pin, mode, cb, this); } + Node.prototype.getAnalogPin = function(pin, cb) { this.xbee.getAnalogPin(pin, cb, this); } From 9af5b3039beb5a3e6ddd13898760d5077527459d Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 17 Mar 2013 22:10:15 +0100 Subject: [PATCH 114/140] v0.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce183cf..c1112cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.1", + "version": "0.3.2", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", From 60275f70d55c931fd859cdc8433caca047f47987 Mon Sep 17 00:00:00 2001 From: Bryan Paluch Date: Fri, 5 Apr 2013 16:59:08 -0400 Subject: [PATCH 115/140] fix OOB error for send, and added Buffer send --- index.js | 5 +++-- lib/xbee-api.js | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 1eb8c30..1c90193 100644 --- a/index.js +++ b/index.js @@ -320,8 +320,9 @@ XBee.prototype.send = function(data, remote64, remote16, _cb) { var frame = new api.TransmitRFData(); frame.destination64 = remote64.dec; frame.destination16 = remote16.dec; - frame.RFData = data.slice(0, C.MAX_PAYLOAD_SIZE); - data = data.slice(C.MAX_PAYLOAD_SIZE); + var length = (C.MAX_PAYLOAD_SIZE < data.length) ? C.MAX_PAYLOAD_SIZE : data.length; + frame.RFData = data.slice(0, length); + data = data.slice(length); packets.push(this._makeTask({ data: frame.getBytes(), cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 6495413..1891013 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -254,7 +254,10 @@ TransmitRFData.prototype.getPayload = function() { if (this.RFData) { for(var j=0; j Date: Sun, 21 Apr 2013 21:04:59 +0200 Subject: [PATCH 116/140] new npm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1112cf..52e5f46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.2", + "version": "0.3.3", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", From ffacc4f5d84ccd040bbc00e14132c6f436e084f5 Mon Sep 17 00:00:00 2001 From: Warren Bloomer Date: Mon, 13 May 2013 18:58:55 +1000 Subject: [PATCH 117/140] Use serialport 1.1 and handle Explicit RX. --- index.js | 36 +++++++++++++++++++++++++++++++++++- lib/xbee-api.js | 17 ++++++++++++++++- package.json | 2 +- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 1c90193..2008986 100644 --- a/index.js +++ b/index.js @@ -141,9 +141,13 @@ XBee.prototype.init = function(cb) { self.serial.on("open", function() { self.readParameters.bind(self)(cb); }); + self.serial.on("close", function() { + console.log("serial port closed"); + }); var exit = function() { self.serial.close(function(err) { + console.log("closed port"); if (err) console.log("Error closing port: "+util.inspect(err)); process.exit(); }); @@ -198,11 +202,23 @@ XBee.prototype.init = function(cb) { self.nodes[data.remote64.hex]._onDataSampleRx(data); } + // Added by Warren + self._onExplicitRx = function(data) { + if (!self.nodes[data.remote64.hex]) { + var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); + self.emit("newNodeDiscovered", node); + } + self.nodes[data.remote64.hex]._onExplicitRx(data); + } + self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + // Added by Warren + self.serial.on(C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX, self._onExplicitRx); + self._queue = async.queue(function(task, callback) { async.series(task.packets, function(err, data) { if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); @@ -311,7 +327,11 @@ XBee.prototype.discover = function(cb) { XBee.prototype.broadcast = function(data, cb) { var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; var remote16 = [0xff,0xfe]; - this.send(data, remote64, remote16, cb); + + // fix by WFB + addr64 = { dec: remote64, hex: Tools.bArr2HexStr(remote64) } + addr16 = { dec: remote16, hex: Tools.bArr2HexStr(remote16) } + this.send(data, addr64, addr16, cb); } XBee.prototype.send = function(data, remote64, remote16, _cb) { @@ -511,6 +531,20 @@ Node.prototype._onDataSampleRx = function(packet) { } } + +// Added by Warren +Node.prototype._onExplicitRx = function(packet) { + var data = new Buffer(packet.rawData).toString('ascii'); + if (this.xbee.use_heartbeat) { + this.refreshTimeout(); + if (data === this.xbee.heartbeat_packet) return; + } + + if (this.parser !== undefined) this.parser.parse(data); + else this.emit('explicit', data, packet); +} + + Node.prototype.timeoutOccured = function() { this.connected = false; this.emit('disconnect'); diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 1891013..fa9c0f0 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -332,7 +332,7 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } - //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); + console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); emitter.emit(evt, json); } } @@ -457,6 +457,21 @@ frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { } }; +// Added by Warren +frames[C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('sourceEndpoint') + .readByte('destEndpoint') + .readByte('clusterId', 2) + .readByte('profileId', 2) + .readByte('receiveOptions') + .collectPayload('rawData'); + } +}; + frames[C.FRAME_TYPE.MODEM_STATUS] = { parse: function(parser) { parser diff --git a/package.json b/package.json index c1112cf..db1174b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ ], "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.0.x", + "serialport": "1.1.x", "async": "0.1.x" }, "engines": { From f96ec13a68ee92be1384eb77e2b04be37405ee3f Mon Sep 17 00:00:00 2001 From: Warren Bloomer Date: Tue, 14 May 2013 04:48:00 +1000 Subject: [PATCH 118/140] Allow for extension for zigbee functionality --- index.js | 44 +++++++++----------------------------- lib/xbee-api.js | 57 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/index.js b/index.js index 2008986..2b5dff8 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,11 @@ function XBee(options) { util.inherits(XBee, EventEmitter); + +XBee.prototype._createNode = function(data) { + return new Node(this, data, this.data_parser) +} + XBee.prototype._makeTask = function(packet) { var self = this; return function Writer(cb) { @@ -51,9 +56,10 @@ XBee.prototype._makeTask = function(packet) { cb(err); } else { //console.log(util.inspect(packet.data)); + //console.log("written data: " + packet.cbid + " : " + results); if (results != packet.data.length) return cb(new Error("Not all bytes written")); self.serial.once(packet.cbid, function(packet) { - //console.log("Got Respones: "+packet.cbid); + console.log("Got Respones: "+packet.cbid); clearTimeout(timeout); var error = null; if (packet.commandStatus !== undefined) { @@ -116,7 +122,7 @@ XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { XBee.prototype._handleNodeIdentification = function(node) { if (!this.nodes[node.remote64.hex]) { - this.nodes[node.remote64.hex] = new Node(this, node, this.data_parser); + this.nodes[node.remote64.hex] = this._createNode(node); this.emit("newNodeDiscovered", this.nodes[node.remote64.hex]); } else { // update 16-bit address, as it may change during reconnects. @@ -141,13 +147,9 @@ XBee.prototype.init = function(cb) { self.serial.on("open", function() { self.readParameters.bind(self)(cb); }); - self.serial.on("close", function() { - console.log("serial port closed"); - }); var exit = function() { self.serial.close(function(err) { - console.log("closed port"); if (err) console.log("Error closing port: "+util.inspect(err)); process.exit(); }); @@ -202,23 +204,11 @@ XBee.prototype.init = function(cb) { self.nodes[data.remote64.hex]._onDataSampleRx(data); } - // Added by Warren - self._onExplicitRx = function(data) { - if (!self.nodes[data.remote64.hex]) { - var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); - self.emit("newNodeDiscovered", node); - } - self.nodes[data.remote64.hex]._onExplicitRx(data); - } - self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); - // Added by Warren - self.serial.on(C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX, self._onExplicitRx); - self._queue = async.queue(function(task, callback) { async.series(task.packets, function(err, data) { if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); @@ -298,7 +288,7 @@ XBee.prototype.addNode = function(remote64, remote16, parser) { var node = self.nodes[node_data.remote64.hex]; if (!node) { - node = self.nodes[node_data.remote64.hex] = new Node(self, node_data, self.data_parser); + node = self.nodes[node_data.remote64.hex] = self._createNode(node_data); node.connected = false; } @@ -506,7 +496,7 @@ function Node(xbee, params, data_parser) { this.parser = data_parser(this); this.timeout = {}; this.connected = true; - this.refreshTimeout(); + this.refreshTimeout(); } util.inherits(Node, EventEmitter); @@ -531,20 +521,6 @@ Node.prototype._onDataSampleRx = function(packet) { } } - -// Added by Warren -Node.prototype._onExplicitRx = function(packet) { - var data = new Buffer(packet.rawData).toString('ascii'); - if (this.xbee.use_heartbeat) { - this.refreshTimeout(); - if (data === this.xbee.heartbeat_packet) return; - } - - if (this.parser !== undefined) this.parser.parse(data); - else this.emit('explicit', data, packet); -} - - Node.prototype.timeoutOccured = function() { this.connected = false; this.emit('disconnect'); diff --git a/lib/xbee-api.js b/lib/xbee-api.js index fa9c0f0..7a2cfef 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -122,6 +122,7 @@ Packet.prototype.getBytes = function() { res.push(packetdata[p] ^ 0x20); } else res.push(packetdata[p]); } + return new Buffer(res, 'ascii'); } @@ -332,7 +333,11 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } - console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); + else if ([C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX].indexOf(json.ft) >= 0) { + var txn = json.profileId == 0 ? json.rawData[0] : json.rawData[1]; + evt += C.EVT_SEP+txn; + } + //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); emitter.emit(evt, json); } } @@ -375,6 +380,21 @@ PacketParser.prototype.readByte = function(name, length) { return this; } +PacketParser.prototype.readInt = function(name, length) { + var bytes = []; + if (typeof length === 'number') + bytes = this.payload.splice(0,length); + else bytes = this.payload.splice(0,1)[0]; + var val = 0; + for (var i = 0; i < bytes.length; ++i) { + val = val << 8; + val += bytes[i]; + } + this.write[name] = val; + + return this; +} + PacketParser.prototype.readAddr64 = function(name) { return this.readAddr(name, 8); } @@ -457,6 +477,24 @@ frames[C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET] = { } }; +frames[C.FRAME_TYPE.MODEM_STATUS] = { + parse: function(parser) { + parser + .readByte('status'); + } +} + +frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { + parse: function(parser) { + parser + .readAddr64('remote64') + .readAddr16('remote16') + .readByte('receiveOptions') + .collectPayload('ioSample'); + } +}; + + // Added by Warren frames[C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX] = { parse: function(parser) { @@ -465,27 +503,22 @@ frames[C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX] = { .readAddr16('remote16') .readByte('sourceEndpoint') .readByte('destEndpoint') - .readByte('clusterId', 2) - .readByte('profileId', 2) + .readInt('clusterId', 2) + .readInt('profileId', 2) .readByte('receiveOptions') .collectPayload('rawData'); } }; -frames[C.FRAME_TYPE.MODEM_STATUS] = { - parse: function(parser) { - parser - .readByte('status'); - } -} - -frames[C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX] = { +// Added by Warren +frames[C.FRAME_TYPE.ROUTE_RECORD] = { parse: function(parser) { parser .readAddr64('remote64') .readAddr16('remote16') .readByte('receiveOptions') - .collectPayload('ioSample'); + .readByte('numAddresses') // number of addresses in the source route + .collectPayload('rawData'); // each address is a pair of bytes } }; From 9b7e4760fe2120551d268f0e6fa05dd5c6d885e1 Mon Sep 17 00:00:00 2001 From: Warren Bloomer Date: Thu, 16 May 2013 16:44:32 +1000 Subject: [PATCH 119/140] Only allow callback tracking for responses to requests --- lib/xbee-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 7a2cfef..f017b50 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -333,7 +333,7 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } - else if ([C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX].indexOf(json.ft) >= 0) { + else if ( C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX == json.ft && 1 == json.receiveOptions ) { var txn = json.profileId == 0 ? json.rawData[0] : json.rawData[1]; evt += C.EVT_SEP+txn; } From 3ee285a9440029b209f45b16791392790f5b18e4 Mon Sep 17 00:00:00 2001 From: lowi Date: Sun, 26 May 2013 16:34:42 +0200 Subject: [PATCH 120/140] bumped node engine version number to 0.10.x --- package.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index db1174b..30a038f 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ - { - "name": "svd-xbee", - "version": "0.3.2", - "description": "A more high level fork of Richard Morrison's node-xbee", - "author": { - "name": "Jan Kolkmeier", - "email": "jankolkmeier@gmail.com" - }, +{ + "name": "svd-xbee", + "version": "0.3.2", + "description": "A more high level fork of Richard Morrison's node-xbee", + "author": { + "name": "Jan Kolkmeier", + "email": "jankolkmeier@gmail.com" + }, "maintainers": "Jan Kolkmeier ", "contributors": [ { @@ -31,15 +31,15 @@ ], "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.1.x", - "async": "0.1.x" + "serialport": "~1.1.0", + "async": "~0.2.8" }, "engines": { - "node": "0.6.x | 0.8.x" + "node": "0.10.x" }, "repository": { "type": "git", - "url": "git://github.com/jouz/svd-xbee.git" + "url": "git://github.com/lowi-yeah/svd-xbee" }, "readmeFilename": "README.md" } From 91123b65ecc79e07ef222a48d26a56f41eab000e Mon Sep 17 00:00:00 2001 From: lowi Date: Mon, 27 May 2013 12:52:00 +0200 Subject: [PATCH 121/140] pixel data parsing bug. used to be ascii (and thus 7 bit). this dropped one bit. data now parsed correctly --- index.js | 1027 +++++++++++++++++++++++++++--------------------------- 1 file changed, 518 insertions(+), 509 deletions(-) diff --git a/index.js b/index.js index 2b5dff8..6f7c052 100644 --- a/index.js +++ b/index.js @@ -1,390 +1,398 @@ +"use strict"; // see: ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ + var util = require('util'); var EventEmitter = require('events').EventEmitter; var api = require("./lib/xbee-api.js"); var serialport = require("serialport"); var async = require('async'); var os = require('os'); +var StringDecoder = require('string_decoder').StringDecoder; +var decoder = new StringDecoder('utf-8'); var C = exports.C = api.Constants; var Tools = exports.Tools = api.tools; -function XBee(options) { - EventEmitter.call(this); +function XBee(options) { + EventEmitter.call(this); - // Option Parsing - if (typeof options === 'string') { - this.options = { port: options }; - } else { - this.options = options; - } + // Option Parsing + if (typeof options === 'string') { + this.options = { port: options }; + } else { + this.options = options; + } - this.tools = Tools; // Remove this, use XBee.Tools instead + this.tools = Tools; // Remove this, use XBee.Tools instead - this.data_parser = options.data_parser || undefined; + this.data_parser = options.data_parser || undefined; - this.use_heartbeat = options.use_heartbeat || false; - this.heartbeat_packet = options.heartbeat_packet || '```'; - this.heartbeat_timeout = options.heartbeat_timeout || 8000; + this.use_heartbeat = options.use_heartbeat || false; + this.heartbeat_packet = options.heartbeat_packet || '```'; + this.heartbeat_timeout = options.heartbeat_timeout || 8000; - // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? - this.transmit_status_timeout = options.transmit_status_timeout || 1000; + // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? + this.transmit_status_timeout = options.transmit_status_timeout || 1000; - if (options.api_mode) api.api_mode = options.api_mode; + if (options.api_mode) api.api_mode = options.api_mode; - // Current nodes - this.nodes = {}; + // Current nodes + this.nodes = {}; } util.inherits(XBee, EventEmitter); -XBee.prototype._createNode = function(data) { - return new Node(this, data, this.data_parser) -} - -XBee.prototype._makeTask = function(packet) { - var self = this; - return function Writer(cb) { - //console.log("<<< "+util.inspect(packet.data)); - //console.log("<<< "+packet.data); - - var timeout = setTimeout(function() { - cb({ msg: "Never got Transmit status from XBee" }); - }, self.transmit_status_timeout ); - self.serial.write(packet.data, function(err, results) { - if (err) { - cb(err); - } else { - //console.log(util.inspect(packet.data)); - //console.log("written data: " + packet.cbid + " : " + results); - if (results != packet.data.length) return cb(new Error("Not all bytes written")); - self.serial.once(packet.cbid, function(packet) { - console.log("Got Respones: "+packet.cbid); - clearTimeout(timeout); - var error = null; - if (packet.commandStatus !== undefined) { - if (packet.commandStatus != C.COMMAND_STATUS.OK) { - error = C.COMMAND_STATUS[packet.commandStatus]; +XBee.prototype._createNode = function (data) { + return new Node(this, data, this.data_parser) +} + +XBee.prototype._makeTask = function (packet) { + var self = this; + return function Writer(cb) { + //console.log("<<< "+util.inspect(packet.data)); + //console.log("<<< "+packet.data); + + var timeout = setTimeout(function () { + cb({ msg: "Never got Transmit status from XBee" }); + }, self.transmit_status_timeout); + self.serial.write(packet.data, function (err, results) { + if (err) { + cb(err); + } else { + //console.log(util.inspect(packet.data)); + //console.log("written data: " + packet.cbid + " : " + results); + if (results != packet.data.length) return cb(new Error("Not all bytes written")); + self.serial.once(packet.cbid, function (packet) { +// console.log("Got Respones: "+packet.cbid); + clearTimeout(timeout); + var error = null; + if (packet.commandStatus !== undefined) { + if (packet.commandStatus != C.COMMAND_STATUS.OK) { + error = C.COMMAND_STATUS[packet.commandStatus]; + } + packet = packet.commandData; + } else if (packet.deliveryStatus !== undefined) { + if (packet.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { + error = C.DELIVERY_STATUS[packet.deliveryStatus]; + } + } + cb(error, packet); + }); } - packet = packet.commandData; - } else if (packet.deliveryStatus !== undefined) { - if (packet.deliveryStatus != C.DELIVERY_STATUS.SUCCESS) { - error = C.DELIVERY_STATUS[packet.deliveryStatus]; - } - } - cb(error, packet); }); - } - }); - }; + }; } -XBee.prototype._AT = function(cmd, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; - } +XBee.prototype._AT = function (cmd, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } - var frame = new api.ATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; + var frame = new api.ATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + var cbid = C.FRAME_TYPE.AT_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets: packet, cb: _cb }); + return cbid; } // TODO: Merge this up with _AT to save some space -XBee.prototype._remoteAT = function(cmd, remote64, remote16, val, _cb) { - // val parameter is optional - if (typeof val === 'function') { - _cb = val; - val = undefined; - } - - var frame = new api.RemoteATCommand(); - frame.setCommand(cmd); - frame.commandParameter = val; - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; - var packet = [this._makeTask({ - data: frame.getBytes(), - cbid: cbid - })]; - this._queue.push({ packets:packet, cb:_cb }); - return cbid; -} - -XBee.prototype._handleNodeIdentification = function(node) { - if (!this.nodes[node.remote64.hex]) { - this.nodes[node.remote64.hex] = this._createNode(node); - this.emit("newNodeDiscovered", this.nodes[node.remote64.hex]); - } else { - // update 16-bit address, as it may change during reconnects. - this.nodes[node.remote64.hex].remote16 = node.remote16; - this.nodes[node.remote64.hex].id = node.id; - this.nodes[node.remote64.hex].emit("discovered", this.nodes[node.remote64.hex]); - } - this.nodes[node.remote64.hex].connected = true; -} - -XBee.prototype.init = function(cb) { - var self = this; - // Serial connection to the XBee - self.serial = new serialport.SerialPort(self.options.port, { - baudrate: self.options.baudrate || 9600, - databits: 8, - stopbits: 1, - parity: 'none', - parser: api.packetBuilder() - }); - - self.serial.on("open", function() { - self.readParameters.bind(self)(cb); - }); - - var exit = function() { - self.serial.close(function(err) { - if (err) console.log("Error closing port: "+util.inspect(err)); - process.exit(); - }); - } - - if (os.platform() !== 'win32') { - process.on('SIGINT', exit); - } - - - /* Frame-specific Handlers */ - - // Whenever a node reports with an identification frame. - self._onNodeIdentification = function(packet) { - var node = parseNodeIdentificationPayload(packet.nodeIdentificationPayload); - self._handleNodeIdentification(node); - } - - // Modem Status - self._onModemStatus = function(packet) { - if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { - self.emit("joinedNetwork", packet); - } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { - self.emit("hardwareReset", packet); - } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { - self.emit("watchdogReset", packet); - } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { - self.emit("disassociated", packet); - } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { - self.emit("coordinatorStarted", packet); +XBee.prototype._remoteAT = function (cmd, remote64, remote16, val, _cb) { + // val parameter is optional + if (typeof val === 'function') { + _cb = val; + val = undefined; + } + + var frame = new api.RemoteATCommand(); + frame.setCommand(cmd); + frame.commandParameter = val; + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + var cbid = C.FRAME_TYPE.REMOTE_COMMAND_RESPONSE + C.EVT_SEP + frame.frameId; + var packet = [this._makeTask({ + data: frame.getBytes(), + cbid: cbid + })]; + this._queue.push({ packets: packet, cb: _cb }); + return cbid; +} + +XBee.prototype._handleNodeIdentification = function (node) { + if (!this.nodes[node.remote64.hex]) { + this.nodes[node.remote64.hex] = this._createNode(node); + this.emit("newNodeDiscovered", this.nodes[node.remote64.hex]); } else { - console.warn("Modem status: ", C.MODEM_STATUS[res.status]); + // update 16-bit address, as it may change during reconnects. + this.nodes[node.remote64.hex].remote16 = node.remote16; + this.nodes[node.remote64.hex].id = node.id; + this.nodes[node.remote64.hex].emit("discovered", this.nodes[node.remote64.hex]); } - } + this.nodes[node.remote64.hex].connected = true; +} + +XBee.prototype.init = function (cb) { + var self = this; + // Serial connection to the XBee + self.serial = new serialport.SerialPort(self.options.port, { + baudrate: self.options.baudrate || 9600, + databits: 8, + stopbits: 1, + parity: 'none', + parser: api.packetBuilder() + }); - // Messages - self._onReceivePacket = function(data) { - if (!self.nodes[data.remote64.hex]) { - var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); - self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? + self.serial.on("open", function () { + self.readParameters.bind(self)(cb); + }); + + var exit = function () { + self.serial.close(function (err) { + if (err) console.log("Error closing port: " + util.inspect(err)); + process.exit(); + }); } - self.nodes[data.remote64.hex]._onReceivePacket(data); - } - - // Data samples (from XBee's I/O) - self._onDataSampleRx = function(data) { - // Todo: data from local xbee? - if (!self.nodes[data.remote64.hex]) { - var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); - self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? + + if (os.platform() !== 'win32') { + process.on('SIGINT', exit); } - self.nodes[data.remote64.hex]._onDataSampleRx(data); - } - - self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); - self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); - self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); - self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); - - self._queue = async.queue(function(task, callback) { - async.series(task.packets, function(err, data) { - if (typeof task.cb === 'function') task.cb(err, data[data.length-1]); - callback(); - }); - }, 1); - return self; -} -XBee.prototype.readParameters = function(_done_cb) { - var self = this; + /* Frame-specific Handlers */ + + // Whenever a node reports with an identification frame. + self._onNodeIdentification = function (packet) { + var node = parseNodeIdentificationPayload(packet.nodeIdentificationPayload); + self._handleNodeIdentification(node); + } - // Returns a function that initiates an AT command to - // query a configuration parameter's value. - // To be passed to an async.parallel. - var QF = function(command, val, f) { // Format the result using f - f = typeof f !== 'undefined' ? f : function(a){return a}; - return function(cb) { - self._AT(command, val, function(err, res) { - if (!err) { - cb(err, f(res)); + // Modem Status + self._onModemStatus = function (packet) { + if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { + self.emit("joinedNetwork", packet); + } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { + self.emit("hardwareReset", packet); + } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { + self.emit("watchdogReset", packet); + } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { + self.emit("disassociated", packet); + } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { + self.emit("coordinatorStarted", packet); } else { - console.error('Error: XBee.readParameters.QF; ', err.msg); + console.warn("Modem status: ", C.MODEM_STATUS[res.status]); } - }); } - } - - var parameters = { - panid: QF('ID', undefined, Tools.bArr2HexStr), - id: QF('NI', undefined, Tools.bArr2Str), - sourceHigh: QF('SH', undefined, Tools.bArr2HexStr), - sourceLow: QF('SL', undefined, Tools.bArr2HexStr), - nodeDiscoveryTime: QF('NT', undefined, function(a) { return 100 * Tools.bArr2Dec(a); }) - }; - - var done = function(err, results) { - if (err) { - self.emit("error", new Error("Failure to read XBee params: "+util.inspect(err))); - if (typeof _done_cb === 'function') _done_cb(err); + + // Messages + self._onReceivePacket = function (data) { + if (!self.nodes[data.remote64.hex]) { + var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); + self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? + } + self.nodes[data.remote64.hex]._onReceivePacket(data); + } + + // Data samples (from XBee's I/O) + self._onDataSampleRx = function (data) { + // Todo: data from local xbee? + if (!self.nodes[data.remote64.hex]) { + var node = self.addNode(data.remote64.dec, data.remote16.dec, self.data_parser); + self.emit("newNodeDiscovered", node); // TODO: Should this be a different event? + } + self.nodes[data.remote64.hex]._onDataSampleRx(data); + } + + self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); + self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); + self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + + self._queue = async.queue(function (task, callback) { + async.series(task.packets, function (err, data) { + if (typeof task.cb === 'function') task.cb(err, data[data.length - 1]); + callback(); + }); + }, 1); + + return self; +} + +XBee.prototype.readParameters = function (_done_cb) { + var self = this; + + // Returns a function that initiates an AT command to + // query a configuration parameter's value. + // To be passed to an async.parallel. + var QF = function (command, val, f) { // Format the result using f + f = typeof f !== 'undefined' ? f : function (a) { + return a + }; + return function (cb) { + self._AT(command, val, function (err, res) { + if (!err) { + cb(err, f(res)); + } else { + console.error('Error: XBee.readParameters.QF; ', err.msg); + } + }); + } } - self.parameters = results; - self.emit("initialized", self.parameters); - if (typeof _done_cb === 'function') _done_cb(null, self.parameters); - } - - // Using async to read parameters - var res_stop = Object.keys(parameters).length; - var results = {}; - for (k in parameters) { - parameters[k]((function(key) { - return function(err, data) { - if (err) return done(err, null); - results[key] = data; - // TODO: Timeout? - if (--res_stop === 0) { - done(null, results); + + var parameters = { + panid: QF('ID', undefined, Tools.bArr2HexStr), + id: QF('NI', undefined, Tools.bArr2Str), + sourceHigh: QF('SH', undefined, Tools.bArr2HexStr), + sourceLow: QF('SL', undefined, Tools.bArr2HexStr), + nodeDiscoveryTime: QF('NT', undefined, function (a) { + return 100 * Tools.bArr2Dec(a); + }) + }; + + var done = function (err, results) { + if (err) { + self.emit("error", new Error("Failure to read XBee params: " + util.inspect(err))); + if (typeof _done_cb === 'function') _done_cb(err); } - } - })(k)); - } + self.parameters = results; + self.emit("initialized", self.parameters); + if (typeof _done_cb === 'function') _done_cb(null, self.parameters); + } + + // Using async to read parameters + var res_stop = Object.keys(parameters).length; + var results = {}; + for (k in parameters) { + parameters[k]((function (key) { + return function (err, data) { + if (err) return done(err, null); + results[key] = data; + // TODO: Timeout? + if (--res_stop === 0) { + done(null, results); + } + } + })(k)); + } } // Add a node by hand. -XBee.prototype.addNode = function(remote64, remote16, parser) { - var self = this; - //var remote16 = [0xff,0xfe]; // Unknown - if (!remote16 instanceof Array) { - parser = remote16; - } - var node_data = { - remote16: { dec: remote16, hex: Tools.bArr2HexStr(remote16) }, - remote64: { dec: remote64, hex: Tools.bArr2HexStr(remote64) } - }; - - var node = self.nodes[node_data.remote64.hex]; - - if (!node) { - node = self.nodes[node_data.remote64.hex] = self._createNode(node_data); - node.connected = false; - } - - if (typeof parser === "function") - node.parser = parser(node); - - return node; +XBee.prototype.addNode = function (remote64, remote16, parser) { + var self = this; + //var remote16 = [0xff,0xfe]; // Unknown + if (!remote16 instanceof Array) { + parser = remote16; + } + var node_data = { + remote16: { dec: remote16, hex: Tools.bArr2HexStr(remote16) }, + remote64: { dec: remote64, hex: Tools.bArr2HexStr(remote64) } + }; + + var node = self.nodes[node_data.remote64.hex]; + + if (!node) { + node = self.nodes[node_data.remote64.hex] = self._createNode(node_data); + node.connected = false; + } + + if (typeof parser === "function") + node.parser = parser(node); + + return node; } // Run network discovery. Associated nodes can report in // for config.nodeDiscoveryTime ms. -XBee.prototype.discover = function(cb) { - var self = this; - var cbid = self._AT('ND'); - self.serial.on(cbid, function(packet) { - var node = parseNodeIdentificationPayload(packet.commandData); - self._handleNodeIdentification(node); - }) - setTimeout(function() { - if (typeof cb === 'function') cb(); - self.removeAllListeners(cbid); - self.emit("discoveryEnd"); - }, self.parameters.nodeDiscoveryTime || 6000); -} - -XBee.prototype.broadcast = function(data, cb) { - var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - var remote16 = [0xff,0xfe]; - - // fix by WFB - addr64 = { dec: remote64, hex: Tools.bArr2HexStr(remote64) } - addr16 = { dec: remote16, hex: Tools.bArr2HexStr(remote16) } - this.send(data, addr64, addr16, cb); -} - -XBee.prototype.send = function(data, remote64, remote16, _cb) { - var packets = []; - while (data.length > 0) { - var frame = new api.TransmitRFData(); - frame.destination64 = remote64.dec; - frame.destination16 = remote16.dec; - var length = (C.MAX_PAYLOAD_SIZE < data.length) ? C.MAX_PAYLOAD_SIZE : data.length; - frame.RFData = data.slice(0, length); - data = data.slice(length); - packets.push(this._makeTask({ - data: frame.getBytes(), - cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId - })); - } +XBee.prototype.discover = function (cb) { + var self = this; + var cbid = self._AT('ND'); + self.serial.on(cbid, function (packet) { + var node = parseNodeIdentificationPayload(packet.commandData); + self._handleNodeIdentification(node); + }) + setTimeout(function () { + if (typeof cb === 'function') cb(); + self.removeAllListeners(cbid); + self.emit("discoveryEnd"); + }, self.parameters.nodeDiscoveryTime || 6000); +} + +XBee.prototype.broadcast = function (data, cb) { + var remote64 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff]; + var remote16 = [0xff, 0xfe]; + + // fix by WFB + var addr64 = { dec: remote64, hex: Tools.bArr2HexStr(remote64) } + var addr16 = { dec: remote16, hex: Tools.bArr2HexStr(remote16) } + this.send(data, addr64, addr16, cb); +} + +XBee.prototype.send = function (data, remote64, remote16, _cb) { + var packets = []; + while (data.length > 0) { + var frame = new api.TransmitRFData(); + frame.destination64 = remote64.dec; + frame.destination16 = remote16.dec; + var length = (C.MAX_PAYLOAD_SIZE < data.length) ? C.MAX_PAYLOAD_SIZE : data.length; + frame.RFData = data.slice(0, length); + data = data.slice(length); + packets.push(this._makeTask({ + data: frame.getBytes(), + cbid: C.FRAME_TYPE.ZIGBEE_TRANSMIT_STATUS + C.EVT_SEP + frame.frameId + })); + } - this._queue.push({ packets:packets, cb:_cb }); + this._queue.push({ packets: packets, cb: _cb }); } -XBee.prototype.AT = function(cmd, val, _cb) { - this._AT(cmd, val, _cb); +XBee.prototype.AT = function (cmd, val, _cb) { + this._AT(cmd, val, _cb); } -XBee.prototype.setChangeDetection = function(pins, cb, remote) { - // TODO: this is lazy... - var mask = "000000000000".split(''); - for (pin in pins) { - var _pin = pins[pin]; - if (typeof _pin == "number") { - mask[C.CHANGE_DETECTION.PIN[_pin]] = '1'; - } else { - mask[C.CHANGE_DETECTION[_pin]] = '1'; +XBee.prototype.setChangeDetection = function (pins, cb, remote) { + // TODO: this is lazy... + var mask = "000000000000".split(''); + for (var pin in pins) { + var _pin = pins[pin]; + if (typeof _pin == "number") { + mask[C.CHANGE_DETECTION.PIN[_pin]] = '1'; + } else { + mask[C.CHANGE_DETECTION[_pin]] = '1'; + } } - } - var val = parseInt(mask.reverse().join(''), 2); - val = Tools.dec2bArr(val, 2); + var val = parseInt(mask.reverse().join(''), 2); + val = Tools.dec2bArr(val, 2); - if (remote) - this._remoteAT("IC", remote.remote64, remote.remote16, val, cb); - else - this._AT("IC", val, cb); + if (remote) + this._remoteAT("IC", remote.remote64, remote.remote16, val, cb); + else + this._AT("IC", val, cb); } -XBee.prototype.setSampleInterval = function(interval, cb, remote) { - var _interval = Tools.dec2bArr(interval, 2); - if (remote) - this._remoteAT("IR", remote.remote64, remote.remote16, _interval, cb); - else - this._AT("IR", _interval, cb); +XBee.prototype.setSampleInterval = function (interval, cb, remote) { + var _interval = Tools.dec2bArr(interval, 2); + if (remote) + this._remoteAT("IR", remote.remote64, remote.remote16, _interval, cb); + else + this._AT("IR", _interval, cb); } -XBee.prototype.getSample = function(cb, remote) { - var _cb = function(err, res) { - if (err) cb(err); - else cb(err, parseIOSample(res)); - } +XBee.prototype.getSample = function (cb, remote) { + var _cb = function (err, res) { + if (err) cb(err); + else cb(err, parseIOSample(res)); + } - if (remote) - this._remoteAT("IS", remote.remote64, remote.remote16, _cb); - else - this._AT("IS", _cb); + if (remote) + this._remoteAT("IS", remote.remote64, remote.remote16, _cb); + else + this._AT("IS", _cb); } @@ -393,19 +401,19 @@ XBee.prototype.getSample = function(cb, remote) { // we assume the descriptive name (AO3, etc.) // If "pin" is a number // we assume the phsical pin is meant (~1-20) -XBee.prototype.getAnalogPin = function(pin, cb, remote) { - var _pin; - if (typeof pin == "number") - _pin = C.ANALOG_CHANNEL.MASK[C.ANALOG_CHANNEL.PIN[pin]][0] - else _pin = pin; - - this.getSample(function(err, res) { - if (err) cb(err); - else if (res.analogSamples[_pin] == undefined) - cb(new Error("XBee not configured to take analog samples on pin "+_pin)); - else - cb(err, res.analogSamples[_pin]); - }, remote); +XBee.prototype.getAnalogPin = function (pin, cb, remote) { + var _pin; + if (typeof pin == "number") + _pin = C.ANALOG_CHANNEL.MASK[C.ANALOG_CHANNEL.PIN[pin]][0] + else _pin = pin; + + this.getSample(function (err, res) { + if (err) cb(err); + else if (res.analogSamples[_pin] == undefined) + cb(new Error("XBee not configured to take analog samples on pin " + _pin)); + else + cb(err, res.analogSamples[_pin]); + }, remote); } // Change the mode of a pin. @@ -413,38 +421,38 @@ XBee.prototype.getAnalogPin = function(pin, cb, remote) { // we assume the descriptive name (DIO3, etc.) // If "pin" is a number // we assume the phsical pin is meant (~1-20) -XBee.prototype.getDigitalPin = function(pin, cb, remote) { - var _pin; - if (typeof pin == "number") - _pin = C.DIGITAL_CHANNEL.MASK[C.DIGITAL_CHANNEL.PIN[pin]][0] - else _pin = pin; - - this.getSample(function(err, res) { - if (err) cb(err); - else if (res.digitalSamples[_pin] == undefined) - cb(new Error("XBee not configured to take digital samples on pin "+_pin)); +XBee.prototype.getDigitalPin = function (pin, cb, remote) { + var _pin; + if (typeof pin == "number") + _pin = C.DIGITAL_CHANNEL.MASK[C.DIGITAL_CHANNEL.PIN[pin]][0] + else _pin = pin; + + this.getSample(function (err, res) { + if (err) cb(err); + else if (res.digitalSamples[_pin] == undefined) + cb(new Error("XBee not configured to take digital samples on pin " + _pin)); + else + cb(err, res.digitalSamples[_pin]); + }, remote); +} + +XBee.prototype.getPinMode = function (pin, mode, cb, remote) { + var _pin; + if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; + else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; + else _pin = pin; + if (_pin == undefined) + return cb(new Error("Unknown pin: " + pin)); + + var _cb = function (err, res) { + if (err) cb(err); + else cb(err, res[0]); // Or should we return the name of the mode? + }; + + if (remote) + this._remoteAT(_pin, remote.remote64, remote.remote16, _cb); else - cb(err, res.digitalSamples[_pin]); - }, remote); -} - -XBee.prototype.getPinMode = function(pin, mode, cb, remote) { - var _pin; - if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; - else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; - else _pin = pin; - if (_pin == undefined) - return cb(new Error("Unknown pin: "+pin)); - - var _cb = function(err, res) { - if (err) cb(err); - else cb(err, res[0]); // Or should we return the name of the mode? - }; - - if (remote) - this._remoteAT(_pin, remote.remote64, remote.remote16, _cb); - else - this._AT(_pin, _cb); + this._AT(_pin, _cb); } // Change the mode of a pin. @@ -456,199 +464,200 @@ XBee.prototype.getPinMode = function(pin, mode, cb, remote) { // we assume it is the byte parameter // If "mode" is a string // we assume descriptive mode (DISABLED, DIGITAL_INPUT, ...) see constants.js -XBee.prototype.setPinMode = function(pin, mode, cb, remote) { - var _pin, _mode; - if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; - else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; - else _pin = pin; - if (_pin == undefined || C.PIN_MODE[_pin] == undefined) - return cb(new Error("Unknown pin: "+pin)); - if (typeof mode == "string") { - _mode = C.PIN_MODE[_pin][mode]; - } else _mode = mode; - if (_mode == undefined || C.PIN_MODE[_pin][mode] == undefined) - return cb(new Error("Unknown mode "+mode+" for pin "+pin)); - _mode = parseInt(_mode); // Make sure mode is dec - - if (remote) - this._remoteAT(_pin, remote.remote64, remote.remote16, [_mode], cb); - else - this._AT(_pin, [_mode], cb); -} - -XBee.prototype.setDestinationNode = function(dest, cb, remote) { - cb("Not Implemented Yet"); +XBee.prototype.setPinMode = function (pin, mode, cb, remote) { + var _pin, _mode; + if (typeof pin == "number") _pin = C.PIN_COMMAND.PIN[pin]; + else if (typeof pin == "string" && pin.length != 2) _pin = C.PIN_COMMAND[pin]; + else _pin = pin; + if (_pin == undefined || C.PIN_MODE[_pin] == undefined) + return cb(new Error("Unknown pin: " + pin)); + if (typeof mode == "string") { + _mode = C.PIN_MODE[_pin][mode]; + } else _mode = mode; + if (_mode == undefined || C.PIN_MODE[_pin][mode] == undefined) + return cb(new Error("Unknown mode " + mode + " for pin " + pin)); + _mode = parseInt(_mode); // Make sure mode is dec + + if (remote) + this._remoteAT(_pin, remote.remote64, remote.remote16, [_mode], cb); + else + this._AT(_pin, [_mode], cb); } -exports.XBee = XBee; +XBee.prototype.setDestinationNode = function (dest, cb, remote) { + cb("Not Implemented Yet"); +} +exports.XBee = XBee; function Node(xbee, params, data_parser) { - EventEmitter.call(this); - this.xbee = xbee; - this.remote16 = params.remote16; - this.remote64 = params.remote64; - this.id = params.id || ""; - this.deviceType = params.deviceType || -1; - this.buffer = ""; - if (typeof data_parser === 'function') - this.parser = data_parser(this); - this.timeout = {}; - this.connected = true; - this.refreshTimeout(); + EventEmitter.call(this); + this.xbee = xbee; + this.remote16 = params.remote16; + this.remote64 = params.remote64; + this.id = params.id || ""; + this.deviceType = params.deviceType || -1; + this.buffer = ""; + if (typeof data_parser === 'function') + this.parser = data_parser(this); + this.timeout = {}; + this.connected = true; + this.refreshTimeout(); } util.inherits(Node, EventEmitter); -Node.prototype._onReceivePacket = function(packet) { - // TODO: should be buffer all along! - var data = new Buffer(packet.rawData).toString('ascii'); - if (this.xbee.use_heartbeat) { - this.refreshTimeout(); - if (data === this.xbee.heartbeat_packet) return; - } +Node.prototype._onReceivePacket = function (packet) { + +// the original code used Buffer.toString('ascii') +// but ascii is 7 bit only, so the most significant bit of each value got trahsed. + var data = decoder.write(packet.rawData); + if (this.xbee.use_heartbeat) { + this.refreshTimeout(); + if (data === this.xbee.heartbeat_packet) return; + } - if (this.parser !== undefined) this.parser.parse(data); - else this.emit('data', data, packet); + if (this.parser !== undefined) this.parser.parse(data); + else this.emit('data', data, packet); } -Node.prototype._onDataSampleRx = function(packet) { - var sample = parseIOSample(packet.ioSample); - this.emit('io', sample); - if (this.xbee.use_heartbeat) { - this.refreshTimeout(); - } +Node.prototype._onDataSampleRx = function (packet) { + var sample = parseIOSample(packet.ioSample); + this.emit('io', sample); + if (this.xbee.use_heartbeat) { + this.refreshTimeout(); + } } -Node.prototype.timeoutOccured = function() { - this.connected = false; - this.emit('disconnect'); +Node.prototype.timeoutOccured = function () { + this.connected = false; + this.emit('disconnect'); } -Node.prototype.refreshTimeout = function() { - clearTimeout(this.timeout); - this.timeout = setTimeout(this.timeoutOccured.bind(this), this.xbee.heartbeat_timeout); - if (!this.connected) { - this.connected = true; - // todo other stuff - } +Node.prototype.refreshTimeout = function () { + clearTimeout(this.timeout); + this.timeout = setTimeout(this.timeoutOccured.bind(this), this.xbee.heartbeat_timeout); + if (!this.connected) { + this.connected = true; + // todo other stuff + } } -Node.prototype.send = function(data, cb) { - this.xbee.send(data, this.remote64, this.remote16, cb); +Node.prototype.send = function (data, cb) { + this.xbee.send(data, this.remote64, this.remote16, cb); } -Node.prototype.AT = function(cmd, val, cb) { - // val parameter is optional - if (typeof val === "function") { - // use val as the callback in this case - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); - } else { - this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); - } +Node.prototype.AT = function (cmd, val, cb) { + // val parameter is optional + if (typeof val === "function") { + // use val as the callback in this case + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val); + } else { + this.xbee._remoteAT(cmd, this.remote64, this.remote16, val, cb); + } } -Node.prototype.isCoordinator = function() { - return this.deviceType === C.DEVICE_TYPES.COORDINATOR; +Node.prototype.isCoordinator = function () { + return this.deviceType === C.DEVICE_TYPES.COORDINATOR; } -Node.prototype.isRouter = function() { - return this.deviceType === C.DEVICE_TYPES.ROUTER; +Node.prototype.isRouter = function () { + return this.deviceType === C.DEVICE_TYPES.ROUTER; } -Node.prototype.isEndDevice = function() { - return this.deviceType === C.DEVICE_TYPES.END_DEVICE +Node.prototype.isEndDevice = function () { + return this.deviceType === C.DEVICE_TYPES.END_DEVICE } -Node.prototype.setChangeDetection = function(pins, cb) { - this.xbee.setChangeDetection(pins, cb, this); +Node.prototype.setChangeDetection = function (pins, cb) { + this.xbee.setChangeDetection(pins, cb, this); } -Node.prototype.setSampleInterval = function(interval, cb) { - this.xbee.setSampleInterval(interval, cb, this); +Node.prototype.setSampleInterval = function (interval, cb) { + this.xbee.setSampleInterval(interval, cb, this); } -Node.prototype.getSample = function(cb) { - this.xbee.getSample(cb, this); +Node.prototype.getSample = function (cb) { + this.xbee.getSample(cb, this); }; -Node.prototype.setPinMode = function(pin, mode, cb) { - this.xbee.setPinMode(pin, mode, cb, this); +Node.prototype.setPinMode = function (pin, mode, cb) { + this.xbee.setPinMode(pin, mode, cb, this); } -Node.prototype.getAnalogPin = function(pin, cb) { - this.xbee.getAnalogPin(pin, cb, this); +Node.prototype.getAnalogPin = function (pin, cb) { + this.xbee.getAnalogPin(pin, cb, this); } -Node.prototype.getDigitalPin = function(pin, cb) { - this.xbee.getDigitalPin(pin, cb, this); +Node.prototype.getDigitalPin = function (pin, cb) { + this.xbee.getDigitalPin(pin, cb, this); } -Node.prototype.setDestinationNode = function(dest, cb) { - this.xbee.setDestinationNode(dest, cb); +Node.prototype.setDestinationNode = function (dest, cb) { + this.xbee.setDestinationNode(dest, cb); } exports.Node = Node; -var parseIOSample = exports.parseIOSample = function(sample) { - var res = { - digitalSamples: {}, - analogSamples: {} - } - - var numSamples = sample.splice(0,1)[0]; - var mskD = sample.splice(0,2); - mskD = (mskD[0] << 8) | mskD[1]; - var mskA = sample.splice(0,1)[0]; - - if (mskD > 0) { - var digitalSamples = sample.splice(0,2); - var valD = (digitalSamples[0] << 8) | digitalSamples[1]; - for (var bit in C.DIGITAL_CHANNELS.MASK) { - if ((mskD & (1 << bit)) >> bit) { - res.digitalSamples[C.DIGITAL_CHANNELS.MASK[bit][0]] = (valD & (1 << bit)) >> bit; - } +var parseIOSample = exports.parseIOSample = function (sample) { + var res = { + digitalSamples: {}, + analogSamples: {} + } + + var numSamples = sample.splice(0, 1)[0]; + var mskD = sample.splice(0, 2); + mskD = (mskD[0] << 8) | mskD[1]; + var mskA = sample.splice(0, 1)[0]; + + if (mskD > 0) { + var digitalSamples = sample.splice(0, 2); + var valD = (digitalSamples[0] << 8) | digitalSamples[1]; + for (var bit in C.DIGITAL_CHANNELS.MASK) { + if ((mskD & (1 << bit)) >> bit) { + res.digitalSamples[C.DIGITAL_CHANNELS.MASK[bit][0]] = (valD & (1 << bit)) >> bit; + } + } } - } - - if (mskA > 0) { - var analogSamples = sample.splice(0); - var sampleNr = 0; - for (var bit in C.ANALOG_CHANNELS.MASK) { - if ((mskA & (1 << bit)) >> bit) { - var valA = (analogSamples[sampleNr*2] << 8) | analogSamples[sampleNr*2+1]; - // Convert to mV - res.analogSamples[C.ANALOG_CHANNELS.MASK[bit][0]] = (valA * 1200) / 1023; - sampleNr++; - } + + if (mskA > 0) { + var analogSamples = sample.splice(0); + var sampleNr = 0; + for (var bit in C.ANALOG_CHANNELS.MASK) { + if ((mskA & (1 << bit)) >> bit) { + var valA = (analogSamples[sampleNr * 2] << 8) | analogSamples[sampleNr * 2 + 1]; + // Convert to mV + res.analogSamples[C.ANALOG_CHANNELS.MASK[bit][0]] = (valA * 1200) / 1023; + sampleNr++; + } + } } - } - return res; + return res; } -var parseAddress = exports.parseAddress = function(dec) { - return { - dec: dec, - hex: Tools.bArr2HexStr(dec) - } +var parseAddress = exports.parseAddress = function (dec) { + return { + dec: dec, + hex: Tools.bArr2HexStr(dec) + } } -var parseNodeIdentificationPayload = exports.parseNodeIdentificationPayload = function(payload) { - var res = {} - res.id = ""; +var parseNodeIdentificationPayload = exports.parseNodeIdentificationPayload = function (payload) { + var res = {} + res.id = ""; - res.remote16 = parseAddress(payload.splice(0, 2)); - res.remote64 = parseAddress(payload.splice(0, 8)); - while(payload[0] != 0x00) res.id += String.fromCharCode(payload.splice(0,1)[0]); - payload.splice(0,1); // Read 0x00 away - res.remoteParent16 = parseAddress(payload.splice(0, 2)); - res.deviceType = payload.splice(0, 1)[0]; - res.sourceEvent = payload.splice(0, 1)[0]; - res.nodeIdentificationPayload = payload.splice(0); - // res.status = payload.splice(0, 1)[0]; - // ... + res.remote16 = parseAddress(payload.splice(0, 2)); + res.remote64 = parseAddress(payload.splice(0, 8)); + while (payload[0] != 0x00) res.id += String.fromCharCode(payload.splice(0, 1)[0]); + payload.splice(0, 1); // Read 0x00 away + res.remoteParent16 = parseAddress(payload.splice(0, 2)); + res.deviceType = payload.splice(0, 1)[0]; + res.sourceEvent = payload.splice(0, 1)[0]; + res.nodeIdentificationPayload = payload.splice(0); + // res.status = payload.splice(0, 1)[0]; + // ... - return res; + return res; } From a795203b0f7337be36d6d0f562a30b70934e78fb Mon Sep 17 00:00:00 2001 From: lowi Date: Mon, 27 May 2013 13:18:59 +0200 Subject: [PATCH 122/140] pixel data parsing bug. used to be ascii (and thus 7 bit). this dropped one bit. data now parsed correctly --- index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 6f7c052..7de25e1 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,6 @@ var api = require("./lib/xbee-api.js"); var serialport = require("serialport"); var async = require('async'); var os = require('os'); -var StringDecoder = require('string_decoder').StringDecoder; -var decoder = new StringDecoder('utf-8'); var C = exports.C = api.Constants; var Tools = exports.Tools = api.tools; @@ -512,7 +510,13 @@ Node.prototype._onReceivePacket = function (packet) { // the original code used Buffer.toString('ascii') // but ascii is 7 bit only, so the most significant bit of each value got trahsed. - var data = decoder.write(packet.rawData); +// this is not elegant, but it works +// TODO: refactor data string creation from buffer + var data = ""; + for(var i = 0; i < packet.rawData.length; i+= 1) { + data += String.fromCharCode(packet.rawData[i]); + } + if (this.xbee.use_heartbeat) { this.refreshTimeout(); if (data === this.xbee.heartbeat_packet) return; From 7bcc200490b6eeb1b3f8b7f18ade130860d3654f Mon Sep 17 00:00:00 2001 From: Martin Jarvis Date: Fri, 31 May 2013 17:58:18 +0200 Subject: [PATCH 123/140] Update package.json Changed serialport dependency to allow latest 1.1.x version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1112cf..db1174b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ ], "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.0.x", + "serialport": "1.1.x", "async": "0.1.x" }, "engines": { From c339d5119cb6f4c4ef2ff4b685e41ad108158373 Mon Sep 17 00:00:00 2001 From: Martin Jarvis Date: Wed, 10 Jul 2013 18:14:09 +0100 Subject: [PATCH 124/140] Update package.json Added node 0.10.x to list of engnes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db1174b..39116be 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "async": "0.1.x" }, "engines": { - "node": "0.6.x | 0.8.x" + "node": "0.6.x | 0.8.x | 0.10.x" }, "repository": { "type": "git", From a941777d10567544fedbe788c200062288aaa44f Mon Sep 17 00:00:00 2001 From: Martin Jarvis Date: Tue, 23 Jul 2013 08:21:03 +0100 Subject: [PATCH 125/140] New conversion from hex string to byte array, plus better description comments --- lib/xbee-api.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/xbee-api.js b/lib/xbee-api.js index 1891013..a66d84c 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -7,6 +7,10 @@ exports = module.exports; var tools = exports.tools = {}; +// Converts a single decimal value to a Hex string, padding with leading zeros as required. +// e.g. 100 would become '64' and 8 would become '08' +// params d - decimal value to be converted +// padding - maximum number of leading zeros to add to the left side of the string tools.dec2Hex = function(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; @@ -18,6 +22,9 @@ tools.dec2Hex = function(d, padding) { return hex; } +// Converts a byte array to a Hex string. +// e.g. The 8 byte XBee 64 bit address [0x00, 0x13, 0xa2, 0x00, 0x40, 0x8b, 0x94, 0x37] would become '0013a200408b9437' +// params a - byte array to be converted tools.bArr2HexStr = function(a) { var s = ''; for(i in a) { @@ -34,9 +41,10 @@ tools.bArr2Str = function(a) { return s; } +// Converts a byte array like [3, 21] into a decimal value. +// e.g. [3,21] --> 3 * 256 + 21 = 789 +// params a - byte array to convert tools.bArr2Dec = function(a) { - // given a byte array like [3,21], convert to a decimal value. - // e.g. [3,21] --> 3 * 256 + 21 = 789 var r = 0; for (var i = 0; i < a.length; i++) { var power = a.length - i - 1; @@ -45,6 +53,9 @@ tools.bArr2Dec = function(a) { return r } +// Converts a decimal number to into an array of 2 byte values. +// params a - decimal number to convert +// m - length of array to return tools.dec2bArr = function(a, m) { var r = []; while (a > 0 || r.length < m) { @@ -54,6 +65,14 @@ tools.dec2bArr = function(a, m) { return r; } +// Converts a Hex string (typically an XBee address) into an array of 2 byte values. +// e.g. The XBee 64 bit address '0013a200408b9437' would become [0x00, 0x13, 0xa2, 0x00, 0x40, 0x8b, 0x94, 0x37] +// params str - Hex string to be converted, should contain hex characters pairs +tools.hexStr2bArr = function(str) { + return str.match(/../g).map( function(chars){ return parseInt(chars,16) }); +} + + // module-level variable for storing a frameId. // Gets incremented by 1 each time it's used, so that you can // tell which responses relate to which XBee commands From 1f957bab8151f6ee836f92022783917aaf9325eb Mon Sep 17 00:00:00 2001 From: stormboy Date: Thu, 25 Jul 2013 16:54:01 +1000 Subject: [PATCH 126/140] declare k in for loop --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7de25e1..a8382f7 100644 --- a/index.js +++ b/index.js @@ -265,7 +265,7 @@ XBee.prototype.readParameters = function (_done_cb) { // Using async to read parameters var res_stop = Object.keys(parameters).length; var results = {}; - for (k in parameters) { + for (var k in parameters) { parameters[k]((function (key) { return function (err, data) { if (err) return done(err, null); From 19eca4371aa2bed29408d8067d53e26bf617c83b Mon Sep 17 00:00:00 2001 From: stormboy Date: Thu, 25 Jul 2013 22:47:42 +1000 Subject: [PATCH 127/140] fixed misnamed var. res => packet --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index a8382f7..c60337d 100644 --- a/index.js +++ b/index.js @@ -172,18 +172,18 @@ XBee.prototype.init = function (cb) { // Modem Status self._onModemStatus = function (packet) { - if (res.status == C.MODEM_STATUS.JOINED_NETWORK) { + if (packet.status == C.MODEM_STATUS.JOINED_NETWORK) { self.emit("joinedNetwork", packet); - } else if (res.status == C.MODEM_STATUS.HARDWARE_RESET) { + } else if (packet.status == C.MODEM_STATUS.HARDWARE_RESET) { self.emit("hardwareReset", packet); - } else if (res.status == C.MODEM_STATUS.WATCHDOG_RESET) { + } else if (packet.status == C.MODEM_STATUS.WATCHDOG_RESET) { self.emit("watchdogReset", packet); - } else if (res.status == C.MODEM_STATUS.DISASSOCIATED) { + } else if (packet.status == C.MODEM_STATUS.DISASSOCIATED) { self.emit("disassociated", packet); - } else if (res.status == C.MODEM_STATUS.COORDINATOR_STARTED) { + } else if (packet.status == C.MODEM_STATUS.COORDINATOR_STARTED) { self.emit("coordinatorStarted", packet); } else { - console.warn("Modem status: ", C.MODEM_STATUS[res.status]); + console.warn("Modem status: ", C.MODEM_STATUS[packet.status]); } } From 2f82fadad70adad689e9521db630ac4917351166 Mon Sep 17 00:00:00 2001 From: stormboy Date: Fri, 26 Jul 2013 02:29:13 +1000 Subject: [PATCH 128/140] Allow parallel throughput of messages so that a message can be sent while a previous message is waiting for its response. --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index c60337d..4a50a21 100644 --- a/index.js +++ b/index.js @@ -211,13 +211,14 @@ XBee.prototype.init = function (cb) { self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); + var numParallel = 16; // maximum number of parallel messages to be sent. self._queue = async.queue(function (task, callback) { async.series(task.packets, function (err, data) { if (typeof task.cb === 'function') task.cb(err, data[data.length - 1]); callback(); }); - }, 1); - + }, numParallel); + return self; } From 795bc8a102a75a29c7783892db582d028d842ec6 Mon Sep 17 00:00:00 2001 From: stormboy Date: Fri, 26 Jul 2013 02:34:38 +1000 Subject: [PATCH 129/140] Max parallel messages is now configurable --- .gitignore | 1 + index.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index fc14622..f1d78e0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules *.swp .npmignore npm-debug.log +/.project diff --git a/index.js b/index.js index 4a50a21..db2f5b4 100644 --- a/index.js +++ b/index.js @@ -30,6 +30,9 @@ function XBee(options) { // How long (in ms) shall we wait before deciding that a transmit hasn't been successful? this.transmit_status_timeout = options.transmit_status_timeout || 1000; + + // maximum number of outbound messages to be waiting for a response + this.max_parallel_messages = options.max_parallel_messages || 1; if (options.api_mode) api.api_mode = options.api_mode; @@ -61,7 +64,7 @@ XBee.prototype._makeTask = function (packet) { //console.log("written data: " + packet.cbid + " : " + results); if (results != packet.data.length) return cb(new Error("Not all bytes written")); self.serial.once(packet.cbid, function (packet) { -// console.log("Got Respones: "+packet.cbid); + //console.log("Got Respones: "+packet.cbid); clearTimeout(timeout); var error = null; if (packet.commandStatus !== undefined) { @@ -211,13 +214,12 @@ XBee.prototype.init = function (cb) { self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); - var numParallel = 16; // maximum number of parallel messages to be sent. self._queue = async.queue(function (task, callback) { async.series(task.packets, function (err, data) { if (typeof task.cb === 'function') task.cb(err, data[data.length - 1]); callback(); }); - }, numParallel); + }, self.max_parallel_messages); return self; } From 9f2ffa7bb8ab2ea42dc8c5db355b58d7374f8783 Mon Sep 17 00:00:00 2001 From: stormboy Date: Sun, 4 Aug 2013 17:53:12 +1000 Subject: [PATCH 130/140] Allow incoming explicit messages to be received. --- index.js | 18 +++++++++++++----- lib/xbee-api.js | 3 ++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index db2f5b4..8a6594d 100644 --- a/index.js +++ b/index.js @@ -209,11 +209,19 @@ XBee.prototype.init = function (cb) { self.nodes[data.remote64.hex]._onDataSampleRx(data); } - self.serial.on(C.FRAME_TYPE.MODEM_STATUS, self._onModemStatus); - self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, self._onNodeIdentification); - self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, self._onReceivePacket); - self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, self._onDataSampleRx); - + self.serial.on(C.FRAME_TYPE.MODEM_STATUS, function(packet) { + self._onModemStatus(packet); + }); + self.serial.on(C.FRAME_TYPE.NODE_IDENTIFICATION, function(packet) { + self.self._onNodeIdentification(packet); + }); + self.serial.on(C.FRAME_TYPE.ZIGBEE_RECEIVE_PACKET, function(packet) { + self.self._onReceivePacket(packet); + }); + self.serial.on(C.FRAME_TYPE.ZIGBEE_IO_DATA_SAMPLE_RX, function(packet) { + self.self._onDataSampleRx(packet); + }); + self._queue = async.queue(function (task, callback) { async.series(task.packets, function (err, data) { if (typeof task.cb === 'function') task.cb(err, data[data.length - 1]); diff --git a/lib/xbee-api.js b/lib/xbee-api.js index f017b50..fcd461d 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -333,8 +333,9 @@ exports.packetBuilder = function () { C.FRAME_TYPE.AT_COMMAND_RESPONSE].indexOf(json.ft) >= 0) { evt += C.EVT_SEP+json.frameId; } - else if ( C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX == json.ft && 1 == json.receiveOptions ) { + else if ( C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX == json.ft && C.RECEIVE_OPTIONS.PACKET_ACKNOWLEDGED == json.receiveOptions ) { var txn = json.profileId == 0 ? json.rawData[0] : json.rawData[1]; + emitter.emit(evt, json); // emit C.FRAME_TYPE.ZIGBEE_EXPLICIT_RX event without txn. Some devices use PACKET_ACKNOWLEDGED when sending events for bound endpoints evt += C.EVT_SEP+txn; } //console.log(">>> "+C.FRAME_TYPE[json.ft]+" "+evt+" "+json.rawData); From 798908d8afd3e460a608ca2677ac06a73c369209 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 25 Aug 2013 16:16:51 +0200 Subject: [PATCH 131/140] updated version --- examples/nutshell/nutshell.js | 17 +++++++++++++++-- package.json | 6 +++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/nutshell/nutshell.js b/examples/nutshell/nutshell.js index 5e1eb8a..6129b43 100644 --- a/examples/nutshell/nutshell.js +++ b/examples/nutshell/nutshell.js @@ -1,9 +1,22 @@ +var util = require('util'); var XBee = require('../../index.js').XBee; var xbee = new XBee({ - port: 'COM3', // replace with yours + port: 'COM5', // replace with yours baudrate: 9600 // 9600 is default -}) +}).init(); + +xbee.on("initialized", function(params) { + console.log("XBee Parameters: %s", util.inspect(params)); + + xbee.discover(); + console.log("Node discovery starded..."); +}); + +xbee.on("discoveryEnd", function() { + console.log("...node discovery over"); +}); + // Add Set to your remote node var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0x2f,0xe4]); diff --git a/package.json b/package.json index 52e5f46..633cb9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.3", + "version": "0.3.4", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", @@ -31,11 +31,11 @@ ], "homepage": "https://github.com/jouz/svd-xbee", "dependencies": { - "serialport": "1.0.x", + "serialport": "1.1.x", "async": "0.1.x" }, "engines": { - "node": "0.6.x | 0.8.x" + "node": "0.6.x | 0.8.x | 0.10.x" }, "repository": { "type": "git", From 99bf5781d5faf3bd191376657d08fb26fe5eebf2 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Sun, 25 Aug 2013 18:58:36 +0200 Subject: [PATCH 132/140] fixed readme example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0be79ba..2531483 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ var XBee = require('svd-xbee'); var xbee = new XBee({ port: 'COM3', // replace with yours baudrate: 9600 // 9600 is default -}) +}).init(); var robot = xbee.addNode([0x00,0x13,0xa2,0x00,0x40,0x61,0xaa,0xe2]); From 909bda03920b1628f5e073d13e1acd9e32afc74b Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 28 Aug 2013 21:20:52 +0200 Subject: [PATCH 133/140] set remote16 to "unknown" addr if not passed on manual create --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 1c90193..79c9349 100644 --- a/index.js +++ b/index.js @@ -270,10 +270,12 @@ XBee.prototype.readParameters = function(_done_cb) { // Add a node by hand. XBee.prototype.addNode = function(remote64, remote16, parser) { var self = this; - //var remote16 = [0xff,0xfe]; // Unknown if (!remote16 instanceof Array) { parser = remote16; + } else if (!remote16) { + remote16 = [0xff,0xfe]; // Unknown } + var node_data = { remote16: { dec: remote16, hex: Tools.bArr2HexStr(remote16) }, remote64: { dec: remote64, hex: Tools.bArr2HexStr(remote64) } From b63d3be0734a2cb3a81129bc70ea9c83a30af106 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Wed, 28 Aug 2013 23:10:53 +0200 Subject: [PATCH 134/140] updated npm version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 633cb9b..8478449 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.4", + "version": "0.3.5", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", From d27993926164c600ae575c065289408204c1f009 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 6 Sep 2013 20:56:17 +0200 Subject: [PATCH 135/140] added note to readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2531483..35d2945 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +## NOTE! +Check out [xbee-api](http://github.com/jouz/xbee-api). It separates out the API component and is aimed at being a more solid and tested module for working with XBees. New higher level modules based on [xbee-api](http://github.com/jouz/xbee-api) are in development here: [xbee-stream](http://github.com/jouz/xbee-stream) and here [xbee-nodes](http://github.com/jouz/xbee-nodes). + # SVD-XBEE [Digi's xbee modules](http://www.digi.com/xbee) are good for quickly building low power wireless networks. They can be used to send/receive text data from serial ports of different devices. XBees can also be used alone for their on board digital and analog I/O capabilities. From a57e8faddec12f8dc21627736f434b2fdd53a2a6 Mon Sep 17 00:00:00 2001 From: Igorshp Date: Thu, 9 Jan 2014 01:03:39 +0000 Subject: [PATCH 136/140] Bugfix for xbee.broadcast send() requires remote64 to have .dec attribute --- index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 79c9349..b6da929 100644 --- a/index.js +++ b/index.js @@ -311,8 +311,10 @@ XBee.prototype.discover = function(cb) { } XBee.prototype.broadcast = function(data, cb) { - var remote64 = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; - var remote16 = [0xff,0xfe]; + var remote64 = {}; + var remote16 = {}; + remote64.dec = [0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff]; + remote16.dec = [0xff,0xfe]; this.send(data, remote64, remote16, cb); } From 87033c0cebcbcd3efe845ca4ed0291d474d3d26a Mon Sep 17 00:00:00 2001 From: Igor Shpakov Date: Wed, 26 Feb 2014 00:27:25 +0000 Subject: [PATCH 137/140] added packet to io event io event doesn't convey any info on the node. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index b6da929..22f5937 100644 --- a/index.js +++ b/index.js @@ -509,7 +509,7 @@ Node.prototype._onReceivePacket = function(packet) { Node.prototype._onDataSampleRx = function(packet) { var sample = parseIOSample(packet.ioSample); - this.emit('io', sample); + this.emit('io', sample, packet); if (this.xbee.use_heartbeat) { this.refreshTimeout(); } From fa00e978a382d9bc3289fb0fb534e891b2e85b16 Mon Sep 17 00:00:00 2001 From: Jan Kolkmeier Date: Fri, 28 Feb 2014 21:46:42 +0100 Subject: [PATCH 138/140] npm update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8478449..96082bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svd-xbee", - "version": "0.3.5", + "version": "0.3.6", "description": "A more high level fork of Richard Morrison's node-xbee", "author": { "name": "Jan Kolkmeier", From 80ab9a0de0becc744c8b369bee6a0e8d4b51962d Mon Sep 17 00:00:00 2001 From: "Brian K. Smith" Date: Thu, 13 Mar 2014 12:49:23 -0500 Subject: [PATCH 139/140] Bug fix for an issue where the parser tries to start a new packet if the checksum equals 0x7E. --- lib/xbee-api.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/xbee-api.js b/lib/xbee-api.js index a66d84c..63cb0ad 100644 --- a/lib/xbee-api.js +++ b/lib/xbee-api.js @@ -293,6 +293,7 @@ exports.packetBuilder = function () { var running_total = 0; var checksum = -1; var escape_next = false; + var escaped = false; return function(emitter, buffer) { // Collecting data. @@ -307,12 +308,15 @@ exports.packetBuilder = function () { if (escape_next) { b = 0x20 ^ b; escape_next = false; + escaped = true; } packpos += 1; - // Detected start of packet. - if (b == C.START_BYTE) { + // Detect start of packet, ONLY if this byte wasn't an escaped 0x7E (C.START_BYTE). The start + // delimeter will only be escaped if it is within the body of a packet, thus we shouldn't + // start a new packet if this value was escaped. + if (b == C.START_BYTE && !escaped) { packpos = 0; packlen = 0; running_total = 0; @@ -321,6 +325,8 @@ exports.packetBuilder = function () { escape_next = false; } + escaped = false; + if (packpos == 1) packlen += b << 8; // most significant bit of the length if (packpos == 2) packlen += b; // least significant bit of the length if ((packlen > 0) && (packpos > 2)) { From 1b6ff340e71a4dc8002ca6bb8830ec93c1ca3af0 Mon Sep 17 00:00:00 2001 From: stormboy Date: Fri, 23 May 2014 11:24:55 +1000 Subject: [PATCH 140/140] use addNode(), like other cases, instead of _createNode --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2eb3ad1..dff39ec 100644 --- a/index.js +++ b/index.js @@ -127,8 +127,9 @@ XBee.prototype._remoteAT = function (cmd, remote64, remote16, val, _cb) { XBee.prototype._handleNodeIdentification = function (node) { if (!this.nodes[node.remote64.hex]) { - this.nodes[node.remote64.hex] = this._createNode(node); - this.emit("newNodeDiscovered", this.nodes[node.remote64.hex]); + var node = self.addNode(node.remote64.dec, node.remote16.dec, this.data_parser); + //this.nodes[node.remote64.hex] = this._createNode(node); + this.emit("newNodeDiscovered", node); } else { // update 16-bit address, as it may change during reconnects. this.nodes[node.remote64.hex].remote16 = node.remote16;