Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Nerves + Phoenix Saves a Father's Sanity!

Joel Byler
September 06, 2016

Nerves + Phoenix Saves a Father's Sanity!

Recently I've been able to encourage my children to do their chores and not fight about it. Its been made possible with the help of Elixir and an Umbrella project which combines the use of Phoenix and Nerves (and some prototyping hardware) to build a chore system which rewards each kid with their own internet access after they've done their work for the day. It also makes it really easy to revoke access when they misbehave. In this talk I'll take you with me back through the journey, and I'll talk about why it was needed, what I learned, and how the the final outcome has saved my sanity.

Joel Byler

September 06, 2016
Tweet

Other Decks in Programming

Transcript

  1. http://i1.kym-cdn.com/photos/images/facebook/000/234/739/fa5.jpg Full Disclosure • Inspired by Paul Wilson at ElixirConfEU

    • I started this project as an excuse to learn elixir • I’m still learning
  2. I work at CoverMyMeds! • Helping patients get the medications

    they need be healthy • Consistently rated best place to work in central Ohio • Mostly Ruby / Rails but exploring Elixir / Phoenix • Columbus, Cleveland, and Remote
  3. But, that means… - The password needs to be changed

    EVERY DAY - There’s only one password, and I have more than one child. - I have lots of other stuff on wifi, like MY phone and laptop - Also, my wife has her stuff, she’s not into this idea at all.
  4. Idea I can build a website! - The website can

    have the kids login and confirm that their chores are done - It will be like Starbucks or McDonalds - After the chores are done, then I can give them access to the internet - Its called a captive portal!
  5. Idea I’ve heard of a Raspberry Pi used as a

    wireless AP • Raspberry Pi 3 has a built in wifi adapter! I just heard about this new Elixir framework, Nerves • Lets you build your own embedded system with elixir • Works on Raspberry Pi and many others • Looks like it makes that really easy! I can build the website in Phoenix!
  6. Why is a Web UI Important? - users need a

    way to interact with IoT projects - easy to build an intuitive user interface - better than blinking light codes and buttons - users typically will have a cell phone or laptop nearby anyway
  7. My Desired Approach - I want to learn Elixir! -

    Phoenix: able to perform well on less hardware - Nerves - include additional elixir apps using umbrella - uses buildroot to specify linux dependencies - start phoenix automatically on boot - easily package entire project into a micro SD image
  8. What’s in the Umbrella? firmware - initializes router user_interface -

    captive portal + admin functions captive_portal_redirector - 302 redirect to captive portal router_controls - wraps system calls chore_repository - data repository Separated Concerns:
  9. What’s in the Umbrella? def application do [ mod: {Firmware,

    []}, applications: [ :captive_portal_login_redirector, :router_controls, :user_interface]] end def deps do [ {:captive_portal_login_redirector, in_umbrella: true}, {:router_controls, in_umbrella: true}, {:user_interface, in_umbrella: true}] end
  10. Linux Deps - dnsmasq: dns and dhcp - hostapd: wifi

    access point - iptables: firewall - netfilter: filter traffic - arp: identify devices (address resolution) * these dependencies are not included in standard Nerves systems
  11. Custom System - clone repo for system git clone [email protected]:nerves-project/…

    - customize with buildroot make menuconfig - build make system - reference from nerves application NERVES_SYSTEM=/path/to/build/directory
  12. Buildroot `menuconfig` - native libraries and applications dnsmasq, hostapd `linux-menuconfig`

    - linux kernel drivers iptables, netfilter `busybox-menuconfig` - simple command line utilities vim, grep, etc. more details available at:
 https://github.com/nerves-project/nerves_system_br
  13. Minimal DNSMAQ Config # /etc/dnsmasq.conf bogus-priv server=/localnet/192.168.24.1 local=/localnet/ interface=wlan0 domain=localnet

    dhcp-range=192.168.24.50,192.168.24.250,2h # 3 - router, 6 - dns-server dhcp-option=3,192.168.24.1 dhcp-option=6,192.168.24.1 dhcp-authoritative typically at /etc/dnsmasq.conf
  14. rootfs-additions # firmware/config/config.exs config :nerves, :firmware, rootfs_additions: "config/rootfs-additions" firmware/config !""

    config.exs #"" rootfs-additions #"" etc !"" dnsmasq.conf !"" hostapd $ #"" hostapd.conf #"" erlinit.config
  15. A few iptables commands # new chain called internet iptables

    -t mangle -N internet # attach tcp traffic on wlan0 to internet target iptables -t mangle -A PREROUTING -i wlan0 -p tcp -m tcp -j internet # mark new connections iptables -t mangle -A internet -j MARK --set-mark 99 # forward marked connections iptables -t nat -A PREROUTING -i wlan0 -p tcp -m mark --mark 99 -m tcp \ --dport 80 -j DNAT --to-destination 192.168.24.1
  16. User Interface --no-ecto - Starts automatically because its a dependency

    of the firmware (nerves) application - Single controller - Only two actions, two templates - Get next (or first) chore, next, next, done. - KEEP IT SIMPLE! A fairly simple Phoenix app
  17. Persistence tried but didn’t feel like a great fit -

    Ecto - Erlang Term Storage (ETS) - Amnesia / Mnesia this worked well - cellulose/persistent_storage I don’t want to hard code those chores Remember one of the apps from earlier? :chore_repository
  18. Persistence alias PersistentStorage def repository_file do, Application.get_env(:chore_repository, :config)[:file] end def

    setup do PersistentStorage.setup path: repository_file end def insert(chore_step, name, description, required) do PersistentStorage.put(chore_step, %{ name: name, description: description, required: required }) end def next_chore(chore_step) do chore_step + 1 |> PersistentStorage.get |> map_to_chore end
  19. Testing! Because of the numerous linux dependencies, this app uses

    lots of OS commands. System.cmd("arp", ["-a", "192.168.24.19"]) Not easy to put the OS in a state in which tests will pass for this sort of code. So how do we fix this?
  20. Testing! Don’t mock as a verb, mock as a noun

    defmodule Router.OsCommand do @system Application.get_env(:router, :system_client) def os_cmd(command, arguments) do @system.cmd(command, arguments) end end # router/config/test.exs config :router, system_client: Router.Fakes.System # router/config/prod.exs config :router, system_client: System
  21. Testing! defmodule Router.Fakes.System do def cmd("arp", ["-a", "192.168.24.42"]) do {“blah

    blah (192.168.24.42) at ab:cd:ef:ab:cd:ef yada.yada", 0} end end defmodule Router.AddressResolution do import Router.OsCommand @doc """ Returns arp result or a default one as a fallback ## Examples iex> AddressResolution.fetch_arp "192.168.24.42" {blah blah (192.168.24.42) at ab:cd:ef:ab:cd:ef yada.yada, 0} """ def fetch_arp(ip) do os_cmd("arp", ["-a", ip]) end end
  22. Change of Plans - Chores are good, keep them -

    Whats missing? - Fun activities! - Get outside and get some fresh air - Exercise, its good for you - Interact with others (games, conversation, high fives!) - Let someone else know you care about them - These are things that I need to do myself!
  23. Closing Statements • Hacking on side projects is a fun

    way to learn something new • You should try it! • Share the fun, others love to learn this stuff too! • Include your friends and family • Don’t be upset if the project takes a different turn iex> Elixir |> Nerves |> Phoenix {:success, “wonderful”}
  24. Special Thanks Justin Schneck Frank Hunleth Greg Mefford Garth Hitchens

    Chris Dutton Keyvan Fatehi @pressy4pie @requisite0 Nerves Core Team #nerves on slack @jarel @ssachse My Family CoverMyMeds