lib/enum.ex

defmodule Baud.Enum do
  @moduledoc """
  Serial port enumeration module.
  """

  @dev "/dev"

  # https://regex101.com/
  # FTDI /dev/tty.usbserial-FTYHQD9MA | /dev/tty.usbserial-FTYHQD9MB | ...
  # Prolific /dev/tty.usbserial | /dev/tty.usbserial2 | ...
  # http://stackoverflow.com/questions/8632586/macos-whats-the-difference-between-dev-baud-and-dev-cu
  @darwinre ~r/(cu|tty)\.usbserial.*/
  @linuxre ~r/ttyS[0-9]*|ttyUSB[0-9]*/

  @doc """
  Lists the names of the available serial ports.

  Returns: `["COM1", "/dev/ttyUSB0", "/dev/tty.usbserial-FTYHQD9MA"]`
  """
  def list() do
    # uname -s
    # {:unix, :darwin} MacPro OS X El Capitan
    # {:unix, :linux} Ubuntu 16.04 64x
    # {:unix, :linux} Ubuntu 16.04 32x
    # {:win32, :nt} Windows 7 32x con OTP 32x
    # {:win32, :nt} Windows 7 64x con OTP 64x
    list(:os.type())
  end

  defp list({:unix, :darwin}) do
    for file <- File.ls!(@dev),
        Regex.match?(@darwinre, file) do
      Path.join(@dev, file)
    end
  end

  defp list({:unix, :linux}) do
    for file <- File.ls!(@dev),
        Regex.match?(@linuxre, file) do
      Path.join(@dev, file)
    end
  end

  # http://stackoverflow.com/questions/1388871/how-do-i-get-a-list-of-available-serial-ports-in-win32
  # UNC Paths \\\\.\\COM11
  defp list({:win32, :nt}) do
    {:ok, handle} = :win32reg.open([:read])
    # key may not exists until one port is added
    case :win32reg.change_key(handle, '\\local_machine\\hardware\\devicemap\\serialcomm') do
      :ok ->
        # {:ok,  [{'\\Device\\ProlificSerial2', 'COM4'}, {'\\Device\\ProlificSerial3', 'COM3'}]}
        {:ok, values} = :win32reg.values(handle)
        :ok = :win32reg.close(handle)

        for {_, comm} <- values do
          # returned as char lists
          to_string(comm)
        end

      _ ->
        []
    end
  end
end