-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserialport.js
217 lines (208 loc) · 5.87 KB
/
serialport.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
var child_process = require("child_process")
, stream = require("stream")
, fs = require("fs")
, tty = require("tty")
, os = require("os")
, util = require("util");
/* Implements a Duplex stream for reading from and writing to a serial port.
This implementation uses `stty` to initialize the stream; then, it simply reads
from or writes to the serial port using the "tty" Node.js library.
The options object allows you to pass named options to the serial port during
initialization. The valid attributes for the options object are the following:
- serialPort - the serial port device name (required)
- baudRate - Baud Rate (defaults to 9600)
- dataBits - Data Bits - Must be one of: 8, 7, 6, or 5. (defaults to 8)
- stopBits - Stop Bits - Must be one of: 1 or 2. (defaults to 1)
- parity - Parity - Must be one of: 'none', 'odd', 'even',
'mark', 'space' (defaults to 'none')
- sttyPath - the path to the `stty` command (defaults to /bin/stty)
- initTimeout - maximum initialization duration (defaults to 10 seconds);
set to `null` to allow an infinite amount of time
- sttyArgs - an array of additional arguments to pass to the `stty` command
*/
function SerialPort(opts) {
stream.Duplex.call(this, {
//"allowHalfOpen": true
});
this.serialPort = opts.serialPort;
this.baudRate = opts.baudRate || 9600;
this.dataBits = opts.dataBits || 8;
this.stopBits = opts.stopBits || 1;
this.parity = opts.parity || 'none';
this.sttyPath = opts.sttyPath || "/bin/stty";
if(opts.initTimeout === undefined) {
opts.initTimeout = 10000;
}
this.initTimeout = opts.initTimeout;
this.sttyArgs = opts.sttyArgs || [];
this._fd = null;
this._readStream = null;
this._writeStream = null;
}
util.inherits(SerialPort, stream.Duplex);
module.exports = SerialPort;
SerialPort.prototype.initialize = function(cb) {
var self = this;
//If there is no callback, then just emit errors
if(typeof cb !== "function") {
cb = function(err) {
if(err) {
self.emit("error", err);
}
};
}
//Check to see if initialization is complete
if(self._readStream || self._writeStream || self._fd != null) {
return cb(new Error("SerialPort: Please close the port before reinitializing.") );
}
//Device name
if(self.serialPort == null) {
return cb(new Error("SerialPort: Path to serial port device was not specified.") );
}
var args = [];
if(os.platform() === "linux") {
args.push("-F");
}
else if(os.platform() === "darwin") {
args.push("-f");
}
else {
throw new Error("Operating system not supported.");
}
//Set a few sane options
args.push(self.serialPort, "raw", "-onlcr", "-iexten", "-echo",
"-echoe", "-echok", "-echoctl", "-echoke");
//Baud rate
args.push("speed", self.baudRate);
//Data bits
if(self.dataBits < 5 || self.dataBits > 8) {
return cb(new Error("SerialPort: Data byte must be between 5 and 8 bits.") );
}
args.push("cs" + self.dataBits);
//Stop bits
if(self.stopBits === 2) {
args.push("cstopb");
}
else if(self.stopBits === 1) {
args.push("-cstopb");
}
else {
return cb(new Error("SerialPort: Number of stop bits must be 1 or 2.") );
}
//Parity
switch(self.parity) {
case "none":
args.push("-parenb");
break;
case "odd":
args.push("parenb", "-parext", "parodd");
break;
case "even":
args.push("parenb", "-parext", "-parodd");
break;
case "mark":
args.push("parenb", "parext", "parodd");
break;
case "space":
args.push("parenb", "parext", "-parodd");
break;
default:
return cb(new Error("SerialPort: Invalid parity: " + self.parity) );
}
//sttyArgs
if(self.sttyArgs instanceof Array){
args = args.concat(self.sttyArgs);
}
//Get the file descriptors for reading/writing the SerialPort
fs.open(self.serialPort, "r+", function(err, readFd) {
self._fd = readFd;
if(err) {
return cb(err);
}
//Spawn stty process
var child = child_process.spawn(self.sttyPath, args),
initTimer;
//Set a timer just in case the process hangs
if(self.initTimeout != null) {
initTimer = setTimeout(function() {
child.removeListener("exit", exitListener);
child.kill("SIGKILL");
fs.closeSync(readFd);
cb(new Error("SerialPort: stty did not complete in a timely manner") );
}, self.initTimeout);
}
child.on("exit", exitListener);
function exitListener(code, signal) {
clearTimeout(initTimer);
if(code === 0) {
openSerialPortDevice(readFd);
}
else {
fs.closeSync(readFd);
cb(new Error("SerialPort: stty returned exit code " + code) );
}
}
})
//Once the process exits successfully, we call this function
function openSerialPortDevice(readFd) {
//Save TTY streams
self._readStream = new tty.ReadStream(readFd);
self._readStream.setRawMode(true);
self._writeStream = self._readStream;
//Setup error handlers
self._readStream.on("error", function(err) {
self.emit("error", err);
});
//Setup read event handlers
self._readStream.on("data", function(chunk) {
self.push(chunk);
});
self._readStream.on("end", function() {
self.push(null);
});
self._readStream.on("close", function() {
self.emit("close");
});
//Emit open event
self.emit("open",self._readStream);
}
};
SerialPort.prototype._read = function(size) {
return;
};
SerialPort.prototype._write = function(chunk, encoding, cb) {
whenOpen(this, function() {
//cb is called once data is flushed
this._writeStream.write(chunk, encoding, cb);
});
};
SerialPort.prototype.close = function(cb) {
var self = this;
//If there is no callback, then just emit errors
if(typeof cb !== "function") {
cb = function(err) {
if(err) {
self.emit("error", err);
}
};
}
//Close both ReadStream and WriteStream Sockets
if(self._readStream != null) {
self._readStream.end();
}
self._readStream = self._writeStream = null;
if(self._fd != null) {
fs.close(self._fd, cb);
self._fd = null;
} else {
cb(null);
}
};
function whenOpen(self, cb) {
if(self._writeStream == null) {
self.on("open", cb);
}
else {
cb.call(self);
}
}