Skip to content

Add docs to Mix tasks #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions lib/ex_atom_vm.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
defmodule ExAtomVM do
@moduledoc """
Documentation for ExAtomVM.
"""

@doc """
Hello world.

## Examples

iex> ExAtomVM.hello()
:world

"""
def hello do
:world
end
@moduledoc "README.md"
|> File.read!()
end
14 changes: 14 additions & 0 deletions lib/mix/tasks/check.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
defmodule Mix.Tasks.Atomvm.Check do
use Mix.Task
@shortdoc "Check application code for use of unsupported instructions"

@moduledoc """
Verifies that the functions and modules used are either part of the application source (or deps) or supported by AtomVM.

The check will catch the use of any standard Elixir modules or functions used in the application that are not included in exavmlib.

> #### Info {: .info}
>
> Note. The `Mix.Tasks.Atomvm.Packbeam` task depends on this one, so users will likely never need to use it directly.
"""

alias Mix.Project

def run(args) do
Expand Down Expand Up @@ -201,6 +214,7 @@ defmodule Mix.Tasks.Atomvm.Check do
you must remove the parentheses: map.field
""")
end

IO.puts("error: following missing instructions are used:")
print_list(missing_instructions)
IO.puts("")
Expand Down
57 changes: 57 additions & 0 deletions lib/mix/tasks/esp32_flash.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,62 @@
defmodule Mix.Tasks.Atomvm.Esp32.Flash do
use Mix.Task

@shortdoc "Flash the application to an ESP32 micro-controller"

@moduledoc """
Flashes the application to an ESP32 micro-controller.

> #### Important {: .warning}
>
> Before running this task, you must flash the AtomVM virtual machine to the target device.
>
> This tasks depends on `esptool` and can be installed using package managers:
> - linux (debian): apt install esptool
> - macos: brew install esptool
> - or follow these [installation instructions](https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html#installation) when not available through a package manager.

## Usage example

Within your AtomVM mix project run

`
$ mix atomvm.esp32.flash
`

Or with optional flags (which will override the config in mix.exs)

`
$ mix atomvm.esp32.flash --port /dev/tty.usbserial-0001
`

Or detect the port automatically with

`
$ mix atomvm.esp32.flash --port auto
`

## Configuration

ExAtomVM can be configured from the mix.ex file and supports the following settings for the
`atomvm.esp32.flash` task.

* `:flash_offset` - The start address of the flash to write the application to in hexademical format,
defaults to `0x250000`.

* `:chip` - Chip type, defaults to `auto`.

* `:port` - The port to which device is connected on the host computer, defaults to `/dev/ttyUSB0`.

* `:baud` - The BAUD rate used when flashing to device, defaults to `115200`.

## Command line options

Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name
as the [supported properties](#module-configuration)

For example, you can use the `--port` option to specify or override the port property.
"""

alias Mix.Project
alias Mix.Tasks.Atomvm.Packbeam

Expand Down
33 changes: 33 additions & 0 deletions lib/mix/tasks/packbeam.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
defmodule Mix.Tasks.Atomvm.Packbeam do
use Mix.Task

@shortdoc "Bundle the application into an AVM file"

@moduledoc """
Bundle an application into an AVM file that can be flashed to a micro-controller and (or directly on a unix host) executed by the AtomVM virtual machine.

> #### Info {: .info}
>
> Normally using this task manually is not required, it is called automatically by `atomvm.esp32.flash`, `atomvm.stm32.flash` and `atomvm.pico.flash`.

## Usage example

Within your AtomVM mix project run

`
$ mix atomvm.packbeam
`

## Configuration

ExAtomVM can be configured from the mix.ex file and supports the following settings for the
`atomvm.packbeam` task.

* `:start` - The name of the module containing the start/0 entrypoint function. Only to be used to override the `:start` options defined in the the projects `mix.exs` This would not normally be needed, unless the user had an alternate mode of operation e.g like a client/server app that normally builds the client, but when building the server uses a different start module.

## Command line options

Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name
as the [supported properties](#module-configuration)

For example, you can use the `--start` option to specify or override the `start` property.
"""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we mentioned the dependencies elsewhere, this is actually another that users will not often need to use directly. The various MCU.flash tasks depend on this one, it is only for generic_unix, or WASM (?) that this task will be required for users.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow. But this is only an example that it is possible to set this flag if needed. Or do you mean that this flag is always needed when running this particular task directly? (I've no problem with changing copy if wished)

Copy link
Contributor

@UncleGrumpy UncleGrumpy Feb 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't mean the flag, I meant using the atomvm.packbeam task directly is not needed for micro-controllers. The atomvm.esp32.flash, atomvm.stm32.flash, and atomvm.pico.flash all depend on atomvm.packbeam, so using the packbeam task directly is only needed to create an AVM file for generic_unix... and possibly WASM, but I am not up to speed on deploying an Elixir WASM application, so I don't know all the details there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We mentioned in the uf2create task that the atomvm.pico.flash task depends on uf2create task, so using it directly is not normally needed... I thought we might mention similarly about the atomvm.packbeam task.

alias ExAtomVM.PackBEAM
alias Mix.Project
alias Mix.Tasks.Atomvm.Check
Expand Down
121 changes: 113 additions & 8 deletions lib/mix/tasks/pico_flash.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
defmodule Mix.Tasks.Atomvm.Pico.Flash do
use Mix.Task

@shortdoc "Flash the application to a pico micro-controller"

@moduledoc """
Flashes the application to a Raspberry Pi RP2

You can build with all boards supported by Raspberry Pi pico SDK, including Pico, Pico-W and Pico2.
AtomVM also works with clones such as RP2040 Zero.

> #### Important {: .warning}
>
> Before running this task, you must flash the AtomVM virtual machine to the target device.
>
> The pico provided partition is expected to be mounted. MacOS and most Linux desktop environments will do this automatically, for some it will need to be setup by the user.

## Usage example

Within your AtomVM mix project run

`
$ mix atomvm.pico.flash
`

Or with optional flags (which will override the config in mix.exs)

`
$ mix atomvm.pico.flash --picotool /some/path
`

## Configuration

ExAtomVM can be configured from the mix.ex file and supports the following settings for the
`atomvm.pico.flash` task.

* `:pico_path` - The full path to the pico mount point, defaults to `"/run/media/${USER}/RPI-RP2"` on linux; `"/Volumes/RPI-RP2"` on darwin (Mac)

* `:pico_reset` - The full path to the pico device to reset if required, default `"/dev/ttyACM*"` on linux; `"/dev/cu.usbmodem14*"` on darwin (Mac)

* `:picotool` - The full path to picotool executable (currently optional), default `undefined`

## Command line options

Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name
as the [supported properties](#module-configuration)

For example, you can use the `--picotool` option to specify or override the `picotool` property.
"""

alias Mix.Project
alias Mix.Tasks.Atomvm.Uf2create

Expand All @@ -9,12 +57,38 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
with {:atomvm, {:ok, avm_config}} <- {:atomvm, Keyword.fetch(config, :atomvm)},
{:args, {:ok, options}} <- {:args, parse_args(args)},
{:uf2, :ok} <- {:uf2, Uf2create.run(args)} do
pico_path =
Map.get(options, :pico_path, Keyword.get(avm_config, :pico_path, System.get_env("ATOMVM_PICO_MOUNT_PATH", get_default_mount())))
pico_path =
Map.get(
options,
:pico_path,
Keyword.get(
avm_config,
:pico_path,
System.get_env("ATOMVM_PICO_MOUNT_PATH", get_default_mount())
)
)

pico_reset =
Map.get(options, :pico_reset, Keyword.get(avm_config, :pico_reset, System.get_env("ATOMVM_PICO_RESET_DEV", get_reset_base())))
Map.get(
options,
:pico_reset,
Keyword.get(
avm_config,
:pico_reset,
System.get_env("ATOMVM_PICO_RESET_DEV", get_reset_base())
)
)

picotool =
Map.get(options, :picotool, Keyword.get(avm_config, :picotool, System.get_env("ATOMVM_PICOTOOL_PATH", "#{:os.find_executable(~c"picotool")}")))
Map.get(
options,
:picotool,
Keyword.get(
avm_config,
:picotool,
System.get_env("ATOMVM_PICOTOOL_PATH", "#{:os.find_executable(~c"picotool")}")
)
)

do_flash(pico_path, pico_reset, picotool)
else
Expand Down Expand Up @@ -70,13 +144,16 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
case Map.get(filestat, :type) do
:directory ->
:ok

_ ->
IO.puts("Object found at #{mount} is not a directory")
exit({:shutdown, 1})
end

{:error, :enoent} ->
Process.sleep(1000)
wait_for_mount(mount, count + 1)

error ->
IO.puts("unexpected error: #{error} while checking pico mount path.")
exit({:shutdown, 1})
Expand All @@ -94,10 +171,12 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
case Map.get(info, :type) do
:directory ->
:ok

_ ->
IO.puts("error: object at pico mount path not a directory. Abort!")
exit({:shutdown, 1})
end

_ ->
IO.puts("error: Pico not mounted. Abort!")
exit({:shutdown, 1})
Expand All @@ -123,16 +202,19 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
case Path.wildcard(resetdev) do
[] ->
false

[device | _t] ->
case File.stat(device) do
{:ok, info} ->
case Map.get(info, :type) do
:device -> {true, device}
_ -> false
end

_ ->
false
end

_ ->
false
end
Expand All @@ -146,25 +228,40 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
{"", 0} ->
# Pause to let the device settle
Process.sleep(200)

error ->
case picotool do
false ->
IO.puts("Error: #{error}\nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode.")
IO.puts(
"Error: #{error}\nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode."
)

exit({:shutdown, 1})

_ ->
IO.puts("Warning: #{error}\nFor faster flashing remember to disconnect serial monitor first.")
IO.puts(
"Warning: #{error}\nFor faster flashing remember to disconnect serial monitor first."
)

reset_args = ["reboot", "-f", "-u"]
IO.puts("Disconnecting serial monitor with `picotool #{:lists.join(" ", reset_args)}` in 5 seconds...")

IO.puts(
"Disconnecting serial monitor with `picotool #{:lists.join(" ", reset_args)}` in 5 seconds..."
)

Process.sleep(5000)

case :string.trim(System.cmd(picotool, reset_args)) do
{status, 0} ->
case status do
"The device was asked to reboot into BOOTSEL mode." ->
:ok

pt_error ->
IO.puts("Failed to prepare pico for flashing: #{pt_error}")
exit({:shutdown, 1})
end

_ ->
IO.puts("Failed to prepare pico for flashing: #{error}")
end
Expand All @@ -176,14 +273,22 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do
case needs_reset(pico_reset) do
false ->
:ok

{true, reset_port} ->
do_reset(reset_port, picotool)
IO.puts("Waiting for the device at path #{pico_path} to settle and mount...")
wait_for_mount(pico_path, 0)
end

check_pico_mount(pico_path)
_bytes = File.copy!("#{Project.config()[:app]}.uf2", "#{pico_path}/#{Project.config()[:app]}.uf2", :infinity)

_bytes =
File.copy!(
"#{Project.config()[:app]}.uf2",
"#{pico_path}/#{Project.config()[:app]}.uf2",
:infinity
)

IO.puts("Successfully loaded #{Project.config()[:app]} to the pico device at #{pico_path}.")
end
end
Loading