VintageNet
Overview
Nerves uses VintageNet to manage network interfaces. VintageNet takes human-readable network configuration descriptions and transforms them into instructions for the Linux kernel, network daemons and other IP networking code whether it’s in Elixir, Erlang, or C.
A second important feature of VintageNet is to monitor network interfaces for internet-connectivity and failures. It automatically switches between network interfaces to prioritize fast and inexpensive interfaces like wired Ethernet, but fall back to slower interfaces like cellular connections when necessary.
Technologies
VintageNet
serves as the configuration tooling for various networking
technologies, such as Ethernet and WiFi, which must be included as a dependency
in your project alongside :vintage_net
There are many officially supported technologies, or you can implement your own
with the VintageNet.Technology
behavior:
- VintageNetWiFi
- VintageNetEthernet
- VintageNetDirect (most typically USB)
- VintageNetQMI (cellular)
Connecting WiFi
Some technologies, such as VintageNetWiFi
provide convenience functions for
quick configurations at runtime. Use the inputs below to specify your SSID and
password.
If you haven’t connected already, checkout Configure WiFi
Properties
VintageNet
maintains a key/value store for networking information. This can
be queried and subscribed to for changes. For example, the overall network
connection (internet-connected, lan-connected or disconnected) is stored under
the "connection"
key:
VintageNet.get(["connection"])
This notebook only shows a fraction of properties. For a more complete reference, see VintageNet#Properties.
Network interface-specific keys are scoped by interface name. I.e.,
["interface", interface_name, ..]
. This shows the IP addresses on an
interface. If you’re connected via a USB cable, replace "wlan0"
with
"usb0"
. For wired Ethernet, use "eth0"
. The addresses are all on Erlang
tuple form and can be converted to familiar strings using :inet.ntoa/1
.
VintageNet.get(["interface", "wlan0", "addresses"])
Each VintageNet technology has its own set of properties and these are documented in the corresponding library. For Wifi, see VintageNetWiFi’s property documentation.
Sometimes it’s helpful to browse all keys using VintageNet.get_by_prefix/1
:
VintageNet.get_by_prefix(["interface", "wlan0"])
Bonus
Since VintageNet uses :ets
under the hood to store properties, Livebook can
render them as a nice table:
Kino.ETS.new(VintageNet)
Property subscriptions
Polling these properties for changes is not ideal so VintageNet
supports
property subscriptions.
If your device supports WiFi, the following subscribes to the known list of access points, starts a scan going and then waits for the first update.
key = ["interface", "wlan0", "wifi", "access_points"]
VintageNet.subscribe(key)
VintageNet.scan("wlan0")
receive do
{VintageNet, ^key, _old_value, aps, meta} ->
IO.inspect(aps, label: "Access Points from Scan")
IO.inspect(meta, label: "Event meta")
end
VintageNet.unsubscribe(key)
Bonus
Graph the available WiFi networks around you based on their frequency band and signal percentage 👇
alias VegaLite, as: Vl
VintageNet.scan("wlan0")
widget =
Vl.new(title: "2.4 GHz", width: 300)
|> Vl.encode_field(:x, "channel", title: "Channel")
|> Vl.encode_field(:y, "signal_percent", title: "Signal Percent", sort: :descending)
|> Vl.encode_field(:color, "ssid", title: "SSID")
|> Vl.mark(:circle, tooltip: [:signal_percent])
|> Kino.VegaLite.new()
|> tap(&Kino.render/1)
Kino.VegaLite.periodically(widget, 30000, 0, fn i ->
aps =
VintageNet.get(["interface", "wlan0", "wifi", "access_points"])
|> Enum.filter(fn %{band: band} -> band == :wifi_2_4_ghz end)
|> Enum.map(&Map.from_struct/1)
Kino.VegaLite.clear(widget)
Kino.VegaLite.push_many(widget, aps)
# Initiate scan for next time
VintageNet.scan("wlan0")
{:cont, i + 1}
end)