Isnor Creative
Isnor Creative Blog
Ruby, Ruby on Rails, Ember, Elm, Phoenix, Elixir, React, Vue

Feb 8, 2016

Raspberry Pi Phoenix Embedded with Nerves and Bake

I’m just getting started with Nerves – which allows you to easily build stripped down firmware images for your microcomputer that boot directly into Elixir very quickly – Raspberry Pi and the world of embedded computing.

Garth Hitchens gave an incendiary talk at ElixirConf 2015 which is a great starting point on the subject:

Follow the typical steps for a new Phoenix application:

  mix pi_phoenix
  mix do deps.get, compile

Add nerves and nervesoethernet to mix.exs:

  • add nerves and nervesioethernet to def application
  • add nerves and nervesioethernet to deps:
{:nerves, github: "nerves-project/nerves"}
{:nerves_io_ethernet, github: "nerves-project/nerves_io_ethernet"}

Start ethernet: in lib/pi.ex:

def start(_type, _args) do
  {:ok, _pid} = Nerves.IO.Ethernet.setup :eth0


config :pi_phoenix, PiPhoenix.Endpoint, 
http: [port: 80],
server: true,
check_origin: ["localhost",""]

Install Bake

ruby -e "$(curl -fsSL"

Add Bakefile to project root:

use Bake.Config

platform :nerves
default_target :rpi2

target :rpi2,
  recipe: {"nerves/rpi2", "~> 0.1"}
bake system get
bake toolchain get
MIX_ENV=prod bake firmware

Stick your SD card into the drive

Install fwup and then burn the image to your SD

brew install fwup
sudo fwup -a -i _images/pi_phoenix-rpi2.fw -t complete
  • Remove the SD card when ready
  • Put the SD card into the Raspberry Pi
  • Connect the Raspberry Pi to network via ethernet cable
  • Connect the Raspberry Pi to monitor via HDMI cable
  • Power up Raspberry Pi
  • You’ll need to discover the port of your Raspberry Pi – I did this by visiting by my router control panel
  • When you visit the IP of your Raspberry Pi you should now see the typical Welcome to Phoenix startup screen.

Broadcasting LED blinks on Phoenix channels

Once I had this in place, I started blinking GPIO leds, and broadcasting that on a channel, so that I could see the LEDs lighting up on a breadboard at the same time as they were lighting up on any network connected computers who visited the Raspberry PI server IP address.

lib/app.ex example:

defmodule BlinkyChannels do
  use Application
  require Logger
  alias Nerves.IO.Ethernet

  def start(_type, _args) do

    {:ok, _pid} = Nerves.IO.Ethernet.setup :eth0
    import Supervisor.Spec, warn: false

    children = [supervisor(BlinkyChannels.Endpoint, [])]
    opts = [strategy: :one_for_one, name: BlinkyChannels.Supervisor]
    Supervisor.start_link(children, opts)
    # GPIO LED setup
    leds = [17, 27, 22, 23]
    Enum.each(leds, fn(led) -> start_led(led) end)
    {:ok, spawn(fn -> blink_forever(leds) end)}


  defp start_led(led) do
    :os.cmd('echo #{led} > /sys/class/gpio/export')
    :os.cmd('echo out > /sys/class/gpio/gpio#{led}/direction')

  defp blink_forever(leds) do
    Enum.each(leds, fn(led) -> blink(led) end)

  defp blink(led) do
  defp blink_on(led) do
    set_gpio_val(led, 1)
    BlinkyChannels.Endpoint.broadcast!("blinko:alert", "new:blink_it", %{led: led})    

  def blink_off(led) do
    set_gpio_val(led, 0)
    BlinkyChannels.Endpoint.broadcast!("blinko:alert", "new:unblink_it", %{})

  defp set_gpio_val(gpio, val) do
    :os.cmd('echo #{val} > /sys/class/gpio/gpio#{gpio}/value')
  defp sleep(time) do
  def config_change(changed, _new, removed) do
    BlinkyPhoenix.Endpoint.config_change(changed, removed)


web/static/js/socket.js example

import { Socket } from "deps/phoenix/web/static/js/phoenix";

const socket = new Socket("/socket", {params: {token: window.userToken}});


const channel ="blinko:alert", {});
const dot = document.getElementById("dot");

channel.on("new:blink_it", payload => {"new blink on ok");
    let color;
    switch(payload.led) {
    case 17:
       color = 'red'; break;
    case 22:
       color = 'green'; break;
    case 23:
         color = 'yellow'; break;
    case 27:
        color = 'blue'; break;
    dot.className = color;

channel.on("new:unblink_it", () => {
  dot.className = "";

  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) });

export default socket;

web/channels/user_socket.ex example:

defmodule BlinkyChannels.UserSocket do
  use Phoenix.Socket
  channel "blinko:*", BlinkyChannels.BlinkoChannel

  transport :websocket, Phoenix.Transports.WebSocket, check_origin: false

  def connect(_params, socket) do
    {:ok, socket}

  def id(_socket), do: nil


channel example:

defmodule BlinkyChannels.BlinkoChannel do

  use BlinkyChannels.Web, :channel

  def join("blinko:alert", _params, socket) do
    {:ok, socket}

  def handle_out(event, payload, socket) do
    push socket, event, payload
    {:noreply, socket}


web/template/layout/app.html.eex example

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
      body    { width: 100vw; height: 100vh; background-color: black; }
      .container { display: flex; justify-content: center; align-items:center; height: 100vh; width: 100vw; }
      .dot { width:300px; border-radius: 300px; height: 300px; border: 1px solid #EEE; }
      .red    { background-color: red; }
      .green  { background-color: green; }
      .yellow { background-color: yellow; }
      .blue   { background-color: blue; }
    <div class="container" role="main"><div id="dot"></div></div>
    <script src="&lt;%= static_path(@conn, "/js/app.js") %&gt;"></script>

I am available for Elixir/Phoenix consulting work – get in touch to learn more.

Gordon B. Isnor

Gordon B. Isnor writes about Ruby on Rails, Ember.js, Elm, Elixir, Phoenix, React, Vue and the web.
If you enjoyed this article, you may be interested in the occasional newsletter.

I am now available for project work. I have availability to build greenfield sites and applications, to maintain and update/upgrade existing applications, team augmentation. I offer website/web application assessment packages that can help with SEO/security/performance/accessibility and best practices. Let’s talk

comments powered by Disqus