2 * Copyright 2013,2014 IBM Corp.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 module.exports = function(RED) {
19 var settings = RED.settings;
20 var events = require("events");
21 var util = require("util");
22 var serialp = require("serialport");
23 var bufMaxSize = 32768; // Max serial buffer size, for inputs...
25 // TODO: 'serialPool' should be encapsulated in SerialPortNode
27 function SerialPortNode(n) {
28 RED.nodes.createNode(this,n);
29 this.serialport = n.serialport;
30 this.newline = n.newline;
31 this.addchar = n.addchar || "false";
32 this.serialbaud = parseInt(n.serialbaud) || 57600;
33 this.databits = parseInt(n.databits) || 8;
34 this.parity = n.parity || "none";
35 this.stopbits = parseInt(n.stopbits) || 1;
36 this.bin = n.bin || "false";
37 this.out = n.out || "char";
39 RED.nodes.registerType("serial-port",SerialPortNode);
41 function SerialOutNode(n) {
42 RED.nodes.createNode(this,n);
43 this.serial = n.serial;
44 this.serialConfig = RED.nodes.getNode(this.serial);
46 if (this.serialConfig) {
48 node.port = serialPool.get(this.serialConfig.serialport,
49 this.serialConfig.serialbaud,
50 this.serialConfig.databits,
51 this.serialConfig.parity,
52 this.serialConfig.stopbits,
53 this.serialConfig.newline);
55 if (node.serialConfig.addchar == "true") {
56 node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0");
58 node.on("input",function(msg) {
59 var payload = msg.payload;
60 if (!Buffer.isBuffer(payload)) {
61 if (typeof payload === "object") {
62 payload = JSON.stringify(payload);
64 payload = payload.toString();
66 payload += node.addCh;
67 } else if (node.addCh !== "") {
68 payload = Buffer.concat([payload,new Buffer(node.addCh)]);
70 node.port.write(payload,function(err,res) {
76 node.port.on('ready', function() {
77 node.status({fill:"green",shape:"dot",text:"connected"});
79 node.port.on('closed', function() {
80 node.status({fill:"red",shape:"ring",text:"not connected"});
83 this.error("missing serial config");
86 this.on("close", function(done) {
87 if (this.serialConfig) {
88 serialPool.close(this.serialConfig.serialport,done);
94 RED.nodes.registerType("serial out",SerialOutNode);
97 function SerialInNode(n) {
98 RED.nodes.createNode(this,n);
99 this.serial = n.serial;
100 this.serialConfig = RED.nodes.getNode(this.serial);
102 if (this.serialConfig) {
106 if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); }
107 else { buf = new Buffer(Number(node.serialConfig.newline)); }
109 node.status({fill:"grey",shape:"dot",text:"unknown"});
110 node.port = serialPool.get(this.serialConfig.serialport,
111 this.serialConfig.serialbaud,
112 this.serialConfig.databits,
113 this.serialConfig.parity,
114 this.serialConfig.stopbits,
115 this.serialConfig.newline
119 if (node.serialConfig.newline.substr(0,2) == "0x") {
120 splitc = new Buffer([parseInt(node.serialConfig.newline)]);
122 splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"));
125 this.port.on('data', function(msg) {
126 // single char buffer
127 if ((node.serialConfig.newline === 0)||(node.serialConfig.newline === "")) {
128 if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); }
129 else { node.send({"payload": new Buffer([msg])}); }
132 // do the timer thing
133 if (node.serialConfig.out === "time") {
139 node.tout = setTimeout(function () {
141 var m = new Buffer(i+1);
143 if (node.serialConfig.bin !== "bin") { m = m.toString(); }
144 node.send({"payload": m});
146 }, node.serialConfig.newline);
151 // count bytes into a buffer...
152 else if (node.serialConfig.out === "count") {
155 if ( i >= parseInt(node.serialConfig.newline)) {
156 var m = new Buffer(i);
158 if (node.serialConfig.bin !== "bin") { m = m.toString(); }
159 node.send({"payload":m});
164 // look to match char...
165 else if (node.serialConfig.out === "char") {
168 if ((msg === splitc[0]) || (i === bufMaxSize)) {
169 var m = new Buffer(i);
171 if (node.serialConfig.bin !== "bin") { m = m.toString(); }
172 node.send({"payload":m});
177 else { console.log("Should never get here"); }
180 this.port.on('ready', function() {
181 node.status({fill:"green",shape:"dot",text:"connected"});
183 this.port.on('closed', function() {
184 node.status({fill:"red",shape:"ring",text:"not connected"});
187 this.error("missing serial config");
190 this.on("close", function(done) {
191 if (this.serialConfig) {
192 serialPool.close(this.serialConfig.serialport,done);
198 RED.nodes.registerType("serial in",SerialInNode);
201 var serialPool = function() {
202 var connections = {};
204 get:function(port,baud,databits,parity,stopbits,newline,callback) {
206 if (!connections[id]) {
207 connections[id] = function() {
209 _emitter: new events.EventEmitter(),
213 on: function(a,b) { this._emitter.on(a,b); },
214 close: function(cb) { this.serial.close(cb); },
215 write: function(m,cb) { this.serial.write(m,cb); },
217 //newline = newline.replace("\\n","\n").replace("\\r","\r");
218 var setupSerial = function() {
219 //if (newline == "") {
220 obj.serial = new serialp.SerialPort(port,{
225 parser: serialp.parsers.raw
226 },true, function(err, results) { if (err) { obj.serial.emit('error',err); } });
229 // obj.serial = new serialp.SerialPort(port,{
231 // databits: databits,
233 // stopbits: stopbits,
234 // parser: serialp.parsers.readline(newline)
235 // },true, function(err, results) { if (err) obj.serial.emit('error',err); });
237 obj.serial.on('error', function(err) {
238 util.log("[serial] serial port "+port+" error "+err);
239 obj._emitter.emit('closed');
240 obj.tout = setTimeout(function() {
242 }, settings.serialReconnectTime);
244 obj.serial.on('close', function() {
246 util.log("[serial] serial port "+port+" closed unexpectedly");
247 obj._emitter.emit('closed');
248 obj.tout = setTimeout(function() {
250 }, settings.serialReconnectTime);
253 obj.serial.on('open',function() {
254 util.log("[serial] serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits);
255 if (obj.tout) { clearTimeout(obj.tout); }
256 //obj.serial.flush();
257 obj._emitter.emit('ready');
259 obj.serial.on('data',function(d) {
260 //console.log(Buffer.isBuffer(d),d.length,d);
261 //if (typeof d !== "string") {
262 // //d = d.toString();
263 for (var z=0; z<d.length; z++) {
264 obj._emitter.emit('data',d[z]);
268 // obj._emitter.emit('data',d);
271 obj.serial.on("disconnect",function() {
272 util.log("[serial] serial port "+port+" gone away");
279 return connections[id];
281 close: function(port,done) {
282 if (connections[port]) {
283 if (connections[port].tout != null) {
284 clearTimeout(connections[port].tout);
286 connections[port]._closing = true;
288 connections[port].close(function() {
289 util.log("[serial] serial port closed");
294 delete connections[port];
302 RED.httpAdmin.get("/serialports",function(req,res) {
303 serialp.list(function (err, ports) {
304 //console.log(JSON.stringify(ports));
305 res.writeHead(200, {'Content-Type': 'text/plain'});
306 res.write(JSON.stringify(ports));