Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

UART

uart.livemd

UART

Mix.install([
  {:circuits_uart, "~> 1.3"},
  {:vega_lite, "~> 0.1.5"},
  {:kino_vega_lite, "~> 0.1.7"},
  {:kino, "~> 0.8.0"}
])

Introduction

This livebook opens a serial port. Whenever a line is received over it, this line is timestamped and appended to a logfile.

Discovery

uarts = Circuits.UART.enumerate()

Configuration

kino_device = Kino.Input.text("Device name", default: "ttyUSB0")
kino_speed = Kino.Input.text("Device speed", default: "115200")
kino_filename = Kino.Input.text("Filename", default: "uart_log.csv")

Kino.Layout.grid([kino_device, kino_speed, kino_filename])
device = Kino.Input.read(kino_device)
speed = String.to_integer(Kino.Input.read(kino_speed))
filename = Kino.Input.read(kino_filename)
IO.puts("port #{device} @ #{speed} baud → #{filename}")

Logger

GenServer for logging time/value pairs to a CSV file:

defmodule LogDumper do
  use GenServer

  # interface

  def start_link(filename) do
    GenServer.start_link(__MODULE__, {filename}, name: __MODULE__)
  end

  def append(entries) do
    GenServer.cast(__MODULE__, {:append, entries})
  end

  # callbacks

  @impl true
  def init({filename}) do
    {:ok, _file} = File.open(filename, [:append])
  end

  @impl true
  def handle_cast({:append, entries}, file) do
    write(file, entries)
    {:noreply, file}
  end

  # helpers

  defp write(_file, []) do
    nil
  end

  defp write(file, [first | remaining]) do
    IO.write(file, "#{first[:t] / 1000},#{first[:value]}\n")
    write(file, remaining)
  end
end

Start the logger:

{:ok, pid_logger} = LogDumper.start_link(filename)

Gateway

defmodule Gateway do
  use GenServer

  # behaviour functions

  def start_link(device, speed) do
    GenServer.start(__MODULE__, {device, speed}, name: __MODULE__)
  end

  # callback functions

  @impl true
  def init({device, speed}) do
    {:ok, uart_pid} = Circuits.UART.start_link()

    :ok =
      Circuits.UART.open(uart_pid, device,
        speed: speed,
        active: true,
        framing: Circuits.UART.Framing.Line
      )

    {:ok, %{device: device, speed: speed, pid: uart_pid}}
  end

  #  @impl true
  #  def handle_info({:circuits_uart, _, {:error, :eio}}, state) do
  #    disconnect(state)
  #  end

  @impl true
  def handle_info({:circuits_uart, _, payload}, state) do
    IO.puts("Message: " <> payload)

    LogDumper.append([
      %{t: :os.system_time(:milli_seconds), value: String.trim_trailing(payload)}
    ])

    {:noreply, state}
  end
end

Start the gateway:

{:ok, gateway_pid} = Gateway.start_link(device, speed)

Kill the process when we are done:

Process.exit(gateway_pid, :kill)