[Elixir/Nerves] LCDにHello!

日本語 | [English](https://dev.to/mnishiguchi/elixir-nerves-hello-to-lcd-with-i2c-interface-31ca)

本記事は[「#NervesJP Advent Calendar 2020」](https://qiita.com/advent-calendar/2020/nervesjp)の21日目です。

前日は@piacerexさんの[Respberry Pi Zero WHにElixir IoTフレームワーク「Nerves」を入れてみた](https://qiita.com/piacerex/items/98e1bde676263f5f9f81)でした。

## はじめに

プログラミング言語を学ぶときは、まず「Hello」と文字を表示するところから始まりますが、IoTの世界ではLチカがその役目をしているようです。僕がLチカ後に興味を持ったのは、11日目に話をした[パルス幅変調 (PWM)でのLチカ](https://qiita.com/mnishiguchi/items/4bdf88acf0ab0e8e2c7e)でした。

しばらくLチカプラスαで遊んだあと、その次にやりたくなったことは、何らかの方法で実際に「Hello」表示することでした。その話をします。

![nerves_hello_lcd_20201219_152639](https://user-images.githubusercontent.com/7563926/102699565-b5646700-4213-11eb-9ca1-a11bd10c619d.gif)

## どういう手法で「Hello」？

[Nerves JP](https://nerves-jp.connpass.com)の@takasehidekiさんからのアドバイスでどーんとまとめて入手した部品が多数手元にあったので、それぞれの使い方をザクッと把握しながら、どれにしようかと検討をはじめました。以下のものを検討しました。

- LEDをたくさんならべる
- 赤色７セグメントLED表示器（1文字）
- 赤色７セグメントLED表示器（4文字）
- 赤色ドットマトリクスLED（8×8）
- LCDキャラクタディスプレイモジュール（16×2行）
- Web
- Mobile

色々悩んでLCDにI2Cインターフェースを取り付け、シリアル通信することに決めました。理由は、下記のとおりです。

- WebとMobileは実用的かもしれないが、今は電子工作をしたい
- 他の選択肢が物理的な配線が多くてめんどくさそう
- LCDに興味をもった
- I2Cで接続すると少ない配線で通信できるのがカッコいい

## 調査

まずは、ひたすらググり、Youtubeのビデオも片っ端から見ました。いくつか役に立ったものを挙げます。

- [Hitachi HD44780 LCD controller datasheet](https://cdn-shop.adafruit.com/datasheets/HD44780.pdf)
- [Hitachi HD44780 LCD controller
 | Wikipedia](https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller)
- [How a Character LCD works Part 1 by The 8-Bit Guy | Youtube](https://youtu.be/hZRL8luuPb8)
- [How to Use Character LCD Module | elm-chan.org](http://elm-chan.org/docs/lcd/hd44780_e.html)
- [Difference between interfacing character 16×2 lcd in 4-bit and 8-bit mode with microcontroller | engineersgarage.com](https://www.engineersgarage.com/knowledge_share/lcd-in-4-bit-mode-and-8-bit-mode/)
- [Character LCDs | adafruit](https://learn.adafruit.com/character-lcds)
- [I2C Serial Interface 1602 LCD Module User Guide](http://www.handsontec.com/dataspecs/module/I2C_1602_LCD.pdf)

配線に関しては、パラレル通信でもシリアル通信でも特に何も難しいことはありませんでした。
プログラミングに関しては、ほとんどがC、C++やPythonで書かれたLCDドライバを使用する場合の説明が多く、Elixirプログラミングする上であまり参考にはなりませんでした。

Elixirで使えそうなライブラリを探しました。I2Cシリアル通信には[Elixir Circuits - I2C](https://github.com/elixir-circuits/circuits_i2c)が使えるのは、知っていたのですが、肝心のLCD操作のライブラリに良いのがありません。[ExLCD](https://github.com/cthree/ex_lcd)がありますが、（2020年12月現在）長い間メンテされてなくI2Cにも対応していません。ExLCDをフォークして改善を試みましたが、既存の実装に納得行かない部分が色々あったので、それを参考にしつつ自分で実装することにしました。

しばらくの間、他言語で書かれたLCDドライバーのソースコードをいくつか読んでElixirで同様のことができるのかを探りました。ある時点で、LCD本体自体の資料（データシート）に出くわし、それらのLCDドライバーはただデータシートの通りにコマンドを実行している単純なものであると気づきました。当たり前の事だったのかもしれません。気づいてよかった。

## デモアプリ

まずLCD本体自体の資料（データシート）をよく読み、各種ピンの役割、初期化の方法、4ビットモード、各種コマンドについての理解を深めました。実験用Nervesアプリで試行錯誤しながら、コードを書いていきました。

動くようになるまで、結構時間がかかりました。例えば「Hello」と入力してるのに変な文字が表示されたりしました。焦りました。

一旦動くのが確認されたら、最低限のテストを書いて回帰試験しながら、リファクタリングしていきました。LCDを動かす部分とデモアプリを切り離せたので、LCDを動かす部分は[ライブラリ](https://github.com/mnishiguchi/lcd_display)に、デモアプリは[そのライブラリの使用例](https://github.com/mnishiguchi/lcd_display/tree/main/examples/nerves_hello_lcd)とすることにしました。

もし、Nervesに取り組むのが初めての方おられましたら、2日目に@kentaroさんが発表された[ウェブチカでElixir/Nervesに入門する（2020年12月版）](https://qiita.com/kentaro/items/e8df79aa93b9fe9a567e)がわかりやすく説明してくれています。また、[NervesJP Slack](https://join.slack.com/t/nerves-jp/shared_invite/enQtNzc0NTM1OTA5MzQ1LTg5NTAyYThiYzRlNDRmNDIwM2ZlZTJiZDc1MmE5NTFjYzA5OTE4ZTM5OWQxODFhZjY1NWJmZTc4NThkMjQ1Yjk)にジョインされれば、コミュニティが色々教えてくれます。僕もそのNervesJP Slackにお世話になっている一人です。

![](https://user-images.githubusercontent.com/7563926/103111408-b550d680-461a-11eb-8140-48adefe99eb2.gif)


### ハードウェア

- ターゲット機器 (Raspberry Pi等)
- [Micro SDカード](https://ja.wikipedia.org/wiki/SD%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%89)
- [LCD display (16x2)](https://www.google.com/search?q=16x2+LCD+display&tbm=isch)
- [LCD用I2Cインターフェイス](https://www.google.com/search?q=Char+LCD+display+I2C
)
- ジャンパーワイヤ

Connect:

- 5V to [5V Power](https://pinout.xyz/pinout/5v_power)
- GND to [Ground](https://pinout.xyz/pinout/ground)
- SDA to [SDA](https://pinout.xyz/pinout/pin3_gpio2)
- SCL to [SCL](https://pinout.xyz/pinout/pin5_gpio3)

[![](https://user-images.githubusercontent.com/7563926/102290618-65358e00-3f0f-11eb-9031-ecd5227af653.png)](https://pinout.xyz/)

### デモアプリをダウンロード

```
git clone git@github.com:mnishiguchi/lcd_display.git
cd examples/nerves_hello_lcd
```

### ファームウェアをSDカードに焼く

必要な環境関数をセット。 [ターゲット機器タグ一覧](https://hexdocs.pm/nerves/targets.html)

```sh
$ export WIFI_SSID=_____  # your WIFI id
$ export WIFI_PSK=______  # your WIFI password
$ export MIX_TARGET=rpi4  # your target board
```

依存するライブラリをインストール。

```sh
$ mix deps.get
```

FYI: ファームウェア関連のコマンド一覧。

```sh
$ mix help | grep firmware

mix burn                   # Write a firmware image to an SDCard
mix firmware               # Build a firmware bundle
mix firmware.burn          # Build a firmware bundle and write it to an SDCard
mix firmware.gen.script    # Generates a shell script for pushing firmware updates
mix firmware.image         # Create a firmware image file
mix firmware.metadata      # Print out metadata for the current firmware
mix firmware.patch         # Build a firmware patch
mix firmware.unpack        # Unpack a firmware bundle for inspection
mix upload                 # Uploads firmware to a Nerves device over SSH
```

ファームウェアをつくる。

```sh
$ mix firmware
```

SDカードをホストマシンにSDカードを挿入。
下記のコマンドでファームウェアをSDカードに焼く。

```
$ mix firmware.burn
```

SDカードをターゲット機器にSDカードを挿入。ターゲット機器の電源オン。

### ターゲット機器にWIFI接続

WIFIが接続できているか確認。

```
$ ping nerves.local
```

SSHでターゲット機器に接続。しばらくすると、[IEｘシェル](https://hexdocs.pm/iex/IEx.html)が起動する。

```sh
$ 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)> RingLogger.attach
:ok
iex(2)> NervesHelloLcd.hello_i2c
```

- `RingLogger.attach`関数を呼ぶと、ログが表示される。
- `NervesHelloLcd.hello_i2c`関数を呼ぶと、LCDに`Hello world`が表示される。

![nerves_hello_lcd_20201213_185620](https://user-images.githubusercontent.com/7563926/102028171-ba8a6780-3d76-11eb-94f4-f82272fc3063.gif)

## さいごに

いい経験になりました。内容によってはデータシートさえしっかり読めば、特に専用ライブラリがなくても自分でできることがわかりました。

最終的に、ラズパイのGPIOピンをフルに使ってのHelloもやってみました。見た目上、I2Cを使うと配線が少なくシンプルに見えますが、実際はIOエキスパンダが間にあるかどうかの違いでだけで、やってることは同じなのですね。

これからNervesを始める人にも手軽にLCDを操作する一助になれば幸いです。

明日は@ringo156さんです。

[![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F240349%2F5ef22bb9-f357-778c-1bff-b018cce54948.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&w=1400&fit=max&s=c746dae99633be5b9eadf524d7a7b191)](https://nerves-jp.connpass.com/)