Geovane Fedrecheski

geonnave@

IoT

Nerves

  • IoT ?

  • Quick Demo

  • Nerves

  • Explained Demo

IoT?

The Internet

The Internet of Things

Demo

mix firmware
mix firmware.burn
defmodule HelloGpio.Blinky do
  use GenServer
  require Logger

  @ledpin 26

  def start_link do
    GenServer.start_link __MODULE__, [], name: __MODULE__
  end

  def toggle, do: GenServer.cast(__MODULE__, :toggle)

  def init(_) do
    Logger.debug "Initializing.."
    {:ok, pin} = Gpio.start_link(@ledpin, :output)
    Gpio.write(pin, 1)
    {:ok, {pin, 1}}
  end

  def handle_cast(:toggle, {pin, pin_state}) do
    new_pin_state = if pin_state == 0, do: 1, else: 0
    Gpio.write(pin, new_pin_state)
    {:noreply, {pin, new_pin_state}}
  end
end

IoT =~

Embedded webservices

O Elixir da IoT

Raspberry Pi

Beaglebone Black

defmodule HelloGpio.Blinky do
  use GenServer
  require Logger

  @ledpin 26

  def start_link do
    GenServer.start_link __MODULE__, [], name: __MODULE__
  end

  def toggle, do: GenServer.cast(__MODULE__, :toggle)

  def init(_) do
    {:ok, pin} = Gpio.start_link(@ledpin, :output)
    Gpio.write(pin, 1)
    {:ok, {pin, 1}}
  end

  def handle_cast(:toggle, {pin, pin_state}) do
    new_pin_state = if pin_state == 0, do: 1, else: 0
    Gpio.write(pin, new_pin_state)
    {:noreply, {pin, new_pin_state}}
  end
end
wget 'http://director.downloads.raspberrypi.org/
raspbian_lite/images/raspbian_lite-2016-09-28/
2016-09-23-raspbian-jessie-lite.zip'

unzip 2016-09-23-raspbian-jessie-lite.zip
sudo dd \
 if=2016-09-23-raspbian-jessie-lite.img \
 of=/dev/sdc
sudo vi /etc/apt/sources.list
# deb http://packages.erlang-solutions.com/debian wheezy contrib

wget http://packages.erlang-solutions.com/debian/erlang_solutions.asc
sudo apt-key add erlang_solutions.asc

sudo apt-get update
sudo apt-get install git erlang

git clone https://github.com/elixir-lang/elixir.git
cd elixir
git checkout tags/v1.3.4
make clean test

# waaaaaaaaaaaaaait a LOT (aprox. 1h)

export PATH="$PATH:/path/to/elixir/bin"

SSH

defmodule Broker.AccessControl do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def authorize(request) do
    GenServer.call __MODULE__, {:authorize, request}
  end

  def handle_call({:authorize, request}, _from, _current) do
    ans = Broker.AccessControl.PDP.authorize(request)
    {:reply, ans, ans}
  end
end

Compile on board takes time...

host$ ssh pi@192.168.0.1

pi$ mix nerves.new hello_nerves --target rpi
pi$ cd hello_nerves
pi$ mix deps.get
pi$ mix compile
# takes TIME...

pi$ iex -S mix

But..

  • Repeat the process for each board?

    • Maybe write a script

  • Configuration/Compilation Takes time..

  • What if want to use another board? (will need a different setup)

Nerves

Craft and deploy bulletproof embedded software in Elixir

Based on

Buildroot is a simple, efficient and easy-to-use tool to generate embedded Linux systems through cross-compilation.

Code

Package

Run

Prepare

VM

ARM

Toolchain

=

+

+

|>

{

,

}

=

Nerves

(installation)

(have Erlang & Elixir on host)

sudo apt-get install ssh-askpass
sudo apt-get install squashfs-tools

wget 'https://github.com/fhunleth/fwup/releases/
download/v0.9.2/fwup_0.9.2_amd64.deb'
sudo dpkg -i fwup_0.9.2_amd64.deb

mix archive.install 'https://github.com/nerves-project/
 archives/raw/master/nerves_bootstrap.ez'

Code

Package

Run

Prepare

mix nerves.new hello_nerves --target rpi3
cd hello_nerves
mix deps.get
mix compile
(write your project in Elixir)
mix firmware
mix firmware.burn
NERVES_TARGET=bbb mix compile

How does it compare?

Image sizes

Nerves

Raspbian

~300MB

~20MB

Boot time

Nerves

Raspbian

~30 sec.

~6 sec.

Multiple Boards

Nerves

Raspbian

All drivers included

Nerves

Raspbian

How about other frameworks?

?

The VM

Concurreny

Fault tolerance

Data structures

Consistent libs

Modern tooling

+

Small images

Multiple boards

Quick iteration

Cases

Demo

(explained)

HTTP

WiFi

GPIO

mix new hello_nerves --umbrella
cd sample/apps
mix nerves.new fw --target rpi3
mix phoenix.new ui --no-ecto --no-brunch
mix new hello_gpio --sup

cd fw
mix deps.get
mix compile
├── apps
│   ├── fw
│   ├── hello_gpio
│   └── ui
├── config
│   └── config.exs
├── mix.exs
├── mix.lock
└── README.md

Prepare

Code

apps/hello_gpio
├── config/
├── lib
│   ├── hello_gpio.ex
│   └── blinky.ex
├── mix.exs
├── README.md
└── test/
# mix.exs
defp deps do
  [{:elixir_ale, "~> 0.5.5"}]
end
# lib/blinky.ex
defmodule HelloGpio.Blinky do
  use GenServer
  require Logger

  @ledpin 26

  def start_link do
    GenServer.start_link __MODULE__, [], name: __MODULE__
  end

  def toggle, do: GenServer.cast(__MODULE__, :toggle)

  def init(_) do
    {:ok, pin} = Gpio.start_link(@ledpin, :output)
    Gpio.write(pin, 1)
    {:ok, {pin, 1}}
  end

  def handle_cast(:toggle, {pin, pin_state}) do
    new_pin_state = if pin_state == 0, do: 1, else: 0
    Gpio.write(pin, new_pin_state)
    {:noreply, {pin, new_pin_state}}
  end
end
apps/ui
├── config/
├── lib/
├── mix.exs
├── priv/
├── README.md
├── test/
└── web
    ├── channels/
    ├── controllers
    │   └── page_controller.ex
    ├── gettext.ex
    ├── router.ex
    ├── templates/
    ├── views/
    └── web.ex
# web/controllers/page_controller.ex
defmodule Ui.PageController do
  use Ui.Web, :controller
  require Logger

  def index(conn, _params) do
    render conn, "index.html"
  end

  def update(conn, _params) do
    Logger.debug "Will toggle LED!"
    HelloGpio.Blinky.toggle
    render conn, "index.html"
  end
end
# web/router.ex
scope "/", Ui do
  pipe_through :browser
  get "/", PageController, :index
  put "/", PageController, :update
end
apps/fw
├── config
│   └── config.exs
├── lib
│   └── fw.ex
├── mix.exs
├── README.md
└── test/
mix deps.get
mix compile
# mix.exs
def deps do
  [
    {:hello_gpio, in_umbrella: true},
    {:ui, in_umbrella: true},
    {:nerves_interim_wifi, "~> 0.1.0"},
    {:nerves_firmware_http, 
     github: "nerves-project/nerves_firmware_http"},
    {:nerves, "~> 0.3.0"}
  ]
end
# lib/fw.ex
Nerves.InterimWiFi.setup "wlan0",
  ssid: "eu sei oqvc fez no verao passado",
  key_mgmt: :"WPA-PSK",
  psk: "3neeszombees"

Package

mix firmware
mix firmware.burn
apps/fw
├── config
│   └── config.exs
├── lib
│   └── fw.ex
├── mix.exs
├── README.md
└── test/
$ ls -lah _images/rpi3/fw.fw
-rw-rw-r-- 21M _images/rpi3/fw.fw

Run

FOTA (Firmware Over the Air) Upgrades!

$ http POST "192.168.0.121:8988/firmware" \
content-type:application/x-firmware \
x-reboot:true < firmware.fw

Run

# mix.exs
def deps do
  [
    {:hello_gpio, in_umbrella: true},
    {:ui, in_umbrella: true},
    {:nerves_interim_wifi, "~> 0.1.0"},
    {:nerves_firmware_http, 
     github: "nerves-project/nerves_firmware_http"},
    {:nerves, "~> 0.3.0"}
  ]
end

Remote Debugging

├── apps
│   ├── fw
│   │   ├── _build
│   │   ├── config
│   │   ├── deps
│   │   ├── _images
│   │   ├── lib
│   │   ├── mix.exs
│   │   ├── mix.lock
│   │   ├── README.md
│   │   ├── rel
│   │   └── test
# rel/vm.args
-name raspi@192.168.0.7
-setcookie secret
iex --name host1@192.168.0.6 \
    --remsh raspi@192.168.0.7 \
    --cookie secret

Remote Debugging

remsh

Remote Debugging

iex --name host1@192.168.0.6 \
    --cookie secret

:observer

iex(host2@192.168.0.6)> :observer.start

Nodes

 -> Connect node

 -> input "raspi@192.168.0.7"

For more Information

Obrigado

geonnave@

Elixir of IoT with Nerves

By Geovane Fedrecheski

Elixir of IoT with Nerves

  • 1,509