AtomVM 小型Erlang 虚拟机
AtomVM
什么是AtomVM
AtomVM 是一个从零开始实现的 BEAM 虚拟机(即 Erlang 虚拟机),专为运行在如 ESP32 和 STM32 等微控制器上设计。它允许开发者使用 Erlang 或 Elixir 编写 IoT 应用程序,并部署到内存有限的嵌入式系统上。 同时,它也支持运行于传统系统(如 Linux、macOS),但这些平台通常用于开发或调试用途。
特性
- Erlang 运行时,能够运行编译后的BEAM文件
- 支持Erlang和Elixir类型,包括integers, strings, lists, maps, binaries, Enums, 等
- 具有高效的垃圾收集和数据共享的内存管理环境
- 支持函数式编程
- 平台专注于并发设计,用户可以轻松生成、监控并管理轻量级进程,使 IoT 设备具备同时处理多个任务的能力。
- 支持对称多处理(SMP),无需任何代码更改即可利用支持它的平台上所有可用的内核(例如 ESP32);
- 内置丰富的网络API,用于编写强大的物联网程序
- 内置标准设备协议接口GPIO、I2C、SPI 和 UART
为什么选择 Erlang/Elixir
AtomVM 应用程序的部署环境远比典型编程环境更加受限
环境限制
- 内存: 典型的 ESP32 配备 520K RAM 和 4MB 闪存,类似于 1980 年代中期的桌面计算机。
- 接口限制: 大多数微控制器环境不支持本地 POSIX API,常见的操作系统抽象(如进程、线程或文件)在许多情况下不可用
BEAM 的优势
- 抢占式多任务环境: BEAM 提供的抢占式多任务环境让许多常见的操作系统抽象(特别是涉及线程和并发的)变得不再必要。
- 轻量级“进程”: Erlang 和 Elixir 支持轻量级“进程”,通过消息传递实现进程间通信,支持抢占式多任务和每个进程的堆分配及垃圾回收。
AtomVM 的设计哲学
"AtomVM 从一开始就设计为在小型、廉价的嵌入式设备上运行,系统资源(内存、CPU、存储)受到严格限制。"
资源优化
- 内存使用: AtomVM 优先考虑最小化内存消耗和堆碎片化。
- 工具链利用: AtomVM 旨在利用现有的 Erlang 和 Elixir 生态系统中的工具链,包括编译器,减少不必要的功能和复杂性
开始使用
这里我们用esp32作为例子
操作系统: linux
准备
| Erlang Version | Elixir Version |
|---|---|
| ✅ OTP 21 | ✅ 1.7 |
| ✅ OTP 22 | ✅ 1.8 |
| ✅ OTP 23 | ✅ 1.11 |
| ✅ OTP 24 | ✅ 1.14 |
| ✅ OTP 25 | ✅ 1.14 |
| ✅ OTP 26 | ✅ 1.15 |
| ✅ OTP 27 | ✅ 1.17 |
| Espressif SoCs | AtomVM support |
|---|---|
| ESP32 | ✅ |
| ESP32c2 | ✅ |
| ESP32c3 | ✅ |
| ESP32c6 | ✅ |
| ESP32h2 | ✅ |
| ESP32s2 | ✅ |
| ESP32s3 | ✅ |
| ESP32p4 Datasheet Pending |
| IDF SDK supported versions | AtomVM support |
|---|---|
| ESP-IDF v5.0 | ✅ |
| ESP-IDF v5.1 | ✅ |
| ESP-IDF v5.2 | ✅ |
| ESP-IDF v5.3 | ✅ |
| ESP-IDF v5.4 | ✅ |
esp-idf
esp-idf 是乐鑫官方推出的物联网开发框架, 也是AtomVM依赖的工具
github: https://github.com/espressif/esp-idf
自动配置环境变量(仅限当前shell)
. ./export.sh
Erlang/OTP
Erlang运行时,负责编译erlang程序
github: https://github.com/erlang/otp
$ erl
Erlang/OTP 27 [erts-15.2.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]
Eshell V15.2.4 (press Ctrl+G to abort, type help(). for help)
1>
我的版本是27,目前最新支持到27,28可能会有问题?
rebar3
rebar3 是用来构建、管理和发布 Erlang 应用的自动化工具,用来构建AtomVM项目。
github: https://github.com/erlang/rebar3
运行一下命令检查是否安装完成
$ rebar3 -v
rebar 3.24.0 on Erlang/OTP 27 Erts 15.2.4
atomvm_packbeam
将beam文件打包成avm, 因为beam需要打包成avm文件烧录到esp32
github:https://github.com/atomvm/atomvm_packbeam
doc: https://atomvm.github.io/atomvm_packbeam/readme.html
atomvm_rebar3_plugin
它是rebar3的一个插件,用于创建和烧录基于AtomVM的Erlang程序。建议使用
doc: https://atomvm.github.io/atomvm_rebar3_plugin/readme.html
esp32 部署概述
ESP32 AtomVM 虚拟机是一个在 ESP32 平台上运行的 IDF 应用程序。作为 IDF 应用程序,它提供目标代码来启动 ESP 设备并执行 AtomVM 虚拟机代码,而 AtomVM 虚拟机代码又负责执行 Erlang/Elixir 应用程序。
AtomVM 虚拟机是用 C 语言实现的,AtomVM 二进制映像包含从 C 源文件编译而来的二进制目标代码,以及 ESP 引导加载程序和分区图,它告诉 ESP32 闪存模块是如何布局的。
AtomVM 开发者通常使用 Erlang 或 Elixir 编写应用程序。这些源文件会被编译成 BEAM 字节码,然后再组装成 AtomVM 的“packbeam”( .avm )文件。该 packbeam 文件会被烧录到 ESP32 设备上,从数据分区地址 0x210000 开始。AtomVM 启动时,会在此分区中查找第一个导出 start/0 函数的 BEAM 模块。找到该模块后,BEAM 字节码将从该位置开始执行。
ESP32 flash模块上 AtomVM 虚拟机和 Erlang/Elixir 应用程序的布局
| |
+---------------+ ----------- 0x0 | 0x1000 | 0x2000 (varies by esp32 flavor)
| boot loader | ^
+---------------+ |
| partition map | | AtomVM
+---------------+ | binary
| | | image
| AtomVM | |
| Virtual | |
| Machine | |
| | v
+---------------+ ----------- 0x210000 for thin images or
| | ^ 0x250000 for images with Elixir modules
| | |
| data | | Erlang/Elixir
| partition | | Application
| | |
| | v
+---------------+ ----------- end
+-----------------+ ------------- 0x0 | 0x1000 | 0x2000
| | ^
| boot loader | 28KB |
| | |
+-----------------+ |
| partition table | 3KB |
+-----------------+ |
| | |
| NVS | 24KB |
| | |
+-----------------+ |
| PHY_INIT | 4KB |
+-----------------+ | AtomVM
| | | binary
| | | image
| | |
| AtomVM | |
| Virtual | 1.75MB |
| Machine | |
| | |
| | |
+-----------------+ |
| boot.avm | 256-512KB v
+-----------------+ ------------- 0x210000 for Erlang only images or
| | ^ 0x250000 for images with Elixir modules
| | |
| main.avm | 1MB+ | Erlang/Elixir
| | | Application
| | |
| | v
+-----------------+ ------------- end
部署 ESP32 AtomVM 虚拟机
有两个方式部署到esp32设备
- 烧录发布好的二进制镜像
- 从源码构建镜像
将二进制镜像烧录esp32
-
清除esp32设备上的程序
$ esptool.py --chip auto --port /dev/ttyUSB0 --baud 921600 erase_flash -
下载好的镜像
-
使用esptool.py 烧录 镜像到esp32 引导程序到起始地址
$ esptool.py \ --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 \ /path/to/Atomvm-esp32-v0.6.0.img注意替换镜像路径,根据型号选择偏移量,普通的esp32就是 0x1000
ESP32 0x1000ESP32-S2 0x1000ESP32-S3 0x0ESP32-C2 0x0ESP32-C3 0x0ESP32-C6 0x0ESP32-H2 0x0ESP32-P4 0x2000
源码构建AtomVM
如果需要对AtomVM自定义开发还是推荐源码构建。这里详细介绍一下构建过程, 官方文档: https://doc.atomvm.org/v0.6.6/build-instructions.html#building-for-esp32
为了为 ESP32 构建完整的 AtomVM 映像,需要为通用 UNIX 平台构建 AtomVM, 也就是先编译一边linux平台源码
linux平台编译
进入AtomVM 根目录下执行下面命令
$ mkdir build
$ cd build
$ cmake ..
编译目标
$ make -j 8
build/src 下生成AtomVM 执行文件
开始编译esp32部分
-
首先激活esp-idf,也就是设置环境变量
$ cd <ESP-IDF-ROOT-DIR> $ . ./export.sh -
然后进入到AtomVM源码部分, 紧接着进入 esp32目录
$ cd <atomvm-source-tree-root> $ cd src/platforms/esp32 -
更新本地configure
$ idf.py set-target esp32 $ idf.py reconfigure -
开始构建
$ idf.py build -
直接烧录
直接将AtomVM烧录到esp32,
$ idf.py flash -
构建镜像
构建仅包含erlang的镜像 在
src/platform/esp32运行下面命令$ ./build/mkimage.sh在当前目录的build里面生成镜像文件,可以用第一种烧录二进制镜像的方法烧录镜像
为esp32开发AtomVM应用程序
-
新建一个文件夹作为项目目录
$ mkdir atom_project -
新建一个
rebar.config文件写入下面内容, 加载atomvm_rebar3_plugin插件{plugins, [ atomvm_rebar3_plugin ]}. -
新建我们第一个项目
$ rebar3 new atomvm_app hello_world -
接下来我们修改
hello_world/src/hello_world.erl, 不停地打印hello world!start() -> loop(), ok. loop() -> io:format("hello world!~n"), timer:sleep(1000), loop(). -
进入
hello_world项目目录下编译avm文件$ rebar3 packbeam avm 文件生成在`hello_world/_build/default/lib/hello_world.avm`
为esp32部署AtomVM应用程序
我们完成了最小应用程序hello_world 项目开发,接下来开始烧录程序到esp32
以下命令需要在激活esp-idf工具的shell中进行
$ rebar3 atomvm esp32_flash --port /dev/ttyUSB0 --baud 921600
可以使用idf.py monitor查看输出
###########################################################
### ######## ####### ## ## ## ## ## ##
## ## ## ## ## ### ### ## ## ### ###
## ## ## ## ## #### #### ## ## #### ####
## ## ## ## ## ## ### ## ## ## ## ### ##
######### ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ##
## ## ## ####### ## ## ### ## ##
###########################################################
I (821) AtomVM: Starting AtomVM revision 0.6.5
I (831) sys: Loaded BEAM partition boot.avm at address 0x1d0000 (size=262144 bytes)
I (861) network_driver: Initialized network interface
I (861) network_driver: Created default event loop
I (871) AtomVM: Found startup beam esp32init.beam
I (871) AtomVM: Starting esp32init.beam...
---
AtomVM init.
I (881) sys: Loaded BEAM partition main.avm at address 0x210000 (size=1048576 bytes)
Starting application...
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!

浙公网安备 33010602011771号