5
votes

Is there a good way of detecting all connected devices connected on serial ports on linux? Im programming in C++ but other examples are welcome as well.

You can just try to open every port and when it succeeds you add it to the list of ports but this seems not a really good solution.

You could go into the dev directors and since my serial port is a USB port I can check which ttyUSB.. files have been made. But this doesn't work for non USB serial ports since files for tty0 up to tty63 are always in this directory.

My example:

std::string port;
int fd 
std::vector<std::string>> list;
for(int i = 0; i < 256; ++i)
{
    port.clear();
    port.append("/dev/ttyUSB");
    port.append(std::to_string(i));
    fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_DELAY);
    if(fd != -1)
    {
        list.push_back(port);
    }
}

Thanks!

2
Serial ports are usually named ttyS0 and so on.Hasturkun
This gives me the feel of an XY question, where your actual problem is X, and you think the solution is Y, so you ask for Y. Why do you want to know about serial ports on the system?Mats Petersson
On 1 of the ports a zigbee device is connected. Since I want to make it user friendly I want to list all possibilities so that the user can pick. Normally if you plug in your zigbee device you should get exactly one port number returned to you. @Hasturkun In my case since my serial device uses a USB connection it is named ttyUSB0.Silver
@RoelStorms: I was mostly responding to the part about not working for non-USB serial ports.Hasturkun
in theory you could do the same for /dev/ttyX if you want normal serial ports. But again not the best way to go.Silver

2 Answers

9
votes

The standard way of enumerating devices in Linux is to browse the /sys filesystem. In this case, you can to the following:

  1. Enumerate all files in /sys/class/tty
  2. For each directory /sys/class/tty/foo, check if /sys/class/tty/foo/device exists using lstat().
    • If it does not exist then you are dealing with some kind of virtual tty device (virtual console port, ptmx, etc...) and you can discard it.
    • If it exists then retain serial port foo.

You should be left with a list of actual serial ports.

0
votes

Given that a number of years have passed since this was answered I am adding this answer. This answer works for later versions of linux. It also uses the new std::filesystem introduced in c++17. std::filesystem is available in earlier versions of c++ either through boost or in the namespace std::experimental::filesystem (use #include <experimental/filesystem>). If using boost you must include compiled component system

This example also works out the where the symlink points to and returns it's canonical name.

#include <iostream>
#include <string>
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>

using std::cout;
namespace fs = boost::filesystem;

std::vector<std::string> get_available_ports() {
    std::vector<std::string> port_names;

    fs::path p("/dev/serial/by-id");
    try {
      if (!exists(p)) {
        throw std::runtime_error(p.generic_string() + " does not exist");
      } else {
        for (fs::directory_entry &de : fs::directory_iterator(p)) {
          if (is_symlink(de.symlink_status())) {
            fs::path symlink_points_at = read_symlink(de);
            fs::path canonical_path = fs::canonical(symlink_points_at, p);
            port_names.push_back(canonical_path.generic_string());
          }
        }
      }
    } catch (const fs::filesystem_error &ex) {
      cout << ex.what() << '\n';
      throw ex;
    }
    std::sort(port_names.begin(), port_names.end());
    return port_names;
}