User Interfaces

Phoenix Web Interfaces

Phoenix makes an excellent companion to Nerves applications by offering an easy-to-use, powerful framework to create user interfaces in parallel with Nerves device code.

Choosing a project structure

Although Nerves supports umbrella projects, the preferred project structure is to simply use separate Mix projects side-by-side with path dependencies between them if they’re in the same source code repository. We call this a “poncho project” structure. For the reasoning behind this, please see this blog post describing poncho projects.

Using a poncho project structure

First, generate the two new apps in a containing folder:

$ mkdir nervy && cd nervy
$ mix nerves.new fw
$ mix phx.new ui --no-ecto --no-brunch

Now, add the Phoenix ui app and the nerves_network library to the fw app as dependencies:

# fw/mix.exs

# ...
def deps do
  [{:nerves_network, "~> 0.3"},
   {:ui, path: "../ui"}]
end
# ...

Next: Configure Networking

Using an umbrella project structure

First, generate a new umbrella app, called nervy in this case:

$ mix new nervy --umbrella

Next, create your sub-applications for Nerves and for Phoenix:

$ cd nervy/apps
$ mix nerves.new fw
$ mix phx.new ui --no-ecto --no-brunch

Now, add the Phoenix ui app and the nerves_network library to the fw app as dependencies:

# apps/fw/mix.exs

# ...
def deps do
  [{:ui, in_umbrella: true},
   {:nerves_network, "~> 0.3"}]
end
# ...
Specifying configuration order

By default, the top-level configuration loads the application configurations in an unordered way:

import_config "../apps/*/config/config.exs"

This can cause problems if the ui config is applied last: we may lose overrides applied in the fw config. You need to force the order in which the config files get imported:

# config/config.exs

use Mix.Config

import_config "../apps/ui/config/config.exs"
import_config "../apps/fw/config/config.exs"

Configure networking

In order to start the network when fw boots, add nerves_network to the bootloader configuration in config.exs.

# fw/config/config.exs

# ...
config :bootloader,
  init: [:nerves_runtime, :nerves_network]
# ...

To set the default networking configuration:

# fw/config/config.exs

# ...

# For WiFi, set regulatory domain to avoid restrictive default
config :nerves_network,
  regulatory_domain: "US"

config :nerves_network, :default,
  wlan0: [
    ssid: System.get_env("NERVES_NETWORK_SSID"),
    psk: System.get_env("NERVES_NETWORK_PSK"),
    key_mgmt: String.to_atom(System.get_env("NERVES_NETWORK_MGMT"))
  ],
  eth0: [
    ipv4_address_method: :dhcp
  ]

# ...

For more network settings, see the nerves_network project.

Configure Phoenix

In order to build the ui Phoenix app into the Nerves fw app, you will need to make some changes to your fw application configuration:

# fw/config/config.exs

# ...
config :ui, UiWeb.Endpoint,
  url: [host: "localhost"],
  http: [port: 80],
  secret_key_base: "#############################",
  root: Path.dirname(__DIR__),
  server: true,
  render_errors: [view: UiWeb.ErrorView, accepts: ~w(html json)],
  pubsub: [name: Nerves.PubSub, adapter: Phoenix.PubSub.PG2],
  code_reloader: false

config :logger, level: :debug
# ...

There you have it! A Phoenix web application ready to run on your Nerves device. By separating the Phoenix application from the Nerves application, you could easily distribute the development between team members and continue to leverage the features we have all come to love from Phoenix, like live code reloading.

When developing your UI, you can simply run the Phoenix server from the UI application:

$ cd path/to/ui
$ mix phoenix.server

When it’s time to create your firmware:

$ cd path/to/fw
$ export MIX_TARGET=rpi3
$ mix deps.get
$ mix firmware
$ mix firmware.burn