DEV Community

Cover image for Getting Started with AtomVM: Setup with Prebuilt Firmware (ESP32-S3, September 2025)
Masatoshi Nishiguchi
Masatoshi Nishiguchi

Posted on

Getting Started with AtomVM: Setup with Prebuilt Firmware (ESP32-S3, September 2025)

I previously tried using AtomVM, a lightweight virtual machine that lets you run Elixir (and Erlang) applications directly on microcontrollers like the ESP32-S3, but honestly, I rushed through the process without fully understanding the underlying details — especially around firmware setup.

This time around, I wanted to go back and properly understand how to set things up, focusing specifically on getting started with AtomVM using a prebuilt firmware image. The goal is to simplify the onboarding experience without building anything from source.

This post doesn’t dig into advanced features. Instead, it’s a hands-on guide to get your ESP32-S3 board up and running with AtomVM, so you can start running Elixir code on real hardware as quickly as possible.

Originally published in Japanese: AtomVM 入門: ビルド済みイメージを活用した環境構築 (2025年9月)


Target Environment and Devices

Here's what I used for this setup.

Target Board

Host Machine

  • Linux (Debian-based — LMDE6)
  • Elixir 1.17 with Erlang/OTP 27
  • AtomVM v0.6.6 (prebuilt firmware)
  • esptool 5.0 — official flashing tool for ESP32
  • picocom 3.1 — for serial monitoring
  • Python 3.13 — required by esptool

Notes

  • esptool is a Python tool and requires Python 3.x.
  • Since we're using a prebuilt AtomVM image, you do not need to install ESP-IDF.
  • For compatibility info (e.g. which Elixir & OTP versions are supported by AtomVM), refer to the official release notes.

Installing esptool

esptool is the official command-line utility for flashing firmware to ESP32 boards.

We'll install it using pip, which makes it easy to manage and update.

pip install "esptool>=5,<6"
Enter fullscreen mode Exit fullscreen mode
  • Version 5.x introduces hyphenated options instead of older underscore syntax.
  • Make sure Python 3.x is installed on your system.

Checking the Serial Port

When you connect your ESP32-S3 via USB, Linux will create a new serial device (e.g., /dev/ttyACM0). You’ll need this port name when flashing firmware or uploading .avm files.

On Linux, you can run:

dmesg | grep tty
Enter fullscreen mode Exit fullscreen mode

Output might look like:

[12345.678901] cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device
Enter fullscreen mode Exit fullscreen mode

In this case, use /dev/ttyACM0.

You’re typically looking for something like /dev/ttyACM0 or /dev/ttyUSB0, depending on your board and USB-to-serial chip.


Verifying esptool and Board Connection

Once esptool is installed, you can check that:

  1. It's correctly installed
  2. It can communicate with your ESP32-S3 board
# Check version
esptool.py version

# Query flash chip information
esptool.py -p /dev/ttyACM0 flash-id
Enter fullscreen mode Exit fullscreen mode

If this succeeds, you're ready to start flashing firmware.


Downloading and Flashing AtomVM Firmware

We’ll be using a prebuilt binary from the AtomVM GitHub Releases, so there’s no need to build anything from source.

We’ll use the official prebuilt AtomVM image (v0.6.6) for ESP32-S3.

Download the image

# Destination folder is arbitrary
mkdir -p $HOME/Projects/atomvm && cd $_

curl -LO https://github.com/atomvm/AtomVM/releases/download/v0.6.6/AtomVM-esp32s3-elixir-v0.6.6.img
Enter fullscreen mode Exit fullscreen mode

Erase flash (recommended)

esptool.py --chip auto --port /dev/ttyACM0 --baud 921600 erase-flash
Enter fullscreen mode Exit fullscreen mode

Write AtomVM firmware

esptool.py --chip auto --port /dev/ttyACM0 --baud 921600   write_flash 0x0 $HOME/Projects/atomvm/AtomVM-esp32s3-elixir-v0.6.6.img
Enter fullscreen mode Exit fullscreen mode

According to the AtomVM docs, the bootloader offset for ESP32-S3 is 0x0.


Building and Flashing an Elixir Application

With AtomVM running on your ESP32-S3, you can now deploy an actual Elixir application to it!

In this section, we'll use the classic Blinky sample to flash an .avm (AtomVM bytecode) file that blinks the onboard LED.

Get the sample project

Clone the official atomvm_examples repo:

cd ~/Projects/atomvm  # Or any directory you like

git clone https://github.com/atomvm/atomvm_examples.git
cd atomvm_examples/elixir/Blinky
Enter fullscreen mode Exit fullscreen mode

Install dependencies (mainly exatomvm):

mix deps.get
Enter fullscreen mode Exit fullscreen mode

Adjust GPIO pin for XIAO ESP32-S3

By default, the sample blinks GPIO 2 — but XIAO’s onboard LED is wired to GPIO 21 (active low). Update lib/blinky.ex accordingly:

defmodule Blinky do
  @pin 21

  def start() do
    :gpio.set_pin_mode(@pin, :output)
    loop(@pin, :low)  # LOW = LED ON (active-low)
  end

  defp loop(pin, level) do
    :io.format(~c"Setting pin ~p ~p~n", [pin, level])
    :gpio.digital_write(pin, level)
    Process.sleep(1000)
    loop(pin, toggle(level))
  end

  defp toggle(:high), do: :low
  defp toggle(:low),  do: :high
end
Enter fullscreen mode Exit fullscreen mode

Configure mix.exs with flash offset

You must set the .avm flash offset to 0x250000. Without this, the ESP32-S3 won’t recognize your app at boot.

def atomvm do
  [
    start: SampleApp,
    flash_offset: 0x250000  # important!
  ]
end
Enter fullscreen mode Exit fullscreen mode

Package the app

Build the .avm files using:

mix atomvm.packbeam
Enter fullscreen mode Exit fullscreen mode

This generates .avm files such as:

  • Blinky.avm
  • deps.avm
  • priv.avm

Flash the app to ESP32-S3

Finally, push your app:

mix atomvm.esp32.flash --port /dev/ttyACM0
Enter fullscreen mode Exit fullscreen mode

This will write the .avm files to the right spot, without touching the AtomVM firmware.

You should now see the LED start blinking 🎉


Viewing Logs over Serial

Your Blinky app doesn't just blink the LED — it also prints log messages via serial.

To view these logs in real time, you'll need a serial monitor. We'll use picocom — a simple, lightweight tool that works great on Linux.

To start monitoring logs from your ESP32-S3 board:

picocom /dev/ttyACM0 --baud 115200
Enter fullscreen mode Exit fullscreen mode

This opens the serial console. You should immediately start seeing output like:

These messages come from your Elixir code (:io.format), showing the pin toggling every second.

To exit picocom, press: Ctrl+A then Ctrl+X.


Wrapping Up

In this post, we walked through the full process of setting up an ESP32-S3 board to run Elixir code using AtomVM — no custom firmware builds required.

This setup makes it easy to start tinkering with embedded Elixir, even without diving deep into ESP-IDF or AtomVM internals.

Next steps might include integrating with sensors, displaying data, or connecting to the cloud via MQTT.

Hopefully, this gave you a smooth start with AtomVM on ESP32-S3!


Links


Top comments (0)