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



This content originally appeared on DEV Community and was authored by Masatoshi Nishiguchi

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.

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"
  • 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

Output might look like:

[12345.678901] cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device

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

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

Erase flash (recommended)

esptool.py --chip auto --port /dev/ttyACM0 --baud 921600 erase-flash

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

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

Install dependencies (mainly exatomvm):

mix deps.get

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

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

Package the app

Build the .avm files using:

mix atomvm.packbeam

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

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

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


This content originally appeared on DEV Community and was authored by Masatoshi Nishiguchi