Skip to content

Commit 9d89ca6

Browse files
committed
more robust serial conn establishment
Instead of trying to establish the serial connection in the Board constructor, delegate this to a separate `setup()`. (**API change**!) This allows the user to add listeners to the Board’s `error` event, which signifies that the connection could not be established. (This was already intended, but wasn't possible, as far as I can tell.) Also displays a more intuitive error message.
1 parent 546ec6b commit 9d89ca6

2 files changed

Lines changed: 82 additions & 54 deletions

File tree

lib/board.js

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
var events = require('events'),
32
child = require('child_process'),
43
util = require('util'),
@@ -7,24 +6,37 @@ var events = require('events'),
76

87
/*
98
* The main Arduino constructor
10-
* Connect to the serial port and bind
9+
*
10+
* [NEW] Does *not* open a serial connection. Add a `setup()` call to do so.
11+
*
12+
* This allows the user to add a listener for error events that occur
13+
* during the serial connection attempts.
1114
*/
1215
var Board = function (options) {
1316
this.log('info', 'initializing');
14-
this.debug = options && options.debug || false;
15-
this.device = options && options.device || 'usb|ttyACM*|ttyS0';
17+
this.debug = options && options.debug || false;
18+
this.devregex = options && options.devregex || 'usb|ttyACM*|ttyS0';
1619
this.baudrate = options && options.baudrate || 115200;
1720
this.writeBuffer = [];
21+
}
22+
23+
/*
24+
* EventEmitter, I choose you!
25+
*/
26+
util.inherits(Board, events.EventEmitter);
1827

28+
/*
29+
* Establish the serial connection
30+
*
31+
* Tries to open a serial connection with `connectSerial()`.
32+
* If successful, the connection is initialized ("clearing bytes", debug mode).
33+
* If unsuccessful, error event is emitted; if no listeners: error is thrown.
34+
*/
35+
Board.prototype.setup = function() {
1936
var self = this;
20-
this.detect(function (err, serial) {
21-
if (err) {
22-
if(self.listeners('error').length)
23-
self.emit('error', err);
24-
else
25-
throw new Error(err);
26-
}else{
27-
self.serial = serial;
37+
this.connectSerial(function(found) {
38+
if (found) {
39+
self.serial = found;
2840
self.emit('connected');
2941

3042
self.log('info', 'binding serial events');
@@ -56,52 +68,69 @@ var Board = function (options) {
5668

5769
self.emit('ready');
5870
}, 500);
71+
} else {
72+
msg = "Could not establish Serial connection to Arduino.";
73+
if (self.listeners('error').length > 0) {
74+
self.emit('error', msg);
75+
} else {
76+
throw new Error(msg);
77+
}
5978
}
6079
});
6180
}
6281

6382
/*
64-
* EventEmitter, I choose you!
83+
* (Tries to) Open serial connection with Arduino (formerly named `detect()`)
84+
*
85+
* Loops through devices matching `devregex` and naively tries to open a serial
86+
* connection with each until one succeeds.
87+
* This should really message the device and wait for a correct response to
88+
* ensure that we're actually connected to an Arduino running `duino`. FIXME
89+
*
90+
* Returns false if no serial connection could be established.
6591
*/
66-
util.inherits(Board, events.EventEmitter);
67-
68-
/*
69-
* Detect an Arduino board
70-
* Loop through all USB devices and try to connect
71-
* This should really message the device and wait for a correct response
72-
*/
73-
Board.prototype.detect = function (callback) {
92+
Board.prototype.connectSerial = function (callback) {
7493
this.log('info', 'attempting to find Arduino board');
7594
var self = this;
76-
child.exec('ls /dev | grep -E "'+ self.device +'"', function(err, stdout, stderr){
77-
var usb = stdout.slice(0, -1).split('\n'),
78-
found = false,
79-
err = null,
80-
possible, temp;
81-
82-
while ( usb.length ) {
83-
possible = usb.pop();
84-
85-
if (possible.slice(0, 2) !== 'cu') {
86-
try {
87-
temp = new serial.SerialPort('/dev/' + possible, {
88-
baudrate: self.baudrate,
89-
parser: serial.parsers.readline('\n')
90-
});
91-
} catch (e) {
92-
err = e;
93-
}
94-
if (!err) {
95-
found = temp;
96-
self.log('info', 'found board at ' + temp.port);
97-
break;
98-
} else {
99-
err = new Error('Could not find Arduino');
100-
}
101-
}
95+
child.exec('ls /dev | grep -E "'+ self.devregex +'"', function(err, stdout, stderr){
96+
97+
function skipUnwanted(e) {
98+
return (e !== '') && (e.slice(0, 2) !== 'cu');
10299
}
103100

104-
callback(err, found);
101+
var devices = stdout.slice(0, -1).split('\n').filter(skipUnwanted),
102+
device,
103+
candidate;
104+
105+
// loop over list of possible Arduinos
106+
// do not stop (even/especially on error, that's the point!) until
107+
// - we run out of options, or
108+
// - a serial connection is established
109+
var success = devices.some(function(device) {
110+
try {
111+
self.log('debug', 'attempting to open serial conn.: ' + device);
112+
candidate = new serial.SerialPort('/dev/' + device, {
113+
baudrate: self.baudrate,
114+
parser: serial.parsers.readline('\n')
115+
});
116+
117+
// connection succeeded, though we don't test whether it's an Arduino
118+
self.log('info', 'found board at /dev/' + device);
119+
return true;
120+
121+
} catch (e) {
122+
// ignore error and cont.
123+
self.log('warning', 'error while establishing serial connection with'
124+
+ '/dev/' + device + '; trying next');
125+
return false;
126+
}
127+
});
128+
129+
if (success) {
130+
callback(candidate);
131+
} else {
132+
callback(false);
133+
}
105134
});
106135
}
107136

@@ -137,18 +166,17 @@ Board.prototype.write = function (m) {
137166
}
138167
}
139168

140-
/*
141-
* Add a 0 to the front of a single-digit pin number
142-
*/
169+
// Add a 0 to the front of a single-digit pin number
143170
Board.prototype.normalizePin = function (pin) {
144171
return this.lpad( 2, '0', pin );
145172
}
146173

174+
// Left-pad values with 0s so it has three digits.
147175
Board.prototype.normalizeVal = function(val) {
148-
return this.lpad( 3, '0', val );
176+
return this.lpad( 3, '0', val );
149177
}
150178

151-
//
179+
// Left-pad `str` with `chr` and return the last `len` digits
152180
Board.prototype.lpad = function(len, chr, str) {
153181
return (Array(len + 1).join(chr || ' ') + str).substr(-len);
154182
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
],
1717
"name": "duino",
1818
"description": "Arduino framework for mad scientists",
19-
"version": "0.0.11",
19+
"version": "0.1.0",
2020
"keywords": [
2121
"arduino",
2222
"serial",

0 commit comments

Comments
 (0)