Project Blinkenlights: embedded Elixir on a Raspberry Pi
November 27, 2016
Introduction
Today I’d like to start a new series of blog posts describing my own journey into the world of embedded Elixir. In this first post I’ll describe the the IoT way of saying Hello World: A Raspberry Pi on steroids nerves booting in three seconds or so into a running Elixir program which in turn uses Raspberry’s GPIO to make a LED on a breadboard go Blinkenlights.
Prerequisites
The following hardware is required for this project. Click on the thumbnails to get more information.
. | . | . |
---|---|---|
![]() Jumper cables |
![]() 3.3V LED |
![]() Raspberry Pi |
![]() Breakout kit |
![]() 330Ω Resistors |
![]() Breadboard |
Wiring everything together
Now the fun part begins. First attach the breakout board to the breadboard as shown in the figure below.

Make sure that the the two rows of pins of the breakout board go on either side of the central divide on the breadboard as shown below. Otherwise you’ll damage your Raspberry as soon as you connect it to power.

With the breakout board in place we then connect the power and ground rails of the breadboard with 3V3 (power) and GND (ground) of the Raspberry.

Next we fit the LED on the breadboard. Make sure that you alway put the legs of the LED into two different columns.

Then we connect the column with the rounded and longer legged power side of the LED to the column of GPIO #18. You could also user e.g. #4, #17, #24 etc. Just remember the pin you actaully used because this is the number you specify in your Elixir code later on.

At last we connect the column with the flattened and shorter legged ground side of the LED to the ground rail using a 330Ω resistor.

Now you simply connect the Raspberry Pi to the breakout board using a ribbon cable. Make sure that the white or red colored stripe of your ribbon cable goes to the outer edge of your Raspberry.

Programming Blinkenlights
Now that we have our hardware ready we need to instruct the Raspberry Pi to actually blink the LED by cyclically turning power on and of for GPIO pin #18.
We do so be writing the necessary business logic in Elixir. Then we rely on the excellent Nerves Project for the heavy lifting. Nerves basically compiles our Elixir code and automatically packs it together with a BEAM runtime and a minimal linux into a ~12MB image and then burns this image to an SD card. Once this is done you just put the SD card into the Raspberry Pi and power it up. Within three seconds or so, Raspberry is completely up and running your Elixir program which blinks the LED. So let’s get started.
First we install the required software following the Nerves installation instructions. On a mac with Homebrew installed this is as simple as:
$ brew update
$ brew install erlang
$ brew install Elixir
$ brew install fwup squashfs coreutils
Linux users can follow these installation instructions. Windows users: I guess that installing a virtual machine (e.g. virtualbox) is the easiest way to get Elixir, Erlang, and fwup set up and running.
Next we need to install hex
and rebar
Elixir packages locally. This is where Elixir’s mix command starts to shine. To quote the documentation: Mix is a build tool that ships with Elixir that provides tasks for creating, compiling, testing your application, managing its dependencies and much more. Installing the aforementioned packages locally is as easy as:
$ mix local.hex
$ mix local.rebar
Next we install nerves_boostrap
package which basically takes care of all the heavy lifting in the background that is necessary to compile our Elixir code into a complete firmware image including a custom linux, an Erlang beam runtime environment, at boot configuration, etc. so that we only have to put that firmware image (a single file) onto a SD card and put that one into our Raspberry Pi.
Nerves Bootstrap package also provides a command line tool that let’s us create a default nerves project directory with sensible defaults (similar e.g. to rails new
for web projects).
$ mix archive.install https://github.com/nerves-project/archives/raw/master/nerves_bootstrap.ez
And that’s it! Now we’re ready to start coding our business logic. First we create a new nerves project called blinkenlights:
$ mix nerves.new blinkenlights --target rpi
Note that you might want to specify a different target platform depending on the hardware you use. Here’s a list of currently supported target platforms that - at the time of writing - includes BeagleBone Black, all sorts of Raspberry Pi, Lego EV3, Alix, Intel Galileo 2, AG150, and QEmu Arm.
Now cd
into the newly created project directory and download the required third party libraries.
$ cd hello_nerves
$ mix deps.get
The deps.get
command downloads quite a few libraries to your computer. So this this might take some time. Occasionally I get an error message during this step. Simply repeating it solves the problem for me.
Before we start coding we need to add the Elixir_ale library to our project. It provides simplified Elixir access to Raspberry GPIO. Open mix.exs
and add Elixir_ale
dependency as follows:
def deps do
[
{:nerves, "~> 0.3.0"},
{:Elixir_ale, "~> 0.5.6"}
]
end
Afterwards run deps.get
one more time to download Elixir_ale
to your computer.
Now open the file lib/blinkenlights.ex
in your favorite editor (I prefer Atom). This is the file where we add the business logic that ultimately makes the LED blink by cyclically turning power on and off on GPIO pin #18:

Replace the contents in lib/blinkenlights.ex
with the following code:
defmodule Blinkenlights do
use Application
@moduledoc """
Simple example to blink a LED light
on a breadboard which is wired to GPIO #18.
"""
require Logger
require Gpio
@on_duration 200 # ms
@off_duration 200 # ms
@gpio_pin 18 #connect to GPIO pin #18
def start(_type, _args) do
Logger.debug "Blinkenlights forever..."
{:ok, pid} = Gpio.start_link(@gpio_pin, :output)
spawn(fn -> blink_led_forever(pid) end)
{:ok, self}
end
defp blink_led_forever(pid) do
Gpio.write(pid, 1)
:timer.sleep(@on_duration)
Gpio.write(pid, 0)
:timer.sleep(@off_duration)
blink_led_forever(pid)
end
end
Without going into the very details you can easily see that there is a method start
which outputs a log statement, retrieves a reference (PID) to GPIO pin #18, and then calls the method blink_led_forever
providing it whith the reference to the GPIO port.
The method blink_led_forever
sends an instruction to turn power on to the given GPIO reference, sleeps for 200ms, sends an instruction to turn power off, sleeps another 200ms, and then recursively calls itself again.
If all that code looks a bit unfamiliar to you, no worries: The good news is: We’re done already. No more coding required.
And for those whoe wish to learn more about Elixir language itself: There is an excellent introduction to Elixir as part of its main documentation. Also José Valim, inventor of Elixir, posted a very nice introductory article at howistart.org. Last but not least I can highly recommend the book Programming Elixir by Dave Thomas.
One more thing: We need to transfer our code to Raspberry. Fear not. This is the best part of Nerves Project!
Simply put an SD card into the SD card reader of your computer (note that it’s content will be completely erased so make sure that’s okay for you). Then open a terminal, cd
into your project directory, and run
mix compile
mix firmware
mix firmware.burn
Particularly the command mix firmware
may need some time to complete as it compiles your Elixir code, downloads the linux image for your version of Raspberry Pi, and then merges both into the final firmware image. firmware.burn
automatically “burns” the image onto the SD card you inserted before (you are asked for permission before it starts).
Now that’s it. Just put the SD card into your Raspberry Pi and power it up. It takes about 3 seconds to boot right into your Elixir code which then leds you LED blink.
Enjoy! card into your Raspberry Pi and power it up. It takes about 3 seconds to boot right into your Elixir code which then leds you LED blink.
Enjoy! card into your Raspberry Pi and power it up. It takes about 3 seconds to boot right into your Elixir code which then leds you LED blink.
Enjoy!