Elixir/Nerves: blinking LED
Tags: nerves, elixir, raspberrypi, iot
Published: 2024-06-24 01:48:42

---
title: Elixir/Nerves: blinking LED
tags:
  - nerves
  - elixir
  - raspberrypi
  - iot
private: false
updated_at: ""
id: null
organization_url_name: fukuokaex
slide: false
ignorePublish: false
---

[Elixir]: https://elixir-lang.org/
[Nerves]: https://nerves-project.org/
[Nerves Livebook]: https://github.com/nerves-livebook/nerves_livebook
[GenServer]: https://hexdocs.pm/elixir/GenServer.html
[circuits_gpio]: https://hexdocs.pm/circuits_gpio/readme.html
[GPIO]: https://en.wikipedia.org/wiki/General-purpose_input/output

## Introduction

[myasu]: https://twitter.com/etcinitd
[nako_sleep_9h]: https://twitter.com/nako_sleep_9h

When we first learn [Nerves], one of the things we come up with is blinking LEDs on a breadboard.

Today I'd like to try one implementation using GenServer.

This is actually my outcome at this event I participated in last weekend "[Shop for parts in Akihabara and get started on Nerves right away!](https://piyopiyoex.connpass.com/event/317734/)". It was so much fun hanging out in [Akihabara, Tokyo](https://en.wikipedia.org/wiki/Akihabara) with [Nerves] enthusiasts.

Thank you very much to the organizers [myasu] and [nako_sleep_9h]!

[myasu] is very knowledgeable about electric and electronic stuff. I learned tons of pro tips from him, which I might write up as my future posts. He is the author of this book about Nerves.

[![](https://nextpublishing.jp/wp-content/uploads/2023/11/N01905.jpg)](https://nextpublishing.jp/book/17353.html)

[nako_sleep_9h] is awesome at organizing and mc'ing fun-driven engineering events across Japan. She wrote a [report on Day 1](https://qiita.com/nako_sleep_9h/items/8956a061b014f11cc65c) for this event.

[![](https://pbs.twimg.com/media/GQqpzB3acAASr-t?format=jpg&name=large)](https://twitter.com/nako_sleep_9h/status/1804441299258359890)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">めでたい、光った光った〜<a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;ref_src=twsrc%5Etfw">#piyopiyoex</a> <a href="https://t.co/5WBqnFftQU">pic.twitter.com/5WBqnFftQU</a></p>&mdash; nako@9時間睡眠 (@nako_sleep_9h) <a href="https://twitter.com/nako_sleep_9h/status/1804441299258359890?ref_src=twsrc%5Etfw">June 22, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Nerves Livebook

There are more than a few ways to get started with [Nerves] IoT projects but one of the easiest is to use prebuild [Nerves Livebook] firmware. [Nerves Livebook] allows us to control our target devices such as Raspberry Pi, doing Elixir coding in a web browser.

https://github.com/nerves-livebook/nerves_livebook/blob/main/README.md

## Blink using Circuits GPIO

[Nerves Livebook] has a nice tutorial notebook about how to use the [circuits_gpio] package to control a [GPIO] as an output, and blink an LED. It shows us what electric parts we need minimally and how we wire them before getting down to business.

https://github.com/nerves-livebook/nerves_livebook/blob/main/priv/samples/basics/blink.livemd

## Wrapping our LED blinking logic in GenServer

When we want to repeat the blinking forever but want to stop at some stage without restarting the device, it is nice to prepare a [GenServer] that manages the LED blinking. There can be many ways to achieve similar effect but here is one implementation using GenServer.

First, let's write a simple module that wraps [GPIO]-related logic within so that we can forget about how exactly we need to communicate with our LEDs.

```elixir
defmodule LedBlink do
  def open(led_pin) do
    Circuits.GPIO.open(led_pin, :output)
  end

  def close(led_pin) do
    Circuits.GPIO.close(led_pin)
  end

  def toggle(gpio, 1), do: off(gpio)
  def toggle(gpio, 0), do: on(gpio)
  def toggle(gpio, _), do: on(gpio)

  def on(gpio) do
    :ok = Circuits.GPIO.write(gpio, 1)

    {:ok, 1}
  end

  def off(gpio) do
    :ok = Circuits.GPIO.write(gpio, 0)

    {:ok, 0}
  end
end
```

Then next, we make a [GenServer] that lets an LED blink forever.

```elixir
defmodule BlinkServer do
  use GenServer, restart: :temporary

  require Logger

  @run_interval_ms 1000

  ## Client

  @doc """
  Start a BlinkServer for the provided GPIO pin. It lets the LED blink forever.

  ### Examples

      iex>  BlinkServer.start_link(led_pin: "GPIO17")

  """
  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  def stop() do
    GenServer.stop(__MODULE__)
  end

  ## Callbacks

  @impl true
  def init(opts) do
    led_pin = Access.fetch!(opts, :led_pin)

    initial_state = %{
      gpio_ref: nil,
      led_state: 0,
      led_pin: led_pin
    }

    {:ok, initial_state, {:continue, :init_gpio}}
  end

  @impl true
  def handle_continue(:init_gpio, state) do
    case LedBlink.open(state.led_pin) do
      {:ok, gpio_ref} ->
        new_state = %{state | gpio_ref: gpio_ref}

        send(self(), :toggle_led_state)

        {:noreply, new_state}

      {:error, error} ->
        {:stop, error}
    end
  end

  @impl true
  def handle_info(:toggle_led_state, state) do
    {:ok, new_led_state} = LedBlink.toggle(state.gpio_ref, state.led_state)

    new_state = %{state | led_state: new_led_state}
    Logger.debug("toggled LED: #{new_state.led_state}")

    Process.send_after(self(), :toggle_led_state, @run_interval_ms)

    {:noreply, new_state}
  end

  @impl true
  def terminate(reason, state) do
    LedBlink.close(state.gpio_ref)
    Logger.debug("terminated: #{reason}")

    :ok
  end
end
```

Here is how to use our BlinkServer.

When starting BlinkServer, we specify the GPIO pin we use for our LED.

When we are done experimenting, we can stop our running BlinkServer worker.

```elixir
Logger.configure(level: :debug)

BlinkServer.start_link(led_pin: "GPIO17")

Process.sleep(10_000)

BlinkServer.stop()
```

This implementation of BlinkServer is a singleton [GenServer], meaning we are not allowed to start more than one BlinkServer workers. So if we change our mind on the [GPIO] pin we use, for example, we would need to stop the worker and start a new worker with different options.

## circuits_gpio package

[circuits_gpio] is the one that lets us control [GPIO] in our Elixir code.

## Wrapping up

We wrote a simple [GenServer] that lets our LED blink.

Once you get a feel of it, I would like to see your LED blinking logic. Please share!

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今日は初Nerves！<br>8月末のSWESTに向けた知識を付けていこう～ <a href="https://t.co/SrGEUJjzkS">https://t.co/SrGEUJjzkS</a></p>&mdash; FRICK (@TewiEwi_no96) <a href="https://twitter.com/TewiEwi_no96/status/1804302628907814965?ref_src=twsrc%5Etfw">June 21, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Nerves勉強会2日目！<br>開始〜 <a href="https://t.co/zgMKDEGrH8">https://t.co/zgMKDEGrH8</a> <a href="https://t.co/rT7QiHslAa">pic.twitter.com/rT7QiHslAa</a></p>&mdash; FRICK (@TewiEwi_no96) <a href="https://twitter.com/TewiEwi_no96/status/1804692392622305757?ref_src=twsrc%5Etfw">June 23, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;ref_src=twsrc%5Etfw">#piyopiyoex</a><br>【オフライン】秋葉原でパーツお買い物＆そのままNerves入門！ <br>はじまたー <a href="https://t.co/BPiPBxZMTq">pic.twitter.com/BPiPBxZMTq</a></p>&mdash; ysaito (@ysaito8015) <a href="https://twitter.com/ysaito8015/status/1804695245550157840?ref_src=twsrc%5Etfw">June 23, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">神田明神きたよ<a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;ref_src=twsrc%5Etfw">#piyopiyoex</a> <a href="https://t.co/xuxuiWcl2F">pic.twitter.com/xuxuiWcl2F</a></p>&mdash; nako@9時間睡眠 (@nako_sleep_9h) <a href="https://twitter.com/nako_sleep_9h/status/1804374303284302326?ref_src=twsrc%5Etfw">June 22, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今日はこちらへ参加！ <a href="https://t.co/L28N3O6nIF">https://t.co/L28N3O6nIF</a></p>&mdash; myasu (@etcinitd) <a href="https://twitter.com/etcinitd/status/1804313482428584446?ref_src=twsrc%5Etfw">June 22, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今日はDay2!!<a href="https://t.co/hgJwwqGO9W">https://t.co/hgJwwqGO9W</a></p>&mdash; myasu (@etcinitd) <a href="https://twitter.com/etcinitd/status/1804676385459536130?ref_src=twsrc%5Etfw">June 23, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>