BLOG
Avoid These Early Pitfalls of Networking on Nerves and Elixir
Unfortunately after building the firmware and flashing it to a Raspberry Pi, I encountered an error trying to make a call with HTTPoison:
Since I’m reasonably sure that the above URL is not malformed, I was able to reason pretty confidently that the culprit must be internet connectivity issues. Sure enough, Nerves does not come with network management out of the box. Fortunately, the Nerves Project does maintain a networking lib that I was able to easily plug into the build. (As a bonus I recommend checking out Init Gadget for a bunch of great default utils including Nerves Network!)
So I was ready to try my code again but once more, failure. Using Nerves.Network.status("eth0")
I was able to see that the network had not been configured yet. After waiting a few moments, and trying a few more status check saw that a connection was eventually established. Since my project was going to be using a Supervisor to manage a GenServer that would be attempting to automatically make network requests I needed to be sure that my network was configured so that the GenServer wouldn’t crash when it tried to boot up:
defmodule TimingExample.Launcher do
require Logger
use GenServer
def start_link(opts) do
opts = Keyword.merge([name: __MODULE__], opts)
GenServer.start_link(__MODULE__, :ok, opts)
end
def init(_) do
check_network(Nerves.Network.status("eth0"))
HTTPoison.get!("https://www.verytechnology.com/")
{:ok, {}}
end
defp check_network(%{ipv4_address: ip}) do
Logger.info("Connected")
end
defp check_network(_) do
Process.sleep(1000)
check_network(Nerves.Network.status("eth0"))
end
end
As you can see above, checking the network status for an IP address is a simple, if inelegant, solution that allowed me to move forward. I was not able to go very far, however:
This error was a bit trickier to debug for me. Suffice to say that I spent a few cycles on this problem which turned out to be a timing issue. Calling DateTime.utc_now().year
returned the value 1970
! My first instinct was to turn to NTP:
def init(_) do
update_time()
HTTPoison.get!("https://www.verytechnology.com/")
{:ok, {}}
end
defp update_time do
if DateTime.utc_now().year == 1970 do
System.cmd("ntpd", ["-q", "-p", "pool.ntp.org"])
Process.sleep(1000)
update_time()
end
end
This has the added benefit of no longer needing to check the network since NTP will fail to synchronize until the network connection has been established. However, after some further research, I found another great add-on for Nerves: Nerves Time. This package will handle the nitty-gritty of managing NTP and will actually make sure your device time is closely estimated until is able to synchronize with NTP. So I have one more adjustment to make to my code:
defp check_time do
if !Nerves.Time.synchronized?() do
Process.sleep(1000)
check_time()
end
end
Great! Now for a final flash and test:
Thanks for reading and I hope that this post helps you avoid some early pitfalls of Nerves development. If you have any comments or suggestions or angry rants please feel free to leave feedback on Heartbeat.