Elixir "poncho" project with Nerves firmware and Phoenix UI

Search for a command to run...

I wrote a script called phx-docker-compose-new that can be used instead of mix phx.new to generate a new Phoenix application. Building the development environment for a Phoenix application using Docker Compose is convenient because you can set up not...
この記事について Raspberry Pi、TensorFlow、Pythonのいずれにも詳しくない筆者が、物体検出をやって楽しんだ成果の記録です。 TensorFlow公式の物体検出のサンプルプログラムを実行します。 動作環境 ボード Raspberry Pi 4 Model B OS Raspberry Pi OS (32-bit または 64-bit) デスクトップ環境 カメラ Raspberry Pi カメラモジュール v2 Python Python ...
One day I was trying to upgrade one of my Phoenix 1.5 apps to Phoenix 1.6. In that app, I customize Bootstrap styles using SCSS. I want to talk about what I found and stumbled on when setting up SCSS. TIL All explained in Phoenix 1.6 Asset Managemen...
This is written in Japanese. I might convert it to English later, maybe. はじめに Elixirのテストでモックを用意するときに利用するElixirパッケージとして、moxが人気です。Elixir作者のJosé Valimさんが作ったからということもありますが、ただモックを用意するだけではなくElixirアプリの構成をより良くするためのアイデアにまで言及されているので、教科書のようなものと思っています。 一言でいうと「その場...
TIL Access supports keyword lists (Keyword) and maps (Map) out of the box, but not structs. There is a reason why Elixir does not implement Access behaviour for structs. Perfer data.key for accessing predefined atom keys of map or struct. Access be...
Today I learned how to make a [poncho project] with the Nerves firmware and Phoenix UI.
Poncho projects are an project structure alternative to Umprella projects.
Here is an example file structure.
hello_poncho
├── README.md
├── hello_poncho_firmware
└── hello_poncho_ui
The folder names can be verbose if a top-level project name is prepended to projects. I once considered to shorten them somehow, but I decided to go with long names because it is nice to be obvious about what exactly they are.
MacOS BigSur 11.6
elixir 1.12.3-otp-24
erlang 24.1
The easiest way to do it is just clone the Nerves Project's official hello_phoenix example. The Nerves core team and the community keep the example up-to-date. It should just work.
Clone the example project, then follow the instructions in hello_phoenix README.
cd some/location
git clone git@github.com:nerves-project/nerves_examples.git
cd nerves_examples/hello_phoenix
We can make a poncho app like hello_phoenix quite easily. Here is what it boils down to.
cd some/location
# Decide on the project name
MY_PROJECT_NAME=hello_poncho
# Create the project directory and move into it
mkdir $MY_PROJECT_NAME && cd $MY_PROJECT_NAME
# Create README.md
echo "$(curl -L https://raw.githubusercontent.com/nerves-project/nerves_examples/main/hello_phoenix/README.md)" > README.md
# Create Nerves firmware project
mix archive.install hex nerves_bootstrap
mix nerves.new "$MY_PROJECT_NAME"_firmware
# Create Phoenix UI project
mix archive.install hex phx_new
mix phx.new "$MY_PROJECT_NAME"_ui --no-ecto --no-mailer
We want to keep eslint from getting loaded at runtime. It causes the firmware to crash on load.
# hello_poncho/hello_poncho_ui/mix.exs
defp deps do
[
{:phoenix, "~> 1.6.0"},
# ...
{:esbuild, "~> 0.2", runtime: Mix.env() == :dev && Mix.target() == :host},
# ...
]
end
# hello_poncho/hello_poncho_firmware/mix.exs
defp deps do
[
# Dependencies for all targets
{:nerves, "~> 1.7", runtime: false},
# ...
{:hello_poncho_ui, path: "../hello_poncho_ui", targets: @all_targets, env: Mix.env()},
# ...
]
end
According to the Nerves official User Interfaces documentation:
If we're using a poncho project structure, we'll need to keep in mind that the my_app_ui configuration won't be applied automatically, so we should either import it from there or duplicate the required configuration.
To me personally, I prefer to keep all the necessary settings in hello_poncho_firmware/config/target.exs.
# hello_poncho/hello_poncho_firmware/config/target.exs
# as of phoenix 1.6.2
config :hello_poncho_ui, MyAppUiWeb.Endpoint,
url: [host: "nerves.local"],
http: [port: 80],
cache_static_manifest: "priv/static/cache_manifest.json",
secret_key_base: "HEY05EB1dFVSu6KykKHuS4rQPQzSHv4F7mGVB/gnDLrIu75wE/ytBXy2TaL3A6RA",
live_view: [signing_salt: "AAAABjEyERMkxgDh"],
check_origin: false,
render_errors: [view: MyAppUiWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: Ui.PubSub,
# Start the server since we're running in a release instead of through `mix`
server: true,
# Nerves root filesystem is read-only, so disable the code reloader
code_reloader: false
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
Optionally we can edit the configuration for wlan0. We could either hard code our WiFi settings or provide them through environment variables.
# hello_poncho/hello_poncho_firmware/config/target.exs
config :vintage_net,
regulatory_domain: "US",
config: [
{"usb0", %{type: VintageNetDirect}},
{"eth0",
%{
type: VintageNetEthernet,
ipv4: %{method: :dhcp}
}},
{"wlan0",
%{
type: VintageNetWiFi,
vintage_net_wifi: %{
networks: [
%{
key_mgmt: :wpa_psk,
ssid: System.get_env("NERVES_WIFI_SSID"),
psk: System.get_env("NERVES_WIFI_PSK")
}
]
},
ipv4: %{method: :dhcp}
}}
]
When developing the UI, we can simply run the Phoenix server from the hello_poncho/hello_poncho_ui project directory.
cd path/to/hello_poncho/hello_poncho_ui
iex -S mix phx.server
First we build our assets in the hello_poncho/hello_poncho_ui project directory.
cd path/to/hello_poncho/hello_poncho_ui
# Specify our target device.
export MIX_TARGET=rpi0
mix deps.get
# This needs to be repeated when you change JS or CSS files.
mix assets.deploy
When it's time to deploy firmware to our hardware, we can do it from the hello_poncho/hello_poncho_firmware project directory.
cd path/to/hello_poncho/hello_poncho_firmware
# Specify our target device.
export MIX_TARGET=rpi0
mix deps.get
# Build the firmware.
mix firmware
# Connect the MicroSD card to our host machine, then burn the firmware.
mix firmware.burn
Visit http://nerves.local and we will see the familiar Phoenix page that is hosted on our target device.

For subsequent firmware updates can be done through the network.
mix firmware
mix upload nerves.local
Sometimes I have an issue resolving dependencies for some reason (TODO: What exactly happened?)
I remember these command fixed the issue
mix deps.clean --allrm rf _build depsAlso make sure that both the firmware and the UI have the same MIX_ENV and MIX_TARGET set.
In case, we need to check the config, we can run Application.get_env/2 from our taget device's IEx console.
Application.get_env :hello_poncho_ui, HelloPonchoUiWeb.Endpoint
At first, the poncho project looked scary to me, but it is actually pretty simple. The Nerves firmware combined with the Phoenix UI seems so powerful that I may consider using this pattern for my other Nerves projects.
That's it! Here are some resources I read and found helpful.