View Source TTY0TTY (tty0tty v1.0.1)

tty0tty creates 2 pseudo-ttys that are connected together allowing you to run unit tests which use serial ports (like Circuits.UART) without the need for external hardware of adapters.

defmodule SerialTest do
  use ExUnit.Case, async: true

  test "can open a serial port" do
    {:ok, uart} = Circuits.UART.start_link()

    port_name = "/tmp/dummy1"

    # Open serial port before use
    :ok = TTY0TTY.open(port_name)

    assert :ok = Cicuits.UART.open(uart, port_name)
  end
end

Under the hood, TTY0TTY.open/2 opens 2 devices (<port_name>, and <port_name>-twin) and connects their TX <-> RX to emulate the serial connection. This allows you to also verifying reading serial data by sending data to the connected twin port:

defmodule SerialTest do
  use ExUnit.Case, async: true

  test "can read a serial port" do
    {:ok, uart} = Circuits.UART.start_link()

    port_name = "/tmp/dummy1"

    # Open serial port before use
    :ok = TTY0TTY.open(port_name)

    assert :ok = Cicuits.UART.open(uart, port_name)

    File.write!([port_name, "-twin"], "howdy!")

    assert_receive {:circuits_uart, ^port_name, "howdy!"}
  end
end

Summary

Functions

Returns a specification to start this module under a supervisor.

Close an open tty0tty path

List all the currently open tty0tty processes

Open a null modem at the specified device path

Lookup where the tty0tty process is

Functions

child_spec(arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

close(dev_path)

@spec close(String.t() | Supervisor.supervisor()) :: :ok

Close an open tty0tty path

list()

@spec list() :: [{String.t(), pid()}]

List all the currently open tty0tty processes

open(dev_path, opts \\ [])

@spec open(
  String.t(),
  keyword()
) :: :ok | {:error, :enoent}

Open a null modem at the specified device path

Supports MuonTrap options for customizing the modem startup. See MuonTrap.Options for supported options.

Creating the modem takes a variable amount of time across systems, so this blocks until the devices are confirmed open. This timing can be controlled with a :timeout option in milliseconds (default 5000)

File Permissions

Some systems heavily restrict the /dev path and attempting to open a device there would fail without elevated privileges. Consider opening devices in other places with user access, such as /tmp or /mnt

start_link(opts \\ [])

whereis(dev_path)

@spec whereis(String.t()) :: pid() | nil

Lookup where the tty0tty process is