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.
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.