DEV Community

Masatoshi Nishiguchi
Masatoshi Nishiguchi

Posted on

[Elixir/Nerves] Get started with LED Blinking on Raspberry Pi

One day in September 2020, I suddenly felt like exploring remote programming meetups in Japan, my home country after having been in the US for about 9 years. Since I was interested in Elixir programming language, I was focused on Elixir in my searching for meetups. I realized that Japan's Elixir community so active and enthusiastic. First one I found was fukuoka.ex/kokura.ex - Fukuoka Elixir community, which is probably the largest one as far as I know. After visiting a few remote online events, I started participating in Nerves JP (Nerves community in Japan), where I learned Nerves IoT framework. I was a total beginner in hardware or physics but really enjoy playing with Nerves and Raspberry Pi thanks to the communities around the world.

I decided to write up so that more people can get started and have fun with Nerves.

Alt Text

Install necessary tools

For the setup, I just followed the Installation doc.

# Quick check
elixir --version
Enter fullscreen mode Exit fullscreen mode

Once installation is done, I studied the basics reading the Getting Started doc.

Although the above doc talks about how to create a new Nerves app, I would recommend fellow beginners to try running the official "Blinky" example in nerves-project/nerves_examples first. I found it helpful because I actually see an LED blinking on my device without writing any code. I got a sense of accomplishment after being confused what I was supposed to do.

It took me a while to understand and get used to the Nerves development, but you'll be fine. If you get stuck, your friends from around the world including me will help you at these places:

  • Slack elixir-lang #nerves invite
  • Elixir Forum - make sure you add Nerves tag to your questions
  • Your local Elixir community

Download Blinky example project

Blinky is a Nerves official example app that blinks a green on-board LED of your target device.

First I cloned nerves_examples from Github.

cd path/to/some/folder
git clone git@github.com:nerves-project/nerves_examples.git
Enter fullscreen mode Exit fullscreen mode

Move to the blinky project directory.

cd nerves_examples/blinky
Enter fullscreen mode Exit fullscreen mode

Connect to a target device

One thing we need to think about is how to connect to our target device, such as Raspberry Pi. According to the doc, we can make a network connection to our target device, using:

  • Ethernet cable
  • USB cable
  • WIFI

I personally use WIFI. All I needed was just to edit one section of config/target.exs file and to provide my WIFI id and password.

--- a/blinky/config/target.exs
+++ b/blinky/config/target.exs
@@ -58,7 +58,20 @@ config :vintage_net,
        type: VintageNetEthernet,
        ipv4: %{method: :dhcp}
      }},
-    {"wlan0", %{type: VintageNetWiFi}}
+     {"wlan0",
+     %{
+       type: VintageNetWiFi,
+       vintage_net_wifi: %{
+         networks: [
+           %{
+             key_mgmt: :wpa_psk,
+             ssid: System.get_env("WIFI_SSID") || raise "Environment variable WIFI_SSID is missing.",
+             psk: System.get_env("WIFI_PSK") || raise "Environment variable WIFI_PSK is missing.",
+           }
+         ]
+       },
+       ipv4: %{method: :dhcp}
+     }}
   ]

 config :mdns_lite,
Enter fullscreen mode Exit fullscreen mode

The WIFI ID and password can be hardcoded in that file, but I did not want to so I decided to pass them through environment variables.

export WIFI_SSID=xxxxxxxx
export WIFI_PSK=xxxxxxxx
Enter fullscreen mode Exit fullscreen mode

Alternatively you could manage your environment variables using utility programs like direnv.

Create the firmware bundle

Before running any firmware command, we need to tell Nerves what our target device is. Here is the list of supported targets.
For example, my target is "Raspberry Pi 4", so I run export MIX_TARGET=rpi4

export MIX_TARGET=<your-target>
Enter fullscreen mode Exit fullscreen mode

FYI

# List some commands related to firmware
mix help | grep firmware

# Check currently-set target tag
echo $MIX_TARGET
Enter fullscreen mode Exit fullscreen mode

Get dependencies

mix deps.get
Enter fullscreen mode Exit fullscreen mode

Build a firmware bundle

mix firmware
Enter fullscreen mode Exit fullscreen mode

Write a firmware image to an SDCard

Insert an SD card to your host machine (not the target), then run mix burn.

mix burn
Use 119.38 GiB memory card found at /dev/rdisk2? [Yn] y # Type y then hit enter
Enter fullscreen mode Exit fullscreen mode

Check the connection

  • Insert the SD card to your target device
  • Turn on the target device
  • Count 30 in your head and
  • Verify the connection by running ping nerves.local
❯ ping nerves.local

PING nerves.local (10.0.0.179): 56 data bytes
64 bytes from 10.0.0.179: icmp_seq=0 ttl=64 time=111.131 ms
64 bytes from 10.0.0.179: icmp_seq=1 ttl=64 time=5.939 ms
64 bytes from 10.0.0.179: icmp_seq=2 ttl=64 time=6.247 ms
...
Enter fullscreen mode Exit fullscreen mode

Verify the blinking

Once the connection is established, you should see an LED on your target device blinking.

SSH into the target

If you have used SSH before, probably you can SSH into your target device without doing anything. If not, you need to set it up.

ssh nerves.local
Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help)
Toolshed imported. Run h(Toolshed) for more info.
RingLogger is collecting log messages from Elixir and Linux. To see the
messages, either attach the current IEx session to the logger:

  RingLogger.attach

or print the next messages in the log:

  RingLogger.next

iex(1)> ls
lib          releases
iex(2)> top
Total processes: 201

Application  Name or PID                   Reds/Δ      Mbox/Δ     Total/Δ      Heap/Δ     Stack/Δ
blinky       <0.1165.0>                   1670K/1670K     0/0      1375/1375    987/987       9/9
kernel       file_server_2                 672K/672K      0/0      2208/2208   1598/1598     12/12
undefined    <0.3.0>                       306K/306K      0/0       233/233     233/233       3/3
undefined    application_controller        262K/262K      0/0       56K/56K     28K/28K       8/8
undefined    erl_prim_loader               209K/209K      0/0      136K/136K    17K/17K       7/7
vintage_net  <0.1179.0>=Elixir.VintageNet  192K/192K      0/0       32K/32K    4185/4185     13/13
vintage_net  <0.1181.0>=Elixir.VintageNet  104K/104K      0/0      5172/5172   2586/2586     13/13
kernel       code_server                   102K/102K      0/0      310K/310K   118K/118K      5/5
nerves_runti Elixir.Nerves.Runtime.Log.Km   81K/81K       0/0       32K/32K    4185/4185     12/12
vintage_net  <0.1110.0>=Elixir.VintageNet   68K/68K       0/0      3585/3585   2586/2586     11/11
iex(3)>
Enter fullscreen mode Exit fullscreen mode

Instead of Linux shell, we use interactive Elixir shell (iex) to interact with our target device.

Because it is not a Linux shell, we cannot use Linux commands there but we can run something similar that is powered by fhunleth/toolshed library that is automatically imported into our IEX shell.

Run exit command to close the IEX shell.

Next steps

I googled around what else I could do for learning about my Raspberry Pi and physics. Right now, I've been blinking LEDs in various ways on my breadboard. There are infinite amount of things to do. Whenever I have a question, I ask somebody in the Elixir/Nerves communities. The other day I was lucky, I got an answer from a co-authors of Nerves framework!

Elixir Forum - Can I add an LED to /sys/class/leds and control it using nerves_leds?

When I see some question that I can answer, I am tring to answer to contribute back to the community.

That's it. I hope this will help somebody start Nerves. Happy coding!

P.S. I am still learning. If I have something wrong, please feel free to correct me :)

Discussion (0)