Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ linux detect all serial ports

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!

like image 400
Silver Avatar asked Mar 11 '13 15:03

Silver


2 Answers

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.

like image 114
Celada Avatar answered Oct 23 '22 01:10

Celada


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;
}
like image 1
Nathaniel Johnson Avatar answered Oct 23 '22 01:10

Nathaniel Johnson