Entorno de Desarrollo
En esta sección se mostrará cómo configurar el entorno de desarrollo y compilar AtomVM para ser ejecutado en la ESP32 utilizando un sistema operativo Linux.
Linux/MacOS
Se recomienda usar Devenv para gestionar dependencias del sistema. El siguiente archivo devenv.nix define todas las dependencias necesarias para compilar un proyecto Elixir a avm, y luego flashear el resultado a un ESP32:
{ lib, pkgs, ... }:
{
languages.elixir.enable = true;
packages = [
pkgs.inotify-tools
pkgs.esptool
pkgs.picocom
] ++
lib.optionals pkgs.stdenv.isDarwin [
# for ExUnit notifier
pkgs.terminal-notifier
# for package - file_system
pkgs.darwin.apple_sdk.frameworks.CoreFoundation
pkgs.darwin.apple_sdk.frameworks.CoreServices
];
scripts.flash.exec = ''
mix atomvm.packbeam && sudo esptool --chip auto --port /dev/ttyUSB0 --baud 921600 \
write-flash 0x250000 serial_test.avm
'';
scripts.monitor.exec = ''
sudo picocom -b 115200 /dev/ttyUSB0
'';
scripts.format.exec = ''
sudo esptool --chip auto --port /dev/ttyUSB0 --baud 921600 erase-flash
'';
}
El entorno también incluye un alias para flashear el programa (flash) y un alias para monitorear la salida serial (monitor). Estos alias se pueden ejectuar directamente a través de la shell. Para entrar a la shell de devenv, se debe ejecutar el comando devenv shell.
Se asume que la entrada de ESP32 se encuentra en /dev/ttyUSB0. En caso de no encontrarse ahí (se puede verificar con ls /dev/ttyUSB*), se debe cambiar en la definición de los scripts.
Mise
También es una opción el utilizar la herramienta mise para la instalación de esptool.
$ mise use -g pipx
$ pipx --version
$ mise use -g pipx:esptool
$ esptool version
Windows
Para Windows se recomienda la instalación de una máquina virtual con Linux o utilizar WSL.
Preparar ESP32
Para correr programas Elixir compilados a avm, es necesario primero flashear la imagen de AtomVM (con soporte para Elixir) en la ESP32.
# eliminar memoria flash
sudo esptool --chip auto --port /dev/ttyUSB0 --baud 921600 erase-flash
# flashear AtomVM (descarga en https://github.com/atomvm/AtomVM/releases)
sudo esptool \
--chip auto \
--port /dev/ttyUSB0 --baud 921600 \
--before default_reset --after hard_reset \
write_flash -u \
--flash_mode dio --flash_freq 40m --flash_size detect \
0x1000 \
./AtomVM-esp32-elixir-v0.6.6.img
Encender un LED con AtomVM
defmodule LedTest.MixProject do
use Mix.Project
def project do
[
app: :led_test,
version: "0.1.0",
elixir: "~> 1.18",
start_permanent: Mix.env() == :prod,
deps: deps(),
atomvm: [
start: LedTest,
flash_offset: 0x250000
]
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:exatomvm, git: "https://github.com/atomvm/ExAtomVM/"}
]
end
end
defmodule LedTest do
@test_duty 3000
@test_fade_time 2000
@led_1 2 # led de ESP32
def start do
ledc_hs_timer = [
{:duty_resolution, 13},
{:freq_hz, 5000},
{:speed_mode, LEDC.high_speed_mode()},
{:timer_num, @high_speed_timer}
]
:ok = LEDC.timer_config(ledc_hs_timer)
ledc_channel = [
[
{:channel, 0},
{:duty, 0},
{:gpio_num, @led_1},
{:speed_mode, LEDC.high_speed_mode()},
{:hpoint, 0},
{:timer_sel, @high_speed_timer}
]
]
Enum.each(ledc_channel, fn channel_config -> :ok = LEDC.channel_config(channel_config) end)
:ok = LEDC.fade_func_install(0)
loop(ledc_channel)
end
def loop(ledc_channel) do
:io.format('1. LEDC fade up to duty = ~p~n', [@test_duty])
Enum.each(ledc_channel, fn channel_config -> do_stage_1(channel_config) end)
Process.sleep(@test_fade_time)
:io.format('2. LEDC fade down to duty = 0~n')
Enum.each(ledc_channel, fn channel_config -> do_stage_2(channel_config) end)
Process.sleep(@test_fade_time)
:io.format('3. LEDC set duty = ~p without fade~n', [@test_duty])
Enum.each(ledc_channel, fn channel_config -> do_stage_3(channel_config) end)
Process.sleep(@test_fade_time)
:io.format('4. LEDC set duty = 0 without fade~n')
Enum.each(ledc_channel, fn channel_config -> do_stage_4(channel_config) end)
Process.sleep(@test_fade_time)
loop(ledc_channel)
end
defp do_stage_1(channel_config) do
speed_mode = :proplists.get_value(:speed_mode, channel_config)
channel = :proplists.get_value(:channel, channel_config)
:ok = LEDC.set_fade_with_time(speed_mode, channel, @test_duty, @test_fade_time)
:ok = LEDC.fade_start(speed_mode, channel, LEDC.fade_no_wait())
end
defp do_stage_2(channel_config) do
speed_mode = :proplists.get_value(:speed_mode, channel_config)
channel = :proplists.get_value(:channel, channel_config)
:ok = LEDC.set_fade_with_time(speed_mode, channel, 0, @test_fade_time)
:ok = LEDC.fade_start(speed_mode, channel, LEDC.fade_no_wait())
end
defp do_stage_3(channel_config) do
speed_mode = :proplists.get_value(:speed_mode, channel_config)
channel = :proplists.get_value(:channel, channel_config)
:ok = LEDC.set_duty(speed_mode, channel, @test_duty)
:ok = LEDC.update_duty(speed_mode, channel)
end
defp do_stage_4(channel_config) do
speed_mode = :proplists.get_value(:speed_mode, channel_config)
channel = :proplists.get_value(:channel, channel_config)
:ok = LEDC.set_duty(speed_mode, channel, 0)
:ok = LEDC.update_duty(speed_mode, channel)
end
end
Luego, en una shell con devenv:
flash
Si logras ejecutar éste código y el Led prende. Exitosamente has configurado el entorno de desarrollo para AtomVM.
Compilar AtomVM
También es opción compilar un ejecutable de AtomVM que permita probar el código dentro de un computador Linux o MacOS.
Para esto se necesitan las siguientes dependencias:
-
cmake
-
gperf
-
rebar3
Estas pueden ser instaladas en Linux (Debian) por ejemplo:
$ sudo apt install cmake
$ sudo apt install gperf
$ sudo apt install rebar3
Y en MacOS utilizando MacPorts por ejemplo:
$ sudo port install cmake
$ sudo port install gperf
$ sudo port install rebar3
|
rebar3 necesita erlang. Por lo que si ya tienes erlang instalado solo debes descargar el archivo. |
$ curl https://s3.amazonaws.com/rebar3/rebar3 -o rebar3
$ chmod +x rebar3
$ sudo mv rebar3 /usr/local/bin
$ rebar3 --version
rebar 3.25.1 on Erlang/OTP 26 Erts 14.2.5.9
Luego se puede compilar:
$ git clone https://github.com/atomvm/AtomVM.git
$ cd AtomVM/
$ mkdir build
$ cd build
$ cmake ..
$ make -j 16
Esto generará un ejecutable que se puede crear un script como avm.
El ejecutable está dentro del directorio build/src.
$ mkdir /usr/local/bin/atomvm
$ cp src/AtomVM /usr/local/bin/atomvm
$ cp lib/atomvmlib.avm /usr/local/bin/atomvm
$ touch /usr/local/bin/avm
$ chmod +x /usr/local/bin/avm
#!/usr/bin/env bash
/usr/local/bin/atomvm/AtomVM /usr/local/bin/atomvm/atomvmlib.avm "$@"
Y se puede usar de la siguiente forma
$ avm ./examples/erlang/hello_world.avm
Hello World
Return value: ok
Elixir
Para tener soporte de Elixir se debe seguir el siguiente ejemplo:
$ git clone https://github.com/atomvm/atomvm_examples.git
$ cd atomvm_examples/elixir/HelloWorld
$ mix deps.get
$ mix atomvm.packbeam
$ avm hello_world.avm
Hello World
Return value: ok
|
Muchas bibliotecas de Elixir como Enum y Regex, entre otras no están disponibles en AtomVM debido a que es una máquina virtual muy pequeña. Se debe tener en consideración sobre qué dependencias utilizar en los proyectos, muchas veces se preferirá código Erlang para suplir lo que haga falta. |