SlideShare a Scribd company logo
Nerves Project
BADGE HACKING WORKSHOP
▸ Introduction
▸ Definitions
▸ Nerves Intro
▸ Host Tools
▸ Targets
▸ Host / Target Prep
What are we going to Do?
▸ Interfacing with Hardware
▸ Boot to IEx
▸ Pin Muxing
▸ Blinky with elixir_ale
▸ Blinky with Firmata and Arduino
What are we going to Do?
▸ Building the Badge
▸ Project Layout
▸ Arduino and Firmata
▸ Connecting to Twitter
▸ Web Interfaces
What are we going to Do?
▸ Advanced Configuration
▸ Modifying a Nerves System
▸ Initialization with erlinit
▸ Firmware Updates
▸ Licensing
What are we going to Do?
Introduction
SECTION 1
▸ Introduction
▸ Definitions
▸ Nerves Intro
▸ Host Tools
▸ Targets
▸ Host / Target Prep
What are we going to Do?
DEFINITIONS
1.1
▸ host
▸ target
▸ toolchain
▸ system
Definitions
▸ artifact
▸ assemble
▸ firmware bundle
▸ firmware image
host
The computer on which you are editing source code,
compiling, and assembling firmware
Definitions
target
The platform for which your firmware is built (for
example, Raspberry Pi, Raspberry Pi 2, or Beaglebone
Black)
Definitions
toolchain
The tools required to build code for the target, such as
compilers, linkers, binutils, and C runtime
Definitions
system
A lean Buildroot-based Linux distribution that has
been customized and cross-compiled for a particular
target
Definitions
assembly
The process of combining system, application, and
configuration into a firmware bundle
Definitions
firmware bundle
A single file that contains an assembled version of
everything needed to burn firmware
Definitions
firmware image
Built from a firmware bundle and contains the partition
table, partitions, bootloader, etc.
Definitions
NERVES
1.2
Nerves
Your Elixir project
and dependencies
Linux, C libraries,
Erlang runtime
Nerves System
Image
OTP release
Firmware bundle
Compiling on your machine
YOUR APP
ELIXIR
C CODE
NIF / PORTS
MIX
BEAM
BINARY
YOUR APP
(ARCH SPECIFIC)
Mixing firmware
MIX
YOUR APP
(FOR RPI2)
TOOLCHAIN
SYSTEM
rpi2
Precompile
compile
Toolchains
TOOLCHAINTOOLCHAIN CONFIG
• crosstool-ng
• for target
• host configs
• compilers
• run on host
• compile for
target
What’s in a Nerves system package?
▸ Elixir build infrastructure
▸ mix.exs – build the system image via mix
▸ nerves.exs – additional system image information such as
where to find pre-built system images
▸ Buildroot configuration
▸ nerves_defconfig – top level configuration options
▸ Custom package definitions
▸ Linux kernel configuration and patches
▸ Board-specific root file system additions
Nerves Package Artifacts
SYSTEM
NERVES_SYSTEM_RPI3
Package
TOOLCHAIN
SYSTEM (TOOLCHAIN)
Artifact
Create a new Nerves app
New Projects
$ mix nerves.new my_app --target linkit
MIX FILE
defmodule MyApp.Mixfile do
use Mix.Project
@target System.get_env("NERVES_TARGET") || "linkit"
…
end
New Projects
MIX FILE
defmodule MyApp.Mixfile do
…
def project do
[app: :my_app,
version: "0.1.0",
archives: [nerves_bootstrap: "~> 0.1"],
target: @target,
deps_path: "deps/#{@target}",
build_path: "_build/#{@target}",
aliases: aliases,
deps: deps ++ system(@target)]
end
end
New Projects
MIX FILE
defmodule MyApp.Mixfile do
…
def aliases do
["deps.precompile": ["nerves.precompile", "deps.precompile"],
"deps.loadpaths": ["deps.loadpaths", "nerves.loadpaths"]]
end
end
New Projects
MIX FILE
defmodule MyApp.Mixfile do
…
def system(target) do
[{:"nerves_system_#{target}", ">= 0.0.0"}]
end
…
end
New Projects
Firmware
$ mix firmware
$ mix firmware.burn
Flash layout
Master Boot Record
Bootloaders
Root Filesystem A
Read-only
Root Filesystem B
Read-only
Application Data
Read-write
Linux kernel
Erlang
C libraries and apps
OTP release
App settings
Database
Logs
Other files
Nerves root filesystem
.
├── bin
├── dev
├── etc
├── lib
├── mnt
├── proc
├── root
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var
├── srv
│ └── erlang
│ ├── lib
│ │ ├── compiler-7.0
│ │ ├── elixir-1.3.2
│ │ ├── kernel-5.0
│ │ ├── logger-1.3.1
│ │ ├── your_app-0.1.0
│ │ ├── sasl-3.0
│ │ └── stdlib-3.0
│ └── releases
│ ├── 0.0.1
│ │ ├── your_app.boot
│ │ ├── your_app.rel
│ │ ├── your_app.script
│ │ ├── sys.config
│ │ └── vm.args
│ ├── RELEASES
│ └── start_erl.data
HOST PREP
1.3
NERVES
▸ Erlang OTP ~> 19
▸ Elixir ~> 1.3.2
▸ fwup ~> 0.8.2
▸ squashfs-tools
▸ nerves_bootstrap
Host Tools
▸ Arduino IDE ~> 1.6
BADGE HACKING
Host Tools
# copy and untar system and toolchain
$ export NERVES_SYSTEM=/path/to/uncompressed/system
$ export NERVES_TOOLCHAIN=/path/to/uncompressed/toolchain
Cached Nerves System and Nerves Toolchain
Since bandwidth is limited
Make sure you unset after the conference.
Installation Time
ERLANG & ELIXIR
Host Tools
$ brew update
$ brew install elixir
Mac OS
Linux
https://www.erlang-solutions.com/resources/download.html
http://elixir-lang.org/install.html
FWUP
Host Tools
$ brew install fwup
Download Debian package from https://github.com/fhunleth/fwup/
$ sudo dpkg -i fwup_0.8.2_amd64.deb
Mac OS
Linux
ADDITIONAL TOOLS
Host Tools
Mac OS
Linux
$ brew install squashfs coreutils picocom
$ sudo apt-get install squashfs-tools picocom
$ sudo vigr
# Add yourself to the dialout group
dialout:x:20:yourusername
ADDITIONAL TOOLS
Host Tools
All
$ mix local.hex
$ mix local.rebar
$ mix archive.install https://github.com/nerves-project/archives/raw/master/
nerves_bootstrap.ez
CONSOLE CABLE DRIVERS
Host Tools
Mac OS
Linux
https://www.adafruit.com/images/product-files/954/
PL2303_MacOSX_1_6_0_20151022.zip
"Everything just works on Linux"
~Frank Hunleth
ARDUINO TOOLS
Host Tools
▸ Install Arduino IDE ~> 1.6.0
▸ Arduino -> Preferences -> Additional Boards Manager
URLs
▸ http://download.labs.mediatek.com/
package_mtk_linkit_smart_7688_test_index.json
▸ Tools -> Board: ... -> Boards Manager
▸ Search: linkit -> install 0.1.8
SYSTEMS / TARGETS
1.4
Official Nerves systems
▸ nerves_system_bbb (black and green)
▸ nerves_system_rpi, nerves_system_rpi2,
nerves_system_rpi3
▸ nerves_system_qemu_arm
▸ nerves_system_alix, nerves_system_ag150
▸ nerves_system_galileo
▸ nerves_system_linkit
TARGET SPECIFIC CONFIG
# config.exs
use Mix.Config
import_config "#{Mix.Project.config[:target]}.exs"
Multi-Target
CHANGING TARGETS
NERVES_TARGET=linkit mix deps.get
export NERVES_TARGET=linkit
mix deps.get
mix deps.get
# @target System.get_env("NERVES_TARGET") || "linkit"
Multi-Target
Systems
Linkit Smart Raspberry Pi 3
Linkit Smart Duo
ATMega32U4
Linkit Smart
LINKIT SMART
LINKIT SMART
1.5
LinkIt Smart 7688 Flash
▸ 32 MB NAND Flash
▸ Bootloader and Linux kernel stored here
▸ LinkIt bootloader doesn’t support FTL so don’t update
too many times
▸ Linux mtd driver provides access (/dev/mtdblock1, etc.)
▸ MicroSD card
▸ Root filesystems
▸ Application data
Typical Nerves Flash layout
Master Boot Record
Bootloaders
Root Filesystem A
Read-only
Root Filesystem B
Read-only
Application Data
Read-write
Linux kernel
Erlang
C libraries and apps
OTP release
App settings
Database
Logs
Other files
Flash layout
Master Boot Record
Root Filesystem A
Read-only
Root Filesystem B
Read-only
Application Data
Read-write
Erlang
C libraries and apps
OTP release
App settings
Database
Logs
Other files
Bootloader
Linux Kernel
The Console
▸ Black - Ground
▸ Red - 5v
▸ White - RX
▸ Green - TX
The Console
▸ Black - Ground
▸ Red - 5v
▸ White(RX) - TX
▸ Green(TX) - RX
Don’t worry about accidentally swapping RX and TX. If nothing shows up on

the console, just swap them.
The Console
▸ Baud: 57600
▸ Bits: 8
▸ Parity: None
▸ Stop Bits: 1
The Console
$ picocom -b 57600 /dev/tty.usbserial
BURN BOOTLOADER
▸ Connect a 3.3V FTDI cable (GND, RX, and TX) to the LinkIt
Smart. Power up the LinkIt Smart and verify that you can
see text and type. You should be interacting with the
default OpenWRT firmware.
▸ Remove power from the LinkIt Smart
LinkIt Smart Prep
BURN BOOTLOADER
▸ Plug the USB Flash drive into the LinkIt Smart via the On-
the-go cable. Make sure that it's plugged into the USB
Host connector.
▸ Press the 'b' key repeatedly on the serial port while
rebooting the LinkIt Smart.
▸ Stop when you see that it is programming the Flash.
LinkIt Smart Prep
BURN LINUX KERNEL
▸ Press the '5' key repeatedly on the serial port while
rebooting the LinkIt Smart.
▸ Stop when you see that it is programming the Flash.
▸ Should be looking for rootfs on SD
LinkIt Smart Prep
▸ http://labs.mediatek.com/site/global/developer_tools/
mediatek_linkit_smart_7688/hdk_intro/index.gsp
LinkIt Smart Revert
RECAP
▸ What is Nerves
▸ Definitions
▸ Differences In Targets
▸ Preparing your Host
▸ Preparing the Target
Title
Interfacing Hardware
SECTION 2
▸ Interfacing with Hardware
▸ Boot to IEx
▸ Pin Muxing
▸ Blinky with elixir_ale
▸ Blinky with Firmata and Arduino
What are we going to Do?
BOOTING TO IEX
2.1
Booting to IEx
$ mix nerves.new console --target linkit
$ cd console
$ mix deps.get
$ mix firmware
$ mix firmware.burn
https://github.com/mobileoverlord/console
Fast track
Code it up
Adding IEx Helpers
defmodule Console.IExHelpers do
def cat(file) do
File.read!(file)
|> IO.puts
end
end
Adding IEx Helpers
# config/rootfs-additions/etc/iex.exs
import Console.IExHelpers
Adding IEx Helpers
# config/config.exs
config :nerves, :firmware,
rootfs_additions: "config/rootfs-additions"
Adding IEx Helpers
# rel/vm.args
## Start the Elixir shell
-noshell
-user Elixir.IEx.CLI
-extra --no-halt
+iex --dot-iex /etc/iex.exs
Adding IEx Helpers
$ mix firmware
$ mix firmware.burn
Adding IEx Helpers
iex(1)> cat "/etc/iex.exs"
import Console.IExHelpers
:ok
PIN MUXING
2.2
The problem
GPIO
SPI
I2C
Camera In
Ethernet
Timer
PWM
MMC
Display
More pins than

fit on a chip!
DRAM
Power
A layer of indirection
GPIO
SPI
I2C
Camera In
Ethernet
Timer
PWM
MMC
Display
DRAM
Power
Pin mux
Physical
pins
Beaglebone Black (TI AM335x)
http://www.embedded-things.com/bbb/beaglebone-black-pin-mux-spreadsheet/
LinkIt Smart (MT7688)
▸ Bootloader
▸ Some pins have to configured as early as possible
▸ Usually only deal with this on custom boards
▸ Linux kernel device tree
▸ Textual description of the hardware configuration
▸ Compiled down and loaded early in the Linux boot process
▸ Device tree overlays may be loaded after boot (but not
supported on the LinkIt Smart)
▸ Custom programs
Configuring pinmux’d processors
▸ Device tree
▸ linux-4.4.14/arch/mips/boot/dts/ralink/LINKIT7688.dts
▸ Compiled to LINKIT7688.dtb
▸ Usermode app - pinmux
▸ https://github.com/nerves-project/nerves_system_linkit/
tree/develop/package/mtk-linkit
▸ Invoke on the Linkit Smart as:
LinkIt Smart pinmux configuration
/usr/bin/pinmux set ephy gpio
BLINKY ELIXIR ALE
2.3
Positive
Negative
Host Tools
$ mix nerves.new blinky_ale --target linkit
https://github.com/mobileoverlord/blinky_ale
Fast track
MIX FILE
defmodule BlinkyAle.Mixfile do
…
def application do
[mod: {BlinkyAle, []},
applications: [:logger, :elixir_ale]]
end
def deps do
[{:nerves, "~> 0.3.0"},
{:elixir_ale, "~> 0.5.6"}]
end
end
Blinky Elixir Ale
defmodule BlinkyAle do
use Application
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec, warn: false
# Define workers and child supervisors to be supervised
children = [
worker(Task, [fn -> blink end], restart: :transient),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BlinkyAle.Supervisor]
Supervisor.start_link(children, opts)
end
...
end
Blinky Elixir Ale
defmodule BlinkyAle do
...
def blink do
:os.cmd '/usr/bin/pinmux set ephy gpio'
{:ok, pid} = Gpio.start_link(43, :output)
blink_forever(pid)
end
def blink_forever(pid) do
Gpio.write(pid, 1)
:timer.sleep(1000)
Gpio.write(pid, 0)
:timer.sleep(1000)
blink_forever(pid)
end
end
Blinky Elixir Ale
Code it up
BLINKY FIRMATA
2.4
Arduino D13 LED
Arduino Serial / Power
Running on the Host
// SimpleFirmata.ino
Firmata.begin(57600);
while (!Serial) {
;
}
Arduino Firmata
▸ File -> Examples -> Firmata -> StandardFirmata
Write to the Arduino
Running on the Host
$ mix new blinky_firmata_host
https://github.com/mobileoverlord/blinky_firmata_host
Fast track
Running on the Host
defmodule BlinkyFirmataHost.Mixfile do
...
def application do
[applications: [:logger, :firmata],
mod: {BlinkyFirmataHost, []}]
end
defp deps do
[{:firmata, github: "mobileoverlord/firmata"}]
end
end
Running on the Host
defmodule BlinkyFirmataHost.Protocol do
use GenServer
use Firmata.Protocol.Mixin
alias Firmata.Board
def start_link(tty, opts  []) do
GenServer.start_link(__MODULE__, [tty, opts], name: __MODULE__)
end
def init([tty, opts]) do
IO.puts "Init"
{:ok, board} = Board.start_link(tty, opts)
{:ok, %{
board: board
}}
end
...
end
Running on the Host
defmodule BlinkyFirmataHost.Protocol do
...
def handle_info({:firmata, {:pin_map, _pin_map}}, s) do
IO.puts "Set Pin Map"
Board.set_pin_mode(s.board, 13, @output)
send(self, {:blink, 1})
{:noreply, s}
end
def handle_info({:blink, state}, s) do
IO.puts "Blink"
Board.digital_write(s.board, 13, state)
state = if state == 1, do: 0, else: 1
Process.send_after(self, {:blink, state}, 1000)
{:noreply, s}
end
def handle_info(_, s) do
{:noreply, s}
end
end
Running on the Host
Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)> Nerves.UART.enumerate
%{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{},
"/dev/cu.MyQC-SPPDev-1" => %{},
"/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id:
43777,
vendor_id: 3725}}
Running on the Host
iex(2)> BlinkyFirmataHost.Protocol.start_link "/dev/cu.usbmodem1421"
Init
{:ok, #PID<0.156.0>}
Set Pin Map
Blink
It Blinks!
Running on the Target
// StandardFirmata.ino
// Connecting from LinkIt
Serial1.begin(57600);
Firmata.begin(Serial1);
// Connecting from Host
// Firmata.begin(57600);
// while (!Serial) {
// ;
//
// }
Write to the Arduino
Running on the Target
$ mix nerves.new blinky_firmata --target linkit
https://github.com/mobileoverlord/blinky_firmata
Fast track
Running on the Target
defmodule BlinkyFirmata.Mixfile do
...
def application do
[applications: [:logger, :firmata],
mod: {BlinkyFirmata, []}]
end
defp deps do
[{:nerves, "~> 0.3.0"},
{:firmata, github: "mobileoverlord/firmata"}]
end
end
Running on the Target
defmodule BlinkyFirmata.Protocol do
use GenServer
use Firmata.Protocol.Mixin
alias Firmata.Board
def start_link(tty, opts  []) do
GenServer.start_link(__MODULE__, [tty, opts], name: __MODULE__)
end
def init([tty, opts]) do
IO.puts "Init"
{:ok, board} = Board.start_link(tty, opts)
{:ok, %{
board: board
}}
end
...
end
Running on the Target
defmodule BlinkyFirmata.Protocol do
...
def handle_info({:firmata, {:pin_map, _pin_map}}, s) do
IO.puts "Set Pin Map"
Board.set_pin_mode(s.board, 13, @output)
send(self, {:blink, 1})
{:noreply, s}
end
def handle_info({:blink, state}, s) do
IO.puts "Blink"
Board.digital_write(s.board, 13, state)
state = if state == 1, do: 0, else: 1
Process.send_after(self, {:blink, state}, 1000)
{:noreply, s}
end
def handle_info(_, s) do
{:noreply, s}
end
end
Running on the Target
defmodule BlinkyFirmata do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
# Define workers and child supervisors to be supervised
children = [
worker(BlinkyFirmata.Protocol, ["ttyS0"]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BlinkyFirmata.Supervisor]
Supervisor.start_link(children, opts)
end
end
Running on the Target
$ mix firmware
$ mix firmware.burn
Boot it up
It Blinks!
▸ Elixir Ale
▸ Firmata
▸ Ports
▸ When to use which strategy
Recap
Building the Badge
SECTION 3
▸ Building the Badge
▸ Project Layout
▸ Arduino and Firmata
▸ Connecting to Twitter
▸ Web Interfaces
What are we going to Do?
PROJECT LAYOUT
3.1
UMBRELLA
▸ Organize facets of code in our project for isolation
▸ Ability to run aspects on Host
Project Layout
Project Layout
$ mix new badge --umbrella
└── badge
├── README.md
├── apps
├── config
│   └── config.exs
└── mix.exs
https://github.com/mobileoverlord/badge
Fast track
Project Layout
$ cd badge/apps
$ mix nerves.new badge_fw --target linkit
$ mix new badge_lib
├── badge_fw
└── badge_lib
Project Layout
# apps/badge_fw/mix.exs
def application do
[mod: {BadgeFw, []},
applications: [:logger, :badge_lib]]
end
def deps do
[{:nerves, "~> 0.3.0"},
{:badge_lib, in_umbrella: true}]
end
Code it up
Project Layout
# apps/badge_fw
$ mix deps.get
$ mix firmware
Project Layout
# apps/badge_fw
$ mix deps.get
$ mix firmware
** (UndefinedFunctionError) function :relx.do/2 is undefined (module :relx is
not available)
If you are at the top of the umbrella
INITIALIZATION
3.2
Initialization
# apps/badge_fw/mix.exs
def application do
[mod: {BadgeFw, []},
applications: [:logger, :badge_lib, :nerves_interim_wifi]]
end
def deps do
[{:nerves, "~> 0.3.0"},
{:nerves_interim_wifi, "~> 0.1"},
{:badge_lib, in_umbrella: true}]
end
# apps/badge_fw
$ mix deps.get
Initialization
# apps/badge_fw/lib/badge_fw.ex
defmodule BadgeFw do
use Application
alias Nerves.InterimWiFi, as: WiFi
def start(_type, _args) do
import Supervisor.Spec, warn: false
:os.cmd('modprobe mt7603e')
# Define workers and child supervisors to be supervised
children = [
worker(Task, [fn -> network end], restart: :transient),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BadgeFw.Supervisor]
Supervisor.start_link(children, opts)
end
def network do
wlan_config = Application.get_env(:badge_fw, :wlan0)
WiFi.setup "wlan0", wlan_config
end
end
Initialization
# apps/badge_fw/config/config.exs
use Mix.Config
config :badge_fw, :wlan0,
ssid: "Nerves",
key_mgmt: :"WPA-PSK",
psk: "nervesnet"
Test it out
:inet.gethostbyname 'nerves-project.org'
$ mix firmware
$ mix firmware.burn
Initialization
Initialization
# apps/badge_fw/mix.exs
def application do
[mod: {BadgeFw, []},
applications: [:logger, :badge_lib, :nerves_interim_wifi,
:nerves_ntp]]
end
def deps do
[{:nerves, "~> 0.3.0"},
{:nerves_interim_wifi, "~> 0.1"},
{:nerves_ntp, "~> 0.1"},
{:badge_lib, in_umbrella: true}]
end
# apps/badge_fw
$ mix deps.get
# apps/badge_fw/config/config.exs
config :nerves_ntp, :ntpd, "/usr/sbin/ntpd"
config :nerves_ntp, :servers, [
"0.pool.ntp.org",
"1.pool.ntp.org",
"2.pool.ntp.org",
"3.pool.ntp.org"
]
Initialization
# badge_fw/rel/vm.args
-sname badge
-setcookie nerves
## Start the Elixir shell
-noshell
-user Elixir.IEx.CLI
-extra --no-halt
Initialization
Enable distributed Erlang
remsh
iex --sname host --cookie nerves --remsh badge@nerves-244a
FIRMATA ARDUINO
3.3
Connecting the Components
Vibration motor Display
D9 I2C
$ git clone https://github.com/mobileoverlord/badge_firmata
Arduino Firmata
# badge_lib/mix.exs
def application do
[applications: [:logger, :firmata]]
end
defp deps do
[{:firmata, github: "mobileoverlord/firmata"}]
end
Arduino Firmata
$ mix deps.get
badge/apps/badge_lib/lib/badge_lib/firmata.ex
New File
Arduino Firmata
Write to the Arduino
Arduino Firmata
defmodule BadgeLib.Firmata do
use GenServer
use Firmata.Protocol.Modes
alias Firmata.Board, as: Board
def start_link(opts  []) do
port = opts[:port] || "ttyS0"
speed = opts[:speed] || 57600
serial_opts = [speed: speed]
GenServer.start_link(__MODULE__, [port, serial_opts], name: __MODULE__)
end
def init([port, serial_opts]) do
{:ok, board} = Board.start_link(port, serial_opts)
{:ok, %{
board: board
}}
end
...
end
Arduino Firmata
defmodule BadgeLib.Firmata do
...
def handle_info({:firmata, {:pin_map, _pin_map}}, s) do
{:noreply, s}
end
def handle_info(_, s) do
{:noreply, s}
end
end
Arduino Firmata
defmodule BadgeLib.Firmata do
use GenServer
use Firmata.Protocol.Modes
alias Firmata.Board, as: Board
@high 1
@low 0
@vibration_pin 9
def start_link(opts  [])
def vibrate(state  @high) do
GenServer.call(__MODULE__, {:vibrate, state})
end
...
end
Arduino Firmata
defmodule BadgeLib.Firmata do
...
def handle_call({:vibrate, state}, _from, s) do
Board.digital_write(s.board, @vibration_pin, state)
{:reply, :ok, s}
end
def handle_info({:firmata, {:pin_map, _pin_map}}, s) do
Board.set_pin_mode(s.board, @vibration_pin, @output)
{:noreply, s}
end
end
Running on the host
Firmata.begin(57600);
while (!Serial) {
;
}
Running on the host
Firmata.begin(57600);
while (!Serial) {
;
}
Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)> Nerves.UART.enumerate
%{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{},
"/dev/cu.MyQC-SPPDev-1" => %{},
"/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id:
43777,
vendor_id: 3725}}
Running on the host
%{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{},
"/dev/cu.MyQC-SPPDev-1" => %{},
"/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id:
43777,
vendor_id: 3725}}
iex(2)> BadgeLib.Firmata.start_link(port: "/dev/cu.usbmodem1421")
Running on the host
iex(3)> BadgeLib.Firmata.vibrate
Arduino Firmata
defmodule BadgeLib.Firmata do
...
@vibration_pulse 300
@vibration_times 7
def vibrate_pulse() do
GenServer.call(__MODULE__, :vibrate_pulse)
end
...
end
Arduino Firmata
defmodule BadgeLib.Firmata do
...
def handle_call(:vibrate_pulse, _from, s) do
send(self, {:vibrate_pulse, 0, 1})
{:reply, :ok, s}
end
def handle_info({:vibrate_pulse, @vibration_times, _},s) do
Board.digital_write(s.board, @vibration_pin, @low)
{:noreply, s}
end
def handle_info({:vibrate_pulse, times, state}, s) do
Board.digital_write(s.board, @vibration_pin, state)
state =
if state == 0, do: 1, else: 0
Process.send_after(self,
{:vibrate_pulse, times + 1, state}, @vibration_pulse)
{:noreply, s}
end
end
Running on the host
Arduino Firmata
defmodule BadgeLib.Firmata do
...
@display_clear 0x81
@display_text 0x82
@display_time 20_000
def text(message) do
GenServer.call(__MODULE__, {:text, message})
end
def clear() do
GenServer.call(__MODULE__, :clear)
end
...
end
Arduino Firmata
defmodule BadgeLib.Firmata do
...
def handle_call({:text, message}, _from, s) do
Board.sysex_write(s.board, @display_clear, "")
resp = Board.sysex_write(s.board, @display_text, message)
Process.send_after(self, :clear_display, @display_time)
{:reply, {:ok, resp}, s}
end
def handle_call(:clear, _from, s) do
resp = Board.sysex_write(s.board, @display_clear, "")
{:reply, {:ok, resp}, s}
end
def handle_info(:clear_display, s) do
Board.sysex_write(s.board, @display_clear, "")
{:noreply, s}
end
...
end
Running on the host
Arduino Firmata
defmodule BadgeFw do
use Application
alias Nerves.InterimWiFi, as: WiFi
def start(_type, _args) do
import Supervisor.Spec, warn: false
:os.cmd('modprobe mt7603e')
# Define workers and child supervisors to be supervised
children = [
worker(Task, [fn -> network end], restart: :transient),
worker(BadgeLib.Firmata, []),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BadgeFw.Supervisor]
Supervisor.start_link(children, opts)
end
...
end
Running on the target
TWITTER
3.4
Connecting to Twitter
# badge_lib/mix.exs
def application do
[applications: [:logger, :firmata, :oauth, :extwitter]]
end
defp deps do
[{:firmata, github: "mobileoverlord/firmata"},
{:oauth, github: "tim/erlang-oauth"},
{:extwitter, "~> 0.6"}]
end
Connecting to Twitter
defmodule BadgeFw.Worker do
use GenServer
def start_link(opts  []) do
GenServer.start_link(__MODULE__, [], opts)
end
def init([]) do
BadgeLib.Firmata.clear()
{:ok, %{}}
end
end
defmodule BadgeFw do
use Application
alias Nerves.InterimWiFi, as: WiFi
def start(_type, _args) do
import Supervisor.Spec, warn: false
:os.cmd('modprobe mt7603e')
# Define workers and child supervisors to be supervised
children = [
worker(Task, [fn -> network end], restart: :transient),
worker(BadgeLib.Firmata, []),
worker(BadgeFw.Worker, []),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BadgeFw.Supervisor]
Supervisor.start_link(children, opts)
end
...
end
Connecting to Twitter
defmodule BadgeFw.Worker do
use GenServer
@hashtag "#NervesBadge"
@handle "@ElixirConf"
@interval 25_000
def init([]) do
BadgeLib.Firmata.clear()
Process.send_after(self, :update, @interval)
{:ok, %{last: {nil, nil}}}
end
end
Connecting to Twitter
def handle_info(:update, %{last: {lhash, luser}} = s) do
{hash, user} = {@handle, @hashtag}
new_hash = get_tweet(hash)
new_user = get_tweet(user)
last =
cond do
new_hash != lhash ->
display_tweet(new_hash)
{new_hash, luser}
new_user != luser ->
display_tweet(new_user)
{lhash, new_user}
true -> {lhash, luser}
end
Process.send_after(self, :update, @interval)
{:noreply, %{s | last: last}}
end
Connecting to Twitter
def get_tweet(search) do
case ExTwitter.search(search, [count: 1]) do
[tweet] -> tweet
_ -> nil
end
end
def display_tweet(tweet) do
IO.puts "display tweet"
BadgeLib.Utf8ToASCII.convert(tweet.text)
|> BadgeLib.Firmata.text
BadgeLib.Firmata.vibrate_pulse
end
Connecting to Twitter
defmodule BadgeLib.Utf8ToASCII do
def convert(string), do: convert(string, <<>>)
def convert(<<c::utf8, rest::binary>>, result) when c <= 127 do
convert(rest, result <> <<c::utf8>>)
end
def convert(<<_::utf8, rest::binary>>, result) do
convert(rest, result)
end
def convert(<<>>, result) do
result
end
end
Connecting to Twitter
# badge_fw/config/config.exs
config :extwitter, :oauth, [
consumer_key: "vnBfkubUmv10QRcQjFU3lXKin",
consumer_secret: "XUk3fsulkfraaapUyMOfnVRtd8fXdlkKMQvhjDv5nnEVrsk7yA",
access_token: System.get_env("TWITTER_ACCESS_TOKEN"),
access_token_secret: System.get_env("TWITTER_ACCESS_TOKEN_SECRET")
]
Connecting to Twitter
Running on the target
WEB INTERFACES
3.5
Web Interfaces
$ cd badge/apps
$ git clone https://github.com/lancehalvorsen/badge_settings
# badge_fw/mix.exs
def application do
[mod: {BadgeFw, []},
applications: [:logger, :badge_lib,
:nerves_interim_wifi, :nerves_ntp, :badge_settings]]
end
def deps do
[{:nerves, "~> 0.3.0"},
{:nerves_interim_wifi, "~> 0.1"},
{:nerves_ntp, "~> 0.1"},
{:badge_lib, in_umbrella: true},
{:badge_settings, in_umbrella: true}]
end
Web Interfaces
Web Interfaces
# badge_fw/config/config.exs
config :badge_settings, :nerves_settings, %{
settings_file: "/root/nerves_settings.txt",
device_name: "My Awesome Device",
application_password: System.get_env("BADGE_CONFIG_PASSWORD") ||
"nerves_rulz!"
}
config :badge_settings, BadgeSettings.Endpoint,
url: [host: "0.0.0.0"],
http: [port: 80],
secret_key_base: "R02jL0Vi+tFH7YOecTua/oc0b2dETOQT8/
Sg9dD56EDKqmd8jRAdqa0CyZ7tOFIt",
render_errors: [view: BadgeSettings.ErrorView, accepts: ~w(html json)],
server: true,
pubsub: [name: BadgeSettings.PubSub,
adapter: Phoenix.PubSub.PG2]
Web Interfaces
$ cd apps/badge_settings
$ mix deps.get
$ npm install
$ ./node_modules/brunch/bin/brunch build --production
$ MIX_ENV=prod mix phoenix.digest
Running on the host
Running on the target
Web Interfaces
def network do
wlan_config =
case settings do
{:ok, settings} ->
[psk: settings.password, ssid: settings.ssid]
_ ->
Application.get_env(:badge_fw, :wlan0)
end
WiFi.setup "wlan0", wlan_config
end
def settings do
settings_file =
Application.get_env(:badge_settings, :nerves_settings).settings_file
case File.read(settings_file) do
{:error, :enoent} = error -> error
{:ok, ""} -> {:error, :empty}
{:ok, contents} ->
unencoded = :erlang.binary_to_term(contents)
{:ok, unencoded}
end
end
Web Interfaces
def handle_info(:update, %{last: {lhash, luser}} = s) do
{hash, user} =
case BadgeFw.settings do
{:ok, settings} ->
{Map.get(settings, :handle), Map.get(settings, :hashtag)}
_ ->
{@handle, @hashtag}
end
...
end
RECAP
▸ Project Layout
▸ Initialization
▸ Interfacing with Arduino
▸ Connecting to Services
▸ Web Interfaces with Phoenix
Building the Badge
Advanced Config
SECTION 4
▸ Advanced Configuration
▸ Modifying a Nerves System
▸ Initialization with erlinit
▸ Firmware Updates
▸ Licensing
What are we going to Do?
MODIFYING SYSTEMS
4.1
Reasons to make your own system
▸ Add a library or application that can’t be built using mix
▸ Postgres
▸ Qt
▸ Add a Linux kernel module or patch the kernel
▸ WiFi drivers
▸ Audio, webcams, USB peripherals
▸ Patch or change the bootloader
▸ Enable a Busybox command
▸ Likely if you need to run a shell script
▸ Consider re-implementing in Elixir
Reasons NOT to make your own system
▸ Add files to the root filesystem
▸ These can be added via the rootfs-additions mechanism
▸ Change the iex terminal tty or alter how Erlang is started
▸ Create your own erlinit.config for your project
▸ Add or remove files from the firmware update files
▸ Create your own fwup.conf for your project
▸ Summary: Try to avoid modifying systems too much since it will
take away the benefits of working with the Elixir tools
Prereqs to working with systems
▸ Concepts
▸ Buildroot
▸ Linux kernel configuration
▸ Busybox
▸ Build and deploy requirements
▸ Linux (native, VM, or Docker)
▸ Someplace to put the system image since it’s too large
for hex.pm
Buildroot
▸ Toolchain, bootloader, kernel, root filesystem

builder for embedded Linux
▸ Cross-compiled
▸ Support for building ~1800 programs and

libraries
▸ Menu system for enabling and configuring packages
▸ Uses Makefiles, but not necessary to know make even to
add packages
▸ Very well documented
World views
▸ Buildroot normally is the top-level project that builds firmware
images
▸ Nerves uses Buildroot to produce the system images that later
get combined with OTP releases
▸ Consequences
▸ Erlang packages in Buildroot aren’t usable (no ejabberd)
▸ Nerves images are much smaller due to not including all (or
most) of OTP
▸ For most development, Nerves builds are faster and can be
run on OSX
Defconfigs and .configs
▸ Anything using Kconfig to manage configuration options uses
these (Linux, Buildroot, Busybox)
▸ .config
▸ Found in the root of the build directory for the project
▸ Has the values for ALL options – hidden, derived, etc.
▸ defconfig
▸ Subset of .config with only non-default options
▸ Usually stored in source control
▸ make savedefconfig
Activity: Start a system build
▸ Clone one of the official Nerves system images
▸ https://github.com/nerves-project/nerves_system_*
▸ Method 1 – official builds
▸ cd nerves_system_foo; mix deps.get; mix compile
▸ CTRL-C to stop (mix clean may be needed to start over)
▸ Method 2 – easier for development
▸ Clone https://github.com/nerves-project/nerves_system_br
▸ nerves_system_br/create-build.sh 

nerves_system_foo/nerves_defconfig out
▸ cd out; make
Activity: Compile in a new package
▸ Go to the out directory from the last activity
▸ make menuconfig
▸ Find and enable postgresql
▸ Hint: Type the “/” key to search for postgresql. Press the number
▸ While you’re in menuconfig, take a look around
▸ Exit menuconfig. Be sure to save!
▸ Inspect .config and see that BR2_PACKAGE_POSTGRESQL is enabled
▸ make savedefconfig
▸ Inspect nerves_system_foo/nerves_defconfig has POSTGRESQL enabled
▸ Run make if you have time
The Linux kernel
▸ Why Linux? Best device driver support for embedded
▸ Nerves uses a trimmed down Linux kernel to keep 

the image size reasonable
▸ Many device drivers compiled into the kernel so no

need to load them at initialization
▸ Some device drivers still compiled as modules
▸ Module parameters unknown until runtime
▸ Don’t work when compiled into the kernel
▸ More generic images
Activity: Enable a device driver
▸ Go to the out directory from before
▸ make linux-menuconfig
▸ Enable support for USB->serial adapters – called USB Modem (CDC
ACM) support in the kernel
▸ Use “/” to search for it if you’re not sure where it is
▸ Exit and save
▸ Inspect build/linux-x.y.z/.config to see that the option was saved
▸ make linux-savedefconfig
▸ Inspect build/linux-x.y.z/defconfig to verify the option again
▸ cp build/linux-x.y.z/defconfig to the Linux defconfig for the Nerves
system. This is usually called linux-x.y.defconfig
Busybox
▸ Provides tons of Unix apps in one small binary
▸ ls, sh, dd, ps, find, cat, tail, tar, cd, mkdir, etc.
▸ ntpd, dhcpc
▸ vi (sorry, no emacs)
▸ Ideally, Nerves would not have to use Busybox
▸ Wishlist: project that adds common shell commands to
the IEx shell and improves OS process inspection
▸ Removing Busybox is not trivial so it will be with us for a
while
Activity: Modify the Busybox config
▸ Go to the out directory from before
▸ make busybox-menuconfig
▸ Enable the ping utility, exit and save
▸ Verify that CONFIG_PING is enabled in build/busybox-1.x.y/.config
▸ If your system overrides the default busybox configuration, copy the new
one on top of it
▸ If not,
▸ Copy build/busybox-1.x.y/.config to ../busybox.config
▸ make menuconfig
▸ Set the Busybox configuration to ${NERVES_DEFCONFIG_DIR}/
busybox.config
▸ make savedefconfig
Using your custom Nerves system
▸ Go to the out directory from before
▸ export NERVES_SYSTEM=$PWD
▸ Now go to your Elixir project and run mix
Publishing a custom Nerves system
▸ What you’ll need
▸ Someplace to hold your nerves_system_xyz
▸ Someplace to hold the built tarball for the system
▸ GitHub supports both - attach the tarball to a release
▸ hex.pm only supports the source repository
nerves.exs
config :nerves_system_linkit, :nerves_env,
type: :system,
version: version,
mirrors: [
"https://github.com/nerves-project/nerves_system_linkit/releases/
download/v#{version}/nerves_system_linkit-v#{version}.tar.gz",
"https://s3.amazonaws.com/nerves/artifacts/nerves_system_linkit-
#{version}.tar.gz"],

…
Final steps
▸ Go back to your system’s out directory
▸ make system
▸ Publish the created tarball
▸ Publish the source on hex.pm or git
▸ Let everyone know to reference your Nerves system image
in their mix.exs
ERLINIT
4.2
erlinit
▸ Replacement for /sbin/init that starts the Erlang virtual
machine
▸ Basic initialization of the Linux user land
▸ Loopback network connection
▸ Mounts /tmp, /proc, /sys
▸ Configures the tty
▸ Configuration stored in /etc/erlinit.conf
▸ Can be overridden by passing parameters to the Linux
kernel from the bootloader
erlinit debugging options
▸ --verbose
▸ --hang-on-exit
▸ Useful to capture error messages when the VM exits
▸ --run-on-exit /bin/sh
▸ Drop into a shell if the VM exits
▸ Exiting the shell reboots or hangs
▸ --warn-unused-tty
▸ erlinit will tell you what options to pass it to use the shell on
the terminal you’re looking at
erlinit features
▸ Mount filesystems
▸ Configure a unique hostname
▸ --hostname-pattern and --uniqueid-exec
▸ Nerves uses fhunleth/boardid to read serial numbers
▸ Wrap the launch of the Erlang VM in another program
▸ --alternate-exec
▸ Perform some custom system-specific initialization that can’t be
done in Erlang or Elixir
▸ Capture the terminal with dtach to route it to a GUI
▸ Run the Erlang VM as a regular user
erlinit pitfalls
▸ Running shell scripts to initialize the system
▸ Move initialization to Elixir to take advantage of OTP
supervision
▸ Currently no standard way of handing system
initialization in Nerves
▸ Assuming writable filesystems always can be mounted
▸ Failures happen – must be handled in Elixir
▸ erlinit can’t report errors so Elixir must check
FIRMWARE UPDATE
4.3
fwup
▸ Firmware update packaging and application
▸ Packages
▸ Zip-formatted archives
▸ Metadata
▸ All data files protected by cryptographic 

hashes
▸ Packages can be cryptographically signed
▸ Very simple scripts supported (lack of functionality is a
feature)
fwup processing
Master Boot Record
FAT Boot partition
Root Filesystem A
Read-only
Root Filesystem B
Read-only
Application Data
Read-write
rootfs.img
uImage
other files
on_init
on_finish
.fw file
fwup processing
1
2
3
4
5
Anatomy of a fwup.conf file
▸ Resources
▸ Files that are included as part of the zip archive
▸ Not required to be used when upgrading
▸ Tasks
▸ Instructions for applying updates
▸ Only one task is run at a time
▸ Tasks may have conditions for when they’re run
▸ Common task names: “complete” and “upgrade”
LinkIt Smart fwup.conf
# Let the rootfs have room to grow up to 64 MiB and align
# it to the nearest 1 MB boundary
define(ROOTFS_A_PART_OFFSET, 2048)
define(ROOTFS_A_PART_COUNT, 131072)
define(ROOTFS_B_PART_OFFSET, 133120)
define(ROOTFS_B_PART_COUNT, 131072)
# Application partition
# NOTE: Keep the total amount used under 1.78 GiB so that
# everything fits in the "2 GB" eMMC.
define(APP_PART_OFFSET, 264192)
define(APP_PART_COUNT, 1048576)
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
# Firmware metadata
meta-product = "Nerves Firmware"
meta-description = ""
meta-version = ${NERVES_SDK_VERSION}
meta-platform = "linkit"
meta-architecture = "mips"
meta-author = "Frank Hunleth"
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
file-resource rootfs.img {
host-path = ${ROOTFS}
}
file-resource uImage {
host-path = "${NERVES_SYSTEM}/images/uImage"
assert-size-lte = 30720 # 15 MiB
}
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
mbr mbr-a {
partition 0 {
block-offset = ${ROOTFS_A_PART_OFFSET}
block-count = ${ROOTFS_A_PART_COUNT}
type = 0x83 # Linux
}
partition 1 {
block-offset = ${APP_PART_OFFSET}
block-count = ${APP_PART_COUNT}
type = 0xc # FAT32
}
# partition 2 and 3 are unused
}
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
# This firmware task writes everything to the destination media
task complete {
on-init {
mbr_write(mbr-a)
}
on-resource rootfs.img {
# write to the first rootfs partition
raw_write(${ROOTFS_A_PART_OFFSET})
}
on-finish {
fat_mkfs(${APP_PART_OFFSET}, ${APP_PART_COUNT})
fat_setlabel(${APP_PART_OFFSET}, "APPDATA")
}
}
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
task upgrade.a {
# This task upgrades the A partition
require-partition-offset(0,
${ROOTFS_B_PART_OFFSET})
on-resource rootfs.img {
# write to the first rootfs partition
raw_write(${ROOTFS_A_PART_OFFSET})
}
on-finish {
# Switch over to boot the new rootfs
mbr_write(mbr-a)
}
}
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
LinkIt Smart fwup.conf
https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
task upgrade.b {
# This task upgrades the B partition
require-partition-offset(0,
${ROOTFS_A_PART_OFFSET})
on-resource rootfs.img {
# write to the first rootfs partition
raw_write(${ROOTFS_B_PART_OFFSET})
}
on-finish {
# Switch over to boot the new firmware
mbr_write(mbr-b)
}
}
FAT filesystem commands
Command Description
fat_mkfs(block_offset, block_count) Create a FAT file system
fat_write(block_offset, filename) Write the resource to the file system
fat_mv(block_offset, oldname, newname) Rename a file
fat_rm(block_offset, filename) Delete a file
fat_mkdir(block_offset, filename) Create a directory
fat_touch(block_offset, filename) Create an empty file if the file doesn't exist
LICENSING
4.4
Shipping Nerves - Licensing
▸ Buildroot infrastructure in
Nerves can aid process
▸ make legal-info
▸ Other licenses
▸ nerves-toolchain
▸ gcc
▸ crosstool-ng
▸ All of your mix
dependencies
legal-info
├── buildroot.config
├── host-licenses
│ ├── autoconf
│ │ ├── COPYING.EXCEPTION
│ │ └── COPYINGv3
│ └── ...
│ └── README
├── host-licenses.txt
├── host-manifest.csv
├── host-sources
├── licenses
│ ├── busybox
│ │ └── LICENSE
│ ├── erlang
│ │ └── LICENSE.txt
│ └── ...
├── licenses.txt
├── manifest.csv
├── README
└── sources
├── boardid-v0.4.0.tar.gz
├── busybox-1.24.1.tar.bz2
├── ...
└── zlib-1.2.8.tar.xz
Elixir Conf 2016
Nerves: Connected beyond the Node
Thursday
1:30 PM - 2:15 PM
Track 1
Justin Schneck
Building "learn to touch type" glove with
Elixir and Arduino2:15 PM - 3:00 PM
Track 2 Tetiana
Dushenkivska
Nerves + Phoenix Saves
a Father's Sanity!
Friday
1:30 PM - 2:15 PM
Track 2
Joel Byler
The Joy of Connecting Elixir to the
Physical World3:30 PM - 4:15 PM
Track 1
Frank Hunleth
Keynote4:30 PM - 5:30 PM Boyd Multerer
Elixir Conf 2016
Thank You
& Nerves

More Related Content

What's hot

Composer the right way - SunshinePHP
Composer the right way - SunshinePHPComposer the right way - SunshinePHP
Composer the right way - SunshinePHP
Rafael Dohms
 
Process injection - Malware style
Process injection - Malware styleProcess injection - Malware style
Process injection - Malware style
Sander Demeester
 
Captain Hook: Pirating AVs to Bypass Exploit Mitigations
Captain Hook: Pirating AVs to Bypass Exploit MitigationsCaptain Hook: Pirating AVs to Bypass Exploit Mitigations
Captain Hook: Pirating AVs to Bypass Exploit Mitigations
enSilo
 
Divorcing System
Divorcing SystemDivorcing System
Divorcing System
Shawn Sorichetti
 
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
linuxlab_conf
 
Embedded Android : System Development - Part IV
Embedded Android : System Development - Part IVEmbedded Android : System Development - Part IV
Embedded Android : System Development - Part IV
Emertxe Information Technologies Pvt Ltd
 
Jonathan Corbet - Keynote: The Kernel Report
Jonathan Corbet - Keynote: The Kernel ReportJonathan Corbet - Keynote: The Kernel Report
Jonathan Corbet - Keynote: The Kernel Report
linuxlab_conf
 
Embedded Recipes 2019 - Testing firmware the devops way
Embedded Recipes 2019 - Testing firmware the devops wayEmbedded Recipes 2019 - Testing firmware the devops way
Embedded Recipes 2019 - Testing firmware the devops way
Anne Nicolas
 
Dependency management with Composer
Dependency management with ComposerDependency management with Composer
Dependency management with Composer
Jason Grimes
 
Power of linked list
Power of linked listPower of linked list
Power of linked list
Peter Hlavaty
 
Steelcon 2014 - Process Injection with Python
Steelcon 2014 - Process Injection with PythonSteelcon 2014 - Process Injection with Python
Steelcon 2014 - Process Injection with Python
infodox
 
One Year of Porting - Post-mortem of two Linux/SteamOS launches
One Year of Porting - Post-mortem of two Linux/SteamOS launchesOne Year of Porting - Post-mortem of two Linux/SteamOS launches
One Year of Porting - Post-mortem of two Linux/SteamOS launches
Leszek Godlewski
 
Linux as a gaming platform - Errata
Linux as a gaming platform - ErrataLinux as a gaming platform - Errata
Linux as a gaming platform - Errata
Leszek Godlewski
 
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytes
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytesWindows Kernel Exploitation : This Time Font hunt you down in 4 bytes
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytes
Peter Hlavaty
 
Linux day 2016 Yocto Project
Linux day 2016 Yocto ProjectLinux day 2016 Yocto Project
Linux day 2016 Yocto Project
Marco Cavallini
 
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily JobLuca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
linuxlab_conf
 
Attack on the Core
Attack on the CoreAttack on the Core
Attack on the Core
Peter Hlavaty
 
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
Marco Cavallini
 
Introduction to yocto
Introduction to yoctoIntroduction to yocto
Introduction to yocto
Alex Gonzalez
 
Vulnerability desing patterns
Vulnerability desing patternsVulnerability desing patterns
Vulnerability desing patterns
Peter Hlavaty
 

What's hot (20)

Composer the right way - SunshinePHP
Composer the right way - SunshinePHPComposer the right way - SunshinePHP
Composer the right way - SunshinePHP
 
Process injection - Malware style
Process injection - Malware styleProcess injection - Malware style
Process injection - Malware style
 
Captain Hook: Pirating AVs to Bypass Exploit Mitigations
Captain Hook: Pirating AVs to Bypass Exploit MitigationsCaptain Hook: Pirating AVs to Bypass Exploit Mitigations
Captain Hook: Pirating AVs to Bypass Exploit Mitigations
 
Divorcing System
Divorcing SystemDivorcing System
Divorcing System
 
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
Valerio Di Giampietro - Introduction To IoT Reverse Engineering with an examp...
 
Embedded Android : System Development - Part IV
Embedded Android : System Development - Part IVEmbedded Android : System Development - Part IV
Embedded Android : System Development - Part IV
 
Jonathan Corbet - Keynote: The Kernel Report
Jonathan Corbet - Keynote: The Kernel ReportJonathan Corbet - Keynote: The Kernel Report
Jonathan Corbet - Keynote: The Kernel Report
 
Embedded Recipes 2019 - Testing firmware the devops way
Embedded Recipes 2019 - Testing firmware the devops wayEmbedded Recipes 2019 - Testing firmware the devops way
Embedded Recipes 2019 - Testing firmware the devops way
 
Dependency management with Composer
Dependency management with ComposerDependency management with Composer
Dependency management with Composer
 
Power of linked list
Power of linked listPower of linked list
Power of linked list
 
Steelcon 2014 - Process Injection with Python
Steelcon 2014 - Process Injection with PythonSteelcon 2014 - Process Injection with Python
Steelcon 2014 - Process Injection with Python
 
One Year of Porting - Post-mortem of two Linux/SteamOS launches
One Year of Porting - Post-mortem of two Linux/SteamOS launchesOne Year of Porting - Post-mortem of two Linux/SteamOS launches
One Year of Porting - Post-mortem of two Linux/SteamOS launches
 
Linux as a gaming platform - Errata
Linux as a gaming platform - ErrataLinux as a gaming platform - Errata
Linux as a gaming platform - Errata
 
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytes
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytesWindows Kernel Exploitation : This Time Font hunt you down in 4 bytes
Windows Kernel Exploitation : This Time Font hunt you down in 4 bytes
 
Linux day 2016 Yocto Project
Linux day 2016 Yocto ProjectLinux day 2016 Yocto Project
Linux day 2016 Yocto Project
 
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily JobLuca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
Luca Ceresoli - Buildroot vs Yocto: Differences for Your Daily Job
 
Attack on the Core
Attack on the CoreAttack on the Core
Attack on the Core
 
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
Marco Cavallini @ LinuxLab 2018 : Workshop Yocto Project, an automatic genera...
 
Introduction to yocto
Introduction to yoctoIntroduction to yocto
Introduction to yocto
 
Vulnerability desing patterns
Vulnerability desing patternsVulnerability desing patterns
Vulnerability desing patterns
 

Similar to Badge Hacking with Nerves Workshop - ElixirConf 2016 - Justin Schneck and Frank Hunleth

24HOP Introduction to Linux for SQL Server DBAs
24HOP Introduction to Linux for SQL Server DBAs24HOP Introduction to Linux for SQL Server DBAs
24HOP Introduction to Linux for SQL Server DBAs
Kellyn Pot'Vin-Gorman
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)
Dimitrios Platis
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
biicode
 
Texas 2013
Texas 2013Texas 2013
Texas 2013
krispcbsd
 
Self 2013
Self 2013Self 2013
Self 2013
krispcbsd
 
BSDCan2013
BSDCan2013BSDCan2013
BSDCan2013
krispcbsd
 
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
Bastian Feder
 
Red Hat Training
Red Hat   TrainingRed Hat   Training
Red Hat Training
Open Source Group
 
Prizm Installation Guide
Prizm Installation GuidePrizm Installation Guide
Prizm Installation Guide
vjvarenya
 
LinuxTraining_3.pptx
LinuxTraining_3.pptxLinuxTraining_3.pptx
LinuxTraining_3.pptx
eyob51
 
J+s
J+sJ+s
J+s
happyuk
 
How to make debian package from scratch (linux)
How to make debian package from scratch (linux)How to make debian package from scratch (linux)
How to make debian package from scratch (linux)
Thierry Gayet
 
Oracle11g On Fedora14
Oracle11g On Fedora14Oracle11g On Fedora14
Oracle11g On Fedora14
kmsa
 
Oracle11g on fedora14
Oracle11g on fedora14Oracle11g on fedora14
Oracle11g on fedora14
Khalid Matar Albuflasah
 
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
Michael Lee
 
OpenWRT guide and memo
OpenWRT guide and memoOpenWRT guide and memo
OpenWRT guide and memo
家榮 吳
 
Ubuntu Practice and Configuration
Ubuntu Practice and ConfigurationUbuntu Practice and Configuration
Ubuntu Practice and Configuration
Manoj Sahu
 
CI workflow in a web studio
CI workflow in a web studioCI workflow in a web studio
CI workflow in a web studio
deWeb
 
Linux
Linux Linux
Linux
Mindtree
 
IzPack at LyonJUG'11
IzPack at LyonJUG'11IzPack at LyonJUG'11
IzPack at LyonJUG'11
julien.ponge
 

Similar to Badge Hacking with Nerves Workshop - ElixirConf 2016 - Justin Schneck and Frank Hunleth (20)

24HOP Introduction to Linux for SQL Server DBAs
24HOP Introduction to Linux for SQL Server DBAs24HOP Introduction to Linux for SQL Server DBAs
24HOP Introduction to Linux for SQL Server DBAs
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Texas 2013
Texas 2013Texas 2013
Texas 2013
 
Self 2013
Self 2013Self 2013
Self 2013
 
BSDCan2013
BSDCan2013BSDCan2013
BSDCan2013
 
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
Advanced Eclipse Workshop (held at IPC2010 -spring edition-)
 
Red Hat Training
Red Hat   TrainingRed Hat   Training
Red Hat Training
 
Prizm Installation Guide
Prizm Installation GuidePrizm Installation Guide
Prizm Installation Guide
 
LinuxTraining_3.pptx
LinuxTraining_3.pptxLinuxTraining_3.pptx
LinuxTraining_3.pptx
 
J+s
J+sJ+s
J+s
 
How to make debian package from scratch (linux)
How to make debian package from scratch (linux)How to make debian package from scratch (linux)
How to make debian package from scratch (linux)
 
Oracle11g On Fedora14
Oracle11g On Fedora14Oracle11g On Fedora14
Oracle11g On Fedora14
 
Oracle11g on fedora14
Oracle11g on fedora14Oracle11g on fedora14
Oracle11g on fedora14
 
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
Setup of EDA tools and workstation environment variables in NCTU 307 Lab. wor...
 
OpenWRT guide and memo
OpenWRT guide and memoOpenWRT guide and memo
OpenWRT guide and memo
 
Ubuntu Practice and Configuration
Ubuntu Practice and ConfigurationUbuntu Practice and Configuration
Ubuntu Practice and Configuration
 
CI workflow in a web studio
CI workflow in a web studioCI workflow in a web studio
CI workflow in a web studio
 
Linux
Linux Linux
Linux
 
IzPack at LyonJUG'11
IzPack at LyonJUG'11IzPack at LyonJUG'11
IzPack at LyonJUG'11
 

Recently uploaded

Gas agency management system project report.pdf
Gas agency management system project report.pdfGas agency management system project report.pdf
Gas agency management system project report.pdf
Kamal Acharya
 
Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...
Prakhyath Rai
 
Digital Twins Computer Networking Paper Presentation.pptx
Digital Twins Computer Networking Paper Presentation.pptxDigital Twins Computer Networking Paper Presentation.pptx
Digital Twins Computer Networking Paper Presentation.pptx
aryanpankaj78
 
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by AnantLLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
Anant Corporation
 
Generative AI Use cases applications solutions and implementation.pdf
Generative AI Use cases applications solutions and implementation.pdfGenerative AI Use cases applications solutions and implementation.pdf
Generative AI Use cases applications solutions and implementation.pdf
mahaffeycheryld
 
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
IJECEIAES
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
Divyanshu
 
Rainfall intensity duration frequency curve statistical analysis and modeling...
Rainfall intensity duration frequency curve statistical analysis and modeling...Rainfall intensity duration frequency curve statistical analysis and modeling...
Rainfall intensity duration frequency curve statistical analysis and modeling...
bijceesjournal
 
Data Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason WebinarData Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason Webinar
UReason
 
CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1
PKavitha10
 
morris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdfmorris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdf
ycwu0509
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
Yasser Mahgoub
 
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
Paris Salesforce Developer Group
 
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
shadow0702a
 
Curve Fitting in Numerical Methods Regression
Curve Fitting in Numerical Methods RegressionCurve Fitting in Numerical Methods Regression
Curve Fitting in Numerical Methods Regression
Nada Hikmah
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
Yasser Mahgoub
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
kandramariana6
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
MDSABBIROJJAMANPAYEL
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
IJECEIAES
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
171ticu
 

Recently uploaded (20)

Gas agency management system project report.pdf
Gas agency management system project report.pdfGas agency management system project report.pdf
Gas agency management system project report.pdf
 
Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...
 
Digital Twins Computer Networking Paper Presentation.pptx
Digital Twins Computer Networking Paper Presentation.pptxDigital Twins Computer Networking Paper Presentation.pptx
Digital Twins Computer Networking Paper Presentation.pptx
 
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by AnantLLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
 
Generative AI Use cases applications solutions and implementation.pdf
Generative AI Use cases applications solutions and implementation.pdfGenerative AI Use cases applications solutions and implementation.pdf
Generative AI Use cases applications solutions and implementation.pdf
 
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
 
Rainfall intensity duration frequency curve statistical analysis and modeling...
Rainfall intensity duration frequency curve statistical analysis and modeling...Rainfall intensity duration frequency curve statistical analysis and modeling...
Rainfall intensity duration frequency curve statistical analysis and modeling...
 
Data Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason WebinarData Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason Webinar
 
CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1
 
morris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdfmorris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdf
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
 
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
AI + Data Community Tour - Build the Next Generation of Apps with the Einstei...
 
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
Use PyCharm for remote debugging of WSL on a Windo cf5c162d672e4e58b4dde5d797...
 
Curve Fitting in Numerical Methods Regression
Curve Fitting in Numerical Methods RegressionCurve Fitting in Numerical Methods Regression
Curve Fitting in Numerical Methods Regression
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
 

Badge Hacking with Nerves Workshop - ElixirConf 2016 - Justin Schneck and Frank Hunleth

  • 2. ▸ Introduction ▸ Definitions ▸ Nerves Intro ▸ Host Tools ▸ Targets ▸ Host / Target Prep What are we going to Do?
  • 3. ▸ Interfacing with Hardware ▸ Boot to IEx ▸ Pin Muxing ▸ Blinky with elixir_ale ▸ Blinky with Firmata and Arduino What are we going to Do?
  • 4. ▸ Building the Badge ▸ Project Layout ▸ Arduino and Firmata ▸ Connecting to Twitter ▸ Web Interfaces What are we going to Do?
  • 5. ▸ Advanced Configuration ▸ Modifying a Nerves System ▸ Initialization with erlinit ▸ Firmware Updates ▸ Licensing What are we going to Do?
  • 7. ▸ Introduction ▸ Definitions ▸ Nerves Intro ▸ Host Tools ▸ Targets ▸ Host / Target Prep What are we going to Do?
  • 9. ▸ host ▸ target ▸ toolchain ▸ system Definitions ▸ artifact ▸ assemble ▸ firmware bundle ▸ firmware image
  • 10. host The computer on which you are editing source code, compiling, and assembling firmware Definitions
  • 11. target The platform for which your firmware is built (for example, Raspberry Pi, Raspberry Pi 2, or Beaglebone Black) Definitions
  • 12. toolchain The tools required to build code for the target, such as compilers, linkers, binutils, and C runtime Definitions
  • 13. system A lean Buildroot-based Linux distribution that has been customized and cross-compiled for a particular target Definitions
  • 14. assembly The process of combining system, application, and configuration into a firmware bundle Definitions
  • 15. firmware bundle A single file that contains an assembled version of everything needed to burn firmware Definitions
  • 16. firmware image Built from a firmware bundle and contains the partition table, partitions, bootloader, etc. Definitions
  • 18. Nerves Your Elixir project and dependencies Linux, C libraries, Erlang runtime Nerves System Image OTP release Firmware bundle
  • 19. Compiling on your machine YOUR APP ELIXIR C CODE NIF / PORTS MIX BEAM BINARY YOUR APP (ARCH SPECIFIC)
  • 20. Mixing firmware MIX YOUR APP (FOR RPI2) TOOLCHAIN SYSTEM rpi2 Precompile compile
  • 21. Toolchains TOOLCHAINTOOLCHAIN CONFIG • crosstool-ng • for target • host configs • compilers • run on host • compile for target
  • 22. What’s in a Nerves system package? ▸ Elixir build infrastructure ▸ mix.exs – build the system image via mix ▸ nerves.exs – additional system image information such as where to find pre-built system images ▸ Buildroot configuration ▸ nerves_defconfig – top level configuration options ▸ Custom package definitions ▸ Linux kernel configuration and patches ▸ Board-specific root file system additions
  • 24. Create a new Nerves app
  • 25. New Projects $ mix nerves.new my_app --target linkit
  • 26. MIX FILE defmodule MyApp.Mixfile do use Mix.Project @target System.get_env("NERVES_TARGET") || "linkit" … end New Projects
  • 27. MIX FILE defmodule MyApp.Mixfile do … def project do [app: :my_app, version: "0.1.0", archives: [nerves_bootstrap: "~> 0.1"], target: @target, deps_path: "deps/#{@target}", build_path: "_build/#{@target}", aliases: aliases, deps: deps ++ system(@target)] end end New Projects
  • 28. MIX FILE defmodule MyApp.Mixfile do … def aliases do ["deps.precompile": ["nerves.precompile", "deps.precompile"], "deps.loadpaths": ["deps.loadpaths", "nerves.loadpaths"]] end end New Projects
  • 29. MIX FILE defmodule MyApp.Mixfile do … def system(target) do [{:"nerves_system_#{target}", ">= 0.0.0"}] end … end New Projects
  • 30. Firmware $ mix firmware $ mix firmware.burn
  • 31. Flash layout Master Boot Record Bootloaders Root Filesystem A Read-only Root Filesystem B Read-only Application Data Read-write Linux kernel Erlang C libraries and apps OTP release App settings Database Logs Other files
  • 32. Nerves root filesystem . ├── bin ├── dev ├── etc ├── lib ├── mnt ├── proc ├── root ├── sbin ├── srv ├── sys ├── tmp ├── usr └── var ├── srv │ └── erlang │ ├── lib │ │ ├── compiler-7.0 │ │ ├── elixir-1.3.2 │ │ ├── kernel-5.0 │ │ ├── logger-1.3.1 │ │ ├── your_app-0.1.0 │ │ ├── sasl-3.0 │ │ └── stdlib-3.0 │ └── releases │ ├── 0.0.1 │ │ ├── your_app.boot │ │ ├── your_app.rel │ │ ├── your_app.script │ │ ├── sys.config │ │ └── vm.args │ ├── RELEASES │ └── start_erl.data
  • 34. NERVES ▸ Erlang OTP ~> 19 ▸ Elixir ~> 1.3.2 ▸ fwup ~> 0.8.2 ▸ squashfs-tools ▸ nerves_bootstrap Host Tools ▸ Arduino IDE ~> 1.6 BADGE HACKING
  • 35. Host Tools # copy and untar system and toolchain $ export NERVES_SYSTEM=/path/to/uncompressed/system $ export NERVES_TOOLCHAIN=/path/to/uncompressed/toolchain Cached Nerves System and Nerves Toolchain Since bandwidth is limited Make sure you unset after the conference.
  • 37. ERLANG & ELIXIR Host Tools $ brew update $ brew install elixir Mac OS Linux https://www.erlang-solutions.com/resources/download.html http://elixir-lang.org/install.html
  • 38. FWUP Host Tools $ brew install fwup Download Debian package from https://github.com/fhunleth/fwup/ $ sudo dpkg -i fwup_0.8.2_amd64.deb Mac OS Linux
  • 39. ADDITIONAL TOOLS Host Tools Mac OS Linux $ brew install squashfs coreutils picocom $ sudo apt-get install squashfs-tools picocom $ sudo vigr # Add yourself to the dialout group dialout:x:20:yourusername
  • 40. ADDITIONAL TOOLS Host Tools All $ mix local.hex $ mix local.rebar $ mix archive.install https://github.com/nerves-project/archives/raw/master/ nerves_bootstrap.ez
  • 41. CONSOLE CABLE DRIVERS Host Tools Mac OS Linux https://www.adafruit.com/images/product-files/954/ PL2303_MacOSX_1_6_0_20151022.zip "Everything just works on Linux" ~Frank Hunleth
  • 42. ARDUINO TOOLS Host Tools ▸ Install Arduino IDE ~> 1.6.0 ▸ Arduino -> Preferences -> Additional Boards Manager URLs ▸ http://download.labs.mediatek.com/ package_mtk_linkit_smart_7688_test_index.json ▸ Tools -> Board: ... -> Boards Manager ▸ Search: linkit -> install 0.1.8
  • 44. Official Nerves systems ▸ nerves_system_bbb (black and green) ▸ nerves_system_rpi, nerves_system_rpi2, nerves_system_rpi3 ▸ nerves_system_qemu_arm ▸ nerves_system_alix, nerves_system_ag150 ▸ nerves_system_galileo ▸ nerves_system_linkit
  • 45. TARGET SPECIFIC CONFIG # config.exs use Mix.Config import_config "#{Mix.Project.config[:target]}.exs" Multi-Target
  • 46. CHANGING TARGETS NERVES_TARGET=linkit mix deps.get export NERVES_TARGET=linkit mix deps.get mix deps.get # @target System.get_env("NERVES_TARGET") || "linkit" Multi-Target
  • 51.
  • 53. LinkIt Smart 7688 Flash ▸ 32 MB NAND Flash ▸ Bootloader and Linux kernel stored here ▸ LinkIt bootloader doesn’t support FTL so don’t update too many times ▸ Linux mtd driver provides access (/dev/mtdblock1, etc.) ▸ MicroSD card ▸ Root filesystems ▸ Application data
  • 54. Typical Nerves Flash layout Master Boot Record Bootloaders Root Filesystem A Read-only Root Filesystem B Read-only Application Data Read-write Linux kernel Erlang C libraries and apps OTP release App settings Database Logs Other files
  • 55. Flash layout Master Boot Record Root Filesystem A Read-only Root Filesystem B Read-only Application Data Read-write Erlang C libraries and apps OTP release App settings Database Logs Other files Bootloader Linux Kernel
  • 56. The Console ▸ Black - Ground ▸ Red - 5v ▸ White - RX ▸ Green - TX
  • 57. The Console ▸ Black - Ground ▸ Red - 5v ▸ White(RX) - TX ▸ Green(TX) - RX Don’t worry about accidentally swapping RX and TX. If nothing shows up on
 the console, just swap them.
  • 58. The Console ▸ Baud: 57600 ▸ Bits: 8 ▸ Parity: None ▸ Stop Bits: 1
  • 59. The Console $ picocom -b 57600 /dev/tty.usbserial
  • 60. BURN BOOTLOADER ▸ Connect a 3.3V FTDI cable (GND, RX, and TX) to the LinkIt Smart. Power up the LinkIt Smart and verify that you can see text and type. You should be interacting with the default OpenWRT firmware. ▸ Remove power from the LinkIt Smart LinkIt Smart Prep
  • 61. BURN BOOTLOADER ▸ Plug the USB Flash drive into the LinkIt Smart via the On- the-go cable. Make sure that it's plugged into the USB Host connector. ▸ Press the 'b' key repeatedly on the serial port while rebooting the LinkIt Smart. ▸ Stop when you see that it is programming the Flash. LinkIt Smart Prep
  • 62. BURN LINUX KERNEL ▸ Press the '5' key repeatedly on the serial port while rebooting the LinkIt Smart. ▸ Stop when you see that it is programming the Flash. ▸ Should be looking for rootfs on SD LinkIt Smart Prep
  • 64. RECAP ▸ What is Nerves ▸ Definitions ▸ Differences In Targets ▸ Preparing your Host ▸ Preparing the Target Title
  • 66. ▸ Interfacing with Hardware ▸ Boot to IEx ▸ Pin Muxing ▸ Blinky with elixir_ale ▸ Blinky with Firmata and Arduino What are we going to Do?
  • 68. Booting to IEx $ mix nerves.new console --target linkit $ cd console $ mix deps.get $ mix firmware $ mix firmware.burn https://github.com/mobileoverlord/console Fast track
  • 70. Adding IEx Helpers defmodule Console.IExHelpers do def cat(file) do File.read!(file) |> IO.puts end end
  • 71. Adding IEx Helpers # config/rootfs-additions/etc/iex.exs import Console.IExHelpers
  • 72. Adding IEx Helpers # config/config.exs config :nerves, :firmware, rootfs_additions: "config/rootfs-additions"
  • 73. Adding IEx Helpers # rel/vm.args ## Start the Elixir shell -noshell -user Elixir.IEx.CLI -extra --no-halt +iex --dot-iex /etc/iex.exs
  • 74. Adding IEx Helpers $ mix firmware $ mix firmware.burn
  • 75. Adding IEx Helpers iex(1)> cat "/etc/iex.exs" import Console.IExHelpers :ok
  • 78. A layer of indirection GPIO SPI I2C Camera In Ethernet Timer PWM MMC Display DRAM Power Pin mux Physical pins
  • 79. Beaglebone Black (TI AM335x) http://www.embedded-things.com/bbb/beaglebone-black-pin-mux-spreadsheet/
  • 81. ▸ Bootloader ▸ Some pins have to configured as early as possible ▸ Usually only deal with this on custom boards ▸ Linux kernel device tree ▸ Textual description of the hardware configuration ▸ Compiled down and loaded early in the Linux boot process ▸ Device tree overlays may be loaded after boot (but not supported on the LinkIt Smart) ▸ Custom programs Configuring pinmux’d processors
  • 82. ▸ Device tree ▸ linux-4.4.14/arch/mips/boot/dts/ralink/LINKIT7688.dts ▸ Compiled to LINKIT7688.dtb ▸ Usermode app - pinmux ▸ https://github.com/nerves-project/nerves_system_linkit/ tree/develop/package/mtk-linkit ▸ Invoke on the Linkit Smart as: LinkIt Smart pinmux configuration /usr/bin/pinmux set ephy gpio
  • 84.
  • 86. Host Tools $ mix nerves.new blinky_ale --target linkit https://github.com/mobileoverlord/blinky_ale Fast track
  • 87. MIX FILE defmodule BlinkyAle.Mixfile do … def application do [mod: {BlinkyAle, []}, applications: [:logger, :elixir_ale]] end def deps do [{:nerves, "~> 0.3.0"}, {:elixir_ale, "~> 0.5.6"}] end end Blinky Elixir Ale
  • 88. defmodule BlinkyAle do use Application # See http://elixir-lang.org/docs/stable/elixir/Application.html # for more information on OTP Applications def start(_type, _args) do import Supervisor.Spec, warn: false # Define workers and child supervisors to be supervised children = [ worker(Task, [fn -> blink end], restart: :transient), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: BlinkyAle.Supervisor] Supervisor.start_link(children, opts) end ... end Blinky Elixir Ale
  • 89. defmodule BlinkyAle do ... def blink do :os.cmd '/usr/bin/pinmux set ephy gpio' {:ok, pid} = Gpio.start_link(43, :output) blink_forever(pid) end def blink_forever(pid) do Gpio.write(pid, 1) :timer.sleep(1000) Gpio.write(pid, 0) :timer.sleep(1000) blink_forever(pid) end end Blinky Elixir Ale
  • 92. Arduino D13 LED Arduino Serial / Power
  • 93. Running on the Host // SimpleFirmata.ino Firmata.begin(57600); while (!Serial) { ; }
  • 94. Arduino Firmata ▸ File -> Examples -> Firmata -> StandardFirmata
  • 95. Write to the Arduino
  • 96. Running on the Host $ mix new blinky_firmata_host https://github.com/mobileoverlord/blinky_firmata_host Fast track
  • 97. Running on the Host defmodule BlinkyFirmataHost.Mixfile do ... def application do [applications: [:logger, :firmata], mod: {BlinkyFirmataHost, []}] end defp deps do [{:firmata, github: "mobileoverlord/firmata"}] end end
  • 98. Running on the Host defmodule BlinkyFirmataHost.Protocol do use GenServer use Firmata.Protocol.Mixin alias Firmata.Board def start_link(tty, opts []) do GenServer.start_link(__MODULE__, [tty, opts], name: __MODULE__) end def init([tty, opts]) do IO.puts "Init" {:ok, board} = Board.start_link(tty, opts) {:ok, %{ board: board }} end ... end
  • 99. Running on the Host defmodule BlinkyFirmataHost.Protocol do ... def handle_info({:firmata, {:pin_map, _pin_map}}, s) do IO.puts "Set Pin Map" Board.set_pin_mode(s.board, 13, @output) send(self, {:blink, 1}) {:noreply, s} end def handle_info({:blink, state}, s) do IO.puts "Blink" Board.digital_write(s.board, 13, state) state = if state == 1, do: 0, else: 1 Process.send_after(self, {:blink, state}, 1000) {:noreply, s} end def handle_info(_, s) do {:noreply, s} end end
  • 100. Running on the Host Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Nerves.UART.enumerate %{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{}, "/dev/cu.MyQC-SPPDev-1" => %{}, "/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id: 43777, vendor_id: 3725}}
  • 101. Running on the Host iex(2)> BlinkyFirmataHost.Protocol.start_link "/dev/cu.usbmodem1421" Init {:ok, #PID<0.156.0>} Set Pin Map Blink
  • 103. Running on the Target // StandardFirmata.ino // Connecting from LinkIt Serial1.begin(57600); Firmata.begin(Serial1); // Connecting from Host // Firmata.begin(57600); // while (!Serial) { // ; // // }
  • 104. Write to the Arduino
  • 105. Running on the Target $ mix nerves.new blinky_firmata --target linkit https://github.com/mobileoverlord/blinky_firmata Fast track
  • 106. Running on the Target defmodule BlinkyFirmata.Mixfile do ... def application do [applications: [:logger, :firmata], mod: {BlinkyFirmata, []}] end defp deps do [{:nerves, "~> 0.3.0"}, {:firmata, github: "mobileoverlord/firmata"}] end end
  • 107. Running on the Target defmodule BlinkyFirmata.Protocol do use GenServer use Firmata.Protocol.Mixin alias Firmata.Board def start_link(tty, opts []) do GenServer.start_link(__MODULE__, [tty, opts], name: __MODULE__) end def init([tty, opts]) do IO.puts "Init" {:ok, board} = Board.start_link(tty, opts) {:ok, %{ board: board }} end ... end
  • 108. Running on the Target defmodule BlinkyFirmata.Protocol do ... def handle_info({:firmata, {:pin_map, _pin_map}}, s) do IO.puts "Set Pin Map" Board.set_pin_mode(s.board, 13, @output) send(self, {:blink, 1}) {:noreply, s} end def handle_info({:blink, state}, s) do IO.puts "Blink" Board.digital_write(s.board, 13, state) state = if state == 1, do: 0, else: 1 Process.send_after(self, {:blink, state}, 1000) {:noreply, s} end def handle_info(_, s) do {:noreply, s} end end
  • 109. Running on the Target defmodule BlinkyFirmata do use Application def start(_type, _args) do import Supervisor.Spec, warn: false # Define workers and child supervisors to be supervised children = [ worker(BlinkyFirmata.Protocol, ["ttyS0"]), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: BlinkyFirmata.Supervisor] Supervisor.start_link(children, opts) end end
  • 110. Running on the Target $ mix firmware $ mix firmware.burn
  • 113. ▸ Elixir Ale ▸ Firmata ▸ Ports ▸ When to use which strategy Recap
  • 115. ▸ Building the Badge ▸ Project Layout ▸ Arduino and Firmata ▸ Connecting to Twitter ▸ Web Interfaces What are we going to Do?
  • 117. UMBRELLA ▸ Organize facets of code in our project for isolation ▸ Ability to run aspects on Host Project Layout
  • 118. Project Layout $ mix new badge --umbrella └── badge ├── README.md ├── apps ├── config │   └── config.exs └── mix.exs https://github.com/mobileoverlord/badge Fast track
  • 119. Project Layout $ cd badge/apps $ mix nerves.new badge_fw --target linkit $ mix new badge_lib ├── badge_fw └── badge_lib
  • 120. Project Layout # apps/badge_fw/mix.exs def application do [mod: {BadgeFw, []}, applications: [:logger, :badge_lib]] end def deps do [{:nerves, "~> 0.3.0"}, {:badge_lib, in_umbrella: true}] end
  • 122. Project Layout # apps/badge_fw $ mix deps.get $ mix firmware
  • 123. Project Layout # apps/badge_fw $ mix deps.get $ mix firmware ** (UndefinedFunctionError) function :relx.do/2 is undefined (module :relx is not available) If you are at the top of the umbrella
  • 125. Initialization # apps/badge_fw/mix.exs def application do [mod: {BadgeFw, []}, applications: [:logger, :badge_lib, :nerves_interim_wifi]] end def deps do [{:nerves, "~> 0.3.0"}, {:nerves_interim_wifi, "~> 0.1"}, {:badge_lib, in_umbrella: true}] end # apps/badge_fw $ mix deps.get
  • 126. Initialization # apps/badge_fw/lib/badge_fw.ex defmodule BadgeFw do use Application alias Nerves.InterimWiFi, as: WiFi def start(_type, _args) do import Supervisor.Spec, warn: false :os.cmd('modprobe mt7603e') # Define workers and child supervisors to be supervised children = [ worker(Task, [fn -> network end], restart: :transient), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: BadgeFw.Supervisor] Supervisor.start_link(children, opts) end def network do wlan_config = Application.get_env(:badge_fw, :wlan0) WiFi.setup "wlan0", wlan_config end end
  • 127. Initialization # apps/badge_fw/config/config.exs use Mix.Config config :badge_fw, :wlan0, ssid: "Nerves", key_mgmt: :"WPA-PSK", psk: "nervesnet"
  • 128. Test it out :inet.gethostbyname 'nerves-project.org' $ mix firmware $ mix firmware.burn Initialization
  • 129. Initialization # apps/badge_fw/mix.exs def application do [mod: {BadgeFw, []}, applications: [:logger, :badge_lib, :nerves_interim_wifi, :nerves_ntp]] end def deps do [{:nerves, "~> 0.3.0"}, {:nerves_interim_wifi, "~> 0.1"}, {:nerves_ntp, "~> 0.1"}, {:badge_lib, in_umbrella: true}] end # apps/badge_fw $ mix deps.get
  • 130. # apps/badge_fw/config/config.exs config :nerves_ntp, :ntpd, "/usr/sbin/ntpd" config :nerves_ntp, :servers, [ "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org" ] Initialization
  • 131. # badge_fw/rel/vm.args -sname badge -setcookie nerves ## Start the Elixir shell -noshell -user Elixir.IEx.CLI -extra --no-halt Initialization Enable distributed Erlang
  • 132. remsh iex --sname host --cookie nerves --remsh badge@nerves-244a
  • 134. Connecting the Components Vibration motor Display D9 I2C
  • 135. $ git clone https://github.com/mobileoverlord/badge_firmata Arduino Firmata
  • 136. # badge_lib/mix.exs def application do [applications: [:logger, :firmata]] end defp deps do [{:firmata, github: "mobileoverlord/firmata"}] end Arduino Firmata $ mix deps.get
  • 138. Write to the Arduino
  • 139. Arduino Firmata defmodule BadgeLib.Firmata do use GenServer use Firmata.Protocol.Modes alias Firmata.Board, as: Board def start_link(opts []) do port = opts[:port] || "ttyS0" speed = opts[:speed] || 57600 serial_opts = [speed: speed] GenServer.start_link(__MODULE__, [port, serial_opts], name: __MODULE__) end def init([port, serial_opts]) do {:ok, board} = Board.start_link(port, serial_opts) {:ok, %{ board: board }} end ... end
  • 140. Arduino Firmata defmodule BadgeLib.Firmata do ... def handle_info({:firmata, {:pin_map, _pin_map}}, s) do {:noreply, s} end def handle_info(_, s) do {:noreply, s} end end
  • 141. Arduino Firmata defmodule BadgeLib.Firmata do use GenServer use Firmata.Protocol.Modes alias Firmata.Board, as: Board @high 1 @low 0 @vibration_pin 9 def start_link(opts []) def vibrate(state @high) do GenServer.call(__MODULE__, {:vibrate, state}) end ... end
  • 142. Arduino Firmata defmodule BadgeLib.Firmata do ... def handle_call({:vibrate, state}, _from, s) do Board.digital_write(s.board, @vibration_pin, state) {:reply, :ok, s} end def handle_info({:firmata, {:pin_map, _pin_map}}, s) do Board.set_pin_mode(s.board, @vibration_pin, @output) {:noreply, s} end end
  • 143. Running on the host Firmata.begin(57600); while (!Serial) { ; }
  • 144. Running on the host Firmata.begin(57600); while (!Serial) { ; } Interactive Elixir (1.3.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Nerves.UART.enumerate %{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{}, "/dev/cu.MyQC-SPPDev-1" => %{}, "/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id: 43777, vendor_id: 3725}}
  • 145. Running on the host %{"/dev/cu.Bluetooth-Incoming-Port" => %{}, "/dev/cu.MyQC-SPPDev" => %{}, "/dev/cu.MyQC-SPPDev-1" => %{}, "/dev/cu.usbmodem1421" => %{manufacturer: "MediaTek Labs", product_id: 43777, vendor_id: 3725}} iex(2)> BadgeLib.Firmata.start_link(port: "/dev/cu.usbmodem1421")
  • 146. Running on the host iex(3)> BadgeLib.Firmata.vibrate
  • 147. Arduino Firmata defmodule BadgeLib.Firmata do ... @vibration_pulse 300 @vibration_times 7 def vibrate_pulse() do GenServer.call(__MODULE__, :vibrate_pulse) end ... end
  • 148. Arduino Firmata defmodule BadgeLib.Firmata do ... def handle_call(:vibrate_pulse, _from, s) do send(self, {:vibrate_pulse, 0, 1}) {:reply, :ok, s} end def handle_info({:vibrate_pulse, @vibration_times, _},s) do Board.digital_write(s.board, @vibration_pin, @low) {:noreply, s} end def handle_info({:vibrate_pulse, times, state}, s) do Board.digital_write(s.board, @vibration_pin, state) state = if state == 0, do: 1, else: 0 Process.send_after(self, {:vibrate_pulse, times + 1, state}, @vibration_pulse) {:noreply, s} end end
  • 149. Running on the host
  • 150. Arduino Firmata defmodule BadgeLib.Firmata do ... @display_clear 0x81 @display_text 0x82 @display_time 20_000 def text(message) do GenServer.call(__MODULE__, {:text, message}) end def clear() do GenServer.call(__MODULE__, :clear) end ... end
  • 151. Arduino Firmata defmodule BadgeLib.Firmata do ... def handle_call({:text, message}, _from, s) do Board.sysex_write(s.board, @display_clear, "") resp = Board.sysex_write(s.board, @display_text, message) Process.send_after(self, :clear_display, @display_time) {:reply, {:ok, resp}, s} end def handle_call(:clear, _from, s) do resp = Board.sysex_write(s.board, @display_clear, "") {:reply, {:ok, resp}, s} end def handle_info(:clear_display, s) do Board.sysex_write(s.board, @display_clear, "") {:noreply, s} end ... end
  • 152. Running on the host
  • 153. Arduino Firmata defmodule BadgeFw do use Application alias Nerves.InterimWiFi, as: WiFi def start(_type, _args) do import Supervisor.Spec, warn: false :os.cmd('modprobe mt7603e') # Define workers and child supervisors to be supervised children = [ worker(Task, [fn -> network end], restart: :transient), worker(BadgeLib.Firmata, []), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: BadgeFw.Supervisor] Supervisor.start_link(children, opts) end ... end
  • 154. Running on the target
  • 156. Connecting to Twitter # badge_lib/mix.exs def application do [applications: [:logger, :firmata, :oauth, :extwitter]] end defp deps do [{:firmata, github: "mobileoverlord/firmata"}, {:oauth, github: "tim/erlang-oauth"}, {:extwitter, "~> 0.6"}] end
  • 157. Connecting to Twitter defmodule BadgeFw.Worker do use GenServer def start_link(opts []) do GenServer.start_link(__MODULE__, [], opts) end def init([]) do BadgeLib.Firmata.clear() {:ok, %{}} end end
  • 158. defmodule BadgeFw do use Application alias Nerves.InterimWiFi, as: WiFi def start(_type, _args) do import Supervisor.Spec, warn: false :os.cmd('modprobe mt7603e') # Define workers and child supervisors to be supervised children = [ worker(Task, [fn -> network end], restart: :transient), worker(BadgeLib.Firmata, []), worker(BadgeFw.Worker, []), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: BadgeFw.Supervisor] Supervisor.start_link(children, opts) end ... end Connecting to Twitter
  • 159. defmodule BadgeFw.Worker do use GenServer @hashtag "#NervesBadge" @handle "@ElixirConf" @interval 25_000 def init([]) do BadgeLib.Firmata.clear() Process.send_after(self, :update, @interval) {:ok, %{last: {nil, nil}}} end end Connecting to Twitter
  • 160. def handle_info(:update, %{last: {lhash, luser}} = s) do {hash, user} = {@handle, @hashtag} new_hash = get_tweet(hash) new_user = get_tweet(user) last = cond do new_hash != lhash -> display_tweet(new_hash) {new_hash, luser} new_user != luser -> display_tweet(new_user) {lhash, new_user} true -> {lhash, luser} end Process.send_after(self, :update, @interval) {:noreply, %{s | last: last}} end Connecting to Twitter
  • 161. def get_tweet(search) do case ExTwitter.search(search, [count: 1]) do [tweet] -> tweet _ -> nil end end def display_tweet(tweet) do IO.puts "display tweet" BadgeLib.Utf8ToASCII.convert(tweet.text) |> BadgeLib.Firmata.text BadgeLib.Firmata.vibrate_pulse end Connecting to Twitter
  • 162. defmodule BadgeLib.Utf8ToASCII do def convert(string), do: convert(string, <<>>) def convert(<<c::utf8, rest::binary>>, result) when c <= 127 do convert(rest, result <> <<c::utf8>>) end def convert(<<_::utf8, rest::binary>>, result) do convert(rest, result) end def convert(<<>>, result) do result end end Connecting to Twitter
  • 163. # badge_fw/config/config.exs config :extwitter, :oauth, [ consumer_key: "vnBfkubUmv10QRcQjFU3lXKin", consumer_secret: "XUk3fsulkfraaapUyMOfnVRtd8fXdlkKMQvhjDv5nnEVrsk7yA", access_token: System.get_env("TWITTER_ACCESS_TOKEN"), access_token_secret: System.get_env("TWITTER_ACCESS_TOKEN_SECRET") ] Connecting to Twitter
  • 164. Running on the target
  • 166. Web Interfaces $ cd badge/apps $ git clone https://github.com/lancehalvorsen/badge_settings
  • 167. # badge_fw/mix.exs def application do [mod: {BadgeFw, []}, applications: [:logger, :badge_lib, :nerves_interim_wifi, :nerves_ntp, :badge_settings]] end def deps do [{:nerves, "~> 0.3.0"}, {:nerves_interim_wifi, "~> 0.1"}, {:nerves_ntp, "~> 0.1"}, {:badge_lib, in_umbrella: true}, {:badge_settings, in_umbrella: true}] end Web Interfaces
  • 168. Web Interfaces # badge_fw/config/config.exs config :badge_settings, :nerves_settings, %{ settings_file: "/root/nerves_settings.txt", device_name: "My Awesome Device", application_password: System.get_env("BADGE_CONFIG_PASSWORD") || "nerves_rulz!" } config :badge_settings, BadgeSettings.Endpoint, url: [host: "0.0.0.0"], http: [port: 80], secret_key_base: "R02jL0Vi+tFH7YOecTua/oc0b2dETOQT8/ Sg9dD56EDKqmd8jRAdqa0CyZ7tOFIt", render_errors: [view: BadgeSettings.ErrorView, accepts: ~w(html json)], server: true, pubsub: [name: BadgeSettings.PubSub, adapter: Phoenix.PubSub.PG2]
  • 169. Web Interfaces $ cd apps/badge_settings $ mix deps.get $ npm install $ ./node_modules/brunch/bin/brunch build --production $ MIX_ENV=prod mix phoenix.digest
  • 170. Running on the host
  • 171. Running on the target
  • 172.
  • 173. Web Interfaces def network do wlan_config = case settings do {:ok, settings} -> [psk: settings.password, ssid: settings.ssid] _ -> Application.get_env(:badge_fw, :wlan0) end WiFi.setup "wlan0", wlan_config end def settings do settings_file = Application.get_env(:badge_settings, :nerves_settings).settings_file case File.read(settings_file) do {:error, :enoent} = error -> error {:ok, ""} -> {:error, :empty} {:ok, contents} -> unencoded = :erlang.binary_to_term(contents) {:ok, unencoded} end end
  • 174. Web Interfaces def handle_info(:update, %{last: {lhash, luser}} = s) do {hash, user} = case BadgeFw.settings do {:ok, settings} -> {Map.get(settings, :handle), Map.get(settings, :hashtag)} _ -> {@handle, @hashtag} end ... end
  • 175. RECAP ▸ Project Layout ▸ Initialization ▸ Interfacing with Arduino ▸ Connecting to Services ▸ Web Interfaces with Phoenix Building the Badge
  • 177. ▸ Advanced Configuration ▸ Modifying a Nerves System ▸ Initialization with erlinit ▸ Firmware Updates ▸ Licensing What are we going to Do?
  • 179. Reasons to make your own system ▸ Add a library or application that can’t be built using mix ▸ Postgres ▸ Qt ▸ Add a Linux kernel module or patch the kernel ▸ WiFi drivers ▸ Audio, webcams, USB peripherals ▸ Patch or change the bootloader ▸ Enable a Busybox command ▸ Likely if you need to run a shell script ▸ Consider re-implementing in Elixir
  • 180. Reasons NOT to make your own system ▸ Add files to the root filesystem ▸ These can be added via the rootfs-additions mechanism ▸ Change the iex terminal tty or alter how Erlang is started ▸ Create your own erlinit.config for your project ▸ Add or remove files from the firmware update files ▸ Create your own fwup.conf for your project ▸ Summary: Try to avoid modifying systems too much since it will take away the benefits of working with the Elixir tools
  • 181. Prereqs to working with systems ▸ Concepts ▸ Buildroot ▸ Linux kernel configuration ▸ Busybox ▸ Build and deploy requirements ▸ Linux (native, VM, or Docker) ▸ Someplace to put the system image since it’s too large for hex.pm
  • 182. Buildroot ▸ Toolchain, bootloader, kernel, root filesystem
 builder for embedded Linux ▸ Cross-compiled ▸ Support for building ~1800 programs and
 libraries ▸ Menu system for enabling and configuring packages ▸ Uses Makefiles, but not necessary to know make even to add packages ▸ Very well documented
  • 183. World views ▸ Buildroot normally is the top-level project that builds firmware images ▸ Nerves uses Buildroot to produce the system images that later get combined with OTP releases ▸ Consequences ▸ Erlang packages in Buildroot aren’t usable (no ejabberd) ▸ Nerves images are much smaller due to not including all (or most) of OTP ▸ For most development, Nerves builds are faster and can be run on OSX
  • 184. Defconfigs and .configs ▸ Anything using Kconfig to manage configuration options uses these (Linux, Buildroot, Busybox) ▸ .config ▸ Found in the root of the build directory for the project ▸ Has the values for ALL options – hidden, derived, etc. ▸ defconfig ▸ Subset of .config with only non-default options ▸ Usually stored in source control ▸ make savedefconfig
  • 185. Activity: Start a system build ▸ Clone one of the official Nerves system images ▸ https://github.com/nerves-project/nerves_system_* ▸ Method 1 – official builds ▸ cd nerves_system_foo; mix deps.get; mix compile ▸ CTRL-C to stop (mix clean may be needed to start over) ▸ Method 2 – easier for development ▸ Clone https://github.com/nerves-project/nerves_system_br ▸ nerves_system_br/create-build.sh 
 nerves_system_foo/nerves_defconfig out ▸ cd out; make
  • 186. Activity: Compile in a new package ▸ Go to the out directory from the last activity ▸ make menuconfig ▸ Find and enable postgresql ▸ Hint: Type the “/” key to search for postgresql. Press the number ▸ While you’re in menuconfig, take a look around ▸ Exit menuconfig. Be sure to save! ▸ Inspect .config and see that BR2_PACKAGE_POSTGRESQL is enabled ▸ make savedefconfig ▸ Inspect nerves_system_foo/nerves_defconfig has POSTGRESQL enabled ▸ Run make if you have time
  • 187. The Linux kernel ▸ Why Linux? Best device driver support for embedded ▸ Nerves uses a trimmed down Linux kernel to keep 
 the image size reasonable ▸ Many device drivers compiled into the kernel so no
 need to load them at initialization ▸ Some device drivers still compiled as modules ▸ Module parameters unknown until runtime ▸ Don’t work when compiled into the kernel ▸ More generic images
  • 188. Activity: Enable a device driver ▸ Go to the out directory from before ▸ make linux-menuconfig ▸ Enable support for USB->serial adapters – called USB Modem (CDC ACM) support in the kernel ▸ Use “/” to search for it if you’re not sure where it is ▸ Exit and save ▸ Inspect build/linux-x.y.z/.config to see that the option was saved ▸ make linux-savedefconfig ▸ Inspect build/linux-x.y.z/defconfig to verify the option again ▸ cp build/linux-x.y.z/defconfig to the Linux defconfig for the Nerves system. This is usually called linux-x.y.defconfig
  • 189. Busybox ▸ Provides tons of Unix apps in one small binary ▸ ls, sh, dd, ps, find, cat, tail, tar, cd, mkdir, etc. ▸ ntpd, dhcpc ▸ vi (sorry, no emacs) ▸ Ideally, Nerves would not have to use Busybox ▸ Wishlist: project that adds common shell commands to the IEx shell and improves OS process inspection ▸ Removing Busybox is not trivial so it will be with us for a while
  • 190. Activity: Modify the Busybox config ▸ Go to the out directory from before ▸ make busybox-menuconfig ▸ Enable the ping utility, exit and save ▸ Verify that CONFIG_PING is enabled in build/busybox-1.x.y/.config ▸ If your system overrides the default busybox configuration, copy the new one on top of it ▸ If not, ▸ Copy build/busybox-1.x.y/.config to ../busybox.config ▸ make menuconfig ▸ Set the Busybox configuration to ${NERVES_DEFCONFIG_DIR}/ busybox.config ▸ make savedefconfig
  • 191. Using your custom Nerves system ▸ Go to the out directory from before ▸ export NERVES_SYSTEM=$PWD ▸ Now go to your Elixir project and run mix
  • 192. Publishing a custom Nerves system ▸ What you’ll need ▸ Someplace to hold your nerves_system_xyz ▸ Someplace to hold the built tarball for the system ▸ GitHub supports both - attach the tarball to a release ▸ hex.pm only supports the source repository
  • 193. nerves.exs config :nerves_system_linkit, :nerves_env, type: :system, version: version, mirrors: [ "https://github.com/nerves-project/nerves_system_linkit/releases/ download/v#{version}/nerves_system_linkit-v#{version}.tar.gz", "https://s3.amazonaws.com/nerves/artifacts/nerves_system_linkit- #{version}.tar.gz"],
 …
  • 194. Final steps ▸ Go back to your system’s out directory ▸ make system ▸ Publish the created tarball ▸ Publish the source on hex.pm or git ▸ Let everyone know to reference your Nerves system image in their mix.exs
  • 196. erlinit ▸ Replacement for /sbin/init that starts the Erlang virtual machine ▸ Basic initialization of the Linux user land ▸ Loopback network connection ▸ Mounts /tmp, /proc, /sys ▸ Configures the tty ▸ Configuration stored in /etc/erlinit.conf ▸ Can be overridden by passing parameters to the Linux kernel from the bootloader
  • 197. erlinit debugging options ▸ --verbose ▸ --hang-on-exit ▸ Useful to capture error messages when the VM exits ▸ --run-on-exit /bin/sh ▸ Drop into a shell if the VM exits ▸ Exiting the shell reboots or hangs ▸ --warn-unused-tty ▸ erlinit will tell you what options to pass it to use the shell on the terminal you’re looking at
  • 198. erlinit features ▸ Mount filesystems ▸ Configure a unique hostname ▸ --hostname-pattern and --uniqueid-exec ▸ Nerves uses fhunleth/boardid to read serial numbers ▸ Wrap the launch of the Erlang VM in another program ▸ --alternate-exec ▸ Perform some custom system-specific initialization that can’t be done in Erlang or Elixir ▸ Capture the terminal with dtach to route it to a GUI ▸ Run the Erlang VM as a regular user
  • 199. erlinit pitfalls ▸ Running shell scripts to initialize the system ▸ Move initialization to Elixir to take advantage of OTP supervision ▸ Currently no standard way of handing system initialization in Nerves ▸ Assuming writable filesystems always can be mounted ▸ Failures happen – must be handled in Elixir ▸ erlinit can’t report errors so Elixir must check
  • 201. fwup ▸ Firmware update packaging and application ▸ Packages ▸ Zip-formatted archives ▸ Metadata ▸ All data files protected by cryptographic 
 hashes ▸ Packages can be cryptographically signed ▸ Very simple scripts supported (lack of functionality is a feature)
  • 202. fwup processing Master Boot Record FAT Boot partition Root Filesystem A Read-only Root Filesystem B Read-only Application Data Read-write rootfs.img uImage other files on_init on_finish .fw file fwup processing 1 2 3 4 5
  • 203. Anatomy of a fwup.conf file ▸ Resources ▸ Files that are included as part of the zip archive ▸ Not required to be used when upgrading ▸ Tasks ▸ Instructions for applying updates ▸ Only one task is run at a time ▸ Tasks may have conditions for when they’re run ▸ Common task names: “complete” and “upgrade”
  • 204. LinkIt Smart fwup.conf # Let the rootfs have room to grow up to 64 MiB and align # it to the nearest 1 MB boundary define(ROOTFS_A_PART_OFFSET, 2048) define(ROOTFS_A_PART_COUNT, 131072) define(ROOTFS_B_PART_OFFSET, 133120) define(ROOTFS_B_PART_COUNT, 131072) # Application partition # NOTE: Keep the total amount used under 1.78 GiB so that # everything fits in the "2 GB" eMMC. define(APP_PART_OFFSET, 264192) define(APP_PART_COUNT, 1048576) https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 205. LinkIt Smart fwup.conf # Firmware metadata meta-product = "Nerves Firmware" meta-description = "" meta-version = ${NERVES_SDK_VERSION} meta-platform = "linkit" meta-architecture = "mips" meta-author = "Frank Hunleth" https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 206. LinkIt Smart fwup.conf file-resource rootfs.img { host-path = ${ROOTFS} } file-resource uImage { host-path = "${NERVES_SYSTEM}/images/uImage" assert-size-lte = 30720 # 15 MiB } https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 207. LinkIt Smart fwup.conf mbr mbr-a { partition 0 { block-offset = ${ROOTFS_A_PART_OFFSET} block-count = ${ROOTFS_A_PART_COUNT} type = 0x83 # Linux } partition 1 { block-offset = ${APP_PART_OFFSET} block-count = ${APP_PART_COUNT} type = 0xc # FAT32 } # partition 2 and 3 are unused } https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 208. LinkIt Smart fwup.conf # This firmware task writes everything to the destination media task complete { on-init { mbr_write(mbr-a) } on-resource rootfs.img { # write to the first rootfs partition raw_write(${ROOTFS_A_PART_OFFSET}) } on-finish { fat_mkfs(${APP_PART_OFFSET}, ${APP_PART_COUNT}) fat_setlabel(${APP_PART_OFFSET}, "APPDATA") } } https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 209. LinkIt Smart fwup.conf task upgrade.a { # This task upgrades the A partition require-partition-offset(0, ${ROOTFS_B_PART_OFFSET}) on-resource rootfs.img { # write to the first rootfs partition raw_write(${ROOTFS_A_PART_OFFSET}) } on-finish { # Switch over to boot the new rootfs mbr_write(mbr-a) } } https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf
  • 210. LinkIt Smart fwup.conf https://github.com/nerves-project/nerves_system_linkit/blob/develop/fwup.conf task upgrade.b { # This task upgrades the B partition require-partition-offset(0, ${ROOTFS_A_PART_OFFSET}) on-resource rootfs.img { # write to the first rootfs partition raw_write(${ROOTFS_B_PART_OFFSET}) } on-finish { # Switch over to boot the new firmware mbr_write(mbr-b) } }
  • 211. FAT filesystem commands Command Description fat_mkfs(block_offset, block_count) Create a FAT file system fat_write(block_offset, filename) Write the resource to the file system fat_mv(block_offset, oldname, newname) Rename a file fat_rm(block_offset, filename) Delete a file fat_mkdir(block_offset, filename) Create a directory fat_touch(block_offset, filename) Create an empty file if the file doesn't exist
  • 213. Shipping Nerves - Licensing ▸ Buildroot infrastructure in Nerves can aid process ▸ make legal-info ▸ Other licenses ▸ nerves-toolchain ▸ gcc ▸ crosstool-ng ▸ All of your mix dependencies legal-info ├── buildroot.config ├── host-licenses │ ├── autoconf │ │ ├── COPYING.EXCEPTION │ │ └── COPYINGv3 │ └── ... │ └── README ├── host-licenses.txt ├── host-manifest.csv ├── host-sources ├── licenses │ ├── busybox │ │ └── LICENSE │ ├── erlang │ │ └── LICENSE.txt │ └── ... ├── licenses.txt ├── manifest.csv ├── README └── sources ├── boardid-v0.4.0.tar.gz ├── busybox-1.24.1.tar.bz2 ├── ... └── zlib-1.2.8.tar.xz
  • 214. Elixir Conf 2016 Nerves: Connected beyond the Node Thursday 1:30 PM - 2:15 PM Track 1 Justin Schneck Building "learn to touch type" glove with Elixir and Arduino2:15 PM - 3:00 PM Track 2 Tetiana Dushenkivska Nerves + Phoenix Saves a Father's Sanity! Friday 1:30 PM - 2:15 PM Track 2 Joel Byler The Joy of Connecting Elixir to the Physical World3:30 PM - 4:15 PM Track 1 Frank Hunleth Keynote4:30 PM - 5:30 PM Boyd Multerer
  • 215. Elixir Conf 2016 Thank You & Nerves