serialport / node-serialport

Access serial ports with JavaScript. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them!
https://serialport.io
MIT License
5.82k stars 1.01k forks source link

serialPort.list doesn't work on Linux distros with /dev/serial directory #343

Closed ghost closed 9 years ago

ghost commented 10 years ago

On the Linux distro i'm using (OpenWRT), there is no /dev/serial directory. To my understanding, it exists only on certain Linux distros, and may be missing on others too (depending on the type of device manager being used, i.e hotplug, udev, etc... I guess).

When trying to list serialport on unix/linux (listUnix function in serialport.js), you use this directory. This means that on some Linux distros, serialports cannot be listed.

I can connect directly to the serialport, using /dev/ttyUSB0 for example. Fixing this would greatly improve the generic use of serialport by other nodejs modules, that use the serialport.list function to automatically find a device, such as in firmata/johnny-five.

Thanks

reconbot commented 10 years ago

I'm open to this but, how can we tell which /dev/tty devices are serialports?

voodootikigod commented 10 years ago

would the ability to provide a target directory as an option be sufficient?

asherkin commented 10 years ago

USB serial devices can be found by looking at /sys/bus/usb-serial/devices//tty/, that plus /dev/ttyS would possibly be a good default set.

joseariel commented 10 years ago

I'm having the same issue, is there a way to work around it? I'm trying to use johnny-five inside the yun, which relies on serialport.

amosk commented 10 years ago

serialPort.list only returns ttyUSB0 for me...

I can confirm this works on Ubuntu:

$ ls /sys/bus/usb-serial/devices/ ttyUSB0 ttyUSB1

haydockjp commented 9 years ago

Is there something additional that needs to be installed to have the links in /dev/bus/usb-serial/devices? I am looking at Ubuntu (14.04.1 LTS) and Raspberry Pi. They both seem to have slightly different folder /dev/bus/usb/devices

haydockjp commented 9 years ago

I am having a play to see if I can get

exec('ls /dev/*/by-id/*usb*', function (err, output) {

working, in place of

fs.readdir("/dev/serial/by-id", function (err, files) {
haydockjp commented 9 years ago

I don't have enough time to look into this, maybe it will help someone else work it out. The code below finds the devices and calls the exec. The calling code doesn't seem to be able to process "ports"

Here are the changes I made

serialport.js

At the end, I added a check for linux

if (process.platform === 'win32') {
    factory.list = SerialPortBinding.list;
  } else if (process.platform === 'darwin') {
    factory.list = SerialPortBinding.list;
  } else if (process.platform === 'linux') {
    factory.list = listLinux;
  } else {
    factory.list = listUnix;
  }

I also added a "listLilnux" method just below the "listUnix" method

    function listLinux(callback) {
        function udev_parser(udev_output, callback) {
            function udev_output_to_json(output) {
                var result = {};
                var lines = output.split('\n');
                for (var i = 0; i < lines.length; i++) {
                    var line = lines[i].trim();
                    if (line !== "") {
                        var line_parts = lines[i].split("=");
                        result[line_parts[0].trim()] = line_parts[1].trim();
                    }
                }
                return result;
            }
            var as_json = udev_output_to_json(udev_output);
            var pnpId = as_json.DEVLINKS.split(" ")[0];
            pnpId = pnpId.substring(pnpId.lastIndexOf("/") + 1);
            var port = {
                comName: as_json.DEVNAME,
                manufacturer: as_json.ID_VENDOR,
                serialNumber: as_json.ID_SERIAL,
                pnpId: pnpId,
                vendorId: "0x" + as_json.ID_VENDOR_ID,
                productId: "0x" + as_json.ID_MODEL_ID
            };

            console.log("port: %j", port);
            callback(null, port);
        }

        exec('ls /dev/*/by-id/*usb*', function (err, output) {
            if (err) {
                console.log(err);

                // if this directory is not found this could just be because it's not plugged in
                if (err.errno === 34) {
                    return callback(null, []);
                }

                if (callback) {
                    callback(err);
                } else {
                    factory.emit('error', err);
                }
                return;
            }

            var files = output.split('\n');
            for (var i = 0; i < files.length; i++) {
                var file = files[i].trim();
                if (file !== "") {
                    console.log("exec: " + file);
                    exec('/sbin/udevadm info --query=property -p $(/sbin/udevadm info -q path -n ' + file + ')', function (err, stdout) {
                        if (err) {
                            if (callback) {
                                callback(err);
                            } else {
                                factory.emit('error', err);
                            }
                            return;
                        }

                        udev_parser(stdout, callback);
                    });
                }
            }
        });
    }

if I create a test file and just put this in it

var serialport = require("serialport");
var SerialPort = serialport.SerialPort;

// list serial ports:
serialport.list(function (err, ports) {
    console.log("list");
    console.log("err: " + err);
});

This is what I see logged to the console

exec: /dev/disk/by-id/usb-Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0
exec: /dev/disk/by-id/usb-Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0-part1
exec: /dev/snd/by-id/usb-046d_08d7-01
exec: /dev/v4l/by-id/usb-046d_08d7-video-index0
port: {"comName":"/dev/sdb","manufacturer":"Kingston","serialNumber":"Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0","pnpId":"8:16","vendorId":"0x0951","productId":"0x1666"}
list
err: null
port: {"comName":"/dev/sdb1","manufacturer":"Kingston","serialNumber":"Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0","pnpId":"8:17","vendorId":"0x0951","productId":"0x1666"}
list
err: null
port: {"comName":"/dev/snd/controlC0","manufacturer":"046d","serialNumber":"046d_08d7","pnpId":"116:4","vendorId":"0x046d","productId":"0x08d7"}
list
err: null
port: {"comName":"/dev/video0","manufacturer":"046d","serialNumber":"046d_08d7","pnpId":"81:0","vendorId":"0x046d","productId":"0x08d7"}
list
err: null

If I try the normal test code for listing

var serialport = require("serialport");
var SerialPort = serialport.SerialPort;

// list serial ports:
serialport.list(function (err, ports) {
    console.log("list");
    console.log("err: " + err);
    ports.forEach(function(port) {
        console.log(port.comName);
    });
});

I see this logged to the console

exec: /dev/disk/by-id/usb-Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0
exec: /dev/disk/by-id/usb-Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0-part1
exec: /dev/snd/by-id/usb-046d_08d7-01
exec: /dev/v4l/by-id/usb-046d_08d7-video-index0
port: {"comName":"/dev/sdb","manufacturer":"Kingston","serialNumber":"Kingston_DataTraveler_3.0_60A44C41382CBDA15B5731B6-0:0","pnpId":"8:16","vendorId":"0x0951","productId":"0x1666"}
list
err: null

/opt/projects/nodejs/serial_test/testit.js:8
    ports.forEach(function(port) {
          ^
TypeError: Object #<Object> has no method 'forEach'
    at /opt/projects/nodejs/serial_test/testit.js:8:11
    at udev_parser (/opt/projects/nodejs/serial_test/node_modules/serialport/serialport.js:582:6)
    at /opt/projects/nodejs/serial_test/node_modules/serialport/serialport.js:617:4
    at ChildProcess.exithandler (child_process.js:603:7)
    at ChildProcess.EventEmitter.emit (events.js:98:17)
    at maybeClose (child_process.js:703:16)
    at Socket.<anonymous> (child_process.js:916:11)
    at Socket.EventEmitter.emit (events.js:95:17)
    at Pipe.close (net.js:451:12)
felipap commented 9 years ago

Thanks, @haydockjp. Seem to be working fine on Ubuntu 14.04. Is there a pull request for this?

felipap commented 9 years ago

Ops. You removed the async call, so the listLinux() callback is getting called multiple times.

felipap commented 9 years ago

Sorry for the spam. This is the fix:

function listLinux(callback) {
  function udev_parser(udev_output, callback) {
    function udev_output_to_json(output) {
      var result = {};
      var lines = output.split('\n');
      for (var i = 0; i < lines.length; i++) {
        var line = lines[i].trim();
        if (line !== "") {
          var line_parts = lines[i].split("=");
          result[line_parts[0].trim()] = line_parts[1].trim();
        }
      }
      return result;
    }
    var as_json = udev_output_to_json(udev_output);
    var pnpId = as_json.DEVLINKS.split(" ")[0];
    pnpId = pnpId.substring(pnpId.lastIndexOf("/") + 1);
    var port = {
      comName: as_json.DEVNAME,
      manufacturer: as_json.ID_VENDOR,
      serialNumber: as_json.ID_SERIAL,
      pnpId: pnpId,
      vendorId: "0x" + as_json.ID_VENDOR_ID,
      productId: "0x" + as_json.ID_MODEL_ID
    };
    callback(null, port);
  }

  exec('ls /dev/*/by-id/*usb*', function(err, output) {
    if (err) {
      console.log(err);

      // if this directory is not found this could just be because it's not plugged in
      if (err.errno === 34) {
        return callback(null, []);
      }

      if (callback) {
        callback(err);
      } else {
        factory.emit('error', err);
      }
      return;
    }

    var files = output.split('\n');
    async.map(files, function(file, callback) {
      file = file.trim();
      if (file === "") {
        callback('');
        return;
      }
      console.log("exec: " + file);
      exec('/sbin/udevadm info --query=property -p $(/sbin/udevadm info -q path -n ' + file + ')', function(err, stdout) {
        if (err) {
          if (callback) {
            callback(err);
          } else {
            factory.emit('error', err);
          }
          return;
        }
        udev_parser(stdout, callback);
      });
    }, callback)
  });
}
reyiyo commented 9 years ago

Hi!

Is there any pull request to fix this? I'm having the same problem. If there isn't any I can create one with the fix from @f03lipe

voodootikigod commented 9 years ago

To my knowledge there isnt a PR out there , I encourage you to make and submit it!

Cheers

On Sunday, April 19, 2015, Sergio Orbe notifications@github.com wrote:

Hi!

Is there any pull request to fix this? I'm having the same problem. If there isn't any I can create one with the fix from @f03lipe https://github.com/f03lipe

— Reply to this email directly or view it on GitHub https://github.com/voodootikigod/node-serialport/issues/343#issuecomment-94311685 .

Chris Williams

@voodootikigod http://twitter.com/voodootikigod | GitHub http://github.com/voodootikigod

The things I make that you should check out: SaferAging http://www.saferaging.com/ | JSConf http://jsconf.com/ | RobotsConf http://robotsconf.com/ | RobotsWeekly http://robotsweekly.com/

Help me end the negativity on the internet, share this http://jsconf.eu/2011/an_end_to_negativity.html.

reyiyo commented 9 years ago

Hi!

I've tested it with serialport v. 1.6.3 and node 0.12.2 on debian and ubuntu and the problem seems to be gone.

voodootikigod commented 9 years ago

Yay!

Chris Williams

@voodootikigod http://twitter.com/voodootikigod | GitHub http://github.com/voodootikigod

The things I make that you should check out: SaferAging http://www.saferaging.com/ | JSConf http://jsconf.com/ | RobotsConf http://robotsconf.com/ | RobotsWeekly http://robotsweekly.com/

Help me end the negativity on the internet, share this http://jsconf.eu/2011/an_end_to_negativity.html.

On Fri, May 1, 2015 at 1:41 AM, Sergio Orbe notifications@github.com wrote:

Hi!

I've tested it with serialport v. 1.6.3 and node 0.12.2 on debian and ubuntu and the problem seems to be gone.

— Reply to this email directly or view it on GitHub https://github.com/voodootikigod/node-serialport/issues/343#issuecomment-98049596 .

ChrisTerBeke commented 9 years ago

Running 1.7.4, still having this issue here

mayankandrobo commented 9 years ago

Guys having same issues can anyone help new to this part..

cparawhore commented 9 years ago

I'm having issues too

/home/christian/node_modules/johnny-five/node_modules/serialport/serialport.js:541

var pnpId = as_json.DEVLINKS.split(' ')[0];
                                   ^
TypeError: Cannot call method 'split' of undefined
elafargue commented 8 years ago

I finally decided to fix the problem the other way around: adding a /dev/serial tree on a distribution that does not do it by default is actually very easy, so rather than fix node-serialport, I decided to add the udev entry on my system. I documented this on the build instructions of the project I'm working on ('Node-serialport issues on Linux' section) : http://wizkers.github.io/wizkers-doc/building/

lmk if this helps !

TL;DR; : create the following file: /etc/udev/rules.d/60-ed-persistent-serial.rules and put the two lines below in it:

ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"
amuchembled commented 8 years ago

Same error for me... any solution ?

elafargue commented 8 years ago

@amuchembled does the solution I described just above work for you?

amuchembled commented 8 years ago

No sorry, I'm on a Rpi, raspbian jessie. File doesn't seems to exists :

cat /etc/udev/rules.d/60-ed-persistent-serial.rules cat: /etc/udev/rules.d/60-ed-persistent-serial.rules: No such file or directory

But the folder contains :

40-scratch.rules 99-com.rules

elafargue commented 8 years ago

well of course: you need to create that file and put those two lines in it :) then reboot

amuchembled commented 8 years ago

@elafargue Thank you, your solution works ! ;D

elafargue commented 8 years ago

Awesome! Thanks for letting us know. Maybe this should be added in the README.md of the node-serialport package...

NHQ commented 8 years ago

@elafargue unfortunately, that solution is not working for me.

cat /etc/udev/rules.d/60-ed-persistent-serial.rules
D_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"

error:

/home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:541
      var pnpId = as_json.DEVLINKS.split(' ')[0];
                                  ^

TypeError: Cannot read property 'split' of undefined
    at udev_parser (/home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:541:35)
    at /home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:592:11
    at ChildProcess.exithandler (child_process.js:194:7)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:818:16)
    at Socket.<anonymous> (internal/child_process.js:319:11)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at Pipe._onclose (net.js:469:12)
jacobrosenthal commented 8 years ago

DEVLINKS.split issue is fixed in 2.0.5.

On Thu, Nov 12, 2015 at 5:53 AM, JOHNNY notifications@github.com wrote:

@elafargue https://github.com/elafargue unfortunately, that solution is not working for me.

cat /etc/udev/rules.d/60-ed-persistent-serial.rules D_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"

error:

/home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:541 var pnpId = as_json.DEVLINKS.split(' ')[0]; ^

TypeError: Cannot read property 'split' of undefined at udev_parser (/home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:541:35) at /home/johnny/projects/arduino/node_modules/johnny-five/node_modules/serialport/serialport.js:592:11 at ChildProcess.exithandler (child_process.js:194:7) at emitTwo (events.js:87:13) at ChildProcess.emit (events.js:172:7) at maybeClose (internal/child_process.js:818:16) at Socket. (internal/child_process.js:319:11) at emitOne (events.js:77:13) at Socket.emit (events.js:169:7) at Pipe._onclose (net.js:469:12)

— Reply to this email directly or view it on GitHub https://github.com/voodootikigod/node-serialport/issues/343#issuecomment-156071809 .

claytonbez commented 7 years ago

Anyone still having this issue? I solved it by running the node script where I use the serial port as sudo.

reconbot commented 7 years ago

If sudo works than it's a permission problem and not related to this. Try adding your user to the dialout group and open a new issue with details if you're still having problems.

ChrisTerBeke commented 7 years ago

We eventually moved away from node-serialport and wrote our own serial implementation in C++ for our 3D printing drivers. Repo available here for anyone that's interested: https://github.com/PRINTR3D/formide-drivers. The code where we scan for devices is here: https://github.com/PRINTR3D/formide-drivers/blob/e87371e3b0e2b67d2302321f10587172ff1b5c84/src/utils/find_devices.hpp.

Edit: our drivers were made to only work on Unix systems, hence the hardcoded /dev stuff.