Skip to content

Commit 8c43697

Browse files
committed
Unify the serialport.list output
- [osx] remove the 0x from productID and locationId and vendorId - [linux] remove the starting 0x from any values, small refactor - [linux] Fixup output using the hex encoded fields - [windows] Grab the serial number from the pnp id - Isolates list to it's own platform files (c++) - Better ensure our serialports fields are present
1 parent 8b07a48 commit 8c43697

21 files changed

+793
-550
lines changed

.docs/README.hbs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -405,15 +405,15 @@ $ serialport-list -h
405405

406406

407407
$ serialport-list
408-
/dev/cu.Bluetooth-Incoming-Port
409-
/dev/cu.usbmodem1421 Arduino (www.arduino.cc)
408+
/dev/tty.Bluetooth-Incoming-Port
409+
/dev/tty.usbmodem1421 Arduino (www.arduino.cc)
410410

411411
$ serialport-list -f json
412-
[{"comName":"/dev/cu.Bluetooth-Incoming-Port"},{"comName":"/dev/cu.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"0x14200000","vendorId":"0x2341","productId":"0x0043"}]
412+
[{"comName":"/dev/tty.Bluetooth-Incoming-Port"},{"comName":"/dev/tty.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"14200000","vendorId":"2341","productId":"0043"}]
413413

414414
$ serialport-list -f jsonline
415-
{"comName":"/dev/cu.Bluetooth-Incoming-Port"}
416-
{"comName":"/dev/cu.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"0x14200000","vendorId":"0x2341","productId":"0x0043"}
415+
{"comName":"/dev/tty.Bluetooth-Incoming-Port"}
416+
{"comName":"/dev/tty.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"14200000","vendorId":"2341","productId":"0043"}
417417
```
418418

419419
### Serial Port Terminal
@@ -439,40 +439,40 @@ $ serialport-term -h
439439
--echo --localecho Print characters as you type them
440440

441441
$ serialport-term -l
442-
/dev/cu.Bluetooth-Incoming-Port
443-
/dev/cu.usbmodem1421 Arduino (www.arduino.cc)
442+
/dev/tty.Bluetooth-Incoming-Port
443+
/dev/tty.usbmodem1421 Arduino (www.arduino.cc)
444444
```
445445

446446
### Serial Port Repl
447447
`serialport-repl` provides a nodejs repl for working with serialport. This is valuable when debugging.
448448

449449
You can make use of the `serialport-repl` command with;
450450
```bash
451-
serialport-repl # to auto detect an arduino
452-
serialport-repl /path/name # to connect to a specific port
451+
$ serialport-repl # to auto detect an arduino
452+
$ serialport-repl /dev/tty.usbmodem1421 # to connect to a specific port
453453
```
454454

455455
It will load a serialport object with debugging turned on.
456456
```
457457
serialport:binding:auto-detect loading DarwinBinding +0ms
458-
port = SerialPort("/path/name", { autoOpen: false })
458+
port = SerialPort("/dev/tty.usbmodem1421", { autoOpen: false })
459459
globals { SerialPort, portName, port }
460460
> SerialPort.list()
461461
serialport:main .list +6s
462-
[ { comName: '/path/name',
463-
manufacturer: undefined,
464-
serialNumber: undefined,
462+
[ { comName: '/dev/tty.usbmodem1421',
463+
manufacturer: 'Arduino (www.arduino.cc)',
464+
serialNumber: '752303138333518011C1',
465465
pnpId: undefined,
466-
locationId: undefined,
467-
vendorId: undefined,
468-
productId: undefined } ]
466+
locationId: '14200000',
467+
vendorId: '2341',
468+
productId: '0043' } ]
469469
> port.write('Calling all Autobots!')
470470
true
471471
> port.read()
472472
serialport:main _read queueing _read for after open +1m
473473
null
474474
> port.open()
475-
serialport:main opening path: serialport-repl +30s
475+
serialport:main opening path: /dev/tty.usbmodem1421 +30s
476476
serialport:bindings open +1ms
477477
```
478478

README.md

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,8 @@ parser.on('data', console.log);
788788
#### `SerialPort.list([callback])` ⇒ <code>Promise</code>
789789
Retrieves a list of available serial ports with metadata. Only the `comName` is guaranteed. If unavailable the other fields will be undefined. The `comName` is either the path or an identifier (eg `COM1`) used to open the SerialPort.
790790

791+
We make an effort to identify the hardware attached and have consistent results between systems. Linux and OS X are mostly consistent. Windows relies on 3rd party device drivers for the information and is unable to guarantee the information. On windows If you have a USB connected device can we provide a serial number otherwise it will be `undefined`. The `pnpId` and `locationId` are not the same or present on all systems. The examples below were run with the same Arduino Uno.
792+
791793
**Kind**: static method of [<code>SerialPort</code>](#exp_module_serialport--SerialPort)
792794
**Returns**: <code>Promise</code> - Resolves with the list of available serial ports.
793795

@@ -797,17 +799,38 @@ Retrieves a list of available serial ports with metadata. Only the `comName` is
797799

798800
**Example**
799801
```js
800-
// example port information
802+
// OSX example port
801803
{
802-
comName: '/dev/cu.usbmodem1421',
804+
comName: '/dev/tty.usbmodem1421',
803805
manufacturer: 'Arduino (www.arduino.cc)',
804-
serialNumber: '757533138333964011C1',
806+
serialNumber: '752303138333518011C1',
805807
pnpId: undefined,
806-
locationId: '0x14200000',
807-
vendorId: '0x2341',
808-
productId: '0x0043'
808+
locationId: '14500000',
809+
productId: '0043',
810+
vendorId: '2341'
811+
}
812+
813+
// Linux example port
814+
{
815+
comName: '/dev/ttyACM0',
816+
manufacturer: 'Arduino (www.arduino.cc)',
817+
serialNumber: '752303138333518011C1',
818+
pnpId: 'usb-Arduino__www.arduino.cc__0043_752303138333518011C1-if00',
819+
locationId: undefined,
820+
productId: '0043',
821+
vendorId: '2341'
809822
}
810823

824+
// Windows example port
825+
{
826+
comName: 'COM3',
827+
manufacturer: 'Arduino LLC (www.arduino.cc)',
828+
serialNumber: '752303138333518011C1',
829+
pnpId: 'USB\\VID_2341&PID_0043\\752303138333518011C1',
830+
locationId: 'Port_#0003.Hub_#0001',
831+
productId: '0043',
832+
vendorId: '2341'
833+
}
811834
```
812835

813836
```js
@@ -820,6 +843,7 @@ SerialPort.list(function (err, ports) {
820843
console.log(port.manufacturer);
821844
});
822845
});
846+
823847
// promise approach
824848
SerialPort.list()
825849
.then(ports) {...});
@@ -1145,15 +1169,15 @@ $ serialport-list -h
11451169

11461170

11471171
$ serialport-list
1148-
/dev/cu.Bluetooth-Incoming-Port
1149-
/dev/cu.usbmodem1421 Arduino (www.arduino.cc)
1172+
/dev/tty.Bluetooth-Incoming-Port
1173+
/dev/tty.usbmodem1421 Arduino (www.arduino.cc)
11501174

11511175
$ serialport-list -f json
1152-
[{"comName":"/dev/cu.Bluetooth-Incoming-Port"},{"comName":"/dev/cu.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"0x14200000","vendorId":"0x2341","productId":"0x0043"}]
1176+
[{"comName":"/dev/tty.Bluetooth-Incoming-Port"},{"comName":"/dev/tty.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"14200000","vendorId":"2341","productId":"0043"}]
11531177

11541178
$ serialport-list -f jsonline
1155-
{"comName":"/dev/cu.Bluetooth-Incoming-Port"}
1156-
{"comName":"/dev/cu.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"0x14200000","vendorId":"0x2341","productId":"0x0043"}
1179+
{"comName":"/dev/tty.Bluetooth-Incoming-Port"}
1180+
{"comName":"/dev/tty.usbmodem1421","manufacturer":"Arduino (www.arduino.cc)","serialNumber":"752303138333518011C1","locationId":"14200000","vendorId":"2341","productId":"0043"}
11571181
```
11581182

11591183
### Serial Port Terminal
@@ -1179,40 +1203,40 @@ $ serialport-term -h
11791203
--echo --localecho Print characters as you type them
11801204

11811205
$ serialport-term -l
1182-
/dev/cu.Bluetooth-Incoming-Port
1183-
/dev/cu.usbmodem1421 Arduino (www.arduino.cc)
1206+
/dev/tty.Bluetooth-Incoming-Port
1207+
/dev/tty.usbmodem1421 Arduino (www.arduino.cc)
11841208
```
11851209

11861210
### Serial Port Repl
11871211
`serialport-repl` provides a nodejs repl for working with serialport. This is valuable when debugging.
11881212

11891213
You can make use of the `serialport-repl` command with;
11901214
```bash
1191-
serialport-repl # to auto detect an arduino
1192-
serialport-repl /path/name # to connect to a specific port
1215+
$ serialport-repl # to auto detect an arduino
1216+
$ serialport-repl /dev/tty.usbmodem1421 # to connect to a specific port
11931217
```
11941218

11951219
It will load a serialport object with debugging turned on.
11961220
```
11971221
serialport:binding:auto-detect loading DarwinBinding +0ms
1198-
port = SerialPort("/path/name", { autoOpen: false })
1222+
port = SerialPort("/dev/tty.usbmodem1421", { autoOpen: false })
11991223
globals { SerialPort, portName, port }
12001224
> SerialPort.list()
12011225
serialport:main .list +6s
1202-
[ { comName: '/path/name',
1203-
manufacturer: undefined,
1204-
serialNumber: undefined,
1226+
[ { comName: '/dev/tty.usbmodem1421',
1227+
manufacturer: 'Arduino (www.arduino.cc)',
1228+
serialNumber: '752303138333518011C1',
12051229
pnpId: undefined,
1206-
locationId: undefined,
1207-
vendorId: undefined,
1208-
productId: undefined } ]
1230+
locationId: '14200000',
1231+
vendorId: '2341',
1232+
productId: '0043' } ]
12091233
> port.write('Calling all Autobots!')
12101234
true
12111235
> port.read()
12121236
serialport:main _read queueing _read for after open +1m
12131237
null
12141238
> port.open()
1215-
serialport:main opening path: serialport-repl +30s
1239+
serialport:main opening path: /dev/tty.usbmodem1421 +30s
12161240
serialport:bindings open +1ms
12171241
```
12181242

UPGRADE_GUIDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
Upgrading from 4.x to 5.x
22
-------------
3-
5.x is a major rewrite to make node serialport a NodeJS stream. While the api surface is similar there have been a number of changes to ensure more consistent error handling and operation of a serial port.
3+
5.x is a major rewrite to make node serialport a NodeJS stream. While the api surface is similar there have been a number of changes to ensure more consistent error handling and operation of a serial port.
44

55
- Removed the `disconnect` event. The `close` event now fires with a disconnect error object in the event of a disconnection.
66
- `drain` now waits for the current javascript write to complete before calling the system level drain.
77
- `port.isOpen` is now a property not a function
8+
- `SerialPort.list` has slightly different output with more information, decoded strings and `0x` prefixes removed from some properties.
9+
- `SerialPort.list` now returns a promise if no call back is provided
810

911
The exact changes will go here see #1046
1012

bin/find-arduino.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44
// outputs the path to an arduino or nothing
55

66
const serialport = require('../');
7-
serialport.list((err, ports) => {
8-
if (err) {
9-
console.error(err);
7+
serialport.list()
8+
.then(ports => ports.find(port => /arduino/i.test(port.manufacturer)))
9+
.then(port => {
10+
if (!port) { throw new Error('Arduino Not found') }
11+
console.log(port.comName);
12+
})
13+
.catch((err) => {
14+
console.error(err.message);
1015
process.exit(1);
11-
}
12-
ports.forEach((port) => {
13-
if (/arduino/i.test(port.manufacturer)) {
14-
console.log(port.comName);
15-
process.exit(0);
16-
}
1716
});
18-
process.exit(1);
19-
});

binding.gyp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
{
2626
'sources': [
2727
'src/serialport_unix.cpp',
28-
'src/poller.cpp'
28+
'src/poller.cpp',
29+
'src/darwin_list.cpp'
2930
],
3031
'xcode_settings': {
3132
'OTHER_LDFLAGS': [

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
Version 5.0.0-beta9
2+
-------------
3+
- [all] `Serialport.list` now has more consistent output across all platforms.
4+
- [linux] `Serialport.list` is now faster and less resource intensive thanks to @akaJes for contributing this!
5+
16
Version 5.0.0-beta8
27
-------------
38
If we're lucky this will be the last of the betas. The remaining potentially blocking issues have to do with improving `SerialPort.list` which would change their output. The two issues are #1220 and #1084. I need help on those two issues, if I'm not able to close them soon, I'll release anyway, and they'll be fixed for 6x. This release is large enough. -@reconbot

lib/bindings/linux-list.js

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,97 @@
11
'use strict';
22

33
const childProcess = require('child_process');
4-
const LineStream = require('../parsers/readline');
4+
const Readline = require('../parsers/readline');
55

6-
function checkPathAndDevice(path) {
7-
// get only serial port names
6+
// get only serial port names
7+
function checkPathOfDevice(path) {
88
return (/(tty(S|ACM|USB|AMA|MFD)|rfcomm)/).test(path) && path;
99
}
10+
1011
function propName(name) {
1112
return {
1213
'DEVNAME': 'comName',
13-
'ID_VENDOR': 'manufacturer',
14-
'ID_SERIAL': 'serialNumber',
14+
'ID_VENDOR_ENC': 'manufacturer',
15+
'ID_SERIAL_SHORT': 'serialNumber',
1516
'ID_VENDOR_ID': 'vendorId',
16-
'ID_MODEL_ID': 'productId',
17+
'ID_MODEL_ENC': 'productId',
1718
'DEVLINKS': 'pnpId'
1819
}[name.toUpperCase()];
1920
}
21+
22+
function decodeHexEscape(str) {
23+
return str.replace(/\\x([a-fA-F0-9]{2})/g, (a, b) => {
24+
return String.fromCharCode(parseInt(b, 16));
25+
});
26+
}
27+
2028
function propVal(name, val) {
21-
if (/productId|vendorId/.test(name) && !/^0x/.test(val)) {
22-
return `0x${val}`;
23-
}
2429
if (name === 'pnpId') {
25-
return val.split(' ')
26-
.map((path) => path.match(/\/by-id\/(.*)/))
27-
.filter(a => a).map(a => a[1])[0];
30+
const match = val.match(/\/by-id\/([^\s]+)/);
31+
return (match && match[1]) || undefined;
32+
}
33+
if (name === 'manufacturer') {
34+
return decodeHexEscape(val);
35+
}
36+
if (name === 'productId') {
37+
return decodeHexEscape(val);
38+
}
39+
if (/^0x/.test(val)) {
40+
return val.substr(2);
2841
}
2942
return val;
3043
}
3144

3245
function listLinux() {
3346
return new Promise((resolve, reject) => {
47+
const ports = [];
3448
const ude = childProcess.spawn('udevadm', ['info', '-e']);
49+
const lines = ude.stdout.pipe(new Readline());
3550
ude.on('error', reject);
36-
const lines = new LineStream();
37-
const ports = [];
38-
let obj, prop, name;
39-
lines.on('finish', () => resolve(ports));
4051
lines.on('error', reject);
41-
lines.on('data', (data) => {
42-
const line = data.toString();
43-
if ((name = line.match(/^N: (.*)/))) {
44-
if (checkPathAndDevice(name[1])) {
45-
ports.push(obj = {});
46-
}
47-
} else
48-
if ((name = line.match(/^E: (.*)=(.*)/))) {
49-
if (obj && (prop = propName(name[1]))) {
50-
obj[prop] = propVal(prop, name[2]);
52+
53+
let port = {};
54+
let skipPort = false;
55+
lines.on('data', (line) => {
56+
const lineType = line.slice(0, 1);
57+
const data = line.slice(3);
58+
// new port entry
59+
if (lineType === 'P') {
60+
port = {
61+
manufacturer: undefined,
62+
serialNumber: undefined,
63+
pnpId: undefined,
64+
locationId: undefined,
65+
vendorId: undefined,
66+
productId: undefined
67+
};
68+
skipPort = false;
69+
return;
70+
}
71+
72+
if (skipPort) { return }
73+
74+
// Check dev name and save port if it matches flag to skip the rest of the data if not
75+
if (lineType === 'N') {
76+
if (checkPathOfDevice(data)) {
77+
ports.push(port);
78+
} else {
79+
skipPort = true;
5180
}
52-
} else
53-
if (/^P: /.test(line)) {
54-
obj = undefined;
81+
return;
82+
}
83+
84+
// parse data about each port
85+
if (lineType === 'E') {
86+
const keyValue = data.match(/^(.+)=(.*)/);
87+
if (!keyValue) { return }
88+
const key = propName(keyValue[1]);
89+
if (!key) { return }
90+
port[key] = propVal(key, keyValue[2]);
5591
}
5692
});
57-
ude.stdout.pipe(lines);
93+
94+
lines.on('finish', () => resolve(ports));
5895
});
5996
}
6097

0 commit comments

Comments
 (0)