78 lines
1.9 KiB
Elixir
78 lines
1.9 KiB
Elixir
defmodule FarmbotFirmware.UARTTransport do
|
|
@moduledoc """
|
|
Handles sending/receiving GCODEs over UART.
|
|
This is the mechanism that official Farmbot's communicate with
|
|
official Farmbot-Arduino-Firmware's over.
|
|
"""
|
|
alias FarmbotFirmware.{GCODE, UartDefaultAdapter}
|
|
use GenServer
|
|
require Logger
|
|
|
|
@error_retry_ms 5_000
|
|
|
|
def init(args) do
|
|
device = Keyword.fetch!(args, :device)
|
|
handle_gcode = Keyword.fetch!(args, :handle_gcode)
|
|
reset = Keyword.get(args, :reset)
|
|
{:ok, uart} = uart_adapter().start_link()
|
|
|
|
{:ok,
|
|
%{
|
|
uart: uart,
|
|
device: device,
|
|
open: false,
|
|
handle_gcode: handle_gcode,
|
|
reset: reset
|
|
}, 0}
|
|
end
|
|
|
|
def terminate(_, %{uart: uart}) do
|
|
uart_adapter().stop(uart)
|
|
end
|
|
|
|
def handle_info(:timeout, %{open: false} = state) do
|
|
opts = uart_adapter().generate_opts()
|
|
|
|
with :ok <- open(state.uart, state.device, opts),
|
|
:ok <- reset(state) do
|
|
{:noreply, %{state | open: true}}
|
|
else
|
|
{:error, reason} ->
|
|
Logger.error("Error opening #{state.device}: #{inspect(reason)}")
|
|
{:noreply, %{state | open: false}, @error_retry_ms}
|
|
end
|
|
end
|
|
|
|
def handle_info({:circuits_uart, _, {:error, reason}}, state) do
|
|
{:stop, {:uart_error, reason}, state}
|
|
end
|
|
|
|
def handle_info({:circuits_uart, _, data}, state) when is_binary(data) do
|
|
code = GCODE.decode(String.trim(data))
|
|
state.handle_gcode.(code)
|
|
{:noreply, state}
|
|
end
|
|
|
|
def handle_call(code, _from, state) do
|
|
str = GCODE.encode(code)
|
|
r = uart_adapter().write(state.uart, str)
|
|
{:reply, r, state}
|
|
end
|
|
|
|
def reset(state) do
|
|
if module = state[:reset] do
|
|
module.reset()
|
|
else
|
|
:ok
|
|
end
|
|
end
|
|
|
|
def open(uart_pid, device_path, opts) do
|
|
uart_adapter().open(uart_pid, device_path, opts)
|
|
end
|
|
|
|
def uart_adapter() do
|
|
Application.get_env(:farmbot_firmware, :uart_adapter, UartDefaultAdapter)
|
|
end
|
|
end
|