Example Programs
AtomVM includes a collection of useful examples for getting started. This section describes what these examples do, and how to run them, for example, on an ESP32 device.
Erlang Examples
Erlang examples may be run in the UNIX shell or on supported microcontroller devices.
hello_world
This example program prints the string “Hello World” and quits.
Command line
The hello_world.avm
file will get created as part of a build. This file may be supplied as an argument to the AtomVM
command:
shell$ ./src/AtomVM ./examples/erlang/hello_world.avm
hello_world
Return value: ok
udp_server
This example program listens on UDP port 44444 and will print information about the received message, including the source IP, (ephemeral) source port, and packet received, to the console.
Command line
The udp_server.avm
file will get created as part of a build. This file may be supplied as an argument to the AtomVM
command:
shell$ ./src/AtomVM ./examples/erlang/udp_server.avm
Opened UDP socket on "0.0.0.0:44404".
Waiting to receive data...
You can send UDP packets to the AtomVM instance using netcat
(or nc
on some platforms), in a separate terminal window:
shell$ nc -u localhost 44404
This command will wait for you to enter a line of text, e.g.,
testing 1 2 3
In the AtomVM termianl window, you see:
Received UDP packet <<116,101,115,116,105,110,103,32,49,32,50,32,51,10>> from "127.0.0.1:55261"
Waiting to receive data...
Note. Netcat appends a newline character at the end of the input, so the packet binary does not display as printable text.
udp_client
This example program send the packet of data (“:アトムVM”) over UDP to port 44444 on the loopback address every 5 seconds, in a loop. The program will print a period (.
) to the console, every time it sends a message.
This command may be used in tandem with the udp_server
program to illustrate sending messages between AtomVM processes over UDP.
Command line
The udp_client.avm
file will get created as part of a build. This file may be supplied as an argument to the AtomVM
command:
shell$ ./src/AtomVM ./examples/erlang/udp_client.avm
Opened UDP socket on "0.0.0.0:63665".
Sent <<58,-94,-56,-32,54,45>>
Sent <<58,-94,-56,-32,54,45>>
Sent <<58,-94,-56,-32,54,45>>
...
If you are running the udp_server
program, you should see messages like the following printed to the console:
Received UDP packet <<58,-94,-56,-32,54,45>> from "127.0.0.1:63665"
Waiting to receive data...
Received UDP packet <<58,-94,-56,-32,54,45>> from "127.0.0.1:63665"
Waiting to receive data...
Received UDP packet <<58,-94,-56,-32,54,45>> from "127.0.0.1:63665"
Waiting to receive data...
...
Note. AtomVM does not currently treat characters outside of the printable ASCII character set as printable characters.
tcp_server
This example program listens on TCP port 44404 and accept connections on that port. Once accepted, it will wait for packets to be sent from the client. Once received, the server will print the packet received to the console, and then echo the packet back to the calling client.
Command line
The tcp_server.avm
file will get created as part of a build. This file may be supplied as an argument to the AtomVM
command:
shell$ ./src/AtomVM ./examples/erlang/tcp_server.avm
Listening on "0.0.0.0:44404".
Waiting to accept connection...
You can send TCP packets to the AtomVM instance using netcat
(or nc
on some platforms), in a separate terminal window:
shell$ nc localhost 44404
This will open a TCP connection to the tcp_server
, and you should see the following on the console:
Accepted connection. local: "127.0.0.1:44404" peer: "127.0.0.1:56628"
Waiting to receive data...
Waiting to accept connection...
The netcat command will wait for you to enter a line of text, e.g.,
testing 1 2 3
In the AtomVM terminal window, you see:
Received packet [116,101,115,116,105,110,103,32,49,32,50,32,51,10] from "127.0.0.1:56628". Echoing back...
Waiting to receive data...
Note. Netcat appends a newline character at the end of the input, so the packet binary does not display as printable text.
You may enter as much data as you like, though by default, the packet size will be limited to 128 bytes.
If you stop the netcat
command (via ^C), you should
Connection closed.
printed to the AtomVM console.
Note that you can have multiple, concurrent TCP/IP connections to your AtomVM server.
tcp_client
This example program send the packet of data (“:アトムVM”) over TCP to port 44404 on the loopback address every 1 second, in a loop. The program will wait for a response from the server, before proceeding.
This command may be used in tandem with the tcp_server
program to illustrate sending messages between AtomVM processes over TCP.
Note. You will need to change the
Address
variable in thetcp_client.erl
program in order to test against an AtomVM server running on a different host or device.
Command line
The tcp_client.avm
file will get created as part of a build. This file may be supplied as an argument to the AtomVM
command:
shell$ ./src/AtomVM ./examples/erlang/tcp_client.avm
Connected to "127.0.0.1:44404" from "127.0.0.1:56741"
Sent <<58,-94,-56,-32,54,45>> to "127.0.0.1:44404"
Received [58,162,200,224,54,45] from "127.0.0.1:44404"
Sent <<58,-94,-56,-32,54,45>> to "127.0.0.1:44404"
Received [58,162,200,224,54,45] from "127.0.0.1:44404"
Sent <<58,-94,-56,-32,54,45>> to "127.0.0.1:44404"
Received [58,162,200,224,54,45] from "127.0.0.1:44404"
...
If you are running the tcp _server
program, you should see messages like the following printed to the console:
Accepted connection. local: "127.0.0.1:44404" peer: "127.0.0.1:56741"
Waiting to receive data...
Waiting to accept connection...
Received packet [58,162,200,224,54,45] from "127.0.0.1:56741". Echoing back...
Waiting to receive data...
Received packet [58,162,200,224,54,45] from "127.0.0.1:56741". Echoing back...
Waiting to receive data...
Received packet [58,162,200,224,54,45] from "127.0.0.1:56741". Echoing back...
Waiting to receive data...
...
Note. AtomVM does not currently treat characters outside of the printable ASCII character set as printable characters.
You may run multiple concurrent instances of the tcp_client
against a single tcp_server
instance.
ESP32 Examples
AtomVM includes examples that are specifically designed for the ESP32 and other microcontrollers.
Flashing AtomVM Examples for ESP32
In order to run the ESP32 examples, you will need to flash the example AVM files that are created as part of the build to your device.
In the remainder of this document, we assume the flash.sh
script, located in the tools/dev
directory of the AtomVM source tree.
Note. You must set the
ESP_IDF
environment variable to the root directory of the ESP IDF SDK installation on your development machine.
You can control the serial port and baud rate via the FLASH_SERIAL_PORT
and FLASH_BAUD_RATE
environment variables, e.g.,
shell$ export FLASH_SERIAL_PORT="/dev/tty.SLAB_USBtoUART"
shell$ export FLASH_BAUD_RATE=921600
The default values for these variables, if not set, are /dev/ttyUSB0
and 115200
, respectively.
Note. Experiment with baud rates (e.g., 921600). You may find you can shorten the flash-debug-flash cycle with higher rates.
You can montor the console output of these examples by issuing the monitor
target to make
, in the src/platforms/esp32
directory of the AtomVM source tree:
shell$ make monitor
MONITOR
--- WARNING: Serial ports accessed as /dev/tty.* will hang gdb if launched.
--- Using /dev/cu.SLAB_USBtoUART instead...
--- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57
...
blink
The blink
example will turn the blue LED on an ESP32 SoC (pin 2) on and off, once every second.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/blink.avm
...
Hard resetting via RTS pin...
You should see the blue LED turn on and off on your ESP32 device.
esp_random
This demo program illustrates use of the ESP32 random
, restart
, and reset_reason
functions.
The program will generate a random binary of a random size (at most 127 bytes) every 5 seconds. If a 0-length byte sequence is generated (1:128 probability), the ESP will restart.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/esp_random.avm
...
Hard resetting via RTS pin...
You should see something like the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: esp_random.beam...
---
Reset reason: esp_rst_poweron
Random bytes: <<71,13,221,24,8,15,...,197,120,152,205>>
Random bytes: <<37,155,124,177,44,141,40,106,...,43,48,62,109,2,78,39,107>>
Random bytes: <<217,210,239,183,...,78,68,253,146,212,71,17,208,219,126,240,218,34,0,152,80,20,166,194,106>>
Random bytes: <<112,77,123,249,162,...,238,237,128,227,58,29,64,74>>
...
<<"">>
ets Jun 8 2016 00:22:57
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: esp_random.beam...
---
esp_rst_sw
Random bytes: <<155,174,204,143,232,202,136,...,118,177,77,230,10,21,72,91,92,160,198,115,249,217,206,52,102,32,230>>
...
esp_nvs
This demo program illustrates the use of ESP32 non-volatile storage (NVS).
The program will store the number of times the device has been rebooted, along with the start time, in NVS.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/esp_nvs.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: esp_nvs.beam...
---
Saving count 0 to NVS...
Reset device to increment.
AtomVM finished with return value = ok
going to sleep forever..
Hit the reset button on your device, and the ESP device will reboot, and display something like the following:
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: esp_nvs.beam...
---
Saving count 1 to NVS...
Reset device to increment.
AtomVM finished with return value = ok
going to sleep forever..
reformat_nvs
This demo program will reformat the non-volatile storage (NVS) partition.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/reformat_nvs.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: esp_nvs.beam...
---
Warning: Reformatted NVS partition!
AtomVM finished with return value = ok
going to sleep forever..
The NVS partition on your ESP device should be reformatted.
Note. This program will irrevocably delete all existing key-values stored on the NVS partition. Use with caution.
set_network_config
This demo program can be used to set the WIFI credentials in NVS. Setting WIFI credentials in NVS can greatly simplify the task of running ESP programs that require connectivity to WIFI networks.
Note. Credentials are stored unencrypted and in plaintext and should not be considered secure. Future versions may use encrypted NVS storage.
Edit the sta_network_config.erl
program and set the Ssid
binary with your WIFI AP SSID, and Psk
binary with the password used to access your WIFI network. Save the file, rebuild, and flash to your device:
shell$ make
...
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/sta_network_config.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: set_network_config.beam...
---
{atomvm,sta_ssid,<<"myssid">>}
{atomvm,sta_psk,<<"xxxxxx">>}
AtomVM finished with return value = ok
going to sleep forever..
You may now run programs that use your WIFI network (see below) without needing to enter WIFI credentials.
sta_network
The sta_network
example will connect to your local WiFi network and obtain and IP address. Once a connection is established, a connected
message will be displayed. Once an IP address is obtained, the device IP address, netmask, and gateway will be displayed on the console.
Note. AtomVM currently only supports station mode (STA).
Note. AtomVM currently only supports IPv4 addresses.
Note. If you have not set WIFI credentials in NVS (see above), you will need to edit the
examples/erlang/esp32/sta_network.erl
source file and set thessid
andpsk
parameters to match your local WiFi network, and then rebuild the example.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/sta_network.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Starting: sta_network.beam...
---
I (220) wifi: wifi driver task: 3ffc3b54, prio:23, stack:3584, core=0
I (220) wifi: wifi firmware version: d5da5a5
I (220) wifi: config NVS flash: enabled
I (220) wifi: config nano formatting: disabled
I (230) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (240) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (270) wifi: Init dynamic tx buffer num: 32
I (270) wifi: Init data frame dynamic rx buffer num: 32
I (270) wifi: Init management frame dynamic rx buffer num: 32
I (270) wifi: Init static rx buffer size: 1600
I (280) wifi: Init static rx buffer num: 10
I (280) wifi: Init dynamic rx buffer num: 32
I (290) NETWORK: starting wifi: SSID: [myssid], password: [XXXXXXXX].
I (360) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (370) wifi: mode : sta (3c:71:bf:84:d9:08)
I (370) NETWORK: SYSTEM_EVENT_STA_START received.
I (490) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1
I (1470) wifi: state: init -> auth (b0)
I (1480) wifi: state: auth -> assoc (0)
I (1490) wifi: state: assoc -> run (10)
I (1500) wifi: connected with myssid, channel 1
I (1500) wifi: pm start, type: 1
I (1500) NETWORK: SYSTEM_EVENT_STA_CONNECTED received.
I (3690) event: sta ip: 192.168.1.236, mask: 255.255.255.0, gw: 192.168.1.1
I (3690) NETWORK: SYSTEM_EVENT_STA_GOT_IP: 192.168.1.236
connected
{{192,168,1,236},{255,255,255,0},{192,168,1,1}}
udp_server_blink
The udp_server_blink
example will connect to your local WiFi network and obtain and IP address. It will then start a UDP server on port 44444. When a UDP message is received, the blue LED on the ESP32 SoC (pin 2) will toggle on and off.
Note. AtomVM currently only supports station mode (STA).
Note. AtomVM currently only supports IPv4 addresses.
Note. You will need to edit the
examples/erlang/esp32/udp_server_blink.erl
source file and set thessid
andpsk
parameters to match your local WiFi network, and then rebuild the example.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/udp_server_blink.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
...
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: udp_server_blink.beam...
---
I (222) wifi: wifi driver task: 3ffc3de8, prio:23, stack:3584, core=0
I (222) wifi: wifi firmware version: d5da5a5
I (222) wifi: config NVS flash: enabled
I (232) wifi: config nano formatting: disabled
I (232) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (242) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (312) wifi: Init dynamic tx buffer num: 32
I (312) wifi: Init data frame dynamic rx buffer num: 32
I (312) wifi: Init management frame dynamic rx buffer num: 32
I (312) wifi: Init static rx buffer size: 1600
I (322) wifi: Init static rx buffer num: 10
I (322) wifi: Init dynamic rx buffer num: 32
I (332) NETWORK: starting wifi: SSID: [myssid], password: [XXXXXXXX].
I (442) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (442) wifi: mode : sta (3c:71:bf:84:d9:08)
I (442) NETWORK: SYSTEM_EVENT_STA_START received.
I (572) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1
I (1552) wifi: state: init -> auth (b0)
I (1552) wifi: state: auth -> assoc (0)
I (1562) wifi: state: assoc -> run (10)
I (1582) wifi: connected with myssid, channel 1
I (1582) wifi: pm start, type: 1
I (1582) NETWORK: SYSTEM_EVENT_STA_CONNECTED received.
I (2212) event: sta ip: 192.168.1.236, mask: 255.255.255.0, gw: 192.168.1.1
I (2212) NETWORK: SYSTEM_EVENT_STA_GOT_IP: 192.168.1.236
Acquired IP address: "192.168.1.236" Netmask: "255.255.255.0" Gateway: "192.168.1.1"
Opened UDP socket on "0.0.0.0:44404".
Waiting to receive data...
You can send UDP packets to the AtomVM instance using netcat
(or nc
on some platforms), in a separate terminal window:
shell$ nc -u 192.168.1.236 44404
Every time you enter a line of text, the blue LED on the ESP32 SoC (pin 2) should toggle on and off, and you should see output on the console, such as
Received UDP packet <<100,115,102,115,100,10>> from "192.168.1.237:53291"
Waiting to receive data...
tcp_server_blink
The tcp_server_blink
example will connect to your local WiFi network and obtain and IP address. It will then start a TCP server on port 44404. When a TCP message is received, the blue LED on the ESP32 SoC (pin 2) will toggle on and off.
Note. AtomVM currently only supports station mode (STA).
Note. AtomVM currently only supports IPv4 addresses.
Note. You will need to edit the
examples/erlang/esp32/tcp_server_blink.erl
source file and set thessid
andpsk
parameters to match your local WiFi network, and then rebuild the example.
Flash the example program to your device as follows:
shell$ ./tools/dev/flash.sh build/examples/erlang/esp32/tcp_server_blink.avm
...
Hard resetting via RTS pin...
You should see the following output when monitoring the ESP32 output (truncated for brevity):
shell$ make monitor
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: tcp_server_blink.beam...
---
start
I (296) wifi: wifi driver task: 3ffc650c, prio:23, stack:3584, core=0
I (296) wifi: wifi firmware version: 9415913
I (296) wifi: config NVS flash: enabled
I (296) wifi: config nano formatting: disabled
I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (346) wifi: Init dynamic tx buffer num: 32
I (346) wifi: Init data frame dynamic rx buffer num: 32
I (346) wifi: Init management frame dynamic rx buffer num: 32
I (346) wifi: Init static rx buffer size: 1600
I (356) wifi: Init static rx buffer num: 10
I (356) wifi: Init dynamic rx buffer num: 32
I (366) NETWORK: starting wifi: SSID: [myssid], password: [XXXXXXXX].
I (446) phy: phy_version: 4008, c9ae59f, Jan 25 2019, 16:54:06, 0, 0
I (446) wifi: mode : sta (3c:71:bf:84:d9:08)
I (446) NETWORK: SYSTEM_EVENT_STA_START received.
I (1176) wifi: n:6 0, o:1 0, ap:255 255, sta:6 0, prof:1
I (2156) wifi: state: init -> auth (b0)
I (2166) wifi: state: auth -> assoc (0)
I (2166) wifi: state: assoc -> run (10)
I (2196) wifi: connected with myssid, channel 6
I (2196) wifi: pm start, type: 1
I (2196) NETWORK: SYSTEM_EVENT_STA_CONNECTED received.
I (2746) event: sta ip: 192.168.1.236, mask: 255.255.255.0, gw: 192.168.1.1
I (2746) NETWORK: SYSTEM_EVENT_STA_GOT_IP: 192.168.1.236
Acquired IP address: "192.168.1.236" Netmask: "255.255.255.0" Gateway: "192.168.1.1"
Listening on "0.0.0.0:44404".
Waiting to accept connection...
You can send TCP packets to the AtomVM instance using netcat
(or nc
on some platforms), in a separate terminal window, e.g.,
shell$ nc 192.168.1.236 44404
On the ESP32 console, you should see:
Accepted connection. local: "192.168.1.236:44404" peer: "192.168.1.237:55275"
Waiting to receive data...
Waiting to accept connection...
Every time you enter a line of text, the blue LED on the ESP32 SoC (pin 2) should toggle on and off, and the data you entered should get echoed back to the netcat
console.
On the ESP32 console, you should see:
Received packet [115,100,102,115,100,102,10] from "192.168.1.237:55275". Echoing back...
Waiting to receive data...
every time a packet is sent to the server.