bring back fw flashing
parent
f991a2be2d
commit
d36116eb3d
|
@ -45,7 +45,7 @@ config :farmbot, :behaviour,
|
|||
config :farmbot, :farmware,
|
||||
first_part_farmware_manifest_url: "https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests/master/manifest.json"
|
||||
|
||||
config :farmbot, expected_fw_version: ["5.0.3.F", "5.0.3.R"]
|
||||
config :farmbot, expected_fw_versions: ["5.0.7.F", "5.0.7.R"]
|
||||
|
||||
case target do
|
||||
"host" ->
|
||||
|
|
|
@ -46,7 +46,7 @@ config :farmbot, Farmbot.System.ConfigStorage,
|
|||
config :farmbot, :behaviour,
|
||||
authorization: Farmbot.Bootstrap.Authorization,
|
||||
system_tasks: Farmbot.Host.SystemTasks,
|
||||
update_handler: Farmbot.Host.UpdateHandler
|
||||
# firmware_handler: Farmbot.Firmware.UartHandler
|
||||
update_handler: Farmbot.Host.UpdateHandler,
|
||||
firmware_handler: Farmbot.Firmware.UartHandler
|
||||
|
||||
config :farmbot, :uart_handler, tty: "/dev/ttyACM0"
|
||||
|
|
|
@ -24,6 +24,22 @@ defmodule Farmbot.CeleryScript.AST.Node.ConfigUpdate do
|
|||
defp do_reduce_os([%{args: %{label: key, value: value}} | rest], env) do
|
||||
Logger.busy 2, "Updating: #{inspect key}: #{value}"
|
||||
case lookup_os_config(key, value) do
|
||||
{:ok, {:string, "settings", val}} when val in ["farmduino", "arduino"] ->
|
||||
Farmbot.System.ConfigStorage.update_config_value(:string, "settings", key, val)
|
||||
|
||||
if Application.get_env(:farmbot, :behaviour)[:firmware_handler] == Farmbot.Firmware.UartHandler do
|
||||
Logger.warn 1, "Updating #{val} firmware."
|
||||
old_env = Application.get_env(:farmbot, :behaviour)
|
||||
new_env = Keyword.put(old_env, :firmware_handler, Farmbot.Firmware.StubHandler)
|
||||
Application.put_env(:farmbot, :behaviour, new_env)
|
||||
GenServer.stop(Farmbot.Firmware, :shutdown)
|
||||
Farmbot.Firmware.UartHandler.Update.maybe_update_firmware(val)
|
||||
Application.put_env(:farmbot, :behaviour, old_env)
|
||||
GenServer.stop(Farmbot.Firmware, :shutdown)
|
||||
end
|
||||
|
||||
do_reduce_os(rest, env)
|
||||
|
||||
{:ok, {type, group, value}} ->
|
||||
Farmbot.System.ConfigStorage.update_config_value(type, group, key, value)
|
||||
do_reduce_os(rest, env)
|
||||
|
|
|
@ -109,14 +109,17 @@ defmodule Farmbot.Firmware do
|
|||
handler_mod =
|
||||
Application.get_env(:farmbot, :behaviour)[:firmware_handler] || raise("No fw handler.")
|
||||
|
||||
{:ok, handler} = handler_mod.start_link()
|
||||
Process.flag(:trap_exit, true)
|
||||
case handler_mod.start_link() do
|
||||
{:ok, handler} ->
|
||||
Process.flag(:trap_exit, true)
|
||||
{
|
||||
:producer_consumer,
|
||||
%State{handler: handler, handler_mod: handler_mod},
|
||||
subscribe_to: [handler], dispatcher: GenStage.BroadcastDispatcher
|
||||
}
|
||||
{:stop, err, state} -> {:stop, err, state}
|
||||
end
|
||||
|
||||
{
|
||||
:producer_consumer,
|
||||
%State{handler: handler, handler_mod: handler_mod},
|
||||
subscribe_to: [handler], dispatcher: GenStage.BroadcastDispatcher
|
||||
}
|
||||
end
|
||||
|
||||
def handle_info({:EXIT, _, reason}, state) do
|
||||
|
@ -269,6 +272,13 @@ defmodule Farmbot.Firmware do
|
|||
end
|
||||
|
||||
defp handle_gcode({:report_software_version, version}, state) do
|
||||
case String.last(version) do
|
||||
"F" ->
|
||||
Farmbot.System.ConfigStorage.update_config_value(:string, "settings", "firmware_hardware", "farmduino")
|
||||
"R" ->
|
||||
Farmbot.System.ConfigStorage.update_config_value(:string, "settings", "firmware_hardware", "arduino")
|
||||
_ -> :ok
|
||||
end
|
||||
{:informational_settings, %{firmware_version: version}, state}
|
||||
end
|
||||
|
||||
|
|
|
@ -80,26 +80,25 @@ defmodule Farmbot.Firmware.UartHandler do
|
|||
|
||||
def init([]) do
|
||||
# If in dev environment, it is expected that this be done at compile time.
|
||||
# If ini target environment, this should be done by `Farmbot.Firmware.AutoDetector`.
|
||||
# If in target environment, this should be done by `Farmbot.Firmware.AutoDetector`.
|
||||
tty =
|
||||
Application.get_env(:farmbot, :uart_handler)[:tty] || raise "Please configure uart handler!"
|
||||
|
||||
{:ok, nerves} = UART.start_link()
|
||||
Process.link(nerves)
|
||||
|
||||
case open_tty(nerves, tty) do
|
||||
:ok -> {:producer, %State{nerves: nerves}, dispatcher: GenStage.BroadcastDispatcher}
|
||||
case open_tty(tty) do
|
||||
{:ok, nerves} -> {:producer, %State{nerves: nerves}, dispatcher: GenStage.BroadcastDispatcher}
|
||||
err -> {:stop, err, :no_state}
|
||||
end
|
||||
end
|
||||
|
||||
defp open_tty(nerves, tty) do
|
||||
case UART.open(nerves, tty, speed: 115_200, active: true) do
|
||||
defp open_tty(tty) do
|
||||
{:ok, nerves} = UART.start_link()
|
||||
Process.link(nerves)
|
||||
case UART.open(nerves, tty, [speed: 115_200, active: true]) do
|
||||
:ok ->
|
||||
:ok = configure_uart(nerves, true)
|
||||
# Flush the buffers so we start fresh
|
||||
:ok = UART.flush(nerves)
|
||||
:ok
|
||||
{:ok, nerves}
|
||||
|
||||
err ->
|
||||
err
|
||||
|
@ -117,6 +116,13 @@ defmodule Farmbot.Firmware.UartHandler do
|
|||
|
||||
# if there is an error, we assume something bad has happened, and we probably
|
||||
# Are better off crashing here, and being restarted.
|
||||
def handle_info({:nerves_uart, _, {:error, :eio}}, state) do
|
||||
old_env = Application.get_env(:farmbot, :behaviour)
|
||||
new_env = Keyword.put(old_env, :firmware_handler, Farmbot.Firmware.StubHandler)
|
||||
Application.put_env(:farmbot, :behaviour, new_env)
|
||||
{:stop, {:error, :eio}, state}
|
||||
end
|
||||
|
||||
def handle_info({:nerves_uart, _, {:error, reason}}, state) do
|
||||
{:stop, {:error, reason}, state}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
defmodule Farmbot.Firmware.UartHandler.Update do
|
||||
@moduledoc false
|
||||
use Farmbot.Logger
|
||||
def maybe_update_firmware(hardware \\ nil) do
|
||||
tty = Application.get_all_env(:farmbot)[:uart_handler][:tty]
|
||||
hardware = case hardware do
|
||||
"farmduino" -> "F"
|
||||
"arduino" -> "R"
|
||||
nil -> "R"
|
||||
end
|
||||
if tty do
|
||||
do_connect_and_maybe_update(tty, hardware)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_connect_and_maybe_update(tty, hardware) do
|
||||
case Nerves.UART.start_link() do
|
||||
{:ok, uart} ->
|
||||
opts = [
|
||||
active: true,
|
||||
framing: {Nerves.UART.Framing.Line, separator: "\r\n"},
|
||||
speed: 115200
|
||||
]
|
||||
:ok = Nerves.UART.open(uart, tty, [speed: 115200])
|
||||
:ok = Nerves.UART.configure(uart, opts)
|
||||
Logger.busy 3, "Waiting for firmware idle report."
|
||||
do_fw_loop(uart, tty, :idle, hardware)
|
||||
{:error, reason} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update: #{inspect reason}"
|
||||
end
|
||||
end
|
||||
|
||||
defp do_fw_loop(uart, tty, flag, hardware) do
|
||||
receive do
|
||||
{:nerves_uart, _, {:error, reason}} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update during idle step: #{inspect reason}"
|
||||
close(uart, tty)
|
||||
{:nerves_uart, _, data} ->
|
||||
if String.contains?(data, "R00") do
|
||||
case flag do
|
||||
:idle ->
|
||||
Logger.busy 3, "Waiting for next idle."
|
||||
do_fw_loop(uart, tty, :version, hardware)
|
||||
:version ->
|
||||
Process.sleep(500)
|
||||
# tell the FW to report its version.
|
||||
Nerves.UART.write(uart, "F83")
|
||||
Logger.busy 3, "Waiting for firmware version report."
|
||||
do_wait_version(uart, tty, hardware)
|
||||
end
|
||||
else
|
||||
do_fw_loop(uart, tty, flag, hardware)
|
||||
end
|
||||
after
|
||||
15_000 ->
|
||||
Logger.warn 1, "timeout waiting for firmware idle. Forcing flash."
|
||||
do_flash(hardware, uart, tty)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_wait_version(uart, tty, hardware) do
|
||||
receive do
|
||||
{:nerves_uart, _, {:error, reason}} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update: #{inspect reason}"
|
||||
close(uart, tty)
|
||||
{:nerves_uart, _, data} ->
|
||||
case String.split(data, "R83 ") do
|
||||
[_] ->
|
||||
# IO.puts "got data: #{data}"
|
||||
do_wait_version(uart, tty, hardware)
|
||||
["", ver_with_q] -> do_maybe_flash(ver_with_q, uart, tty, hardware)
|
||||
end
|
||||
after
|
||||
15_000 ->
|
||||
Logger.warn 1, "timeout waiting for firmware version. Forcing flash."
|
||||
do_flash(hardware, uart, tty)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_maybe_flash(ver_with_q, uart, tty, hardware) do
|
||||
current_version = case String.split(ver_with_q, " Q") do
|
||||
[ver] -> ver
|
||||
[ver, _] -> ver
|
||||
end
|
||||
expected = Application.get_env(:farmbot, :expected_fw_versions)
|
||||
fw_hw = String.last(current_version)
|
||||
cond do
|
||||
fw_hw != hardware ->
|
||||
Logger.warn 3, "Switching firmware hardware."
|
||||
do_flash(hardware, uart, tty)
|
||||
current_version in expected ->
|
||||
Logger.success 1, "Firmware is already correct version."
|
||||
true ->
|
||||
Logger.busy 1, "#{current_version} != #{inspect expected}"
|
||||
do_flash(fw_hw, uart, tty)
|
||||
end
|
||||
end
|
||||
|
||||
# Farmduino
|
||||
defp do_flash("F", uart, tty) do
|
||||
avrdude("#{:code.priv_dir(:farmbot)}/farmduino-firmware.hex", uart, tty)
|
||||
end
|
||||
|
||||
# Anything else. (should always be "R")
|
||||
defp do_flash(_, uart, tty) do
|
||||
avrdude("#{:code.priv_dir(:farmbot)}/arduino-firmware.hex", uart, tty)
|
||||
end
|
||||
|
||||
defp close(uart, _tty) do
|
||||
if Process.alive?(uart) do
|
||||
Nerves.UART.close(uart)
|
||||
Nerves.UART.stop(uart)
|
||||
Process.sleep(500) # to allow the FD to be closed.
|
||||
end
|
||||
end
|
||||
|
||||
def avrdude(fw_file, uart, tty) do
|
||||
close(uart, tty)
|
||||
case System.cmd("avrdude", ~w"-q -q -patmega2560 -cwiring -P#{tty} -b115200 -D -V -Uflash:w:#{fw_file}:i", [stderr_to_stdout: true, into: IO.stream(:stdio, :line)]) do
|
||||
{_, 0} -> Logger.success 1, "Firmware flashed!"
|
||||
{_, err_code} -> Logger.error 1, "Failed to flash Firmware! #{err_code}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -162,8 +162,16 @@ defmodule Farmbot.Target.Bootstrap.Configurator.Router do
|
|||
case conn.body_params do
|
||||
%{"firmware_hardware" => hw} when hw in ["arduino", "farmduino"] ->
|
||||
ConfigStorage.update_config_value(:string, "settings", "firmware_hardware", hw)
|
||||
# TODO Flash firmware here.
|
||||
# If Application.get_env(:farmbot, :uart_handler, :tty) do...
|
||||
|
||||
if Application.get_env(:farmbot, :behaviour)[:firmware_handler] == Farmbot.Firmware.UartHandler do
|
||||
Logger.warn 1, "Updating #{hw} firmware."
|
||||
old_env = Application.get_env(:farmbot, :behaviour)
|
||||
new_env = Keyword.put(old_env, :firmware_handler, Farmbot.Firmware.StubHandler)
|
||||
Application.put_env(:farmbot, :behaviour, new_env)
|
||||
Farmbot.Firmware.UartHandler.Update.maybe_update_firmware(hw)
|
||||
Application.put_env(:farmbot, :behaviour, old_env)
|
||||
end
|
||||
|
||||
redir(conn, "/credentials")
|
||||
|
||||
%{"firmware_hardware" => "custom"} ->
|
||||
|
|
|
@ -15,55 +15,7 @@ defmodule Farmbot.Target.UpdateHandler do
|
|||
end
|
||||
|
||||
def post_update do
|
||||
maybe_update_firmware()
|
||||
Farmbot.Firmware.UartHandler.Update.maybe_update_firmware()
|
||||
:ok
|
||||
end
|
||||
|
||||
defp maybe_update_firmware do
|
||||
tty = Application.get_all_env(:farmbot)[:uart_handler][:tty]
|
||||
if tty do
|
||||
do_connect_and_maybe_update(tty)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_connect_and_maybe_update(tty, retries \\ 0) do
|
||||
case Nerves.UART.start_link() do
|
||||
{:ok, uart} ->
|
||||
opts = [
|
||||
active: true,
|
||||
framing: {Nerves.UART.Framing.Line, separator: "\r\n"}
|
||||
]
|
||||
Nerves.UART.open(uart, tty, opts)
|
||||
do_fw_loop(uart, tty)
|
||||
{:error, reason} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update: #{inspect reason}"
|
||||
end
|
||||
end
|
||||
|
||||
defp do_wait_idle(uart, tty) do
|
||||
receive do
|
||||
{:nerves_uart, ^tty, {:error, reason}} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update: #{inspect reason}"
|
||||
{:nerves_uart, ^tty, data} ->
|
||||
if String.contains?(data, "R00") do
|
||||
# tell the FW to report its version.
|
||||
Nerves.UART.write("F83")
|
||||
do_wait_version(uart, tty)
|
||||
else
|
||||
do_wait_idle(uart, tty)
|
||||
end
|
||||
after
|
||||
15_000 ->
|
||||
Logger.warn 1, "timeout waiting for firmware. Forcing flash."
|
||||
end
|
||||
end
|
||||
|
||||
defp do_wait_version(uart, tty) do
|
||||
receive do
|
||||
{:nerves_uart, ^tty, {:error, reason}} ->
|
||||
Logger.error 1, "Failed to connect to firmware for update: #{inspect reason}"
|
||||
{:nerves_uart, ^tty, data} ->
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue