From 9e346ceff83dd93d313d1fd12ab17285067450cc Mon Sep 17 00:00:00 2001 From: Masatoshi Nishiguchi <7563926+mnishiguchi@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:48:51 +0900 Subject: [PATCH] Add Elixir WiFi example --- elixir/README.md | 1 + elixir/Wifi/README.md | 28 +++++++++ elixir/Wifi/config/config.exs | 34 ++++++++++ elixir/Wifi/lib/wifi.ex | 113 ++++++++++++++++++++++++++++++++++ elixir/Wifi/mix.exs | 51 +++++++++++++++ 5 files changed, 227 insertions(+) create mode 100644 elixir/Wifi/README.md create mode 100644 elixir/Wifi/config/config.exs create mode 100644 elixir/Wifi/lib/wifi.ex create mode 100644 elixir/Wifi/mix.exs diff --git a/elixir/README.md b/elixir/README.md index 07acf74..45dea9d 100644 --- a/elixir/README.md +++ b/elixir/README.md @@ -17,6 +17,7 @@ Some example programs can only run on specific platform. The following table su | Blinky | ✅ | ✅ | ✅ | ✦ | ❌ | | HelloWorld | ✅ | ✅ | ✅ | ✅ | ✅ | | LEDC_Example | ✅ | ❌ | ❌ | ❌ | ❌ | +| WifiExample | ✅ | ❌ | ❌ | ✅ | ❌ | ✦ Works, but requires editing to use onboard LED, see the README in the examples directory. diff --git a/elixir/Wifi/README.md b/elixir/Wifi/README.md new file mode 100644 index 0000000..7c1faee --- /dev/null +++ b/elixir/Wifi/README.md @@ -0,0 +1,28 @@ + + +# `Wifi` Application + +Welcome to the `Wifi` AtomVM application. + +The `Wifi` AtomVM application demonstrates how to configure an ESP32 device for both Station (STA) and Access Point (AP) modes, allowing an ESP32 device to join an existing WiFi network or to serve as a WiFi access point for other devices. + +For more information about programming on the AtomVM platform, see the [AtomVM Programmers Guide](https://doc.atomvm.org/latest/programmers-guide.html). + +For general information about building and executing Elixir AtomVM example programs, see the Elixir example program [README](../README.md). + +> **IMPORTANT** If you are running this example program on a device that +> supports WiFi (e.g., the ESP32), you must first edit `config/config.exs` and +> set the WiFi Station (STA) SSID and PSK before building this application: +> +> ```elixir +> config :wifi, +> sta: [ +> dhcp_hostname: "my_device_name", +> ssid: "my_sta_ssid", +> psk: "my_sta_psk" +> ] +> ``` diff --git a/elixir/Wifi/config/config.exs b/elixir/Wifi/config/config.exs new file mode 100644 index 0000000..b0d2780 --- /dev/null +++ b/elixir/Wifi/config/config.exs @@ -0,0 +1,34 @@ +# +# This file is part of AtomVM. +# +# Copyright 2026 Masatoshi Nishiguchi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +import Config + +config :wifi, + ap: [ + ssid: "my_ap_ssid", + psk: "my_ap_psk", + ap_channel: 1, + max_connections: 4 + ], + sta: [ + dhcp_hostname: "my_device_name", + ssid: "my_sta_ssid", + psk: "my_sta_psk" + ] diff --git a/elixir/Wifi/lib/wifi.ex b/elixir/Wifi/lib/wifi.ex new file mode 100644 index 0000000..03639f0 --- /dev/null +++ b/elixir/Wifi/lib/wifi.ex @@ -0,0 +1,113 @@ +# +# This file is part of AtomVM. +# +# Copyright 2026 Masatoshi Nishiguchi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +defmodule Wifi do + @moduledoc """ + Wi-Fi example for Elixir. + """ + + @compile {:no_warn_undefined, :atomvm} + @compile {:no_warn_undefined, :network} + + @sta_config Application.compile_env!(:wifi, :sta) + @ap_config Application.compile_env!(:wifi, :ap) + + @clock_interval_ms 5_000 + + def start do + case verify_platform(:atomvm.platform()) do + :ok -> + start_network() + + error -> + error + end + end + + defp start_network() do + network_config = [ + ap: + [ + ap_started: &ap_started/0, + sta_connected: &sta_connected/1, + sta_ip_assigned: &sta_ip_assigned/1, + sta_disconnected: &sta_disconnected/1 + ] ++ @ap_config, + sta: + [ + connected: &connected/0, + got_ip: &got_ip/1, + disconnected: &disconnected/0 + ] ++ @sta_config, + sntp: [ + host: "time-d-b.nist.gov", + synchronized: &sntp_synchronized/1 + ] + ] + + case :network.start(network_config) do + {:ok, _pid} -> + IO.puts("wifi: network started") + Process.sleep(:infinity) + + error -> + error + end + end + + defp ap_started, do: IO.puts("AP started") + + defp sta_connected(mac), do: IO.puts("STA connected with mac #{inspect(mac)}") + + defp sta_disconnected(mac), do: IO.puts("STA disconnected with mac #{inspect(mac)}") + + defp sta_ip_assigned(address), do: IO.puts("STA assigned address #{inspect(address)}") + + defp connected, do: IO.puts("STA connected") + + defp disconnected, do: IO.puts("STA disconnected") + + defp got_ip(ip_info) do + IO.puts("Got IP: #{inspect(ip_info)}") + _clock_pid = spawn(fn -> clock_loop() end) + :ok + end + + defp sntp_synchronized(timeval = {_tv_sec, _tv_usec}) do + IO.puts("Synchronized time with SNTP server #{inspect(timeval)}") + end + + defp verify_platform(:esp32), do: :ok + defp verify_platform(:pico), do: :ok + defp verify_platform(platform), do: {:error, {:unsupported_platform, platform}} + + defp clock_loop do + {{year, month, day}, {hour, minute, second}} = :erlang.universaltime() + ms = :erlang.system_time(:millisecond) + + :io.format( + ~c"Date: ~4..0B/~2..0B/~2..0B ~2..0B:~2..0B:~2..0B (~pms)~n", + [year, month, day, hour, minute, second, ms] + ) + + Process.sleep(@clock_interval_ms) + clock_loop() + end +end diff --git a/elixir/Wifi/mix.exs b/elixir/Wifi/mix.exs new file mode 100644 index 0000000..c28f904 --- /dev/null +++ b/elixir/Wifi/mix.exs @@ -0,0 +1,51 @@ +# +# This file is part of AtomVM. +# +# Copyright 2026 Masatoshi Nishiguchi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +defmodule Wifi.MixProject do + use Mix.Project + + def project do + [ + app: :wifi, + version: "0.1.0", + elixir: "~> 1.17", + start_permanent: Mix.env() == :prod, + deps: deps(), + atomvm: [ + start: Wifi, + flash_offset: 0x250000 + ] + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:exatomvm, git: "https://github.com/atomvm/ExAtomVM/", branch: "main"} + ] + end +end