Skip to main content
nerves_hub_link_atomvm_esp32 is a NervesHub device agent for AtomVM on the ESP32, written in Erlang. nerves_hub_link_atomvm_esp32_ex is an Elixir layer over it — a convenience wrapper rather than an abstraction, so the two can be mixed freely.
A device needs AtomVM built from source. The WebSocket transport is an ESP-IDF component, so a stock AtomVM build cannot reach NervesHub at all. This is a prerequisite for connecting, not for an optional feature.

Building the VM

Three changes to an AtomVM build, each buying one thing: Only the first is mandatory. Leaving the partitions out gives a device that connects, reports what it is running, answers the console and carries the extensions — it simply has nowhere to put an update other than the partition it is executing from. Leaving libsodium out means a device configured with firmware_keys reports verification_unavailable rather than accepting an update it cannot check. Both are supported choices. The agent ships the files the VM needs in priv/atomvm. The repository’s building the VM section has the full sequence.
Use idf.py set-target rather than reconfigure when adding the transport component. CMake caches its component list, so a plain idf.py build afterwards reports success without ever compiling the new component — a silent failure. Supported ESP-IDF versions are v5.2 to v5.5; AtomVM does not build against v6.
From Elixir, a mix task does the VM preparation for you:

Installing

The transport stays a git dependency because it is not published — it is an ESP-IDF component first and an Erlang library second, and the half that matters is compiled into the VM rather than fetched by rebar3.

Connecting

There is no URL in either call — where it connects is worked out for you.

Handling events

The agent sends {nerves_hub, Event} to the calling process, or in Elixir to whichever process is named as its :handler, so a device is naturally written as a GenServer receiving them in handle_info/2. And the device reports back:

Partition sizing

main.avm and alt.avm must be the same size, since either has to hold the archive. Read the offsets off the device rather than from a checkout — writing from a stale copy of the table lands the application inside boot.avm, and the only symptom is Failed app start: invalid_avm. Both repositories document the reasoning behind each partition size. In short: boot.avm is larger than stock because an Elixir device needs elixir_esp32boot.avm, and factory is larger because libsodium adds around 140K. An Erlang device without signature verification can use the stock sizes for both.