AtomVM 小型Erlang 虚拟机

AtomVM

什么是AtomVM

AtomVM 是一个从零开始实现的 BEAM 虚拟机(即 Erlang 虚拟机),专为运行在如 ESP32STM32 等微控制器上设计。它允许开发者使用 Erlang 或 Elixir 编写 IoT 应用程序,并部署到内存有限的嵌入式系统上。 同时,它也支持运行于传统系统(如 Linux、macOS),但这些平台通常用于开发或调试用途。

特性

  1. Erlang 运行时,能够运行编译后的BEAM文件
  2. 支持Erlang和Elixir类型,包括integers, strings, lists, maps, binaries, Enums, 等
  3. 具有高效的垃圾收集和数据共享的内存管理环境
  4. 支持函数式编程
  5. 平台专注于并发设计,用户可以轻松生成、监控并管理轻量级进程,使 IoT 设备具备同时处理多个任务的能力。
  6. 支持对称多处理(SMP),无需任何代码更改即可利用支持它的平台上所有可用的内核(例如 ESP32);
  7. 内置丰富的网络API,用于编写强大的物联网程序
  8. 内置标准设备协议接口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设备

  1. 烧录发布好的二进制镜像
  2. 从源码构建镜像
将二进制镜像烧录esp32
  1. 清除esp32设备上的程序

    $ esptool.py --chip auto --port /dev/ttyUSB0 --baud 921600 erase_flash
    
  2. 下载好的镜像

    github: https://github.com/atomvm/AtomVM/releases

  3. 使用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 0x1000
    ESP32-S2 0x1000
    ESP32-S3 0x0
    ESP32-C2 0x0
    ESP32-C3 0x0
    ESP32-C6 0x0
    ESP32-H2 0x0
    ESP32-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部分
  1. 首先激活esp-idf,也就是设置环境变量

    $ cd <ESP-IDF-ROOT-DIR>
    $ . ./export.sh
    
  2. 然后进入到AtomVM源码部分, 紧接着进入 esp32目录

    $ cd <atomvm-source-tree-root>
    $ cd src/platforms/esp32
    
  3. 更新本地configure

    $ idf.py set-target esp32
    $ idf.py reconfigure
    
  4. 开始构建

    $ idf.py build
    
  5. 直接烧录

    直接将AtomVM烧录到esp32,

    $ idf.py flash
    
  6. 构建镜像

    构建仅包含erlang的镜像 在src/platform/esp32运行下面命令

    $ ./build/mkimage.sh
    

    在当前目录的build里面生成镜像文件,可以用第一种烧录二进制镜像的方法烧录镜像

为esp32开发AtomVM应用程序

  1. 新建一个文件夹作为项目目录

    $ mkdir atom_project
    
  2. 新建一个rebar.config文件写入下面内容, 加载 atomvm_rebar3_plugin插件

    {plugins, [
      atomvm_rebar3_plugin
    ]}.
    
  3. 新建我们第一个项目

    $ rebar3 new atomvm_app hello_world
    
  4. 接下来我们修改hello_world/src/hello_world.erl, 不停地打印hello world!

    start() ->
      loop(),
      ok.
    
    loop() ->
      io:format("hello world!~n"),
      timer:sleep(1000),
      loop().
    
  5. 进入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!
posted @ 2025-07-24 18:02  GWZHG  阅读(59)  评论(0)    收藏  举报